Wednesday, February 3, 2010

Bootstrapping Lithium for a more flexible location

I've just started fiddling around with Lithium, a relatively new PHP framework that takes advantage of namespaces in PHP 5.3. At the time of writing, I'm playing with the 0.5 release.

Lithium, and it's collaborators, has it's roots in CakePHP, a framework that has pulled me out of the PHP doldrums, with a reasonable MVC implementation that is easy to get running, and doesn't make me want to tear my eyes out.

Lithium as also introduced me to Git, a distributed source control tool that has been around for quite a while, but is gathering momentum (if it hasn't done so already), as a replacement for SVN.

One of the things I look at when developing a new project is the ability for multiple developers to share the code base without having to configure it specifically for their environment. For example, there are only three developers where I work, and we use SVN. It's nice to be able to checkout a project, set up a virtual host on our local Apache installations and hook into the same database for development or bug fixing. If we're all running off the same codebase, then I shouldn't have to have a specific config just for my machine. In the few cases were we do, we usually have some sort of config directory with configs for each of our systems, so at least that is committed to the SVN, and we make sure the appropriate config file is installed for the particular environment.

One of the things that I want to do, and haven't been able to do with CakePHP, is keep the framework separate from the application, to the point where I do not check in the framework with the project. With Lithium, it looks like this a possible thing to do. I'm hoping that I'll be able to use the PHP include_path to control which version of the Lithium framework the application uses, and then I can use Git to update the Lithium frame, and Git on my application, and neither have to worry about getting in the way of each other.

I should point out that most, if not all, of my development work is done on a Windows XP machine. However, most of my production deployments go to Linux installs. Platform independence is something I always look for, but it's not always a given. I'm pointing this out, just in case you see something that doesn't look right.

My first task, before starting on my new app, was to get Lithium. After a little messing around with ssh keys, I managed to clone the Git rep into c:\dev\libs\lithium.

I then created the basics of my project in c:\dev\projects\appbook and copied the c:\dev\libs\lithium\app directory in there. I also set myself up a VirtualHost in Apache for the app, and set the include path to include the Lithium framework.

php_value include_path .;c:\dev\libs\lithium

The next part was to change the bootstrap so that the include_path could be used. The default bootstrap assumes that the libraries directory containing the Lithium framework will be at the same level as the app directory. In my case, that's not so. What I didn't want to do is hardcode the directory of the lithium framework in the bootstrap, because if someone else wants to work on this, then they'll have to customize the bootstrap for their environment.

So, I let the include_path do it's job and changed the one liner for the LITHIUM_LIBRARY_PATH to the following:

$path = dirname(dirname(__DIR__));
$libraries = realpath_ip(DS.'libraries'.DS.'lithium'.DS.'core'.DS.'Libraries.php');
if ($libraries) {
  $path = str_replace(DS.'libraries'.DS.'lithium'.DS.'core'.DS.'Libraries.php', '', $libraries);
define('LITHIUM_LIBRARY_PATH', $path.DS.'libraries');

This makes the system lookup a known Lithium framework file on the include_path, and determines the appropriate real path from there. realpath_ip() is a function that mimics what realpath does, except it searches the include_path. This was an adaption of file_exists_ip(), a handy bit of code found in the comments of file_exists().

I had to use the platform independent DIRECTORY_SEPARATOR, otherwise the str_replace() would not work.

And here's the function, which I put at the bottom of the app/config/bootstrap.php.

function realpath_ip($filename) {
  if(function_exists("get_include_path")) {
   $include_path = get_include_path();
  } elseif(false !== ($ip = ini_get("include_path"))) {
   $include_path = $ip;
  } else {return false;}
  if(false !== strpos($include_path, PATH_SEPARATOR)) {
   if(false !== ($temp = explode(PATH_SEPARATOR, $include_path)) && count($temp) > 0) {
       for($n = 0; $n < count($temp); $n++) {
           if(false !== @file_exists($temp[$n] . $filename)) {
               return realpath($temp[$n] . $filename);
          return false;
      } else {return false;}
  } elseif(!empty($include_path)) {
      if(false !== @file_exists($include_path)) {
          return realpath($include_path);
      } else {return false;}
  } else {return false;}

I'm hoping that when the next release of Lithium is made in two weeks time, I can update, but not have to make any changes to the work done so far, because of it (unless there are major changes in the framework structure, and I'm using namespaces that no longer exist).

Note to Self: Get a plugin or something to help edit and display PHP code, without having to change greater than or less than signs to html entities.

No comments:

Post a Comment