johnpbloch/wordpress-core-installer
Composer plugin that installs WordPress core outside vendor, designed for setups with WordPress in a subdirectory and wp-content moved elsewhere to avoid updates wiping content. Supports custom install paths via wordpress-install-dir.
Add to composer.json:
{
"require": {
"johnpbloch/wordpress-core-installer": "^2.0"
},
"extra": {
"installer-paths": {
"wp/": ["type:wordpress-core"]
}
}
}
wp/ with your desired WordPress root directory (e.g., wordpress/).Initialize WordPress Core:
composer require wordpress/wordpress:^5.0
vendor/ into the specified directory (e.g., wp/).Configure wp-config.php:
wp-config.php to your WordPress root (e.g., wp/wp-config.php).WP_CONTENT_DIR and WP_CONTENT_URL to point to a custom location (e.g., ../wp-content):
define('WP_CONTENT_DIR', dirname(__DIR__) . '/wp-content');
define('WP_CONTENT_URL', '/wp-content');
Symlink wp-content (Optional but Recommended):
ln -s ../wp-content wp/wp-content
wp-content is outside the WordPress core directory and preserved during updates.Verify Installation:
composer install and check that WordPress files are installed in wp/ but wp-content remains untouched.If integrating with Laravel (e.g., for a headless WordPress setup):
/project-root
├── laravel/ # Laravel app
├── wp/ # WordPress core (managed by this package)
└── wp-content/ # Shared or separate wp-content
laravel/routes/web.php, proxy WordPress requests:
Route::prefix('wp')->group(function () {
require __DIR__ . '/../wp/wp-load.php';
});
composer update wordpress/wordpress to update WordPress core without touching wp-content.vendor/ remains separate, avoiding conflicts.Installation:
composer require wordpress/wordpress:^5.0
wp/ (or custom path via wordpress-install-dir in extra).Updates:
composer update wordpress/wordpress
wp-content is preserved due to symlinking.Custom Paths:
Override default wp/ location in composer.json:
"extra": {
"wordpress-install-dir": "custom-wordpress-path"
}
/wp using this package.index.php to route /wp* to WordPress:
if (file_exists($wp = __DIR__ . '/../wp/wp-load.php')) {
require $wp;
}
Route::prefix('wp') for WordPress admin routes.wp-contentstorage/app/wp-content to WordPress:
ln -s ../../laravel/storage/app/wp-content wp/wp-content
wp-config.php:
define('WP_CONTENT_DIR', dirname(__DIR__) . '/laravel/storage/app/wp-content');
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-php@v3
- run: composer install --prefer-dist --no-dev
- run: composer update wordpress/wordpress
- run: php laravel/artisan config:clear
env() to dynamically set WordPress paths:
define('WP_CONTENT_DIR', base_path('wp-content-' . env('APP_ENV')));
composer.json:
"extra": {
"wordpress-install-dir": {
"wordpress/wordpress": {
"dev": "wp-dev",
"prod": "wp-prod"
}
}
}
Symlink Issues:
wp-content symlinks may break if Laravel’s storage/ is excluded from Git or not writable.wp-content is a relative symlink (not absolute) and test permissions:
ln -s ../wp-content wp/wp-content # Relative path
chmod -R 755 wp/wp-content
Composer Autoload Conflicts:
WP_Query vs. Laravel’s QueryBuilder).ClassLoader to exclude WordPress files:
$loader = require __DIR__ . '/../vendor/autoload.php';
$loader->addPsr4('App\\', __DIR__ . '/../laravel/app');
$loader->addPsr4('WordPress\\', __DIR__ . '/../wp/wp-includes');
WordPress Updates Breaking Laravel:
wp-includes/load.php, breaking Laravel’s bootstrap.composer why-not wordpress/wordpress to debug constraints.GPL License Conflicts:
wp-php.Stale Package Maintenance:
composer.json):
"require": {
"composer/composer": "^2.0",
"php": "^8.0"
}
Verify Installation:
composer show wordpress/wordpress
ls -la wp/ # Check if WordPress files exist
Check Symlinks:
ls -la wp/wp-content # Should point to ../wp-content
Composer Debug:
composer diagnose
composer why wordpress/wordpress
WordPress Bootstrap Issues:
wp/wp-load.php exists.wp/wp-config.php.wp/ directory (chmod -R 755 wp/).Custom Installer Logic: Extend the installer by creating a custom Composer plugin:
namespace App\Composer;
use Composer\Plugin\PluginInterface;
use Composer\IO\IOInterface;
use Composer\Composer;
class WordPressCustomizer implements PluginInterface {
public function activate(Composer $composer, IOInterface $io) {
// Hook into WordPress installation
}
}
Post-Install Scripts:
Use Composer’s post-install-cmd to run Laravel migrations after WordPress install:
"scripts": {
"post-install-cmd": [
"php laravel/artisan migrate --force",
"php laravel/artisan wp:setup" // Custom Artisan command
]
}
Dynamic Paths:
Use Laravel’s config() to set WordPress paths dynamically:
define('WP_CONTENT_DIR', config('wordpress.content_dir'));
Configure in .env:
WORDPRESS_CONTENT_DIR=/path
How can I help you explore Laravel packages today?