Andrew Welch · Insights · #composer #php #package

Published , updated · 5 min read ·


Please consider 🎗 sponsoring me 🎗 to keep writing articles like this.

Composer for the Rest of Us

Learn about how you can lever­age Com­pos­er like a pro to man­age & install your depen­den­cies in PHP-based projects

Composer for the rest of us

Com­pos­er is a tool for depen­den­cy man­age­ment in PHP. This means you use it to declare the PHP pack­ages your project depends on and it will man­age them for you.

Tools like Com­pos­er can be a lit­tle intim­i­dat­ing when you first approach them, but like every­thing else, under­stand­ing how it works will get you a long way to a peace­ful & pro­duc­tive rela­tion­ship with Composer.

Much like npm in the Javascript world, Com­pos­er allows you to:

  1. List the depen­den­cies of your project in a file (composer.json)
  2. Install ver­sions of these depen­den­cies that are all com­pat­i­ble with each other
  3. Update all of these depen­den­cies in an auto­mat­ed yet struc­tured way

Com­pos­er also gen­er­ates autoload infor­ma­tion, which allows class­es from dis­parate pack­ages to be used easily.

Despite their quirks, pack­age man­agers are crit­i­cal for devel­op­ing mod­ern appli­ca­tions, because of the mix of pack­ages they depend on.

In this arti­cle, you’ll learn the impor­tant fun­da­men­tals of Com­pos­er, as well as a num­ber of pro tips & use­ful Com­pos­er com­mands you can use every day.

“Learning is a treasure that will follow its owner everywhere.” — Chinese Proverb

It’s impor­tant to under­stand some of the fun­da­men­tal con­cepts in Com­pos­er, so we can build upon them.

Link Composer Essentials

Let’s start with some def­i­n­i­tions of impor­tant things in the Com­pos­er world:

  • composer — a com­mand line tool writ­ten in PHP that you need to either install local­ly, or run via Dock­er in order to use it.
  • Pack​ag​ist​.org — the pri­ma­ry reg­istry of PHP pack­ages that are avail­able to be man­aged by Composer.
  • composer.json — the file where you list the PHP depen­den­cies your project has, as well as oth­er meta-infor­ma­tion about your project (name, descrip­tion, license, etc.)
  • composer.lock — a file gen­er­at­ed by Com­pos­er after it resolves all of your pack­age depen­den­cies, and installs them. It con­tains the exact ver­sions of every pack­age that is actu­al­ly installed.

The composer.json file is the hub of the Com­pos­er world, so let’s have a look at an exam­ple of a sim­ple composer.json file:

{
    "name": "nystudio107/composer-example",
    "description": "An example composer.json file",
    "license": "MIT",
    "authors": [
        {
            "name": "Andrew Welch",
            "email": "andrew@nystduio107.com"
        }
    ],
    "require": {
        "twig/twig": "^v3.0.0",
        "vlucas/phpdotenv": "~5.4.0",
        "elvanto/litemoji": "3.0.1"
    }
}

The composer.json file is just a stan­dard JSON that con­forms to the com­pos­er schema, which defines all of the avail­able fields and what they are used for.

You can cre­ate the composer.json file your­self in any text edi­tor, or you can use the composer init CLI com­mand to cre­ate it inter­ac­tive­ly for you.

Link The Require field

Let’s focus on the require field, because there are some crit­i­cal things going on there:

    "require": {
        "twig/twig": "^v3.0.0",
        "vlucas/phpdotenv": "~5.4.0",
        "elvanto/litemoji": "3.0.1"
    }

The require field in the composer.json file is where we spec­i­fy the pack­ages that are PHP depen­den­cies that our project needs to work.

On the left is the pack­age spec­i­fi­er, in vendor / name for­mat, and on the right is the ver­sion con­straint we want. Ver­sion con­straints are a com­bi­na­tion of start­ing pack­age ver­sion num­bers expressed as semvers (seman­tic ver­sions) and oper­a­tors that deter­mine what that pack­age can be updat­ed to.

When Com­pos­er needs to install or update your pro­jec­t’s depen­den­cies, it finds them by look­ing up the vendor/name on pack​ag​ist​.org (by default), and then uses the ver­sion con­straint to find a ver­sion that match­es your requirements.

It then resolves the require­ments of all of the oth­er pack­ages that your project uses to arrive at the most up-to-date com­bi­na­tion of pack­ages that sat­is­fy all of them.

