В настоящее время я рассматриваю использование классов Reflection (главным образом ReflectionClass и ReflectionMethod) в своей собственной веб-среде MVC, потому что мне нужно автоматически запускать классы контроллеров и вызывать их методы без какой-либо необходимой настройки (подход «соглашение по конфигурации»).
Меня беспокоит производительность, хотя я думаю, что запросы к базе данных скорее всего будут большими узкими местами, чем реальный PHP-код.
Итак, мне интересно, есть ли у кого-нибудь хороший или плохой опыт работы с PHP 5 Reflection с точки зрения производительности.
Кроме того, мне было бы интересно узнать, использует ли какой-либо из популярных фреймворков PHP (CI, Cake, Symfony и т. Д.) Использование Reflection.
Не беспокойтесь. Установите Xdebug и убедитесь, где находится узкое место.
Существует стоимость использования рефлексии, но зависит ли это от того, что вы делаете. Если вы используете диспетчер диспетчера / запроса с помощью Reflection, то это всего лишь одно использование для каждого запроса. Абсолютно ничтожно.
Если вы используете свой слой ORM с помощью отражения, используйте его для каждого объекта или даже для каждого доступа к свойству и создайте сотни или тысячи объектов, тогда это может быть дорогостоящим.
Я сравнивал эти 3 варианта (в другом тесте не было разбиения циклов процессора и было 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 (массив ('directCall', 'variableCall', 'reflectCall'), 1000000));
Array ( [directCall] => 4.13348770 [variableCall] => 6.82747173 [reflectedCall] => 8.67534351 )
И относительное время, также с 1 000 000 итераций (отдельный прогон):
ph () -> Dump (Benchmark (массив ('directCall', 'variableCall', 'reflectCall'), 1000000, true));
Array ( [directCall] => 1.00000000 [variableCall] => 1.67164707 [reflectedCall] => 2.13174915 )
Похоже, что производительность отражения была значительно увеличена в 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; }
Вызов статической функции 1 миллион раз будет стоить ~ 0,31 секунды на моей машине. При использовании ReflectionMethod он стоит ~ 1,82 секунды. Это означает, что на ~ 500% дороже использовать API Reflection.
Это код, который я использовал:
<?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;
Очевидно, что фактическое воздействие зависит от количества вызовов, которые вы ожидаете сделать
Кроме того, мне было бы интересно узнать, использует ли какой-либо из популярных фреймворков PHP (CI, Cake, Symfony и т. Д.) Использование Reflection.
http://framework.zend.com/manual/en/zend.server.reflection.html
«Как правило, эта функциональность будет использоваться разработчиками классов серверов для платформы».
Накладные расходы небольшие, поэтому нет большого штрафа за производительность. Другие вещи, такие как db, обработка шаблонов и т. Д. – проблемы с производительностью, протестируйте свою инфраструктуру с помощью простого действия, чтобы увидеть, насколько это быстро.
Например, приведенный ниже код (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 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 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 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 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); } }
В моем случае отражение это всего на 230% медленнее, чем вызов метода класса напрямую, который так же быстро, как функция call_user_func.
Иногда использование чего-то типа call_user_func_array () может дать вам то, что вам нужно. Не знаю, как отличается производительность.
CodeIgniter defenitly использует Reflections. И я уверен, что другие тоже делают. Изучите класс Controller в папке system / controller в ci-установке.
на основе кода, который @Alix Axel предоставил
Поэтому для полноты я решил обернуть каждую опцию в классе и включить кеширование объектов там, где это применимо. здесь были результаты и код. Результаты на PHP 5.6 на i7-4710HQ
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));