Drupal 9 with Composer on OpenBSD
Tags: drupal, cms, openbsd, symfony
Published: 2021-05-25 12:13:46

## Summary

[Symfony](https://symfony.com/) is one of [PHP](https://www.php.net/) web frameworks, and my favorite one, because it is clearly classified and functional, is designed with security in mind, is accompanied with useful helpers like [MakeBundle](https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html), and also provides great official [documentation](https://symfony.com/doc/current/index.html). [Drupal](https://drupal.org/), one of content management systems aka CMS, is based on Symfony.
When I try to build some web service, they are usually good candidates. Symfony gives primitive possibility of creating flexible data structure and user interface. Conversely, Drupal offers robust components to build service containing authentication, user roles and functional fields.
This post shows how I built a server of Drupal 9, the latest stable, as a trial.
Besides, [Drupal Association](https://www.drupal.org/u/drupal-association) wrote about ["Why We Use Symfony"](https://www.drupal.org/association/supporters/blog/why-we-use-symfony)

### Environment

- OS: [OpenBSD](https://www.openbsd.org/) 6.9
- App: PHP 7.4
- Package Manager: [Composer](https://getcomposer.org/) 2.0
- Database: [MariaDB](https://mariadb.org/) 10.5
- CMS: Drupal 9

## Tutorial

### Database

Connect to a database server.

```console
$ mysql -u root -p
```

Create a database and a user.

```sql
CREATE DATABASE <database> \
    CHARACTER SET utf8mb4 \
    COLLATE utf8mb4_unicode_ci;
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES \
    ON <database>.* TO \
    <dbuser>@'localhost' \
    IDENTIFIED BY '<dbpassword>';
FLUSH PRIVILEGES;
```

### App

#### Environment preparation

Get PHP from [Ports](https://www.openbsd.org/faq/ports/ports.html) collection.

```console
$ doas pkg_add php
$ # choose php-7.4
```

Also required PHP extensions.

```console
$ doas pkg_add php-curl php-gd php-intl php-zip php-mysqli php-pdo_mysql # + php-[...] if necessary
$ # choose those of php-7.4
```

Then install other packages if necessary.

```console
$ # install other packages if necessary
$ doas pkg_add unzip composer git
```

#### Composer task

First, verify composer is bound to php 7.4, the target version.

```console
$ composer -vvv about
Failed to initialize global composer: Composer could not find the config file: /.../.composer/composer.json
To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section
Running 2.0.12 (2021-04-01 10:14:59) with PHP 7.4.19 on OpenBSD / 6.9
Failed to initialize global composer: Composer could not find the config file: /.../.composer/composer.json
To initialize a project, please create a composer.json file as described in the https://getcomposer.org/ "Getting Started" section
Composer - Dependency Manager for PHP
Composer is a dependency manager tracking local dependencies of your projects and libraries.
See https://getcomposer.org/ for more information.
```

OK. Output is "Running 2.0.12 (2021-04-01 10:14:59) with PHP 7.4.19 on OpenBSD / 6.9".
Then run `composer` to create a Drupal project.

```console
$ composer create-project drupal/recommended-project <drupal-dir>
```

Besides, in case `composer` is not bound to php-7.4, `composer.phar` is available:

```console
$ # alternatively
$ #php-7.4 /usr/local/libexec/composer.phar create-project drupal/recommended-project <drupal-dir>
```

The output was like this (partially omitted):

```console
Creating a "drupal/recommended-project" project at "./drupal"
Installing drupal/recommended-project (9.1.8)
  - Downloading drupal/recommended-project (9.1.8)
  - Installing drupal/recommended-project (9.1.8): Extracting archive
Created project in /var/www/.../<drupal-dir>
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 61 installs, 0 updates, 0 removals
  - Downloading composer/installers (v1.11.0)
  - Downloading symfony/...
  ...
  - Downloading pear/archive_tar (1.4.13)
  - Downloading drupal/core (9.1.8)
  - Installing composer/installers (v1.11.0): Extracting archive
  - Installing drupal/core-composer-scaffold (9.1.8): Extracting archive
  - Installing drupal/core-project-message (9.1.8): Extracting archive
  - Installing typo3/phar-stream-wrapper (v3.1.6): Extracting archive
  - Installing symfony/polyfill-mbstring (v1.20.0): Extracting archive
  - Installing symfony/polyfill-ctype (v1.20.0): Extracting archive
  - Installing twig/twig (v2.14.1): Extracting archive
  ...
  - Installing psr/http-message (1.0.1): Extracting archive
  ...
  - Installing doctrine/annotations (1.11.1): Extracting archive
  - Installing doctrine/reflection (1.2.2): Extracting archive
  - Installing composer/semver (3.2.2): Extracting archive
  - Installing asm89/stack-cors (1.3.0): Extracting archive
  - Installing drupal/core (9.1.8): Extracting archive
  - Installing drupal/core-recommended (9.1.8)
Package doctrine/reflection is abandoned, you should avoid using it. Use roave/better-reflection instead.
Generating autoload files
38 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Scaffolding files for drupal/core:
  - Copy [project-root]/.editorconfig from assets/scaffold/files/editorconfig
  - Copy [project-root]/.gitattributes from assets/scaffold/files/gitattributes
  - Copy [web-root]/.csslintrc from assets/scaffold/files/csslintrc
  - Copy [web-root]/.eslintignore from assets/scaffold/files/eslintignore
  - Copy [web-root]/.eslintrc.json from assets/scaffold/files/eslintrc.json
  - Copy [web-root]/.ht.router.php from assets/scaffold/files/ht.router.php
  - Copy [web-root]/.htaccess from assets/scaffold/files/htaccess
  - Copy [web-root]/example.gitignore from assets/scaffold/files/example.gitignore
  - Copy [web-root]/index.php from assets/scaffold/files/index.php
  - Copy [web-root]/INSTALL.txt from assets/scaffold/files/drupal.INSTALL.txt
  - Copy [web-root]/README.txt from assets/scaffold/files/drupal.README.txt
  - Copy [web-root]/robots.txt from assets/scaffold/files/robots.txt
  - Copy [web-root]/update.php from assets/scaffold/files/update.php
  - Copy [web-root]/web.config from assets/scaffold/files/web.config
  - Copy [web-root]/sites/README.txt from assets/scaffold/files/sites.README.txt
  - Copy [web-root]/sites/development.services.yml from assets/scaffold/files/development.services.yml
  - Copy [web-root]/sites/example.settings.local.php from assets/scaffold/files/example.settings.local.php
  - Copy [web-root]/sites/example.sites.php from assets/scaffold/files/example.sites.php
  - Copy [web-root]/sites/default/default.services.yml from assets/scaffold/files/default.services.yml
  - Copy [web-root]/sites/default/default.settings.php from assets/scaffold/files/default.settings.php
  - Copy [web-root]/modules/README.txt from assets/scaffold/files/modules.README.txt
  - Copy [web-root]/profiles/README.txt from assets/scaffold/files/profiles.README.txt
  - Copy [web-root]/themes/README.txt from assets/scaffold/files/themes.README.txt
  * Homepage: https://www.drupal.org/project/drupal
  * Support:
    * docs: https://www.drupal.org/docs/user_guide/en/index.html
    * chat: https://www.drupal.org/node/314178
                                                         
  Congratulations, you’ve installed the Drupal codebase  
  from the drupal/recommended-project template!          
                                                         

Next steps:
  * Install the site: https://www.drupal.org/docs/8/install
  * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html
  * Get support: https://www.drupal.org/support
  * Get involved with the Drupal community:
      https://www.drupal.org/getting-involved
  * Remove the plugin that prints this message:
      composer remove drupal/core-project-message
  * Homepage: https://www.drupal.org/project/drupal
  * Support:
    * docs: https://www.drupal.org/docs/user_guide/en/index.html
    * chat: https://www.drupal.org/node/314178
```

#### Additional configuration of directory and permission

A bit more work in command line.

```console
$ cd <drupal-dir>

$ doas chown -R www: ./web/{sites/default, modules, themes}

$ mkdir ./config
$ doas chown -R www: ./config
```

### PHP-FPM

`php74_fpm` daemon is running? If not, use `rcctl enable/start php74_fpm`. My past post:

{% post nabbisen/php-fpm-on-openbsd-2iof %}

### Web

Set up web server to enable the flow:

```
Client --(request)--> Web (httpd) --> Gateway (PHP-FPM) --> App = Drupal
```

The configuration file of OpenBSD [httpd](https://man.openbsd.org/httpd.8) is [/etc/httpd.conf](https://man.openbsd.org/httpd.conf.5):

```console
$ doas nvim /etc/httpd.conf
```

Add the definitions below (and more as needed):

```apache
server "<fqdn>" {
        listen on $ext_addr tls port 443
        tls {
                certificate     "/etc/ssl/<fqdn>.pem"
                key             "/etc/ssl/private/<fqdn>.key"
        }
        log {
                access  "<fqdn>-access.log"
                error   "<fqdn>-error.log"
        }

        root "/<drupal-dir>/web" # (with chroot)
        directory index index.php

        location "/*.php" { 
                fastcgi socket "/run/php-fpm.sock"
        }
        location "/*.php[/?]*" { 
                fastcgi socket "/run/php-fpm.sock"
        } 
        location match "/[^\.]+/?$" {
                fastcgi socket "/run/php-fpm.sock"
                request rewrite "/index.php/%1"
        }
}
```

Remember __`root` has to end with `/web`__.
Load the settings.

```console
$ doas rcctl restart httpd
```

### Web installer

With web browser, connect to https://\<fqdn\>, which will be redirected to https://\<fqdn\>/core/install.php.
Follow the messages as below.

#### Language

![Language](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvai88ymepl9mdswqsfv.png)

#### Installation profile

![Installation profile](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5lk8p9wi3nvw15dgx1qj.png)

#### Database configuration

![Database configuration 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lxbm9tw5mqm6fwyaomew.png)
![Database configuration 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/styh9xx2pl9pp1olv66h.png)

#### Site configuration

![Site configuration 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwmgpidkw57hs58vagqs.png)
![Site configuration 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06p16fnj7don2ak3k4qq.png)
![Site configuration 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zu2aayowgnvituu51c9.png)

#### Completed

![Completed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz6t9ga0kqcoz38bcvhl.png)

I have make trials to manage the site since then. I feel I have got along with Drupal 9. Thus, so far, so good🙂