Version numbers resolve to tagged releases of a package in their VCS repository

By default, Com­pos­er looks up the ver­sions as tags from a pack­age’s Ver­sion Con­trol Sys­tem (VCS) repos­i­to­ry (usu­al­ly GitHub).

So the ver­sion you ask for will resolve to a spe­cif­ic tagged release of the pack­age. A tag just points to a spe­cif­ic VCS com­mit in the repository.

There is also a require-dev field that is func­tion­al­ly the same as the require field, except that depen­den­cies list­ed here are intend­ed to be installed only in your local devel­op­ment environment.

require-dev is for things like tests, debug­ging & pro­fil­ing tools that you only need when devel­op­ing your project.

Link Semver Versions

Since most PHP pack­ages are using semver as a ver­sion­ing method­ol­o­gy, let’s have a look at what that means.

Semver is impor­tant because it pro­vides a stan­dard that defines what the . delim­it­ed ver­sion num­bers mean:

Php composer semver
  • oper­a­tor — tech­ni­cal­ly not part of semver, but deter­mines the ver­sion con­straint (see below)
  • major ver­sion — incre­ment­ed when the pack­age makes incom­pat­i­ble API changes
  • minor ver­sion — incre­ment­ed when the pack­age adds func­tion­al­i­ty in a back­wards com­pat­i­ble manner
  • patch ver­sion — incre­ment­ed when the pack­age makes back­wards com­pat­i­ble bug fixes

The major ver­sion, minor ver­sion, and patch ver­sion are . delim­it­ed, and togeth­er form the semver.

Occa­sion­al­ly, you might see a ver­sion with a 4th ver­sion spec­i­fi­er, such as 4.2.0.1. Some­times called a hot­fix” ver­sion, this isn’t strict­ly part of semver. But any ver­sion con­straint that receives at least patch ver­sion updates (see below) will also get hot­fix ver­sion updates.

Note that with Com­pos­er, some semvers have a v in them (v3.0.0) and some don’t (2.0.0). The v is an option­al con­ven­tion, that is stripped out by Com­pos­er before eval­u­at­ing a semver, so you can use it or not.

Now let’s have a look at how semvers are used with oper­a­tors to make a ver­sion constraint.

Link Version Constraints

The oper­a­tor in a ver­sion con­straint can be com­para­tors like <, <=, > or >=, but typ­i­cal­ly you’ll use two spe­cial operators:

  • ^ — the caret oper­a­tor spec­i­fies that you want minor & patch ver­sion updates up to, but not includ­ing the next major ver­sion
  • ~ — the tilde oper­a­tor spec­i­fies that you want patch ver­sion updates up to, but not includ­ing the next minor ver­sion

If no oper­a­tor is spec­i­fied, then you will not get any­thing oth­er than the exact ver­sion spec­i­fied via the semver.

The idea is that you spec­i­fy an ini­tial pack­age ver­sion, and use the oper­a­tor to tell Com­pos­er the con­straints it should use when updat­ing that pack­age for you.

So let’s look at the ver­sions in our exam­ple composer.json file, and what they mean:

  • "twig/twig": "^v3.0.0" — Com­pos­er will update the pack­age to any ver­sion that is less than 4.x (the next major ver­sion), so you get all minor and patch ver­sion updates
  • "vlucas/phpdotenv": "~5.4.0" — Com­pos­er will update the pack­age to any ver­sion that is less than 5.5.x (the next minor ver­sion), so you get all patch ver­sion updates
  • "elvanto/litemoji": "3.0.1" — Com­pos­er will always install exact­ly ver­sion 3.0.1 of this pack­age, so you get no updates

In most projects, you’ll almost always use the ^ caret oper­a­tor to spec­i­fy your depen­den­cies. This is because you want all back­wards com­pat­i­ble minor & patch ver­sion updates, but don’t want Com­pos­er to update your pack­ages to major ver­sions that may have break­ing changes.

In some cas­es, if you’re very con­ser­v­a­tive or a pack­age isn’t strict­ly adher­ing to semver, you might use the ~ tilde oper­a­tor to only get patch ver­sion updates.

A great inter­ac­tive tool for learn­ing ver­sion con­straints inter­ac­tive­ly is the Pack­ag­ist Semver Check­er:

Packagist semver checker

Pack­ag­ist Semver Checker

