Skip to content

Commit

Permalink
Added createClosure
Browse files Browse the repository at this point in the history
  • Loading branch information
sorinsarca committed Dec 27, 2024
1 parent e08bbc0 commit d3ccc67
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 27 deletions.
23 changes: 14 additions & 9 deletions src/ClosureInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(
* Function imports including namespace
* @var string
*/
private string $imports,
private string $header,

/**
* Function body
Expand Down Expand Up @@ -57,7 +57,7 @@ public function __construct(
public function key(): string
{
if (!isset($this->key)) {
$this->key = md5($this->imports . "\n" . $this->body);
$this->key = self::createKey($this->header, $this->body);
// save it to cache
self::$cache[$this->key] = $this;
}
Expand Down Expand Up @@ -120,8 +120,8 @@ public function getFactoryPHP(bool $phpTag = true): string
// we create a random param name to avoid collisions
$varName = '$___opis_closure_' . rand(1_000_000, 9_999_999) . "___";
$code = $phpTag ? '<?php' . "\n" : "";
if ($this->imports) {
$code .= $this->imports . "\n";
if ($this->header) {
$code .= $this->header . "\n";
}
$code .= "return function (?array &{$varName} = null): \\Closure {
if ({$varName}) {
Expand All @@ -138,17 +138,17 @@ public function getFactoryPHP(bool $phpTag = true): string
public function getIncludePHP(bool $phpTag = true): string
{
$code = $phpTag ? '<?php' . "\n" : "";
if ($this->imports) {
$code .= $this->imports . "\n";
if ($this->header) {
$code .= $this->header . "\n";
}
return $code . "return {$this->body};";
}

public function __serialize(): array
{
$data = ['key' => $this->key()];
if ($this->imports) {
$data['imports'] = $this->imports;
if ($this->header) {
$data['imports'] = $this->header;
}
$data['body'] = $this->body;
if ($this->use) {
Expand All @@ -163,12 +163,17 @@ public function __serialize(): array
public function __unserialize(array $data): void
{
$this->key = $data['key'] ?? null;
$this->imports = $data['imports'] ?? '';
$this->header = $data['imports'] ?? '';
$this->body = $data['body'];
$this->use = $data['use'] ?? null;
$this->flags = $data['flags'] ?? 0;
}

public static function createKey(string $body, ?string $header = null): string
{
return md5(($header ?? "") . "\n" . $body);
}

public static function flags(
bool $isShort = false,
bool $isStatic = false,
Expand Down
7 changes: 3 additions & 4 deletions src/DeserializationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,8 @@ private function v3_unboxClosure(array &$data): Closure
"scope" => null,
];

$v3_imports = "/* v3 */";
// @see ClosureInfo::key()
if (!($info = ClosureInfo::resolve(md5($v3_imports . "\n" . $data["function"])))) {
$v3_header = "/* v3 */";
if (!($info = ClosureInfo::resolve(ClosureInfo::createKey($v3_header, $data["function"])))) {
$flags = 0;

// use some heuristics for flags
Expand All @@ -341,7 +340,7 @@ private function v3_unboxClosure(array &$data): Closure
}

// create info
$info = new ClosureInfo($v3_imports, $data["function"], $data["use"] ? array_keys($data["use"]) : null, $flags);
$info = new ClosureInfo($v3_header, $data["function"], $data["use"] ? array_keys($data["use"]) : null, $flags);
}

if ($info->isStatic()) {
Expand Down
49 changes: 35 additions & 14 deletions src/Serializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Opis\Closure;

use UnitEnum;
use Closure, UnitEnum;
use Opis\Closure\Security\{
DefaultSecurityProvider,
SecurityProviderInterface,
Expand Down Expand Up @@ -63,57 +63,57 @@ public static function init(
self::$enumExists = interface_exists(UnitEnum::class, false);

// add spl serializations
self::setCustom(
self::register(
\ArrayObject::class,
[CustomSplSerialization::class, "sArrayObject"],
[CustomSplSerialization::class, "uArrayObject"],
);
self::setCustom(
self::register(
\SplDoublyLinkedList::class,
[CustomSplSerialization::class, "sDoublyLinkedList"],
[CustomSplSerialization::class, "uDoublyLinkedList"],
);
self::setCustom(
self::register(
\SplStack::class,
[CustomSplSerialization::class, "sStack"],
[CustomSplSerialization::class, "uStack"],
);
self::setCustom(
self::register(
\SplQueue::class,
[CustomSplSerialization::class, "sQueue"],
[CustomSplSerialization::class, "uQueue"],
);
self::setCustom(
self::register(
\SplPriorityQueue::class,
[CustomSplSerialization::class, "sPriorityQueue"],
[CustomSplSerialization::class, "uPriorityQueue"],
);
self::setCustom(
self::register(
\SplObjectStorage::class,
[CustomSplSerialization::class, "sObjectStorage"],
[CustomSplSerialization::class, "uObjectStorage"],
);
self::setCustom(
self::register(
\SplFixedArray::class,
[CustomSplSerialization::class, "sFixedArray"],
[CustomSplSerialization::class, "uFixedArray"],
);
self::setCustom(
self::register(
\SplMinHeap::class,
[CustomSplSerialization::class, "sHeap"],
[CustomSplSerialization::class, "uMinHeap"],
);
self::setCustom(
self::register(
\SplMaxHeap::class,
[CustomSplSerialization::class, "sHeap"],
[CustomSplSerialization::class, "uMaxHeap"],
);
self::setCustom(
self::register(
\WeakMap::class,
[CustomSplSerialization::class, "sWeakMap"],
[CustomSplSerialization::class, "uWeakMap"],
);
self::setCustom(
self::register(
\WeakReference::class,
[CustomSplSerialization::class, "sWeakReference"],
[CustomSplSerialization::class, "uWeakReference"],
Expand Down Expand Up @@ -260,15 +260,36 @@ public static function noBox(string ...$class): void
}

/**
* Use custom serialization/deserialization for a class
* Register custom serialization/deserialization for a class
*/
public static function setCustom(string $class, ?callable $serialize, ?callable $unserialize): void
public static function register(string $class, ?callable $serialize, ?callable $unserialize): void
{
$data = self::getClassInfo($class);
$data->serialize = $serialize;
$data->unserialize = $unserialize;
}

/**
* Helper method used to replace the deprecated create_function() from PHP
* @param string $args
* @param string $body
* @return Closure
*/
public static function createClosure(string $args, string $body): Closure
{
// make sure we are registered
self::$init || self::init();

$header = "/* created with " . self::class . "::" . __METHOD__ . "() */";
$body = "static function (" . $args . ") {\n" . $body . "\n}";

if (!($info = ClosureInfo::resolve(ClosureInfo::createKey($header, $body)))) {
$info = new ClosureInfo($header, $body, null, ClosureInfo::FLAG_IS_STATIC);
}

return ($info->getFactory(null))();
}

/**
* Set current security provider
*/
Expand Down

0 comments on commit d3ccc67

Please sign in to comment.