PHP 5reflectionAPI性能

我目前正在考虑在我自己的MVC Web框架中使用Reflection类(ReflectionClass和ReflectionMethod),因为我需要自动实例化控制器类并在没有任何必要configuration的情况下调用它们的方法(“约定configuration”方法)。

即使我认为数据库请求可能比实际的PHP代码有更大的瓶颈,但我担心性能。

所以,我想知道,从性能的angular度来看,任何人对PHP5reflection有什么好的或不好的经验。

此外,我很想知道任何stream行的PHP框架(CI,Cake,Symfony等)是否实际使用了Reflection。

不要担心。 安装Xdebug并确定瓶颈在哪里。

使用reflection是有代价的,但是否重要取决于你在做什么。 如果您使用Reflection来实现控制器/请求调度程序,那么它只是每个请求的一个用法。 绝对可以忽略不计。

如果你使用reflection来实现你的ORM层,那么为每个对象甚至每一个属性的访问使用它,并创build数百或数千个对象,那么这可能是昂贵的。

我对这三个选项进行了基准testing(另一个基准testing并没有拆分CPU周期,而是4岁):

 class foo { public static function bar() { return __METHOD__; } } function directCall() { return foo::bar($_SERVER['REQUEST_TIME']); } function variableCall() { return call_user_func(array('foo', 'bar'), $_SERVER['REQUEST_TIME']); } function reflectedCall() { return (new ReflectionMethod('foo', 'bar'))->invoke(null, $_SERVER['REQUEST_TIME']); } 

1,000,000次迭代所需的绝对时间:

print_r(Benchmark(array('directCall','variableCall','reflectedCall'),1000000));

 Array ( [directCall] => 4.13348770 [variableCall] => 6.82747173 [reflectedCall] => 8.67534351 ) 

和相对的时间,还有100万次迭代(单独运行):

ph() – > Dump(Benchmark(array('directCall','variableCall','reflectedCall'),1000000,true));

 Array ( [directCall] => 1.00000000 [variableCall] => 1.67164707 [reflectedCall] => 2.13174915 ) 

5.4.7的reflectionperformance似乎大幅上升(从〜500%下降到〜213% )。

这是我使用的Benchmark()函数,如果有人想重新运行这个基准:

 function Benchmark($callbacks, $iterations = 100, $relative = false) { set_time_limit(0); if (count($callbacks = array_filter((array) $callbacks, 'is_callable')) > 0) { $result = array_fill_keys($callbacks, 0); $arguments = array_slice(func_get_args(), 3); for ($i = 0; $i < $iterations; ++$i) { foreach ($result as $key => $value) { $value = microtime(true); call_user_func_array($key, $arguments); $result[$key] += microtime(true) - $value; } } asort($result, SORT_NUMERIC); foreach (array_reverse($result) as $key => $value) { if ($relative === true) { $value /= reset($result); } $result[$key] = number_format($value, 8, '.', ''); } return $result; } return false; } 

调用一个静态函数100万次将在我的机器上花费约0.31秒。 使用ReflectionMethod时,耗时约1.82秒。 这意味着使用Reflection API要花费500%的成本。

这是我用的方式:

 <?PHP class test { static function f(){ return; } } $s = microtime(true); for ($i=0; $i<1000000; $i++) { test::f('x'); } echo ($a=microtime(true) - $s)."\n"; $s = microtime(true); for ($i=0; $i<1000000; $i++) { $rm = new ReflectionMethod('test', 'f'); $rm->invokeArgs(null, array('f')); } echo ($b=microtime(true) - $s)."\n"; echo 100/$a*$b; 

显然,实际影响取决于你期望的打电话数量

此外,我很想知道任何stream行的PHP框架(CI,Cake,Symfony等)是否实际使用了Reflection。

http://framework.zend.com/manual/en/zend.server.reflection.html

“通常情况下,这个function只能被框架的服务器类的开发者使用。”

开销很小,所以没有大的性能损失其他的东西,如数据库,模板处理等性能问题,testing你的框架一个简单的动作,看看它是多快。