You can enter real-world pack­ages and ver­sion con­straints, and visu­al­ly see what matches.

Link Advanced Version Constraints

Most peo­ple can prob­a­bly skip over this sec­tion, unless you run into sit­u­a­tions where you have some edge cas­es to solve in terms of ver­sion con­straints. But it’s pre­sent­ed here for those who live on the edge.

If you want to go wild, and spec­i­fy every ver­sion after the ini­tial ver­sion, you can do this:

  • "twig/twig": ">=v3.0.0" — Com­pos­er will update the pack­age to any ver­sion that is greater than or equal to 3.0.0. This is typ­i­cal­ly not a great idea, because it means you will get all major ver­sion updates (which may con­tain break­ing changes)

You can also have mul­ti­ple ver­sion con­straints for a giv­en pack­age, delim­it­ed by || for a log­i­cal or. This is use­ful if you can use mul­ti­ple ver­sions of a pack­age, because oth­er pack­ages may have more spe­cif­ic constraints:

  • "twig/twig": "^v3.0.0 || ^v4.0.0" — Com­pos­er will update the pack­age to any ver­sion that is less than 5.x (all 3.x and 4.x versions)

You can also have mul­ti­ple ver­sion con­straints delim­it­ed by a space or a , for a log­i­cal and:

  • "twig/twig": ">=v3.0.0,<3.5.0" — Com­pos­er will update the pack­age to any ver­sion that is greater than or equal to 3.0.0 and less than 3.5 . ">=v3.0.0 <3.5.0" is anoth­er way to write the same thing

As men­tioned ear­li­er, ver­sions resolve to tagged releas­es in a pack­age’s VCS repos­i­to­ry. A tag just points to a spe­cif­ic VCS com­mit in the repository.

If instead you need to use a branch of a repos­i­to­ry (maybe there is no tagged release), you can spec­i­fy that branch by adding the dev- pre­fix to the branch name:

  • "craftcms/cms": "dev-develop" — Com­pos­er will clone the repos­i­to­ry down, and check out the develop branch

If you need to use a branch that has a name that looks like a ver­sion, you spec­i­fy that branch by adding a -dev suf­fix instead:

  • "craftcms/cms": "5.0-dev" — Com­pos­er will clone the repos­i­to­ry down, and check out the 5.0 branch

If you need to use a branch of a pack­age’s repos­i­to­ry, but oth­er depen­den­cies also require the same pack­age with a ver­sion con­straint, you can use alias­es with as:

  • "nystudio107/craft-seomatic": "dev-develop-v4 as 4.0.9" — Com­pos­er will clone the repos­i­to­ry down, and check out the develop-v4 branch, but alias it to 4.0.9 so that any oth­er pack­age that requires "nystudio107/craft-seomatic": "^4.0.0" will still be happy

See Writ­ing Ver­sion Con­straints for even more exam­ples of what you can do with ver­sion constraints.

Link Adding Packages

To add a pack­age to your project, you can just open up your text edi­tor, and man­u­al­ly add a pack­age and a version

    "require": {
        "twig/twig": "^v3.0.0",
        "vlucas/phpdotenv": "~5.4.0",
        "elvanto/litemoji": "3.0.1",
        "guzzlehttp/guzzle": "^7.0.0"
    }

How­ev­er, this requires that you know both the vendor/name of the pack­age, and also the right ver­sion con­straint to put in. 

Instead, you can use the Com­pos­er CLI to add the pack­age with composer require:

composer require guzzlehttp/guzzle

It will find a ver­sion of the pack­age that is the lat­est ver­sion that will work with all of your oth­er project pack­ages, and add it to the composer.json for you:

    "guzzlehttp/guzzle": "^7.4",

It will set the ver­sion con­straint to the lat­est minor ver­sion of the pack­age that works with your depen­den­cies, and use the ^ caret oper­a­tor, so you get all minor & patch updates of the package.

If you don’t know the full vendor/name of the pack­age, you can find it inter­ac­tive­ly by using composer require with­out any arguments:

Php composer require cli

Using the composer require com­mand will also install the new pack­age for you, and update any oth­er pack­ages as appropriate.

Link Updating Packages

The rea­son we went so in-depth on semvers and ver­sion con­straints is that when you want to update your pack­ages, you can just use the composer update CLI command:

composer update

Com­pos­er will then auto­mat­i­cal­ly update all of your pack­ages to the lat­est ver­sions that sat­is­fy all of the ver­sion con­straints in all of the packages.

