раздаю src: __autoload(), рекурсиво сканирующая каталог classes...

dimgel

Новичок
раздаю src: __autoload(), рекурсиво сканирующая каталог classes...

Всем привет!

Вот, решил поделиться.

В условия резкой нехватки namespaces, я хотя бы обеспечил себя возможностью хранить файлы классов в различных подкаталогах. При первом вызове __autoload() сканирует все подкаталоги каталога classes, и выстраивает ассоциативный массив (className => filePath). Имена файлов могут иметь вид className.php или className-version.php (например, DBV-001.obsolete.php), последнее я сделал ради попытки как-то упорядочить свой бардак разных версий классов для разных проектов в едином репозитарии, с возможностью быстрого(?) и удобного(?) обновления сайтов, использующих одни и те же версии интерфейсов. В процессе сканирования функция проверяет уникальность имени класса.

Файл /_php/__autoload.php:

PHP:
<?php
// (c) dimgel
// function __autoload() - class loader system function.


// Configuration:
$__autoload__classBase = dirname(__FILE__) . '/classes/';


function __autoload($className) {
	global $__autoload__classBase;
	static $classPaths = NULL;  // Associative array (className => filePath).
	if (!$classPaths) { 
		$classPaths = __autoload__scanPaths();
	}
	if (isset($classPaths[$className])) {
		include_once($__autoload__classBase . $classPaths[$className]);
	}
	// If requested class is still not loaded after function exits, 
	// PHP triggers 'Class not found' fatal error.
}


// Internal. Recursive. 
// Parameter $path is relative to _php/classes, e.g. "" for root, or "DBAPI/".
// Returns associative array (className => filePath) for given $path.
function __autoload__scanPaths($path = "") {
	global $__autoload__classBase;
	$result = array();
	$absPath = $__autoload__classBase . $path;
	
	// Strip last '/' from $absPath before calling scandir().
	$fileNames = scandir(substr($absPath, 0, strlen($absPath)-1));
	if ($fileNames) {
		foreach($fileNames as $fileName) {
			if (($fileName == '.') || ($fileName == '..')) {
				continue;
			}
			if (is_dir($absPath . $fileName)) {
				$subResult = __autoload__scanPaths($path . $fileName . "/");
				foreach($subResult as $subClassName => $subFilePath) {
					__autoload__addClass($result, $subClassName, $subFilePath);
				}
			}
			else {
				if (preg_match("/^([a-zA-Z0-9_]+)(-[0-9a-z\\.]+)?\\.php$/", 
						$fileName, $matches)) 
				{
					__autoload__addClass($result, $matches[1], $path . $fileName);
				}
			}
		}
	}
	return $result;
}


// Internal. Adds class into result array, with className uniqueness checks.
function __autoload__addClass(&$result, $className, $filePath) {
	if (!isset($result[$className])) {
		$result[$className] = $filePath;
	}
	else {
		throw new Exception("Classloader ambiguity for class '$className': files '" . 
					$result[$className] . "' and '$filePath'.");
	}
}
?>
 

_RVK_

Новичок
Нда... при каждом запросе сканировать идректорию? Или я не заметил у тебя там компиляции?
 

dimgel

Новичок
Компиляции нет. А директории у меня не 20-ти уровней вложенности, и существенного замедления я у себя не заметил (imho один mysql select сожрет больше). Зато жуть как удобно.
 

Scud

Новичок
Я для того чтобы не сканировать директории каждый раз сбрасываю резльтат сканирования с помощью serialize() на диск в файл autoloader.data, и провожу скан только если не нашел на диске этот файл.
 

crocodile2u

http://vbolshov.org.ru
Scud
Угу. добавил новый класс и все посыпалось, если забыл удалить этот автолоадер.дата.
 

Scud

Новичок
Не обязательно, можно например сделать так
if (!isset($classPaths[$className])) {
$this->rescan();
}

делов то, а вот если и после рескана нет, то всё посыпалось ;)
 

a_[w]

Новичок
Re: раздаю src: __autoload(), рекурсиво сканирующая каталог classes...

Файл /_php/__autoload.php:

PHP:
	// Strip last '/' from $absPath before calling scandir().
	$fileNames = scandir(substr($absPath, 0, strlen($absPath)-1));
PHP:
	$flieNames = scandir(rtrim($absPath, '\/')); //Так, наверное, будет быстрее.
