How do I use PHP namespaces with autoload?
Asked Answered
W

13

111

I get this error when I try to use autoload and namespaces:

Fatal error: Class 'Class1' not found in /usr/local/www/apache22/data/public/php5.3/test.php on line 10

Can anyone tell me what I am doing wrong?

Here is my code:

Class1.php:

<?php

namespace Person\Barnes\David
{
    class Class1
    {
        public function __construct()
        {
            echo __CLASS__;
        }
    }
}

?>

test.php:

<?php

function __autoload($class)
{
    require $class . '.php';
}

use Person\Barnes\David;

$class = new Class1();

?>
Wohlen answered 2/12, 2009 at 5:55 Comment(0)
C
128

Class1 is not in the global scope.

Note that this is an old answer and things have changed since the days where you couldn't assume the support for spl_autoload_register() which was introduced in PHP 5.1 (now many years ago!).

These days, you would likely be using Composer. Under the hood, this would be something along the lines of this snippet to enable class autoloading.

spl_autoload_register(function ($class) {
    // Adapt this depending on your directory structure
    $parts = explode('\\', $class);
    include end($parts) . '.php';
});

For completeness, here is the old answer:

To load a class that is not defined in the global scope, you need to use an autoloader.

<?php

// Note that `__autoload()` is removed as of PHP 8 in favour of 
// `spl_autoload_register()`, see above
function __autoload($class)
{
    // Adapt this depending on your directory structure
    $parts = explode('\\', $class);
    require end($parts) . '.php';
}

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

or without aliases:

use Person\Barnes\David\Class1;

$class = new Class1();
Checkup answered 9/12, 2009 at 1:35 Comment(4)
You don't have to use AS. That's not why this solution works. You could just as easily do: use Person\Barnes\David\Class1; (which is equivalent to use Person\Barnes\David\Class1 as Class1;).Utley
Thanks ,this works. But I cant understand why we can just use $class = new Class1(); when we have already defined "use Person\Barnes\David; " before ?Brunette
@user346665 you must use use Person\Barnes\David\Class1; in order to do $class = new Class1();. With use Person\Barnes\David; you must do $class = new David\Class1();. The use keyword by itself is the equivalent of use Person\Barnes\David\Class1 as Class1; or use Person\Barnes\David as David;, respectively for each example.Staffan
For those reading in 2018, use @prince-billy-graham solution with spl_autoload_registerReedreedbird
B
31

As mentioned Pascal MARTIN, you should replace the '\' with DIRECTORY_SEPARATOR for example:

$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
include($filename);

Also I would suggest you to reorganize the dirrectory structure, to make the code more readable. This could be an alternative:

Directory structure:

ProjectRoot
 |- lib

File: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Make the sub directory for each namespace you are defined.

File: /ProjectRoot/test.php

define('BASE_PATH', realpath(dirname(__FILE__)));
function my_autoloader($class)
{
    $filename = BASE_PATH . '/lib/' . str_replace('\\', '/', $class) . '.php';
    include($filename);
}
spl_autoload_register('my_autoloader');

use Person\Barnes\David as MyPerson;
$class = new MyPerson\Class1();
  • I used php 5 recomendation for autoloader declaration. If you are still with PHP 4, replace it with the old syntax: function __autoload($class)
Bergmans answered 14/5, 2013 at 1:1 Comment(0)
A
20

Your __autoload function will receive the full class-name, including the namespace name.

This means, in your case, the __autoload function will receive 'Person\Barnes\David\Class1', and not only 'Class1'.

So, you have to modify your autoloading code, to deal with that kind of "more-complicated" name ; a solution often used is to organize your files using one level of directory per "level" of namespaces, and, when autoloading, replace '\' in the namespace name by DIRECTORY_SEPARATOR.

Armyn answered 2/12, 2009 at 6:0 Comment(3)
This is not what I found. When I did put the statement die($class); in the __autoload function, it printed out 'Class1"', not 'Person\Barnes\David\Class1'Wohlen
True. $class parameter of autoload is the class name as written in constructor call.Rhythmical
Downvote for "Your __autoload function will receive the full class-name, including the namespace name" - this is only true if you have explicitly used the class you're trying to reference, not if you've merely used the namespace it belongs to. The OP's mistake was that he'd used the namespace containing a class and was then expecting his autoload function to magically get passed the full classpath somehow. This answer doesn't really address the OP's mistake.Packston
B
16

I do something like this: See this GitHub Example

spl_autoload_register('AutoLoader');

function AutoLoader($className)
{
    $file = str_replace('\\',DIRECTORY_SEPARATOR,$className);

    require_once 'classes' . DIRECTORY_SEPARATOR . $file . '.php'; 
    //Make your own path, Might need to use Magics like ___DIR___
}
Bengali answered 31/5, 2015 at 18:5 Comment(1)
Nice and simple. If one should be looking for that .)Unimpeachable
P
4

I see that the autoload functions only receive the "full" classname - with all the namespaces preceeding it - in the following two cases:

[a] $a = new The\Full\Namespace\CoolClass();

[b] use The\Full\Namespace as SomeNamespace; (at the top of your source file) followed by $a = new SomeNamespace\CoolClass();

I see that the autoload functions DO NOT receive the full classname in the following case:

[c] use The\Full\Namespace; (at the top of your source file) followed by $a = new CoolClass();

UPDATE: [c] is a mistake and isn't how namespaces work anyway. I can report that, instead of [c], the following two cases also work well:

[d] use The\Full\Namespace; (at the top of your source file) followed by $a = new Namespace\CoolClass();

