Dependency Management and WordPress: A Proposal

I came across this article – “Dependency Management and WordPress: A Proposal“, which provides an excellent overview of some of the recent developments and discussions in the area of composer integration with WordPress, and even more generically, some of the issues around dependency management in an ecosystem as large and complex as that of WordPress.

It’s been a while since I checked what’s going on in this area.  A couple of years back, I linked to an article that shows a way to use composer with WordPress, and since then I’ve built something similar for our use at work.

But it’s good to see that the problem is not tossed and forgotten, and that there are some very smart people still trying to work it out.

composer-git-hooks – manage git hooks in your composer config

composer-git-hooks looks awesome!  From the project page description:

Manage git hooks easily in your composer configuration. This package makes it easy to implement a consistent project-wide usage of git hooks. Specifying hooks in the composer file makes them available for every member of the project team. This provides a consistent environment and behavior for everyone which is great.

Validating JSON against schema in PHP

GitHub was rather slow yesterday, which affected the speed of installing composer dependencies (since most of them are hosted on GitHub anyway).  Staring at a slowly scrolling list of installed dependencies, I noticed something interesting.

...
  - Installing seld/jsonlint (1.6.0)
  - Installing justinrainbow/json-schema (5.1.0)
...

Of course, I’ve heard of the seld/jsonlint before.  It’s a port of zaach/jsonlint JavaScript tool to PHP, written by Jordi Boggiano, aka Seldaek, the genius who brought us composer dependency manager and packagist.org repository.

But JSON schema? What’s that?

The last time I heard the word “schema” in a non-database context, it was in the XML domain.  And I hate XML with passion.  It’s ugly and horrible and should die a quick death.  The sooner, the better.

But with all its ugliness, XML has does something right – it allows the schema definition, against which the XML file can be validated later.

Can I have the same with JSON?  Well, apparently, yes!

justinrainbow/json-schema package allows one to define a schema for what’s allowed in the JSON file, and than validate against it.  And even more than that – it supports both required values and default values too.

Seeing the package being installed right next to something by Seldaek, I figured, composer might be using it already.  A quick look in the repository confirmed my suspicion.  Composer documentation provides more information, and links to an even more helpful JSON-Schema.org.

Mind.  Officially.  Blown.

At work, we use a whole lot of configuration files for many of our projects.  Those files which are intended for tech-savvy users, are usually in JSON or PHP format, without much validation attached to them.   Those files which are for non-technical users, usually rely on even simpler formats like INI and CSV.  I see this all changing and improving soon.

But before any of that happens, I need to play around with these amazing tools.  Here’s a quick first look that I did:

  1. Install the JSON validator: composer require justinrainbow/json-schema
  2. Create an example config.json file that I will be validating.
  3. Create a simple schema.json file that defines what is valid.
  4. Create a simple index.php file to tie it altogether, mostly just coping code from the documentation.

My config.json file looks like this:

{
	"blah": "foobar",
	"foo": "bar"
}

My schema.json file looks like this:

{
	"type": "object",
	"properties": {
		"blah": {
			"type": "string"
		},
		"version": {
			"type": "string",
			"default": "v1.0.0"
		}
	}
}

And, finally, my index.php file looks like this:

<?php
require_once 'vendor/autoload.php';

use JsonSchema\Validator;
use JsonSchema\Constraints\Constraint;

$config = json_decode(file_get_contents('config.json'));
$validator = new Validator; $validator->validate(
	$config,
	(object)['$ref' => 'file://' . realpath('schema.json')],
	Constraint::CHECK_MODE_APPLY_DEFAULTS
);

if ($validator->isValid()) {
	echo "JSON validates OK\n";
} else {
	echo "JSON validation errors:\n";
	foreach ($validator->getErrors() as $error) {
		print_r($error);
	}
}

print "\nResulting config:\n";
print_r($config);

When I run it, I get the following output:

$ php index.php 
JSON validates OK

Resulting config:
stdClass Object
(
    [blah] => foobar
    [foo] => bar
    [version] => v1.0.0
)

What if I change my config.json to have something invalid, like an integer instead of a string?

