包括子类必须首先包括父类

时间:2012-01-18 14:49:32

标签: php php-5.3

我已经向这个问题提出了类似的问题,但我认为它措辞严厉且令人困惑,所以我希望能让它更清晰一点。

我正在使用原生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版本)所以我很困惑为什么其他人没有遇到这个问题。

1 个答案:

答案 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 Helpclass Models_Help将存在Models/Help.php

不幸的是,大多数示例在这里都没用,因为我不知道你的自定义框架会做什么样的奇怪事情。看一下the Zend Framework autoloader,它使用前缀注册在目录中指向类前缀(Model_)。