Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP5反射API性能_Php_Performance_Reflection - Fatal编程技术网

PHP5反射API性能

PHP5反射API性能,php,performance,reflection,Php,Performance,Reflection,我目前正在考虑在我自己的MVC web框架中使用反射类(主要是ReflectionClass和ReflectionMethod),因为我需要自动实例化控制器类并调用它们的方法,而无需任何必要的配置(“约定优先于配置”方法) 我关心的是性能,尽管我认为数据库请求可能比实际的PHP代码有更大的瓶颈 所以,我想知道是否有人从性能的角度对PHP5反射有任何好的或坏的经验 此外,我想知道流行的PHP框架(CI、Cake、Symfony等)是否真的使用了反射。安装并确保瓶颈在哪里 使用反射是有代价的,但这是

我目前正在考虑在我自己的MVC web框架中使用反射类(主要是ReflectionClass和ReflectionMethod),因为我需要自动实例化控制器类并调用它们的方法,而无需任何必要的配置(“约定优先于配置”方法)

我关心的是性能,尽管我认为数据库请求可能比实际的PHP代码有更大的瓶颈

所以,我想知道是否有人从性能的角度对PHP5反射有任何好的或坏的经验


此外,我想知道流行的PHP框架(CI、Cake、Symfony等)是否真的使用了反射。安装并确保瓶颈在哪里

使用反射是有代价的,但这是否重要取决于你在做什么。如果您使用反射实现控制器/请求调度器,那么每个请求只需使用一次。完全可以忽略不计


如果您使用反射实现ORM层,将其用于每个对象,甚至用于对属性的每个访问,并创建数百或数千个对象,那么它可能会很昂贵。

开销很小,因此不会造成很大的性能损失其他东西,如数据库、模板处理等,都是性能问题,用一个简单的动作测试你的框架,看看它有多快

例如,使用反射的代码bellow(frontcontroller)可以在几毫秒内完成任务

<?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);
    }
}

此外,我很想知道是否有
流行的PHP框架之一(CI,
蛋糕、Symfony等)实际使用
反思


“通常,此功能仅由框架服务器类的开发人员使用。”

在我的机器上调用静态函数100万次将花费约0.31秒。使用ReflectionMethod时,成本约为1.82秒。这意味着使用反射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;

CodeIgniter防御性地使用反射。我打赌其他人也会。查看ci安装中system/Controller文件夹中的控制器类

在我的例子中,反射只比直接调用class方法慢230%,后者与调用_user _func函数一样快。

有时使用类似于call _user _func _array()的方法可以满足您的需要。不知道性能有什么不同。

我对这3个选项进行了基准测试(另一个基准测试没有拆分CPU周期,已经有4年了):

1000000次迭代所用的绝对时间:

打印(基准(数组('directCall','variableCall',', “反射呼叫”),1000000)

以及相对时间,也包括1000000次迭代(单独运行):

ph()->Dump(基准测试(数组('directCall','variableCall',', “reflectedCall”),1000000,true)

在5.4.7中,反射性能似乎有了很大的提高(从~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;
}
函数基准($callbacks,$iterations=100,$relative=false)
{
设置时间限制(0);
if(count($callbacks=array_filter((array)$callbacks,'is_callable'))>0)
{
$result=数组填充键($callbacks,0);
$arguments=array\u slice(func\u get\u args(),3);
对于($i=0;$i<$iterations;++$i)
{
foreach($结果为$key=>$value)
{
$value=微时间(真);
调用用户函数数组($key,$arguments);
$result[$key]+=microtime(true)-$value;
}
}
asort($result,SORT\u NUMERIC);
foreach(数组_反向($result)为$key=>$value)
{
如果($relative==true)
{
$value/=重置($result);
}
$result[$key]=数字格式($value,8'.','');
}
返回$result;
}
返回false;
}

基于@Alix Axel提供的代码

因此,为了完整性,我决定将每个选项包装在一个类中,并在适用的地方包括对象缓存。下面是结果和代码 在i7-4710HQ上的PHP5.6结果

数组(
'直接'=>'5.18932366',
'变量'=>'5.62969398',
'反射'=>'6.59285069',
'用户'=>'7.40568614',
)
代码:
函数基准($callbacks,$iterations=100,$relative=false)
{
设置时间限制(0);
if(count($callbacks=array_filter((array)$callbacks,'is_callable'))>0)
{
$result=array\u fill\u key(array\u key($callbacks),0);
$arguments=array\u slice(func\u get\u args(),3);
对于($i=0;$i<$iterations;++$i)
{
foreach($结果为$key=>$value)
{
$value=microtime(true);call_user_func_数组($callbacks[$key],$arguments);$result[$key]+=microtime(true)-$value;
}
}
asort($result,SORT\u NUMERIC);
foreach(数组_反向($result)为$key=>$value)
{
如果($relative==true)
{
$value/=重置($result);
}
$result[$key]=数字格式($value,8'.','');
}
返回$result;
}
返回false;
}
福班{
公共静态功能条(){
返回方法;
}
}
类TesterDirect{
公共功能测试(){
返回foo::bar($\u服务器['REQUEST\u TIME']);
}
}
类测试变量{
private$class='foo';
公共功能测试(){
$class=$this->class;
返回$class::bar($\u服务器['REQUEST\u TIME']);
}
}
类测试员{
private$method=array('foo','bar');
公共功能测试(){
返回call_user_func($this->method,$\u SERVER['REQUEST\u TIME']);
}
}
类测试器反射{
private$class='foo';
私有$reflectionMethod;
公共函数构造
Array
(
    [directCall] => 4.13348770
    [variableCall] => 6.82747173
    [reflectedCall] => 8.67534351
)
Array
(
    [directCall] => 1.00000000
    [variableCall] => 1.67164707
    [reflectedCall] => 2.13174915
)
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;
}