Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/**
4
 * Hoa
5
 *
6
 *
7
 * @license
8
 *
9
 * New BSD License
10
 *
11
 * Copyright © 2007-2017, Hoa community. All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions are met:
15
 *     * Redistributions of source code must retain the above copyright
16
 *       notice, this list of conditions and the following disclaimer.
17
 *     * Redistributions in binary form must reproduce the above copyright
18
 *       notice, this list of conditions and the following disclaimer in the
19
 *       documentation and/or other materials provided with the distribution.
20
 *     * Neither the name of the Hoa nor the names of its contributors may be
21
 *       used to endorse or promote products derived from this software without
22
 *       specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
 * POSSIBILITY OF SUCH DAMAGE.
35
 */
36
 
37
namespace Psy\Readline\Hoa;
38
 
39
/**
40
 * Abstract class for all `hoa://`'s nodes.
41
 */
42
class ProtocolNode implements \ArrayAccess, \IteratorAggregate
43
{
44
    /**
45
     * Node's name.
46
     */
47
    protected $_name = null;
48
 
49
    /**
50
     * Path for the `reach` method.
51
     */
52
    protected $_reach = null;
53
 
54
    /**
55
     * Children of the node.
56
     */
57
    private $_children = [];
58
 
59
    /**
60
     * Construct a protocol's node.
61
     * If it is not a data object (i.e. if it does not extend this class to
62
     * overload the `$_name` attribute), we can set the `$_name` attribute
63
     * dynamically. This is useful to create a node on-the-fly.
64
     */
65
    public function __construct(string $name = null, string $reach = null, array $children = [])
66
    {
67
        if (null !== $name) {
68
            $this->_name = $name;
69
        }
70
 
71
        if (null !== $reach) {
72
            $this->_reach = $reach;
73
        }
74
 
75
        foreach ($children as $child) {
76
            $this[] = $child;
77
        }
78
 
79
        return;
80
    }
81
 
82
    /**
83
     * Add a node.
84
     */
85
    #[\ReturnTypeWillChange]
86
    public function offsetSet($name, $node)
87
    {
88
        if (!($node instanceof self)) {
89
            throw new ProtocolException('Protocol node must extend %s.', 0, __CLASS__);
90
        }
91
 
92
        if (empty($name)) {
93
            $name = $node->getName();
94
        }
95
 
96
        if (empty($name)) {
97
            throw new ProtocolException('Cannot add a node to the `hoa://` protocol without a name.', 1);
98
        }
99
 
100
        $this->_children[$name] = $node;
101
    }
102
 
103
    /**
104
     * Get a specific node.
105
     */
106
    public function offsetGet($name): self
107
    {
108
        if (!isset($this[$name])) {
109
            throw new ProtocolException('Node %s does not exist.', 2, $name);
110
        }
111
 
112
        return $this->_children[$name];
113
    }
114
 
115
    /**
116
     * Check if a node exists.
117
     */
118
    public function offsetExists($name): bool
119
    {
120
        return true === \array_key_exists($name, $this->_children);
121
    }
122
 
123
    /**
124
     * Remove a node.
125
     */
126
    #[\ReturnTypeWillChange]
127
    public function offsetUnset($name)
128
    {
129
        unset($this->_children[$name]);
130
    }
131
 
132
    /**
133
     * Resolve a path, i.e. iterate the nodes tree and reach the queue of
134
     * the path.
135
     */
136
    protected function _resolve(string $path, &$accumulator, string $id = null)
137
    {
138
        if (\substr($path, 0, 6) === 'hoa://') {
139
            $path = \substr($path, 6);
140
        }
141
 
142
        if (empty($path)) {
143
            return null;
144
        }
145
 
146
        if (null === $accumulator) {
147
            $accumulator = [];
148
            $posId = \strpos($path, '#');
149
 
150
            if (false !== $posId) {
151
                $id = \substr($path, $posId + 1);
152
                $path = \substr($path, 0, $posId);
153
            } else {
154
                $id = null;
155
            }
156
        }
157
 
158
        $path = \trim($path, '/');
159
        $pos = \strpos($path, '/');
160
 
161
        if (false !== $pos) {
162
            $next = \substr($path, 0, $pos);
163
        } else {
164
            $next = $path;
165
        }
166
 
167
        if (isset($this[$next])) {
168
            if (false === $pos) {
169
                if (null === $id) {
170
                    $this->_resolveChoice($this[$next]->reach(), $accumulator);
171
 
172
                    return true;
173
                }
174
 
175
                $accumulator = null;
176
 
177
                return $this[$next]->reachId($id);
178
            }
179
 
180
            $tnext = $this[$next];
181
            $this->_resolveChoice($tnext->reach(), $accumulator);
182
 
183
            return $tnext->_resolve(\substr($path, $pos + 1), $accumulator, $id);
184
        }
185
 
186
        $this->_resolveChoice($this->reach($path), $accumulator);
187
 
188
        return true;
189
    }
190
 
191
    /**
192
     * Resolve choices, i.e. a reach value has a “;”.
193
     */
194
    protected function _resolveChoice($reach, &$accumulator)
195
    {
196
        if (null === $reach) {
197
            $reach = '';
198
        }
199
 
200
        if (empty($accumulator)) {
201
            $accumulator = \explode(';', $reach);
202
 
203
            return;
204
        }
205
 
206
        if (false === \strpos($reach, ';')) {
207
            if (false !== $pos = \strrpos($reach, "\r")) {
208
                $reach = \substr($reach, $pos + 1);
209
 
210
                foreach ($accumulator as &$entry) {
211
                    $entry = null;
212
                }
213
            }
214
 
215
            foreach ($accumulator as &$entry) {
216
                $entry .= $reach;
217
            }
218
 
219
            return;
220
        }
221
 
222
        $choices = \explode(';', $reach);
223
        $ref = $accumulator;
224
        $accumulator = [];
225
 
226
        foreach ($choices as $choice) {
227
            if (false !== $pos = \strrpos($choice, "\r")) {
228
                $choice = \substr($choice, $pos + 1);
229
 
230
                foreach ($ref as $entry) {
231
                    $accumulator[] = $choice;
232
                }
233
            } else {
234
                foreach ($ref as $entry) {
235
                    $accumulator[] = $entry.$choice;
236
                }
237
            }
238
        }
239
 
240
        unset($ref);
241
 
242
        return;
243
    }
244
 
245
    /**
246
     * Queue of the node.
247
     * Generic one. Must be overrided in children classes.
248
     */
249
    public function reach(string $queue = null)
250
    {
251
        return empty($queue) ? $this->_reach : $queue;
252
    }
253
 
254
    /**
255
     * ID of the component.
256
     * Generic one. Should be overrided in children classes.
257
     */
258
    public function reachId(string $id)
259
    {
260
        throw new ProtocolException('The node %s has no ID support (tried to reach #%s).', 4, [$this->getName(), $id]);
261
    }
262
 
263
    /**
264
     * Set a new reach value.
265
     */
266
    public function setReach(string $reach)
267
    {
268
        $old = $this->_reach;
269
        $this->_reach = $reach;
270
 
271
        return $old;
272
    }
273
 
274
    /**
275
     * Get node's name.
276
     */
277
    public function getName()
278
    {
279
        return $this->_name;
280
    }
281
 
282
    /**
283
     * Get reach's root.
284
     */
285
    protected function getReach()
286
    {
287
        return $this->_reach;
288
    }
289
 
290
    /**
291
     * Get an iterator.
292
     */
293
    public function getIterator(): \ArrayIterator
294
    {
295
        return new \ArrayIterator($this->_children);
296
    }
297
 
298
    /**
299
     * Get root the protocol.
300
     */
301
    public static function getRoot(): Protocol
302
    {
303
        return Protocol::getInstance();
304
    }
305
 
306
    /**
307
     * Print a tree of component.
308
     */
309
    public function __toString(): string
310
    {
311
        static $i = 0;
312
 
313
        $out = \str_repeat('  ', $i).$this->getName()."\n";
314
 
315
        foreach ($this as $node) {
316
            ++$i;
317
            $out .= $node;
318
            --$i;
319
        }
320
 
321
        return $out;
322
    }
323
}