Viewing file: Native.php (13.67 KB) -rwxrwxr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
namespace Laravel\SerializableClosure\Serializers;
use Closure; use Laravel\SerializableClosure\Contracts\Serializable; use Laravel\SerializableClosure\SerializableClosure; use Laravel\SerializableClosure\Support\ClosureScope; use Laravel\SerializableClosure\Support\ClosureStream; use Laravel\SerializableClosure\Support\ReflectionClosure; use Laravel\SerializableClosure\Support\SelfReference; use ReflectionObject; use UnitEnum;
class Native implements Serializable { /** * Transform the use variables before serialization. * * @var \Closure|null */ public static $transformUseVariables;
/** * Resolve the use variables after unserialization. * * @var \Closure|null */ public static $resolveUseVariables;
/** * The closure to be serialized/unserialized. * * @var \Closure */ protected $closure;
/** * The closure's reflection. * * @var \Laravel\SerializableClosure\Support\ReflectionClosure|null */ protected $reflector;
/** * The closure's code. * * @var array|null */ protected $code;
/** * The closure's reference. * * @var string */ protected $reference;
/** * The closure's scope. * * @var \Laravel\SerializableClosure\Support\ClosureScope|null */ protected $scope;
/** * The "key" that marks an array as recursive. */ const ARRAY_RECURSIVE_KEY = 'LARAVEL_SERIALIZABLE_RECURSIVE_KEY';
/** * Creates a new serializable closure instance. * * @param \Closure $closure * @return void */ public function __construct(Closure $closure) { $this->closure = $closure; }
/** * Resolve the closure with the given arguments. * * @return mixed */ public function __invoke() { return call_user_func_array($this->closure, func_get_args()); }
/** * Gets the closure. * * @return \Closure */ public function getClosure() { return $this->closure; }
/** * Get the serializable representation of the closure. * * @return array */ public function __serialize() { if ($this->scope === null) { $this->scope = new ClosureScope(); $this->scope->toSerialize++; }
$this->scope->serializations++;
$scope = $object = null; $reflector = $this->getReflector();
if ($reflector->isBindingRequired()) { $object = $reflector->getClosureThis();
static::wrapClosures($object, $this->scope); }
if ($scope = $reflector->getClosureScopeClass()) { $scope = $scope->name; }
$this->reference = spl_object_hash($this->closure);
$this->scope[$this->closure] = $this;
$use = $reflector->getUseVariables();
if (static::$transformUseVariables) { $use = call_user_func(static::$transformUseVariables, $reflector->getUseVariables()); }
$code = $reflector->getCode();
$this->mapByReference($use);
$data = [ 'use' => $use, 'function' => $code, 'scope' => $scope, 'this' => $object, 'self' => $this->reference, ];
if (! --$this->scope->serializations && ! --$this->scope->toSerialize) { $this->scope = null; }
return $data; }
/** * Restore the closure after serialization. * * @param array $data * @return void */ public function __unserialize($data) { ClosureStream::register();
$this->code = $data; unset($data);
$this->code['objects'] = [];
if ($this->code['use']) { $this->scope = new ClosureScope();
if (static::$resolveUseVariables) { $this->code['use'] = call_user_func(static::$resolveUseVariables, $this->code['use']); }
$this->mapPointers($this->code['use']);
extract($this->code['use'], EXTR_OVERWRITE | EXTR_REFS);
$this->scope = null; }
$this->closure = include ClosureStream::STREAM_PROTO.'://'.$this->code['function'];
if ($this->code['this'] === $this) { $this->code['this'] = null; }
$this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']);
if (! empty($this->code['objects'])) { foreach ($this->code['objects'] as $item) { $item['property']->setValue($item['instance'], $item['object']->getClosure()); } }
$this->code = $this->code['function']; }
/** * Ensures the given closures are serializable. * * @param mixed $data * @param \Laravel\SerializableClosure\Support\ClosureScope $storage * @return void */ public static function wrapClosures(&$data, $storage) { if ($data instanceof Closure) { $data = new static($data); } elseif (is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; }
$data[self::ARRAY_RECURSIVE_KEY] = true;
foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; } static::wrapClosures($value, $storage); }
unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($storage[$data])) { $data = $storage[$data];
return; }
$data = $storage[$data] = clone $data;
foreach ($data as &$value) { static::wrapClosures($value, $storage); }
unset($value); } elseif (is_object($data) && ! $data instanceof static) { if (isset($storage[$data])) { $data = $storage[$data];
return; }
$instance = $data; $reflection = new ReflectionObject($instance);
if (! $reflection->isUserDefined()) { $storage[$instance] = $data;
return; }
$storage[$instance] = $data = $reflection->newInstanceWithoutConstructor();
do { if (! $reflection->isUserDefined()) { break; }
foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) { continue; }
$property->setAccessible(true);
if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) { continue; }
$value = $property->getValue($instance);
if (is_array($value) || is_object($value)) { static::wrapClosures($value, $storage); }
$property->setValue($data, $value); } } while ($reflection = $reflection->getParentClass()); } }
/** * Gets the closure's reflector. * * @return \Laravel\SerializableClosure\Support\ReflectionClosure */ public function getReflector() { if ($this->reflector === null) { $this->code = null; $this->reflector = new ReflectionClosure($this->closure); }
return $this->reflector; }
/** * Internal method used to map closure pointers. * * @param mixed $data * @return void */ protected function mapPointers(&$data) { $scope = $this->scope;
if ($data instanceof static) { $data = &$data->closure; } elseif (is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; }
$data[self::ARRAY_RECURSIVE_KEY] = true;
foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; } elseif ($value instanceof static) { $data[$key] = &$value->closure; } elseif ($value instanceof SelfReference && $value->hash === $this->code['self']) { $data[$key] = &$this->closure; } else { $this->mapPointers($value); } }
unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($scope[$data])) { return; }
$scope[$data] = true;
foreach ($data as $key => &$value) { if ($value instanceof SelfReference && $value->hash === $this->code['self']) { $data->{$key} = &$this->closure; } elseif (is_array($value) || is_object($value)) { $this->mapPointers($value); } }
unset($value); } elseif (is_object($data) && ! ($data instanceof Closure)) { if (isset($scope[$data])) { return; }
$scope[$data] = true; $reflection = new ReflectionObject($data);
do { if (! $reflection->isUserDefined()) { break; }
foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) { continue; }
$property->setAccessible(true);
if (PHP_VERSION >= 7.4 && ! $property->isInitialized($data)) { continue; }
$item = $property->getValue($data);
if ($item instanceof SerializableClosure || ($item instanceof SelfReference && $item->hash === $this->code['self'])) { $this->code['objects'][] = [ 'instance' => $data, 'property' => $property, 'object' => $item instanceof SelfReference ? $this : $item, ]; } elseif (is_array($item) || is_object($item)) { $this->mapPointers($item); $property->setValue($data, $item); } } } while ($reflection = $reflection->getParentClass()); } }
/** * Internal method used to map closures by reference. * * @param mixed $data * @return void */ protected function mapByReference(&$data) { if ($data instanceof Closure) { if ($data === $this->closure) { $data = new SelfReference($this->reference);
return; }
if (isset($this->scope[$data])) { $data = $this->scope[$data];
return; }
$instance = new static($data);
$instance->scope = $this->scope;
$data = $this->scope[$data] = $instance; } elseif (is_array($data)) { if (isset($data[self::ARRAY_RECURSIVE_KEY])) { return; }
$data[self::ARRAY_RECURSIVE_KEY] = true;
foreach ($data as $key => &$value) { if ($key === self::ARRAY_RECURSIVE_KEY) { continue; }
$this->mapByReference($value); }
unset($value); unset($data[self::ARRAY_RECURSIVE_KEY]); } elseif ($data instanceof \stdClass) { if (isset($this->scope[$data])) { $data = $this->scope[$data];
return; }
$instance = $data; $this->scope[$instance] = $data = clone $data;
foreach ($data as &$value) { $this->mapByReference($value); }
unset($value); } elseif (is_object($data) && ! $data instanceof SerializableClosure) { if (isset($this->scope[$data])) { $data = $this->scope[$data];
return; }
$instance = $data;
if ($data instanceof UnitEnum) { $this->scope[$instance] = $data;
return; }
$reflection = new ReflectionObject($data);
if (! $reflection->isUserDefined()) { $this->scope[$instance] = $data;
return; }
$this->scope[$instance] = $data = $reflection->newInstanceWithoutConstructor();
do { if (! $reflection->isUserDefined()) { break; }
foreach ($reflection->getProperties() as $property) { if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) { continue; }
$property->setAccessible(true);
if (PHP_VERSION >= 7.4 && ! $property->isInitialized($instance)) { continue; }
$value = $property->getValue($instance);
if (is_array($value) || is_object($value)) { $this->mapByReference($value); }
$property->setValue($data, $value); } } while ($reflection = $reflection->getParentClass()); } } }
|