Paul M. Jones

Don't listen to the crowd, they say "jump."

PHP-Styler 0.5.0 Released

PHP-Styler is a companion to PHP-Parser for reconstructing PHP code after it has been deconstructed into an abstract syntax tree. As a pretty-printer for AST nodes, PHP-Styler will completely reformat your PHP code, discarding any previous formatting entirely. It will also split lines at idiomatically appropriate points for the given maximum line length, a feature I'm especially happy with.

Improvements and Additions

This release sees dramatic speed improvements (about 600% !) from the last-publicized 0.1.0 release, mostly from removing the php -l linting step in favor of using the error-checking from PHP-Parser. In addition, the code reassembly logic has been completely rewritten to use a Line object that splits into sub-Line objects, and applies splits to each Line independently. This results in a much more robust line-splitting process.

I have also added a bare-bones document on how to customize Styler output -- including notes on how to customize spacing around operators, and on opening-brace placement. If you have a coding style guide that you like, I invite you to try writing a customized Styler for it; I can advertise it on the PHP-Styler site.

And although you can use the preview command after installing PHP-Styler to safely preview any reformatting, I have put up a temporary online demonstration site -- type or paste in any PHP code to watch it get reformatted live. (As a side note, this gave me an excuse to try HTMX which really is quite easy to use.)

Line-Splitting Example

In testing the line-splitting logic, I applied PHP-Styler to several public codebases, including the HttpFoundation package from Symfony. Here is some original Symfony code before PHP-Styler gets hold of it:

class ExpressionRequestMatcher extends RequestMatcher
{
    // ...

    public function matches(): bool
    {
        // ...

        return $this->language->evaluate($this->expression, [
            'request' => $request,
            'method' => $request->getMethod(),
            'path' => rawurldecode($request->getPathInfo()),
            'host' => $request->getHost(),
            'ip' => $request->getClientIp(),
            'attributes' => $request->attributes->all(),
        ]) && parent::matches($request);
    }
}

Now to apply PHP-Styler. (The following is a simplified explanation of the line-splitting logic, looking just at the matches() return expression.)

In the first evolution of line-splitting, PHP-Styler begins by placing the entire statement on a single line with no splits at all.

        return $this->language->evaluate($this->expression, ['request' => $request, 'method' => $request->getMethod(), 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), 'attributes' => $request->attributes->all()]) && parent::matches($request);

Clearly this line is too long. PHP-Styler recognizes this and applies a second evolution to split lines at booleans:

        return $this->language->evaluate($this->expression, ['request' => $request, 'method' => $request->getMethod(), 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), 'attributes' => $request->attributes->all()])
            && parent::matches($request);

The boolean line is now within the limit, but the fluent method call above it is not. The third evolution applies an idiomatic member-operator split:

        return $this->language
            ->evaluate($this->expression, ['request' => $request, 'method' => $request->getMethod(), 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), 'attributes' => $request->attributes->all()])
            && parent::matches($request);

The $language property stays with $this, but the evaluate() method call gets split -- even then, the line remains too long. The fourth evolution applies an argument split on that line:

        return $this->language
            ->evaluate(
                $this->expression,
                ['request' => $request, 'method' => $request->getMethod(), 'path' => rawurldecode($request->getPathInfo()), 'host' => $request->getHost(), 'ip' => $request->getClientIp(), 'attributes' => $request->attributes->all()]
            )
            && parent::matches($request);

Now the arguments are split out to their own lines and indented; the only remaining over-long line is the second argument. The fifth evolution applies an array split:

        return $this->language
            ->evaluate(
                $this->expression,
                [
                    'request' => $request,
                    'method' => $request->getMethod(),
                    'path' => rawurldecode($request->getPathInfo()),
                    'host' => $request->getHost(),
                    'ip' => $request->getClientIp(),
                    'attributes' => $request->attributes->all()
                ],
            )
            && parent::matches($request);

Now that the array argument has been split and indented, all of the lines are within the limit, and PHP-Styler moves on to the next chunk of code.

As we can see, the end result is similar but not identical to the original Symfony code. Still, the PHP-Styler presentation is arguably a reasonable alternative to the original.

What's important here is that no matter the style of the original code, the restyled code will always end up the same way. The original code could be an absolute mess, but PHP-Styler will present it coherently regardless. All style decisions will look like they have been made by a single mind, granting at least one aspect of consistency and coherence to any codebase.

