Top Banner
Decoupled Libraries for PHP 5.4: The Aura Project PHP Conference Brazil 2012 auraphp.github.com paul-m-jones.com @pmjones Saturday, December 1, 12
57

Decoupled Library Packages for PHP 5.4

Dec 17, 2014

Download

Technology

pmjones88

 
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Decoupled Library Packages for PHP 5.4

Decoupled Libraries for PHP 5.4:The Aura Project

PHP Conference Brazil 2012

auraphp.github.com

paul-m-jones.com@pmjones

Saturday, December 1, 12

Page 2: Decoupled Library Packages for PHP 5.4

Read These

Saturday, December 1, 12

Page 3: Decoupled Library Packages for PHP 5.4

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

Page 4: Decoupled Library Packages for PHP 5.4

Overview

• Background of libraries vs. frameworks

• Principles of decoupled libraries

• Examples: individual Aura libraries

Saturday, December 1, 12

Page 5: Decoupled Library Packages for PHP 5.4

Background:Libraries vs Frameworks

Saturday, December 1, 12

Page 6: Decoupled Library Packages for PHP 5.4

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

Page 7: Decoupled Library Packages for PHP 5.4

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 framework

Saturday, December 1, 12

Page 8: Decoupled Library Packages for PHP 5.4

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

Page 9: Decoupled Library Packages for PHP 5.4

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

Page 10: Decoupled Library Packages for PHP 5.4

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-math

Saturday, December 1, 12

Page 11: Decoupled Library Packages for PHP 5.4

The Aura Projectfor PHP 5.4+

Saturday, December 1, 12

Page 12: Decoupled Library Packages for PHP 5.4

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 packages

Saturday, December 1, 12

Page 13: Decoupled Library Packages for PHP 5.4

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

Page 14: Decoupled Library Packages for PHP 5.4

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 keyword

Saturday, December 1, 12

Page 15: Decoupled Library Packages for PHP 5.4

Service Locator Examplesclass Foo{ protected $db; public function __construct() { $this->db = Locator::get('db'); }}

Saturday, December 1, 12

Page 16: Decoupled Library Packages for PHP 5.4

class Foo{ protected $db; public function __construct(Locator $locator) { $this->db = $locator->get('db'); }}

Saturday, December 1, 12

Page 17: Decoupled Library Packages for PHP 5.4

Dependency Injection Examplesclass Foo{ protected $db; public function __construct(Database $db) { $this->db = $db; }}

Saturday, December 1, 12

Page 18: Decoupled Library Packages for PHP 5.4

class Foo{ protected $db; public function setDb(Database $db) { $this->db = $db; }}

Saturday, December 1, 12

Page 19: Decoupled Library Packages for PHP 5.4

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

Page 20: Decoupled Library Packages for PHP 5.4

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

Page 21: Decoupled Library Packages for PHP 5.4

Aura.Router

Saturday, December 1, 12

Page 22: Decoupled Library Packages for PHP 5.4

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

Page 23: Decoupled Library Packages for PHP 5.4

Package Organization

Saturday, December 1, 12

Page 24: Decoupled Library Packages for PHP 5.4

Instantiation

// (standalone, no autoloader)$map = require '/path/to/Aura.Router/scripts/instance.php';

// (with autoloader)use Aura\Router\Map;use Aura\Router\DefinitionFactory;use Aura\Router\RouteFactory;

$map = new Map(new DefinitionFactory, new RouteFactory);

Saturday, December 1, 12

Page 25: Decoupled Library Packages for PHP 5.4

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

Page 26: Decoupled Library Packages for PHP 5.4

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

Page 27: Decoupled Library Packages for PHP 5.4

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

Page 28: Decoupled Library Packages for PHP 5.4

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

Page 29: Decoupled Library Packages for PHP 5.4

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

Page 30: Decoupled Library Packages for PHP 5.4

Micro-Framework Dispatcher

$params = $route->values;$controller = $params['controller'];unset($params['controller']);echo $controller($params);

Saturday, December 1, 12

Page 31: Decoupled Library Packages for PHP 5.4

Aura.Web

Saturday, December 1, 12

Page 32: Decoupled Library Packages for PHP 5.4

DescriptionThe 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

Page 33: Decoupled Library Packages for PHP 5.4

