Build Your Own Software Packages
I recently refactored parts of a web application for an upcoming Twilio tutorial into three open source packages. Here’s what motivated the decision to do so, and why I think you should create them too.
How well do you really know Composer? Do you just know composer install, update, and require, and some of the composer.json configuration? Or do you really know it? In this series, you’re going to take your skills to the next level; starting with the command-line.
I know that a series on Composer might seem odd. But, as Composer’s been a part of PHP for so long now, I feel that it’s something which most of us take for granted.
It’s revolutionised the PHP landscape, making it easier than ever before to build great software in PHP. But do we really know how to get the most out of it. For that reason, I’ve created this series, so that you level up your skills and really get the most from it.
In part one of this series, I’m going to take you through Composer’s command-line. I’ve cherry-picked a key subset of the command-line options, and focused in on key switches, so that you can do more than you already can.
I’ve decided to skip install
, update
, and require
, as they’ve been covered copious times before. Sorry to disappoint if that’s what you were expecting.
We’re going to start off with the create-project option. This one I didn’t know about for the longest time. But when I did, I was more than excited to start using. If you’re not familiar with it, you’re probably used to doing the following:
git clone your_package_uri your/project/directory/path
cd your/project/directory/path
composer install
The create-project command does all this for you. Say that you’re creating an application based on Zend Expressive and it’s going to be in a directory called expressive-demo-app
in the current directory. Here’s how you would do it with the create-project command:
composer create-project -s rc zendframework/zend-expressive-skeleton expressive-demo-app
Note that I’ve passed the -s
switch, with rc
as a parameter to that. This indicates the minimum-stability level allowed. We could also have passed a number of others.
We could specify a working directory, other than the current one, using the -d
or --working-directory
switch. This can be helpful if you’re scripting up the process — or you just don’t want to cd
somewhere, then cd
back again.
We could avoid the package scripts running by passing the --no-scripts
switch. We could prevent Composer’s output progress indicator from running, if that causes issues with the script we’re using to automate the process, by passing the --no-progress
switch.
Interestingly, we can also prevent installation of the packages dependencies, by passing the --no-install
switch.
This is, I find, a very interesting and helpful command. Sure, you can open up composer.json
, but that’s not a scriptable process. So, instead use the show
command. This, as the name implies, shows a host of information about installed and configured packages.
It provides the ability to list the installed packages, list information about the current package (your composer.json configuration), and more.
Ok, let’s say I’m curious, and want to know the names of the currently installed packages for my application. To do that, I’d run the following command:
composer show -N # I could also pass the --name-only switch
This will print a sorted list of the names of the currently installed packages, similar to this:
behat/gherkin
codeception/codeception
container-interop/container-interop
dflydev/fig-cookies
doctrine/annotations
doctrine/cache
doctrine/collections
doctrine/common
How about getting a list of all the installed packages, with all their details? To do that, we’d instead pass either the -i
or --installed
switches. Doing that will show a listing similar to this:
zendframework/zend-loader 2.6.x-dev dcacfc3
zendframework/zend-servicemanager 3.2.x-dev 6ed63fd
zendframework/zend-stdlib 3.2.x-dev 16818ed
Here, you see the name of the package along with its installed version, and the git commit hash. As there’s quite some overlap with the outdated
command, which we cover later. So I’ll leave a more in-depth coverage of some of the commands until then.
Now what if I wanted to know all of the dependencies of the installed packages, whether that was another package or a PHP extension? This can be handy if you’re wondering why a package is installed, or just what it requires, without plumbing through copious composer.json files.
To do that, we’d pass the -t
or --tree
switch, which would give us output similar to this:
──symfony/browser-kit >=2.7 <4.0
│ ├──php >=5.5.9
│ └──symfony/dom-crawler ~2.8|~3.0
│ ├──php >=5.5.9
│ └──symfony/polyfill-mbstring ~1.0
│ └──php >=5.3.3
Here, you can see that symfony/browser-kit requires php 5.5.9 (or higher) and symfony/dom-crawler. Symfony/dom-crawler requires PHP 5.5.9, and symfony/polyfill-mbstring, and so on. Sometimes it’s hard to quickly find out what’s requiring what. This command can save you a lot of time.
What if I wanted to know about the current package. To do that, I could pass the -s
or --self
switches, which for my application, would return the following:
name : settermjd/zend-expressive-auth
descrip. : This package provides HTML-based authentication middleware for Zend Expressive applications
keywords :
versions : * dev-create-rest-authentication-class
type : project
license : BSD 3-clause "New" or "Revised" License (BSD-3-Clause) (OSI approved) https://spdx.org/licenses/BSD-3-Clause.html#licenseText
source : [] 9d62baa0975a5ddd779dd942b5494a73ec59cd4c
dist : [] 9d62baa0975a5ddd779dd942b5494a73ec59cd4c
names : settermjd/zend-expressive-auth
autoload
psr-4
App\ => src/App/
requires
php ^5.5 || ^7.0
doctrine/common ^2.6
monolog/monolog ^1.21
ocramius/psr7-session ^2.0
roave/security-advisories dev-master
shrikeh/teapot ^2.1
zendframework/zend-code ^3.0
zendframework/zend-config ^2.6
zendframework/zend-expressive ^1.0
zendframework/zend-expressive-fastroute ^1.0
zendframework/zend-expressive-helpers ^2.0
zendframework/zend-expressive-zendviewrenderer ^1.0
zendframework/zend-form ^2.9
zendframework/zend-servicemanager ^2.7.3 || ^3.0
zendframework/zend-stdlib ^2.7 || ^3.0
sandrokeil/interop-config ^1.0
requires (dev)
codeception/codeception ^2.2
filp/whoops ^1.1 || ^2.0
guzzlehttp/guzzle ^6.2
phpunit/phpunit ^4.8
squizlabs/php_codesniffer ^2.3
Here, you can see that it prints out the package meta data, followed by the configured autoloader namespaces, followed by the production and development dependencies.
We’ve seen in require and install how to add packages to a package. So it’s great that there’s an inverse command — remove. Remove, as the name implies, removes packages from your composer.json
and then runs an update to remove the physical dependency (and in turn their dependencies) from your vendor directory.
As with install and update, remove can remove one or many packages simultaneously. So, say I had zendframework/zend-stdlib and sandrokeil/interop-config as dependencies. Here’s an example of how I could remove just one, or both, of them:
// remove just one package
composer remove zendframework/zend-stdlib
// remove multiple packages at once
composer remove zendframework/zend-stdlib sandrokeil/interop-config
By default this only affects non-dev packages. So if you want to change development dependencies instead, then pass the --dev
switch as well.
Now as Composer’s used in a range of situations, from setup to deployment some of the default actions may or may not be desirable. So, it’s good to know that you can customize the process a little, to suit your needs.
If you don’t need to remove the dependency, then pass the --no-update
switch. If progress output would be a problem in your scripts, or you simply don’t want it, then pass the --no-progress
switch. If you don’t want any of the scripts defined for the package to run, then pass the --no-scripts
switch.
Ever tried to install a new package in an existing repository, but Composer’s not let you, complaining of a conflict? Perhaps you’ve seen output similar to that below and shaken your head, wondering how to fix it.
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Conclusion: don't install mcustiel/phiremock-codeception-extension v1.2.1
- Conclusion: remove zendframework/zend-diactoros 1.4.x-dev
- Installation request for mcustiel/phiremock-codeception-extension ^1.2 -> satisfiable by mcustiel/phiremock-codeception-extension[v1.2.0, v1.2.1].
- Conclusion: don't install zendframework/zend-diactoros 1.4.x-dev
- mcustiel/phiremock-codeception-extension v1.2.0 requires mcustiel/phiremock ^1.1.0 -> satisfiable by mcustiel/phiremock[v1.1.0, v1.1.1, v1.1.2, v1.2.0, v1.2.1, v1.3.0, v1.3.1].
- mcustiel/phiremock v1.1.0 requires mcustiel/power-route ^2.0 -> satisfiable by mcustiel/power-route[v2.0.0, v2.1.0, v2.2.0].
- mcustiel/phiremock v1.1.1 requires mcustiel/power-route ^2.0 -> satisfiable by mcustiel/power-route[v2.0.0, v2.1.0, v2.2.0].
/* truncated for readability */
Installation failed, reverting ./composer.json to its original content.
Here, I tried to install mcustiel/phiremock-codeception-extension
, but one of its dependencies depends on a version zendframework/zend-diactoros
.
This is further complicated because another package depends on a higher version of zend-diactoros
. While the message helps debug the basics, it doesn’t help us track down the issue fully.
That’s where the depends
option comes to the rescue. To find out which package also depends on Zend-Diactoros, I can run the following command:
composer depends zendframework/zend-diactoros
This turned up the following:
zendframework/zend-expressive dev-develop requires zendframework/zend-diactoros (^1.1)
Here, we see that Zend Expressive is the package that requires Zend Diactoros. That’s great. But why? Well, let’s go find out, by using the -r
switch.
The -r
or --recursive
switch, recursively searches through the required packages and provides a path from the searched package through each successive package, showing all the packages which are involved. Here’s what it showed when I used the --recursive
switch:
settermjd/zend-expressive-auth rest-auth requires zendframework/zend-expressive (^1.0)
zendframework/zend-expressive dev-develop requires zendframework/zend-diactoros (^1.1)
Here, I see that my package, which I’m developing, requires Zend Expressive ^1.0, which in turn requires Zend Diactoros ^1.1. Great that I know that, but it isn’t the easiest to read.
While text is great for computers, I find it a lot easier to read when it has some colour and formatting. Turns out there’s a switch for that as well: the -t
or --tree
switch. But re-running depends with one of the two, we get the output below:
zendframework/zend-diactoros 1.4.x-dev PSR HTTP Message implementations
└──zendframework/zend-expressive dev-develop (requires zendframework/zend-diactoros ^1.1)
└──settermjd/zend-expressive-auth dev-create-rest-authentication-class (requires zendframework/zend-expressive ^1.0)
Here, we see the packages which require Zend Diactoros, but in a much more readable way. When I found this, I never looked back.
Want to know which packages which your application uses that are outdated? Then the aptly named outdated option is for you. While a proxy to composer show -l
it’s helpful in its own right.
Let’s run it in the package that I’ve been working on and see what it shows. I’ll run composer outdated
, and the result is below, which I’ve truncated for readability.
composer outdated
codeception/codeception 2.2.x-dev 08cbe80 dev-master c8df4d7 BDD-style testing framework
container-interop/container-interop 1.1.0 dev-master 8cbf8b1 Promoting the interoperability of container objects (DIC, SL...
While a little hard to read, it has the following format:
package name / installed version / latest installed commit / latest version / latest commit / package description
There’s a lot of information being shown. But it’s helpful if you really want to dig deep. To help out, the output of the latest version and commit hash, which you won’t see here, is color-coded.
If it’s green, you’re at the latest version. If it’s yellow, there’s an update available. If it’s red, there’s a new version available, which is semver-compatible, and you are advised to upgrade.
By default, you see everything, which isn’t always helpful. In my own case it was quite overwhelming. There are two switches which help us make sense of this; these are:
--d
or -D
Show only packages that are outdated--outdated
or -o
Show only packages directly required by the root package (your application)You know when you run composer require
you often see a message similar to “This package suggest package X”? These are a list of packages which the package maintainers suggest that you use, but don’t enforce you to. How often do you pay attention to these?
How often do you wonder what they were, and want to find them quickly and easily? Sure, you can look through all the composer.json files, to find this out. But that’s tedious. The suggests
command does it all for you.
Running composer suggests
in my own repository, it suggested a large list, which includes the following:
aws/aws-sdk-php
doctrine/couchdb
ext-amqp
ext-intl
fabpot/php-cs-fixer
filp/whoops
mdanter/ecc
mongodb/mongodb
mouf/pimple-interop
ocramius/proxy-manager
php-amqplib/php-amqplib
php-console/php-console
rollbar/rollbar
ruflin/elastica
xtreamwayz/pimple-container-interop
zendframework/zend-authentication
zendframework/zend-expressive-aurarouter
zendframework/zend-expressive-zendrouter
zendframework/zend-feed
zendframework/zendservice-recaptcha
As well as the other packages, they were printed out as you see them listed above. No duplicates, and sorted in alphabetical order for readability. How long would it take you to go through all the composer.json files and do that by hand, or to create a script to do it for you? Arguably, too long!
Perhaps though you’re not interested in every suggestion. Perhaps you’re just interested in the suggestions for a specific package, such as Zend Expressive. If that’s the case, then include that package (or those packages), like this:
composer suggests zendframework/zend-expressive
Want to know why packages were suggested? There’s a switch for that. It’s the --by-suggestion
switch. By adding it to the previous command, I’m shown the following output:
aura/di is suggested by:
- zendframework/zend-expressive: 3.0.*@beta to make use of Aura.Di dependency injection container
filp/whoops is suggested by:
- zendframework/zend-expressive: ^2.0 to use the Whoops error handler
xtreamwayz/pimple-container-interop is suggested by:
- zendframework/zend-expressive: ^1.0 to use Pimple for dependency injection
There, you can see each of the suggested packages, and why they were suggested. That makes it explicitly clear. Now you know why you would use them, and when.
Those were nine of Composer’s key commands, along with a series of command switches; ones which should help you make even better use of Composer than you already do.
In the second part of the series you’ll learn how to automate projects using Composer scripts. And in the third and final part of the series, you’ll learn how to use forked repositories with Composer.
If you want to know more, I strongly encourage you to check out the command-line section of the Composer manual. It’s filled with a wealth of great information.
Didn’t like my command choices and switches? Which ones would you have chosen? And why?
I recently refactored parts of a web application for an upcoming Twilio tutorial into three open source packages. Here’s what motivated the decision to do so, and why I think you should create them too.
Do you need to set environment variables for Composer scripts to work? Here’s how.
If you need to migrate Zend Expressive applications from version one to two, don’t do everything by hand! Save yourself time, and make use of Zend Expressive Tooling.
For the longest time, Zend Framework hasn’t had the strongest support for command-line tooling and scaffolding. However, in recent times, that’s all changed. Come find out how to use Expressive’s new tooling support to create modules and middleware rapidly.
Please consider buying me a coffee. It really helps me to keep producing new tutorials.
Join the discussion
comments powered by Disqus