diff --git a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md index fb1825ca..a8445813 100644 --- a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md +++ b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md @@ -1,7 +1,8 @@ --- title: "Using DDEV to spin up a legacy PHP application" pubDate: 2025-05-30 -#modifiedDate: 2025-05-30 +modifiedDate: 2026-09-24 +modifiedComment: "Updated for current DDEV: the old `db` image now follows the installed DDEV version instead of a fixed one, the entrypoint patch matches the current `docker-entrypoint.sh`, and `x-ddev` keys label the services in `ddev describe`." summary: How to use DDEV with outdated PHP and MySQL versions author: Garvin Hicking featureImage: @@ -30,7 +31,7 @@ I quickly faced three major issues: Thanks to the outstanding DDEV support on Discord, I was quickly able to find a way with minimal effort, just by creating very small custom, additional docker-compose YAML files. -One advantage (of many) of using DDEV instead the underlying Docker Compose is that so many things are pre-configured and "just work". So I really did not want to migrate everything to Docker Compose on my own, do my custom routing, PHP-FPM integration and whatnot. +One advantage (of many) of using DDEV instead of the underlying Docker Compose is that so many things are pre-configured and "just work". So I really did not want to migrate everything to Docker Compose on my own, do my custom routing, PHP-FPM integration and whatnot. Just being able to "bait and switch" the PHP and DB container with a different base Docker image was all that was needed for me: @@ -38,45 +39,67 @@ Just being able to "bait and switch" the PHP and DB container with a different b I created the base `~/legacyphp/.ddev/config.yaml` file manually inside my `~/legacyphp` project directory, setting `legacyphp` as the project name. -Note that I configured PHP and MySQL versions that are supported by DDEV for this first: +MySQL 8.0 here is only a placeholder that DDEV accepts. Step 2 replaces it with MySQL 5.5. There is no `php_version`, because the PHP version in the `web` container does not matter: Step 3 adds a separate container for PHP 5.3. ```yaml +# .ddev/config.yaml name: legacyphp type: php docroot: htdocs -php_version: "8.3" webserver_type: apache-fpm database: type: mysql version: "8.0" +hooks: + pre-start: + - exec-host: | + DDEV_DB_LEGACY_IMAGE=$(echo "$DDEV_DBIMAGE" | sed 's/mysql-8.0/mysql-5.5/') + ddev dotenv set .ddev/.env.db.local --ddev-db-legacy-image="$DDEV_DB_LEGACY_IMAGE" >/dev/null ``` +The [`pre-start` hook](https://docs.ddev.com/en/stable/users/configuration/hooks/) keeps this setup up to date. `$DDEV_DBIMAGE` is the `db` image DDEV is about to use, for example `ddev/ddev-dbserver-mysql-8.0:v1.25.4`. Changing `mysql-8.0` to `mysql-5.5` gives the old image from the same DDEV release, so it stays in sync when you upgrade DDEV. + +[`ddev dotenv set`](https://docs.ddev.com/en/stable/users/usage/commands/#dotenv-set) saves that value in `.ddev/.env.db.local`, so the compose file in Step 2 can use it as `${DDEV_DB_LEGACY_IMAGE}`. + ## Step 2: Rewire DB +:::note +Only needed on ARM64. +::: + Next I created the very small file `~/legacyphp/.ddev/docker-compose.db.yaml` in the same directory next to `config.yaml`: ```yaml +# .ddev/docker-compose.db.yaml services: db: platform: linux/amd64 build: args: - BASE_IMAGE: ddev/ddev-dbserver-mysql-5.5:v1.24.6 + BASE_IMAGE: ${DDEV_DB_LEGACY_IMAGE} + #image: ${DDEV_DB_LEGACY_IMAGE}-${DDEV_SITENAME}-built entrypoint: - sh - -c - | cp /docker-entrypoint.sh ~/docker-entrypoint.sh - sed -i '157s|.*|if false; then|' ~/docker-entrypoint.sh - sed -i '175s|.*|echo mysql_8.0 >/var/lib/mysql/db_mariadb_version.txt|' ~/docker-entrypoint.sh + # report MySQL 8.0 so the version check passes + sed -i 's|server_db_version=.*|server_db_version=mysql_8.0|g' ~/docker-entrypoint.sh + sed -i 's|database_db_version=.*|database_db_version=mysql_8.0|g' ~/docker-entrypoint.sh + # but keep the real MySQL 5.5 configuration + sed -i 's|BEST_MATCH=.*|BEST_MATCH=/etc/mysql/version-conf.d/mysql_5.5.cnf.txt|g' ~/docker-entrypoint.sh exec ~/docker-entrypoint.sh + x-ddev: + describe-info: "Using mysql:5.5" ``` -Three things are noteworthy: +A few things are noteworthy: -- Setting `linux/amd64` as the platform will require Rosetta to be available on the macOS ARM64 platform -- The `BASE_IMAGE` is set to a DDEV `db` container of legacy Docker images that are still provided. -- Changing the `entrypoint` is a workaround to prevent DDEV complaining about a mismatching MySQL version after restarting the project. The small script "tricks" the DDEV inspection into believing, the version matches the one configured in `.ddev/config.yaml`. +- Setting `linux/amd64` as the platform will require Rosetta to be available on the macOS ARM64 platform. +- `BASE_IMAGE` uses the `${DDEV_DB_LEGACY_IMAGE}` value from the hook, so the old image always matches your DDEV version. +- The `image` name stops the build from overwriting DDEV's own `ddev-dbserver-mysql-8.0` image on your machine. +- Changing the `entrypoint` stops DDEV from complaining about a version mismatch when you restart the project. The container runs MySQL 5.5, but has to look like the MySQL 8.0 set in `.ddev/config.yaml`. +- [`x-ddev`](https://docs.ddev.com/en/stable/users/extend/custom-docker-services/#customizing-ddev-describe-output) adds a note to `ddev describe`, so you can see the real version even though `.ddev/config.yaml` says 8.0. ## Step 3: Rewire PHP @@ -85,6 +108,7 @@ Using a different PHP version is just a few lines more work, because we are not This is done via the file `~/legacyphp/.ddev/docker-compose.php.yaml`: ```yaml +# .ddev/docker-compose.php.yaml services: php: container_name: ddev-${DDEV_SITENAME}-php @@ -106,19 +130,32 @@ services: - NEW_GID=${DDEV_GID} - DDEV_PHP_VERSION - IS_DDEV_PROJECT=true + x-ddev: + describe-url-port: | + PHP 5.3 shell: + ddev ssh -s php -u devilbox + describe-info: "Using PHP 5.3" + ssh-shell: bash web: + x-ddev: + describe-url-port: | + PHP 5.3 shell: + ddev ssh -s php -u devilbox + describe-info: "Using PHP 5.3" depends_on: - php ``` Note here that we use `devilbox/php-fpm` with our needed version, and a bind-mount takes care the PHP container can access our main project root directory. -A special mount of `~/legacyphp/.ddev/php/` is included so that we can control the `php.ini` configuration, if needed. For example you could disable the OPCache+APC in case you're doing some legacy benchmarking that should not be falsified via caching, I created a very small file `~/legacyphp/.ddev/php/php.ini` file with the contents: +The `x-ddev` blocks only change what `ddev describe` shows, but they save confusion later, because otherwise nothing hints that PHP runs in another container. The longer note goes in `describe-url-port` because that column is wider than `describe-info`. `ssh-shell: bash` is useful too, because custom services use `sh` by default. + +A special mount of `~/legacyphp/.ddev/php/` is included so that we can control the `php.ini` configuration, if needed. For example, you could disable OPcache and APC when doing legacy benchmarking that caching would distort. I created a very small `~/legacyphp/.ddev/php/php.ini` file with the contents: ```ini -# This is an example. -# apc.enabled=Off -# opcache.enable=Off +; .ddev/php/php.ini +apc.enabled=Off +opcache.enable=Off ``` ## Step 4: Utilize the PHP container with an Apache proxy @@ -126,6 +163,7 @@ A special mount of `~/legacyphp/.ddev/php/` is included so that we can control t To execute PHP with our external PHP Docker image, I created the following file in `~/legacyphp/.ddev/apache/apache-site.conf`: ```apache +# .ddev/apache/apache-site.conf RewriteEngine On RewriteCond %{HTTP:X-Forwarded-Proto} =https @@ -134,6 +172,9 @@ To execute PHP with our external PHP Docker image, I created the following file SetEnvIf X-Forwarded-Proto "https" HTTPS=on Alias "/phpstatus" "/var/www/phpstatus.php" + + SetHandler "proxy:unix:/var/run/php/php-fpm.sock|fcgi://localhost" + DocumentRoot /var/www/html/htdocs AllowOverride All @@ -164,9 +205,11 @@ Your MySQL connection id is 5 Server version: 5.5.62-log MySQL Community Server (GPL) ``` +`ddev describe` shows the `x-ddev` notes from Steps 2 and 3, so you can see the real PHP and MySQL versions next to the services that run them. + ## Caveats -You can enter the PHP Docker container with a command like `docker exec -it -u devilbox ddev-legacyphp-php bash` if you need/want to execute PHP commands on shell-level, because the regular `web` container will run with the more recent PHP 8.3 version. +You can enter the PHP Docker container with `ddev ssh -s php -u devilbox` if you need to execute PHP commands at the shell level, because the regular `web` container runs a much more recent PHP version. So if you need to perform composer CLI calls, be sure to do this within the matching PHP container. Another thing to pay attention to is that if you for example want to utilize Mailpit with TYPO3's mail configuration, you can not use `localhost:1025` as an SMTP server. `localhost` in PHP's case will be that devilbox PHP container, and not the DDEV web container. Instead you need to setup `web:1025` as the hostname.