Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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