Tag Archives: php

Deploy using git archive

When deploying code I by now almost always use git archive for that no matter whether that is library code or actually product code that gets deployed onto a (web)server.

Recently I had a chat with someone that so far hadn’t heard of that so I realized perhaps it’s time to write about it.

What is it

git archive is a tool that exports the content of a git repository into an archive file. But while doing so it also uses information in a .gitattributes file to decide whether to include a file into the archive or not and also whether to modify a file.

A lot of people already know about the .gitattributes file as it makes sure that certain files are removed from the archive that is created by github when creating a release.

The documentation for .gitattributes has (amongst a huge number of other information and awesome things that can be done via that file – but that’s for some other time) a special part about creating archives that talks about two attributes:

Continue reading Deploy using git archive

ICU in PHP-images from Docker-Hub

Recently I was preparing a talk for the fwdays PHP in Kyiv. A talk about internationalization. And gues what: One of the parts of internationalization is: Timezones. Yes, I know, I am fond of them and all but that’s not what this is going to be about.

In the talk I am showing how to propperly render a datetime for a given locale in a given timezone using the IntlDateFormatter. As the talk will be in Kyiv I thought it’s only fair to use the Europe/Kyiv timezone.

The timezone Europe/Kyiv is a rather new one and was introduced in 2022 when the former timezone Europe/Kiev was renamed to Europe/Kyiv as that by now was the more widely used international spelling of the Ukrainian capital (Thank you, Vladimir, for making the world aware that the russian transcription isn’t the correct one!!)

So I added my example and thought I’d better run it through a current PHP-Version to check wether my code is actually sane and runs.

As the ICU-extension is not by default part of the images on docker-hub I created this little Dockerfile to get an image with the ICU extension:

FROM php:8.3-rc-fpm
MAINTAINER andreas@stella-maris.solutions

RUN apt-get update \
 && apt-get install -y libicu-dev \
 && docker-php-ext-configure intl --enable-intl \
 && docker-php-ext-install -j$(nproc) intl \

It’s using the latest PHP 8.3 image and adds the ICU extension. Yes! it’s not slim, there is cleanup missing etc but that is not the point here, it’s just to demonstrate something.

I created a container via docker build -t icutest .

So now let’s check the ICU version.

$ docker run icutest bash -c "php -i | grep -i icu"
ICU version => 72.1
ICU Data version => 72.1
ICU TZData version => 2022e
ICU Unicode version => 15.0
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Wait! ICU TZData version => 2022e?

As https://data.iana.org/time-zones/tzdb-2022e/ shows, that’s from October 2022! What the heck?

What’s the timezone-DB version that PHPs DateTime lib uses?

$ docker run icutest bash -c "php -i | grep -i Olson"
"Olson" Timezone Database Version => 2024.1

Oh! Great!

So we learn different things here:

PHP libraries use separate timezone-data

The DateTime extension (you know: date, DateTimeImmutable etc) use the latest timezone-db version available at compile time of the PHP-library. (Thank you Derick for that!) One of the reasons why one should stay up-to-date with PHP-versions. If that’s not possible, you can use the timezonedb pecl-package.

But the Intl Extension – as it is a wrapper around the ICU4C library – uses it’s own timezone database. Also the last one available at release time of the library.

So why do we have a mismatch there? Because at the time of writing the current release of ICU is 75.1 – and not 72.1 as the output of PHP-info shows.

The issue I am running into here is that I installed the libicu-dev package when building the container. And that installs the – by now outdated – ICU data as debian/ubuntu do not update those packages to higher major versions. An upgrade to a higher major version of a lib only happens when updating Debian/Ubuntu to a higher Major.

Despite the fact that neither Debian/Ubuntu nor ICU are using Semantic Versioning…

Don’t come with a problem!

OK. I now had analyzed the problem (my main problem was that the code didn’t run because I was using the bullseye image and not the bookworm one which is on an even older timezone DB that didn’t know Europe/Kyiv as it was from 2019…).

How to fix it?

Well, the solution seems simple: Update the ICU extension…

So I fiddled a bit around and updated another library to handle the non-semver versioning that ICU uses and ended up with this somewhat more complex Dockerfile

What happens here

Let’s shortly go through the dockerfile

