php自动加载和命名空间,php中的基本自动加载和命名空间

这篇博客探讨了在PHP中如何处理命名空间和自动加载的挑战。通过创建一个懒加载注册表,实现了类的延迟实例化,并利用JSON配置文件解析类映射,以简化文件系统依赖。同时,通过自定义的自动加载器,确保只有在需要时才加载类定义,从而提高效率。博客还提出了对现有设计的改进建议,包括减少魔法常量和提高代码可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我在这看到两件事.

第一个使你的问题有点复杂.您希望使用命名空间,但您当前的配置是通过文件系统.到目前为止,类定义文件的文件名不包含命名空间.所以你不能像实际那样继续.

第二个是你没有PHP自动加载所涵盖的内容,你只需加载一组已定义的类并将其注册到注册表中.

我不确定你是否需要PHP自动加载.当然,将两者结合在一起看起来很有希望.解决第一点可能会帮助你解决后面的问题,所以我建议先从它开始.

让我们使隐藏的依赖项更加明显.在您目前的设计中,您有三件事:

>对象在注册表中注册的名称.

>包含类定义的文件名.

>类本身的名称.

2.和3.的值在一个中,您从文件名解析类本身的名称.如上所述,命名空间现在使这变得复杂.解决方案很简单,您可以从包含此信息的文件中读取,而不是从目录列表中读取.轻量级配置文件格式是json:

{

"Service": {

"file": "test.class.php",

"class": "Library\\Of\\Something\\ConcreteService"

}

}

现在包含三个所需的依赖项,用于通过名称将类注册到注册表中,因为文件名也是已知的.

然后,您可以在注册表中注册类:

class Registry

{

public function registerClass($name, $class) {

$this->$name = new $class($this);

}

}

并为json格式添加一个加载器类:

interface Register

{

public function register(Registry $registry);

}

class JsonClassmapLoader implements Register

{

private $file;

public function __construct($file) {

$this->file = $file;

}

public function register(Registry $registry) {

$definitions = $this->loadDefinitionsFromFile();

foreach ($definitions as $name => $definition) {

$class = $definition->class;

$path = dirname($this->file) . '/' . $definition->file;

$this->define($class, $path);

$registry->registerClass($name, $class);

}

}

protected function define($class, $path) {

if (!class_exists($class)) {

require($path);

}

}

protected function loadDefinitionsFromFile() {

$json = file_get_contents($this->file);

return json_decode($json);

}

}

这里没有太大的魔力,json文件中的文件名相对于它的目录.如果尚未定义类(此处触发PHP自动加载),则需要该类的文件.完成后,该类按其名称注册:

$registry = new Registry();

$json = new JsonClassmapLoader('path/registry.json');

$json->register($registry);

echo $registry->Service->invoke(); # Done.

这个例子也非常简单,而且很有效.所以第一个问题就解决了.

第二个问题是自动加载.这个当前的变体和你以前的系统确实隐藏了其他东西.有两个中心事情要做.一个是实际加载类定义,另一个是实例化对象.

在您的原始示例中,技术上不需要自动加载,因为对象在注册表中注册的那一刻,它也是实例化的.您这样做也是为它分配注册表.我不知道你是否只是因为这样做,或者这只是发生在你身上.你在问题中写下你需要的那个.

因此,如果您想将自动加载到您的注册表中(或延迟加载),这会有所不同.由于你的设计已经搞砸了,让我们继续添加更多魔力.您希望将注册表组​​件的实例化推迟到第一次使用它的时刻.

与在注册表中一样,组件的名称比它的实际类型更重要,这已经非常动态并且只是一个字符串.要延迟组件创建,在注册时但在访问时不会创建类.这可以通过使用需要新类型注册表的__get函数来实现:

class LazyRegistry extends Registry

{

private $defines = [];

public function registerClass($name, $class)

{

$this->defines[$name] = $class;

}

public function __get($name) {

$class = $this->defines[$name];

return $this->$name = new $class($this);

}

}

用法示例再次完全相同,但是,注册表的类型已更改:

$registry = new LazyRegistry();

$json = new JsonClassmapLoader('path/registry.json');

$json->register($registry);

echo $registry->Service->invoke(); # Done.

