Tinker-like REPL for Mezzio – Matthew Weier O’Phinney

Today in the Laminas Slack, somebody asked if there was an equivalent to Laravel’s Tinker REPL.
The short answer is “no”, but I had a suggestion for them.

PHP REPL

The first part of my answer to the question was suggesting they use the PHP REPL.

PHP has had a REPL since version 5.1.0, which you can invoke using php -a.
Once you’ve started, you can do anything you’d normally do in PHP, including such things as including files, declaring namespaces, declaring classes, etc.

I’ve often wanted to test how classes work, or run one-off jobs without writing a script.
To accomplish these tasks, I’ll include the autoloader installed by Composer:

$ php -a
php > include './vendor/autoload.php';

Once I’ve done that, I can reference any classes, functions, and constants autoloaded by Composer, as well as anything PHP exposes.

Mezzio container

The Mezzio skeleton sets up a few files in its config/ subdirectory that give us a number of ready-to use artifacts.

The first is config/config.php, which aggregates and returns all application configuration, including from config providers as well as local autoloaded configuration files (those in config/autload/).
From the REPL, you can dump this information directly if you want:

$ php -a
php > include './vendor/autoload.php'; // we need autoloading for most config providers
php > $config = include './config/config.php';
php > var_export($config);

The second, and more important to this exercise, is config/container.php.
This file autoloads, configures, and returns the PSR-11 DI container defined in the application.
By using this approach, we can keep Mezzio agnostic of the specific container used, and leave it to that file to properly instantiate and configure it for us.
We provide out-of-the-box versions of that file for a number of containers, and have a specification for dependency configuration that we recommend to ensure that dependency configuration provided by third-party packages can be detected and utilized.

Because this is just a PHP file, and returns the PSR-11 container, we can include that and capture it to a variable in the REPL:

$ php -a
php > include './vendor/autoload.php';
php > $container = include './config/container.php';

From there, you can then pull any configured services, including the configuration, and start interacting with them:

php > include './vendor/autoload.php';
php > $container = include './config/container.php';
php > $config = $container->get('config');
php > echo $config['debug'] ? 'In debug mode' : 'In production mode';
php > $httpClient = $container->get(Http\Adapter\Guzzle7\Client::class);

Fin

While not quite as powerful as Tinker, the PHP REPL, coupled with Composer autoloading and a configured PSR-11 container, is a fantastic tool for interacting with your project.
I can definitely recommend this as a way to play with and experiment with your application code!

mwop Tinker-like REPL for Mezzio was originally published on https://mwop.net by .

PHP releases on hold – PHP: Hypertext Preprocessor

As announced on the php.internals mailing list, a pair of malicious commits were made in the PHP source code repository over the weekend. These commits were immediately noticed and reverted, and thus never reached end users. The investigation into the root cause and exact scope of the compromise is still ongoing, therefore releases will be put on hold for two weeks assuming no further issues are discovered. Thank you for bearing with us while we endeavor to ensure that PHP is a stable and reliable platform for web development.

PHP Internals News: Episode 79: New in Initialisers – Derick Rethans

PHP Internals News: Episode 79: New in Initialisers

In this episode of “PHP Internals News” I chat with Nikita Popov (Twitter, GitHub, Website) about the “New in Initialisers” RFC.

The RSS feed for this podcast is https://derickrethans.nl/feed-phpinternalsnews.xml, you can download this episode’s MP3 file, and it’s available on Spotify and iTunes. There is a dedicated website: https://phpinternals.news

Transcript

Derick Rethans 0:14

Hi, I’m Derick. Welcome to PHP internals news, a podcast dedicated to explain the latest developments in the PHP language. As you might have noticed, the podcasts are currently not coming out once every week, as there are not enough RFCs being submitted for weekly episodes. I suspect that this will change soon again. This is episode 79. In this episode, I speak with Nikita Popov, about a few more language additions that he’s proposing. Nikita, how are you doing today?

Nikita Popov 0:43

I’m doing well, Derick, how are you doing?

Derick Rethans 0:45

I’m pretty good as though, I always much happier when it’s sunny outside.