{
	"blah": 1,
	"foo": "bar"
}

The validation fails with a helpful information:

$ php index.php 
JSON validation errors:
Array
(
    [property] => blah
    [pointer] => /blah
    [message] => Integer value found, but a string is required
    [constraint] => type
)

Resulting config:
stdClass Object
(
    [blah] => 1
    [foo] => bar
    [version] => v1.0.0
)

This is great. Maybe even beyond great!

The possibilities here are endless.  First of all, we can obviously validate the configuration files.  Secondly, we can automatically generate the documentation for the supported configuration options and values.  It’s probably not going to be super fantastic at first, but it will cover ALL supported cases and will always be up-to-date.  Thirdly, this whole thing can be taken to the next level very easily, since the schema files are JSON themselves, which means schema’s can be generated on the fly.

For example, in our projects, we allow the admin/developer to specify which database field of a table is used as display field (in links and such).  Only existing database fields should be allowed.  So, we can generate the schema with available fields on project deployment, and then validate the user configuration against his particular database setup.

There are probably even better ways to utilize all this, but I’ll have to think about it, which is not easy with the mind blown…

Update (March 16, 2017): also have a look at some alternative JSON Schema validators.  JSON Guard might be a slightly better option.

Composer require inline alias

Here’s a feature of composer that I didn’t know about until a few days ago – require inline alias.  Here’s the example from the documentation:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/you/monolog"
        }
    ],
    "require": {
        "symfony/monolog-bundle": "2.0",
        "monolog/monolog": "dev-bugfix as 1.0.x-dev"
    }
}

This is super useful when you have dependencies in your project that require a particular version of a third-party library or plugin, and you want to try a branch of that library or plugin. Switching to the branch alias doesn’t solve the problem, as everything that has version constraints on that requirement, will complain. With inline alias, you can alias a particular branch of the dependency as a particular version.

With inline alias, composer will fetch the branch that you want, but will assume that that branch works as a particular version that you specify, and thus satisfy all the other dependencies that require that particular version.

In my particular case, I was working on the CakePHP-based application, which was using a few CakePHP plugins (installed via composer).  Those plugins require CakePHP v3+.  I wanted to test a branch of CakePHP which had a particular fix I was interested in, but without disabling all the plugins.  Switching my application’s composer to require a branch dissatisfied all the plugins, as now composer didn’t know if the branch that I am requiring is of the CakePHP v3 or not.  Aliasing the branch to v3.4.1 (current stable version at the time) worked like a charm.

composer-patches – Simple patches plugin for Composer

composer-patches is a plugin for Composer which helps with applying patches to the installed dependencies.  It supports patches from URLs, local files, and from other dependencies.

I think this is absolutely brilliant!

It’s quite often that one finds bugs and issues in external dependencies.  Once the bug (or even the pull request with the fix) is submitted to the vendor, it can take anywhere from a few hours to a few weeks to be resolved and a new version to be released.

If you have a fix for the problem and need it in your project right away, and can’t wait until the vendor releases the new version, your best choice is to fork the dependency, fix the problem, and use your repository instead of the vendor’s package.  This works, but it’s messy.

With the patches plugin to composer, you can still use the vendor’s package and just apply a patch with composer, until the new version is available.  Clean and simple.

This also helps with testing things and working with different changes by different people, if you want to try things out – no need to choose between multiple repositories.  Just select the patches that you want and apply them at the environment you need.

Given that most development work is happening on GitHub these days, this composer plugin is even more useful than what I might think at first.  You see, GitHub provides patch and diff URL for each commit – all you need to do is add the extension to the URL.  For example, take this recent commit to my dotfiles repository.

Commit screen

If you add a “.patch” extension to this URL, you’ll get a patch output, which is useful for git am, and other commands (more on using git with email):

Patch screen

 

If you add a “.diff” extension to this URL, you’ll get a unified diff output, which you can either apply with diff and patch utils, or use with the composer-patches plugin.

Unified diff screen

So, this gives you a way of applying any commit on GitHub (and other repositories) via composer to any of your dependencies.  This is mind blowing!

 

Composer magic

