إذا سبق لك أن أضعت نصف يوم لأنك تعتقد أن البرنامج يعمل على جهازك فقط ولا يعمل على جهاز زميلك، فإن Docker غالبًا ما يكون الحل. الفائدة الحقيقية ليست في "امتلاك" WordPress "في وعاء"، يتعلق الأمر بالتجميد كل التبعيات (PHP، الإضافات، قاعدة البيانات، Redis، البريد، الأدوات) والقدرة على إعادة بناء البيئة بشكل متطابق.

ما سنقوم ببنائه

ستقوم بإعداد بيئة تطوير WordPress قابل للتكرار، مصمم خصيصًا لـ WordPress 6.9.4 (أبريل 2026) و PHP 8.3 + (الحد الأدنى الموصى به لإصدار PHP: 8.1). الهدف هو الحصول على بيئة تطوير "شبه جاهزة للإنتاج" ولكنها موجهة نحو التطوير: Xdebug، وWP-CLI، وسجلات قابلة للقراءة، وخاصية جمع عناوين البريد الإلكتروني. مخبأ Redis اختياري، وهناك عملية تهيئة تلقائية.

هذا الإعداد مناسب لما يلي:

  • مواقع العرض والمدونات ذات القوالب المخصصة (أو قوالب الأطفال)،
  • منشئ المواقع (Divi 5، Elementor، Avada) حيث تريد عزل الإضافات والإصدارات،
  • Agences الذين يحتاجون إلى دمج مطور جديد بسرعة أو إعادة إنتاج خطأ برمجي لدى أحد العملاء.

في النهاية، ستعرف:

  • قم بتشغيل ووردبريس 6.9.4 بأمر واحد،
  • تثبيت ووردبريس والإضافات والبيانات الأساسية تلقائيًا.
  • تصحيح الأخطاء بشكل صحيح (Xdebug + السجلات)،
  • التقاط رسائل البريد الإلكتروني الصادرة دون الحاجة إلى استخدام خادم SMTP حقيقي،
  • قم بإصدار التكوين دون إصدار الأسرار.

ملخص سريع

  • نبدأ بمشروع Git مع .env (بدون إصدار) + .env.example (نسخة).
  • نحن نستخدم عامل ميناء يؤلف مع الخدمات: wp, db, redis, mailpit, traefik (خيار).
  • نحن نبني صورة wp-dev مرتكز على wordpress:php8.3-apache + Xdebug + WP-CLI.
  • نقوم بأتمتة التثبيت عبر WP-CLI (بشكل متكرر) ونحتفظ بالتحميلات في وحدة تخزين.
  • نطبق استراتيجية تسجيل وتصحيح أخطاء مستقرة: WP_DEBUG_LOGسجلات Apache، Xdebug في "trigger".

متى يستخدم هذا الحل

أوصي به إذا كانت لديك واحدة على الأقل من هذه الاحتياجات:

  • إعادة إنتاج خطأ في العميل : تقوم بتجميد إصدارات PHP/extensions و MySQL/MariaDB، وتتجنب "الاختلافات غير المرئية".
  • العمل في مجموعة نفس البنية للجميع، بما في ذلك التكامل المستمر.
  • إدارة مشاريع متعددة يتضمن كل مشروع حزمة البرامج الخاصة به، دون تلويث جهازك.
  • اختبار الإضافات الثقيلة (المطورين، ووكومرس) دون الإضرار ببيئتك المحلية.

في تجربتي، هذا مفيد بشكل خاص في مواقع Elementor/Avada حيث يؤدي وجود امتداد PHP مفقود (intl، zip، gd) إلى تعطل استيراد القالب "فقط"، دون أي رسالة قابلة للاستخدام.

متى لا يجب استخدام هذا الحل

  • إذا كنت تستخدم جهازًا محدود الإمكانيات (ذاكرة الوصول العشوائي/وحدة المعالجة المركزية) وكان مشروعك صغيرًا جدًا: فقد يكون Docker أبطأ من PHP المحلي.
  • إذا كنت تقوم فقط بالمحتوى (بدون تطوير): فإن إدارة بيئة الاختبار غالبًا ما تكون أبسط.
  • إذا كان فريقك يفتقر إلى الانضباط اللازم لإدارة الإصدارات بشكل صحيح .env.example أما بالنسبة للبرامج النصية: فستقوم بإنشاء "Docker يعمل لشخص واحد فقط"، وهو أمر أسوأ من ذي قبل.

قبل البدء (المتطلبات الأساسية)

استعد كما لو كنت ستكسر شيئًا ما، لأنك ستكسر شيئًا ما.

المتطلبات الفنية

  • Docker Desktop (لنظامي التشغيل macOS/Windows) أو Docker Engine (لنظام التشغيل Linux) + إضافة Compose. الوثائق: دوكر يؤلف.
  • شخص سخيف.
  • محرر نصوص يتعامل مع نهايات الأسطر بشكل جيد (مثل VS Code).

إصدارات مستهدفة

  • ووردبريس: 6.9.4 (يمكنك تغيير الإصدار لاحقًا، لكننا سنبدأ بهذا الإصدار).
  • بي أتش بي : 8.3 في الصورة (متوافق مع WP 6.9.4، وأعلى من الحد الأدنى 8.1).
  • MariaDB: 10.11 LTS (حل وسط جيد بين الاستقرار والتوافق).

