Class test{
function test1()
{
echo 'inside test1';
}
function test2()
{
echo 'test2';
}
function test3()
{
echo 'test3';
}
}
$obj = new test;
$obj->test2();//prints test2
$obj->test3();//prints test3
Run Code Online (Sandbox Code Playgroud)
现在我的问题是,
如何在任何被调用的函数执行之前调用另一个函数?在上面的例子中,如何为每个其他函数调用自动调用'test1'函数,这样我就可以得到输出,
test1
test2
test1
test3
Run Code Online (Sandbox Code Playgroud)
目前我正在获得输出
test2
test3
Run Code Online (Sandbox Code Playgroud)
我不能在每个函数定义中调用'test1'函数,因为可能有很多函数.我需要一种方法来在调用类的任何函数之前自动调用函数.
任何替代方式也可以.
Kri*_*ard 65
你最好的选择是魔术方法__call,见下面例如:
<?php
class test {
function __construct(){}
private function test1(){
echo "In test1", PHP_EOL;
}
private function test2(){
echo "test2", PHP_EOL;
}
protected function test3(){
return "test3" . PHP_EOL;
}
public function __call($method,$arguments) {
if(method_exists($this, $method)) {
$this->test1();
return call_user_func_array(array($this,$method),$arguments);
}
}
}
$a = new test;
$a->test2();
echo $a->test3();
/*
* Output:
* In test1
* test2
* In test1
* test3
*/
Run Code Online (Sandbox Code Playgroud)
请注意,test2
并且test3
在因调用protected
和调用它们的上下文中不可见private
.如果方法是公开的,则上述示例将失败.
test1
不必申报private
.
更新:添加指向ideone的链接,添加带返回值的示例.
Ocr*_*ius 20
由于http://ocramius.github.io/presentations/proxy-pattern-in-php/#/71所有以前的尝试基本上都是有缺陷的
这是一个简单的例子,取自我的幻灯片:
class BankAccount { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
这是我们的"可怜"拦截器逻辑:
class PoorProxy {
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($method, $args) {
return call_user_func_array(
$this->wrapped,
$args
);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我们要调用以下方法:
function pay(BankAccount $account) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
那么这将不起作用:
$account = new PoorProxy(new BankAccount());
pay($account); // KABOOM!
Run Code Online (Sandbox Code Playgroud)
这适用于建议实施"代理"的所有解决方案.
建议明确使用其他方法然后调用内部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() { /**/ }
}
Run Code Online (Sandbox Code Playgroud)
以下是您使用它的方式:
$account = new BankAccountProxy(new BankAccount());
pay($account); // WORKS!
Run Code Online (Sandbox Code Playgroud)
这是一种类型安全,干净的解决方案,但它涉及大量编码,因此请仅将其作为示例.
编写这个样板代码并不好玩,因此您可能希望使用不同的方法.
为了让您了解这类问题的复杂程度,我可以告诉您,我编写了一个完整的库来解决这些问题,而一些更聪明,更聪明的老年人甚至去发明了一种完全不同的范例,称为"面向方面"编程"(AOP).
因此,我建议你研究一下我认为可能以更清洁的方式解决问题的3个解决方案:
使用ProxyManager的" 访问拦截器 ",它基本上是一种代理类型,允许您在调用其他方法时运行闭包(示例).以下是如何代理对$object
公共API的所有调用的示例:
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)
)
);
}
Run Code Online (Sandbox Code Playgroud)
然后只是随心所欲地使用build_wrapper
.
使用GO-AOP-PHP,这是一个完全用PHP编写的实际AOP库,但是会将这种逻辑应用于您定义切点的所有类实例.这可能是您想要的,也可能不是,如果您的$callOnMethod
应用仅适用于特定情况,那么AOP不是您想要的.
使用PHP AOP扩展,我认为这不是一个好的解决方案,主要是因为GO-AOP-PHP以更优雅/可调试的方式解决了这个问题,并且因为PHP中的扩展本质上是一团糟(即归因于PHP内部,而不是扩展开发人员).此外,通过使用扩展,您正在使您的应用程序尽可能不可移植(尝试说服系统管理员安装PHP的编译版本,如果您敢),并且您不能在酷的新引擎上使用您的应用程序,例如HHVM.
也许它有点过时但是这里来了我的2美分......
我不认为通过__call()访问私有方法是个好主意.如果你有一个方法,你真的不想在你的对象之外被调用,你就无法避免它发生.
我认为一个更优雅的解决方案应该是创建某种通用代理/装饰器并在其中使用__call().让我说明一下:
class Proxy
{
private $proxifiedClass;
function __construct($proxifiedClass)
{
$this->proxifiedClass = $proxifiedClass;
}
public function __call($methodName, $arguments)
{
if (is_callable(
array($this->proxifiedClass, $methodName)))
{
doSomethingBeforeCall();
call_user_func(array($this->proxifiedClass, $methodName), $arguments);
doSomethingAfterCall();
}
else
{
$class = get_class($this->proxifiedClass);
throw new \BadMethodCallException("No callable method $methodName at $class class");
}
}
private function doSomethingBeforeCall()
{
echo 'Before call';
//code here
}
private function doSomethingAfterCall()
{
echo 'After call';
//code here
}
}
Run Code Online (Sandbox Code Playgroud)
现在简单的测试类:
class Test
{
public function methodOne()
{
echo 'Method one';
}
public function methodTwo()
{
echo 'Method two';
}
private function methodThree()
{
echo 'Method three';
}
}
Run Code Online (Sandbox Code Playgroud)
你现在需要做的就是:
$obj = new Proxy(new Test());
$obj->methodOne();
$obj->methodTwo();
$obj->methodThree(); // This will fail, methodThree is private
Run Code Online (Sandbox Code Playgroud)
好处:
1)您只需要一个代理类,它将适用于您的所有对象.2)您不会不尊重可访问性规则.3)您无需更改代理对象.
缺点:包装原始对象后,您将失去界面/合约.如果您使用带有频率的类型提示可能是一个问题.
归档时间: |
|
查看次数: |
46921 次 |
最近记录: |