Decoupled Library Packages for PHP 5.4
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Decoupled Library Packages for PHP 5.4

on

  • 5,765 views

 

Statistics

Views

Total Views
5,765
Views on SlideShare
5,263
Embed Views
502

Actions

Likes
5
Downloads
12
Comments
0

1 Embed 502

http://paul-m-jones.com 502

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Decoupled Library Packages for PHP 5.4 Presentation Transcript

  • 1. Decoupled Libraries for PHP 5.4: The Aura Project PHP Conference Brazil 2012 auraphp.github.com paul-m-jones.com @pmjonesSaturday, December 1, 12
  • 2. Read TheseSaturday, December 1, 12
  • 3. About Me • PHP since 1999 • Developer, Senior Developer, Team Lead, Architect,VP Engineering • Aura project, benchmarking series, Solar framework, Savant template system • Zend_DB, Zend_View • ZCE Education Advisory Board, PHP-FIG Voting Member (PSR-0/1/2)Saturday, December 1, 12
  • 4. Overview • Background of libraries vs. frameworks • Principles of decoupled libraries • Examples: individual Aura librariesSaturday, December 1, 12
  • 5. Background: Libraries vs FrameworksSaturday, December 1, 12
  • 6. Frameworks: Bad! • PHP 3, PHP 4, and early PHP 5: “framework” a bad word (“CMS” was ok) • Libraries and collections: Horde, PEAR, phpLib, phpClasses • Not unified in operation: different constructor signatures, different method verbiage, different usage idioms, tough to combine • Started Solar (solarphp.com) in 2005 as a library collection (PHP 5, E_STRICT)Saturday, December 1, 12
  • 7. Frameworks: Good! • Rails: “framework” suddenly acceptable • Cake, CodeIgniter, Symfony, Zend • Tapped into developer needs • All delivered as a monolithic whole • Re-branded Solar libraries as a frameworkSaturday, December 1, 12
  • 8. Frameworks: Good? • Want to use just part of a framework? Difficult. • Download entire framework and try to use one part ... • ... except it has dependencies. • Have to set up parts you don’t care about.Saturday, December 1, 12
  • 9. Frameworks: Re-Evaluating • PHP 5.3 “full stack”: Lithium, Symfony 2, Zend Framework 2, others • Micro-frameworks: Glue, Limonade, Silex, Slim • Context, router/dispatcher, HTTP request/response, session manager • Ed Finkler, “The Micro-PHP Manifesto” (microphp.org) • Componentized: Symfony 2 (kind of), Zend Framework 2 (kind of)Saturday, December 1, 12
  • 10. install: zend-config zend-http depends: zend-escaper zend-filter zend-i18n zend-loader zend-servicemanager zend-stdlib zend-uri zend-validator suggest: pecl-weakref zendframework/zend-di zendframework/zend-crypt zendframework/zend-db zendframework/zend-mathSaturday, December 1, 12
  • 11. The Aura Project for PHP 5.4+Saturday, December 1, 12
  • 12. Rewrite Solar • Solar Framework: 5+ years old at the time (Oct 2010) • Monolithic; tough to use just parts of it • Extract components and rewrite using PHP 5.4 • Independent, de-coupled library packagesSaturday, December 1, 12
  • 13. Driving Principles • Libraries first, framework later • No dependencies on any other package (self-contained) • Tests and assets encapsulated within package • No use of globals within packages (e.g., $_SERVER) • Carry data across package boundaries using data transfer objects • Use a separated interface for each shared tool (signal, log, cache, etc)Saturday, December 1, 12
  • 14. Use Dependency Injection • Solar used service locator and universal constructor • In Aura, that would mean a package dependency • So, all packages are set up for dependency injection • You can use any DI container you like (Aura.Di is nice ;-) • Factories and builders instead of new keywordSaturday, December 1, 12
  • 15. Service Locator Examples class Foo { protected $db; public function __construct() { $this->db = Locator::get(db); } }Saturday, December 1, 12
  • 16. class Foo{ protected $db; public function __construct(Locator $locator) { $this->db = $locator->get(db); }}Saturday, December 1, 12
  • 17. Dependency Injection Examples class Foo { protected $db; public function __construct(Database $db) { $this->db = $db; } }Saturday, December 1, 12
  • 18. class Foo { protected $db; public function setDb(Database $db) { $this->db = $db; } }Saturday, December 1, 12
  • 19. class Foo{ protected $db_factory; public function __construct(DatabaseFactory $db_factory) { $this->db_factory = $db_factory; } public function doSomething() { $db = $this->db_factory->newInstance(); }}Saturday, December 1, 12
  • 20. Dependency Injection > Service Locator • Locator hides dependencies • Locator is itself a dependency (all libraries need it) • Harder to write tests • DI reveals dependencies • Is not itself a dependency • Easier to write tests, mocks, etc.Saturday, December 1, 12
  • 21. Aura.RouterSaturday, December 1, 12
  • 22. Description Aura Router is a PHP package that implements web routing. Given a URL path and a copy of $_SERVER, it will extract controller, action, and parameter values for a specific application route.Saturday, December 1, 12
  • 23. Package OrganizationSaturday, December 1, 12
  • 24. Instantiation // (standalone, no autoloader) $map = require /path/to/Aura.Router/scripts/instance.php; // (with autoloader) use AuraRouterMap; use AuraRouterDefinitionFactory; use AuraRouterRouteFactory; $map = new Map(new DefinitionFactory, new RouteFactory);Saturday, December 1, 12
  • 25. Creating Routes // add a simple named route without params $map->add(home, /); // add a simple unnamed route with params $map->add(null, /{:controller}/{:action}/{:id:(d+)}); // add a complex named route $map->add(read, /blog/read/{:id}{:format}, [ params => [ id => (d+), format => (..+)?, ], values => [ controller => blog, action => read, format => .html, ], ));Saturday, December 1, 12
  • 26. Matching Routes // get the incoming request URI path $path = parse_url($_SERVER[REQUEST_URI], PHP_URL_PATH); // get the route based on the path and server $route = $map->match($path, $_SERVER); The `match()` method does not parse the URI or use `$_SERVER` internally. This is because different systems may have different ways of representing that information; e.g., through a URI object or a context object. As long as you can pass the string path and a server array, you can use Aura Router in your application foundation or framework.Saturday, December 1, 12
  • 27. Route Values $route = $map->match(/blog/read/42.json, $_SERVER); var_export($route->values); // shows these values: [ controller => blog, action => read, id => 42, format => .json, ]Saturday, December 1, 12
  • 28. Dispatching Routes $params = $route->values; $class = ucfirst($params[controller]) . Page; unset($params[controller]); $method = $params[action] . Action; unset($params[action]); $object = new $class(); echo $object->$method($params);Saturday, December 1, 12
  • 29. Micro-Framework Route $map->add(read, /blog/read/{:id}{:format}, [ params => [ id => (d+), format => (..+)?, ], values => [ controller => function ($args) { $id = (int) $args[id]; return "Reading blog ID {$id}"; }, format => .html, ], ));Saturday, December 1, 12
  • 30. Micro-Framework Dispatcher $params = $route->values; $controller = $params[controller]; unset($params[controller]); echo $controller($params);Saturday, December 1, 12
  • 31. Aura.WebSaturday, December 1, 12
  • 32. Description The Aura Web package provides tools to build web page controllers, including an `AbstractPage` for action methods, a `Context` class for discovering the request environment, and a `Response` transfer object that describes the eventual HTTP response. (Note that the `Response` transfer object is not itself an HTTP response.)Saturday, December 1, 12
  • 33. Setup // include all package files, // or add to your autoloader include /path/to/Aura.Web/src.php; // create a page controller namespace VendorPackageWeb; use AuraWebAbstractPage; class Page extends AbstractPage { // controller body }Saturday, December 1, 12
  • 34. Instantiation and Calling use VendorPackageWebPage; use AuraWebContext; use AuraWebAccept; use AuraWebResponse; use AuraWebSignal; use AuraWebRendererNone as Renderer; $params = [ action => hello, format => .html, noun => world, ]; $page = new Page( new Context($GLOBALS), new Accept($_SERVER), new Response, new Signal, new Renderer, $params ); $response_transfer = $page->exec();Saturday, December 1, 12
  • 35. use VendorPackageWebPage; use AuraWebContext; use AuraWebAccept; use AuraWebResponse; use AuraWebSignal; use AuraWebRendererNone as Renderer;Saturday, December 1, 12
  • 36. $params = [ action => hello, format => .html, noun => world, ];Saturday, December 1, 12
  • 37. $page = new Page( new Context($GLOBALS), new Accept($_SERVER), new Response, new Signal, new Renderer, $params ); $response_transfer = $page->exec();Saturday, December 1, 12
  • 38. Naive Factory class NaivePageFactory { protected $map = [ page-name => VendorPackageWebPage, ]; public function newInstance($name, array $params) { $class = $this->map[$name]; return new $class( new Context($GLOBALS), new Accept($_SERVER), new Response, new Signal, new Renderer, $params ); } }Saturday, December 1, 12
  • 39. Instantiation With Naive Factory use NaivePageFactory; $params = [ action => hello, format => .html, noun => world, ]; $factory = new NaivePageFactory; $page = $factory->newInstance(page-name, $params); $response_transfer = $page->exec();Saturday, December 1, 12
  • 40. Important Parts • $this->params for incoming parameters • $this->context for get, post, files, etc. • $this->accept for content-type, language, encoding, etc • $this->response for headers, cookies, content (data transfer object) • $this->signal for signals/events/notifiers (separated interface) • $this->renderer for rendering strategy (default “none”) • $this->data for data to be rendered • (pre|post)_(exec|action|render) hooks, and new catch_exception hookSaturday, December 1, 12
  • 41. Action Method namespace VendorPackageWeb; use AuraWebAbstractPage; class Page extends AbstractPage { protected function actionHello($noun = null) { $noun = htmlspecialchars($noun, ENT_QUOTES, UTF-8); $content = "Hello, {$noun}!"; $this->response->setContent($content); } }Saturday, December 1, 12
  • 42. Data Capture protected function actionHello($noun = null) { $this->data->noun = $noun; }Saturday, December 1, 12
  • 43. Rendering Strategy class NaiveRenderer extends AbstractRenderer { public function exec() { // get data from controller $data = (array) $this->controller->getData(); // pick a template file based on controller action $action = $this->controller->getAction(); $__file__ = "/path/to/templates/{$action}.php"; // closure to execute template file $template = function () use (array $data, $__file__) { ob_start(); extract($data); require $__file__; return ob_get_clean(); }; // invoke closure $content = $template(); // set content on response, and done! $response = $this->controller->getResponse(); $response->setContent($content); } }Saturday, December 1, 12
  • 44. // get data from controller$data = (array) $this->controller->getData();// pick a template file based on controller action$action = $this->controller->getAction();$__file__ = "/path/to/templates/{$action}.php";Saturday, December 1, 12
  • 45. // closure to execute template file$template = function () use (array $data, $__file__) { ob_start(); extract($data); require $__file__; return ob_get_clean();};// invoke closure$content = $template();Saturday, December 1, 12
  • 46. // set content on response, and done!$response = $this->controller->getResponse();$response->setContent($content);Saturday, December 1, 12
  • 47. // template file hello.php$noun = htmlspecialchars($noun, ENT_QUOTES, UTF-8);echo "Hello {$noun}!";Saturday, December 1, 12
  • 48. Naive Factory and Naive Renderer class NaivePageFactory { protected $map = [ page-name => VendorPackageWebPage, ]; public function newInstance($name, array $params) { $class = $this->map[$name]; return new $class( new Context($GLOBALS), new Accept($_SERVER), new Response, new Signal, new NaiveRenderer, // <-- strategy $params ); } }Saturday, December 1, 12
  • 49. Response Delivery • ResponseTransfer object is *not* an HTTP response • It is a Data Transfer Object • Must convert it to a real HTTP response • Allows any HTTP library, or noneSaturday, December 1, 12
  • 50. Delivery Code $headers = $response_transfer->getHeaders(); foreach ($headers as $label => $value) { header($label, $value); } $cookies = $response_transfer->getCookies(); foreach ($cookies as $cookie) { setcookie( $cookie[value], $cookie[expire], $cookie[path], $cookie[domain], $cookie[secure], $cookie[httponly] ); } echo $response_transfer->getContent();Saturday, December 1, 12
  • 51. Full-Stack FrameworkSaturday, December 1, 12
  • 52. Packages (Stable and Beta) • Aura.Autoload • Aura.Router • Aura.Cli • Aura.Session • Aura.Di • Aura.Signal • Aura.Filter • Aura.Sql • Aura.Http • Aura.Uri • Aura.Intl • Aura.View • Aura.Marshal • Aura.WebSaturday, December 1, 12
  • 53. Aura.Framework and System • “Aura.Framework” package to connect all the others into a cohesive whole • “Aura.Demo” package to provide “hello world” • “System” package to provide a project skeleton • Still betaSaturday, December 1, 12
  • 54. Saturday, December 1, 12
  • 55. ConclusionSaturday, December 1, 12
  • 56. • Background of libraries vs. frameworks • Principles of decoupled libraries • Individual Aura packagesSaturday, December 1, 12
  • 57. Obrigado! auraphp.github.com paul-m-jones.com @pmjonesSaturday, December 1, 12