Proyectos de Subversion Moodle

Rev

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