Autoload namespaces based on directory structure
Asked Answered
A

5

8

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!';
        }
    }
?>
Abrahamsen answered 1/6, 2015 at 9:8 Comment(9)
Possible duplicate of https://mcmap.net/q/1325503/-php-spl_autoloadBicapsular
@OhAuth Excuse me. I have absolutely no idea how you can possibly consider this a duplicate or even remotely related.Abrahamsen
Is there a namespace should be declared in someclass.php?Gramicidin
@Gramicidin I tried that. Doesn't seem to work, unless like I said, i'm missing something.Abrahamsen
someclass is not in the classes namespace. Namespaces are not the same as directories.Tinner
@Abrahamsen hmm... At least, first answer in the above link provides example of autoloading functionGramicidin
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).Kike
@BhargavRao Why was the answer by SednaSoft deleted?Milreis
@Kike - I reinstated your answer, but included the content of the link. Your answer may have been deleted as a link only answer.Embrocation
M
5

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

<?php 
namespace classes;
Marty answered 1/6, 2015 at 9:35 Comment(0)
R
5

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:


Rolypoly answered 1/7, 2017 at 8:44 Comment(1)
The first two references are for PHP 7.Milreis
E
2

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

Embrocation answered 30/6, 2017 at 9:31 Comment(0)
B
2

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

Buxton answered 1/7, 2017 at 8:57 Comment(7)
I roll my own just fine, just saying... For example github.com/ArtisticPhoenix/Evo/blob/master/EVO/Autoloader.phpMckelvey
@Mckelvey But why?Buxton
Why? because I don't want my code depending on any third party systems. That's one reason, another is my AutoLoader can register multiple namespace path pairs so I can re-base my namespace ( reset them to a top folder), and because I can.Mckelvey
@Mckelvey If you look closely at github.com/ArtisticPhoenix/Evo, it already depends on at least seven 3rd-party systems. Take a look at wiki.c2.com/?WheelFactory and wiki.c2.com/?NotInventedHere. While it sure can be fun and an exercise to build these systems yourself, your efforts will be more wisely spend on something else.Buxton
what 3rd party systems? PHP? ha I mean frameworks ( as in code base ), anyway, I am a full time PHP senior developer and CIO, so I spend my normal time maintaining 4 websites and a custom B2B platform that runs on Rabbit, Sphinx and Mongo. So ... yea... While it may be sad, coding stuff, is my idea of fun, I am married with late Teenage children after all.Mckelvey
Our company also uses a Amember, Wordpress, CI integration for most of our sites that run on CentOS, with a Full forward proxy DMZ, its SSAE-16/SOC3 compliant. So I am well aware of "re-inventing" the wheel ( or not re-inventing it ) That doesn't mean you cant make a better wheel...Mckelvey
BTW @Mckelvey your link is not accessible anymoreSubmissive
T
0

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.

Turbulence answered 4/7, 2017 at 20:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.