Skip to content

Instantly share code, notes, and snippets.

@weierophinney
Last active March 21, 2020 19:59
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weierophinney/df937d870b0d8bded4d1185ef510aaed to your computer and use it in GitHub Desktop.
Save weierophinney/df937d870b0d8bded4d1185ef510aaed to your computer and use it in GitHub Desktop.
How to test migration to Laminas.

How to test Laminas Migration

It's time to start testing Laminas migration!

DO NOT USE IN PRODUCTION

The packages we are providing currently are not the final packages, and provided for testing purposes only. We are strictly looking for feedback on the migration process and its impact on your applications. Deploy at your own risk!

First off, huge kudos to Michał Bundyra (@michalbundyra and @webimpress on GitHub), for the huge amount of effort he has put into making this happen. Anything I throw at him, he has found a solution for, and the ability to migrate the repos would not be anywhere near where it is today without what he has done.

The bits and pieces

There are a few pieces to make it possible to do this testing, and I think it's important to know what they are.

  • I have taken all repositories we plan to migrate, and applied our rewrite tool to each tag and the HEAD commits of the master and develop branches ONLY. Since tarballs are only created for tagged releases, you cannot use dev-(master|develop) or * constraints.

  • From that, I have created a Composer repository at the URL https://laminas.mwop.net/repo/testing

  • We have created three userland tools to help facilitate migrations:

    • laminas/laminas-zendframework-bridge is a helper package that intercepts autoload requests for ZF classes and, if an equivalent Laminas class is present, autoloads that instead; additionally, it aliases the original ZF class to the Laminas version. This package is added to every Laminas package at every release currently, and is what allows them to be used as replacements, since the aliasing means that normal inheritance continues to work just as it did previously.

      Additionally, it provides a configuration post processor that can be registered both with zend-config-aggregator (Expressive applications) or zend-modulemanager (MVC applications) for the purposes of rewriting known keys and values to their Laminas equivalents. This feature, when used, makes most third party libraries de facto forwards compatible with Laminas.

    • laminas/laminas-dependency-plugin is a Composer plugin that intercepts install and update operations. If a request for a ZF package is detected, it will slip-stream in the equivalent Laminas package instead. This is particularly of interest following a migration to ensure that nested dependencies on ZF packages use Laminas versions.

    • laminas/laminas-migration is a command line tool to facilitate migrating a project or third-party library to target Laminas. It will update the composer.json to target Laminas packages, and rewrite references to ZF classes and configuration throughout your application to the Laminas equivalents, add a dependency on laminas/laminas-dependency-plugin, and inject the configuration post processor provided by laminas/laminas-zendframework-bridge into your MVC or Expressive application if it can.

Testing migration

Required PHP version

The migration tooling, as well as the dependency plugin and bridge package, requires PHP 5.6 and up.

While many of the package versions support previous PHP versions, we chose to support only versions supported either by PHP.net or commercially. The lowest supported version we could find was PHP 5.6. As such, you will need to update to at least that version of PHP before attempting a migration.

Ideally, we recommend upgrading to the latest stable version of PHP whenever possible.

0. Ensure you have an up-to-date Composer

Due to features of Composer our dependency plugin uses, we require Composer 1.7.0 and up. If you're unsure what version you are on, run composer --version. If you are on an older version, run composer self-update.

1. Install laminas-migration

To migrate a project, first install the laminas/laminas-migration package.

Via Composer

Install the library globally using Composer:

$ composer global require laminas/laminas-migration

If you choose this option, you will need to ensure that the vendor/bin/ subdirectory of your > global Composer installation is in your environment $PATH.

You can find where the global Composer installation is by executing:

$ composer global config home

On Linux and Mac operating systems, update your shell configuration to add that path to your $PATH environment variable.

Adding to the PATH

The mechanism for adding to your environment $PATH variable depends on your operating system.

For Linux, Mac, and other *nix variants, you can do so by adding a line like the following at the end of your profile configuration file (e.g., $HOME/.bashrc, $HOME/.zshrc, $HOME/.profile, etc.):

export PATH={path to add}:$PATH

For Windows, the situation is a bit more involved; this HOWTO provides a good tutorial on the subject.

Via cloning

Clone the repository somewhere:

$ git clone https://github.com/laminas/laminas-migration.git

Install dependencies:

$ cd laminas-migration
$ composer install

From there, either add the bin/ directory to your $PATH (see the note on adding to the PATH, above), symlink the bin/laminas-migration script to a directory in your $PATH, or create an alias to the bin/laminas-migration script using your shell:

# Adding to PATH:
$ export PATH=/path/to/laminas-migration/bin:$PATH
# Symlinking to a directory in your PATH:
$ cd $HOME/bin && ln -s /path/to/laminas-migration/bin/laminas-migration .
# creating an alias:
$ alias laminas-migration=/path/to/laminas-migration/bin/laminas-migration

