Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

__autoload() с кешированием (Антон Макаренко)
Автор Сообщение
Антон Макаренко
Участник форума



Зарегистрирован: 05.02.2004
Сообщ.: 374
Карма: 30
   поощрить/наказать

Откуда: Киев

СообщениеДобавлено: Вс Июн 01, 2008 4:38 am (написано за 1 минуту 43 секунды)
   Заголовок сообщения: __autoload() с кешированием
Ответить с цитатой

Функция автоматически инклудит файлы с классами, см. официальное руководство. Кроме этого, умеет склеивать загруженные автоматически файлы и записывать их в один, а при последующих запусках скрипта — инклудить этот файл, чтобы не дергать лишний раз каждый файл с классом.

Пример кода, который следует разместить где-нибудь вначале скрипта — простой вариант:
Код (php): скопировать код в буфер обмена
require 'lib/__autoload.php';
"Full-featured"-вариант:
Код (php): скопировать код в буфер обмена
// setup include path
if (!defined('PATH_SEPARATOR'))
    define('PATH_SEPARATOR', getenv('COMSPEC')? ';' : ':');
ini_set('include_path', ini_get('include_path')
    . PATH_SEPARATOR . dirname(__FILE__) . '/lib'
    . PATH_SEPARATOR . dirname(__FILE__) . '/sys/classes'
);

// initiate autoload
define('__AUTOLOAD_CACHE_DIR', 'cache/classes');
define('__AUTOLOAD_MUTEX_FILE', 'cache/__is_autoload');
require 'lib/__autoload.php';
register_shutdown_function('__autoload');
Именование и пути
Именование классов и структура их папок/файлов должна соответствовать соглашениям по именованию, применяемых в Zend Framework:
Цитата:
Имена классов могут содержать только буквенно-числовые символы. ... Символы нижнего подчеркивания допустимы в местах разделителей пути - имя файла "Zend/Db/Table.php" должно указывать на класс с именем "Zend_Db_Table".
...
Т.е., имя класса разрезается по подчеркиванию "_", все составные части, кроме последней, будут считаться именами папок, а последняя — именем файла.

Подразумевается, что относительные пути и их приоритетность правильным образом заданы разработчиком в include path самостоятельно. Так, если в корне вашего проекта лежит файл lib/DbSimple/Generic.php, то путь к папке <корень проекта>/lib к моменту запуска функции уже должен быть зашит в include path. Поскольку в вышеуказанном файле лежит класс с именем DbSimple_Generic, то функция запросит файл DbSimple/Generic.php с помощью require.

Это основная область применения функции. К стандарту Zend лучше привыкнуть и начать использовать, не зря его написали. Насколько я знаю, стандарт PEAR именует размещает классы по аналогичной схеме.

Можно объявить константу __AUTOLOAD_INCLUDE, которая заставит функцию использовать include вместо require.

Кеширование
Если кеширование включено, функция будет склеивать содержимое найденных файлов в один, и при последующих запусках скрипта сначала включать этот файл, а затем пытаться включить прочие файлы, если они еще нужны. И так до тех пор, пока в кеш не будут занесены все-все требуемые когда-либо файлы.

Говорят, при определенных условиях такое склеивание может ускорить работу скриптов.

Чтобы кеширование заработало, нужно сперва определить папку, куда записывать кеш, и файл-семафор (может я неправильно назвал, поправьте если что), который будет управлять кешем — это константы __AUTOLOAD_CACHE_DIR и __AUTOLOAD_MUTEX_FILE соответственно. Причем, чтобы отключить кеширование или перезагрузить кеш заново, нужно либо удалить файл, либо поменять его время модификации соответственно. Т.е., чтобы перегрузить кеш по команде из какой-то программы, достаточно вызвать touch() этого файла.

Запись в файл кеша происходит при завершении работы скрипта. Можно, конечно, вызвать __autoload() напрямую где-нибудь в конце скрипта, но лучше сделать это автоматически, с помощью register_shutdown_function(). Такой способ позволит делать запись кеша в максимально удобный момент.

Имя файла кеша будет содержать метку времени файла-семафора. Если метка времени семафора поменялась, будет просто создан новый файл кеша с новой меткой. С каждым дописыванием, функция вставляет комментарий с меткой времени, когда файл был дописан.

Надежность и эффективность
При автозагрузке файла, происходит такой же require, как будто бы вы делали его вручную из скрипта. Любые ошибки здесь неуместны, поэтому при малейшей проблеме будет вылетать Fatal error. На первый взгляд это может показаться неудобным, но на самом деле это отличный повод сделать все ваши классы по единому строгому стандарту. То же касается путей: папка с кешем должна быть доступна для записи, либо чтобы была возможность ее создать (функция об этом позаботится сама, или опять же, бросится ошибкой).

