Advent 2023: PSR-15 – Matthew Weier O’Phinney

I’ve mentioned a few times over the course of this 2023 Advent series that the longer I’m in the tech field, the more I appreciate and favor simple solutions.
I was reminded of this yesterday when I read this article on return types in Laravel controllers by Joel Clermont.

Request

Please, please, please do not take this as an attack on Laravel or on Joel.
I have nothing but respect for Joel, and while I’m not a fan of Laravel, I’m also not a hater.
It’s never a bad thing to have a popular framework that brings folks to a language; Laravel has done that in spades for PHP.

Summarize the article, already…

In the article, Joel notes the problem with providing return types in a Laravel controller is due to the fact that it could return a view, a JSON response, an array, a redirect, or more.
If there are multiple types that could be returned, based on the request context, you would need to provide a union type.
And if you refactor or make changes to the controller later that result in new types being returned, you now need to remember to change the return type declaration.

In other words, it introduces brittleness.

So what?

I’ve worked on multiple iterations of a major MVC framework, and I ran into these same issues.
As PHP’s type system got incrementally better, the cracks in how frameworks interact with controllers became more evident.
Personally, I find the increasing number of type capabilities in PHP to be a huge boon in helping the correctness of applications, and preventing whole classes of errors.
But if the framework prevents you from using the type system, or makes adding type declarations into a situation that can now introduce errors, it puts the developer and maintainer of an application into a problematic situation.

What are the alternatives?

I worked for quite some time on PSR-7 HTTP Message Interfaces, largely so that we could have a proper HTTP message abstraction in PHP on which to build a better foundation for applications and frameworks.
From this emerged PSR-15 HTTP Server Request Handlers (which I sponsored and collaborated on, but was not primary author of).

What I love about PSR-15 is that there is no ambiguity about what you return from middleware or a handler.
You return a response.
That’s all you can return.

This means there’s no magic about different return values resulting in different behavior from the framework.
You don’t need to keep a mental map about what will happen, or do a deep dive into the framework internals to understand the ramifications of returning a view versus an array.

Instead, your handler will create a response, and provide the logic for how that is done.
If you need html, you render a template, and feed it to the response.
If you need JSON, you serialize data to JSON, and feed it to the response.
If you need a redirect, you create a response with the appropriate status code and Location header.
And so on and on.

Yes, this can lead to a little extra code at times, but:

  • You can see exactly what you intend to return to the user, and why.
  • If you try and return anything but a response, it’ll result in a TypeError.
  • You can test all of the different possible returns easily, by doing assertions on the returned response based on different requests provided to the handler or middleware.

But should you do everything in a handler?
What about things that will happen for whole sections of the site, or will be repeated in many locations, like initializing a session, or checking for an authenticated user, or validating headers, or caching?

For those things, PSR-15 provides middleware.
These are expected to be chained together, like a pipeline or a command bus, and the request is passed down through them, and a response returned on the way back up.
They’re a powerful way to provide re-usable pieces of functionality to your application.

What’s more, using middleware is often far easier to understand than how and when various events will intercept a request.
You can see the list of middleware for a given handler, and understand that they act either as filters on the incoming request (authentication, caching, etc.), or as decorators on the response (e.g. encoding or compressing the response, caching, etc.).
Since each does exactly one thing (ideally), you can test how each works, and understand how and when to compose each, and how they might work in combination.

Truncated by Planet PHP, read more at the original (another 865 bytes)

Advent 2023: Doctrine DBAL – Matthew Weier O’Phinney

I’ve mostly taken database abstraction for granted since I started at Zend.
We had a decent abstraction layer in ZF1, and improved it for ZF2.
There were a lot quirks to it — you really had to dive in and look at the various SQL abstraction classes to understand how to do more complex stuff — but it worked, and was always right there and available in the projects I worked on.

In the last couple of years, though, we came to the realization in the Laminas Project that we didn’t really have anybody with the expertise or time to maintain it.
We’ve marked it security-only twice now, and while we’ve managed to keep it updated to each new PHP version, it’s becoming harder and harder, and whenever there’s a CI issue, it’s anybody’s guess as to whether or not we’ll be able to get it resolved.

My alternatives have been straight PDO, or Doctrine DBAL, with the latter being my preference.

Doctrine what?

When most folks who use PHP hear “Doctrine”, they immediately think “ORM“; it’s how most folks use it, and what it’s best known for.

