diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..45c4b62 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +.* export-ignore +Tests export-ignore +phpunit export-ignore +phpunit.xml.dist export-ignore diff --git a/.github/workflows/code_checks.yaml b/.github/workflows/code_checks.yaml index b1efaba..2c82bf4 100644 --- a/.github/workflows/code_checks.yaml +++ b/.github/workflows/code_checks.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install JS dependencies run: | @@ -24,7 +24,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1'] + php: ['8.0', '8.1', '8.2'] dependencies: [highest] symfony: ['*'] include: @@ -34,11 +34,11 @@ jobs: symfony: '*' # Minimum supported dependencies with the latest supported PHP version - - php: '8.1' + - php: '8.2' dependencies: lowest symfony: '*' - - php: '8.1' + - php: '8.2' dependencies: highest symfony: '*' @@ -51,9 +51,17 @@ jobs: - php: '8.0' dependencies: highest symfony: '6.0.*' + + - php: '8.1' + dependencies: highest + symfony: '6.2.*' + + - php: '8.2' + dependencies: highest + symfony: '7.0.*' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -63,16 +71,17 @@ jobs: - name: Require Symfony version if: matrix.symfony != '*' run: | - composer global require --no-interaction --no-progress symfony/flex:^1.11 + composer global config --no-plugins allow-plugins.symfony/flex true + composer global require --no-interaction --no-progress symfony/flex:^2.2 composer config extra.symfony.require ${{ matrix.symfony }} - name: Update project dependencies - uses: ramsey/composer-install@v1 + uses: ramsey/composer-install@v2 with: dependency-versions: ${{ matrix.dependencies }} - name: Cache PHPUnit - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: vendor/bin/.phpunit key: ${{ runner.os }}-phpunit-${{ matrix.php }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 92537f6..c7fd7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog +## v3.5.0 - 2024-01-23 +- Fix TypeScript error when verbatimModuleSyntax is enabled ([#476](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/476)) +- Define RoutesResponse as a Service ([#474](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/474)) +- Ignore session in stateless requests ([#468](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/468)) +- Add option to skip registering compile hooks ([#462](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/462)) + +## v3.4.1 - 2023-12-15 +- fix: do not use BannerPlugin but newer webpack-inject-plugin instead to fix vulnerability + +## v3.4.0 - 2023-12-12 +- Allow Symfony 7.0 ([#471](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/471)) +- fix: remove webpack-inject-plugin dependency ([#464](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/464)) +- Docs only: remove $ so gitclip works ([#472](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/472)) +- Docs only: Update console note ([#463](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/463)) + +## v3.3.0 - 2023-07-04 +- add support for Windows when using the webpack plugin ([#444](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/444)) +- Add PHP 8.2 tests ([#449](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/449)) +- Phpunit config file migration ([#450](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/450))) +- Deprecation fixes (PHP 8 ([#451](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/451)) and Symfony 6.3 ([#460](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/460))) +- JSON Callback validator static call instead of new object ([#458](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/458)) +- Optimize package size by excluding tests ([#457](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/457)) + +## v3.2.1 - 2022-07-01 +- fix for webpack plugin: fosRoute.json dir created at root ([#443](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/443)) + ## v3.2.0 - 2022-06-30 - [BC break] Use Symfony Flex default path. Will break if you're still using the `web` directory and not defining the path ([#433](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/433)) - Add webpack plugin to automatically load the routes with no user interactions ([#429](https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/pull/429)) diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php index 2935d69..5218851 100644 --- a/Command/DumpCommand.php +++ b/Command/DumpCommand.php @@ -30,8 +30,13 @@ #[AsCommand('fos:js-routing:dump', 'Dumps exposed routes to the filesystem')] class DumpCommand extends Command { - public function __construct(private ExposedRoutesExtractorInterface $extractor, private SerializerInterface $serializer, private string $projectDir, private ?string $requestContextBaseUrl = null) - { + public function __construct( + private RoutesResponse $routesResponse, + private ExposedRoutesExtractorInterface $extractor, + private SerializerInterface $serializer, + private string $projectDir, + private ?string $requestContextBaseUrl = null, + ) { parent::__construct(); } @@ -132,9 +137,7 @@ private function doDump(InputInterface $input, OutputInterface $output): void $output->writeln('[file+] '.$targetPath); - $baseUrl = null !== $this->requestContextBaseUrl ? - $this->requestContextBaseUrl : - $this->extractor->getBaseUrl() + $baseUrl = $this->requestContextBaseUrl ?? $this->extractor->getBaseUrl() ; if ($input->getOption('pretty-print')) { @@ -143,20 +146,16 @@ private function doDump(InputInterface $input, OutputInterface $output): void $params = []; } - $content = $serializer->serialize( - new RoutesResponse( - $baseUrl, - $extractor->getRoutes(), - $extractor->getPrefix($input->getOption('locale')), - $extractor->getHost(), - $extractor->getPort(), - $extractor->getScheme(), - $input->getOption('locale'), - $domain - ), - 'json', - $params - ); + $this->routesResponse->setBaseUrl($baseUrl); + $this->routesResponse->setRoutes($extractor->getRoutes()); + $this->routesResponse->setPrefix($extractor->getPrefix($input->getOption('locale'))); + $this->routesResponse->setHost($extractor->getHost()); + $this->routesResponse->setPort($extractor->getPort()); + $this->routesResponse->setScheme($extractor->getScheme()); + $this->routesResponse->setLocale($input->getOption('locale')); + $this->routesResponse->setDomains($domain); + + $content = $serializer->serialize($this->routesResponse, 'json', $params); if ('js' == $input->getOption('format')) { $content = sprintf('%s(%s);', $input->getOption('callback'), $content); diff --git a/Controller/Controller.php b/Controller/Controller.php index 22f8305..f67a1e1 100644 --- a/Controller/Controller.php +++ b/Controller/Controller.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; -use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; /** * Controller class. @@ -38,16 +38,21 @@ class Controller * @param ExposedRoutesExtractorInterface $exposedRoutesExtractor the extractor service * @param bool $debug */ - public function __construct(private mixed $serializer, private ExposedRoutesExtractorInterface $exposedRoutesExtractor, array $cacheControl = [], private bool $debug = false) - { + public function __construct( + private RoutesResponse $routesResponse, + private mixed $serializer, + private ExposedRoutesExtractorInterface $exposedRoutesExtractor, + array $cacheControl = [], + private bool $debug = false, + ) { $this->cacheControlConfig = new CacheControlConfig($cacheControl); } public function indexAction(Request $request, $_format): Response { - $session = $request->hasSession() ? $request->getSession() : null; - - if ($request->hasPreviousSession() && $session->getFlashBag() instanceof AutoExpireFlashBag) { + if (!$request->attributes->getBoolean('_stateless') && $request->hasSession() + && ($session = $request->getSession())->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag + ) { // keep current flashes for one more request if using AutoExpireFlashBag $session->getFlashBag()->setAll($session->getFlashBag()->peekAll()); } @@ -68,23 +73,20 @@ public function indexAction(Request $request, $_format): Response ); } - $routesResponse = new RoutesResponse( - $this->exposedRoutesExtractor->getBaseUrl(), - $exposedRoutes, - $this->exposedRoutesExtractor->getPrefix($request->getLocale()), - $this->exposedRoutesExtractor->getHost(), - $this->exposedRoutesExtractor->getPort(), - $this->exposedRoutesExtractor->getScheme(), - $request->getLocale(), - $request->query->has('domain') ? explode(',', $request->query->get('domain')) : [] - ); + $this->routesResponse->setBaseUrl($this->exposedRoutesExtractor->getBaseUrl()); + $this->routesResponse->setRoutes($exposedRoutes); + $this->routesResponse->setPrefix($this->exposedRoutesExtractor->getPrefix($request->getLocale())); + $this->routesResponse->setHost($this->exposedRoutesExtractor->getHost()); + $this->routesResponse->setPort($this->exposedRoutesExtractor->getPort()); + $this->routesResponse->setScheme($this->exposedRoutesExtractor->getScheme()); + $this->routesResponse->setLocale($request->getLocale()); + $this->routesResponse->setDomains($request->query->has('domain') ? explode(',', $request->query->get('domain')) : []); - $content = $this->serializer->serialize($routesResponse, 'json'); + $content = $this->serializer->serialize($this->routesResponse, 'json'); if (null !== $callback = $request->query->get('callback')) { - $validator = new \JsonpCallbackValidator(); - if (!$validator->validate($callback)) { - throw new HttpException(400, 'Invalid JSONP callback value'); + if (!\JsonpCallbackValidator::validate($callback)) { + throw new BadRequestHttpException('Invalid JSONP callback value'); } $content = '/**/'.$callback.'('.$content.');'; diff --git a/DependencyInjection/FOSJsRoutingExtension.php b/DependencyInjection/FOSJsRoutingExtension.php index 8034835..aac5d86 100644 --- a/DependencyInjection/FOSJsRoutingExtension.php +++ b/DependencyInjection/FOSJsRoutingExtension.php @@ -17,8 +17,8 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; /** * @author William DURAND @@ -51,7 +51,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->setParameter( 'fos_js_routing.request_context_base_url', - $config['request_context_base_url'] ? $config['request_context_base_url'] : null + $config['request_context_base_url'] ?: null ); if (isset($config['cache_control'])) { diff --git a/Extractor/ExposedRoutesExtractor.php b/Extractor/ExposedRoutesExtractor.php index 50be07c..1c8e475 100644 --- a/Extractor/ExposedRoutesExtractor.php +++ b/Extractor/ExposedRoutesExtractor.php @@ -143,7 +143,7 @@ public function getScheme(): string /** * {@inheritDoc} */ - public function getCachePath(string $locale = null): string + public function getCachePath(?string $locale = null): string { $cachePath = $this->cacheDir.DIRECTORY_SEPARATOR.'fosJsRouting'; if (!file_exists($cachePath)) { @@ -189,7 +189,7 @@ protected function getDomainByRouteMatches($matches, $name): int|string|null $matches = array_flip(array_intersect_key($matches, array_flip($this->availableDomains))); - return isset($matches[$name]) ? $matches[$name] : null; + return $matches[$name] ?? null; } protected function extractDomainPatterns($routesToExpose): array diff --git a/Resources/config/controllers.xml b/Resources/config/controllers.xml index ecc7e54..5413804 100644 --- a/Resources/config/controllers.xml +++ b/Resources/config/controllers.xml @@ -7,6 +7,7 @@ + %fos_js_routing.cache_control% diff --git a/Resources/config/services.xml b/Resources/config/services.xml index efdd671..cd71b95 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -5,6 +5,7 @@ FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor + FOS\JsRoutingBundle\Response\RoutesResponse @@ -14,8 +15,9 @@ %kernel.cache_dir% %kernel.bundles% - + + %kernel.project_dir% diff --git a/Resources/doc/commands.rst b/Resources/doc/commands.rst index d26d8f9..d0c8c40 100644 --- a/Resources/doc/commands.rst +++ b/Resources/doc/commands.rst @@ -38,7 +38,11 @@ Or inside assetic, do .. caution:: You should follow the Symfony documentation about generating URLs - in the console: `Configuring The Request Context Globally`_. + in the console: `Forcing HTTPS on Generated URLs`_, as the console is unaware + of the host/port combination you use during a request. You can also set the + `HTTP_HOST` environment variable to hold your hostname including the port you + use (i.e. `localhost:8443`). You can also use the `setHost` and `setPort` + methods on the `Router` object to set it at runtime. .. tip:: @@ -58,4 +62,4 @@ This command lists all exposed routes: # Symfony 3 $ php bin/console fos:js-routing:debug [name] -.. _`Configuring The Request Context Globally`: http://symfony.com/doc/current/cookbook/console/sending_emails.html#configuring-the-request-context-globally +.. _`Forcing HTTPS on Generated URLs`: https://symfony.com/doc/current/routing.html#forcing-https-on-generated-urls diff --git a/Resources/doc/installation.rst b/Resources/doc/installation.rst index c829ef3..6444201 100644 --- a/Resources/doc/installation.rst +++ b/Resources/doc/installation.rst @@ -9,7 +9,7 @@ following command to download the latest stable version of this bundle: .. code-block:: bash - $ composer require friendsofsymfony/jsrouting-bundle + composer require friendsofsymfony/jsrouting-bundle This command requires you to have Composer installed globally, as explained in the `installation chapter`_ of the Composer documentation. @@ -52,7 +52,7 @@ Execute the following command to publish the assets required by the bundle: .. code-block:: bash - $ php bin/console assets:install --symlink public + php bin/console assets:install --symlink public .. _`installation chapter`: https://getcomposer.org/doc/00-intro.md @@ -60,4 +60,4 @@ Step 5: If you are using webpack, install the npm package locally ----------------------------------------------------------------- .. code-block:: bash - $ yarn add -D ./vendor/friendsofsymfony/jsrouting-bundle/Resources/ + yarn add -D ./vendor/friendsofsymfony/jsrouting-bundle/Resources/ diff --git a/Resources/doc/usage.rst b/Resources/doc/usage.rst index 8aa080c..cb04cc9 100644 --- a/Resources/doc/usage.rst +++ b/Resources/doc/usage.rst @@ -34,6 +34,22 @@ If you are using webpack and Encore to package your assets you can use the webpa Then use it simply by importing ``import Routing from 'fos-router';`` in your js or ts code +The plugin hooks into the webpack `build` and `watch` process and triggers the `fos:js-routing:dump` command automatically, +once routes have been changed. + +To avoid that, e.g. when building the frontend on a machine or docker image/layer, where no PHP is present, you can configure the +plugin to use a static dumped `routes.json` and suppress automatic recompilation of the file, by passing some options to the plugin: + +.. code-block:: js + + const FosRouting = require('fos-router/webpack/FosRouting'); + //... + Encore + .addPlugin(new FosRouting( + { target: './assets/js/routes.json' }, // <- path to dumped routes.json + false // <- set false to suppress automatic recompilation of the file + ) + ) Alternatively you can use the dump command and export your routes to json, this command will create a json file into the ``public/js`` folder: diff --git a/Resources/js/.gitattributes b/Resources/js/.gitattributes new file mode 100644 index 0000000..4ae5633 --- /dev/null +++ b/Resources/js/.gitattributes @@ -0,0 +1,3 @@ +router.test.js export-ignore +router_test.html export-ignore +run_jsunit.js export-ignore diff --git a/Resources/package.json b/Resources/package.json index 3a71964..5d3ab5c 100755 --- a/Resources/package.json +++ b/Resources/package.json @@ -1,6 +1,6 @@ { "name": "fos-router", - "version": "2.4.3", + "version": "2.5.0", "description": "A pretty nice way to use the routes generated by the FOSJsRoutingBundle in your JavaScript.", "keywords": [ "router", @@ -49,6 +49,6 @@ "prepublish": "npm run build" }, "dependencies": { - "webpack-inject-plugin": "^1.5.5" + "@bpnetguy/webpack-inject-plugin": "^2.0.4" } } diff --git a/Resources/ts/router.test-d.ts b/Resources/ts/router.test-d.ts index e9aadf7..4b65185 100644 --- a/Resources/ts/router.test-d.ts +++ b/Resources/ts/router.test-d.ts @@ -1,6 +1,6 @@ import { expectType } from 'tsd'; -import { RoutesMap } from '../js/router'; -import { Route, Router, Routing } from './router'; +import type { RoutesMap } from '../js/router'; +import { type Route, Router, Routing } from './router'; import routes from './routes.json'; expectType(Router.getInstance()); diff --git a/Resources/webpack/FosRouting.js b/Resources/webpack/FosRouting.js index d8a1996..4dccfee 100644 --- a/Resources/webpack/FosRouting.js +++ b/Resources/webpack/FosRouting.js @@ -5,7 +5,7 @@ const fs = require('fs'); const path = require('path'); const util = require('util'); -const InjectPlugin = require('webpack-inject-plugin').default; +const InjectPlugin = require('@bpnetguy/webpack-inject-plugin').default; const execFile = util.promisify(require('child_process').execFile); const readFile = util.promisify(fs.readFile); @@ -18,9 +18,10 @@ class FosRouting { locale: '', prettyPrint: false, domain: [], + php: 'php' }; - constructor(options = {}) { + constructor(options = {}, registerCompileHooks = true) { this.options = Object.assign({target: 'var/cache/fosRoutes.json'}, this.default, options, {format: 'json'}); this.finalTarget = path.resolve(process.cwd(), this.options.target); this.options.target = path.resolve(process.cwd(), this.options.target.replace(/\.json$/, '.tmp.json')); @@ -28,6 +29,7 @@ class FosRouting { if (this.options.target === this.finalTarget) { this.options.target += '.tmp'; } + this.registerCompileHooks = registerCompileHooks; } // Values don't need to be escaped because node already does that @@ -62,31 +64,39 @@ class FosRouting { } return pass; }, []); - await execFile('bin/console', ['fos:js-routing:dump', ...args]); - const content = await readFile(this.options.target); - await rmFile(this.options.target); - if (!prevContent || content.compare(prevContent) !== 0) { - await makeDir(path.basename(this.finalTarget), {recursive: true}) - await writeFile(this.finalTarget, content); - prevContent = content; - if (comp.modifiedFiles && !comp.modifiedFiles.has(this.finalTarget)) { - comp.modifiedFiles.add(this.finalTarget); + await execFile(this.options.php, ['bin/console', 'fos:js-routing:dump', ...args]); + try { + const content = await readFile(this.options.target); + await rmFile(this.options.target); + if (!prevContent || content.compare(prevContent) !== 0) { + await makeDir(path.dirname(this.finalTarget), {recursive: true}); + await writeFile(this.finalTarget, content); + prevContent = content; + if (comp.modifiedFiles && !comp.modifiedFiles.has(this.finalTarget)) { + comp.modifiedFiles.add(this.finalTarget); + } } + } catch (e) { + const logger = compiler.getInfrastructureLogger('FosRouting'); + logger.error(e.toString()); } callback(); }; - compiler.hooks.beforeRun.tapAsync('RouteDump', compile); - compiler.hooks.watchRun.tapAsync('RouteDump_Watch', (comp, callback) => { - if (!comp.modifiedFiles || !comp.modifiedFiles.has(this.finalTarget)) { - compile(comp, callback); - } else { - callback(); - } - }); + + if (this.registerCompileHooks === true) { + compiler.hooks.beforeRun.tapAsync('RouteDump', compile); + compiler.hooks.watchRun.tapAsync('RouteDump_Watch', (comp, callback) => { + if (!comp.modifiedFiles || !comp.modifiedFiles.has(this.finalTarget)) { + compile(comp, callback); + } else { + callback(); + } + }); + } new InjectPlugin(() => { return 'import Routing from "fos-router";' + - 'import routes from "' + this.finalTarget + '";' + + 'import routes from '+JSON.stringify(this.finalTarget)+';' + 'Routing.setRoutingData(routes);'; }).apply(compiler); } diff --git a/Response/RoutesResponse.php b/Response/RoutesResponse.php index e4b9aa9..332217b 100644 --- a/Response/RoutesResponse.php +++ b/Response/RoutesResponse.php @@ -17,24 +17,25 @@ class RoutesResponse { - private $routes; - - public function __construct(private string $baseUrl, RouteCollection $routes = null, - private ?string $prefix = null, private ?string $host = null, - private ?string $port = null, private ?string $scheme = null, - private ?string $locale = null, private array $domains = []) - { + protected $routes; + + public function __construct( + protected ?string $baseUrl = null, + ?RouteCollection $routes = null, + protected ?string $prefix = null, + protected ?string $host = null, + protected ?string $port = null, + protected ?string $scheme = null, + protected ?string $locale = null, + protected array $domains = [], + ) { $this->routes = $routes ?: new RouteCollection(); } - public function getBaseUrl(): string - { - return $this->baseUrl; - } - public function getRoutes(): array { $exposedRoutes = []; + foreach ($this->routes->all() as $name => $route) { if (!$route->hasOption('expose')) { $domain = 'default'; @@ -74,28 +75,78 @@ public function getRoutes(): array return $exposedRoutes; } + public function setRoutes(RouteCollection $routes): void + { + $this->routes = $routes; + } + + public function getBaseUrl(): string + { + return $this->baseUrl; + } + + public function setBaseUrl(string $baseUrl): void + { + $this->baseUrl = $baseUrl; + } + public function getPrefix(): ?string { return $this->prefix; } + public function setPrefix(?string $prefix): void + { + $this->prefix = $prefix; + } + public function getHost(): ?string { return $this->host; } + public function setHost(?string $host): void + { + $this->host = $host; + } + public function getPort(): ?string { return $this->port; } + public function setPort(?string $port): void + { + $this->port = $port; + } + public function getScheme(): ?string { return $this->scheme; } + public function setScheme(?string $scheme): void + { + $this->scheme = $scheme; + } + public function getLocale(): ?string { return $this->locale; } + + public function setLocale(?string $locale): void + { + $this->locale = $locale; + } + + public function getDomains(): array + { + return $this->domains; + } + + public function setDomains(array $domains): void + { + $this->domains = $domains; + } } diff --git a/Serializer/Denormalizer/RouteCollectionDenormalizer.php b/Serializer/Denormalizer/RouteCollectionDenormalizer.php index 03eb3d7..53ef704 100644 --- a/Serializer/Denormalizer/RouteCollectionDenormalizer.php +++ b/Serializer/Denormalizer/RouteCollectionDenormalizer.php @@ -22,7 +22,7 @@ class RouteCollectionDenormalizer implements DenormalizerInterface /** * {@inheritDoc} */ - public function denormalize(mixed $data, string $type, string $format = null, array $context = []): RouteCollection + public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): RouteCollection { $collection = new RouteCollection(); @@ -45,7 +45,7 @@ public function denormalize(mixed $data, string $type, string $format = null, ar /** * {@inheritDoc} */ - public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool + public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool { if (!is_array($data)) { return false; @@ -65,4 +65,9 @@ public function supportsDenormalization(mixed $data, string $type, string $forma return true; } + + public function getSupportedTypes(?string $format): array + { + return ['*' => false]; + } } diff --git a/Serializer/Normalizer/RouteCollectionNormalizer.php b/Serializer/Normalizer/RouteCollectionNormalizer.php index a5e7b62..eef523c 100644 --- a/Serializer/Normalizer/RouteCollectionNormalizer.php +++ b/Serializer/Normalizer/RouteCollectionNormalizer.php @@ -24,7 +24,7 @@ class RouteCollectionNormalizer implements NormalizerInterface /** * {@inheritDoc} */ - public function normalize(mixed $object, string $format = null, array $context = []): array + public function normalize(mixed $object, ?string $format = null, array $context = []): array { $collection = []; @@ -47,8 +47,13 @@ public function normalize(mixed $object, string $format = null, array $context = /** * {@inheritDoc} */ - public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool { return $data instanceof RouteCollection; } + + public function getSupportedTypes(?string $format): array + { + return [RouteCollection::class => true]; + } } diff --git a/Serializer/Normalizer/RoutesResponseNormalizer.php b/Serializer/Normalizer/RoutesResponseNormalizer.php index 0ec5617..5586916 100644 --- a/Serializer/Normalizer/RoutesResponseNormalizer.php +++ b/Serializer/Normalizer/RoutesResponseNormalizer.php @@ -24,7 +24,7 @@ class RoutesResponseNormalizer implements NormalizerInterface /** * {@inheritDoc} */ - public function normalize(mixed $object, string $format = null, array $context = []): array + public function normalize(mixed $object, ?string $format = null, array $context = []): array { return [ 'base_url' => $object->getBaseUrl(), @@ -40,8 +40,13 @@ public function normalize(mixed $object, string $format = null, array $context = /** * {@inheritDoc} */ - public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool { return $data instanceof RoutesResponse; } + + public function getSupportedTypes(?string $format): array + { + return [RoutesResponse::class => true]; + } } diff --git a/Tests/Command/DumpCommandTest.php b/Tests/Command/DumpCommandTest.php index 7f9df10..583ecda 100644 --- a/Tests/Command/DumpCommandTest.php +++ b/Tests/Command/DumpCommandTest.php @@ -14,26 +14,35 @@ namespace FOS\JsRoutingBundle\Tests\Command; use FOS\JsRoutingBundle\Command\DumpCommand; +use FOS\JsRoutingBundle\Response\RoutesResponse; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Routing\Router; +use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor; class DumpCommandTest extends TestCase { + protected RoutesResponse $routesResponse; protected $extractor; protected $router; private $serializer; public function setUp(): void { - $this->extractor = $this->getMockBuilder('FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor') + $this->routesResponse = $this->getMockBuilder(RoutesResponse::class) ->disableOriginalConstructor() ->getMock(); - $this->router = $this->getMockBuilder('Symfony\Component\Routing\Router') + $this->extractor = $this->getMockBuilder(ExposedRoutesExtractor::class) ->disableOriginalConstructor() ->getMock(); - $this->serializer = $this->getMockBuilder('Symfony\Component\Serializer\SerializerInterface') + $this->router = $this->getMockBuilder(Router::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->serializer = $this->getMockBuilder(SerializerInterface::class) ->disableOriginalConstructor() ->getMock(); } @@ -44,7 +53,12 @@ public function testExecute(): void ->method('serialize') ->will($this->returnValue('{"base_url":"","routes":{"literal":{"tokens":[["text","\/homepage"]],"defaults":[],"requirements":[],"hosttokens":[]},"blog":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog-post"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]]}},"prefix":"","host":"","scheme":""}')); - $command = new DumpCommand($this->extractor, $this->serializer, '/root/dir'); + $command = new DumpCommand( + $this->routesResponse, + $this->extractor, + $this->serializer, + '/root/dir', + ); $tester = new CommandTester($command); $tester->execute(['--target' => '/tmp/dump-command-test']); @@ -61,7 +75,12 @@ public function testExecuteCallbackOption(): void ->method('serialize') ->will($this->returnValue('{"base_url":"","routes":{"literal":{"tokens":[["text","\/homepage"]],"defaults":[],"requirements":[],"hosttokens":[]},"blog":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog-post"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]]}},"prefix":"","host":"","scheme":""}')); - $command = new DumpCommand($this->extractor, $this->serializer, '/root/dir'); + $command = new DumpCommand( + $this->routesResponse, + $this->extractor, + $this->serializer, + '/root/dir', + ); $tester = new CommandTester($command); $tester->execute([ @@ -83,7 +102,12 @@ public function testExecuteFormatOption(): void ->method('serialize') ->will($this->returnValue($json)); - $command = new DumpCommand($this->extractor, $this->serializer, '/root/dir'); + $command = new DumpCommand( + $this->routesResponse, + $this->extractor, + $this->serializer, + '/root/dir', + ); $tester = new CommandTester($command); $tester->execute([ @@ -101,7 +125,13 @@ public function testExecuteUnableToCreateDirectory(): void { $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Unable to create directory /root/dir/public/js'); - $command = new DumpCommand($this->extractor, $this->serializer, '/root/dir'); + + $command = new DumpCommand( + $this->routesResponse, + $this->extractor, + $this->serializer, + '/root/dir', + ); $tester = new CommandTester($command); $tester->execute([]); @@ -115,7 +145,12 @@ public function testExecuteUnableToWriteFile(): void ->method('serialize') ->will($this->returnValue('{"base_url":"","routes":{"literal":{"tokens":[["text","\/homepage"]],"defaults":[],"requirements":[],"hosttokens":[]},"blog":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog-post"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]]}},"prefix":"","host":"","scheme":""}')); - $command = new DumpCommand($this->extractor, $this->serializer, '/root/dir'); + $command = new DumpCommand( + $this->routesResponse, + $this->extractor, + $this->serializer, + '/root/dir', + ); $tester = new CommandTester($command); $tester->execute(['--target' => '/tmp']); diff --git a/Tests/Command/RouterDebugExposedCommandTest.php b/Tests/Command/RouterDebugExposedCommandTest.php index 0e604de..a410de0 100644 --- a/Tests/Command/RouterDebugExposedCommandTest.php +++ b/Tests/Command/RouterDebugExposedCommandTest.php @@ -18,6 +18,8 @@ use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Router; +use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor; class RouterDebugExposedCommandTest extends TestCase { @@ -26,11 +28,11 @@ class RouterDebugExposedCommandTest extends TestCase public function setUp(): void { - $this->extractor = $this->getMockBuilder('FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractor') + $this->extractor = $this->getMockBuilder(ExposedRoutesExtractor::class) ->disableOriginalConstructor() ->getMock(); - $this->router = $this->getMockBuilder('Symfony\Component\Routing\Router') + $this->router = $this->getMockBuilder(Router::class) ->disableOriginalConstructor() ->getMock(); } diff --git a/Tests/Controller/ControllerTest.php b/Tests/Controller/ControllerTest.php index 41608d2..9183b58 100644 --- a/Tests/Controller/ControllerTest.php +++ b/Tests/Controller/ControllerTest.php @@ -14,6 +14,7 @@ namespace FOS\JsRoutingBundle\Tests\Controller; use FOS\JsRoutingBundle\Controller\Controller; +use FOS\JsRoutingBundle\Response\RoutesResponse; use FOS\JsRoutingBundle\Serializer\Denormalizer\RouteCollectionDenormalizer; use FOS\JsRoutingBundle\Serializer\Normalizer\RouteCollectionNormalizer; use FOS\JsRoutingBundle\Serializer\Normalizer\RoutesResponseNormalizer; @@ -24,13 +25,16 @@ use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Serializer; +use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface; class ControllerTest extends TestCase { - private $cachePath; + protected RoutesResponse $routesResponse; + private string $cachePath; public function setUp(): void { + $this->routesResponse = new RoutesResponse(); $this->cachePath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'fosJsRouting'.DIRECTORY_SEPARATOR.'data.json'; } @@ -46,8 +50,9 @@ public function testIndexAction(): void $routes->add('blog', new Route('/blog-post/{slug}', [], [], [], 'localhost')); $controller = new Controller( + $this->routesResponse, $this->getSerializer(), - $this->getExtractor($routes) + $this->getExtractor($routes), ); $response = $controller->indexAction($this->getRequest('/'), 'json'); @@ -62,8 +67,9 @@ public function testIndexWithExplicitTokenAction(): void $routes->add('blog', new Route('/blog-post/{!slug}', [], [], [], 'localhost')); $controller = new Controller( + $this->routesResponse, $this->getSerializer(), - $this->getExtractor($routes) + $this->getExtractor($routes), ); $response = $controller->indexAction($this->getRequest('/'), 'json'); @@ -78,8 +84,9 @@ public function testIndexActionWithLocalizedRoutes(): void $routes->add('blog', new Route('/blog-post/{slug}/{_locale}', [], [], [], 'localhost')); $controller = new Controller( + $this->routesResponse, $this->getSerializer(), - $this->getExtractor($routes) + $this->getExtractor($routes), ); $response = $controller->indexAction($this->getRequest('/'), 'json'); @@ -93,8 +100,9 @@ public function testConfigCache(): void $routes->add('literal', new Route('/homepage')); $controller = new Controller( + $this->routesResponse, $this->getSerializer(), - $this->getExtractor($routes) + $this->getExtractor($routes), ); $response = $controller->indexAction($this->getRequest('/'), 'json'); @@ -110,7 +118,11 @@ public function testConfigCache(): void */ public function testGenerateWithCallback($callback): void { - $controller = new Controller($this->getSerializer(), $this->getExtractor()); + $controller = new Controller( + $this->routesResponse, + $this->getSerializer(), + $this->getExtractor(), + ); $response = $controller->indexAction($this->getRequest('/', 'GET', ['callback' => $callback]), 'json'); $this->assertEquals( @@ -119,7 +131,7 @@ public function testGenerateWithCallback($callback): void ); } - public static function dataProviderForTestGenerateWithCallback() + public static function dataProviderForTestGenerateWithCallback(): array { return [ ['fos.Router.data'], @@ -130,13 +142,21 @@ public static function dataProviderForTestGenerateWithCallback() public function testGenerateWithInvalidCallback(): void { $this->expectException(HttpException::class); - $controller = new Controller($this->getSerializer(), $this->getExtractor()); + $controller = new Controller( + $this->routesResponse, + $this->getSerializer(), + $this->getExtractor(), + ); $controller->indexAction($this->getRequest('/', 'GET', ['callback' => '(function xss(x) {evil()})']), 'json'); } public function testIndexActionWithoutRoutes(): void { - $controller = new Controller($this->getSerializer(), $this->getExtractor()); + $controller = new Controller( + $this->routesResponse, + $this->getSerializer(), + $this->getExtractor(), + ); $response = $controller->indexAction($this->getRequest('/'), 'json'); $this->assertEquals('{"base_url":"","routes":[],"prefix":"","host":"","port":null,"scheme":"","locale":"en"}', $response->getContent()); @@ -160,7 +180,12 @@ public function testCacheControl(): void 'vary' => [], ]; - $controller = new Controller($this->getSerializer(), $this->getExtractor(), $cacheControlConfig); + $controller = new Controller( + $this->routesResponse, + $this->getSerializer(), + $this->getExtractor(), + $cacheControlConfig, + ); $response = $controller->indexAction($this->getRequest('/'), 'json'); $this->assertTrue($response->headers->hasCacheControlDirective('public')); @@ -188,8 +213,9 @@ public function testExposeDomain(): void ['expose' => 'blog'], 'localhost')); $controller = new Controller( + $this->routesResponse, $this->getSerializer(), - $this->getExtractor($routes) + $this->getExtractor($routes), ); $response = $controller->indexAction($this->getRequest('/'), 'json'); @@ -217,13 +243,13 @@ public function testExposeDomain(): void $this->assertEquals('{"base_url":"","routes":{"homepage":{"tokens":[["text","\/"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_index":{"tokens":[["text","\/admin"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"admin_pages":{"tokens":[["text","\/admin\/path"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"blog_index":{"tokens":[["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]},"blog_post":{"tokens":[["variable","\/","[^\/]++","slug"],["text","\/blog"]],"defaults":[],"requirements":[],"hosttokens":[["text","localhost"]],"methods":[],"schemes":[]}},"prefix":"","host":"","port":null,"scheme":"","locale":"en"}', $response->getContent()); } - private function getExtractor(RouteCollection $exposedRoutes = null, $baseUrl = '') + private function getExtractor(?RouteCollection $exposedRoutes = null, $baseUrl = '') { if (null === $exposedRoutes) { $exposedRoutes = new RouteCollection(); } - $extractor = $this->getMockBuilder('FOS\\JsRoutingBundle\\Extractor\\ExposedRoutesExtractorInterface')->getMock(); + $extractor = $this->getMockBuilder(ExposedRoutesExtractorInterface::class)->getMock(); $extractor ->expects($this->any()) ->method('getRoutes') @@ -258,9 +284,9 @@ private function getExtractor(RouteCollection $exposedRoutes = null, $baseUrl = return $extractor; } - private function getSerializer() + private function getSerializer(): Serializer { - if (!class_exists('Symfony\\Component\\Serializer\\Serializer')) { + if (!class_exists(Serializer::class)) { $this->markTestSkipped('The Serializer component is not available.'); } @@ -273,7 +299,7 @@ private function getSerializer() ]); } - private function getRequest($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null) + private function getRequest($uri, $method = 'GET', $parameters = [], $cookies = [], $files = [], $server = [], $content = null): Request { return Request::create($uri, $method, $parameters, $cookies, $files, $server, $content); } diff --git a/Tests/DependencyInjection/FOSJsRoutingExtensionTest.php b/Tests/DependencyInjection/FOSJsRoutingExtensionTest.php index 94ee92a..b22cf1d 100644 --- a/Tests/DependencyInjection/FOSJsRoutingExtensionTest.php +++ b/Tests/DependencyInjection/FOSJsRoutingExtensionTest.php @@ -21,7 +21,7 @@ class FOSJsRoutingExtensionTest extends TestCase { public function setUp(): void { - if (!class_exists('Symfony\Component\DependencyInjection\ContainerBuilder')) { + if (!class_exists(ContainerBuilder::class)) { $this->markTestSkipped('The DependencyInjection component is not available.'); } } @@ -56,7 +56,7 @@ public function testLoadSetupsSerializerIfNotGiven(): void $this->assertEquals('{"foo":"bar"}', $serializer->serialize(['foo' => 'bar'], 'json')); } - private function load(array $configs) + private function load(array $configs): ContainerBuilder { $container = new ContainerBuilder(); diff --git a/Tests/Extractor/ExposedRoutesExtractorTest.php b/Tests/Extractor/ExposedRoutesExtractorTest.php index 3e3ea2a..e631b95 100644 --- a/Tests/Extractor/ExposedRoutesExtractorTest.php +++ b/Tests/Extractor/ExposedRoutesExtractorTest.php @@ -18,6 +18,7 @@ use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Router; /** * ExposedRoutesExtractorTest class. @@ -26,11 +27,11 @@ */ class ExposedRoutesExtractorTest extends TestCase { - private $cacheDir; + private string $cacheDir; public function setUp(): void { - if (!class_exists('Symfony\\Component\\Routing\\Route')) { + if (!class_exists(Route::class)) { $this->markTestSkipped('The Routing component is not available.'); } @@ -85,7 +86,7 @@ public function testGetRoutesWithPatterns(): void public function testGetCachePath(): void { - $router = $this->getMockBuilder('Symfony\\Component\\Routing\\Router') + $router = $this->getMockBuilder(Router::class) ->disableOriginalConstructor() ->getMock(); @@ -100,7 +101,7 @@ public function testGetHostOverHttp($host, $httpPort, $expected): void { $requestContext = new RequestContext('/app_dev.php', 'GET', $host, 'http', $httpPort); - $router = $this->getMockBuilder('Symfony\\Component\\Routing\\Router') + $router = $this->getMockBuilder(Router::class) ->disableOriginalConstructor() ->getMock(); $router->expects($this->atLeastOnce()) @@ -115,7 +116,7 @@ public function testGetHostOverHttp($host, $httpPort, $expected): void /** * @return array */ - public function provideTestGetHostOverHttp() + public function provideTestGetHostOverHttp(): array { return [ 'HTTP Standard' => ['127.0.0.1', 80, '127.0.0.1'], @@ -130,7 +131,7 @@ public function testGetHostOverHttps($host, $httpsPort, $expected): void { $requestContext = new RequestContext('/app_dev.php', 'GET', $host, 'https', 80, $httpsPort); - $router = $this->getMockBuilder('Symfony\\Component\\Routing\\Router') + $router = $this->getMockBuilder(Router::class) ->disableOriginalConstructor() ->getMock(); $router->expects($this->atLeastOnce()) @@ -167,10 +168,7 @@ public function testExposeFalse(): void } } - /** - * @return array - */ - public function provideTestGetHostOverHttps() + public function provideTestGetHostOverHttps(): array { return [ 'HTTPS Standard' => ['127.0.0.1', 443, '127.0.0.1'], @@ -180,12 +178,10 @@ public function provideTestGetHostOverHttps() /** * Get a mock object which represents a Router. - * - * @return \Symfony\Component\Routing\Router */ - private function getRouter(RouteCollection $routes) + private function getRouter(RouteCollection $routes): \Symfony\Component\Routing\Router { - $router = $this->getMockBuilder('Symfony\\Component\\Routing\\Router') + $router = $this->getMockBuilder(Router::class) ->disableOriginalConstructor() ->getMock(); $router diff --git a/Tests/Serializer/Normalizer/RouteCollectionNormalizerTest.php b/Tests/Serializer/Normalizer/RouteCollectionNormalizerTest.php index 5400bf7..6884a6b 100644 --- a/Tests/Serializer/Normalizer/RouteCollectionNormalizerTest.php +++ b/Tests/Serializer/Normalizer/RouteCollectionNormalizerTest.php @@ -17,6 +17,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouteCompiler; /** * Class RouteCollectionNormalizerTest. @@ -47,7 +48,7 @@ public function testNormalize(): void 'defaults' => [], 'requirements' => [], 'options' => [ - 'compiler_class' => 'Symfony\Component\Routing\RouteCompiler', + 'compiler_class' => RouteCompiler::class, ], 'schemes' => [], 'methods' => [], @@ -59,7 +60,7 @@ public function testNormalize(): void 'defaults' => [], 'requirements' => [], 'options' => [ - 'compiler_class' => 'Symfony\Component\Routing\RouteCompiler', + 'compiler_class' => RouteCompiler::class, ], 'schemes' => [], 'methods' => [], @@ -71,7 +72,7 @@ public function testNormalize(): void 'defaults' => [], 'requirements' => [], 'options' => [ - 'compiler_class' => 'Symfony\Component\Routing\RouteCompiler', + 'compiler_class' => RouteCompiler::class, ], 'schemes' => [], 'methods' => [], diff --git a/Tests/Serializer/Normalizer/RoutesResponseNormalizerTest.php b/Tests/Serializer/Normalizer/RoutesResponseNormalizerTest.php index 0daa830..bd71b08 100644 --- a/Tests/Serializer/Normalizer/RoutesResponseNormalizerTest.php +++ b/Tests/Serializer/Normalizer/RoutesResponseNormalizerTest.php @@ -16,13 +16,14 @@ use FOS\JsRoutingBundle\Serializer\Normalizer\RouteCollectionNormalizer; use FOS\JsRoutingBundle\Serializer\Normalizer\RoutesResponseNormalizer; use PHPUnit\Framework\TestCase; +use FOS\JsRoutingBundle\Response\RoutesResponse; class RoutesResponseNormalizerTest extends TestCase { public function testSupportsNormalization(): void { $normalizer = new RoutesResponseNormalizer(new RouteCollectionNormalizer()); - $response = $this->getMockBuilder('FOS\JsRoutingBundle\Response\RoutesResponse') + $response = $this->getMockBuilder(RoutesResponse::class) ->disableOriginalConstructor() ->getMock(); @@ -33,7 +34,7 @@ public function testSupportsNormalization(): void public function testNormalize(): void { $normalizer = new RoutesResponseNormalizer(new RouteCollectionNormalizer()); - $response = $this->getMockBuilder('FOS\JsRoutingBundle\Response\RoutesResponse') + $response = $this->getMockBuilder(RoutesResponse::class) ->disableOriginalConstructor() ->getMock(); diff --git a/Util/CacheControlConfig.php b/Util/CacheControlConfig.php index 14893f5..9b863b6 100644 --- a/Util/CacheControlConfig.php +++ b/Util/CacheControlConfig.php @@ -14,6 +14,7 @@ namespace FOS\JsRoutingBundle\Util; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; class CacheControlConfig { @@ -27,6 +28,8 @@ public function apply(Response $response): void return; } + $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, 'true'); + $this->parameters['public'] ? $response->setPublic() : $response->setPrivate(); if (is_int($this->parameters['maxage'])) { diff --git a/composer.json b/composer.json index 1ff67e3..b417f4d 100644 --- a/composer.json +++ b/composer.json @@ -17,14 +17,14 @@ ], "require": { "php": "^8.0", - "symfony/framework-bundle": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0.1", - "symfony/console": "^5.4|^6.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/serializer": "^5.4|^6.0.1|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", "willdurand/jsonp-callback-validator": "~1.1|^2.0" }, "require-dev": { - "symfony/expression-language": "^5.4|^6.0", - "symfony/phpunit-bridge": "^5.4|^6.0" + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0" }, "autoload": { "psr-4": { "FOS\\JsRoutingBundle\\": "" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 2d684b2..05cd3a3 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,19 +1,21 @@ - - - - ./Tests - - - - - - ./ - - ./Resources/ - ./Tests/ - ./vendor/ - - - + + + + ./ + + + ./Resources/ + ./Tests/ + ./vendor/ + + + + + ./Tests + +