SAz
SAz

Reputation: 365

Autoload namespaces based on directory structure

According to the top comment on the PHP page spl_autoload_register( ) :

Good news for PHP 5.3 users with namespaced classes:

When you create a subfolder structure matching the namespaces of the >containing classes, you will never even have to define an autoloader.

<?php
    spl_autoload_extensions(".php"); // comma-separated list
    spl_autoload_register();
?>

However, when I have the following structure:

* classes/someclass.php

* index.php

Where someclass.php contains the following:

<?php
    class someclass {
        function __construct( ) {
            echo 'It works!';
        }
    }
?>

and index.php contains:

<?php
    spl_autoload_extensions(".php");
    spl_autoload_register();

    new classes\someclass;
?>

Then I get the following error:

Fatal error: spl_autoload(): Class classes\someclass could not be loaded

Am I getting this wrong? How can I make this work?

From the comments

This doesn't work either for the class:

<?php
    namespace classes;
    class someclass {
        function __construct( ) {
            echo 'It works!';
        }
    }
?>

Upvotes: 8

Views: 2806

Answers (5)

php_boy
php_boy

Reputation: 41

Do not use the spl_autoload_extensions () function if the files to load only have the php extension. In my case, I create a class called autoload .. something similar to this:

<?php

class Autoload
{
    private static $_extensions = array(
        0 => '.inc',
        1 => '.lib.php',
        2 => '.class.php',
    );

    public function __construct()
    {
        self::set_include_path();
        spl_autoload_extensions(implode(',', self::$_extensions));
        spl_autoload_register(__CLASS__, 'load_class');
    }

    public static function set_include_paths()
    {
        set_include_path(implode(PATH_SEPARATOR, array(
            realpath('classes');
            realpath('system');
            ...
            get_include_path();,
        )));
    }

    public function load_class($class)
    {
        if (!empty($class)) {
            spl_autoload($class);
        }

        return false;
    }
}

The use of ?> At the end of the file is not necessary.

Upvotes: 0

localheinz
localheinz

Reputation: 9582

Do not roll your own autoloading, but use composer instead.

Create a composer.json in the root of your project:

{
    "autoload": {
        "psr-4": {
            "classes\\": "classes/"
        }
    }
}

Install composer, then run

 composer dump-autoload 

In your index.php, require the autoloader:

require __DIR__ . '/vendor/autoload.php';

For reference, see

Upvotes: 2

hakre
hakre

Reputation: 197767

TLDR; It works, but:

  • namespace classes; is missing in classes/someclass.php
  • set_include_path(__DIR__); is missing in index.php
  • (spl_autoload_extensions(".php") is not necessary)

Include Path

The SPL autoload implementation is include path based. Using a dot as include path is relative to the working directory (!) which is independent from script file location. __DIR__ names the exact directory that is needed if the classes folder lies next to the index.php file as in the scenario.

Directory Separator mapping

Next the autoloader implementation does map the class namespace separator properly on Unix systems. In case that got lost in the o/p, the PHP source code clearly has this.

Case Sensitivity of File Names

What the source code also shows is that file-names to load are made lowercase. That is, if your file system is case sensitive, the file and directory names must be in lower case.


Refernces:


Upvotes: 5

Greenonline
Greenonline

Reputation: 1377

The user, SedaSoft, who had posted the Good news comment you which you refer, had subsequently posted another comment, due to a revision of ideas:

What I said here previously is only true on Windows. The built-in default autoloader that is registered when you call spl_autoload_register() without any arguments simply adds the qualified class name plus the registered file extension (.php) to each of the include paths and tries to include that file.

Example (on Windows):

include paths:

- "."
- "d:/projects/phplib"

qualified class name to load:

network\http\rest\Resource

Here's what happens:

PHP tries to load

'.\\network\\http\\rest\\Resource.php'
-> file not found

PHP tries to load

'd:/projects/phplib\\network\\http\\rest\\Resource.php'
-> file found and included

Note the slashes and backslashes in the file path. On Windows this works perfectly, but on a Linux machine, the backslashes won't work and additionally the file names are case-sensitive.

That's why on Linux the quick-and-easy way would be to convert these qualified class names to slashes and to lowercase and pass them to the built-in autoloader like so:

<?php
spl_autoload_register(
  function ($pClassName) {
    spl_autoload(strtolower(str_replace("\\", "/", $pClassName)));
  }
);
?>

But this means, you have to save all your classes with lowercase file names. Otherwise, if you omit the strtolower call, you have to use the class names exactly as specified by the file name, which can be annoying for class names that are defined with non-straightforward case like e. g. XMLHttpRequest.

I prefer the lowercase approach, because it is easier to use and the file name conversion can be done automatically on deploying.


Note that this commenter had actually posted an answer here on SO today, containing the above links (along with a short explantation), but it was subsequently deleted, whilst in the review queue - presumably due to its brevity. I have reinstated the commenter's answer, along with the content of the link. Their answer was as follows:

I wrote that comment on php.net some time ago when I was working on a Windows system. Later, I had to partially revoke what I wrote in that comment on the very same page in another comment, which also contains a possible solution to the problem that is the easiest I could think of (apart from using Composer).

Here is a screenshot of the original answer:

Original post

Upvotes: 2

m4t1t0
m4t1t0

Reputation: 5721

In your someclass.php file you must define the namespace at the begginning.

<?php 
namespace classes;

Upvotes: 5

Related Questions