Completed
Push — master ( b32ba2...3a9807 )
by Hong
03:09
created

ReferenceTrait::resolveReference()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 28
rs 8.5806
cc 4
eloc 13
nc 4
nop 2
1
<?php
2
/**
3
 * Phossa Project
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Library
8
 * @package   Phossa2\Shared
9
 * @copyright Copyright (c) 2016 phossa.com
10
 * @license   http://mit-license.org/ MIT License
11
 * @link      http://www.phossa.com/
12
 */
13
/*# declare(strict_types=1); */
14
15
namespace Phossa2\Shared\Reference;
16
17
use Phossa2\Shared\Exception\RuntimeException;
18
use Phossa2\Shared\Message\Message;
19
/**
20
 * ReferenceTrait
21
 *
22
 * Provides reference & dereference methods
23
 *
24
 * @package Phossa2\Shared
25
 * @author  Hong Zhang <[email protected]>
26
 * @see     ReferenceInterface
27
 * @version 2.0.4
28
 * @since   2.0.4 added
29
 */
30
trait ReferenceTrait
31
{
32
    /**
33
     * refernece start chars
34
     *
35
     * @var    string
36
     * @access protected
37
     */
38
    protected $ref_start = '${';
39
40
    /**
41
     * reference ending chars
42
     *
43
     * @var    string
44
     * @access protected
45
     */
46
    protected $ref_end = '}';
47
48
    /**
49
     * cached pattern to match
50
     *
51
     * @var    string
52
     * @access protected
53
     */
54
    protected $ref_pattern = '~(\$\{((?:(?!\$\{|\}).)+?)\})~';
55
56
    /**
57
     * unresolved reference
58
     *
59
     * @var    array
60
     * @access protected
61
     */
62
    protected $unresolved = [];
63
64
    /**
65
     * {@inheritDoc}
66
     */
67
    public function setReference(
68
        /*# string */ $start,
69
        /*# string */ $end
70
    ) {
71
        $this->ref_start = $start;
72
        $this->ref_end = $end;
73
74
        // build pattern
75
        $s = preg_quote($start);
76
        $e = preg_quote($end);
77
        $this->ref_pattern = sprintf(
78
            "~(%s((?:(?!%s|%s).)+?)%s)~", $s, $s, $e, $e
79
        );
80
81
        return $this;
82
    }
83
84
    /**
85
     * {@inheritDoc}
86
     */
87
    public function hasReference(
88
        /*# string */ $subject,
89
        array &$matched
90
    )/*# : bool */ {
91
        if (is_string($subject) &&
92
            false !== strpos($subject, $this->ref_start) &&
93
            preg_match($this->ref_pattern, $subject, $matched)
94
        ) {
95
            return true;
96
        }
97
        return false;
98
    }
99
100
    /**
101
     * {@inheritDoc}
102
     */
103
    public function deReference(/*# string */ $subject)
104
    {
105
        $matched = [];
106
        $loop = 0;
107
        while ($this->hasReference($subject, $matched)) {
108
            $val = $this->resolveReference($matched[2], $loop++);
109
            if (is_string($val)) {
110
                $subject = str_replace($matched[1], $val, $subject);
111
            } else {
112
                return $val;
113
            }
114
        }
115
        return $subject;
116
    }
117
118
    /**
119
     * {@inheritDoc}
120
     */
121
    public function deReferenceArray(&$dataArray)
122
    {
123
        if (!is_array($dataArray)) {
124
            return;
125
        }
126
127
        foreach ($dataArray as &$data) {
128
            if (is_string($data)) {
129
                $data = $this->deReference($data);
130
            }
131
            $this->dereferenceArray($data);
132
        }
133
    }
134
135
    /**
136
     * Resolve the reference $name
137
     *
138
     * @param  string $name
139
     * @param  int $loop
140
     * @return mixed
141
     * @throws RuntimeException if loop found
142
     * @access protected
143
     */
144
    protected function resolveReference(/*# string */ $name, /*# int */ $loop)
145
    {
146
        // unresolved found
147
        if (isset($this->unresolved[$name])) {
148
            return $this->unresolved[$name];
149
        }
150
151
        // loop found
152
        if ($loop > 10) {
153
            throw new RuntimeException(
154
                Message::get(Message::MSG_REF_MALFORMED, $name),
155
                Message::MSG_REF_MALFORMED
156
            );
157
        }
158
159
        // get the referenced value
160
        $val = $this->getReference($name);
161
162
        // not found
163
        if (is_null($val)) {
164
            $this->unresolved[$name] = $this->resolveUnknown($name);
165
            return $this->unresolved[$name];
166
167
        // found it
168
        } else {
169
            return $val;
170
        }
171
    }
172
173
    /**
174
     * For unknown reference $name
175
     *
176
     * @param  string $name
177
     * @return mixed
178
     * @access protected
179
     */
180
    abstract protected function resolveUnknown(/*# string */ $name);
181
182
    /**
183
     * The real resolving method. return NULL for unknown reference
184
     *
185
     * @param  string $name
186
     * @return mixed
187
     * @access protected
188
     */
189
    abstract protected function getReference(/*# string */ $name);
190
}
191