Updating the migration tooling

  • Last updates: 2019-12-02

If you are not a first-time tester, and you last installed the migration tooling before the date listed above, you will need to upgrade your tooling in order to test.

Global composer installations

Run the following commands:

$ composer global remove laminas/laminas-migration
$ composer global require laminas/laminas-migration

Git checkout installations

Run the following commands:

$ cd path/to/laminas-migration
$ git fetch origin
$ git rebase origin/master
# or checkout the latest tag:
$ git checkout 0.2.0
$ composer install

2. Run the migration command

From there, enter a project you wish to migrate, and run the following:

$ laminas-migration migrate

You may want to use the --exclude or -e option one or more times for directories to exclude from the rewrite; on the ZF website and my own, I used -e data, for instance:

$ laminas-migration migrate -e data

Module and Config Post Processor injection

If you are migrating an MVC, Apigility, or Expressive application, the migration tooling attempts to inject some code in your application. This can fail if you have non-standard configuration.

  • For MVC and Apigility applications, the migration tooling attempts to add Laminas\ZendFrameworkBridge as a module to the top of the config/modules.config.php file. If injection fails, add the module in a way appropriate to your application.

  • For Expressive applications, the migration tooling attempts to add Laminas\ZendFrameworkBridge\ConfigPostProcessor as a post processor class to the ConfigAggregator constructor. The ConfigAggregator constructor has the following signature:

    public function __construct(
        array $providers = [],
        ?string $cachedConfigFile = null,
        array $postProcessors = []
    )

    Typically, the structure of the config/config.php file in an Expressive application looks like the following:

    $cacheConfig = [
        'config_cache_path' => 'data/cache/app_config.php',
    ];
    
    $aggregator = new ConfigAggregator([
        // config providers from 3rd party code
        // ...
    
        // App-specific modules
        // ...
    
        // Include cache configuration
        new ArrayProvider($cacheConfig),
    
        // Load application config in a pre-defined order in such a way that local settings
        // overwrite global settings. (Loaded as first to last):
        //   - `global.php`
        //   - `*.global.php`
        //   - `local.php`
        //   - `*.local.php`
        new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
    
        // Load development config if it exists
        new PhpFileProvider('config/development.config.php'),
    ], $cacheConfig['config_cache_path']);
    
    return $aggregator->getMergedConfig();

    As such, the migration tooling rewrites the second to last line to read:

    ], $cacheConfig['config_cache_path'], [\Laminas\ZendFrameworkBridge\ConfigPostProcessor::class]);

    In most cases, failure to inject means that the individual arguments have been pushed to their own line. In such cases, add the third argument as detailed above.

    In other cases, applications may already be using post processors. If so, add \Laminas\ZendFrameworkBridge\ConfigPostProcessor::class to the list of post processors.

3. Add the Laminas composer repository

This step will go away once we migrate the repositories to their final resting places on GitHub. However, until then, we need to add the custom repository I created:

$ composer config repositories.laminas composer https://laminas.mwop.net/repo/testing

4. Install dependencies

Once migration is done and you've added the repository, you can install dependencies:

$ composer install

5. Test

Run your unit tests, do end-to-end tests, whatever — but exercise the application in some way, and let us know if anything does not work. The more specific you can be, the better!

Please report your experiences in the #laminas-migration-testing channel on the ZF Slack.

Clear your caches

If your application is not running in development mode, you will need to clear any configuration caches you have before testing. If you are using zf-development-mode (which becomes laminas-development-mode!), try enabling development mode:

$ composer development-enable

Expressive users can use the clear-config-cache command:

$ composer clear-config-cache

Summary

To summarize the steps to test:

$ composer global require laminas/laminas-migration
$ cd some/project
$ laminas-migration migrate -e data
$ composer config repositories.laminas composer https://laminas.mwop.net/repo/testing
$ composer install

and report to the #laminas-migration-testing channel on the ZF Slack.

We want to hear ALL experiences, both if it worked and if it failed, as well as the type of application you tested migrations against. The more information we have about use cases, the more we know about how well the tool covers the various projects in the ecosystem.

Feel free to invite others to test!

Happy testing!

@samsonasik
Copy link

@michalbundyra I retried with re-run composer clear-cache. First, I now get error:

Module (LaminasDeveloperTools) could not be initialized

It seems the module.config.php injection/replace add both "LaminasDeveloperTools" and "Laminas\DeveloperTools". I removed the "LaminasDeveloperTools" from the list, then copy :

cp vendor/laminas/laminas-developer-tools/config/laminas-developer-tools.local.php.dist config/autoload/laminas-developer-tools.local.php

The developer tools show up:

ldt

@samsonasik
Copy link

samsonasik commented Nov 15, 2019

@michalbundyra the Laminas\DeveloperTools and ZendDeveloperTools namespaces seems now resolved. Thank you.

I created a legacy branch for SanSessionToolbar which can be installed with:

