Completed
Push — master ( 3a9807...50ee9c )
by Hong
02:59
created

ReferenceTrait::resolveReference()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 22
rs 9.2
cc 3
eloc 10
nc 3
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
     * {@inheritDoc}
58
     */
59
    public function setReference(
60
        /*# string */ $start,
61
        /*# string */ $end
62
    ) {
63
        $this->ref_start = $start;
64
        $this->ref_end = $end;
65
66
        // build pattern
67
        $s = preg_quote($start);
68
        $e = preg_quote($end);
69
        $this->ref_pattern = sprintf(
70
            "~(%s((?:(?!%s|%s).)+?)%s)~", $s, $s, $e, $e
71
        );
72
73
        return $this;
74
    }
75
76
    /**
77
     * {@inheritDoc}
78
     */
79
    public function hasReference(
80
        /*# string */ $subject,
81
        array &$matched
82
    )/*# : bool */ {
83
        if (is_string($subject) &&
84
            false !== strpos($subject, $this->ref_start) &&
85
            preg_match($this->ref_pattern, $subject, $matched)
86
        ) {
87
            return true;
88
        }
89
        return false;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function deReference(/*# string */ $subject)
96
    {
97
        $loop = 0;
98
        $matched = [];
99
100
        while ($this->hasReference($subject, $matched)) {
101
            // resolve the reference
102
            $val = $this->resolveReference($matched[2], $loop++);
103
104
            // resolved to another string
105
            if (is_string($val)) {
106
                $subject = str_replace($matched[1], $val, $subject);
107
108
            // resolved to array or object
109
            } else {
110
                return $this->checkValue($val, $subject, $matched[1]);
111
            }
112
        }
113
        return $subject;
114
    }
115
116
    /**
117
     * {@inheritDoc}
118
     */
119
    public function deReferenceArray(&$dataArray)
120
    {
121
        if (!is_array($dataArray)) {
122
            return;
123
        }
124
125
        foreach ($dataArray as &$data) {
126
            if (is_string($data)) {
127
                $data = $this->deReference($data);
128
            }
129
            $this->dereferenceArray($data);
130
        }
131
    }
132
133
    /**
134
     * Check dereferenced value
135
     *
136
     * @param  mixed $value
137
     * @param  string $subject
138
     * @param  string $reference
139
     * @return mixed
140
     * @throws RuntimeException
141
     * @access protected
142
     */
143
    protected function checkValue(
144
        $value,
145
        /*# string */ $subject,
146
        /*# string */ $reference
147
    ) {
148
        // partial match
149
        if ($subject != $reference) {
150
            throw new RuntimeException(
151
                Message::get(Message::MSG_REF_MALFORMED, $reference),
152
                Message::MSG_REF_MALFORMED
153
            );
154
        }
155
        return $value;
156
    }
157
158
    /**
159
     * Resolve the reference $name
160
     *
161
     * @param  string $name
162
     * @param  int $loop
163
     * @return mixed
164
     * @throws RuntimeException if loop found
165
     * @access protected
166
     */
167
    protected function resolveReference(/*# string */ $name, /*# int */ $loop)
168
    {
169
        // loop found
170
        if ($loop > 10) {
171
            throw new RuntimeException(
172
                Message::get(Message::MSG_REF_MALFORMED, $name),
173
                Message::MSG_REF_MALFORMED
174
            );
175
        }
176
177
        // get the referenced value
178
        $val = $this->getReference($name);
179
180
        // not found
181
        if (is_null($val)) {
182
            return $this->resolveUnknown($name);
183
184
        // found it
185
        } else {
186
            return $val;
187
        }
188
    }
189
190
    /**
191
     * For unknown reference $name
192
     *
193
     * @param  string $name
194
     * @return mixed
195
     * @access protected
196
     */
197
    abstract protected function resolveUnknown(/*# string */ $name);
198
199
    /**
200
     * The real resolving method. return NULL for unknown reference
201
     *
202
     * @param  string $name
203
     * @return mixed
204
     * @access protected
205
     */
206
    abstract protected function getReference(/*# string */ $name);
207
}
208