johnpbloch/wordpress-core
Composer-friendly WordPress core package that mirrors official releases for dependency management. Use it to pull WordPress into PHP/Laravel projects or custom setups, pin versions, and keep updates predictable without manually downloading or committing core files.
Installation
composer require johnpbloch/wordpress-core
Add to composer.json under autoload:
"autoload": {
"psr-4": {
"App\\": "app/",
"WordPress\\": "vendor/johnpbloch/wordpress-core/src/"
}
}
Run composer dump-autoload.
Bootstrapping WordPress
Initialize WordPress in a Laravel service provider (e.g., WordPressServiceProvider):
use WordPress\Core\Bootstrap;
public function boot()
{
$wp = Bootstrap::init(
base_path('wp'), // Path to WordPress core files
base_path('public/wp-content') // Path to wp-content
);
}
First Use Case: Querying Posts
Use the WP_Query wrapper to fetch posts:
use WordPress\Query\WP_Query;
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 5,
]);
foreach ($query->posts as $post) {
echo $post->post_title;
}
Key Files to Explore
src/Query/: WP_Query wrapper and helpers.src/Post/: Post type and post object utilities.src/Plugin/: Plugin management utilities.src/Theme/: Theme management utilities.Use the package to interact with WordPress APIs while keeping Laravel’s dependency injection:
// In a Laravel controller
public function __construct(private WP_Query $wpQuery) {}
public function index()
{
$posts = $this->wpQuery->getPosts(['posts_per_page' => 3]);
return view('posts.index', compact('posts'));
}
Register custom post types or taxonomies via Laravel’s service container:
$this->app->singleton('wp-post-type', function () {
return new \WordPress\Post\PostType([
'name' => 'portfolio',
'labels' => ['singular_name' => 'Portfolio'],
]);
});
Load WordPress plugins/themes dynamically:
use WordPress\Plugin\Plugin;
$plugin = new Plugin('akismet/akismet.php');
$plugin->activate();
Use Laravel middleware to handle WordPress-specific logic:
Route::get('/wp-admin', function () {
// WordPress admin logic
})->middleware(\WordPress\Core\Middleware\WordPressAdmin::class);
Render WordPress templates in Laravel views:
// In a Laravel Blade template
@php
$wpTemplate = new \WordPress\Theme\Template('page.php');
echo $wpTemplate->render(['post' => $post]);
@endphp
Use Laravel’s router to proxy requests to WordPress:
Route::get('/blog/{slug}', function ($slug) {
$post = \WordPress\Query\WP_Query::getPostBySlug($slug);
return view('blog.post', compact('post'));
})->where('slug', '.*');
Leverage Laravel’s Eloquent for WordPress data:
class Post extends Model
{
protected $table = 'wp_posts';
}
Use the package’s WP_Query for complex queries where Eloquent falls short.
Cache WordPress queries in Laravel’s cache:
$posts = Cache::remember('wp_posts_homepage', now()->addHours(1), function () {
return WP_Query::getPosts(['posts_per_page' => 5]);
});
Listen to WordPress actions/filters via Laravel events:
// In EventServiceProvider
protected $listen = [
\WordPress\Core\Events\WordPressInitialized::class => [
\App\Listeners\SetupWordPress::class,
],
];
Bootstrap::init() is called once (e.g., in a service provider’s boot()). Use Laravel’s container to singleton the WordPress instance:
$this->app->singleton('wp', function () {
return Bootstrap::init(base_path('wp'), base_path('public/wp-content'));
});
wp_). Mismatches cause queries to fail.wp-config.php or override it in the package’s config:
$wp->setTablePrefix('laravel_');
wp-content must exist in the filesystem.Storage::disk('public')->symlink(
'wp-content/plugins/akismet',
'wp-content/plugins/akismet-link'
);
define('WP_USE_THEMES', false); // Disable themes if not needed
add_action('wp_loaded', ...) for hooks that should run after WordPress is fully loaded.Add to wp-config.php (or Laravel’s .env):
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true); // Logs to wp-content/debug.log
define('WP_DEBUG_DISPLAY', false);
Use the package’s query logger:
\WordPress\Query\WP_Query::enableLogging();
Check logs in storage/logs/wordpress.log.
Test WordPress interactions in isolation using Laravel’s fresh() or withoutEvents():
$response = $this->withoutMiddleware()->get('/blog/test');
Extend the WP_Query class to add Laravel-flavored methods:
namespace App\Extensions;
use WordPress\Query\WP_Query as BaseQuery;
class WP_Query extends BaseQuery
{
public function withEloquent()
{
return $this->posts()->get()->toBase();
}
}
Create a dedicated provider for WordPress-specific bindings:
namespace App\Providers;
use WordPress\Core\Bootstrap;
class WordPressServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('wp', function () {
return Bootstrap::init(...);
});
}
}
Combine Laravel’s auth with WordPress users:
use WordPress\User\User;
$wpUser = User::getByEmail('user@example.com');
$laravelUser = User::firstOrCreate(['email' => 'user@example.com']);
Override WordPress’s transients with Laravel’s cache:
add_filter('pre_option_transient_', function ($value, $option) {
return Cache::get("wp_transient_{$option}");
}, 10, 2);
Use Laravel Echo/Pusher to push WordPress changes in real-time:
add_action('save_post', function ($postId) {
event(new \App\Events\PostUpdated($postId));
});
How can I help you explore Laravel packages today?