diff --git a/.github/workflows/ci-static-analysis.yaml b/.github/workflows/ci-static-analysis.yaml index 1ff990c56..cd338a584 100644 --- a/.github/workflows/ci-static-analysis.yaml +++ b/.github/workflows/ci-static-analysis.yaml @@ -26,6 +26,8 @@ jobs: - name: Install PHP uses: "shivammathur/setup-php@v2" + with: + php-version: 8.2 - name: Validate composer.json @@ -58,6 +60,8 @@ jobs: - name: Install PHP uses: "shivammathur/setup-php@v2" + with: + php-version: 8.2 - name: Install Composer Dependencies uses: "ramsey/composer-install@v2" diff --git a/.github/workflows/ci-windows.yaml b/.github/workflows/ci-windows.yaml index e35abb645..e99e6c559 100644 --- a/.github/workflows/ci-windows.yaml +++ b/.github/workflows/ci-windows.yaml @@ -13,6 +13,7 @@ env: SYMFONY_PHPUNIT_DIR: "$HOME/symfony-bridge/.phpunit" MAKER_SKIP_MERCURE_TEST: 1 MAKER_SKIP_PANTHER_TEST: 1 + MAKER_TEST_WINDOWS: 1 MAKER_DISABLE_FILE_LINKS: 1 MAKER_ALLOW_DEV_DEPS_IN_APP: 0 SYMFONY_VERSION: '7.0.x-dev' diff --git a/.symfony.bundle.yaml b/.symfony.bundle.yaml index a9c304aee..5c0f1017a 100644 --- a/.symfony.bundle.yaml +++ b/.symfony.bundle.yaml @@ -2,5 +2,5 @@ branches: ["main"] maintained_branches: ["main"] -doc_dir: "src/Resources/doc" +doc_dir: "docs" diff --git a/_docs_build/.gitignore b/_docs_build/.gitignore new file mode 100644 index 000000000..2cf7a3ffd --- /dev/null +++ b/_docs_build/.gitignore @@ -0,0 +1 @@ +composer.lock \ No newline at end of file diff --git a/_docs_build/build.php b/_docs_build/build.php index 20bec5ad2..94130918a 100755 --- a/_docs_build/build.php +++ b/_docs_build/build.php @@ -30,7 +30,7 @@ $outputDir = __DIR__.'/output'; $buildConfig = (new BuildConfig()) ->setSymfonyVersion('7.1') - ->setContentDir(__DIR__.'/../src/Resources/doc') + ->setContentDir(__DIR__.'/../docs') ->setOutputDir($outputDir) ->setImagesDir(__DIR__.'/output/_images') ->setImagesPublicPrefix('_images') diff --git a/_docs_build/composer.lock b/_docs_build/composer.lock deleted file mode 100644 index 5ee65995c..000000000 --- a/_docs_build/composer.lock +++ /dev/null @@ -1,1867 +0,0 @@ -{ - "_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": "54d13426e2db0a0cca88612376b5c9e0", - "packages": [ - { - "name": "doctrine/event-manager", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", - "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.28" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "time": "2022-10-12T20:59:15+00:00" - }, - { - "name": "doctrine/rst-parser", - "version": "0.5.6", - "source": { - "type": "git", - "url": "https://github.com/doctrine/rst-parser.git", - "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104", - "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104", - "shasum": "" - }, - "require": { - "doctrine/event-manager": "^1.0 || ^2.0", - "php": "^7.2 || ^8.0", - "symfony/filesystem": "^4.1 || ^5.0 || ^6.0 || ^7.0", - "symfony/finder": "^4.1 || ^5.0 || ^6.0 || ^7.0", - "symfony/polyfill-mbstring": "^1.0", - "symfony/string": "^5.3 || ^6.0 || ^7.0", - "symfony/translation-contracts": "^1.1 || ^2.0 || ^3.0", - "twig/twig": "^2.9 || ^3.3" - }, - "require-dev": { - "doctrine/coding-standard": "^11.0", - "gajus/dindent": "^2.0.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-phpunit": "^1.2", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", - "symfony/css-selector": "4.4 || ^5.2 || ^6.0 || ^7.0", - "symfony/dom-crawler": "4.4 || ^5.2 || ^6.0 || ^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\RST\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Grégoire Passault", - "email": "g.passault@gmail.com", - "homepage": "http://www.gregwar.com/" - }, - { - "name": "Jonathan H. Wage", - "email": "jonwage@gmail.com", - "homepage": "https://jwage.com" - } - ], - "description": "PHP library to parse reStructuredText documents and generate HTML or LaTeX documents.", - "homepage": "https://github.com/doctrine/rst-parser", - "keywords": [ - "html", - "latex", - "markup", - "parser", - "reStructuredText", - "rst" - ], - "support": { - "issues": "https://github.com/doctrine/rst-parser/issues", - "source": "https://github.com/doctrine/rst-parser/tree/0.5.6" - }, - "time": "2024-01-14T11:02:23+00:00" - }, - { - "name": "masterminds/html5", - "version": "2.9.0", - "source": { - "type": "git", - "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Masterminds\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matt Butcher", - "email": "technosophos@gmail.com" - }, - { - "name": "Matt Farina", - "email": "matt@mattfarina.com" - }, - { - "name": "Asmir Mustafic", - "email": "goetas@gmail.com" - } - ], - "description": "An HTML5 parser and serializer.", - "homepage": "http://masterminds.github.io/html5-php", - "keywords": [ - "HTML5", - "dom", - "html", - "parser", - "querypath", - "serializer", - "xml" - ], - "support": { - "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" - }, - "time": "2024-03-31T07:05:07+00:00" - }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, - { - "name": "scrivo/highlight.php", - "version": "v9.18.1.10", - "source": { - "type": "git", - "url": "https://github.com/scrivo/highlight.php.git", - "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e", - "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": ">=5.4" - }, - "require-dev": { - "phpunit/phpunit": "^4.8|^5.7", - "sabberworm/php-css-parser": "^8.3", - "symfony/finder": "^2.8|^3.4|^5.4", - "symfony/var-dumper": "^2.8|^3.4|^5.4" - }, - "suggest": { - "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords" - }, - "type": "library", - "autoload": { - "files": [ - "HighlightUtilities/functions.php" - ], - "psr-0": { - "Highlight\\": "", - "HighlightUtilities\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Geert Bergman", - "homepage": "http://www.scrivo.org/", - "role": "Project Author" - }, - { - "name": "Vladimir Jimenez", - "homepage": "https://allejo.io", - "role": "Maintainer" - }, - { - "name": "Martin Folkers", - "homepage": "https://twobrain.io", - "role": "Contributor" - } - ], - "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js", - "keywords": [ - "code", - "highlight", - "highlight.js", - "highlight.php", - "syntax" - ], - "support": { - "issues": "https://github.com/scrivo/highlight.php/issues", - "source": "https://github.com/scrivo/highlight.php" - }, - "funding": [ - { - "url": "https://github.com/allejo", - "type": "github" - } - ], - "time": "2022-12-17T21:53:22+00:00" - }, - { - "name": "symfony-tools/docs-builder", - "version": "v0.23.0", - "source": { - "type": "git", - "url": "https://github.com/symfony-tools/docs-builder.git", - "reference": "218bfe8bef908cea3934851fd294ffa0d48db059" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/218bfe8bef908cea3934851fd294ffa0d48db059", - "reference": "218bfe8bef908cea3934851fd294ffa0d48db059", - "shasum": "" - }, - "require": { - "doctrine/rst-parser": "^0.5", - "ext-curl": "*", - "ext-json": "*", - "php": ">=7.4", - "scrivo/highlight.php": "^9.18.1", - "symfony/console": "^5.2 || ^6.0 || ^7.0", - "symfony/css-selector": "^5.2 || ^6.0 || ^7.0", - "symfony/dom-crawler": "^5.2 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.2 || ^6.0 || ^7.0", - "symfony/finder": "^5.2 || ^6.0 || ^7.0", - "symfony/http-client": "^5.2 || ^6.0 || ^7.0", - "twig/twig": "^2.14 || ^3.3" - }, - "require-dev": { - "gajus/dindent": "^2.0", - "masterminds/html5": "^2.7", - "symfony/phpunit-bridge": "^5.2 || ^6.0 || ^7.0", - "symfony/process": "^5.2 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/docs-builder" - ], - "type": "project", - "autoload": { - "psr-4": { - "SymfonyDocsBuilder\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The build system for Symfony's documentation", - "support": { - "issues": "https://github.com/symfony-tools/docs-builder/issues", - "source": "https://github.com/symfony-tools/docs-builder/tree/v0.23.0" - }, - "time": "2023-12-26T14:50:41+00:00" - }, - { - "name": "symfony/console", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", - "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "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": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/css-selector", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/css-selector.git", - "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c5d5c2103c3762aff27a27e1e2409e30a79083b", - "reference": "1c5d5c2103c3762aff27a27e1e2409e30a79083b", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\CssSelector\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Converts CSS selectors to XPath expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-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/v3.5.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": "2024-04-18T09:32:20+00:00" - }, - { - "name": "symfony/dom-crawler", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/dom-crawler.git", - "reference": "2088c5da700b1e7a8689fffc10dda6c1f643deea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2088c5da700b1e7a8689fffc10dda6c1f643deea", - "reference": "2088c5da700b1e7a8689fffc10dda6c1f643deea", - "shasum": "" - }, - "require": { - "masterminds/html5": "^2.6", - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "symfony/css-selector": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\DomCrawler\\": "" - }, - "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": "Eases DOM navigation for HTML and XML documents", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/filesystem", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/78dde75f8f6dbbca4ec436a4b0087f7af02076d4", - "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/process": "^5.4|^6.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "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": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/finder", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "511c48990be17358c23bf45c5d71ab85d40fb764" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764", - "reference": "511c48990be17358c23bf45c5d71ab85d40fb764", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "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": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.7" - }, - "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": "2024-04-23T10:36:43+00:00" - }, - { - "name": "symfony/http-client", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client.git", - "reference": "3683d8107cf1efdd24795cc5f7482be1eded34ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/3683d8107cf1efdd24795cc5f7482be1eded34ac", - "reference": "3683d8107cf1efdd24795cc5f7482be1eded34ac", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "^3.4.1", - "symfony/service-contracts": "^2.5|^3" - }, - "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" - }, - "provide": { - "php-http/async-client-implementation": "*", - "php-http/client-implementation": "*", - "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "3.0" - }, - "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", - "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4|^2.0", - "nyholm/psr7": "^1.0", - "php-http/httplug": "^1.0|^2.0", - "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", - "homepage": "https://symfony.com", - "keywords": [ - "http" - ], - "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/http-client-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "20414d96f391677bf80078aa55baece78b82647d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", - "reference": "20414d96f391677bf80078aa55baece78b82647d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\HttpClient\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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": "Generic abstractions related to HTTP clients", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "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.29.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": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "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 intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.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": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "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 intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.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": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "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.29.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": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.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": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/process", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "cdb1c81c145fd5aa9b0038bab694035020943381" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381", - "reference": "cdb1c81c145fd5aa9b0038bab694035020943381", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "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": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" - }, - { - "name": "symfony/string", - "version": "v6.4.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ffeb9591c61f65a68d47f77d12b83fa530227a69", - "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "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": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v6.4.7" - }, - "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": "2024-04-18T09:22:46+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "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": "Generic abstractions related to translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.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": "2024-04-18T09:32:20+00:00" - }, - { - "name": "twig/twig", - "version": "v3.10.3", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", - "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", - "shasum": "" - }, - "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" - }, - "require-dev": { - "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/Resources/core.php", - "src/Resources/debug.php", - "src/Resources/escaper.php", - "src/Resources/string_loader.php" - ], - "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.10.3" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/twig/twig", - "type": "tidelift" - } - ], - "time": "2024-05-16T10:04:27+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "dev", - "stability-flags": [], - "prefer-stable": true, - "prefer-lowest": false, - "platform": { - "php": ">=8.1" - }, - "platform-dev": [], - "platform-overrides": { - "php": "8.1.0" - }, - "plugin-api-version": "2.6.0" -} diff --git a/composer.json b/composer.json index 3b9010d17..df1c451ba 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "php": ">=8.1", "doctrine/inflector": "^2.0", "nikic/php-parser": "^4.18|^5.0", + "php-cs-fixer/shim": "^v3.64", "symfony/config": "^6.4|^7.0", "symfony/console": "^6.4|^7.0", "symfony/dependency-injection": "^6.4|^7.0", diff --git a/src/Resources/help/MakeAuth.txt b/config/help/MakeAuth.txt similarity index 100% rename from src/Resources/help/MakeAuth.txt rename to config/help/MakeAuth.txt diff --git a/src/Resources/help/MakeCommand.txt b/config/help/MakeCommand.txt similarity index 100% rename from src/Resources/help/MakeCommand.txt rename to config/help/MakeCommand.txt diff --git a/src/Resources/help/MakeController.txt b/config/help/MakeController.txt similarity index 100% rename from src/Resources/help/MakeController.txt rename to config/help/MakeController.txt diff --git a/src/Resources/help/MakeCrud.txt b/config/help/MakeCrud.txt similarity index 100% rename from src/Resources/help/MakeCrud.txt rename to config/help/MakeCrud.txt diff --git a/src/Resources/help/MakeDockerDatabase.txt b/config/help/MakeDockerDatabase.txt similarity index 97% rename from src/Resources/help/MakeDockerDatabase.txt rename to config/help/MakeDockerDatabase.txt index b4eaccc3f..426f33943 100644 --- a/src/Resources/help/MakeDockerDatabase.txt +++ b/config/help/MakeDockerDatabase.txt @@ -1,5 +1,5 @@ -The %command.name% command generates or updates databases services in compose.yaml - -php %command.full_name% - -Supports MySQL, MariaDB and PostgreSQL +The %command.name% command generates or updates databases services in compose.yaml + +php %command.full_name% + +Supports MySQL, MariaDB and PostgreSQL diff --git a/src/Resources/help/MakeEntity.txt b/config/help/MakeEntity.txt similarity index 100% rename from src/Resources/help/MakeEntity.txt rename to config/help/MakeEntity.txt diff --git a/src/Resources/help/MakeFixture.txt b/config/help/MakeFixture.txt similarity index 100% rename from src/Resources/help/MakeFixture.txt rename to config/help/MakeFixture.txt diff --git a/src/Resources/help/MakeForm.txt b/config/help/MakeForm.txt similarity index 100% rename from src/Resources/help/MakeForm.txt rename to config/help/MakeForm.txt diff --git a/src/Resources/help/MakeFunctionalTest.txt b/config/help/MakeFunctionalTest.txt similarity index 100% rename from src/Resources/help/MakeFunctionalTest.txt rename to config/help/MakeFunctionalTest.txt diff --git a/src/Resources/help/MakeListener.txt b/config/help/MakeListener.txt similarity index 100% rename from src/Resources/help/MakeListener.txt rename to config/help/MakeListener.txt diff --git a/src/Resources/help/MakeMessage.txt b/config/help/MakeMessage.txt similarity index 100% rename from src/Resources/help/MakeMessage.txt rename to config/help/MakeMessage.txt diff --git a/src/Resources/help/MakeMiddleware.txt b/config/help/MakeMiddleware.txt similarity index 100% rename from src/Resources/help/MakeMiddleware.txt rename to config/help/MakeMiddleware.txt diff --git a/src/Resources/help/MakeMigration.txt b/config/help/MakeMigration.txt similarity index 100% rename from src/Resources/help/MakeMigration.txt rename to config/help/MakeMigration.txt diff --git a/src/Resources/help/MakeRegistrationForm.txt b/config/help/MakeRegistrationForm.txt similarity index 100% rename from src/Resources/help/MakeRegistrationForm.txt rename to config/help/MakeRegistrationForm.txt diff --git a/src/Resources/help/MakeResetPassword.txt b/config/help/MakeResetPassword.txt similarity index 100% rename from src/Resources/help/MakeResetPassword.txt rename to config/help/MakeResetPassword.txt diff --git a/src/Resources/help/MakeScheduler.txt b/config/help/MakeScheduler.txt similarity index 100% rename from src/Resources/help/MakeScheduler.txt rename to config/help/MakeScheduler.txt diff --git a/src/Resources/help/MakeSerializerEncoder.txt b/config/help/MakeSerializerEncoder.txt similarity index 100% rename from src/Resources/help/MakeSerializerEncoder.txt rename to config/help/MakeSerializerEncoder.txt diff --git a/src/Resources/help/MakeSerializerNormalizer.txt b/config/help/MakeSerializerNormalizer.txt similarity index 100% rename from src/Resources/help/MakeSerializerNormalizer.txt rename to config/help/MakeSerializerNormalizer.txt diff --git a/src/Resources/help/MakeStimulusController.txt b/config/help/MakeStimulusController.txt similarity index 100% rename from src/Resources/help/MakeStimulusController.txt rename to config/help/MakeStimulusController.txt diff --git a/src/Resources/help/MakeSubscriber.txt b/config/help/MakeSubscriber.txt similarity index 100% rename from src/Resources/help/MakeSubscriber.txt rename to config/help/MakeSubscriber.txt diff --git a/src/Resources/help/MakeTest.txt b/config/help/MakeTest.txt similarity index 100% rename from src/Resources/help/MakeTest.txt rename to config/help/MakeTest.txt diff --git a/src/Resources/help/MakeTwigExtension.txt b/config/help/MakeTwigExtension.txt similarity index 100% rename from src/Resources/help/MakeTwigExtension.txt rename to config/help/MakeTwigExtension.txt diff --git a/src/Resources/help/MakeUnitTest.txt b/config/help/MakeUnitTest.txt similarity index 100% rename from src/Resources/help/MakeUnitTest.txt rename to config/help/MakeUnitTest.txt diff --git a/src/Resources/help/MakeUser.txt b/config/help/MakeUser.txt similarity index 100% rename from src/Resources/help/MakeUser.txt rename to config/help/MakeUser.txt diff --git a/src/Resources/help/MakeValidator.txt b/config/help/MakeValidator.txt similarity index 100% rename from src/Resources/help/MakeValidator.txt rename to config/help/MakeValidator.txt diff --git a/src/Resources/help/MakeVoter.txt b/config/help/MakeVoter.txt similarity index 100% rename from src/Resources/help/MakeVoter.txt rename to config/help/MakeVoter.txt diff --git a/src/Resources/help/MakeWebhook.txt b/config/help/MakeWebhook.txt similarity index 100% rename from src/Resources/help/MakeWebhook.txt rename to config/help/MakeWebhook.txt diff --git a/src/Resources/help/_WithTests.txt b/config/help/_WithTests.txt similarity index 100% rename from src/Resources/help/_WithTests.txt rename to config/help/_WithTests.txt diff --git a/src/Resources/help/_WithUid.txt b/config/help/_WithUid.txt similarity index 100% rename from src/Resources/help/_WithUid.txt rename to config/help/_WithUid.txt diff --git a/src/Resources/help/security/MakeCustom.txt b/config/help/security/MakeCustom.txt similarity index 100% rename from src/Resources/help/security/MakeCustom.txt rename to config/help/security/MakeCustom.txt diff --git a/src/Resources/help/security/MakeFormLogin.txt b/config/help/security/MakeFormLogin.txt similarity index 100% rename from src/Resources/help/security/MakeFormLogin.txt rename to config/help/security/MakeFormLogin.txt diff --git a/src/Resources/config/makers.xml b/config/makers.xml similarity index 100% rename from src/Resources/config/makers.xml rename to config/makers.xml diff --git a/src/Resources/config/php-cs-fixer.config.php b/config/php-cs-fixer.config.php similarity index 67% rename from src/Resources/config/php-cs-fixer.config.php rename to config/php-cs-fixer.config.php index 9b5ee2826..59acf276b 100644 --- a/src/Resources/config/php-cs-fixer.config.php +++ b/config/php-cs-fixer.config.php @@ -9,6 +9,14 @@ * file that was distributed with this source code. */ +/* + * This PHP-CS-Fixer config file is used by the TemplateLinter for userland + * code when say make:controller is run. If a user does not have a php-cs-fixer + * config file, this one is used on the generated PHP files. + * + * It should not be confused by the root level .php-cs-fixer.dist.php config + * which is used to maintain the MakerBundle codebase itself. + */ return (new PhpCsFixer\Config()) ->setRules([ '@Symfony' => true, diff --git a/src/Resources/config/services.xml b/config/services.xml similarity index 98% rename from src/Resources/config/services.xml rename to config/services.xml index 5fbc0439b..8f0b2a209 100644 --- a/src/Resources/config/services.xml +++ b/config/services.xml @@ -41,6 +41,7 @@ + %env(default::string:MAKER_PHP_CS_FIXER_BINARY_PATH)% %env(default::string:MAKER_PHP_CS_FIXER_CONFIG_PATH)% diff --git a/src/Resources/doc/index.rst b/docs/index.rst similarity index 97% rename from src/Resources/doc/index.rst rename to docs/index.rst index 1d56c1e9c..e1942b5e6 100644 --- a/src/Resources/doc/index.rst +++ b/docs/index.rst @@ -41,6 +41,9 @@ optional arguments and options. Check them out with the ``--help`` option: $ php bin/console make:controller --help +.. caution:: + + ``make:entity`` requires ``doctrine/orm`` to be installed and configured. This maker support only ORM, not ODM. Linting Generated Code ______________________ diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d35e0fca4..8cf19ce2a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,8 +19,12 @@ + + + + diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php deleted file mode 100644 index 15c718318..000000000 --- a/src/DependencyInjection/Configuration.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\MakerBundle\DependencyInjection; - -use Symfony\Component\Config\Definition\Builder\TreeBuilder; -use Symfony\Component\Config\Definition\ConfigurationInterface; - -class Configuration implements ConfigurationInterface -{ - public function getConfigTreeBuilder(): TreeBuilder - { - $treeBuilder = new TreeBuilder('maker'); - $rootNode = $treeBuilder->getRootNode(); - - $rootNode - ->children() - ->scalarNode('root_namespace')->defaultValue('App')->end() - ->booleanNode('generate_final_classes')->defaultTrue()->end() - ->booleanNode('generate_final_entities')->defaultFalse()->end() - ->end() - ; - - return $treeBuilder; - } -} diff --git a/src/DependencyInjection/MakerExtension.php b/src/DependencyInjection/MakerExtension.php deleted file mode 100644 index 1de775bff..000000000 --- a/src/DependencyInjection/MakerExtension.php +++ /dev/null @@ -1,58 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\MakerBundle\DependencyInjection; - -use Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass\MakeCommandRegistrationPass; -use Symfony\Bundle\MakerBundle\MakerInterface; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; - -/** - * This is the class that loads and manages your bundle configuration. - * - * @see http://symfony.com/doc/current/cookbook/bundles/extension.html - */ -class MakerExtension extends Extension -{ - public function load(array $configs, ContainerBuilder $container): void - { - $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - $loader->load('services.xml'); - $loader->load('makers.xml'); - - $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); - - $rootNamespace = trim($config['root_namespace'], '\\'); - - $autoloaderFinderDefinition = $container->getDefinition('maker.autoloader_finder'); - $autoloaderFinderDefinition->replaceArgument(0, $rootNamespace); - - $makeCommandDefinition = $container->getDefinition('maker.generator'); - $makeCommandDefinition->replaceArgument(1, $rootNamespace); - - $doctrineHelperDefinition = $container->getDefinition('maker.doctrine_helper'); - $doctrineHelperDefinition->replaceArgument(0, $rootNamespace.'\\Entity'); - - $componentGeneratorDefinition = $container->getDefinition('maker.template_component_generator'); - $componentGeneratorDefinition - ->replaceArgument(0, $config['generate_final_classes']) - ->replaceArgument(1, $config['generate_final_entities']) - ->replaceArgument(2, $rootNamespace) - ; - - $container->registerForAutoconfiguration(MakerInterface::class) - ->addTag(MakeCommandRegistrationPass::MAKER_TAG); - } -} diff --git a/src/Generator.php b/src/Generator.php index 315b33427..fbadc1bca 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -63,7 +63,7 @@ public function generateClass(string $className, string $templateName, array $va $targetPath = $this->fileManager->getRelativePathForFutureClass($className); if (null === $targetPath) { - throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "\\My\\Full\\Namespace\\%s"', $className, Str::getShortClassName($className))); + throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "My\\Full\\Namespace\\%s"', $className, Str::getShortClassName($className))); } $variables = array_merge($variables, [ @@ -76,6 +76,41 @@ public function generateClass(string $className, string $templateName, array $va return $targetPath; } + /** + * Future replacement for generateClass(). + * + * @internal + * + * @param string $templateName Template name in the templates/ dir to use + * @param array $variables Array of variables to pass to the template + * @param bool $isController Set to true if generating a Controller that needs + * access to the TemplateComponentGenerator ("generator") in + * the twig template. e.g. to create route attributes for a route method + * + * @return string The path where the file will be created + * + * @throws \Exception + */ + final public function generateClassFromClassData(ClassData $classData, string $templateName, array $variables = [], bool $isController = false): string + { + $classData = $this->templateComponentGenerator->configureClass($classData); + $targetPath = $this->fileManager->getRelativePathForFutureClass($classData->getFullClassName()); + + if (null === $targetPath) { + throw new \LogicException(\sprintf('Could not determine where to locate the new class "%s", maybe try with a full namespace like "My\\Full\\Namespace\\%s"', $classData->getFullClassName(), $classData->getClassName())); + } + + $globalTemplateVars = ['class_data' => $classData]; + + if ($isController) { + $globalTemplateVars['generator'] = $this->templateComponentGenerator; + } + + $this->addOperation($targetPath, $templateName, array_merge($variables, $globalTemplateVars)); + + return $targetPath; + } + /** * Generate a normal file from a template. * @@ -264,10 +299,14 @@ private function addOperation(string $targetPath, string $templateName, array $v $templatePath = $templateName; if (!file_exists($templatePath)) { - $templatePath = __DIR__.'/Resources/skeleton/'.$templateName; + $templatePath = \sprintf('%s/templates/%s', \dirname(__DIR__), $templateName); if (!file_exists($templatePath)) { - throw new \Exception(\sprintf('Cannot find template "%s"', $templateName)); + $templatePath = $this->getTemplateFromLegacySkeletonPath($templateName); + } + + if (!file_exists($templatePath)) { + throw new \Exception(\sprintf('Cannot find template "%s" in the templates/ dir.', $templateName)); } } @@ -276,4 +315,27 @@ private function addOperation(string $targetPath, string $templateName, array $v 'variables' => $variables, ]; } + + /** + * @legacy - Remove when public generate methods become "internal" to MakerBundle in v2 + */ + private function getTemplateFromLegacySkeletonPath(string $templateName): string + { + $templatePath = $templateName; + if (!file_exists($templatePath)) { + $templatePath = __DIR__.'/Resources/skeleton/'.$templateName; + + if (!file_exists($templatePath)) { + throw new \Exception(\sprintf('Cannot find template "%s"', $templateName)); + } + } + + @trigger_deprecation( + 'symfony/maker-bundle', + '1.62.0', + 'Storing templates in src/Resources/skeleton is deprecated. Store MakerBundle templates in the "~/templates/" dir instead.', + ); + + return $templatePath; + } } diff --git a/src/Maker/AbstractMaker.php b/src/Maker/AbstractMaker.php index 530bae0dd..bbc00062e 100644 --- a/src/Maker/AbstractMaker.php +++ b/src/Maker/AbstractMaker.php @@ -55,4 +55,17 @@ protected function addDependencies(array $dependencies, ?string $message = null) $message ); } + + /** + * Get the help file contents needed for "setHelp()" of a maker. + * + * @param string $helpFileName the filename (omit path) of the help file located in config/help/ + * e.g. MakeController.txt + * + * @internal + */ + final protected function getHelpFileContents(string $helpFileName): string + { + return file_get_contents(\sprintf('%s/config/help/%s', \dirname(__DIR__, 2), $helpFileName)); + } } diff --git a/src/Maker/Common/CanGenerateTestsTrait.php b/src/Maker/Common/CanGenerateTestsTrait.php index 18d7566af..aae231704 100644 --- a/src/Maker/Common/CanGenerateTestsTrait.php +++ b/src/Maker/Common/CanGenerateTestsTrait.php @@ -28,7 +28,7 @@ trait CanGenerateTestsTrait public function configureCommandWithTestsOption(Command $command): Command { - $testsHelp = file_get_contents(\dirname(__DIR__, 2).'/Resources/help/_WithTests.txt'); + $testsHelp = file_get_contents(\dirname(__DIR__, 3).'/config/help/_WithTests.txt'); $help = $command->getHelp()."\n".$testsHelp; $command diff --git a/src/Maker/Common/UidTrait.php b/src/Maker/Common/UidTrait.php index 25e8269de..d3ecb6ea4 100644 --- a/src/Maker/Common/UidTrait.php +++ b/src/Maker/Common/UidTrait.php @@ -34,7 +34,7 @@ trait UidTrait */ protected function addWithUuidOption(Command $command): Command { - $uidHelp = file_get_contents(\dirname(__DIR__, 2).'/Resources/help/_WithUid.txt'); + $uidHelp = file_get_contents(\dirname(__DIR__, 3).'/config/help/_WithUid.txt'); $help = $command->getHelp()."\n".$uidHelp; $command diff --git a/src/Maker/MakeAuthenticator.php b/src/Maker/MakeAuthenticator.php index 155f44fe7..3f7c9e417 100644 --- a/src/Maker/MakeAuthenticator.php +++ b/src/Maker/MakeAuthenticator.php @@ -92,7 +92,8 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeAuth.txt')); + ->setHelp($this->getHelpFileContents('MakeAuth.txt')) + ; } public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void diff --git a/src/Maker/MakeCommand.php b/src/Maker/MakeCommand.php index e8f586d19..52d331c53 100644 --- a/src/Maker/MakeCommand.php +++ b/src/Maker/MakeCommand.php @@ -58,7 +58,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, \sprintf('Choose a command name (e.g. app:%s)', Str::asCommand(Str::getRandomTerm()))) - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeCommand.txt')) + ->setHelp($this->getHelpFileContents('MakeCommand.txt')) ; } diff --git a/src/Maker/MakeController.php b/src/Maker/MakeController.php index 4cf84e1f6..58e049eb5 100644 --- a/src/Maker/MakeController.php +++ b/src/Maker/MakeController.php @@ -12,13 +12,15 @@ namespace Symfony\Bundle\MakerBundle\Maker; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; +use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait; use Symfony\Bundle\MakerBundle\Str; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; use Symfony\Bundle\MakerBundle\Util\PhpCompatUtil; -use Symfony\Bundle\MakerBundle\Util\UseStatementGenerator; use Symfony\Bundle\TwigBundle\TwigBundle; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -34,6 +36,13 @@ */ final class MakeController extends AbstractMaker { + use CanGenerateTestsTrait; + + private bool $isInvokable; + private ClassData $controllerClassData; + private bool $usesTwigTemplate; + private string $twigTemplatePath; + public function __construct(private ?PhpCompatUtil $phpCompatUtil = null) { if (null !== $phpCompatUtil) { @@ -61,55 +70,89 @@ public function configureCommand(Command $command, InputConfiguration $inputConf ->addArgument('controller-class', InputArgument::OPTIONAL, \sprintf('Choose a name for your controller class (e.g. %sController)', Str::asClassName(Str::getRandomTerm()))) ->addOption('no-template', null, InputOption::VALUE_NONE, 'Use this option to disable template generation') ->addOption('invokable', 'i', InputOption::VALUE_NONE, 'Use this option to create an invokable controller') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeController.txt')) + ->setHelp($this->getHelpFileContents('MakeController.txt')) ; + + $this->configureCommandWithTestsOption($command); } - public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void + public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void { - $controllerClassNameDetails = $generator->createClassNameDetails( - $input->getArgument('controller-class'), - 'Controller\\', - 'Controller' - ); + $this->usesTwigTemplate = $this->isTwigInstalled() && !$input->getOption('no-template'); + $this->isInvokable = (bool) $input->getOption('invokable'); - $withTemplate = $this->isTwigInstalled() && !$input->getOption('no-template'); - $isInvokable = (bool) $input->getOption('invokable'); - - $useStatements = new UseStatementGenerator([ - AbstractController::class, - $withTemplate ? Response::class : JsonResponse::class, - Route::class, - ]); - - $templateName = Str::asFilePath($controllerClassNameDetails->getRelativeNameWithoutSuffix()) - .($isInvokable ? '.html.twig' : '/index.html.twig'); - - $controllerPath = $generator->generateController( - $controllerClassNameDetails->getFullName(), - 'controller/Controller.tpl.php', - [ - 'use_statements' => $useStatements, - 'route_path' => Str::asRoutePath($controllerClassNameDetails->getRelativeNameWithoutSuffix()), - 'route_name' => Str::asRouteName($controllerClassNameDetails->getRelativeNameWithoutSuffix()), - 'method_name' => $isInvokable ? '__invoke' : 'index', - 'with_template' => $withTemplate, - 'template_name' => $templateName, + $controllerClass = $input->getArgument('controller-class'); + $controllerClassName = \sprintf('Controller\%s', $controllerClass); + + // If the class name provided is absolute, we do not assume it will live in src/Controller + // e.g. src/Custom/Location/For/MyController instead of src/Controller/MyController + if ($isAbsoluteNamespace = '\\' === $controllerClass[0]) { + $controllerClassName = substr($controllerClass, 1); + } + + $this->controllerClassData = ClassData::create( + class: $controllerClassName, + suffix: 'Controller', + extendsClass: AbstractController::class, + useStatements: [ + $this->usesTwigTemplate ? Response::class : JsonResponse::class, + Route::class, ] ); - if ($withTemplate) { + // Again if the class name is absolute, lets not make assumptions about where the twig template + // should live. E.g. templates/custom/location/for/my_controller.html.twig instead of + // templates/my/controller.html.twig. We do however remove the root_namespace prefix in either case + // so we don't end up with templates/app/my/controller.html.twig + $templateName = $isAbsoluteNamespace ? + $this->controllerClassData->getFullClassName(withoutRootNamespace: true, withoutSuffix: true) : + $this->controllerClassData->getClassName(relative: true, withoutSuffix: true) + ; + + // Convert the twig template name into a file path where it will be generated. + $this->twigTemplatePath = \sprintf('%s%s', Str::asFilePath($templateName), $this->isInvokable ? '.html.twig' : '/index.html.twig'); + + $this->interactSetGenerateTests($input, $io); + } + + public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void + { + $controllerPath = $generator->generateClassFromClassData($this->controllerClassData, 'controller/Controller.tpl.php', [ + 'route_path' => Str::asRoutePath($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)), + 'route_name' => Str::AsRouteName($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)), + 'method_name' => $this->isInvokable ? '__invoke' : 'index', + 'with_template' => $this->usesTwigTemplate, + 'template_name' => $this->twigTemplatePath, + ], true); + + if ($this->usesTwigTemplate) { $generator->generateTemplate( - $templateName, + $this->twigTemplatePath, 'controller/twig_template.tpl.php', [ 'controller_path' => $controllerPath, 'root_directory' => $generator->getRootDirectory(), - 'class_name' => $controllerClassNameDetails->getShortName(), + 'class_name' => $this->controllerClassData->getClassName(), ] ); } + if ($this->shouldGenerateTests()) { + $testClassData = ClassData::create( + class: \sprintf('Tests\Controller\%s', $this->controllerClassData->getClassName(relative: true, withoutSuffix: true)), + suffix: 'ControllerTest', + extendsClass: WebTestCase::class, + ); + + $generator->generateClassFromClassData($testClassData, 'controller/test/Test.tpl.php', [ + 'route_path' => Str::asRoutePath($this->controllerClassData->getClassName(relative: true, withoutSuffix: true)), + ]); + + if (!class_exists(WebTestCase::class)) { + $io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.'); + } + } + $generator->writeChanges(); $this->writeSuccessMessage($io); diff --git a/src/Maker/MakeCrud.php b/src/Maker/MakeCrud.php index 0a37b2ba4..f535d24bd 100644 --- a/src/Maker/MakeCrud.php +++ b/src/Maker/MakeCrud.php @@ -71,7 +71,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('entity-class', InputArgument::OPTIONAL, \sprintf('The class name of the entity to create CRUD (e.g. %s)', Str::asClassName(Str::getRandomTerm()))) - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeCrud.txt')) + ->setHelp($this->getHelpFileContents('MakeCrud.txt')) ; $inputConfig->setArgumentAsNonInteractive('entity-class'); diff --git a/src/Maker/MakeDockerDatabase.php b/src/Maker/MakeDockerDatabase.php index bb8391669..5f6ffcb08 100644 --- a/src/Maker/MakeDockerDatabase.php +++ b/src/Maker/MakeDockerDatabase.php @@ -65,7 +65,7 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeDockerDatabase.txt')) + ->setHelp($this->getHelpFileContents('MakeDockerDatabase.txt')) ; } diff --git a/src/Maker/MakeEntity.php b/src/Maker/MakeEntity.php index dfc25301d..e0498f36b 100644 --- a/src/Maker/MakeEntity.php +++ b/src/Maker/MakeEntity.php @@ -97,7 +97,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf ->addOption('broadcast', 'b', InputOption::VALUE_NONE, 'Add the ability to broadcast entity updates using Symfony UX Turbo?') ->addOption('regenerate', null, InputOption::VALUE_NONE, 'Instead of adding new fields, simply generate the methods (e.g. getter/setter) for existing fields') ->addOption('overwrite', null, InputOption::VALUE_NONE, 'Overwrite any existing getter/setter methods') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeEntity.txt')) + ->setHelp($this->getHelpFileContents('MakeEntity.txt')) ; $this->addWithUuidOption($command); diff --git a/src/Maker/MakeFixtures.php b/src/Maker/MakeFixtures.php index befc384bc..137ef5c98 100644 --- a/src/Maker/MakeFixtures.php +++ b/src/Maker/MakeFixtures.php @@ -44,7 +44,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('fixtures-class', InputArgument::OPTIONAL, 'The class name of the fixtures to create (e.g. AppFixtures)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeFixture.txt')) + ->setHelp($this->getHelpFileContents('MakeFixture.txt')) ; } diff --git a/src/Maker/MakeForm.php b/src/Maker/MakeForm.php index 4859064b8..7387ead57 100644 --- a/src/Maker/MakeForm.php +++ b/src/Maker/MakeForm.php @@ -53,7 +53,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->addArgument('name', InputArgument::OPTIONAL, \sprintf('The name of the form class (e.g. %sType)', Str::asClassName(Str::getRandomTerm()))) ->addArgument('bound-class', InputArgument::OPTIONAL, 'The name of Entity or fully qualified model class name that the new form will be bound to (empty for none)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeForm.txt')) + ->setHelp($this->getHelpFileContents('MakeForm.txt')) ; $inputConfig->setArgumentAsNonInteractive('bound-class'); diff --git a/src/Maker/MakeFunctionalTest.php b/src/Maker/MakeFunctionalTest.php index 69277c585..16f0281c0 100644 --- a/src/Maker/MakeFunctionalTest.php +++ b/src/Maker/MakeFunctionalTest.php @@ -50,7 +50,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the functional test class (e.g. DefaultControllerTest)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeFunctionalTest.txt')) + ->setHelp($this->getHelpFileContents('MakeFunctionalTest.txt')) ; } diff --git a/src/Maker/MakeListener.php b/src/Maker/MakeListener.php index 298c419ec..6b00b51f2 100644 --- a/src/Maker/MakeListener.php +++ b/src/Maker/MakeListener.php @@ -66,7 +66,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your event listener or subscriber (e.g. ExceptionListener or ExceptionSubscriber)') ->addArgument('event', InputArgument::OPTIONAL, 'What event do you want to listen to?') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeListener.txt')) + ->setHelp($this->getHelpFileContents('MakeListener.txt')) ; $inputConfig->setArgumentAsNonInteractive('event'); diff --git a/src/Maker/MakeMessage.php b/src/Maker/MakeMessage.php index 682601a44..74fc1e692 100644 --- a/src/Maker/MakeMessage.php +++ b/src/Maker/MakeMessage.php @@ -50,7 +50,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the message class (e.g. SendEmailMessage)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeMessage.txt')) + ->setHelp($this->getHelpFileContents('MakeMessage.txt')) ; } diff --git a/src/Maker/MakeMessengerMiddleware.php b/src/Maker/MakeMessengerMiddleware.php index 2b1c189e9..f640d90cb 100644 --- a/src/Maker/MakeMessengerMiddleware.php +++ b/src/Maker/MakeMessengerMiddleware.php @@ -45,7 +45,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the middleware class (e.g. CustomMiddleware)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeMessage.txt')); + ->setHelp($this->getHelpFileContents('MakeMessage.txt')) + ; } public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void diff --git a/src/Maker/MakeMigration.php b/src/Maker/MakeMigration.php index 5c0afc054..206a8d84d 100644 --- a/src/Maker/MakeMigration.php +++ b/src/Maker/MakeMigration.php @@ -60,7 +60,7 @@ public function setApplication(Application $application) public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeMigration.txt')) + ->setHelp($this->getHelpFileContents('MakeMigration.txt')) ; if (class_exists(MigrationsDiffDoctrineCommand::class)) { diff --git a/src/Maker/MakeRegistrationForm.php b/src/Maker/MakeRegistrationForm.php index eeb5237ee..48a8ae34a 100644 --- a/src/Maker/MakeRegistrationForm.php +++ b/src/Maker/MakeRegistrationForm.php @@ -108,7 +108,7 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeRegistrationForm.txt')) + ->setHelp($this->getHelpFileContents('MakeRegistrationForm.txt')) ; $this->configureCommandWithTestsOption($command); diff --git a/src/Maker/MakeResetPassword.php b/src/Maker/MakeResetPassword.php index 38938f24f..ae7b039c7 100644 --- a/src/Maker/MakeResetPassword.php +++ b/src/Maker/MakeResetPassword.php @@ -119,7 +119,7 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeResetPassword.txt')) + ->setHelp($this->getHelpFileContents('MakeResetPassword.txt')) ; $this->addWithUuidOption($command); diff --git a/src/Maker/MakeSchedule.php b/src/Maker/MakeSchedule.php index 999fd000f..ff1c94ffd 100644 --- a/src/Maker/MakeSchedule.php +++ b/src/Maker/MakeSchedule.php @@ -58,7 +58,7 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeScheduler.txt')) + ->setHelp($this->getHelpFileContents('MakeScheduler.txt')) ; } diff --git a/src/Maker/MakeSerializerEncoder.php b/src/Maker/MakeSerializerEncoder.php index 7bdb69f11..8fc033b6b 100644 --- a/src/Maker/MakeSerializerEncoder.php +++ b/src/Maker/MakeSerializerEncoder.php @@ -44,7 +44,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your encoder (e.g. YamlEncoder)') ->addArgument('format', InputArgument::OPTIONAL, 'Pick your format name (e.g. yaml)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeSerializerEncoder.txt')) + ->setHelp($this->getHelpFileContents('MakeSerializerEncoder.txt')) ; } diff --git a/src/Maker/MakeSerializerNormalizer.php b/src/Maker/MakeSerializerNormalizer.php index a23f24d56..39381ece1 100644 --- a/src/Maker/MakeSerializerNormalizer.php +++ b/src/Maker/MakeSerializerNormalizer.php @@ -54,7 +54,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your normalizer (e.g. UserNormalizer)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeSerializerNormalizer.txt')) + ->setHelp($this->getHelpFileContents('MakeSerializerNormalizer.txt')) ; } diff --git a/src/Maker/MakeStimulusController.php b/src/Maker/MakeStimulusController.php index 95bee3890..0610c7d3b 100644 --- a/src/Maker/MakeStimulusController.php +++ b/src/Maker/MakeStimulusController.php @@ -44,7 +44,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::REQUIRED, 'The name of the Stimulus controller (e.g. hello)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeStimulusController.txt')); + ->setHelp($this->getHelpFileContents('MakeStimulusController.txt')) + ; } public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void diff --git a/src/Maker/MakeSubscriber.php b/src/Maker/MakeSubscriber.php index faba42df3..cf1002c82 100644 --- a/src/Maker/MakeSubscriber.php +++ b/src/Maker/MakeSubscriber.php @@ -55,7 +55,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->addArgument('name', InputArgument::OPTIONAL, 'Choose a class name for your event subscriber (e.g. ExceptionSubscriber)') ->addArgument('event', InputArgument::OPTIONAL, 'What event do you want to subscribe to?') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeSubscriber.txt')) + ->setHelp($this->getHelpFileContents('MakeSubscriber.txt')) ; $inputConfig->setArgumentAsNonInteractive('event'); diff --git a/src/Maker/MakeTest.php b/src/Maker/MakeTest.php index 25ffa3512..47be59ab0 100644 --- a/src/Maker/MakeTest.php +++ b/src/Maker/MakeTest.php @@ -82,7 +82,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->addArgument('type', InputArgument::OPTIONAL, 'The type of test: '.implode(', ', $typesDesc)) ->addArgument('name', InputArgument::OPTIONAL, 'The name of the test class (e.g. BlogPostTest)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeTest.txt').implode("\n", $typesHelp)); + ->setHelp($this->getHelpFileContents('MakeTest.txt').implode("\n", $typesHelp)) + ; $inputConfig->setArgumentAsNonInteractive('name'); $inputConfig->setArgumentAsNonInteractive('type'); diff --git a/src/Maker/MakeTwigComponent.php b/src/Maker/MakeTwigComponent.php index 91908ebe5..4bf55ee8d 100644 --- a/src/Maker/MakeTwigComponent.php +++ b/src/Maker/MakeTwigComponent.php @@ -79,14 +79,14 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $generator->generateClass( $factory->getFullName(), - \sprintf('%s/../Resources/skeleton/twig/%s', __DIR__, $live ? 'LiveComponent.tpl.php' : 'Component.tpl.php'), + \sprintf('%s/templates/twig/%s', \dirname(__DIR__, 2), $live ? 'LiveComponent.tpl.php' : 'Component.tpl.php'), [ 'live' => $live, ] ); $generator->generateTemplate( "components/{$templatePath}.html.twig", - \sprintf('%s/../Resources/skeleton/twig/%s', __DIR__, 'component_template.tpl.php') + \sprintf('%s/templates/twig/%s', \dirname(__DIR__, 2), 'component_template.tpl.php') ); $generator->writeChanges(); diff --git a/src/Maker/MakeTwigExtension.php b/src/Maker/MakeTwigExtension.php index a51ffe996..809b09259 100644 --- a/src/Maker/MakeTwigExtension.php +++ b/src/Maker/MakeTwigExtension.php @@ -44,7 +44,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the Twig extension class (e.g. AppExtension)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeTwigExtension.txt')) + ->setHelp($this->getHelpFileContents('MakeTwigExtension.txt')) ; } diff --git a/src/Maker/MakeUnitTest.php b/src/Maker/MakeUnitTest.php index d7ef272f6..79a407fbd 100644 --- a/src/Maker/MakeUnitTest.php +++ b/src/Maker/MakeUnitTest.php @@ -45,7 +45,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the unit test class (e.g. UtilTest)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeUnitTest.txt')) + ->setHelp($this->getHelpFileContents('MakeUnitTest.txt')) ; } diff --git a/src/Maker/MakeUser.php b/src/Maker/MakeUser.php index 8027672f6..ddfd0721a 100644 --- a/src/Maker/MakeUser.php +++ b/src/Maker/MakeUser.php @@ -77,7 +77,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf ->addOption('is-entity', null, InputOption::VALUE_NONE, 'Do you want to store user data in the database (via Doctrine)?') ->addOption('identity-property-name', null, InputOption::VALUE_REQUIRED, 'Enter a property name that will be the unique "display" name for the user (e.g. email, username, uuid)') ->addOption('with-password', null, InputOption::VALUE_NONE, 'Will this app be responsible for checking the password? Choose No if the password is actually checked by some other system (e.g. a single sign-on server)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeUser.txt')); + ->setHelp($this->getHelpFileContents('MakeUser.txt')) + ; $this->addWithUuidOption($command); diff --git a/src/Maker/MakeValidator.php b/src/Maker/MakeValidator.php index 7fb12c299..f638a12d5 100644 --- a/src/Maker/MakeValidator.php +++ b/src/Maker/MakeValidator.php @@ -16,9 +16,12 @@ use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; use Symfony\Bundle\MakerBundle\Str; +use Symfony\Bundle\MakerBundle\Util\ClassSource\Model\ClassData; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Validation; /** @@ -42,33 +45,38 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the validator class (e.g. EnabledValidator)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeValidator.txt')) + ->setHelp($this->getHelpFileContents('MakeValidator.txt')) ; } /** @return void */ public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) { - $validatorClassNameDetails = $generator->createClassNameDetails( - $input->getArgument('name'), - 'Validator\\', - 'Validator' + $validatorClassData = ClassData::create( + class: \sprintf('Validator\\%s', $input->getArgument('name')), + suffix: 'Validator', + extendsClass: ConstraintValidator::class, + useStatements: [ + Constraint::class, + ], ); - $constraintFullClassName = Str::removeSuffix($validatorClassNameDetails->getFullName(), 'Validator'); + $constraintDataClass = ClassData::create( + class: \sprintf('Validator\\%s', Str::removeSuffix($validatorClassData->getClassName(), 'Validator')), + extendsClass: Constraint::class, + ); - $generator->generateClass( - $validatorClassNameDetails->getFullName(), + $generator->generateClassFromClassData( + $validatorClassData, 'validator/Validator.tpl.php', [ - 'constraint_class_name' => Str::getShortClassName($constraintFullClassName), + 'constraint_class_name' => $constraintDataClass->getClassName(), ] ); - $generator->generateClass( - $constraintFullClassName, + $generator->generateClassFromClassData( + $constraintDataClass, 'validator/Constraint.tpl.php', - [] ); $generator->writeChanges(); diff --git a/src/Maker/MakeVoter.php b/src/Maker/MakeVoter.php index faabc993e..0e12f28f1 100644 --- a/src/Maker/MakeVoter.php +++ b/src/Maker/MakeVoter.php @@ -43,7 +43,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'The name of the security voter class (e.g. BlogPostVoter)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeVoter.txt')) + ->setHelp($this->getHelpFileContents('MakeVoter.txt')) ; } @@ -60,10 +60,9 @@ class: \sprintf('Security\Voter\%s', $input->getArgument('name')), ] ); - $generator->generateClass( - $voterClassData->getFullClassName(), + $generator->generateClassFromClassData( + $voterClassData, 'security/Voter.tpl.php', - ['class_data' => $voterClassData] ); $generator->writeChanges(); diff --git a/src/Maker/MakeWebhook.php b/src/Maker/MakeWebhook.php index a0c77a074..a7b3f2423 100644 --- a/src/Maker/MakeWebhook.php +++ b/src/Maker/MakeWebhook.php @@ -90,7 +90,7 @@ public function configureCommand(Command $command, InputConfiguration $inputConf { $command ->addArgument('name', InputArgument::OPTIONAL, 'Name of the webhook to create (e.g. github, stripe, ...)') - ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeWebhook.txt')) + ->setHelp($this->getHelpFileContents('MakeWebhook.txt')) ; $inputConfig->setArgumentAsNonInteractive('name'); diff --git a/src/Maker/Security/MakeCustomAuthenticator.php b/src/Maker/Security/MakeCustomAuthenticator.php index 6cc1407f6..c3ecae946 100644 --- a/src/Maker/Security/MakeCustomAuthenticator.php +++ b/src/Maker/Security/MakeCustomAuthenticator.php @@ -68,7 +68,7 @@ public static function getCommandDescription(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { $command - ->setHelp(file_get_contents(__DIR__.'/../../Resources/help/security/MakeCustom.txt')) + ->setHelp($this->getHelpFileContents('security/MakeCustom.txt')) ; } diff --git a/src/Maker/Security/MakeFormLogin.php b/src/Maker/Security/MakeFormLogin.php index 8ae6e012e..641c048d9 100644 --- a/src/Maker/Security/MakeFormLogin.php +++ b/src/Maker/Security/MakeFormLogin.php @@ -77,7 +77,9 @@ public static function getCommandName(): string public function configureCommand(Command $command, InputConfiguration $inputConfig): void { - $command->setHelp(file_get_contents(\dirname(__DIR__, 2).'/Resources/help/security/MakeFormLogin.txt')); + $command + ->setHelp($this->getHelpFileContents('security/MakeFormLogin.txt')) + ; $this->configureCommandWithTestsOption($command); } diff --git a/src/MakerBundle.php b/src/MakerBundle.php index 427e2103c..42516aec8 100644 --- a/src/MakerBundle.php +++ b/src/MakerBundle.php @@ -14,16 +14,57 @@ use Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass\MakeCommandRegistrationPass; use Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass\RemoveMissingParametersPass; use Symfony\Bundle\MakerBundle\DependencyInjection\CompilerPass\SetDoctrineAnnotatedPrefixesPass; +use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator; use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symfony\Component\HttpKernel\Bundle\AbstractBundle; /** * @author Javier Eguiluz * @author Ryan Weaver */ -class MakerBundle extends Bundle +class MakerBundle extends AbstractBundle { + protected string $extensionAlias = 'maker'; + + public function configure(DefinitionConfigurator $definition): void + { + $definition->rootNode() + ->children() + ->scalarNode('root_namespace')->defaultValue('App')->end() + ->booleanNode('generate_final_classes')->defaultTrue()->end() + ->booleanNode('generate_final_entities')->defaultFalse()->end() + ->end() + ; + } + + public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void + { + $container->import('../config/services.xml'); + $container->import('../config/makers.xml'); + + $rootNamespace = trim($config['root_namespace'], '\\'); + + $container->services() + ->get('maker.autoloader_finder') + ->arg(0, $rootNamespace) + ->get('maker.generator') + ->arg(1, $rootNamespace) + ->get('maker.doctrine_helper') + ->arg(0, \sprintf('%s\\Entity', $rootNamespace)) + ->get('maker.template_component_generator') + ->arg(0, $config['generate_final_classes']) + ->arg(1, $config['generate_final_entities']) + ->arg(2, $rootNamespace) + ; + + $builder + ->registerForAutoconfiguration(MakerInterface::class) + ->addTag(MakeCommandRegistrationPass::MAKER_TAG) + ; + } + public function build(ContainerBuilder $container): void { // add a priority so we run before the core command pass diff --git a/src/Resources/bin/php-cs-fixer-v3.49.0.phar b/src/Resources/bin/php-cs-fixer-v3.49.0.phar deleted file mode 100755 index cbbbe5834..000000000 Binary files a/src/Resources/bin/php-cs-fixer-v3.49.0.phar and /dev/null differ diff --git a/src/Resources/skeleton/validator/Constraint.tpl.php b/src/Resources/skeleton/validator/Constraint.tpl.php deleted file mode 100644 index 7baafa472..000000000 --- a/src/Resources/skeleton/validator/Constraint.tpl.php +++ /dev/null @@ -1,20 +0,0 @@ - - -namespace ; - -use Symfony\Component\Validator\Constraint; - -/** - * @Annotation - * - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - */ -#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class extends Constraint -{ - /* - * Any public properties become valid options for the annotation. - * Then, use these in your validator class. - */ - public string $message = 'The value "{{ value }}" is not valid.'; -} diff --git a/src/Test/MakerTestRunner.php b/src/Test/MakerTestRunner.php index 0eb07a3e0..4e6287437 100644 --- a/src/Test/MakerTestRunner.php +++ b/src/Test/MakerTestRunner.php @@ -35,7 +35,16 @@ public function runMaker(array $inputs, string $argumentsString = '', bool $allo { $this->executedMakerProcess = $this->environment->runMaker($inputs, $argumentsString, $allowedToFail, $envVars); - return $this->executedMakerProcess->getOutput(); + $output = $this->executedMakerProcess->getOutput(); + + // Allows for debugging the actual CLI output from within a test process. E.g. Manually viewing the output of the + // `make:voter` command that was run within the MakeVoterTest from your local command line. + // You should never use this in CI unless you know what you're doing - resource intensive. + if ('true' === getenv('MAKER_TEST_DUMP_OUTPUT')) { + dump(['Maker Process Output' => $output, 'Maker Process Error Output' => $this->executedMakerProcess->getErrorOutput()]); + } + + return $output; } /** @@ -198,7 +207,7 @@ public function updateSchema(): void public function runTests(): void { $internalTestProcess = MakerTestProcess::create( - \sprintf('php %s', $this->getPath('/bin/phpunit')), + \sprintf('php %s', $this->getPath('bin/phpunit')), $this->environment->getPath()) ->run(true) ; diff --git a/src/Util/ClassSource/Model/ClassData.php b/src/Util/ClassSource/Model/ClassData.php index 988404f2a..4ce0cd605 100644 --- a/src/Util/ClassSource/Model/ClassData.php +++ b/src/Util/ClassSource/Model/ClassData.php @@ -29,7 +29,11 @@ private function __construct( private UseStatementGenerator $useStatementGenerator, private bool $isFinal = true, private string $rootNamespace = 'App', + private ?string $classSuffix = null, ) { + if (str_starts_with(haystack: $this->namespace, needle: $this->rootNamespace)) { + $this->namespace = substr_replace(string: $this->namespace, replace: '', offset: 0, length: \strlen($this->rootNamespace) + 1); + } } public static function create(string $class, ?string $suffix = null, ?string $extendsClass = null, bool $isEntity = false, array $useStatements = []): self @@ -52,12 +56,30 @@ className: Str::asClassName($className), extends: null === $extendsClass ? null : Str::getShortClassName($extendsClass), isEntity: $isEntity, useStatementGenerator: $useStatements, + classSuffix: $suffix, ); } - public function getClassName(): string + public function getClassName(bool $relative = false, bool $withoutSuffix = false): string { - return $this->className; + if (!$withoutSuffix && !$relative) { + return $this->className; + } + + if ($relative) { + $class = \sprintf('%s\%s', $this->namespace, $this->className); + + $firstNsSeparatorPosition = stripos($class, '\\'); + $class = substr_replace(string: $class, replace: '', offset: 0, length: $firstNsSeparatorPosition + 1); + + if ($withoutSuffix) { + $class = Str::removeSuffix($class, $this->classSuffix); + } + + return $class; + } + + return Str::removeSuffix($this->className, $this->classSuffix); } public function getNamespace(): string @@ -66,12 +88,31 @@ public function getNamespace(): string return $this->rootNamespace; } + // Namespace is already absolute, don't add the rootNamespace. + if (str_starts_with($this->namespace, '\\')) { + return substr_replace($this->namespace, '', 0, 1); + } + return \sprintf('%s\%s', $this->rootNamespace, $this->namespace); } - public function getFullClassName(): string + /** + * Get the full class name. + * + * @param bool $withoutRootNamespace Get the full class name without global root namespace. e.g. "App" + * @param bool $withoutSuffix Get the full class name without the class suffix. e.g. "MyController" instead of "MyControllerController" + */ + public function getFullClassName($withoutRootNamespace = false, $withoutSuffix = false): string { - return \sprintf('%s\%s', $this->getNamespace(), $this->className); + $className = \sprintf('%s\%s', $this->getNamespace(), $withoutSuffix ? Str::removeSuffix($this->className, $this->classSuffix) : $this->className); + + if ($withoutRootNamespace) { + if (str_starts_with(haystack: $className, needle: $this->rootNamespace)) { + $className = substr_replace(string: $className, replace: '', offset: 0, length: \strlen($this->rootNamespace) + 1); + } + } + + return $className; } public function setRootNamespace(string $rootNamespace): self diff --git a/src/Util/ClassSourceManipulator.php b/src/Util/ClassSourceManipulator.php index 25b57bdbb..e68211223 100644 --- a/src/Util/ClassSourceManipulator.php +++ b/src/Util/ClassSourceManipulator.php @@ -143,7 +143,7 @@ public function addEntityField(ClassProperty $mapping): void } $propertyType = $typeHint; - if ($propertyType && !$defaultValue) { + if ($propertyType && !$defaultValue && 'mixed' !== $propertyType) { // all property types $propertyType = '?'.$propertyType; } @@ -162,13 +162,13 @@ public function addEntityField(ClassProperty $mapping): void // getter methods always have nullable return values // because even though these are required in the db, they may not be set yet // unless there is a default value - null === $defaultValue, + null === $defaultValue && 'mixed' !== $propertyType, $commentLines ); // don't generate setters for id fields if (!($mapping->id ?? false)) { - $this->addSetter($mapping->propertyName, $typeHint, $nullable); + $this->addSetter($mapping->propertyName, $typeHint, $nullable && 'mixed' !== $propertyType); } } @@ -485,10 +485,6 @@ private function createSetterNodeBuilder(string $propertyName, $type, bool $isNu private function getSetterName(string $propertyName, $type): string { - if ('bool' === $type && 0 === strncasecmp($propertyName, 'is', 2)) { - return 'set'.Str::asCamelCase(substr($propertyName, 2)); - } - return 'set'.Str::asCamelCase($propertyName); } diff --git a/src/Util/TemplateLinter.php b/src/Util/TemplateLinter.php index 1d29d195d..19907938d 100644 --- a/src/Util/TemplateLinter.php +++ b/src/Util/TemplateLinter.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\MakerBundle\Util; use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException; +use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\ExecutableFinder; use Symfony\Component\Process\Process; @@ -25,14 +26,12 @@ */ final class TemplateLinter { - // Version must match bundled version file name. e.g. php-cs-fixer-v3.49.9.phar - public const BUNDLED_PHP_CS_FIXER_VERSION = '3.49.0'; - private bool $usingBundledPhpCsFixer = true; private bool $usingBundledPhpCsFixerConfig = true; private bool $needsPhpCmdPrefix = true; public function __construct( + private FileManager $fileManager, private ?string $phpCsFixerBinaryPath = null, private ?string $phpCsFixerConfigPath = null, ) { @@ -98,9 +97,15 @@ public function writeLinterMessage(OutputInterface $output): void private function setBinary(): void { - // Use Bundled PHP-CS-Fixer + // Use Bundled (shim) PHP-CS-Fixer if (null === $this->phpCsFixerBinaryPath) { - $this->phpCsFixerBinaryPath = \sprintf('%s/Resources/bin/php-cs-fixer-v%s.phar', \dirname(__DIR__), self::BUNDLED_PHP_CS_FIXER_VERSION); + $shimLocation = \sprintf('%s/vendor/bin/php-cs-fixer', \dirname(__DIR__, 2)); + + if (is_file($shimLocation)) { + $this->phpCsFixerBinaryPath = $shimLocation; + + return; + } return; } @@ -129,7 +134,8 @@ private function setBinary(): void private function setConfig(): void { // No config provided, but there is a dist config file in the project dir - if (null === $this->phpCsFixerConfigPath && file_exists($defaultConfigPath = '.php-cs-fixer.dist.php')) { + $defaultConfigPath = \sprintf('%s/.php-cs-fixer.dist.php', $this->fileManager->getRootDirectory()); + if (null === $this->phpCsFixerConfigPath && file_exists($defaultConfigPath)) { $this->phpCsFixerConfigPath = $defaultConfigPath; $this->usingBundledPhpCsFixerConfig = false; @@ -139,7 +145,7 @@ private function setConfig(): void // No config provided and no project dist config - use our config if (null === $this->phpCsFixerConfigPath) { - $this->phpCsFixerConfigPath = \dirname(__DIR__).'/Resources/config/php-cs-fixer.config.php'; + $this->phpCsFixerConfigPath = \sprintf('%s/config/php-cs-fixer.config.php', \dirname(__DIR__, 2)); return; } diff --git a/src/Resources/skeleton/Class.tpl.php b/templates/Class.tpl.php similarity index 100% rename from src/Resources/skeleton/Class.tpl.php rename to templates/Class.tpl.php diff --git a/src/Resources/skeleton/authenticator/EmptyAuthenticator.tpl.php b/templates/authenticator/EmptyAuthenticator.tpl.php similarity index 100% rename from src/Resources/skeleton/authenticator/EmptyAuthenticator.tpl.php rename to templates/authenticator/EmptyAuthenticator.tpl.php diff --git a/src/Resources/skeleton/authenticator/EmptySecurityController.tpl.php b/templates/authenticator/EmptySecurityController.tpl.php similarity index 100% rename from src/Resources/skeleton/authenticator/EmptySecurityController.tpl.php rename to templates/authenticator/EmptySecurityController.tpl.php diff --git a/src/Resources/skeleton/authenticator/LoginFormAuthenticator.tpl.php b/templates/authenticator/LoginFormAuthenticator.tpl.php similarity index 100% rename from src/Resources/skeleton/authenticator/LoginFormAuthenticator.tpl.php rename to templates/authenticator/LoginFormAuthenticator.tpl.php diff --git a/src/Resources/skeleton/authenticator/login_form.tpl.php b/templates/authenticator/login_form.tpl.php similarity index 100% rename from src/Resources/skeleton/authenticator/login_form.tpl.php rename to templates/authenticator/login_form.tpl.php diff --git a/src/Resources/skeleton/command/Command.tpl.php b/templates/command/Command.tpl.php similarity index 100% rename from src/Resources/skeleton/command/Command.tpl.php rename to templates/command/Command.tpl.php diff --git a/src/Resources/skeleton/controller/Controller.tpl.php b/templates/controller/Controller.tpl.php similarity index 71% rename from src/Resources/skeleton/controller/Controller.tpl.php rename to templates/controller/Controller.tpl.php index 8f509e699..9d9948457 100644 --- a/src/Resources/skeleton/controller/Controller.tpl.php +++ b/templates/controller/Controller.tpl.php @@ -1,10 +1,10 @@ -namespace ; +namespace getNamespace(); ?>; - +getUseStatements(); ?> -class extends AbstractController +getClassDeclaration(); ?> { generateRouteForControllerMethod($route_path, $route_name); ?> public function (): ResponseJsonResponse @@ -12,7 +12,7 @@ public function (): Response return $this->render('', [ - 'controller_name' => '', + 'controller_name' => 'getClassName() ?>', ]); return $this->json([ diff --git a/templates/controller/test/Test.tpl.php b/templates/controller/test/Test.tpl.php new file mode 100644 index 000000000..802fcc694 --- /dev/null +++ b/templates/controller/test/Test.tpl.php @@ -0,0 +1,16 @@ + + +namespace getNamespace(); ?>; + +getUseStatements(); ?> + +getClassDeclaration(); ?> +{ + public function testIndex(): void + { + $client = static::createClient(); + $client->request('GET', ''); + + self::assertResponseIsSuccessful(); + } +} diff --git a/src/Resources/skeleton/controller/twig_template.tpl.php b/templates/controller/twig_template.tpl.php similarity index 100% rename from src/Resources/skeleton/controller/twig_template.tpl.php rename to templates/controller/twig_template.tpl.php diff --git a/src/Resources/skeleton/crud/controller/Controller.tpl.php b/templates/crud/controller/Controller.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/controller/Controller.tpl.php rename to templates/crud/controller/Controller.tpl.php diff --git a/src/Resources/skeleton/crud/templates/_delete_form.tpl.php b/templates/crud/templates/_delete_form.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/_delete_form.tpl.php rename to templates/crud/templates/_delete_form.tpl.php diff --git a/src/Resources/skeleton/crud/templates/_form.tpl.php b/templates/crud/templates/_form.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/_form.tpl.php rename to templates/crud/templates/_form.tpl.php diff --git a/src/Resources/skeleton/crud/templates/edit.tpl.php b/templates/crud/templates/edit.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/edit.tpl.php rename to templates/crud/templates/edit.tpl.php diff --git a/src/Resources/skeleton/crud/templates/index.tpl.php b/templates/crud/templates/index.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/index.tpl.php rename to templates/crud/templates/index.tpl.php diff --git a/src/Resources/skeleton/crud/templates/new.tpl.php b/templates/crud/templates/new.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/new.tpl.php rename to templates/crud/templates/new.tpl.php diff --git a/src/Resources/skeleton/crud/templates/show.tpl.php b/templates/crud/templates/show.tpl.php similarity index 100% rename from src/Resources/skeleton/crud/templates/show.tpl.php rename to templates/crud/templates/show.tpl.php diff --git a/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php b/templates/crud/test/Test.EntityManager.tpl.php similarity index 86% rename from src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php rename to templates/crud/test/Test.EntityManager.tpl.php index 36003d7e4..031a8cb6e 100644 --- a/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php +++ b/templates/crud/test/Test.EntityManager.tpl.php @@ -9,16 +9,16 @@ { private KernelBrowser $client; private EntityManagerInterface $manager; - private EntityRepository $repository; + private EntityRepository $Repository; private string $path = '/'; protected function setUp(): void { $this->client = static::createClient(); $this->manager = static::getContainer()->get('doctrine')->getManager(); - $this->repository = $this->manager->getRepository(::class); + $this->Repository = $this->manager->getRepository(::class); - foreach ($this->repository->findAll() as $object) { + foreach ($this->Repository->findAll() as $object) { $this->manager->remove($object); } @@ -52,7 +52,7 @@ public function testNew(): void self::assertResponseRedirects($this->path); - self::assertSame(1, $this->repository->count([])); + self::assertSame(1, $this->Repository->count([])); } public function testShow(): void @@ -95,7 +95,7 @@ public function testEdit(): void self::assertResponseRedirects('/'); - $fixture = $this->repository->findAll(); + $fixture = $this->Repository->findAll(); $typeOptions): ?> self::assertSame('Something New', $fixture[0]->get()); @@ -117,6 +117,6 @@ public function testRemove(): void $this->client->submitForm('Delete'); self::assertResponseRedirects('/'); - self::assertSame(0, $this->repository->count([])); + self::assertSame(0, $this->Repository->count([])); } } diff --git a/src/Resources/skeleton/doctrine/Entity.tpl.php b/templates/doctrine/Entity.tpl.php similarity index 100% rename from src/Resources/skeleton/doctrine/Entity.tpl.php rename to templates/doctrine/Entity.tpl.php diff --git a/src/Resources/skeleton/doctrine/Fixtures.tpl.php b/templates/doctrine/Fixtures.tpl.php similarity index 100% rename from src/Resources/skeleton/doctrine/Fixtures.tpl.php rename to templates/doctrine/Fixtures.tpl.php diff --git a/src/Resources/skeleton/doctrine/Repository.tpl.php b/templates/doctrine/Repository.tpl.php similarity index 100% rename from src/Resources/skeleton/doctrine/Repository.tpl.php rename to templates/doctrine/Repository.tpl.php diff --git a/src/Resources/skeleton/doctrine/broadcast_twig_template.tpl.php b/templates/doctrine/broadcast_twig_template.tpl.php similarity index 100% rename from src/Resources/skeleton/doctrine/broadcast_twig_template.tpl.php rename to templates/doctrine/broadcast_twig_template.tpl.php diff --git a/src/Resources/skeleton/event/Listener.tpl.php b/templates/event/Listener.tpl.php similarity index 100% rename from src/Resources/skeleton/event/Listener.tpl.php rename to templates/event/Listener.tpl.php diff --git a/src/Resources/skeleton/event/Subscriber.tpl.php b/templates/event/Subscriber.tpl.php similarity index 100% rename from src/Resources/skeleton/event/Subscriber.tpl.php rename to templates/event/Subscriber.tpl.php diff --git a/src/Resources/skeleton/form/Type.tpl.php b/templates/form/Type.tpl.php similarity index 100% rename from src/Resources/skeleton/form/Type.tpl.php rename to templates/form/Type.tpl.php diff --git a/src/Resources/skeleton/message/Message.tpl.php b/templates/message/Message.tpl.php similarity index 100% rename from src/Resources/skeleton/message/Message.tpl.php rename to templates/message/Message.tpl.php diff --git a/src/Resources/skeleton/message/MessageHandler.tpl.php b/templates/message/MessageHandler.tpl.php similarity index 100% rename from src/Resources/skeleton/message/MessageHandler.tpl.php rename to templates/message/MessageHandler.tpl.php diff --git a/src/Resources/skeleton/middleware/Middleware.tpl.php b/templates/middleware/Middleware.tpl.php similarity index 100% rename from src/Resources/skeleton/middleware/Middleware.tpl.php rename to templates/middleware/Middleware.tpl.php diff --git a/src/Resources/skeleton/registration/RegistrationController.tpl.php b/templates/registration/RegistrationController.tpl.php similarity index 100% rename from src/Resources/skeleton/registration/RegistrationController.tpl.php rename to templates/registration/RegistrationController.tpl.php diff --git a/src/Resources/skeleton/registration/Test.WithVerify.tpl.php b/templates/registration/Test.WithVerify.tpl.php similarity index 100% rename from src/Resources/skeleton/registration/Test.WithVerify.tpl.php rename to templates/registration/Test.WithVerify.tpl.php diff --git a/src/Resources/skeleton/registration/Test.WithoutVerify.tpl.php b/templates/registration/Test.WithoutVerify.tpl.php similarity index 100% rename from src/Resources/skeleton/registration/Test.WithoutVerify.tpl.php rename to templates/registration/Test.WithoutVerify.tpl.php diff --git a/src/Resources/skeleton/registration/twig_email.tpl.php b/templates/registration/twig_email.tpl.php similarity index 100% rename from src/Resources/skeleton/registration/twig_email.tpl.php rename to templates/registration/twig_email.tpl.php diff --git a/src/Resources/skeleton/registration/twig_template.tpl.php b/templates/registration/twig_template.tpl.php similarity index 100% rename from src/Resources/skeleton/registration/twig_template.tpl.php rename to templates/registration/twig_template.tpl.php diff --git a/src/Resources/skeleton/resetPassword/ChangePasswordFormType.tpl.php b/templates/resetPassword/ChangePasswordFormType.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/ChangePasswordFormType.tpl.php rename to templates/resetPassword/ChangePasswordFormType.tpl.php diff --git a/src/Resources/skeleton/resetPassword/ResetPasswordController.tpl.php b/templates/resetPassword/ResetPasswordController.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/ResetPasswordController.tpl.php rename to templates/resetPassword/ResetPasswordController.tpl.php diff --git a/src/Resources/skeleton/resetPassword/ResetPasswordRequestFormType.tpl.php b/templates/resetPassword/ResetPasswordRequestFormType.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/ResetPasswordRequestFormType.tpl.php rename to templates/resetPassword/ResetPasswordRequestFormType.tpl.php diff --git a/src/Resources/skeleton/resetPassword/Test.ResetPasswordController.tpl.php b/templates/resetPassword/Test.ResetPasswordController.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/Test.ResetPasswordController.tpl.php rename to templates/resetPassword/Test.ResetPasswordController.tpl.php diff --git a/src/Resources/skeleton/resetPassword/twig_check_email.tpl.php b/templates/resetPassword/twig_check_email.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/twig_check_email.tpl.php rename to templates/resetPassword/twig_check_email.tpl.php diff --git a/src/Resources/skeleton/resetPassword/twig_email.tpl.php b/templates/resetPassword/twig_email.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/twig_email.tpl.php rename to templates/resetPassword/twig_email.tpl.php diff --git a/src/Resources/skeleton/resetPassword/twig_request.tpl.php b/templates/resetPassword/twig_request.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/twig_request.tpl.php rename to templates/resetPassword/twig_request.tpl.php diff --git a/src/Resources/skeleton/resetPassword/twig_reset.tpl.php b/templates/resetPassword/twig_reset.tpl.php similarity index 100% rename from src/Resources/skeleton/resetPassword/twig_reset.tpl.php rename to templates/resetPassword/twig_reset.tpl.php diff --git a/src/Resources/skeleton/scheduler/Schedule.tpl.php b/templates/scheduler/Schedule.tpl.php similarity index 100% rename from src/Resources/skeleton/scheduler/Schedule.tpl.php rename to templates/scheduler/Schedule.tpl.php diff --git a/src/Resources/skeleton/security/UserProvider.tpl.php b/templates/security/UserProvider.tpl.php similarity index 100% rename from src/Resources/skeleton/security/UserProvider.tpl.php rename to templates/security/UserProvider.tpl.php diff --git a/src/Resources/skeleton/security/Voter.tpl.php b/templates/security/Voter.tpl.php similarity index 100% rename from src/Resources/skeleton/security/Voter.tpl.php rename to templates/security/Voter.tpl.php diff --git a/src/Resources/skeleton/security/custom/Authenticator.tpl.php b/templates/security/custom/Authenticator.tpl.php similarity index 100% rename from src/Resources/skeleton/security/custom/Authenticator.tpl.php rename to templates/security/custom/Authenticator.tpl.php diff --git a/src/Resources/skeleton/security/formLogin/LoginController.tpl.php b/templates/security/formLogin/LoginController.tpl.php similarity index 100% rename from src/Resources/skeleton/security/formLogin/LoginController.tpl.php rename to templates/security/formLogin/LoginController.tpl.php diff --git a/src/Resources/skeleton/security/formLogin/Test.LoginController.tpl.php b/templates/security/formLogin/Test.LoginController.tpl.php similarity index 100% rename from src/Resources/skeleton/security/formLogin/Test.LoginController.tpl.php rename to templates/security/formLogin/Test.LoginController.tpl.php diff --git a/src/Resources/skeleton/security/formLogin/login_form.tpl.php b/templates/security/formLogin/login_form.tpl.php similarity index 100% rename from src/Resources/skeleton/security/formLogin/login_form.tpl.php rename to templates/security/formLogin/login_form.tpl.php diff --git a/src/Resources/skeleton/serializer/Encoder.tpl.php b/templates/serializer/Encoder.tpl.php similarity index 100% rename from src/Resources/skeleton/serializer/Encoder.tpl.php rename to templates/serializer/Encoder.tpl.php diff --git a/src/Resources/skeleton/serializer/Normalizer.tpl.php b/templates/serializer/Normalizer.tpl.php similarity index 100% rename from src/Resources/skeleton/serializer/Normalizer.tpl.php rename to templates/serializer/Normalizer.tpl.php diff --git a/src/Resources/skeleton/stimulus/Controller.tpl.php b/templates/stimulus/Controller.tpl.php similarity index 100% rename from src/Resources/skeleton/stimulus/Controller.tpl.php rename to templates/stimulus/Controller.tpl.php diff --git a/src/Resources/skeleton/test/ApiTestCase.tpl.php b/templates/test/ApiTestCase.tpl.php similarity index 100% rename from src/Resources/skeleton/test/ApiTestCase.tpl.php rename to templates/test/ApiTestCase.tpl.php diff --git a/src/Resources/skeleton/test/Functional.tpl.php b/templates/test/Functional.tpl.php similarity index 100% rename from src/Resources/skeleton/test/Functional.tpl.php rename to templates/test/Functional.tpl.php diff --git a/src/Resources/skeleton/test/KernelTestCase.tpl.php b/templates/test/KernelTestCase.tpl.php similarity index 100% rename from src/Resources/skeleton/test/KernelTestCase.tpl.php rename to templates/test/KernelTestCase.tpl.php diff --git a/src/Resources/skeleton/test/PantherTestCase.tpl.php b/templates/test/PantherTestCase.tpl.php similarity index 100% rename from src/Resources/skeleton/test/PantherTestCase.tpl.php rename to templates/test/PantherTestCase.tpl.php diff --git a/src/Resources/skeleton/test/TestCase.tpl.php b/templates/test/TestCase.tpl.php similarity index 100% rename from src/Resources/skeleton/test/TestCase.tpl.php rename to templates/test/TestCase.tpl.php diff --git a/src/Resources/skeleton/test/Unit.tpl.php b/templates/test/Unit.tpl.php similarity index 100% rename from src/Resources/skeleton/test/Unit.tpl.php rename to templates/test/Unit.tpl.php diff --git a/src/Resources/skeleton/test/WebTestCase.tpl.php b/templates/test/WebTestCase.tpl.php similarity index 100% rename from src/Resources/skeleton/test/WebTestCase.tpl.php rename to templates/test/WebTestCase.tpl.php diff --git a/src/Resources/skeleton/twig/Component.tpl.php b/templates/twig/Component.tpl.php similarity index 100% rename from src/Resources/skeleton/twig/Component.tpl.php rename to templates/twig/Component.tpl.php diff --git a/src/Resources/skeleton/twig/Extension.tpl.php b/templates/twig/Extension.tpl.php similarity index 100% rename from src/Resources/skeleton/twig/Extension.tpl.php rename to templates/twig/Extension.tpl.php diff --git a/src/Resources/skeleton/twig/LiveComponent.tpl.php b/templates/twig/LiveComponent.tpl.php similarity index 100% rename from src/Resources/skeleton/twig/LiveComponent.tpl.php rename to templates/twig/LiveComponent.tpl.php diff --git a/src/Resources/skeleton/twig/Runtime.tpl.php b/templates/twig/Runtime.tpl.php similarity index 100% rename from src/Resources/skeleton/twig/Runtime.tpl.php rename to templates/twig/Runtime.tpl.php diff --git a/src/Resources/skeleton/twig/component_template.tpl.php b/templates/twig/component_template.tpl.php similarity index 100% rename from src/Resources/skeleton/twig/component_template.tpl.php rename to templates/twig/component_template.tpl.php diff --git a/templates/validator/Constraint.tpl.php b/templates/validator/Constraint.tpl.php new file mode 100644 index 000000000..414c19eeb --- /dev/null +++ b/templates/validator/Constraint.tpl.php @@ -0,0 +1,21 @@ + + +namespace getNamespace(); ?>; + +getUseStatements(); ?> + +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] +getClassDeclaration(); ?> +{ + public string $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.'; + + // You can use #[HasNamedArguments] to make some constraint options required. + // All configurable options must be passed to the constructor. + public function __construct( + public string $mode = 'strict', + ?array $groups = null, + mixed $payload = null + ) { + parent::__construct([], $groups, $payload); + } +} diff --git a/templates/validator/Validator.tpl.php b/templates/validator/Validator.tpl.php new file mode 100644 index 000000000..ef711e888 --- /dev/null +++ b/templates/validator/Validator.tpl.php @@ -0,0 +1,23 @@ + + +namespace getNamespace(); ?>; + +getUseStatements(); ?> + +getClassDeclaration(); ?> +{ + public function validate(mixed $value, Constraint $constraint): void + { + /** @var $constraint */ + + if (null === $value || '' === $value) { + return; + } + + // TODO: implement the validation here + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $value) + ->addViolation() + ; + } +} diff --git a/src/Resources/skeleton/verifyEmail/EmailVerifier.tpl.php b/templates/verifyEmail/EmailVerifier.tpl.php similarity index 97% rename from src/Resources/skeleton/verifyEmail/EmailVerifier.tpl.php rename to templates/verifyEmail/EmailVerifier.tpl.php index f5e37f7f2..85422dfc9 100644 --- a/src/Resources/skeleton/verifyEmail/EmailVerifier.tpl.php +++ b/templates/verifyEmail/EmailVerifier.tpl.php @@ -43,7 +43,7 @@ public function handleEmailConfirmation(Request $request, verifyEmailHelper->validateEmailConfirmationFromRequest($request, (string) $user->(), (string) $user->()); - $user->setVerified(true); + $user->setIsVerified(true); $this->entityManager->persist($user); $this->entityManager->flush(); diff --git a/src/Resources/skeleton/webhook/RequestParser.tpl.php b/templates/webhook/RequestParser.tpl.php similarity index 100% rename from src/Resources/skeleton/webhook/RequestParser.tpl.php rename to templates/webhook/RequestParser.tpl.php diff --git a/src/Resources/skeleton/webhook/WebhookConsumer.tpl.php b/templates/webhook/WebhookConsumer.tpl.php similarity index 100% rename from src/Resources/skeleton/webhook/WebhookConsumer.tpl.php rename to templates/webhook/WebhookConsumer.tpl.php diff --git a/tests/Command/MakerCommandTest.php b/tests/Command/MakerCommandTest.php index 06aa9823d..d52b2d723 100644 --- a/tests/Command/MakerCommandTest.php +++ b/tests/Command/MakerCommandTest.php @@ -40,7 +40,7 @@ public function testExceptionOnMissingDependencies(): void $fileManager = $this->createMock(FileManager::class); - $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'App'), new TemplateLinter()); + $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'App'), new TemplateLinter($fileManager)); // needed because it's normally set by the Application $command->setName('make:foo'); $tester = new CommandTester($command); @@ -53,7 +53,7 @@ public function testExceptionOnUnknownRootNamespace(): void $fileManager = $this->createMock(FileManager::class); - $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'Unknown'), new TemplateLinter()); + $command = new MakerCommand($maker, $fileManager, new Generator($fileManager, 'Unknown'), new TemplateLinter($fileManager)); // needed because it's normally set by the Application $command->setName('make:foo'); $tester = new CommandTester($command); diff --git a/tests/Maker/MakeControllerTest.php b/tests/Maker/MakeControllerTest.php index 026d390c5..656a4884a 100644 --- a/tests/Maker/MakeControllerTest.php +++ b/tests/Maker/MakeControllerTest.php @@ -15,6 +15,11 @@ use Symfony\Bundle\MakerBundle\Test\MakerTestCase; use Symfony\Bundle\MakerBundle\Test\MakerTestRunner; +/** + * Passing namespaces interactively can be done like "App\Controller\MyController" + * but passing as a command argument, you must add a double set of slashes. e.g. + * "App\\\\Controller\\\\MyController". + */ class MakeControllerTest extends MakerTestCase { protected function getMakerClass(): string @@ -32,6 +37,41 @@ public function getTestDetails(): \Generator ]); $this->assertContainsCount('created: ', $output, 1); + $this->runControllerTest($runner, 'it_generates_a_controller.php'); + + // Ensure the generated controller matches what we expect + self::assertSame( + expected: file_get_contents(\dirname(__DIR__).'/fixtures/make-controller/expected/FinalController.php'), + actual: file_get_contents($runner->getPath('src/Controller/FooBarController.php')) + ); + }), + ]; + + yield 'it_generates_a_controller-with-tests' => [$this->createMakerTest() + ->addExtraDependencies('symfony/test-pack') + ->run(function (MakerTestRunner $runner) { + $output = $runner->runMaker([ + 'FooBar', // controller class name + 'y', // create tests + ]); + + $this->assertStringContainsString('src/Controller/FooBarController.php', $output); + $this->assertStringContainsString('tests/Controller/FooBarControllerTest.php', $output); + + $this->assertFileExists($runner->getPath('src/Controller/FooBarController.php')); + $this->assertFileExists($runner->getPath('tests/Controller/FooBarControllerTest.php')); + + $this->runControllerTest($runner, 'it_generates_a_controller.php'); + }), + ]; + + yield 'it_generates_a_controller__no_input' => [$this->createMakerTest() + ->run(function (MakerTestRunner $runner) { + $output = $runner->runMaker([], 'FooBar'); + + $this->assertContainsCount('created: ', $output, 1); + + $this->assertFileExists($runner->getPath('src/Controller/FooBarController.php')); $this->runControllerTest($runner, 'it_generates_a_controller.php'); }), @@ -49,6 +89,24 @@ public function getTestDetails(): \Generator self::assertFileExists($controllerPath); $this->runControllerTest($runner, 'it_generates_a_controller_with_twig.php'); + + // Ensure the generated controller matches what we expect + self::assertSame( + expected: file_get_contents(\dirname(__DIR__).'/fixtures/make-controller/expected/FinalControllerWithTemplate.php'), + actual: file_get_contents($runner->getPath('src/Controller/FooTwigController.php')) + ); + }), + ]; + + yield 'it_generates_a_controller_with_twig__no_input' => [$this->createMakerTest() + ->addExtraDependencies('twig') + ->run(function (MakerTestRunner $runner) { + $runner->runMaker([], 'FooTwig'); + + $this->assertFileExists($runner->getPath('src/Controller/FooTwigController.php')); + $this->assertFileExists($runner->getPath('templates/foo_twig/index.html.twig')); + + $this->runControllerTest($runner, 'it_generates_a_controller_with_twig.php'); }), ]; @@ -98,6 +156,19 @@ public function getTestDetails(): \Generator }), ]; + yield 'it_generates_a_controller_in_sub_namespace__no_input' => [$this->createMakerTest() + ->skipTest( + message: 'Test Skipped - MAKER_TEST_WINDOWS is true.', + skipped: getenv('MAKER_TEST_WINDOWS') + ) + ->run(function (MakerTestRunner $runner) { + $output = $runner->runMaker([], 'Admin\\\\FooBar'); + + $this->assertFileExists($runner->getPath('src/Controller/Admin/FooBarController.php')); + $this->assertStringContainsString('src/Controller/Admin/FooBarController.php', $output); + }), + ]; + yield 'it_generates_a_controller_in_sub_namespace_with_template' => [$this->createMakerTest() ->addExtraDependencies('twig') ->run(function (MakerTestRunner $runner) { @@ -129,6 +200,22 @@ public function getTestDetails(): \Generator }), ]; + yield 'it_generates_a_controller_with_full_custom_namespace__no_input' => [$this->createMakerTest() + ->skipTest( + message: 'Test Skipped - MAKER_TEST_WINDOWS is true.', + skipped: getenv('MAKER_TEST_WINDOWS') + ) + ->addExtraDependencies('twig') + ->run(function (MakerTestRunner $runner) { + $output = $runner->runMaker([], '\\\\App\\\\Foo\\\\Bar\\\\CoolController'); + + self::assertFileExists($runner->getPath('templates/foo/bar/cool/index.html.twig')); + + $this->assertStringContainsString('src/Foo/Bar/CoolController.php', $output); + $this->assertStringContainsString('templates/foo/bar/cool/index.html.twig', $output); + }), + ]; + yield 'it_generates_a_controller_with_invoke' => [$this->createMakerTest() ->addExtraDependencies('twig') ->run(function (MakerTestRunner $runner) { diff --git a/tests/Maker/MakeEntityTest.php b/tests/Maker/MakeEntityTest.php index 4403a53c2..54059bad3 100644 --- a/tests/Maker/MakeEntityTest.php +++ b/tests/Maker/MakeEntityTest.php @@ -120,6 +120,8 @@ public function getTestDetails(): \Generator ]; yield 'it_creates_a_new_class_and_api_resource' => [$this->createMakeEntityTest() + // @legacy - re-enable test when https://github.com/symfony/recipes/pull/1339 is merged + ->skipTest('Waiting for https://github.com/symfony/recipes/pull/1339') ->addExtraDependencies('api') ->run(function (MakerTestRunner $runner) { $runner->runMaker([ @@ -679,6 +681,8 @@ public function getTestDetails(): \Generator ]; yield 'it_makes_new_entity_no_to_all_extras' => [$this->createMakeEntityTestForMercure() + // @legacy - re-enable test when https://github.com/symfony/recipes/pull/1339 is merged + ->skipTest('Waiting for https://github.com/symfony/recipes/pull/1339') ->addExtraDependencies('api') // special setup done in createMakeEntityTestForMercure() ->run(function (MakerTestRunner $runner) { diff --git a/tests/Maker/MakeValidatorTest.php b/tests/Maker/MakeValidatorTest.php index 0ca11939c..acc551b0c 100644 --- a/tests/Maker/MakeValidatorTest.php +++ b/tests/Maker/MakeValidatorTest.php @@ -32,6 +32,18 @@ public function getTestDetails(): \Generator 'FooBar', ] ); + + // Validator + $expectedVoterPath = \dirname(__DIR__).'/fixtures/make-validator/expected/FooBarValidator.php'; + $generatedVoter = $runner->getPath('src/Validator/FooBarValidator.php'); + + self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter)); + + // Constraint + $expectedVoterPath = \dirname(__DIR__).'/fixtures/make-validator/expected/FooBar.php'; + $generatedVoter = $runner->getPath('src/Validator/FooBar.php'); + + self::assertSame(file_get_contents($expectedVoterPath), file_get_contents($generatedVoter)); }), ]; } diff --git a/tests/Maker/TemplateLinterTest.php b/tests/Maker/TemplateLinterTest.php index cf14850ab..2747bb033 100644 --- a/tests/Maker/TemplateLinterTest.php +++ b/tests/Maker/TemplateLinterTest.php @@ -33,7 +33,6 @@ protected function getMakerClass(): string public function getTestDetails(): \Generator { yield 'lints_templates_with_custom_php_cs_fixer_and_config' => [$this->createMakerTest() - ->addExtraDependencies('php-cs-fixer/shim') ->run(function (MakerTestRunner $runner) { $runner->copy('template-linter/php-cs-fixer.test.php', 'php-cs-fixer.test.php'); @@ -53,13 +52,12 @@ public function getTestDetails(): \Generator self::assertStringContainsString('Linted by custom php-cs-config', $generatedTemplate); - $expectedOutput = 'System PHP-CS-Fixer (bin/php-cs-fixer) & System PHP-CS-Fixer Configuration (php-cs-fixer.test.php)'; + $expectedOutput = 'System PHP-CS-Fixer (bin/php-cs-fixer) & System PHP-CS-Fixer Configuration'; self::assertStringContainsString($expectedOutput, $output); }), ]; yield 'lints_templates_with_flex_generated_config_file' => [$this->createMakerTest() - ->addExtraDependencies('php-cs-fixer/shim') ->run(function (MakerTestRunner $runner) { $runner->replaceInFile( '.php-cs-fixer.dist.php', @@ -79,13 +77,14 @@ public function getTestDetails(): \Generator self::assertStringContainsString('Linted with stock php-cs-config', $generatedTemplate); - $expectedOutput = 'Bundled PHP-CS-Fixer & System PHP-CS-Fixer Configuration (.php-cs-fixer.dist.php)'; + $expectedOutput = 'Bundled PHP-CS-Fixer & System PHP-CS-Fixer Configuration'; self::assertStringContainsString($expectedOutput, $output); }), ]; yield 'lints_templates_with_bundled_php_cs_fixer' => [$this->createMakerTest() ->run(function (MakerTestRunner $runner) { + $runner->deleteFile('.php-cs-fixer.dist.php'); // Voter class name $output = $runner->runMaker(['FooBar']); diff --git a/tests/Util/ClassSource/ClassDataTest.php b/tests/Util/ClassSource/ClassDataTest.php index cbad75dec..27d345028 100644 --- a/tests/Util/ClassSource/ClassDataTest.php +++ b/tests/Util/ClassSource/ClassDataTest.php @@ -91,4 +91,56 @@ public function namespaceDataProvider(): \Generator yield ['MyController', 'Maker', 'Maker', 'Maker\MyController']; yield ['Controller\MyController', 'Maker', 'Maker\Controller', 'Maker\Controller\MyController']; } + + public function testGetClassName(): void + { + $class = ClassData::create(class: 'Controller\\Foo', suffix: 'Controller'); + self::assertSame('FooController', $class->getClassName()); + self::assertSame('Foo', $class->getClassName(relative: false, withoutSuffix: true)); + self::assertSame('FooController', $class->getClassName(relative: true, withoutSuffix: false)); + self::assertSame('Foo', $class->getClassName(relative: true, withoutSuffix: true)); + self::assertSame('App\Controller\FooController', $class->getFullClassName()); + } + + public function testGetClassNameRelativeNamespace(): void + { + $class = ClassData::create(class: 'Controller\\Admin\\Foo', suffix: 'Controller'); + self::assertSame('FooController', $class->getClassName()); + self::assertSame('Foo', $class->getClassName(relative: false, withoutSuffix: true)); + self::assertSame('Admin\FooController', $class->getClassName(relative: true, withoutSuffix: false)); + self::assertSame('Admin\Foo', $class->getClassName(relative: true, withoutSuffix: true)); + self::assertSame('App\Controller\Admin\FooController', $class->getFullClassName()); + } + + public function testGetClassNameWithAbsoluteNamespace(): void + { + $class = ClassData::create(class: '\\Foo\\Bar\\Admin\\Baz', suffix: 'Controller'); + self::assertSame('BazController', $class->getClassName()); + self::assertSame('Foo\Bar\Admin', $class->getNamespace()); + self::assertSame('Foo\Bar\Admin\BazController', $class->getFullClassName()); + } + + /** @dataProvider fullClassNameProvider */ + public function testGetFullClassName(string $class, ?string $rootNamespace, bool $withoutRootNamespace, bool $withoutSuffix, string $expectedFullClassName): void + { + $class = ClassData::create($class, suffix: 'Controller'); + + if (null !== $rootNamespace) { + $class->setRootNamespace($rootNamespace); + } + + self::assertSame($expectedFullClassName, $class->getFullClassName(withoutRootNamespace: $withoutRootNamespace, withoutSuffix: $withoutSuffix)); + } + + public function fullClassNameProvider(): \Generator + { + yield ['Controller\MyController', null, false, false, 'App\Controller\MyController']; + yield ['Controller\MyController', null, true, false, 'Controller\MyController']; + yield ['Controller\MyController', null, false, true, 'App\Controller\My']; + yield ['Controller\MyController', null, true, true, 'Controller\My']; + yield ['Controller\MyController', 'Custom', false, false, 'Custom\Controller\MyController']; + yield ['Controller\MyController', 'Custom', true, false, 'Controller\MyController']; + yield ['Controller\MyController', 'Custom', false, true, 'Custom\Controller\My']; + yield ['Controller\MyController', 'Custom', true, true, 'Controller\My']; + } } diff --git a/tests/Util/TemplateLinterTest.php b/tests/Util/TemplateLinterTest.php index aca250c4f..f5086b6e7 100644 --- a/tests/Util/TemplateLinterTest.php +++ b/tests/Util/TemplateLinterTest.php @@ -11,7 +11,9 @@ namespace Symfony\Bundle\MakerBundle\Tests\Util; +use Composer\InstalledVersions; use PHPUnit\Framework\TestCase; +use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Util\TemplateLinter; use Symfony\Component\Process\Process; @@ -28,27 +30,30 @@ public function testExceptionBinaryPathDoesntExist(): void { $this->expectExceptionMessage('The MAKER_PHP_CS_FIXER_BINARY_PATH provided: /some/bad/path does not exist.'); - new TemplateLinter(phpCsFixerBinaryPath: '/some/bad/path'); + new TemplateLinter($this->createMock(FileManager::class), phpCsFixerBinaryPath: '/some/bad/path'); } public function testExceptionThrownIfConfigPathDoesntExist(): void { $this->expectExceptionMessage('The MAKER_PHP_CS_FIXER_CONFIG_PATH provided: /bad/config/path does not exist.'); - new TemplateLinter(phpCsFixerConfigPath: '/bad/config/path'); + new TemplateLinter($this->createMock(FileManager::class), phpCsFixerConfigPath: '/bad/config/path'); } public function testPhpCsFixerVersion(): void { $this->markTestSkippedOnWindows(); - $fixerPath = \sprintf('%s/src/Resources/bin/php-cs-fixer-v%s.phar', \dirname(__DIR__, 2), TemplateLinter::BUNDLED_PHP_CS_FIXER_VERSION); + $fixerPath = \sprintf('%s/vendor/php-cs-fixer/shim/php-cs-fixer', \dirname(__DIR__, 2)); + + // Get the installed version and remove the preceding "v" + $expectedVersion = ltrim(InstalledVersions::getPrettyVersion('php-cs-fixer/shim'), 'v'); $process = Process::fromShellCommandline(\sprintf('%s -V', $fixerPath)); $process->run(); - self::assertStringContainsString(TemplateLinter::BUNDLED_PHP_CS_FIXER_VERSION, $process->getOutput()); + self::assertStringContainsString($expectedVersion, $process->getOutput()); } private function markTestSkippedOnWindows(): void diff --git a/tests/Util/fixtures/add_setter/User_bool_begins_with_is.php b/tests/Util/fixtures/add_setter/User_bool_begins_with_is.php index 7b52a6158..f55b60a4f 100644 --- a/tests/Util/fixtures/add_setter/User_bool_begins_with_is.php +++ b/tests/Util/fixtures/add_setter/User_bool_begins_with_is.php @@ -17,7 +17,7 @@ public function getId(): ?int return $this->id; } - public function setFooProp(bool $isFooProp): static + public function setIsFooProp(bool $isFooProp): static { $this->isFooProp = $isFooProp; diff --git a/tests/fixtures/make-controller/expected/FinalController.php b/tests/fixtures/make-controller/expected/FinalController.php new file mode 100644 index 000000000..30dcd8f1b --- /dev/null +++ b/tests/fixtures/make-controller/expected/FinalController.php @@ -0,0 +1,19 @@ +json([ + 'message' => 'Welcome to your new controller!', + 'path' => 'src/Controller/FooBarController.php', + ]); + } +} diff --git a/tests/fixtures/make-controller/expected/FinalControllerWithTemplate.php b/tests/fixtures/make-controller/expected/FinalControllerWithTemplate.php new file mode 100644 index 000000000..0a1c4e566 --- /dev/null +++ b/tests/fixtures/make-controller/expected/FinalControllerWithTemplate.php @@ -0,0 +1,18 @@ +render('foo_twig/index.html.twig', [ + 'controller_name' => 'FooTwigController', + ]); + } +} diff --git a/tests/fixtures/make-controller/tests/it_generates_a_controller.php b/tests/fixtures/make-controller/tests/it_generates_a_controller.php index 8884cd1fa..f0a6a5651 100644 --- a/tests/fixtures/make-controller/tests/it_generates_a_controller.php +++ b/tests/fixtures/make-controller/tests/it_generates_a_controller.php @@ -4,7 +4,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -class GeneratedControllerTest extends WebTestCase +final class GeneratedControllerTest extends WebTestCase { public function testController() { diff --git a/tests/fixtures/make-controller/tests/it_generates_a_controller_with_twig.php b/tests/fixtures/make-controller/tests/it_generates_a_controller_with_twig.php index c21887ea1..e3980fe57 100644 --- a/tests/fixtures/make-controller/tests/it_generates_a_controller_with_twig.php +++ b/tests/fixtures/make-controller/tests/it_generates_a_controller_with_twig.php @@ -4,7 +4,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -class GeneratedControllerTest extends WebTestCase +final class GeneratedControllerTest extends WebTestCase { public function testController() { diff --git a/tests/fixtures/make-controller/tests/it_generates_an_invokable_controller.php b/tests/fixtures/make-controller/tests/it_generates_an_invokable_controller.php index ed26ac147..6e6a80a22 100644 --- a/tests/fixtures/make-controller/tests/it_generates_an_invokable_controller.php +++ b/tests/fixtures/make-controller/tests/it_generates_an_invokable_controller.php @@ -4,7 +4,7 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -class GeneratedControllerTest extends WebTestCase +final class GeneratedControllerTest extends WebTestCase { public function testControllerValidity() { diff --git a/tests/fixtures/make-serializer-normalizer/EntityFixtureNormalizer.php b/tests/fixtures/make-serializer-normalizer/EntityFixtureNormalizer.php index 848110fab..4316e3b27 100644 --- a/tests/fixtures/make-serializer-normalizer/EntityFixtureNormalizer.php +++ b/tests/fixtures/make-serializer-normalizer/EntityFixtureNormalizer.php @@ -10,7 +10,7 @@ class EntityFixtureNormalizer implements NormalizerInterface { public function __construct( #[Autowire(service: 'serializer.normalizer.object')] - private NormalizerInterface $normalizer + private NormalizerInterface $normalizer, ) { } diff --git a/tests/fixtures/make-serializer-normalizer/FooBarNormalizer.php b/tests/fixtures/make-serializer-normalizer/FooBarNormalizer.php index 3f253fdbd..8a8c50aaf 100644 --- a/tests/fixtures/make-serializer-normalizer/FooBarNormalizer.php +++ b/tests/fixtures/make-serializer-normalizer/FooBarNormalizer.php @@ -9,7 +9,7 @@ class FooBarNormalizer implements NormalizerInterface { public function __construct( #[Autowire(service: 'serializer.normalizer.object')] - private NormalizerInterface $normalizer + private NormalizerInterface $normalizer, ) { } diff --git a/tests/fixtures/make-validator/expected/FooBar.php b/tests/fixtures/make-validator/expected/FooBar.php new file mode 100644 index 000000000..239fd8bf1 --- /dev/null +++ b/tests/fixtures/make-validator/expected/FooBar.php @@ -0,0 +1,21 @@ + +; +namespace App\Validator; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; -class extends ConstraintValidator +final class FooBarValidator extends ConstraintValidator { public function validate(mixed $value, Constraint $constraint): void { - /** @var $constraint */ + /* @var FooBar $constraint */ if (null === $value || '' === $value) { return; @@ -18,6 +18,7 @@ public function validate(mixed $value, Constraint $constraint): void // TODO: implement the validation here $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $value) - ->addViolation(); + ->addViolation() + ; } } diff --git a/tests/fixtures/make-voter/expected/FooBarVoter.php b/tests/fixtures/make-voter/expected/FooBarVoter.php index 2cfc1ee22..2a2cf060c 100644 --- a/tests/fixtures/make-voter/expected/FooBarVoter.php +++ b/tests/fixtures/make-voter/expected/FooBarVoter.php @@ -22,7 +22,6 @@ protected function supports(string $attribute, mixed $subject): bool protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); - // if the user is anonymous, do not grant access if (!$user instanceof UserInterface) { return false; @@ -34,7 +33,6 @@ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInter // logic to determine if the user can EDIT // return true or false break; - case self::VIEW: // logic to determine if the user can VIEW // return true or false diff --git a/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php b/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php index e38fb4ad2..f67500726 100644 --- a/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php +++ b/tests/fixtures/make-voter/expected/not_final_FooBarVoter.php @@ -22,7 +22,6 @@ protected function supports(string $attribute, mixed $subject): bool protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { $user = $token->getUser(); - // if the user is anonymous, do not grant access if (!$user instanceof UserInterface) { return false; @@ -34,7 +33,6 @@ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInter // logic to determine if the user can EDIT // return true or false break; - case self::VIEW: // logic to determine if the user can VIEW // return true or false