gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 23:05:48 25 4
gpt4 key购买 nike

我正在更新我之前编写的框架。我想采用新的标准,例如名称空间和使用自动加载功能。

现在,我的框架具有一个非常基本但实用的自动加载功能,如下所示:

protected function runClasses()
{
$itemHandler = opendir( V_APP_PATH );
while( ( $item = readdir( $itemHandler ) ) !== false )
{
if ( substr( $item, 0, 1 ) != "." )
{
if( !is_dir( $item ) && substr( $item, -10 ) == ".class.php" )
{
if( !class_exists( $this->registry->$item ) )
{
require_once( V_APP_PATH . $item );
$item = str_replace( ".class.php", "", $item );
$this->registry->$item = new $item( $this->registry );
}
}
}
}
}


正如您在代码中看到的那样,该函数仅限于单个文件夹,但它的优点是它将类加载到我的注册表中,从而允许我通过执行类似于 $this->registry->Class->somevar的操作来访问其他文件中的特定类。我需要的功能。

我需要/想要完成的是使用自动加载器功能,但该功能不仅限于单个文件夹,而是能够在多个文件夹中导航并实例化所需的类。

我只有一些测试文件,这是我当前的文件结构:



对于MyClass2我有:

namespace model;
Class MyClass2 {
function __construct() {
echo "MyClass2 is now loaded!";
}
}


对于MyClass1,我有:

Class MyClass1 {
function __construct() {
echo "MyClass1 is now loaded!<br />";
}
}


对于自动加载,我有:

function __autoload( $className ) {
$file = $className . ".php";
printf( "%s <br />", $file );
if(file_exists($file)) {
require_once $file;
}
}

$obj = new MyClass1();
$obj2 = new model\MyClass2();


我的问题是设置方法,无法为MyClass2找到文件,所以我想知道我做错了什么,其次,有没有像我的第一个“自动加载”功能那样无需指定名称空间的方法在自动加载文件并将其分配给我的注册表?

很抱歉给您一个冗长的问题,但我们将不胜感激。

最佳答案

我在这里看到两件事。

第一个使您的问题有点复杂。您想使用命名空间,但是当前的配置是通过文件系统进行的。到目前为止,类定义文件的文件名不包含名称空间。因此,您不能像实际那样继续下去。

第二个是您没有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,它允许您使用多个自动加载器功能。这样做通常很有用的原因有很多(例如,假设您使用了第三方软件包),但是,这种动态魔术盒(称为您的 Registry)正是它的完美工具。一个直接的解决方案(并且不做任何过早的优化)是为注册表定义中的每个类注册一个自动加载器函数。然后,这需要一种新型的加载程序,而autoloader函数仅是两行代码左右:

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.


现在,您可以像地狱一样懒惰。这意味着要再次更改代码。因为您想远程控制将这些文件实际放入该特定目录的必要性。等等,这就是您要的内容,因此我们将其留在此处。

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

完整的代码示例(不包含 registry.jsontest.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文件的格式,以防止出现第一手配置问题。

还应考虑传递给每个构造函数的注册表对象不是一个参数,而是代表动态数量的参数。迟早会开始产生副作用。如果您过多地使用了注册表,那么越早越好。这些副作用将使您花费很多维护成本,因为通过设计,这已经是有缺陷的,因此您只能通过辛苦工作,对回归进行繁重的集成测试等来控制它。

但是,根据您自己的经验,这只是一些看法,并不是您以后告诉我的时候我没有注意到。

关于php - PHP中的基本自动加载和命名空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12516837/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com