Sepeda lain: menulis autoloader kelas Anda sendiri untuk Bitrix

Tidak peduli apa kata orang, tetapi saya pikir penemuan sepeda adalah hal yang bermanfaat. Menggunakan perpustakaan dan kerangka kerja yang sudah jadi, tentu saja, itu baik, tetapi kadang-kadang Anda harus menunda dan membuat sesuatu milik Anda sendiri. Inilah cara kami menjaga otak dalam kondisi yang baik dan menyadari potensi kreatif kami.



Artikel ini akan panjang, jadi duduklah saat saya mulai.





UPD: Ternyata, metode yang dijelaskan dalam artikel ini tidak membantu dalam semua kasus - ketika datang ke ORM, di mana penamaan kelas dan file berbeda (misalnya, kelas ConfigTable, yang ada di file config.php), masalah dan kesalahan dimulai. Karena itu, masih lebih baik menggunakan Composer.




Jadi, Bitrix, atau lebih tepatnya, Bitrix Framework. Meskipun ada API yang kaya, dari waktu ke waktu ada kebutuhan untuk membuat kelas / perpustakaan Anda sendiri, serta menghubungkan yang pihak ketiga. Karena itu, untuk mulai dengan, mari kita lihat metode pengisian otomatis yang ada.



Lama yang baik termasuk / membutuhkan. Saya menambahkannya murni untuk referensi sejarah. Meskipun pada awal jalur pemrograman saya, saya menempatkan kelas dan pustaka yang diperlukan ke dalam folder terpisah, membuat file terpisah di mana saya memasukkan semua kelas ini dan hanya kemudian memasukkan file dengan inklusi (saya minta maaf untuk tautologi).



Komposer.Memungkinkan Anda untuk menghubungkan kelas Anda sendiri dan perpustakaan pihak ketiga. Namun, ketika menambahkan kelas baru, itu membutuhkan pembaruan manual. Selain itu, kelas itu sendiri, file dan ruang nama juga perlu ditulis secara manual. Anda dapat membaca tentang cara membuat teman Bitrix dengan komposer di sini



Bitrix loader . Ini digunakan baik untuk menghubungkan modul dan untuk kelas autoloading. Namun, sebelum menghubungkan kelas yang diperlukan, Anda harus membentuk array, di mana kunci akan menjadi nama-nama kelas, dan nilai-nilai jalur ke sana. Dan semuanya akan terlihat seperti ini:



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

Loader::registerAutloadClasses(null, $classes);


Modul khusus. Mereka mengatakan bahwa ini adalah cara yang paling direkomendasikan - Anda membuat modul, menginstalnya di area admin, lalu menghubungkannya di mana saja dan menggunakannya untuk kesenangan Anda. Ini terlihat sederhana, tetapi dalam kenyataannya kami memiliki yang berikut:



  • Selain kelas menulis, Anda juga harus mendaftarkan prosedur untuk menginstal dan menghapus modul. Ada sejumlah parameter dan metode yang diperlukan, yang tanpanya modul mungkin tidak berfungsi (meskipun saya tidak tahu, saya belum mengujinya)
  • Kelas tidak akan berfungsi tanpa menghubungkan modul
  • Tidak selalu masuk akal untuk memindahkan kelas ke modul terpisah


Namun demikian, jika Anda menulis modul lokal Anda dan kemudian memutuskan untuk menambahkan beberapa kelas ke dalamnya, maka untuk menggunakannya Anda tidak perlu menginstal ulang modul - panggil metode yang diperlukan di tempat yang tepat, dan hanya itu!



Nah, sekarang, sebenarnya, sepeda itu sendiri ...



Setelah menganalisis semua metode di atas, saya memikirkan apa yang akan muncul sehingga cukup dengan hanya menambahkan kelas baru ke tempat tertentu, dan mereka kemudian akan dimuat secara otomatis, tanpa menambahkan elemen baru ke array namespaces dan path file.



Akibatnya, diputuskan untuk menulis modul khusus - tidak peduli betapa aneh kedengarannya, tetapi ide ini menurut saya lebih berhasil daripada menambahkan beberapa fungsi ke init.php - yang secara otomatis akan memuat semua kelas dari direktori yang diperlukan.



Saya akan menghilangkan proses penulisan instalasi / penghapusan modul - siapa pun yang membutuhkannya, mereka akan mencari di sumbernya, dan langsung menuju fungsi utama.



Karena Pada awalnya, jumlah level bersarang folder tidak diketahui, maka metode harus rekursif. Kami juga akan menggunakan kelas Bitrix \ Main \ Loader, yang akan memuat kelas-kelas tersebut.



Mari kita bayangkan bahwa kita memutuskan untuk meletakkan semua kelas kita di direktori / local / php_interface / lib:



gambar



Juga, kita mungkin memiliki file yang tidak mengandung kelas dan, karenanya, tidak boleh dimasukkan dalam autoloader, jadi poin ini juga harus diperhitungkan.



Jadi ayo pergi.



namespace Ramapriya\LoadManager;

use Bitrix\Main\Loader;

class Autoload
{
}


Pertama-tama, kita perlu mendapatkan semua isi folder kita. Untuk melakukan ini, mari kita menulis metode 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;
    }


Outputnya harus sebagai berikut:







Seperti yang bisa kita lihat, struktur file dihormati, sehingga Anda dapat mulai membentuk array untuk autoloading:



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


Jika semuanya dilakukan dengan benar, maka pada akhirnya kita akan mendapatkan array yang dihasilkan untuk autoloading menggunakan bitrix loader:







Untuk memeriksa fungsionalitasnya, tambahkan file MainException.php ke folder dengan pengecualian yang berisi kelas berikut:



<?php

namespace Ramapriya\Exceptions;

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


Seperti yang bisa kita lihat, file kita telah dimuat ke dalam array kelas: Ke







depan, mari kita coba panggil pengecualian baru kami:



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


Sebagai hasilnya, kita akan melihat:



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


Jadi, tetap bagi kita untuk menerapkan metode autoloading untuk array yang dihasilkan. Untuk tujuan ini, kita akan menulis metode dengan loadClasses nama paling dangkal, di mana kita akan melewati array yang dihasilkan:




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


Metode ini menggunakan bitrix loader, yang mendaftarkan array dengan kelas kami.



Sekarang hanya ada sedikit yang tersisa - untuk membentuk array dengan kelas dan memuatnya menggunakan kelas yang telah kita tulis. Untuk melakukan ini, di folder lib kami, buat file 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);


Selanjutnya, mari kita sertakan file ini di init.php:



// init.php

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

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


Alih-alih sebuah kesimpulan



Nah, selamat, sepeda kami siap dan melakukan pekerjaan yang sangat baik dengan fungsinya.

Sumbernya, seperti biasa, ada di github .



Terima kasih atas perhatian anda



All Articles