Installing Composer on MacOS
January 1, 2021 by Areg Sarkissian
This post is part of the Get Started with Production Ready Laravel series of posts.
installing composer
The following steps are described at Composer
Prior to installing make sure that /usr/local/bin
is in your system $PATH variable.
1-Download the setup script
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
2-Optionally, verify the script hash
php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
Make sure to replace the hash code above with the latest code from Composer or from Composer Public Keys.
3-Run the setup script
php composer-setup.php --filename=composer --install-dir=/usr/local/bin
4-Remove the downloaded setup script
php -r "unlink('composer-setup.php');"
5-Verify installed version
composer --version
The composer script install should have created a
~/.composer
directory that holds the global package installations and added aCOMPOSER_HOME
environment variable with that path to your system $PATH in your bash profile.
Installing composer the quick install way
This alternative installation method uses curl to install composer and does not verify the hash
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
Upgrading composer
Upgrade composer to the latest version:
composer self-update
Verify the updated version
composer --version
removing composer
Just delete the composer files to remove composer.
rm /usr/local/bin/composer
rm -rf ~/.composer
Composer global packages home
When you install composer a composer home directory is created where global packages (non project specific packages) can be installed.
Run the following command to see the location location of the symlinks to global package executables:
composer global config bin-dir --absolute
The result on my machine is:
~/.composer/vendor/bin
Make sure to add this path to your system $PATH so that we can execute the commands from any directory
Below are symlinks for two cli packages installed globally on my machine. They point to the vendor directory where the packages are installed:
~/.composer/vendor/bin/laravel -> ~/.composer/vendor/laravel/installer/bin/laravel
~/.composer/vendor/bin/valet -> ~/.composer/vendor/laravel/valet/valet
The global composer configuration file and vendor directory paths are in composer home directory.
~/.composer/composer.json
~/.composer/composer.lock
~/.composer/vendor/
composer commands and the composer pipeline
Composer has a set of commands for adding, removing, updating and installing packages and their dependancies.
These commands can be executed as part of a pipeline to ultimately install or remove installed pakages.
Composer also has a command for generating autoload classmap files for then classes in your application. This command runs at the end of the pipeline.
Each command that is executed performs its task then calls the next command in the pipeline.
Each command when executed generates an artifact that can be used by any of the commands after it in the pipeline.
The left to right sequence of commands in the pipeline is shown below.
The first command in the sequence can be either the composer require
or the composer remove
command.
For composer require we have the sequence below:
composer require -> composer update -> composer install -> composer dump-autoload
And here is the sequence for composer remove:
composer remove -> composer update -> composer install -> composer dump-autoload
Below each command step of the pipeline starting from the left, is shown in a single line to detail the artifact created or updated by the command that will be used by the next command in the pipeline:
composer remove (removes specified package from composer.json)
composer require (adds package with version constraint to composer.json, creates composer.json if it not exist)
composer update (uses composer.json to update composer.lock, creates composer.lock if it does not exists)
composer install (uses composer.lock to download and install packages in the vendor directory, creates the vendor directory if it does not exist)
composer dump-autoload (generates autoload classmap files from composer.json, then runs any scripts in composer.json scripts section)
We can start at any point in the pipeline, calling any of the commands in the flow to regenerate the artifact it is responsible as long as the artifact it depends on exists and is in sync with previously generated artifacts.
Below composer commands are described in detail in its own section.
creating the initial composer.json file
This will interactively create a new composer.json file:
composer init
If you don’t create a composer.json file before requiring packages, then the composer.json file will automatically be created for you when you require your first package.
requiring packages
Install packages using the require command:
#the latest version
composer require vendor/package
This will add the package to the require
section of the composer.json file and then call composer update
described in the section below to install the package.
We can also specify an optional version number using a version constraint.
#specifying a version
composer require vendor/package:<version>
We can install multiple packages as well by separating them with a space.
To add a package as a development dependency we can use the –dev option:
composer require vendor/package --dev
This will add the package to the require-dev
section of the composer.json file and then call composer update
described in the section below to install the package.
To add a package globally we can use the global modifier:
composer global require vendor/package
The global require command adds the package to the composer.json in the composer home directory (~/.composer) and then call composer global update
described in the section below to install the package inside the vendor directory in the composer home directory.
The composer require
command will create the composer.json file if it does not exist.
removing packages
The remove command will remove the installed package from the composer.json file and then call composer update
described in the section below to uninstall the package.
composer remove vendor/package
Add the global modifier to remove a globally installed package
composer global remove vendor/package
Multiple package names separated by a space character can be specified.
The global remove command removes the package from the composer.json in the composer home directory (~/.composer) and then call composer global update
described in the section below to uninstall the package.
updating packages
The composer update command resolves dependancies from packages specified in composer.json and updates the composer.lock file with specific versions of packages to install then calls composer install
command to install the packages. It will create the composer.lock file if it does not exist.
If there are changes in composer.json from the last version that created or updated the composer.lock file, then the update command will update the composer.lock file based on resolution of the changes in composer.json.
Also if there are new package versions released that satisfy the version roll forward constraint of the package in composer.json then the update command will update the composer.lock file with the latest version of the package that satisfies the version constraint.
composer update
To update global packages we can use the global subcommand which uses the composer.json from our composer home directory to create or update composer.json file in the composer home directory and then call composer global install to install global packages installed into the vendor directory in the composer home directory:
#update all global packages
composer global update
We can use the dry run flag to see the effects of the update before we actually run it
composer update --dry-run
The composer require and composer remove commands call the composer update command after adding or removing packages in composer.json
installing packages
composer dump-autoload generates autoload files PHP autoload files specified the composer.json autoload section, then runs the scripts from the scripts section of composer.json
Run the install command to install the specific versions of packages specified in the composer.lock file into the vendor directory:
composer install
composer install downloads and installs the package versions specified in the composer.lock file into the vendor directory. If the vendor directory does not exist it will be created. It then calls composer dump-autoload to generate the PHP autoload files. If the –optimize-autoload option is specified on the command line or the optimize-autoloader setting in composer.json is set to true, then it calls composer dump-autoload with the –optimize option.
To install packages globally we can use the global modifier.
composer global install
The global install uses the composer.lock in the composer home directory to install the package versions specified in the composer.lock file into the vendor directory of the composer home directory. If the vendor directory does not exist it will be created.
The global install command also creates symlinks to executables within the installed packages, in the vendor/bin directory of the home directory.
Composer install Options for production builds
composer install --no-dev --optimize-autoloader --no-interation --no-scripts --no-progress --prefer-dist
The –no-dev option only installs non development dependancies in a production environment.
The –no-script option makes sure that the scripts in the scripts section of the composer.json file do not run.
The –optimize-autoloader generates optimized PHP autoload files.
note –prefer-dist tries to install from packagist.org and falls back to git repository. Replace it with –prefer-source to try to install from the git repository then fallbask to packagist.org
Updating installed package version constraint
We saw earlier that we can use the composer update
command to upgrade package to the latest version within their specified version roll forward constraint.
When no package version is specified in composer.json then the default constraint is to move to the latest version of the package (which is no constrain).
However sometimes we want to change the version constraint for the package or we want to downgrade or upgrade to a specific version.
In that case we can remove the package using composer remove and then re-install the package using composer require with the specific package version that we want or with the new package version constraint.
Another way is to manually update the package version in composer.json and then run the composer update command.
Sometime either method may fail due to version conflicts. How to overcome that is described in the next section.
Issues with version conflict when using composer update
If there are package version conflict when running composer update or running composer require which runs composer update then the solution is to first delete the vendor directory and composer.lock file.
rm -rf vendor
rm composer.lock
Then open the composer.json and add, remove or update package versions in the require and require-dev sections. Then either run composer update or composer require (if you want to add additional packages).
This will run composer update which will then generate a new composer.lock file and call composer install which will create the vendor directory and add the new resolved package versions from the composer.lock file into the vendor directory.
displaying composer.lock file dependency tree
composer show -t
dumping the autoload configuration in development
Whenever we update the PSR-4 autoloader configuration in composer.json file we need to make composer aware of the changes.
We can do this by dumping the autoloader configuration. The command generates and\or updates the autoload PHP files in the vendor directory. The autoload files contain a classmap
mapping between PHP classes and their file location.
composer dump-autoload
Specifying the –optimize or -o option will optimize the generated classmap.
composer dump-autoload --optimize
Instead of the command line option we can specify the optimization through setting in composer.json
{
"config": {
"optimize-autoloader": true
}
}
Running compose install will call composer dump-autoload. Passing in –optimize-autoloader to composer install will call composer dump-autoload –optimize
dumping the autoload configuration in production
To dump an optimized version of the autoloader for production we can use the -o or –optimize option
#generate classmap file vendor/composer/autoload_psr4.php
composer dump-autoload --optimize
As previously noted, when we run the composer install
command it will call composer dump-autoload to generate the autoload classmap after installing the packages. So in production we can use the -o or –optimize-autoloader option when running composer install to have composer dump-autoload be called with the –optimize option.
composer install --optimize-autoloader --no-dev --no-interation --no-scripts --no-progress --prefer-dist
if project does not use dynamic classes than –classmap-authoritative optimizer option does a better optimization than –optimize-autoloader option
PSR-4 autoloader configuration
The autoloader configuration in the composer.json file maps a one or more class namespace names to a specific directory name under the root directory of the project. The namespace name on the left side of the colon is arbitrary and does not need to match the directory name on the right side.
Here is an example that maps two namespaces:
{
"PSR-4": {
"\\App": "/app",
"\\Resources\\Views": "/resources/views"
}
}
Note the class namespace in the mappings use a
\\
escape sequence.
The PSR-4 class namespacing convention then follows the directory hierarchy under the PSR-4 mapping, starting from the PSR-4 mapped directory on down.
Using the example above, The app
directory in the root of the project will map to the \App
namespace and the app/config
subdirectory will map to the \App\Config
namespace. Therefore a
a class named User
in the file app/user.php
will have a fully qualified class name of \App\User
and a class named Database
in the file app/configs/database.php
will have a fully qualified class name of \App\Config\Database
.
Note that the fully qualified namespace names start with a backslash signifying the root namespace.
You can always use the fully qualified namespace in your PSR-4 files. However when a PSR-4 namespace is declared within the file, then the code can reference other classes within that namespace and the hierarchy below it using relative class names. For example we might have a file app/book.php
and a file app/user.php
that both declare namespace App
. Then we in User
class can reference the the Book
class without its fully qualified namespace and vice versa. Assuming app/configs/database.php
declares namespace App\Config
then it can be referenced from the either class as Config\Database
which is qualified relative to the App
namespace.
The Composer.json structure
The composer.json schema below will illustrate components of a composer.json file:
{
"classmap": ["database/Seeders", "database/Factories", "factories/"],
"files": ["global/functions.php"],
"autoload": {
"psr-4": {
"App\\": "app/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests": "test/"
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/Seldaek/monolog"
}
],
"require": {
"php": ">=7.4",
"symfony/yaml": "2.6.4",
"monolog/monolog": "1.12.0"
},
"require-dev": {
"phpunit/phpunit": "^8"
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": ["@php artisan key:generate --ansi"]
},
"bin": ["valet"],
"extra": {
"laravel": {
"dont-discover": []
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
require section is for loading packages. It also can specify the PHP version to use.
The require-dev section is for packages only used for development that are installed with the –dev option. The –no-dev option for the install command in production will bypass loading those packages.
The autoload section is for our PSR-4 classes and generates vendor/composer/autoload_psr4.php
the autoload-dev section is for loading our PSR-4 classes that are only used for development. The –no-dev option for the install command in production will bypass generating an autoload file for those mapping.
classmap section is for loading classes in directories that are in a directory path outside the psr4 directory and generates vendor/composer/autoload_classmap.php
Composer, starting with the project root scans the specified classmap files and directories for php classes and requires them.
files section is for loading files with global php functions
repositories section is for instructing composer to look for a package to install from the repo location first
the bin section lists binary executables installed by the package in a vendor/bin directory
the config section contains the following settings:
The optimize-autoloader set to true makes composer dump-autoload optimize the autoload php classmap files
The preferred-install wet to “dist” attempts to download packages for packegist.org before falling back to the package repository
Package versioning version constraints
Using semantic versioning the ^ allows both minor and patch versions to increment to future minor and patch releases starting with the specific minor and patch version specified
^2 (2.0.0) is equivalent to version that is >=2.0.0 but <3.0.0
^2.6 (2.6.0) is equivalent to version that is >=2.6.0 but <3.0.0
^2.6.4 is equivalent to version that is >=2.6.4 but <3.0.0
Using semantic versioning the ~ allows only patch version to increment to future patch releases starting with the patch version specified:
~2 (2.0.0) is equivalent to version that is >=2.0.0 but <2.1.0
~2.6 (2.6.0) is equivalent to version that is >=2.6.0 but <2.7.0
~2.6.4 is equivalent to version that is >=2.6.4 but <2.7.0
composer autoloader and PHP namespaces relationship
In the index.php bootstrap file:
require_once __DIR__ . '/../vendor/autoload.php';
psr 4 directory structure and namespace mapping
assuming PSR-4 mapping in composer.json
{
"PSR-4": {
"\\App": "/app"
}
}
The classes in files in the directory hierarchy map accordingly:
app\User.php => namespace App;
app\Config\Storage.php => namespace App\Config;
app\Helpers\Time.php => namespace App\Helpers;
The namespace in the files matches the directory structure starting with the PSR-4 mapped namespace
in app\User.php
<?php
namespace App;
use App\Helpers\Time;
class User{}
in app\Helpers\Time.php
<?php
namespace App\Helpers;
class Time{}
in app\Config\Storage.php
<?php
namespace App\Config;
use App\User;
use App\Helpers\Time;
class Storage{}
Also note that the namespace references using the use
keyword start from the PSR-4 mapped namespace and follow the directory hierarchy.
Finally we have a bootstrap file named index.php that autoloads all classes by including the vendor/autoload.php file
require_once __DIR__ . '/../vendor/autoload.php';
use App\User;
use App\Config\Storage;
$user = new User;
$db = new Storage;
Again note that the namespace references using the use
keyword start from the PSR-4 mapped namespace and follow the directory hierarchy.