Nikita Popov 0:48

Yeah, for us to weather also turned today. Yesterday it was still cold.

Derick Rethans 0:53

We’re here to talk about a few RFCs. The first one, titled “New in Initializers”. What is this RFC about?

Nikita Popov 1:00

The context is that PHP has basically two types of expressions: ones, the ones used on normal code, and the other one in a couple of special places. These special places include parameter default values, property default values, constants, static variable defaults, and finally attribute arguments. These don’t accept arbitrary expressions but only a certain subset. So we call those usually constant expressions, even though they are not maybe constant in the most technical sense. The differences really that these are evaluated separately so they don’t run on the normal PHP virtual machine. There is a separate evaluator that basically evaluates an abstract syntax tree directly. They are just like, have different technical underpinnings.

Derick Rethans 1:49

Because it is possible to for example, define a default value to seven plus 12?

Nikita Popov 1:54

Exactly. It’s possible to define it to seven plus 12, but maybe not to seven plus variable A, or seven plus some function call or something like that.

Derick Rethans 2:03

I guess the RFC is about changing this so that you can do things like this. What are you proposing to add?

Nikita Popov 2:09

Yes, exactly. So my addition is a very small one, actually. I’m only allowing a single new thing and that’s using new, so you can use new, whatever, as a parameter default, property default, and so on.

Derick Rethans 2:23

In this new call, pretty much a constructor call for a class of course, can arguments to be dynamic, or do they need to be constant as well?

Nikita Popov 2:33

Rules are always recursive, so you can like pass arguments to your constructor, but they also have to follow the usual rules. So again, they also have to be constant expressions, or after this RFC, they can also include new, but they cannot include variable references and so on.

Derick Rethans 2:50

Is this something that is being defined at the grammar level or somewher

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

Laminas CI Automation – Matthew Weier O’Phinney

The Laminas Project has close to 200 repositories between the main project, Laminas API Tools, and Mezzio.
It’s a lot to maintain, and keeping on top of incoming patches can be a gargantuan task, much less creating releases.

That’s why this past year, we’ve spent a bunch of time on streamlining our processes; we want to be able to review, merge, and release changes quickly and confidently.
To that end, we have developed a number of GitHub Actions to make these processes as easy as possible for our maintainers.

Automated Releases

The first was a brainchild of Marco Pivetta (aka Ocramius).
He wanted a way to make releases as simple as possible.

Before this, we had a rather convoluted process:

  • If a pull request was against our “master” branch:
    • Merge to “master”
    • Merge to “develop” (which generally resulted in merge conflicts, due to differences in the CHANGELOG.md file between branches)
    • Create a branch off of “master” to set the release version
    • Bump the release version in the CHANGELOG.md
    • Merge the release branch into “master”
    • Merge the release branch into “develop” (again, merge conflicts)
    • Tag the release, copying the relevant CHANGELOG.md entry into the tag description
    • Push the release
    • Create a release on GitHub from the tag, and, again, copy the CHANGELOG.md entry into the description
  • If a pull request was against our “develop” branch:
    • Merge to “develop”
    • Merge “develop” to “master”
    • Create a branch off of “master” to set the release version
    • Bump the release version in the CHANGELOG.md
    • Merge the release branch into “master”
    • Merge the release branch into “develop”
    • Bump the release version in the CHANGELOG.md file in the “develop” branch to the next minor version
    • Tag the release from “master”, copying the relevant CHANGELOG.md entry into the tag description
    • Push the release
    • Create a release on GitHub from the tag, and, again, copy the CHANGELOG.md entry into the description

A lot of the work around tagging and creating the GitHub release are handled by my keep-a-changelog tooling, but it was still work, and there was a lot of boilerplate and busywork involved.

Marco’s big idea: what if we assigned issues and pull requests to GitHub milestones, and, when the milestone was closed, the release was created automatically?

This led to the creation of our automatic-releases GitHub action.

