Переопределение методов модулей с помощью ...Hook'ов!

В LiveStreet 0.4 появилась возможность переопределять не только целиком модули, но и отдельные методы. Это позволить разным плагинам бесконфликтно переопределять разные методы одного модуля.

Принцип действия этого механизма основан на Hook'ах:
  1. Вызов каждого метода сопровождается выполнением хуков — module_ModuleName_MethodName_before и module_ModuleName_MethodName_after, соответственно ДО и ПОСЛЕ вызова метода модуля. В первом случаи в хук передаются параметры вызова метода, во втором передается результат выполнения метода модуля.
  2. На module_ModuleName_MethodName_before можно повесить специальный хук — delegate, результат выполнения которого и будет «результатом» выполнения метода модуля

Пример:
<?php 
class HookTest extends Hook {
	public function RegisterHook() {
		$this->AddDelegateHook('module_text_parser_before','testHook',__CLASS__,-3);
	}

	public function testHook($aVars) {
		return 'Topic text > '.$aVars[0];
	}
}
?>
Здесь происходит переопределение метода Parser модуля Text, в результате к тексту топика будет добавлена строчка 'Topic text > ', при этом исходный метод (Text_Parser()) выполнен не будет.

Список методов для делегирования:
Hook_AddDelegateModule($sName,$sCallBack,$iPriority=1)
Hook_AddDelegateFunction($sName,$sCallBack,$iPriority=1)
Hook_AddDelegateHook($sName,$sCallBack,$iPriority=1,$aParams=array())
Использование этих актуально только для хуков методов модулей.

Важное замечание:
Если на module_ModuleName_MethodName_before висит несколько хуков, то сначала они разбиваются на две группы: стандартные и делегирующие. Внутри каждой группы хуки сортируются по приоритету, затем выполняется стандартная группа и только затем делегирующая. Причем в делегирующей группе выполняется только первый хук, остальные отбрасываются для исключения конфликтов.

20 комментариев

avatar
Отлично. То что нужно.
avatar
Спасибо. Теперь наверняка меньше будет конфликтов между плагинами.
avatar
«Стандартный» хук и «делегирующий» хук *_before в общем-то абсолютно одинаковы, и отличаются только тем, что «стандартных» может быть сколько угодно, а «делегирующий» — только один, так?

Я бы в таком случае иначе сделал бы. Предлагаю завести некий контейнер статуса выполнения метода, и в нем указывать, надо продолжать выполнение цепочки хуков и самого метода или нет. Плюс — вызов всех параметров по ссылке. Тогда, приведенный выше пример мог бы выглядеть так:
public function testHook(&$aVars) {
  Engine::setMethodStatus(HOOK_BREAK);
  return 'Topic text > '.$aVars[0];
}
Возвращаем обработанный текст и прекращаем его дальнейшую обработку.

Но если хотим продолжить обработку после нашего хука, то так:
public function testHook(&$aVars) {
  $aVars[0] = 'Topic text > '.$aVars[0];
  Engine::setMethodStatus(HOOK_CONTINUE);
  return;
}
А вот тут мы и текст меняем (меняя содержимое передаваемого аргумента), и позволяем его обрабатывать дальше.
avatar
в текущей реализации движок гарантирует две вещи:
1. выполняться все стандартные хуки
2. выполнение хука произойдет до выполнения метода или его делегата, т.к. хук прописан как _before

в твоём подходе ни 1, ни 2 пункт не выполняются, что посеет хаос :)
avatar
п.2 Я писал только про хуки класса "_before".

п.1 В предлагаемом мной варианте нет деления на «стандартные» и «делегирующие». Любой хук в любое время (в зависимости от логики его работы) может стать, фактически, делегирующим. Когда это может быть полезно: напр., если в хуке проверяется какое-то условие (например, при добавлении топика), и это условие не выполнено и нужно прервать процесс. И в этом случае не играет особой роли, какие хуки выполнились, а какие нет, топик добавлять нельзя.

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

Если же никакой хук не посчитает нужным прервать выполнение последующих хуков, то оба пункта выполняются без проблем. А хаос — его, при желании, можно в любом месте создать :)
avatar
В предлагаемом мной варианте нет деления на «стандартные» и «делегирующие». Любой хук в любое время (в зависимости от логики его работы) может стать, фактически, делегирующим.
результатом этого будет то, что все хуки после делегирующего либо не будут выполнены, либо будут выполнены по факту как _after, хотя они установлены как _bofore.
В изложенном в топике варианте все хуки будут выполняться всегда, даже если этого не нужно.
именно, только не все, а стандартные. Один хук не должен влиять на выполнение других(например хуки разных плагинов), кроме случая когда несколько хуков делегирующие. Этот случай аналогичем тому, когда разные плагины делегируют один и тот же модуль.
avatar
Да, и передача параметров по ссылке дает неплохие возможности. Напр., может быть плагин, который проверяет орфографию добавляемого топика. А другой плагин — на лету преобразует все ссылки в редирект. Т.е. надо, чтобы исходный сначала был передан 1-му плагину, потом этот измененный текст был передан другому плагину, и уже модифицированный текст отдается парсеру.
avatar
с передачей по ссылке согласен
avatar
закоммитил вариант с передачей параметров по ссылке, но вот чувствую выражения вида
$result=call_user_func_array(array($oHook,$aHook['callback']),array(&$aVars));
работать не будут в PHP 5.3 и там где запрещена явная передача по ссылке
avatar
хотя этот фикс должен помочь trac.lsdev.ru/livestreet/changeset/820
комментарий был удален
avatar
Интересно как всё это скажется на производительности. Проводились какие-либо замеры?
avatar
а как таки переопределить метод модуля плагина?

если AddDelegateHook переопределяет метод модуля, то какую такую функцию переопределяет AddDelegateFunction?
avatar
зы. мечтаю о документации
avatar
если AddDelegateHook переопределяет метод модуля, то какую такую функцию переопределяет AddDelegateFunction?
оба они переопределяют методы, только в первом случаи будет вызван метод текущего хука, а во втором обычная функция
комментарий был удален
avatar
я, наверное, чего-то не понимаю, но почему
return $this->Topic_GetTopicsByFilter($aFilter,$iPage,$iPerPage);
вызывает хук module_Topic_GetTopicsByFilter_before, а
return $this->GetTopicsByFilter($aFilter,$iPage,$iPerPage);
вообще никакого хука не вызывает, как дальше жить?
  • xyz
  • 0
avatar
Скорей всего интерпретируется так:
первое — вызов метода модуля Topic
второе — вызод метода текущего класса, то есть пока еще не известно, что это модуль.
avatar
а жить дальше-то как, в смысле, возможно ли хук поставить?
avatar
использовать Inherits
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.