Simulating Packages in PHP


Most modern languages have a concept of packages, wherein related classes are stored together. PHP sadly doesn’t have a similar concept. For example in Java we can use the following line to imports all classes from the java.awt.event package.

import java.awt.event.*;


But in the spirit of ‘programming into the language’, what we can do is try to simulate a ‘package-like’ concept in PHP. Take the following directory structure.
package_dir_struct1

So now if we want to import the ‘login.php’ file in our code we must be able to do the following with a newly created ‘import’ function:

import("com.codediesel.Security.login");

This will include the ‘login.php’ file from the ‘com/codediesel/Security’ directory. To include all the files from the ‘Security’ directory we can do the following:

import("com.codediesel.Security.*);

As you can see that this is actually quite different from what the actual package concept implies, which is to import related classes.

Anyways, taking it as a conceptual idea only the code for the import function is shown below:

<?php
 
$basePath = "./";
 
function import($classPath)
{
    global $basePath;
 
    /* If the path ends with a '.*' then include
       all the files in the last given directory.
    */
    if(preg_match('/(\.\*)$/', $classPath))
    {
        $importFilePath = substr($classPath, 0, strlen($classPath) - 2);
        $importFilePath = str_replace(".", "/", $importFilePath);
        $d= dir($basePath . $importFilePath);
 
        while(false !== ($file = $d->read())) 
        {
            /* Reject parent, current directories and sub directories */
            if(($file == '.')  ||
               ($file == '..') ||
               (is_dir($d->path . "/" . $file)))
            {
                continue;
            }
 
            require_once($basePath . $importFilePath . "/" . $file);
        }
    } else {
        /* If a single file is specified */
        $importFile = str_replace(".", "/", $classPath) . ".php";
    	require_once($basePath . $importFile);
    }
}
 
?>

Please note that this is just an idea. Detailed error handling is not included to keep the code conceptually simple to understand. It would be fun if readers could bounce around some thoughts.

Now the Problems

The first main problem is that this is not exactly what the package concept implies. What we are essentially doing is dressing-up the ‘include’ concept in new clothes.

The second is that for multiple file inclusion as shown above, we cannot predict in which sequence the files will be included, which can raise some dependency issues.

Including a large number of files can incur a small performance hit. This can be better handled if developed as a PECL extension.

This site is a digital habitat of Sameer Borate, a freelance web developer working in PHP, MySQL and WordPress. I also provide web scraping services, website design and development and integration of various Open Source API's. Contact me at metapix[at]gmail.com for any new project requirements and price quotes.

9 Responses

1

Topbit

November 5th, 2009 at 4:51 am

This problem was solved some time ago in PHP, and in a manner that can speed up the use of a potential hierarchy of classes, rather than slow it down by including files that are not being used.

It’s AutoLoading, as demonstrated with such tools as Zend_Loader, part of Zend Framework (though it can be used separately to the rest of it)

sameer

November 5th, 2009 at 5:13 am

I’m aware of the Autoload functionality in PHP5; what I wanted to do was to somehow replicate the syntax offered by other languages.

3

Chris Renner

November 5th, 2009 at 8:03 am

Sameer,

I think the question Topbit raises (though it was not stated this plainly), is why is this necessary or even desirable, given that we already have Autoload.

I’m not saying it is or isn’t, I’d just like to see if there’s an answer. I’m curious, but I need to understand the utility of your concept.

sameer

November 5th, 2009 at 8:20 am

To put it plainly, I just needed to bring the Java package concept as-it-is to php, it doesn’t have to have any immediate utility in the daily practices of a programmer. Autoload is a nice thing, albeit somewhat retrofitted into php. Anyways its just an idea, if it doesn’t work than so be it.

5

jozef

November 5th, 2009 at 8:23 am

Doctrine 2.0 has class loader for PHP 5.3 namespace separator “\” . It is also compatible with Zend/Pear naming conventions.
http://www.doctrine-project.org
look at file Doctrine\Common\ClassLoader.php

6

Giorgio Sironi

November 5th, 2009 at 11:29 am

I also think now you can implement the package structure with namespaces (Doctrine\Entity\Manager). Though, it was already present as a convention for class names (Zend_Auth_Adapter for instance).

7

Ian

November 5th, 2009 at 2:31 pm

You may also want to look int PHAR packages, which have been around for a while as a PECL extension, but the functionality is baked in to PHP 5.3 by default now. Granted the concept is more or less jars for PHP, but you can use them to bundle similar classes together and load them in one fell swoop if you’re clever about it.

Also, it might also be worth looking into SPL autoloaders rather than the vanilla __autoload magic if you’re not familiar with them. You could write specific functions that load classes from well known file system hierarchies, thus eliminating the need for an “import” kinda thing, but still being able to maintain the java-like package structure you enjoy. The nice thing about SPL auto loaders is that you can define as many as you want, and PHP will execute all the autoloaders you’ve defined and registered before throwing up a class doesn’t exist error (to put it crudely). They’re also faster than regular ol’ __autoload, so there’s no huge penalty (outside filesystem access) for using them :)

