Strauss-RS

A common issue for Wordpress developers is dependencies.

Dependencies allow you to import functionality from other developers quickly and easily. The problems start when everybody wants to do the exact same thing.

Conflicts

Say Plugin A wants to import the HTTP Client Guzzle, they might add "guzzlehttp/guzzle": "^6.0" to their Composer dependencies and leave it there. It all works perfectly for them.

Then, Plugin B comes along. They’re a brand new plugin and then also decide to include Guzzle HTTP in their plugin. So they add `”guzzlehttp/guzzle”: “^8.0″` to their dependencies.

Now you have two plugin each expecting different versions of the Guzzle dependency. The problem is that Composer will only be able to register one of them.

If Plugin A runs first, then any use of Guzzle will resolve to version 6. If Plugin B runs first, then version 8 of Guzzle is registered.

Both outcomes hold equal potential catastrophe to both plugins.

The Solution

The solution is both simple, and extraordinarily complex: we prefix the classes.

Instead of using GuzzleHttp\Client, we use Acme\PluginA\Vendor\GuzzleHttp\Client and VeridianDynamics\PluginB\Vendor\GuzzleHttp\Client.

The complex part is that now every other dependency in your project has to use prefixed namespace, and there are hundreds of different ways in which that could happen.

That’s where the previous solutions came in:

  • PHP Scoper
  • Mozart
  • Strauss

All had slightly different approaches to the task, but all attempted the same solution of vendor prefixing.

The Problem with the Solution

Honestly, we’ve spent countless hours working with the various solutions. Strauss was the only one that worked 100% in a world where 0.1% failure can crash an entire site.

So why didn’t we call it a day there? Speed.

You see all of these solutions use PHP to parse the files, they use PHP to analyse the files and they use PHP to re-write the files.

And PHP is not fast at this kind of task.

In fact running Strauss on our plugin could take several minutes, and in a CI/CD pipeline, time is literally money.

Let’s Get Rusty

So the method of Strauss was perfect. We just needed a way to speed it up: enter Rust.

Rust has quickly garnered a reputation for “blazing” fast operation in various environments, excelling at file input/output especially. All of the linters and formatters are moving to Rust. Vite moved to Rust. Even Typescript is moving to Rust. Even Laravel is rumoured to be experimenting with the language.

So a few months back, we decided to port Strauss to Rust as a virtual drop-in replacement.

After countless tests and attempts, we got there and waited with bated breath to see how it worked on our fairly chunky plugin. Would we shave off only a few seconds? Would we halve the processing time?

Nope.

The entire prefixing finished in less than 2 seconds, down from about 2-3 minutes.

Benchmarks

To test this, we ran the small and medium projects 7 times, and the large and x-large projects 3 times, taking the median of each project.

PHP ran using v8.4.26 with no OPCache and no memory limit on a 12-thread system.

ProjectPackagesPHP filesStrauss PHP 0.31.1Strauss RSStrauss RS (1 thread)Speedup
Small (monolog)21420.82 s0.012 s0.025 s~52×
Medium (guzzle, flysystem, finder)122661.52 s0.018 s0.054 s~74×
Large (laravel/framework)735,36919.3 s0.18 s0.73 s~107×
XL (aws/aws-sdk-php)153,76947.0 s0.36 s1.40 s~129×

We were truly amazed.

What’s Next?

Strauss-RS is still in early development, so we’ll keep on bug-hunting. Feel free to join us on our linked GitHub repository.

Full installation and usage instructions are in the README file.

Part of evoMark's contribution to Free and Open Source Software (FOSS)

To read more about evoMark and our open-source software, head to our Open-Source Software page

Get in Touch

Get in touch with us today 01202 790300

monday 09:00 - 17:00
tuesday 09:00 - 17:00
wednesday 09:00 - 17:00
thursday 09:00 - 17:00
friday 09:00 - 16:00
saturday Closed
sunday Closed