Integrating Symfony and WordPress
First off I want to thank this post for getting me started on this project, it saved me a lot of time and most of the steps here are just adjustments to their implementation.
This guide will be geared toward symfony 1.2 and WordPress 2.8+. Earlier versions of WordPress should integrate in relatively the same way but earlier versions of Symfony may cause complications.
The Implementation: This integration works by creating a custom web controller that pre-loads all of the WordPress functionality alongside Symfony. Next we dedicate a module to pass-through all of the WP requests which then uses output buffering to capture the WordPress rendering of the request without a WordPress theme and places it within the Symfony application’s layout.
The Result: Have all of the functionality and extensibility of WordPress within a Symfony application, rendered in your Symfony application’s layout for consistency and having access to the functionality from both platforms.
Step 1: Install WordPress
Download the latest WordPress and extract it into the web folder that you’ll be serving it out of. Integrating an existing WordPress installation? Just copy the entire installation’s containing folder into the web directory of your Symfony app. For the purposes of this guide I’ll be setting it up in /blog/ so after this step I’ll have:
web/blog/index.php web/blog/license.txt web/blog/readme.html ...
Step 2: Create a Dedicated Web Controller
Copy your application’s production and dev web controllers (for this example I’m using an application called ‘frontend’ so my controllers would be index.php and frontend_dev.php) to wordpress.php and wordpress_dev.php respectively. In each of these new files add the following two lines just after loading the application configuration:
define('WP_USE_THEMES', true);
require_once(sfConfig::get('sf_web_dir') . '/blog/wp-load.php');
For reference, my complete production controller becomes:
<?php
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', false);
define('WP_USE_THEMES', true);
require_once(sfConfig::get('sf_web_dir') . '/blog/wp-load.php');
sfContext::createInstance($configuration)->dispatch();
Add URL redirection for your controller by adding the following to your web root’s .htaccess file before your index.php controller redirection (line 15 of the default Symfony .htaccess file) :
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^blog/(.*)$ wordpress.php [QSA,L]
Step 3: Create the Module
Create a new module in your application:
./symfony init-module frontend wordpress
Adjust the routing.yml (apps/frontend/config/routing.yml) to include the following:
blog:
url: /blog/*
param: { module: wordpress, action: index }
Change your index action (apps/frontend/modules/wordpress/actions/actions.class.php) to be the following:
public function executeIndex(sfWebRequest $request) {
ob_start();
wp();
require_once( ABSPATH . WPINC . '/template-loader.php' );
if (function_exists('is_feed') && is_feed()) {
ob_end_flush();
throw new sfStopException();
}
else {
$this->blog = ob_get_clean();
}
}
Set your index template (apps/frontend/modules/wordpress/templates/indexSuccess.php) to include:
<?php echo $blog ?>
Step 4: Resolving Conflicts
Right now we have everything we need to let both of the systems work together except for one very large issue: both Symfony and WordPress define and use the functions esc_js() and __(). To resolve this I chose to rename WordPress’s versions to wp_esc_js and wp__ respectively. On a Unix system the following shell commands can be run from the top level of your wordpress installation directory to rename all instances of these functions:
find . -name *.php -print | xargs sed -i 's/\(\b\)esc_js\(\b\)/\1wp_esc_js\2/g' find . -name *.php -print | xargs sed -i 's/\(\b\)__\(\b\)/\1wp__\2/g'
Some systems will return an error from find after running the above, the solution for this is to enclose your search pattern in quotes:
find . -name "*.php" -print | xargs sed -i 's/\(\b\)esc_js\(\b\)/\1wp_esc_js\2/g' find . -name "*.php" -print | xargs sed -i 's/\(\b\)__\(\b\)/\1wp__\2/g'
Run this as many times as you’d like on the same files without incident.
In the comments Ka has provided the following for the above on OSX:
find . -name ‘*.php’ | xargs perl -pi -e ’s/(\b)__(\b)/\1wp__\2/g’
Step 5: The Layout
WordPress is still rendering all of it’s content in the configured WordPress theme and then that is all being rendered in your Symfony layout. One option here is to empty your theme’s header.php, footer.php and sidebar.php files. Another approach involves removing all calls to your theme’s get_header(), get_footer() and get_sidebar() functions, the following list should encompass all of the files you’ll need to change:
404.php archive.php archives.php image.php index.php links.php page.php search.php single.php
The good news is that you have access to all of these functions from within your Symfony application’s layout. For example, to include the sidebar back in to your blog pages simply add the following to your current Symfony layout:
<?php if(function_exists('get_sidebar')) { get_sidebar(); } ?>
Step 6: Wrapping Up
Run through the WordPress guided installation if you haven’t already. When you’re finished and logged in you’ll notice that redirecting to the admin homepage after logging in gives you a Symfony 404. This can be corrected by adding the following to your .htaccess file, replacing ‘blog’ with your WordPress installation directory:
RewriteRule ^blog/wp-admin/$ blog/wp-admin/index.php
Step 7: A Model
Run the following to generate a schema from the new WordPress tables:
Propel:
./symfony propel:build-schema
Doctrine:
./symfony doctrine:build-schema
To get proper export and import through fixtures files be sure to add the table references in to the generated schema, they won’t be generated from the previous commands. As of right now this includes:
wp_postmeta.post_id
wp_term_relationships.object_id
wp_term_relationships.term_taxonomy_id
wp_term_taxonomy.term_id
wp_usermeta.user_id
Another note: I can only attest to this issue occurring in a Propel setup but there are 3 primary keys on the wp_options table and Propel doesn’t seem to be able to determine a proper identifier for these objects when generating fixtures so they all overwrite each other until you only end up with one object. To correct this I remove the primaryKey:true attribute on everything but the option_id column for this table and I suggest you do the same.
Enjoy
That’s it, you’re all set. If you have any comments or questions or if you found this guide useful please feel free to leave a comment below.
In The Future
Eventually you’re going to want to upgrade WordPress to another version. After you’ve installed the upgrade re-apply steps 4 and 7 and you’ll be good to go.
My thanks to Ka and Paulo for their improvements to this guide.
Update: After playing with the Trackback piece of WordPress it seems that this feature needs some tweaking to work with the set up above. To get trackback support again add the following to the top of the ‘wp-trackback.php’ in your WordPress installation directory:
global $wp, $wpdb, $posts, $wp_query;