Может ли PHP __autoload() класс из другого __autoload() того же класса?

Хорошо, это сложный вопрос... Я думаю, и у меня есть чувство, что ответ просто нет, но в этом случае я хотел бы получить ответы на альтернативные варианты.

У меня есть очень сложная функция __autoload() в фреймворке, которая может динамически создавать классы. Для динамического создания класса AuthActions требуются три класса: fRecordSet, RecordSet и AuthAction (обратите внимание, что в нем нет буквы S).

Мой автозагрузчик будет искать статический метод «init» для любого загружаемого класса и пытаться его запустить. В моем классе ActiveRecord он пытается использовать AuthActions, чтобы получить список поддерживаемых действий для конкретной активной записи. AuthAction (без S) также вызывает AuthActions внутри своей инициализации, поэтому в основном Active Record загружается и пытается загрузить AuthActions, вызывая загрузку трех других, а затем, когда он заканчивает загрузку AuthAction, все еще в исходном автозагрузчике, AuthAction пытается вызвать AuthActions, который запускает другую автозагрузку, поскольку исходная еще не завершена.

Это приводит к следующему, в котором есть некоторые операторы эха для уточнения:

Попытка загрузить ActiveRecord
Попытка загрузить fActiveRecord
fActiveRecord, загруженная через /var/www/dotink.org/inkwelldemo/inc/lib/flourish
ActiveRecord, загруженная через /var/www/dotink.org/inkwelldemo/ inc/lib
Попытка загрузить AuthActions
Попытка загрузить RecordSet
Попытка загрузить fRecordSet
fRecordSet загружается через /var/www/dotink.org/inkwelldemo/inc/lib/flourish
RecordSet загружается через /var/www/dotink.org/inkwelldemo/inc/lib
Попытка загрузить AuthAction
AuthAction загружается через /var/www/dotink.org/inkwelldemo/models

Неустранимая ошибка: класс AuthActions не найден в /var/www/dotink.org/inkwelldemo/models/AuthAction.php в строке 24.

Проблема здесь в том, что последующий вызов __autoload('AuthActions') будет успешным, потому что три необходимых ему класса теперь на месте... но кажется, что он умирает только из-за того, что он уже пытается автоматически загрузить 'AuthActions' -- это, кажется, жестко записано в PHP.

При тестировании я обнаружил, что следующее будет зацикливаться навсегда без ошибок:

function __autoload($class) {
  __autoload($class);
}

$foo = new Bar();

В то время как этот ниже будет ошибкой аналогично:

function __autoload($class) {
  $test = new Bar();
}

$foo = new Bar();

Такое поведение кажется непоследовательным, поскольку, по сути, они должны означать одно и то же (своего рода). Если бы внутренние автозагрузки PHP действовали как пользовательский вызов __autoload(), я не думаю, что у меня была бы проблема (или если бы я это сделал, то это была бы проблема вечного зацикливания, что было бы отдельной проблемой определения, почему класс не загружался для разрешения зависимостей).

Короче говоря, мне нужен способ либо рекурсивно зациклить автозагрузчик, подобный этому, на основе автозагрузок, запускаемых PHP ... это просто невозможно? Возможно, это ошибка моей конкретной версии? Кажется, это влияет на 5.2.6 - 5.3.2 в моих тестах, поэтому я не могу представить, что это ошибка в целом.

Обновлять:

Ниже приведен код метода инициализации() для AuthAction:

        /**
     * Initializes the AuthAction model
     *
     * @param array $config The configuration array
     * @return void
     */
    static public function init($config) {

        // Establish permission definitions and supported actions

        $every_permission  = 0;
        $supported_actions = array();

        foreach (AuthActions::build() as $auth_action) {
            $action_name         = $auth_action->getName();
            $action_value        = intval($auth_action->getBitValue());
            $every_permission    = $every_permission | $action_value;
            $supported_actions[] = $action_name;
            define(self::makeDefinition($action_name), $action_value);
        }
        define('PERM_ALL', $every_permission);

    }

