Subversion-Projekte lars-tiefland.laravel_shop

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 sebastian/global-state.
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\GlobalState;
11
 
12
use const PHP_VERSION_ID;
13
use function array_keys;
14
use function array_merge;
15
use function array_reverse;
16
use function func_get_args;
17
use function get_declared_classes;
18
use function get_declared_interfaces;
19
use function get_declared_traits;
20
use function get_defined_constants;
21
use function get_defined_functions;
22
use function get_included_files;
23
use function in_array;
24
use function ini_get_all;
25
use function is_array;
26
use function is_object;
27
use function is_resource;
28
use function is_scalar;
29
use function serialize;
30
use function unserialize;
31
use ReflectionClass;
32
use SebastianBergmann\ObjectReflector\ObjectReflector;
33
use SebastianBergmann\RecursionContext\Context;
34
use Throwable;
35
 
36
/**
37
 * A snapshot of global state.
38
 */
39
class Snapshot
40
{
41
    /**
42
     * @var ExcludeList
43
     */
44
    private $excludeList;
45
 
46
    /**
47
     * @var array
48
     */
49
    private $globalVariables = [];
50
 
51
    /**
52
     * @var array
53
     */
54
    private $superGlobalArrays = [];
55
 
56
    /**
57
     * @var array
58
     */
59
    private $superGlobalVariables = [];
60
 
61
    /**
62
     * @var array
63
     */
64
    private $staticAttributes = [];
65
 
66
    /**
67
     * @var array
68
     */
69
    private $iniSettings = [];
70
 
71
    /**
72
     * @var array
73
     */
74
    private $includedFiles = [];
75
 
76
    /**
77
     * @var array
78
     */
79
    private $constants = [];
80
 
81
    /**
82
     * @var array
83
     */
84
    private $functions = [];
85
 
86
    /**
87
     * @var array
88
     */
89
    private $interfaces = [];
90
 
91
    /**
92
     * @var array
93
     */
94
    private $classes = [];
95
 
96
    /**
97
     * @var array
98
     */
99
    private $traits = [];
100
 
101
    /**
102
     * Creates a snapshot of the current global state.
103
     */
104
    public function __construct(ExcludeList $excludeList = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true)
105
    {
106
        $this->excludeList = $excludeList ?: new ExcludeList;
107
 
108
        if ($includeConstants) {
109
            $this->snapshotConstants();
110
        }
111
 
112
        if ($includeFunctions) {
113
            $this->snapshotFunctions();
114
        }
115
 
116
        if ($includeClasses || $includeStaticAttributes) {
117
            $this->snapshotClasses();
118
        }
119
 
120
        if ($includeInterfaces) {
121
            $this->snapshotInterfaces();
122
        }
123
 
124
        if ($includeGlobalVariables) {
125
            $this->setupSuperGlobalArrays();
126
            $this->snapshotGlobals();
127
        }
128
 
129
        if ($includeStaticAttributes) {
130
            $this->snapshotStaticAttributes();
131
        }
132
 
133
        if ($includeIniSettings) {
134
            $this->iniSettings = ini_get_all(null, false);
135
        }
136
 
137
        if ($includeIncludedFiles) {
138
            $this->includedFiles = get_included_files();
139
        }
140
 
141
        if ($includeTraits) {
142
            $this->traits = get_declared_traits();
143
        }
144
    }
145
 
146
    public function excludeList(): ExcludeList
147
    {
148
        return $this->excludeList;
149
    }
150
 
151
    public function globalVariables(): array
152
    {
153
        return $this->globalVariables;
154
    }
155
 
156
    public function superGlobalVariables(): array
157
    {
158
        return $this->superGlobalVariables;
159
    }
160
 
161
    public function superGlobalArrays(): array
162
    {
163
        return $this->superGlobalArrays;
164
    }
165
 
166
    public function staticAttributes(): array
167
    {
168
        return $this->staticAttributes;
169
    }
170
 
171
    public function iniSettings(): array
172
    {
173
        return $this->iniSettings;
174
    }
175
 
176
    public function includedFiles(): array
177
    {
178
        return $this->includedFiles;
179
    }
180
 
181
    public function constants(): array
182
    {
183
        return $this->constants;
184
    }
185
 
186
    public function functions(): array
187
    {
188
        return $this->functions;
189
    }
190
 
191
    public function interfaces(): array
192
    {
193
        return $this->interfaces;
194
    }
195
 
196
    public function classes(): array
197
    {
198
        return $this->classes;
199
    }
200
 
201
    public function traits(): array
202
    {
203
        return $this->traits;
204
    }
205
 
206
    /**
207
     * Creates a snapshot user-defined constants.
208
     */
209
    private function snapshotConstants(): void
210
    {
211
        $constants = get_defined_constants(true);
212
 
213
        if (isset($constants['user'])) {
214
            $this->constants = $constants['user'];
215
        }
216
    }
217
 
218
    /**
219
     * Creates a snapshot user-defined functions.
220
     */
221
    private function snapshotFunctions(): void
222
    {
223
        $functions = get_defined_functions();
224
 
225
        $this->functions = $functions['user'];
226
    }
227
 
228
    /**
229
     * Creates a snapshot user-defined classes.
230
     */
231
    private function snapshotClasses(): void
232
    {
233
        foreach (array_reverse(get_declared_classes()) as $className) {
234
            $class = new ReflectionClass($className);
235
 
236
            if (!$class->isUserDefined()) {
237
                break;
238
            }
239
 
240
            $this->classes[] = $className;
241
        }
242
 
243
        $this->classes = array_reverse($this->classes);
244
    }
245
 
246
    /**
247
     * Creates a snapshot user-defined interfaces.
248
     */
249
    private function snapshotInterfaces(): void
250
    {
251
        foreach (array_reverse(get_declared_interfaces()) as $interfaceName) {
252
            $class = new ReflectionClass($interfaceName);
253
 
254
            if (!$class->isUserDefined()) {
255
                break;
256
            }
257
 
258
            $this->interfaces[] = $interfaceName;
259
        }
260
 
261
        $this->interfaces = array_reverse($this->interfaces);
262
    }
263
 
264
    /**
265
     * Creates a snapshot of all global and super-global variables.
266
     */
267
    private function snapshotGlobals(): void
268
    {
269
        $superGlobalArrays = $this->superGlobalArrays();
270
 
271
        foreach ($superGlobalArrays as $superGlobalArray) {
272
            $this->snapshotSuperGlobalArray($superGlobalArray);
273
        }
274
 
275
        foreach (array_keys($GLOBALS) as $key) {
276
            if ($key !== 'GLOBALS' &&
277
                !in_array($key, $superGlobalArrays, true) &&
278
                $this->canBeSerialized($GLOBALS[$key]) &&
279
                !$this->excludeList->isGlobalVariableExcluded($key)) {
280
                /* @noinspection UnserializeExploitsInspection */
281
                $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key]));
282
            }
283
        }
284
    }
285
 
286
    /**
287
     * Creates a snapshot a super-global variable array.
288
     */
289
    private function snapshotSuperGlobalArray(string $superGlobalArray): void
290
    {
291
        $this->superGlobalVariables[$superGlobalArray] = [];
292
 
293
        if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) {
294
            foreach ($GLOBALS[$superGlobalArray] as $key => $value) {
295
                /* @noinspection UnserializeExploitsInspection */
296
                $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value));
297
            }
298
        }
299
    }
300
 
301
    /**
302
     * Creates a snapshot of all static attributes in user-defined classes.
303
     */
304
    private function snapshotStaticAttributes(): void
305
    {
306
        foreach ($this->classes as $className) {
307
            $class    = new ReflectionClass($className);
308
            $snapshot = [];
309
 
310
            foreach ($class->getProperties() as $attribute) {
311
                if ($attribute->isStatic()) {
312
                    $name = $attribute->getName();
313
 
314
                    if ($this->excludeList->isStaticAttributeExcluded($className, $name)) {
315
                        continue;
316
                    }
317
 
318
                    $attribute->setAccessible(true);
319
 
320
                    if (PHP_VERSION_ID >= 70400 && !$attribute->isInitialized()) {
321
                        continue;
322
                    }
323
 
324
                    $value = $attribute->getValue();
325
 
326
                    if ($this->canBeSerialized($value)) {
327
                        /* @noinspection UnserializeExploitsInspection */
328
                        $snapshot[$name] = unserialize(serialize($value));
329
                    }
330
                }
331
            }
332
 
333
            if (!empty($snapshot)) {
334
                $this->staticAttributes[$className] = $snapshot;
335
            }
336
        }
337
    }
338
 
339
    /**
340
     * Returns a list of all super-global variable arrays.
341
     */
342
    private function setupSuperGlobalArrays(): void
343
    {
344
        $this->superGlobalArrays = [
345
            '_ENV',
346
            '_POST',
347
            '_GET',
348
            '_COOKIE',
349
            '_SERVER',
350
            '_FILES',
351
            '_REQUEST',
352
        ];
353
    }
354
 
355
    private function canBeSerialized($variable): bool
356
    {
357
        if (is_scalar($variable) || $variable === null) {
358
            return true;
359
        }
360
 
361
        if (is_resource($variable)) {
362
            return false;
363
        }
364
 
365
        foreach ($this->enumerateObjectsAndResources($variable) as $value) {
366
            if (is_resource($value)) {
367
                return false;
368
            }
369
 
370
            if (is_object($value)) {
371
                $class = new ReflectionClass($value);
372
 
373
                if ($class->isAnonymous()) {
374
                    return false;
375
                }
376
 
377
                try {
378
                    @serialize($value);
379
                } catch (Throwable $t) {
380
                    return false;
381
                }
382
            }
383
        }
384
 
385
        return true;
386
    }
387
 
388
    private function enumerateObjectsAndResources($variable): array
389
    {
390
        if (isset(func_get_args()[1])) {
391
            $processed = func_get_args()[1];
392
        } else {
393
            $processed = new Context;
394
        }
395
 
396
        $result = [];
397
 
398
        if ($processed->contains($variable)) {
399
            return $result;
400
        }
401
 
402
        $array = $variable;
403
        $processed->add($variable);
404
 
405
        if (is_array($variable)) {
406
            foreach ($array as $element) {
407
                if (!is_array($element) && !is_object($element) && !is_resource($element)) {
408
                    continue;
409
                }
410
 
411
                if (!is_resource($element)) {
412
                    /** @noinspection SlowArrayOperationsInLoopInspection */
413
                    $result = array_merge(
414
                        $result,
415
                        $this->enumerateObjectsAndResources($element, $processed)
416
                    );
417
                } else {
418
                    $result[] = $element;
419
                }
420
            }
421
        } else {
422
            $result[] = $variable;
423
 
424
            foreach ((new ObjectReflector)->getAttributes($variable) as $value) {
425
                if (!is_array($value) && !is_object($value) && !is_resource($value)) {
426
                    continue;
427
                }
428
 
429
                if (!is_resource($value)) {
430
                    /** @noinspection SlowArrayOperationsInLoopInspection */
431
                    $result = array_merge(
432
                        $result,
433
                        $this->enumerateObjectsAndResources($value, $processed)
434
                    );
435
                } else {
436
                    $result[] = $value;
437
                }
438
            }
439
        }
440
 
441
        return $result;
442
    }
443
}