hanoi php day 2008 - 01.pham cong dinh - how.to.build.your.own.framework

Download Hanoi php day 2008 - 01.pham cong dinh - how.to.build.your.own.framework

If you can't read please download the document

Upload: nguyen-duc-phu

Post on 20-Jun-2015

2.950 views

Category:

Technology


2 download

TRANSCRIPT

  • 1. How to learn to buildyour own PHP framework Bridging the gap between PHP, OOP and Software Architecture Pham Cong Dinh (a.k.a pcdinh) Software Developer Hanoi PHP Day - December 2008 Hanoi - Vietnam

2. Introduction

  • A foundation member of JavaVietnam since 2003 ( http://www.javavietnam.org )
  • A foundation member of PHPVietnam Discussion Group since 2004 ( http://groups.google.com/group/phpvietnam )
  • Lead web developer with WorldVest Base Inc.
  • Java is my first love since 1999
  • PHP is my real lover since 2003. I love the way PHP community works
  • Sometimes I works on Python, Ruby, Erlang
  • I am a strong believer in dynamic programming languages, JavaScript, web standards, convergence of web as a platform and Outsourcing 2.0
  • I spent 2 years to play with my framework named Pone (PHP One)

3. Objectives

  • Where is PHP now? It is changing.
    • Enterprise oriented: which is driven by Yahoo, Facebook, Zend, Sun/MySQL, Oracle, OmniTI PHP is too big. It can not just be ignored
    • Object Oriented Programming adoption
    • Increased complexity of web applications
    • Web vs. Adobe Air, MS Silverlight, JavaFX, Google Native Client
    • Trends Will Move Applications to the Web: Consumer Innovation Setting the Pace, Rise of the Power Collaborator, New Economics of Scale for IT, Barriers to Adoption Are Falling
    • Scale-out wins
  • Understanding what framework designers think
  • Building up shared mindsets
  • Providing food for thought

4. Agenda 40 slides

  • Making judgments
  • Top notch frameworks and their shortcomings
  • A broader view on your framework
  • Lessons to learn

5. Making judgments

  • Common wisdom: Reinventing the wheel
  • Good
    • You know it inside and out
    • You control its pace
    • It fits your needs. Sometimes, your need is unique
    • It teaches you how the world around you works
    • License: This is why GPL is sometime a bad thing
  • Bad
    • You may not as good as other ones
    • No community
    • No outside contributors
    • Reinventing the square wheel

6. Making judgments

  • To develop a framework is just like to set up a business
  • Think of your limitation: time, resources, knowledge to build/test/document/maintain your own mental baby
  • Know your team: how to train them
  • Know the market : known frameworks in the market. Sometimes your needs are satisfied to some extent in several little-known frameworks
  • Starts with pencil and paper: its components and how they interact
  • Starts with API: learn how to design an API first
  • You never do it right from day one

7. Know the market

  • CakePHP shortcomings
  • Zend Framework shortcomings
  • Third party frameworks shortcomings

8. Know the market - CakePHP

  • Misleading terms: plugin, model
    • Plugin: CakePHP allows you to set up a combination of controllers, models, and views and release them as a packaged application
  • Too database centric:CakePHP naming convention is driven by table names, not dependency injection mechanism.
  • Admin routing sucks: why do we need one-and-only backend for the whole application/plugin/etc?
  • Flat application structure: plugin/controller/action and no more.
  • Global space constants

9. Know the market - CakePHP

  • No elegant way to change media file (css, javascript, meta content) on each layout page, controlled by a Controller.

10. Know the market - CakePHP

  • loadModel(), loadController() are not about dependency injection
  • E.x: You want to provide access to a model from a Component Say you have a model called FooBar in a file called foo_bar.php
  • loadModel ('FooBar');
  • $this ->FooBar=& new FooBar();
  • loadModel() maybe deprecated in favor of
  • App::import ('Model', 'ModelName');

11. Know the market - CakePHP

  • beforeFilter(), afterFilter() are coupled with a certain controller (controller is a heavy object. It should avoid being hit too soon)

12. Know the market - CakePHP

  • Reuse of view via elements withrequestAction()is bad and expensive
    • The dispatcher is called for each call to a controller (routing, figures out what (Plugin)/Controller/Action is request, loops through all $paths->controllerPaths files, to figure out what Controller to load)
    • The controller is set up again
  • Behavior: controllerActAsModel
  • Controller is an interface to another tier
  • Controller is not designed to provide data for internal components
  • Cache unfriendly

13. Know the market - CakePHP

  • Caching hits its hard time because there is no way to get generated view content
  • What about URL-based caching, session/cookie-based caching, geo-based caching, date-based caching
    • (there are a lot of things to tell about CakePHP but it is all for today)

14. Know the market Zend Framework

  • Zend Framework tries to be a better PEAR
    • Powered by a solid foundation
    • A solid and controllable licensing (CLA)
    • More strictly controlled development environment
    • Enterprise-oriented class library
    • A well-defined roadmap and versioning
  • Zend Framework is a glue framework or framework-oriented class library

15. Know the market Zend Framework

  • Zend Framework is extremely big and bloated
    • Zend Framework 1.6.2: 1261 file, 267 folders
    • Zend_Mail: 33 files
    • Zend_Pdf: 89 files
    • Zend_Controller: 50 files
    • Zend_View: 57 files
    • Drupal includes folders: 33 files
  • Zend Framework is designed most like Java frameworks
    • Small class file
    • Lot of classes: object graph is hard (see next)
    • Atomic responsibility
    • Strongly embrace design patterns

16. 17. Know the market Zend Framework

  • Everything is an object, even a HTML button or checkbox. The same to Java (see Apache Wicket, Tapestry, JBoss Seam)
    • classZend_View_Helper_FormResetextendsZend_View_Helper_FormElement
    • {
    • public functionformReset($name = '', $value = 'Reset', $attribs = null)
    • {
    • $info = $this->_getInfo($name, $value, $attribs);
    • extract ($info); // name, value, attribs, options, listsep, disable
    • // check if disabled
    • $disabled = '';
    • if($disable) {
    • $disabled = ' disabled="disabled"';
    • }
    • // get closing tag
    • $endTag = '>';
    • if ($this->view->doctype()->isXhtml()) {
    • $endTag = ' />';
    • }
    • // Render button
    • $xhtml = '
    • . ' name="' . $this->view->escape($name) . '"'
    • . ' id="' . $this->view->escape($id) . '"'
    • . $disabled;
    • . . . . . . . . .
    • }
    • }

18. Know the market Zend Framework

  • What Zend Framework brings
    • Lot of files are loaded per request which is a bad thing for a dynamic, interpreted language and stateless platform like PHP
    • Much more memory usage
    • Bad thing for PHP memory management model in which memory is allocated in small chunks
    • Zend Framework code: There are lot of require_once() call inside an if statement which is bad for opcode caching mechanism
    • Zend Framework leaves shared hosting in the cold.
      • 700 sites per server are quite normal
      • No control over file system optimization
      • No control over memory
      • No control over opcode caching

19. Know the market Zend Framework

  • A glue framework requires you to know every concrete class and how to use them in a application life cycle
  • A lot of things to consider means bootstrapping is a mess

20. Know the market Zend Framework

  • define ('ROOT_DIR', dirname(dirname(dirname(__FILE__))));
  • define ('APP_DIR',dirname(dirname(__FILE__)));
  • set_include_path ('.' . PATH_SEPARATOR . APP_DIR . '/lib/' . PATH_SEPARATOR . APP_DIR . '/application/default/models/' . PATH_SEPARATOR . ROOT_DIR . '/shared/lib/' . PATH_SEPARATOR . get_include_path());
  • //This requires that your Zend library lies in ROOT_DIR/shared/lib/
  • //make classes autoload without doing require
  • require_once ('Zend/Loader.php');
  • Zend_Loader::registerAutoload();
  • if ( defined ('ENV') !== TRUE) {
  • define ('ENV','production'); //change staging to production to go to production settings
  • }
  • $config = new Zend_Config_Xml(APP_DIR . '/config/config.xml', ENV);
  • Zend_Registry::set('config',$config);
  • //init session
  • $session =newZend_Session_Namespace($config->session_name);
  • Zend_Registry::set('session',$session);
  • Zend_Db_Table::setDefaultAdapter(Zend_Db::factory(Zend_Registry::get('config')->database));
  • /**
  • * Init the Smarty view wrapper and set smarty suffix to the view scripts.
  • */
  • $view =newEZ_View_Smarty($config->smarty->toArray());

21. Know the market Zend Framework

  • // use the viewrenderer to keep the code DRY instantiate and add the helper in one go
  • $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
  • $viewRenderer->setView($view);
  • $viewRenderer->setViewSuffix($config->smarty->suffix);
  • /**
  • * Set inflector for Zend_Layout
  • */
  • $inflector =newZend_Filter_Inflector(':script.:suffix');
  • $inflector->addRules(array(':script' =>array ('Word_CamelCaseToDash', 'StringToLower'), 'suffix'=> $config->layout->suffix));
  • // Initialise Zend_Layout's MVC helpers
  • Zend_Layout::startMvc( array ('layoutPath' => ROOT_DIR.$config->layout->layoutPath,
  • 'view' => $view,
  • 'contentKey' => $config->layout->contentKey,
  • 'inflector' => $inflector));
  • $front = Zend_Controller_Front::getInstance();
  • $front->setControllerDirectory(array(
  • 'default' => '../application/default/controllers',
  • 'blog'=> '../application/blog/controllers',
  • ));
  • $front->throwExceptions(true);
  • // enable logging to default.log
  • $writer =newZend_Log_Writer_Stream(APP_DIR.'/data/log/default.log');
  • $logger =newZend_Log($writer);
  • // give easy access to the logger
  • Zend_Registry::set('logger', $logger);
  • try{
  • $front->dispatch();
  • }catch (Exception $e) {
  • echonl2br($e->__toString());
  • }

22. Know the market Zend Framework

  • Zend Framework is different.
    • It is not a solid application framework like CakePHP, it is designed to be a platform on which other frameworks are built
  • Technical details should be mentioned in another talk (enough for today)

23. A broader view on your framework

  • MVC Push or Pull
    • MVC Push or Passive View

24. A broader view on your framework

  • MVC Push or Pull
    • MVC Pull or so-called HMVC (see next): break a big controller into small ones

25. A broader view on your framework 26. 27. A broader view on your framework

  • MVC Push or Pull:HMVC implementation
    • Master Controller
    • /**
    • * Show the home page
    • *
    • * @linkhttp://www.wvbresearch.com/home/index/index
    • * @nameindex
    • * @access public
    • */
    • public function indexAction()
    • {
    • // Attach placeholder: the name of ElementGroup
    • $this->_layout->registerBody('homeIndex');
    • // Set content for the response
    • $this->_response->setContent($this->_layout->render('index3col'));
    • }

28. A broader view on your framework

  • MVC Push or Pull: HMVC implementation
    • Group Controller
    • classGroup_HomeIndexextendsPone_View_ElementGroup
    • {
    • /**
    • * Elements in in this group
    • *
    • * @var array
    • */
    • protected$_elementsInGroup = array(
    • 'homeTopNegativeEpsSurprises', 'homeTopPositiveEpsSurprises',
    • 'homeIntroduction', 'brokerRatingsUpgrades', 'homeAnalystEstimatesSearchBox',
    • 'homeResearchReportSearchBox', 'latestResearchReports'
    • );
    • protected$_templateFile = 'homeIndex';
    • public functionsetup()
    • {
    • }
    • }

29. A broader view on your framework

  • MVC Push or Pull: HMVC implementation
    • Element Controller
    • classElement_LatestResearchReportsextendsPone_View_Element
    • {
    • protected$_templateFile = 'latestResearchReportsOnHome';
    • /**
    • * List of recent research reports
    • *
    • * @var Pone_DataSet
    • */
    • public$researchReports;
    • public functionsetup()
    • {
    • $module= Pone::getContext()->getFront()->getRequest()->getModuleName();
    • $numberOfItems = 7;
    • if('home'!== $module)
    • {
    • $this->_templateFile = 'latestResearchReports';
    • $numberOfItems= 10;
    • }
    • $dbConn= Pone_Action_Helper_Database::getInstance()->getConnection('oracleweb', true);
    • $researchReportDs= ResearchReportDatabaseService::getInstance($dbConn);
    • $this->researchReports = $researchReportDs->findRecentList($numberOfItems);
    • }
    • }

30. A broader view on your framework

  • MVC Push or Pull: HMVC implementation
    • Element Controller template
    • < div class =" featureBlockHeader ">
    • < h2 >Latest reports h2 >
    • div >
    • < divclass=" textBox ">
    • < divclass=" textBoxContent ">
    • < ulclass=" imgList ">
    • < li >get('report_id') ?>">
    • div >
    • div >

31. A broader view on your framework

  • IDE support
    • Code completion rocks
    • MVC Push is bad for view data documentation
    • Zend_Registry is bad for code completion
      • Zend_Registry :: set('logger', $logger);
    • Think of interface because implementing a way to load dynamic class from a variable or an array element.
    • Learn how to write DocBlock

32. A broader view on your framework

  • Core feature set
    • MVC framework
      • Model layer: DBO, File handling/transformation, business rules, workflows, search, messaging, memory, remote resource access
    • Validation framework instead of form handling
    • Unified directory structure: model classes, controllers, views (page fragments, layouts), plugins, filters, custom exceptions, helpers
    • Session
    • Authentication and ACL: Abstract and extensible
      • HTTP Digest
      • Database backed
      • SAML/SSO
      • Serializable Unified Session User Object

33. A broader view on your framework

  • Core feature set
    • Validation framework
    • class Form_Signup extends Pone_Form_Input
    • {
    • . . . . . .
    • public function onPost()
    • {
    • // Email
    • $emailRules= array(
    • Pone_Form_Rule::EMAIL => array('feedback'=> _t('common.error.email.notvalid'))
    • );
    • $this->setValidationRule('email', $emailRules);
    • // Email 2
    • $email2Rules= array(
    • Pone_Form_Rule::STRING_EQUAL => array('feedback' => _t('common.error.reemail.not_match'),
    • 'reference' => 'email')
    • );
    • $this->setValidationRule('email2', $email2Rules);
    • // password
    • $passwordRules= array(
    • Pone_Form_Rule::NOT_EMPTY=> array('feedback' => _t('common.error.password.empty'))
    • );
    • $this->setValidationRule('password', $passwordRules);
    • // password 2
    • $password2Rules= array(
    • Pone_Form_Rule::STRING_EQUAL => array('feedback' => _t('common.error.repassword.not_match'),
    • 'reference' => 'password')
    • );
    • $this->setValidationRule('password2', $password2Rules);
    • }
    • }

34. A broader view on your framework

  • Much more things that need to take into account
    • Behavior layer
    • Caching
      • Distributed caching
      • Local caching
    • Dependency Injection framework
    • Internationalization
    • (enough for today)

35. Lessons to learn

  • Take your hand dirty please.
  • Singleton is bad thing when dependency injection and unit testing are taken into consideration
    • can't replace it with an interface
    • Factory allows for both discovery and instance management of the service providers
    • Final classes should keep singleton objects
    • $dbConn =Pone_Action_Helper_Database :: getInstance ()->getConnection('oracleweb', true);
    • $researchReportDs =ResearchReportDatabaseService :: getInstance ($dbConn);
    • $this->researchReports = $researchReportDs->findRecentList($numberOfItems);

36. Lessons to learn

  • Factory and interface make good things
    • Factory and Adapter are good for service providers
  • $conn= Pone_Database_ConnectionFactory::getConnection($config);
  • $stmt= $conn->createStatement();
  • $stmt ->addBatch("INSERT INTO test2 VALUES (1007, 'pcdinh1007', 1)");
  • $stmt ->addBatch("INSERT INTO test2 VALUES (1009, 'pcdinh1009', 1)");
  • $stmt ->addBatch("INSERT INTO test2 VALUES (1010, 'pcdinh1010', 1)");
  • $conn ->beginTransaction();
  • $updateCounts= $stmt->executeBatch();

37. Lessons to learn

  • Fluent interface/object chaining sometimes is a bad thing
    • Law of Demeter
  • $module = Pone::getContext()->getFront()->getRequest()->getModuleName();

38. Lessons to learn

  • Dont think DAO or ActiveRecord, think Domain Respository

39. Lessons to learn

  • An interface between Model and Controller must be defined
    • Model class returns an array: bad thing. How to catch errors and deal with them in the view template

40. Lessons to learn

  • Dependency Injection
    • Does all injection through the constructor
    • $libBasePath = $basePath.'/libs';
    • $appBasePath = $basePath.'/apps';
    • Pone::executeContext( new BenchmarkContext() , $basePath, $appBasePath, $libBasePath);
    • OR
    • $front->setRequest(new Pone_Request( new Pone_Request_SimpleUrlParser() ));
    • Use Template Method design pattern
      • Seam
      • if (session_id() === '' && PHP_SAPI != 'cli')
      • {
      • Pone::getContext()->loadSessionUserClass();
      • $started = session_start(); // PHP 5.3: returns false or true
      • $this->_started = true;
      • }
    • Use XML/YAML like in Spring, Symfony which is somewhat heavy in an interpreted language like PHP

41. Design by Interface

  • Rule: Dont call me, I will call you
  • Template Method
  • Convention over configuration
  • Thats end for today

42. Thanks you

  • Any question?