From fead1203be19721303a96973b194af13f53de667 Mon Sep 17 00:00:00 2001 From: whwyy Date: Wed, 24 Jun 2026 07:26:00 +0800 Subject: [PATCH] eee --- src/Annotate/Defer.php | 31 ++++ src/Annotate/DeferProxyGenerator.php | 248 +++++++++++++++++++++++++++ src/Annotate/DeferRegistry.php | 127 ++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 src/Annotate/Defer.php create mode 100644 src/Annotate/DeferProxyGenerator.php create mode 100644 src/Annotate/DeferRegistry.php diff --git a/src/Annotate/Defer.php b/src/Annotate/Defer.php new file mode 100644 index 0000000..918193b --- /dev/null +++ b/src/Annotate/Defer.php @@ -0,0 +1,31 @@ +newInstanceArgs($construct); + } + + $reflect = new ReflectionClass($className); + return $reflect->newInstanceArgs($construct); + } + + + /** + * @param string $className + * @return string + */ + private static function generate(string $className): string + { + $reflect = new ReflectionClass($className); + $methods = DeferRegistry::getAll($className); + $stmts = []; + + foreach ($methods as $methodName => $defers) { + if (!$reflect->hasMethod($methodName)) { + continue; + } + + $method = $reflect->getMethod($methodName); + if ($method->isPrivate() || $method->isStatic() || $method->isFinal() || $method->isConstructor() || $method->isDestructor()) { + continue; + } + + $stmts[] = self::buildMethod($method); + } + + if (empty($stmts)) { + return ''; + } + + $classNode = new Stmt\Class_( + new Name($className . '__DeferProxy'), + [ + 'extends' => new Name\FullyQualified($className), + 'stmts' => $stmts, + ] + ); + + $namespace = $reflect->getNamespaceName(); + + $namespaceNode = new Stmt\Namespace_( + $namespace !== '' ? new Name($namespace) : null, + [$classNode] + ); + + $printer = new Standard(); + return $printer->prettyPrintFile([$namespaceNode]); + } + + + /** + * @param ReflectionMethod $method + * @return Stmt\ClassMethod + */ + private static function buildMethod(ReflectionMethod $method): Stmt\ClassMethod + { + $methodName = $method->getName(); + $params = []; + $args = []; + + foreach ($method->getParameters() as $param) { + $type = null; + $refType = $param->getType(); + if ($refType instanceof \ReflectionNamedType) { + $type = new Name($refType->getName()); + } + + $default = null; + if ($param->isDefaultValueAvailable()) { + $default = self::buildDefaultValue($param); + } + + $var = new Expr\Variable($param->getName()); + + $params[] = new Node\Param( + $var, + $default, + $type, + byRef: $param->isPassedByReference(), + variadic: $param->isVariadic() + ); + + $args[] = new Node\Arg( + $var, + byRef: $param->isPassedByReference(), + unpack: $param->isVariadic() + ); + } + + $returnType = null; + $refReturnType = $method->getReturnType(); + if ($refReturnType instanceof \ReflectionNamedType) { + $returnType = new Name($refReturnType->getName()); + } + + $parentCall = new Expr\StaticCall( + new Name('parent'), + $methodName, + $args + ); + + $stmts = [ + new Stmt\Expression(new Expr\Assign(new Expr\Variable('result'), $parentCall)), + new Stmt\Expression( + new Expr\StaticCall( + new Name\FullyQualified(DeferRegistry::class), + 'execute', + [ + new Node\Arg(new Expr\ClassConstFetch(new Name\FullyQualified($method->getDeclaringClass()->getName()), 'class')), + new Node\Arg(new Node\Scalar\String_($methodName)), + ] + ) + ), + new Stmt\Return_(new Expr\Variable('result')), + ]; + + return new Stmt\ClassMethod( + $methodName, + [ + 'flags' => $method->isPublic() ? Stmt\Class_::MODIFIER_PUBLIC : Stmt\Class_::MODIFIER_PROTECTED, + 'params' => $params, + 'returnType' => $returnType, + 'stmts' => $stmts, + ] + ); + } + + + /** + * @param \ReflectionParameter $param + * @return Node\Expr + */ + private static function buildDefaultValue(\ReflectionParameter $param): Node\Expr + { + if (!$param->isDefaultValueAvailable()) { + return new Expr\ConstFetch(new Name('null')); + } + + $value = $param->getDefaultValue(); + + return match (true) { + is_null($value) => new Expr\ConstFetch(new Name('null')), + is_bool($value) => new Expr\ConstFetch(new Name($value ? 'true' : 'false')), + is_int($value) => new Node\Scalar\LNumber($value), + is_float($value) => new Node\Scalar\DNumber($value), + is_string($value)=> new Node\Scalar\String_($value), + is_array($value) => new Expr\Array_( + array_map(fn($k, $v) => new Expr\ArrayItem( + self::buildDefaultValueFromScalar($v), + is_string($k) ? new Node\Scalar\String_($k) : null + ), array_keys($value), $value) + ), + default => new Expr\ConstFetch(new Name('null')), + }; + } + + + /** + * @param mixed $value + * @return Node\Expr + */ + private static function buildDefaultValueFromScalar(mixed $value): Node\Expr + { + return match (true) { + is_null($value) => new Expr\ConstFetch(new Name('null')), + is_bool($value) => new Expr\ConstFetch(new Name($value ? 'true' : 'false')), + is_int($value) => new Node\Scalar\LNumber($value), + is_float($value) => new Node\Scalar\DNumber($value), + is_string($value)=> new Node\Scalar\String_($value), + default => new Expr\ConstFetch(new Name('null')), + }; + } + + + /** + * @param string $className + * @return string|null + */ + private static function getCacheFile(string $className): ?string + { + if (self::$cacheDir === null) { + if (defined('APP_PATH')) { + self::$cacheDir = APP_PATH . 'runtime/proxies/'; + } else { + return null; + } + } + + return self::$cacheDir . str_replace('\\', '_', $className) . '__DeferProxy.php'; + } + +} diff --git a/src/Annotate/DeferRegistry.php b/src/Annotate/DeferRegistry.php new file mode 100644 index 0000000..3d6d102 --- /dev/null +++ b/src/Annotate/DeferRegistry.php @@ -0,0 +1,127 @@ + "ClassName::method" => Defer[] + */ + private static array $registry = []; + + + /** + * @param string $class + * @param string $method + * @param Defer $defer + * @return void + */ + public static function add(string $class, string $method, Defer $defer): void + { + $key = self::key($class, $method); + self::$registry[$key][] = $defer; + } + + + /** + * @param string $class + * @param string $method + * @return Defer[] + */ + public static function get(string $class, string $method): array + { + return self::$registry[self::key($class, $method)] ?? []; + } + + + /** + * @param string $class + * @return bool + */ + public static function hasAny(string $class): bool + { + $prefix = $class . '::'; + foreach (array_keys(self::$registry) as $key) { + if (str_starts_with($key, $prefix)) { + return true; + } + } + return false; + } + + + /** + * @param string $class + * @return array method => Defer[] + */ + public static function getAll(string $class): array + { + $result = []; + $prefix = $class . '::'; + foreach (self::$registry as $key => $defers) { + if (str_starts_with($key, $prefix)) { + $method = substr($key, strlen($prefix)); + $result[$method] = $defers; + } + } + return $result; + } + + + /** + * @param string $class + * @param string $method + * @return void + */ + public static function execute(string $class, string $method): void + { + $key = self::key($class, $method); + if (!isset(self::$registry[$key])) { + return; + } + + $defers = self::$registry[$key]; + unset(self::$registry[$key]); + + foreach ($defers as $defer) { + try { + $callback = $defer->callback; + $params = $defer->params; + + if (is_array($callback)) { + [$cbClass, $cbMethod] = $callback; + $instance = \Kiri::getDi()->get($cbClass); + call_user_func([$instance, $cbMethod], ...$params); + } else { + $instance = \Kiri::getDi()->get($callback); + call_user_func([$instance, '__invoke'], ...$params); + } + } catch (\Throwable $throwable) { + \Kiri::getLogger()->error('Defer callback failed: ' . $throwable->getMessage()); + } + } + } + + + /** + * @return void + */ + public static function clear(): void + { + self::$registry = []; + } + + + /** + * @param string $class + * @param string $method + * @return string + */ + private static function key(string $class, string $method): string + { + return $class . '::' . $method; + } + +}