symfony-cmf / Routing

Routing component building on the Symfony Routing component
Other
289 stars 70 forks source link

TypeError for LazyRouteCollection::all() when inner route provider returns generator from getRoutesByNames() #278

Closed andrew-demb closed 2 years ago

andrew-demb commented 2 years ago

Environment

Symfony packages

$ composer show --latest 'symfony/*'
symfony/asset                      v6.1.0  v6.1.0  Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files
symfony/browser-kit                v6.1.0  v6.1.0  Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically
symfony/cache                      v5.4.9  v6.1.2  Provides an extended PSR-6, PSR-16 (and tags) implementation
symfony/cache-contracts            v2.5.1  v3.1.1  Generic abstractions related to caching
symfony/config                     v6.1.0  v6.1.0  Helps you find, load, combine, autofill and validate configuration values of any kind
symfony/console                    v5.4.9  v6.1.2  Eases the creation of beautiful and testable command line interfaces
symfony/debug-bundle               v6.1.0  v6.1.0  Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework
symfony/dependency-injection       v6.1.0  v6.1.2  Allows you to standardize and centralize the way objects are constructed in your application
symfony/deprecation-contracts      v3.1.0  v3.1.1  A generic function and convention to trigger deprecation notices
symfony/dom-crawler                v6.1.0  v6.1.0  Eases DOM navigation for HTML and XML documents
symfony/dotenv                     v6.1.0  v6.1.0  Registers environment variables from a .env file
symfony/error-handler              v6.1.0  v6.1.0  Provides tools to manage errors and ease debugging PHP code
symfony/event-dispatcher           v6.1.0  v6.1.0  Provides tools that allow your application components to communicate with each other by dispatching events and listening to them
symfony/event-dispatcher-contracts v3.1.0  v3.1.1  Generic abstractions related to dispatching event
symfony/expression-language        v5.4.8  v6.1.2  Provides an engine that can compile and evaluate expressions
symfony/filesystem                 v6.1.0  v6.1.0  Provides basic utilities for the filesystem
symfony/finder                     v6.1.0  v6.1.0  Finds files and directories via an intuitive fluent interface
symfony/flex                       v1.19.1 v2.2.2  Composer plugin for Symfony
symfony/form                       v6.1.1  v6.1.2  Allows to easily create, process and reuse HTML forms
symfony/framework-bundle           v5.4.9  v6.1.2  Provides a tight integration between Symfony components and the Symfony full-stack framework
symfony/http-foundation            v6.1.1  v6.1.2  Defines an object-oriented layer for the HTTP specification
symfony/http-kernel                v6.1.1  v6.1.2  Provides a structured process for converting a Request into a Response
symfony/mime                       v6.1.1  v6.1.1  Allows manipulating MIME messages
symfony/options-resolver           v6.1.0  v6.1.0  Provides an improved replacement for the array_replace PHP function
symfony/password-hasher            v6.1.0  v6.1.0  Provides password hashing utilities
symfony/polyfill-apcu              v1.26.0 v1.26.0 Symfony polyfill backporting apcu_* functions to lower PHP versions
symfony/polyfill-ctype             v1.26.0 v1.26.0 Symfony polyfill for ctype functions
symfony/polyfill-intl-grapheme     v1.26.0 v1.26.0 Symfony polyfill for intl's grapheme_* functions
symfony/polyfill-intl-icu          v1.26.0 v1.26.0 Symfony polyfill for intl's ICU-related data and classes
symfony/polyfill-intl-idn          v1.26.0 v1.26.0 Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions
symfony/polyfill-intl-normalizer   v1.26.0 v1.26.0 Symfony polyfill for intl's Normalizer class and related functions
symfony/polyfill-mbstring          v1.26.0 v1.26.0 Symfony polyfill for the Mbstring extension
symfony/polyfill-php72             v1.26.0 v1.26.0 Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions
symfony/polyfill-php73             v1.26.0 v1.26.0 Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions
symfony/polyfill-php80             v1.26.0 v1.26.0 Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions
symfony/polyfill-php81             v1.26.0 v1.26.0 Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions
symfony/process                    v6.1.0  v6.1.0  Executes commands in sub-processes
symfony/property-access            v6.1.0  v6.1.0  Provides functions to read and write from/to an object or array using a simple string notation
symfony/property-info              v6.1.1  v6.1.2  Extracts information about PHP class' properties using metadata of popular sources
symfony/psr-http-message-bridge    v2.1.2  v2.1.2  PSR HTTP message bridge
symfony/routing                    v6.1.1  v6.1.1  Maps an HTTP request to a set of configuration variables
symfony/security-bundle            v6.1.0  v6.1.0  Provides a tight integration of the Security component into the Symfony full-stack framework
symfony/security-core              v6.1.0  v6.1.2  Symfony Security Component - Core Library
symfony/security-csrf              v6.1.0  v6.1.0  Symfony Security Component - CSRF Library
symfony/security-http              v6.1.1  v6.1.2  Symfony Security Component - HTTP Integration
symfony/serializer                 v5.4.9  v6.1.2  Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.
symfony/service-contracts          v2.5.1  v3.1.1  Generic abstractions related to writing services
symfony/stopwatch                  v6.1.0  v6.1.0  Provides a way to profile code
symfony/string                     v6.1.0  v6.1.2  Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way
symfony/templating                 v6.1.0  v6.1.0  Provides all the tools needed to build any kind of template system
symfony/translation                v6.1.0  v6.1.0  Provides tools to internationalize your application
symfony/translation-contracts      v3.1.0  v3.1.1  Generic abstractions related to translation
symfony/twig-bridge                v6.1.0  v6.1.2  Provides integration for Twig with various Symfony components
symfony/twig-bundle                v6.1.1  v6.1.1  Provides a tight integration of Twig into the Symfony full-stack framework
symfony/validator                  v5.4.8  v6.1.1  Provides tools to validate values
symfony/var-dumper                 v6.1.0  v6.1.0  Provides mechanisms for walking through any arbitrary PHP variable
symfony/var-exporter               v6.1.1  v6.1.1  Allows exporting any serializable PHP data structure to plain PHP code
symfony/web-link                   v6.1.0  v6.1.0  Manages links between resources
symfony/web-profiler-bundle        v6.1.1  v6.1.2  Provides a development tool that gives detailed information about the execution of any request
symfony/webpack-encore-bundle      v1.14.1 v1.14.1 Integration with your Symfony app & Webpack Encore!
symfony/yaml                       v6.1.0  v6.1.2  Loads and dumps YAML files