Вы можете видеть, где он вызывает AuthActions как отдельный класс, и заметьте, что это происходит только потому, что он загружается в исходной попытке загрузки, которая терпит неудачу. Очевидно, что я могу удалить этот код, и он будет работать — первоначальная загрузка AuthActions завершится успешно, поскольку затем будут загружены все необходимые классы.

Тем не менее, init() является наиболее подходящим местом для этого кода, и даже если я не могу использовать взаимозависимые классы, которые еще не были загружены в init(), я все же предпочел бы сохранить эту функциональность. Любой альтернативный способ реализации этой функциональности был бы замечательным... В идеале PHP должен иметь события для таких вещей, где вы могли бы, например, зарегистрировать обратный вызов, когда, скажем, запускается "автозагружаемое" событие. Это позволит запустить код ПОСЛЕ исходной автозагрузки и устранит это, казалось бы, бессмысленное ограничение.

Теперь, с учетом сказанного, любые предложения по автоматическому вызову init() для класса каждый раз, когда он загружается, приветствуются и приветствуются.


person Community    schedule 16.07.2010    source источник


Ответы (2)


Метод __autoload вызывается только тогда, когда вы пытаетесь использовать класс/интерфейс, который еще не определен.

Поэтому на вашем примере

function __autoload($class) {
  $test = new Bar();
}

$foo = new Bar();

Вы пытаетесь загрузить класс bar, который не требуется/требуется_один раз, поэтому класс не определен, поэтому он вызывает метод __autoload в качестве последнего средства, а затем в автозагрузке вы снова пытаетесь загрузить тот же класс, который не был определен.

Правильный способ использования __autoload будет таким, как показано на сайте php.

<?php
function __autoload($class_name) {
    require_once $class_name . '.php';
}

$obj  = new MyClass1();
$obj2 = new MyClass2(); 
?>

И все ваши настройки инициализации можно поместить в __constructor, не так ли?

если я что-то упустил из вашего вопроса...

person ozatomic    schedule 16.07.2010
comment
Возможно, вы захотите проверить реализации SPL для __autoload, чтобы получить лучшее представление о том, как он предназначен для использования. Вы можете сделать довольно гладкую загрузку файлов, если будете следовать структуре класса/каталога в стиле Zend и пространству имен или использовать 5.3 и механику пространства имен. us2.php.net/manual/en/function.spl-autoload. php - person tsgrasser; 16.07.2010
comment
Насколько мне известно, SPL не делает ничего, кроме создания стека автозагрузчиков. Мой делает что-то совсем другое, но, тем не менее, данная проблема не решается автозагрузчиками SPL, поскольку каждый автозагрузчик по-прежнему будет лечиться с одной и той же внезапной смертью при любых таких попытках загрузить тот же класс, который загружается в данный момент. - person ; 16.07.2010
comment
Могу ли я также добавить к оригинальному плакату ozatomic, примеры, которые я предоставил, были просто для того, чтобы показать, как поведение отличается в зависимости от того, запускается ли автозагрузка внутри, от того, вызывается ли она пользователем. Это относится к моей проблеме только постольку, поскольку мне нужно запустить ее во второй раз. Я не просто пытаюсь вечно загружать один и тот же класс, я понимаю, что автозагрузчик используется не так. - person ; 16.07.2010

я думаю, что это

Мой автозагрузчик будет искать статический метод «init» для любого загружаемого класса и пытаться его запустить.

является ключом к вашей проблеме. Ваш автозагрузчик не должен ничего запускать, он должен включать (или иным образом определять) класс и ничего больше.

Какой код вы пытаетесь запустить, когда класс определен (т.е. в вашем статическом методе "init")?

person Brenton Alker    schedule 16.07.2010
comment
Брентон, я отредактировал исходный вопрос, включив в него код инициализации и еще несколько абзацев, чтобы уточнить, что я ищу в своем решении. Я ценю ваше замечание о том, что автозагрузчик не должен ничего запускать, однако я не могу придумать другого способа точно гарантировать, что метод вызывается для класса после его загрузки. - person ; 16.07.2010