├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── composer.json
└── src
├── AbstractOptions.php
├── ArrayObject.php
├── ArraySerializableInterface.php
├── ArrayStack.php
├── ArrayUtils.php
├── ArrayUtils
├── MergeRemoveKey.php
├── MergeReplaceKey.php
└── MergeReplaceKeyInterface.php
├── ConsoleHelper.php
├── DispatchableInterface.php
├── ErrorHandler.php
├── Exception
├── BadMethodCallException.php
├── DomainException.php
├── ExceptionInterface.php
├── ExtensionNotLoadedException.php
├── InvalidArgumentException.php
├── LogicException.php
└── RuntimeException.php
├── FastPriorityQueue.php
├── Glob.php
├── Guard
├── AllGuardsTrait.php
├── ArrayOrTraversableGuardTrait.php
├── EmptyGuardTrait.php
└── NullGuardTrait.php
├── InitializableInterface.php
├── JsonSerializable.php
├── Message.php
├── MessageInterface.php
├── ParameterObjectInterface.php
├── Parameters.php
├── ParametersInterface.php
├── PriorityList.php
├── PriorityQueue.php
├── Request.php
├── RequestInterface.php
├── Response.php
├── ResponseInterface.php
├── SplPriorityQueue.php
├── SplQueue.php
├── SplStack.php
├── StringUtils.php
└── StringWrapper
├── AbstractStringWrapper.php
├── Iconv.php
├── Intl.php
├── MbString.php
├── Native.php
└── StringWrapperInterface.php
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file, in reverse chronological order by release.
4 |
5 | ## 3.2.2 - TBD
6 |
7 | ### Added
8 |
9 | - [#96](https://github.com/zendframework/zend-stdlib/pull/96) Added PHP 7.3 support
10 |
11 | ### Changed
12 |
13 | - Nothing.
14 |
15 | ### Deprecated
16 |
17 | - Nothing.
18 |
19 | ### Removed
20 |
21 | - Nothing.
22 |
23 | ### Fixed
24 |
25 | - Nothing.
26 |
27 | ## 3.2.1 - 2018-08-28
28 |
29 | ### Added
30 |
31 | - Nothing.
32 |
33 | ### Changed
34 |
35 | - Nothing.
36 |
37 | ### Deprecated
38 |
39 | - Nothing.
40 |
41 | ### Removed
42 |
43 | - Nothing.
44 |
45 | ### Fixed
46 |
47 | - [#92](https://github.com/zendframework/zend-stdlib/pull/92) fixes serialization of `SplPriorityQueue` by ensuring its `$serial`
48 | property is also serialized.
49 |
50 | - [#91](https://github.com/zendframework/zend-stdlib/pull/91) fixes behavior in the `ArrayObject` implementation that was not
51 | compatible with PHP 7.3.
52 |
53 | ## 3.2.0 - 2018-04-30
54 |
55 | ### Added
56 |
57 | - [#87](https://github.com/zendframework/zend-stdlib/pull/87) adds support for PHP 7.2.
58 |
59 | ### Changed
60 |
61 | - Nothing.
62 |
63 | ### Deprecated
64 |
65 | - Nothing.
66 |
67 | ### Removed
68 |
69 | - [#87](https://github.com/zendframework/zend-stdlib/pull/87) removes support for HHVM.
70 |
71 | ### Fixed
72 |
73 | - Nothing.
74 |
75 | ## 3.1.1 - 2018-04-12
76 |
77 | ### Added
78 |
79 | - Nothing.
80 |
81 | ### Changed
82 |
83 | - [#67](https://github.com/zendframework/zend-stdlib/pull/67) changes the typehint of the `$content` property
84 | of the `Message` class to indicate it is a string. All known implementations
85 | already assumed this.
86 |
87 | ### Deprecated
88 |
89 | - Nothing.
90 |
91 | ### Removed
92 |
93 | - Nothing.
94 |
95 | ### Fixed
96 |
97 | - [#60](https://github.com/zendframework/zend-stdlib/pull/60) fixes an issue whereby calling `remove()` would
98 | incorrectly re-calculate the maximum priority stored in the queue.
99 |
100 | - [#60](https://github.com/zendframework/zend-stdlib/pull/60) fixes an infinite loop condition that can occur when
101 | inserting an item at 0 priority.
102 |
103 | ## 3.1.0 - 2016-09-13
104 |
105 | ### Added
106 |
107 | - [#63](https://github.com/zendframework/zend-stdlib/pull/63) adds a new
108 | `Zend\Stdlib\ConsoleHelper` class, providing minimal support for writing
109 | output to `STDOUT` and `STDERR`, with optional colorization, when the console
110 | supports that feature.
111 |
112 | ### Deprecated
113 |
114 | - [#38](https://github.com/zendframework/zend-stdlib/pull/38) deprecates
115 | `Zend\Stdlib\JsonSerializable`, as all supported version of PHP now support
116 | it.
117 |
118 | ### Removed
119 |
120 | - Nothing.
121 |
122 | ### Fixed
123 |
124 | - Nothing.
125 |
126 | ## 3.0.1 - 2016-04-12
127 |
128 | ### Added
129 |
130 | - Nothing.
131 |
132 | ### Deprecated
133 |
134 | - Nothing.
135 |
136 | ### Removed
137 |
138 | - Nothing.
139 |
140 | ### Fixed
141 |
142 | - [#59](https://github.com/zendframework/zend-stdlib/pull/59) fixes a notice
143 | when defining the `Zend\Json\Json::GLOB_BRACE` constant on systems using
144 | non-gcc glob implementations.
145 |
146 | ## 3.0.0 - 2016-02-03
147 |
148 | ### Added
149 |
150 | - [#51](https://github.com/zendframework/zend-stdlib/pull/51) adds PHP 7 as a
151 | supported PHP version.
152 | - [#51](https://github.com/zendframework/zend-stdlib/pull/51) adds a migration
153 | document from v2 to v3. Hint: if you use hydrators, you need to be using
154 | zend-hydrator instead!
155 | - [#51](https://github.com/zendframework/zend-stdlib/pull/51) adds automated
156 | documentation builds to gh-pages.
157 |
158 | ### Deprecated
159 |
160 | - Nothing.
161 |
162 | ### Removed
163 |
164 | - [#33](https://github.com/zendframework/zend-stdlib/pull/33) - removed
165 | deprecated classes
166 | - *All Hydrator classes* see #22.
167 | - `Zend\Stdlib\CallbackHandler` see #35
168 | - [#37](https://github.com/zendframework/zend-stdlib/pull/37) - removed
169 | deprecated classes and polyfills:
170 | - `Zend\Stdlib\DateTime`; this had been deprecated since 2.5, and only
171 | existed as a polyfill for the `createFromISO8601()` support, now standard
172 | in all PHP versions we support.
173 | - `Zend\Stdlib\Exception\InvalidCallbackException`, which was unused since #33.
174 | - `Zend\Stdlib\Guard\GuardUtils`, which duplicated `Zend\Stdlib\Guard\AllGuardsTrait`
175 | to allow usage with pre-PHP 5.4 versions.
176 | - `src/compatibility/autoload.php`, which has been dprecated since 2.5.
177 | - [#37](https://github.com/zendframework/zend-stdlib/pull/37) - removed
178 | unneeded dependencies:
179 | - zend-config (used only in testing ArrayUtils, and the test was redundant)
180 | - zend-serializer (no longer used)
181 | - [#51](https://github.com/zendframework/zend-stdlib/pull/51) removes the
182 | documentation for hydrators, as those are part of the zend-hydrator
183 | component.
184 |
185 | ### Fixed
186 |
187 | - Nothing.
188 |
189 | ## 2.7.4 - 2015-10-15
190 |
191 | ### Added
192 |
193 | - Nothing.
194 |
195 | ### Deprecated
196 |
197 | - [#35](https://github.com/zendframework/zend-stdlib/pull/35) deprecates
198 | `Zend\Stdlib\CallbackHandler`, as the one component that used it,
199 | zend-eventmanager, will no longer depend on it starting in v3.
200 |
201 | ### Removed
202 |
203 | - Nothing.
204 |
205 | ### Fixed
206 |
207 | - Nothing.
208 |
209 | ## 2.7.3 - 2015-09-24
210 |
211 | ### Added
212 |
213 | - Nothing.
214 |
215 | ### Deprecated
216 |
217 | - Nothing.
218 |
219 | ### Removed
220 |
221 | - Nothing.
222 |
223 | ### Fixed
224 |
225 | - [#27](https://github.com/zendframework/zend-stdlib/pull/27) fixes a race
226 | condition in the `FastPriorityQueue::remove()` logic that occurs when removing
227 | items iteratively from the same priority of a queue.
228 |
229 | ## 2.7.2 - 2015-09-23
230 |
231 | ### Added
232 |
233 | - Nothing.
234 |
235 | ### Deprecated
236 |
237 | - Nothing.
238 |
239 | ### Removed
240 |
241 | - Nothing.
242 |
243 | ### Fixed
244 |
245 | - [#26](https://github.com/zendframework/zend-stdlib/pull/26) fixes a subtle
246 | inheritance issue with deprecation in the hydrators, and updates the
247 | `HydratorInterface` to also extend the zend-hydrator `HydratorInterface` to
248 | ensure LSP is preserved.
249 |
250 | ## 2.7.1 - 2015-09-22
251 |
252 | ### Added
253 |
254 | - Nothing.
255 |
256 | ### Deprecated
257 |
258 | - Nothing.
259 |
260 | ### Removed
261 |
262 | - Nothing.
263 |
264 | ### Fixed
265 |
266 | - [#24](https://github.com/zendframework/zend-stdlib/pull/24) fixes an import in
267 | `FastPriorityQueue` to alias `SplPriorityQueue` in order to disambiguate with
268 | the local override present in the component.
269 |
270 | ## 2.7.0 - 2015-09-22
271 |
272 | ### Added
273 |
274 | - [#19](https://github.com/zendframework/zend-stdlib/pull/19) adds a new
275 | `FastPriorityQueue` implementation. It follows the same signature as
276 | `SplPriorityQueue`, but uses a performance-optimized algorithm:
277 |
278 | - inserts are 2x faster than `SplPriorityQueue` and 3x faster than the
279 | `Zend\Stdlib\PriorityQueue` implementation.
280 | - extracts are 4x faster than `SplPriorityQueue` and 4-5x faster than the
281 | `Zend\Stdlib\PriorityQueue` implementation.
282 |
283 | The intention is to use this as a drop-in replacement in the
284 | `zend-eventmanager` component to provide performance benefits.
285 |
286 | ### Deprecated
287 |
288 | - [#20](https://github.com/zendframework/zend-stdlib/pull/20) deprecates *all
289 | hydrator* classes, in favor of the new [zend-hydrator](https://github.com/zendframework/zend-hydrator)
290 | component. All classes were updated to extend their zend-hydrator equivalents,
291 | and marked as `@deprecated`, indicating the equivalent class from the other
292 | repository.
293 |
294 | Users *should* immediately start changing their code to use the zend-hydrator
295 | equivalents; in most cases, this can be as easy as removing the `Stdlib`
296 | namespace from import statements or hydrator configuration. Hydrators will be
297 | removed entirely from zend-stdlib in v3.0, and all future updates to hydrators
298 | will occur in the zend-hydrator library.
299 |
300 | Changes with backwards compatibility implications:
301 |
302 | - Users implementing `Zend\Stdlib\Hydrator\HydratorAwareInterface` will need to
303 | update their `setHydrator()` implementation to typehint on
304 | `Zend\Hydrator\HydratorInterface`. This can be done by changing the import
305 | statement for that interface as follows:
306 |
307 | ```php
308 | // Replace this:
309 | use Zend\Stdlib\Hydrator\HydratorInterface;
310 | // with this:
311 | use Zend\Hydrator\HydratorInterface;
312 | ```
313 |
314 | If you are not using imports, change the typehint within the signature itself:
315 |
316 | ```php
317 | // Replace this:
318 | public function setHydrator(\Zend\Stdlib\Hydrator\HydratorInterface $hydrator)
319 | // with this:
320 | public function setHydrator(\Zend\Hydrator\HydratorInterface $hydrator)
321 | ```
322 |
323 | If you are using `Zend\Stdlib\Hydrator\HydratorAwareTrait`, no changes are
324 | necessary, unless you override that method.
325 |
326 | - If you were catching hydrator-generated exceptions, these were previously in
327 | the `Zend\Stdlib\Exception` namespace. You will need to update your code to
328 | catch exceptions in the `Zend\Hydrator\Exception` namespace.
329 |
330 | - Users who *do* migrate to zend-hydrator may end up in a situation where
331 | their code will not work with existing libraries that are still type-hinting
332 | on the zend-stdlib interfaces. We will be attempting to address that ASAP,
333 | but the deprecation within zend-stdlib is necessary as a first step.
334 |
335 | In the meantime, you can write hydrators targeting zend-stdlib still in
336 | order to guarantee compatibility.
337 |
338 | ### Removed
339 |
340 | - Nothing.
341 |
342 | ### Fixed
343 |
344 | - Nothing.
345 |
346 | ## 2.6.0 - 2015-07-21
347 |
348 | ### Added
349 |
350 | - [#13](https://github.com/zendframework/zend-stdlib/pull/13) adds
351 | `Zend\Stdlib\Hydrator\Iterator`, which provides mechanisms for hydrating
352 | objects when iterating a traversable. This allows creating generic collection
353 | resultsets; the original idea was pulled from
354 | [PhlyMongo](https://github.com/phly/PhlyMongo), where it was used to hydrate
355 | collections retrieved from MongoDB.
356 |
357 | ### Deprecated
358 |
359 | - Nothing.
360 |
361 | ### Removed
362 |
363 | - Nothing.
364 |
365 | ### Fixed
366 |
367 | - Nothing.
368 |
369 | ## 2.5.2 - 2015-07-21
370 |
371 | ### Added
372 |
373 | - Nothing.
374 |
375 | ### Deprecated
376 |
377 | - Nothing.
378 |
379 | ### Removed
380 |
381 | - Nothing.
382 |
383 | ### Fixed
384 |
385 | - [#9](https://github.com/zendframework/zend-stdlib/pull/9) fixes an issue with
386 | count incrementation during insert in PriorityList, ensuring that incrementation only
387 | occurs when the item inserted was not previously present in the list.
388 |
389 | ## 2.4.4 - 2015-07-21
390 |
391 | ### Added
392 |
393 | - Nothing.
394 |
395 | ### Deprecated
396 |
397 | - Nothing.
398 |
399 | ### Removed
400 |
401 | - Nothing.
402 |
403 | ### Fixed
404 |
405 | - [#9](https://github.com/zendframework/zend-stdlib/pull/9) fixes an issue with
406 | count incrementation during insert in PriorityList, ensuring that incrementation only
407 | occurs when the item inserted was not previously present in the list.
408 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2005-2018, Zend Technologies USA, Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | - Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | - Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from this
16 | software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zend-stdlib
2 |
3 | > ## Repository abandoned 2019-12-31
4 | >
5 | > This repository has moved to [laminas/laminas-stdlib](https://github.com/laminas/laminas-stdlib).
6 |
7 | [](https://secure.travis-ci.org/zendframework/zend-stdlib)
8 | [](https://coveralls.io/github/zendframework/zend-stdlib?branch=master)
9 |
10 | `Zend\Stdlib` is a set of components that implements general purpose utility
11 | class for different scopes like:
12 |
13 | - array utilities functions;
14 | - general messaging systems;
15 | - string wrappers;
16 | - etc.
17 |
18 | ---
19 |
20 | - File issues at https://github.com/zendframework/zend-stdlib/issues
21 | - Documentation is at https://docs.zendframework.com/zend-stdlib/
22 |
23 | ## Benchmarks
24 |
25 | We provide scripts for benchmarking zend-stdlib using the
26 | [PHPBench](https://github.com/phpbench/phpbench) framework; these can be
27 | found in the `benchmark/` directory.
28 |
29 | To execute the benchmarks you can run the following command:
30 |
31 | ```bash
32 | $ vendor/bin/phpbench run --report=aggregate
33 | ```
34 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zendframework/zend-stdlib",
3 | "description": "SPL extensions, array utilities, error handlers, and more",
4 | "license": "BSD-3-Clause",
5 | "keywords": [
6 | "zf",
7 | "zendframework",
8 | "stdlib"
9 | ],
10 | "support": {
11 | "docs": "https://docs.zendframework.com/zend-stdlib/",
12 | "issues": "https://github.com/zendframework/zend-stdlib/issues",
13 | "source": "https://github.com/zendframework/zend-stdlib",
14 | "rss": "https://github.com/zendframework/zend-stdlib/releases.atom",
15 | "slack": "https://zendframework-slack.herokuapp.com",
16 | "forum": "https://discourse.zendframework.com/c/questions/components"
17 | },
18 | "require": {
19 | "php": "^5.6 || ^7.0"
20 | },
21 | "require-dev": {
22 | "phpbench/phpbench": "^0.13",
23 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
24 | "zendframework/zend-coding-standard": "~1.0.0"
25 | },
26 | "autoload": {
27 | "psr-4": {
28 | "Zend\\Stdlib\\": "src/"
29 | }
30 | },
31 | "autoload-dev": {
32 | "psr-4": {
33 | "ZendTest\\Stdlib\\": "test/",
34 | "ZendBench\\Stdlib\\": "benchmark/"
35 | }
36 | },
37 | "config": {
38 | "sort-packages": true
39 | },
40 | "extra": {
41 | "branch-alias": {
42 | "dev-master": "3.2.x-dev",
43 | "dev-develop": "3.3.x-dev"
44 | }
45 | },
46 | "scripts": {
47 | "check": [
48 | "@cs-check",
49 | "@test"
50 | ],
51 | "cs-check": "phpcs",
52 | "cs-fix": "phpcbf",
53 | "test": "phpunit --colors=always",
54 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/AbstractOptions.php:
--------------------------------------------------------------------------------
1 | setFromArray($options);
35 | }
36 | }
37 |
38 | /**
39 | * Set one or more configuration properties
40 | *
41 | * @param array|Traversable|AbstractOptions $options
42 | * @throws Exception\InvalidArgumentException
43 | * @return AbstractOptions Provides fluent interface
44 | */
45 | public function setFromArray($options)
46 | {
47 | if ($options instanceof self) {
48 | $options = $options->toArray();
49 | }
50 |
51 | if (! is_array($options) && ! $options instanceof Traversable) {
52 | throw new Exception\InvalidArgumentException(
53 | sprintf(
54 | 'Parameter provided to %s must be an %s, %s or %s',
55 | __METHOD__,
56 | 'array',
57 | 'Traversable',
58 | 'Zend\Stdlib\AbstractOptions'
59 | )
60 | );
61 | }
62 |
63 | foreach ($options as $key => $value) {
64 | $this->__set($key, $value);
65 | }
66 |
67 | return $this;
68 | }
69 |
70 | /**
71 | * Cast to array
72 | *
73 | * @return array
74 | */
75 | public function toArray()
76 | {
77 | $array = [];
78 | $transform = function ($letters) {
79 | $letter = array_shift($letters);
80 | return '_' . strtolower($letter);
81 | };
82 | foreach ($this as $key => $value) {
83 | if ($key === '__strictMode__') {
84 | continue;
85 | }
86 | $normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key);
87 | $array[$normalizedKey] = $value;
88 | }
89 | return $array;
90 | }
91 |
92 | /**
93 | * Set a configuration property
94 | *
95 | * @see ParameterObject::__set()
96 | * @param string $key
97 | * @param mixed $value
98 | * @throws Exception\BadMethodCallException
99 | * @return void
100 | */
101 | public function __set($key, $value)
102 | {
103 | $setter = 'set' . str_replace('_', '', $key);
104 |
105 | if (is_callable([$this, $setter])) {
106 | $this->{$setter}($value);
107 |
108 | return;
109 | }
110 |
111 | if ($this->__strictMode__) {
112 | throw new Exception\BadMethodCallException(sprintf(
113 | 'The option "%s" does not have a callable "%s" ("%s") setter method which must be defined',
114 | $key,
115 | 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))),
116 | $setter
117 | ));
118 | }
119 | }
120 |
121 | /**
122 | * Get a configuration property
123 | *
124 | * @see ParameterObject::__get()
125 | * @param string $key
126 | * @throws Exception\BadMethodCallException
127 | * @return mixed
128 | */
129 | public function __get($key)
130 | {
131 | $getter = 'get' . str_replace('_', '', $key);
132 |
133 | if (is_callable([$this, $getter])) {
134 | return $this->{$getter}();
135 | }
136 |
137 | throw new Exception\BadMethodCallException(sprintf(
138 | 'The option "%s" does not have a callable "%s" getter method which must be defined',
139 | $key,
140 | 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)))
141 | ));
142 | }
143 |
144 | /**
145 | * Test if a configuration property is null
146 | * @see ParameterObject::__isset()
147 | * @param string $key
148 | * @return bool
149 | */
150 | public function __isset($key)
151 | {
152 | $getter = 'get' . str_replace('_', '', $key);
153 |
154 | return method_exists($this, $getter) && null !== $this->__get($key);
155 | }
156 |
157 | /**
158 | * Set a configuration property to NULL
159 | *
160 | * @see ParameterObject::__unset()
161 | * @param string $key
162 | * @throws Exception\InvalidArgumentException
163 | * @return void
164 | */
165 | public function __unset($key)
166 | {
167 | try {
168 | $this->__set($key, null);
169 | } catch (Exception\BadMethodCallException $e) {
170 | throw new Exception\InvalidArgumentException(
171 | 'The class property $' . $key . ' cannot be unset as'
172 | . ' NULL is an invalid value for it',
173 | 0,
174 | $e
175 | );
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/ArrayObject.php:
--------------------------------------------------------------------------------
1 | setFlags($flags);
65 | $this->storage = $input;
66 | $this->setIteratorClass($iteratorClass);
67 | $this->protectedProperties = array_keys(get_object_vars($this));
68 | }
69 |
70 | /**
71 | * Returns whether the requested key exists
72 | *
73 | * @param mixed $key
74 | * @return bool
75 | */
76 | public function __isset($key)
77 | {
78 | if ($this->flag == self::ARRAY_AS_PROPS) {
79 | return $this->offsetExists($key);
80 | }
81 | if (in_array($key, $this->protectedProperties)) {
82 | throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
83 | }
84 |
85 | return isset($this->$key);
86 | }
87 |
88 | /**
89 | * Sets the value at the specified key to value
90 | *
91 | * @param mixed $key
92 | * @param mixed $value
93 | * @return void
94 | */
95 | public function __set($key, $value)
96 | {
97 | if ($this->flag == self::ARRAY_AS_PROPS) {
98 | return $this->offsetSet($key, $value);
99 | }
100 | if (in_array($key, $this->protectedProperties)) {
101 | throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
102 | }
103 | $this->$key = $value;
104 | }
105 |
106 | /**
107 | * Unsets the value at the specified key
108 | *
109 | * @param mixed $key
110 | * @return void
111 | */
112 | public function __unset($key)
113 | {
114 | if ($this->flag == self::ARRAY_AS_PROPS) {
115 | return $this->offsetUnset($key);
116 | }
117 | if (in_array($key, $this->protectedProperties)) {
118 | throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
119 | }
120 | unset($this->$key);
121 | }
122 |
123 | /**
124 | * Returns the value at the specified key by reference
125 | *
126 | * @param mixed $key
127 | * @return mixed
128 | */
129 | public function &__get($key)
130 | {
131 | $ret = null;
132 | if ($this->flag == self::ARRAY_AS_PROPS) {
133 | $ret =& $this->offsetGet($key);
134 |
135 | return $ret;
136 | }
137 | if (in_array($key, $this->protectedProperties)) {
138 | throw new Exception\InvalidArgumentException('$key is a protected property, use a different key');
139 | }
140 |
141 | return $this->$key;
142 | }
143 |
144 | /**
145 | * Appends the value
146 | *
147 | * @param mixed $value
148 | * @return void
149 | */
150 | public function append($value)
151 | {
152 | $this->storage[] = $value;
153 | }
154 |
155 | /**
156 | * Sort the entries by value
157 | *
158 | * @return void
159 | */
160 | public function asort()
161 | {
162 | asort($this->storage);
163 | }
164 |
165 | /**
166 | * Get the number of public properties in the ArrayObject
167 | *
168 | * @return int
169 | */
170 | public function count()
171 | {
172 | return count($this->storage);
173 | }
174 |
175 | /**
176 | * Exchange the array for another one.
177 | *
178 | * @param array|ArrayObject $data
179 | * @return array
180 | */
181 | public function exchangeArray($data)
182 | {
183 | if (! is_array($data) && ! is_object($data)) {
184 | throw new Exception\InvalidArgumentException(
185 | 'Passed variable is not an array or object, using empty array instead'
186 | );
187 | }
188 |
189 | if (is_object($data) && ($data instanceof self || $data instanceof \ArrayObject)) {
190 | $data = $data->getArrayCopy();
191 | }
192 | if (! is_array($data)) {
193 | $data = (array) $data;
194 | }
195 |
196 | $storage = $this->storage;
197 |
198 | $this->storage = $data;
199 |
200 | return $storage;
201 | }
202 |
203 | /**
204 | * Creates a copy of the ArrayObject.
205 | *
206 | * @return array
207 | */
208 | public function getArrayCopy()
209 | {
210 | return $this->storage;
211 | }
212 |
213 | /**
214 | * Gets the behavior flags.
215 | *
216 | * @return int
217 | */
218 | public function getFlags()
219 | {
220 | return $this->flag;
221 | }
222 |
223 | /**
224 | * Create a new iterator from an ArrayObject instance
225 | *
226 | * @return \Iterator
227 | */
228 | public function getIterator()
229 | {
230 | $class = $this->iteratorClass;
231 |
232 | return new $class($this->storage);
233 | }
234 |
235 | /**
236 | * Gets the iterator classname for the ArrayObject.
237 | *
238 | * @return string
239 | */
240 | public function getIteratorClass()
241 | {
242 | return $this->iteratorClass;
243 | }
244 |
245 | /**
246 | * Sort the entries by key
247 | *
248 | * @return void
249 | */
250 | public function ksort()
251 | {
252 | ksort($this->storage);
253 | }
254 |
255 | /**
256 | * Sort an array using a case insensitive "natural order" algorithm
257 | *
258 | * @return void
259 | */
260 | public function natcasesort()
261 | {
262 | natcasesort($this->storage);
263 | }
264 |
265 | /**
266 | * Sort entries using a "natural order" algorithm
267 | *
268 | * @return void
269 | */
270 | public function natsort()
271 | {
272 | natsort($this->storage);
273 | }
274 |
275 | /**
276 | * Returns whether the requested key exists
277 | *
278 | * @param mixed $key
279 | * @return bool
280 | */
281 | public function offsetExists($key)
282 | {
283 | return isset($this->storage[$key]);
284 | }
285 |
286 | /**
287 | * Returns the value at the specified key
288 | *
289 | * @param mixed $key
290 | * @return mixed
291 | */
292 | public function &offsetGet($key)
293 | {
294 | $ret = null;
295 | if (! $this->offsetExists($key)) {
296 | return $ret;
297 | }
298 | $ret =& $this->storage[$key];
299 |
300 | return $ret;
301 | }
302 |
303 | /**
304 | * Sets the value at the specified key to value
305 | *
306 | * @param mixed $key
307 | * @param mixed $value
308 | * @return void
309 | */
310 | public function offsetSet($key, $value)
311 | {
312 | $this->storage[$key] = $value;
313 | }
314 |
315 | /**
316 | * Unsets the value at the specified key
317 | *
318 | * @param mixed $key
319 | * @return void
320 | */
321 | public function offsetUnset($key)
322 | {
323 | if ($this->offsetExists($key)) {
324 | unset($this->storage[$key]);
325 | }
326 | }
327 |
328 | /**
329 | * Serialize an ArrayObject
330 | *
331 | * @return string
332 | */
333 | public function serialize()
334 | {
335 | return serialize(get_object_vars($this));
336 | }
337 |
338 | /**
339 | * Sets the behavior flags
340 | *
341 | * @param int $flags
342 | * @return void
343 | */
344 | public function setFlags($flags)
345 | {
346 | $this->flag = $flags;
347 | }
348 |
349 | /**
350 | * Sets the iterator classname for the ArrayObject
351 | *
352 | * @param string $class
353 | * @return void
354 | */
355 | public function setIteratorClass($class)
356 | {
357 | if (class_exists($class)) {
358 | $this->iteratorClass = $class;
359 |
360 | return ;
361 | }
362 |
363 | if (strpos($class, '\\') === 0) {
364 | $class = '\\' . $class;
365 | if (class_exists($class)) {
366 | $this->iteratorClass = $class;
367 |
368 | return ;
369 | }
370 | }
371 |
372 | throw new Exception\InvalidArgumentException('The iterator class does not exist');
373 | }
374 |
375 | /**
376 | * Sort the entries with a user-defined comparison function and maintain key association
377 | *
378 | * @param callable $function
379 | * @return void
380 | */
381 | public function uasort($function)
382 | {
383 | if (is_callable($function)) {
384 | uasort($this->storage, $function);
385 | }
386 | }
387 |
388 | /**
389 | * Sort the entries by keys using a user-defined comparison function
390 | *
391 | * @param callable $function
392 | * @return void
393 | */
394 | public function uksort($function)
395 | {
396 | if (is_callable($function)) {
397 | uksort($this->storage, $function);
398 | }
399 | }
400 |
401 | /**
402 | * Unserialize an ArrayObject
403 | *
404 | * @param string $data
405 | * @return void
406 | */
407 | public function unserialize($data)
408 | {
409 | $ar = unserialize($data);
410 | $this->protectedProperties = array_keys(get_object_vars($this));
411 |
412 | $this->setFlags($ar['flag']);
413 | $this->exchangeArray($ar['storage']);
414 | $this->setIteratorClass($ar['iteratorClass']);
415 |
416 | foreach ($ar as $k => $v) {
417 | switch ($k) {
418 | case 'flag':
419 | $this->setFlags($v);
420 | break;
421 | case 'storage':
422 | $this->exchangeArray($v);
423 | break;
424 | case 'iteratorClass':
425 | $this->setIteratorClass($v);
426 | break;
427 | case 'protectedProperties':
428 | break;
429 | default:
430 | $this->__set($k, $v);
431 | }
432 | }
433 | }
434 | }
435 |
--------------------------------------------------------------------------------
/src/ArraySerializableInterface.php:
--------------------------------------------------------------------------------
1 | getArrayCopy();
31 | return new ArrayIterator(array_reverse($array));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/ArrayUtils.php:
--------------------------------------------------------------------------------
1 | 0;
51 | }
52 |
53 | /**
54 | * Test whether an array contains one or more integer keys
55 | *
56 | * @param mixed $value
57 | * @param bool $allowEmpty Should an empty array() return true
58 | * @return bool
59 | */
60 | public static function hasIntegerKeys($value, $allowEmpty = false)
61 | {
62 | if (! is_array($value)) {
63 | return false;
64 | }
65 |
66 | if (! $value) {
67 | return $allowEmpty;
68 | }
69 |
70 | return count(array_filter(array_keys($value), 'is_int')) > 0;
71 | }
72 |
73 | /**
74 | * Test whether an array contains one or more numeric keys.
75 | *
76 | * A numeric key can be one of the following:
77 | * - an integer 1,
78 | * - a string with a number '20'
79 | * - a string with negative number: '-1000'
80 | * - a float: 2.2120, -78.150999
81 | * - a string with float: '4000.99999', '-10.10'
82 | *
83 | * @param mixed $value
84 | * @param bool $allowEmpty Should an empty array() return true
85 | * @return bool
86 | */
87 | public static function hasNumericKeys($value, $allowEmpty = false)
88 | {
89 | if (! is_array($value)) {
90 | return false;
91 | }
92 |
93 | if (! $value) {
94 | return $allowEmpty;
95 | }
96 |
97 | return count(array_filter(array_keys($value), 'is_numeric')) > 0;
98 | }
99 |
100 | /**
101 | * Test whether an array is a list
102 | *
103 | * A list is a collection of values assigned to continuous integer keys
104 | * starting at 0 and ending at count() - 1.
105 | *
106 | * For example:
107 | *
108 | * $list = array('a', 'b', 'c', 'd');
109 | * $list = array(
110 | * 0 => 'foo',
111 | * 1 => 'bar',
112 | * 2 => array('foo' => 'baz'),
113 | * );
114 | *
115 | *
116 | * @param mixed $value
117 | * @param bool $allowEmpty Is an empty list a valid list?
118 | * @return bool
119 | */
120 | public static function isList($value, $allowEmpty = false)
121 | {
122 | if (! is_array($value)) {
123 | return false;
124 | }
125 |
126 | if (! $value) {
127 | return $allowEmpty;
128 | }
129 |
130 | return (array_values($value) === $value);
131 | }
132 |
133 | /**
134 | * Test whether an array is a hash table.
135 | *
136 | * An array is a hash table if:
137 | *
138 | * 1. Contains one or more non-integer keys, or
139 | * 2. Integer keys are non-continuous or misaligned (not starting with 0)
140 | *
141 | * For example:
142 | *
143 | * $hash = array(
144 | * 'foo' => 15,
145 | * 'bar' => false,
146 | * );
147 | * $hash = array(
148 | * 1995 => 'Birth of PHP',
149 | * 2009 => 'PHP 5.3.0',
150 | * 2012 => 'PHP 5.4.0',
151 | * );
152 | * $hash = array(
153 | * 'formElement,
154 | * 'options' => array( 'debug' => true ),
155 | * );
156 | *
157 | *
158 | * @param mixed $value
159 | * @param bool $allowEmpty Is an empty array() a valid hash table?
160 | * @return bool
161 | */
162 | public static function isHashTable($value, $allowEmpty = false)
163 | {
164 | if (! is_array($value)) {
165 | return false;
166 | }
167 |
168 | if (! $value) {
169 | return $allowEmpty;
170 | }
171 |
172 | return (array_values($value) !== $value);
173 | }
174 |
175 | /**
176 | * Checks if a value exists in an array.
177 | *
178 | * Due to "foo" == 0 === TRUE with in_array when strict = false, an option
179 | * has been added to prevent this. When $strict = 0/false, the most secure
180 | * non-strict check is implemented. if $strict = -1, the default in_array
181 | * non-strict behaviour is used.
182 | *
183 | * @param mixed $needle
184 | * @param array $haystack
185 | * @param int|bool $strict
186 | * @return bool
187 | */
188 | public static function inArray($needle, array $haystack, $strict = false)
189 | {
190 | if (! $strict) {
191 | if (is_int($needle) || is_float($needle)) {
192 | $needle = (string) $needle;
193 | }
194 | if (is_string($needle)) {
195 | foreach ($haystack as &$h) {
196 | if (is_int($h) || is_float($h)) {
197 | $h = (string) $h;
198 | }
199 | }
200 | }
201 | }
202 | return in_array($needle, $haystack, $strict);
203 | }
204 |
205 | /**
206 | * Convert an iterator to an array.
207 | *
208 | * Converts an iterator to an array. The $recursive flag, on by default,
209 | * hints whether or not you want to do so recursively.
210 | *
211 | * @param array|Traversable $iterator The array or Traversable object to convert
212 | * @param bool $recursive Recursively check all nested structures
213 | * @throws Exception\InvalidArgumentException if $iterator is not an array or a Traversable object
214 | * @return array
215 | */
216 | public static function iteratorToArray($iterator, $recursive = true)
217 | {
218 | if (! is_array($iterator) && ! $iterator instanceof Traversable) {
219 | throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable object');
220 | }
221 |
222 | if (! $recursive) {
223 | if (is_array($iterator)) {
224 | return $iterator;
225 | }
226 |
227 | return iterator_to_array($iterator);
228 | }
229 |
230 | if (method_exists($iterator, 'toArray')) {
231 | return $iterator->toArray();
232 | }
233 |
234 | $array = [];
235 | foreach ($iterator as $key => $value) {
236 | if (is_scalar($value)) {
237 | $array[$key] = $value;
238 | continue;
239 | }
240 |
241 | if ($value instanceof Traversable) {
242 | $array[$key] = static::iteratorToArray($value, $recursive);
243 | continue;
244 | }
245 |
246 | if (is_array($value)) {
247 | $array[$key] = static::iteratorToArray($value, $recursive);
248 | continue;
249 | }
250 |
251 | $array[$key] = $value;
252 | }
253 |
254 | return $array;
255 | }
256 |
257 | /**
258 | * Merge two arrays together.
259 | *
260 | * If an integer key exists in both arrays and preserveNumericKeys is false, the value
261 | * from the second array will be appended to the first array. If both values are arrays, they
262 | * are merged together, else the value of the second array overwrites the one of the first array.
263 | *
264 | * @param array $a
265 | * @param array $b
266 | * @param bool $preserveNumericKeys
267 | * @return array
268 | */
269 | public static function merge(array $a, array $b, $preserveNumericKeys = false)
270 | {
271 | foreach ($b as $key => $value) {
272 | if ($value instanceof MergeReplaceKeyInterface) {
273 | $a[$key] = $value->getData();
274 | } elseif (isset($a[$key]) || array_key_exists($key, $a)) {
275 | if ($value instanceof MergeRemoveKey) {
276 | unset($a[$key]);
277 | } elseif (! $preserveNumericKeys && is_int($key)) {
278 | $a[] = $value;
279 | } elseif (is_array($value) && is_array($a[$key])) {
280 | $a[$key] = static::merge($a[$key], $value, $preserveNumericKeys);
281 | } else {
282 | $a[$key] = $value;
283 | }
284 | } else {
285 | if (! $value instanceof MergeRemoveKey) {
286 | $a[$key] = $value;
287 | }
288 | }
289 | }
290 |
291 | return $a;
292 | }
293 |
294 | /**
295 | * @deprecated Since 3.2.0; use the native array_filter methods
296 | *
297 | * @param array $data
298 | * @param callable $callback
299 | * @param null|int $flag
300 | * @return array
301 | * @throws Exception\InvalidArgumentException
302 | */
303 | public static function filter(array $data, $callback, $flag = null)
304 | {
305 | if (! is_callable($callback)) {
306 | throw new Exception\InvalidArgumentException(sprintf(
307 | 'Second parameter of %s must be callable',
308 | __METHOD__
309 | ));
310 | }
311 |
312 | return array_filter($data, $callback, $flag);
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/src/ArrayUtils/MergeRemoveKey.php:
--------------------------------------------------------------------------------
1 | data = $data;
25 | }
26 |
27 | /**
28 | * {@inheritDoc}
29 | */
30 | public function getData()
31 | {
32 | return $this->data;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/ArrayUtils/MergeReplaceKeyInterface.php:
--------------------------------------------------------------------------------
1 | message`,
16 | * `message`)
17 | * - Write output to a specified stream, optionally with colorization.
18 | * - Write a line of output to a specified stream, optionally with
19 | * colorization, using the system EOL sequence..
20 | * - Write an error message to STDERR.
21 | *
22 | * Colorization will only occur when expected sequences are discovered, and
23 | * then, only if the console terminal allows it.
24 | *
25 | * Essentially, provides the bare minimum to allow you to provide messages to
26 | * the current console.
27 | */
28 | class ConsoleHelper
29 | {
30 | const COLOR_GREEN = "\033[32m";
31 | const COLOR_RED = "\033[31m";
32 | const COLOR_RESET = "\033[0m";
33 |
34 | const HIGHLIGHT_INFO = 'info';
35 | const HIGHLIGHT_ERROR = 'error';
36 |
37 | private $highlightMap = [
38 | self::HIGHLIGHT_INFO => self::COLOR_GREEN,
39 | self::HIGHLIGHT_ERROR => self::COLOR_RED,
40 | ];
41 |
42 | /**
43 | * @var string Exists only for testing.
44 | */
45 | private $eol = PHP_EOL;
46 |
47 | /**
48 | * @var resource Exists only for testing.
49 | */
50 | private $stderr = STDERR;
51 |
52 | /**
53 | * @var bool
54 | */
55 | private $supportsColor;
56 |
57 | /**
58 | * @param resource $resource
59 | */
60 | public function __construct($resource = STDOUT)
61 | {
62 | $this->supportsColor = $this->detectColorCapabilities($resource);
63 | }
64 |
65 | /**
66 | * Colorize a string for use with the terminal.
67 | *
68 | * Takes strings formatted as `string` and formats them per the
69 | * $highlightMap; if color support is disabled, simply removes the formatting
70 | * tags.
71 | *
72 | * @param string $string
73 | * @return string
74 | */
75 | public function colorize($string)
76 | {
77 | $reset = $this->supportsColor ? self::COLOR_RESET : '';
78 | foreach ($this->highlightMap as $key => $color) {
79 | $pattern = sprintf('#<%s>(.*?)%s>#s', $key, $key);
80 | $color = $this->supportsColor ? $color : '';
81 | $string = preg_replace($pattern, $color . '$1' . $reset, $string);
82 | }
83 | return $string;
84 | }
85 |
86 | /**
87 | * @param string $string
88 | * @param bool $colorize Whether or not to colorize the string
89 | * @param resource $resource Defaults to STDOUT
90 | * @return void
91 | */
92 | public function write($string, $colorize = true, $resource = STDOUT)
93 | {
94 | if ($colorize) {
95 | $string = $this->colorize($string);
96 | }
97 |
98 | $string = $this->formatNewlines($string);
99 |
100 | fwrite($resource, $string);
101 | }
102 |
103 | /**
104 | * @param string $string
105 | * @param bool $colorize Whether or not to colorize the line
106 | * @param resource $resource Defaults to STDOUT
107 | * @return void
108 | */
109 | public function writeLine($string, $colorize = true, $resource = STDOUT)
110 | {
111 | $this->write($string . $this->eol, $colorize, $resource);
112 | }
113 |
114 | /**
115 | * Emit an error message.
116 | *
117 | * Wraps the message in ``, and passes it to `writeLine()`,
118 | * using STDERR as the resource; emits an additional empty line when done,
119 | * also to STDERR.
120 | *
121 | * @param string $message
122 | * @return void
123 | */
124 | public function writeErrorMessage($message)
125 | {
126 | $this->writeLine(sprintf('%s', $message), true, $this->stderr);
127 | $this->writeLine('', false, $this->stderr);
128 | }
129 |
130 | /**
131 | * @param resource $resource
132 | * @return bool
133 | */
134 | private function detectColorCapabilities($resource = STDOUT)
135 | {
136 | if ('\\' === DIRECTORY_SEPARATOR) {
137 | // Windows
138 | return false !== getenv('ANSICON')
139 | || 'ON' === getenv('ConEmuANSI')
140 | || 'xterm' === getenv('TERM');
141 | }
142 |
143 | return function_exists('posix_isatty') && posix_isatty($resource);
144 | }
145 |
146 | /**
147 | * Ensure newlines are appropriate for the current terminal.
148 | *
149 | * @param string
150 | * @return string
151 | */
152 | private function formatNewlines($string)
153 | {
154 | $string = str_replace($this->eol, "\0PHP_EOL\0", $string);
155 | $string = preg_replace("/(\r\n|\n|\r)/", $this->eol, $string);
156 | return str_replace("\0PHP_EOL\0", $this->eol, $string);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/DispatchableInterface.php:
--------------------------------------------------------------------------------
1 | values[$priority][] = $value;
97 | if (! isset($this->priorities[$priority])) {
98 | $this->priorities[$priority] = $priority;
99 | $this->maxPriority = $this->maxPriority === null ? $priority : max($priority, $this->maxPriority);
100 | }
101 | ++$this->count;
102 | }
103 |
104 | /**
105 | * Extract an element in the queue according to the priority and the
106 | * order of insertion
107 | *
108 | * @return mixed
109 | */
110 | public function extract()
111 | {
112 | if (! $this->valid()) {
113 | return false;
114 | }
115 | $value = $this->current();
116 | $this->nextAndRemove();
117 | return $value;
118 | }
119 |
120 | /**
121 | * Remove an item from the queue
122 | *
123 | * This is different than {@link extract()}; its purpose is to dequeue an
124 | * item.
125 | *
126 | * Note: this removes the first item matching the provided item found. If
127 | * the same item has been added multiple times, it will not remove other
128 | * instances.
129 | *
130 | * @param mixed $datum
131 | * @return bool False if the item was not found, true otherwise.
132 | */
133 | public function remove($datum)
134 | {
135 | $currentIndex = $this->index;
136 | $currentSubIndex = $this->subIndex;
137 | $currentPriority = $this->maxPriority;
138 |
139 | $this->rewind();
140 | while ($this->valid()) {
141 | if (current($this->values[$this->maxPriority]) === $datum) {
142 | $index = key($this->values[$this->maxPriority]);
143 | unset($this->values[$this->maxPriority][$index]);
144 |
145 | // The `next()` method advances the internal array pointer, so we need to use the `reset()` function,
146 | // otherwise we would lose all elements before the place the pointer points.
147 | reset($this->values[$this->maxPriority]);
148 |
149 | $this->index = $currentIndex;
150 | $this->subIndex = $currentSubIndex;
151 |
152 | // If the array is empty we need to destroy the unnecessary priority,
153 | // otherwise we would end up with an incorrect value of `$this->count`
154 | // {@see \Zend\Stdlib\FastPriorityQueue::nextAndRemove()}.
155 | if (empty($this->values[$this->maxPriority])) {
156 | unset($this->values[$this->maxPriority]);
157 | unset($this->priorities[$this->maxPriority]);
158 | if ($this->maxPriority === $currentPriority) {
159 | $this->subIndex = 0;
160 | }
161 | }
162 |
163 | $this->maxPriority = empty($this->priorities) ? null : max($this->priorities);
164 | --$this->count;
165 | return true;
166 | }
167 | $this->next();
168 | }
169 | return false;
170 | }
171 |
172 | /**
173 | * Get the total number of elements in the queue
174 | *
175 | * @return integer
176 | */
177 | public function count()
178 | {
179 | return $this->count;
180 | }
181 |
182 | /**
183 | * Get the current element in the queue
184 | *
185 | * @return mixed
186 | */
187 | public function current()
188 | {
189 | switch ($this->extractFlag) {
190 | case self::EXTR_DATA:
191 | return current($this->values[$this->maxPriority]);
192 | case self::EXTR_PRIORITY:
193 | return $this->maxPriority;
194 | case self::EXTR_BOTH:
195 | return [
196 | 'data' => current($this->values[$this->maxPriority]),
197 | 'priority' => $this->maxPriority
198 | ];
199 | }
200 | }
201 |
202 | /**
203 | * Get the index of the current element in the queue
204 | *
205 | * @return integer
206 | */
207 | public function key()
208 | {
209 | return $this->index;
210 | }
211 |
212 | /**
213 | * Set the iterator pointer to the next element in the queue
214 | * removing the previous element
215 | */
216 | protected function nextAndRemove()
217 | {
218 | $key = key($this->values[$this->maxPriority]);
219 |
220 | if (false === next($this->values[$this->maxPriority])) {
221 | unset($this->priorities[$this->maxPriority]);
222 | unset($this->values[$this->maxPriority]);
223 | $this->maxPriority = empty($this->priorities) ? null : max($this->priorities);
224 | $this->subIndex = -1;
225 | } else {
226 | unset($this->values[$this->maxPriority][$key]);
227 | }
228 | ++$this->index;
229 | ++$this->subIndex;
230 | --$this->count;
231 | }
232 |
233 | /**
234 | * Set the iterator pointer to the next element in the queue
235 | * without removing the previous element
236 | */
237 | public function next()
238 | {
239 | if (false === next($this->values[$this->maxPriority])) {
240 | unset($this->subPriorities[$this->maxPriority]);
241 | reset($this->values[$this->maxPriority]);
242 | $this->maxPriority = empty($this->subPriorities) ? null : max($this->subPriorities);
243 | $this->subIndex = -1;
244 | }
245 | ++$this->index;
246 | ++$this->subIndex;
247 | }
248 |
249 | /**
250 | * Check if the current iterator is valid
251 | *
252 | * @return boolean
253 | */
254 | public function valid()
255 | {
256 | return isset($this->values[$this->maxPriority]);
257 | }
258 |
259 | /**
260 | * Rewind the current iterator
261 | */
262 | public function rewind()
263 | {
264 | $this->subPriorities = $this->priorities;
265 | $this->maxPriority = empty($this->priorities) ? 0 : max($this->priorities);
266 | $this->index = 0;
267 | $this->subIndex = 0;
268 | }
269 |
270 | /**
271 | * Serialize to an array
272 | *
273 | * Array will be priority => data pairs
274 | *
275 | * @return array
276 | */
277 | public function toArray()
278 | {
279 | $array = [];
280 | foreach (clone $this as $item) {
281 | $array[] = $item;
282 | }
283 | return $array;
284 | }
285 |
286 | /**
287 | * Serialize
288 | *
289 | * @return string
290 | */
291 | public function serialize()
292 | {
293 | $clone = clone $this;
294 | $clone->setExtractFlags(self::EXTR_BOTH);
295 |
296 | $data = [];
297 | foreach ($clone as $item) {
298 | $data[] = $item;
299 | }
300 |
301 | return serialize($data);
302 | }
303 |
304 | /**
305 | * Deserialize
306 | *
307 | * @param string $data
308 | * @return void
309 | */
310 | public function unserialize($data)
311 | {
312 | foreach (unserialize($data) as $item) {
313 | $this->insert($item['data'], $item['priority']);
314 | }
315 | }
316 |
317 | /**
318 | * Set the extract flag
319 | *
320 | * @param integer $flag
321 | */
322 | public function setExtractFlags($flag)
323 | {
324 | switch ($flag) {
325 | case self::EXTR_DATA:
326 | case self::EXTR_PRIORITY:
327 | case self::EXTR_BOTH:
328 | $this->extractFlag = $flag;
329 | break;
330 | default:
331 | throw new Exception\InvalidArgumentException("The extract flag specified is not valid");
332 | }
333 | }
334 |
335 | /**
336 | * Check if the queue is empty
337 | *
338 | * @return boolean
339 | */
340 | public function isEmpty()
341 | {
342 | return empty($this->values);
343 | }
344 |
345 | /**
346 | * Does the queue contain the given datum?
347 | *
348 | * @param mixed $datum
349 | * @return bool
350 | */
351 | public function contains($datum)
352 | {
353 | foreach ($this->values as $values) {
354 | if (in_array($datum, $values)) {
355 | return true;
356 | }
357 | }
358 | return false;
359 | }
360 |
361 | /**
362 | * Does the queue have an item with the given priority?
363 | *
364 | * @param int $priority
365 | * @return bool
366 | */
367 | public function hasPriority($priority)
368 | {
369 | return isset($this->values[$priority]);
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/src/Glob.php:
--------------------------------------------------------------------------------
1 | GLOB_MARK,
61 | self::GLOB_NOSORT => GLOB_NOSORT,
62 | self::GLOB_NOCHECK => GLOB_NOCHECK,
63 | self::GLOB_NOESCAPE => GLOB_NOESCAPE,
64 | self::GLOB_BRACE => defined('GLOB_BRACE') ? GLOB_BRACE : 0,
65 | self::GLOB_ONLYDIR => GLOB_ONLYDIR,
66 | self::GLOB_ERR => GLOB_ERR,
67 | ];
68 |
69 | $globFlags = 0;
70 |
71 | foreach ($flagMap as $internalFlag => $globFlag) {
72 | if ($flags & $internalFlag) {
73 | $globFlags |= $globFlag;
74 | }
75 | }
76 | } else {
77 | $globFlags = 0;
78 | }
79 |
80 | ErrorHandler::start();
81 | $res = glob($pattern, $globFlags);
82 | $err = ErrorHandler::stop();
83 | if ($res === false) {
84 | throw new Exception\RuntimeException("glob('{$pattern}', {$globFlags}) failed", 0, $err);
85 | }
86 | return $res;
87 | }
88 |
89 | /**
90 | * Expand braces manually, then use the system glob.
91 | *
92 | * @param string $pattern
93 | * @param int $flags
94 | * @return array
95 | * @throws Exception\RuntimeException
96 | */
97 | protected static function fallbackGlob($pattern, $flags)
98 | {
99 | if (! $flags & self::GLOB_BRACE) {
100 | return static::systemGlob($pattern, $flags);
101 | }
102 |
103 | $flags &= ~self::GLOB_BRACE;
104 | $length = strlen($pattern);
105 | $paths = [];
106 |
107 | if ($flags & self::GLOB_NOESCAPE) {
108 | $begin = strpos($pattern, '{');
109 | } else {
110 | $begin = 0;
111 |
112 | while (true) {
113 | if ($begin === $length) {
114 | $begin = false;
115 | break;
116 | } elseif ($pattern[$begin] === '\\' && ($begin + 1) < $length) {
117 | $begin++;
118 | } elseif ($pattern[$begin] === '{') {
119 | break;
120 | }
121 |
122 | $begin++;
123 | }
124 | }
125 |
126 | if ($begin === false) {
127 | return static::systemGlob($pattern, $flags);
128 | }
129 |
130 | $next = static::nextBraceSub($pattern, $begin + 1, $flags);
131 |
132 | if ($next === null) {
133 | return static::systemGlob($pattern, $flags);
134 | }
135 |
136 | $rest = $next;
137 |
138 | while ($pattern[$rest] !== '}') {
139 | $rest = static::nextBraceSub($pattern, $rest + 1, $flags);
140 |
141 | if ($rest === null) {
142 | return static::systemGlob($pattern, $flags);
143 | }
144 | }
145 |
146 | $p = $begin + 1;
147 |
148 | while (true) {
149 | $subPattern = substr($pattern, 0, $begin)
150 | . substr($pattern, $p, $next - $p)
151 | . substr($pattern, $rest + 1);
152 |
153 | $result = static::fallbackGlob($subPattern, $flags | self::GLOB_BRACE);
154 |
155 | if ($result) {
156 | $paths = array_merge($paths, $result);
157 | }
158 |
159 | if ($pattern[$next] === '}') {
160 | break;
161 | }
162 |
163 | $p = $next + 1;
164 | $next = static::nextBraceSub($pattern, $p, $flags);
165 | }
166 |
167 | return array_unique($paths);
168 | }
169 |
170 | /**
171 | * Find the end of the sub-pattern in a brace expression.
172 | *
173 | * @param string $pattern
174 | * @param int $begin
175 | * @param int $flags
176 | * @return int|null
177 | */
178 | protected static function nextBraceSub($pattern, $begin, $flags)
179 | {
180 | $length = strlen($pattern);
181 | $depth = 0;
182 | $current = $begin;
183 |
184 | while ($current < $length) {
185 | if (! $flags & self::GLOB_NOESCAPE && $pattern[$current] === '\\') {
186 | if (++$current === $length) {
187 | break;
188 | }
189 |
190 | $current++;
191 | } else {
192 | if (($pattern[$current] === '}' && $depth-- === 0) || ($pattern[$current] === ',' && $depth === 0)) {
193 | break;
194 | } elseif ($pattern[$current++] === '{') {
195 | $depth++;
196 | }
197 | }
198 | }
199 |
200 | return ($current < $length ? $current : null);
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/Guard/AllGuardsTrait.php:
--------------------------------------------------------------------------------
1 | metadata[$spec] = $value;
41 | return $this;
42 | }
43 | if (! is_array($spec) && ! $spec instanceof Traversable) {
44 | throw new Exception\InvalidArgumentException(sprintf(
45 | 'Expected a string, array, or Traversable argument in first position; received "%s"',
46 | (is_object($spec) ? get_class($spec) : gettype($spec))
47 | ));
48 | }
49 | foreach ($spec as $key => $value) {
50 | $this->metadata[$key] = $value;
51 | }
52 | return $this;
53 | }
54 |
55 | /**
56 | * Retrieve all metadata or a single metadatum as specified by key
57 | *
58 | * @param null|string|int $key
59 | * @param null|mixed $default
60 | * @throws Exception\InvalidArgumentException
61 | * @return mixed
62 | */
63 | public function getMetadata($key = null, $default = null)
64 | {
65 | if (null === $key) {
66 | return $this->metadata;
67 | }
68 |
69 | if (! is_scalar($key)) {
70 | throw new Exception\InvalidArgumentException('Non-scalar argument provided for key');
71 | }
72 |
73 | if (array_key_exists($key, $this->metadata)) {
74 | return $this->metadata[$key];
75 | }
76 |
77 | return $default;
78 | }
79 |
80 | /**
81 | * Set message content
82 | *
83 | * @param mixed $value
84 | * @return Message
85 | */
86 | public function setContent($value)
87 | {
88 | $this->content = $value;
89 | return $this;
90 | }
91 |
92 | /**
93 | * Get message content
94 | *
95 | * @return mixed
96 | */
97 | public function getContent()
98 | {
99 | return $this->content;
100 | }
101 |
102 | /**
103 | * @return string
104 | */
105 | public function toString()
106 | {
107 | $request = '';
108 | foreach ($this->getMetadata() as $key => $value) {
109 | $request .= sprintf(
110 | "%s: %s\r\n",
111 | (string) $key,
112 | (string) $value
113 | );
114 | }
115 | $request .= "\r\n" . $this->getContent();
116 | return $request;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/MessageInterface.php:
--------------------------------------------------------------------------------
1 | exchangeArray($values);
41 | }
42 |
43 | /**
44 | * Populate from query string
45 | *
46 | * @param string $string
47 | * @return void
48 | */
49 | public function fromString($string)
50 | {
51 | $array = [];
52 | parse_str($string, $array);
53 | $this->fromArray($array);
54 | }
55 |
56 | /**
57 | * Serialize to native PHP array
58 | *
59 | * @return array
60 | */
61 | public function toArray()
62 | {
63 | return $this->getArrayCopy();
64 | }
65 |
66 | /**
67 | * Serialize to query string
68 | *
69 | * @return string
70 | */
71 | public function toString()
72 | {
73 | return http_build_query($this->toArray());
74 | }
75 |
76 | /**
77 | * Retrieve by key
78 | *
79 | * Returns null if the key does not exist.
80 | *
81 | * @param string $name
82 | * @return mixed
83 | */
84 | public function offsetGet($name)
85 | {
86 | if ($this->offsetExists($name)) {
87 | return parent::offsetGet($name);
88 | }
89 | return;
90 | }
91 |
92 | /**
93 | * @param string $name
94 | * @param mixed $default optional default value
95 | * @return mixed
96 | */
97 | public function get($name, $default = null)
98 | {
99 | if ($this->offsetExists($name)) {
100 | return parent::offsetGet($name);
101 | }
102 | return $default;
103 | }
104 |
105 | /**
106 | * @param string $name
107 | * @param mixed $value
108 | * @return Parameters
109 | */
110 | public function set($name, $value)
111 | {
112 | $this[$name] = $value;
113 | return $this;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/ParametersInterface.php:
--------------------------------------------------------------------------------
1 | items[$name])) {
66 | $this->count++;
67 | }
68 |
69 | $this->sorted = false;
70 |
71 | $this->items[$name] = [
72 | 'data' => $value,
73 | 'priority' => (int) $priority,
74 | 'serial' => $this->serial++,
75 | ];
76 | }
77 |
78 | /**
79 | * @param string $name
80 | * @param int $priority
81 | *
82 | * @return $this
83 | *
84 | * @throws \Exception
85 | */
86 | public function setPriority($name, $priority)
87 | {
88 | if (! isset($this->items[$name])) {
89 | throw new \Exception("item $name not found");
90 | }
91 |
92 | $this->items[$name]['priority'] = (int) $priority;
93 | $this->sorted = false;
94 |
95 | return $this;
96 | }
97 |
98 | /**
99 | * Remove a item.
100 | *
101 | * @param string $name
102 | * @return void
103 | */
104 | public function remove($name)
105 | {
106 | if (isset($this->items[$name])) {
107 | $this->count--;
108 | }
109 |
110 | unset($this->items[$name]);
111 | }
112 |
113 | /**
114 | * Remove all items.
115 | *
116 | * @return void
117 | */
118 | public function clear()
119 | {
120 | $this->items = [];
121 | $this->serial = 0;
122 | $this->count = 0;
123 | $this->sorted = false;
124 | }
125 |
126 | /**
127 | * Get a item.
128 | *
129 | * @param string $name
130 | * @return mixed
131 | */
132 | public function get($name)
133 | {
134 | if (! isset($this->items[$name])) {
135 | return;
136 | }
137 |
138 | return $this->items[$name]['data'];
139 | }
140 |
141 | /**
142 | * Sort all items.
143 | *
144 | * @return void
145 | */
146 | protected function sort()
147 | {
148 | if (! $this->sorted) {
149 | uasort($this->items, [$this, 'compare']);
150 | $this->sorted = true;
151 | }
152 | }
153 |
154 | /**
155 | * Compare the priority of two items.
156 | *
157 | * @param array $item1,
158 | * @param array $item2
159 | * @return int
160 | */
161 | protected function compare(array $item1, array $item2)
162 | {
163 | return ($item1['priority'] === $item2['priority'])
164 | ? ($item1['serial'] > $item2['serial'] ? -1 : 1) * $this->isLIFO
165 | : ($item1['priority'] > $item2['priority'] ? -1 : 1);
166 | }
167 |
168 | /**
169 | * Get/Set serial order mode
170 | *
171 | * @param bool|null $flag
172 | *
173 | * @return bool
174 | */
175 | public function isLIFO($flag = null)
176 | {
177 | if ($flag !== null) {
178 | $isLifo = $flag === true ? 1 : -1;
179 |
180 | if ($isLifo !== $this->isLIFO) {
181 | $this->isLIFO = $isLifo;
182 | $this->sorted = false;
183 | }
184 | }
185 |
186 | return 1 === $this->isLIFO;
187 | }
188 |
189 | /**
190 | * {@inheritDoc}
191 | */
192 | public function rewind()
193 | {
194 | $this->sort();
195 | reset($this->items);
196 | }
197 |
198 | /**
199 | * {@inheritDoc}
200 | */
201 | public function current()
202 | {
203 | $this->sorted || $this->sort();
204 | $node = current($this->items);
205 |
206 | return $node ? $node['data'] : false;
207 | }
208 |
209 | /**
210 | * {@inheritDoc}
211 | */
212 | public function key()
213 | {
214 | $this->sorted || $this->sort();
215 | return key($this->items);
216 | }
217 |
218 | /**
219 | * {@inheritDoc}
220 | */
221 | public function next()
222 | {
223 | $node = next($this->items);
224 |
225 | return $node ? $node['data'] : false;
226 | }
227 |
228 | /**
229 | * {@inheritDoc}
230 | */
231 | public function valid()
232 | {
233 | return current($this->items) !== false;
234 | }
235 |
236 | /**
237 | * @return self
238 | */
239 | public function getIterator()
240 | {
241 | return clone $this;
242 | }
243 |
244 | /**
245 | * {@inheritDoc}
246 | */
247 | public function count()
248 | {
249 | return $this->count;
250 | }
251 |
252 | /**
253 | * Return list as array
254 | *
255 | * @param int $flag
256 | *
257 | * @return array
258 | */
259 | public function toArray($flag = self::EXTR_DATA)
260 | {
261 | $this->sort();
262 |
263 | if ($flag == self::EXTR_BOTH) {
264 | return $this->items;
265 | }
266 |
267 | return array_map(
268 | function ($item) use ($flag) {
269 | return ($flag == PriorityList::EXTR_PRIORITY) ? $item['priority'] : $item['data'];
270 | },
271 | $this->items
272 | );
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/src/PriorityQueue.php:
--------------------------------------------------------------------------------
1 | items[] = [
66 | 'data' => $data,
67 | 'priority' => $priority,
68 | ];
69 | $this->getQueue()->insert($data, $priority);
70 | return $this;
71 | }
72 |
73 | /**
74 | * Remove an item from the queue
75 | *
76 | * This is different than {@link extract()}; its purpose is to dequeue an
77 | * item.
78 | *
79 | * This operation is potentially expensive, as it requires
80 | * re-initialization and re-population of the inner queue.
81 | *
82 | * Note: this removes the first item matching the provided item found. If
83 | * the same item has been added multiple times, it will not remove other
84 | * instances.
85 | *
86 | * @param mixed $datum
87 | * @return bool False if the item was not found, true otherwise.
88 | */
89 | public function remove($datum)
90 | {
91 | $found = false;
92 | foreach ($this->items as $key => $item) {
93 | if ($item['data'] === $datum) {
94 | $found = true;
95 | break;
96 | }
97 | }
98 | if ($found) {
99 | unset($this->items[$key]);
100 | $this->queue = null;
101 |
102 | if (! $this->isEmpty()) {
103 | $queue = $this->getQueue();
104 | foreach ($this->items as $item) {
105 | $queue->insert($item['data'], $item['priority']);
106 | }
107 | }
108 | return true;
109 | }
110 | return false;
111 | }
112 |
113 | /**
114 | * Is the queue empty?
115 | *
116 | * @return bool
117 | */
118 | public function isEmpty()
119 | {
120 | return (0 === $this->count());
121 | }
122 |
123 | /**
124 | * How many items are in the queue?
125 | *
126 | * @return int
127 | */
128 | public function count()
129 | {
130 | return count($this->items);
131 | }
132 |
133 | /**
134 | * Peek at the top node in the queue, based on priority.
135 | *
136 | * @return mixed
137 | */
138 | public function top()
139 | {
140 | return $this->getIterator()->top();
141 | }
142 |
143 | /**
144 | * Extract a node from the inner queue and sift up
145 | *
146 | * @return mixed
147 | */
148 | public function extract()
149 | {
150 | return $this->getQueue()->extract();
151 | }
152 |
153 | /**
154 | * Retrieve the inner iterator
155 | *
156 | * SplPriorityQueue acts as a heap, which typically implies that as items
157 | * are iterated, they are also removed. This does not work for situations
158 | * where the queue may be iterated multiple times. As such, this class
159 | * aggregates the values, and also injects an SplPriorityQueue. This method
160 | * retrieves the inner queue object, and clones it for purposes of
161 | * iteration.
162 | *
163 | * @return SplPriorityQueue
164 | */
165 | public function getIterator()
166 | {
167 | $queue = $this->getQueue();
168 | return clone $queue;
169 | }
170 |
171 | /**
172 | * Serialize the data structure
173 | *
174 | * @return string
175 | */
176 | public function serialize()
177 | {
178 | return serialize($this->items);
179 | }
180 |
181 | /**
182 | * Unserialize a string into a PriorityQueue object
183 | *
184 | * Serialization format is compatible with {@link Zend\Stdlib\SplPriorityQueue}
185 | *
186 | * @param string $data
187 | * @return void
188 | */
189 | public function unserialize($data)
190 | {
191 | foreach (unserialize($data) as $item) {
192 | $this->insert($item['data'], $item['priority']);
193 | }
194 | }
195 |
196 | /**
197 | * Serialize to an array
198 | *
199 | * By default, returns only the item data, and in the order registered (not
200 | * sorted). You may provide one of the EXTR_* flags as an argument, allowing
201 | * the ability to return priorities or both data and priority.
202 | *
203 | * @param int $flag
204 | * @return array
205 | */
206 | public function toArray($flag = self::EXTR_DATA)
207 | {
208 | switch ($flag) {
209 | case self::EXTR_BOTH:
210 | return $this->items;
211 | case self::EXTR_PRIORITY:
212 | return array_map(function ($item) {
213 | return $item['priority'];
214 | }, $this->items);
215 | case self::EXTR_DATA:
216 | default:
217 | return array_map(function ($item) {
218 | return $item['data'];
219 | }, $this->items);
220 | }
221 | }
222 |
223 | /**
224 | * Specify the internal queue class
225 | *
226 | * Please see {@link getIterator()} for details on the necessity of an
227 | * internal queue class. The class provided should extend SplPriorityQueue.
228 | *
229 | * @param string $class
230 | * @return PriorityQueue
231 | */
232 | public function setInternalQueueClass($class)
233 | {
234 | $this->queueClass = (string) $class;
235 | return $this;
236 | }
237 |
238 | /**
239 | * Does the queue contain the given datum?
240 | *
241 | * @param mixed $datum
242 | * @return bool
243 | */
244 | public function contains($datum)
245 | {
246 | foreach ($this->items as $item) {
247 | if ($item['data'] === $datum) {
248 | return true;
249 | }
250 | }
251 | return false;
252 | }
253 |
254 | /**
255 | * Does the queue have an item with the given priority?
256 | *
257 | * @param int $priority
258 | * @return bool
259 | */
260 | public function hasPriority($priority)
261 | {
262 | foreach ($this->items as $item) {
263 | if ($item['priority'] === $priority) {
264 | return true;
265 | }
266 | }
267 | return false;
268 | }
269 |
270 | /**
271 | * Get the inner priority queue instance
272 | *
273 | * @throws Exception\DomainException
274 | * @return SplPriorityQueue
275 | */
276 | protected function getQueue()
277 | {
278 | if (null === $this->queue) {
279 | $this->queue = new $this->queueClass();
280 | if (! $this->queue instanceof \SplPriorityQueue) {
281 | throw new Exception\DomainException(sprintf(
282 | 'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"',
283 | get_class($this->queue)
284 | ));
285 | }
286 | }
287 | return $this->queue;
288 | }
289 |
290 | /**
291 | * Add support for deep cloning
292 | *
293 | * @return void
294 | */
295 | public function __clone()
296 | {
297 | if (null !== $this->queue) {
298 | $this->queue = clone $this->queue;
299 | }
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/src/Request.php:
--------------------------------------------------------------------------------
1 | serial--];
41 | }
42 | parent::insert($datum, $priority);
43 | }
44 |
45 | /**
46 | * Serialize to an array
47 | *
48 | * Array will be priority => data pairs
49 | *
50 | * @return array
51 | */
52 | public function toArray()
53 | {
54 | $array = [];
55 | foreach (clone $this as $item) {
56 | $array[] = $item;
57 | }
58 | return $array;
59 | }
60 |
61 | /**
62 | * Serialize
63 | *
64 | * @return string
65 | */
66 | public function serialize()
67 | {
68 | $clone = clone $this;
69 | $clone->setExtractFlags(self::EXTR_BOTH);
70 |
71 | $data = [];
72 | foreach ($clone as $item) {
73 | $data[] = $item;
74 | }
75 |
76 | return serialize($data);
77 | }
78 |
79 | /**
80 | * Deserialize
81 | *
82 | * @param string $data
83 | * @return void
84 | */
85 | public function unserialize($data)
86 | {
87 | $this->serial = PHP_INT_MAX;
88 | foreach (unserialize($data) as $item) {
89 | $this->serial--;
90 | $this->insert($item['data'], $item['priority']);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/SplQueue.php:
--------------------------------------------------------------------------------
1 | toArray());
41 | }
42 |
43 | /**
44 | * Unserialize
45 | *
46 | * @param string $data
47 | * @return void
48 | */
49 | public function unserialize($data)
50 | {
51 | foreach (unserialize($data) as $item) {
52 | $this->push($item);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/SplStack.php:
--------------------------------------------------------------------------------
1 | toArray());
41 | }
42 |
43 | /**
44 | * Unserialize
45 | *
46 | * @param string $data
47 | * @return void
48 | */
49 | public function unserialize($data)
50 | {
51 | foreach (unserialize($data) as $item) {
52 | $this->unshift($item);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/StringUtils.php:
--------------------------------------------------------------------------------
1 | setEncoding($encoding, $convertEncoding);
131 | return $wrapper;
132 | }
133 | }
134 |
135 | throw new Exception\RuntimeException(
136 | 'No wrapper found supporting "' . $encoding . '"'
137 | . (($convertEncoding !== null) ? ' and "' . $convertEncoding . '"' : '')
138 | );
139 | }
140 |
141 | /**
142 | * Get a list of all known single-byte character encodings
143 | *
144 | * @return string[]
145 | */
146 | public static function getSingleByteEncodings()
147 | {
148 | return static::$singleByteEncodings;
149 | }
150 |
151 | /**
152 | * Check if a given encoding is a known single-byte character encoding
153 | *
154 | * @param string $encoding
155 | * @return bool
156 | */
157 | public static function isSingleByteEncoding($encoding)
158 | {
159 | return in_array(strtoupper($encoding), static::$singleByteEncodings);
160 | }
161 |
162 | /**
163 | * Check if a given string is valid UTF-8 encoded
164 | *
165 | * @param string $str
166 | * @return bool
167 | */
168 | public static function isValidUtf8($str)
169 | {
170 | return is_string($str) && ($str === '' || preg_match('/^./su', $str) == 1);
171 | }
172 |
173 | /**
174 | * Is PCRE compiled with Unicode support?
175 | *
176 | * @return bool
177 | */
178 | public static function hasPcreUnicodeSupport()
179 | {
180 | if (static::$hasPcreUnicodeSupport === null) {
181 | ErrorHandler::start();
182 | static::$hasPcreUnicodeSupport = defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1;
183 | ErrorHandler::stop();
184 | }
185 | return static::$hasPcreUnicodeSupport;
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/src/StringWrapper/AbstractStringWrapper.php:
--------------------------------------------------------------------------------
1 | convertEncoding = $convertEncodingUpper;
79 | } else {
80 | $this->convertEncoding = null;
81 | }
82 | $this->encoding = $encodingUpper;
83 |
84 | return $this;
85 | }
86 |
87 | /**
88 | * Get the defined character encoding to work with
89 | *
90 | * @return string
91 | * @throws Exception\LogicException If no encoding was defined
92 | */
93 | public function getEncoding()
94 | {
95 | return $this->encoding;
96 | }
97 |
98 | /**
99 | * Get the defined character encoding to convert to
100 | *
101 | * @return string|null
102 | */
103 | public function getConvertEncoding()
104 | {
105 | return $this->convertEncoding;
106 | }
107 |
108 | /**
109 | * Convert a string from defined character encoding to the defined convert encoding
110 | *
111 | * @param string $str
112 | * @param bool $reverse
113 | * @return string|false
114 | */
115 | public function convert($str, $reverse = false)
116 | {
117 | $encoding = $this->getEncoding();
118 | $convertEncoding = $this->getConvertEncoding();
119 | if ($convertEncoding === null) {
120 | throw new Exception\LogicException(
121 | 'No convert encoding defined'
122 | );
123 | }
124 |
125 | if ($encoding === $convertEncoding) {
126 | return $str;
127 | }
128 |
129 | $from = $reverse ? $convertEncoding : $encoding;
130 | $to = $reverse ? $encoding : $convertEncoding;
131 | throw new Exception\RuntimeException(sprintf(
132 | 'Converting from "%s" to "%s" isn\'t supported by this string wrapper',
133 | $from,
134 | $to
135 | ));
136 | }
137 |
138 | /**
139 | * Wraps a string to a given number of characters
140 | *
141 | * @param string $string
142 | * @param int $width
143 | * @param string $break
144 | * @param bool $cut
145 | * @return string|false
146 | */
147 | public function wordWrap($string, $width = 75, $break = "\n", $cut = false)
148 | {
149 | $string = (string) $string;
150 | if ($string === '') {
151 | return '';
152 | }
153 |
154 | $break = (string) $break;
155 | if ($break === '') {
156 | throw new Exception\InvalidArgumentException('Break string cannot be empty');
157 | }
158 |
159 | $width = (int) $width;
160 | if ($width === 0 && $cut) {
161 | throw new Exception\InvalidArgumentException('Cannot force cut when width is zero');
162 | }
163 |
164 | if (StringUtils::isSingleByteEncoding($this->getEncoding())) {
165 | return wordwrap($string, $width, $break, $cut);
166 | }
167 |
168 | $stringWidth = $this->strlen($string);
169 | $breakWidth = $this->strlen($break);
170 |
171 | $result = '';
172 | $lastStart = $lastSpace = 0;
173 |
174 | for ($current = 0; $current < $stringWidth; $current++) {
175 | $char = $this->substr($string, $current, 1);
176 |
177 | $possibleBreak = $char;
178 | if ($breakWidth !== 1) {
179 | $possibleBreak = $this->substr($string, $current, $breakWidth);
180 | }
181 |
182 | if ($possibleBreak === $break) {
183 | $result .= $this->substr($string, $lastStart, $current - $lastStart + $breakWidth);
184 | $current += $breakWidth - 1;
185 | $lastStart = $lastSpace = $current + 1;
186 | continue;
187 | }
188 |
189 | if ($char === ' ') {
190 | if ($current - $lastStart >= $width) {
191 | $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break;
192 | $lastStart = $current + 1;
193 | }
194 |
195 | $lastSpace = $current;
196 | continue;
197 | }
198 |
199 | if ($current - $lastStart >= $width && $cut && $lastStart >= $lastSpace) {
200 | $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break;
201 | $lastStart = $lastSpace = $current;
202 | continue;
203 | }
204 |
205 | if ($current - $lastStart >= $width && $lastStart < $lastSpace) {
206 | $result .= $this->substr($string, $lastStart, $lastSpace - $lastStart) . $break;
207 | $lastStart = $lastSpace = $lastSpace + 1;
208 | continue;
209 | }
210 | }
211 |
212 | if ($lastStart !== $current) {
213 | $result .= $this->substr($string, $lastStart, $current - $lastStart);
214 | }
215 |
216 | return $result;
217 | }
218 |
219 | /**
220 | * Pad a string to a certain length with another string
221 | *
222 | * @param string $input
223 | * @param int $padLength
224 | * @param string $padString
225 | * @param int $padType
226 | * @return string
227 | */
228 | public function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT)
229 | {
230 | if (StringUtils::isSingleByteEncoding($this->getEncoding())) {
231 | return str_pad($input, $padLength, $padString, $padType);
232 | }
233 |
234 | $lengthOfPadding = $padLength - $this->strlen($input);
235 | if ($lengthOfPadding <= 0) {
236 | return $input;
237 | }
238 |
239 | $padStringLength = $this->strlen($padString);
240 | if ($padStringLength === 0) {
241 | return $input;
242 | }
243 |
244 | $repeatCount = floor($lengthOfPadding / $padStringLength);
245 |
246 | if ($padType === STR_PAD_BOTH) {
247 | $repeatCountLeft = $repeatCountRight = ($repeatCount - $repeatCount % 2) / 2;
248 |
249 | $lastStringLength = $lengthOfPadding - 2 * $repeatCountLeft * $padStringLength;
250 | $lastStringLeftLength = $lastStringRightLength = floor($lastStringLength / 2);
251 | $lastStringRightLength += $lastStringLength % 2;
252 |
253 | $lastStringLeft = $this->substr($padString, 0, $lastStringLeftLength);
254 | $lastStringRight = $this->substr($padString, 0, $lastStringRightLength);
255 |
256 | return str_repeat($padString, $repeatCountLeft) . $lastStringLeft
257 | . $input
258 | . str_repeat($padString, $repeatCountRight) . $lastStringRight;
259 | }
260 |
261 | $lastString = $this->substr($padString, 0, $lengthOfPadding % $padStringLength);
262 |
263 | if ($padType === STR_PAD_LEFT) {
264 | return str_repeat($padString, $repeatCount) . $lastString . $input;
265 | }
266 |
267 | return $input . str_repeat($padString, $repeatCount) . $lastString;
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/src/StringWrapper/Iconv.php:
--------------------------------------------------------------------------------
1 | getEncoding());
233 | }
234 |
235 | /**
236 | * Returns the portion of string specified by the start and length parameters
237 | *
238 | * @param string $str
239 | * @param int $offset
240 | * @param int|null $length
241 | * @return string|false
242 | */
243 | public function substr($str, $offset = 0, $length = null)
244 | {
245 | return iconv_substr($str, $offset, $length, $this->getEncoding());
246 | }
247 |
248 | /**
249 | * Find the position of the first occurrence of a substring in a string
250 | *
251 | * @param string $haystack
252 | * @param string $needle
253 | * @param int $offset
254 | * @return int|false
255 | */
256 | public function strpos($haystack, $needle, $offset = 0)
257 | {
258 | return iconv_strpos($haystack, $needle, $offset, $this->getEncoding());
259 | }
260 |
261 | /**
262 | * Convert a string from defined encoding to the defined convert encoding
263 | *
264 | * @param string $str
265 | * @param bool $reverse
266 | * @return string|false
267 | */
268 | public function convert($str, $reverse = false)
269 | {
270 | $encoding = $this->getEncoding();
271 | $convertEncoding = $this->getConvertEncoding();
272 | if ($convertEncoding === null) {
273 | throw new Exception\LogicException(
274 | 'No convert encoding defined'
275 | );
276 | }
277 |
278 | if ($encoding === $convertEncoding) {
279 | return $str;
280 | }
281 |
282 | $fromEncoding = $reverse ? $convertEncoding : $encoding;
283 | $toEncoding = $reverse ? $encoding : $convertEncoding;
284 |
285 | // automatically add "//IGNORE" to not stop converting on invalid characters
286 | // invalid characters triggers a notice anyway
287 | return iconv($fromEncoding, $toEncoding . '//IGNORE', $str);
288 | }
289 | }
290 |
--------------------------------------------------------------------------------
/src/StringWrapper/Intl.php:
--------------------------------------------------------------------------------
1 | getEncoding());
67 | }
68 |
69 | /**
70 | * Returns the portion of string specified by the start and length parameters
71 | *
72 | * @param string $str
73 | * @param int $offset
74 | * @param int|null $length
75 | * @return string|false
76 | */
77 | public function substr($str, $offset = 0, $length = null)
78 | {
79 | return mb_substr($str, $offset, $length, $this->getEncoding());
80 | }
81 |
82 | /**
83 | * Find the position of the first occurrence of a substring in a string
84 | *
85 | * @param string $haystack
86 | * @param string $needle
87 | * @param int $offset
88 | * @return int|false
89 | */
90 | public function strpos($haystack, $needle, $offset = 0)
91 | {
92 | return mb_strpos($haystack, $needle, $offset, $this->getEncoding());
93 | }
94 |
95 | /**
96 | * Convert a string from defined encoding to the defined convert encoding
97 | *
98 | * @param string $str
99 | * @param bool $reverse
100 | * @return string|false
101 | */
102 | public function convert($str, $reverse = false)
103 | {
104 | $encoding = $this->getEncoding();
105 | $convertEncoding = $this->getConvertEncoding();
106 |
107 | if ($convertEncoding === null) {
108 | throw new Exception\LogicException(
109 | 'No convert encoding defined'
110 | );
111 | }
112 |
113 | if ($encoding === $convertEncoding) {
114 | return $str;
115 | }
116 |
117 | $fromEncoding = $reverse ? $convertEncoding : $encoding;
118 | $toEncoding = $reverse ? $encoding : $convertEncoding;
119 | return mb_convert_encoding($str, $toEncoding, $fromEncoding);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/StringWrapper/Native.php:
--------------------------------------------------------------------------------
1 | convertEncoding = $encodingUpper;
80 | }
81 |
82 | if ($convertEncoding !== null) {
83 | if ($encodingUpper !== strtoupper($convertEncoding)) {
84 | throw new Exception\InvalidArgumentException(
85 | 'Wrapper doesn\'t support to convert between character encodings'
86 | );
87 | }
88 |
89 | $this->convertEncoding = $encodingUpper;
90 | } else {
91 | $this->convertEncoding = null;
92 | }
93 | $this->encoding = $encodingUpper;
94 |
95 | return $this;
96 | }
97 |
98 | /**
99 | * Returns the length of the given string
100 | *
101 | * @param string $str
102 | * @return int|false
103 | */
104 | public function strlen($str)
105 | {
106 | return strlen($str);
107 | }
108 |
109 | /**
110 | * Returns the portion of string specified by the start and length parameters
111 | *
112 | * @param string $str
113 | * @param int $offset
114 | * @param int|null $length
115 | * @return string|false
116 | */
117 | public function substr($str, $offset = 0, $length = null)
118 | {
119 | return substr($str, $offset, $length);
120 | }
121 |
122 | /**
123 | * Find the position of the first occurrence of a substring in a string
124 | *
125 | * @param string $haystack
126 | * @param string $needle
127 | * @param int $offset
128 | * @return int|false
129 | */
130 | public function strpos($haystack, $needle, $offset = 0)
131 | {
132 | return strpos($haystack, $needle, $offset);
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/StringWrapper/StringWrapperInterface.php:
--------------------------------------------------------------------------------
1 |