Integrate WordPress into symfony

6 March 2009

What better topic to start this technical blog about symfony than to talk about my experience of integrating WordPress into symfony !

I was looking for a nice blogging solution for symfony, and all I found was a very simple plugin and a lot of people encouraging me to build my own blog. Even though it is a nice exercise, my philosophy is to not reinvent the wheel. WordPress is surely the best free blogging tool available, so I preferred to spend time integrating it into my symfony application than to create yet another sfVeryEasyBlogPlugin.

Integrating WordPress into symfony can be done in three steps :

  • integrating the blog into the application and its layout
  • merging the authentification system
  • integrating the blogging information into the symfony application

There is a wiki page handling the last two steps : http://trac.symfony-project.org/wiki/HowToIntegrateWordPressAndBbPressWithSymfony, written by Michael Nolan but I actually concentrated my efforts on the first step for the moment and that is what I will describe.

Integrating WordPress into a symfony application and its layout

Install WordPress

We need to store the wordpress files somewhere, I chose to create a new plugin sfWordpressPlugin and put the whole wordpress into the folder

plugins/sfWordpressPlugin/lib/vendor/wordpress

I then created a symbolic link in the web directory called blog pointing to the wordpress directory. That way I was able to run the WordPress configuration and let it create its database

ln -s ../plugins/sfWordpressPlugin/lib/vendor/wordpress web/blog

Create a blog module

We then need a new module, which can be put in the new wordpress plugin and which I called sfWordpress. I enabled it in my frontend application and added the following routing :

blog:
  url:   /blog/*
  param: { module: sfWordpress, action: index }

Create an action that executes WordPress

It now becomes a little tricky. I want to execute WordPress from inside symfony. The goal is to use output_buffering to send the output to the template. I experienced three difficulties :

  • some actions in WordPress output specific headers, such as feed actions, so their output should be sent directly to the browser and not go through the symfony template
  • including the wordpress files inside of a function and using buffering to store the output seemed like an easy solution, unless WordPress used a lot of global constants… which is unfortunately the case ! WordPress has some very bad coding habits, they use a dozen of global variable, and some of them have such stupid names as “$name” which means anyone can override them by error (Me for example…)
  • the __() function of WordPress and symfony are conflicting…

I was able to overcome these difficulties, and here is how my action looks like :

/**
 * intégration de WordPress
 *
 * @param sfWebRequest $request
 * @author fabriceb
 * @since Mar 4, 2009 fabriceb
 */
 public function executeIndex(sfWebRequest $request)
 {
   // Don't load symfony's I18N
   $standard_helpers = sfConfig::get('sf_standard_helpers');
   $standard_helpers = array_diff($standard_helpers, array('I18N'));
   sfConfig::set('sf_standard_helpers', $standard_helpers);

   define('WP_USE_THEMES', true);
   chdir( dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'wordpress' );
   global $wpdb;
   ob_start();
   require_once( 'wp-blog-header.php' );
   $this->blog = ob_get_contents();
   if (function_exists('is_feed') && is_feed())
   {
     ob_end_flush();
     throw new sfStopException();
   }
   else
   {
     ob_end_clean();
   }
 }

And I had to hack the wp-blog-header.php file to solve the problem of all the global variables :

require_once( dirname(__FILE__) . '/wp-load.php' );

 // @HACK FABRICE
 // All variables defined here are considered global by WordPress
 $local_global_vars = get_defined_vars();
 foreach($local_global_vars as $local_name => $local_value)
 {
   $GLOBALS[$local_name] = $local_value;
 }
 // Don't create new global variables ourselves, and do not overwrite other global variables, for example $name...
 unset($local_name, $local_value, $local_global_vars);
 // @HACK FABRICE

 wp();

 // @HACK Fabrice
 global $posts;
 // @HACK Fabrice

 require_once( ABSPATH . WPINC . '/template-loader.php' );

My only small disappointment for the moment is that I did not solve the I18N conflict, I just avoided it. I will try to come back on this later to fnd a real solution… using namespaces for example ? :-)

Integrate WordPress view in the symfony view

I created a new theme in my WordPress, based on the default one, which goal was to output only the content of the blog without the layout. It is actually quite easy to do, you just go in each of the php files of the template and you remove all the references to the following functions :

  • get_header()
  • get_footer()
  • get_sidebar()

That way, the output of WordPress stored in the buffer is just the main content stripped out of the layout.

After that, the indexSuccess.php in the sfWordpress module is simple :

< ?php echo $blog ?>

However you still want to include WordPress’s header or sidebar in your own layout. To do that I did the changes directly in my layout.php, as for example in the header :

< ?php if (defined('WP_USE_THEMES') && WP_USE_THEMES): ?>
  < ?php get_header(); ?>
< ?php else: ?>
  < ?php include_http_metas() ?>
  < ?php include_metas() ?>
  < ?php include_title() ?>
< ?php endif; ?>

Or for the sidebar :

< ?php if (defined('WP_USE_THEMES') && WP_USE_THEMES): ?> < ?php get_sidebar(); ?> < ?php else : ?> < ?php include_component('reference', 'quickList') ?> < ?php endif; ?>

Secure the application

It was a big surprise to see that every dynamic WordPress file is actually accessible from the web server. I do not feel at ease with this, and I plan on blocking direct access to any of the files. However for the moment I still see two files that are necessary and I have not yet wrapped inside a symfony action :

  • wp-comments-post.php which is used to post the comments
  • xmlrpc.php which is used for the pingbacks

So my philosophy for the moment is to trust WordPress for the frontend files, and block access to all the other directories by including the following .htaccess in each of them :


        AuthUserFile /etc/apache2/.htpasswd
        AuthName "Admin only"
        AuthType Basic
        require valid-user

Url rewriting

