From 3c48a4658d819b8f5ed8c633272307fd46468d8c Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Wed, 1 Feb 2023 21:37:45 +0100 Subject: [PATCH] Introduce fibers with proper type hints --- composer.json | 3 +- composer.lock | 80 ++++++++++++++++++++++++++++++- src/Generator/Client.php | 47 ++++++++++++++++-- src/Generator/ClientInterface.php | 31 +++++++++++- 4 files changed, 154 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 7785b33..35c0e72 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,8 @@ "react/http": "^1.8", "reactivex/rxphp": "^2.0", "api-clients/contracts": "dev-main", - "eventsauce/object-hydrator": "^1.1" + "eventsauce/object-hydrator": "^1.1", + "react/async": "^4.0" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index f4cb4f6..5b0c1d1 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": "509fa4fc49313811feb0caf000d961ff", + "content-hash": "28b5456a1aa031efa5767c996db20ca8", "packages": [ { "name": "api-clients/contracts", @@ -925,6 +925,84 @@ }, "time": "2018-10-30T17:12:04+00:00" }, + { + "name": "react/async", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/async.git", + "reference": "2aa8d89057e1059f59666e4204100636249b7be0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/async/zipball/2aa8d89057e1059f59666e4204100636249b7be0", + "reference": "2aa8d89057e1059f59666e4204100636249b7be0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "react/event-loop": "^1.2", + "react/promise": "^3.0 || ^2.8 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Async\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async utilities and fibers for ReactPHP", + "keywords": [ + "async", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/async/issues", + "source": "https://github.com/reactphp/async/tree/v4.0.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-07-11T14:21:02+00:00" + }, { "name": "react/cache", "version": "v1.2.0", diff --git a/src/Generator/Client.php b/src/Generator/Client.php index 5b142e4..0bc966d 100644 --- a/src/Generator/Client.php +++ b/src/Generator/Client.php @@ -115,6 +115,7 @@ public static function generate(string $namespace, array $clients, SchemaRegistr ) ); + $rawCallReturnTypes = []; $operationCalls = []; $callReturnTypes = []; @@ -129,10 +130,10 @@ public static function generate(string $namespace, array $clients, SchemaRegistr $fallbackName = 'Operation\\' . $operationGroup . '\\Response\\' . (new Convert(str_replace('/', '\\', $contentType) . '\\H' . $code ))->toPascal(); $object = '\\' . $namespace . 'Schema\\' . $schemaRegistry->get($contentTypeSchema->schema, $fallbackName); $callReturnTypes[] = ($contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class . '<' : '') . $object . ($contentTypeSchema->schema->type === 'array' ? '>' : ''); - $contentTypeCases[] = $returnType[] = $contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class : $object; + $rawCallReturnTypes[] = $contentTypeCases[] = $returnType[] = $contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class : $object; } if (count($contentTypeCases) === 0) { - $returnType[] = $callReturnTypes[] = 'int'; + $rawCallReturnTypes[] = $returnType[] = $callReturnTypes[] = 'int'; } } $operationCalls[] = [ @@ -175,7 +176,7 @@ public static function generate(string $namespace, array $clients, SchemaRegistr } $class->addStmt( - $factory->method('call')->makePublic()->setReturnType( + $factory->method('callAsync')->makePublic()->setReturnType( new Node\Name('\\' . PromiseInterface::class) )->setDocComment( new Doc(implode(PHP_EOL, [ @@ -340,6 +341,46 @@ public static function generate(string $namespace, array $clients, SchemaRegistr )) ); + $class->addStmt( + $factory->method('call')->makePublic()->setDocComment( + new Doc(implode(PHP_EOL, [ + '/**', + ' * @return ' . (function (array $operationCalls): string { + $count = count($operationCalls); + $lastItem = $count - 1; + $left = ''; + $right = ''; + for ($i = 0; $i < $count; $i++) { + if ($i !== $lastItem) { + $left .= '($call is ' . $operationCalls[$i]['className'] . '::OPERATION_MATCH ? ' . implode('|', array_unique($operationCalls[$i]['returnType'])) . ' : '; + } else { + $left .= implode('|', array_unique($operationCalls[$i]['returnType'])); + } + $right .= ')'; + } + return $left . $right; + })($operationCalls), + ' */', + ])) + )->setReturnType(implode('|', array_unique($rawCallReturnTypes)))->addParam((new Param('call'))->setType('string'))->addParam((new Param('params'))->setType('array')->setDefault([]))->addStmt(new Node\Stmt\Return_( + new Node\Expr\FuncCall( + new Node\Name('\React\Async\await'), + [ + new Node\Arg( + new Node\Expr\MethodCall( + new Node\Expr\Variable('this'), + new Node\Name('callAsync'), + [ + new Node\Arg(new Node\Expr\Variable('call')), + new Node\Arg(new Node\Expr\Variable('params')), + ] + ) + ), + ], + ) + )) + ); + yield new File($namespace . '\\' . 'Client', $stmt->addStmt($class)->getNode()); } } diff --git a/src/Generator/ClientInterface.php b/src/Generator/ClientInterface.php index 3528576..93a2ea5 100644 --- a/src/Generator/ClientInterface.php +++ b/src/Generator/ClientInterface.php @@ -35,6 +35,7 @@ public static function generate(string $namespace, array $clients, SchemaRegistr $stmt = $factory->namespace(rtrim($namespace, '\\')); $class = $factory->interface('ClientInterface'); + $rawCallReturnTypes = []; $operationCalls = []; $callReturnTypes = []; @@ -49,10 +50,10 @@ public static function generate(string $namespace, array $clients, SchemaRegistr $fallbackName = 'Operation\\' . $operationGroup . '\\Response\\' . (new Convert(str_replace('/', '\\', $contentType) . '\\H' . $code ))->toPascal(); $object = '\\' . $namespace . 'Schema\\' . $schemaRegistry->get($contentTypeSchema->schema, $fallbackName); $callReturnTypes[] = ($contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class . '<' : '') . $object . ($contentTypeSchema->schema->type === 'array' ? '>' : ''); - $contentTypeCases[] = $returnType[] = $contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class : $object; + $rawCallReturnTypes[] = $contentTypeCases[] = $returnType[] = $contentTypeSchema->schema->type === 'array' ? '\\' . Observable::class : $object; } if (count($contentTypeCases) === 0) { - $returnType[] = $callReturnTypes[] = 'int'; + $rawCallReturnTypes[] = $returnType[] = $callReturnTypes[] = 'int'; } } $operationCalls[] = [ @@ -74,6 +75,32 @@ public static function generate(string $namespace, array $clients, SchemaRegistr $class->addStmt( $factory->method('call')->makePublic()->setReturnType( + new Node\Name(implode('|', array_unique($rawCallReturnTypes))) + )->setDocComment( + new Doc(implode(PHP_EOL, [ + '/**', + ' * @return ' . (function (array $operationCalls): string { + $count = count($operationCalls); + $lastItem = $count - 1; + $left = ''; + $right = ''; + for ($i = 0; $i < $count; $i++) { + if ($i !== $lastItem) { + $left .= '($call is ' . $operationCalls[$i]['className'] . '::OPERATION_MATCH ? ' . implode('|', array_unique($operationCalls[$i]['returnType'])) . ' : '; + } else { + $left .= implode('|', array_unique($operationCalls[$i]['returnType'])); + } + $right .= ')'; + } + return $left . $right; + })($operationCalls), + ' */', + ])) + )->addParam((new Param('call'))->setType('string'))->addParam((new Param('params'))->setType('array')->setDefault([])) + ); + + $class->addStmt( + $factory->method('callAsync')->makePublic()->setReturnType( new Node\Name('\\' . PromiseInterface::class) )->setDocComment( new Doc(implode(PHP_EOL, [