Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/3rdparty.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCôme Chilliet <come.chilliet@nextcloud.com>2022-06-21 10:57:58 +0300
committerCôme Chilliet <come.chilliet@nextcloud.com>2022-06-21 11:02:41 +0300
commitb5f73e95a36edde3bf36bea8c72204692af1a0b5 (patch)
treea1a88e1c77181130c10c922e866b4d2d3c38b15a
parent02403021310540a848eb2385e979fcc0563ee730 (diff)
Put back opis/closure in requirements
We will need to keep both dependencies for migration purposes. We can drop opis/closure in a few releases. Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
-rw-r--r--composer.json1
-rw-r--r--composer.lock67
-rw-r--r--composer/autoload_classmap.php10
-rw-r--r--composer/autoload_files.php1
-rw-r--r--composer/autoload_psr4.php1
-rw-r--r--composer/autoload_static.php16
-rw-r--r--composer/installed.json68
-rw-r--r--composer/installed.php13
-rw-r--r--opis/closure/LICENSE20
-rw-r--r--opis/closure/NOTICE9
-rw-r--r--opis/closure/README.md92
-rw-r--r--opis/closure/autoload.php39
-rw-r--r--opis/closure/composer.json44
-rw-r--r--opis/closure/functions.php41
-rw-r--r--opis/closure/src/Analyzer.php62
-rw-r--r--opis/closure/src/ClosureContext.php34
-rw-r--r--opis/closure/src/ClosureScope.php25
-rw-r--r--opis/closure/src/ClosureStream.php99
-rw-r--r--opis/closure/src/ISecurityProvider.php25
-rw-r--r--opis/closure/src/ReflectionClosure.php1093
-rw-r--r--opis/closure/src/SecurityException.php18
-rw-r--r--opis/closure/src/SecurityProvider.php42
-rw-r--r--opis/closure/src/SelfReference.php31
-rw-r--r--opis/closure/src/SerializableClosure.php678
24 files changed, 2526 insertions, 3 deletions
diff --git a/composer.json b/composer.json
index c2fc2444..0099fa05 100644
--- a/composer.json
+++ b/composer.json
@@ -34,6 +34,7 @@
"microsoft/azure-storage-blob": "^1.5",
"nextcloud/lognormalizer": "^1.0",
"nikic/php-parser": "^4.2",
+ "opis/closure": "^3.6",
"pear/archive_tar": "^1.4.9",
"pear/pear-core-minimal": "^v1.10",
"php-ds/php-ds": "^1.3",
diff --git a/composer.lock b/composer.lock
index 7571fb70..0e643768 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "e0d2361c53dfcb8895529aef14967a7a",
+ "content-hash": "0ac4f55294fd40eceec3134a252cfc24",
"packages": [
{
"name": "aws/aws-sdk-php",
@@ -2308,6 +2308,71 @@
"time": "2021-05-03T19:11:20+00:00"
},
{
+ "name": "opis/closure",
+ "version": "3.6.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/closure.git",
+ "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
+ "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "jeremeamia/superclosure": "^2.0",
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.6.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "functions.php"
+ ],
+ "psr-4": {
+ "Opis\\Closure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
+ "homepage": "https://opis.io/closure",
+ "keywords": [
+ "anonymous functions",
+ "closure",
+ "function",
+ "serializable",
+ "serialization",
+ "serialize"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/closure/issues",
+ "source": "https://github.com/opis/closure/tree/3.6.3"
+ },
+ "time": "2022-01-27T09:35:39+00:00"
+ },
+ {
"name": "pear/archive_tar",
"version": "1.4.14",
"source": {
diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php
index 7bbafbcb..b5499d8c 100644
--- a/composer/autoload_classmap.php
+++ b/composer/autoload_classmap.php
@@ -1814,6 +1814,16 @@ return array(
'OpenStack\\ObjectStore\\v1\\Params' => $vendorDir . '/php-opencloud/openstack/src/ObjectStore/v1/Params.php',
'OpenStack\\ObjectStore\\v1\\Service' => $vendorDir . '/php-opencloud/openstack/src/ObjectStore/v1/Service.php',
'OpenStack\\OpenStack' => $vendorDir . '/php-opencloud/openstack/src/OpenStack.php',
+ 'Opis\\Closure\\Analyzer' => $vendorDir . '/opis/closure/src/Analyzer.php',
+ 'Opis\\Closure\\ClosureContext' => $vendorDir . '/opis/closure/src/ClosureContext.php',
+ 'Opis\\Closure\\ClosureScope' => $vendorDir . '/opis/closure/src/ClosureScope.php',
+ 'Opis\\Closure\\ClosureStream' => $vendorDir . '/opis/closure/src/ClosureStream.php',
+ 'Opis\\Closure\\ISecurityProvider' => $vendorDir . '/opis/closure/src/ISecurityProvider.php',
+ 'Opis\\Closure\\ReflectionClosure' => $vendorDir . '/opis/closure/src/ReflectionClosure.php',
+ 'Opis\\Closure\\SecurityException' => $vendorDir . '/opis/closure/src/SecurityException.php',
+ 'Opis\\Closure\\SecurityProvider' => $vendorDir . '/opis/closure/src/SecurityProvider.php',
+ 'Opis\\Closure\\SelfReference' => $vendorDir . '/opis/closure/src/SelfReference.php',
+ 'Opis\\Closure\\SerializableClosure' => $vendorDir . '/opis/closure/src/SerializableClosure.php',
'PEAR' => $vendorDir . '/pear/pear-core-minimal/src/PEAR.php',
'PEAR_ErrorStack' => $vendorDir . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php',
'PEAR_Exception' => $vendorDir . '/pear/pear_exception/PEAR/Exception.php',
diff --git a/composer/autoload_files.php b/composer/autoload_files.php
index 1ae107d3..02b06c86 100644
--- a/composer/autoload_files.php
+++ b/composer/autoload_files.php
@@ -119,6 +119,7 @@ return array(
'fe43ca06499ac37bc2dedd823af71eb5' => $vendorDir . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => $vendorDir . '/thecodingmachine/safe/generated/zlib.php',
'8a9dc1de0ca7e01f3e08231539562f61' => $vendorDir . '/aws/aws-sdk-php/src/functions.php',
+ '538ca81a9a966a6716601ecf48f4eaef' => $vendorDir . '/opis/closure/functions.php',
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php
index b5e49156..64242249 100644
--- a/composer/autoload_psr4.php
+++ b/composer/autoload_psr4.php
@@ -54,6 +54,7 @@ return array(
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
'PackageVersions\\' => array($vendorDir . '/composer/package-versions-deprecated/src/PackageVersions'),
+ 'Opis\\Closure\\' => array($vendorDir . '/opis/closure/src'),
'OpenStack\\' => array($vendorDir . '/php-opencloud/openstack/src'),
'Nextcloud\\LogNormalizer\\' => array($vendorDir . '/nextcloud/lognormalizer/src'),
'MicrosoftAzure\\Storage\\Common\\' => array($vendorDir . '/microsoft/azure-storage-common/src/Common'),
diff --git a/composer/autoload_static.php b/composer/autoload_static.php
index 511885fc..43ff4254 100644
--- a/composer/autoload_static.php
+++ b/composer/autoload_static.php
@@ -120,6 +120,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
'fe43ca06499ac37bc2dedd823af71eb5' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zip.php',
'356736db98a6834f0a886b8d509b0ecd' => __DIR__ . '/..' . '/thecodingmachine/safe/generated/zlib.php',
'8a9dc1de0ca7e01f3e08231539562f61' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/functions.php',
+ '538ca81a9a966a6716601ecf48f4eaef' => __DIR__ . '/..' . '/opis/closure/functions.php',
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
@@ -206,6 +207,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
),
'O' =>
array (
+ 'Opis\\Closure\\' => 13,
'OpenStack\\' => 10,
),
'N' =>
@@ -476,6 +478,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
array (
0 => __DIR__ . '/..' . '/composer/package-versions-deprecated/src/PackageVersions',
),
+ 'Opis\\Closure\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/opis/closure/src',
+ ),
'OpenStack\\' =>
array (
0 => __DIR__ . '/..' . '/php-opencloud/openstack/src',
@@ -2444,6 +2450,16 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
'OpenStack\\ObjectStore\\v1\\Params' => __DIR__ . '/..' . '/php-opencloud/openstack/src/ObjectStore/v1/Params.php',
'OpenStack\\ObjectStore\\v1\\Service' => __DIR__ . '/..' . '/php-opencloud/openstack/src/ObjectStore/v1/Service.php',
'OpenStack\\OpenStack' => __DIR__ . '/..' . '/php-opencloud/openstack/src/OpenStack.php',
+ 'Opis\\Closure\\Analyzer' => __DIR__ . '/..' . '/opis/closure/src/Analyzer.php',
+ 'Opis\\Closure\\ClosureContext' => __DIR__ . '/..' . '/opis/closure/src/ClosureContext.php',
+ 'Opis\\Closure\\ClosureScope' => __DIR__ . '/..' . '/opis/closure/src/ClosureScope.php',
+ 'Opis\\Closure\\ClosureStream' => __DIR__ . '/..' . '/opis/closure/src/ClosureStream.php',
+ 'Opis\\Closure\\ISecurityProvider' => __DIR__ . '/..' . '/opis/closure/src/ISecurityProvider.php',
+ 'Opis\\Closure\\ReflectionClosure' => __DIR__ . '/..' . '/opis/closure/src/ReflectionClosure.php',
+ 'Opis\\Closure\\SecurityException' => __DIR__ . '/..' . '/opis/closure/src/SecurityException.php',
+ 'Opis\\Closure\\SecurityProvider' => __DIR__ . '/..' . '/opis/closure/src/SecurityProvider.php',
+ 'Opis\\Closure\\SelfReference' => __DIR__ . '/..' . '/opis/closure/src/SelfReference.php',
+ 'Opis\\Closure\\SerializableClosure' => __DIR__ . '/..' . '/opis/closure/src/SerializableClosure.php',
'PEAR' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR.php',
'PEAR_ErrorStack' => __DIR__ . '/..' . '/pear/pear-core-minimal/src/PEAR/ErrorStack.php',
'PEAR_Exception' => __DIR__ . '/..' . '/pear/pear_exception/PEAR/Exception.php',
diff --git a/composer/installed.json b/composer/installed.json
index 4b0b87dc..dd513ba3 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -2404,6 +2404,74 @@
"install-path": "../nikic/php-parser"
},
{
+ "name": "opis/closure",
+ "version": "3.6.3",
+ "version_normalized": "3.6.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/opis/closure.git",
+ "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
+ "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "jeremeamia/superclosure": "^2.0",
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "time": "2022-01-27T09:35:39+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.6.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "functions.php"
+ ],
+ "psr-4": {
+ "Opis\\Closure\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
+ "homepage": "https://opis.io/closure",
+ "keywords": [
+ "anonymous functions",
+ "closure",
+ "function",
+ "serializable",
+ "serialization",
+ "serialize"
+ ],
+ "support": {
+ "issues": "https://github.com/opis/closure/issues",
+ "source": "https://github.com/opis/closure/tree/3.6.3"
+ },
+ "install-path": "../opis/closure"
+ },
+ {
"name": "pear/archive_tar",
"version": "1.4.14",
"version_normalized": "1.4.14.0",
diff --git a/composer/installed.php b/composer/installed.php
index 0e9276d4..71d8bbf2 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'nextcloud/3rdparty',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '70e442d437625996457bb36431fc14f288d4640d',
+ 'reference' => '02403021310540a848eb2385e979fcc0563ee730',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -301,7 +301,7 @@
'nextcloud/3rdparty' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => '70e442d437625996457bb36431fc14f288d4640d',
+ 'reference' => '02403021310540a848eb2385e979fcc0563ee730',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -331,6 +331,15 @@
0 => '1.11.99',
),
),
+ 'opis/closure' => array(
+ 'pretty_version' => '3.6.3',
+ 'version' => '3.6.3.0',
+ 'reference' => '3d81e4309d2a927abbe66df935f4bb60082805ad',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../opis/closure',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'pear/archive_tar' => array(
'pretty_version' => '1.4.14',
'version' => '1.4.14.0',
diff --git a/opis/closure/LICENSE b/opis/closure/LICENSE
new file mode 100644
index 00000000..9c0a19ba
--- /dev/null
+++ b/opis/closure/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2018-2019 Zindex Software
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/opis/closure/NOTICE b/opis/closure/NOTICE
new file mode 100644
index 00000000..ae5caa62
--- /dev/null
+++ b/opis/closure/NOTICE
@@ -0,0 +1,9 @@
+Opis Closure
+Copyright 2018-2019 Zindex Software
+
+This product includes software developed at
+Zindex Software (http://zindex.software).
+
+This software was originally developed by Marius Sarca and Sorin Sarca
+(Copyright 2014-2018). The copyright info was changed with the permission
+of the original authors. \ No newline at end of file
diff --git a/opis/closure/README.md b/opis/closure/README.md
new file mode 100644
index 00000000..f5f3ad8c
--- /dev/null
+++ b/opis/closure/README.md
@@ -0,0 +1,92 @@
+Opis Closure
+====================
+[![Tests](https://github.com/opis/closure/workflows/Tests/badge.svg)](https://github.com/opis/closure/actions)
+[![Latest Stable Version](https://poser.pugx.org/opis/closure/v/stable.png)](https://packagist.org/packages/opis/closure)
+[![Latest Unstable Version](https://poser.pugx.org/opis/closure/v/unstable.png)](https://packagist.org/packages/opis/closure)
+[![License](https://poser.pugx.org/opis/closure/license.png)](https://packagist.org/packages/opis/closure)
+
+Serializable closures
+---------------------
+**Opis Closure** is a library that aims to overcome PHP's limitations regarding closure
+serialization by providing a wrapper that will make all closures serializable.
+
+**The library's key features:**
+
+- Serialize any closure
+- Serialize arbitrary objects
+- Doesn't use `eval` for closure serialization or unserialization
+- Works with any PHP version that has support for closures
+- Supports PHP 7 syntax
+- Handles all variables referenced/imported in `use()` and automatically wraps all referenced/imported closures for
+proper serialization
+- Handles recursive closures
+- Handles magic constants like `__FILE__`, `__DIR__`, `__LINE__`, `__NAMESPACE__`, `__CLASS__`,
+`__TRAIT__`, `__METHOD__` and `__FUNCTION__`.
+- Automatically resolves all class names, function names and constant names used inside the closure
+- Track closure's residing source by using the `#trackme` directive
+- Simple and very fast parser
+- Any error or exception, that might occur when executing an unserialized closure, can be caught and treated properly
+- You can serialize/unserialize any closure unlimited times, even those previously unserialized
+(this is possible because `eval()` is not used for unserialization)
+- Handles static closures
+- Supports cryptographically signed closures
+- Provides a reflector that can give you information about the serialized closure
+- Provides an analyzer for *SuperClosure* library
+- Automatically detects when the scope and/or the bound object of a closure needs to be serialized
+in order for the closure to work after deserialization
+
+## Documentation
+
+The full documentation for this library can be found [here][documentation].
+
+## License
+
+**Opis Closure** is licensed under the [MIT License (MIT)][license].
+
+## Requirements
+
+* PHP ^5.4 || ^7.0 || ^8.0
+
+## Installation
+
+**Opis Closure** is available on [Packagist] and it can be installed from a
+command line interface by using [Composer].
+
+```bash
+composer require opis/closure
+```
+
+Or you could directly reference it into your `composer.json` file as a dependency
+
+```json
+{
+ "require": {
+ "opis/closure": "^3.5"
+ }
+}
+```
+
+### Migrating from 2.x
+
+If your project needs to support PHP 5.3 you can continue using the `2.x` version
+of **Opis Closure**. Otherwise, assuming you are not using one of the removed/refactored classes or features(see
+[CHANGELOG]), migrating to version `3.x` is simply a matter of updating your `composer.json` file.
+
+### Semantic versioning
+
+**Opis Closure** follows [semantic versioning][SemVer] specifications.
+
+### Arbitrary object serialization
+
+We've added this feature in order to be able to support the serialization of a closure's bound object.
+The implementation is far from being perfect, and it's really hard to make it work flawless.
+We will try to improve this, but we can't guarantee anything.
+So our advice regarding the `Opis\Closure\serialize|unserialize` functions is to use them with caution.
+
+
+[documentation]: https://www.opis.io/closure "Opis Closure"
+[license]: http://opensource.org/licenses/MIT "MIT License"
+[Packagist]: https://packagist.org/packages/opis/closure "Packagist"
+[Composer]: https://getcomposer.org "Composer"
+[SemVer]: http://semver.org/ "Semantic versioning"
+[CHANGELOG]: https://github.com/opis/closure/blob/master/CHANGELOG.md "Changelog"
diff --git a/opis/closure/autoload.php b/opis/closure/autoload.php
new file mode 100644
index 00000000..2354ea56
--- /dev/null
+++ b/opis/closure/autoload.php
@@ -0,0 +1,39 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+require_once __DIR__ . '/functions.php';
+
+spl_autoload_register(function($class){
+
+ $class = ltrim($class, '\\');
+ $dir = __DIR__ . '/src';
+ $namespace = 'Opis\Closure';
+
+ if(strpos($class, $namespace) === 0)
+ {
+ $class = substr($class, strlen($namespace));
+ $path = '';
+ if(($pos = strripos($class, '\\')) !== FALSE)
+ {
+ $path = str_replace('\\', '/', substr($class, 0, $pos)) . '/';
+ $class = substr($class, $pos + 1);
+ }
+ $path .= str_replace('_', '/', $class) . '.php';
+ $dir .= '/' . $path;
+
+ if(file_exists($dir))
+ {
+ include $dir;
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+
+});
diff --git a/opis/closure/composer.json b/opis/closure/composer.json
new file mode 100644
index 00000000..5d7d77ac
--- /dev/null
+++ b/opis/closure/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "opis/closure",
+ "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
+ "keywords": ["closure", "serialization", "function", "serializable", "serialize", "anonymous functions"],
+ "homepage": "https://opis.io/closure",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Marius Sarca",
+ "email": "marius.sarca@gmail.com"
+ },
+ {
+ "name": "Sorin Sarca",
+ "email": "sarca_sorin@hotmail.com"
+ }
+ ],
+ "require": {
+ "php": "^5.4 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "jeremeamia/superclosure": "^2.0",
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Opis\\Closure\\": "src/"
+ },
+ "files": ["functions.php"]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Opis\\Closure\\Test\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.6.x-dev"
+ }
+ },
+ "config": {
+ "preferred-install": "dist",
+ "sort-packages": true
+ }
+}
diff --git a/opis/closure/functions.php b/opis/closure/functions.php
new file mode 100644
index 00000000..38dcb91f
--- /dev/null
+++ b/opis/closure/functions.php
@@ -0,0 +1,41 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+/**
+ * Serialize
+ *
+ * @param mixed $data
+ * @return string
+ */
+function serialize($data)
+{
+ SerializableClosure::enterContext();
+ SerializableClosure::wrapClosures($data);
+ $data = \serialize($data);
+ SerializableClosure::exitContext();
+ return $data;
+}
+
+/**
+ * Unserialize
+ *
+ * @param string $data
+ * @param array|null $options
+ * @return mixed
+ */
+function unserialize($data, array $options = null)
+{
+ SerializableClosure::enterContext();
+ $data = ($options === null || \PHP_MAJOR_VERSION < 7)
+ ? \unserialize($data)
+ : \unserialize($data, $options);
+ SerializableClosure::unwrapClosures($data);
+ SerializableClosure::exitContext();
+ return $data;
+}
diff --git a/opis/closure/src/Analyzer.php b/opis/closure/src/Analyzer.php
new file mode 100644
index 00000000..9618b1c7
--- /dev/null
+++ b/opis/closure/src/Analyzer.php
@@ -0,0 +1,62 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+use Closure;
+use SuperClosure\Analyzer\ClosureAnalyzer;
+
+/**
+ * @deprecated We'll remove this class
+ */
+class Analyzer extends ClosureAnalyzer
+{
+ /**
+ * Analyzer a given closure.
+ *
+ * @param Closure $closure
+ *
+ * @return array
+ */
+ public function analyze(Closure $closure)
+ {
+ $reflection = new ReflectionClosure($closure);
+ $scope = $reflection->getClosureScopeClass();
+
+ $data = [
+ 'reflection' => $reflection,
+ 'code' => $reflection->getCode(),
+ 'hasThis' => $reflection->isBindingRequired(),
+ 'context' => $reflection->getUseVariables(),
+ 'hasRefs' => false,
+ 'binding' => $reflection->getClosureThis(),
+ 'scope' => $scope ? $scope->getName() : null,
+ 'isStatic' => $reflection->isStatic(),
+ ];
+
+ return $data;
+ }
+
+ /**
+ * @param array $data
+ * @return mixed
+ */
+ protected function determineCode(array &$data)
+ {
+ return null;
+ }
+
+ /**
+ * @param array $data
+ * @return mixed
+ */
+ protected function determineContext(array &$data)
+ {
+ return null;
+ }
+
+}
diff --git a/opis/closure/src/ClosureContext.php b/opis/closure/src/ClosureContext.php
new file mode 100644
index 00000000..d8f13d9d
--- /dev/null
+++ b/opis/closure/src/ClosureContext.php
@@ -0,0 +1,34 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+/**
+ * Closure context class
+ * @internal
+ */
+class ClosureContext
+{
+ /**
+ * @var ClosureScope Closures scope
+ */
+ public $scope;
+
+ /**
+ * @var integer
+ */
+ public $locks;
+
+ /**
+ * Constructor
+ */
+ public function __construct()
+ {
+ $this->scope = new ClosureScope();
+ $this->locks = 0;
+ }
+} \ No newline at end of file
diff --git a/opis/closure/src/ClosureScope.php b/opis/closure/src/ClosureScope.php
new file mode 100644
index 00000000..3afd9b55
--- /dev/null
+++ b/opis/closure/src/ClosureScope.php
@@ -0,0 +1,25 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+/**
+ * Closure scope class
+ * @internal
+ */
+class ClosureScope extends \SplObjectStorage
+{
+ /**
+ * @var integer Number of serializations in current scope
+ */
+ public $serializations = 0;
+
+ /**
+ * @var integer Number of closures that have to be serialized
+ */
+ public $toserialize = 0;
+} \ No newline at end of file
diff --git a/opis/closure/src/ClosureStream.php b/opis/closure/src/ClosureStream.php
new file mode 100644
index 00000000..d77d97c8
--- /dev/null
+++ b/opis/closure/src/ClosureStream.php
@@ -0,0 +1,99 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+/**
+ * @internal
+ */
+class ClosureStream
+{
+ const STREAM_PROTO = 'closure';
+
+ protected static $isRegistered = false;
+
+ protected $content;
+
+ protected $length;
+
+ protected $pointer = 0;
+
+ 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;
+ }
+
+ public function stream_read($count)
+ {
+ $value = substr($this->content, $this->pointer, $count);
+ $this->pointer += $count;
+ return $value;
+ }
+
+ public function stream_eof()
+ {
+ return $this->pointer >= $this->length;
+ }
+
+ public function stream_set_option($option, $arg1, $arg2)
+ {
+ return false;
+ }
+
+ public function stream_stat()
+ {
+ $stat = stat(__FILE__);
+ $stat[7] = $stat['size'] = $this->length;
+ return $stat;
+ }
+
+ public function url_stat($path, $flags)
+ {
+ $stat = stat(__FILE__);
+ $stat[7] = $stat['size'] = $this->length;
+ return $stat;
+ }
+
+ 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;
+ }
+
+ public function stream_tell()
+ {
+ return $this->pointer;
+ }
+
+ public static function register()
+ {
+ if (!static::$isRegistered) {
+ static::$isRegistered = stream_wrapper_register(static::STREAM_PROTO, __CLASS__);
+ }
+ }
+
+}
diff --git a/opis/closure/src/ISecurityProvider.php b/opis/closure/src/ISecurityProvider.php
new file mode 100644
index 00000000..54e2e20a
--- /dev/null
+++ b/opis/closure/src/ISecurityProvider.php
@@ -0,0 +1,25 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+interface ISecurityProvider
+{
+ /**
+ * Sign serialized closure
+ * @param string $closure
+ * @return array
+ */
+ public function sign($closure);
+
+ /**
+ * Verify signature
+ * @param array $data
+ * @return bool
+ */
+ public function verify(array $data);
+} \ No newline at end of file
diff --git a/opis/closure/src/ReflectionClosure.php b/opis/closure/src/ReflectionClosure.php
new file mode 100644
index 00000000..1f6a8322
--- /dev/null
+++ b/opis/closure/src/ReflectionClosure.php
@@ -0,0 +1,1093 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', -4);
+defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', -5);
+defined('T_FN') || define('T_FN', -6);
+
+use Closure;
+use ReflectionFunction;
+
+class ReflectionClosure extends ReflectionFunction
+{
+ protected $code;
+ protected $tokens;
+ protected $hashedName;
+ protected $useVariables;
+ protected $isStaticClosure;
+ protected $isScopeRequired;
+ protected $isBindingRequired;
+ protected $isShortClosure;
+
+ protected static $files = array();
+ protected static $classes = array();
+ protected static $functions = array();
+ protected static $constants = array();
+ protected static $structures = array();
+
+
+ /**
+ * ReflectionClosure constructor.
+ * @param Closure $closure
+ * @param string|null $code This is ignored. Do not use it
+ * @throws \ReflectionException
+ */
+ public function __construct(Closure $closure, $code = null)
+ {
+ parent::__construct($closure);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStatic()
+ {
+ if ($this->isStaticClosure === null) {
+ $this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static';
+ }
+
+ return $this->isStaticClosure;
+ }
+
+ public function isShortClosure()
+ {
+ if ($this->isShortClosure === null) {
+ $code = $this->getCode();
+ if ($this->isStatic()) {
+ $code = substr($code, 6);
+ }
+ $this->isShortClosure = strtolower(substr(trim($code), 0, 2)) === 'fn';
+ }
+
+ return $this->isShortClosure;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCode()
+ {
+ if($this->code !== null){
+ return $this->code;
+ }
+
+ $fileName = $this->getFileName();
+ $line = $this->getStartLine() - 1;
+
+ $className = null;
+
+ if (null !== $className = $this->getClosureScopeClass()) {
+ $className = '\\' . trim($className->getName(), '\\');
+ }
+
+ $builtin_types = self::getBuiltinTypes();
+ $class_keywords = ['self', 'static', 'parent'];
+
+ $ns = $this->getNamespaceName();
+ $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\' . $ns);
+
+ $_file = var_export($fileName, true);
+ $_dir = var_export(dirname($fileName), true);
+ $_namespace = var_export($ns, true);
+ $_class = var_export(trim($className, '\\'), true);
+ $_function = $ns . ($ns == '' ? '' : '\\') . '{closure}';
+ $_method = ($className == '' ? '' : trim($className, '\\') . '::') . $_function;
+ $_function = var_export($_function, true);
+ $_method = var_export($_method, true);
+ $_trait = null;
+
+ $tokens = $this->getTokens();
+ $state = $lastState = 'start';
+ $inside_structure = false;
+ $isShortClosure = false;
+ $inside_structure_mark = 0;
+ $open = 0;
+ $code = '';
+ $id_start = $id_start_ci = $id_name = $context = '';
+ $classes = $functions = $constants = null;
+ $use = array();
+ $lineAdd = 0;
+ $isUsingScope = false;
+ $isUsingThisObject = false;
+
+ for($i = 0, $l = count($tokens); $i < $l; $i++) {
+ $token = $tokens[$i];
+ switch ($state) {
+ case 'start':
+ if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) {
+ $code .= $token[1];
+ $state = $token[0] === T_FUNCTION ? 'function' : 'static';
+ } elseif ($token[0] === T_FN) {
+ $isShortClosure = true;
+ $code .= $token[1];
+ $state = 'closure_args';
+ }
+ break;
+ case 'static':
+ if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_FUNCTION) {
+ $code .= $token[1];
+ if ($token[0] === T_FUNCTION) {
+ $state = 'function';
+ }
+ } elseif ($token[0] === T_FN) {
+ $isShortClosure = true;
+ $code .= $token[1];
+ $state = 'closure_args';
+ } else {
+ $code = '';
+ $state = 'start';
+ }
+ break;
+ case 'function':
+ switch ($token[0]){
+ case T_STRING:
+ $code = '';
+ $state = 'named_function';
+ break;
+ case '(':
+ $code .= '(';
+ $state = 'closure_args';
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ }
+ break;
+ case 'named_function':
+ if($token[0] === T_FUNCTION || $token[0] === T_STATIC){
+ $code = $token[1];
+ $state = $token[0] === T_FUNCTION ? 'function' : 'static';
+ } elseif ($token[0] === T_FN) {
+ $isShortClosure = true;
+ $code .= $token[1];
+ $state = 'closure_args';
+ }
+ break;
+ case 'closure_args':
+ switch ($token[0]){
+ case T_NAME_QUALIFIED:
+ list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
+ $context = 'args';
+ $state = 'id_name';
+ $lastState = 'closure_args';
+ break;
+ case T_NS_SEPARATOR:
+ case T_STRING:
+ $id_start = $token[1];
+ $id_start_ci = strtolower($id_start);
+ $id_name = '';
+ $context = 'args';
+ $state = 'id_name';
+ $lastState = 'closure_args';
+ break;
+ case T_USE:
+ $code .= $token[1];
+ $state = 'use';
+ break;
+ case T_DOUBLE_ARROW:
+ $code .= $token[1];
+ if ($isShortClosure) {
+ $state = 'closure';
+ }
+ break;
+ case ':':
+ $code .= ':';
+ $state = 'return';
+ break;
+ case '{':
+ $code .= '{';
+ $state = 'closure';
+ $open++;
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ }
+ break;
+ case 'use':
+ switch ($token[0]){
+ case T_VARIABLE:
+ $use[] = substr($token[1], 1);
+ $code .= $token[1];
+ break;
+ case '{':
+ $code .= '{';
+ $state = 'closure';
+ $open++;
+ break;
+ case ':':
+ $code .= ':';
+ $state = 'return';
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ break;
+ }
+ break;
+ case 'return':
+ switch ($token[0]){
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $code .= $token[1];
+ break;
+ case T_NS_SEPARATOR:
+ case T_STRING:
+ $id_start = $token[1];
+ $id_start_ci = strtolower($id_start);
+ $id_name = '';
+ $context = 'return_type';
+ $state = 'id_name';
+ $lastState = 'return';
+ break 2;
+ case T_NAME_QUALIFIED:
+ list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
+ $context = 'return_type';
+ $state = 'id_name';
+ $lastState = 'return';
+ break 2;
+ case T_DOUBLE_ARROW:
+ $code .= $token[1];
+ if ($isShortClosure) {
+ $state = 'closure';
+ }
+ break;
+ case '{':
+ $code .= '{';
+ $state = 'closure';
+ $open++;
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ break;
+ }
+ break;
+ case 'closure':
+ switch ($token[0]){
+ case T_CURLY_OPEN:
+ case T_DOLLAR_OPEN_CURLY_BRACES:
+ case '{':
+ $code .= is_array($token) ? $token[1] : $token;
+ $open++;
+ break;
+ case '}':
+ $code .= '}';
+ if(--$open === 0 && !$isShortClosure){
+ break 3;
+ } elseif ($inside_structure) {
+ $inside_structure = !($open === $inside_structure_mark);
+ }
+ break;
+ case '(':
+ case '[':
+ $code .= $token[0];
+ if ($isShortClosure) {
+ $open++;
+ }
+ break;
+ case ')':
+ case ']':
+ if ($isShortClosure) {
+ if ($open === 0) {
+ break 3;
+ }
+ --$open;
+ }
+ $code .= $token[0];
+ break;
+ case ',':
+ case ';':
+ if ($isShortClosure && $open === 0) {
+ break 3;
+ }
+ $code .= $token[0];
+ break;
+ case T_LINE:
+ $code .= $token[2] - $line + $lineAdd;
+ break;
+ case T_FILE:
+ $code .= $_file;
+ break;
+ case T_DIR:
+ $code .= $_dir;
+ break;
+ case T_NS_C:
+ $code .= $_namespace;
+ break;
+ case T_CLASS_C:
+ $code .= $inside_structure ? $token[1] : $_class;
+ break;
+ case T_FUNC_C:
+ $code .= $inside_structure ? $token[1] : $_function;
+ break;
+ case T_METHOD_C:
+ $code .= $inside_structure ? $token[1] : $_method;
+ break;
+ case T_COMMENT:
+ if (substr($token[1], 0, 8) === '#trackme') {
+ $timestamp = time();
+ $code .= '/**' . PHP_EOL;
+ $code .= '* Date : ' . date(DATE_W3C, $timestamp) . PHP_EOL;
+ $code .= '* Timestamp : ' . $timestamp . PHP_EOL;
+ $code .= '* Line : ' . ($line + 1) . PHP_EOL;
+ $code .= '* File : ' . $_file . PHP_EOL . '*/' . PHP_EOL;
+ $lineAdd += 5;
+ } else {
+ $code .= $token[1];
+ }
+ break;
+ case T_VARIABLE:
+ if($token[1] == '$this' && !$inside_structure){
+ $isUsingThisObject = true;
+ }
+ $code .= $token[1];
+ break;
+ case T_STATIC:
+ case T_NS_SEPARATOR:
+ case T_STRING:
+ $id_start = $token[1];
+ $id_start_ci = strtolower($id_start);
+ $id_name = '';
+ $context = 'root';
+ $state = 'id_name';
+ $lastState = 'closure';
+ break 2;
+ case T_NAME_QUALIFIED:
+ list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
+ $context = 'root';
+ $state = 'id_name';
+ $lastState = 'closure';
+ break 2;
+ case T_NEW:
+ $code .= $token[1];
+ $context = 'new';
+ $state = 'id_start';
+ $lastState = 'closure';
+ break 2;
+ case T_USE:
+ $code .= $token[1];
+ $context = 'use';
+ $state = 'id_start';
+ $lastState = 'closure';
+ break;
+ case T_INSTANCEOF:
+ case T_INSTEADOF:
+ $code .= $token[1];
+ $context = 'instanceof';
+ $state = 'id_start';
+ $lastState = 'closure';
+ break;
+ case T_OBJECT_OPERATOR:
+ case T_DOUBLE_COLON:
+ $code .= $token[1];
+ $lastState = 'closure';
+ $state = 'ignore_next';
+ break;
+ case T_FUNCTION:
+ $code .= $token[1];
+ $state = 'closure_args';
+ if (!$inside_structure) {
+ $inside_structure = true;
+ $inside_structure_mark = $open;
+ }
+ break;
+ case T_TRAIT_C:
+ if ($_trait === null) {
+ $startLine = $this->getStartLine();
+ $endLine = $this->getEndLine();
+ $structures = $this->getStructures();
+
+ $_trait = '';
+
+ foreach ($structures as &$struct) {
+ if ($struct['type'] === 'trait' &&
+ $struct['start'] <= $startLine &&
+ $struct['end'] >= $endLine
+ ) {
+ $_trait = ($ns == '' ? '' : $ns . '\\') . $struct['name'];
+ break;
+ }
+ }
+
+ $_trait = var_export($_trait, true);
+ }
+
+ $code .= $_trait;
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ }
+ break;
+ case 'ignore_next':
+ switch ($token[0]){
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $code .= $token[1];
+ break;
+ case T_CLASS:
+ case T_NEW:
+ case T_STATIC:
+ case T_VARIABLE:
+ case T_STRING:
+ case T_CLASS_C:
+ case T_FILE:
+ case T_DIR:
+ case T_METHOD_C:
+ case T_FUNC_C:
+ case T_FUNCTION:
+ case T_INSTANCEOF:
+ case T_LINE:
+ case T_NS_C:
+ case T_TRAIT_C:
+ case T_USE:
+ $code .= $token[1];
+ $state = $lastState;
+ break;
+ default:
+ $state = $lastState;
+ $i--;
+ }
+ break;
+ case 'id_start':
+ switch ($token[0]){
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $code .= $token[1];
+ break;
+ case T_NS_SEPARATOR:
+ case T_NAME_FULLY_QUALIFIED:
+ case T_STRING:
+ case T_STATIC:
+ $id_start = $token[1];
+ $id_start_ci = strtolower($id_start);
+ $id_name = '';
+ $state = 'id_name';
+ break 2;
+ case T_NAME_QUALIFIED:
+ list($id_start, $id_start_ci, $id_name) = $this->parseNameQualified($token[1]);
+ $state = 'id_name';
+ break 2;
+ case T_VARIABLE:
+ $code .= $token[1];
+ $state = $lastState;
+ break;
+ case T_CLASS:
+ $code .= $token[1];
+ $state = 'anonymous';
+ break;
+ default:
+ $i--;//reprocess last
+ $state = 'id_name';
+ }
+ break;
+ case 'id_name':
+ switch ($token[0]){
+ case T_NAME_QUALIFIED:
+ case T_NS_SEPARATOR:
+ case T_STRING:
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ $id_name .= $token[1];
+ break;
+ case '(':
+ if ($isShortClosure) {
+ $open++;
+ }
+ if($context === 'new' || false !== strpos($id_name, '\\')){
+ if($id_start_ci === 'self' || $id_start_ci === 'static') {
+ if (!$inside_structure) {
+ $isUsingScope = true;
+ }
+ } elseif ($id_start !== '\\' && !in_array($id_start_ci, $class_keywords)) {
+ if ($classes === null) {
+ $classes = $this->getClasses();
+ }
+ if (isset($classes[$id_start_ci])) {
+ $id_start = $classes[$id_start_ci];
+ }
+ if($id_start[0] !== '\\'){
+ $id_start = $nsf . '\\' . $id_start;
+ }
+ }
+ } else {
+ if($id_start !== '\\'){
+ if($functions === null){
+ $functions = $this->getFunctions();
+ }
+ if(isset($functions[$id_start_ci])){
+ $id_start = $functions[$id_start_ci];
+ } elseif ($nsf !== '\\' && function_exists($nsf . '\\' . $id_start)) {
+ $id_start = $nsf . '\\' . $id_start;
+ // Cache it to functions array
+ $functions[$id_start_ci] = $id_start;
+ }
+ }
+ }
+ $code .= $id_start . $id_name . '(';
+ $state = $lastState;
+ break;
+ case T_VARIABLE:
+ case T_DOUBLE_COLON:
+ if($id_start !== '\\') {
+ if($id_start_ci === 'self' || $id_start_ci === 'parent'){
+ if (!$inside_structure) {
+ $isUsingScope = true;
+ }
+ } elseif ($id_start_ci === 'static') {
+ if (!$inside_structure) {
+ $isUsingScope = $token[0] === T_DOUBLE_COLON;
+ }
+ } elseif (!(\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))){
+ if ($classes === null) {
+ $classes = $this->getClasses();
+ }
+ if (isset($classes[$id_start_ci])) {
+ $id_start = $classes[$id_start_ci];
+ }
+ if($id_start[0] !== '\\'){
+ $id_start = $nsf . '\\' . $id_start;
+ }
+ }
+ }
+
+ $code .= $id_start . $id_name . $token[1];
+ $state = $token[0] === T_DOUBLE_COLON ? 'ignore_next' : $lastState;
+ break;
+ default:
+ if($id_start !== '\\' && !defined($id_start)){
+ if($constants === null){
+ $constants = $this->getConstants();
+ }
+ if(isset($constants[$id_start])){
+ $id_start = $constants[$id_start];
+ } elseif($context === 'new'){
+ if(in_array($id_start_ci, $class_keywords)) {
+ if (!$inside_structure) {
+ $isUsingScope = true;
+ }
+ } else {
+ if ($classes === null) {
+ $classes = $this->getClasses();
+ }
+ if (isset($classes[$id_start_ci])) {
+ $id_start = $classes[$id_start_ci];
+ }
+ if ($id_start[0] !== '\\') {
+ $id_start = $nsf . '\\' . $id_start;
+ }
+ }
+ } elseif($context === 'use' ||
+ $context === 'instanceof' ||
+ $context === 'args' ||
+ $context === 'return_type' ||
+ $context === 'extends' ||
+ $context === 'root'
+ ){
+ if(in_array($id_start_ci, $class_keywords)){
+ if (!$inside_structure && !$id_start_ci === 'static') {
+ $isUsingScope = true;
+ }
+ } elseif (!(\PHP_MAJOR_VERSION >= 7 && in_array($id_start_ci, $builtin_types))){
+ if($classes === null){
+ $classes = $this->getClasses();
+ }
+ if(isset($classes[$id_start_ci])){
+ $id_start = $classes[$id_start_ci];
+ }
+ if($id_start[0] !== '\\'){
+ $id_start = $nsf . '\\' . $id_start;
+ }
+ }
+ }
+ }
+ $code .= $id_start . $id_name;
+ $state = $lastState;
+ $i--;//reprocess last token
+ }
+ break;
+ case 'anonymous':
+ switch ($token[0]) {
+ case T_NS_SEPARATOR:
+ case T_STRING:
+ $id_start = $token[1];
+ $id_start_ci = strtolower($id_start);
+ $id_name = '';
+ $state = 'id_name';
+ $context = 'extends';
+ $lastState = 'anonymous';
+ break;
+ case '{':
+ $state = 'closure';
+ if (!$inside_structure) {
+ $inside_structure = true;
+ $inside_structure_mark = $open;
+ }
+ $i--;
+ break;
+ default:
+ $code .= is_array($token) ? $token[1] : $token;
+ }
+ break;
+ }
+ }
+
+ if ($isShortClosure) {
+ $this->useVariables = $this->getStaticVariables();
+ } else {
+ $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
+ }
+
+ $this->isShortClosure = $isShortClosure;
+ $this->isBindingRequired = $isUsingThisObject;
+ $this->isScopeRequired = $isUsingScope;
+ $this->code = $code;
+
+ return $this->code;
+ }
+
+ /**
+ * @return array
+ */
+ private static function getBuiltinTypes()
+ {
+ // PHP 5
+ if (\PHP_MAJOR_VERSION === 5) {
+ return ['array', 'callable'];
+ }
+
+ // PHP 8
+ if (\PHP_MAJOR_VERSION === 8) {
+ return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object', 'mixed', 'false', 'null'];
+ }
+
+ // PHP 7
+ switch (\PHP_MINOR_VERSION) {
+ case 0:
+ return ['array', 'callable', 'string', 'int', 'bool', 'float'];
+ case 1:
+ return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void'];
+ default:
+ return ['array', 'callable', 'string', 'int', 'bool', 'float', 'iterable', 'void', 'object'];
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function getUseVariables()
+ {
+ if($this->useVariables !== null){
+ return $this->useVariables;
+ }
+
+ $tokens = $this->getTokens();
+ $use = array();
+ $state = 'start';
+
+ foreach ($tokens as &$token) {
+ $is_array = is_array($token);
+
+ switch ($state) {
+ case 'start':
+ if ($is_array && $token[0] === T_USE) {
+ $state = 'use';
+ }
+ break;
+ case 'use':
+ if ($is_array) {
+ if ($token[0] === T_VARIABLE) {
+ $use[] = substr($token[1], 1);
+ }
+ } elseif ($token == ')') {
+ break 2;
+ }
+ break;
+ }
+ }
+
+ $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
+
+ return $this->useVariables;
+ }
+
+ /**
+ * return bool
+ */
+ public function isBindingRequired()
+ {
+ if($this->isBindingRequired === null){
+ $this->getCode();
+ }
+
+ return $this->isBindingRequired;
+ }
+
+ /**
+ * return bool
+ */
+ public function isScopeRequired()
+ {
+ if($this->isScopeRequired === null){
+ $this->getCode();
+ }
+
+ return $this->isScopeRequired;
+ }
+
+ /**
+ * @return string
+ */
+ protected function getHashedFileName()
+ {
+ if ($this->hashedName === null) {
+ $this->hashedName = sha1($this->getFileName());
+ }
+
+ return $this->hashedName;
+ }
+
+ /**
+ * @return array
+ */
+ protected function getFileTokens()
+ {
+ $key = $this->getHashedFileName();
+
+ if (!isset(static::$files[$key])) {
+ static::$files[$key] = token_get_all(file_get_contents($this->getFileName()));
+ }
+
+ return static::$files[$key];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getTokens()
+ {
+ if ($this->tokens === null) {
+ $tokens = $this->getFileTokens();
+ $startLine = $this->getStartLine();
+ $endLine = $this->getEndLine();
+ $results = array();
+ $start = false;
+
+ foreach ($tokens as &$token) {
+ if (!is_array($token)) {
+ if ($start) {
+ $results[] = $token;
+ }
+
+ continue;
+ }
+
+ $line = $token[2];
+
+ if ($line <= $endLine) {
+ if ($line >= $startLine) {
+ $start = true;
+ $results[] = $token;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ $this->tokens = $results;
+ }
+
+ return $this->tokens;
+ }
+
+ /**
+ * @return array
+ */
+ protected function getClasses()
+ {
+ $key = $this->getHashedFileName();
+
+ if (!isset(static::$classes[$key])) {
+ $this->fetchItems();
+ }
+
+ return static::$classes[$key];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getFunctions()
+ {
+ $key = $this->getHashedFileName();
+
+ if (!isset(static::$functions[$key])) {
+ $this->fetchItems();
+ }
+
+ return static::$functions[$key];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getConstants()
+ {
+ $key = $this->getHashedFileName();
+
+ if (!isset(static::$constants[$key])) {
+ $this->fetchItems();
+ }
+
+ return static::$constants[$key];
+ }
+
+ /**
+ * @return array
+ */
+ protected function getStructures()
+ {
+ $key = $this->getHashedFileName();
+
+ if (!isset(static::$structures[$key])) {
+ $this->fetchItems();
+ }
+
+ return static::$structures[$key];
+ }
+
+ protected function fetchItems()
+ {
+ $key = $this->getHashedFileName();
+
+ $classes = array();
+ $functions = array();
+ $constants = array();
+ $structures = array();
+ $tokens = $this->getFileTokens();
+
+ $open = 0;
+ $state = 'start';
+ $lastState = '';
+ $prefix = '';
+ $name = '';
+ $alias = '';
+ $isFunc = $isConst = false;
+
+ $startLine = $endLine = 0;
+ $structType = $structName = '';
+ $structIgnore = false;
+
+ foreach ($tokens as $token) {
+
+ switch ($state) {
+ case 'start':
+ switch ($token[0]) {
+ case T_CLASS:
+ case T_INTERFACE:
+ case T_TRAIT:
+ $state = 'before_structure';
+ $startLine = $token[2];
+ $structType = $token[0] == T_CLASS
+ ? 'class'
+ : ($token[0] == T_INTERFACE ? 'interface' : 'trait');
+ break;
+ case T_USE:
+ $state = 'use';
+ $prefix = $name = $alias = '';
+ $isFunc = $isConst = false;
+ break;
+ case T_FUNCTION:
+ $state = 'structure';
+ $structIgnore = true;
+ break;
+ case T_NEW:
+ $state = 'new';
+ break;
+ case T_OBJECT_OPERATOR:
+ case T_DOUBLE_COLON:
+ $state = 'invoke';
+ break;
+ }
+ break;
+ case 'use':
+ switch ($token[0]) {
+ case T_FUNCTION:
+ $isFunc = true;
+ break;
+ case T_CONST:
+ $isConst = true;
+ break;
+ case T_NS_SEPARATOR:
+ $name .= $token[1];
+ break;
+ case T_STRING:
+ $name .= $token[1];
+ $alias = $token[1];
+ break;
+ case T_NAME_QUALIFIED:
+ $name .= $token[1];
+ $pieces = explode('\\', $token[1]);
+ $alias = end($pieces);
+ break;
+ case T_AS:
+ $lastState = 'use';
+ $state = 'alias';
+ break;
+ case '{':
+ $prefix = $name;
+ $name = $alias = '';
+ $state = 'use-group';
+ break;
+ case ',':
+ case ';':
+ if ($name === '' || $name[0] !== '\\') {
+ $name = '\\' . $name;
+ }
+
+ if ($alias !== '') {
+ if ($isFunc) {
+ $functions[strtolower($alias)] = $name;
+ } elseif ($isConst) {
+ $constants[$alias] = $name;
+ } else {
+ $classes[strtolower($alias)] = $name;
+ }
+ }
+ $name = $alias = '';
+ $state = $token === ';' ? 'start' : 'use';
+ break;
+ }
+ break;
+ case 'use-group':
+ switch ($token[0]) {
+ case T_NS_SEPARATOR:
+ $name .= $token[1];
+ break;
+ case T_NAME_QUALIFIED:
+ $name .= $token[1];
+ $pieces = explode('\\', $token[1]);
+ $alias = end($pieces);
+ break;
+ case T_STRING:
+ $name .= $token[1];
+ $alias = $token[1];
+ break;
+ case T_AS:
+ $lastState = 'use-group';
+ $state = 'alias';
+ break;
+ case ',':
+ case '}':
+
+ if ($prefix === '' || $prefix[0] !== '\\') {
+ $prefix = '\\' . $prefix;
+ }
+
+ if ($alias !== '') {
+ if ($isFunc) {
+ $functions[strtolower($alias)] = $prefix . $name;
+ } elseif ($isConst) {
+ $constants[$alias] = $prefix . $name;
+ } else {
+ $classes[strtolower($alias)] = $prefix . $name;
+ }
+ }
+ $name = $alias = '';
+ $state = $token === '}' ? 'use' : 'use-group';
+ break;
+ }
+ break;
+ case 'alias':
+ if ($token[0] === T_STRING) {
+ $alias = $token[1];
+ $state = $lastState;
+ }
+ break;
+ case 'new':
+ switch ($token[0]) {
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ break 2;
+ case T_CLASS:
+ $state = 'structure';
+ $structIgnore = true;
+ break;
+ default:
+ $state = 'start';
+ }
+ break;
+ case 'invoke':
+ switch ($token[0]) {
+ case T_WHITESPACE:
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ break 2;
+ default:
+ $state = 'start';
+ }
+ break;
+ case 'before_structure':
+ if ($token[0] == T_STRING) {
+ $structName = $token[1];
+ $state = 'structure';
+ }
+ break;
+ case 'structure':
+ switch ($token[0]) {
+ case '{':
+ case T_CURLY_OPEN:
+ case T_DOLLAR_OPEN_CURLY_BRACES:
+ $open++;
+ break;
+ case '}':
+ if (--$open == 0) {
+ if(!$structIgnore){
+ $structures[] = array(
+ 'type' => $structType,
+ 'name' => $structName,
+ 'start' => $startLine,
+ 'end' => $endLine,
+ );
+ }
+ $structIgnore = false;
+ $state = 'start';
+ }
+ break;
+ default:
+ if (is_array($token)) {
+ $endLine = $token[2];
+ }
+ }
+ break;
+ }
+ }
+
+ static::$classes[$key] = $classes;
+ static::$functions[$key] = $functions;
+ static::$constants[$key] = $constants;
+ static::$structures[$key] = $structures;
+ }
+
+ private function parseNameQualified($token)
+ {
+ $pieces = explode('\\', $token);
+
+ $id_start = array_shift($pieces);
+
+ $id_start_ci = strtolower($id_start);
+
+ $id_name = '\\' . implode('\\', $pieces);
+
+ return [$id_start, $id_start_ci, $id_name];
+ }
+}
diff --git a/opis/closure/src/SecurityException.php b/opis/closure/src/SecurityException.php
new file mode 100644
index 00000000..b65f5cb8
--- /dev/null
+++ b/opis/closure/src/SecurityException.php
@@ -0,0 +1,18 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+use Exception;
+
+/**
+ * Security exception class
+ */
+class SecurityException extends Exception
+{
+
+} \ No newline at end of file
diff --git a/opis/closure/src/SecurityProvider.php b/opis/closure/src/SecurityProvider.php
new file mode 100644
index 00000000..19529173
--- /dev/null
+++ b/opis/closure/src/SecurityProvider.php
@@ -0,0 +1,42 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+class SecurityProvider implements ISecurityProvider
+{
+ /** @var string */
+ protected $secret;
+
+ /**
+ * SecurityProvider constructor.
+ * @param string $secret
+ */
+ public function __construct($secret)
+ {
+ $this->secret = $secret;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function sign($closure)
+ {
+ return array(
+ 'closure' => $closure,
+ 'hash' => base64_encode(hash_hmac('sha256', $closure, $this->secret, true)),
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function verify(array $data)
+ {
+ return base64_encode(hash_hmac('sha256', $data['closure'], $this->secret, true)) === $data['hash'];
+ }
+} \ No newline at end of file
diff --git a/opis/closure/src/SelfReference.php b/opis/closure/src/SelfReference.php
new file mode 100644
index 00000000..425edf27
--- /dev/null
+++ b/opis/closure/src/SelfReference.php
@@ -0,0 +1,31 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+
+/**
+ * Helper class used to indicate a reference to an object
+ * @internal
+ */
+class SelfReference
+{
+ /**
+ * @var string An unique hash representing the object
+ */
+ public $hash;
+
+ /**
+ * Constructor
+ *
+ * @param string $hash
+ */
+ public function __construct($hash)
+ {
+ $this->hash = $hash;
+ }
+} \ No newline at end of file
diff --git a/opis/closure/src/SerializableClosure.php b/opis/closure/src/SerializableClosure.php
new file mode 100644
index 00000000..1025ff51
--- /dev/null
+++ b/opis/closure/src/SerializableClosure.php
@@ -0,0 +1,678 @@
+<?php
+/* ===========================================================================
+ * Copyright (c) 2018-2021 Zindex Software
+ *
+ * Licensed under the MIT License
+ * =========================================================================== */
+
+namespace Opis\Closure;
+
+use Closure;
+use Serializable;
+use SplObjectStorage;
+use ReflectionObject;
+
+/**
+ * Provides a wrapper for serialization of closures
+ */
+class SerializableClosure implements Serializable
+{
+ /**
+ * @var Closure Wrapped closure
+ *
+ * @see \Opis\Closure\SerializableClosure::getClosure()
+ */
+ protected $closure;
+
+ /**
+ * @var ReflectionClosure A reflection instance for closure
+ *
+ * @see \Opis\Closure\SerializableClosure::getReflector()
+ */
+ protected $reflector;
+
+ /**
+ * @var mixed Used at deserialization to hold variables
+ *
+ * @see \Opis\Closure\SerializableClosure::unserialize()
+ * @see \Opis\Closure\SerializableClosure::getReflector()
+ */
+ protected $code;
+
+ /**
+ * @var string Closure's ID
+ */
+ protected $reference;
+
+ /**
+ * @var string Closure scope
+ */
+ protected $scope;
+
+ /**
+ * @var ClosureContext Context of closure, used in serialization
+ */
+ protected static $context;
+
+ /**
+ * @var ISecurityProvider|null
+ */
+ protected static $securityProvider;
+
+ /** Array recursive constant*/
+ const ARRAY_RECURSIVE_KEY = '¯\_(ツ)_/¯';
+
+ /**
+ * Constructor
+ *
+ * @param Closure $closure Closure you want to serialize
+ */
+ public function __construct(Closure $closure)
+ {
+ $this->closure = $closure;
+ if (static::$context !== null) {
+ $this->scope = static::$context->scope;
+ $this->scope->toserialize++;
+ }
+ }
+
+ /**
+ * Get the Closure object
+ *
+ * @return Closure The wrapped closure
+ */
+ public function getClosure()
+ {
+ return $this->closure;
+ }
+
+ /**
+ * Get the reflector for closure
+ *
+ * @return ReflectionClosure
+ */
+ public function getReflector()
+ {
+ if ($this->reflector === null) {
+ $this->reflector = new ReflectionClosure($this->closure);
+ $this->code = null;
+ }
+
+ return $this->reflector;
+ }
+
+ /**
+ * Implementation of magic method __invoke()
+ */
+ public function __invoke()
+ {
+ return call_user_func_array($this->closure, func_get_args());
+ }
+
+ /**
+ * Implementation of Serializable::serialize()
+ *
+ * @return string The serialized closure
+ */
+ 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;
+ }
+ } else {
+ if($scope = $reflector->getClosureScopeClass()){
+ $scope = $scope->name;
+ }
+ }
+
+ $this->reference = spl_object_hash($this->closure);
+
+ $this->scope[$this->closure] = $this;
+
+ $use = $this->transformUseVariables($reflector->getUseVariables());
+ $code = $reflector->getCode();
+
+ $this->mapByReference($use);
+
+ $ret = \serialize(array(
+ 'use' => $use,
+ 'function' => $code,
+ 'scope' => $scope,
+ 'this' => $object,
+ 'self' => $this->reference,
+ ));
+
+ if (static::$securityProvider !== null) {
+ $data = static::$securityProvider->sign($ret);
+ $ret = '@' . $data['hash'] . '.' . $data['closure'];
+ }
+
+ if (!--$this->scope->serializations && !--$this->scope->toserialize) {
+ $this->scope = null;
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Transform the use variables before serialization.
+ *
+ * @param array $data The Closure's use variables
+ * @return array
+ */
+ protected function transformUseVariables($data)
+ {
+ return $data;
+ }
+
+ /**
+ * Implementation of Serializable::unserialize()
+ *
+ * @param string $data Serialized data
+ * @throws SecurityException
+ */
+ public function unserialize($data)
+ {
+ ClosureStream::register();
+
+ if (static::$securityProvider !== null) {
+ if ($data[0] !== '@') {
+ throw new SecurityException("The serialized closure is not signed. ".
+ "Make sure you use a security provider for both serialization and unserialization.");
+ }
+
+ if ($data[1] !== '{') {
+ $separator = strpos($data, '.');
+ if ($separator === false) {
+ throw new SecurityException('Invalid signed closure');
+ }
+ $hash = substr($data, 1, $separator - 1);
+ $closure = substr($data, $separator + 1);
+
+ $data = ['hash' => $hash, 'closure' => $closure];
+
+ unset($hash, $closure);
+ } else {
+ $data = json_decode(substr($data, 1), true);
+ }
+
+ if (!is_array($data) || !static::$securityProvider->verify($data)) {
+ throw new SecurityException("Your serialized closure might have been modified and it's unsafe to be unserialized. " .
+ "Make sure you use the same security provider, with the same settings, " .
+ "both for serialization and unserialization.");
+ }
+
+ $data = $data['closure'];
+ } elseif ($data[0] === '@') {
+ if ($data[1] !== '{') {
+ $separator = strpos($data, '.');
+ if ($separator === false) {
+ throw new SecurityException('Invalid signed closure');
+ }
+ $hash = substr($data, 1, $separator - 1);
+ $closure = substr($data, $separator + 1);
+
+ $data = ['hash' => $hash, 'closure' => $closure];
+
+ unset($hash, $closure);
+ } else {
+ $data = json_decode(substr($data, 1), true);
+ }
+
+ if (!is_array($data) || !isset($data['closure']) || !isset($data['hash'])) {
+ throw new SecurityException('Invalid signed closure');
+ }
+
+ $data = $data['closure'];
+ }
+
+ $this->code = \unserialize($data);
+
+ // unset data
+ unset($data);
+
+ $this->code['objects'] = array();
+
+ if ($this->code['use']) {
+ $this->scope = new ClosureScope();
+ $this->code['use'] = $this->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'];
+ }
+
+ /**
+ * Resolve the use variables after unserialization.
+ *
+ * @param array $data The Closure's transformed use variables
+ * @return array
+ */
+ protected function resolveUseVariables($data)
+ {
+ return $data;
+ }
+
+ /**
+ * Wraps a closure and sets the serialization context (if any)
+ *
+ * @param Closure $closure Closure to be wrapped
+ *
+ * @return self The wrapped closure
+ */
+ public static function from(Closure $closure)
+ {
+ if (static::$context === null) {
+ $instance = new static($closure);
+ } elseif (isset(static::$context->scope[$closure])) {
+ $instance = static::$context->scope[$closure];
+ } else {
+ $instance = new static($closure);
+ static::$context->scope[$closure] = $instance;
+ }
+
+ return $instance;
+ }
+
+ /**
+ * Increments the context lock counter or creates a new context if none exist
+ */
+ public static function enterContext()
+ {
+ if (static::$context === null) {
+ static::$context = new ClosureContext();
+ }
+
+ static::$context->locks++;
+ }
+
+ /**
+ * Decrements the context lock counter and destroy the context when it reaches to 0
+ */
+ public static function exitContext()
+ {
+ if (static::$context !== null && !--static::$context->locks) {
+ static::$context = null;
+ }
+ }
+
+ /**
+ * @param string $secret
+ */
+ public static function setSecretKey($secret)
+ {
+ if(static::$securityProvider === null){
+ static::$securityProvider = new SecurityProvider($secret);
+ }
+ }
+
+ /**
+ * @param ISecurityProvider $securityProvider
+ */
+ public static function addSecurityProvider(ISecurityProvider $securityProvider)
+ {
+ static::$securityProvider = $securityProvider;
+ }
+
+ /**
+ * Remove security provider
+ */
+ public static function removeSecurityProvider()
+ {
+ static::$securityProvider = null;
+ }
+
+ /**
+ * @return null|ISecurityProvider
+ */
+ public static function getSecurityProvider()
+ {
+ return static::$securityProvider;
+ }
+
+ /**
+ * Wrap closures
+ *
+ * @internal
+ * @param $data
+ * @param ClosureScope|SplObjectStorage|null $storage
+ */
+ public static function wrapClosures(&$data, SplObjectStorage $storage = null)
+ {
+ if($storage === null){
+ $storage = static::$context->scope;
+ }
+
+ if($data instanceof Closure){
+ $data = static::from($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());
+ }
+ }
+
+ /**
+ * Unwrap closures
+ *
+ * @internal
+ * @param $data
+ * @param SplObjectStorage|null $storage
+ */
+ public static function unwrapClosures(&$data, SplObjectStorage $storage = null)
+ {
+ if($storage === null){
+ $storage = static::$context->scope;
+ }
+
+ if($data instanceof static){
+ $data = $data->getClosure();
+ } 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::unwrapClosures($value, $storage);
+ }
+ unset($data[self::ARRAY_RECURSIVE_KEY]);
+ }elseif ($data instanceof \stdClass){
+ if(isset($storage[$data])){
+ return;
+ }
+ $storage[$data] = true;
+ foreach ($data as &$property){
+ static::unwrapClosures($property, $storage);
+ }
+ } elseif (is_object($data) && !($data instanceof Closure)){
+ if(isset($storage[$data])){
+ return;
+ }
+ $storage[$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;
+ }
+ $value = $property->getValue($data);
+ if(is_array($value) || is_object($value)){
+ static::unwrapClosures($value, $storage);
+ $property->setValue($data, $value);
+ }
+ };
+ } while($reflection = $reflection->getParentClass());
+ }
+ }
+
+ /**
+ * Creates a new closure from arbitrary code,
+ * emulating create_function, but without using eval
+ *
+ * @param string$args
+ * @param string $code
+ * @return Closure
+ */
+ public static function createClosure($args, $code)
+ {
+ ClosureStream::register();
+ return include(ClosureStream::STREAM_PROTO . '://function(' . $args. '){' . $code . '};');
+ }
+
+ /**
+ * Internal method used to map closure pointers
+ * @internal
+ * @param $data
+ */
+ 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'][] = array(
+ '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
+ *
+ * @internal
+ * @param mixed &$data
+ */
+ 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);
+
+ if (static::$context !== null) {
+ static::$context->scope->toserialize--;
+ } else {
+ $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;
+ $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());
+ }
+ }
+
+}