Whoever says anything, but I believe that the invention of bicycles is a useful thing. Using ready-made libraries and frameworks is, of course, good, but sometimes it’s worth putting them aside and creating something of your own. So we keep our brain in good shape and realize our creative potential.

The article promises to be long, so get comfortable, I'm starting.

So, Bitrix, or rather, Bitrix Framework. Despite the presence of a rich API, periodically there is a need to create your own classes/libraries, as well as connecting third-party ones. Therefore, for starters, consider the existing startup methods.

Good old include/require. I added it purely for historical reference. Although at the dawn of my programming path, I put the necessary classes and libraries into a separate folder, created a separate file where I included all these classes, and only then included the file with inclusions (I apologize for the tautology).

Composer. Allows you to connect both your own classes and third-party libraries. However, adding new classes requires manual updating. In addition, the classes themselves, files, and namespaces themselves also need to be written manually. On how to make friends with Bitrix composer, you can read here

Bitrix Loader . It is used both for connecting modules and for autoload classes. However, before connecting the necessary classes, it is necessary to form an array, where the class names will act as keys, and the path values ​​to them. And it will look something like this:

$classes=[ 'Namespace\\Package\\ClassName' => '/path/to/class.php' ]; Loader::registerAutloadClasses(null, $classes); 

Own modules. They say that this is the most recommended way - you create a module, install it in the admin panel, then connect it anywhere and use it for your pleasure. Everything looks simple, but in reality we have the following:

  • In addition to writing classes, you must also specify the procedure for installing and removing the module. There are a number of required parameters and methods, without which the module may not work (although I don’t know, I didn’t check it)
  • Classes will not work without connecting the module
  • It doesn’t always make sense to place the class in a separate module

Nevertheless, if you wrote your local module and then decided to add a couple more classes to it, then to use them you no longer need to reinstall the module - just call the necessary methods in the right place, that's all!

Well, now, actually, the bike itself.


After analyzing all the above methods, I thought about what to come up with so that it would be enough just to add new classes to a specific place, and they would then be loaded automatically, without adding new elements to the array of namespaces and file paths.

In the end, it was decided to write a special module - no matter how strange it may sound, but this idea seemed to me more successful than adding several functions to init.php - which will automatically load all classes from the necessary directory.

I’ll omit the process of writing the installation/uninstallation of the module - anyone who needs it will look in the source, and immediately go to the main functionality.

Because Since the initial number of folder nesting levels is unknown, the methods need to be recursive. We will also use the Bitrix \ Main \ Loader class, which will load the classes.

Imagine that we decided to store all our classes in the directory/local/php_interface/lib:

image

Also, we may have files that do not contain classes and, therefore, should not be included in the autoloader, so this point should also be taken into account.

So let's go.

namespace Ramapriya\LoadManager; use Bitrix\Main\Loader; class Autoload { } 

First of all, we need to get all the contents of our folder.To do this, we write the scanDirectory method:

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; } 

The output should be the following:

ITKarma picture

As we can see, the file structure is respected, so you can start creating an array for startup:

/* Тут уже добавляется переменная $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; } 

If everything is done correctly, then in the end we will get a generated array for autoload using the bitrix loader:

ITKarma picture

To test the health, add the MainException.php file to the folder with exceptions, which contains the following class:

<?php namespace Ramapriya\Exceptions; class MainException extends \Exception { public function __construct($message=null, $code=0, Exception $previous=null) { parent::__construct($message, $code, $previous); } } 

As we can see, our file is loaded into an array of classes:

ITKarma picture

Looking ahead, let's try to raise our new exception:

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

As a result, we will see:

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

So, it remains for us to implement the autoload method of the resulting array. For this purpose, we will write a method with the most commonplace name loadClasses, where we will pass the resulting array:

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

This method uses a bitrix loader, which registers an array with our classes.

Now there is very little left - to form an array with classes and load them using the class we wrote. To do this, create an include.php file in our lib folder:

<?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); 

Next, connect this file to init.php:

//init.php $includeFile=$_SERVER['DOCUMENT_ROOT']. '/local/php_interface/lib/include.php'; if(file_exists($includeFile)) { require_once $includeFile; } 

Instead of a conclusion


Well, congratulations, our bike is ready and does its job perfectly.
Sources, as always, on the github .

Thanks for your attention.

Source