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:

ÄÖÜ

Book excerpt – Decoupling from infrastructure, Conclusion – Matthias Noback

This article is an excerpt from my book Advanced Web Application Architecture. It contains a couple of sections from the conclusion of Part I: Decoupling from infrastructure.

This chapter covers:

  • A deeper discussion on the distinction between core and
    infrastructure code
  • A summary of the strategy for pushing infrastructure to the sides
  • A recommendation for using a domain- and test-first approach to
    software development
  • A closer look at the concept of “pure” object-oriented programming

Core code and infrastructure code

In Chapter 1 we’ve looked
at definitions for the terms core code and infrastructure code. What
I personally find useful about these definitions is that you can look at
a piece of code and find out if the definitions apply to it. You can
then decide if it’s either core or infrastructure code. But there are
other ways of applying these terms to software. One way is to consider
the bigger picture of the application and its interactions with
actors. You’ll find the term actor in books about user stories and use
cases by authors like Ivar Jacobson and Alistair Cockburn, who make a
distinction between:

  1. Primary actors, which act upon our system
  2. Secondary or supporting actors, upon which our system acts

As an example, a primary actor could be a person using their web
browser to send an HTTP POST request to our application. A
supporting actor could be the relational database that our application
sends an SQL INSERT query to. Communicating with both actors requires
many infrastructural elements to be in place. The web server should be
up an running, and it should be accessible from the internet. The server
needs to pass incoming requests to the application, which likely uses a
web framework to process the HTTP messages and dispatch them to the
right controllers. On the other end of the application some data may
have to be stored in the database. PHP needs to have a PDO driver
installed before it can connect to and communicate with the database.
Most likely you’ll need a lot of supporting code as well to do the
mapping from domain objects to database records. All of the code
involved in this process, including a lot of third-party libraries and
frameworks, as well as software that isn’t maintained by yourself (like
the web server), should be considered infrastructure code.

Most of the time between the primary actor sending an HTTP request to
your server, and the database storing the modified data, will be spent
by running infrastructure code and most of this code can be found in PHP
extensions, frameworks, and libraries. But somewhere between ingoing and
outgoing communication the server will call some of your own code, the
so-called user code.

User code is what makes your application special: what things can you
do with your application?
You can order an e-book. You can pay for it.
What kind of things can you learn from your application? You can see
what e-books are available. And once you’ve bought one, you can download
it. Frameworks, libraries, and PHP extensions could never help you with
this kind of code, because it’s domain-specific: it’s your business
logic.

The following figure shows that user
code is in the middle of a lot of infrastructure code:

Even if we try to
ignore most of the surrounding infrastructure while working on and
testing user code, we’ll often find that this code is hard to work with.
That’s because the code still contains many infrastructural details. A
use case may be inseparable from the web controller that invokes it. The
use of service locators and the likes prevents code from running in
isolation, or in a different context. Calls to external services require
the external service to be available when we want to locally test our
code. And so on…

If that’s the case, user code consists of a mix of infrastructure code
and core code.
The following figure shows what this looks like:

When I look at this diagram, I immediately feel
the urge to push the bits of infrastructure co

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

Testing your controllers when you have a decoupled core – Matthias Noback

A lot can happen in 9 years. Back then I was still advocating that you should unit-test your controllers and that setter injection is very helpful when replacing controller dependencies with test doubles. I’ve changed my mind: constructor injection is the right way for any service object, including controllers. And controllers shouldn’t be unit tested, because:

  • Those unit tests tend to be a one-to-one copy of the controller code itself. There is no healthy distance between the test and the implementation.
  • Controllers need some form of integrated testing, because by zooming in on the class-level, you don’t know if the controller will behave well when the application is actually used. Is the routing configuration correct? Can the framework resolve all of the controller’s arguments? Will dependencies be injected properly? And so on.

The alternative I mentioned in 2012 is to write functional tests for your controller. But this is not preferable in the end. These tests are slow and fragile, because you end up invoking much more code than just the domain logic.

Ports and adapters

If you’re using a decoupled approach, you can already test your domain logic using fast, stable, coarse-grained unit tests. So you particularly don’t want to let your controller tests also invoke domain logic. You only want to verify that the controller correctly invokes your domain logic. We’ve seen one approach in Talk review: Thomas Pierrain at DDD Africa, where Thomas explained how he includes controller logic in his coarse-grained unit tests. He also mentioned that it’s not “by the book”, so here I’d like to take the time to explain what by the book would look like.

Hexagonal architecture prescribes that all application ports should be interfaces. That’s because right-side ports should potentially have more than one adapter. The port, being an interface, allows you to define a contract for communicating with external services. On the left side, the ports should be interfaces too, because this allows you to replace the port with a mock when testing the left-side adapter.

Following the example from my previous post, this is a schema of the use case “purchasing an e-book”:

PurchaseEbookController and PurchaseRepositoryUsingSql are adapter classes (which need supporting code from frameworks, libraries, etc.). On the left side, the adapter takes the request data and uses it to determine how to call the PurchaseEbookService, which represents the port. On the right side there is another port: the PurchaseRepository. One of its adapters is PurchaseRepositoryUsingSql. The following diagram shows how this setup allows you to invoke the left-side port from a test runner, without any problem, while you can replace the right-side port adapter with a fake repository:

Left-side adapter tests

Since the test case replaces the controller, the controller remains untested. Even though the controller should be only a few lines of code, there may be problems hiding there that will only be uncovered by exploratory (browser) testing or once the application has been deployed to production.

In this case it would help to create an adapter test for the controller. This is not a unit test, but an integrated test. We don’t invoke the controller object directly, but travel the normal route from browser request to response, through the web server, the framework, and back. This ensures that we got everything right, and leave (almost) no room for mistakes in interpreting framework configuration (routing, security, dependency injection, etc.).

We have already established that you wouldn’t want to test domain logic again through a web request. Two things we need to solve for adapter tests then:

  1. The controller shouldn’t invoke the domain logic. Instead, we should only verify that it calls the port (which is an interface) in the right way. The pattern for this is called mocking: we need a test double that records the calls made to it and makes assertions about those calls (the number of times, and the arguments provided).
  2. We need a way to inject this mock into the controller as a constructor argument. Thi

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