composer require --dev san/san-session-toolbar:dev-legacy

and add SanSessionToolbar after Laminas\DeveloperTools and it shows the session toolbar:

san-session-toolbar

@weierophinney
Copy link
Author

@shughi94

I've tried to reproduce your issue, but so far have not been able to. To reproduce, I did the following:

  • Created a new Expressive application via composer create-project.
  • Added the zendframework/zend-problem-details package.
  • Created a custom exception type that implements ProblemDetailsExceptionInterface and uses the CommonProblemDetailsExceptionTrait to provide the bulk of implementation.
  • Modified the shipped PingHandler to throw this exception.

I then served the application and accessed the /api/ping URL.

Both before and after migration, this returns an HTML page containing the exception stack trace. This is as expected; the shipped ErrorHandler middleware has no facilities for returning errors in any other format currently.

When I modified the route for the /api/ping URL to be a middleware pipeline that includes the ProblemDetailsMiddleware, I received the hoped for JSON output - as I expected. And this was true both before and after migration.

As such, I think perhaps you had a custom ErrorHandler middleware before...

@vatoer
Copy link

vatoer commented Feb 29, 2020

hello. @weierophinney samsonasik
i tried to migrate but got error

$composer install

The requested package laminas/laminas-component-installer could not be found in any version, there may be a typo in the package name.

Screen Shot 2020-03-01 at 04 38 51

and then i tried to add repositories

$ composer config repositories.laminas composer https://laminas.mwop.net/repo/testing

but i get another error

The "https://laminas.mwop.net/repo/testing/packages.json" file could not be downloaded:

Screen Shot 2020-03-01 at 04 40 53

anyone can help ?

thank you

@weierophinney
Copy link
Author

weierophinney commented Feb 29, 2020 via email

@vatoer
Copy link

vatoer commented Mar 1, 2020

hello again @weierophinney.

i removed https://laminas.mwop.net/repo/testing repositories

and then I start over with https://docs.laminas.dev/migration/

here is my composer.json , i omitted the rest

...
"minimum-stability": "dev",
    "prefer-stable": true,
    "require": {
        "php": "^5.6 || ^7.0",
        "laminas/laminas-component-installer": "^1.0",
        "laminas/laminas-mvc": "^3.0.1",
        "laminas/laminas-development-mode": "^3.0",
        "laminas/laminas-cache": "^2.7.1",
        "laminas/laminas-db": "^2.8.1",
        "laminas/laminas-mvc-form": "^1.0",
        "laminas/laminas-json": "^3.0",
        "laminas/laminas-log": "^2.9",
        "laminas/laminas-mvc-i18n": "^1.0",
        "laminas/laminas-mvc-plugins": "^1.0.1",
        "laminas/laminas-psr7bridge": "^0.2.2",
        "laminas/laminas-session": "^2.8",
        "laminas/laminas-servicemanager-di": "^1.0",
        "firebase/php-jwt": "^5.0",
        "laminas/laminas-servicemanager": "^3.3",
        "laminas/laminas-permissions-rbac": "^2.6",
        "laminas/laminas-math": "^3.1",
        "laminas/laminas-mail": "^2.10",
        "laminas/laminas-serializer": "^2.9",
        "laminas/laminas-validator": "^2.10",
        "ramsey/uuid": "^3.8",
        "vladmeh/zf3-tcpdf": "*",
        "laminas/laminas-dependency-plugin": "^1.0"
    },
..

but still got error


Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.


  Problem 1
    - The requested package laminas/laminas-component-installer could not be found in any version, there may be a typo in the package name.

any Idea ?

Thanks

@vatoer
Copy link

vatoer commented Mar 1, 2020

i've figured out

most likely because this setting in composer.json

"minimum-stability": "dev",
 "prefer-stable": true,

"repositories":
    [
        {
            "type": "composer",
            "url": "https:\/\/www.phpclasses.org\/"
        },
        {
            "packagist": false
        }
    ]

fix error after i delete these.

Thank you

@panvid
Copy link

panvid commented Mar 12, 2020

This package is added to every Laminas package at every release currently, and is what allows them to be used as replacements, since the aliasing means that normal inheritance continues to work just as it did previously.

When will the package laminas/laminas-zendframework-bridge removed? Today it was hiding a forgotten Zend replacement error with magic.

@weierophinney
Copy link
Author

@dpauli Please report such issues in the relevant repositories.

The short answer is: we'll be removing the bridge from each package when we bump to a new major version. The reason is because the packages need to act as replacements for the original packages, and, as such, need to allow users to refer to the legacy classes in order to prevent backwards compatibility breaks.

If you are seeing issues when the bridge is in place, report it against the package that depends on the bridge, or, if it's more general, against the bridge package itself.

@panvid
Copy link

panvid commented Mar 12, 2020

Thanks for the quick response, so we wait for the next major releases of each package :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment