Writing code for anything takes time. Time usually spent lifting some components from other projects. After a while, you've probably already got 99% of the initial setup code for the new project elsewhere, floating around in your git repositories. Imagine just reusing this code from a single place in multiple projects. This is exactly the job of a dependency manager.
Every developer has been there. You load your editor for your new project, load the editor for an old project, and painstakingly start copying and pasting code.
Let's say you've got a class within your code, and it's complicated. You don't want to rewrite it again. Likewise, you know if there's a bug you'll likely need to fix it across several projects. This is the perfect use case for packaging it up for Composer.
What is Composer?
Composer is a dependency manager system for PHP. It's a way of forcing a project to use a very specific set of libraries at very specific points in their development history. For example, you could force a library called
SomeLib to use exactly version
1.2.345. It's very much similar to NPM for Node.js and Gradle for Java.
Right, but how do we get started with it?
First things first, Composer can either use public repositories (you'll find these on Packagist), or alternatively it can also use private repositories. We'll focus on private repositories for this exercise. There's a full working example of this in our GitHub organisation that you can refer to if you get stuck.
Let's imagine you have this class below and it's very complicated logic (I know, it's pretty basic looking, but bear with me!). Now, you've written it, and it's beautiful. So beautiful in fact that you're proud of yourself for writing something so great. Take a picture, send it around to your friends, boast on Twitter. Celebrate it!
Initially we want an entirely empty directory. It doesn't really matter what you call it, just make a directory, somewhere.
Within that directory we're going to need a folder called
src and a file called
The directory should look like this now:
Let's copy the class from above into the
src/ directory. The structure will now look like this:
src/ src/MySuperClass.php composer.json
Next we need to edit that
composer.json file. We'll start with the basic working version and get into the more complicated controls afterwards. Add this content to your empty
composer.json file, changing the value of the
"name" section to be
your-git-username/your-package-name. You might end up with something like...
For our example, we're using the package name of
district-5/blog-example-composer-package because our GitHub username is
district-5 and the repository is called
blog-example-composer-package (see this repository). This just makes things easier to translate, although you can name them anything you want.
We can give this a quick test. Assuming you have Composer set up (if not, visit here and install Composer before returning to the instructions).
In your terminal (or shell), run:
Composer will output the following. If there's an error, or something went wrong, look back at the examples above and make sure you've not made a mistake somewhere.
If it looks the same as the above, we can move on. You'll likely notice that there are some new items in the project tree. Specifically, a new
composer.lock file which contains all of the data needed for composer to reinstall the package, and a folder called
vendor folder will contain the all-important
autoload.php file contains the class mappings to automatically load a class when you ask for it.
Let's give it a quick test...
In the root directory (the same directory as your
composer.json file we'll create a
test.php file. Don't worry about this too much. We'll be removing it again in a second. Within this file you'll need the following content...
In your terminal, within the directory containing the
test.php file, we'll run a quick command to check the output.
Hopefully the output from this is
string(5) "world". If it is, great! If it's not, it's likely you've changed either the namespace in the PHP file, or the namespace in the
"autoload" section of the
We're finished with the
test.php file, so you can delete it now.
At this point, you've got everything you need to start packaging, so let's move on to the finer tuning. If you'd rather stay at this point, you can skip to the "Let's get packaging" section below.
The example class we used here has a typed property, which means it will 'require' at least PHP version 7.4 to work. We can add this to a new
"require" section within the Composer file. Add this section to the middle of the composer file (I generally place it above the
What we've actually done now is prevent any projects using less than PHP 7.4 from using this library. Nice 😎
Let's get packaging!
We're using git for this example, so to prevent the repository being filled with files we don't need, we'll create a
In this file, we'll be excluding files from git that we just don't need. For example, on a Mac you'll want to ignore
.DS_Store specifically, but for Windows there are files such as the
Thumbs.db file that you'll want to also exclude. GitHub runs an excellent repository of
.gitignore files here, and we've included out .gitignore file in our repository for this example.
For the basic example here let's just add the basics. Add the next lines into your
At this point, the only paths that will get committed to git are:
Let's initialise the repository and push it to your origin repository...
Let's check which files are pending...
Add these files to staging...
Commit the files...
Assuming you have a git repository set up already, let's add the origin and push. Remember to change the git repository link below
Now we need to push that commit into the new repository. We can do this by issuing...
Amazing. But let's create a tag of this code also. By doing so we lock in the version numbers. We'll start with
0.0.1. After we've created the tag, we'll also push it.
Using the tagged version...
We'll leave the library now. Let's go into a project directory and create (or edit) a
Because in this example we're using a a private repository, we'll need to tell the project level
composer.json (not the library file from above) where to find the the code. But if you're using a public repository, and you've set things up with Packagist, you can just omit the
"repositories" section entirely.
What we've done now is tell the project Composer where to find our
district-5/blog-example-composer-package and to always use version
0.0.1. You can alter the value from
"0.0.1" to be
>=0.0.1 if the required version should be at least
At this point, as long as your project references the
vendor/autoload.php file before usage of the library class you'll be reusing code!
Updating the library...
Let's say you've just made a change to the library and you want to update it in the project. Go ahead and change your library code, commit and push the change to master. Next you'll want to create the tag using the
git tag -a 0.0.2 -m 0.0.2 and push it with
git push origin 0.0.2.
Load the project (not the library). If you used
0.0.1 as the version in the project you'll edit the project level
composer.json file now to contain the following, but if you've used
>=0.0.1 you can skip this next snippet...
Next we'll update just this library. Be extra careful with this one. If you ran a global update you'd likely break another dependency.
Within the project directory (not the library)...
There you have it. Your project is now using version
0.0.2 of the library.
Good to know...
Projects are slightly different to libraries. Typically speaking, a library should not contain a
composer.lock file in the git history, and neither a project or library should ever have the
vendor directory committed. The versions required should be dictated by your project, although there are use cases where you may want to store the lock file for the library also.
Here at District5 we don't believe in writing the same code twice. By keeping clean libraries of code, we're able to vastly decrease development costs for our customers by reusing instead of rewriting.
We're incredibly proud to be proactive about squashing bugs and rolling out bug fixes for our customers in a timely fashion. Packaging plays a huge part in our business and helps streamline development.