My First Experience with “Modern” PHP Development
(Following all these software design “best practices” is tough. Am I missing something? Am I asking too much?)
I finally decided to dive into “modern” PHP development, with all the tools it’s acquired since I started coding in PHP. Along the way, I’ve tried to apply those best practices in software design I’ve heard about.
What follows is my thoughts on various aspects of modern PHP development. A lot of times I feel I’m not understanding something, or that I’m asking too much for the sake of best practices. I’m definitely not an expert, so I appreciate any corrections or clarifications.
Composer is a wonderful tool for managing packages. I picked it up pretty quickly. Or at least I think so.
I tried installing frameworks using Composer, because Composer can keep track of versions and update packages automatically. But I couldn’t get anything working, because Composer just installs things to the
vendor directory. No
It seems you just have to download an archive and unzip it. That sounds okay, but then how are you going to update it?
As it turns out, you can install framework libraries using Composer, but you need to write your own
index.php. In some cases this is as easy as setting your routes and starting the framework.
One framework I tried is Fat-Free Framework (also known as “F3”). I wanted to start with a small project, and this framework calls itself a “micro-framework,” so I went for it. I installed the core classes and wrote a very simple
Something that didn’t sit well with me was the fact that all the classes are in the top-level namespace. For example, the base framework class is
\Base rather than something like
\FatFree\Base. But this is a minor problem.
Aside from that, I noticed something odd about callbacks: Why would you specify a non-static method like
Foo->bar? I assume that F3 does something like
(new Foo)->bar($f3, $params) to invoke the callback. But if you can’t pass anything to the constructor to control the creation of the
Foo, there’s no point in creating a new object! You could accomplish the same thing with a function or static method.
It turns out that F3 does pass arguments to the constructor: the same arguments it passes to the callback method! Why? This is utterly pointless. Or at least unintuitive. But however you look at it, it isn’t documented anywhere.
It seems the only good way to use a non-static method is to use a callable array or an anonymous function, which works but (also) isn’t documented:
$handler = new MyHandler(/* actual parameters! */); $callback = array($handler, 'respond'); $f3->route('GET /something/@arg', $callback);
Another thing that bugs me about callbacks is the arguments that get passed to them. One is the parameters from the route pattern. Another is the framework instance, so the callback can access the framework’s features.
That set off alarm bells in my mind because it violates the dependency inversion principle. Part of that principle says:
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Here the high-level module (the callback/controller) depends on a low-level module (the framework). In practical terms, this means it is harder to unit-test the controller.
A better way to provide the functionality to the callback is to have the controller depend on a simple, mockable interface and then introduce the functionality through dependency injection. This is certainly possible in F3 with the callable-array callback I mentioned earlier.
When I think unit testing in PHP, I think PHPUnit. I used PHPUnit a bit for an earlier project, so I had a general understanding of how to write tests. But I never got around to using PHPUnit for stubs and mocks.
PHPUnit’s stub and mock features seem pretty good. But where’s the API reference? The manual has a reference for assertions and annotations, so why not this? Sure, there are plenty of examples, but examples aren’t everything. Well, there is a method list for the mock builder buried in the middle of the chapter. Hmm.