Symfony CMF packages

$ composer show --latest 'symfony-cmf/*'
symfony-cmf/routing 3.0.0 3.0.0 Extends the Symfony routing component for dynamic routes and chaining several routers

Subject

Signature of \Symfony\Cmf\Component\Routing\RouteProviderInterface::getRoutesByNames allows to return any iterable value (https://github.com/symfony-cmf/Routing/blob/c85d3667262a33ae71653da6f7e66c96aa8b7043/src/RouteProviderInterface.php#L80-L82). But \Symfony\Cmf\Component\Routing\LazyRouteCollection::all (https://github.com/symfony-cmf/Routing/blob/c85d3667262a33ae71653da6f7e66c96aa8b7043/src/LazyRouteCollection.php#L35-L43) which uses mentioned above method forces return type to be an array.

Steps to reproduce

  1. Implement \Symfony\Cmf\Component\Routing\RouteProviderInterface interface with returning a generator from \Symfony\Cmf\Component\Routing\RouteProviderInterface::getRoutesByNames method
  2. Initiate calling for \Symfony\Cmf\Component\Routing\LazyRouteCollection::all (with wrapped provider implemented in previous step). For Symfony application this can be initiated with bin/console debug:router command.

Expected results

No errors

Actual results

Console output:

$  bin/console debug:router -v

In LazyRouteCollection.php line 42:

[TypeError]
Symfony\Cmf\Component\Routing\LazyRouteCollection::all(): Return value must be of type array, Generator returned

Exception trace:
at /app/vendor/symfony-cmf/routing/src/LazyRouteCollection.php:42
Symfony\Cmf\Component\Routing\LazyRouteCollection->all() at /app/vendor/symfony/routing/RouteCollection.php:160
Symfony\Component\Routing\RouteCollection->addCollection() at /app/vendor/symfony-cmf/routing/src/ChainRouteCollection.php:62
Symfony\Cmf\Component\Routing\ChainRouteCollection->all() at /app/vendor/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php:57
Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor->describeRouteCollection() at /app/vendor/symfony/framework-bundle/Console/Descriptor/Descriptor.php:47
Symfony\Bundle\FrameworkBundle\Console\Descriptor\Descriptor->describe() at /app/vendor/symfony/console/Helper/DescriptorHelper.php:65
Symfony\Component\Console\Helper\DescriptorHelper->describe() at /app/vendor/symfony/framework-bundle/Command/RouterDebugCommand.php:132
Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand->execute() at /app/vendor/symfony/console/Command/Command.php:298
Symfony\Component\Console\Command\Command->run() at /app/vendor/symfony/console/Application.php:1042
Symfony\Component\Console\Application->doRunCommand() at /app/vendor/symfony/framework-bundle/Console/Application.php:96
Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /app/vendor/symfony/console/Application.php:299
Symfony\Component\Console\Application->doRun() at /app/vendor/symfony/framework-bundle/Console/Application.php:82
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /app/vendor/symfony/console/Application.php:171
Symfony\Component\Console\Application->run() at /app/bin/console:29

debug:router [--show-controllers] [--format FORMAT] [--raw] [--] [<name>]

Here's a reproducing test:

<?php

declare(strict_types=1);

namespace Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Cmf\Component\Routing\LazyRouteCollection;
use Symfony\Cmf\Component\Routing\RouteProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
use Symfony\Component\Routing\Route as SymfonyRoute;
use Symfony\Component\Routing\RouteCollection;

final class IssueTest extends TestCase
{
    public function testIssue(): void
    {
        $provider = new class implements RouteProviderInterface {
            public function getRouteCollectionForRequest(Request $request): RouteCollection
            {
                return new RouteCollection();
            }

            public function getRouteByName(string $name): SymfonyRoute
            {
                throw new RouteNotFoundException();
            }

            public function getRoutesByNames(?array $names = null): iterable
            {
                yield from [];
            }
        };

        $c = new LazyRouteCollection($provider);

        $this->assertSame([], $c->all());
    }
}
dbu commented 2 years ago

thanks for the fix in #279 !