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?
- Register each package as a standalone line in
composer.json
:
{
"autoload": {
"psr-4": {
"App\\FileSystem\\": "packages/file-system/src",
"App\\Auth\\": "packages/auth/src"
}
}
}
- Keep
/app
separated. - Use
dash-format
for directory paths. - Use
CamelCase
for namespaces. - Use
packages/<package-name>/src
andpackages/<package-name>/tests
directory convention. - Use single root
composer.json
to autoload them all. - Use single root
phpunit.xml
to run test on them all.
<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!
Have you find this post useful? Do you want more?
Follow me on Twitter, RSS or support me on GitHub Sponsors.