You can also elect to update only an indi­vid­ual pack­age by spec­i­fy­ing it:

composer update guzzlehttp/guzzle

In either case, Com­pos­er then writes out the exact ver­sions of all of the pack­ages that end up get­ting installed to the composer.lock file.

A good way to think about the rela­tion­ship between the composer.json file and the composer.lock file is this:

The composer.json file is what you order off of the menu, the composer.lock file is whatever ends up getting delivered to your table

Usu­al­ly, you’ll only use composer update in local devel­op­ment. A typ­i­cal work­flow looks like this:

  • Run composer update in local devel­op­ment to update your packages
  • Test to ensure your project con­tin­ues to work as expected
  • Check the mod­i­fied composer.lock file that is gen­er­at­ed into your VCS repository
  • Deploy that updat­ed composer.lock file to pro­duc­tion or CI pipeline
  • On pro­duc­tion or in your CI pipeline, run composer install

What composer install does is it installs the exact ver­sions of all of your pack­ages that are list­ed in the composer.lock file.

It does­n’t update any­thing, it installs the exact ver­sions that you’ve test­ed and know work.

This is the same method­ol­o­gy you would use in a team envi­ron­ment. After updat­ing pack­ages as not­ed above, have the oth­ers on your team pull down the mod­i­fied composer.lock file and run composer install.

This ensures that every­one is run­ning the same exact pack­age versions.

The actu­al com­mand we typ­i­cal­ly use on pro­duc­tion or CI pipeline is:

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

Let’s break down what the flags mean:

  • --no-dev — Skip installing pack­ages list­ed in require-dev
  • --no-progress.- Removes the progress dis­play that can mess with some ter­mi­nals or scripts which don’t han­dle back­space characters
  • --no-interaction — Do not ask any inter­ac­tive questions
  • --optimize-autoloader — Con­vert PSR0/4 autoload­ing to classmap to get a faster autoloader. This is rec­om­mend­ed espe­cial­ly for pro­duc­tion, but can take a bit of time to run so it is cur­rent­ly not done by default

For an in-depth look at the composer.lock file, have a look at the arti­cle PHP: The Com­pos­er Lock File. For more on deploy­ments, check out the Atom­ic Deploy­ments with­out Tears article.

Link Useful Composer Commands

Here are some use­ful Com­pos­er CLI com­mands that will make your life easier:

  • composer require — add a new pack­age to your pro­jec­t’s composer.json file, and install it
  • composer remove — remove an installed pack­age from vendor/​and your composer.json file
  • composer update — update your pro­jec­t’s depen­den­cies to the lat­est ver­sions that sat­is­fy all of the pack­ages ver­sion constraints
  • composer install — install the exact ver­sions of the pack­ages list­ed in the gen­er­at­ed composer.lock file
  • composer reinstall — unin­stalls and rein­stalls com­pos­er pack­ages, for a clean install of the packages
  • composer show -D — list all of your pro­jec­t’s direct depen­den­cies and installed versions
  • composer outdated -D — list all of your pro­jec­t’s direct depen­den­cies that have new­er updates available
  • composer init — cre­ate a new composer.json file interactively

composer outdated -D is espe­cial­ly use­ful when deter­min­ing when a project needs to be updated:

Php composer outdated d

com­pos­er out­dat­ed ‑D

For a com­plete list of all of the avail­able com­mands, check out the Com­pos­er CLI ref­er­ence.

Link Platform Requirements

In addi­tion to PHP pack­ages, both the require and require-dev fields also sup­port ref­er­ences to spe­cif­ic PHP ver­sions and PHP exten­sions your project needs to run successfully:

    "require": {
        "php": ">=7.4.0",
        "ext-mbstring": "*"
        "twig/twig": "^v3.0.0",
        "vlucas/phpdotenv": "~5.4.0",
        "elvanto/litemoji": "3.0.1"
    }

Both php and ext- require­ments are spe­cial in that they don’t install any­thing. They just ensure that these min­i­mum plat­form require­ments are present before Com­pos­er will attempt to install your pro­jec­t’s dependencies.

  • php is a spe­cial case that checks to ensure that the installed ver­sion of PHP match­es at least the ver­sion con­straint specified
  • Any­thing pre­fixed with ext- refers to a PHP exten­sion. The * in the ver­sion con­straint is a wild­card which means any ver­sion is fine, as long as it is installed.