III.

There's still more to be done with PHP-Styler, such as adding annotations to ignore a file for styling, or to force array expansion across lines. Inline comments present some problems, as does variable interpolation into double-quoted strings with newlines. Unfortunately, those are because of how the upstream PHP-Parser works, so I'll have to report those and hope for a solution there.

Even so, PHP-Styler is good enough for my own projects now, and I'm happy to have spent the time working it up.


Teller: Money for Legacy Applications in PHP

I am happy to say that MoneyPHP 4.2.0 incorporates my Teller contribution for handling monetary math in legacy codebases.

Legacy codebases often use float math for monetary calculations, which leads to problems with fractions-of-pennies in monetary amounts. The proper solution is to introduce a Money object, and use Money objects in place of float math. However, doing so can be quite a hardship, especially when the float values need to be moved to and from database storage. Intercepting and converting the float values (often represented by strings) into Money objects on their way out of the database, then reconverting them to their original format on their way back to the database, can be very difficult and time-consuming.

To help ease the transition from float math to Money objects, use a Teller instance to replace float math for monetary calculations in place. Without Teller, your math might look like this ...

$price = 234.56;
$discount = 0.05;
$discountAmount = $price * $discount;
// => 11.728

... and you end up with an extraneous $0.008. Whereas with Teller, you can replace the money math in place, without needing to create Money objects:

// create a Teller, perhaps in a DI container ...
$teller = \Money\Teller::USD();

// ... then use it for monetary math:
$price = 234.56;
$discount = 0.05;
$discountAmount = $teller->multiply($price, $discount);
// => '11.73'

The main drawback is that you cannot use two different currencies with the Teller; you can use only one.

Teller supports the full range of Money operations. You can read the documentation for it here.


There Is Only One Science

"All science is either physics or stamp-collecting."

-- Ernest Rutherford

I recall hearing it first another way, I think via Heinlein: "There is only one science. It is called 'physics.' Everything else is stamp-collecting." I like that phrasing better, because it highlights the disparity more plainly.

Anything that calls itself "science" must be based on observation, but only physics provides a predictive capacity from those observations. The other "sciences" consist only of collections of observations; those observations can be categorized, organized, and reorganized as new observations are collected, but they do not provide a predictive capacity. They do not tell you what your next observation will be with much certainty. If you find a strongly predictive capacity, chances are that there is a physics component underlying the observations.

Thus, the further you get from "physics", the further you get from "science."


Introducing PHP-Styler

PHP-Styler is a companion to PHP-Parser for reconstructing PHP code after it has been deconstructed into an abstract syntax tree. As an alternative to the pretty-printer bundled with PHP-Parser, it supports:

  • configurable line-length, line-ending, and indent-string
  • automatic indenting and line breaks
  • automatic splitting of "too-long" lines
  • programmable/customizable styling

PHP-Styler is not a coding style "fixer" per se. Instead, it will completely rebuild PHP code from the AST, applying its style rules along the way. The result is a consistent formatting style, regardless of the original formatting.

Whereas something like PHP-CS-Fixer has scores of configuration parameters, PHP-Styler gives you imperative control over its output. Extend the default Styler implementation with your own overrides to change where braces go, to place or remove the space after ! conditions, whether or not if/elseif/else should be on separate lines, and so on.

I'm especially happy with the line-splitting logic. PHP-Styler begins by building one statement per line, but if the line is too long, it follows an incremental set of rules to split that line into shorter readable parts.

There are still some outstanding formatting issues around closures and comments, but I have smoke-tested PHP-Styler against several codebases besides my own, and I found the results surprisingly acceptable (not perfect, but unexpectedly good). It's certainly good enough for my own projects, though it did reveal some of my own preferences that I was not aware of, as well as some inconsistencies in my own style choices.

If you are using PHP-Parser and want a better pretty-printer, or if you have been looking for an alternative to PHP-CS-Fixer, give PHP-Styler a try!


Qiq Tags and PHP Tokens

I released Qiq 2.1.0 last week, with a new feature made possible only by using PHP tokens during the Qiq compiling process.

I.

