Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the Recursion Context package.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianBergmann\RecursionContext;
11
 
12
use const PHP_INT_MAX;
13
use const PHP_INT_MIN;
14
use function array_pop;
15
use function array_slice;
16
use function count;
17
use function is_array;
18
use function is_object;
19
use function random_int;
20
use function spl_object_hash;
21
use SplObjectStorage;
22
 
23
/**
24
 * A context containing previously processed arrays and objects
25
 * when recursively processing a value.
26
 */
27
final class Context
28
{
29
    /**
30
     * @var array[]
31
     */
32
    private $arrays;
33
 
34
    /**
35
     * @var SplObjectStorage
36
     */
37
    private $objects;
38
 
39
    /**
40
     * Initialises the context.
41
     */
42
    public function __construct()
43
    {
44
        $this->arrays  = [];
45
        $this->objects = new SplObjectStorage;
46
    }
47
 
48
    /**
49
     * @codeCoverageIgnore
50
     */
51
    public function __destruct()
52
    {
53
        foreach ($this->arrays as &$array) {
54
            if (is_array($array)) {
55
                array_pop($array);
56
                array_pop($array);
57
            }
58
        }
59
    }
60
 
61
    /**
62
     * Adds a value to the context.
63
     *
64
     * @param array|object $value the value to add
65
     *
66
     * @throws InvalidArgumentException Thrown if $value is not an array or object
67
     *
68
     * @return bool|int|string the ID of the stored value, either as a string or integer
69
     *
70
     * @psalm-template T
71
     * @psalm-param T $value
72
     * @param-out T $value
73
     */
74
    public function add(&$value)
75
    {
76
        if (is_array($value)) {
77
            return $this->addArray($value);
78
        }
79
 
80
        if (is_object($value)) {
81
            return $this->addObject($value);
82
        }
83
 
84
        throw new InvalidArgumentException(
85
            'Only arrays and objects are supported'
86
        );
87
    }
88
 
89
    /**
90
     * Checks if the given value exists within the context.
91
     *
92
     * @param array|object $value the value to check
93
     *
94
     * @throws InvalidArgumentException Thrown if $value is not an array or object
95
     *
96
     * @return false|int|string the string or integer ID of the stored value if it has already been seen, or false if the value is not stored
97
     *
98
     * @psalm-template T
99
     * @psalm-param T $value
100
     * @param-out T $value
101
     */
102
    public function contains(&$value)
103
    {
104
        if (is_array($value)) {
105
            return $this->containsArray($value);
106
        }
107
 
108
        if (is_object($value)) {
109
            return $this->containsObject($value);
110
        }
111
 
112
        throw new InvalidArgumentException(
113
            'Only arrays and objects are supported'
114
        );
115
    }
116
 
117
    /**
118
     * @return bool|int
119
     */
120
    private function addArray(array &$array)
121
    {
122
        $key = $this->containsArray($array);
123
 
124
        if ($key !== false) {
125
            return $key;
126
        }
127
 
128
        $key            = count($this->arrays);
129
        $this->arrays[] = &$array;
130
 
131
        if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) {
132
            $array[] = $key;
133
            $array[] = $this->objects;
134
        } else { /* cover the improbable case too */
135
            do {
136
                $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
137
            } while (isset($array[$key]));
138
 
139
            $array[$key] = $key;
140
 
141
            do {
142
                $key = random_int(PHP_INT_MIN, PHP_INT_MAX);
143
            } while (isset($array[$key]));
144
 
145
            $array[$key] = $this->objects;
146
        }
147
 
148
        return $key;
149
    }
150
 
151
    /**
152
     * @param object $object
153
     */
154
    private function addObject($object): string
155
    {
156
        if (!$this->objects->contains($object)) {
157
            $this->objects->attach($object);
158
        }
159
 
160
        return spl_object_hash($object);
161
    }
162
 
163
    /**
164
     * @return false|int
165
     */
166
    private function containsArray(array &$array)
167
    {
168
        $end = array_slice($array, -2);
169
 
170
        return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false;
171
    }
172
 
173
    /**
174
     * @param object $value
175
     *
176
     * @return false|string
177
     */
178
    private function containsObject($value)
179
    {
180
        if ($this->objects->contains($value)) {
181
            return spl_object_hash($value);
182
        }
183
 
184
        return false;
185
    }
186
}