手动类加载失败背后的错误概念

时间:2017-01-31 09:22:04

标签: php

我有一个带有手动类加载和以下层次结构树的遗留项目:

A --> B --> C --> C1
            \---> C2

要获得C作为切入点,我写了以下内容:

<?php // index.php
require_once __DIR__ . '/C.php';
<?php // A.php
class A {
}
<?php // B.php
require_once __DIR__ . '/A.php';
class B extends A {
}
<?php // C.php
require_once __DIR__ . '/B.php';
require_once __DIR__ . '/C1.php';
require_once __DIR__ . '/C2.php';
class C extends B {
}
<?php // C1.php
class C1 extends C {
}
<?php // C2.php
class C2 extends C {
}

index.php抛出:

  

致命错误:第2行的C:\ test \ C1.php中找不到“C”类

那是class C1 extends C

我必须对PHP内部有一些基本的误解,因为如果我用相应的文件内容替换每个require_once,它会运行得很完美。我错过了什么?

5 个答案:

答案 0 :(得分:4)

C不应该知道它的衍生物,所以它应该是

<?php // C.php
require_once __DIR__ . '/B.php';
class C extends B {
}

<?php // C1.php
require_once __DIR__ . '/C.php';

class C1 extends C {
}

<?php // C2.php
require_once __DIR__ . '/C.php';

class C2 extends C {
}

答案 1 :(得分:1)

目前您定义了C1C2类,但尚未定义类C,因此PHP会导致致命错误。

以下是一个示例,了解应该更改哪些内容才能使其正常工作:

<?php // C.php
require_once __DIR__ . '/B.php';

class C extends B {
}

require_once __DIR__ . '/C1.php';
require_once __DIR__ . '/C2.php';

将所有类编写在单个文件中是有效的,因为PHP编译器正在处理整个文件内容并相应地使用所需的类。调用在文件末尾定义的函数时会出现相同的行为。

使用includes,编译器仅使用先前编译的资源。

答案 2 :(得分:1)

问题始于此文件:

<?php // C.php
require_once __DIR__ . '/B.php';
require_once __DIR__ . '/C1.php';
require_once __DIR__ . '/C2.php';
class C extends B {
}

include()/include_once()/require()/require_once()语句将包含文件的内容复制粘贴到它们出现的文件中。

实际的包含发生在运行时。当编译器看到该行时:

class C extends B

它会触发错误,因为它对B一无所知。

如果您放置B.php文件的内容而不是require_once语句,则情况会有所不同。在编译C.php期间,编译器会了解B的定义,并能够基于它创建class C的定义。

使其运行的最佳方式(截至2017年)是使用autoloading(大约10年前不推荐手动加载)。您可以编写一个符合项目需求的简单自动加载器功能(如文档中所述),或者更好的是,您可以将所有课程放在namespace中,遵循PSR-4建议和使用Composer生成自动加载器。

<强>更新

使用自动加载器无需使用include() / require()。项目文件的内容如下所示:

<?php // A.php
namespace MyProject;
class A {
}

<?php // B.php
namespace MyProject;
class B extends A {
}

<?php // C.php
namespace MyProject;
class C extends B {
}

// ...and so on...

<强>结论:

正如OP在问题中指定的那样,它是一个使用include() / require()语句手动加载的遗留项目。这个答案提出了一种替代方法,这种方法最适合新项目。对于遗留项目,大多数情况下实现自动加载(以及删除分散在整个项目中的include() / require()语句)所需的工作量超出了优势。

答案 3 :(得分:1)

我将以最简单的方式解释错误发生的原因。

当您使用require_once语句时,编译器会在看到语句时包含所需的文件,因此我们可以恢复所有包含的文件,如下所示:

//Start index.php
// index.php require File C
// file C.php requires B.php
// file B.php requires A.php
class A {
}
// end file A.php
class B extends A {
}
//end file B.php
//file C.php require C1.php
//file C1.php
class C1 extends C {
}
//// ** ERROR class C still not declared ! PHP Stops.

所以你需要在扩展它之前包含C类:)

答案 4 :(得分:1)

这是问题所在。

PHP将在加载后解释整个脚本。这包括类函数和其他符号的声明。条件函数/类有一个值得注意的例外。

例如,这适用于https://eval.in/727681

class A {
}
// end file A.php
class B extends A {
}
//end file B.php
//file C.php require C1.php
//file C1.php
class C1 extends C {
}

class C extends B {
}

但是这个不是(https://eval.in/727682):

class A {
}
// end file A.php
class B extends A {
}
//end file B.php
//file C.php require C1.php
//file C1.php
class C1 extends C {
}

if (true) {
class C extends B {
}
}
  

致命错误:未找到“C”类

现在真正的问题是,PHP通常会“向前看”以查看即将发布的内容,明确声明,但会独立处理每个文件。这意味着 你不能包含一个带有“promise”的类文件,你最终会声明它的依赖项。