Qiq is a template system for PHP. It uses native <?php ... ?> for the template syntax, but it allows for using a Qiq {{ ... }} syntax as well. Qiq syntax is the same as PHP, with a light dusting of syntax sugar added to make it more concise. That means a helper call in Qiq syntax ...

{{= anchor('http://example.com') }}

... gets $this-> added to it when Qiq compiles it to PHP ...

<?= $this->anchor('http://example.com') ?>

... making helper usage more concise in Qiq. It also means you can mix PHP in with Qiq as much or as little as you like, because Qiq code is pretty much just PHP code.

II.

One thing which always irked me a bit was that if you use a helper under Qiq syntax, but not as the opening keyword, you would still need to use $this-> inside the Qiq tags. For example, the following variable assignment would generate an error ...

{{ $a = anchor('http://example.net') }}

... because the compiler looked only at the opening keyword, and did not know to add $this-> to the anchor() call. Instead, if you were past the first keyword, you would have to add $this-> yourself:

{{ $a = $this->anchor('http://example.net') }}

That adds a bit of cognitive friction, but it didn't bother me too much, until someone else brought it up.

III.

Having a real bug report/feature request motivated me to address the issue. The problem was, how could the Qiq compiler examine the remaining code inside the Qiq tags, look for words that qualify as function calls, and add $this-> to them?

My first thought was to bring in nikic/php-parser and use it as part of a secondary compiling process. The idea was to convert the Qiq {{ ... }} syntax to PHP first, then run that PHP code through php-parser, find all plain function calls, and add $this-> to them.

That turned out to be more difficult than I thought it would be; walking the AST properly involved more effort than I would have liked for this endeavor. In addition, the php-parser pretty-printer for generating the converted output did not look suited to this particular task. I want very much for the Qiq code and the resulting PHP code to match up very closely, and the pretty-printer cannot guarantee that at this point.

IV.

