WordPress sur OxPHP
WordPress est une application en mode traditionnel. Elle possède de nombreux points d'entrée physiques (index.php, wp-login.php, wp-cron.php, ainsi que tout le répertoire wp-admin/), chacun destiné à être atteint comme un véritable fichier. Le mode de routage traditionnel d'OxPHP, celui par défaut lorsque ENTRY_FILE n'est pas défini, les sert exactement de cette façon. Ne définissez pas ENTRY_FILE : le mode framework canaliserait chaque requête vers un unique contrôleur frontal et casserait wp-admin.
Cette recette montre aussi la seconde forme de build. Plutôt que de copier OxPHP dans une base PHP, elle étend directement l'image du runtime OxPHP, en compilant les extensions de WordPress dans un stage de build et en y déposant les fichiers .so.
Vue d'ensemble de la stack
- Image OxPHP :
ghcr.io/oxphp/oxphp:0.10.0(PHP 8.5), étendue sur place - Mode de routage : Traditionnel (pas de
ENTRY_FILE) - Extensions ajoutées :
mysqli,pdo_mysql,gd,zip,intl,exif,bcmath - Services : OxPHP + MySQL + un conteneur annexe WP-CLI (profil
cli) - Durcissement :
PHP_DENY_PATHSbloque l'exécution directe de fichiers.phpsouswp-content/uploads/ - URL :
http://localhost:8090· internehttp://localhost:9091/health
Organisation du projet
wp-oxphp/
├── Dockerfile
├── docker-compose.yml
└── wordpress/ # the WordPress tree (download from wordpress.org)
└── wp-config.php # reads WORDPRESS_* environment variableswordpress/wp-config.php lit ses paramètres depuis l'environnement du conteneur :
define( 'DB_NAME', getenv( 'WORDPRESS_DB_NAME' ) ?: 'wordpress' );
define( 'DB_USER', getenv( 'WORDPRESS_DB_USER' ) ?: 'wordpress' );
define( 'DB_PASSWORD', getenv( 'WORDPRESS_DB_PASSWORD' ) ?: 'wordpress' );
define( 'DB_HOST', getenv( 'WORDPRESS_DB_HOST' ) ?: 'db:3306' );
$__site_url = getenv( 'WORDPRESS_SITE_URL' ) ?: 'http://localhost:8090';
define( 'WP_HOME', $__site_url );
define( 'WP_SITEURL', $__site_url );Dockerfile et Compose
Ce Dockerfile compile les extensions contre un php:8.5-zts-alpine correspondant (même ABI que l'image OxPHP, no-debug-zts-20250925) et copie les fichiers .so dans le runtime OxPHP :
# ── Stage 1: compile WordPress PHP extensions against PHP 8.5 ─────
FROM php:8.5-zts-alpine3.23 AS ext-builder
RUN apk add --no-cache \
icu-dev libzip-dev libpng-dev libjpeg-turbo-dev freetype-dev oniguruma-dev \
&& docker-php-ext-configure gd --with-jpeg --with-freetype \
&& docker-php-ext-install -j"$(nproc)" \
mysqli pdo_mysql gd zip intl exif bcmath
RUN EXT_DIR=$(php -r 'echo ini_get("extension_dir");') && mkdir -p /ext-out \
&& cp "$EXT_DIR"/mysqli.so "$EXT_DIR"/pdo_mysql.so "$EXT_DIR"/gd.so \
"$EXT_DIR"/zip.so "$EXT_DIR"/intl.so "$EXT_DIR"/exif.so \
"$EXT_DIR"/bcmath.so /ext-out/
# ── Stage 2: OxPHP runtime with WordPress extensions ─────────────
FROM ghcr.io/oxphp/oxphp:0.10.0 AS runtime
USER root
RUN apk add --no-cache icu-libs libzip libpng libjpeg-turbo freetype oniguruma
# Drop the compiled extensions into OxPHP's PHP 8.5 extension dir
COPY --from=ext-builder /ext-out/*.so \
/usr/local/lib/php/extensions/no-debug-zts-20250925/
RUN { \
echo "extension=mysqli.so"; echo "extension=pdo_mysql.so"; \
echo "extension=gd.so"; echo "extension=zip.so"; \
echo "extension=intl.so"; echo "extension=exif.so"; \
echo "extension=bcmath.so"; \
} > /usr/local/etc/php/conf.d/wordpress-extensions.ini
RUN { \
echo "upload_max_filesize=64M"; echo "post_max_size=64M"; \
echo "memory_limit=256M"; echo "max_execution_time=300"; \
echo "max_input_vars=3000"; \
} > /usr/local/etc/php/conf.d/wordpress.ini
EXPOSE 80services:
wp:
build:
context: .
image: wp-oxphp/oxphp-wordpress:dev
container_name: wp-oxphp
ports:
- "8090:80"
- "9091:9090"
volumes:
- ./wordpress:/var/www/html/public # WordPress tree, live-editable
environment:
# Traditional routing — NO ENTRY_FILE, so wp-admin/*.php, wp-login.php,
# wp-cron.php are served as physical files the way WordPress expects.
# LISTEN_ADDR (0.0.0.0:80) and DOCUMENT_ROOT (/var/www/html/public) are
# the OxPHP defaults, so both are omitted.
INTERNAL_ADDR: 0.0.0.0:9090
ACCESS_LOG: all
# Block direct PHP execution where user content lands — defeats a shell
# uploaded into wp-content/uploads by a vulnerable plugin. Do NOT add
# /wp-content/plugins/** or /wp-content/themes/** — some plugins expose
# directly-callable .php endpoints there.
PHP_DENY_PATHS: "/wp-content/uploads/**,/wp-content/cache/**,/wp-content/upgrade/**"
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_SITE_URL: http://localhost:8090
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://0.0.0.0:9090/health"]
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
db:
image: mysql:9
container_name: wp-oxphp-db
ports:
- "3307:3306"
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: root
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-proot"]
interval: 5s
timeout: 5s
retries: 20
start_period: 15s
restart: unless-stopped
# On-demand WP-CLI — the OxPHP runtime image ships no `wp` binary.
wpcli:
image: wordpress:cli-php8.4
container_name: wp-oxphp-wpcli
profiles: ["cli"]
user: "0:0"
volumes:
- ./wordpress:/var/www/html
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_SITE_URL: http://localhost:8090
depends_on:
db:
condition: service_healthy
volumes:
db_data:Le répertoire d'extensions (no-debug-zts-20250925) est le tag ABI PHP 8.5 ZTS. Si vous construisez sur l'image OxPHP PHP 8.4, il devient no-debug-zts-20240924. Déduisez-le au moment du build avec php -r 'echo ini_get("extension_dir");' plutôt que de le coder en dur.
Installation et premier démarrage
docker compose up -d --build
docker compose run --rm wpcli wp core install \
--url=http://localhost:8090 --title="OxPHP WordPress" \
--admin_user=admin --admin_password=admin [email protected]Notes OxPHP
- Le mode traditionnel est obligatoire. WordPress a besoin que
wp-admin/,wp-login.php,wp-cron.php, etc. s'exécutent en tant que fichiers physiques. LaissezENTRY_FILEnon défini. - L'image du runtime OxPHP ne fournit aucun binaire
wp(c'est une image de service minimale). Le travail en CLI passe par le conteneur annexewpclisous le profil Composecli, qui partage le même volumewordpress/et la même base de données. wp-config.phplit l'environnement du conteneur viagetenv(), si bien que les identifiants de la base de données et l'URL du site vivent dans Compose, et non codés en dur dans le fichier.PHP_DENY_PATHSdurcit les répertoires d'upload. Parce que le mode traditionnel exécute des fichiers.phpphysiques, un shell téléversé danswp-content/uploads/par un plugin vulnérable s'exécuterait sinon.PHP_DENY_PATHSbloque l'exécution de.phpsous ces chemins, en confrontant l'URI avant toute E/S disque, de sorte qu'il n'y a aucun oracle d'existence. Il ne s'applique qu'en modes traditionnel et SPA ; en mode framework, c'est un no-op (le contrôleur frontal empêche déjà l'exécution directe de.php), ce qui explique pourquoi les recettes en mode framework présentées ici n'en ont pas besoin. Voir Liste de refus d'exécution PHP.
Vérification
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8090/ # 200
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8090/wp-login.php # 200 (physical file)
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8090/wp-content/uploads/x.php # 404 (PHP_DENY_PATHS)
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9091/health # 200Voir aussi
- Routage · Liste de refus d'exécution PHP · Guide Docker
- OpenCart, l'autre recette en mode traditionnel