另一辆自行车:为Bitrix编写自己的自动加载器类

不管有人怎么说,但是我认为自行车的发明是有用的。使用现成的库和框架固然很好,但是有时您应该推迟使用它们并创建自己的东西。这就是我们保持大脑良好状态并发挥创造潜能的方式。



这篇文章会很长,所以在我开始时请坐下。





UPD:事实证明,本文中描述的方法在所有情况下均无济于事-当涉及到ORM时,类和文件的命名是不同的(例如config.php文件中的ConfigTable类),问题和错误开始出现。因此,最好还是使用Composer。




因此,Bitrix或更确切地说是Bitrix Framework。尽管存在丰富的API,但仍需要不时创建自己的类/库以及连接第三方类/库。因此,首先,让我们看一下现有的自动加载方法。



好旧的包括/要求。我添加它纯粹是为了历史参考。尽管在我编程的初期,我将必要的类和库放在一个单独的文件夹中,创建了一个单独的文件,其中包含了所有这些类,然后才将该文件包含在内(对于重言式,我深表歉意)。



作曲家。允许您连接自己的类和第三方库。但是,添加新类时,需要手动更新。此外,类本身,文件和名称空间也需要手动编写。您可以



Bitrix loader中阅读有关如何与作曲家结识Bitrix的知识它用于连接模块和自动加载类。但是,在连接必要的类之前,您将必须形成一个数组,其中的键将是类的名称以及它们的路径值。一切看起来像这样:



$classes = [
    'Namespace\\Package\\ClassName' => '/path/to/class.php'
];

Loader::registerAutloadClasses(null, $classes);


定制模块。他们说这是最推荐的方法-创建一个模块,将其安装在管理区域中,然后将其连接到任何地方,然后随便使用。看起来很简单,但实际上我们有以下几点:



  • 除了编写类,您还需要注册安装和卸下模块的过程。有许多必需的参数和方法,没有这些参数和方法,模块可能无法正常工作(尽管我不知道,但我尚未对其进行测试)
  • 如果不连接模块,类将无法工作
  • 将类移到单独的模块中并不总是很有意义


但是,如果您编写了本地模块,然后决定向其添加更多的类,那么使用它们就不再需要重新安装模块了-只需在正确的位置调用必要的方法即可!



好吧,事实上,自行车本身...



在分析完所有上述方法之后,我考虑了要提出的内容,这样就可以简单地将新类添加到特定位置,然后将它们自动加载,而无需在名称空间和文件路径数组中添加新元素。



结果,决定编写一个特殊的模块 -听起来可能很奇怪,但是在我看来,这个想法比向init.php中添加几个功能更成功-该模块将自动从所需目录中加载所有类。



我将省略编写模块安装/删除的过程-无论需要什么,他们都会在源代码中查找并直接使用主要功能。



因为 最初,文件夹嵌套级别的数量是未知的,然后这些方法需要递归。我们还将使用Bitrix \ Main \ Loader类,它将加载这些类。



假设我们决定将所有类都放在/ local / php_interface / lib目录中:



图片



此外,我们可能具有不包含类的文件,因此不应将它们包含在自动加载器中,因此也应考虑这一点。



所以走吧



namespace Ramapriya\LoadManager;

use Bitrix\Main\Loader;

class Autoload
{
}


首先,我们需要获取文件夹的所有内容。为此,让我们编写scanDirectory方法:



    public static function scanDirectory(string $dir) : array
    {
        $result = [];
        $scanner = scandir($dir); //   
        foreach ($scanner as $scan) {
            switch ($scan) {
                // 
                case '.': 
                case '..':
                    break;
                default:
//                          
                    $item = $dir . '/' . $scan; 
                    $SplFileInfo = new \SplFileInfo($item);
    
                    if($SplFileInfo->isFile()) {
//    ,        
                        $result[] = $scan; 
                        
                    } elseif ($SplFileInfo->isDir()) {
//    ,                                 
                        $result[$scan] = self::scanDirectory($item, $result[$scan]); 
    
                    }
            }
        }
    
        return $result;
    }


