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
2
 
3
namespace Laravel\SerializableClosure\Serializers;
4
 
5
use Closure;
6
use DateTimeInterface;
7
use Laravel\SerializableClosure\Contracts\Serializable;
8
use Laravel\SerializableClosure\SerializableClosure;
9
use Laravel\SerializableClosure\Support\ClosureScope;
10
use Laravel\SerializableClosure\Support\ClosureStream;
11
use Laravel\SerializableClosure\Support\ReflectionClosure;
12
use Laravel\SerializableClosure\Support\SelfReference;
13
use ReflectionObject;
14
use UnitEnum;
15
 
16
class Native implements Serializable
17
{
18
    /**
19
     * Transform the use variables before serialization.
20
     *
21
     * @var \Closure|null
22
     */
23
    public static $transformUseVariables;
24
 
25
    /**
26
     * Resolve the use variables after unserialization.
27
     *
28
     * @var \Closure|null
29
     */
30
    public static $resolveUseVariables;
31
 
32
    /**
33
     * The closure to be serialized/unserialized.
34
     *
35
     * @var \Closure
36
     */
37
    protected $closure;
38
 
39
    /**
40
     * The closure's reflection.
41
     *
42
     * @var \Laravel\SerializableClosure\Support\ReflectionClosure|null
43
     */
44
    protected $reflector;
45
 
46
    /**
47
     * The closure's code.
48
     *
49
     * @var array|null
50
     */
51
    protected $code;
52
 
53
    /**
54
     * The closure's reference.
55
     *
56
     * @var string
57
     */
58
    protected $reference;
59
 
60
    /**
61
     * The closure's scope.
62
     *
63
     * @var \Laravel\SerializableClosure\Support\ClosureScope|null
64
     */
65
    protected $scope;
66
 
67
    /**
68
     * The "key" that marks an array as recursive.
69
     */
70
    const ARRAY_RECURSIVE_KEY = 'LARAVEL_SERIALIZABLE_RECURSIVE_KEY';
71
 
72
    /**
73
     * Creates a new serializable closure instance.
74
     *
75
     * @param  \Closure  $closure
76
     * @return void
77
     */
78
    public function __construct(Closure $closure)
79
    {
80
        $this->closure = $closure;
81
    }
82
 
83
    /**
84
     * Resolve the closure with the given arguments.
85
     *
86
     * @return mixed
87
     */
88
    public function __invoke()
89
    {
90
        return call_user_func_array($this->closure, func_get_args());
91
    }
92
 
93
    /**
94
     * Gets the closure.
95
     *
96
     * @return \Closure
97
     */
98
    public function getClosure()
99
    {
100
        return $this->closure;
101
    }
102
 
103
    /**
104
     * Get the serializable representation of the closure.
105
     *
106
     * @return array
107
     */
108
    public function __serialize()
109
    {
110
        if ($this->scope === null) {
111
            $this->scope = new ClosureScope();
112
            $this->scope->toSerialize++;
113
        }
114
 
115
        $this->scope->serializations++;
116
 
117
        $scope = $object = null;
118
        $reflector = $this->getReflector();
119
 
120
        if ($reflector->isBindingRequired()) {
121
            $object = $reflector->getClosureThis();
122
 
123
            static::wrapClosures($object, $this->scope);
124
        }
125
 
126
        if ($scope = $reflector->getClosureScopeClass()) {
127
            $scope = $scope->name;
128
        }
129
 
130
        $this->reference = spl_object_hash($this->closure);
131
 
132
        $this->scope[$this->closure] = $this;
133
 
134
        $use = $reflector->getUseVariables();
135
 
136
        if (static::$transformUseVariables) {
137
            $use = call_user_func(static::$transformUseVariables, $reflector->getUseVariables());
138
        }
139
 
140
        $code = $reflector->getCode();
141
 
142
        $this->mapByReference($use);
143
 
144
        $data = [
145
            'use' => $use,
146
            'function' => $code,
147
            'scope' => $scope,
148
            'this' => $object,
149
            'self' => $this->reference,
150
        ];
151
 
152
        if (! --$this->scope->serializations && ! --$this->scope->toSerialize) {
153
            $this->scope = null;
154
        }
155
 
156
        return $data;
157
    }
158
 
159
    /**
160
     * Restore the closure after serialization.
161
     *
162
     * @param  array  $data
163
     * @return void
164
     */
165
    public function __unserialize($data)
166
    {
167
        ClosureStream::register();
168
 
169
        $this->code = $data;
170
        unset($data);
171
 
172
        $this->code['objects'] = [];
173
 
174
        if ($this->code['use']) {
175
            $this->scope = new ClosureScope();
176
 
177
            if (static::$resolveUseVariables) {
178
                $this->code['use'] = call_user_func(static::$resolveUseVariables, $this->code['use']);
179
            }
180
 
181
            $this->mapPointers($this->code['use']);
182
 
183
            extract($this->code['use'], EXTR_OVERWRITE | EXTR_REFS);
184
 
185
            $this->scope = null;
186
        }
187
 
188
        $this->closure = include ClosureStream::STREAM_PROTO.'://'.$this->code['function'];
189
 
190
        if ($this->code['this'] === $this) {
191
            $this->code['this'] = null;
192
        }
193
 
194
        $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']);
195
 
196
        if (! empty($this->code['objects'])) {
197
            foreach ($this->code['objects'] as $item) {
198
                $item['property']->setValue($item['instance'], $item['object']->getClosure());
199
            }
200
        }
201
 
202
        $this->code = $this->code['function'];
203
    }
204
 
205
    /**
206
     * Ensures the given closures are serializable.
207
     *
208
     * @param  mixed  $data
209
     * @param  \Laravel\SerializableClosure\Support\ClosureScope  $storage
210
     * @return void
211
     */
212
    public static function wrapClosures(&$data, $storage)
213
    {
214
        if ($data instanceof Closure) {
215
            $data = new static($data);
216
        } elseif (is_array($data)) {
217
            if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
218
                return;
219
            }
220
 
221
            $data[self::ARRAY_RECURSIVE_KEY] = true;
222
 
223
            foreach ($data as $key => &$value) {
224
                if ($key === self::ARRAY_RECURSIVE_KEY) {
225
                    continue;
226
                }
227
                static::wrapClosures($value, $storage);
228
            }
229
 
230
            unset($value);
231
            unset($data[self::ARRAY_RECURSIVE_KEY]);
232
        } elseif ($data instanceof \stdClass) {
233
            if (isset($storage[$data])) {
234
                $data = $storage[$data];
235
 
236
                return;
237
            }
238
 
239
            $data = $storage[$data] = clone $data;
240
 
241
            foreach ($data as &$value) {
242
                static::wrapClosures($value, $storage);
243
            }
244
 
245
            unset($value);
246
        } elseif (is_object($data) && ! $data instanceof static && ! $data instanceof UnitEnum) {
247
            if (isset($storage[$data])) {
248
                $data = $storage[$data];
249
 
250
                return;
251
            }
252
 
253
            $instance = $data;
254
            $reflection = new ReflectionObject($instance);
255
 
256
            if (! $reflection->isUserDefined()) {
257
                $storage[$instance] = $data;
258
 
259
                return;
260
            }
261
 
262
            $storage[$instance] = $data = $reflection->newInstanceWithoutConstructor();
263
 
264
            do {
265
                if (! $reflection->isUserDefined()) {
266
                    break;
267
                }
268
 
269
                foreach ($reflection->getProperties() as $property) {
270
                    if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
271
                        continue;
272
                    }
273
 
274
                    $property->setAccessible(true);
275
 
276
                    if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) {
277
                        continue;
278
                    }
279
 
280
                    $value = $property->getValue($instance);
281
 
282
                    if (is_array($value) || is_object($value)) {
283
                        static::wrapClosures($value, $storage);
284
                    }
285
 
286
                    $property->setValue($data, $value);
287
                }
288
            } while ($reflection = $reflection->getParentClass());
289
        }
290
    }
291
 
292
    /**
293
     * Gets the closure's reflector.
294
     *
295
     * @return \Laravel\SerializableClosure\Support\ReflectionClosure
296
     */
297
    public function getReflector()
298
    {
299
        if ($this->reflector === null) {
300
            $this->code = null;
301
            $this->reflector = new ReflectionClosure($this->closure);
302
        }
303
 
304
        return $this->reflector;
305
    }
306
 
307
    /**
308
     * Internal method used to map closure pointers.
309
     *
310
     * @param  mixed  $data
311
     * @return void
312
     */
313
    protected function mapPointers(&$data)
314
    {
315
        $scope = $this->scope;
316
 
317
        if ($data instanceof static) {
318
            $data = &$data->closure;
319
        } elseif (is_array($data)) {
320
            if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
321
                return;
322
            }
323
 
324
            $data[self::ARRAY_RECURSIVE_KEY] = true;
325
 
326
            foreach ($data as $key => &$value) {
327
                if ($key === self::ARRAY_RECURSIVE_KEY) {
328
                    continue;
329
                } elseif ($value instanceof static) {
330
                    $data[$key] = &$value->closure;
331
                } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']) {
332
                    $data[$key] = &$this->closure;
333
                } else {
334
                    $this->mapPointers($value);
335
                }
336
            }
337
 
338
            unset($value);
339
            unset($data[self::ARRAY_RECURSIVE_KEY]);
340
        } elseif ($data instanceof \stdClass) {
341
            if (isset($scope[$data])) {
342
                return;
343
            }
344
 
345
            $scope[$data] = true;
346
 
347
            foreach ($data as $key => &$value) {
348
                if ($value instanceof SelfReference && $value->hash === $this->code['self']) {
349
                    $data->{$key} = &$this->closure;
350
                } elseif (is_array($value) || is_object($value)) {
351
                    $this->mapPointers($value);
352
                }
353
            }
354
 
355
            unset($value);
356
        } elseif (is_object($data) && ! ($data instanceof Closure)) {
357
            if (isset($scope[$data])) {
358
                return;
359
            }
360
 
361
            $scope[$data] = true;
362
            $reflection = new ReflectionObject($data);
363
 
364
            do {
365
                if (! $reflection->isUserDefined()) {
366
                    break;
367
                }
368
 
369
                foreach ($reflection->getProperties() as $property) {
370
                    if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
371
                        continue;
372
                    }
373
 
374
                    $property->setAccessible(true);
375
 
376
                    if (PHP_VERSION >= 7.4 && ! $property->isInitialized($data)) {
377
                        continue;
378
                    }
379
 
380
                    $item = $property->getValue($data);
381
 
382
                    if ($item instanceof SerializableClosure || ($item instanceof SelfReference && $item->hash === $this->code['self'])) {
383
                        $this->code['objects'][] = [
384
                            'instance' => $data,
385
                            'property' => $property,
386
                            'object' => $item instanceof SelfReference ? $this : $item,
387
                        ];
388
                    } elseif (is_array($item) || is_object($item)) {
389
                        $this->mapPointers($item);
390
                        $property->setValue($data, $item);
391
                    }
392
                }
393
            } while ($reflection = $reflection->getParentClass());
394
        }
395
    }
396
 
397
    /**
398
     * Internal method used to map closures by reference.
399
     *
400
     * @param  mixed  $data
401
     * @return void
402
     */
403
    protected function mapByReference(&$data)
404
    {
405
        if ($data instanceof Closure) {
406
            if ($data === $this->closure) {
407
                $data = new SelfReference($this->reference);
408
 
409
                return;
410
            }
411
 
412
            if (isset($this->scope[$data])) {
413
                $data = $this->scope[$data];
414
 
415
                return;
416
            }
417
 
418
            $instance = new static($data);
419
 
420
            $instance->scope = $this->scope;
421
 
422
            $data = $this->scope[$data] = $instance;
423
        } elseif (is_array($data)) {
424
            if (isset($data[self::ARRAY_RECURSIVE_KEY])) {
425
                return;
426
            }
427
 
428
            $data[self::ARRAY_RECURSIVE_KEY] = true;
429
 
430
            foreach ($data as $key => &$value) {
431
                if ($key === self::ARRAY_RECURSIVE_KEY) {
432
                    continue;
433
                }
434
 
435
                $this->mapByReference($value);
436
            }
437
 
438
            unset($value);
439
            unset($data[self::ARRAY_RECURSIVE_KEY]);
440
        } elseif ($data instanceof \stdClass) {
441
            if (isset($this->scope[$data])) {
442
                $data = $this->scope[$data];
443
 
444
                return;
445
            }
446
 
447
            $instance = $data;
448
            $this->scope[$instance] = $data = clone $data;
449
 
450
            foreach ($data as &$value) {
451
                $this->mapByReference($value);
452
            }
453
 
454
            unset($value);
455
        } elseif (is_object($data) && ! $data instanceof SerializableClosure) {
456
            if (isset($this->scope[$data])) {
457
                $data = $this->scope[$data];
458
 
459
                return;
460
            }
461
 
462
            $instance = $data;
463
 
464
            if ($data instanceof DateTimeInterface) {
465
                $this->scope[$instance] = $data;
466
 
467
                return;
468
            }
469
 
470
            if ($data instanceof UnitEnum) {
471
                $this->scope[$instance] = $data;
472
 
473
                return;
474
            }
475
 
476
            $reflection = new ReflectionObject($data);
477
 
478
            if (! $reflection->isUserDefined()) {
479
                $this->scope[$instance] = $data;
480
 
481
                return;
482
            }
483
 
484
            $this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor();
485
 
486
            do {
487
                if (! $reflection->isUserDefined()) {
488
                    break;
489
                }
490
 
491
                foreach ($reflection->getProperties() as $property) {
492
                    if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
493
                        continue;
494
                    }
495
 
496
                    $property->setAccessible(true);
497
 
498
                    if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) {
499
                        continue;
500
                    }
501
 
502
                    $value = $property->getValue($instance);
503
 
504
                    if (is_array($value) || is_object($value)) {
505
                        $this->mapByReference($value);
506
                    }
507
 
508
                    $property->setValue($data, $value);
509
                }
510
            } while ($reflection = $reflection->getParentClass());
511
        }
512
    }
513
}