Friday, August 13, 2010

Integrating CakePHP with legacy and CodeIgniter

This post will discuss how I integrated CakePHP with a legacy application that had already been integrated with CodeIgniter.

Motivation


Sometime ago, I decided to make a legacy application a bit more of a pleasure to maintain by introducing MVC. At the time, CodeIgniter offered the best flexibility, with regards to integration in the realms of session management, and models. I was already doing projects in CakePHP, but at the time, it didn't seem to be flexible enough. The might have been back in late 2008.

So I kicked off the initial integration, then ended up passing alot of the work on to my minions (Hi minions!). Earlier this year, my minions ceased being my minions (bye minions. I miss you!) and I've had to do a bit of maintaining and new project work with the legacy application. Having worked so long with CakePHP, CodeIgniter was like a very unwelcome swim in the middle of winter. The thing that erked me the most was the lack of a built in ORM. I went looking for some, but the recommended ones were quite strict, and that wouldn't do for integrating with the legacy database.

I had already done some work with integrating CakePHP with CKEditor (for sessions), and CakePHP 1.3.3 did seem to be at a point where you could use it's models for legacy database table naming conventions. So I bit the bullet, loaded it, and pointed it firmly at the existing CodeIgniter part of the application and said "No more".

Preparation


Unlike my previous article regarding integration of CakePHP and CKEditor, I won't be posting alot of code in this article. If you're going to do something similar with your legacy product, you really need to understand how your legacy app works, and you need to understand how CodeIgniter works (if you're looking at that bit too).

Now, before reading much further, you might want to read about integrating CodeIgniter with a legacy application. This will give you a run down on the existing structure. Then have a read of integrating CakePHP and CKEditor, and a few of the same methods from that will be used here.

The Big Picture


So what's novel about what I'm doing? Well, I've got one existing framework that works with mod_rewrite and some legacy mush that calls the PHP directly, and now I'm about to introduce a second framework that also uses mod_rewrite, and all three will need to be able to share the same session, and not trip up on each others virtual directories. Let us take a look at the directory structure.

To protect the guilty (that would be me), I'm going to give the legacy app an imaginative name, different but not far removed, from the actual name of the legacy application. Lets call it LDS for Legacy Database System. Recently, it had a rename, but hasn't actually been formally rebranded. This has helped a little with the real application. We'll go with a rebranding of NDS for New Database System.

Here's a sample of the tree structure:

src This is the root of the application. It's has an index.php, plus ci_index.php, ci_open.php and ci_close.php that was used for CodeIgniter integration.

src/lds This is the main directory of the legacy application. When I first started, there were only a few subdirectories and a whole bunch of programs sitting in here. Yucko. There are a few more directories now, trying to group like portions of the legacy app, but there are still quite a few programs floating around in there. When you visit the website, you'll visit http://localhost/lds/. Anything with the lds in the URL is legacy code.

src/lds/codeigniter This is the CodeIgniter directory, as per the normal CodeIgniter directory structure.

src/lds/system This is the CodeIgniter system directory, as per the normal CodeIgniter directory structure. There's a libraries and language as well, and whatever others ones are required, however, I thought I'd just list this one so you get the picture.

src/lds/application This is the CodeIgniter application directory for the pure CodeIgniter code. If you were visiting the Documents controller, implemented with CodeIgniter application, you would visit http://localhost/documents.

src/lds/legacy This is the CodeIgniter legacy application directory. It's a cutdown version of src/lds/application used by the legacy application to establish a CI instance for session access.

src/nds This is the main directory of our new CakePHP stuff.

src/nds/cake This is the CakePHP directory.

src/nds/app This is the CakePHP app directory. When you visit the Documents controller, implemented with CakePHP, you would visit http://localhost/nds/documents.

With this arrangement, I didn't have to change the .htaccess file in src.

Having already applied changes to src/nds/app/webroot/index.php to cater for external applications, the next hurdle is logins and session sharing. Since most of the new work will be done with CakePHP, I've decided to use CakePHPs sessions, and allow the legacy app and the CI framework to access them.

CakePHP and the Legacy App


For the legacy app, following the same method as described in the CKEditor article, I modified the code that does the session check to declare an external app, include the src/nds/app/webroot/index.php, import the Session class, start a session and read the Auth.User. For new sessions, I created a new login screen via CakePHP, providing login and logout methods.

As a side note, the legacy app doesn't have tricky ACLs, and users passwords are stored as plain text in the database. To this end, I overloaded the default AuthComponent with a NDSAuthComponent that assigns itself to $this->Auth during initialisation (so you can pretend you're playing with the default Auth component), and does not hash the password.

In my travels, I also came across something else that was interesting. I needed to call session_write_close() during the destruction of the CakeSession object. I thought I'd try and "do it right" by extending CakeSession, and overriding the __destruct() method, and then create a new Session.save type called nds_session. However, database connections seem to be semi-hardcoded for cake_session types only, so I also had to override the __construct() method, and copy the same processing for nds_session, has happens for cake_session, with regards to connecting the default Session class to the datasource. In turn, I then had to replace the SessionComponent with a version that extended NDSSession instead of CakeSession.

That almost covers the easy stuff for CakePHP and the legacy application. There's one last change regarding accessing legacy code. There are some nice library calls in there that I'd like to access from CakePHP, instead of having to copy the code into a Component or Helper or Library and maintain it twice. So I needed a way to include the legacy app on the CakePHP include path.

Also CakePHP and CodeIgniter do not play well together. I could not include both frameworks within the legacy code, so I had to remove all references to CI in the legacy code, and replace them with calls to CakePHP. This was mostly for session calls anyway, so it's no biggy. However, I did have one component, written in CI that handled a recently accessed client list. I haven't dealt with it yet, but will probably have to write an equivalent helper/component in CakePHP.

To do this, I altered src/nds/app/webroot/index.php to define a LDS_ROOT and include it in the include_path setup. I also added some define statements to prevent legacy code from reconnecting to the database if called from CakePHP, since this reconnection tends to ruin the database connection for the remainder of the CakePHP processing.

CakePHP and CodeIgniter


The next part was to integrate CakePHP and CodeIgniter. Unfortunately, these two frameworks to not play well together, so the only real integration I could achieve was to provide a Session class to CodeIgniter that could read CakePHP sessions.

This involves overwriting the CI_Session class with a version that conforms to the method signature of the original CI_Session class, but also handles session management the same way that the CakeSession does. That includes timeouts, Security.salt config, User Agent checking, Session regeneration, and Flash messaging. This is the code I'm not going to give over.

It's a horrible hack.. a mish mash of the CakeSession and CI_Session, using CI for database access, and including the CakePHP String and Set classes for reuse. So while you've not got the code, you've got the basics of how to create your own CI_Session to use CakeSessions.

The End!


Now, I'm still in the middle of the development, and not everything has been tested, especially passing Flash messages from CakePHP to CI and visa versa. Hopefully, it's not something I'll have to deal with, but it's something on the list to take a look at.

I hope this has given you a little insight into how to integrate CakePHP with your legacy application.

No comments:

Post a Comment