30 июня 2013 г., 16:13
Вызов системных процессоров в своих установочных пакетах для MODX Revolution
Топик наверняка будет полезен тем, кто собирает свои пакеты для MODX Revolution.
Собираю один пакет, и вот понадобилось мне, чтобы при установке пакета сразу прописывались доступы к контекстам. Выполнить пользовательский php-скрипт в пакете не особая проблема, это даже в топике описывать не буду. А вот решил я, чтобы много кода не писать и по религии все было, тупо вызвать процессор $modx->runProcessor('security/access/addacl', $params);
Дело не хитрое, быстро прописал все, собрал пакетик, пробую установить, и…
Лог установки в окошко не полный вышел, процесс установки явно встал колом, а на сервер бесконечно отправляются запросы на проверку результата установки, а в ответ от сервера тишина… Вот так вот как обычно желание сэкономить пару строк кода приводит к потере нескольких часов времени и полному вскрытию движка…
Немного теории:
Когда мы устанавливаем через админку пакет (у нас же там Ajax по всей комнате), отправляется запрос на установку, потом шаги всякие и т.п., и после окончательного «Установить», отправляет окончательный запрос на установку, после чего начинают периодически отправляться на сервер запросы, проверяющие ход установки и выводящие логи. Запросы прекращают идти только после того, как получат окончательный ответ с результатом True or False.
Теперь суть возникшей проблемы:
Оказывается, вызов процессора не проходит даром. Сюрприз крылся к логировании ошибки, возникшей в процессоре. Когда в процессоре возникла ошибка, он попытался ее залогировать $modx->error->failure($error);, и у него даже получилось. Так а почему же тогда установка колом встала?
Забегая вперед, можно сказать, что установка в целом проходила в обычном режиме, и даже все устанавливалось, а вот связь клиент-сервер рушилась, то точнее «общение» на уровне логов. Посмотрим метод $modx->error->failure().
public function failure($message = '', $object = null) { return $this->process($message, false, $object); }
А там
public function process($message = '', $status = false, $object = null) { if (isset($this->modx->registry) && $this->modx->registry->isLogging()) { $this->modx->registry->resetLogging(); }
И здесь самое интересное: $this->modx->registry->resetLogging(); То есть тупо сбрасывается текущее логирование… modRegistry::resetLogging()
public function resetLogging() { if ($this->_loggingRegister && $this->_prevLogTarget && $this->_prevLogLevel) { $this->modx->setLogTarget($this->_prevLogTarget); $this->modx->setLogLevel($this->_prevLogLevel); $this->_loggingRegister = null; } }
И что же получается? Клиент постоянно шлет запросы на сервер, а там лог сброшен, и никакого ответа уже для него нет. Собственно, это и печально.
Но расковырять проблему было гораздо проще, чем найти ее решение. Не буду утомлять большим количеством буков, напишу решение вкратце:
$LogTarget = $modx->getLogTarget(); $LogTopic = $LogTarget->subscriptions[0]; /* processor_code */ $modx->runProcessor($processor); $modx->registry->setLogging($LogTarget, $LogTopic);
То есть перед вызовом процессоров, получаем текущий инстанс логера, перехватываем его идентификатор (Топик), затем выполняем нужные нам процессоры, после чего опять передаем MODX-у этот логер и продолжаем писать в него что нам нужно до окончания установки пакета.
Только надо иметь ввиду, что нельзя такой фокус провернуть дважды в одном скрипте, то есть такое не проканает:
$LogTarget = $modx->getLogTarget(); $LogTopic = $LogTarget->subscriptions[0]; /* processor_code */ $modx->runProcessor($processor); $modx->registry->setLogging($LogTarget, $LogTopic); $LogTarget = $modx->getLogTarget(); $LogTopic = $LogTarget->subscriptions[0]; /* processor_code */ $modx->runProcessor($processor); $modx->registry->setLogging($LogTarget, $LogTopic);
Почему? Потому что метод $modx->registry->setLogging принимает ссылку на объект.
public function setLogging(modRegister &$register, $topic, $level = modX::LOG_LEVEL_ERROR)
А метод modRegistry::resetLogging() убивает этот объект ($this->_loggingRegister = null;). То есть в первый раз мы еще успеваем что-то перехватить, во второй раз уже нечего перехватывать, и наш объект уничтожен.