Setup// include all package files,// or add to your autoloaderinclude '/path/to/Aura.Web/src.php';

// create a page controllernamespace Vendor\Package\Web;

use Aura\Web\AbstractPage;

class Page extends AbstractPage{ // controller body}

Saturday, December 1, 12

Page 34: Decoupled Library Packages for PHP 5.4

Instantiation and Callinguse Vendor\Package\Web\Page;use Aura\Web\Context;use Aura\Web\Accept;use Aura\Web\Response;use Aura\Web\Signal;use Aura\Web\Renderer\None 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

Page 35: Decoupled Library Packages for PHP 5.4

use Vendor\Package\Web\Page;use Aura\Web\Context;use Aura\Web\Accept;use Aura\Web\Response;use Aura\Web\Signal;use Aura\Web\Renderer\None as Renderer;

Saturday, December 1, 12

Page 36: Decoupled Library Packages for PHP 5.4

$params = [ 'action' => 'hello', 'format' => '.html', 'noun' => 'world',];

Saturday, December 1, 12

Page 37: Decoupled Library Packages for PHP 5.4

$page = new Page( new Context($GLOBALS), new Accept($_SERVER), new Response, new Signal, new Renderer, $params);

$response_transfer = $page->exec();

Saturday, December 1, 12

Page 38: Decoupled Library Packages for PHP 5.4

Naive Factoryclass NaivePageFactory{ protected $map = [ 'page-name' => 'Vendor\Package\Web\Page', ]; 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

Page 39: Decoupled Library Packages for PHP 5.4

Instantiation With Naive Factoryuse 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

Page 40: Decoupled Library Packages for PHP 5.4

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 hook

Saturday, December 1, 12

Page 41: Decoupled Library Packages for PHP 5.4

Action Methodnamespace Vendor\Package\Web;

use Aura\Web\AbstractPage;

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

Page 42: Decoupled Library Packages for PHP 5.4

Data Capture

protected function actionHello($noun = null){ $this->data->noun = $noun;}

Saturday, December 1, 12

Page 43: Decoupled Library Packages for PHP 5.4

Rendering Strategyclass 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

Page 44: Decoupled Library Packages for PHP 5.4

// 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

Page 45: Decoupled Library Packages for PHP 5.4

// 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

Page 46: Decoupled Library Packages for PHP 5.4

// set content on response, and done! $response = $this->controller->getResponse(); $response->setContent($content);

Saturday, December 1, 12

Page 47: Decoupled Library Packages for PHP 5.4

// template file 'hello.php'$noun = htmlspecialchars($noun, ENT_QUOTES, 'UTF-8');echo "Hello {$noun}!";

Saturday, December 1, 12

Page 48: Decoupled Library Packages for PHP 5.4

Naive Factory and Naive Rendererclass NaivePageFactory{ protected $map = [ 'page-name' => 'Vendor\Package\Web\Page', ]; 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

Page 49: Decoupled Library Packages for PHP 5.4

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 none

Saturday, December 1, 12

Page 50: Decoupled Library Packages for PHP 5.4

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

Page 51: Decoupled Library Packages for PHP 5.4

Full-Stack Framework

Saturday, December 1, 12

Page 52: Decoupled Library Packages for PHP 5.4

Packages (Stable and Beta)•Aura.Autoload

•Aura.Cli

•Aura.Di

• Aura.Filter

•Aura.Http

• Aura.Intl

•Aura.Marshal

•Aura.Router

• Aura.Session

•Aura.Signal

•Aura.Sql

•Aura.Uri

•Aura.View

•Aura.WebSaturday, December 1, 12

Page 53: Decoupled Library Packages for PHP 5.4

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 beta

Saturday, December 1, 12

Page 54: Decoupled Library Packages for PHP 5.4

Saturday, December 1, 12

Page 55: Decoupled Library Packages for PHP 5.4

Conclusion

Saturday, December 1, 12

Page 56: Decoupled Library Packages for PHP 5.4

• Background of libraries vs. frameworks

• Principles of decoupled libraries

• Individual Aura packages

Saturday, December 1, 12

Page 57: Decoupled Library Packages for PHP 5.4

Obrigado!

auraphp.github.com

paul-m-jones.com@pmjones

Saturday, December 1, 12