如何在自动加载中使用PHP名称空间?


104

当我尝试使用自动加载和命名空间时,出现此错误:

致命错误:第10行的/usr/local/www/apache22/data/public/php5.3/test.php中找不到类'Class1'

谁能告诉我我在做什么错?

这是我的代码:

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();

?>

Answers:


119

Class1不在全局范围内。

请参见下面的工作示例:

<?php

function __autoload($class)
{
    $parts = explode('\\', $class);
    require end($parts) . '.php';
}

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

编辑(2009-12-14):

为了澄清起见,我使用“ use ... as”是为了简化示例。

替代方法如下:

$class = new Person\Barnes\David\Class1();

要么

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
您不必使用AS。这不是为什么该解决方案有效的原因。您可以轻松地做到:(use Person\Barnes\David\Class1;等效于use Person\Barnes\David\Class1 as Class1;)。
cartbeforehorse 2013年

1
谢谢,这有效。但是我不明白为什么我们只能使用$ class = new Class1();。在我们之前已经定义“使用Person \ Barnes \ David;”时?
user345602

4
您必须使用@ user346665 use Person\Barnes\David\Class1;才能执行此操作$class = new Class1();。跟use Person\Barnes\David;你一定要做$class = new David\Class1();。在use通过本身关键字是相当于use Person\Barnes\David\Class1 as Class1;use Person\Barnes\David as David;,分别对于每个实施例。
贾斯汀C

对于那些在2018年阅读的人,请将@ prince-billy-graham解决方案与spl_autoload_register结合使用-Bruno
de Oliveira

26

如Pascal MARTIN所述,您应使用DIRECTORY_SEPARATOR替换'\',例如:

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

我也建议您重新组织目录结构,以使代码更具可读性。这可以是一种替代方法:

目录结构:

ProjectRoot
 |- lib

文件: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • 为定义的每个名称空间创建子目录。

文件: /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();
  • 我使用php 5推荐进行自动加载器声明。如果仍然使用PHP 4,请使用旧语法替换它:function __autoload($ class)

18

您的__autoload函数将收到完整的类名称,包括名称空间名称。

这意味着,在您的情况下,该__autoload函数将接收“ Person\Barnes\David\Class1”,而不仅是“ Class1”。

因此,您必须修改自动加载代码,以处理这种“更复杂”的名称;经常使用的解决方案是在每个“级别”的命名空间中使用一个目录级别来组织文件,并且在自动加载时,\将命名空间名称中的' ' 替换为DIRECTORY_SEPARATOR


1
这不是我发现的。当我确实声明语句die($ class); 在__autoload函数中,它打印出“ Class1”,而不是“ Person \ Barnes \ David \ Class1”
David Barnes,

真正。autoload的$ class参数是在构造函数调用中编写的类名称。
tishma

1
对于“您的__autoload函数将获得完整的类名称,包括名称空间名称”的下注-仅当您明确use要尝试引用的类时,这才是正确的,而仅use当其所属的名称空间时,则为true 。OP的错误是他将use命名空间包含一个类,然后期望他的自动加载功能以某种方式神奇地传递了完整的类路径。这个答案并没有真正解决OP的错误。
Mark Amery 2014年

15

我做这样的事情:请参阅此GitHub示例

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___
}

3
漂亮又简单。如果有人应该找那个。)
dennis 2015年

3

我从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);
    }
});

3

我看到在以下两种情况下,自动加载功能仅接收“完整”类名-所有名称空间都位于该类名之前:

[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();

我发现在以下情况下,自动加载功能不会收到完整的类名:

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

更新:[c]是一个错误,而不是名称空间的工作方式。我可以报告说,以下两种情况也可以很好地代替[c]:

[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();

希望这可以帮助。


另外,该use关键字在PHP交互式命令行界面(php --interactive)中无法正常工作;
安德鲁·拉尔森

3

我在一行中使用了这个简单的技巧:

spl_autoload_register(function($name){
        require_once 'lib/'.str_replace('\\','/',$name).'.php';
    });

1

有同样的问题,只是发现了这个:

当创建与包含类的名称空间匹配的子文件夹结构时,您甚至都不需要定义自动加载器。

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

它像魅力一样运作

此处有更多信息:http : //www.php.net/manual/zh/function.spl-autoload-register.php#92514

编辑:这会由于反斜杠而在Linux上引起问题...有关immeëmosol的有效解决方案,请参见此处

命名空间自动加载可在Windows下运行,但不能在Linux上运行


1

使用有一个陷阱,尽管它是迄今为止最快的方法,但它也希望您的所有文件名都小写。

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

例如:

包含类SomeSuperClass.php的文件需要命名为somesuperclass.php,如果您使用区分大小写的文件系统(如Linux),这是一个陷阱,但前提是您的文件名为SomeSuperClass.php,但在Windows下不是问题。

在您的代码中使用__autoload可能仍可与当前版本的PHP一起使用,但希望此功能在以后不再使用并最终删除。

那么剩下哪些选项:

该版本可与PHP 5.3及更高版本一起使用,并允许使用文件名SomeSuperClass.php和somesuperclass.php。如果您使用的是5.3.2及更高版本,则此自动加载器的运行速度会更快。

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

2
作为旁注,str_replace ([ '_','\\'] '/', $className );它的速度是两个str_replace的两倍
Itay Moav -Malimovka 16-4-24

只要php文件是大写还是小写都无关紧要,目录仍然会区分大小写
Mike

1

我最近发现tanerkuc的答案很有帮助!只是想补充一点,使用strrpos()+ substr()explode()+ 快一点end()

spl_autoload_register( function( $class ) {
    $pos = strrpos( $class, '\\' );
    include ( $pos === false ? $class : substr( $class, $pos + 1 ) ).'.php';
});

1

我会花两分钱给相对的初学者,或者不需要简单的spl_autoload_register()设置,而无需所有理论知识:只需为每个类创建一个php文件,将该php文件命名为与您的类名称相同的名称,并保留您的类文件与您所讨论的php文件位于同一目录中,那么它将起作用:

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

谷歌搜索此函数中的各个部分应该可以回答其工作原理。PS:我使用Linux,这在Linux上有效。Windows人员应该首先对其进行测试。


1

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

您需要将类文件放入名为Classes的文件夹中,该文件夹与PHP应用程序的入口点位于同一目录中。如果类使用名称空间,则名称空间将转换为目录结构。与许多其他自动加载器不同,下划线不会转换为目录结构(使用PHP <5.3伪名称空间以及PHP> = 5.3实名称空间非常棘手)。

<?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');

您需要将以下代码放入主PHP脚本(入口点):

require_once("Classes/Autoloader.php");

这是示例目录布局:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business classC {}
    Deeper/
      ClassD.php - namespace BusinessDeeper classD {}

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();
}
?>

1
尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以改善其长期价值。
伊桑(Ethan)'18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.