النسخ الاحتياطي والبيئة

  • لا تختبر هذا على موقع مباشر. افعل ذلك في مستودع مخصص.
  • إذا كنت تقوم بنقل موقع موجود: تصدير قاعدة البيانات + wp-content/uploads قبل البدء.

الأمن (محلي ولكنه ليس "خالياً من المخاطر")

  • لا تلتزم أبداً .env (كلمات مرور قاعدة البيانات، والملحات، والمفاتيح).
  • تجنب كشف MySQL/Redis على 0.0.0.0 إذا لم يكن ذلك ضرورياً.

مراجع رسمية مفيدة:


الخطوة 1: إنشاء بنية مشروع نظيفة وقابلة للتحديث

الهدف: فصل ما هو تم إصداره (التكوين، البرامج النصية) لما ليس (الأسرار، وحدات التخزين، التحميلات).

1) أنشئ بنية الشجرة

في مجلد فارغ:

mkdir -p wp-docker/{docker,bin,wp,wp-content,logs}
cd wp-docker

ستحصل على:

  • docker/ ملف Dockerfile، ملف إعدادات Apache/PHP، نصوص الدخول
  • bin/ : البرامج النصية المساعدة (التثبيت، إعادة التعيين، استيراد قاعدة البيانات، إلخ.)
  • wp/ نواة ووردبريس (موسعة) أو جذر المستند حسب الإصدار
  • wp-content/ قوالب/إضافات ذات إصدارات متعددة (اختياري)
  • logs/ سجلات دائمة

2) أضف ملف .gitignore صارمًا

Créez .gitignore في الجذر:

cat > .gitignore <<'EOF'
# Secrets
.env

