Исходя из последнего блога о лени в PHP ,
еще немного Lazy Initialization, на этот раз применимо к загрузке классов.
В большинстве языков программирования объекты «дороги» с точки зрения производительности. В PHP «стоимость объектов» ощущается еще больше, благодаря PHP, помимо прочего, распределяет память между запросами страниц (см. Часть 1 ).
Чтобы продемонстрировать это, вот простой класс управления формой (основанный на контроллерах форм в WACT );
class FormController { var $initial_action; var $invalid_action; var $valid_action; var $validator; /** * This action will be performed if the form has not yet * been posted */ function registerInitialAction(& $action) { $this->initial_action = & $action; } /** * This action is performed if the form IS posted by * invalid data was submitted */ function registerInvalidAction(& $action) { $this->invalid_action = & $action; } /** * This action is performed if the form IS posted and * the submitted data was validated */ function registerValidAction(& $action) { $this->valid_action = & $action; } /** * Registers the object for validation */ function registerValidator(& $validator) { $this->validator = & $validator; } /** * Run the form controller */ function run() { # Has the form been posted? if ( !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') ) { # No - perform the initial action $this->initial_action->perform(); } else { # Yes - need to validate if ( ! $this->validator->isValid() ) { # Invalid data - perform the invalid action $this->invalid_action->perform(); } else { # All OK - perform the valid action $this->valid_action->perform(); } } } }
Чтобы использовать этот контроллер, я определяю свои собственные классы «действий», которые содержат метод execute, например;
class UserUpdateInitial { function perform() { // display the HTML form } }
class UserUpdateInitial { function perform() { // display the HTML form } }
и
class UserUpdateValid { function perform() { // pull data from $_POST and update database // display a some HTML saying "success" } }
class UserUpdateValid { function perform() { // pull data from $_POST and update database // display a some HTML saying "success" } }
Тогда я могу использовать контроллер формы, как;
< ?php // Load all the necessary classes require_once 'lib/controller/formcontroller.php'; require_once 'lib/action/userupdateinitial.php'; require_once 'lib/action/userupdateinvalid.php'; require_once 'lib/action/userupdatevalid.php'; require_once 'lib/validator/validator.php'; // Create the FormController $F = & new FormController(); // Register the actions $F->registerInitialAction(new UserUpdateInitial()); $F->registerInvalidAction(new UserUpdateInvalid()); $F->registerInvalidAction(new UserUpdateValid()); // Validation $V = & new Validator(); $V->addRule(new SizeRule('username',1,15)); $V->addRule(new MatchRule('password','confirmpassword')); $F->registerValidator($V); // Run the form controller $F->run(); ?>
< ?php // Load all the necessary classes require_once 'lib/controller/formcontroller.php'; require_once 'lib/action/userupdateinitial.php'; require_once 'lib/action/userupdateinvalid.php'; require_once 'lib/action/userupdatevalid.php'; require_once 'lib/validator/validator.php'; // Create the FormController $F = & new FormController(); // Register the actions $F->registerInitialAction(new UserUpdateInitial()); $F->registerInvalidAction(new UserUpdateInvalid()); $F->registerInvalidAction(new UserUpdateValid()); // Validation $V = & new Validator(); $V->addRule(new SizeRule('username',1,15)); $V->addRule(new MatchRule('password','confirmpassword')); $F->registerValidator($V); // Run the form controller $F->run(); ?>
Если вам это не по душе, найдите время, чтобы изучить метод FormController :: run (), описанный выше, и вы должны увидеть, как это сочетается.
Класс Validator и «Правила» я оставлю как воображаемый код, чтобы держать это в фокусе.
Все идет нормально. Классы контроля формы и проверки теперь являются кодом, который я могу повторно использовать для любой (простой) формы, что позволяет мне сосредоточиться на разработке «действий», специфичных для этой формы.
Но когда дело доходит до эффективности, есть проблема. Если вы посмотрите на FormController::run()
вы заметите несколько вещей;
- Класс Validator используется только в том случае, если форма действительно отправлена. В начальном состоянии формы (отображая пустую форму) мне не нужен валидатор (пока ничего не поддается проверке).
- — Для одного запроса страницы выполняется только одно действие. Но для каждого запроса страницы я создаю три «объекта действия», два из которых не используются.
- Возможно, для этого простого примера я могу обойтись без него, но для сложной формы с несколькими страницами и множеством полей для проверки в сочетании со всеми видами других «инфраструктурных» классов (доступ к БД и т. Д.) Он быстро становится слишком дорогим.
Так я могу улучшить вещи? Для ответа введите Джеффа, который недавно поднял эту тему, в « Составлении объектов» по доверенности . Результат этого обсуждения можно найти в WACT в виде функции ResolveHandle (можно найти в WACT: framework / util / handle.inc.php);
< ?php //-------------------------------------------------------------------------------- // Copyright 2003 Procata, Inc. // Released under the LGPL license ( http://www.gnu.org/copyleft/lesser.html) //-------------------------------------------------------------------------------- /** * @package WACT_UTIL * @version $Id: handle.inc.php,v 1.1 2004/03/10 01:53:49 jeffmoore Exp $ */ /** * Takes a "handle" to an object and modifies it to convert it to an instance * of the class. Allows for "lazy loading" of objects on demand. * @see http://wact.sourceforge.net/index.php/ResolveHandle * @param mixed * @return void * @access public * @package WACT */ function ResolveHandle(&$Handle) { if (!is_object($Handle) && !is_null($Handle)) { if (is_array($Handle)) { $Class = array_shift($Handle); $ConstructionArgs = $Handle; } else { $ConstructionArgs = array(); $Class = $Handle; } if (is_integer($Pos = strpos($Class, '|'))) { $File = substr($Class, 0, $Pos); $Class = substr($Class, $Pos + 1); require_once $File; } switch (count($ConstructionArgs)) { case 0: $Handle = new $Class(); // =& doesn't work here. Why? break; case 1: $Handle = new $Class(array_shift($ConstructionArgs)); break; case 2: $Handle = new $Class( array_shift($ConstructionArgs), array_shift($ConstructionArgs)); break; case 3: $Handle = new $Class( array_shift($ConstructionArgs), array_shift($ConstructionArgs), array_shift($ConstructionArgs)); break; default: // Too many arguments for this cobbled together implemenentation. 🙁 die(); } } } ?>
< ?php //-------------------------------------------------------------------------------- // Copyright 2003 Procata, Inc. // Released under the LGPL license ( http://www.gnu.org/copyleft/lesser.html) //-------------------------------------------------------------------------------- /** * @package WACT_UTIL * @version $Id: handle.inc.php,v 1.1 2004/03/10 01:53:49 jeffmoore Exp $ */ /** * Takes a "handle" to an object and modifies it to convert it to an instance * of the class. Allows for "lazy loading" of objects on demand. * @see http://wact.sourceforge.net/index.php/ResolveHandle * @param mixed * @return void * @access public * @package WACT */ function ResolveHandle(&$Handle) { if (!is_object($Handle) && !is_null($Handle)) { if (is_array($Handle)) { $Class = array_shift($Handle); $ConstructionArgs = $Handle; } else { $ConstructionArgs = array(); $Class = $Handle; } if (is_integer($Pos = strpos($Class, '|'))) { $File = substr($Class, 0, $Pos); $Class = substr($Class, $Pos + 1); require_once $File; } switch (count($ConstructionArgs)) { case 0: $Handle = new $Class(); // =& doesn't work here. Why? break; case 1: $Handle = new $Class(array_shift($ConstructionArgs)); break; case 2: $Handle = new $Class( array_shift($ConstructionArgs), array_shift($ConstructionArgs)); break; case 3: $Handle = new $Class( array_shift($ConstructionArgs), array_shift($ConstructionArgs), array_shift($ConstructionArgs)); break; default: // Too many arguments for this cobbled together implemenentation. 🙁 die(); } } } ?>
Эта загадочно выглядящая функция обеспечивает дополнительный «протокол» для ссылки на объекты в дополнение к обычному назначению переменных PHP. Поместите другой путь, а не это;
require_once 'lib/action/userupdateinitial.php'; //... $F->registerInitialAction(new UserUpdateInitial());
require_once 'lib/action/userupdateinitial.php'; //... $F->registerInitialAction(new UserUpdateInitial());
Теперь я могу сделать только это;
$F->registerInitialAction('lib/action/userupdateinitial.php|UserUpdateInitial');
$F->registerInitialAction('lib/action/userupdateinitial.php|UserUpdateInitial');
Вместо того чтобы регистрировать сам объект, я зарегистрировал только строку, содержащую имя файла и имя класса, которые могут быть «разрешены» позже, когда этот объект необходим.
Мне нужно обновить метод FormController :: run (), чтобы воспользоваться ResolveHandle ();
(ОБНОВЛЕНИЕ): Измените сценарий ниже… оригинальное использование ResolveHandle было неверным
/** * Run the form controller */ function run() { if ( !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') ) { // Resolve the InitialAction ResolveHandle($this->initial_action); $this->initial_action->peform(); } else { // Resolve the Validator ResolveHandle($this->validator); if ( ! $this->validator->isValid() ) { // Resolve the InvalidAction ResolveHandle($this->invalid_action); $this->invalid_action->perform(); } else { // Resolve the ValidAction ResolveHandle($this->valid_action); $this->valid_action->perform(); } } }
/** * Run the form controller */ function run() { if ( !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') ) { // Resolve the InitialAction ResolveHandle($this->initial_action); $this->initial_action->peform(); } else { // Resolve the Validator ResolveHandle($this->validator); if ( ! $this->validator->isValid() ) { // Resolve the InvalidAction ResolveHandle($this->invalid_action); $this->invalid_action->perform(); } else { // Resolve the ValidAction ResolveHandle($this->valid_action); $this->valid_action->perform(); } } }
Используя ResolveHandle()
, объекты создаются только по мере необходимости, сокращая накладные расходы производительности только до тех объектов, которые необходимы для обработки текущего запроса.
Обратите внимание, что я мог бы просто «зарегистрировать» имена классов и файлов для этого конкретного примера, но ResolveHandle()
Джеффа более гибок в том, что он поддерживает несколько различных типов «ссылок» (включая нормальную ссылку на реальный объект), а также Передача объектов в конструктор. Дополнительные примеры можно найти по адресу http://wact.sourceforge.net/index.php/ResolveHandle .
Хотя в этом примере я сосредоточился на обработке форм, общий принцип можно было бы распространить на другие проблемные области в приложениях PHP, от объектно-реляционного сопоставления до построения модели предметной области. Подробнее об этом в другой раз.