Repository: mailpoet/mailpoet
Pull request #6127: Add PHP 8.3 images and use it in automated tests [MAILPOET-6297]
Author: login: lysyjan
Created: 2025-03-10T10:54:07Z | Merged: 2025-03-12T06:51:03Z

## Description

This PR adds dev configuration for PHP 8.3 and uses this version also in automated tests.

## Code review notes

I have some questions and would like to discuss them.
- Should I configure using PHP 8.3 by default for local development?
- Is there something that I overlooked and should be checked?

## QA notes

_N/A_

## Linked PRs

https://github.com/mailpoet/mailpoet-premium/pull/944
https://github.com/mailpoet/wordpress-images/pull/52

## Linked tickets

[MAILPOET-6297]

## After-merge notes

_N/A_

## Tasks

- [ ] I followed [best practices](https://codex.wordpress.org/I18n_for_WordPress_Developers) for translations
- [ ] I added sufficient test coverage
- [ ] I embraced TypeScript by either creating new files in TypeScript or converting existing JavaScript files when making changes

## Preview

[Preview in WordPress Playground](https://account.mailpoet.com/playground/new/branch:research-php-8-3)

_The latest successful build from `research-php-8-3` will be used. If none is available, the link won't work._

[MAILPOET-6297]: https://mailpoet.atlassian.net/browse/MAILPOET-6297?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 75ac1d8119..5632fe9067 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -104,7 +104,7 @@ executors:
   wpcli_php_latest:
     <<: *default_job_config
     docker:
-      - image: mailpoet/wordpress:8.2_20241126.1
+      - image: mailpoet/wordpress:8.3_20250305.1
 
   wpcli_php_mysql_oldest:
     <<: *default_job_config
@@ -115,7 +115,7 @@ executors:
   wpcli_php_mysql_latest:
     <<: *default_job_config
     docker:
-      - image: mailpoet/wordpress:8.2_20241126.1
+      - image: mailpoet/wordpress:8.3_20250305.1
       - image: cimg/mysql:8.0
         command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_520_ci
 
@@ -1034,7 +1034,7 @@ workflows:
       - static_analysis:
           <<: *slack-fail-post-step
           name: static_analysis_php8
-          php_version: 80200
+          php_version: 80300
           requires:
             - build
       - security_analysis:
diff --git a/dev/dashboard/index.html b/dev/dashboard/index.html
index ea0d32d003..b29235fbd1 100644
--- a/dev/dashboard/index.html
+++ b/dev/dashboard/index.html
@@ -56,15 +56,6 @@
           <td>App for development</td>
           <td><a href="http://localhost:8002">http://localhost:8002</a></td>
         </tr>
-        <tr>
-          <td>🚥</td>
-          <td>
-            <a href="http://localhost:8003">WordPress Tests</a>
-            [<a href="http://localhost:8003/wp-admin/">wp-admin</a>]
-          </td>
-          <td>App for E2E tests</td>
-          <td><a href="http://localhost:8003">http://localhost:8003</a></td>
-        </tr>
         <tr>
           <td>💾</td>
           <td>
diff --git a/dev/database/create_test_db.sh b/dev/database/create_test_db.sh
deleted file mode 100755
index 41124365af..0000000000
--- a/dev/database/create_test_db.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-set -e
-
-RUNMYSQL="mysql --user=root --password=${MYSQL_ROOT_PASSWORD}"
-
-$RUNMYSQL <<EOSQL
-CREATE DATABASE ${MYSQL_TEST_DATABASE};
-GRANT ALL PRIVILEGES ON ${MYSQL_TEST_DATABASE}.* TO '${MYSQL_USER}'@'%';
-FLUSH PRIVILEGES;
-EOSQL
diff --git a/dev/php82/Dockerfile b/dev/php82/Dockerfile
index 6f7522955b..5c4f6f2d7e 100644
--- a/dev/php82/Dockerfile
+++ b/dev/php82/Dockerfile
@@ -27,7 +27,7 @@ RUN printf "account default\nhost smtp\nport 1025" > /etc/msmtprc
 
 # xdebug build an config
 ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/xdebug.ini
-RUN git clone -b "3.3.1" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
+RUN git clone -b "3.4.2" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
     && docker-php-ext-configure xdebug --enable-xdebug-dev \
     && docker-php-ext-install xdebug \
     && mkdir /tmp/debug
diff --git a/dev/php83/Dockerfile b/dev/php83/Dockerfile
new file mode 100644
index 0000000000..3e38ee73da
--- /dev/null
+++ b/dev/php83/Dockerfile
@@ -0,0 +1,46 @@
+FROM wordpress:php8.3-apache
+
+ARG UID=1000
+ARG GID=1000
+
+# additinal extensions
+RUN apt-get update \
+	&& apt-get install -y git zlib1g-dev libzip-dev zip wget gnupg msmtp libpng-dev gettext subversion \
+	&& \
+    # Install NodeJS, enable Corepack
+    curl -sL https://deb.nodesource.com/setup_19.x | bash - && \
+    apt-get install -y nodejs build-essential && \
+    corepack enable && \
+	\
+	# Install WP-CLI
+	curl -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
+	chmod +x /usr/local/bin/wp && \
+	\
+  # Clean up
+  apt-get clean && \
+  rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+COPY dev/php.ini /usr/local/etc/php/conf.d/php_user.ini
+
+# msmtp config
+RUN printf "account default\nhost smtp\nport 1025" > /etc/msmtprc
+
+# xdebug build an config
+ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/xdebug.ini
+RUN git clone -b "3.4.2" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
+    && docker-php-ext-configure xdebug --enable-xdebug-dev \
+    && docker-php-ext-install xdebug \
+    && mkdir /tmp/debug
+COPY dev/xdebug.ini /tmp/xdebug.ini
+RUN cat /tmp/xdebug.ini >> $XDEBUGINI_PATH
+
+# php extensions
+RUN docker-php-ext-install pdo_mysql
+RUN docker-php-ext-install mysqli
+
+# allow .htaccess files (between <Directory /var/www/> and </Directory>, which is WordPress installation)
+RUN sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
+
+# ensure existing content in /var/www/html respects UID and GID, give Node permissions for Corepack
+RUN chown -R ${UID}:${GID} /var/www/html && \
+  mkdir -p /.node && chown -R ${UID}:${GID} /.node
diff --git a/docker-compose.override.macos-sample.yml b/docker-compose.override.macos-sample.yml
index e6bb551bf9..02c52229af 100644
--- a/docker-compose.override.macos-sample.yml
+++ b/docker-compose.override.macos-sample.yml
@@ -9,11 +9,6 @@ services:
       - nfs-mailpoet:/var/www/html/wp-content/plugins/mailpoet
       - nfs-mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium
 
-  test_wordpress:
-    volumes:
-      - nfs-mailpoet:/var/www/html/wp-content/plugins/mailpoet
-      - nfs-mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium
-
 volumes:
   nfs-wordpress:
     driver: local
diff --git a/docker-compose.yml b/docker-compose.yml
index 865cb07aa0..0241e78ee9 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -12,12 +12,10 @@ services:
     image: mysql:5.7
     volumes:
       - my-datavolume:/var/lib/mysql
-      - ./dev/database/create_test_db.sh:/docker-entrypoint-initdb.d/10-create_test_db.sh
     command: --sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ANSI,ONLY_FULL_GROUP_BY
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
-      MYSQL_TEST_DATABASE: test_wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress
 
@@ -25,7 +23,7 @@ services:
     container_name: mp-wp
     build:
       context: .
-      dockerfile: dev/php82/Dockerfile
+      dockerfile: dev/php83/Dockerfile
       args:
         UID: ${UID:-1000}
         GID: ${GID:-1000}
@@ -60,31 +58,6 @@ services:
       - './packages:/var/www/html/wp-content/plugins/packages'
       - './templates:/var/www/templates'
 
-  test_wordpress:
-    container_name: mp-test-wp
-    build:
-      context: .
-      dockerfile: dev/php81/Dockerfile
-      args:
-        UID: ${UID:-1000}
-        GID: ${GID:-1000}
-    ports:
-      - '8003:80'
-    depends_on:
-      - db
-      - smtp
-    user: ${UID:-1000}:${GID:-1000}
-    environment:
-      WORDPRESS_DB_NAME: test_wordpress
-      WORDPRESS_DB_HOST: db:3306
-      WORDPRESS_DB_USER: wordpress
-      WORDPRESS_DB_PASSWORD: wordpress
-      PHP_IDE_CONFIG: 'serverName=Mailpoet'
-    volumes:
-      - './mailpoet:/var/www/html/wp-content/plugins/mailpoet'
-      - './mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium'
-      - './packages:/var/www/html/wp-content/plugins/packages'
-
   smtp:
     container_name: mp-mailhog
     image: mailhog/mailhog:v1.0.0
diff --git a/tests_env/composer.json b/tests_env/composer.json
index 7e0ed8960c..62ee29e4e2 100644
--- a/tests_env/composer.json
+++ b/tests_env/composer.json
@@ -20,10 +20,12 @@
   },
   "scripts": {
     "post-install-cmd": [
-      "php ./tasks/fix-php82-codeception.php"
+      "php ./tasks/fix-php82-codeception.php",
+      "php ./tasks/fix-php83-codeception.php"
     ],
     "post-update-cmd": [
-      "php ./tasks/fix-php82-codeception.php"
+      "php ./tasks/fix-php82-codeception.php",
+      "php ./tasks/fix-php83-codeception.php"
     ]
   }
 }
