123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- <?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 && ! $data instanceof UnitEnum) {
- 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());
- }
- }
- }
|