输出应为以下内容:







如我们所见,文件结构受到尊重,因此您可以开始形成用于自动加载的数组:



/*     $defaultNamespace,        . 
   php-,      
*/
    public static function prepareAutoloadClassesArray(string $directory, string $defaultNamespace, array $excludeFiles) : array
    {
        $result = [];
//   
        $scanner = self::scanDirectory($directory); 
    
        foreach ($scanner as $key => $value) {
    
            $sep = '\\';
            
            switch(gettype($key)) {
                
                case 'string':
//     ,    
                    $SplFileInfo = new \SplFileInfo($directory . '/' . $key);
                    $classNamespace = $defaultNamespace . $sep . $key;
    
                    if($SplFileInfo->isDir()) {
//   ,    ,   ,    ,      
                        $tempResult = self::prepareAutoloadClassesArray($directory . '/' . $key, $classNamespace, $excludeFiles);
                        foreach($tempResult as $class => $file) {
//         
                            $result[$class] = $file; 
                        }
                    }
    
                    break;
    
                case 'integer':
//    - ,        
                    $SplFileInfo = new \SplFileInfo($directory . '/' . $value);
//      (           ,    )
                    $classNamespace = $defaultNamespace . $sep . str_ireplace('.php', '', $SplFileInfo->getBasename()); 

//      php-
                    if(
                        $SplFileInfo->isFile() &&
                        $SplFileInfo->getExtension() === 'php'
                    ) {
 //      ,      
                        foreach($excludeFiles as $excludeFile) {
                            if($SplFileInfo->getBasename() !== $excludeFile) {
//        
                                $result[$classNamespace] = str_ireplace($_SERVER['DOCUMENT_ROOT'], '', $directory . '/' . $value); 
                            }
                        }                        
                        
                    }
    
                    break;
                    
            }
    
        }
    
        return $result;
    }


如果一切都正确完成,那么最后,我们将获得一个生成的数组,以便使用bitrix加载器自动加载:







要检查功能,请将MainException.php文件添加到文件夹中,但包含以下类的异常除外:



<?php

namespace Ramapriya\Exceptions;

class MainException extends \Exception
{
    public function __construct($message = null, $code = 0, Exception $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }
}


如我们所见,我们的文件已加载到一个类数组中:







展望未来,让我们尝试调用我们的新异常:



throw new Ramapriya\Exceptions\MainException('test exception');


结果,我们将看到:



[Ramapriya\Exceptions\MainException] 
test exception (0)


因此,仍然需要为结果数组实现自动加载方法。为此,我们将编写一个具有最普通名称loadClasses的方法,并在其中传递结果数组:




    public static function loadClasses(array $classes, $moduleId = null)
    {
        Loader::registerAutoloadClasses($moduleId, $classes);
    }


此方法使用bitrix加载程序,该加载程序在我们的类中注册一个数组。



现在剩下的几乎没有-用类形成一个数组并使用我们编写的类加载它们。为此,请在我们的lib文件夹中创建一个include.php文件:



<?php

use Bitrix\Main\Loader;
use Bitrix\Main\Application;
use Ramapriya\LoadManager\Autoload;

//    -      ,     
Loader::includeModule('ramapriya.loadmanager');

$defaultNamespace = 'Ramapriya';
$excludeFiles = ['include.php'];

$libDir = Application::getDocumentRoot() . '/local/php_interface/lib';

$autoloadClasses = Autoload::prepareAutoloadClassesArray($libDir, $defaultNamespace, $excludeFiles);

Autoload::loadClasses($autoloadClasses);


接下来,让我们将此文件包含在init.php中:



// init.php

$includeFile = $_SERVER['DOCUMENT_ROOT'] . '/local/php_interface/lib/include.php';

if(file_exists($includeFile)) {
    require_once $includeFile;
}


而不是结论



好了,恭喜,我们的自行车已经准备就绪,并且在功能方面做得很好。

像往常一样,源在github上



感谢您的关注。



All Articles