Все предыдущие попытки в основном испорчен из-за http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71
Вот простой пример, взятый из моих слайдов:
class BankAccount { /* ... */ }
И вот наш "бедный" перехватчик логика:
class PoorProxy {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($method, $args) {
return call_user_func_array(
$this->wrapped,
$args
);
}
}
Теперь, если у нас есть следующий метод для вызова:
function pay(BankAccount $account) { /* ... */ }
Тогда это не будет работать:
$account = new PoorProxy(new BankAccount());
pay($account); // KABOOM!
Это относится ко всем решениям, которые предлагают внедряющих «прокси».
Решения, предлагающие явное использование других методов, которые затем называют ваш внутренний API, являются ошибочными, поскольку они заставляют вас менять свой общедоступный API для изменения внутреннего поведения и снижают безопасность типов.
Решение, предлагаемое Kristoffer не учитывает public
методов, что также является проблемой, так как вы не можете переписать свой API, чтобы сделать все это private
или protected
.
Вот решение, которое решает эту проблему частично:
class BankAccountProxy extends BankAccount {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function doThings() { // inherited public method
$this->doOtherThingsOnMethodCall();
return $this->wrapped->doThings();
}
private function doOtherThingsOnMethodCall() { /**/ }
}
Вот как вы его используете:
$account = new BankAccountProxy(new BankAccount());
pay($account); // WORKS!
Это тип-безопасное, чистое решение, но оно вовлекает много кодирования, поэтому, пожалуйста, возьмите его только в качестве примера.
Написание этого шаблонного кода НЕ весело, поэтому вы можете использовать разные подходы.
Чтобы дать вам представление о том, как сложна эта категория проблем, я могу вам сказать, что я написал an entire library решить их, а некоторые умнее, мудрее, пожилые люди даже пошли и изобрели совершенно иную парадигму, называется "Aspect Oriented Programming" (AOP) ,
Поэтому я предлагаю вам взглянуть на эти 3 решения, которые я думаю, что может быть в состоянии решить вашу проблему в более чистым способом:
Использование ProxyManager «s„access interceptor“, который является в основном прокси тип, который позволяет запускать закрытие при вызове других методов (example). Вот пример того, как прокси все вызовы к общественному API $object
«s:
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
function build_wrapper($object, callable $callOnMethod) {
return (new AccessInterceptorValueHolderFactory)
->createProxy(
$object,
array_map(
function() use ($callOnMethod) {
return $callOnMethod;
},
(new ReflectionClass($object))
->getMethods(ReflectionMethod::IS_PUBLIC)
)
);
}
затем просто использовать build_wrapper
, как вам нравится.
Использовать GO-AOP-PHP, который является реальной библиотекой AOP, полностью написанной на PHP, но будет применять эту логику для ВСЕХ экземпляров классов, для которых вы определяете точечные сокращения. Это может быть или не быть тем, что вы хотите, и если ваш $callOnMethod
должен применяться только для определенных экземпляров, тогда AOP не то, что вы ищете.
Используйте PHP AOP Extension, что я не верю, чтобы быть хорошим решением, в основном потому, что GO-AOP-PHP решает эту проблему в более элегантном/отладочном образом, и потому, что расширения в PHP по своей сути беспорядок (который должен быть приписан к внутренним PHP, а не к разработчикам расширений). Кроме того, используя расширение, вы делаете свое приложение как можно более портативным (попробуйте убедить sysadmin, чтобы установить скомпилированную версию PHP, если вы смеете), и вы не можете использовать свое приложение в крутых новых двигателях, таких как как HHVM.
На самом деле, а) у вас могут быть методы, также как и защищенные, просто не публичные, и б) вам не нужен _ – ktolis
. Вы правы, защита также будет работать, я удалил '_' в примере и сделал 'test3'' protected'. –
Должно ли 'return'in' __call' – Alex2php