Merge branch 'MDL-86460-main' of https://github.com/andimendunia/moodle
This commit is contained in:
@@ -21,11 +21,11 @@
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/support": "^10.0|^11.0|^12.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||
"nesbot/carbon": "^2.67|^3.0",
|
||||
"pestphp/pest": "^2.36|^3.0",
|
||||
"pestphp/pest": "^2.36|^3.0|^4.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"symfony/var-dumper": "^6.2.0|^7.0.0"
|
||||
"symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -43,10 +43,13 @@
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"audit": {
|
||||
"block-insecure": false
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
|
||||
@@ -1 +1,22 @@
|
||||
See instructions in lib/php-di/readme_moodle.md
|
||||
Description of Serializable Closure library import into Moodle.
|
||||
|
||||
Source: https://github.com/laravel/serializable-closure
|
||||
|
||||
This library is a dependency of php-di/php-di, used by Moodle's dependency
|
||||
injection container (core\di).
|
||||
|
||||
To update:
|
||||
- Download the desired release from https://github.com/laravel/serializable-closure/releases
|
||||
- Delete the contents of the src/ directory completely and copy in the new one
|
||||
- Copy composer.json, LICENSE.md and README.md from the zip root
|
||||
- Edit public/lib/thirdpartylibs.xml and update the version
|
||||
|
||||
To test:
|
||||
- vendor/bin/phpunit public/lib/tests/di_test.php
|
||||
|
||||
History:
|
||||
- MDL-81671: Successfully upgraded 1.3.2 -> 1.3.3 (Moodle 4.5, Paul Holden)
|
||||
- MDL-84180: Attempted 1.3.3 -> 2.0.x, closed Won't Fix -- blocked by php-di
|
||||
only supporting ^1.0 at the time. Andrew Lyons subsequently fixed
|
||||
php-di upstream (PHP-DI/PHP-DI PR #899), enabling 2.x support.
|
||||
- MDL-86460: Upgraded 2.0.3 -> 2.0.10 (Moodle 5.2)
|
||||
|
||||
@@ -255,7 +255,7 @@ class Native implements Serializable
|
||||
$instance = $data;
|
||||
$reflection = new ReflectionObject($instance);
|
||||
|
||||
if (! $reflection->isUserDefined()) {
|
||||
if (! $reflection->isUserDefined() || $reflection->hasMethod('__serialize')) {
|
||||
$storage[$instance] = $data;
|
||||
|
||||
return;
|
||||
@@ -269,12 +269,10 @@ class Native implements Serializable
|
||||
}
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
|
||||
if (! $property->isInitialized($instance)) {
|
||||
continue;
|
||||
}
|
||||
@@ -369,12 +367,10 @@ class Native implements Serializable
|
||||
}
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined()) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
|
||||
if (! $property->isInitialized($data) || $property->isReadOnly()) {
|
||||
continue;
|
||||
}
|
||||
@@ -477,7 +473,7 @@ class Native implements Serializable
|
||||
|
||||
$reflection = new ReflectionObject($data);
|
||||
|
||||
if (! $reflection->isUserDefined()) {
|
||||
if (! $reflection->isUserDefined() || $reflection->hasMethod('__serialize')) {
|
||||
$this->scope[$instance] = $data;
|
||||
|
||||
return;
|
||||
@@ -491,12 +487,10 @@ class Native implements Serializable
|
||||
}
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || $this->isVirtualProperty($property)) {
|
||||
if ($property->isStatic() || ! $property->getDeclaringClass()->isUserDefined() || static::isVirtualProperty($property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
|
||||
if (! $property->isInitialized($instance) || ($property->isReadOnly() && $property->class !== $reflection->name)) {
|
||||
continue;
|
||||
}
|
||||
@@ -519,7 +513,7 @@ class Native implements Serializable
|
||||
* @param \ReflectionProperty $property
|
||||
* @return bool
|
||||
*/
|
||||
protected function isVirtualProperty(ReflectionProperty $property): bool
|
||||
protected static function isVirtualProperty(ReflectionProperty $property): bool
|
||||
{
|
||||
return method_exists($property, 'isVirtual') && $property->isVirtual();
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@ class ReflectionClosure extends ReflectionFunction
|
||||
$isUsingScope = false;
|
||||
$isUsingThisObject = false;
|
||||
|
||||
$candidates = [];
|
||||
|
||||
for ($i = 0, $l = count($tokens); $i < $l; $i++) {
|
||||
$token = $tokens[$i];
|
||||
|
||||
@@ -296,7 +298,14 @@ class ReflectionClosure extends ReflectionFunction
|
||||
case '}':
|
||||
$code .= '}';
|
||||
if (--$open === 0 && ! $isShortClosure) {
|
||||
break 3;
|
||||
$reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope);
|
||||
$code = $reset['code'];
|
||||
$state = $reset['state'];
|
||||
$open = $reset['open'];
|
||||
$use = $reset['use'];
|
||||
$isShortClosure = $reset['isShortClosure'];
|
||||
$isUsingThisObject = $reset['isUsingThisObject'];
|
||||
$isUsingScope = $reset['isUsingScope'];
|
||||
} elseif ($inside_structure) {
|
||||
$inside_structure = ! ($open === $inside_structure_mark);
|
||||
}
|
||||
@@ -312,7 +321,15 @@ class ReflectionClosure extends ReflectionFunction
|
||||
case ']':
|
||||
if ($isShortClosure) {
|
||||
if ($open === 0) {
|
||||
break 3;
|
||||
$reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope);
|
||||
$code = $reset['code'];
|
||||
$state = $reset['state'];
|
||||
$open = $reset['open'];
|
||||
$use = $reset['use'];
|
||||
$isShortClosure = $reset['isShortClosure'];
|
||||
$isUsingThisObject = $reset['isUsingThisObject'];
|
||||
$isUsingScope = $reset['isUsingScope'];
|
||||
continue 3;
|
||||
}
|
||||
$open--;
|
||||
}
|
||||
@@ -321,7 +338,15 @@ class ReflectionClosure extends ReflectionFunction
|
||||
case ',':
|
||||
case ';':
|
||||
if ($isShortClosure && $open === 0) {
|
||||
break 3;
|
||||
$reset = $this->collectCandidate($candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope);
|
||||
$code = $reset['code'];
|
||||
$state = $reset['state'];
|
||||
$open = $reset['open'];
|
||||
$use = $reset['use'];
|
||||
$isShortClosure = $reset['isShortClosure'];
|
||||
$isUsingThisObject = $reset['isUsingThisObject'];
|
||||
$isUsingScope = $reset['isUsingScope'];
|
||||
continue 3;
|
||||
}
|
||||
$code .= $token[0];
|
||||
break;
|
||||
@@ -670,22 +695,12 @@ class ReflectionClosure extends ReflectionFunction
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
$attributesCode = array_map(function ($attribute) {
|
||||
$arguments = $attribute->getArguments();
|
||||
|
||||
$name = $attribute->getName();
|
||||
$arguments = implode(', ', array_map(function ($argument, $key) {
|
||||
$argument = sprintf("'%s'", str_replace("'", "\\'", $argument));
|
||||
$argument = var_export($argument, true);
|
||||
|
||||
if (is_string($key)) {
|
||||
$argument = sprintf('%s: %s', $key, $argument);
|
||||
@@ -697,6 +712,47 @@ class ReflectionClosure extends ReflectionFunction
|
||||
return "#[$name($arguments)]";
|
||||
}, $this->getAttributes());
|
||||
|
||||
if (count($candidates) > 1) {
|
||||
$lastItem = array_pop($candidates);
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if (! $this->verifyCandidateSignature($candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->applyCandidate($candidate);
|
||||
|
||||
$code = $candidate['code'];
|
||||
|
||||
if (! empty($attributesCode)) {
|
||||
$code = implode("\n", array_merge($attributesCode, [$code]));
|
||||
}
|
||||
|
||||
$this->code = $code;
|
||||
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
$candidates[] = $lastItem;
|
||||
}
|
||||
|
||||
$lastItem = array_pop($candidates);
|
||||
|
||||
if ($lastItem) {
|
||||
$this->applyCandidate($lastItem);
|
||||
$code = $lastItem['code'];
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
if (! empty($attributesCode)) {
|
||||
$code = implode("\n", array_merge($attributesCode, [$code]));
|
||||
}
|
||||
@@ -727,6 +783,10 @@ class ReflectionClosure extends ReflectionFunction
|
||||
return $this->useVariables;
|
||||
}
|
||||
|
||||
if ($this->isShortClosure()) {
|
||||
return $this->useVariables;
|
||||
}
|
||||
|
||||
$tokens = $this->getTokens();
|
||||
$use = [];
|
||||
$state = 'start';
|
||||
@@ -865,13 +925,18 @@ class ReflectionClosure extends ReflectionFunction
|
||||
*/
|
||||
protected function getClasses()
|
||||
{
|
||||
$key = $this->getHashedFileName();
|
||||
$line = $this->getStartLine();
|
||||
|
||||
if (! isset(static::$classes[$key])) {
|
||||
$this->fetchItems();
|
||||
foreach ($this->getStructures() as $struct) {
|
||||
if ($struct['type'] === 'namespace' &&
|
||||
$struct['start'] <= $line &&
|
||||
$struct['end'] >= $line
|
||||
) {
|
||||
return $struct['classes'];
|
||||
}
|
||||
}
|
||||
|
||||
return static::$classes[$key];
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -945,14 +1010,36 @@ class ReflectionClosure extends ReflectionFunction
|
||||
$alias = '';
|
||||
$isFunc = $isConst = false;
|
||||
|
||||
$startLine = $endLine = 0;
|
||||
$startLine = $lastKnownLine = 0;
|
||||
$structType = $structName = '';
|
||||
$structIgnore = false;
|
||||
|
||||
$namespace = '';
|
||||
$namespaceStartLine = 0;
|
||||
$namespaceBraced = false;
|
||||
$namespaceClasses = [];
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
if (is_array($token)) {
|
||||
$lastKnownLine = $token[2];
|
||||
}
|
||||
|
||||
switch ($state) {
|
||||
case 'start':
|
||||
switch ($token[0]) {
|
||||
case T_NAMESPACE:
|
||||
$structures[] = [
|
||||
'type' => 'namespace',
|
||||
'name' => $namespace,
|
||||
'start' => $namespaceStartLine,
|
||||
'end' => $token[2] - 1,
|
||||
'classes' => $namespaceClasses,
|
||||
];
|
||||
$namespace = '';
|
||||
$namespaceClasses = [];
|
||||
$state = 'namespace';
|
||||
$namespaceStartLine = $token[2];
|
||||
break;
|
||||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
case T_TRAIT:
|
||||
@@ -978,6 +1065,33 @@ class ReflectionClosure extends ReflectionFunction
|
||||
case T_DOUBLE_COLON:
|
||||
$state = 'invoke';
|
||||
break;
|
||||
case '}':
|
||||
if ($namespaceBraced) {
|
||||
$structures[] = [
|
||||
'type' => 'namespace',
|
||||
'name' => $namespace,
|
||||
'start' => $namespaceStartLine,
|
||||
'end' => $lastKnownLine,
|
||||
'classes' => $namespaceClasses,
|
||||
];
|
||||
$namespaceBraced = false;
|
||||
$namespace = '';
|
||||
$namespaceClasses = [];
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'namespace':
|
||||
switch ($token[0]) {
|
||||
case T_STRING:
|
||||
case T_NAME_QUALIFIED:
|
||||
$namespace = $token[1];
|
||||
break;
|
||||
case ';':
|
||||
case '{':
|
||||
$state = 'start';
|
||||
$namespaceBraced = $token[0] === '{';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'use':
|
||||
@@ -1022,6 +1136,7 @@ class ReflectionClosure extends ReflectionFunction
|
||||
$constants[$alias] = $name;
|
||||
} else {
|
||||
$classes[strtolower($alias)] = $name;
|
||||
$namespaceClasses[strtolower($alias)] = $name;
|
||||
}
|
||||
}
|
||||
$name = $alias = '';
|
||||
@@ -1061,6 +1176,7 @@ class ReflectionClosure extends ReflectionFunction
|
||||
$constants[$alias] = $prefix.$name;
|
||||
} else {
|
||||
$classes[strtolower($alias)] = $prefix.$name;
|
||||
$namespaceClasses[strtolower($alias)] = $prefix.$name;
|
||||
}
|
||||
}
|
||||
$name = $alias = '';
|
||||
@@ -1118,22 +1234,26 @@ class ReflectionClosure extends ReflectionFunction
|
||||
'type' => $structType,
|
||||
'name' => $structName,
|
||||
'start' => $startLine,
|
||||
'end' => $endLine,
|
||||
'end' => $lastKnownLine,
|
||||
];
|
||||
}
|
||||
$structIgnore = false;
|
||||
$state = 'start';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_array($token)) {
|
||||
$endLine = $token[2];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$structures[] = [
|
||||
'type' => 'namespace',
|
||||
'name' => $namespace,
|
||||
'start' => $namespaceStartLine,
|
||||
'end' => PHP_INT_MAX,
|
||||
'classes' => $namespaceClasses,
|
||||
];
|
||||
|
||||
static::$classes[$key] = $classes;
|
||||
static::$functions[$key] = $functions;
|
||||
static::$constants[$key] = $constants;
|
||||
@@ -1147,20 +1267,19 @@ class ReflectionClosure extends ReflectionFunction
|
||||
*/
|
||||
protected function getClosureNamespaceName()
|
||||
{
|
||||
$ns = $this->getNamespaceName();
|
||||
$startLine = $this->getStartLine();
|
||||
$endLine = $this->getEndLine();
|
||||
|
||||
$name = $this->getName();
|
||||
|
||||
// First class callables...
|
||||
if ($name !== '{closure}'
|
||||
&& ! str_contains($name, '{closure:/')
|
||||
&& ! str_contains($name, '{closure:\\')
|
||||
&& empty($ns)
|
||||
&& ! is_null($this->getClosureScopeClass())) {
|
||||
$ns = $this->getClosureScopeClass()->getNamespaceName();
|
||||
foreach ($this->getStructures() as $struct) {
|
||||
if ($struct['type'] === 'namespace' &&
|
||||
$struct['start'] <= $startLine &&
|
||||
$struct['end'] >= $endLine
|
||||
) {
|
||||
return $struct['name'];
|
||||
}
|
||||
}
|
||||
|
||||
return $ns;
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1181,4 +1300,135 @@ class ReflectionClosure extends ReflectionFunction
|
||||
|
||||
return [$id_start, $id_start_ci, $id_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a closure candidate and reset state for finding the next one.
|
||||
*
|
||||
* @param array $candidates
|
||||
* @param string $code
|
||||
* @param array $use
|
||||
* @param bool $isShortClosure
|
||||
* @param bool $isUsingThisObject
|
||||
* @param bool $isUsingScope
|
||||
* @return array
|
||||
*/
|
||||
protected function collectCandidate(&$candidates, $code, $use, $isShortClosure, $isUsingThisObject, $isUsingScope)
|
||||
{
|
||||
$candidates[] = [
|
||||
'code' => $code,
|
||||
'use' => $use,
|
||||
'isShortClosure' => $isShortClosure,
|
||||
'isUsingThisObject' => $isUsingThisObject,
|
||||
'isUsingScope' => $isUsingScope,
|
||||
];
|
||||
|
||||
return [
|
||||
'code' => '',
|
||||
'state' => 'start',
|
||||
'open' => 0,
|
||||
'use' => [],
|
||||
'isShortClosure' => false,
|
||||
'isUsingThisObject' => false,
|
||||
'isUsingScope' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a candidate's properties to this instance.
|
||||
*
|
||||
* @param array $candidate
|
||||
* @return void
|
||||
*/
|
||||
protected function applyCandidate($candidate)
|
||||
{
|
||||
if ($candidate['isShortClosure']) {
|
||||
$this->useVariables = $this->getStaticVariables();
|
||||
} else {
|
||||
$this->useVariables = empty($candidate['use'])
|
||||
? $candidate['use']
|
||||
: array_intersect_key($this->getStaticVariables(), array_flip($candidate['use']));
|
||||
}
|
||||
|
||||
$this->isShortClosure = $candidate['isShortClosure'];
|
||||
$this->isBindingRequired = $candidate['isUsingThisObject'];
|
||||
$this->isScopeRequired = $candidate['isUsingScope'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a candidate matches the closure's signature.
|
||||
*
|
||||
* @param array $candidate
|
||||
* @return bool
|
||||
*/
|
||||
protected function verifyCandidateSignature($candidate)
|
||||
{
|
||||
$code = $candidate['code'];
|
||||
$use = $candidate['use'];
|
||||
$isShortClosure = $candidate['isShortClosure'];
|
||||
|
||||
// Check if code starts with 'static' (more precise than searching anywhere in code)
|
||||
$isStaticCode = strtolower(substr(ltrim($code), 0, 6)) === 'static';
|
||||
if (parent::isStatic() !== $isStaticCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the candidate to extract parameters and variables
|
||||
$tokens = token_get_all('<?php '.$code);
|
||||
$params = [];
|
||||
$vars = [];
|
||||
$state = 'start';
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
if (! is_array($token)) {
|
||||
if ($token === '(' && $state === 'start') {
|
||||
$state = 'params';
|
||||
} elseif ($token === ')' && $state === 'params') {
|
||||
$state = 'body';
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token[0] === T_VARIABLE) {
|
||||
$name = substr($token[1], 1);
|
||||
|
||||
if ($state === 'params') {
|
||||
$params[] = $name;
|
||||
} elseif ($state === 'body' && $name !== 'this') {
|
||||
$vars[$name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify parameter count
|
||||
if (parent::getNumberOfParameters() !== count($params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify use/captured variables
|
||||
if ($isShortClosure) {
|
||||
$actualVars = array_keys(parent::getStaticVariables());
|
||||
$foundCaptures = array_diff(array_keys($vars), $params);
|
||||
|
||||
if (count($foundCaptures) !== count($actualVars)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count(array_diff($foundCaptures, $actualVars)) > 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$actualStaticVariables = array_keys(parent::getStaticVariables());
|
||||
|
||||
if (! empty($use) && count(array_diff($use, $actualStaticVariables)) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (count($use) !== count(parent::getStaticVariables())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,7 +791,7 @@ All rights reserved.</copyright>
|
||||
<library>
|
||||
<location>laravel/serializable-closure</location>
|
||||
<name>Serializable Closure</name>
|
||||
<version>2.0.3</version>
|
||||
<version>2.0.10</version>
|
||||
<license>MIT</license>
|
||||
<repository>https://github.com/laravel/serializable-closure</repository>
|
||||
</library>
|
||||
|
||||
Reference in New Issue
Block a user