It’s impor­tant to spec­i­fy any PHP exten­sions that your project relies on, because not all PHP instal­la­tions may have the required exten­sions installed.

Not doing so means that it’s pos­si­ble PHP could throw a run­time error try­ing to exe­cute your project, if the req­ui­site PHP exten­sion isn’t present.

Link Config Field

There is also a config field in the composer.json file that allows you to spec­i­fy glob­al con­fig­u­ra­tion set­tings that Com­pos­er should use:

  "config": {
    "platform": {
      "php": "7.4",
      "ext-mbstring": "1.0.1"
    },
    "allow-plugins": {
      "craftcms/plugin-installer": true,
      "yiisoft/yii2-composer": true
    },
    "optimize-autoloader": true,
    "sort-packages": true
  },

The platform field inside of the config field looks very sim­i­lar to what we list­ed in the require field above in PLAT­FORM REQUIRE­MENTS, but it has a sub­tle difference.

It lets you spec­i­fy fake plat­form require­ments for PHP and PHP exten­sions. Typ­i­cal­ly this is done when envi­ron­ments have dif­fer­ent ver­sions of PHP installed, and you want to ensure that your pack­ages are all installed with the appro­pri­ate version.

For exam­ple, let’s say have are run­ning PHP 8.1 in our local devel­op­ment envi­ron­ment, but the pro­duc­tion envi­ron­ment we deploy our project to is only run­ning PHP 7.4.

In the plat­form field, we’d set "php": "7.4" to ensure that any pack­ages that get installed local­ly will run on PHP 7.4, so they will work both in our local devel­op­ment envi­ron­ment, and also on production.

The allow-plugins field is required as of Com­pos­er 2.2.0 or lat­er to list any Com­pos­er plu­g­ins that should be allowed to run when using Composer.

The optimize-autoloader field ensures that when pack­ages are installed, the autoload classmap will be opti­mized. This is false by default, because it can take a lit­tle extra time to do, but it is rec­om­mend­ed to be done on pro­duc­tion installs, because it makes the run­time res­o­lu­tion of Com­pos­er pack­ages faster.

The sort-packages field caus­es Com­pos­er to sort the pack­ages in the require and require-dev fields alpha­bet­i­cal­ly for you, to keep them neat­ly organized.

These are just a few con­fig options you might be inter­est­ed in using, or have seen out in the wild. For a com­plete list, check out the composer.json Con­fig doc­u­men­ta­tion.

Link Composer Scripts

Com­pos­er also has a scripts field in the composer.json file. Com­pos­er scripts are com­mand-line exe­cutable com­mands that run at var­i­ous stages in the Com­pos­er CLI lifecycle.

For instance, you might want to run some­thing after a composer install or composer update hap­pens. To do that, you’d add this to your composer.json:

  "scripts": {
    "post-install-cmd": "echo 'install finished'",
    "post-update-cmd": "echo 'update finished'"
  }

A great use-case for this is as part of a deploy­ment process, to clear caches after updates are installed via com­pos­er install.

You can also define your own scripts, and run mul­ti­ple CLI commands:

  "scripts": {
    "custom-script": [
      "echo 'custom 1",
      "@php some-script.php",
    ],
    "prepostinstall-cmd": "echo 'install finished'",
    "post-update-cmd": "echo 'update finished'"
  }

The @php direc­tive can be used to run oth­er PHP scripts, and will resolve to what­ev­er PHP process is cur­rent­ly being used.

Cus­tom scripts can then be run via composer run custom-script, or they can be run by the spe­cial Com­pos­er life­cy­cle events:

  "scripts": {
    "custom-script": [
      "echo 'custom 1",
      "@php some-script.php",
    ],
    "post-install-cmd": "echo 'install finished'",
    "post-update-cmd": "@custom-script"
  }

In the exam­ple above, "post-update-cmd": "@custom-script" will cause all of the CLI com­mands defined in custom-script to be run after a composer update is done.

Com­pos­er scripts can be a very handy way to pack­age func­tion­al­i­ty along with your project, and ensure that scripts are run after Com­pos­er life­cy­cle events, regard­less of the environment.

Link Working Locally with Path Repositories

We men­tioned ear­li­er that Com­pos­er by default looks up pack­ages via Pack­ag­ist by default when it tries to find and install your pack­age dependencies.

