我已经向这个问题提出了类似的问题,但我认为它措辞严厉且令人困惑,所以我希望能让它更清晰一点。
我正在使用原生Linux文件系统进行编程。
我有一类HelpTopic:
class HelpTopic extends Help{}
还有一类帮助:
class Help{}
现在我去包括HelpTopic:
include('HelpTopic.php');
即使我没有用new HelpTopic()
实例化HelpTopic,PHP(在Linux文件系统中)仍然会读取类签名并尝试使用HelpTopic加载帮助。
我不会从Windows系统共享的cifs文件系统中获得此行为。
我最好的猜测是,Linux存在一些奇怪之处,导致PHP以这种方式做出反应,但不确定是什么。
有没有人对此问题有任何想法或解决方案?
编辑:
我已经添加了我的加载功能来显示我在做什么:
public static function import($cName, $cPath = null){
if(substr($cName, -2) == "/*"){
$d_name = ROOT.'/'.substr($cName, 0, -2);
$d_files = getDirectoryFileList($d_name, array("\.php")); // Currently only accepts .php
foreach($d_files as $file){
glue::import(substr($file, 0, strrpos($file, '.')), substr($cName, 0, -2).'/'.$file);
}
}else{
if(!$cPath) $cPath = self::$_classMapper[$cName];
if(!isset(self::$_classLoaded[$cName])){
self::$_classLoaded[$cName] = true;
if($cPath[0] == "/" || preg_match("/^application/i", $cPath) > 0 || preg_match("/^glue/i", $cPath) > 0){
return include ROOT.'/'.$cPath;
}else{
return include $cPath;
}
}
return true;
}
}
我通过执行glue::inmport('application/models/*');
来调用它,它会包含我应用中的所有模型。事情是基于Linux的文件系统上的PHP(不是在cifs上)试图在没有实例化的情况下加载我的类的父级。
这是一个非常基本的功能,存在于大多数框架中(事实上大部分代码都基于yiis版本)所以我很困惑为什么其他人没有遇到这个问题。
答案 0 :(得分:4)
即使我没有使用
new HelpTopic()
实例化HelpTopic,PHP仍会读取类签名并尝试使用HelpTopic加载帮助。
正确。
为了知道如何正确定义类,PHP需要解析任何父类(一直向上)和任何接口。这是在定义类时完成的,而不是在使用类时完成的。
您应该查看the PHP documentation on inheritance,其中包含解释此行为的说明:
除非使用自动加载,否则必须在使用之前定义类。如果类扩展另一个类,则必须在子类结构之前声明父类。此规则适用于继承其他类和接口的类。
有两种方法可以解决此问题。
首先,在文件顶部添加一个require_once
,用于定义包含定义父类的文件的子类。这是最简单直接的方式,除非你有自动加载器。
第二种方法是定义一个自动加载器。这也是covered in the documentation。
......你正在使用的东西不是自动加载器。事实上,你应该清除你的代码库是一种可怕的憎恶。这是一个性能削弱,你不应该使用它。它也恰好是错误的。
我们在此处没有getDirectoryFileList()
的定义,因此我假设它使用glob()
或DirectoryIterator
。这是您的问题的根源。您将以 undefined 顺序获取文件列表。或者更确切地说,无论底层文件系统想要给你什么顺序。在一台计算机上,文件系统可能在 Help.php
之前为您提供HelpTopic.php
,而在另一台计算机上,HelpTopic.php
首先显示为
乍一看,您可能认为这可以通过简单的sort
进行修复,但事实并非如此。如果您创建一个Zebra
类会发生什么,然后需要创建一个继承自它的AlbinoZebra
?没有多少目录排序可以满足“加载ASCIIbetical”和“我需要Zebra成为第一”的要求。
让我们也谈谈问题的表现方面。在每个请求上,您将打开一个目录并读取文件列表。这是很多stat
次电话中的一个。这很慢。非常慢。然后,一个接一个,无论你是否需要它们,你都要包含这些文件。这意味着PHP必须编译和解释它们中的每一个。如果你没有使用字节码缓存,如果文件数量增长到一个非平凡的数字,这将彻底破坏性能。
正确构建的自动加载器将完全缓解此问题。自动加载器按需运行 ,这意味着他们永远不会在实际需要之前尝试包含文件。性能良好的自动加载器将根据名称知道类文件所在的位置。在现代PHP中,可以接受的做法是为类命名,以便自动加载器可以轻松地找到它们,使用命名空间或下划线 - 或两者 - 来映射目录分隔符。 (含义namespace \Models; class Help
或class Models_Help
将存在Models/Help.php
)
不幸的是,大多数示例在这里都没用,因为我不知道你的自定义框架会做什么样的奇怪事情。看一下the Zend Framework autoloader,它使用前缀注册在目录中指向类前缀(Model_
)。