Error message

  • Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in _menu_load_objects() (line 579 of /var/www/drupal-7.x/includes/
  • Notice: Trying to access array offset on value of type int in element_children() (line 6600 of /var/www/drupal-7.x/includes/
  • Notice: Trying to access array offset on value of type int in element_children() (line 6600 of /var/www/drupal-7.x/includes/
  • Notice: Trying to access array offset on value of type int in element_children() (line 6600 of /var/www/drupal-7.x/includes/
  • Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /var/www/drupal-7.x/includes/

Number Nine, Number Nine…

Published by Matthew Davidson on Sat, 24/04/2021 - 9:55pm in

I swore I'd never go back to web development. I can pinpoint the precise moment.

I was fat, prematurely old, bald, grey bearded, and dancing up and down the hall in my underpants with the phone pressed to my ear, explaining to a "mate's rates" client why the impossible things he was asking for were impossible. It was made all the more infuriating by the fact that this client fancied himself a guru, boasted of his mastery of the dark arts of awk, sed, and groff, and insisted that he sought my assistance only because his time was so in demand elsewhere.

So while he was insisting that he didn't understand why I was being so difficult, and that it should be trivial to create a totally secure system that didn't require his users to supply an email address or remember a password, I lost my rag.

I can't even remember how I resolved that situation. I think I might have given him his deposit back. But I did resolve to call it a day, prise my web developers' shingle from the wall, and dither about for a while.

Dithering about was no less lucrative, and much better for my health, but there's no fighting age, and lately my body has started falling to bits, so suddenly minimum wage physical labour seems less viable. I find myself thinking where's the harm in looking at what's been going on since I've been away?

It's true I spent a miserable decade trying to be a tech entrepreneur in a small town where the prospective client base had a serious problem with literacy. Not technical literacy; I mean just reading and writing.

The only people who around here who can spell are the very many extremely busy tattoo artists, and even that proficiency is limited to names like "Sienna" or "Kaiden".

It's all very endearing to declare a devotion to one's offspring so indelibly, but surely there are times — during moments of consensual physical intimacy, for example — where one doesn't want to be taken out of the moment by a list of offspring running across a pair of shoulder blades or down a hairy forearm. Or maybe the thought of one's rampant fecundity contributes to the mood. Eww. Suburbia is sick. I digress.

I did enjoy becoming quite proficient at my trade, despite the lack of good it did me, and also the camaraderie of the Drupal community. Perhaps if I were to broaden my sights beyond Mount-Druitt-by-the-Sea, I might find this a trade I could ply from the comfort of my cat-drawn bath chair well into my dotage.

So I've decided to dip my arthritic old toes back into the water by migrating my old Drupal 7 personal website to Drupal 9. The plan is to migrate rather than upgrade in place. I would be creating a new, functionally equivalent and/or superior, site and copying the content (such as it is) over. Upgrading, at a distance of this many years is out of the question.

Preparing Your Work Surface

I'm going to start with a development site on my local computer and than try to use modern best practice to deploy on my Linode server.

Well, this is embarrassing. Surprisingly, my days as an IT professional comfortably predate the purchase of my now-rusting hulk of a desktop PC. It doesn't even have Apache on it. And all my work documentation, including my LAMP install checklist was on a long-gone Drupal site. I'm a hoarder, and don't often burn my bridges, but when I do I always regret it.

So, I'm going to have to remember/relearn all this:

# apt-get install apache
# apt-get install php
# apt-get install mariadb-server mariadb-client

And I at least get the Debian Apache placeholder page:

Look how lovely and calming it is. It should never change. I think I want it on my grave stone. It says, in essense, "Don't feel sad or anxious. Everything is alright."

Surprisingly, Moore's Law has caught up with Drupal, and so Debian 10's default PHP memory allotment is fine. Nevertheless I know I'm going to have to tweak limits when I get to coping with image files, so to pre-empt the urge to hack php.ini:

# cd /etc/php
# touch local.ini
# ln -s /etc/php/local.ini /etc/php/7.3/apache2/conf.d/10-local.ini

So any custom settings go in /etc/php/local.ini, and when PHP is upgraded, I just have to remember to create another symlink of my custom settings for the new version, rather than be prompted to investigate changes to php.ini on every `apt-get dist-upgrade`. (Who doesn't just cross their fingers and opt to keep the current version whenever that happens?)


I'm a Debian person. I like installing software from Debian via apt. I don't like installing binaries from tarballs, much less compiling from source. Really uncomfortable about the idea of layering another package management system on top of apt.

That said, Composer now does the package management for Drupal modules that was hitherto done by our own command line Swiss Army knife, drush. Why reinvent the wheel when you can hand that job over to something that the wider PHP developer community is already using? I'm okay with that.

Also, why not use Composer to install Drupal itself? Yeah… okay.

Debian 10 ships with Composer v1. When you try to use that to install anything, the package repository fires off a warning message, and because I'm skittish, I comply, and manually install Composer v2 according to instructions. With one wrinkle: anything I install that hasn't come from an apt package repository, I put in /opt and symlink the executable to /usr/bin. That way, I always know that anything I use which came from a from a source not in /etc/apt/sources.list can be found there. I love a bit of en passant documentation.

Drupal 9

As I said, I no longer have my old notes, and the official Drupal documentation (which is a mess; some things never change - at least not for the better) mostly refers to Drupal 8 rather than Drupal 9. This isn't quite the offense it seems to be at first glance, as due to Drupal's new continuous upgrades policy, Drupal 9 is just Drupal 8 minus deprecated code. New features get added incrementally with each minor version release, so there is no vast chasm of incompatibility between Drupal 8 and 9 (or later) as there was between Drupal 7 and 8 (and earlier).

So I decide to sally forth in total ignorance with the intention of remedying deficiencies in system requirements as and when they arise, thus:

$ cd /var/www
$ composer create-project drupal/recommended-project
drupal/core 9.1.7 requires ext-dom * -> it is missing from your system. Install or enable PHP's dom extension.

Okay, so:

# apt-get install php-xml
$ composer create-project drupal/recommended-project
drupal/core 9.1.7 requires ext-gd * -> it is missing from your system. Install or enable PHP's gd extension.

 Right, I'm fed up with this already. I go back to the documentation to find the PHP modules I need for a basic install and:

# apt install php-gd
# apt install php-curl
# apt install php-mbstring


$ composer create-project drupal/recommended-project
Congratulations, you’ve installed the Drupal codebase 
from the drupal/recommended-project template!

And sure enough, there's a whole mess o' Drupal in /var/www now. Awesome. Just need an empty database and some Apache configuration.

PHPMyAdmin didn't make it into Debian 10, for some reason, though it is in testing/unstable.

I can't be doing with composing my own SQL queries. My brain is stubbornly multi-dimensional, and refuses to serialise. I love that SQL exists in ubiquity, and admire people who live and prosper in a world of LEFT INNER JOINs and so on, but it's not for me. Not to worry, Debian Backports to the rescue, so I can now create an empty database without breaking my brain.

I'm almost certain that the documentation on used to have a sample Apache virtual host configuration template, but I'm blowed if I can find it, so I cheat and copy from my current site. What I put in /etc/apache2/sites-available/ looks something like this:

<VirtualHost *:80>
        ServerAdmin me@my.domain
        RewriteEngine on
        RewriteCond  %{HTTP_HOST}  !^          [NC]
        RewriteCond  %{HTTP_HOST}  !^$
        RewriteRule  ^/(.*)$1  [L,R]
        DocumentRoot /var/www/
        <Directory />
                Options FollowSymLinks
                AllowOverride None
        <Directory /var/www/>
                RewriteEngine on
                RewriteBase /
                RewriteCond %{REQUEST_FILENAME} !-f
                RewriteCond %{REQUEST_FILENAME} !-d
                RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        ErrorLog /var/log/apache2/error.log
        LogLevel warn
        CustomLog /var/log/apache2/access.log combined
        ServerSignature On

Then it's:

# a2enmod rewrite
# a2ensite
# systemctl restart apache2

And I can finally fire up a web browser and go through the familiar Drupal web install process. I opt for the "minimal" install, which is no exaggeration:

That is well minimal. This is the new "Stark" theme, which does not fail to live up to its name.

Switching to the new "Olivero" theme feels a bit more comfortable:

Still, I can't get around without the trusty old Admin Toolbar module so let's have Composer download that for us:

$ composer require drupal/admin_toolbar
Using version ^3.0 for drupal/admin_toolbar
./composer.json has been updated
Running composer update drupal/admin_toolbar
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking drupal/admin_toolbar (3.0.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Downloading drupal/admin_toolbar (3.0.0)
  - Installing drupal/admin_toolbar (3.0.0): Extracting archive
Package doctrine/reflection is abandoned, you should avoid using it. Use roave/better-reflection instead.
Generating autoload files

Using the web UI, I enable that module and also the Claro administrative theme and holy cow, this is starting to look like Drupal:


While I can now install bits of Drupal with Composer, I'm still missing the ability to actually enable/disable/configure those bits from the command line. For that I need Drush, which sounds like a yeast infection, but I think originally implied "Drupal shell":

$ composer require drush/drush

Drush now lives as a per-site instance under the Drupal root as vendor/bin/drush, which is a pain to type, so there's a wrapper script which will find the the instance appropriate to your current working directory:

# cd /opt
# wget
# chmod 755 drush.phar
# ln -s /opt/drush.phar /usr/bin/drush

So now I can go:

$ drush status
 Drupal version   : 9.1.7                                                
 Site URI         : http://default                                       
 DB driver        : mysql                                                
 DB hostname      : localhost                                            
 DB port          : 3306                                                 
 DB username      : mjdidau                                              
 DB name          : mjdidau                                              
 Database         : Connected                                            
 Drupal bootstrap : Successful                                           
 Default theme    : olivero                                              
 Admin theme      : claro                                                
 PHP binary       : /usr/bin/php7.3                                      
 PHP config       : /etc/php/7.3/cli/php.ini                             
 PHP OS           : Linux                                                
 Drush script     : /opt/drush.phar                                      
 Drush version    : 10.4.3                                               
 Drush temp       : /tmp                                                 
 Drush configs    : /var/www/
 Install profile  : minimal                                              
 Drupal root      : /var/www/                         
 Site path        : sites/default                                        
 Files, Public    : sites/default/files                                  
 Files, Temp      : /tmp

The full list of drush commands used to be had via `drush help`. I always derived some degree of confessional satisfaction from regularly typing `drush help|less`. `drush help` is now reserved for getting help on a particular command; to see all currently-available commands, one must use `drush list`, which when paginated is also quite appropriate.

There are approximately two people on the planet who will be astonished to find from the above that I'm installing a site in sites/default. I was always an advocate for Drupal's multisite capacity; the ability to run multiple websites from a single Drupal install, all sharing the same codebase, core and "contributed" modules and themes. You'd have each site's files, custom modules or themes sitting under sites/

In principle it sounds wonderful. In practice, it never quite worked. Imagine upgrading two sites at once. Then imagine upgrading a dozen sites at once. I never had, for instance, one single Drupal 6 instance. I had a Drupal 6.0 instance and a Drupal 6.1, and a Drupal 6.2, and so on, and I would shift sites one by one up the ladder as necessity dictated.

Ironically, the new Drupal versioning/upgrading regime makes that dream more realistic, but Moore's law has caught up. Server space is so much cheaper than the cost of even my labour that the most trivial website can afford the overhead of all those files it could easily be sharing with others.

That overhead is not trivial. In terms of disk usage, this new, entirely content-free website, currently looks like this:

$ du -chs *
4.0K    composer.json
224K    composer.lock
38M    vendor
128M    web
166M    total

A hundred and sixty-six meg of nothing is pretty extreme. Better start adding some content, I suppose…