ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency Injection)
-
Upload
zfconf-conference -
Category
Documents
-
view
5.020 -
download
1
Transcript of ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency Injection)
Dependency Injection(Внедрение Зависимостей)
ZF, Zend_Application, DI Container, Symfony DI
27 марта 2010 г.
Санкт-Петербург
Кирилл Чебунин
• Ведущий PHP разработчик Equelli, ltd.
• Около 4 лет опыта PHP
• Более 2х лет использования ZendFramework
О Докладчике
Выбор классов
• Адаптеры• Плагины/Хелперы• Интерфейсы и Реализация
Хранилище
$GLOBALS
$logger = new Zend_Log();$writer = new Zend_Log_Writer_Stream('/var/log/myapp.log');$logger->addWriter($writer);
$GLOBALS['log'] = $logger;
class UserController extends Zend_Controller_Action{ public function indexAction() { /* @var $log Zend_Log */ $log = $GLOBALS['log']; $log->log('Index Action', Zend_Log::INFO); }
public function secondAction() { /* @var $log Zend_Log */ $log = $GLOBALS['log']; $log->log('Second Action', Zend_Log::INFO); }
}
Zend_Registry
$logger = new Zend_Log();$writer = new Zend_Log_Writer_Stream('/var/log/myapp.log');$logger->addWriter($writer);
Zend_Registry::set('log', $logger);
class UserController extends Zend_Controller_Action{ public function indexAction() { /* @var $log Zend_Log */ $log = Zend_Registry::get('log'); $log->log('Index Action', Zend_Log::INFO); }
public function secondAction() { /* @var $log Zend_Log */ $log = Zend_Registry::get('log'); $log->log('Second Action', Zend_Log::INFO); }
}
Zend_Application
[production]resources.log.file.writerName = "Stream"resources.log.file.writerParams.stream = "/var/log/myapp.log"
class UserController extends Zend_Controller_Action{ public function indexAction() { /* @var $log Zend_Log */ $log = $this->getInvokeArg('bootstrap')->getResource('log'); $log->log('Index Action', Zend_Log::INFO); }
public function secondAction() { /* @var $log Zend_Log */ $log = $this->getInvokeArg('bootstrap')->getResource('log'); $log->log('Second Action', Zend_Log::INFO); }
}
Zend_Application & Dependency Injection
class My_Application_Resource_Service_UserService extends Zend_Application_Resource_ResourceAbstract{ //[.....]}
class My_Application_Resource_Service_PostService extends Zend_Application_Resource_ResourceAbstract{ //[.....]}
class My_Application_Resource_Service_ArticleService extends Zend_Application_Resource_ResourceAbstract{ //[.....]}
class My_Application_Resource_Service_RoleService extends Zend_Application_Resource_ResourceAbstract{ //[.....]}//[.....]//[.....]//[.....]
Zend_Application & Dependency Injection
class Zend_Application_Resource_Log extends Zend_Application_Resource_ResourceAbstract{ //[.....] /** * Defined by Zend_Application_Resource_Resource * * @return Zend_Log */ public function init() { return $this->getLog(); }
//[.....] public function getLog() { if (null === $this->_log) { $options = $this->getOptions(); $log = Zend_Log::factory($options); $this->setLog($log); } return $this->_log; }}
Конфигурация вместо кода
<service id="log" class="Zend_Log" constructor="factory"> <argument type="collection"> <argument key="file" type="collection"> <argument key="writerName">Stream</argument> <argument key="writerParams" type="collection"> <argument key="stream">/var/log/myapp.log</argument> </argument> </argument> </argument></service>
<service id="log" class="Zend_Log"> <call method="addWriter"> <argument type="service"> <service class="Zend_Log_Writer_Stream"> <argument>/var/log/myapp.log</argument> </service> </argument> </call></service>
Dependency Injection —
A specific form of Inversion of Control (IOC)
Взято из ®Wikipedia, the free encyclopedia
Внедрение зависимости —
Специфическая форма «обращения контроля»
Inversion Of Control (IOC)
PHP DI Containers
• Symfony Dependency Injection• Yadif_Container• Seasar DI Container (S2Container)• Phemto• Xyster_Container• TYPO3 • ...........
Symfony DI Container
• Поддержка XML, YAML, PHP и INI конфигураций• Ленивая загрузка• Constructor and Method Injection• Shared/NotShared ресурсы• Конфигураторы• Алиасы
Замена контейнера
require_once 'Zend/Application.php';require_once 'sfServiceContainerBuilder.php';require_once 'sfServiceContainerLoaderFileXml.php';
//Create Container and load configuration from file$container = new sfServiceContainerBuilder();$loader = new sfServiceContainerLoaderFileXml($container);$loader->load(APPLICATION_PATH . '/configs/dependencies.xml');
// Create application, bootstrap, and run$application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');$application->getBootstrap()->setContainer($container);$application->bootstrap() ->run();
Dependencies.xml <?xml version="1.0" encoding="UTF-8"?><container xmlns="http://symfony-project.org/2.0/container">
<services> <service id="userMapper" class="UserMapperImpl"/> <service id="PostMapper" class="PostMapperImpl"/> <service id="CommentMapper" class="CommentMapperImpl" />
<service id="postService" class="PostServiceImpl"> <call method="setUserMapper"> <argument type="service" id="userMapper" /> </call> <call method="setPostMapper"> <argument type="service" id="postMapper" /> </call> <call method="setCommentMapper"> <argument type="service" id="commentMapper" /> </call> </service> <service id="userService" class="UserServiceImpl"> <call method="setUserMapper"> <argument type="service" id="userMapper" /> </call> </service> </services> </container>
//[..................] /** * Inject properties on Pre-Dispatch */public function preDispatch(){ $actionController = $this->getActionController();
$class = new Zend_Reflection_Class($actionController); $properties = $class->getProperties();
/* @var $property Zend_Reflection_Property */ foreach ($properties as $property) { if ($property->getDocComment()->hasTag('Inject')) { $injectTag = $property->getDocComment()->getTag('Inject'); $serviceName = $injectTag->getDescription(); if (empty($serviceName)) { $serviceName = $this->_formatServiceName( $property->getName()); } if (isset($this->_сontainer->$serviceName)) { $this->_injectProperty( $property, $this->_container->$serviceName ); } } }
}
Инъекция через аннотацииclass UserController extends Zend_Controller_Action{ /** * Logger * @var Zend_Log * @Inject */ private $_log;
public function setLog($log) { $this->_log = $log; }
public function indexAction() { $this->_log->log('Index Action', Zend_Log::INFO); }
public function secondAction() { $this->_log->log('Second Action', Zend_Log::INFO); }
}
Проблемы, советы, размышления
Транзакции, сквозная функциональность
Транзакции
• Транзакции недоступны в сервисном слое в нашей реализации
• Транзакции это уровень сервисов.
• Может быть несколько источников данных.
Общий интерфейс транзакций
interface TransactionManager{ /** * Start a new transaction * @return unknown_type */ public function beginTransaction(); /** * Commit the current transaction * @return unknown_type */ public function commit(); /** * Rollback the current transcation * @return unknown_type */ public function rollback(); }
Использование нескольких менеджеров транзакций
class MultipleTransactionManager implements TransactionManager{ private $tms = array(); public function setTransactionManagers(array $tms) { $this->tms = $tms; return $this; } public function beginTransaction() { /* @var $tm TransactionManager */ foreach ($this->tms as $tm) { $tm->beginTransaction(); } } //[.....] }
Сквозная функциональность
Аспектно-ориентированное программирование (АОП)
Динамические прокси
class SomeClass{ public function someMethod() {}}
class __GeneratedDynamicProxy__ extends SomeClass{ private $proxyManager; public function someMethod() { return $this->proxyManager->invoke(new ReflectionMethod( get_parent_class($this), __FUNCTION__ )); }}
Добавим Аннотаций
public function someMethod(){ $this->log(__METHOD__ . ' start'); if ($this->user->role != 'ROLE_ADMIN') { throw new SecuredException($this->user->role); } $tm = $this->tm; $tm->beginTransaction(); try { doBusiness(); $tm->commit(); } catch (Exception $e) { $tm->rollback(); throw $e; } $this->log(__METHOD__ . ' end');}
/** * Some business method * @Transactional * @Log * @Secured ROLE_ADMIN */public function someMethod(){ doBusiness();}
Хорошая архитектура — Простота конфигурации<service id="actionHelperStack" class="Zend_Controller_Action_HelperBroker" constructor="getStack"> <call method="push"> <argument type="service"> <service class="Zend_Controller_Action_Helper_ViewRenderer"> <argument type="service" id="view"/> </service> </argument> </call> <call method="push"> <argument type="service"> <service class="My_Controller_Action_Helper_DI"/> </argument> </call></service>