Paul M. Jones

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

Capsule DI and Argument Inheritance

I am pleased to announce that I have released Capsule version 3.4.0, now with argument inheritance. (You can see the rest of the changes here.)

As an autowiring dependency injection system for PHP 8.0+, Capsule supports injection of constructor arguments. With the 3.4.0 release, each class defined in Capsule now "inherits" the constructor arguments of its parent. This means you can specify a constructor argument on a parent class, and the child class will use it (that is, unless you set an overriding value on the child class).

Let's say you have this class hierarchy:

abstract class Strategy
{
    public function __construct(
        protected string $path
    ) {
    }

    abstract public function doSomething() : string;
}

class FooStrategy extends Strategy
{
    public function doSomething() : string
    {
        // ...
    }
}

class BarStrategy extends Strategy
{
    public function doSomething() : string
    {
        // ...
    }
}

If you define the constructor arguments for the parent ...

$def->{Strategy::CLASS}
    ->argument('path', '/path/to/resources');

... both FooStrategy and BarStrategy will automatically "inherit" the defined $path value. You won't have to re-define it for every Strategy class extension.

You can always override the "inherited" values by specifying them for the child class directly ...

$def->{FooStrategy::CLASS}
    ->argument('path', '/path/to/other-resoruces');

... in which case the children of that class will inherit the override value. In this way, constructor arguments can be propagated down the inheritance hierarchy.

If you decide that want to disable or interrupt the "inheritance" of arguments for a class, call inherit(null) on its definition:

$def->{BarStrategy::CLASS}
    ->inherit(null);

Why So Hesitant?

The same people who told you ...

  • Brexit would never happen,
  • that Trump would never win,
  • that when he did win it was because of Russian collusion but also because of racism,
  • that you must follow lockdowns while they don’t,
  • that masks don’t work,
  • that masks do work,
  • that social justice protests during pandemic lockdowns are a form of “health intervention,”
  • that ransacking African American communities in the name of fighting racism is a “mostly peaceful” form of protest,
  • that poor and underserved children locked out of shuttered schools are “still learning,”
  • that Jussie Smollett was a victim of a hate crime,
  • that men are toxic,
  • that there is an infinite number of genders,
  • that COVID couldn’t have come from a lab
  • until maybe it did,
  • that closing borders is racist
  • until maybe it isn’t,
  • that you shouldn’t take Trump’s vaccine,
  • that you must take the vaccine developed during the Trump administration,
  • that Andrew Cuomo is a great leader,
  • that Andrew Cuomo is a granny killer,
  • that the number of COVID deaths is one thing and then another

... are the same people telling you now that the vaccine is safe, that you must take it, and that if you don’t, you will be a second-class citizen.

With light editing, from https://www.tabletmag.com/sections/news/articles/vaccines-konstantin-kisin.


What Is Communism?

It means that a small group – let’s call them the nomenklatura — has absolute control over a much larger one, in every aspect of life, society, industry, and commerce. That control is enforced by:

  • a secret police,
  • a system of incentives to induce “good citizens” to betray “dissidents” to those police,
  • and a system of concentration camps for the dissidents.

Within the nomenklatura there is intense jockeying for advancement, plenty of scheming and maneuvering, and enough backstabbing to make any intrigue writer’s salivary glands go into tenth gear. For there are large differences in the degree of privilege enjoyed according to one’s altitude within the nomenklatura.

Yet all this is rationalized as being for “the people:” the subject group, who must stand in endless lines merely to buy a loaf of bread.

Lightly edited for formatting, via http://www.libertystorch.info/2021/11/22/when-systematic-analysis-is-no-longer-necessary/.


Sapien: Request/Response Objects for PHP 8.1

I am happy to announce the 1.0.0 release of Sapien. Like its predecessor ext-request, Sapien provides server API (SAPI) Request and Response objects -- but in userland, for PHP 8.1.

