Home > Symfony, WordPress > Integrating Symfony and WordPress

Integrating Symfony and WordPress

December 2nd, 2009 nick Leave a comment Go to comments

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;
Categories: Symfony, WordPress Tags:
  1. udowsky
    December 4th, 2009 at 05:34 | #1

    for some reason when I activate the RewriteRule in .htaccess I get 404’s even for css-files, images etc… :-/
    do you have an idea? – THX!

  2. udowsky
    December 4th, 2009 at 13:53 | #2

    made a clean install in the meanwhile. Unsuccessful. Clean means.. I took a new symfony project, and proceeded as you described. No chance. still the same problem. Either a 500 or a 404. Meanwhile its sort of urgent to get it running. :-/ TIA!

    • March 12th, 2010 at 03:15 | #3

      Sorry for the late response, WP wasn’t notifying me of new comments.

      As far as your 404s are concerned what does your .htaccess look like? Make sure WordPress doesn’t install it’s .htaccess, it isn’t necessary. The modifications I suggested only apply to requests that aren’t going to files that already exist (the same as Symfony’s rules) so they shouldn’t get in the way of your assets (JS, CSS, images, etc).

      What errors are you getting from your dev controller when you get the 500 responses?

  3. December 9th, 2009 at 07:20 | #4

    Hi Nick,

    Good tutorial.. I’ve gotten to the point where everything is integrated. However, now when trying to wrap my layout.php around the Wordpress content, all I’m getting back is plain text of HTML rather than the HTML itself. Is there something tricky I have to do, to get symfony to render the text as HTML within the layout?

  4. December 9th, 2009 at 09:21 | #5

    Me again,

    On a side note – simply echo $blog – gives me HTML in plain text. However if I take that out of indexSuccess.php and replace it with the has_posts() loop (found in content/themes/default/index.php) directly into the indexSuccess.php file – I get the content rendered successfully – however, I’m not sure that’s how it’s suppose to work. Any ideas?

    • March 12th, 2010 at 03:15 | #6

      It sounds like output escaping is turned on in your application. In your template try this instead:

      < ?php echo $sf_data->getRaw(’blog’); ?>

      That should give you the unescaped WordPress HTML.

  5. January 12th, 2010 at 04:28 | #7

    Step 4 : You may get a find error, so try
    find . -name ‘*.php’ -print | xargs sed -i ’s/\(\b\)esc_js\(\b\)/\1wp_esc_js\2/g’

  6. January 12th, 2010 at 08:45 | #8

    Config : Symfony 1.2 – WP 2.9.1

    For some reason, the wrapping up (step 6) did not work for me.

    • March 12th, 2010 at 03:34 | #9

      Paulo posted a much cleaner solution for step 6 that should fix your issue, I’ve updated the guide with the new method.

  7. January 12th, 2010 at 09:30 | #10

    Tip/Hack : Instead of going through all template files for removing the calls to get_header(), get_footer(), you can empty header.php and footer.php. Not really clean but time saving

  8. sfmajdi
    February 9th, 2010 at 08:31 | #11

    hello!
    I follow youe steps but when I run …./frontend_dev.pho/wordpress or …./wordpress_dev.php/wordpress I v got Fatal error: Call to undefined function wp() in C:\wamp\www\avocat1.0\apps\frontend\modules\wordpress\actions\actions.class.php on line 20
    please help I need to use the wordpress blog in my symfony project
    sf 1.2 doctrine
    wp 8.6 or 9.1

    • March 12th, 2010 at 03:13 | #12

      Sorry for the late response, WP wasn’t notifying me of new comments.

      It sounds like the WordPress functions aren’t getting registered, you’ll need to make sure you’re accessing the blog from a URL that goes through your WordPress web controllers (wordpress.php and wordpress_dev.php in my example). Accessing it from frontend_dev.php won’t work unless you’ve also included the require_once() call for wp-load in there as well. Your request to wordpress_dev.php should have worked correctly as long as you performed Step 2 on it as well. What happens when you access it from just /wordpress/?

  9. February 9th, 2010 at 16:12 | #13

    the __() function of Wordpress and symfony are conflicting..

    Here is the solution :

    rename wordpress’ __() function to wp__()

    with Mac OS X, inside the wordpress folder, launch the find & replace function :
    find . -name ‘*.php’ | xargs perl -pi -e ’s/(\b)__(\b)/\1wp__\2/g’

    • March 12th, 2010 at 03:15 | #14

      Thanks for all of the useful tips, I’ve updated the relevant sections in the article. For the Wrapping Up portion, do you still get redirected to the root directory (/)? It may sound strange but clearing your cookies may fix this for you. Other than that ensure that you changed the right admin_url() call as there are quite a few in that file, you can use the code block I provided as a reference to it’s location.

  10. March 11th, 2010 at 04:03 | #15

    Hello!

    Regarding the Step 6, don’t you think it’s better to add a new rule in the .htaccess like the following?

    RewriteRule ^blog/wp-admin/$ blog/wp-admin/index.php

    This way you don’t have to change wp-login.php . I see it as an interesting alternative.

    Regards,

    • March 12th, 2010 at 03:06 | #16

      Great idea, that will work much more cleanly with upgrades. I’ve updated my post with that solution.

  11. samuel morhaim
    April 2nd, 2010 at 13:32 | #17

    Hi,

    I follow your step by step all the way, but I get the following:

    Accessing myurl.com/wordpress_dev.php/blog results on an error:

    Warning: require_once(/blog/wp-load.php) [function.require-once]: failed to open stream: No such file or directory in /var/www/vhosts/zizzerdog.com/httpdocs/web/wordpress_dev.php on line 13

    Fatal error: require_once() [function.require]: Failed opening required ‘/blog/wp-load.php’ (include_path=’.:/usr/share/pear:/usr/share/php’) in /var/www/vhosts/zizzerdog.com/httpdocs/web/wordpress_dev.php on line 13

    So I go and try to fix the path… so removing from Step 2 line 7, first “slash”
    from: require_once(sfConfig::get(’sf_web_dir’) . ‘/blog/wp-load.php’);
    to: require_once(sfConfig::get(’sf_web_dir’) . ‘blog/wp-load.php’);

    Gets me this error

    Empty module and/or action after parsing the URL “/wp-admin/install.php” (/).

    Also accesing myurl.com/wordpress gives my a symfony error.. wordpress_dev.php gives me same symfony error, no debugging or anything..
    Can you help ?

    thank you.

    • April 2nd, 2010 at 19:18 | #18

      It sounds like your sf_web_dir configuration setting isn’t set at the time that you’re trying to load the wp-load.php, it should be loaded after your ProjectConfiguration::getApplicationConfiguration() call is made.

      As far as the “empty module and/or action” error I can’t be sure, are you generating a link to that URL using the link_to or url_for helpers?

      I checked your site and it seems like it’s working now, it’d be great if you could post your solution in case someone else runs in to this issue.

  12. April 12th, 2010 at 14:24 | #19

    FWIW, I got this working without issue in Symfony 1.0. I had no use for building the Propel models for the Wordpress tables, so I didn’t try to do that. Everything else worked as described. Thanks for your post on this.

  1. No trackbacks yet.