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.



9 thoughts on “Simulating Packages in PHP

  1. 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)

  2. 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. 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.

  4. 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. 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).

  6. 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

  7. 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 );
    }
    }

  8. 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 :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>