Sometimes I sit down, and just do some code for the fun of it. Yesterday I took the time to marry my own Framework with Slim Framework 3.0beta1 – just for the fun of it. The result is quite fast.
I’m not even sure if I should make this post. Most certainly I shouldn’t make this post without this warning: almost everything you will see in this post is unstable so if you get the idea to use this in production, keep in mind that you have been warned, and that you probably will burn in hell (or have to invest some time to get this stuff stable).
Also: this is not a tutorial
The Idea is simple: load slim framework 3.0 into my own framework, to allow it to use my frameworks react based webserver.
Why? For fun, and to see how fast its gets.
ReactPHP is a framework for event-driven, non-blocking I/O with PHP, which has been around for a while. I’ve been toying with it before, and even had some form of react integration done for the pre-rewrite king23.
One of my websites (cmdr.club) runs the api for the route planner on a react based app since about 10 month, and while i had little to no trouble with it, I still feel reactphp is not exactly where it should be yet. It seems that many people in the PHP world either don’t care for non-blocking, or go the “nodejs already has that so lets use nodejs instead” way. Sadly there isn’t any non-blocking libs for many I/O tasks that one has yet.
Reacts code being less than well documented probably does its own part, but personally I hope it will gain more momentum the more it ages.
King23 is my own Microframework – i created it in 2010 for a small statistics website that I build around a game that i was playing back then. Ever since I’ve been using it for almost every web project that I’ve done in my freetime, adding and changing stuff very much to what i needed it to be on the fly.
As you might notice, you will find very little documentation on it, and its tests directory is still empty.
A few weeks ago i decided to rewrite parts of it to modularize it more, adding a dependency injection (dropping the service locator it was using before), and making it PSR-7 conform. This spawned the king23/di package, and shortly after the knight23/core package as a starting point for console scripts.
As I’m writing this post those changes (that are essential for this post) are still in the develop branch, and have not been released yet (as I’m struggeling putting a 1.0.0 on it while I don’t feel it to be fully stable yet).
king23/di is the di container I wrote a few month ago, it is probably the most stable component in this post, and its main purpose is to constructor-inject by interface.
In this post we will “abuse” it a bit, as we need it to be compatible to container-interop/container-interop to work with slim framework 3. More about this later on.
$container = new King23DIDependencyContainer();
// register a service
return new MyInstance();
// also you can register a factory (the default for services is to be singletons)
return new MyOtherInterface();
// an example class to have our interface injected to
public function __construct(MyInterface $myIntance)
// get an instance with the injections done
$instance = $container->getInstance(Example::class);
// the same method can also be used to retrieve a dependency from the container
// this will return the MyInstance instance
When I was rewriting parts of king23, I decided to change the way it handled command line quite far from how it worked in 0.9.*. Also, i changed my understanding of it from being a part of the framework (old), to being a framework for cli apps, based on king23 (new).
The result will surely change over time, as much of it is very rudimentary yet.
So whats castle23? After having used ReactPHP (see below) for a while, and even having had some previous integration in the old king23, I decided to build a small webserver within Knight23, which would use ReactPHP, and which would be able to use psr-7 conform middlewares for everything. The main goal at that time was to run king23 applications within this webserver.
Slim Framework (3.0beta1)
Slim is a microframework which has been around for a while, lots of people know it, lots of people use it.
Lately the slim guys have been rewriting huge parts of it, basically to allow incorporation of other DI containers, Psr-7 support, and modernizing it. Part of the result is that Slim Framework 3.0 can be used as a Psr-7 conform middleware – which caused me to look at the option to load Slim into my middleware queue, replacing my own router with the whole framework, and by that having an extremly easy and effordless way of loading slim framework 3.0 into castle23.
I had castle23 already build, the slim framework guys have released beta1 of 3.0 – so i figured all that was left to do was to build a configuration.
I will list a few files here, and comment on what is done in them, I won’t go much into detail though as this is pretty straight forward, and the code should be easy enough to read.
The script boostrapping and starting the actual knight23 application is almost identical to the one of plain castle23, the main difference being that there is no default route added (as the router is removed from the Middleware queue, that wouldn’t make any sense), and the package name / version fields have been set to something more sensible.
This file is load by the bin/slim-castle23 script, and basically loads all the files containing the dependencies to be injected by the king23/di.
As most of the services are happy with the default from the castle23 example, I simply load the files from there, and only load those where there are differences from this projects config/services/ folder.
This basically contains the middleware queue for castle23, which has the following entries:
- Whoops – this will catch all exceptions that are uncaught, and print them pretty
- StaticFileMiddleware – this middleware will serve static files from a public/ folder (or which other folder is configured through the \King23\Core\SettingsInterface service).
- The \Slim\App as it is registered in config/services/slim-app.php.
Once I had the first take on castle23 + slim framework running, i decided to adapt the king23/di injection so it could be used within slim framework, replacing the one used by default.
As slim has a hand full of services that it requires by default, those are configured in this file. Basically everything except the notFoundHandler is identical to slim-default setup, the notFoundHandler however had to be replaced by a simple own one, as slims default tries to use a method on the request object which is not psr-7 conform, and thus doesn’t exist in the request object thats given to the middleware.
Basically i just register the Slim App here – for its own classname, as i have no interface, and as this is just an example, i go along and declare a basic hello world route here too. then, when i added the DI container, i decided to use it at least once in the example to show that it works – hence I’m reading the package name and the version of the runner (which have been set in bin/slim-castle23).
So, when I decided to hand my own DI Container to slim, I needed to make sure that the container-interopt interface is implemented. The solution was to simply extend my container with a wrapper that would map the two required methods to something equivalent in my container.
That said: it still feels a bit messy, because while it works, it is now looking up instances by random strings, which is not exactly how it was meant to be used – once I’m over that I’ll probably move this file to its own package, so the DI can in theory be used by everything supporting container-interopt then.
Now, as you might have noticed, marrying the two frameworks was actually quite easy, but now – what does the result give us? some frankensteinish monster which loses tons of performance due to the amount of abstraction and frameworks?
To compare performances, i basically build the same app as the one that i injected here, however for the pure-slim approach i actually left out getting the package / version, as those information don’t even exist. So the pure-slim approach is basically: get name from uri, and print hello name. while for the castle23 based solution it is get name from uri, print hello name, print package and version names.
I then run apache bench with a concurrency of 100 and 1000 requests at the slim-only installation. This is over network, so there might be slight fluctuations in measurement.
In this case this is running through nginx as a webserver and hhvm as the runtime
the command being:
ab -c 100 -n 1000 http://my.dev.machine/hello/Peter
Concurrency Level: 100
Time taken for tests: 1.310 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 219000 bytes
HTML transferred: 11000 bytes
Requests per second: 763.23 [#/sec] (mean)
Time per request: 131.022 [ms] (mean)
Time per request: 1.310 [ms] (mean, across all concurrent requests)
Transfer rate: 163.23 [Kbytes/sec] received
763.23 requests – thats not bad.
so the next run was to start bin/slim-castle23 listening on port 8001
hhvm bin/slim-castle23 serve 8001 0.0.0.0
and the run apache bench against that:
ab -c 100 -n 1000 http://slim.devedge.eu:8001/hello/Peter
Concurrency Level: 100
Time taken for tests: 0.476 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 72000 bytes
HTML transferred: 53000 bytes
Requests per second: 2102.35 [#/sec] (mean)
Time per request: 47.566 [ms] (mean)
Time per request: 0.476 [ms] (mean, across all concurrent requests)
Transfer rate: 147.82 [Kbytes/sec] received
2102? now that is an awesome result right there.
so why is it that much faster?
the main speed difference should be that it has compile only once, and keeps an running application – so there is no initializing class loading, etc going one – once the request has been run once it is just a matter of executing the same stuff over and over again.
I would like to give a bit of credit to the non-blocking factor – however with there being very little I/O, there isn’t much of a difference to be made because of that.
- Running Slim Framework 3.0 apps withing castle23 is simple
- Running Slim Framework 3.0 apps within a react based webserver can actually help with its performance
- Sometimes toying around with tons of unstables just to see where it gets you can be a lot of fun.
PS: thanks for taking the time to read this, next time I might look into a more practial post again :)