Сам файл кеша не представляет какой-либо секретности, если ваши автоматически загружаемые файлы содержат только объявления классов. Иными словами, разработчик сам отвечает за целостность и адекватность пространства имен классов, вызовы функций, объявления констант и так далее. Желающим настроить доступ к файлам по-своему, предоставляется возможность в виде констант __AUTOLOAD_DIR_MODE и __AUTOLOAD_FILE_MODE, которые можно определить до запуска функции.

Файлы должны начинаться с открывающего тега PHP: '<?php'. Крайне нежелательно, чтобы файлы заканчивались завершающим тегом '?>', но функция умеет отрезать последний закрывающий тег.

Функция еще не тестировалась в "production"-среде. Потенциально, у нее могут быть проблемы с одновременным обращением нескольких процессов к файлу в момент записи. Я решил не добавлять блокировку файла, потому что эффект их действия мне неизвестен. Также, используется clearstatcache() - лично мне неизвестно, наносит ли это удар по производительности.

Буду рад получить отзывы по использованию функции. Всем спасибо заранее.


__autoload.zip
 Описание:
Функция __autoload()
v0.2

Скачать
 Имя файла:  __autoload.zip
 Размер:  2,1 KB
 Скачали:  535 раз(а)



Последний раз редактировалось: Антон Макаренко (Вт Сен 16, 2008 12:22 am), всего редактировалось 1 раз
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail ICQ Number
samuray
Заглянувший



Зарегистрирован: 14.04.2008
Сообщ.: 6
Карма: 0
   поощрить/наказать


СообщениеДобавлено: Вт Июн 03, 2008 3:27 pm (спустя 2 дня 10 часов 49 минут; написано за 8 минут 15 секунд)
   Заголовок сообщения: Re: __autoload() с кешированием
Ответить с цитатой

Спасибо за труд, работает на ура.
Но, есть один минус. По Вашей схеме - в одном файле должен быть один класс. Если же их больше, то появляются баги.
Например, библиотека MDB2 ругается так :
Цитата:
Cannot redeclare class MDB2_Error in /usr/share/php/pear/MDB2.php on line 977
Все потому, что в файле MDB2.php находятся описание классов MBD2, MDB2_Error и др.

UPD.
Поправлюсь. Ошибка в другом. PEER-овские require_once-ы сводят на нет весь скрипт :(
Так как глупо лезть в PEER библиотеки и убирать все вызовы require_once, было принято решение не кешировать эти библиотеки.
Есть-ли другие варианты?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Константин Жинько [tIT]
Сотрудник «Лаборатории»



Зарегистрирован: 12.06.2004
Сообщ.: 2265
Карма: 111
   поощрить/наказать

Откуда: Москва

СообщениеДобавлено: Сб Июн 07, 2008 10:34 pm (спустя 4 дня 7 часов 6 минут; написано за 40 секунд)
   Заголовок сообщения:
Ответить с цитатой

samuray писал(а):
Есть-ли другие варианты?
повырезать все require_once)
Вернуться к началу
Посмотреть профиль Отправить личное сообщение ICQ Number
samuray
Заглянувший



Зарегистрирован: 14.04.2008
Сообщ.: 6
Карма: 0
   поощрить/наказать


СообщениеДобавлено: Ср Июн 18, 2008 4:32 pm (спустя 10 дней 17 часов 58 минут; написано за 2 минуты 26 секунд)
   Заголовок сообщения:
Ответить с цитатой

Как-нибудь на досуге попробую модифицировать скрипт так, чтобы при кешировании сам убирал все вызовы require_once. Тогда и вырезать ничего не надо будет :)
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Антон Макаренко
Участник форума



Зарегистрирован: 05.02.2004
Сообщ.: 374
Карма: 30
   поощрить/наказать

Откуда: Киев

СообщениеДобавлено: Чт Июн 19, 2008 1:21 am (спустя 8 часов 48 минут; написано за 1 минуту 12 секунд)
   Заголовок сообщения:
Ответить с цитатой

samuray писал(а):
чтобы при кешировании сам убирал все вызовы require_once
Думаю, что смысл есть делать это лишь в общеобразовательных целях. Неужели в реально работающем проекте захочется допускать такое шаманство?
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail ICQ Number
samuray
Заглянувший



Зарегистрирован: 14.04.2008
Сообщ.: 6
Карма: 0
   поощрить/наказать


СообщениеДобавлено: Чт Июн 19, 2008 3:47 pm (спустя 14 часов 26 минут; написано за 4 минуты 32 секунды)
   Заголовок сообщения:
Ответить с цитатой