The Sapien classes do not model HTTP messages per se. Instead, the Request is a readonly value object composed of other readonly value objects representing the PHP superglobals and data structures derived from or composed of them. Similarly, the Response is a wrapper around and buffer for the various response-related global PHP functions.

Read more about Sapien at http://sapienphp.com.


The Sapien project has a long history, starting about 5 years ago as PECL extension in C. It had a second major version bump as part of an RFC to PHP itself, which did not pass.

In the after-action report on the failed RFC, I recorded the prevailing opinion that things like request and response objects should stay in userland. The problem with that, in the case of ext-request, was that readonly immutable objects were possible more effectively in C code than in userland PHP.

However, with the advent of PHP 8.1 and readonly properties, all the functionality of the C code is now available in userland PHP. In a way, that means Sapien is a third version of ext-request, but fully in userland.

Sapien makes extensive use of readonly, presenting a Request value object that is designed to be unchanging after instantiation. And because it's much easier to work in PHP than in C, there are many more value objects composed into the Request than in the C versions, such as:

  • Content, built up from various content-related headers and the request body itself

  • Upload, including the new full_path value from $_FILES entries.

  • Url, computed from the various $_SERVER values

  • Accept value object collections for Type, Charset, Encoding, and Language

  • XForwarded, distilled from the various x-forwarded-* headers

  • Forwarded value object collections derived from the Forwarded header

  • Authorization\Scheme value objects for basic, bearer, and digest authorization

The Response is fundamentally the same as it was in ext-request v1, though the custom file- and JSON-specific response logic has been extracted to specialized classes. Even so, the generic response is capable of handling all sorts of content, including iterables and resources.

So, if you're starting a new PHP 8.1 project, or refactoring a legacy system to PHP 8.1, try out Sapien, because request and response objects make life so much easier.


Qiq Templates for PHP 8

I'm happy to announce the first release of Qiq, a template system for developers who prefer native PHP templates -- but with a light dusting of syntax sugar when you want it. It offers partials, layouts, sections, and a wide range of HTML helpers for tags and forms, along with explicit but concise escaping.

I don't like compiled templates or specialized template languages. Smarty, Twig, etc., are all just too heavy-handed. I don't need a new language, and I don't need to "secure" my templates against designers on my team. I am generally happy with plain PHP as a template language.

However, I do find escaping tedious -- necessary, and easy enough, but tedious. A template helper to output escaped HTML, like this ...

<?= $this->h($var) ?>

... is not that bad -- but even so, it could be a little easier. Imagine this little bit of syntax sugar:

{{h $var }}

All that happens is that {{h ... }} is replaced with <?= $this->h(...) ?>.

Once that is in place, it becomes easy to support helpers, control structures, and other code, all while keeping native PHP as the fallback syntax, because the {{ ... }} tags are essentially stand-ins for PHP tags.

Qiq is PHP -- just with some syntax sugar. Try it out on your next project!


Beating Back Cancel Culture

Some high points from a successful virtual beat-down of SJWs:

Know what to expect. The cancel crowd has its own bullet-point playbook. And they’ll respond aggressively to any symbolic act that threatens their status, or erodes the impression that they are the ones calling the shots. Remember that behind the social-justice veneer lies the brutal logic of power and ego.

  • To maximize the pain you feel, they’ll tag activist groups on social media to inflate their numbers and reach.

  • They’ll bombard every organization you’re part of with demands to censure, discipline, disown, fire, or expel you—often phrasing their appeals in the passive aggressive guise of “concern” and “disappointment.”

  • At other times, they will insult, taunt, and, threaten you in a manner resembling middle-school children having a recess meltdown.

    • In my case, the ringleader called me “a full on misogynist and racist,” “shameful bigot,” “hypocrite,” “clueless,” “tone-deaf,” “snowflake,” and “soulless troll.”

    • She assailed my “privilege and patriarchy,” “lack of basic empathy and ethics,” and “zero self-awareness.”

    • She also questioned whether I’m really a human, and called on NeurIPS to ban me, and for my department to expunge me.

    • Her goal, in short, was to ruin my life.

  • The cancelers will dig up anything they can from your past. And if they can’t find any, they’ll make it up.

