Local Packages 3 Years Later

The first public idea about local packages was published over 3 years ago after 1 year of internal testing.

How do they stand in 2020? How people use it wrong? Are they still the best option to keep low complexity in huge projects?

Just a reminder: what are local packages?

Local packages are decoupled parts of code, located in own packages/<package-name> directory:

/app
/packages
    /file-system
        /src
            FileSystem.php
        /tests
            FileSystemTest.php
/vendor
composer.json

And loaded in composer.json with its PSR-4 namespace:

{
    "require": {
        "favorite/framework": "^5.0"
    },
    "autoload": {
        "psr-4": {
            "App\\FileSystem\\": "packages/file-system/src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\FileSystem\\Tests\\": "packages/file-system/tests"
        }
    }
}

Simple.


Do you want to know more? Look at Composer Local Packages for Dummies.

How People Use it Wrong?

1. Forgetting /src Directory

/packages
    /file-system
        FileSystem.php


/packages
    /file-system
        /src
            FileSystem.php


2. Single Autoload

{
    "autoload": {
        "psr-4": {
            "Packages\\": "packages"
        }
    }
}


{
    "autoload": {
        "psr-4": {
            "Packages\\SpecificPackage\\": "packages/specific-package/src"
        }
    }
}


3. A mix of Paths and Namespace

/packages
    /FileSystem
        /src
            FileSystem.php


/packages
    /file-system
        /src
            FileSystem.php

4. composer.json in Packages

/packages
    /file-system
        /src
            FileSystem.php
        composer.json


/packages
    /file-system
        /src
            FileSystem.php

This is only useful in case of monorepo that splits packages, e.g. Symfony, Symplify. Not for local packages.

How to Do it Right?

{
    "autoload": {
        "psr-4": {
            "App\\FileSystem\\": "packages/file-system/src",
            "App\\Auth\\": "packages/auth/src"
        }
    }
}
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
    bootstrap="vendor/autoload.php"
>
    <testsuites>
        <testsuite name="main">
            <directory>tests</directory>
            <directory>packages/*/tests</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist addUncoveredFilesFromWhitelist="false">
            <directory>src</directory>
            <directory suffix=".php">packages/*/src</directory>
        </whitelist>
    </filter>
</phpunit>

Feedback After 3 Years of Usage in Companies

I've started to test this in Lekarna.cz, 6 years old project, where they still use it. Elasticr and Recruit.is adopted local packages in ~2018, still using it.

The code is much cleaner, comfortable to dive in, and refactor.

If you're careful about all the issues above, there is nothing to stop you from making it work! Give it a try, your future team will thank you.


Happy coding!




Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!