The Solar Framework for PHP

64
Solar for PHP 5 Paul M. Jones ConFoo, Montréal 09 Mar 2011 http://joind.in/2814 1 Wednesday, March 9, 2011

description

 

Transcript of The Solar Framework for PHP

Page 1: The Solar Framework for PHP

Solar for PHP 5

Paul M. JonesConFoo, Montréal

09 Mar 2011

http://joind.in/2814

1

Wednesday, March 9, 2011

Page 2: The Solar Framework for PHP

Read This

2

Wednesday, March 9, 2011

Page 3: The Solar Framework for PHP

About Me• Developer to VP Engineering

• PHP since 1999 (PHP 3)

• Savant Template System (phpsavant.com)

• PEAR Group (2007-2008)

• Zend Framework

• ZCE Advisory Board

• Web framework benchmarks

3

Wednesday, March 9, 2011

Page 4: The Solar Framework for PHP

“Another Framework?”

• http://solarphp.com

• The first E_STRICT framework for PHP 5

• Pre-dates Zend Framework

• Portions of ZF based on early Solar work(DB, DB_Select, DB_Table/Row/Rowset, View, View_Helper, and others)

4

Wednesday, March 9, 2011

Page 5: The Solar Framework for PHP

Overview

• Foundational concepts and techniques

• Dispatch cycle

• Package highlights

• Project architecture

• Performance indicators

5

Wednesday, March 9, 2011

Page 6: The Solar Framework for PHP

Foundations

6

Wednesday, March 9, 2011

Page 7: The Solar Framework for PHP

<?php class Vendor_Foo_Bar {  // Vendor/Foo/Bar.php}

PEAR Standards

• pear.php.net

• 10+ years of public usage and vetting

• Coding style guide

• Class-to-file naming convention (PSR-0)

7

Wednesday, March 9, 2011

Page 8: The Solar Framework for PHP

Class-to-File Effects• Consistent and predictable

(autoloader)

• Low cognitive friction on structure and organization

• Adaptable to change and integration

• Support files in parallel

• Works in every project

8

Wednesday, March 9, 2011

Page 9: The Solar Framework for PHP

Techniques

• Unified constructor

• Unified configuration

• Unified factory

• Lazy-loading registry

• Dependency management

9

Wednesday, March 9, 2011

Page 10: The Solar Framework for PHP

class Foo {  protected $_foo;  protected $_bar;  protected $_baz;  public function __construct( $foo = null, $bar = "dib", $baz = "gir" ) {    $this->_foo = $foo;    $this->_bar = $bar;    $this->_baz = $baz;  }}

Typical Constructor

10

Wednesday, March 9, 2011

Page 11: The Solar Framework for PHP

class Vendor_Foo {  protected $_Vendor_Foo = array(      "foo" => null,      "bar" => "baz",      "dib" => "gir",  );  protected $_foo;  protected $_bar;  protected $_baz;  public function __construct($config = array())  {    $config = array_merge($this->_Vendor_Foo, $config);    $this->_foo = $config["foo"];    $this->_bar = $config["bar"];    $this->_baz = $config["baz"];  }}

Unified Constructor

11

Wednesday, March 9, 2011

Page 12: The Solar Framework for PHP

webhost                  = www.example.comdatabase.adapter         = pdo_mysqldatabase.params.host     = db.example.comdatabase.params.username = dbuserdatabase.params.password = secretdatabase.params.dbname   = dbname

# what class uses them?# what if different classes use "database" as their key?

Typical Config File

12

Wednesday, March 9, 2011

Page 13: The Solar Framework for PHP

<!-- /path/to/config.php --><?php$values = array();$values["Solar_Sql"] = array(  "adapter" => "Solar_Sql_Adapter_Mysql",  "host"    => "db.example.com",  "user"    => "dbuser",  "pass"    => "secret",  "name"    => "dbname",);

return $values;?>

<!-- capture return value from include --><?php $config = include "/path/to/config.php"; ?>

Solar Config File

13

Wednesday, March 9, 2011

Page 14: The Solar Framework for PHP

$config = array(  "Vendor_Foo" => array(    "foo" => "zim",  ),);

// default values from config file$foo = new Vendor_Foo($config['Vendor_Foo']);

Unified Configuration• Given unique class names, and unified constructor ...

• ... configuration keys map directly to class names ...

• ... unified configuration mechanism for all classes.

14

Wednesday, March 9, 2011

Page 15: The Solar Framework for PHP

// non-adapter$foo = Solar::factory("Vendor_Foo"); // Vendor_Foo with config

// adapter: assume that the config file has set the value ...// $config["Solar_Sql"]["host"] = "db1.example.com";$sql = Solar::factory("Solar_Sql");

// factory a new instance with instance-time config$sql_other = Solar::factory("Solar_Sql", array(  "host" => "db2.example.com",));

Unified Factory• Solar::factory() instead of `new` keyword

15

Wednesday, March 9, 2011

Page 16: The Solar Framework for PHP

// populate registry entries$db = new DB::factory('mysql');Registry::set('db', $db);

// later, elsewhere$db = Registry::get('db');

Typical Registry

16

Wednesday, March 9, 2011

Page 17: The Solar Framework for PHP

// lazy, using default adapter and configs.Solar_Registry::set("sql", "Solar_Sql");

// lazy, via config file$config["Solar"]["registry_set"]["sql"] = "Solar_Sql";

// instantiation$sql = Solar_Registry::get("sql");

Lazy-Loading Registry

17

Wednesday, March 9, 2011

Page 18: The Solar Framework for PHP

// typical dependency creationpublic function __construct() {  $this->db = new DB();}

Dependency Management

• How to maintain dependencies on other objects?

• How to configure, replace, or test the other object?

• “Dependency injection” or “service locator”

18

Wednesday, March 9, 2011

Page 19: The Solar Framework for PHP

// constructor-based dependency injection.// $db = DB::getAdapter();// $foo = new Foo($db);public function __construct($db) {  $this->db = $db;}

// setter-based dependency injection.// $foo = new Foo();// $db = DB::getAdapter();// $foo->setDb($db);public function setDb($db) {  $this->db = $db;}

Dependency Injection

19

Wednesday, March 9, 2011

Page 20: The Solar Framework for PHP

// static locator.// $db = DB::getAdapter();// Locator::set("db", $db);public function __construct() {  $this->db = Locator::get("db");}

// instance locator as dependency injection.// $locator = new Locator();// $db = DB::getAdapter();// $locator->set("db", $db);// $foo = new Foo($locator);public function __construct($locator) {  $this->db = $locator->get("db");}

Service Locator

20

Wednesday, March 9, 2011

Page 21: The Solar Framework for PHP

Solar::dependency($class_hint, $specification);

Solar::dependency()

• Combined service locator and dependency injector

• Uses a registry key (which may be lazy-loaded) ...

• ... or a directly-passed dependency object.

21

Wednesday, March 9, 2011

Page 22: The Solar Framework for PHP

class Vendor_Foo extends Solar_Base {  public function _postConstruct() { parent::_postConstruct();    $this->_sql = Solar::dependency(      "Solar_Sql",      $this->_config["sql"]    );  }}

// inject an object$sql = Solar::factory("Solar_Sql");$foo = Solar::factory("Vendor_Foo", array("sql" => $sql));

// locate via registry key "sql"Solar_Registry::set("sql", "Solar_Sql");$foo = Solar::factory("Vendor_Foo", array("sql" => "sql"));

Solar::dependency()

22

Wednesday, March 9, 2011

Page 23: The Solar Framework for PHP

<!-- /path/to/config.php --><?php// lazy-load Solar_Sql instance as "sql" $config["Solar"]["registry_set"]["sql"] = "Solar_Sql";

// use "sql" registry key for Vendor_Foo dependency$config["Vendor_Foo"]["sql"] => "sql";?>

<!-- script file --><?php// now Vendor_Foo locates the "sql" entry automatically$foo = Solar::factory("Vendor_Foo");

Dependency Config

23

Wednesday, March 9, 2011

Page 24: The Solar Framework for PHP

Dynamic Dispatch Cycle

24

Wednesday, March 9, 2011

Page 25: The Solar Framework for PHP

Web Server + PHPFramework

Boot Front Page Action View

25

Wednesday, March 9, 2011

Page 26: The Solar Framework for PHP

$system = dirname(dirname(__FILE__));set_include_path("$system/include");

require "Solar.php";

$config = "$system/config.php";Solar::start($config);

$front = Solar_Registry::get("controller_front");$front->display();

Solar::stop();

Bootstrap

26

Wednesday, March 9, 2011

Page 27: The Solar Framework for PHP

Web App Controllers

• Independent front and page controllers

• URI default format of /controller/action/param/param/param

• Front controller determines only the page controller class, not the action or params

27

Wednesday, March 9, 2011

Page 28: The Solar Framework for PHP

Front Controller

• Stack of class prefixes, e.g. Vendor_App

• /foo/bar/baz => Vendor_App_Foo

28

Wednesday, March 9, 2011

Page 29: The Solar Framework for PHP

$config["Solar_Controller_Front"]["replace"] = array(  "{:alnum}" => "([a-zA-Z0-9]+)",);

$config["Solar_Controller_Front"]["rewrite"] = array(  "page/{:alnum}/edit" => "page/edit/$1",);

$config["Solar_Controller_Front"]["routing"] = array(  "page" => "Other_App_Zim",);

Front Controller• Dynamic rewriter

• Static routing

29

Wednesday, March 9, 2011

Page 30: The Solar Framework for PHP

<?php$config["Solar_Controller_Front"]["rewrite"]["edit-page"] = array(    'pattern' => 'page/{:id}/edit',    'rewrite' => 'page/edit/$1',    'replace' => array(        '{:id}' => '(\d+)',    ),    'default' => array(        '{:id}' => 88,    ););

// then in a view:echo $this->namedAction('edit-page', array('id' => 70));

Front Controller

• Named actions

30

Wednesday, March 9, 2011

Page 31: The Solar Framework for PHP

Page Controller

• Looks at remaining portion of URI path and picks action

• /foo/bar/baz => Vendor_App_Foo

• public function actionBar($param)

• preRun(), preAction(), postAction(), postRun(), preRender(), postRender()

31

Wednesday, March 9, 2011

Page 32: The Solar Framework for PHP

Package Highlights

32

Wednesday, March 9, 2011

Page 33: The Solar Framework for PHP

• SQL adapters

• ORM system

• Form generation

• CLI tooling

• Auth, Role, Access

33

Wednesday, March 9, 2011

Page 34: The Solar Framework for PHP

SQL Adapters

• Adapters for Mysql, Pgsql, Sqlite, Sqlite2, Oracle

• PDO-based, multiple fetch*() styles

• Standardized insert(), update(), delete()

• Abstracted LIMIT, column types, table creation, index creation, sequences, auto-increment, column description, index description

34

Wednesday, March 9, 2011

Page 35: The Solar Framework for PHP

$sql = Solar::factory("Solar_Sql");

$stmt = "SELECT * FROM students WHERE name = :name";

$bind = array(  "name"  => "Bob'; DROP TABLE students;--",);

$list = $sql->fetchAll($stmt, $bind);

Prepared Statements

• Named placeholders and bound values

35

Wednesday, March 9, 2011

Page 36: The Solar Framework for PHP

$select = Solar::factory("Solar_Sql_Select");$select->from("table", array("col", "col", ...))       ->join()      // leftJoin(), innerJoin()       ->where("col = ?", $value)       ->group()       ->having()       ->union()     // unionAll()       ->order()       ->limit()     // page(), paging()       ->fetchAll(); // fetchOne(), fetchPairs(), etc

Query Builder

• Programmatically build complex SELECT statements

36

Wednesday, March 9, 2011

Page 37: The Solar Framework for PHP

$config["Solar_Sql_Adapter_MysqlReplicated"] = array(  "host" => "master.db.example.com",  // ...  "slaves" => array(    0 => array("host" => "slave1.db.example.com"),    1 => array("host" => "slave2.db.example.com"),  ),);

MysqlReplicated• Adapter picks master or slave

• Switch to/from non-replicated with no code changes

• Automatic “gap” (GET-after-POST) management

37

Wednesday, March 9, 2011

Page 38: The Solar Framework for PHP

ORM System• TableDataGateway + DataMapper

Model objects

• Record objects (with relateds)

• Collection of Record objects

• Relationship definition objects

• Catalog for model objects

• Uses underlying SQL layers

38

Wednesday, March 9, 2011

Page 39: The Solar Framework for PHP

class Vendor_Model_Invaders extends Solar_Sql_Model {  protected function _setup() {    $this->_table_name = "invaders"; // default        // reads columns from table, or can be stored in:    // $this->_table_cols = array(...);        // each record has these relateds:    $this->_belongsTo("irk");    $this->_hasOne("robot");    $this->_hasMany("weapons");    $this->_hasMany("invasions");    $this->_hasManyThrough("planets", "invasions");  }}

Model Setup

39

Wednesday, March 9, 2011

Page 40: The Solar Framework for PHP

'merge'          => merge data at 'server' (DB) or 'client'  (PHP)'native_by'      => 'wherein' or 'select' when matching natives'native_col'     => native col to match against foreign col'foreign_class'  => class name of the foreign model'foreign_alias'  => alias of the foreign table name'foreign_col'    => foreign col to match against the native col'cols'           => fetch these foreign cols'conditions'     => where or join-on conditions'foreign_key'    => magic shorthand for the foreign key col'order'          => how to order related rows

... plus fetch-time params and eager-fetch params.

Relationship Definitions

40

Wednesday, March 9, 2011

Page 41: The Solar Framework for PHP

$model = Solar_Registry::get("model_catalog");

$collection = $model->invaders->fetchAll(array(  "where"   => array("type = ?" => "short"),  "group"   => ...  "order"   => ...  "page"    => ...  "paging"  => ...  "eager"   => array("weapons", "robot", "planets"),  ...));

$array  = $model->invaders->fetchAllAsArray(array(...));

$record = $model->invaders->fetchOne(array(...));

Fetching

41

Wednesday, March 9, 2011

Page 42: The Solar Framework for PHP

$zim = $model->invaders->fetchOne(array(  "where" = array("name = ?" => "Zim")));

echo $zim->doom;$zim->enemy = "Dib";

foreach ($zim->weapons as $weapon) {  echo $weapon->name;}

$zim->weapons->appendNew(array("name" => "raygun"));

$zim->save();

Records

42

Wednesday, March 9, 2011

Page 43: The Solar Framework for PHP

<?phpclass Vendor_Model_Invaders extends Solar_Sql_Model_Record{  protected function _setup() {    // ...    $this->_addFilter("bar", "validateAlnum");    $this->_addFilter("foo", "validateInList", array(        "one", "two", "three",    ));    $this->_addFilter("baz", "validateCustomThing");  }}

Record Filters• Solar_Filter, Solar_Filter_Validate*, Solar_Filter_Sanitize*

as generally-available classes, not regular expressions

• Vendor_Model_* will use Vendor_Filter_* automatically

• Applied on $record->save() automatically

43

Wednesday, March 9, 2011

Page 44: The Solar Framework for PHP

$zim  = $model->invaders->fetch($id);$form = $zim->newForm(); // Solar_Form object

$view = Solar::factory("Solar_View");$html = $view->form()             ->auto($form)             ->addProcessGroup("save", "cancel");

echo $html;

// automatic CSRF protection

Form Generation

44

Wednesday, March 9, 2011

Page 45: The Solar Framework for PHP

Much More Model

• Single-table inheritance

• Calculated and virtual columns

• Auto serializing of columns (arrays)

• Auto XML structs (EAV)

• Auto creation of tables and indexes

• Auto count-pages at fetch time

45

Wednesday, March 9, 2011

Page 46: The Solar Framework for PHP

$ ./script/solar make-vendor Vendor

$ ./script/solar make-model Vendor_Model_Invaders

$ ./script/solar make-app Vendor_App_Zim --model-name=invaders

# make-docs, make-tests, run-tests, link-public, ...

CLI Tooling

46

Wednesday, March 9, 2011

Page 47: The Solar Framework for PHP

CLI Controllers

• Independent “console” (front) and “command” (page) controllers

• Direct output with _out(), _outln(), _err(), _errln()

• VT100 escape codes built in

• Vendor-specific invocation

• ./script/vendor command-name

47

Wednesday, March 9, 2011

Page 48: The Solar Framework for PHP

Auth, Role, Access

• Auth adapters (SQL, LDAP, email, etc.)

• Role adapters (File, SQL)

• Access adapters (File, SQL)

• User class is composed of these

48

Wednesday, March 9, 2011

Page 49: The Solar Framework for PHP

Project Architecture

49

Wednesday, March 9, 2011

Page 50: The Solar Framework for PHP

system/  config.php    # config file  config/       # config support  docroot/      # vhost document root    index.php   # bootstrap    public/     # copies or symlinks to public assets  include/      # symlinks to PHP files  script/       # command-line scripts  source/       # actual PHP and support files  sqlite/       # sqlite databases  tmp/          # sessions, logs, caches, etc

System Directories

50

Wednesday, March 9, 2011

Page 51: The Solar Framework for PHP

include/       # single include-path for PHP files  Solar/       # symlink to source/solar/Solar  Solar.php    # symlink to source/solar/Solar.php  Vendor/      # symlink to source/vendor/Vendor  some.php     # symlink to source/other/some.php  else.php     # symlink to source/other/else.php

source/        # all "real" files for a vendor  solar/       # solar files    config/    Solar.php    Solar/      ...  vendor/      # vendor files    Vendor/  other/       # random assortments of 3rd-party libs    some.php    else.php

Source vs. Include

51

Wednesday, March 9, 2011

Page 52: The Solar Framework for PHP

framework/          # librariesapplication/           # separate include path  controllers/    FooController.php  # actions "bar" and "dib"    ZimController.php  # extends Foo  views/    foo/      bar.php      dib.php    zim/      bar.php      dib.php  layouts/    default.php  public/

Typical Page and View

52

Wednesday, March 9, 2011

Page 53: The Solar Framework for PHP

Solar/               # librariesVendor/              # same include path  App/    Foo.php          # actions "bar" and "dib"    Foo/      Layout/        default.php      Public/      View/        bar.php        dib.php    Zim.php          # extends Foo    Zim/      View/          # inherits Foo views        dib.php      # override view

Solar Page And View

53

Wednesday, March 9, 2011

Page 54: The Solar Framework for PHP

Performance Indicators

54

Wednesday, March 9, 2011

Page 55: The Solar Framework for PHP

Benchmarks

• Google “web framework benchmarks”

• Amazon EC2 “Large” instance

• Apache, mod_php 5.3.2, APC

• 10 concurrent users, no sessions

• 5 runs of 1 minute each, pre-warmed cache

55

Wednesday, March 9, 2011

Page 56: The Solar Framework for PHP

Results Table

Framework | relative | average--------------------- | -------- | --------baseline/html | 1.2823 | 3220.56baseline/php | 1.0000 | 2511.54lithium/0.9.9 | 0.0827 | 207.62solar/1.1.1 | 0.1609 | 404.17symfony/1.4.8 | 0.0784 | 196.91symfony/2.0.0pr4 | 0.0897 | 225.22yii/1.1.5 | 0.1891 | 474.82zend/1.10.2-minapp | 0.1136 | 285.28zend/1.10.2-project | 0.0832 | 208.84

56

Wednesday, March 9, 2011

Page 57: The Solar Framework for PHP

Results Graphlithium/0.9.9

solar/1.1.1

symfony/1.4.8

symfony/2.0.0pr4

yii/1.1.5

zend/1.10.2-minapp

zend/1.10.2-project

0 0.05 0.10 0.15 0.20

0.08

0.11

0.19

0.09

0.08

0.16

0.08

57

Wednesday, March 9, 2011

Page 58: The Solar Framework for PHP

• Be suspicious of benchmarks

• Only one data-point in decision making

• Measures only what you test

• https://github.com/pmjones/php-framework-benchmarks

58

Wednesday, March 9, 2011

Page 59: The Solar Framework for PHP

• Foundational concepts and techniques

• Dispatch cycle

• Package highlights

• Project architecture

• Performance indicators

• Stable, mature, proven

Conclusion

59

Wednesday, March 9, 2011

Page 60: The Solar Framework for PHP

Aura Project for PHP 5.3+

aka Solar version 2.0

60

Wednesday, March 9, 2011

Page 61: The Solar Framework for PHP

Changes

• Aura (Solar vs Solr)

• PHP 5.3+ (namespaces, closures, etc)

• DI proper instead of combo SL/DI

• Extract Solar functionality

• Independent library packages

• “System” package to create a framework

61

Wednesday, March 9, 2011

Page 62: The Solar Framework for PHP

Packages

• Autoloader

• DI (constructor/setter)

• Signal slots / event handler

• Web router

• CLI controllers

• “System”

• Next: web controllers and views

62

Wednesday, March 9, 2011

Page 63: The Solar Framework for PHP

Where?

• https://github.com/auraphp

• http://groups.google.com/group/auraphp

• #auraphp on Freenode

63

Wednesday, March 9, 2011

Page 64: The Solar Framework for PHP

Thanks!

http://solarphp.comhttp://auraphp.github.com

http://joind.in/2814

64

Wednesday, March 9, 2011