To work with it, you need to create release branches in your repository, named after semantic versions, and of the form {MAJOR}.{MINOR}.x (that’s a literal “.x” at the end).
(This has a nice side benefit of removing the “master” verbiage from our branches as well.)
You then create milestones named for the next releases you want to create: 1.2.3, 1.3.0, 2.0.0.
From there, you add a small workflow to your application, along with a few secrets (a GPG signing key, a Git author and email for tagging the release, and potentially a privileged GitHub token to allow creating a merge-up request; more on that later).

As you triage, assign your issues and pull requests to milestones.
When all issues and pull requests related to a milestone are complete, you close the milestone, and the workflow takes it from there.

What the workflow does:

  • It pulls the milestone description.
  • It pulls the list of issues and pull requests, along with the people who created them, to create a list of release notes detailing the issues/pull requests closed.
  • If you have a CHANGELOG.md in the Keep A Changelog format, it will update the entry for the release to append the milestone description and release notes pulled in the previous steps, as well as set the release date, pushing the changes back to the branch.
  • It creates a tag using the signing key and git author/email, setting the description to the the changelog entry, or the information from the first two steps, pushing the tag on completion.
  • It creates a release on GitHub, using the same notes provided in the tag description.
  • If a newer release branch exists (e.g., if you were rele

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

PHP Internals News: Episode 78: Moving the PHP Documentation to GIT – Derick Rethans

PHP Internals News: Episode 78: Moving the PHP Documentation to GIT

In this episode of “PHP Internals News” I chat with Andreas Heigl (Twitter, GitHub, Mastodon, Website) to follow up with his project to move the PHP Documentation project from SVN to GIT, which has now completed.

The RSS feed for this podcast is https://derickrethans.nl/feed-phpinternalsnews.xml, you can download this episode’s MP3 file, and it’s available on Spotify and iTunes. There is a dedicated website: https://phpinternals.news

Transcript

Derick Rethans 0:15

Hi, I’m Derick. Welcome to PHP internals news, the podcast dedicated to explaining the latest developments in the PHP language. This is Episode 78. In this episode, I’m talking with Andreas Heigl about moving the PHP documentation to GIT. Andreas, would you please introduce yourself?

Andreas Heigl 0:35

Hi yeah I’m Andreas, I’m working in Germany at a company doing PHP software development. I’m doing a lot of stuff in between, as well. And one of the things that I got annoyed, was always having to go through hilarious ways of contributing to the PHP documentation, every time I found an issue with that. So at one point in time, I thought why not move that to Git and, well, here we are.

Derick Rethans 1:07

Here we are five years later, right? Because we already spoke about moving the documentation to GIT back in 2019 and Episode 28. But now it has finally happened, so I thought it’d be nice to catch up and see what actually has changed and how we ended up getting here. Where would you want to start. What was the hardest thing to sort out in the end?

Andreas Heigl 1:27

Well the hardest thing in the end to sort out was people, as probably always in software development. The technical oddities and the technical bits and pieces were rather fast to solve. What really was taking a long time was, well for one thing, actually, consistently working on that. And on the other hand, chasing down people to actually get stuff done. Because one of the major things here was not the technical side but getting the bits and pieces of information together to get access to the different servers, to the infrastructure of the PHP ecosystem, and chasing down the people that want to help you is one thing, and then chasing down the people that actually can help you is a completely different one. That was for me the most challenging bit, getting actually, to know who can do what and getting, yeah in the end, getting access to the actual machines, the whole ecosystem is running on that was really heavy.

Derick Rethans 2:34

The System Administration of PHP.net systems is very fragmented. There’s some people having access to some machines and some other people having access to other machines and yea it sometimes takes some time to track down where are all these bits actually run.

Andreas Heigl 2:51

One thing is getting tracking down, where the bits run, the other one is, there is an excellent documentation in the wiki, the PHP wiki, which in some parts is kind of outdated. The other thing is, if you don’t actually address the different people themselves personally, it is like screaming into the void so you can can send an email to 15 different people that have access to a box, like someone needs to do this and this and this. And everyone kind of seems to think, yeah, someone else can do that. I just don’t have the time at this point in time Things get delayed, so you’re waiting for an answer for a week; you do some other stuff, so two weeks go into the lab four weeks go into the land, and suddenly two months have passed. You didn’t receive an answer and oh wait a min

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

Dependency injection in Serverless PHP with Bref – Rob Allen

When writing PHP for AWS Lambda, Bref is the way to do it. One thing I like about Bref is that you can use PSR-15 Request Handlers to respond to API Gateway HTTP events as documented in the API Gateway HTTP events section of the Bref docs.

The request handler is the same as used in PSR-7 micro-frameworks like Slim or Mezzio and can be thought of as a controller action. As such, it’s really common to use dependency injection in a controller class and I vaguely remembered Matthieu tweeting about Bref having support so I poked around.

Turns out that its really easy.

Bref supports any PSR-11 container with the Bref::setContainer() static function.

This takes a closure that must return a ContainerInterface, so using PHP-DI, we can set it up like this:

bootstrap.php:

<?php declare(strict_types=1); use Bref\Bref;
use DI\ContainerBuilder; Bref::setContainer(function () { // Create and build the container $containerBuilder = new ContainerBuilder; $containerBuilder->addDefinitions( [ 'debug' => (bool)(getenv('APP_DEBUG') ?: false), Connection::class => function (Container $c) { // create and return a DBAL Connection instance here } ] ); return $containerBuilder->build();
});

Now we can create constructors in our Handler classes and pass in what we need:

src/Handler/UserHandler.php:

<?php declare(strict_types=1); namespace App\Handler; use Doctrine\DBAL\Connection;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface; class UserHandler implements RequestHandlerInterface
{ public function __construct(private Connection $db) { } public function handle(ServerRequestInterface $request): ResponseInterface { // get user data using $this->db and return a Response }
}

Fantastic!

(Note the use of PHP 8’s constructor property promotion too!)

Registering the Handler

To register the handler so that the DI container will be used to instantiate it, we use the class name, not the file name in serverless.yml like this:

functions: users: handler: App\Handler\UserHandler events: - httpApi: 'GET /users'

We use an httpApi event as nothing else makes sense for a handler that returns a Response.

Automatically registering our container

The final piece of the puzzle is how do we automatically call Bref::setContainer()?

The answer to this is Composer‘s ability to autoload files! Any file we register in the files section of autoload will be automatically loaded by Composer’s autoloader, which rather handily is called by Bref itself.

composer.json:

"autoload": { "psr-4": { "App\\": "src/" }, "files": [ "bootstrap.php" ] },

To sum up

Dependency injection is a very valuable tool in our toolbox for writing flexible code that’s easy to reason about and test. This is just as easy in the serverless environment with Bref as it is in a fast-cgi environment.

Xdebug Update: February 2021 – Derick Rethans

Xdebug Update: February 2021

Another monthly update where I explain what happened with Xdebug development in this past month. These will be published on the first Tuesday after the 5th of each month.

Patreon and GitHub supporters will get it earlier, around the first of each month.

You can become a patron or support me through GitHub Sponsors. I am currently 90% towards my $1,000 per month goal (4% less than last month). If you are leading a team or company, then it is also possible to support Xdebug through a subscription.

In February, I worked on Xdebug itself for about 16 hours, with funding being around 32 hours. I am no longer including Xdebug Cloud time in this figure, which is what I spend most of my time on. More on that later.

Releases

The Xdebug 3.0.3 release that came out last week, addresses a few minor issues. One regarding missing information in Code Coverage, another one where local variables are missing if a debugging session was started through xdebug_break(), and another one addressed an issue with xdebug_info() output.

The next release will likely be Xdebug 3.1 where I will focus on making xdebug_info() show even more diagnostics information, and also implement some additional features for Xdebug Cloud, which brings me to the last point of this month’s short newsletter.

Xdebug Cloud

Xdebug Cloud is now released in Beta, and has a new web site address at https://xdebug.cloud — which means that it is now possible to sign up! Packages start at £49/month, and revenue will also be used to further the development of Xdebug.

If you want to be kept up to date with Xdebug Cloud, please sign up to the mailinglist, which I will use to send out an update not more than once a month.

Do tests need static analysis level max? – Matthias Noback

I recently heard this interesting question: if your project uses a static analysis tool like PHPStan or Psalm (as it should), should the tests by analysed too?

The first thing to consider: what are potential reasons for not analysing your test code?

Why not?

1. Tests are messy in terms of types

Tests may use mocks, which can be confusing for the static analyser:

$object = $this->createMock(MockedInterface::class);

The actual type of $object is an intersection type, i.e. $object is both a MockObject and a MockedInterface, but the analyser only recognizes MockObject. You may not like all those warnings about “unknown method” calls on MockObject $object so you exclude test code from the analysis.

2. Static analysis is slow

Static analysis is slow, so you want to reduce the amount of code the analyser has to process on each run. One way to do this is to ignore all the test code.

3. Production code is more important because it gets deployed

Besides performance, another justification for excluding the test code may be that production code is more important. It has to be correct, because it will be deployed. A bug in production code is worse than a bug in test code.

Why yes?

I think we’ve covered the three major objections against analysing test code. If you have other suggestions, please let them know in the comments! Anyway, let’s tackle the objections now, because (as you may have guessed): I think we should have our test code analysed too.

1. Mock types can easily be improved

Static analysers support intersection types so a mocked object can be annotated as MockObject & MockedInterface. They also have plugins or extensions that can derive the resulting type for you:

$object = $this->createMock(MockedInterface::class); // Derived type of $object: MockObject & MockedInterface

2. Static analysers have a cache

Both PHPStan and Psalm use a cache so they don’t have to analyse the entire code base over and over again. You won’t notice any difference if you analyse all your code or just production code (quick tip: if you run the analyser in a Docker container, make sure that the cache directory is not lost after each run; configure it to be within one of the bind-mounted volumes).

3. Test code is just as important as production code

Of course, it’s the production code that gets deployed so its quality needs to be guarded. However, tests play another important role in quality assurance. To care less about your tests means you’ll have trouble maintaining both production code and test code. Test code also deserves to be refactored, and its design needs to evolve over time. When doing so, it will be very important to get feedback from static analysers.

Additional benefits

Separately testing behavior

Statically analysing all the code before running the tests is a great way to ensure that the tests themselves don’t throw any basic errors, like wrong number of method arguments, type mismatches, etc. This allows for a more clear definition of the role of tests versus static analysis: static analysis can tell you that your code will (most likely) run, and the tests can tell you if the code actually implements the behavior you expect from it.

Probing

Running static analysis on your entire code base allows for different refactoring workflows too. Consider a common situation where a method needs an extra required argument. A traditional workflow is:

  • Add an optional argument to this method.
  • Find all usages of this method with your IDE.
  • On each call site, pass the new argument.
  • Finally make the argument required.

At every stage, the code and tests keep working and every step can be committed.

Another approach could be:

  • Add that extra required argument.
  • Run the static analyser and find out what no longer works.

By doing so you might not be prepared to stop at any time. However, it does follow the Mikado style; the first step of which is to Just Do It an

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

PHP: Saving Xhtml creates entity references – Christian Weiske

All my blog posts are Xhtml, because I can load and manipulate them with an XML parser. I do that with scripts when adding IDs for better referencing, and when compiling the blog posts by adding navigation, header and footer.

The pages have no XML declaration because the W3C validator complains that

Saw <?. Probable cause: Attempt to use an XML processing instruction in html. (XML processing instructions are not supported in html.)

But when loading such a Xhtml page with PHP’s SimpleXML library and generating the XML to save it, entities get encoded:

<?php
$xml = 'ÄÖÜ';
$sx = simplexml_load_string($xml);
echo $sx->asXML() . "\n";
?>

This script generates encoded entities:

<?xml version="1.0"?>
ÄÖÜ

I found the solution for that problem in a stack overflow answer: You have to manually declare the encoding – despite the standard saying that UTF-8 is standard when no declaration is given.

dom_import_simplexml($sx)->ownerDocument->encoding = 'UTF-8';

Now the generated XML has proper un-encoded characters:

ÄÖÜ