Decoupled Library Packages for PHP 5.4

  • 6,053 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
6,053
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
13
Comments
0
Likes
5

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

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