所以现在已经推迟了具体服务对象的创建,直到首次访问.但是,这仍然不是自动加载.类定义的加载已经在json加载器中完成.它不会因为已经创造出动态和魔力,而不是那样.我们需要为每个类启动一个自动加载器,它应该在第一次访问对象时启动.例如.我们实际上希望能够在应用程序中使用腐烂的代码,这些代码可能永远不会被注意到,因为我们不关心它是否被使用.但是我们不想把它加载到内存中.

对于自动加载,您应该了解spl_autoload_register,它允许您具有多个自动加载器功能.有很多原因可以解释为什么它通常很有用(例如,想象你使用第三方软件包),但是这个动态魔术盒称为你的注册表,它只是这项工作的完美工具.一个直接的解决方案(并没有进行任何过早的优化)是为我们在注册表定义中的每个类注册一个自动加载器函数.这需要一种新型的加载器,自动加载器功能只需两行代码:

class LazyJsonClassmapLoader extends JsonClassmapLoader

{

protected function define($class, $path) {

$autoloader = function ($classname) use ($class, $path) {

if ($classname === $class) {

require($path);

}

};

spl_autoload_register($autoloader);

}

}

用法示例再次没有太大变化,只是加载器的类型:

$registry = new LazyRegistry();

$json = new LazyJsonClassmapLoader('path/registry.json');

$json->register($registry);

echo $registry->Service->invoke(); # Done.

现在你可以懒得下地狱.这意味着,要再次实际更改代码.因为您希望远程实现将这些文件实际放入该特定目录的必要性.等等,这就是你要求的,所以我们把它留在这里.

否则,请考虑使用将在首次访问时返回实例的callables配置注册表.这通常会使事情变得更加灵活.自动加载 – 如图所示 – 与您实际可以离开基于目录的方法无关,您不再关心代码打包在具体的位置(http://www.getcomposer.org/).

整个代码示例完整(没有registry.json和test.class.php):

class Registry

{

public function registerClass($name, $class) {

$this->$name = new $class($this);

}

}

class LazyRegistry extends Registry

{

private $defines = [];

public function registerClass($name, $class) {

$this->defines[$name] = $class;

}

public function __get($name) {

$class = $this->defines[$name];

return $this->$name = new $class($this);

}

}

interface Register

{

public function register(Registry $registry);

}

class JsonClassmapLoader implements Register

{

private $file;

public function __construct($file) {

$this->file = $file;

}

public function register(Registry $registry) {

$definitions = $this->loadDefinitionsFromFile();

foreach ($definitions as $name => $definition) {

$class = $definition->class;

$path = dirname($this->file) . '/' . $definition->file;

$this->define($class, $path);

$registry->registerClass($name, $class);

}

}

protected function define($class, $path) {

if (!class_exists($class)) {

require($path);

}

}

protected function loadDefinitionsFromFile() {

$json = file_get_contents($this->file);

return json_decode($json);

}

}

class LazyJsonClassmapLoader extends JsonClassmapLoader

{

protected function define($class, $path) {

$autoloader = function ($classname) use ($class, $path) {

if ($classname === $class) {

require($path);

}

};

spl_autoload_register($autoloader);

}

}

$registry = new LazyRegistry();

$json = new LazyJsonClassmapLoader('path/registry.json');

$json->register($registry);

echo $registry->Service->invoke(); # Done.

我希望这是有帮助的,但是这主要是在沙盒中播放,你迟早会粉碎它.您实际想要了解的是控制反转,依赖注入,然后是依赖注入容器.

你有的注册表是某种气味.这一切都充满了魔力和动力.您可能认为这对于开发来说很酷或者在系统中有“插件”(很容易扩展),但是您应该将对象的数量保持在较低水平.

Magic可能很难调试,所以你可能想要检查json文件的格式,如果它在你的情况下首先是有意义的,以防止第一手配置问题.

还要考虑传递给每个构造函数的注册表对象不是一个参数,而是表示动态数量的参数.这将开始迟早产生副作用.如果您使用的注册表太多,那么越早越好.这种副作用会使你的维护成本很高,因为通过设计这已经存在缺陷,因此你只能通过努力工作,对回归等进行大量集成测试来控制它.

然而,制作你自己的经历,这只是一些前景而不是你以后告诉我我没有注意到它.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值