例如,使用reflection的代码(前端控制器)可以在几个毫秒内完成工作

 <?php require_once('sanitize.inc'); /** * MVC Controller * * This Class implements MVC Controller part * * @package MVC * @subpackage Controller * */ class Controller { /** * Standard Controller constructor */ static private $moduleName; static private $actionName; static private $params; /** * Don't allow construction of the controller (this is a singleton) * */ private function __construct() { } /** * Don't allow cloning of the controller (this is a singleton) * */ private function __clone() { } /** * Returns current module name * * @return string */ function getModuleName() { return self :: $moduleName; } /** * Returns current module name * * @return string */ function getActionName() { return self :: $actionName; } /** * Returns the subdomain of the request * * @return string */ function getSubdomain() { return substr($_SERVER['HTTP_HOST'], 0, strpos($_SERVER['HTTP_HOST'], '.')); } function getParameters($moduleName = false, $actionName = false) { if ($moduleName === false or ( $moduleName === self :: $moduleName and $actionName === self :: $actionName )) { return self :: $params; } else { if ($actionName === false) { return false; } else { @include_once ( FRAMEWORK_PATH . '/modules/' . $moduleName . '.php' ); $method = new ReflectionMethod('mod_' . $moduleName, $actionName); foreach ($method->getParameters() as $parameter) { $parameters[$parameter->getName()] = null; } return $parameters; } } } /** * Redirect or direct to a action or default module action and parameters * it has the ability to http redirect to the specified action * internally used to direct to action * * @param string $moduleName * @param string $actionName * @param array $parameters * @param bool $http_redirect * @return bool */ function redirect($moduleName, $actionName, $parameters = null, $http_redirect = false) { self :: $moduleName = $moduleName; self :: $actionName = $actionName; // We assume all will be ok $ok = true; @include_once ( PATH . '/modules/' . $moduleName . '.php' ); // We check if the module's class really exists if (!class_exists('mod_' . $moduleName, false)) { // if the module does not exist route to module main @include_once ( PATH . '/modules/main.php' ); $modClassName = 'mod_main'; $module = new $modClassName(); if (method_exists($module, $moduleName)) { self :: $moduleName = 'main'; self :: $actionName = $moduleName; //$_PARAMS = explode( '/' , $_SERVER['REQUEST_URI'] ); //unset($parameters[0]); //$parameters = array_slice($_PARAMS, 1, -1); $parameters = array_merge(array($actionName), $parameters); //add first parameter } else { $parameters = array($moduleName, $actionName) + $parameters; $actionName = 'index'; $moduleName = 'main'; self :: $moduleName = $moduleName; self :: $actionName = $actionName; } } else { //if the action does not exist route to action index @include_once ( PATH . '/modules/' . $moduleName . '.php' ); $modClassName = 'mod_' . $moduleName; $module = new $modClassName(); if (!method_exists($module, $actionName)) { $parameters = array_merge(array($actionName), $parameters); //add first parameter $actionName = 'index'; } self :: $moduleName = $moduleName; self :: $actionName = $actionName; } if (empty($module)) { $modClassName = 'mod_' . self :: $moduleName; $module = new $modClassName(); } $method = new ReflectionMethod('mod_' . self :: $moduleName, self :: $actionName); //sanitize and set method variables if (is_array($parameters)) { foreach ($method->getParameters() as $parameter) { $param = current($parameters); next($parameters); if ($parameter->isDefaultValueAvailable()) { if ($param !== false) { self :: $params[$parameter->getName()] = sanitizeOne(urldecode(trim($param)), $parameter->getDefaultValue()); } else { self :: $params[$parameter->getName()] = null; } } else { if ($param !== false) {//check if variable is set, avoid notice self :: $params[$parameter->getName()] = sanitizeOne(urldecode(trim($param)), 'str'); } else { self :: $params[$parameter->getName()] = null; } } } } else { foreach ($method->getParameters() as $parameter) { self :: $params[$parameter->getName()] = null; } } if ($http_redirect === false) {//no redirecting just call the action if (is_array(self :: $params)) { $method->invokeArgs($module, self :: $params); } else { $method->invoke($module); } } else { //generate the link to action if (is_array($parameters)) { // pass parameters $link = '/' . $moduleName . '/' . $actionName . '/' . implode('/', self :: $params); } else { $link = '/' . $moduleName . '/' . $actionName; } //redirect browser header('Location:' . $link); //if the browser does not support redirecting then provide a link to the action die('Your browser does not support redirect please click here <a href="' . $link . '">' . $link . '</a>'); } return $ok; } /** * Redirects to action contained within current module */ function redirectAction($actionName, $parameters) { self :: $actionName = $actionName; call_user_func_array(array(&$this, $actionName), $parameters); } public function module($moduleName) { self :: redirect($moduleName, $actionName, $parameters, $http_redirect = false); } /** * Processes the client's REQUEST_URI and handles module loading/unloading and action calling * * @return bool */ public function dispatch() { if ($_SERVER['REQUEST_URI'][strlen($_SERVER['REQUEST_URI']) - 1] !== '/') { $_SERVER['REQUEST_URI'] .= '/'; //add end slash for safety (if missing) } //$_SERVER['REQUEST_URI'] = @str_replace( BASE ,'', $_SERVER['REQUEST_URI']); // We divide the request into 'module' and 'action' and save paramaters into $_PARAMS if ($_SERVER['REQUEST_URI'] != '/') { $_PARAMS = explode('/', $_SERVER['REQUEST_URI']); $moduleName = $_PARAMS[1]; //get module name $actionName = $_PARAMS[2]; //get action unset($_PARAMS[count($_PARAMS) - 1]); //delete last unset($_PARAMS[0]); unset($_PARAMS[1]); unset($_PARAMS[2]); } else { $_PARAMS = null; } if (empty($actionName)) { $actionName = 'index'; //use default index action } if (empty($moduleName)) { $moduleName = 'main'; //use default main module } /* if (isset($_PARAMS)) { $_PARAMS = array_slice($_PARAMS, 3, -1);//delete action and module from array and pass only parameters } */ return self :: redirect($moduleName, $actionName, $_PARAMS); } } 

在我的情况reflection它只比直接调用类方法慢230%,这与call_user_func函数一样快。

有时使用像call_user_func_array()这样的东西可以得到你所需要的东西。 不知道如何performance不同。

CodeIgniter defenitly使用reflection。 我打赌其他人也做。 在ci安装的系统/控制器文件夹中查看Controller类。

基于@Alix Axel提供的代码

为了完整起见,我决定将每个选项包装在一个类中,并在适用的地方包含caching对象。 这里是结果和代码在i7-4710HQ PHP 5.6的结果

 array ( 'Direct' => '5.18932366', 'Variable' => '5.62969398', 'Reflective' => '6.59285069', 'User' => '7.40568614', ) 

码:

 function Benchmark($callbacks, $iterations = 100, $relative = false) { set_time_limit(0); if (count($callbacks = array_filter((array) $callbacks, 'is_callable')) > 0) { $result = array_fill_keys(array_keys($callbacks), 0); $arguments = array_slice(func_get_args(), 3); for ($i = 0; $i < $iterations; ++$i) { foreach ($result as $key => $value) { $value = microtime(true); call_user_func_array($callbacks[$key], $arguments); $result[$key] += microtime(true) - $value; } } asort($result, SORT_NUMERIC); foreach (array_reverse($result) as $key => $value) { if ($relative === true) { $value /= reset($result); } $result[$key] = number_format($value, 8, '.', ''); } return $result; } return false; } class foo { public static function bar() { return __METHOD__; } } class TesterDirect { public function test() { return foo::bar($_SERVER['REQUEST_TIME']); } } class TesterVariable { private $class = 'foo'; public function test() { $class = $this->class; return $class::bar($_SERVER['REQUEST_TIME']); } } class TesterUser { private $method = array('foo', 'bar'); public function test() { return call_user_func($this->method, $_SERVER['REQUEST_TIME']); } } class TesterReflective { private $class = 'foo'; private $reflectionMethod; public function __construct() { $this->reflectionMethod = new ReflectionMethod($this->class, 'bar'); } public function test() { return $this->reflectionMethod->invoke(null, $_SERVER['REQUEST_TIME']); } } $testerDirect = new TesterDirect(); $testerVariable = new TesterVariable(); $testerUser = new TesterUser(); $testerReflective = new TesterReflective(); fputs(STDOUT, var_export(Benchmark(array( 'Direct' => array($testerDirect, 'test'), 'Variable' => array($testerVariable, 'test'), 'User' => array($testerUser, 'test'), 'Reflective' => array($testerReflective, 'test') ), 10000000), true));