Now that everyone is super comfortable with composer, I thought I’d share these two gems which I didn’t know or think about.

composer info

This command lists all of your packages installed with composer.  This is super handy if you want to include a page in your project, listing all the libraries and versions which are currently installed.  It also gives you a description of each library as provided by the package.

composer outdated

This command lists packages which you are using, which have updates available.  With this you can have a better understanding of what will happen if you run composer update (depending on your composer.json of course).

Update (July 21, 2016): Guess what? There is even a way to combine the two with one command: composer info -l .  This will list all the packages, with their versions and descriptions, and with an additional column of the latest version for each package.

Adventure in composer private repositories

First of all, I would like to take this opportunity and wish composer a happy birthday and many more years to come.  It’s been five years, and the world of PHP has changed so drastically that not many people remember how it used to be before.

I would have completely missed the birthday if it wasn’t for all the Google searching I did while working on a weird problem.  But before I get to the problem, let me set the scene.

The big part of composer success is Packagist.org – the repository of almost 100,000 packages (93,572, according to statistics, as of this writing, to be precise), which help one to solve pretty much any problem that PHP can be used for.

As good as the Packagist is, there is often a need for a repository or a package elsewhere.  Whether it’s a commercial library, or sensitive corporate code, having an ability to store it outside of public eye and handle with the same ease and the same tool as the rest of the dependencies is a very welcome feature.

Now we are getting closer to what actually happened.  A while back, I’ve setup deployment and development process for WordPress-based projects.  WordPress doesn’t support composer natively, but there are ways to make it work.  Huge thanks here goes to Roots.

So.  The composer.json file was filled with additional repositories and requirements which told composer where from to fetch things, and where to install them.  Here’s an example:

"repositories": [
    {
        "type": "package",
        "package": {
            "name": "wordpress",
            "type": "webroot",
            "version": "4.4.1",
            "dist": {
                "type": "zip",
                "url": "https://github.com/WordPress/WordPress/archive/4.4.1.zip"
            },
            "require": {
                "fancyguy/webroot-installer": "1.1.0"
            }
        }
    },
    // ...

This got especially useful, when we need to work with commercial plugins, which we couldn’t push to our public repositories, and which we didn’t want to re-distribute with every project.

So far so good. Fast-forward to a few month ago. We are setting up similar development and deployment process, but now for CakePHP-based projects. Things are much easier, since CakePHP 3 natively supports composer for the application itself and for its plugins.

But we still have the need for private repositories here and there, so we follow the same setup as we did for WordPress. This week, we had an unexpected roadblock, which wasn’t making much sense to me. When trying to pull a CakePHP plugin from a private repository, we’d get a nasty exception like this:

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing qobo/cakephp-test-foo (1.0.1)
    Loading from cache


                                                                                                                                              
  [RuntimeException]                                                                                                                          
  Unable to get primary namespace for package qobo/cakephp-test-foo.                                                                          
  Ensure you have added proper 'autoload' section to your plugin's config as stated in README on https://github.com/cakephp/plugin-installer  
                                                                                                                                              

This was confusing. First of all, I’ve never seen this error before. Secondly, I’ve read the README and had the autoloader section in the composer.json as instructed. Thirdly, very similar plugins were working fine.

Long story short, the issue wasn’t related to whether or not the GitHub/BitBucket repository was private or public. It was related to how the repository was configured in the composer.json. Reading and re-reading composer documentation about Repositories finally helped.

You can have a look at our test setup:

CakePHP plugin installer requires the autoload section in composer.json.  But, when the repository is of type “package“, for some reason, the autoload section is ignored altogether.  The RuntimeException “Unable to get primary namespace for package” is thrown by cakephp/plugin-installer (src/Installer/PluginInstaller.php line 274).  The problem is not actually in the cakephp/plugin-installer, but somewhere in the composer.  Maybe it’s intentional or maybe it’s not.  I didn’t have the time to investigate and understand it further.

Switching the repository to type “vcs” fixes the problem.  It also simplifies the composer.json file and removes the need for multiple version definitions, as now composer is using BitBucket/GitHub API to fetch available versions.