This will all seem terrifying, but much less so if you realize that you’re just the latest victim in what is basically a mechanical and dehumanizing process. Insofar as you don’t actually get fired from your job or suffer some other equivalent setback, these are all just words, and they don’t define who you are.

...

Don’t back down. Don’t apologize. Don’t make clarifications, and don’t try to appease the mob. All of these will only be taken as concessions, and embolden the mob to demand more. The real Achilles’s Heel of the cancel crowd is its short attention span. Once they bully someone into submission, they move on to the next victim. It’s a system designed for quick wins. If you don’t back down, they’ll raise the pitch as far as they can—but eventually they’ll be at a loss for what to do next, and all but the most fanatical will lose interest. The few that remain, now bereft of their backup, are just what you need to teach all of them a lesson, as we did in my case.

...

Turn their weapons against them. You may find this to be the most controversial principle, but it’s also arguably the most crucial—as the cancelers won’t stop until they fear that they’ll endure the same consequences that they seek to impose on others. In my case, I watched as investors and customers leaned on the ringleader’s company to rein her in. Even companies that posture heavily in the area of social justice don’t actually want to be stained by the disgraceful behavior of mob leaders. Indeed, I have no doubt that it was an ultimatum from her employer that finally led the ringleader to stop her Twitter outbursts and apologize publicly to her victims, for all to see. Some will say that once we resort to this step, we become as bad as the cancelers. But that’s a false equivalence. The cancel crowd tries to ban people because of their views. We try to stop bullying—behavior that is reprehensible regardless of ideology.

(Edited for formatting.)

See also the Social Justice Attack Survivial Guide.


Capsule 3: Clean, Concise, Composable Dependency Injection

I'm happy to announce that I have released Capsule 3.2.0, an autowiring dependency injection system for PHP 8. (Earlier 3.x releases started a couple of weeks ago, but this is the first proper announcement.)

Capsule works a little bit differently from other autowiring DI systems. The biggest difference is that configuration entries are stored as dynamic public properties on a Definitions object. That makes it very easy to obtain and modify not only object definitions, but also primitive values such as arrays. There are also quite a few lazy-resolution features built in, as well as a definition provider system.

For a comparison with other autowiring DI containers, please see capsulephp/comparison.

While Capsule does offer the ability to ...

  • create, modify, retain, and return objects and values via autowiring;
  • configure and define that creation, modification, and retention logic;
  • inject those objects and values into their dependent objects; and,
  • lazy-resolve values and objects at instantiation time;

... it does not offer:

  • Annotations. Annotations tend to couple a service to a particular container implementation; I think that kind of coupling is wise to be avoid on principle.

  • Caching and compiling. These are nominally performance enhancers, but in my experience they are rarely necessary, and in those rare cases the available speed increases are miniscule compared to other opportunities for optimization (e.g. database queries).

  • File-based configuration. Capsule configuration is defined exclusively via object-oriented PHP code, not via Neon/YAML/XML files or PHP arrays. (As a corollary, there is no special configuration notation to learn for Capsule, only class methods.)

  • In-flight container modification. This means you cannot set or reset new object or value definitions once a Capsule container is instantiated. (There are ways to subvert this restriction, in which case you will get what you deserve.)

  • Invocation injection. Also called method-call injection or action injection, I think this feature lies outside the scope of a DI/IOC system.

  • Tagging. I am ambivalent toward tagging; while I think it is little outside the scope of a DI/IOC system, I can see where others might find it useful. Perhaps a future version of Capsule may include it.

These missing features may be deal-breakers for some developers, in which case they have hundreds of autowiring and non-autowiring DI/IOC systems to choose from, including ...

... among other DI and IOC packages.


AutoRoute 2.0.0 Released, With Value Object Support