In line 5 I install stuff that is necessary for compiling code as I will need to do that with the ICU lib.

In line 6 I download the latest version of the ICU4C library. It uses getlatestassets that will make sure that we will always get the latest relerased version. Sadly the github API doesn’t provide a way to do that out of the box, so I have to use getlatestassets.com here. To make handling easier I store the archive in a file consitently named icu.tgz

The next steps are to extract the archive and change the working directory.

In line 9 I set the LIBDIR variable from what is set for PHP so that the ICU library will afterwards be placed in the right folder where PHP expects all the libs to be. Otherwise I get some odd runtime errors when PHP tries to load a library that is not available.

In line 10 I tell the config script where to put that library file and then it’s make and make install to build and install the library.

Afterwards I remove the archive file. In a production image I’d do some more cleanup here to get the imagesize down.

After that it’s the same commands that I already used in the old Dockerfile, just this time the “correct” library-version will be used.

So let’s create and check the container:

Great! Now we have a current version of ICU with a rather recent version of the timezone-database. And also all the other awesome things that are part of a new ICU version! Like new translations, new emojis etc.

What did I learn?

Whenever I am using the Intl extension in PHP I will now make sure that the PHP-Version I am using is actually using the latest available ICU-library as one can not take for granted that that is delivered by default.

In essence I should probably check that for every library I use, but most of them do not contain such volatile information as the (constantly changing) timezone DB. And usually fixes for critical issues are ported to the respective libraries by the distros. But that requires me to keep on a distro-version that gets these patches!

So in essence the main thing is – as always – Stay up to date

Debug LDAP via TLS

Yesterday I had to do some debugging to find out why an LDAPS connection didn’t work.

The main trouble was that the authLdap plugin for WordPress didn’t work for someone. After a bit of back and forth we figured out that it worked for other applications but not for PHPs LDAP-extension.

The error they got was the usual cryptic Can't contact LDAP server which says nothing at all as that can mean so many different things.

Continue reading Debug LDAP via TLS

Xdebug in Docker

Yesterday Dmitri Goosens wrote about how to integrate Xdebug into almost any Docker Setup in PHPStorm. An article that I definitely need to digest a bit more as the PHPStorm integration is something that I often neglect. I am happy when it works on the CLI 😁

One thing though that Dmitri skips though (an dfor good reasons) is how to get Xdebug into your Docker environment.

As I have done that quite a number of times by now I thought I’d share the way I do it currently.

This is my Dockerfile

FROM php:8.3-rc-fpm
RUN PHPIZE_DEPS="autoconf \
      cmake \
      file \
      g++ \
      gcc \
      libc-dev \
      libpcre2-dev \
      make \
      git \
      pkgconf \
      re2c" \
    && XDEBUG_VERSION="" \
    && if [ "$(php -v | head -n 1 | grep 8.3)" != "" ]; then XDEBUG_VERSION="-3.3.0alpha3"; fi \
    && apt-get update \
    && apt-get install -y $PHPIZE_DEPS \
    && pecl install xdebug${XDEBUG_VERSION} \
    && docker-php-ext-enable xdebug \
    && echo "xdebug.mode=debug\nxdebug.discover_client_host=on\nxdebug.start_with_request=yes\nxdebug.log_level=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
    && apt-get remove -y $PHPIZE_DEPS

That should install a running version of Xdebug on whatever version of PHP you are using and even allows to use the soon to be relesased PHP8.3.

The main lines here are

    pecl install xdebug${XDEBUG_VERSION} \
    docker-php-ext-enable xdebug \

everything else is decoration resp. necessary that those two lines can do their magic.

Feel free to adapt the FROM line to whatever PHP-version you want to use from dockers official PHP-image (remember that that is not maintained by the PHP folks! It’s maintained by Docker!)

After a docker build -t phpxdebugtest . you should now be able to call docker run phpxdebugtest php -v and see this

PHP 8.3.0RC5 (cli) (built: Nov 1 2023 05:26:54) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.0RC5, Copyright (c) Zend Technologies
with Xdebug v3.3.0alpha3, Copyright (c) 2002-2023, by Derick Rethans

And now have fun integrating that Xdebug enabled PHP-Container into your PHPStorm.

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.