17 Composer best practices you must know (updated to 22)

Posted by cherubrock74 on Mon, 29 Nov 2021 13:46:53 +0100

This is a community collaborative translation article. The translation has been completed. For more information, please click Introduction to collaborative translation .

Although most PHP developers know how to use Composer, not everyone is using it effectively or in the best way. So I decided to summarize something important to my daily workflow. The idea of most techniques is_ 「 Play it safe 」_, This means that if there are more ways to deal with something, I will use the least error prone method.

Tip 1: reading documents

i mean it. Official documents Well written. It only takes a few hours to read now, which will save you a lot of time in the future. You'll be surprised how much Composer can.

Tip 2: recognize the difference between "project" and "library"

It is important to realize whether you are creating a "project" or a "library". There are great differences between the two in the process of use.

A library is a reusable package that needs to be added as a dependency - such as symfony/symfony, doctrine/orm, or elasticsearch/elasticsearch.

A typical project is an application that depends on multiple libraries. It is usually not reusable (other projects do not need it to be a dependency). Applications such as e-commerce websites and customer service systems are typical examples.

In the following Tip, I will explain the difference between libraries and projects in more detail.

Tip 3: use the specified dependent version for the application

When creating an application, you should define dependencies with the clearest version number. If you need to parse YAML files, you should specify dependencies in the form of "symfony/yaml": "4.0.2". Even if the dependent library follows Semantic version Specifications will also destroy backward compatibility due to different version numbers and revision numbers. For example, using the form "symfony/symfony": "^3.1", there may be something discarded in version 3.2, which will destroy your application to pass the test in that version. Or maybe in PHP\_ There is a fixed bug in codesniffer, and the code will detect new format problems, which will lead to wrong construction again.

Rely on the upgrade to be cautious, can not hit the luck. The following Tip will explain this in more detail.

It sounds alarmist, but paying attention to this point will prevent your partner from accidentally updating all dependencies when adding new libraries to the project (this may be ignored during code review).

Tip 4: use version range for library dependencies

When creating a library, you should define the maximum range of available versions as much as possible. For example, if you create a library and want to use symfony/yaml library for YAML parsing, you should write as follows:`
"symfony/yaml": "^3.0 || ^4.0"

This means that the library can Symfony 3.x Or 4.x Used in any version of `symfony/yaml` . This is important because this version constraint is passed to applications that use the library. In case there is a conflict between the requests of two libraries, for example, one needs to `~3.1.0` ,Another need `~3.2.0` ,The installation will fail. [![image](https://upload-images.jianshu.io/upload_images/27242968-98ca24f983e8ace0.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang translated 1 week ago [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F153%2Fvote "https://laravel-china.org/translations/7609/sections/153/vote ") [retranslation]( https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F153 "https://laravel-china.org/translations/sections/153 ")

# Tip 5: when developing an application, submit the 'composer.lock' file to the git version library


Created _A project_,Be sure to `composer.lock` File submitted to git This will ensure that everyone - you, your partners, your friends CI Server and your product server - the application running has the same dependent version. At first glance, it seems to add to the snake Tip #It has been mentioned in 3 to use explicit version number constraints. This is not redundant. You should know that the dependencies of the dependencies you use are not bound by these constraints (for example, 'symfony/console' also depends on 'symfony / Polyfill mbstring'). If you do not submit the 'composer.lock' file, you will not get the dependency collection of the same version. [! [image](https://upload-images.jianshu.io/upload_images/27242968-085517b8e2d0e050.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang translated 1 week ago [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F154%2Fvote "https://laravel-china.org/translations/7609/sections/154/vote ") [retranslation]( https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F154 "https://laravel-china.org/translations/sections/154 ")

Tip #6: The development library should add the 'composer.lock' file to the '. gitignore' file
----------------------------------------------------

establish _A library_ (For example, call `acme/my-library`), This shouldn't be `composer.lock` File submitted to git The file is in the library. The file is not valid for projects that use the library It  [There will be no impact](https://link.juejin.cn/?target=https%3A%2F%2Fgetcomposer.org%2Fdoc%2F02-libraries.md%23lock-file "https://getcomposer.org/doc/02-libraries.md#lock-file") Suppose 'acme / my library' uses' monolog/monolog 'as a dependency. You have submitted' composer.lock 'in the version library, and everyone developing' acme / my library 'may be using the old version of Monolog. After the library is developed and used in the actual project, it may be that the installed Monolog is a new version, and it will be connected with the inventory at this time Incompatible. But you never noticed the compatibility problem before, because of this' composer.lock '!

Therefore, the best way to deal with it is to `composer.lock` Add to `.gitignore` File, which avoids the problems caused by accidentally submitting it to the version library.

If you also want to ensure that the library is compatible with different versions of its dependencies, continue to the next section Tip !

 [![image](https://upload-images.jianshu.io/upload_images/27242968-64a472f50df347eb.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")

# Tip 7: different versions of Travis CI build dependencies


> current Tip For libraries only (specify the specific version number for applications).

If you're building an open source library, chances are you'll use it Travis CI To run the build process.

By default, in `composer.json` Where document constraints permit, composer The installation will install the latest possible version of the dependency. This means that for `^3.0 || ^4.0` With such dependency constraints, build installations always use the latest v4 Version distribution package. 3.0 The version will not be tested at all, and the built library may be incompatible with the version, and your users will cry. Fortunately, composer A switch is provided for installing low version dependencies  `--prefer-lowest` (Should be used `--prefer-stable` ,Can prevent installation of unstable versions). Uploaded `.travis.yml` The configuration is similar to the following format:```
language: php