Underlying the ORM is its database abstraction layer (hence “DBAL”).
This library exposes an API that will work across any database it supports; this is essentially what zend-db, and later laminas-db, were doing as well.
What most folks don’t realize is that you can use the DBAL by itself, without the ORM.

Why no ORM?

ORMs are fine.
Really.
But they add an additional layer of complexity to understanding what you are actually doing.
Additionally, if you want to do something that doesn’t quite fit how the ORM works, you’ll need to drop down to the DBAL anyways.
So my take has always been: why not just use the DBAL from the beginning?

So, how does Matthew write code that interacts with the database?

I start by writing value objects that represent discrete aspects of the application.
Most of my work will be in consuming or creating these.
From there, I write a repository class that I use for purposes of persisting and retrieving them.
I can usually extract an interface from this, which aids in my testing, or if I decide I need a different approach to persistence later.

I push the work of mapping the data from the database to these objects, and vice versa, either in the repository, or in the value objects themselves (often via a named constructor).
Using these approaches creates lean code that can be easily tested, and for which there’s no real need to understand the underlying system; it’s all right there in what I’ve written for the application.

Some gripes about the documentation, and some tips

The Doctrine DBAL docs are a bit sparse, particularly when it comes to its SQL abstraction.
And there’s no “getting started” or “basic usage” guide.
In fact, it’s not until the third page within the docs that you get any code examples; thankfully, at that point they give you information on how to get a database connection:

use Doctrine\DBAL\DriverManager; $connectionParams = [ 'dbname' => 'mydb', 'user' => 'user', 'password' => 'secret', 'host' => 'localhost', 'driver' => 'pdo_mysql',
];
$conn = DriverManager::getConnection($connectionParams);

They also provide a number of other approaches, including using a DSN (an acronym they never explain, but based on using PDO, likely means “data source name”).

Once you have a connection, what do you do?
Well the DBAL connection allows you to prepare and execute queries, including via the use of prepared statements.
It provides a variety of methods for fetching individual or multiple rows, with a variety of options for how the data is returned (indexed arrays, associative arrays, individual columns, individual values, etc.).
These retrieval methods are mirrored in the result instances returned when executing prepared statements as well.

And that brings me to the SQL abstraction.

First, it’s really, really good.
It’s minimal, but it covers just about anything you need to do.
If you need to write something complex, you probably can; the beauty is that if you can’t, you can always fall back to a SQL query, and using the connection’s API for binding values.

But the documentation could be better.

It felt like it was written by a database admin who has forgotten more than most people ever learn about databases, and never considered that

Truncated by Planet PHP, read more at the original (another 7318 bytes)

Advent 2023: Forms – Matthew Weier O’Phinney

The first thing I was tasked with after I moved full time to the Zend Framework team (17 years ago! Yikes!) was to create a forms library.
Like all the work I did for ZF in the early days, I first created a working group, gathered requirements, and prioritized features.
There were a lot of requests:

  • Ability to normalize values
  • Ability to validate values
  • Ability to get validation error messages
  • Ability to render html forms, and have customizable markup
  • Ability to do nested values
  • Ability to handle optional values
  • Ability to report missing values

and quite a lot more.
But those are some of the things that stuck out that I can remember off the top of my head.

Zend_Form was considered a big enough new feature that we actually bumped the version from 1.0 to 1.5 to call it out.

And, honestly, in hindsight, it was a mistake.

A mistake?

Considering the timeframe when I was developing Zend_Form, it was actually a good effort, and it’s still one of those features that folks tell me sold them on the framework.
But within a year or two, I was able to see some of the drawbacks.

I first realized the issues when we started integrating the Dojo Toolkit with ZF.
We ended up having to create first a suite of Dojo-specific form elements, and second a whole bnch of Dojo-specific decorators, which were what we used to render form elements.
While the library gave us this flexibility, I saw a few issues:

  • Duplication.
    We had multiple versions of the same form elements, and it was actually possible to get the wrong version for your form context.
    And with duplication comes increased maintenance: any time we fixed an issue in one element, we had to check to see if the same issue existed with the Dojo versions, and fix them there as well.
  • Javascript.
    One of the reasons for integrating Dojo was to allow doing fun things like client-side validation; this allowed giving early feedback, without a round-trip to the server.
    But this also meant that we had validation logic duplicated between the server-side and client-side logic.
    And more interestingly: the form might be sent as a request by javascript, instead of a standard form request, which meant that we needed to validate it only, and then serialize validation status and messages.
    Basically, all the rendering aspects of the form were irrelevant in this scenario.
    Which brings me to…
  • APIs.
    Around this time, APIs started trending.
    It would be a few years before REST became popular and commonly understood by developers, but folks were starting to see that we’d be needing them for the nascent mobile application markets, and that they were going to be a useful way to conduct business-to-business transactions.
    Once you start having APIs in the mix, a library centered on web forms becomes less interesting.