[e] use The\Full\Namespace\CoolClass; (at the top of your source file) followed by $a = new CoolClass();

Hope this helps.

Planish answered 21/8, 2013 at 16:7 Comment(1)
As a side note, the use keyword doesn't work properly in the PHP interactive command-line interface (php --interactive);Astyanax
T
3

I found this gem from Flysystem

spl_autoload_register(function($class) {
    $prefix = 'League\\Flysystem\\';

    if ( ! substr($class, 0, 17) === $prefix) {
        return;
    }

    $class = substr($class, strlen($prefix));
    $location = __DIR__ . 'path/to/flysystem/src/' . str_replace('\\', '/', $class) . '.php';

    if (is_file($location)) {
        require_once($location);
    }
});
Tigges answered 15/2, 2015 at 20:20 Comment(0)
V
3

I use this simple hack in one line:

spl_autoload_register(function($name){
        require_once 'lib/'.str_replace('\\','/',$name).'.php';
    });
Vain answered 29/8, 2018 at 6:52 Comment(0)
U
2

https://thomashunter.name/blog/simple-php-namespace-friendly-autoloader-class/

You’ll want to put your class files into a folder named Classes, which is in the same directory as the entry point into your PHP application. If classes use namespaces, the namespaces will be converted into the directory structure.

Unlike a lot of other auto-loaders, underscores will not be converted into directory structures (it’s tricky to do PHP < 5.3 pseudo namespaces along with PHP >= 5.3 real namespaces).

<?php
class Autoloader {
    static public function loader($className) {
        $filename = "Classes/" . str_replace("\\", '/', $className) . ".php";
        if (file_exists($filename)) {
            include($filename);
            if (class_exists($className)) {
                return TRUE;
            }
        }
        return FALSE;
    }
}
spl_autoload_register('Autoloader::loader');

You’ll want to place the following code into your main PHP script (entry point):

require_once("Classes/Autoloader.php");

Here’s an example directory layout:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business; classC {}
    Deeper/
      ClassD.php - namespace Business\Deeper; classD {}
Unavoidable answered 26/10, 2017 at 7:33 Comment(0)
P
1

had the same issue and just found this :

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

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

It worked like a charm

More info here : http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDIT: this causes problem on Linux because of backslash... See here for working solution by immeëmosol

Namespace Autoload works under windows, but not on Linux

Pieper answered 3/7, 2014 at 13:23 Comment(0)
G
1

Using has a gotcha, while it is by far the fastest method, it also expects all of your filenames to be lowercase.

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

For example:

A file containing the class SomeSuperClass would need to be named somesuperclass.php, this is a gotcha when using a case sensitive filesystem like Linux, if your file is named SomeSuperClass.php but not a problem under Windows.

Using __autoload in your code may still work with current versions of PHP but expect this feature to become deprecated and finally removed in the future.

So what options are left:

This version will work with PHP 5.3 and above and allows for filenames SomeSuperClass.php and somesuperclass.php. If your using 5.3.2 and above, this autoloader will work even faster.

<?php

if ( function_exists ( 'stream_resolve_include_path' ) == false ) {
    function stream_resolve_include_path ( $filename ) {
        $paths = explode ( PATH_SEPARATOR, get_include_path () );
        foreach ( $paths as $path ) {
            $path = realpath ( $path . PATH_SEPARATOR . $filename );
            if ( $path ) {
                return $path;
            }
        }
        return false;
    }
}

spl_autoload_register ( function ( $className, $fileExtensions = null ) {
    $className = str_replace ( '_', '/', $className );
    $className = str_replace ( '\\', '/', $className );
    $file = stream_resolve_include_path ( $className . '.php' );
    if ( $file === false ) {
        $file = stream_resolve_include_path ( strtolower ( $className . '.php' ) );
    }
    if ( $file !== false ) {
        include $file;
        return true;
    }
    return false;
});
Gemmation answered 8/4, 2015 at 1:28 Comment(2)
as a side note, str_replace ([ '_','\\'] '/', $className ); is twice as fast than two str_replaceCabana
As long as it doesn't matter if the php file is upper/lower cased, the directories still remain case sensisivePocked
R
1

I recently found tanerkay's answer very helpful! Just wanted to add that using strrpos() + substr() is slightly faster than explode() + end():

spl_autoload_register( function( $class ) {
    $pos = strrpos( $class, '\\' );
    include ( $pos === false ? $class : substr( $class, $pos + 1 ) ).'.php';
});
Ruinous answered 5/8, 2016 at 2:18 Comment(0)
T
1

I'll throw in my two cents for relative beginners or whatnot wanting a simple spl_autoload_register() setup without all the theory: Just create one php file for each class, name that php file the same as your class name, and keep your class files in the same directory as your php file in question, then this will work:

spl_autoload_register(function ($class_name) {
    require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . $class_name . '.php';
});

Googling the pieces inside this function should answer how it works. PS: I use Linux, and this works on Linux. Windows folks should test it out first.

Transonic answered 22/8, 2016 at 5:45 Comment(0)
B
0
<?php
spl_autoload_register(function ($classname){
   // for security purpose
   //your class name should match the name of your class "file.php"
   $classname = str_replace("..", "", $classname);
   require_once __DIR__.DIRECTORY_SEPARATOR.("classes/$classname.class.php");
});
try {
  $new = new Class1();
} catch (Exception $e) {
   echo "error = ". $e->getMessage();
}
?>
Blythebm answered 17/5, 2018 at 12:5 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Cordage

© 2022 - 2024 — McMap. All rights reserved.