Laying the Groundwork
Last night I gave a talk Laying the Groundwork at Seattle PHP. Over the years, I’ve seen a lot of different projects. Their quality and maintainability varied greatly. So I wanted to help my fellow developers to learn from the mistakes of developers past. Also, judging by what I’ve seen on the web, some PHP developers aren’t using Composer yet. Maybe they haven’t discovered it yet? Maybe they’re just new to PHP? Doesn’t matter. Without making assumptions anyone’s skill level, I have tried to add something for everyone. This article is the same content as my talk in a blog format. I’ve also taken the liberty of expanding upon my thoughts.
Project Structure
There are a number of ways you can structure your project. One way is just to put everything in your main project folder, but that isn’t really a desireable strategy for anything more than a few files. You want to be able to control how your project grows. It’s important build upon a solid foundation. Also, when you can build projects in a predictable way, they are easier to learn and develop A good way to accomplish this is to use conventions that other developers are using.
One option is to use this skeleton project.
$ composer require pds/skeleton
This project provides a validator and a generator. If you like, it’ll create a project structure for you. This project was created by Paul Jones. He did some research on how projects on packagist named their directores and files.
He was right. Before finding his project, I made my ownskeleton project before I found the pds/skeleton
project.
While this project is a useful tool, you don’t have to use it. You can choose to create a few key files and directories that you need.
Key Directories
These are the top-level directories you’ll most likely use for your project.
- bin/
- docs/
- public/
- src/
- tests/
- vendor/
The bin directory
This is where you put your command line programs. This follows the UNIX tradition. Depending upon your application, you may choose to add this directory to your system PATH variable.
The docs directory
A project isn’t really ready to be published until you write some documentation. When you do write it, put it in this directory. The format of the documentation is up to you. You can write it in HTML, Markdown, or something else. I would recommend avoiding PDF or Word files, since this is developer documentation. While Mardown is easier to write, HTML has a a richer capability set.
The public directory
This directory is where you should point your web server. It’s represent the root of your website.
The src directory
When developing a composer package, src/
is where your PHP code goes. There must be only be one class per file, and have a namespace. This directory is also where you map the autoloading of your namespace in composer .json
.
The tests directory
You are writing tests, aren’t you? If not you should be. When you do, put them here.
The vendor directory
The vendor/
directory is special. You don’t edit anything in this directory. It’s where composer puts all the required libraries for your project. Because it contains genereated content, you should not add this to your version control. In fact, project often add vendor
to their .gitignore
file. That helps keep it out of git, and hides the changes that happen within it from you.
Key Files
- CHANGELOG
- LICENSE
- README.md
- composer.json
- composer.lock
- vendor/autoload.php
Some of those files will be familiar to you. I’ll only discuss the ones relevant to composer.
composer.json
This is your primary Composer config file. This is the file to edit when you want to update your package list, add autoloading, change versions, or add repositories.
composer.lock
This file is important to composer, but you shouldn’t edit it directly. This file is used to track the packages and the versions that have been installed. composer.lock and composer.json should both be tracked in version control. They ensure that each user of the package are working with the same set of packages and dependencies.
vendor/autoload.php
The contents of vendor/autoload.php
are regenerated regularly. This file is read by your scripts to access the packages that composer is managing for you. Since this file is in the vendor directory, you’re not commiting it to version control.
Composer
Composer is a dependency manager for PHP according to the composer homepage.
What does Composer do?
Sometimes people called a package manager, but it’s more than that. Composer does a few things for you.
Install Packages
The first thing Composer does, is install and update packages for you. By default, there are 4 types of packages, sorted from the most common to least.
- library
- project
- metapackage
- composer-plugin
The library type is the most common, and it’s the default. So if you forget to specify your package type in composer.json, it will assume it’s a library.
The project is used by some frameworks like Laravel and CodeIgniter. The other two you probably won’t use. Just know that they are available.
Composer doesn’t work in a vacuum. It has to get its packages from somewhere. That place is Packagist.
Packagist is a repository of PHP packages developed by people from all around the world. You can submit your own packages. I recommend that you browse the packages. There’s a lot of great stuff in there.
The predecessor to Packagist was PEAR. In its day, PEAR was influential, and it helped move the PHP ecosystem forward. Today it’s not widely used anymore. I’m sure theres probably one or two things it’s used for, but your time is best spent not worrying about PEAR. Just know that it was important in its time, and move forward by developing with Composer.
Developer Packages
I think of developer packages as being a package subtype, although technically they are not all that different. Their primary difference is how they are installed.
The only difference is you use the --dev
option on the command line, which tells Composer to put them in the require-dev section of the composer.json. Why would someone want to dot this?
These packages are meant to only be used in the development environment. Using the dev option allows you skip the installation of dev packages. By keeping them separate, you can choose not to install them depending upon which environment you want to deploy your application. PHPUnit is a good example. You probably don’t want to run your unit tests in production.
Check Dependencies
Composer is installing package dependencies, but it’s also checking that there aren’t any conflicts between package versions. If there are conflicts, the installation of the package will fail, and then it’s up to you how you want to resolve it.
You can also set the minimum PHP version that your package requires. If the user tries to install your package on a insufficient version of PHP, it’ll fail.
Load the Autoloader
With this one line, you load all the files that composer is managing for you. Your days of adding many require or include statements at the top of your files is over. This results in code that is much cleaner, and easier to manage.
require 'vendor/autoload.php';
Common Composer Commands
This is a list of the commands you’re most likely to use working with Composer
- list
- init
- require
- remove
- dump-autoload
- info
- update
Some of them will be discussed in the next section. I list them above, so that you explore them on your own. You can look them in the documentation on getcomposer.org or in the terminal with composer help <command>
where <command>
is what you want to look up.
$ composer help init
Create a Package
Ok. That’s enough theory and background for now. Let’s step through the process of creating a package
Initialize Your Package
$ composer init \
--license=MIT \
--description="Briefly describe your project" \
--author="Andrew Woods <andrew@example.com>"
Set the Minimum PHP Version
The following command will update your composer.json for you.
$ composer require php ^7.2
The minimum PHP version you choose, should be an actively supported verison. If your current version has moved into the security updates only phase of support, I recommend you update this value to the highest actively supported version, so you can maximize its use.
Add a Package
Composer packages use the format vendor/package, where vendor names represent an organization or developer.
Intuitively, you might want to type composer add
. I know I do. However, the correct command is composer require
. The way to remember that — PHP uses require to load files. So consider composer require analogous to that.
$ composer require vlucas/phpdotenv
Common Application Packages
- monolog/monolog
- symfony/http-foundation
- symfony/console
If you’re using Symfony or Laravel, these are already being used. If you’re create your own framework though, these are definitely worth using.
Add a Developer Package
This is the same command as above, just with an additional--dev
option.
$ composer require --dev phpunit/phpunit
Common Developer Packages
These are just some of the packages that you might want to consider using
- phpunit/phpunit
- squizlabs/php_codesniffer
- phpstan/phpstan
- roave/security-advisories
Add Autoloading To Your Package
Autoloading allows you load a class dynamically when you use it. In your composer file, you need to specify which directory a specific namespace can be found.
Autoload Your Classes
"autoload": {
"psr-4": {
"Awoods\\Example\\": "src/"
}
}
Using Scripts
One way to use scripts is use it as a custom command When you require the squizlabs/php_codesniffer package, two scripts get added to the vendor/bin directory. Below I’m creating a command called cs-check
to call the phpcs command – which will examine all the files in the src
directory and evaluate them according to the PSR-2 coding standard.
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
"scripts": {
"cs-check": "phpcs --standard=PSR2 src",
},
When you run composer list
you will see the following auto-generated description.
Runs the cs-check script as defined in composer.json.
It’s not a very useful description. That can be fixed. You can add custom description. Try to keep it short so it fits on one line in your terminal.
"scripts-descriptions": {
"cs-check": "Check src/ for code style errors",
}
This is a much better description. It helps the developer experience, without affecting your end users.
Personal Repositories
While Packagist is the default location to look for packages, it’s not the only place. You can add your own repositories to search for packages. Perhaps you’re planning to publish your package on Package, but it’s still under development; Maybe it’s a private package for your company. Either way, adding a personal repository to you composer.json is a nice technique to use.
"repositories": [
{
"type": "vcs",
"url": "https://github.com/andrewwoods/world"
}
],
Github is used here. However, it’s not the only option. GitLab and BitBucket can also be used at git repos. However, you can also use SVN, Fossil, and Mercurial. One thing you’ll notice is the lack of a package name. So, how does Composer know which repository has a package? In each repo, there’s a composer.json file. It uses that file, and caches the information locally.
Conclusion
This is just the beginning, but it does the job of laying the groundwork for creating a solid project foundation. One simple idea to expand upon this, is to use a git commit hook to call phpcs to examine your code before you commit.
There’s also other composer commands you can explore. Also, don’t forget to explore the different options. One thing I didn’t get to explore some additional fields in the composer.json schema. So, take some time to look through it, and you’ll be sure to find some great ways to enhance your project.