Deploy local build with Deployer7


Deployer is a great tool to deploy your PHP Project.

Deployer executes a set of commands on the target server to build your project and enable the newly built version. A typical deployment process with Deployer involves SSHing into the target machine, where it performs a Git checkout of the project, installs dependencies via Composer, runs build commands, and possibly triggers some database migrations. When everything is successful, it will symlink the webroot to the new release.

On some servers, however, there are limitations that make this process unfeasible. For instance, you can’t install Composer, Git isn’t available, the CLI PHP version is different and can’t be changed, or certain asset-building processes aren’t possible because Node.js isn’t installed. This is often the case with shared hosting.

Continue reading “Deploy local build with Deployer7”

Symfony integration tests custom header is missing

I am writing an integration test in Symfony with a request that includes custom headers. However, the request fails because the custom header is apparently missing.
What happened was I forgot to add an HTTP prefix to the custom header.

This is a common pitfall when writing integration tests in Symfony and using custom HTTP headers: the necessity to add an HTTP_ prefix to the header in the test.
If you do not add the prefix to the custom header, it will silently not be added to the request, and you have to debug why the request fails.

So this will fail:

static::$client->request('GET', '/ping', [], [], ['X-Custom-Header' => 'custom']);Code language: PHP (php)

And this will work:

static::$client->request('GET', '/ping', [], [], ['HTTP_X-Custom-Header' => 'custom']);Code language: PHP (php)
Continue reading “Symfony integration tests custom header is missing”

Composer bump

Do you miss that the version numbers of your PHP dependencies are automatically updated in the composer.json file after a composer update?
Just like npm or yarn are updating the version numbers in the package.json file.

Then upgrade to Composer 2.4 and say hi to composer dump.
This version introduced a new command composer bump which will update your composer.json file to the precise version which is pinned in the composer.lock file.
It basically will sync the composer.json with the composer.lock versions and will keep the caret version constraints, so you can still make minor or patch version upgrades.

Continue reading “Composer bump”

Testing PDF creation with headless chrome and PHP

I had the task the other day to use a headless chrome to generate PDF files from websites in a PHP app.

The plan was to use chrome-php with a headless chrome to generate the PDF.

Usually you would install chrome/chromium on a linux server via apt and just run chrome from the PATH with chrome.
Since i was on shared hosting i was not sure if this was possible since i was not allowed to run apt commands.
So i tried to use Puppeteer which ships a headless chrome executable and use just this directly.
I installed Puppeteer with npm locally and uploaded the chrome executable to the shared hosting.
Puppeteer will place the headless chrome in the .cache dir in your home directory, f.e.:

~/.cache/puppeteer/chrome/linux-113.0.5672.63/chrome-linux64/chrome

Upload:

scp -r ~/.cache/puppeteer/chrome/linux-113.0.5672.63/chrome-linux64 me@sharedhosting:/usr/home/test

Continue reading “Testing PDF creation with headless chrome and PHP”

Stream an image from Symfony controller

When sending an image/file from a Symfony controller you would usually read the file from filesystem.
This you can do by passing the file path to the BinaryFileResponse class and return it in the controller, as described in the docs:
https://symfony.com/doc/current/components/http_foundation.html#serving-files

use Symfony\Component\HttpFoundation\BinaryFileResponse;

$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);Code language: PHP (php)

But in some cases you might want to create an image dynamically and return it directly from the controller without storing it on the filesystem.
So you can create your image with the GD library and directly stream the GDImage object from the symfony controller with the StreamedResponse class.

Continue reading “Stream an image from Symfony controller”

Doctrine migrations and Postgis

Using Postgres with the Postgis extension to integrate GeoData / GIS functionality in your project is not natively supported by Doctrine and Doctrine migrations.

First you have to add the extension to Postgres, even if you use the Postgis docker image like postgis/postgis:14-3.3-alpine.

So add this SQL statement to the up method of your first migration:
$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis;');

and the DROP statement for the extension to the down method:
$this->addSql('DROP EXTENSION postgis;');

Now, when using Doctrine with Postgres and Postgis extension, migrations still behave a bit odd and try to remove Sequences created by Postgis, because Doctrine migrations does not take Postgis extension’ s built-in Sequences into account.

Continue reading “Doctrine migrations and Postgis”

Symfony deprecation log channel

Are you annoyed of too many deprecation warnings in you logs of your symfony app?
Probably yes because it is really a lot of noise.
However deprecation logs are still useful to ensure future compatibility of your app.

So since version 5.1 symfony will log deprecations to a dedicated log channel when it exists and ships with this monolog config:

monolog:
    channels:
        - deprecation # Deprecations are logged in the dedicated "deprecation" channel when it existsCode language: PHP (php)

This is added already in the recipe and ships when installing symfony.

Ok, but the handler for this deprecation channel is not configured, so you have to do this yourself.
How? Add this to your monolog config:

Continue reading “Symfony deprecation log channel”

Converting umlaute with symfony String component

There are multiple PHP native ways to convert umlaute and other special characters to ascii save formats, but most of them i experience insufficient in the one or other way.

Since i am using symfony anyway i can use the String component which has a handy slugger which converts any characters into safe ascii characters.
It is very flexible and can be customized with locales, custom replacements and closures.

$umlautString = "Müller Meier";
$slugger = new Symfony\Component\String\Slugger\AsciiSlugger('de');
$slugger->slug($umlautString, $seperator = ' ')->toString();
echo $umlautString; // mueller meierCode language: PHP (php)

I guess this will become my go-to method resp. slugger to convert umlaute in any PHP application.

Continue reading “Converting umlaute with symfony String component”

Class “Psr\Log\Test\TestLogger” not found

The psr/log package used to have not only the Interface for PSR-3 Logger, but also actual implementations of the interface like the TestLogger.
The TestLogger could be used as mock for any PSR-3 Logger in your test cases.

However from v3 the TestLogger was removed, so that the psr/log package would focus solely on the Interface.

If you used the TestLogger in your project and you or some of your dependencies upgraded psr/log to >= v3 you most likely saw this error:

Class "Psr\Log\Test\TestLogger" not found

Continue reading “Class “Psr\Log\Test\TestLogger” not found”