composer 🧡 phar

It bothered me for a long time that installing tools via composer cluttered my projects with unnecessary dependencies and also bind my code to the dependencies of my development toolchain and vice-versa.

The easy way to solve that was to use phar-files for the tools I am using in my development chain. So tools like phpunit, phpstan, psalm or phpcs/phpcbf. All of these can be installed via composer require --dev – but also via phive install.

The trouble though when using the phar-files was, that composer didn’t know about them and whenever I wanted to use a plugin for one of those tools, composer didn’t know that the tool was already there and so installed the tool again. Which wasn’t helpful!

I was thinking about multiple ways to handle that. Like a plugin for composer to remove installed PHAR-files from the internal resolver-tree and what other ideas I had. All of these didn’t really work out.

Until a few days it hit me: composers replace config-option!

So what did I do:

After installing my tool – in this case php-codesniffer – via phive install phpcs --copy I created a new composer.json file in the .phive-folder with the following content:

{
    "name": "myproject/phive_stuff",
    "description": "A replacement package for phars",
    "minimum-stability": "stable",
    "license": "MIT",
    "replace" : {
        "squizlabs/php_codesniffer": "*"
    }
}

Now I added this code to my projects composer.json file:

{
    "repositories": [{
        "type": "path",
        "url": ".phive/"
    }]
}

Then all that was left to do was to require the new package via

composer require --dev myproject/phive_stuff 

With all that done I can now install plugins for php-codesniffer via

composer require --dev phpcompatibility/php-compatibility

and composer will realize that php-codesniffer is already installed and not install it again.

Caveat

This has some caveats though! For example there are two tools phpcs and phpcbf that need to be installed via phive while requiring squizlabs/php-codesniffer will install both of them.

Due to the way phive works, the binaries are by default linked into the project from a main folder outside the project which can break when using docker. That’s why I usually call phive with the --copy flag as that actually adds a copy of the phar to the tools -folder.

Due to this linking phpcs suddenly created its config-file in that shared folder which had some unexpected sideeffects. When using --copy the confi is added by default to the tools folder.

So there might be some extra work necessary when using PHARs. But at least it works now 😁

Further ideas

My main idea now was to automate this manual process as that is something that can automatically done by phive when installing (or updating) a tool.

Would that something that helps others as well? Feel free and leave your comment in the feature-request on github

Tweaking a WordPress blog for the fediverse

The Fediverse is taking off. Slightly. Not sure yet whether it’s similar to “Linux on Desktop” but no matter what it’s all about federation. And making it easier to get content right into the timeline of people is worth investigating.

So I decided to try the ActivityPub plugin and see where it leads me.

Installation of the plugin is straight forward. Head to “new Plugins”, search for ActivityPub, install and activate it.

The cool thing is: That’s it!

At least when you have setup your WordPress blog out of the box.

You can now follow the author on the fediverse by checking for @[authorname]@[blog-URI] . So in my case that would be @heiglandreas@andreas.heigl.org

And of course in my case it didn’t work out-of-the-box. Why should it.

Why? Well: Two reasons:

  • One was that I use the Yoast SEO plugin which by default (or did I actually set it up that way?) did redirect requests to the author-page back to the main website. Which is kind of counter productive when you want information about the author. So I changed those settings (“Yoast SEO” => “Search Appearances” => “Archives” – Set “Author Archives” to On)
  • The other was that I am running this blog from a subfolder. Which is something so common, that the plugin authors already have that in their FAQ on the plugin page. So I headed over to the server-config, made the mentioned tweaks, restarted the server and – voila – everything works!

Now I was able to find and follow @heiglandreas@andreas.heigl.org in my Mastodon-Client.

Things that I need to figure out now

The next things on my todo-list are:

  • That’s all nice and dandy on a personal blog. But how do I implement this so that people can actually follow a blog with changing authors – like for example 24daysindecember (did I mention that we are looking for people that want to contribute?)
  • Is there a possibility (or does it at all make sense) to somehow integrate that into my default fediverse-account? Or to get my personal account over to the andreas.heigl.org domain? Or to setup an @andreas@heigl.org fediverse account that also contains the stuff from the blog…

But those are questions that I will possibly answer in a

Increase code coverage successively

I often come across legacy projects that have a very low code coverage (or none at all). Getting such a project up to a high code coverage can be very frustrating as you will have a poor code coverage for a very long time.

So instead of generating an overall code coverage report with every pull request I tend to create a so called patch coverage report that checks how much of the patch is actually covered by tests.

Having something like that in place also allows me to force contributors to include tests for their newly contributed code. Which in turn successively improves the overall code coverage up to a level where I might be able to go for that instead of the patch coverage.

But how to implement that?

That’s not as complicated as it sounds. As Sebastian Bergmann already wrote a tool for that.

Enter phpcov

Using phpcov requires us to

  • first generate a diff against the last code-revision,
  • then generate a coverage-report via phpunit --coverage-php and
  • then run phpcov against those artefacts.

So it’s as complicated as

$ git diff HEAD^1 > /tmp/patch.txt
$ ./tools/phpunit --coverage-php /tmp/coverage.cov
$ ./tools/phpcov patch-coverage --path-prefix /path/to/project /tmp/coverage.cov /tmp/patch.txt

That’s it.

It will return a non-zero value when not all lines are covered and it will tell you which lines aren’t covered.

So add that to your automation to have it executed at whatever stage you like (I recommend in the CI-pipeline of your Pull-/Merge-Request and let that fail whenever the return code is non-zero)

Github Action

If you want to see a way to implement that in GitHub Actions, check out this gist.

Attributes are awesome

Especially in combination with constructor property promotion.

Recently I did a code-review on an entity from a PHP8.0 project. What I saw was this:

The origin

<?php

declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'user')]
class User
{
    #[ORM\Id]
    #[ORM\Column(type: 'integer', unique: true)]
    public int $id;

    #[ORM\Column(type: 'string')]
    public string $username;

    #[ORM\Column(type: 'string')]
    public string $passwordhash;

    public function __construct(
        int $id,
        string $username,
        string $passwordhash,
    ) {
        $this->id = $id;
        $this->username = $username;
        $this->passwordhash = $passwordhash;
    }
}

I was thinking about using constructor property promotion to get rid of the property declaration and property assignment but immediately thought that that would require us to handle the attributes differently. So I shortly checked my favourite search engine and realized that indeed we are able to shorten this whole stuff considerably:

The result

<?php


declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(name: 'user')]
class User
{
    public function __construct(
        #[ORM\Id]
        #[ORM\Column(type: 'integer', unique: true)]
        public int $id,
        #[ORM\Column(type: 'string')]
        public string $username,
        #[ORM\Column(type: 'string')]
        public string $passwordhash,
    ) {}
}

Combining the attributes with the constructor property promotion allows much smaller entity definitions.

Immutability

In our case we also cleared a misunderstanding. My colleague was under the impression that Doctrine requires properties to be public. Otherwise they are not able to be hydrated properly. That is not the case and Doctrine can perfectly hydrate private properties!

So we decided to go for private properties in combination with getters. As in our concrete implementation we actually also need to execute some logic before returning some of the values. So declaring the properties private and having getters provides us with a truly immutable entity.

As we are not using PHP8.1 yet we could not make use of the new readonly declaration which would have solved the issue with the immutability though as we need logic in some cases the approach with using getters always makes more sense in this particular setup.