Да, боюсь что так.
Помимо банальных конструкций require/include_once('filename'), которые вырезать несложно, нашел в PEER-овских библиотеках несколько встроенных автолоадеров с хитрой логикой.
Убирать их скриптом - чистой воды шаманство.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение
Дмитрий Кóтеров
Администратор



Зарегистрирован: 10.03.2003
Сообщ.: 13553
Карма: 405
   поощрить/наказать


СообщениеДобавлено: Ср Авг 13, 2008 11:10 am (спустя 1 месяц 23 дня 19 часов 22 минуты; написано за 1 минуту 35 секунд)
   Заголовок сообщения:
Ответить с цитатой

Можно и не убирать, а где-нибудь просто создать пустые (или почти пустые) файлы и подменить include_path так, чтобы require_once включал именно эти пустые файлы.
Я это не пробовал, просто идея.

Тогда не надо будет ни PEAR менять, ни Zend Framework.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail Посетить сайт автора
Антон Макаренко
Участник форума



Зарегистрирован: 05.02.2004
Сообщ.: 374
Карма: 30
   поощрить/наказать

Откуда: Киев

СообщениеДобавлено: Вт Сен 16, 2008 12:26 am (спустя 1 месяц 2 дня 13 часов 16 минут; написано за 1 минуту 48 секунд)
   Заголовок сообщения:
Ответить с цитатой

Залил в шапку следующую версию.
  1. class_exists() запускается со вторым параметром false, чтобы не вызывать __autoload() рекурсивно
  2. добавлена возможность использовать include вместо require
Вернуться к началу
Посмотреть профиль Отправить личное сообщение Отправить e-mail ICQ Number
Валенок
Участник форума



Зарегистрирован: 06.04.2006
Сообщ.: 521
Карма: -1
   поощрить/наказать


СообщениеДобавлено: Пт Окт 23, 2009 4:47 am (спустя 1 год 1 месяц 7 дней 4 часа 20 минут; написано за 5 минут 11 секунд)
   Заголовок сообщения:
Ответить с цитатой

Может быть, я уже устарел с этим ответом :)
Но вот как я разрешил конфликты с Zend Framework, который сам грузит Zend_Loader в Zend_Application:
Код (php): скопировать код в буфер обмена
spl_autoload_register('__autoload'); // пихаем в стек автолоадеров. если успеем ;) то опередим зенд_лоадер и будет вызываться именно эта функция!
 
А вот еще пара изменений, на сей раз внутри вашей функции:
Рраз, в сохранении всего в один фаил:
Код (php): скопировать код в буфер обмена
foreach ($loadedFiles as $key => $file) {
                       
                $paths = explode(PATH_SEPARATOR, get_include_path());
                foreach ($paths as $path) {
                        if (substr($path, -1) == DIRECTORY_SEPARATOR) {
                                $fullpath = $path.$file;
                        } else {
                                $fullpath = $path.DIRECTORY_SEPARATOR.$file;
                        }
                        if (file_exists($fullpath)) {
                                $file = $fullpath;
                        }
                } // получаем настоящий путь
                       
                $contents[$key] = @trim(file_get_contents($file, true));
                if (empty($contents[$key])) {
                    trigger_error('Failed to load contents from file ' . $file, E_USER_ERROR);
                }

                $contents[$key] = str_replace('__FILE__', "'".realpath($file)."'", $contents[$key]); // вот это важная фигня :) ну, для меня

                $contents[$key] = '?>' . $contents[$key];                // вместо странного удаления 5 символов (я грешным делом не дописываю php к <?) делаем хитро
                if ('?>' === substr($contents[$key], -2)) {
                    $contents[$key] = substr_replace($contents[$key], "\n", -2);
                }
            }
Два, в загрузке фаила:
Код (php): скопировать код в буфер обмена
if (!empty($className)) {
        $path = explode('_', $className);
        $filename = array_pop($path);
        $file = (empty($path) ? '' : implode('/', $path) . '/') . $filename . '.php';
               
        if (!@include($file)) return; // ну мало ли, вдруг его нет или типа того - пусть тогда Zend_Loader разбирается

        // update loaded files list
        if ($isCache) {
            $loadedFiles[] = $file;
        }
    }
Спасибо большое!
Расскажите, пожалуйста, если примете изменения или там чего-нибудь сами поменяете.
Вернуться к началу
Посмотреть профиль Отправить личное сообщение MSN Messenger ICQ Number
Показать сообщения:   
Начaть нoвую тeму   Ответить на тему Часовой пояс: GMT + 3 (Москва)
Страница 1 из 1   
Вы не можете начинать темы. Вы не можете отвечать на сообщения. Вы не можете редактировать свои сообщения. Вы не можете удалять свои сообщения. Вы не можете голосовать в опросах. Вы не можете прилагать файлы к сообщениям. Вы можете скачивать файлы.
  XML