Я тоже реализовал "подобное" только, я это сделал, как эмуляцию инструкции import и packages из Java.
ClassLibrary::import('aw.template.some.package.SomeClass');(использую Singletone паттерн) это же используется в __autoload();
И этот класс не сканирует каждый раз всё дерево т.к. иногда из-за этого могут быть серьёзные задержки. Он создаёт "облегчённую" версию дерева каталогов в XML дереве и в следующие разы обращается именно к нему. + каждый класс имеет вид class.SomeClassName.inc.php, да так как то удобнее....
Скорость, вполне, на уровне.

Единственная проблема с неуникальными именами классов... :(
 

Sender

Новичок
мой автолоадер круче :) мы его обсуждали уже на форуме, правда я немного доработал, обкатал его

http://phpclub.ru/paste/index.php?show=1696
 

dark-demon

d(^-^)b
на тему велосипедов: http://php.net/glob

Компиляции нет. А директории у меня не 20-ти уровней вложенности, и существенного замедления я у себя не заметил (imho один mysql select сожрет больше).
любой проект имеет тягу к росту, поэтому лучше сразу делать хорошо, чем потом в срочном порядке исправлять, когда всё начинает валиться.
 

AmdY

Пью пиво
Команда форума
а ещё есть всем известный вариант.
Код:
firstDir_secondDir_className - такое вот название класа,
файл соответственно находится - firstDir/secondDir/className.php
 

a_[w]

Новичок
Автор оригинала: AmdY
а ещё есть всем известный вариант.
Код:
firstDir_secondDir_className - такое вот название класа,
файл соответственно находится - firstDir/secondDir/className.php
Это и есть разбивка по пакетам. В Java, ActionScript обязательна к соблюдению. Но там легче тем что в разных пакетах могут быть одноимённые классы и, вполне возможно, использоватся в одном проекте. Правда, пока мне это не мешает, а по такому правилу хранить классы, а не на куче, очень даже удобно.
 

standov

Новичок
Есть такая штука - http://php.com.ua/ru/articles/bicycles/loader.htm

Это собтвенно лоадер, использующий соглашения Java но не такой ресурсоемкий как Ваш, вы сами указываете нужные "пакеты"
 

dark-demon

d(^-^)b
AP, нет. какие у тебя есть данные на эту тему в сравнении с предложенными вариантами?

-~{}~ 13.05.07 20:18:

a_[w], и и как в разных пакетах могут быть одноимённые классы, если название пакета содержится в названии класса? :)
 

AP

Новичок
dark-demon, 14 файлов обрабатываются glob'oм 58мс, причём расположенных в одной дтректории...
мой выбор за вариантом AmdY,хотя и не тестил...
 

dark-demon

d(^-^)b
я думаю стоит потестить, а не гадать на кофейной гуще. ничего быстрее банального include в данном случае нет, но от него решили отказаться не смотря на некоторое падение скорости.
 

a_[w]

Новичок
Да, согласен, по поводу пакетов. Но в РНР то не существует пакетов, а именование папок это лишь выдуманная нами функциональность которая, якобы несёт некоторые удобства для программиста. Поэтому я отличаю названия пакетов и имён класса для РНР. И насколько я знаю, ещё достоверно не известно, что именно войдёт в состав РНР - namespaces как в C# или packages как в Java. :)
Резюме: Всё что я написал - чушь, суть от этого не меняется. :)

Далее, хотелось бы отметить, что конечному пользователю наплевать как и куда вы будете складывать классы. :) И в конечном продукте по моему, просто нужно будет собрать классы в одно хранилище и заменить функции использовавшиеся во время тестирования на

include self::LIB_PATH.str_replace('.', '/', $package).'.inc.php';

и не морочить себе(и клиенту) голову.
glob презамечательнейшая ф-ция и отказыватся от неё не вижу смысла. Её скорость работы будет меньше сказыватся на скорости работы всей программы, если сделать кеширование собранных ей данных(информации о пакетах). Но тогда будет занимать время считывание из файла и парсинг формата данных.

З.Ы.: Короче, я хотел сказать, что это всё не так существенно, но не необоснованно.
 

dark-demon

d(^-^)b
Об этом говорилось выше. Это не догма.
да при чём тут догма/недогма? зачем выполнять лишнее действие? "у всего есть причина", - как говаривал товарищ Меровингер :)

C# vs. Java - namespaces vs. packages
то есть никакой? ну, если не рассматривать сферического коня со вложенными пространствами имён...
 
Сверху