As a result, I decided to fall back to the plain old PhpToken functionality (it's an improved version of token_get_all()). This turned out to be much more applicable to the problem at hand. The Qiq compiler process doesn't need a full AST as provided by php-parser, it just needs identify function calls.

Using an extended php token class, the Qiq compiler can tokenize the Qiq-enclosed PHP code, then examine each token to see if it looks like a function call. It does this by ...

  • checking that the current token is a non-keyword non-encapsed string;
  • looking ahead to see if the next significant token is an opening parenthesis;
  • and looking back to make sure the previous significant token is not ->, ?->, ::, or the function keyword.

Under those conditions, the current token is presumed to be a function call, and the compiler adds $this-> to it. (It's possible that these conditions might need some improvement, but testing on existing templates did not reveal any trouble.)

This approach took some trial-and-error, but the resulting solution is only 70 lines of code, and well-targeted to the problem at hand.

V.

As a result, you can now use a helper anywhere inside Qiq {{ ... }} tags, without having to add $this-> yourself. That means the Qiq code from the opening example now assigns the variable without error ...

{{ $a = anchor('http://example.net') }}

... which eliminates the last little bit of cognitive friction I have felt with Qiq.

Many thanks to apple-x-co for submitting the report that motivated this improvement!

Postscript

Try Qiq for your template system, because you like PHP syntax, but you also want:

  • Concise, explicit, context-specific escaping
  • Views, layouts, and partials
  • Blocks and inheritance
  • Rich and extensible HTML helpers
  • Easy-to-implement static analysis
  • Full documentation and unit-testing

AutoShell 1.0 Released

I am proud to announce the first release of AutoShell, a library for building CLI commands in PHP 8.1 and up.

Just as with AutoRoute for web routing, AutoShell automatically maps CLI command names to PHP command classes in a specified namespace. For example, given a namespace of Project\Cli\Command, the CLI command name create-article automatically maps to the Project\Cli\Command\CreateArticle command class.

AutoShell is low-maintenance. Merely adding a class to your source code, in the recognized namespace and with the recognized main method name, automatically makes it available as a command.

AutoShell will automatically map command-line arguments to the command class main method parameters. For example, given this command class ...

<?php
namespace Project\Cli\Command;

use AutoShell\Help;

#[Help("Says hello to a _name_ of your choice.")]
class Hello
{
    public function __invoke(

        #[Help("The _name_ to say hello to.")]
        string $name

    ) : int
    {
        echo "Hello {$name}" . PHP_EOL;
        return 0;
    }
}

... AutoShell will recognize php bin/console.php hello world and output Hello world.

And as you can see, help is defined via attributes. Options are easy to define, too -- as is combining common option sets with command-specific ones.

There's a lot more to AutoShell; take a look at the documentation to get started!


You can read the Reddit discussion about this post here.


Qiq Templates 2.0 Released

I am happy to annouce the 2.0 release of Qiq templates for PHP 8.

Qiq is for developers who prefer native PHP templates, but with less verbosity. It offers:

This release incorporates several substantial changes; you can see the upgrade notes here.

The most important of these changes is that Qiq templates are now easily subject to static analysis. Add a docblock typehint for $this in your template files, recompile any templates using Qiq syntax, and run your static analyzer against your templates. Yes, it's that easy.

The way helpers are implemented has also changed. Previously, you would have to register your helper class with a helper locator, but that made static analysis more difficult. As of this release, helpers are now just methods on a Helpers class, which makes static analysis simple and straightforward. To add a helper, extend the Helpers class and write any method you like. In addition, all helper objects are retained in a PSR-11 container object; use the lightweight Container provided by Qiq, or bring your own (maybe Capsule).

Finally, a Twig-like implementation of "blocks" and "inheritance" has been added to this release, and backported to the latest 1.x release.

If you're a Twig user looking to branch out, or if you like native PHP templates but want something a little more concise, try Qiq!



PDS "composer-script-names" standard now in public review

The PHP Package Development Standards (PDS) initiative researches the PHP package ecosystem to extract common practices existing in real packages. It rationalizes and refines those practices, then publishes them as PDS packages for reference by PHP package authors.


After several weeks of effort, and two weeks of private review, the pds/composer-script-names publication is now open for public review.

This publication defines a standard for Composer script names regarding, as well as a naming standard for those scripts, revealed by examining the entire Packagist ecosystem.

Please leave your review comments as issues on the research repository so we can maintain a good historical record.

When participating in public review, please:

  1. Review the research methodology and the results to make sure I have not missed anything important, that the assessments make sense, and that there are no other stronger interpretations.

  2. Review the final conclusions and recommendations to make sure they follow reasonably from the research.

  3. Note for typos, grammatical errors, and other drafting issues.

Thanks!


Contra Noback on Application Services

Matthias Noback's Advanced Web Application Architecture (AWAA from here on) is excellent throughout. You should buy it and heed its advice. It is a wonderful companion or followup to my own Modernizing Legacy Applications in PHP -- which is still free, though of course I'll happily take your money.

Having read through the book, I paid special attention to the sections on Application Services. The pattern is a good one, and you should use it, but I find I must offer an alternative approach to the AWAA treatment of the pattern.

tl;dr:

  • Each use case should have its own Application Service
  • Treat each Application Service as a Facade, not a Command
  • Each Application Service should return a Domain Payload
  • Only the Presentation should call the Application Service

One Use Case Per Application Service

AWAA suggests that an Application Service may cover either single or multiple use cases.

An application service could have a single method (like the __invoke() method). In that case the service represents a single use case as well. But since several uses cases may share the same set of dependencies you may also create a single class with multiple methods. (pp 255-256)

While it is true that some form of efficiency is gained when use cases "share the same set of dependencies," I think that the gains are illusory.

Instead, I say an Application Service should cover one-and-only-one use case:

  • One Application Service per use case makes the available use cases more visible when browsing through the directory structure. It becomes trivial to see what is supported by the system, and what may not be.

  • Doing so sets up a standard practice within the system, which in turn helps to avoid questions like "Should I put this use case in an existing Application Service, or should I create a separate one?" With this practice in place, there's no need to think about it: always create a separate one.

  • It reduces merge/rebase conflicts when multiple developers are working in the Application Service code. For example, if three developers are working on three separate use cases inside a single Application Service, the chances for conflict are increased. If the use cases are each in separate Application Services, the chances for conflict are reduced.

  • While most use cases may start out simple, some will become more complex, and it is difficult to predict which ones. As a use case becomes more complex, you already have a separate "space" to work in if it is in separate Application Service. The need for extracting and creating a new Application Service is pre-empted when each use case already has its own Application Service.

  • Finally, a single use case Application Service maps very nicely to a single ADR Action, or to a single-action MVC controller. It makes dependency injection much more specific and targeted.

Application Service as Command?

AWAA says Application Service methods are Command methods:

Application service methods are command methods: they change entity state and shouldn’t return anything. (p 257)

But then it goes on to say ...

However, when an application service creates a new entity, you may still return the ID of the new entity. (p 257)

That way, the code that called the Application Service can "use the ID that was returned by the application service to fetch a view model from its view model repository." (p 257)

Here is an example of what the book means; these examples are mine, and not from AWAA. First, the Presentation code, e.g. from a controller action method:

$orderId = $this->createOrderService->__invoke(/* ... */);
$order = $this->orderViewRepository->getById($orderId);

/* ... present the $order in a Response ... */

And the relevant Application Service code:

$order = new Order(/* ... */);
$order = $this->orderWriteRepository->save($order);
return $order->id;

However, I disagree that an Application Service ought to be treated in general as a Command.

While some Application Services might be Commands, the fact that sometimes the Application Service should return something indicates that they do not as a whole follow the Command pattern. It is true that a Command may "publish events" through an event system, but a Command is never supposed to "return" anything.

Application Service as Facade

Instead, I suggest treating the Application Service as a Facade.

That is, an Application Service should be seen as a Facade over the underlying domain operations. A Facade is allowed to return a result. In the case of an Application Service, it should return a result suitable for Presentation.

Modifying the above example, the code becomes something more like the following. First, the Presentation code:

$order = $this->createOrderService->__invoke(/* ... */);

/* present the $order in a Response */

And the relevant Application Service code:

$order = new Order(/* ... */);
$this->orderWriteRepository->save($order);
return $this->orderViewRepository->getById($order->id);

The details may be up for discussion, but the larger point stands: treating the Application Service as a Facade, not a Command, creates a more consistent and predictable idiom, one that actually adheres to the stated pattern.

Payload

Once we treat the Application Service as a Facade instead of a Command, we might ask what it should return. In the above simple cases, the Application Service returns a View Model (per AWAA). However, that presumes the operation was a success. What if the operation fails, whether due to invalid input, raised errors, or uncaught exceptions, or some other kind of failure?

My opinion is that the Presentation code should not have to handle any errors raised or exceptions thrown from an Application Service. If the Presentation code has to catch an exception that has come up through the Application Service, and then figure out what those errors mean, the Presentation code is doing too much -- or at least doing work outside its proper scope.

As a solution for that constraint, I have found that wrapping the Application Service result in a Domain Payload is very effective at handling both success and failure conditions. This allows a consistent return type from each Application Service in the system, and allows the Presentation code to standardize how it presents both success and failure.

For example, the above Presentation code can be modified like so (note the use of an ADR Responder to handle the Response-building work):

$payload = $this->createOrderService->__invoke(/* ... */);
return $this->responder->__invoke($payload);

Likewise, the relevant Application Service code:

try {
    $order = new Order(/* ... */);
    $this->orderWriteRepository->save($order);
    $result = $this->orderViewRepository->getById($order->id);
    return new Payload(Status::CREATED, order: $result);
} catch (Throwable $e) {
    return new Payload(Status::ERROR, error: $e);
}

The inclusion of a Domain Status constant lets the Presentation code know exactly what happened in the Application Service. This means the Presentation code does not have to figure out what the results mean; the Payload tells the Presentation code what the results mean via the Status.

Events and Delegation

Regarding event subscribers and eventual consistency, AWAA advises:

Instead of making the change inside the event subscriber, delegate the call to an application service. (p 270)

If the Application Service actually is a Command, as AWAA suggests, this may be fine.

However, when using a Payload as the return from each Application Service, no Application or Domain activity should delegate to any other Application Service. The Payload is always-and-only to be consumed by the Presentation code, never by any other kind code.

I suggest that if you find you need to use the logic from one Application Service anywhere else, and you are using Payload returns, you should extract that logic from the Application Service and put it into the Domain where it belongs. Then event subscribers can call the relevant Domain element, instead of an Application Service.

Conclusion

These alternative offerings should in no way discourage you from buying and reading Advanced Web Application Architecture by Matthias Noback. It is a very good piece of work with a lot to offer. My suggestions here are more an alternative or refinement than a refutation or rejection. As a developer, you should consider Noback's advice as well as the advice presented here, and determine for yourself which is more appropriate to your circumstances.