Search code examples
phpsymfonysymfony4legacy

Symfony 4 LegacyRouteLoader and LegacyController


I'm trying to implement the LegacyController that is listed on the Symfony documentation https://symfony.com/doc/current/migration.html#legacy-route-loader

Using the custom loader configs for services and routes from: https://symfony.com/doc/current/routing/custom_route_loader.html#creating-a-custom-loader

I wish there was a complete working example listed somewhere.

When I try to run the debug:router or composer:install I just get an error. This after trying various slight variations on this initial config.

$ console debug:router

In FileLoader.php line 166:

  Invalid service id: "App\Legacy\LegacyRouteLoader\" in /var/www/site/config/services.yaml (which is loaded in resource "/var/www/site/config/services.yaml").                                                        


In ContainerBuilder.php line 991:

  Invalid service id: "App\Legacy\LegacyRouteLoader\"  

--

<?php

// src/Legacy/LegacyRouteLoader.php
namespace App\Legacy;

use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class LegacyRouteLoader extends Loader
{

    public function supports($resource, $type = null)
    {
        return 'extra' === $type;
    }

    public function load($resource, $type = null)
    {
        $collection = new RouteCollection();
        $finder = new Finder();
        $finder->files()->name('*.php');

        /** @var SplFileInfo $legacyScriptFile */
        foreach ($finder->in($this->webDir) as $legacyScriptFile) {
            // This assumes all legacy files use ".php" as extension
            $filename = basename($legacyScriptFile->getRelativePathname(), '.php');
            $routeName = sprintf('app.legacy.%s', str_replace('/', '__', $filename));

            $collection->add($routeName, new Route($legacyScriptFile->getRelativePathname(), [
                '_controller' => 'App\Controller\LegacyController::loadLegacyScript',
                'requestPath' => '/' . $legacyScriptFile->getRelativePathname(),
                'legacyScript' => $legacyScriptFile->getPathname(),
            ]));
        }

        return $collection;
    }
}

--

<?php

// src/Controller/LegacyController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\StreamedResponse;

class LegacyController
{
    public function loadLegacyScript(string $requestPath, string $legacyScript)
    {
        return StreamedResponse::create(
            function () use ($requestPath, $legacyScript) {
                $_SERVER['PHP_SELF'] = $requestPath;
                $_SERVER['SCRIPT_NAME'] = $requestPath;
                $_SERVER['SCRIPT_FILENAME'] = $legacyScript;

                chdir(dirname($legacyScript));

                include $legacyScript;
            }
        );
    }
}

--

# config/services.yaml

# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:

services:
  # default configuration for services in *this* file
  _defaults:
    autowire: true      # Automatically injects dependencies in your services.
    autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

  # makes classes in src/ available to be used as services
  # this creates a service per class whose id is the fully-qualified class name
  App\:
    resource: '../src/*'
    exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

  # controllers are imported separately to make sure services can be injected
  # as action arguments even if you don't extend any base controller class
  App\Controller\:
    resource: '../src/Controller'
    tags: ['controller.service_arguments']

  # add more service definitions when explicit configuration is needed
  # please note that last definitions always *replace* previous ones

  App\Legacy\LegacyRouteLoader\:
    tags: ['routing.loader']

--

# config/routes.yaml
#index:
#    path: /
#    controller: App\Controller\DefaultController::index

app_legacy:
  resource: .
  type: extra

Solution

  • Thanks to @cerad above, I managed to get it going with some changes.

    # config/services.yaml
    
    # This file is the entry point to configure your own services.
    # Files in the packages/ subdirectory configure your dependencies.
    
    # Put parameters here that don't need to change on each machine where the app is deployed
    # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
    parameters:
    
    services:
      # default configuration for services in *this* file
      _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
    
      # makes classes in src/ available to be used as services
      # this creates a service per class whose id is the fully-qualified class name
      App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
    
      # controllers are imported separately to make sure services can be injected
      # as action arguments even if you don't extend any base controller class
      App\Controller\:
        resource: '../src/Controller'
        tags: ['controller.service_arguments']
    
      # add more service definitions when explicit configuration is needed
      # please note that last definitions always *replace* previous ones
    
      App\Legacy\LegacyRouteLoader:
        tags: ['routing.loader']
    

    --

    <?php
    
    // src/Legacy/LegacyRouteLoader.php
    namespace App\Legacy;
    
    use Symfony\Component\Config\Loader\Loader;
    use Symfony\Component\Finder\Finder;
    use Symfony\Component\Finder\SplFileInfo;
    use Symfony\Component\Routing\Route;
    use Symfony\Component\Routing\RouteCollection;
    
    class LegacyRouteLoader extends Loader
    {
    
        private $webDir = __DIR__.'/../../public/';
    
        public function supports($resource, $type = null)
        {
            return 'extra' === $type;
        }
    
        public function load($resource, $type = null)
        {
            $collection = new RouteCollection();
            $finder = new Finder();
            $finder->files()->name('*.php');
    
            /** @var SplFileInfo $legacyScriptFile */
            foreach ($finder->in($this->webDir) as $legacyScriptFile) {
                // This assumes all legacy files use ".php" as extension
                $filename = basename($legacyScriptFile->getRelativePathname(), '.php');
                $routeName = sprintf('app.legacy.%s', str_replace('/', '__', $filename));
    
                $collection->add($routeName, new Route($legacyScriptFile->getRelativePathname(), [
                    '_controller' => 'App\Controller\LegacyController::loadLegacyScript',
                    'requestPath' => '/' . $legacyScriptFile->getRelativePathname(),
                    'legacyScript' => $legacyScriptFile->getPathname(),
                ]));
            }
    
            return $collection;
        }
    }