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
 * Build a callable object, i.e. `function`, `class::method`, `object->method` or
41
 * closure. They all have the same behaviour. This callable is an extension of
42
 * native PHP callable (aka callback) to integrate Hoa's structures.
43
 */
44
class Xcallable
45
{
46
    /**
47
     * Callback with the PHP format.
48
     */
49
    protected $_callback = null;
50
 
51
    /**
52
     * Callable hash.
53
     */
54
    protected $_hash = null;
55
 
56
    /**
57
     * Allocates a xcallable based on a callback.
58
     *
59
     * Accepted forms:
60
     *     * `'function'`,
61
     *     * `'class::method'`,
62
     *     * `'class', 'method'`,
63
     *     * `$object, 'method'`,
64
     *     * `$object, ''`,
65
     *     * `function (…) { … }`,
66
     *     * `['class', 'method']`,
67
     *     * `[$object, 'method']`.
68
     *
69
     * # Examples
70
     *
71
     * ```php
72
     * $toUpper = new Hoa\Consistency\Xcallable('strtoupper');
73
     * assert('FOO' === $toUpper('foo'));
74
     * ```
75
     *
76
     * # Exceptions
77
     *
78
     * A `Hoa\Consistency\Exception` exception is thrown if the callback form
79
     * is invalid.
80
     *
81
     * ```php,must_throw(Hoa\Consistency\Exception)
82
     * new Hoa\Consistency\Xcallable('Foo:');
83
     * ```
84
     */
85
    public function __construct($call, $able = '')
86
    {
87
        if ($call instanceof \Closure) {
88
            $this->_callback = $call;
89
 
90
            return;
91
        }
92
 
93
        if (!\is_string($able)) {
94
            throw new Exception('Bad callback form; the able part must be a string.', 0);
95
        }
96
 
97
        if ('' === $able) {
98
            if (\is_string($call)) {
99
                if (false === \strpos($call, '::')) {
100
                    if (!\function_exists($call)) {
101
                        throw new Exception('Bad callback form; function %s does not exist.', 1, $call);
102
                    }
103
 
104
                    $this->_callback = $call;
105
 
106
                    return;
107
                }
108
 
109
                list($call, $able) = \explode('::', $call);
110
            } elseif (\is_object($call)) {
111
                if ($call instanceof StreamOut) {
112
                    $able = null;
113
                } elseif (\method_exists($call, '__invoke')) {
114
                    $able = '__invoke';
115
                } else {
116
                    throw new Exception('Bad callback form; an object but without a known '.'method.', 2);
117
                }
118
            } elseif (\is_array($call) && isset($call[0])) {
119
                if (!isset($call[1])) {
120
                    $this->__construct($call[0]);
121
                    return;
122
                }
123
 
124
                $this->__construct($call[0], $call[1]);
125
                return;
126
            } else {
127
                throw new Exception('Bad callback form.', 3);
128
            }
129
        }
130
 
131
        $this->_callback = [$call, $able];
132
 
133
        return;
134
    }
135
 
136
    /**
137
     * Calls the callable.
138
     */
139
    public function __invoke(...$arguments)
140
    {
141
        $callback = $this->getValidCallback($arguments);
142
 
143
        return $callback(...$arguments);
144
    }
145
 
146
    /**
147
     * Returns a valid PHP callback.
148
     */
149
    public function getValidCallback(array &$arguments = [])
150
    {
151
        $callback = $this->_callback;
152
        $head = null;
153
 
154
        if (isset($arguments[0])) {
155
            $head = &$arguments[0];
156
        }
157
 
158
        // If method is undetermined, we find it (we understand event bucket and
159
        // stream).
160
        if (null !== $head &&
161
            \is_array($callback) &&
162
            null === $callback[1]) {
163
            if ($head instanceof EventBucket) {
164
                $head = $head->getData();
165
            }
166
 
167
            switch ($type = \gettype($head)) {
168
                case 'string':
169
                    if (1 === \strlen($head)) {
170
                        $method = 'writeCharacter';
171
                    } else {
172
                        $method = 'writeString';
173
                    }
174
 
175
                    break;
176
 
177
                case 'boolean':
178
                case 'integer':
179
                case 'array':
180
                    $method = 'write'.\ucfirst($type);
181
 
182
                    break;
183
 
184
                case 'double':
185
                    $method = 'writeFloat';
186
 
187
                    break;
188
 
189
                default:
190
                    $method = 'writeAll';
191
                    $head = $head."\n";
192
            }
193
 
194
            $callback[1] = $method;
195
        }
196
 
197
        return $callback;
198
    }
199
 
200
    /**
201
     * Computes the hash of this callable.
202
     *
203
     * Will produce:
204
     *     * `function#…`,
205
     *     * `class#…::…`,
206
     *     * `object(…)#…::…`,
207
     *     * `closure(…)`.
208
     */
209
    public function getHash(): string
210
    {
211
        if (null !== $this->_hash) {
212
            return $this->_hash;
213
        }
214
 
215
        $_ = &$this->_callback;
216
 
217
        if (\is_string($_)) {
218
            return $this->_hash = 'function#'.$_;
219
        }
220
 
221
        if (\is_array($_)) {
222
            return
223
                $this->_hash =
224
                    (\is_object($_[0])
225
                        ? 'object('.\spl_object_hash($_[0]).')'.
226
                          '#'.\get_class($_[0])
227
                        : 'class#'.$_[0]).
228
                    '::'.
229
                    (null !== $_[1]
230
                        ? $_[1]
231
                        : '???');
232
        }
233
 
234
        return $this->_hash = 'closure('.\spl_object_hash($_).')';
235
    }
236
 
237
    /**
238
     * The string representation of a callable is its hash.
239
     */
240
    public function __toString(): string
241
    {
242
        return $this->getHash();
243
    }
244
 
245
    /**
246
     * Hoa's xcallable() helper.
247
     */
248
    public static function from($call, $able = '')
249
    {
250
        if ($call instanceof self) {
251
            return $call;
252
        }
253
 
254
        return new self($call, $able);
255
    }
256
}