Why Plugin Authors Ignore Performance
45sHigh relatability for developers frustrated with third-party code; sparks debate about plugin quality.
▶ Play Clip"Delivers a clear, practical tutorial with a real performance improvement, but some filler and repetition."
This video demonstrates how to use Composer patches as a last resort to fix performance issues in third-party PHP code, specifically applying a patch to Doctrine DBAL to improve Shopware 6 performance. The presenter shows the installation of the composer-patches plugin, creating and applying a patch, and measuring the performance improvement.
Plugin authors often don't prioritize performance, making it necessary to patch third-party code. Composer patches is a solution for projects using Composer.
Composer patches is a plugin that is not shipped by Composer itself. It allows loading patches from remote sources, but this is dangerous.
Loading patches from remote sources (e.g., GitHub gists) is dangerous because code can change at any time. Always use local patches that are part of your project and can be vetted.
Patching third-party libraries voids their support and guarantee. You must perform your own tests to ensure the patched code works as intended.
The video applies a patch to Doctrine DBAL's SQL parser to improve Shopware 6 performance. The patch combines multiple regular expressions into one using named capture groups.
The plugin is installed via Composer. Version 2 is in beta, so you need to specify the exact version. The command asks for trust to execute scripts.
The patch is obtained from a GitHub pull request by appending '.patch' to the URL. The patch is saved in a 'patches' directory.
In the 'extra' section, add a 'patches' key, specify the library (doctrine/dbal), and provide a description of the patch.
Patching allows pulling performance fixes into older versions without a full upgrade, which might break other things. It's a way to get benefits earlier.
Run 'composer patches relock' to apply patches. Then 'composer patches repatch' without scripts to reinstall the library and apply the patch cleanly.
The patched code uses a combined token pattern with named capture groups, making the parse method simpler and faster.
Using call graphs, the parse method took 24ms before the patch and 10ms after, a ~50% improvement. The combined preg_match replaces three separate calls, and the closure indirection is removed.
Composer patches are a powerful last-resort tool for fixing performance issues in third-party code, but they must be used carefully with local patches and thorough testing. The example shows a significant performance gain with minimal effort.
What is the main purpose of the composer-patches plugin?
It allows you to apply patches to third-party Composer packages to fix issues like performance problems.
00:02
Why should you avoid loading patches from remote sources?
Because the code can be changed at any time, making it dangerous to dynamically load unvetted code.
01:28
What is the command to install the composer-patches plugin?
composer require cweagans/composer-patches:^2.0.0-beta2 (or similar version)
03:11
How do you get a git-compatible patch from a GitHub pull request?
Append '.patch' to the pull request URL.
04:56
What is the command to apply all patches after modifying composer.json?
composer patches relock
07:07
What is the purpose of running 'composer patches repatch --no-scripts'?
It uninstalls and reinstalls the library, then applies the patch to ensure a clean state.
07:24
What was the performance improvement in the example?
The parse method went from 24ms to 10ms, a ~50% improvement.
09:29
What technique is used in the patch to improve performance?
Combining multiple regular expressions into one using named capture groups.
04:27
Remote patches are dangerous
Highlights a critical security risk when using composer patches.
01:28Patching voids support
Important caveat for developers considering patching third-party code.
02:1050% performance improvement
Concrete evidence of the effectiveness of the patch.
09:29Combining regexes with named capture groups
A reusable technique for optimizing regex-heavy code.
04:27[00:02] plugins is a major source of pain for developers working with Magento, Shopware, WordPress or other customizable off-the-shelf PHP applications. Mo, I am Benjamin and in this video I'm going to show how to use
[00:17] composer patches as a last resort to fixing performance problems in third party code. I show how to install the composer patches plugin and an example from doctrine debal that improves Shopware 6 performance. It's a sad but
[00:32] common truths. Plug-in authors more often than not are not interested improving the performance of their code. That makes it necessary to sometimes patch the code from third-party plugins. And one way of doing that in projects
[00:47] that are using composer to install thirdparty plugins is the composer patches plugin. You can find an example of using composer patches to fix performance in thirdparty libraries on our tidebase blog. There I also link to
[01:02] the composer patches plugin on GitHub. Um and the project also has its own website that you can look at with information on the plug-in. This is not information on the plug-in. This is not a primary composer functionality but
[01:16] acts as a plug-in that is not shipped by the composer author itself. There's also a huge word of warning that I need to issue about this. Composer plugins has a
[01:28] feature that allows you to load patches from remote sources. for example, from a from remote sources. for example, from a GitHub gist or any other remote URL dynamically during uh installation of a project. I don't recommend using this
[01:44] remote approach because it's really dangerous to dynamically load code from an unvetted source especially if the code can be changed uh at any point in
[01:56] time. You should always use composer patches in local mode. That means the patches are part of your um project and you can vet those patches. Even if you're using patches to change third party libraries, there's
[02:10] still a word of warning I have to issue. You're obviously breaking all guarantee seals and voiding um sort of the support of a third party library that they have tested their code um to be bug free or as buckree as possible. you should uh
[02:27] perform your own tests with the patched code to make sure it works as intended. For this video, we are going to look at the doctrine debal SQL parser patch again that I talked about in a previous video. Here we are going to apply this
[02:42] patch to an existing Shopware installation to see how we can make use of composer patches. doctrine debal is usually a well-maintained library and you can upgrade it using composer directly. I'm
[02:58] just show going to show this uh to demonstrate how composer patches can work. Following along uh my blog post, we see how we can install the composer plugin. I'm going to copy the
[03:11] instructions. Currently, composer patches uh version two is in beta. That means we need to uh specify the exact version you want to install. If you're watching this video, maybe check if there's still beta 2, the most recent
[03:26] version, or you need to use a different version to install version to install it. Switching over to PHP Storm, we are uh entering the composer command to install this plugin.
[03:44] It asks us if we u trust the plugin to execute uh scripts and we do uh say yes. execute uh scripts and we do uh say yes. The plugin is going to be installed. When the installation is finished, we uh add a patch
[04:00] finished, we uh add a patch to the library. Let's look over the code of um the class that we want to patch again shortly to uh remind us of what we're doing here. The SQL parser of doctrine
[04:15] doctrine um had an inefficient way of parsing SQL statements by uh running several regular expressions
[04:27] on the string independently over and over again calling prep match for each individual um regular expression. Uh the patch that we are going to apply is combining this regular expression in a into a single
[04:43] regular expression and using named capture groups to differentiate which of the different patterns was matched uh at any single time a point of time when the
[04:56] uh regular expression was applied. We can grab that patch directly from GitHub uh going to the pull request on the doctrine data repository appending.patch patch to the URL. We get the git compatible patch. I copy this
[05:13] over. Um, go back to PHP Storm. In PHP Storm, I have a patches Storm. In PHP Storm, I have a patches directory. I create a new patch
[05:27] here. Doctrine Doctrine BalSQL parser pong patch.
[05:39] pasting the code in there. The next step is to modify the composer JSON file. In the extra section, we add a new
[05:51] patches and we specify which composer library we and we specify which composer library we are going to patch. doctrine debal
[06:03] are going to patch. doctrine debal and then we specify another key value pair explaining what the patch is about. SQL paser 3.10 improvement. So in this case we are pulling along a performance fix uh into
[06:20] pulling along a performance fix uh into a um an older version just to uh get the a um an older version just to uh get the benefit earlier um than uh updating to
[06:35] be necessary if the changes from the current version you're on to the version with a fix are too big and they break other things in your application. This way you can partially uh pull code from u more recent versions into your
[06:51] project. Uh especially for performance fixes. This is a really nice way of um possible. To apply this new patch to the project, we run a command that uh the plug-in provides to composer. So composer
[07:07] patches relock applies all the patches. Again, after locking the patches, we can look at the patches.lock pong JSON to see that the doctrine debal patch that we applied uh was registered. We then run composer patches repatch without
[07:24] run composer patches repatch without scripts and it will uninstall doctrine debal reinstalled it again and then apply the patch to have a clean version. We can see that here and after it has optimized the
[07:38] autoloader we can go back to the paza.php and see that the code has paza.php and see that the code has changed to the uh code from the patch. It includes the token pattern with the named capture groups now. And the code
[07:53] in parse is much much simpler. Just executing the P match with this combined token and then based on the named capture group calling different methods capture group calling different methods on the passing visitor. And uh this will
[08:08] improve the performance of uh this API quite a lot because there are just so much fewer functions being called and those functions that are called like PR match are not called multiple times anymore uh on each different patterns
[08:24] but using this combined pattern. Let's quickly look at the call graph to see how this change affects a real world shopperware installation. We are searching for the pasa parse method in the core graph data. And in um
[08:43] a request that was before the patch, this took 24 milliseconds. And we can look at all the methods being called here. The closure um that delegates to the visitors um is quite expensive. But we can also see multiple PM match
[08:59] different pack match calls here as well as the calls to key and current that are necessary to control how the Y loop worked in the previous
[09:14] code. Let's compare this to a trace afterwards. I've generated one uh in preparation before here
[09:29] and looking at a difference of both traces again using the parer parse traces again using the parer parse method we can see that in the improved method we can see that in the improved request it took 10 milliseconds less
[09:42] meaning from 24 10 milliseconds almost 50% improvement and if we look at the 50% improvement and if we look at the changes here. We see there's a new method call for the PR match uh with the named capture groups. This PR match
[09:59] takes 2 seconds and it replaces three P match calls that we had before match calls that we had before um for um again also almost 2 seconds individually. So we have an improvement here. Then we don't need to call key and
[10:14] current anymore. Also, we don't need to uh call the closure anymore. Instead, the new code calls directly the visitor methods except other except name parameters. And because the indirection of the closer is gone, this also
[10:30] improves the code a little bit. If you're interested in more detail on how to combine regular expressions and use named capture groups, you can watch this video here on our channel explaining the SQL paraser improvement in detail or
[10:44] watch any other video uh on our channel on topics of PHP performance improvements. Thank you very much for watching.
⚡ Saved you 0h 10m reading this? Transcribe any YouTube video for free — no signup needed.