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 :
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 :
Or for the sidebar :
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/.htpasswdAuthName "Admin only"AuthType Basicrequire 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
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
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
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
9 March 2009
Ok sorry, I corrected the reference !
15 h 01 min
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
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
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
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
23 June 2009
Great post! Just what I’ve been looking for!
21 h 59 min
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
17 July 2009
Do you still plan on publishing this?
23 h 19 min
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
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
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
8 August 2009
Hi,
Any idea of if the plugin will be released some day?
11 h 21 min
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
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
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
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
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
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
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
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
10 November 2009
Thanks Fabrice,
My apache mod_rewrite module was not enabled. Now it is enabled, and everything works !!
22 h 00 min
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
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 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
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
4 March 2010
The easiest way to fix l10ni18n conflict is to replace all “__(” in WordPress source files with “__l18n(“.
15 h 29 min
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
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