Almost all modern PHP frameworks rely on Composer.
The idea is to have PHP libraries and packages you can reuse through a unique dependency manager.
Composer is ridiculously easy to install, and once you have it on your local machine, you can start using it.
Why should I use it?
The key concept is “reuse”. With Composer, you get access to an extensive range of standardized packages. This way, you don’t reinvent the wheel, and you can easily manage all dependencies.
Autoloading is also one of the killer features of Composer. Instead of doing this:
$root_dir = "/path/to/root_folder";
require $root_dir . "/lib/mylib.php";
require $root_dir . "/lib/mylib2.php";
require $root_dir . "/lib/mylib3.php";
You call only one file, vendor/autoload.php
:
require $root_dir . "/vendor/autoload.php";
No more includes/requires at the beginning of each script. Besides, Composer has a smarter way to load classes. Thanks to an internal mapping, it only loads necessary files.
Quick installation
Please follow instructions here: for Mac and Linux.
For Windows, it’s a little bit different: see instruction
Then run the following command on your terminal:
composer -v
it gives you the specific version of Composer you just installed.
First Composer init
Composer gives you a useful command to get started:
composer init
It guides you with some prompts. The typical scenario might look like that:
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (
/ ) [julien/test]:
Description []:
Author [Julien Maury xxx@unkown.com, n to skip]:
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []:
With those details, Composer will initialize your project and your dependencies. You will get a new vendor
directory where Composer downloads all packages and two files called composer.json
and composer.lock
.
If you open the composer.json
file, you may see something like:
{
"name": "julien/test",
"require": {
"stripe/stripe-php": "^7.37"
},
"authors": [
{
"name": "Julien Maury",
"email": "xxx@unkown.com"
}
]
}
In my example, Composer puts the Stripe package in the vendor
directory. I can now use the Stripe package in my code:
require $root_dir . '/vendor/autoload.php';
$stripe = new \Stripe\StripeClient("MYSECRETKEY");
Simple as that!
Where to find packages?
The most popular place is packagist, but you can add any repository provided it has a composer.json
file.
You have to declare source repositories if it’s not from packagist.org. For example:
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
],
"require": {
"monolog/monolog": "dev-bugfix"
}
}
Composer goes way beyond that
Composer is not only a great dependency manager. It solves problems. Thanks to its internal mapping system (a class loader), it only loads necessary files, so if you have several packages, it does not load all packages on every single request.
It’s reverse logic. If you need to use a class in your vendors, use it, and Composer will look into the vendors and load it for you.
You can control the way Composer autoloads your classes thanks to theautoload
setting in the composer.json
file:
"autoload": {
"classmap": [
"src/myclasses/"
]
}
With this basic rule, you tell Composer to autoload classes in the folder src/myclasses/
, but indeed you can fine-tune it a little bit more…
PSR
PSR stands for PHP Standards Recommendations. Composer allows for easily using those standards when autoloading, so it solves another problem.
Instead of using a genericclassmap
you can use a more specific sets of rulles such as “psr-4”:
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
What’s the point? With those PSR-4 rules, you have to follow some standards to name your files and namespaces/sub-namespaces. Here we have an App namespace in the src
folder.
Because we are using PSR-4, if we want to create the class App\Repository\MyRepository
, the file path must be src/Repository/MyRepository.php
. Composer resolves the path from the namespace.
Running commands vs. editing files
Composer comes with a set of useful commands you can use to add/remove dependencies.
To add a package, you can either edit the composer.json
file and add your dependency in the require section, or run in your terminal something like:
composer require stripe/stripe-php
To remove this package, run:
composer remove stripe/stripe-php
I recommend using command lines, especially for removing stuff.
Optimizing autoload
Autoloading is a powerful feature, but you can even go beyond by switching to an optimized autoload:
"config": {
"optimize-autoloader": true
}
It improves performance by 30-40%. But how?
It spares a lot of filesystem checks. Known classes return their path instantly. Besides, if you enable opcache, it’s even faster!
Composer commands are a little bit slower with this configuration, though. It needs more time to optimize autoloading, but it’s palatable.
The .lock file
Every time you run a composer command, the .lock file is updated. When you run a composer init
, this file is generated for the first time.
It saves the list of installed packages at a specific version and a hash of the composer.json
file.
It’s essential for Composer. It makes sure that people get the same packages as when you developed the project.
The create command
You can use Composer to create projects. It’s excellent to start a new code based on some skeleton.
For example, for my WordPress projects, I appreciate the Bedrock approach. It’s still WordPress but entirely reconfigured to use Composer easily.
You can run the following command to test it:
composer create-project roots/bedrock my-project
Configuration
In this section, I give configuration examples. It is not an exhausting list. Please check this page to see more options.
Vendor dir
It’s possible to change the vendor folder with the vendor-dir
config:
"config": {
"vendor-dir": "the-source"
}
In your code, just load the new path:
require $root_dir . '/the-source/autoload.php';
Sort packages
Make sure, the following goes in your composer.json
file in the config section:
"config": {
"sort-packages": true
}
It sorts packages by name, very handy for maintenance.
The platform configuration
The PHP version is vital. How can you be sure that all your dependencies are compatible with your PHP version in production?
The answer is the platform
configuration in composer.json
:
"config": {
"platform": {
"php": "7.2"
}
}
This way, all your dependencies will be updated accordingly.
Pro tips and versioning
Always commit the composer.lock
file
It’s crucial to commit some files and to ignore some directories. I think you should always commit the composer.lock
file.
Don’t try to resolve git conflicts in the composer.lock
file
This one is a classic error. In case you need to merge or rebase your work, the composer.lock
file may have some conflicts. You mustn’t attempt to resolve conflicts manually. You would get a wrong version of the file that would trigger additional errors.
Instead, run:
composer update --lock
Then, stage the resulting composer.lock
file (git add).
Note that you can add a simple line in the .gitattributes
file to tell Git to skip this file when merging:
/composer.lock -merge
Don’t put everything in the require section
If you need to add dependencies only for debugging, testing, or something specific to your local machine, it’s good practice to add them as require-dev dependencies not require.
Whenever you require something, you can either use require
or require --dev
:
composer require --dev <vendor-name>/<package-name>
The difference is essential. You don’t want all the things in production, especially when it’s not necessary. Composer will separate dev dependencies from other dependencies with a specific section in the composer.json
file:
"require-dev": {
"phpunit/phpunit": "9.2.6",
"mockery/mockery": "1.4.1"
}
If you run a composer install
, then Composer will both dev and core dependencies. You can run a composer install --no-dev
to prevent that behavior, it’s useful when preparing a release for production.
update vs. install vs. dump-autoload command
dump-autoload
The most basic one. It specifically generates the autoload. The composer install
command already includes it, so it’s unnecessary to run it after a composer installation.
install
It installs all dependencies, including external dependencies, and it generates the autoload.
update
It updates all packages. Alternatively, you can update only a specific package :
composer update stripe/stripe-php
Extending Composer with hooks
For that, use the scripts section of the composer.json
file. It gives you entry points to run some additional command (and even PHP scripts) on specific composer events such as installing, updating, or dump-autoload.
Please visit documentation. Their tutorial is extra good, nothing to add.
Note that you can call composer commands along with your custom scripts and commands.
For example, put the following in your composer.json
file:
"scripts": {
"test": [
"@composer show",
"ls -alh",
"curl -I https://dev.to"
]
}
then run:
composer test
Wrap up
I hope you know a little more about Composer, why it’s useful, and how it works.