diff --git a/tests_env/docker/docker-compose.yml b/tests_env/docker/docker-compose.yml
index 399332c4f9..8c90b90a9c 100644
--- a/tests_env/docker/docker-compose.yml
+++ b/tests_env/docker/docker-compose.yml
@@ -1,6 +1,6 @@
 services:
   codeception_acceptance:
-    image: mailpoet/wordpress:${CODECEPTION_IMAGE_VERSION:-8.2-cli_20241126.1}
+    image: mailpoet/wordpress:${CODECEPTION_IMAGE_VERSION:-8.3-cli_20250304.1}
     depends_on:
       - smtp
       - wordpress
@@ -31,7 +31,7 @@ services:
       PHP_IDE_CONFIG: 'serverName=MailPoetTest'
 
   codeception_integration:
-    image: mailpoet/wordpress:${CODECEPTION_IMAGE_VERSION:-8.2-cli_20241126.1}
+    image: mailpoet/wordpress:${CODECEPTION_IMAGE_VERSION:-8.3-cli_20250304.1}
     depends_on:
       - smtp
       - wordpress
diff --git a/tests_env/tasks/fix-php83-codeception.php b/tests_env/tasks/fix-php83-codeception.php
new file mode 100644
index 0000000000..3477a8c14c
--- /dev/null
+++ b/tests_env/tasks/fix-php83-codeception.php
@@ -0,0 +1,68 @@
+<?php
+
+// throw exception if anything fails
+set_error_handler(function ($severity, $message, $file, $line) {
+  throw new ErrorException($message, 0, $severity, $file, $line);
+});
+
+// Fixes for PHP8.3 Compatibility when Generator classes use dynamic properties which are deprecated
+$codeceptionActionsFind = <<<'CODE'
+    protected $name;
+    protected $settings;
+    protected $modules = [];
+    protected $actions;
+    protected $numMethods = 0;
+CODE;
+
+$codeceptionActorFind = <<<'CODE'
+    protected $settings;
+    protected $modules;
+    protected $actions;
+CODE;
+
+$codeceptionActorReplacement = <<<'CODE'
+    protected $settings;
+    protected $di;
+    protected $moduleContainer;
+    protected $modules;
+    protected $actions;
+CODE;
+
+$codeceptionActionsReplacement = <<<'CODE'
+    protected $name;
+    protected $di;
+    protected $moduleContainer;
+    protected $settings;
+    protected $modules = [];
+    protected $actions;
+    protected $numMethods = 0;
+CODE;
+
+// Development packages
+$replacements = [
+  [
+    'file' => __DIR__ . '/../vendor/codeception/codeception/src/Codeception/Lib/Generator/Actions.php',
+    'find' => [
+      $codeceptionActionsFind
+    ],
+    'replace' => [
+      $codeceptionActionsReplacement,
+    ],
+  ],
+  [
+    'file' => __DIR__ . '/../vendor/codeception/codeception/src/Codeception/Lib/Generator/Actor.php',
+    'find' => [
+      $codeceptionActorFind
+    ],
+    'replace' => [
+      $codeceptionActorReplacement,
+    ],
+  ],
+];
+
+// Apply replacements
+foreach ($replacements as $singleFile) {
+  $data = file_get_contents($singleFile['file']);
+  $data = str_replace($singleFile['find'], $singleFile['replace'], $data);
+  file_put_contents($singleFile['file'], $data);
+}


