© Chad Russell 2016

Chad Russell, PHP Development Tool Essentials, 10.1007/978-1-4842-0683-6_4

4. Dependency Management

Chad Russell

(1)Jacksonville, Florida, USA

Dependency management is a system for easily managing (installing, using, updating, uninstalling) library dependencies in your project. The operative word in that statement is easily. For a long time, dependency management in PHP was virtually non-existent.

Of course, PEAR has existed since 1999, but it didn’t fit the bill of providing easy dependency management within your application. It is used for globally installing packages server wide (think apt-get or yum), and anyone who has worked with PEAR’s XML structure to create a package can attest to its lack of easiness. This is where Composer and its complement Packagist come into play.

Composer and Packagist

Composer is a command line tool that was created for easy dependency management in PHP applications. You define your application’s dependencies in a single, simple JSON file format, and Composer will install and update those packages for you. It was inspired by npm and Bundler, which are the respective package managers for Node.js and Ruby, and borrows a number of features and concepts from both of them.

Installing Composer

There are essentially two different ways to install Composer. You can install it locally to your project, or globally as a system-wide executable. If you’re just downloading it to check it out for the first time or don’t have the access level to install it system wide, then a local install is fine. However, the best approach is to install it globally so that you have one Composer version that is installed and being used for all of your projects on the same server without your having to maintain various installs and versions.

Locally

To install Composer locally , simply run the following command in your project directory:

$ curl -sS https://getcomposer.org/installer | php

Once the installer runs, it will download the composer.phar binary to your project. You can then execute it by running php composer.phar.

Globally

To install Composer as a system-wide executable, you will first download the composer.phar then move it to somewhere that is within your PATH on Unix-like systems (Linux/OS X, etc.):

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

You can now execute Composer by typing composer on the command line.

Note

If you are on a Windows system, then you can download the Windows Composer installer to install it globally on your system by visiting getcomposer.org.

Packagist

Packagist is the default Composer repository. It is a public aggregation of PHP packages installable via Composer. You can visit Packagist by visiting packagist.org, where you can easily search through the packages that are available. By referencing the version and package name, Composer knows from where to download the code that you are specifying in your project. As of the time of writing, it contained over 67,000 registered packages, nearly 314,000 versions, and boasted over one billion package installs since April 2012!

In addition to its being a searchable resource for developers looking for information on the packages they wish to install, package authors can easily submit their project to Packagist so that others can use Composer for the project as well.

Once your package has been successfully registered with Packagist, you can enable a service hook in your Bitbucket or GitHub account and have your package updated instantly when you push to your remote repository.

Using Composer

The only requirements to start using Composer in your project are to have Composer installed and to create your project’s composer.json file. This file is where your project dependencies are listed, along with other possible metadata.

The composer.json File

In the most basic form, the only thing that is required is the use of the require key. For our example, we’ll install the PHPUnit framework. We’ll create the project’s composer.json file like this:

{
  "require": {
    "phpunit/phpunit": "4.8.4"
  }
}

Now we’ll install this framework with Composer, like this:

$ composer install

After running this command, we will see output from Composer as it downloads and installs all of the dependencies needed for PHPUnit, which will look something like this:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing sebastian/version (1.0.6)
    Loading from cache


  - Installing sebastian/global-state (1.0.0)
    Loading from cache


  - Installing sebastian/recursion-context (1.0.1)
    Downloading: 100%         

As Composer finishes its installation, you will see it generates a lock file and the autoload files needed to automatically load PHPUnit into your application. You will also have a new folder called vendor that includes all of the packages that Composer just installed.

Installing Additional Packages

Now, when you need to install additional packages into your application, you simply add the entry or entries for your new requirements to your composer.json file and run composer install again. Alternatively, you can make use of the composer require command, which will both add the entry to your composer.json file and install it in one command. For example, one of our application requirements might be to send emails, and we want to use the popular SwiftMailer library. To do this, we first look up SwiftMailer on packagist.com and find the package name, then we run the following command:

$ composer require swiftmailer/swiftmailer
Using version ^5.4 for swiftmailer/swiftmailer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing swiftmailer/swiftmailer (v5.4.1)
    Downloading: 100%         


Writing lock file
Generating autoload files

From the output, we can see it updated our composer.json file, installed the SwiftMailer library package, updated the lock file, and generated new autoload files. If you look at the composer.json file, you’ll now see it updated to reflect SwiftMailer:

{                                                                                                    
  "require": {
    "phpunit/phpunit": "4.8.4",
    "swiftmailer/swiftmailer": "^5.4"
  }
}

One thing you might notice with the new entry in the composer.json file is the version number for SwiftMailerit includes the caret (^) operator. We’ll dive deeper into Composer versions in a few sections, but for now this is the equivalent of telling future composer update commands that we always want the latest stable release that is >=5.4 < 6.0. If we wanted to be more specific with a version, like in the PHPUnit example, then we can optionally pass along a version to the composer require command, like so:

$ composer require swiftmailer/swiftmailer 5.4

Removing Packages

Composer makes adding new libraries to your application easy, and it makes it just as easy to remove packages that are no longer needed. There are two different ways this can be accomplishedeither by manually removing the package declaration from your composer.json file and running composer update or by utilizing the composer remove command .

If we decide we want to remove the PHPUnit library we installed previously, we can do so with this single command:

$ composer remove phpunit/phpunit
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing phpunit/phpunit (4.8.4)
Writing lock file
Generating autoload files

require vs require-dev

Composer makes available two different methods of requiring a package. The first is by using the require declaration, as all of the previous examples have shown. The second is by using the require-dev declaration. You should always use require unless a certain package is only needed for the development of your application and not for running the application in production. A prime example for this would be a unit-testing library, such as PHPUnit. For example, let’s now add PHPUnit back into our application, but this time specify that it’s only needed for development using require-dev:

$ composer require phpunit/phpunit --dev
Using version ^4.8 for phpunit/phpunit
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing phpunit/phpunit (4.8.8)
    Downloading: 100%         


phpunit/phpunit suggests installing phpunit/php-invoker (∼1.1)
Writing lock file
Generating autoload files

Now if you look at the composer.json file, you will see phpunit has been added back, but this time under the require-dev declaration:

{
  "require": {
    "swiftmailer/swiftmailer": "^5.4"
  },
  "require-dev": {
    "phpunit/phpunit": "^4.8"
  }
}

The Composer Lock File

The Composer lock file is a list of the exact versions that Composer has installed into your project. This locks your project into those specific versions. It is important to add this lock file to your Git repository along with the composer.json file so that anyone working on your project will be using the same package versions. This is also important because if you’re using any type of deployment scheme in your staging or production environments, you can utilize composer install to ensure those environments have installed the same exact versions of your packages that you have developed your application against. This also ensures that future updates to these libraries can be done within your application development, committed to your repository, and accurately distributed to these other environments. This also eliminates the need to store all of the various packages that Composer has installed in your source code repository .

Note

Because you track the dependency files and versions using the two Composer files now, there is no need to commit and maintain the vendor folder to your Git repository. You should add the vendor directory to your .gitignore file.

Autoloading

When using Composer, it will generate an autoload file vendor/autoload.php that is used to autoload all of your libraries and packages you have installed with Composer. Simply include this autoload file in your application (ensuring the correct path to the vendor/autoload.php file), and all of the installed packages will be available to you.

require __DIR__ . '/../vendor/autoload.php';

Now, if we wanted to use our SwiftMailer library we installed previously, we could do so simply by calling it:

// Create the SwiftMailer Transport
$transport = Swift_MailTransport::newInstance();


// Create a Mailer instance with our Transport
$mailer = Swift_Mailer::newInstance($transport);


// Create our message
$message = Swift_Message::newInstance('Learning Composer')
    ->setFrom(array('[email protected]' => 'John Doe'))
    ->setTo(array('[email protected]' => 'Jane Doe'))
    ->setBody('Composer is wonderful!');


// Send our message!
$result = $mailer->send($message);

Additional Autoloading

In addition to autoloading all of the installed Composer library packages, you can utilize the Composer autoloader for your own application code as well. To do so, use the “autoload” field in your composer.json file.

For example, if you’re storing your own application’s code in a folder called src, you would add the following entry to your composer.json file :

