这是 PHP(和其他编程语言)没有直接在他们的书中直接说明的差距:您可以有两次相同的变量名(在您的情况下为 $error),但即使相同,这两个不要识别相同的变量(寻址相同的内存)。
一开始这可能看起来不太直接,但是当它涉及构建一个更大的程序时会变得更有意义,因为相当多的易于理解的变量名称都是有限的,因此使每个变量都可以在全局命名空间中通过其名称访问很快就搞砸了整个程序代码。
因此,如果您有兴趣,我会在此处看到有关如何处理此问题的多条路线(如果没有,这里是 some existing Q&A regarding the static keyword in a PHP function)。
第一个比较懒惰,在数据处理方面很可能是不安全的,所以整体编程代码需要非常精确才能安全使用:“不要重新实现数组,全局使用就行”。您可以使用全局变量来做到这一点。
初始化应该在应用程序的开头完成:
$GLOBALS['errors'] = [];
然后,您可以在需要时向该变量添加新条目:
$GLOBALS['errors'][] = $error;
如果您需要再次以列表的形式获取错误,只需将其读出:
var_dump($GLOBALS['errors']);
这实际上是全局变量名errors。全局变量很强大,但维护起来也很昂贵,因为任何代码都可以(有意或通过错误)重置所有错误,例如,甚至更苛刻,将数组转换为字符串,下次某些代码想要添加新条目时将失败.
那么,不使用全局变量的解决方案会是什么样子呢?通过对错误集合的更可控访问的成本(和好处),您可以创建一个表示该错误列表的对象,并将其传递到您想要添加错误的任何地方。
但在展示对象实例之前,我首先展示一个静态类的示例,因为它可能更接近您的心智模型和函数并且它是静态变量-
使用静态修饰符实现,您可以创建一个不可访问的全局变量(私有静态全局),该变量只能由向其添加消息的函数(以及同一类的其他静态函数)访问。
然后很容易添加另一个能够返回该变量内容的全局可访问函数:
final class Errors
{
private static $errors = [];
public static function add(string $error): void
{
self::$errors[] = $error;
}
public static function get(): array
{
return self::$errors;
}
}
该类提供对否则无法访问的静态变量 Errors::$errors 的有限访问。
它确实会在加载此类定义的那一刻自动初始化(例如,通过include/require 或自动加载器),可以通过以下方式添加新错误:
Errors::add($error);
错误可以通过以下方式读出:
var_dump(Errors::get());
现在将其用作全局变量更加安全,但是仍然只有一个全局错误列表,并且代码的每一部分都可以通过Errors::... 类标识符(类的名称)访问它。
因此,一段时间后所有可用名称都被消耗掉的问题并没有解决。
为了防止类可以从静态变为非静态,因此从全局静态函数移动到对象实例。这很容易做到,基本上只需删除静态修饰符并从self::$ 切换到$this->。很容易我一次完成整个示例:
final class Errors
{
private $errors = [];
public function add(string $error): void
{
$this->errors[] = $error;
}
public function get(): array
{
return $this->errors;
}
}
现在要让这个类正常工作,它需要做更多的工作,因为它的好处是
更好的控制。首先它需要初始化:
$errors = new Errors;
之后需要将错误集合传递到需要添加错误的每个地方:
$errors->add($error);
如果您愿意,可以读出目前收集到的所有错误:
var_dump($errors->get());
最后一个变体需要您管理(并因此考虑)代码的哪些部分需要访问哪些特定的错误集合(因为您可以创建多个)。因此,乍一看,这是更多的工作,但从长远来看,这会大大简化事情。您可以更改错误
集合(例如存储在文件中以及数组旁边),无需更改添加消息的所有位置。
您可以为不同的用途提供不同的错误收集对象,例如仅在测试场景或程序的子模块中。
所以这也提供了更多的灵活性(只需从最低限度开始)。
中间的静态类现在可能看起来是圣杯,但我想说这些只是平庸的,并且会很快消耗大量代码,因为它们不如全局变量强大但也会消耗随着您的应用程序变大,该名称不能轻易更改。
如果用途明确,全局变量可以是一个强大的工具,也可以是静态访问。但问题在于细节,而且在更加差异化的层面上,在面向对象编程的意义上使用对象实例通常非常有用。