By the time we started planning for version 2 of ZF, we realized we’d need to reconsider how we did forms.
The first step we took was splitting the validation aspect from the form aspect, and created Zend\InputFilter to address the first, and Zend\Form to address the second.
Input filters encapsulated how to filter, normalize, and validate incoming data.
Forms composed an input filter, and then provided hints for the view layer to allow rendering the elements.
This separation helped a fair bit: you could re-use input filters for handling API or JS requests easily, while the form layer helped with rendering html forms.

But I still feel we didn’t get it right:

  • Our validation component and our input filter component were each stateful.
    When you performed validation, each would store the values, validation status, and validation messages as part of the state.
    This makes re-use within the same request more difficult (it was not uncommon to use the same validator with multiple elements, and this now required multiple instances), makes testing more difficult, and makes it harder to understand if the instance represents the definition, or the results of validation.
  • The longer I’ve worked in web development, the more I’ve realized that while the html generation aspects of these form libraries are useful for prototyping, they inevitably cannot be used for the final production code.
    Designers, user experience experts, and accessibility developers will each want different features represented, and these will never fall into the defaults the framework provides.
    Even if the framework provides customization features, the end result is more programming effort.
    It’s almost always better to code the html markup in your templates, and then feed state (e.g., element IDs

Truncated by Planet PHP, read more at the original (another 7939 bytes)

Cutting through the static – Larry Garfield

Cutting through the static

Static methods and properties have a storied and controversial history in PHP. Some love them, some hate them, some love having something to fight about (naturally).

In practice, I find them useful in very narrow situations. They’re not common, but they do exist. Today, I want to go over some guidelines on when PHP developers should, and shouldn’t, use statics.

In full transparency, I will say that the views expressed here are not universal within the PHP community. They do, however, represent what I believe to be the substantial majority opinion, especially among those who are well-versed in automated testing.

Continue reading this post on PeakD.

Larry
29 November 2023 – 4:28pm

Automating the backslash prefixing for native PHP function calls – Raphael Stolt

After reading the blog post Why does a backslash prefix improve PHP function call performance by Jeroen Deviaene I was looking for a way to automate it for the codebase of the Lean Package Validator, to shave off some miliseconds for it’s CLI. The PHP Coding Standards Fixer has a rule named native_function_invocation which does the very exact task.

Configuring the PHP Coding Standards Fixer

.php-cs-fixer.php
<?php use PhpCsFixer\Config;
use PhpCsFixer\Finder; $finder = Finder::create() ->in([__DIR__, __DIR__ . DIRECTORY_SEPARATOR . 'tests']); $rules = [ 'psr_autoloading' => false, '@PSR2' => true, 'phpdoc_order' => true, 'ordered_imports' => true, 'native_function_invocation' => [ 'include' => ['@internal'], 'exclude' => ['file_put_contents'] ]
]; $cacheDir = \getenv('HOME') ? \getenv('HOME') : __DIR__; $config = new Config(); return $config->setRules($rules) ->setFinder($finder) ->setCacheFile($cacheDir . '/.php-cs-fixer.cache');

To make this rule executeable I needed to add the –allow-risky=yes option to the PHP Coding Standards Fixer calls in the two dedicated Composer scripts shown next.

composer.json
"scripts": { "lpv:test": "phpunit", "lpv:test-with-coverage": "export XDEBUG_MODE=coverage && phpunit --coverage-html coverage-reports", "lpv:cs-fix": "php-cs-fixer --allow-risky=yes fix . -vv || true", "lpv:cs-lint": "php-cs-fixer fix --diff --stop-on-violation --verbose --dry-run --allow-risky=yes", "lpv:configure-commit-template": "git config --add commit.template .gitmessage", "lpv:application-version-guard": "php bin/application-version --verify-tag-match=bin", "lpv:application-phar-version-guard": "php bin/application-version --verify-tag-match=phar", "lpv:static-analyse": "phpstan analyse --configuration phpstan.neon.dist", "lpv:validate-gitattributes": "bin/lean-package-validator validate"
},

After running the lpv:cs-fix Composer script the first time the test of the system under test started failing due to file_put_contents being prefixed with a backslash, so I had to exclude it as shown in the PHP Coding Standards Fixer configuration above.