Reviews:
user: login: costasovo
body: Great job! I added two comments with small suggestions for improvements we could consider.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T11:43:41Z
user: login: costasovo
body: Great job! 

I checked the jobs in Circle CI and didn't notice any issues.
I built the WordPress image locally and ran a couple of random ./do commands and did basic check of admin MailPoet admin pages and didn't see any PHP Notices so I guess we are fine. 

I would merge the PR after the release. 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T08:30:12Z

Review comments:
diff_hunk: @@ -0,0 +1,46 @@
+FROM wordpress:php8.3-apache
+
+ARG UID=1000
+ARG GID=1000
+
+# additinal extensions
+RUN apt-get update \
+	&& apt-get install -y git zlib1g-dev libzip-dev zip wget gnupg msmtp libpng-dev gettext subversion \
+	&& \
+    # Install NodeJS, enable Corepack
+    curl -sL https://deb.nodesource.com/setup_19.x | bash - && \
+    apt-get install -y nodejs build-essential && \
+    corepack enable && \
+	\
+	# Install WP-CLI
+	curl -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
+	chmod +x /usr/local/bin/wp && \
+	\
+  # Clean up
+  apt-get clean && \
+  rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+COPY dev/php.ini /usr/local/etc/php/conf.d/php_user.ini
+
+# msmtp config
+RUN printf "account default\nhost smtp\nport 1025" > /etc/msmtprc
+
+# xdebug build an config
+ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/xdebug.ini
+RUN git clone -b "3.3.1" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
path: dev/php83/Dockerfile
user: login: costasovo
body: There is xdebug [3.4.2 available](https://xdebug.org/updates#x_3_4_2). What about using the new version in the image? We could update also the PHP 8.2 image. The version 3.3.2 has a peformance fix for usage with Apache.
created_at: 2025-03-10T11:35:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,31 +60,6 @@ services:
       - './packages:/var/www/html/wp-content/plugins/packages'
       - './templates:/var/www/templates'
 
-  test_wordpress:
path: docker-compose.yml
user: login: costasovo
body: Nice! Happy to see this cleanup! We could probably also remove the [test database initialization in the DB service](https://github.com/mailpoet/mailpoet/blob/74e1616e15de23e2666ba1dcfb0c9e76e5e0ba1d/docker-compose.yml#L15). AFAIK it was used only for the test site.
created_at: 2025-03-10T11:40:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: costasovo
created_at: 2025-03-10T12:40:26Z
body: @lysyjan, I forgot to answer the questions in the description.

> Should I configure using PHP 8.3 by default for local development?

I would switch. PHP 8.3 should be more performant and I guess it is better to use the newer version. 

> Is there something that I overlooked and should be checked?

Hmm. I see that the premium plugin also has some usages of PHP8.2 at least in Circle CI config. I'm not sure if that should be part of this task or not.
author_association: CONTRIBUTOR
user: login: lysyjan
created_at: 2025-03-10T12:44:44Z
body: @costasovo Thank you.
I had testing premium in my mind, but after moving to another task I forgot. Thank you. I will prepare another PR for the premium plugin.
author_association: CONTRIBUTOR
user: login: lysyjan
created_at: 2025-03-10T16:02:21Z
body: @costasovo I added new commits addressing your suggestions.

I also created two related PRs mentioned in the description.
author_association: CONTRIBUTOR