2013-11-01 100 views
0

这是我的网络结构,例如,PHP spl_autoload_register导致类重复?

index.php 

    'core/model/' 
    'core/helper/' 
    'core/ext/' 

我有model文件夹下这些类文件,

MyClass.php 
MyClass2.php 
classfour.php 

而且我想通过自动加载类自动加载它们,

define ('WEBSITE_DOCROOT', str_replace('\\', '/', dirname(__FILE__)).'/'); 

function autoloadMultipleDirectory($class_name) 
{ 
    // List all the class directories in the array. 
    $main_directories = array(
     'core/model/', 
     'core/helper/', 
     'core/ext/' 
    ); 

    $sub_directories = array(); 

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

    $file_name = end($parts).'.php'; 

    foreach($main_directories as $path_directory) 
    { 
     $iterator = new RecursiveIteratorIterator 
     (
      new RecursiveDirectoryIterator(WEBSITE_DOCROOT.$path_directory), // Must use absolute path to get the files when ajax is used. 
      RecursiveIteratorIterator::SELF_FIRST 
     ); 

     foreach ($iterator as $fileObject) 
     { 
      if ($fileObject->isDir()) 
      { 
       $sub_directories[] = preg_replace('~.*?(?=core|local)~i', '', str_replace('\\', '/', $fileObject->getPathname())) .'/'; 
      } 
     } 
    } 

    // Mearge the main dirs with any sub dirs in them. 
    $merged_directories = array_merge($main_directories,$sub_directories); 

    print_r($merged_directories); 

    // Loop the merge array and include the classes in them. 
    foreach($merged_directories as $path_directory) 
    { 
     if(file_exists(WEBSITE_DOCROOT.$path_directory.$file_name)) 
     { 
      include WEBSITE_DOCROOT.$path_directory.$file_name; 
     } 
    } 
} 

Implementations,

spl_autoload_register('autoloadMultipleDirectory'); 

//define classone 
class classone { } 

//define classtwo 
class classtwo { } 

//define classthree 
class classthree { } 

print_r(get_declared_classes()); 

结果,

Array 
(
    [0] => stdClass 
    ... 
    [131] => tidyNode 
    [132] => classone 
    [133] => classtwo 
    [134] => classthree 
) 

classfourMyClass & MyClass2不在列表中。

和,

$test = new MyClass(); 

$test2 = new MyClass2(); 

$test3 = new classfour(); 

将导致该重复取决于我有多少类实例化,

Array 
(
    [0] => core/model/ 
    [1] => core/helper/ 
    [2] => core/ext/ 
) 
Array 
(
    [0] => core/model/ 
    [1] => core/helper/ 
    [2] => core/ext/ 
) 
Array 
(
    [0] => core/model/ 
    [1] => core/helper/ 
    [2] => core/ext/ 
) 
在我的直播服务器

我得到更奇怪的结果,

Array 
(
    [0] => core/model/ 
    [1] => core/helper/ 
    [2] => core/ext/ 
    [3] => core/model/./ (duplicated) 
    [4] => core/model/../ (duplicated) 
    [5] => core/helper/./ (duplicated) 
    [6] => core/helper/../ (duplicated) 
    [7] => core/ext/./ (duplicated) 
    [8] => core/ext/../ (duplicated) 
) 

任何想法为什么以及如何修复它?

编辑:

如果我有手工类,

include 'core/model/MyClass.php'; 
include 'core/model/MyClass2.php'; 
include 'core/model/classfour.php'; 

print_r(get_declared_classes()); 

$test = new MyClass(); 

$test2 = new MyClass2(); 

$test3 = new classfour(); 

我得到正确的答案,

Array 
(
    [0] => stdClass 
    ... 
    [131] => tidyNode 
    [132] => classone 
    [133] => classtwo 
    [134] => classthree 
    [135] => MyClass 
    [136] => MyClass2 
    [137] => classfour 
) 

classfourMyClass & MyClass2现在在列表中。

怎么回事?

+0

我看到三个不同的'print_r()'输出。这是可以解释的:这是因为自动加载函数被调用了三次。我没有看到“重复”问题。什么被重复和在哪里? – geomagas

+0

请看我上面的编辑。谢谢。 – laukok

+0

'我看到三个不同的print_r()输出。“其他两个在哪里?我只在自动载入类中使用'print' ONCE。 – laukok

回答

2

您正在解决一些不同的问题。

自动加载与手动加载...

当您注册一个自动加载功能,当你实例化一个对象时,它被调用。在这个函数中,你应该检查正在实例化的类,并以某种方式提供相应的类定义,通常包括一个相关的php文件。这很方便,因为您确保只有需要实例化的类才会以JIT方式实际声明。还要注意的是,如果你的类扩展了另一个类,那么自动加载函数也将被调用用于父类,等等递归地为所有的祖先类调用。

这与手动包含类文件不同。只要包含myfooclass.php文件,就会声明相关的类,而不管它是否稍后实例化。

......和get_declared_classes()

get_declared_classes()做什么它的名字所暗示的:它返回与已经在你怎么称呼它时间被宣布的所有类的数组。换句话说,如果您从脚本中的多个位置调用它,则可能会得到不同的结果,具体取决于当时声明了哪些类。

的重复

我可以在直播服务器的结果看到重复的路径,而不是你描述它们的方式。例如,core/model/./core/model/重复,因为前者解决后者。但是core/model/../不是重复的,因为它解析为core/(它只在以后重复,由core/helper/../core/ext/../)。

一个好的做法是使用realpath()这将解决这些问题(以及其他一些问题),并为您提供绝对的,规范化的现有路径。此外,您应该检查是否路径已存在于$sub_directories插入之前:

if ($fileObject->isDir()) 
    { 
    $pathname=$fileObject->getPathname(); 
    $slashed=str_replace('\\', '/', $pathname); 
    $filtered=preg_replace('~.*?(?=core|local)~i', '', $slashed) .'/'; 
    $canonical=realpath($filtered); 
    if(!in_array($canonical,$sub_directories)) $sub_directories[] = $canonical; 
    } 

......或者做一个array_unique()使用它之前:

$merged_directories = array_merge($main_directories,$sub_directories); 
$merged_directories = array_unique($merged_directories); 
print_r($merged_directories); 

当然,这些解决方案被动并且不要求您改变您的应用程序逻辑。

另一个故障安全是使用include_once而不是include,以便不会意外地将同一个文件包含两次。

哦,还有最后一个提示:找到相关文件后,不需要继续搜索。只需break循环:

if(file_exists($path_directory.$file_name)) 
     { 
     include_once $path_directory.$file_name; 
     break; // <-- no point going on 
     } 
+0

非常感谢这个答案!非常感激!! :d – laukok