php:
  - 7.1
  - 7.2

env:
  matrix:
    - PREFER_LOWEST="--prefer-lowest --prefer-stable"
    - PREFER_LOWEST=""

before_script:
  - composer update $PREFER_LOWEST

script:
  - composer ci

See code for details my mhujer/fio-api-php library and the build matrix on Travis CI

Although this solves most incompatibilities, it is important to remember that there are too many combinations between the minimum and maximum versions of dependencies. They may still be incompatible.

Tip 8: sort packages in require and require dev by name

Sorting packages in require and require dev by name is a good practice. This avoids unnecessary merge conflicts when spawning a branch. If you add a package to the end of the list in two branch files, you may encounter a conflict every time you merge. Sorting packages manually can be tedious, so the best way is in composer.json Configure it You can:`
{
...

"config": {
    "sort-packages": true
},

...
}

Later `require` A new package will be automatically added to a correct location (not to the end).

 [![image](https://upload-images.jianshu.io/upload_images/27242968-61c883a85581fef9.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Fusers%2F12155 "https://laravel-china.org/users/12155")bigqiang translated 1 week ago [0](https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2F7609%2Fsections%2F157%2Fvote "https://laravel-china.org/translations/7609/sections/157/vote ") [retranslation]( https://link.juejin.cn/?target=https%3A%2F%2Flaravel-china.org%2Ftranslations%2Fsections%2F157 "https://laravel-china.org/translations/sections/157 ")

# Tip 9: do not merge ` composer.lock during version derivation or merging` 
---------------------------------------

If you are `composer.json` (and `composer.lock`)A new dependency is added to the main branch, and another dependency is added to the main branch before the branch is merged. At this time, you need to derivative your branch. Then `composer.lock` The file will get a merge conflict. Never try to resolve the conflict manually because `composer.lock` The file contains definitions `composer.json` So even if you resolve the conflict, the final merge result lock The file is still wrong. The best solution is to create one in the project root directory with the following line of code `.gitattributes` File, it will tell git Don't try to be right `composer.lock` To merge files:```
/composer.lock -merge

recommend Trunk Based Development Use temporary feature branches to correct this problem. When you have a temporary branch that needs to be merged immediately, the risk of merging the composer.lock file is minimal. You can even create a branch just to add a dependency and merge immediately.

What if the composer.lock encounters a merge conflict during the derivation process? Use the main branch version to solve it. In this way, only modify the composer.json file (add a new package) . then run composer update --lock to update the modification of composer.json file to composer.lock file. Now submit the updated composer.lock file to the version staging area, and then continue the derivation operation.

Tip 10: understand the difference between require and require dev

It is very important to be aware of the difference between require and require dev modules. Packages that need to run in applications or libraries should be defined in require (e.g. Symfony, Doctrine, Twig, Guzzle,...) If you are creating a library, pay attention to what is defined as require, because each dependency of this part is also the dependency of the application using the library.
The packages required to develop applications (or libraries) should be defined in require dev (for example: PHPUnit, PHP\_CodeSniffer, PHPStan).

Tip 11: safely upgrade dependencies

I think everyone has a consensus on the following facts: dependencies should be upgraded regularly. What I want to discuss here is that dependency upgrading should be done in the open and with caution, not because of the needs of other tasks. If the library is upgraded while reconstructing the application, it is difficult to distinguish whether the cause of application crash is caused by reconstruction or upgrading.

You can use the composer updated command to see which dependencies need to be upgraded. It is smart to append a -- direct (or - D) parameter switch, which will only view the dependencies specified by composer.json. There is also a - m parameter switch, which will only view the upgrade list of minor version numbers.

The following steps should be followed for upgrading the dependencies of each old version:

  1. Create new branch
  2. Update the dependency version to the latest version number in the composer.json file
  3. Run composer update phpunit/phpunit -- with dependencies (replace phpunit/phpunit with the upgraded Library)
  4. Check the CHANGELOG file in the version library on Github for major changes. If so, upgrade the application
  5. Test the application locally (you can also see a deprecation warning in the debug bar if you use Symfony)
  6. Submit the modifications (including the necessary modifications for the normal operation of composer.json, composer.lock and other new versions)
  7. Wait until the CI build is completed
  8. Merge and deploy

Sometimes you need to upgrade multiple dependencies at one time, such as upgrading Doctrine or Symfony. In this case, you need to list them all in the upgrade command:

composer update symfony/symfony symfony/monolog-bundle --with-dependencies

Or use wildcards to upgrade all dependencies of the specified namespace:

composer update symfony/* --with-dependencies

It's all tedious work, but it provides additional protection against inadvertently upgrading dependencies.

An acceptable shortcut is to upgrade all dependencies in require dev at once (if the program code has not been modified, otherwise it is recommended to create a separate branch for code review).

Tip 12: define other types of dependencies in composer.json

In addition to defining the library as a dependency, you can also define other things here.

You can define the PHP versions supported by applications and libraries:

"require": {
    "php": "7.1.* || 7.2.*",
},

It can also define the extensions required by applications and libraries. This is very useful when trying to dock your own applications, or when your partner sets up the application environment for the first time.

"require": {
    "ext-mbstring": "*",
    "ext-pdo_mysql": "*",
},

(when Inconsistent extended version When, the version number should be *).

Tip 13: validate composer.json during CI build

Composer.json and composer.lock should always be synchronized. Therefore, it is a good idea to keep automatic checking for them all the time. Adding this as part of your build script will ensure that composer.lock and composer.json are synchronized:

composer validate --no-check-all --strict

Tip 14: using the Composer plug-in in PHPStorm

Here's one composer.json plugin for PHPStorm When manually modifying composer.json, the plug-in will automatically complete and perform some verification

If you are using another IDE (or just an editor), you can use its JSON schema Set up validation

Tip 15: specify the PHP version number of the production environment in composer.json

If you're like me, sometimes Run the latest pre release version of PHP in the local environment , there is a risk that the version of the upgrade dependency cannot run in the production environment. Now I'm using PHP 7.2.0, which means that the library I installed may not run in version 7.1. If the production environment is running version 7.1, the installation will fail. However, don't worry. There is a very simple solution. Just indicate the PHP version number of the production environment in the config section of the composer.json file:

"config": {
    "platform": {
        "php": "7.1"
    }
}

Don't confuse it with the setting of the require part. Its function is different. Your application can run under version 7.1 or 7.2, and the platform version is specified as 7.1 (which means that the upgraded version of the dependency should be compatible with platform version 7.1):

"require": {
    "php": "7.1.* || 7.2.*"
},
"config": {
    "platform": {
        "php": "7.1"
    }
}

Tip 16: use private packages on own managed Gitlab

vcs is recommended as the version library type, and Composer decides the appropriate method to obtain the package. For example, add a fork from Github and use its API to download the. zip file of the entire version library without cloning. However, it will be more complex for a private Gitlab installation. If vcs is used as the version library type, Composer will detect that it is a Gitlab installation and will try to use the API download package (this requires an API key. I don't want to set it, so I only use SSH clone to install): first, indicate that the version library type is git:

"repositories": [
    {
        "type": "git",
        "url": "git@gitlab.mycompany.cz:package-namespace/package-name.git"
    }
]

Then indicate the commonly used packages:

"require": {
    "package-namespace/package-name": "1.0.0"
}

Tip 17: how to temporarily use the bug repair branch under fork

If you find a bug in a public library and fix it in your own fork on Github, you need to install the library from your own version library instead of the official version Library (until the composite is repaired and the repaired version is released).

use Embedded alias Easy to handle:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/you/monolog"
        }
    ],
    "require": {
        "symfony/monolog-bundle": "2.0",
        "monolog/monolog": "dev-bugfix as 1.0.x-dev"
    }
}

Can pass Set path as the version library type Test the repair locally, and then push to update the version library.

Tip 18: use prestissimo to speed up your package installation

Composer has a hirak/prestissimo Plug in, which can be downloaded in parallel, so as to improve the installation speed of dependent packages.

So, what should you do now for such a good thing? You just need to install the plug-in globally immediately, and then it can be automatically used in all projects.

composer global require hirak/prestissimo

Tip 19: when you are not sure, test your version constraints

Even when reading the documentation After that, writing the correct version constraints is also tricky at some times. Fortunately, there are Packagist Semver Checker It can be used to check which part matches a specific constraint. It is not just analyzing version constraints. It shows the actual release version since downloading data from packgist. Check the result for symfony/symfony:^3.1.

View 1 other version

Tip 20: using authoritative class mapping files in a production environment

Should be in a production environment Generate authoritative class mapping file . this allows all classes contained in the class mapping file to load quickly without any checking to the disk file system.

You can run the following commands when the production environment is built:

composer dump-autoload --classmap-authoritative

Tip 21: configure autoload dev for testing

You also don't want to load test files in the production environment (considering the size of test files and memory usage). This can be solved by configuring autoload dev (similar to autoload):

"autoload": {
    "psr-4": {
        "Acme\\": "src/"
    }
},
"autoload-dev": {
    "psr-4": {
        "Acme\\": "tests/"
    }
},

Tip 22: try Composer script

Composer script is a lightweight tool for creating build scripts. I have some questions about this It is also mentioned.

summary

If you disagree with some points and explain why you disagree (don't forget to mark the number of tip), I will be happy.

All translations in this article are for learning and communication purposes only. Please indicate the translator, source and link of this article
Our translation work follows CC protocol , if our work infringes upon your rights and interests, please contact us in time.

Author: folding chair
Link: https://juejin.cn/post/684490...
Source: rare earth Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Topics: Android Programmer composer