survos / bedrock
Makes a Roots Bedrock WordPress site work under the Symfony CLI local server: directory index, DB_PORT folding, and a shared installer.
Fund package maintenance!
Requires
- php: ^8.3
Requires (Dev)
- phpunit/phpunit: ^11.0|^12.0|^13.0
README
Makes a Roots Bedrock WordPress site work under the
Symfony CLI local server, so a WordPress site can be developed the same way as every
other project in this tree — symfony server:start, a .wip domain through the proxy,
host PHP.
None of what this fixes is a Bedrock problem. Bedrock is fine under Apache, nginx,
Valet, DDEV and Trellis. These are all consequences of serving WordPress with a server
built for single-front-controller applications, which WordPress is not: its front end is
web/index.php, but its admin is a tree of real PHP files behind a directory index.
Install
composer require survos/bedrock
web/index.php
<?php require dirname(__DIR__) . '/vendor/autoload.php'; if ($index = Survos\Bedrock\LocalServer::directoryIndex(__DIR__)) { require $index; return; } define('WP_USE_THEMES', true); require __DIR__ . '/wp/wp-blog-header.php';
Composer's autoloader is idempotent, so wp-config.php requiring it again is a no-op.
The require must stay at global scope. WordPress's admin files set globals —
$title, $parent_file, $submenu_file — and requiring one inside a method would make
those function-local and break the admin in ways that are tedious to trace. That is why
directoryIndex() returns a path instead of including it.
config/application.php
Config::define('DB_HOST', Survos\Bedrock\Env::dbHost(env('DB_HOST'), env('DB_PORT')));
bin/install.sh
source vendor/survos/bedrock/bin/bedrock.sh bedrock::boot bedrock::require_db bedrock::install_core wp rewrite structure '/%postname%/' # ... and whatever else this particular site needs
Sourced rather than executed, so install.sh stays the readable record of how that
site is configured.
What each piece is for
LocalServer::directoryIndex() — Roots' documented nginx config for Bedrock is
try_files $uri $uri/ /index.php?$args. symfony server:start --passthru=index.php
implements $uri and the /index.php fallback but not $uri/, so every URL ending
in a slash reaches WordPress's front controller and /wp/wp-admin/ renders the home page
with a 200. Inert under Apache and nginx, which resolve the directory first.
Env::dbHost() — symfony server:start detects the Docker Compose services of the
project it runs from and injects their connection details prefixed with the service name:
a service called db yields DB_HOST, DB_PORT, DB_USER, DB_NAME, DB_PASSWORD.
Bedrock's dotenv repository is immutable, so those override .env — and WordPress has
no DB_PORT, it wants host:port in DB_HOST. The published port is silently dropped
and WordPress dials 3306. Symptom: mysqli_real_connect(): (HY000/2002): Connection refused on web requests while wp-cli connects fine, the CLI getting no injection.
Inert when the database lives outside the project, which is the better arrangement anyway — with no project Compose service there is nothing to inject at all.
bin/bedrock.sh — the parts of an installer every site shares. bedrock::require_db
checks reachability through mysqli rather than wp db check, which shells out to a
mysqlcheck binary that need not be installed.
Gotcha
The Symfony server passes the environment to php-fpm when it starts. After changing
.env — or after moving the database — a running server keeps serving the old values,
and the site 500s with Access denied for user ... while wp-cli works fine. Restart it:
symfony server:stop && symfony server:start -d