MixaDIOR Feodosian

Барахло/Хлам/PHP/Харьков/Феодосия/Я

Построение списка классов приложения на PHP

Август 17th, 2008 in Новости

Задача:
Проиндексировать файлы с php сырцами и построить список классов, описанных в этих файлах. На выходе должен получиться массив вида:
‘имяКласса’ => ‘имяФайлаВКоторомОписанКласс’

Зачем:

Нужно сделать autoload класса при создании объекта в проекте, где никакой структуры директорий и именования классов не наблюдаеться без каких либо перспектив на ее появление.

Решение:
Для начала нужно проиндексировать файлы в проекте, для этой задачи я использовал простенькую самописную рекурсивную функцию, результат выполнения которой - массив сериализировался в файл:

<?php
/**
 * getRecursiveFileList
 * 
 * рекурсивно получаем список всех .php и .inc файлов в директории и ее поддиректориях
 *
 * @param string $path
 * @param array $filelist
 * @return array
 */
function getRecursiveFileList($path, $filelist = array())
{
    $pathFileList = array_diff(scandir($path), array('.','..'));       
    foreach ($pathFileList as $key=>$fsElementName) {
        if (is_dir($path."/".$fsElementName)) {                                   
            $filelist = getRecursiveFileList($path."/".$fsElementName, $filelist);            
        }
        else {       
             //берем только .inc и .php файлы. Обратите внимание, на регулярку, возможно
             //вам нужно будет подправить ее под свои нужды.
             if (preg_match('/^[a-zA-Z\_0-9\.\-]+\.(php|inc)$/',$fsElementName, $matches)) {
                $filelist[] = $path."/".$fsElementName;           
            }
        }  
    }
    return $filelist;
 
}
//в данном примере я индексировал свежий AdoDB, результат сериализировал
$serializedContent = serialize(getRecursiveFileList("../adodb5"));
//дампим в файл
$tempFilelistCacheFileName = "./cache/_fsDump_Adodb";
$handle = fopen($tempFilelistCacheFileName, "w");
fwrite($handle, $serializedContent);
fclose($handle);

Далее на основе этого дампа нам нужно построить уже массив для автолоада. Вот тут начались интересности: многие сразу начинают кричать про get_declared_classes(); , но данное решение нам не подойдет, так как если даже в данном файле нет никаких классов, а есть только пару небрежно оставленных require/include, то индекснем мы его как носителя классов из проиклюдженных файликов. Остаеться только решение на основе синтаксического разбора исходника файла. Для этой задачи мне оптимально подошла функция token_get_all();:

<?php
 
<?php
//получаем сериализировнный массив списка файлов
$serializedContent = file_get_contents("./cache/_fsDump_Adodb");
$filesArray = unserialize($serializedContent);
$autoloadClassFiles = array();
//парсим на токен class и достаем имена классов
foreach ($filesArray as $key=>$fileName) {
    $fileTokens = token_get_all(file_get_contents($fileName));
    foreach ($fileTokens as $key=>$tokenInfo) {
        if ($tokenInfo[0] == T_CLASS) {
            //небольшой хак
            $autoloadClassFiles[$fileTokens[$key+2][1]] = $fileName;
        }
    }
}
//я задампил все это в файлик для примера
$serializedContent = serialize($autoloadClassFiles);
$tempFilelistCacheFileName = "./cache/_classesAutoloadFiles_Adodb5";
$handle = fopen($tempFilelistCacheFileName, "w");
fwrite($handle, $serializedContent);
fclose($handle);

Обяснение такого хака, как $autoloadClassFiles[$fileTokens[$key+2][1]] = $fileName; - довольно простое, после разбора на токены, после T_CLASS идет T_WHITESPACE, а только потом имя класса, имя класса не воспринимаеться как конструкция языка, а идет как T_STRING, тоесть мы банально берем токен через токен :-).
Напоследок хочу сказать, что я не хотел рассказать про готовое к продакшн решение, а хотел рассказать про пути решения данной проблемы, а мегафункциональный класс или консольное приложение напишете сами я думаю.
Скачать этот полностью рабочий пример с adodb5 в комплекте(для жиру :-)) можно скачать тут: http://yomoyo.feodosian.com/downloads/classIndexerExample.zip
Просто пошариться по сырцам можно здесь:
http://yomoyo.feodosian.com/srcFiles/classIndexerExample/

Tags: , ,

Leave a Reply

Архивы