If you enable the url rewriting in WordPress there is actually nothing to do, since the symfony routing already routes any /blog/* url to the WordPress action. However you must be very careful about the .htaccess that WordPress automatically generates and which will break everything !

Therefore I created an empty

plugins/sfWordpressPlugin/lib/vendor/wordpress/.htaccess

owned by root and removed any write access for the user www-data

Conclusion (for the moment)

My plan is to publish very soon the work done in a sfWordpressPlugin and work on the next two steps of integration :

  • Merging authentication systems

This should be quite easy to do without a hack, since the whole authentication system of WordPress is overridable by a plugin. I think Eric Kittell actually already did it, let us hope it is opensource.

  • Exchange contents between symfony and WordPress

There are two solutions here, create a schema file for the wordpress database or use the rss file as a web service content provider. Both are interesting.

Please feel free to comment on this work in progress

Filed under : Programming — by @ 16 h 19 min

31 Comment »

  1. COil say :
    9 March 2009

    Hi Fabrice,

    Interesting post, indeed it would be cool to merge the authentication systems.

    To share datas can’t you do a reverse a the database to have the schema and then include it in your plugin ?

    ++ COil

    9 h 56 min

  2. fabriceb say :
    9 March 2009

    I have looked into merging authentication this week-end and it seems very easy. However to do it cleanly I will need a few hours, and to be honest I do not need it for the moment.

    As for exchanging data, there are two ways to do it : Web service using RSS or XMLrpc or direct access to the database. It really depends on what you actually need.

    I had a similar problematic when integrating SugarCRM into symfony. I did as you describe and it worked very well. But :
    - the database structure of SugarCRM is really straightforward. In WordPress I have not studied in depth the versioning of articles but I saw that the same article is stored multiple times in the same table
    - I needed to have full flexibility on selecting and writing data to SugarCRM. I guess that when integrating the data of the blog in the rest of the symfony application, I will simply need “The last 10 posts”, which the simple RSS feed already provides

    However I lack enough real-life examples of integrating WordPress to know which one is better

    12 h 28 min

  3. Steve Daniels say :
    9 March 2009

    Hi,

    http://trac.symfony-project.org/wiki/HowToIntegrateWordPressAndBbPressWithSymfony is actually by Mike Nolan based on this forum post http://forum.symfony-project.org/index.php/m/23710/#msg_23710 cited in the article.

    Steve Daniels

    12 h 58 min

  4. fabriceb say :
    9 March 2009

    Ok sorry, I corrected the reference !

    15 h 01 min

  5. Laurent Bachelier say :
    9 March 2009

    Latest WordPress versions have autosaved “drafts”.
    I found the database structure of WordPress to be quite obscure in general.

    15 h 12 min

  6. Lawrence Krubner say :
    15 March 2009

    This is very good, very useful information. To be able to use WordPress inside a Symfony, project, with an integrated login system, amounts to being able to extend WordPress with the infinite range allowed by Symfony – and that amounts to a major expansion of WordPress. The potential is great.

    3 h 48 min

  7. Closer To The Ideal » Blog Archive » Integrate Wordpress into symfony say :
    15 March 2009

    [...] An impressive effort: I was looking for a nice blogging solution for symfony, and all I found was a very simple plugin and a lot of people encouraging me to build my own blog. Even though it is a nice exercise, my philosophy is to not reinvent the wheel. WordPress is surely the best free blogging tool available, so I preferred to spend time integrating it into my symfony application than to create yet another sfVeryEasyBlogPlugin.Integrating WordPress into symfony can be done in three steps : [...]

    3 h 57 min

  8. Daily Digest for 2009-05-08 | Pedro Trindade say :
    9 May 2009

    [...] Integrate WordPress into symfony « Theodo, développement agile symfony This entry was written by trindade, posted on May 8, 2009 at 11:59 pm, filed under Lifestream. Bookmark the permalink. Follow any comments here with the RSS feed for this post. Post a comment or leave a trackback: Trackback URL. « Daily Digest for 2009-05-07 [...]

    8 h 07 min

  9. Josh N. say :
    23 June 2009

    Great post! Just what I’ve been looking for!

    21 h 59 min

  10. Colin MacDonald say :
    25 June 2009

    I just did this and for anyone else trying it does not seem to work with WordPress 2.8. 2.7 works just fine through Symfony though.

    12 h 41 min

  11. Bob Corsaro say :
    17 July 2009

    Do you still plan on publishing this?

    23 h 19 min

  12. Daniel Holmes say :
    26 July 2009

    I implemnted the content sharing; getting a symfony template to retrieve blog posts via doctrine models. overall it works well once you get your head around the interesting database design choices of wordpress. however there’s one pitfall i’ve come across using doctrine (i didnt have the problem with propel when i tried it a couple of years ago).

    If you generate a schema from the wordpress tables, then rebuild the database using doctrine you’ll run in to some errors. they wont be apparent at first, but one issue i had was that the admin couldnt login to wordpress, but most things seemed to work ok. the issue is that wordpress randomly has some fields in upper case, some in lower (e.g. in the version i have, comments table has comment_ID, user_id, comment_post_ID). Doctrine converts all field names to lower case, and thus when you regenerate the database with some lower case field names, some queries break in wordpress. Still looking for a solution.

    3 h 23 min

  13. realcnbs say :
    4 August 2009

    To make it work with 2.8 version you have to make this change in wp-settings.php on line 630:

    add this:
    $GLOBALS['wp_widget_factory'] = $wp_widget_factory;

    after this:
    $wp_widget_factory =& new WP_Widget_Factory();

    9 h 30 min

  14. realcnbs say :
    4 August 2009

    and in widgets.php add this on line 408:

    $GLOBALS['_wp_deprecated_widgets_callbacks'] = $_wp_deprecated_widgets_callbacks;

    9 h 40 min

  15. Ceddc say :
    8 August 2009

    Hi,

    Any idea of if the plugin will be released some day?

    11 h 21 min

  16. vinz say :
    19 August 2009

    Naughty Plagiarismt : http://www.xavismeh.com/xav-geek/developpement-xav-geek/555-integrer-wordpress-dans-une-application-symfony-part-1/

    7 h 14 min

  17. Fabrice Bernhard say :
    24 August 2009

    The author of this “plagiarism” had the decency to keep my copyright in the source code. Furthermore he explains in the context of symfony 1.2 and I guess a more recent version of WordPress, so I consider this as a contribution.

    I am unfortunately more focused of the Google Maps Plugin and Facebook Connect Plugin, so I do not have the time to adapt the existing blog article into a nice plugin. What I could do however, if somebody is motivated to contribute, is commit my current plugin, and let someone adapt it to the new WordPress version for example. Anybody interested ?

    11 h 46 min

  18. Joris say :
    27 August 2009

    I have an error when I integrate wordpress with symfony :
    Warning: sfWordpressActions::require_once(wp-blog-header.php)

    I follow your tutorial and I don’t understand why symfony can’t open this file…

    Have you any idea ?

    Thanks in advance

    18 h 31 min

  19. Alex say :
    3 September 2009

    Hi Fabrice,
    I work with symfony 1.2.7 and wordpress 2.5. I try to make an integration but I have some errors with the routing of symfony, when I want to navigate in wordpress. (I didn’t remove the references of the functions get_header(), get_footer(), get_sidebar() because I need them for my project)

    Can you help me to find a solution?

    21 h 03 min

  20. Fabrice Bernhard say :
    6 September 2009

    Joris: your error message is completely unclear. Please be more detailed if you want some help and contact me directly fabriceb -at- theodo . fr

    Alex: this is just a tutorial for an integration and requires good technical skills to adapt to every problem. This is also why I have not taken the time to pluginify this, because it is hard to make it generic. For the routing, have you been careful to remove the .htaccess as mentioned ?

    14 h 33 min

  21. nresni say :
    30 September 2009

    Hi Fabrice,

    I just one problem, I’m unable to connect to backoffice with url /blog/wp-login.php

    thank you in advance

    10 h 18 min

  22. Fabrice Bernhard say :
    21 October 2009

    If you have the standard symfony Apache .htaccess and your followed the tutorial /blog/wp-admin/index.php should work.

    9 h 09 min

  23. Anton Stevense say :
    7 November 2009

    Hi Fabrice,

    Nice post. I tried to implement this.
    However when I access localhost/blog, I get the direct result of WordPress (without being passed by symfony).
    If I use localhost/frontend_dev.php/blog then everything works great!

    What am I doing wrong?

    15 h 36 min

  24. Anton Stevense say :
    10 November 2009

    Thanks Fabrice,

    My apache mod_rewrite module was not enabled. Now it is enabled, and everything works !!

    22 h 00 min

  25. Nick say :
    3 December 2009

    First of all, thank you very much for this article, it was extremely helpful. After working with this implementation for some time I started to run into a lot of integration issues when upgrading to newer versions of WordPress until finally I decided to re-approach the entire implementation. As a result I was able to accomplish this same goal but in a way that doesn’t conflict with WordPress’s global variables or Symfony’s internationalization and I’ve written a post about it here: http://blog.codeclarity.com/2009/12/02/integrating-symfony-and-wordpress/. I wanted to be sure and let you know as I reference your article in mine, my implementation is just a few variations on yours and you were paramount in getting me going on this project. Thanks again.

    3 h 10 min

  26. Nick say :
    3 December 2009

    @nresni dit: If I can take your issue to mean that you can in fact get to the wp-login.php page but when you actually log in successfully you get a Symfony 404 page then all you need to do is set the default login page to be /wp-admin/index.php instead of just /wp-admin/. To do this find the following in wp-login.php:

    $redirect_to = admin_url();

    and replace it with:

    $redirect_to = admin_url(‘index.php’);

    and you should be good to go.

    3 h 13 min

  27. Sagar say :
    27 January 2010

    Hi,
    I am getting the below error
    “Fatal error: Call to a member function register_handler() on a non-object in /var/www/html/pslsym_wordpress/plugins/sfWordpressPlugin/lib/vendor/wordpress/wp-includes/media.php on line 1208″

    pls help

    9 h 38 min

  28. ka say :
    10 February 2010

    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’

    0 h 03 min

  29. denis say :
    4 March 2010

    The easiest way to fix l10ni18n conflict is to replace all “__(” in WordPress source files with “__l18n(“.

    15 h 29 min

  30. Steve say :
    18 May 2011

    Hi

    Not sure if this is still being monitored, been a while since the last post. I have followed the tutorial and have an issue.
    I can log in fine but when going to the front on the blog I keep getitng redirected to /wp-admin/install.php

    Any idea what I am missing.

    Cheers

    Steve

    20 h 00 min

  31. Olmo say :
    2 May 2012

    Thanks great tutorial. I had to rename the function “esc_js” in wp, so I renamed it to “wp_esc_js”. Also you need to remove a whole lot of code from header.php from the template to get the titel from wordpress.

    10 h 54 min


Leave an answer