{
  "autoload": {
    "psr-4": { "MyApplication\": "src/" }
  },
  "require": {
    "swiftmailer/swiftmailer": "^5.4"
  }
}

This tells Composer to register a PSR-4 (The PHP-FIG Autoloading Standard) autoloader for the “MyApplication” namespace. Now, to get Composer to update vendor/autoload.php, you will need to run the dump-autoload command:

$ composer dump-autoload
Generating autoload files

In addition to PSR-4 autoloading, Composer also supports the PSR-0 autoloading, classmap generation, and file includes as valid autoloading methods. However, PSR-4 is the recommended method of autoloading with Composer for its ease of use.

Autoloader Optimization

Although not required for development environments, it is highly recommended that when generating the Composer autoloader for production use, you utilize the built-in autoloader optimizer. It is not uncommon to see performance boosts in your application up to 30 percent, especially if your application is spending a lot of time on the Composer autoload file.

There are two different ways to generate an optimized autoloader. The first is by using the dump-autoload command with the -o parameter:

$ composer dump-autoload -o
Generating optimized autoload files

For example, this command could be set up to run on staging and production environment deployments so that the standard autoloader is being used in development, but testing and production use are utilizing the optimized version.

In addition to generating the optimized autoloader via the dump-autoload command, you can also specify it in your composer.json file so that you are always generating the optimized version. This is done using the config directive:

{
  "autoload": {
    "psr-4": {
      "MyApplication\": "src/"
    }
  },
  "require": {
    "swiftmailer/swiftmailer": "^5.4"
  },
  "require-dev": {
    "phpunit/phpunit": "^4.8"
  },
  "config": {
    "optimize-autoloader": true
  }
}

Package Versions

Composer provides a lot of flexibility when defining the version of a given package that you’re installing in your application. You can essentially break down the definitions into three categories: basic constraints, next signification release, and stability .

Basic Constraints

Exact

Using basic constraints, you can tell Composer to install an exact version by specifying only the number, such as 1.2.4. This will ensure that your application is always using this exact version, regardless of the number of times composer update is run.

"require": {
    "vendor/packagea": "1.5.4"
  }
Range

Composer allows the use of comparison operators to specify a range of valid versions for your application. The valid operators are >, >=, <, <=, !=. In addition, multiple ranges can be specified using logical AND and OR logic. Separating a range by a space or comma is used to denote an AND and a double-pipe || is used to denote an OR. Here are a few valid examples:

"require": {
    "vendor/packagea": ">1.5",
    "vendor/packageb": ">2.0 <3.0",
    "vendor/packagec": ">2.0,<3.0",
    "vendor/packaged": ">1.0 <1.5 || >= 1.7"
}
Wildcard

In addition to specific versions and ranges, you can also specify a version number pattern by using a wildcard in the version declaration. For example, if we wanted any sub-version of the 4.2 branch of a package, it would be specified as:

"require": {
    "vendor/packagea": "4.2.*"
}
Range Hyphen

Another way of specifying ranges is with the use of a hyphen. When using the hyphen notation, partial version numbers on the right side of the hyphen are treated as wildcards. So consider the following example:

"require": {
    "vendor/packagea": "1.5 – 2.0",
    "vendor/packageb": "2.0 – 2.1.0"
  }

packagea in this example is the equivalent of >=1.5 <2.1. Since the version number on the right side is treated as a wildcard, Composer looks at it as 2.0.*.

packageb in this example is the equivalent of >=2.0 <=2.1.0.

Next Significant Release

There are two different operators you can use with Composer to define a version limit up to the next significant release of a given package.

Tilde

With the tilde operator you can define a minimum version mark that you’d like to use for your application while protecting you from having to update to the next significant release of a package (the next x.0 release, for example). Consider the following example:

"require": {
    "vendor/packagea": "∼2.5"
}

This declaration is the same as specifying >= 2.5 but <3.0. You can also define this at the sub-version level by defining your requirement as:

"require": {
    "vendor/packagea": "∼2.5.2"
}

This declaration is the same as specifying >= 2.5.2 but < 2.6.0.

Caret

The caret operator ^ works very similarly to the tilde operator, with a slight difference. It is supposed to always allow non-breaking updates by sticking closer to semantic versioning. Consider the following example:

"require": {
    "vendor/packagea": "^2.5.2"
}

This declaration is the same as specifying >= 2.5.2 but <3.0. As you can see, this is slightly different than the tilde, which would have kept it from updating to 2.6.0. Lastly, regarding packages that are less than 1.0, the caret provides a bit of extra safety and will not allow such a large range of version updates:

"require": {
    "vendor/packagea": "∼0.5"
}

This declaration is the same as specifying >= 0.5.0 but <0.6.0.

Stability

The Composer documentation can become quite confusing when trying to understand the stability of a package that Composer will install. Reading the “Versions” section of the Composer documentation would lead you to think that Composer might randomly pick a dev version of a package solely based on the constraint that you use when specifying the version.

Although this is technically true, this will only apply if you have specified the minimum stability to be dev in your composer.json file. By default, Composer will always select stable packages unless you specifically tell it otherwise using the -dev suffix under the require section, or if you have defined the minimum-stability configuration to dev.

Updating Packages

So far we’ve covered how to install and remove packages with Composer, as well as how to specify the version and stability of the packages your application relies on. The last major operation you will be doing with Composer will be to update your existing libraries. This is performed using the composer update command :

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files

By default, when running composer update, a number of actions are performed. First, if you have made any manual changes to your composer.json file to add or remove a package, it will process those and will install or remove the given packages. Additionally, if any of your package versions are not locked to an exact version, it will look for any updates and install them according to your version specification. Lastly, it will regenerate the autoloading file and lock file and finish its operations.

There are a number of options you can pass to composer update. For example, by passing --dry-run you can see what Composer would do before actually doing it. You may choose to pass --no-dev, which will cause it to skip updating or installing any packages defined under the require-dev declaration. You can also define specific packages that you want it to update without updating all of the packages defined in your composer.json file. You do this by passing one or many packages to it, such as:

$ composer update swiftmailer/swiftmailer guzzlehttp/guzzle

Installing Packages Globally

Composer can be used to manage and install packages globally , similar to PEAR. This can be useful for installing certain utilities globally or even for maintaining updates to a global install of Composer itself.

As an example, if we wanted to update our global version of Composer, we would run the following command:

$ sudo composer self-update

If we wanted to install a utility such as PHPUnit, we would use a command like this one:

$ composer global require phpunit/phpunit
Changed current directory to /home/vagrant/.config/composer
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
Using version ^5.2 for phpunit/phpunit
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
...
Writing lock file
Generating autoload files

Pay attention to the line that immediately follows when executing this command: Changed current directory to. This tells you it will install PHPUnit and its dependencies under /home/vagrant/.config/composer/vendor/. Our current user we’re logged in as is “vagrant.” which is why it chose this directory. You will need to add this directory to your global path for easy execution. Adjust the following command accordingly if you have a .bashrc or a .bash_profile file in your home directory. In my case, I have a .bashrc file, so that is what I’ll use:

$ cd ∼/
$ echo 'export PATH="$PATH:$HOME/.config/composer/vendor/bin"' >> ∼/.bashrc

Now, reload to pick up the path changes, either by logging out and back in or by using the source command:

$ source .bashrc

You can now execute phpunit:

$ phpunit --version
PHPUnit 5.2.9 by Sebastian Bergmann and contributors.

PEAR & Pyrus

As mentioned in the introduction of this chapter, PEAR (the PHP Extension and Application Repository) was once the only method that tried to create a distributable system for providing libraries in your PHP application. However, PEAR has fallen short in many different areas, which paved the way for the creation of better tools for dependency management, like Composer.

PEAR did have some big successes, was and is still used by many different libraries and packages out there, and is still the system that PECL uses to install PHP extensions. The creation of PEAR2 and Pyrus over the last several years was intended to address a number of the shortcomings of PEAR, but they have not seen the traction and widespread community adoption and development that Composer has been enjoying. As a result, PEAR2 and Pyrus have been in alpha status for over four years as of the time of writing.

Is Anyone Still Using Pear?

The answer to this questionin my own opinion, in the opinions of other developers, and based on the current download statistics available on the Pear Download Statistics page is both yes and no. The PHP7-compatible version of Pear has seen over 650,000 downloads since it was originally released in October of 2015, as of the time of writing. There are countless numbers of older PHP applications that still depend on various Pear packages, and therefore it is still in use with these. I do believe, based on our day-to-day development as well as the growing amount of libraries available on Packagist and the large number of open source platforms moving to use and support Composer (Zend Framework 2, Symfony Framework, Drupal 8, Magento 2, etc.), that the use of Pear as a library manager and for installing dependencies in applications is rapidly on a decline.

PECL

Despite the waning use of Pear overall, the PHP Extension Community Library, better known as PECL, is still quite active today. It is a public repository of PHP extensions and is often used to install libraries needed for development. PECL makes use of Pear for installing its libraries, which is evident when you look at the source of the pecl command :

#!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "/usr/bin/php" = '@'php_bin'@'; then
    PHP=php
  else
    PHP="/usr/bin/php"
  fi
fi


# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "/usr/share/php" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""
  else
    INCDIR="/usr/share/php"
    INCARG="-d include_path=/usr/share/php"
  fi
fi


exec $PHP -C -n -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"

Now, let’s look at the source of the peclcmd.php that is referenced in the last line of the pecl command:

<?php
/**
 * PEAR, the PHP Extension and Application Repository
 *
 * Command line interface
 *
 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <[email protected]>
 * @author     Tomas V.V.Cox <[email protected]>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @link       http://pear.php.net/package/PEAR
 */


/**
 * @nodep Gtk
 */
//the space is needed for Windows include paths with trailing backslash
// http://pear.php.net/bugs/bug.php?id=19482
if ('/usr/share/php ' != '@'.'include_path'.'@ ') {
    ini_set('include_path', trim('/usr/share/php '). PATH_SEPARATOR .get_include_path());
    $raw = false;
} else {
    // this is a raw, uninstalled pear, either a cvs checkout or php distro
    $raw = true;
}
define('PEAR_RUNTYPE', 'pecl');
require_once 'pearcmd.php';
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * mode: php
 * End:
 */
// vim600:syn=php


?>

As we can see here, the pecl command is nothing more than a wrapper around the pear command. Every time you use pecl to install a new PHP extension or update an existing pecl extension, pear is being used. Because of this, it makes downloading, compiling, and installing needed PHP extensions very easy. For example, if we wanted to install the APC userland extension APCu, we would simply execute the following :

$ sudo pecl install apcu
downloading apcu-5.1.3.tgz ...
Starting to download apcu-5.1.3.tgz (108,422 bytes)
.........................done: 108,422 bytes
39 source files, building
running: phpize
Configuring for:
PHP Api Version:         20151012
Zend Module Api No:      20151012
Zend Extension Api No:   320151012
...
install ok: channel://pecl.php.net/apcu-5.1.3

As simple as that, PECL and PEAR, along with phpize, have downloaded, compiled, and installed the APCu extension.

Note

If you are using the PHP version available from your operating system’s repository (yum / apt-get) then you can check first to see if a PECL extension is already available to be installed directly from the repository. This will not require the use of PECL.

Should I Be Using PEAR or Pyrus?

Based purely on the current development state and activity of Pyrus and the many benefits and availabilities of packages with Composer and Packagist, I believe PEAR or Pyrus are no longer the best options to be used in new development today.

Because of the global installation nature of PEAR and the management it provides, it can still be a useful tool, and in some cases it is still the only tool for installing system-wide dependencies for certain utilities. Let’s look at an example of installing the PHP CodeSniffer utility on a development machine using PEAR.

Installing a Global Utility Using PEAR

$ sudo pear install PHP_CodeSniffer
downloading PHP_CodeSniffer-2.5.1.tgz ...
Starting to download PHP_CodeSniffer-2.5.1.tgz (484,780 bytes)
....................done: 484,780 bytes
install ok: channel://pear.php.net/PHP_CodeSniffer-2.5.1

PHP CodeSniffer is now immediately available for your use. You can test if it is working like this:

$ phpcs --version
PHP_CodeSniffer version 2.5.1 (stable) by Squiz (http://www.squiz.net)

If you receive a warning when trying to run the previous command, such as PHP Warning:  include_once(PHP/CodeSniffer/CLI.php): failed to open stream: No such file or directory, this means that your php.ini for the PHP CLI either does not exist or does not have the PEAR install path added to the php path. You can check this by following these steps, first checking the include path of pear installed on your system:

$ pear config-get php_dir
/usr/lib/php/pear

Now check which configuration file your PHP CLI is using:

$ php --ini
Configuration File (php.ini) Path: /etc/php/7.0/cli
Loaded Configuration File:         /etc/php/7.0/cli/php.ini

This will give more output than the preceding code, but you want to look for the Loaded Configuration File line.

Now, taking the path of the configuration file listed previously, check the PHP include path:

$ php -c /etc/php/7.0/cli/php.ini -r 'echo get_include_path()."
";'
.:/usr/share/php:

So, as you can see, the include path does not include the pear include path. To solve this, we’ll open php.ini and add it to the include_path directive like this:

include_path = ".:/usr/share/php:/usr/lib/php/pear"

If you try to execute the phpcs command again, it will now execute because it knows where to include the files from.

Summary

In this chapter, we introduced Composer and Packagist and how to use them together for your application dependency management. We covered, start to finish, everything you need in order to use Composer right away as well as the various daily interactions you will use Composer for as you manage the dependencies of your application. We also looked at PEAR and its role in today’s PHP development.

My hope is that you walk away from reading this chapter with a very clear understanding of using Composer and the impact it can start making on your application development, and that you now have the resources and ability to use it right away.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.17.64