I am proud to announce that I have released AutoRoute 2.0.0, now for PHP 8. As an alternative to regex and annotation-based routers, AutoRoute eliminates the need for route definitions by automatically mapping the HTTP action class hierarchy to the HTTP method verb and URL path, reflecting on typehinted action method parameters to determine the dynamic portions of the URL. It presumes that the action class names conform to a well-defined convention, and that the action method parameters indicate the dynamic portions of the URL. This makes the implementation both flexible and relatively maintenance-free.

Of special importance, you can now use value objects as action parameters, and AutoRoute will automatically instantiate them for you. This means things like self-validating value objects, including DDD Identity value objects, are now trivial to work with in your action classes.

For example, whereas you might have done something like this previously ...

// GET /company/1
class GetCompany
{
    public function __invoke(int $id)
    {
        $companyId = new CompanyId($id);
        $company = $this->serviceLayer->fetchCompany($companyId);
        // ...
    }
}

... AutoRoute can now instantiate the CompanyId for you from the appropriate path segment:

// GET /company/1
class GetCompany
{
    public function __invoke(CompanyId $companyId)
    {
        $company = $this->serviceLayer->fetchCompany($companyId);
        // ...
    }
}

AutoRoute 2.0.0 is still faster than FastRoute, though not by as much as the 1.x series; it has dropped from about three times as fast to about two times as fast. (This is due to some internal rework to support value objects as action arguments.) Even so, remember that routing is only one small part of your HTTP user interface, and is unlikely to be a bottleneck in any case.


AutoRoute is 3x Faster Than FastRoute

When I first published AutoRoute in April 2019, I benchmarked it out of habit, to see how it compared to the fastest routing system I knew, FastRoute. The benchmark results at that time typically looked like this on PHP 7.2:

AutoRoute Runtime: 0.11443305015564
FastRoute Runtime: 1.9010598659515
FastRoute Cached:  0.12522411346436

It turned out that AutoRoute was very slightly faster than FastRoute; I was pleased, but surprised. Getting anywhere close to FastRoute was enough for me.

Lately, though, I've been working on the 2.x version of AutoRoute, and I compared 2.x to the 1.x benchmark. I was very unhappy with the results: 2.x was something like 6x slower than 1.x. While investigating the causes, I noticed that I had XDebug turned on. Turning it off brought the comparative benchmark to something much more reasonable.

It turns out that I made the same oversight when benchmarking 1.x against FastRoute, so I have corrected my error and updated the 1.x AutoRoute benchmarks. I am even more surprised now: the scenario reports that AutoRoute is about 3x faster than FastRoute:

AutoRoute Runtime: 0.027965068817139
FastRoute Runtime: 0.21157002449036
FastRoute Cached:  0.10321187973022

Now, remember: routing is only a tiny part of your overall application performance. Using AutoRoute over FastRoute will not triple the speed of your applications. Still, it is satisfying to find better performance where you can.


Why Each Programmer Thinks He Is The Best

I write code that is easy to read and uses intuitive data structures. Everyone else uses weird solutions and writes them poorly.

My innovations are clever responses to unusual circumstances. Everyone else’s are obviously weird.

Every time I have to fix someone’s code, their code is terrible. I never hear about my own code being terrible and in need of fixing. Every time I need to touch my own code again it is because it is so good my customers want to make another use of it.

If someone uses my software it is because it is good code. If they use competing software it is because of their clever marketing.

If I copy code from Stack Exchange I’m being efficient. If someone else does it they couldn’t do it themselves.

If I use an obscure language or library, I’m leveraging my unusual skillset. If someone else does it, they’re prioritizing their own convenience over maintainability.

If someone like John Carmack is just better than me at programming in every way it is because they are in a special heroic reference class that it would be unfair to compare me to but that I obviously aspire to.

Lifted entirely from https://slatestarcodex.com/2020/01/02/why-doctors-think-theyre-the-best/#comment-836896 with light editing and reformatting. (You should read the parent article as well.)

See also The Devil's Dictionary for Developers.