# Volumes / données locales
data/
logs/*.log
wp/wp-config.php

# Dépendances éventuelles
node_modules/
vendor/

# OS / IDE
.DS_Store
.idea/
.vscode/
EOF

3) أنشئ ملفات بيئتك

Créez .env.example (إصدارات):

cat > .env.example <<'EOF'
# Domaine local (utilisé par WP_HOME/WP_SITEURL)
WP_HOST=wp.local

# WordPress
WP_VERSION=6.9.4
WP_ENV=development

# Base de données
DB_NAME=wordpress
DB_USER=wordpress
DB_PASSWORD=wordpress
DB_ROOT_PASSWORD=root
DB_HOST=db

# Ports
HTTP_PORT=8080
MAILPIT_PORT=8025

# Xdebug
XDEBUG_MODE=debug,develop
XDEBUG_TRIGGER=1
EOF

ثم انسخها إلى .env (بدون إصدار):

cp .env.example .env

النتيجة المتوقعة

لديك وديعة جاهزة للاستلام docker-compose.yml بدون حفظ الأسرار. هذا هو الخطأ الذي يقع فيه الكثيرون: كثيراً ما رأيت كلمات مرور قواعد البيانات في Git "لأنها محلية". في اليوم الذي يصبح فيه المستودع عاماً عن طريق الخطأ، يكون الأوان قد فات.


الخطوة 2: كتابة ملف docker-compose قابل للتكرار (PHP 8.3+، MariaDB، Redis)

الهدف: تعريف مجموعة مستقرة، مع وحدات تخزين مسماة، ومنافذ صريحة.

1) أنشئ ملف docker-compose.yml

في الجذر، أنشئ docker-compose.yml :

cat > docker-compose.yml <<'EOF'
services:
  wp:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: wp_app
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
      mailpit:
        condition: service_started
    ports:
      - "${HTTP_PORT:-8080}:80"
    volumes:
      # Code WordPress (core) + contenu
      - ./wp:/var/www/html
      - ./wp-content:/var/www/html/wp-content
      # Logs persistants
      - ./logs:/var/log/wp
    extra_hosts:
      # Permet à Xdebug de joindre l'hôte sur Linux (optionnel sur Mac/Windows)
      - "host.docker.internal:host-gateway"

  db:
    image: mariadb:10.11
    container_name: wp_db
    environment:
      MYSQL_DATABASE: ${DB_NAME:-wordpress}
      MYSQL_USER: ${DB_USER:-wordpress}
      MYSQL_PASSWORD: ${DB_PASSWORD:-wordpress}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-uroot", "-p${DB_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 3s
      retries: 30

  redis:
    image: redis:7-alpine
    container_name: wp_redis
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

  mailpit:
    image: axllent/mailpit:latest
    container_name: wp_mailpit
    ports:
      - "${MAILPIT_PORT:-8025}:8025"

volumes:
  db_data:
  redis_data:
EOF

2) لماذا هذه الخيارات

  • مجلدات تحمل أسماء (db_data, redis_data): يمكنك تجنب إصدار البيانات ويمكنك إعادة التعيين بسهولة.
  • قاعدة بيانات الفحص الصحي : بدون ذلك، قد يحاول WP-CLI التثبيت قبل أن يكون MariaDB جاهزًا (حالة سباق كلاسيكية).
  • ميلبيت يمكنك رؤية رسائل البريد الإلكتروني الصادرة، بدون استخدام بروتوكول SMTP. مفيد جدًا للنماذج.

النتيجة المتوقعة

في هذه المرحلة، docker compose up سوف تفشل مرة أخرى لأن الصورة wp إنه غير موجود. هذا طبيعي: سنقوم ببنائه في الخطوة التالية.


الخطوة 3: إنشاء صورة "تطوير" ووردبريس (Xdebug، WP-CLI، الإضافات)

الهدف هو تجنب الاعتماد على حاوية "ووردبريس افتراضية" عند الحاجة إلى أدوات (مثل WP-CLI، وXdebug، وzip، وintl، إلخ). أفضل استخدام صورة مخصصة، لأنه بخلاف ذلك، ستضطر إلى إجراء تعديلات يدوية داخل الحاوية، مما يجعل إعادة إنتاج المشكلة مستحيلة.

1) إنشاء ملف Dockerfile

cat > docker/Dockerfile <<'EOF'
FROM wordpress:php8.3-apache

# Paquets système utiles en dev (zip, intl, mysql client, etc.)
RUN apt-get update && apt-get install -y --no-install-recommends 
    git 
    unzip 
    less 
    mariadb-client 
    libzip-dev 
    libicu-dev 
  && docker-php-ext-install zip intl 
  && rm -rf /var/lib/apt/lists/*

# Activer mod_rewrite (permalinks)
RUN a2enmod rewrite headers

# Xdebug (install via PECL)
RUN pecl install xdebug 
  && docker-php-ext-enable xdebug

# WP-CLI (binaire officiel)
RUN curl -sSLo /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar 
  && chmod +x /usr/local/bin/wp 
  && wp --info

# Configuration PHP (logs, limites)
COPY docker/php.ini /usr/local/etc/php/conf.d/99-wp-dev.ini

# Configuration Apache (DocumentRoot, logs)
COPY docker/apache.conf /etc/apache2/sites-available/000-default.conf

# Script d'entrée (bootstrap idempotent)
COPY docker/entrypoint.sh /usr/local/bin/wp-entrypoint
RUN chmod +x /usr/local/bin/wp-entrypoint

ENTRYPOINT ["wp-entrypoint"]
CMD ["apache2-foreground"]
EOF

2) أضف ملف docker/php.ini

cat > docker/php.ini <<'EOF'
; Logs PHP vers stderr + fichier (pratique en debug)
log_errors = On
error_reporting = E_ALL
display_errors = Off

; Ajustez selon vos besoins
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 128M
max_execution_time = 120

; Xdebug en mode "trigger" pour éviter de ralentir tout le monde
xdebug.mode = ${XDEBUG_MODE}
xdebug.start_with_request = trigger
xdebug.trigger_value = ${XDEBUG_TRIGGER}

; Sur Mac/Windows: host.docker.internal marche.
; Sur Linux: extra_hosts dans compose.
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003
xdebug.log_level = 0
EOF

3) أضف ملف docker/apache.conf

cat > docker/apache.conf <<'EOF'
<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  DocumentRoot /var/www/html

  <Directory /var/www/html>
    AllowOverride All
    Require all granted
  </Directory>

  ErrorLog /var/log/wp/apache-error.log
  CustomLog /var/log/wp/apache-access.log combined
</VirtualHost>
EOF

4) أضف docker/entrypoint.sh

يقوم هذا البرنامج النصي بثلاثة أشياء: انتظار قاعدة البيانات، وتنزيل ووردبريس إذا لم يكن موجودًا، وإنشاء... wp-config.php التنظيف (باستخدام الأملاح وتصحيح الأخطاء).

cat > docker/entrypoint.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Petit helper de logs
log() {
  echo "[wp-entrypoint] $*"
}

# Attendre MariaDB (évite la course au démarrage)
wait_for_db() {
  log "Attente de la base de données ${DB_HOST:-db}..."
  for i in {1..60}; do
    if mariadb-admin ping -h"${DB_HOST:-db}" -u"${DB_USER:-wordpress}" -p"${DB_PASSWORD:-wordpress}" --silent; then
      log "Base de données prête."
      return 0
    fi
    sleep 2
  done
  log "ERREUR: la base n'est pas prête après 120s."
  return 1
}

# Télécharger WordPress si /var/www/html est vide (ou sans wp-includes)
ensure_wordpress_core() {
  if [ ! -d /var/www/html/wp-includes ]; then
    log "WordPress core absent, téléchargement de WP ${WP_VERSION:-6.9.4}..."
    wp core download --version="${WP_VERSION:-6.9.4}" --path=/var/www/html --allow-root
  else
    log "WordPress core déjà présent."
  fi
}

# Générer wp-config.php si absent
ensure_wp_config() {
  if [ ! -f /var/www/html/wp-config.php ]; then
    log "Génération de wp-config.php..."
    wp config create 
      --path=/var/www/html 
      --dbname="${DB_NAME:-wordpress}" 
      --dbuser="${DB_USER:-wordpress}" 
      --dbpass="${DB_PASSWORD:-wordpress}" 
      --dbhost="${DB_HOST:-db}" 
      --skip-check 
      --allow-root

    # Ajouter des constantes utiles en dev (et Redis en option)
    wp config set WP_ENVIRONMENT_TYPE "${WP_ENV:-development}" --type=constant --allow-root
    wp config set WP_DEBUG true --type=constant --allow-root
    wp config set WP_DEBUG_LOG true --type=constant --allow-root
    wp config set WP_DEBUG_DISPLAY false --type=constant --allow-root
    wp config set SCRIPT_DEBUG true --type=constant --allow-root

    # Logs WordPress vers un fichier monté
    wp config set WP_CONTENT_DIR "/var/www/html/wp-content" --type=constant --allow-root
    wp config set WP_CONTENT_URL "http://localhost/wp-content" --type=constant --allow-root

    # Redis (si plugin présent plus tard)
    wp config set WP_REDIS_HOST "redis" --type=constant --allow-root
    wp config set WP_REDIS_PORT 6379 --type=constant --allow-root
  else
    log "wp-config.php déjà présent."
  fi
}

wait_for_db
ensure_wordpress_core
ensure_wp_config

exec "$@"
EOF

النتيجة المتوقعة

الصورة جاهزة. لديك نقطة تهيئة واحدة قابلة للتحديث، وتتجنب بذلك المأزق الكلاسيكي المتمثل في "لقد قمت بتثبيت WP-CLI في حاويتي يدويًا، ولكن لا أحد آخر لديه ذلك".


الخطوة الرابعة: تثبيت ووردبريس 6.9.4 وقفل الإعدادات

الهدف: بدء تشغيل الحاويات، ثم تنفيذ تثبيت ووردبريس بطريقة مضبوطة (وقابلة للتكرار).

1) ابدأ المكدس

docker compose up -d --build

يفحص:

2) قم بتثبيت ووردبريس عبر WP-CLI

يجري:

docker compose exec wp wp core install 
  --url="http://localhost:8080" 
  --title="WP Docker Dev" 
  --admin_user="admin" 
  --admin_password="admin" 
  --admin_email="[email protected]" 
  --skip-email 
  --allow-root

نعم، كلمة المرور ضعيفة عمداً: هذه بيئة محلية. لا تقم بنسخ هذا النمط في بيئة تجريبية عامة.

3) اضبط الروابط الدائمة

بدون هذا، ستعتقد أن وحدة mod_rewrite معطلة:

docker compose exec wp wp rewrite structure '/%postname%/' --hard --allow-root

النتيجة المتوقعة

يمكنك الاتصال بـ /wp-adminأنشئ مقالاً، وستعمل عناوين URL "الجميلة".


الخطوة 5: أتمتة التثبيت والبيانات (WP-CLI، البرامج النصية)

الهدف: أمر واحد = بيئة جاهزة. هنا يصبح التكرار حقيقة واقعة.

1) أنشئ برنامجًا نصيًا باسم bin/install.sh

cat > bin/install.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Script d'installation idempotent pour WordPress en Docker
# Usage: ./bin/install.sh

HTTP_PORT="${HTTP_PORT:-8080}"

docker compose up -d --build

# Attendre que WP réponde (évite les installs trop rapides)
echo "[install] Attente HTTP..."
for i in {1..60}; do
  if curl -sSf "http://localhost:${HTTP_PORT}/wp-login.php" > /dev/null; then
    break
  fi
  sleep 2
done

# Installer WP si pas déjà installé
if docker compose exec -T wp wp core is-installed --allow-root > /dev/null 2>&1; then
  echo "[install] WordPress déjà installé."
else
  echo "[install] Installation WordPress..."
  docker compose exec -T wp wp core install 
    --url="http://localhost:${HTTP_PORT}" 
    --title="WP Docker Dev" 
    --admin_user="admin" 
    --admin_password="admin" 
    --admin_email="[email protected]" 
    --skip-email 
    --allow-root

  docker compose exec -T wp wp rewrite structure '/%postname%/' --hard --allow-root
fi

# Installer des plugins utiles en dev
echo "[install] Installation plugins dev..."
docker compose exec -T wp wp plugin install query-monitor --activate --allow-root

# Redis cache (optionnel) : on l'installe mais on ne l'active pas si vous ne voulez pas
docker compose exec -T wp wp plugin install redis-cache --allow-root || true

# Réglages de base
docker compose exec -T wp wp option update blogdescription "Environnement reproductible Docker" --allow-root

echo "[install] OK. WP: http://localhost:${HTTP_PORT}  Admin: admin/admin"
EOF

chmod +x bin/install.sh

2) تشغيل برنامج التمهيد

./bin/install.sh

3) لماذا هو أفضل من ملف README

ملف README دائمًا ما يكون فيه خطأ ما. أما البرنامج النصي، من ناحية أخرى، فيتعطل فورًا إذا فُقدت خطوة ما. وعندما يتعطل، تعرف أين تحديدًا.

النتيجة المتوقعة

يمكنك حذف وحدات التخزين الخاصة بك، وإعادة التشغيل ./bin/install.shواستعادة موقع ووردبريس يعمل بشكل صحيح، مع تمكين خاصية مراقبة الاستعلامات.


الخطوة 6: تصحيح الأخطاء والسجلات (WP_DEBUG، Xdebug، Query Monitor، error_log)

الهدف: إظهار الأخطاء دون إبطاء النظام بأكمله.

1) أين يمكن قراءة السجلات

  • سجلات أباتشي: logs/apache-error.log et logs/apache-access.log
  • سجل ووردبريس: افتراضيًا wp-content/debug.log (سيارة WP_DEBUG_LOG في true)

طلب مريح:

tail -f logs/apache-error.log wp-content/debug.log

2) Xdebug دون إبطاء كل شيء

يستخدم الإعداد xdebug.start_with_request=triggerبعبارات ملموسة:

  • بدون مُشغّل: لا يتم تفعيل Xdebug، والأداء صحيح.
  • باستخدام المُشغّل: يمكنك تفعيل تصحيح الأخطاء فقط عندما تحتاج إليه.

مثال: إضافة مُعامل ?XDEBUG_TRIGGER=1 إلى عنوان URL، أو قم بتهيئة بيئة التطوير المتكاملة (IDE) لإرسال ملف تعريف الارتباط/المُشغِّل. مرجع: تصحيح الأخطاء خطوة بخطوة Xdebug.

3) خطأ واقعي: "خطأ فادح... استدعاء دالة غير معرّفة"

ألاحظ هذا غالبًا عند لصق جزء من التعليمات البرمجية في الملف الخطأ (أو تنفيذه مبكرًا جدًا). ​​في ووردبريس، لا تتوفر بعض الخطافات افتراضيًا. إذا كنت تختبر إضافةً متعددة الوظائف، فتأكد من ربط التعليمات البرمجية الخاصة بك بالخطاف المناسب.

اختبار مصغر لملحق mu (سيتم ترقيم إصداراته): إنشاء wp-content/mu-plugins/dev-tools.php :

<?php
/**
 * Plugin Name: Dev Tools (local)
 * Description: Aides au debug en environnement Docker.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'init', function () {
	// Exemple : log contrôlé (évitez var_dump en prod)
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		error_log( '[dev-tools] init OK, WP ' . get_bloginfo( 'version' ) );
	}
}, 20 );

النتيجة: يظهر سطر في wp-content/debug.log مع كل طلب.


الخطوة 7: التقاط رسائل البريد الإلكتروني والتأكد من موثوقية مهمة cron (Mailpit + system cron)

الهدف: التوقف عن "اختبار" رسائل البريد الإلكتروني عن طريق إرسال رسائل حقيقية، وتجنب المفاجآت المتعلقة بـ WP-Cron.

1) جمع رسائل البريد الإلكتروني

يلتقط Mailpit ما يتم إخراجه عبر mail()تستخدم العديد من الإضافات PHPMailer عبر ووردبريس، مما يؤدي غالبًا إلى sendmail في حاوية. قد يختلف هذا الأمر باختلاف الصور.

يُعدّ تثبيت إضافة SMTP محلية تشير إلى Mailpit (أو تهيئة PHPMailer عبر خطاف) طريقةً موثوقة. وللحفاظ على إمكانية إعادة إنتاج المشكلة وتجنب واجهة المستخدم، أُفضّل استخدام إضافة mu.

Créez wp-content/mu-plugins/mailpit.php :

<?php
/**
 * Plugin Name: Mailpit SMTP (local)
 * Description: Force l'envoi SMTP vers Mailpit en dev.
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

add_action( 'phpmailer_init', function ( $phpmailer ) {
	// En local uniquement : si vous mettez ça en prod, vous allez perdre vos e-mails.
	if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE !== 'development' ) {
		return;
	}

	$phpmailer->isSMTP();
	$phpmailer->Host       = 'mailpit';
	$phpmailer->Port       = 1025;
	$phpmailer->SMTPAuth   = false;
	$phpmailer->SMTPSecure = '';
}, 10, 1 );

امتحان :

docker compose exec wp wp eval "wp_mail('[email protected]','Test','Hello');" --allow-root

افتح Mailpit: http://localhost:8025.

2) استبدال WP-Cron بمهمة cron للنظام (قيد التطوير)

يعتمد WP-Cron على حجم الزيارات. في بيئة التطوير، يكون حجم الزيارات عشوائيًا. غالبًا ما أستبدل WP-Cron بمهمة cron كل بضعة أيام. دقائق .

أضف wp-config.php (تم إنشاؤه مسبقًا):

docker compose exec wp wp config set DISABLE_WP_CRON true --type=constant --allow-root

ثم أضف خدمة cron في Compose (خيار متقدم). تحرير docker-compose.yml وأضف:

cat >> docker-compose.yml <<'EOF'

  cron:
    image: curlimages/curl:8.7.1
    container_name: wp_cron
    depends_on:
      - wp
    command: >
      sh -lc "while true; do
        curl -sS http://wp/wp-cron.php?doing_wp_cron=1 > /dev/null || true;
        sleep 60;
      done"
EOF

إعادة تشغيل:

docker compose up -d

النتيجة: يتم تشغيل مهامك المجدولة (عمليات الاستيراد، والمزامنة، والنشرات الإخبارية قيد التطوير) بطريقة مستقرة.


الخطوة 8: إضافة اختبارات سريعة (PHPUnit + wp-env اختياري)

الهدف: توفير حد أدنى من الأمان. حتى في المدونات، تستحق الإضافات المخصصة أو القوالب المصممة خصيصًا بعض الاختبارات على الأقل.

الخيار أ: استخدام PHPUnit داخل الحاوية (بسيط وسريع)

يمكنك تثبيت PHPUnit عبر Composer في قالبك/إضافتك. إذا كان مشروعك يستخدم Composer بالفعل، فهذا هو الوقت الأمثل. وإلا، يمكنك الاكتفاء بالاختبار اليدوي.

مثال (إضافة مخصصة): هيكلة إضافة wp-content/plugins/my-plugin وأضف Composer. لن أتطرق إلى تفاصيل الإعداد الكامل هنا، لأنه يعتمد بشكل كبير على بنية نظامك، ولكن تذكر هذا: احتفظ بالاختبارات خارج بيئة تشغيل WordPress.

الخيار ب: wp-env (أداة التطوير الأساسية الرسمية) للاختبارات المعزولة

@wordpress/env (بيئة ووردبريسيُعدّ هذا الأمر مناسبًا لتشغيل بيئة عمل مؤقتة لأغراض الاختبار، دون التأثير على بيئة Docker الرئيسية. وهو مفيد في عمليات التكامل المستمر/التسليم المستمر (CI/CD).

وثيقة رسمية: @wordpress/env.


النتيجة الكاملة

إذا كنت ترغب في نسخ كل شيء دفعة واحدة، فإليك ملفات المفاتيح المجمعة. ثم قم بتخصيصها عبر .env و mu-plugins.

ملف docker-compose.yml (كامل)

services:
  wp:
    build:
      context: .
      dockerfile: docker/Dockerfile
    container_name: wp_app
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
      mailpit:
        condition: service_started
    ports:
      - "${HTTP_PORT:-8080}:80"
    volumes:
      - ./wp:/var/www/html
      - ./wp-content:/var/www/html/wp-content
      - ./logs:/var/log/wp
    extra_hosts:
      - "host.docker.internal:host-gateway"

  db:
    image: mariadb:10.11
    container_name: wp_db
    environment:
      MYSQL_DATABASE: ${DB_NAME:-wordpress}
      MYSQL_USER: ${DB_USER:-wordpress}
      MYSQL_PASSWORD: ${DB_PASSWORD:-wordpress}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mariadb-admin", "ping", "-h", "127.0.0.1", "-uroot", "-p${DB_ROOT_PASSWORD}"]
      interval: 5s
      timeout: 3s
      retries: 30

  redis:
    image: redis:7-alpine
    container_name: wp_redis
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

  mailpit:
    image: axllent/mailpit:latest
    container_name: wp_mailpit
    ports:
      - "${MAILPIT_PORT:-8025}:8025"

  cron:
    image: curlimages/curl:8.7.1
    container_name: wp_cron
    depends_on:
      - wp
    command: >
      sh -lc "while true; do
        curl -sS http://wp/wp-cron.php?doing_wp_cron=1 > /dev/null || true;
        sleep 60;
      done"

volumes:
  db_data:
  redis_data:

تخصيصات مفيدة

  • المغير HTTP_PORT إذا كان لديك بالفعل خادم محلي على المنفذ 8080.
  • استبدل MariaDB بـ MySQL 8 إذا كانت بيئة الإنتاج الخاصة بك تعمل بنظام MySQL (انتبه إلى ترتيبات/أوضاع SQL).
  • قم بتفعيل Redis على جانب WordPress عن طريق تثبيت/تكوين الإضافة بشكل صحيح (انظر القسم). صيانة).

التكيف مع Divi 5 / Elementor / Avada

لا يقوم البناؤون بتغيير Docker، لكنهم يغيرون قيودك: الذاكرة، ووقت التنفيذ، وحجم التحميل، وأحيانًا تبعيات PHP (zip، intl، gd/imagemagick).

ديفي 5

  • زيادة memory_limit (يبلغ حجمها بالفعل 512 ميجابايت) إذا قمت باستيراد تخطيطات كبيرة.
  • احتفظ wp-content تم إصدار نسخ جزئية: القالب الفرعي + إضافات mu، ولكن ليس ذاكرة التخزين المؤقت.

Elementor

  • غالباً ما يُطلق Elementor طلبات AJAX طويلة: max_execution_time عند 120 ثانية، فإنه يتجنب النتائج السلبية الكاذبة.
  • إذا لم يتم تحميل ملفات CSS/JS: تحقق من الروابط الدائمة وذاكرة التخزين المؤقت للرؤوس (غالباً ما تكون تالفة). AllowOverride أو ذاكرة التخزين المؤقت للمتصفح).

أفادا (منشئ الاندماج)

  • استيراد العروض التوضيحية: تأكد من أن لديك zip et intl (مثبت في ملف Dockerfile)، وإلا ستواجه أخطاء صامتة.
  • يمكن لبرنامج Avada إنشاء العديد من الملفات: احتفظ بها wp-content/uploads من حيث الحجم (وهذا هو الحال بالفعل عبر ./wp-content).

الفحص النهائي

  1. المدخل الأمامي: http://localhost:8080 يعرض الموقع.
  2. صلاحيات المسؤول: /wp-admin لقد نجح الأمر، تم تسجيل الدخول بنجاح.
  3. الروابط الدائمة: مقال في /%postname%/ لا يُرجع رمز الخطأ 404.
  4. السجلات: يظهر خطأ PHP متعمد في wp-content/debug.log (اختبار مع أ) error_log('test') في إضافة mu).
  5. البريد: واحد wp_mail() يظهر في Mailpit.
  6. Xdebug: عن طريق إضافة ?XDEBUG_TRIGGER=1، سيقوم برنامج IDE الخاص بك بالتقاط الطلب (إذا تم تكوينه).

إذا لم تكن النتيجة كما هو متوقع

إليكم جدول تشخيصي أستخدمه كخطوة أولى. وهو يغطي أكثر الأعراض شيوعاً في بيئات ووردبريس التي تعمل بنظام دوكر.

عرض السبب المحتمل التحقق الحلول
صفحة فارغة / 500 خطأ في PHP، إضافة غير متوافقة، ذاكرة قرأ logs/apache-error.log et wp-content/debug.log قم بتعطيل الإضافة عبر WP-CLI، ثم قم بزيادة memory_limitصحح الخطأ
خطأ: "حدث خطأ أثناء إنشاء اتصال بقاعدة البيانات" قاعدة البيانات غير جاهزة، بيانات اعتماد غير صحيحة، وحدات تخزين تالفة docker compose logs db + الاختبارات mariadb-admin ping متحقق .envاحذف وحدة تخزين قاعدة البيانات وأعد تشغيل عملية التثبيت
الروابط الدائمة 404 mod_rewrite معطل / تم حظر AllowOverride تحقق من ملف إعدادات Apache و a2enmod rewrite أعد بناء الصورة، ثم أعد التشغيل wp rewrite structure ... --hard
لا يمكن لـ Xdebug الاتصال المضيف/المنفذ غير صحيح، والمشغل مفقود هل يستمع برنامج IDE إلى المنفذ 9003؟ هل تم إرسال إشارة التنبيه؟ قم بتهيئة بيئة التطوير المتكاملة (IDE)، واستخدمها. host.docker.internalتحقق من جدار الحماية
لا تظهر رسائل البريد الإلكتروني تجاوز إعدادات إضافة SMTP، لم يتم تكوين PHPMailer اختبار wp_mail() + سجلات قم بتفعيل إضافة Mailpit mu-plugin، وتحقق من المضيف mailpit ميناء 1025

أوامر استكشاف الأخطاء وإصلاحها (ردود الفعل)

# Voir l'état
docker compose ps

# Logs d'un service
docker compose logs -f wp
docker compose logs -f db

# Entrer dans le conteneur WP
docker compose exec wp bash

# Désactiver tous les plugins (si wp-admin inaccessible)
docker compose exec wp wp plugin deactivate --all --allow-root

# Reconstruire proprement
docker compose down
docker compose up -d --build

الأخطاء الشائعة والمزالق

خطأ سبب الحلول
انسخ مقتطفًا إلى functions.php الموضوع الخاطئ أنت تقوم بتحرير القالب الرئيسي بدلاً من القالب الفرعي قم بإنشاء نسخة من قالب فرعي، أو استخدم إضافة mu-plugin لرمز التطوير.
نسيان فاصلة منقوطة في إضافة mu خطأ فوري في التحليل، وأحيانًا صفحة فارغة Regarder debug.log et apache-error.log، صحيح، أعد التحميل
الخلط بين الفعل والتصفية (خطاف غير مناسب) لا يتم تنفيذ الكود، أو يتم تنفيذه مبكراً جداً. استعمال init/wp_loaded تحقق من الأولوية حسب الحاجة
اختبار برنامج تعليمي قديم (WP 5.x) باستخدام كود قديم تغيرت الدوال/الثوابت، واختلف السلوك راجع وثائق ووردبريس الرسمية 6.9+ على الموقع developer.wordpress.org
لم يتم تحميل ملفات CSS/JS سيئة wp_enqueue_scriptخطاف خاطئ، ذاكرة تخزين مؤقتة للمتصفح استطلاع رأي حول wp_enqueue_scripts/admin_enqueue_scriptsمسح ذاكرة التخزين المؤقت
فشل WP-CLI: "تعذر الاتصال بقاعدة البيانات" DB غير جاهز (حالة السلالة) الفحص الصحي + الانتظار عند نقطة الدخول (مشمول بالفعل)
خطأ متعلق بإصدار قديم من لغة PHP لقد قمت بتغيير صورة PHP دون إعادة بناء النظام docker compose build --no-cache، يفحص php -v في الحاوية

بديل / متغير

الخيار الأول: LocalWP / DevKinsta (بدون Docker، "يدوي")

إذا كان هدفك الرئيسي هو تطوير قالب أو أداة إنشاء مواقع دون إدارة البنية التحتية، فإن أدوات مثل LocalWP قد تكون أسرع في التعلم. أما عيبها فهو أن إمكانية إعادة إنتاج التغييرات من قِبل الفريق أقل سهولة مقارنةً بمستودع Docker، كما أن تكامل CI/CD غالباً ما يكون أكثر تعقيداً.

الخيار الثاني (متقدم): ترايفيك + HTTPS + نطاقات متعددة

عند إدارة 10 مشاريع بالتوازي، غالبًا ما ألجأ إلى Traefik لتجنب تضارب المنافذ وللحصول على HTTPS محلي. يستغرق إعداده وقتًا أطول، ولكن بعد ذلك ستحصل على:

  • https://projet-a.test, https://projet-b.test...
  • الشهادات التلقائية (mkcert أو Traefik + CA محلي)
  • مجموعة مشاريع متعددة نظيفة

لا أقوم بتفعيله افتراضياً هنا لأبقى مركزاً على ووردبريس، ولكنه امتداد طبيعي.


نصائح السلامة والأداء والصيانة

أمن

  • ممنوع النشر قم بتوصيل المنفذ 3306 (DB) بالمضيف إذا لم يكن ذلك ضروريًا.
  • احتفظ WP_DEBUG_DISPLAY à false حتى في مرحلة التطوير: يمكنك تجنب "تصحيح الأخطاء" من خلال استخدام HTML معطوب.
  • إذا قمت بعرض الموقع على شبكة (عرض توضيحي للعميل)، فقم بتغيير كلمات المرور وتقييد الوصول.

هاملت

  • استخدام Xdebug كمحفز: هذا بحد ذاته مكسب كبير.
  • تجنب تفعيل التخزين المؤقت المكثف أثناء التطوير (التصغير، والدمج) أثناء تصحيح الأخطاء.
  • Opcache: في بيئة التطوير البحتة، غالبًا ما أتركه معطلاً. إذا كنت ترغب في محاكاة بيئة الإنتاج، فقم بتمكينه ثم تعطيله بشكل صحيح.

الدورية

  • تجميد إصدارات الصور (MariaDB، Redis). تجنب ذلك. latest باستثناء خدمة غير حرجة (يمكن أن يبقى Mailpit في أحدث إصدار).
  • قم بتحديث ووردبريس عبر WP-CLI، وليس عن طريق "النقر": يمكنك الاحتفاظ بسجل ويمكنك أتمتة العملية.

أوامر مفيدة:

# Mettre à jour WP (exemple)
docker compose exec wp wp core update --allow-root

# Vérifier version WP
docker compose exec wp wp core version --allow-root

# Vérifier PHP
docker compose exec wp php -v

# Nettoyer volumes (ATTENTION: supprime la DB)
docker compose down -v

لتذهب أبعد من ذلك

  • إضافة خدمة بريس (أو Adminer) فقط إذا احتاج فريقك إليه. أنا أفضل استخدام WP-CLI مع ملفات SQL، لكن بعض عمليات سير العمل تتطلب واجهة مستخدم.
  • أضف ماكيفيلي (أو justfile) لتوحيد الأوامر (make up, make reset, make test).
  • قم بتنفيذ عملية استيراد "آمنة" لقاعدة البيانات: ملف مضغوط + استبدال عنوان URL عبر wp search-replace مع --skip-columns=guid.
  • أنشئ صورة "إنتاجية" (بدون Xdebug، مع opcache) وصورة "تطويرية". استخدم نفس Compose، مع ملفين تعريف.

الموارد


الأسئلة الشائعة

لماذا التسلق ./wp في الحجم بدلاً من بناء النواة في الصورة؟

لأنك ترغب في التبديل السريع بين إصدارات ووردبريس (6.9.4 → 6.9.5) عبر WP-CLI والتحكم في إصدارات البرامج النصية، بدلاً من إعادة بناء صورة النظام مع كل تحديث. بالنسبة للمشاريع التي تتطلب تحكمًا دقيقًا، يمكنك أيضًا "تخصيص" النواة، لكن هذا أسلوب مختلف.

هل يعمل هذا الإعداد مع PHP 8.1 بالضبط؟

نعم، إذا قمت بتغيير الصورة الأساسية (wordpress:php8.1-apacheأستهدف هنا استخدام PHP 8.3 لأنه يعكس بيئات التشغيل الحالية بشكل أفضل. إذا كانت بيئة الإنتاج لديك تعمل على الإصدار 8.1، فاستخدمه لتجنب أي مفاجآت.

كيف يمكنني استيراد قاعدة بيانات العملاء؟

الأكثر موثوقية: تفريغ قاعدة البيانات SQL + wp db import ثم wp search-replace. مثال :

docker compose exec -T wp wp db import /var/www/html/wp-content/uploads/dump.sql --allow-root
docker compose exec wp wp search-replace 'https://www.site-client.tld' 'http://localhost:8080' --skip-columns=guid --allow-root

Pourquoi WP_CONTENT_URL تم تعيينه في http://localhost/wp-content في نقطة الدخول؟

هذا اختصار قد لا يعمل بشكل صحيح إذا قمت بتغيير المنفذ. إذا كنت تستخدم منفذًا مختلفًا، فقم بتعديله (أو احذف هذين الثابتين ودع ووردبريس يتولى الأمر). لقد رأيته يحل حالات معقدة حيث تقوم خوادم البروكسي العكسية المحلية بإعادة كتابة العناوين، ولكنه ليس إلزاميًا.

يعمل Redis، لكن WordPress لا يستخدمه. هل هذا طبيعي؟

نعم. لا يكفي استخدام Redis من جانب الخادم: أنت بحاجة إلى إضافة تخزين مؤقت للكائنات (على سبيل المثال). redis-cacheوبحسب الإضافة، يتم تفعيل التخزين المؤقت عبر WP-CLI أو لوحة التحكم. أثناء التطوير، أقوم بتثبيته ولكن لا أقوم بتفعيله دائمًا.

لماذا نستخدم أداة مراقبة الاستعلامات في التطوير؟

لأنها أسرع طريقة لتحديد الطلبات البطيئة، أو استدعاءات الخطافات المتكررة، أو استدعاءات HTTP المُعطِّلة. في مواقع البناء، غالبًا ما يكون هذا هو الفرق بين "أظن" و"أعرف".

أرى عبارة "تم رفض الإذن" على الملفات في wp-content. ما العمل ؟

يحدث هذا عندما لا تتطابق معرفات المستخدم/المجموعة (UID/GID) للمضيف والحاوية (خاصةً على نظام لينكس). الحل الأمثل هو تشغيل Apache/PHP باستخدام المستخدم المناسب، أو تصحيح أذونات مجلد مشروعك. تجنب هذا الخطأ التلقائي. chmod -R 777.

هل يمكنني استخدام Nginx بدلاً من Apache؟

نعم، ولكن سيتعين عليك إدارة PHP-FPM، وإعدادات Nginx، وقواعد الروابط الدائمة. يُعدّ Apache أسهل في تطبيقه لإنشاء برنامج تعليمي قابل للتكرار، خاصةً إذا كنت ترغب في تقليل الاختلافات بين البيئات.

كيف يمكنني التحقق من الإصدار الدقيق لـ WordPress الموجود في الحاوية؟

docker compose exec wp wp core version --allow-root

أين يمكنني تتبع التغييرات الأساسية إذا تغير سلوك ما بين الإصدارين 6.9.x و 6.10؟

تحقق من التذاكر والالتزامات على Trac و GitHub: