Over a million developers have joined DZone.

Composer Tips for PHP Package Authors & Users

DZone's Guide to

Composer Tips for PHP Package Authors & Users

Composer is a really important PHP tool, and has become a staple of the PHP ecosystem. Read why, and learn what's so great about Composer.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Composer is probably one of the most significant tools for the PHP ecosystem. It has become the de facto standard for package management in PHP. Still, Composer has some hidden or not so obvious features an functionalities of which many developers are unaware. Whether you are a package author and maintaner, or you are consuming some package in your own project, there are some standards and best practices when using Composer that you should be familiar with and which you should follow. This article provides some useful advices in this regard.

Package Authors

As a developer of some PHP library, you tend to offer to a community, to make it useful for other developers. However, that needs to be done responsibly and appropriately. Here are some things that are most often neglected, but which all authors should adhere to.

Tag Releases

Please tag releases of your package! And when tagging, do it semantically. Do not force other developers to depend on the dev-master, which is unstable, in terms of Composer's stability flags, but also in sense that it can change at any time.

You can tag releases manually, by creating/pushing VCS tags, or if your project is hosted on GitHub, you can create a release on your project’s page.


Composer has its own autoloading mechanism, and besides autoloading specification for your sources, you can define it in case of classes from your tests namespace (you write unit tests for your library, right?). Many developers do not care and set autoloading in their composer.json this way:

    "autoload": {
        "psr-4": { 
            "MyLibrary\\": "src/",
            "MyLibrary\\Tests\\": "tests/"

But this is bad. Do NOT pollute the autoloader in production when your package is used as a dependency. Composer also has the autoload-dev property for development purposes, and here is the proper autoloading specification for this example:

    "autoload": {
        "psr-4": { "MyLibrary\\": "src/" }
    "autoload-dev": {
        "psr-4": { "MyLibrary\\Tests\\": "tests/" }

Optionally, you can optimize autoloading of your library sources, by specifying classmap rules:

    "autoload": {
        "psr-4": { "MyLibrary\\": "src/" },
        "classmap": ["src/"]

Branch Alias

A branch alias is a property that can be put into the composer.json, for the purpose of mapping non-version-like branch names (i.e. master, develop) to comparable version names (i.e. 1.0, 2.1.0). This is useful in cases when someone wants to require the latest dev-master, and there may be a problem if other packages require 1.0.*, which will result in a conflict, since dev-master does not match the 1.0.* constraint. Aliases can be defined under the extra section in composer.json, for example:

    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"

Composer documentation has a great article on aliasing itself.

Branch aliasing is not so crucial thing to do, because package users can still bypass this obstacle by requiring inline aliases.

Package Users

We PHP developers enjoy the benefits of having plethora of useful libraries that can be easily integrated into our project using Composer. Still, one should pay attention to some important details.

Accurate Version Requirements

When setting version requirements of your dependencies, you want to be as accurate as possible. Declaring a version constraint of * should be avoided at all costs. Also, depending on the latest development version, in other words using dev-master constraint, is in my opinion careless and risky, and such approach should be avoided, even if package does not have releases, which is a flaw for which its maintainer is responsible for.

At the very least you should specify constraints in form of patterns with a * wildcard, for example: 1.0.*, 2.* and similar. But besides wildcard operator, Composer offers so called “next significant release” operators – tilde and caret, which are mostly useful for projects respecting semantic versioning and are recommended for maximizing inter-package compatibility.

Still, there is always a risk that some sort of bug or BC break will eventually emerge in some of your dependencies that will break your code. Of course, you can be strict and specify exact version of a package, but as a consequence, this approach can lead to a situation where dependency resolution will ultimately fail and abort any install or update procedures, if some other dependency requires a different version.


The purpose of the lock file is to record the exact versions of dependencies in your project. This means that after dependencies are installed and composer.lock file is committed, when your colleague runs composer install, he/she will get same versions of dependencies, even if newer versions were released in the meantime.

Question that often arises regarding composer.lock file is whether it should be committed to the repo at all. This is the everlasting debate, huh? But in my case, there’s no dilemma, I follow these simple rules:

  • Always commit composer.lock in case of applications
  • Never commit composer.lock in case of components

In other words, this means that you should commit the composer.lock file only for applications, but you should never commit it in case of a library someone else is installing and using in his/her project.

Reasons in case of applications are obvious. Putting composer.lock into version makes sure that you and your teammates are using the exact same versions of your dependency packages. What’s more important, it ensures that the same dependencies are used in both production and development environments. It also means that there is no need for version lookups nor dependency resolution, which speeds up the deployment itself.

As for the reasons for not committing composer.lock in case of components development, if you do lock dependencies to a certain version, you might won’t realize that your library is not working with some newer version, even if your CI build is passing, because it will always run with exact same version of a dependency.

Production Deploy

Ok, now there is no doubt, composer.lock is committed, so deploy should be pretty straightforward, you only need to run composer install command which will read locked versions of dependencies and install them, right? Not quite.

In production environment, you want to make sure that all the development stuff is excluded. You do that by using –no-dev flag:

composer install –no-dev

This will completely omit installation of all the packages required for development (require-dev packages), but also, Composer autoloader generation will skip autoload-dev rules.

One more flag that is very desirable in the production is --optimize-autoloader. It will generate a class-map based on regular autoloading rules (PSR-0/4) which will speed up autoloading in your application.

With these in mind, here the recommended way of running composer install when performing a production deploy:

composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader


Tips for package authors:

Tips for package users:

  • Be as accurate as possible when defining version requirements; use Next significant release operators.
  • Always commit composer.lock in case of applications, never in case of components
  • Production deploy install command: composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader


Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

php ,composer ,web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}