Applying patches to OXID eShop projects with composer

The usage of modern tools like composer helps when deploying PHP application nowadays. Sometimes people miss the good old easy way to do things in a way they are used to. One of those things which got harder by using newer tools is patching the source code in external project dependencies. We faced the challenge here at OXID Professional Services team, too and we found an easy and flexible way to have the benefits of both worlds: using composer with its powerful ecosystem and autoloading functionality, and keep the flexibility to patch packages handled by composer located in the vendor directory of your project.

Sometimes you need an urgent fix for a bug or you want to use a feature which isn’t released yet or you simply want to put some code into a not extendable class or something like this. You can do so by using the composer plugin composer-patches. It allows you to specify a patch to any (sub-)package you require in your composer.json like this:

{
  "require": {
    "cweagans/composer-patches": "~1.0",
    "<foo/bar>": "~1.0"
  },
  "extra": {
    "patches": {
      "<foo/bar>": {
        "<give the patch a meaningful name>": "<https://url/to/patch/file.patch>"
      }
    }
  }
}

Using composer-patches with OXID eShop

Here’s an example how to patch an OXID eShop project with this composer plugin.

First I’ll install an OXID-project via composer:

$ composer create-project --no-dev oxid-esales/oxideshop-project oxid_composer_patches dev-b-6.0-ce

Now I’m going to install the composer plugin in my project inside the newly created folder ‘oxid_composer_patches’:

$ cd oxid_composer_patches 
$ composer require cweagans/composer-patches

After that, I add the following block to my composer.json file to the ‘extra’ section:

"patches": { "oxid-esales/oxideshop-ce": { "return empty default string for MariaDB": "https://github.com/OXID-eSales/oxideshop_ce/pull/709.patch" } },

A few things to note here:

  1. We’re not directly requiring the package ‘OXID-esales/oxideshop-ce’ in our composer.json, but we’re using the package “OXID-esales/oxideshop-metapackage-ce”. The metapackage requires ‘OXID-esales/oxideshop-ce’, so we’re even able to patch sub-packages.
  2. You can just add ‘.patch’ to a Pull-Request URL on Github to get the diff (I’m using the PR trim default value to detect empty string in Database class by alfredbez · Pull Request #709 · OXID-eSales/oxideshop_ce · GitHub as an example here)
  3. You can use external patches (via URL) or patches on your local filesystem by providing a path to the patch (we have a .patches directory at our repository root, where we collect project-specific patches)

Check out this guide from Tomas Votruba on how to generate patches directly in your vendor folder.

The extra should look like this now:

[...] 
"extra": { 
"patches": { 
"oxid-esales/oxideshop-ce": { 
"return empty default string for MariaDB": "https://github.com/OXID-eSales/oxideshop_ce/pull/709.patch" 
}
}, 
"incenteev-parameters": { 
"file": "test_config.yml", 
[...]

If you run ‘composer update’ now, you will see that the package “OXID-esales/oxideshop-ce” will be reinstalled and patched:

composer update output showing applied patches

You can verify, that the file is now patched:

$ grep 'has_default =' vendor/oxid-esales/oxideshop-ce/source/Core/Database/Adapter/Doctrine/Database.php
$item->has_default = ('' === trim($default, "'") || is_null($default)) ? false : true;