From c26b6ee69c182469ecd253ae32426b5688321d04 Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 26 Apr 2021 22:55:24 +0200 Subject: [PATCH] Proof of Concept --- composer.json | 30 ++ composer.lock | 766 +++++++++++++++++++++++++++++++++++ src/Generator.php | 55 +++ src/Generator/Operation.php | 125 ++++++ src/Generator/Operations.php | 125 ++++++ src/Generator/Path.php | 59 +++ test.php | 6 + 7 files changed, 1166 insertions(+) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 src/Generator.php create mode 100644 src/Generator/Operation.php create mode 100644 src/Generator/Operations.php create mode 100644 src/Generator/Path.php create mode 100644 test.php diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..f52561c --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "api-clients/openapi-client-generator", + "description": "Generate a client based on an OpenAPI spec", + "license": "MIT", + "authors": [ + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + } + ], + "require": { + "php": "^7.4", + "cebe/php-openapi": "^1.4", + "jawira/case-converter": "^3.4", + "twig/twig": "^3.0", + "nikic/php-parser": "^4.8", + "psr/http-message": "^1.0", + "ringcentral/psr7": "^1.3" + }, + "autoload": { + "psr-4": { + "ApiClients\\Tools\\OpenApiClientGenerator\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "ApiClients\\Tests\\Tools\\OpenApiClientGenerator\\": "tests/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ad0c6dd --- /dev/null +++ b/composer.lock @@ -0,0 +1,766 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "e405fcadc613450f1f7de5d43cbf30c4", + "packages": [ + { + "name": "cebe/php-openapi", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/cebe/php-openapi.git", + "reference": "839772036e555e86f02b61d72fae27e281e8c557" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cebe/php-openapi/zipball/839772036e555e86f02b61d72fae27e281e8c557", + "reference": "839772036e555e86f02b61d72fae27e281e8c557", + "shasum": "" + }, + "require": { + "ext-json": "*", + "justinrainbow/json-schema": "^5.0", + "php": ">=7.1.0", + "symfony/yaml": "^3.4 | ^4.0 | ^5.0" + }, + "require-dev": { + "apis-guru/openapi-directory": "1.0.0", + "cebe/indent": "*", + "mermade/openapi3-examples": "1.0.0", + "nexmo/api-specification": "1.0.0", + "oai/openapi-specification": "3.0.3", + "phpstan/phpstan": "^0.12.0", + "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4" + }, + "bin": [ + "bin/php-openapi" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "cebe\\openapi\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "https://cebe.cc/", + "role": "Creator" + } + ], + "description": "Read and write OpenAPI yaml/json files and make the content accessable in PHP objects.", + "homepage": "https://github.com/cebe/php-openapi#readme", + "keywords": [ + "openapi" + ], + "support": { + "issues": "https://github.com/cebe/php-openapi/issues", + "source": "https://github.com/cebe/php-openapi" + }, + "time": "2021-02-16T09:31:22+00:00" + }, + { + "name": "jawira/case-converter", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/jawira/case-converter.git", + "reference": "b32f881bfe30141a82c3b2d6ee466a2f192c7776" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jawira/case-converter/zipball/b32f881bfe30141a82c3b2d6ee466a2f192c7776", + "reference": "b32f881bfe30141a82c3b2d6ee466a2f192c7776", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=7.1" + }, + "suggest": { + "pds/skeleton": "PHP Package Development Standards", + "phing/phing": "PHP Build Tool" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jawira\\CaseConverter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jawira Portugal", + "email": "dev@tugal.be", + "homepage": "http://jawira.com/" + } + ], + "description": "Convert strings between 13 naming conventions: Snake case, Camel case, Pascal case, Kebab case, Ada case, Train case, Cobol case, Macro case, Upper case, Lower case, Sentence case, Title case and Dot notation.", + "homepage": "https://jawira.github.io/case-converter/", + "keywords": [ + "Ada case", + "Cobol case", + "Macro case", + "Train case", + "camel case", + "dot notation", + "kebab case", + "lower case", + "pascal case", + "sentence case", + "snake case", + "title case", + "upper case" + ], + "support": { + "issues": "https://github.com/jawira/case-converter/issues", + "source": "https://github.com/jawira/case-converter/tree/v3.4.2" + }, + "time": "2021-01-05T08:56:22+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "5.2.10", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10" + }, + "time": "2020-05-27T16:41:55+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.10.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4" + }, + "time": "2020-12-20T10:01:03+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ringcentral/psr7", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/ringcentral/psr7.git", + "reference": "360faaec4b563958b673fb52bbe94e37f14bc686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ringcentral/psr7/zipball/360faaec4b563958b673fb52bbe94e37f14bc686", + "reference": "360faaec4b563958b673fb52bbe94e37f14bc686", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "psr/http-message": "~1.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "RingCentral\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "PSR-7 message implementation", + "keywords": [ + "http", + "message", + "stream", + "uri" + ], + "support": { + "source": "https://github.com/ringcentral/psr7/tree/master" + }, + "time": "2018-05-29T20:21:04+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-23T23:28:01+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/yaml", + "version": "v5.2.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "298a08ddda623485208506fcee08817807a251dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/298a08ddda623485208506fcee08817807a251dd", + "reference": "298a08ddda623485208506fcee08817807a251dd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<4.4" + }, + "require-dev": { + "symfony/console": "^4.4|^5.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v5.2.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-03-06T07:59:01+00:00" + }, + { + "name": "twig/twig", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", + "reference": "1f3b7e2c06cc05d42936a8ad508ff1db7975cdc5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2021-02-08T09:54:36+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.4" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/src/Generator.php b/src/Generator.php new file mode 100644 index 0000000..2c0386e --- /dev/null +++ b/src/Generator.php @@ -0,0 +1,55 @@ +spec = Reader::readFromYamlFile($specUrl); + } + + public function generate(string $namespace, string $destinationPath) + { + $codePrinter = new Standard(); + foreach ($this->spec->paths as $path => $pathItem) { + $pathClassName = str_replace(['{', '}'], ['Cb', 'Rcb'], (new Convert($path))->toPascal()) . 'Path'; + @mkdir(dirname($destinationPath . '/Path' . $pathClassName), 0777, true); + file_put_contents($destinationPath . '/Path/' . $pathClassName . '.php', $codePrinter->prettyPrintFile([ + Path::generate( + $path, + $namespace . str_replace('/', '\\', dirname('Path/' . $pathClassName)), + $namespace, + strrev(explode('/', strrev($pathClassName))[0]), + $pathItem + ), + ]) . PHP_EOL); + foreach ($pathItem->getOperations() as $method => $operation) { + $operationClassName = (new Convert($operation->operationId))->fromTrain()->toPascal() . 'Operation'; + $operations[$method] = $operationClassName; + + @mkdir(dirname($destinationPath . '/Operation/' . $operationClassName), 0777, true); + file_put_contents($destinationPath . '/Operation/' . $operationClassName . '.php', $codePrinter->prettyPrintFile([ + Operation::generate( + $path, + $method, + $namespace . str_replace('/', '\\', dirname('Operation/' . $operationClassName)), + strrev(explode('/', strrev($operationClassName))[0]), + $operation + ), + ]) . PHP_EOL); + } + } + + } +} \ No newline at end of file diff --git a/src/Generator/Operation.php b/src/Generator/Operation.php new file mode 100644 index 0000000..a5b81ec --- /dev/null +++ b/src/Generator/Operation.php @@ -0,0 +1,125 @@ +namespace($namespace); + + $class = $factory->class($className)->makeFinal()->addStmt( + new Node\Stmt\ClassConst( + [ + new Node\Const_( + 'OPERATION_ID', + new Node\Scalar\String_( + $operation->operationId + ) + ), + ], + Class_::MODIFIER_PRIVATE + ) + )->addStmt( + $factory->method('operationId')->makePublic()->setReturnType('string')->addStmt( + new Node\Stmt\Return_( + new Node\Expr\ClassConstFetch( + new Node\Name('self'), + 'OPERATION_ID' + ) + ) + ) +// )->setDocComment('/**' . var_export($operation, true) . '**/'); + ); + + $constructor = $factory->method('__construct'); + $requestReplaces = []; + $query = []; + foreach ($operation->parameters as $parameter) { + $class->addStmt( + $factory-> + property($parameter->name)-> + setDocComment('/**' . (string)$parameter->description . '**/')-> + setType(str_replace([ + 'integer', + 'any', + ], [ + 'int', + '', + ], $parameter->schema->type)) + ); + + $param = (new Param( + $parameter->name + ))->setType( + str_replace([ + 'integer', + 'any', + ], [ + 'int', + '', + ], $parameter->schema->type) + ); + if ($parameter->schema->default !== null) { + $param->setDefault($parameter->schema->default); + } + $constructor->addParam( + $param + )->addStmt( + new Node\Expr\Assign( + new Node\Expr\PropertyFetch( + new Node\Expr\Variable('this'), + $parameter->name + ), + new Node\Expr\Variable($parameter->name), + ) + ); + if ($parameter->in === 'path' || $parameter->in === 'query') { + $requestReplaces['{' . $parameter->name . '}'] = new Node\Expr\PropertyFetch( + new Node\Expr\Variable('this'), + $parameter->name + ); + } + if ($parameter->in === 'query') { + $query[] = $parameter->name . '={' . $parameter->name . '}'; + } + } + $class->addStmt($constructor); + $class->addStmt( + $factory->method('createRequest')->setReturnType('\\' . RequestInterface::class)->addStmt( + new Node\Stmt\Return_( + new Node\Expr\New_( + new Node\Name( + '\\' . Request::class + ), + [ + new Node\Arg(new Node\Scalar\String_($method)), + new Node\Arg(new Node\Expr\FuncCall( + new Node\Name('\str_replace'), + [ + new Node\Expr\Array_(array_map(static fn (string $key): Node\Expr\ArrayItem => new Node\Expr\ArrayItem(new Node\Scalar\String_($key)), array_keys($requestReplaces))), + new Node\Expr\Array_(array_values($requestReplaces)), + new Node\Scalar\String_($path . '?' . implode('&', $query)), + ] + )), + ] + ) + ) + ) + ); + $class->addStmt( + $factory->method('validateResponse') + ); + + return $stmt->addStmt($class)->getNode(); + } +} diff --git a/src/Generator/Operations.php b/src/Generator/Operations.php new file mode 100644 index 0000000..a5b81ec --- /dev/null +++ b/src/Generator/Operations.php @@ -0,0 +1,125 @@ +namespace($namespace); + + $class = $factory->class($className)->makeFinal()->addStmt( + new Node\Stmt\ClassConst( + [ + new Node\Const_( + 'OPERATION_ID', + new Node\Scalar\String_( + $operation->operationId + ) + ), + ], + Class_::MODIFIER_PRIVATE + ) + )->addStmt( + $factory->method('operationId')->makePublic()->setReturnType('string')->addStmt( + new Node\Stmt\Return_( + new Node\Expr\ClassConstFetch( + new Node\Name('self'), + 'OPERATION_ID' + ) + ) + ) +// )->setDocComment('/**' . var_export($operation, true) . '**/'); + ); + + $constructor = $factory->method('__construct'); + $requestReplaces = []; + $query = []; + foreach ($operation->parameters as $parameter) { + $class->addStmt( + $factory-> + property($parameter->name)-> + setDocComment('/**' . (string)$parameter->description . '**/')-> + setType(str_replace([ + 'integer', + 'any', + ], [ + 'int', + '', + ], $parameter->schema->type)) + ); + + $param = (new Param( + $parameter->name + ))->setType( + str_replace([ + 'integer', + 'any', + ], [ + 'int', + '', + ], $parameter->schema->type) + ); + if ($parameter->schema->default !== null) { + $param->setDefault($parameter->schema->default); + } + $constructor->addParam( + $param + )->addStmt( + new Node\Expr\Assign( + new Node\Expr\PropertyFetch( + new Node\Expr\Variable('this'), + $parameter->name + ), + new Node\Expr\Variable($parameter->name), + ) + ); + if ($parameter->in === 'path' || $parameter->in === 'query') { + $requestReplaces['{' . $parameter->name . '}'] = new Node\Expr\PropertyFetch( + new Node\Expr\Variable('this'), + $parameter->name + ); + } + if ($parameter->in === 'query') { + $query[] = $parameter->name . '={' . $parameter->name . '}'; + } + } + $class->addStmt($constructor); + $class->addStmt( + $factory->method('createRequest')->setReturnType('\\' . RequestInterface::class)->addStmt( + new Node\Stmt\Return_( + new Node\Expr\New_( + new Node\Name( + '\\' . Request::class + ), + [ + new Node\Arg(new Node\Scalar\String_($method)), + new Node\Arg(new Node\Expr\FuncCall( + new Node\Name('\str_replace'), + [ + new Node\Expr\Array_(array_map(static fn (string $key): Node\Expr\ArrayItem => new Node\Expr\ArrayItem(new Node\Scalar\String_($key)), array_keys($requestReplaces))), + new Node\Expr\Array_(array_values($requestReplaces)), + new Node\Scalar\String_($path . '?' . implode('&', $query)), + ] + )), + ] + ) + ) + ) + ); + $class->addStmt( + $factory->method('validateResponse') + ); + + return $stmt->addStmt($class)->getNode(); + } +} diff --git a/src/Generator/Path.php b/src/Generator/Path.php new file mode 100644 index 0000000..d833c30 --- /dev/null +++ b/src/Generator/Path.php @@ -0,0 +1,59 @@ +namespace($namespace); + + $class = $factory->class($className)->makeFinal(); + + foreach ($pathItem->getOperations() as $method => $operation) { + $operationClassName = str_replace('/', '\\', '\\' . $baseNamespace . 'Operation/' . (new Convert($operation->operationId))->fromTrain()->toPascal()) . 'Operation'; + $method = + $factory-> + method($method)-> + setReturnType($operationClassName) + ; + $operationConstructorArguments = []; + foreach ($operation->parameters as $parameter) { + $param = (new Param( + $parameter->name + ))->setType( + str_replace([ + 'integer', + 'any', + ], [ + 'int', + '', + ], $parameter->schema->type) + ); + if ($parameter->schema->default !== null) { + $param->setDefault($parameter->schema->default); + } + $method->addParam($param); + $operationConstructorArguments[] = new Node\Expr\Variable($parameter->name); + } + $method->addStmt(new Node\Stmt\Return_(new Node\Expr\New_( + new Node\Name($operationClassName), + $operationConstructorArguments + ))); + $class->addStmt($method); + } + + return $stmt->addStmt($class)->getNode(); + } +} diff --git a/test.php b/test.php new file mode 100644 index 0000000..536d054 --- /dev/null +++ b/test.php @@ -0,0 +1,6 @@ +generate('ApiClients\Github\\', 'generated');