php

Fixing nginx Error: Undefined constant PDO::MYSQL_ATTR_USE_BUFFERED_QUERY

I install a lot of Drupal sites day to day, especially when I'm doing dev work.

In the course of doing that, sometimes I'll be working on infrastructure—whether that's an Ansible playbook to configure a Docker container, or testing something on a fresh server or VM.

In any case, I run into the following error every so often in my Nginx error.log:

"php-fpm" nginx Error: Undefined constant PDO::MYSQL_ATTR_USE_BUFFERED_QUERY

The funny thing is, I don't have that error when I'm running CLI commands, like vendor/bin/drush, and can even install and manage the Drupal site and database on the CLI.

The problem, in my case, was that I had applied php-fpm configs using Ansible, but in my playbook I hadn't restarted php-fpm (in my case, on Ubuntu 22.04, php8.3-fpm) after doing so. So FPM was running with outdated config and didn't know that the MySQL/MariaDB drivers were even present on the system.

Rate limiting requests per IP address in Nginx

Just wanted to post this here, since I've had to do this from time to time, and always had to read through the docs and try to build my own little example of it...

If you're trying to protect an Nginx server from a ton of traffic (especially from a limited number of IP addresses hitting it with possibly DoS or DDoS-type traffic), and don't have any other protection layer in front, you can use limit_req to rate limit requests at whatever rate you choose (over a given time period) for any location on the server.

# Add this to your virtual host config file.
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

# Later, in a `server` block:
server {
    location ~ \.php$ {
        limit_req zone=mylimit;
        ...
    } 
    ...
}

I have had to do this sometimes when I noticed a few bad IPs attacking my servers. You can adjust the rate and zone settings to your liking (the above settings limit requests to any PHP script to 10 per second over a 10 minute period).

Watch out if composer update keeps replacing a dependency

Recently, while working on the codebase for this very site, I tried running composer update to upgrade from Drupal 8.8.4 to 8.8.5. Apparently I did this at just the wrong time, as there was an issue with Drupal's dependencies in 8.9.x-dev which caused it to be selected as the upgrade candidate, and the default drupal/core-recommended Composer setting was to allow dev stability, so my site got updated to 8.9.x-dev, which was a bit of a surprise.

"No worries," I thought, "I use git, so I'm protected!" A git reset later, then change my composer.json to use "minimum-stability": "stable", and all is well with the world, right?

Well, no. You see, the problem is Drupal 8.9.x changed from an abandoned package, zendframework/zend-diactoros, to a new package, laminas/laminas-diactoros, that replaces the abandoned package.

Install Drupal Coder and PHP CodeSniffer to your Drupal project to lint PHP code

In the official Coder Sniffer install guide on Drupal.org, it recommends installing Coder and the Drupal code sniffs globally using the command:

composer global require drupal/coder

I don't particularly like doing that, because I try to encapsulate all project requirements within that project, especially since I'm often working on numerous projects, some on different versions of PHP or Drupal, and installing things globally can cause things to break.

So instead, I've done the following (see this issue) for my new JeffGeerling.com Drupal 8 site codebase (in case you haven't seen, I'm live-streaming the entire migration process!):

Quick and dirty way to strip ANSI terminal output in PHP

From time to time, I write up little PHP scripts to run a command via exec() and dump the output to the screen. Most of the time these are quick throwaway scripts, but sometimes I need them to persist a little longer, or share the output with others, so I make them look a little nicer.

One annoying thing that happens if you interact with CLI tools that generate colorized output is that PHP doesn't translate the terminal (ANSI) color codes into HTML colors, so you end up looking at output like:

Kubernetes master is running at https://10.96.0.1:443

Sensio Labs maintains an excellent ansi-to-html PHP library, and if you're building anything that should be persistent or robust, you should use it. But I wanted a one-line solution for one simple script I was working on, so I spent a couple minutes building out the following regex:

Drupal VM 5.1 ('Recognizer') brings PHP 7.4 support

PHP 7.4.0 running on Drupal VM with Drupal 8's status report page

Drupal VM 5.1.0 was just released (release name Recognizer), and the main feature is PHP 7.4 support; you can now begin running and testing your Drupal sites under PHP 7.4 to check for any incompatibilities.

PHP 7.4 includes some new features like typed properties, arrow functions, and opcache preloading which could help with certain types of code or site deployments (I'm interested to see if opcache preloading could help the startup time of Drupal inside container environments like Kubernetes!).

Running 'php artisan schedule:run' for Laravel in Kubernetes CronJobs

I am working on integrating a few Laravel PHP applications into a new Kubernetes architecture, and every now and then we hit a little snag. For example, the app developers noticed that when their cron job ran (php artisan schedule:run), the MySQL container in the cluster would drop an error message like:

2019-03-27T16:20:05.965157Z 1497 [Note] Aborted connection 1497 to db: 'database' user: 'myuser' host: '10.0.76.130' (Got an error reading communication packets)

In Kubernetes, I had the Laravel app CronJob set up like so:

Cleaning up after adding files in Drupal Behat tests

I've been going kind of crazy covering a particular Drupal site I'm building in Behat tests—testing every bit of core functionality on the site. In this particular case, a feature I'm testing allows users to upload arbitrary files to an SFTP server, then Drupal shows those filenames in a streamlined UI.

I needed to be able to test the user action of "I'm a user, I upload a file to this directory, then I see the file listed in a certain place on the site."

These files are not managed by Drupal (e.g. they're not file field uploads), but if they were, I'd invest some time in resolving this issue in the drupalextension project: "When I attach the file" and Drupal temporary files.

Since they are just random files dropped on the filesystem, I needed to:

The best way to get the full PHP version string

Recently, to automate building, tagging, and pushing my geerlingguy/php-apache Docker Hub image (see this issue), I needed to find a way to reliably determine the PHP major.minor.release version string. You'd think this would be simple.

Well, using Docker, I would run the image and then try:

# php --version
PHP 7.3.0-1+0~20181206202713.23+stretch~1.gbp076afd (cli) (built: Dec  6 2018 20:27:14) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.0-1+0~20181206202713.23+stretch~1.gbp076afd, Copyright (c) 1999-2018, by Zend Technologies

That's great; it outputs the version right at the start. But there are a few problems here:

Drupal startup time and opcache - faster scaling for PHP in containerized environments

Lately I've been spending a lot of time working with Drupal in Kubernetes and other containerized environments; one problem that's bothered me lately is the fact that when autoscaling Drupal, it always takes at least a few seconds to get a new Drupal instance running. Not installing Drupal, configuring the database, building caches; none of that. I'm just talking about having a Drupal site that's already operational, and scaling by adding an additional Drupal instance or container.

One of the principles of the 12 Factor App is:

IX. Disposability

Maximize robustness with fast startup and graceful shutdown.

Disposability is important because it enables things like easy, fast code deployments, easy, fast autoscaling, and high availability. It also forces you to make your code stateless and efficient, so it starts up fast even with a cold cache. Read more about the disposability factor on the 12factor site.