That’s my 2 cents

8

Nicolas BUI

November 5th, 2009 at 4:45 pm

Hi,

I had my own library that has lots of features from Java. And “import” is one of primary features.

In real life use, I’ve split to different method as follow :

class Tine {
static function importClass( $class )
{
$class_path = trim( strtr( $class, ‘.*’, ‘/ ‘ ) );
$class_name = strtolower( basename( $class_path ) );
$class_path = TINE_CLASS_PATH . ‘/’ . $class_path . ‘.php’;
if ( file_exists( $class_path ) && is_file( $class_path ) && !class_exists( $class_name ) )
{
require( $class_path );
}
}

static function importPackage( $package )
{
$class_path = TINE_CLASS_PATH . ‘/’.trim( strtr( $package, ‘.*’, ‘/ ‘) );
self::importFiles( $class_path );
}

static function importFiles( $folder, $create_instance = false )
{
if ( file_exists( $folder ) && is_dir( $folder ) )
{
$append = substr( $folder, -1, 1 ) == ‘/’ ? ” : ‘/’;
$handle = opendir( $folder );
if ( is_resource( $handle ) )
{
while ( true == ( $file = readdir( $handle ) ) )
{
$full_file = $folder . $append . $file;
if ( is_file( $full_file ) )
{
$res = null;
$ext = substr( $file, -4, 4 );
$class_name = substr( $file, 0, -4 );
if ( 0 == strcasecmp( $ext, ‘.php’ ) )
{
$class_exists = class_exists( $class_name );
if ( !$class_exists )
{
require( $folder . $append . $file );
}
if ( $create_instance && $class_exists )
{
new $res[1];
}
}
}
}
closedir( $handle );
}
return;
}
}

static function importFolder( $folder, $create_instance = false )
{
self::importFiles( $folder, $create_instance );
}

static function importFile( $file, $once = true )
{
if( $once )
require_once( $file );
else
require( $file );
}

static function import($class)
{
$class = str_replace( ‘ ‘, ”, $class );
$pos = strpos( $class, ‘/’ );
if ( $pos === false )
{
if ( $class{ strlen( $class ) – 1 } == ‘*’ )
self::importPackage( $class );
else
self::importClass( $class );
}
else
{
if ( is_dir( $class ) )
self::importFiles( $class );
else
self::importFile( $class );
}
}
}

Most of application developped for my clients, use more than 100 classes. So to gain performance, I've found a little way to load faster : As the main goal is to load fewer files, i use a import tracker to generate a file containing most imported files, something like:

static function trackFile( $path ) {
if ( defined('TINE_CLASS_TRACKER') && file_exists( $path ) ) {
global $trackers; $trackers[ $path ] = md5_file( $path );
}
}

9

Catalin

June 8th, 2010 at 3:35 am

I have a similar function and I am happy that I am not the only weirdo that uses such functionality.

I hate puting the namespece in a class names so it is OK to do have import() instrument. I also have a core folder where I put basic classes which are loaded default bu autoload :)

Your thoughts