Ein weiteres Fahrrad: Schreiben Sie Ihren eigenen Autoloader für Bitrix

Egal was jemand sagt, aber ich denke, dass die Erfindung des Fahrrads eine nützliche Sache ist. Die Verwendung vorgefertigter Bibliotheken und Frameworks ist natürlich gut, aber manchmal sollten Sie sie verschieben und etwas Eigenes erstellen. So halten wir das Gehirn in guter Form und realisieren unser kreatives Potenzial.



Der Artikel wird lang sein, also lehnen Sie sich zurück, wenn ich anfange.





UPD: Wie sich herausstellte, hilft die in diesem Artikel beschriebene Methode nicht in allen Fällen - wenn es um ORM geht, bei dem die Benennung von Klassen und Dateien unterschiedlich ist (z. B. die ConfigTable-Klasse in der Datei config.php), beginnen Probleme und Fehler. Daher ist es immer noch besser, Composer zu verwenden.




Also Bitrix oder besser Bitrix Framework. Trotz des Vorhandenseins einer umfangreichen API müssen von Zeit zu Zeit eigene Klassen / Bibliotheken erstellt und Verbindungen von Drittanbietern hergestellt werden. Schauen wir uns daher zunächst die vorhandenen Autoload-Methoden an.



Gute alte enthalten / erfordern. Ich habe es nur als historische Referenz hinzugefügt. Obwohl ich zu Beginn meines Programmierpfads die erforderlichen Klassen und Bibliotheken in einem separaten Ordner abgelegt, eine separate Datei erstellt habe, in die ich alle diese Klassen aufgenommen habe, und erst dann die Datei mit den Einschlüssen aufgenommen habe (ich entschuldige mich für die Tautologie).



Komponist.Ermöglicht das Verbinden Ihrer eigenen Klassen und Bibliotheken von Drittanbietern. Beim Hinzufügen neuer Klassen ist jedoch eine manuelle Aktualisierung erforderlich. Darüber hinaus müssen die Klassen selbst, Dateien und Namespaces auch manuell geschrieben werden. Lesen Sie hier, wie Sie Bitrix mit einem Komponisten befreundet haben.



Bitrix Loader . Es wird sowohl zum Verbinden von Modulen als auch zum automatischen Laden von Klassen verwendet. Bevor Sie jedoch die erforderlichen Klassen verbinden, müssen Sie ein Array bilden, in dem die Schlüssel die Namen der Klassen und die Werte des Pfads zu ihnen sind. Und alles wird ungefähr so ​​aussehen:



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

Loader::registerAutloadClasses(null, $classes);


Benutzerdefinierte Module. Sie sagen, dass dies der am meisten empfohlene Weg ist - Sie erstellen ein Modul, installieren es im Administrationsbereich, schließen es an einer beliebigen Stelle an und verwenden es zu Ihrem Vergnügen. Es sieht einfach aus, aber in Wirklichkeit haben wir Folgendes:



  • Zusätzlich zum Schreiben von Klassen müssen Sie auch die Prozedur zum Installieren und Entfernen des Moduls registrieren. Es gibt eine Reihe erforderlicher Parameter und Methoden, ohne die das Modul möglicherweise nicht funktioniert (obwohl ich es nicht weiß, habe ich es nicht getestet).
  • Klassen funktionieren nicht, ohne das Modul anzuschließen
  • Es ist nicht immer sinnvoll, eine Klasse in ein separates Modul zu verschieben


Wenn Sie jedoch Ihr lokales Modul geschrieben und dann beschlossen haben, ein paar weitere Klassen hinzuzufügen, müssen Sie das Modul nicht mehr neu installieren, um sie zu verwenden. Rufen Sie einfach die erforderlichen Methoden an der richtigen Stelle auf, und fertig!



Nun, in der Tat, das Fahrrad selbst ...



Nachdem ich alle oben genannten Methoden analysiert hatte, überlegte ich, was ich tun sollte, damit es ausreicht, einfach neue Klassen an einer bestimmten Stelle hinzuzufügen, und diese dann automatisch geladen werden, ohne dem Array von Namespaces und Dateipfaden neue Elemente hinzuzufügen.



Aus diesem Grund wurde beschlossen, ein spezielles Modul zu schreiben - so seltsam es auch klingen mag, aber diese Idee schien mir erfolgreicher zu sein, als init.php einige Funktionen hinzuzufügen -, die automatisch alle Klassen aus dem erforderlichen Verzeichnis laden.



Ich werde den Prozess des Schreibens der Installation / Entfernung eines Moduls weglassen - wer es benötigt, wird in der Quelle nachsehen und direkt zur Hauptfunktionalität gehen.



weil Anfangs ist die Anzahl der Verschachtelungsebenen von Ordnern unbekannt, dann müssen die Methoden rekursiv sein. Wir werden auch die Bitrix \ Main \ Loader-Klasse verwenden, die die Klassen lädt.



Stellen wir uns vor, wir haben beschlossen, alle unsere Klassen in das Verzeichnis / local / php_interface / lib zu stellen:



Bild



Möglicherweise haben wir auch Dateien, die keine Klassen enthalten und dementsprechend nicht im Autoloader enthalten sein sollten, daher sollte dieser Punkt ebenfalls berücksichtigt werden.



So lass uns gehen.



namespace Ramapriya\LoadManager;

use Bitrix\Main\Loader;

class Autoload
{
}


Zunächst müssen wir den gesamten Inhalt unseres Ordners abrufen. Schreiben Sie dazu die scanDirectory-Methode:



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


Die Ausgabe sollte wie folgt aussehen:







Wie wir sehen können, wird die Dateistruktur eingehalten, sodass Sie ein Array für das automatische Laden erstellen können:



/*     $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;
    }


Wenn alles richtig gemacht wurde, erhalten wir am Ende ein generiertes Array zum automatischen Laden mit einem Bitrix-Loader:







Um die Funktionalität zu überprüfen, fügen Sie die Datei MainException.php dem Ordner mit Ausnahmen hinzu, die die folgende Klasse enthalten:



<?php

namespace Ramapriya\Exceptions;

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


Wie wir sehen können, wurde unsere Datei in eine Reihe von Klassen geladen:







Mit Blick auf die Zukunft versuchen wir, unsere neue Ausnahme aufzurufen:



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


Als Ergebnis werden wir sehen:



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


Es bleibt also für uns, die Autoloading-Methode für das resultierende Array zu implementieren. Zu diesem Zweck schreiben wir eine Methode mit dem banalsten Namen loadClasses, wobei wir das resultierende Array übergeben:




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


Diese Methode verwendet einen Bitrix-Loader, der ein Array bei unseren Klassen registriert.



Jetzt bleibt nur noch sehr wenig übrig - um ein Array mit Klassen zu bilden und diese mit der von uns geschriebenen Klasse zu laden. Erstellen Sie dazu in unserem lib-Ordner eine include.php-Datei:



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


Als nächstes fügen wir diese Datei in init.php ein:



// init.php

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

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


Anstelle einer Schlussfolgerung



Herzlichen Glückwunsch, unser Fahrrad ist fertig und leistet mit seiner Funktion hervorragende Arbeit.

Die Quellen sind wie immer auf dem Github .



Vielen Dank für Ihre Aufmerksamkeit.



All Articles