first commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "laravel\/serializable-closure",
|
||||
"description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"Serializable",
|
||||
"closure"
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https:\/\/github.com\/laravel\/serializable-closure\/issues",
|
||||
"source": "https:\/\/github.com\/laravel\/serializable-closure"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
},
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "nuno@laravel.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate\/support": "^8.0|^9.0|^10.0|^11.0",
|
||||
"nesbot\/carbon": "^2.61|^3.0",
|
||||
"pestphp\/pest": "^1.21.3",
|
||||
"phpstan\/phpstan": "^1.8.2",
|
||||
"symfony\/var-dumper": "^5.4.11|^6.2.0|^7.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ElementorProDeps\\Laravel\\SerializableClosure\\": "src\/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ElementorProDeps\\Tests\\": "tests\/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp\/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Contracts;
|
||||
|
||||
interface Serializable
|
||||
{
|
||||
/**
|
||||
* Resolve the closure with the given arguments.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke();
|
||||
/**
|
||||
* Gets the closure that got serialized/unserialized.
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
public function getClosure();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Contracts;
|
||||
|
||||
interface Signer
|
||||
{
|
||||
/**
|
||||
* Sign the given serializable.
|
||||
*
|
||||
* @param string $serializable
|
||||
* @return array
|
||||
*/
|
||||
public function sign($serializable);
|
||||
/**
|
||||
* Verify the given signature.
|
||||
*
|
||||
* @param array $signature
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($signature);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions;
|
||||
|
||||
use Exception;
|
||||
class InvalidSignatureException extends Exception
|
||||
{
|
||||
/**
|
||||
* Create a new exception instance.
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($message = 'Your serialized closure might have been modified or it\'s unsafe to be unserialized.')
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions;
|
||||
|
||||
use Exception;
|
||||
class MissingSecretKeyException extends Exception
|
||||
{
|
||||
/**
|
||||
* Create a new exception instance.
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($message = 'No serializable closure secret key has been specified.')
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Exceptions;
|
||||
|
||||
use Exception;
|
||||
class PhpVersionNotSupportedException extends Exception
|
||||
{
|
||||
/**
|
||||
* Create a new exception instance.
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($message = 'PHP 7.3 is not supported.')
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure;
|
||||
|
||||
use Closure;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Exceptions\InvalidSignatureException;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Exceptions\PhpVersionNotSupportedException;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Serializers\Signed;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Signers\Hmac;
|
||||
class SerializableClosure
|
||||
{
|
||||
/**
|
||||
* The closure's serializable.
|
||||
*
|
||||
* @var \Laravel\SerializableClosure\Contracts\Serializable
|
||||
*/
|
||||
protected $serializable;
|
||||
/**
|
||||
* Creates a new serializable closure instance.
|
||||
*
|
||||
* @param \Closure $closure
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Closure $closure)
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
$this->serializable = Serializers\Signed::$signer ? new Serializers\Signed($closure) : new Serializers\Native($closure);
|
||||
}
|
||||
/**
|
||||
* Resolve the closure with the given arguments.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
return \call_user_func_array($this->serializable, \func_get_args());
|
||||
}
|
||||
/**
|
||||
* Gets the closure.
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
public function getClosure()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
return $this->serializable->getClosure();
|
||||
}
|
||||
/**
|
||||
* Create a new unsigned serializable closure instance.
|
||||
*
|
||||
* @param Closure $closure
|
||||
* @return \Laravel\SerializableClosure\UnsignedSerializableClosure
|
||||
*/
|
||||
public static function unsigned(Closure $closure)
|
||||
{
|
||||
return new UnsignedSerializableClosure($closure);
|
||||
}
|
||||
/**
|
||||
* Sets the serializable closure secret key.
|
||||
*
|
||||
* @param string|null $secret
|
||||
* @return void
|
||||
*/
|
||||
public static function setSecretKey($secret)
|
||||
{
|
||||
Serializers\Signed::$signer = $secret ? new Hmac($secret) : null;
|
||||
}
|
||||
/**
|
||||
* Sets the serializable closure secret key.
|
||||
*
|
||||
* @param \Closure|null $transformer
|
||||
* @return void
|
||||
*/
|
||||
public static function transformUseVariablesUsing($transformer)
|
||||
{
|
||||
Serializers\Native::$transformUseVariables = $transformer;
|
||||
}
|
||||
/**
|
||||
* Sets the serializable closure secret key.
|
||||
*
|
||||
* @param \Closure|null $resolver
|
||||
* @return void
|
||||
*/
|
||||
public static function resolveUseVariablesUsing($resolver)
|
||||
{
|
||||
Serializers\Native::$resolveUseVariables = $resolver;
|
||||
}
|
||||
/**
|
||||
* Get the serializable representation of the closure.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize()
|
||||
{
|
||||
return ['serializable' => $this->serializable];
|
||||
}
|
||||
/**
|
||||
* Restore the closure after serialization.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*
|
||||
* @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException
|
||||
*/
|
||||
public function __unserialize($data)
|
||||
{
|
||||
if (Signed::$signer && !$data['serializable'] instanceof Signed) {
|
||||
throw new InvalidSignatureException();
|
||||
}
|
||||
$this->serializable = $data['serializable'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Serializers;
|
||||
|
||||
use Closure;
|
||||
use DateTimeInterface;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Contracts\Serializable;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\SerializableClosure;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Support\ClosureScope;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Support\ClosureStream;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Support\ReflectionClosure;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Support\SelfReference;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\UnsignedSerializableClosure;
|
||||
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;
|
||||
}
|
||||
if (\PHP_VERSION >= 8.1 && $property->isReadOnly()) {
|
||||
continue;
|
||||
}
|
||||
$item = $property->getValue($data);
|
||||
if ($item instanceof SerializableClosure || $item instanceof UnsignedSerializableClosure || $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 && !$data instanceof UnsignedSerializableClosure) {
|
||||
if (isset($this->scope[$data])) {
|
||||
$data = $this->scope[$data];
|
||||
return;
|
||||
}
|
||||
$instance = $data;
|
||||
if ($data instanceof DateTimeInterface) {
|
||||
$this->scope[$instance] = $data;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (\PHP_VERSION >= 8.1 && $property->isReadOnly() && $property->class !== $reflection->name) {
|
||||
continue;
|
||||
}
|
||||
$value = $property->getValue($instance);
|
||||
if (\is_array($value) || \is_object($value)) {
|
||||
$this->mapByReference($value);
|
||||
}
|
||||
$property->setValue($data, $value);
|
||||
}
|
||||
} while ($reflection = $reflection->getParentClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Serializers;
|
||||
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Contracts\Serializable;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Exceptions\InvalidSignatureException;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Exceptions\MissingSecretKeyException;
|
||||
class Signed implements Serializable
|
||||
{
|
||||
/**
|
||||
* The signer that will sign and verify the closure's signature.
|
||||
*
|
||||
* @var \Laravel\SerializableClosure\Contracts\Signer|null
|
||||
*/
|
||||
public static $signer;
|
||||
/**
|
||||
* The closure to be serialized/unserialized.
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $closure;
|
||||
/**
|
||||
* Creates a new serializable closure instance.
|
||||
*
|
||||
* @param \Closure $closure
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($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 (!static::$signer) {
|
||||
throw new MissingSecretKeyException();
|
||||
}
|
||||
return static::$signer->sign(\serialize(new Native($this->closure)));
|
||||
}
|
||||
/**
|
||||
* Restore the closure after serialization.
|
||||
*
|
||||
* @param array $signature
|
||||
* @return void
|
||||
*
|
||||
* @throws \Laravel\SerializableClosure\Exceptions\InvalidSignatureException
|
||||
*/
|
||||
public function __unserialize($signature)
|
||||
{
|
||||
if (static::$signer && !static::$signer->verify($signature)) {
|
||||
throw new InvalidSignatureException();
|
||||
}
|
||||
/** @var \Laravel\SerializableClosure\Contracts\Serializable $serializable */
|
||||
$serializable = \unserialize($signature['serializable']);
|
||||
$this->closure = $serializable->getClosure();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Signers;
|
||||
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Contracts\Signer;
|
||||
class Hmac implements Signer
|
||||
{
|
||||
/**
|
||||
* The secret key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $secret;
|
||||
/**
|
||||
* Creates a new signer instance.
|
||||
*
|
||||
* @param string $secret
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($secret)
|
||||
{
|
||||
$this->secret = $secret;
|
||||
}
|
||||
/**
|
||||
* Sign the given serializable.
|
||||
*
|
||||
* @param string $serialized
|
||||
* @return array
|
||||
*/
|
||||
public function sign($serialized)
|
||||
{
|
||||
return ['serializable' => $serialized, 'hash' => \base64_encode(\hash_hmac('sha256', $serialized, $this->secret, \true))];
|
||||
}
|
||||
/**
|
||||
* Verify the given signature.
|
||||
*
|
||||
* @param array $signature
|
||||
* @return bool
|
||||
*/
|
||||
public function verify($signature)
|
||||
{
|
||||
return \hash_equals(\base64_encode(\hash_hmac('sha256', $signature['serializable'], $this->secret, \true)), $signature['hash']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Support;
|
||||
|
||||
use SplObjectStorage;
|
||||
class ClosureScope extends SplObjectStorage
|
||||
{
|
||||
/**
|
||||
* The number of serializations in current scope.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $serializations = 0;
|
||||
/**
|
||||
* The number of closures that have to be serialized.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $toSerialize = 0;
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Support;
|
||||
|
||||
#[\AllowDynamicProperties]
|
||||
class ClosureStream
|
||||
{
|
||||
/**
|
||||
* The stream protocol.
|
||||
*/
|
||||
const STREAM_PROTO = 'laravel-serializable-closure';
|
||||
/**
|
||||
* Checks if this stream is registered.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $isRegistered = \false;
|
||||
/**
|
||||
* The stream content.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
/**
|
||||
* The stream content.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $length;
|
||||
/**
|
||||
* The stream pointer.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $pointer = 0;
|
||||
/**
|
||||
* Opens file or URL.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $mode
|
||||
* @param string $options
|
||||
* @param string|null $opened_path
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
$this->content = "<?php\nreturn " . \substr($path, \strlen(static::STREAM_PROTO . '://')) . ';';
|
||||
$this->length = \strlen($this->content);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Read from stream.
|
||||
*
|
||||
* @param int $count
|
||||
* @return string
|
||||
*/
|
||||
public function stream_read($count)
|
||||
{
|
||||
$value = \substr($this->content, $this->pointer, $count);
|
||||
$this->pointer += $count;
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* Tests for end-of-file on a file pointer.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_eof()
|
||||
{
|
||||
return $this->pointer >= $this->length;
|
||||
}
|
||||
/**
|
||||
* Change stream options.
|
||||
*
|
||||
* @param int $option
|
||||
* @param int $arg1
|
||||
* @param int $arg2
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Retrieve information about a file resource.
|
||||
*
|
||||
* @return array|bool
|
||||
*/
|
||||
public function stream_stat()
|
||||
{
|
||||
$stat = \stat(__FILE__);
|
||||
// @phpstan-ignore-next-line
|
||||
$stat[7] = $stat['size'] = $this->length;
|
||||
return $stat;
|
||||
}
|
||||
/**
|
||||
* Retrieve information about a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int $flags
|
||||
* @return array|bool
|
||||
*/
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$stat = \stat(__FILE__);
|
||||
// @phpstan-ignore-next-line
|
||||
$stat[7] = $stat['size'] = $this->length;
|
||||
return $stat;
|
||||
}
|
||||
/**
|
||||
* Seeks to specific location in a stream.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $whence
|
||||
* @return bool
|
||||
*/
|
||||
public function stream_seek($offset, $whence = \SEEK_SET)
|
||||
{
|
||||
$crt = $this->pointer;
|
||||
switch ($whence) {
|
||||
case \SEEK_SET:
|
||||
$this->pointer = $offset;
|
||||
break;
|
||||
case \SEEK_CUR:
|
||||
$this->pointer += $offset;
|
||||
break;
|
||||
case \SEEK_END:
|
||||
$this->pointer = $this->length + $offset;
|
||||
break;
|
||||
}
|
||||
if ($this->pointer < 0 || $this->pointer >= $this->length) {
|
||||
$this->pointer = $crt;
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Retrieve the current position of a stream.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->pointer;
|
||||
}
|
||||
/**
|
||||
* Registers the stream.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
if (!static::$isRegistered) {
|
||||
static::$isRegistered = \stream_wrapper_register(static::STREAM_PROTO, __CLASS__);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure\Support;
|
||||
|
||||
class SelfReference
|
||||
{
|
||||
/**
|
||||
* The unique hash representing the object.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $hash;
|
||||
/**
|
||||
* Creates a new self reference instance.
|
||||
*
|
||||
* @param string $hash
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($hash)
|
||||
{
|
||||
$this->hash = $hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorProDeps\Laravel\SerializableClosure;
|
||||
|
||||
use Closure;
|
||||
use ElementorProDeps\Laravel\SerializableClosure\Exceptions\PhpVersionNotSupportedException;
|
||||
class UnsignedSerializableClosure
|
||||
{
|
||||
/**
|
||||
* The closure's serializable.
|
||||
*
|
||||
* @var \Laravel\SerializableClosure\Contracts\Serializable
|
||||
*/
|
||||
protected $serializable;
|
||||
/**
|
||||
* Creates a new serializable closure instance.
|
||||
*
|
||||
* @param \Closure $closure
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Closure $closure)
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
$this->serializable = new Serializers\Native($closure);
|
||||
}
|
||||
/**
|
||||
* Resolve the closure with the given arguments.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
return \call_user_func_array($this->serializable, \func_get_args());
|
||||
}
|
||||
/**
|
||||
* Gets the closure.
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
public function getClosure()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 70400) {
|
||||
throw new PhpVersionNotSupportedException();
|
||||
}
|
||||
return $this->serializable->getClosure();
|
||||
}
|
||||
/**
|
||||
* Get the serializable representation of the closure.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize()
|
||||
{
|
||||
return ['serializable' => $this->serializable];
|
||||
}
|
||||
/**
|
||||
* Restore the closure after serialization.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
public function __unserialize($data)
|
||||
{
|
||||
$this->serializable = $data['serializable'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user