PHP: how to autoload interfaces and abstracts

Run picture Run · Mar 27, 2013 · Viewed 7.9k times · Source

I have this autoloader class to autoload classes initially, but now I want to autoload interfaces and abstracts as well.

So I made the change following this answer,

$reflection = new ReflectionClass($class_name);

# Return boolean if it is an interface.
if ($reflection->isInterface())
{
    $file_name = 'interface_'.strtolower(array_pop($file_pieces)).'.php';
}
else
{
    $file_name = 'class_'.strtolower(array_pop($file_pieces)).'.php';

}

I tested it but this autoloader class does not load interfaces at all. Any ideas what I have missed?

For instance, this is my interface file,

interface_methods.php

and its content,

interface methods 
{
    public function delete();
}

Below is my entire this autoloader class.

class autoloader
{
    /**
     * Set the property.
     */
    public $directory;
    public $recursive;

    public function __construct($directory, $recursive = array('search' => 'models') ) 
    {
        # Store the data into the property.
        $this->directory = $directory;
        $this->recursive = $recursive;

        # When using spl_autoload_register() with class methods, it might seem that it can use only public methods, though it can use private/protected methods as well, if registered from inside the class:
        spl_autoload_register(array($this,'get_class'));
    }

    private function get_class($class_name)
    {
        # List all the class directories in the array.
        if ($this->recursive)
        {
            $array_directories =  self::get_recursive_directory($this->directory);
        }
        else
        {
            if (is_array($this->directory)) $array_directories =  $this->directory;
            else $array_directories =  array($this->directory);
        }

        # Determine the class is an interface.
        $reflection = new ReflectionClass($class_name);

        $file_pieces = explode('\\', $class_name);

        # Return boolean if it is an interface.
        if ($reflection->isInterface())
        {
            $file_name = 'interface_'.strtolower(array_pop($file_pieces)).'.php';
        }
        else
        {
            $file_name = 'class_'.strtolower(array_pop($file_pieces)).'.php';

        }

        # Loop the array.
        foreach($array_directories as $path_directory)
        {
            if(file_exists($path_directory.$file_name)) 
            {
                include $path_directory.$file_name;
            } 
        }

    }

    public function get_recursive_directory($directory)
    {
        $iterator = new RecursiveIteratorIterator
                    (
                        new RecursiveDirectoryIterator($directory),
                        RecursiveIteratorIterator::CHILD_FIRST
                    );

        # This will hold the result.
        $result = array();

        # Loop the directory contents.
        foreach ($iterator as $path) 
        {

            # If object is a directory and matches the search term ('models')...
            if ($path->isDir() && $path->getBasename() === $this->recursive['search']) 
            {

                # Add it to the result array.
                # Must replace the slash in the class - dunno why!
                $result[] = str_replace('\\', '/', $path).'/';
                //$result[] = (string) $path . '/';

            }

        }

        # Return the result in an array.
        return $result;
    }
}

Answer

Sven picture Sven · Mar 27, 2013

PHP makes no difference between any class or interface or abstract class. The autoloader function you define always gets the name of the thing to autoload, and no kind of hint which one it was.

So your naming strategy cannot be autoloaded because you prefix interfaces with "interface_" and classes with "class_". Personally I find such a naming convention rather annoying.

On the other hand, your autoloader is completely unperformant. It scans whole directory trees recursively just to find one class! And the next class has to do all the work again, without the benefit of having done it before!

Please do implement a PSR-0 autoloader if you really want to do it on your own (and not use things like composer to do it for you) and stick to this naming scheme for classes and interfaces.

And please select a distinguishing classname prefix or namespace, and as a first step check inside your autoloader if the class to be loaded has this prefix. Return instantly if it has not. This frees you from having to spin the harddrive and see if the filename for the class exists.

If the prefix does not match, it is not "your" class that wants to be loaded, so your autoloader cannot know how to do it and shouldn't even try, but a different autoloader that was registered will know.