How­ev­er, what if you want to work on your own PHP pack­age local­ly? You can tell Com­pos­er that you have anoth­er repos­i­to­ry that you want it to look at when resolv­ing pack­ages, specif­i­cal­ly a path repos­i­to­ry by spec­i­fy­ing it in your composer.json file:

  "repositories": [
    {
      "type": "path",
      "url": "/Users/andrew/webdev/craft_v4/craft-seomatic"
    }
  ],

The type of the repos­i­to­ry is a path, and the url is the path (which can be a rel­a­tive or an absolute path) to the local direc­to­ry on your com­put­er where Com­pos­er can find the repository.

You can also use a wild­card * char­ac­ter when spec­i­fy­ing your path repository:

  "repositories": [
    {
      "type": "path",
      "url": "/Users/andrew/webdev/craft_v4/*",
    }
  ],

This will cause Com­pos­er to try to resolve all pack­ages by look­ing for them in the path spec­i­fied in the url field. If it fails to find the pack­age there, it’ll fall back on look­ing for it on pack​ag​ist​.org.

Then you can require the PHP pack­age you’re work­ing on in your pro­jec­t’s composer.json file as you nor­mal­ly would, but work on it local­ly (typ­i­cal­ly the PHP pack­age will be a sep­a­rate git repository).

When Com­pos­er resolves a pack­age from a local path repos­i­to­ry, it will cre­ate a sym­link in your pro­jec­t’s vendor/ direc­to­ry, and every­thing will just work”.

Path repos­i­to­ries take pri­or­i­ty over remote VCS repos­i­to­ries and Packagist.

Link Using a Package from a Forked Repository

A sce­nario that unfor­tu­nate­ly hap­pens on occa­sion is that you have a PHP depen­den­cy that your project needs to func­tion. Maybe it’s a plu­g­in of some sort for a CMS, maybe it’s just a straight up PHP package.

But there’s a bug in the pack­age that you need fixed.

You open up the pack­age’s GitHub repos­i­to­ry URL in your brows­er, and your blood runs cold. A cloud of dread forms around you. A bead of sweat glis­tens on your fore­head. Your worst fears are realized:

Php package from forked repository

Aban­doned PHP package

The pack­age you are hav­ing an issue with is aban­doned, or at the very least does­n’t appear to be active­ly updated.

So let’s say you have some PHP chops or the bug fix is rel­a­tive­ly sim­ple, so you roll up your sleeves, you fork the repos­i­to­ry on GitHub. You make the bug fix, and being a good cit­i­zen, you make a pull request so the pack­age main­tain­er can use your bug fix, and every­one benefits.

Unfor­tu­nate­ly, your client needs the fix to their project now. What you can do is tell Com­pos­er to use your fork of the package:

  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/khalwat/craft-seomatic",
    },
  ],
  "require": {
    "nystudio107/craft-seomatic": "dev-develop-v4 as 4.0.9"
  }

We tell Com­pos­er to use the VCS repos­i­to­ry we’ve spec­i­fied when resolv­ing pack­ages, and then we alias the develop-v4 branch to ver­sion 4.0.9 of the pack­age, and away we go!

If the main­tain­er of the orig­i­nal pack­age ends up fix­ing the issue in the orig­i­nal pack­age, no prob­lem. Just remove the VCS repos­i­to­ry and require alias from your composer.json.

For a deep­er dive on this, Ben Cro­ker wrote a more in-depth arti­cle Requir­ing a Forked Repo with Com­pos­er.

Link Compose a masterpiece

So that’s a brief run­down on what you need to know to use Com­pos­er with confidence.

Composer php packages

There’s more to Com­pos­er than can be list­ed in any sin­gle arti­cle, but hope­ful­ly, this primer has giv­en you what you need to make beau­ti­ful music together.

Indeed, the very name Com­pos­er” is a dou­ble enten­dre that alludes to both com­pos­ing music, and also com­pos­ing mul­ti­ple PHP pack­ages togeth­er to cre­ate some­thing larg­er than the sum of its parts.

And yes, the Com­pos­er logo is a con­duc­tor, and makes no sense. 🤔

For an exam­ple of using the Com­pos­er of CLI to update Craft CMS & plu­g­ins, check out the Updat­ing Craft CMS with­out Headaches article.

If you run into issues with Com­pos­er, check out the Trou­bleshoot­ing Com­pos­er Errors knowl­edge base article.