Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Solar for PHP 5                                Paul M. Jones                              ConFoo, Montréal                ...
Read This                               2Wednesday, March 9, 2011
About Me                 •     Developer to VP Engineering                 •     PHP since 1999 (PHP 3)                 • ...
“Another Framework?”              • http://solarphp.com              • The first E_STRICT framework for PHP 5              ...
Overview                   • Foundational concepts and techniques                   • Dispatch cycle                   • P...
Foundations                                6Wednesday, March 9, 2011
PEAR Standards                   • pear.php.net                   • 10+ years of public usage and vetting                 ...
Class-to-File Effects                   •       Consistent and predictable                           (autoloader)         ...
Techniques                   • Unified constructor                   • Unified configuration                   • Unified facto...
Typical Constructor                            class Foo {                              protected $_foo;                  ...
Unified Constructor               class Vendor_Foo {                 protected $_Vendor_Foo = array(                     "f...
Typical Config File             webhost                  = www.example.com             database.adapter         = pdo_mysql...
Solar Config File                 <!-- /path/to/config.php -->                 <?php                 $values = array();    ...
Unified Configuration       • Given unique class names, and unified constructor ...       • ... configuration keys map directl...
Unified Factory          • Solar::factory() instead of `new` keyword      // non-adapter      $foo = Solar::factory("Vendor...
Typical Registry               // populate registry entries               $db = new DB::factory(mysql);               Regi...
Lazy-Loading Registry     // lazy, using default adapter and configs.     Solar_Registry::set("sql", "Solar_Sql");     // ...
Dependency                                 Management       • How to maintain dependencies on other objects?       • How t...
Dependency Injection                  // constructor-based dependency injection.                  // $db = DB::getAdapter(...
Service Locator                   // static locator.                   // $db = DB::getAdapter();                   // Loc...
Solar::dependency()                   • Combined service locator and dependency injector                   • Uses a regist...
Solar::dependency()                class Vendor_Foo extends Solar_Base {                  public function _postConstruct()...
Dependency Config               <!-- /path/to/config.php -->               <?php               // lazy-load Solar_Sql insta...
Dynamic Dispatch Cycle                           24Wednesday, March 9, 2011
Web Server + PHP                                    Framework                           Boot   Front   Page   Action   Vie...
Bootstrap               $system = dirname(dirname(__FILE__));               set_include_path("$system/include");          ...
Web App Controllers                   • Independent front and page controllers                   • URI default format of  ...
Front Controller                   • Stack of class prefixes, e.g. Vendor_App                   • /foo/bar/baz   => Vendor_...
Front Controller                   • Dynamic rewriter                   • Static routing                  $config["Solar_C...
Front Controller                   • Named actions                  <?php                  $config["Solar_Controller_Front...
Page Controller     •      Looks at remaining portion of URI path and picks action     • /foo/bar/baz => Vendor_App_Foo   ...
Package Highlights                                   32Wednesday, March 9, 2011
• SQL adapters                   • ORM system                   • Form generation                   • CLI tooling         ...
SQL Adapters           • Adapters for Mysql, Pgsql, Sqlite, Sqlite2, Oracle           • PDO-based, multiple fetch*() style...
Prepared Statements                   • Named placeholders and bound values            $sql = Solar::factory("Solar_Sql");...
Query Builder        • Programmatically build complex SELECT statements               $select = Solar::factory("Solar_Sql_...
MysqlReplicated                   • Adapter picks master or slave                   • Switch to/from non-replicated with n...
ORM System                       • TableDataGateway + DataMapper                           Model objects                  ...
Model Setup               class Vendor_Model_Invaders extends Solar_Sql_Model {                 protected function _setup(...
Relationship Definitions    merge          => merge data at server (DB) or client  (PHP)    native_by      => wherein or se...
Fetching               $model = Solar_Registry::get("model_catalog");               $collection = $model->invaders->fetchA...
Records               $zim = $model->invaders->fetchOne(array(                 "where" = array("name = ?" => "Zim")       ...
Record Filters                   •       Solar_Filter, Solar_Filter_Validate*, Solar_Filter_Sanitize*                     ...
Form Generation               $zim  = $model->invaders->fetch($id);               $form = $zim->newForm(); // Solar_Form o...
Much More Model                   • Single-table inheritance                   • Calculated and virtual columns           ...
CLI Tooling     $ ./script/solar make-vendor Vendor     $ ./script/solar make-model Vendor_Model_Invaders     $ ./script/s...
CLI Controllers                   • Independent “console” (front) and                           “command” (page) controlle...
Auth, Role, Access                   • Auth adapters (SQL, LDAP, email, etc.)                   • Role adapters (File, SQL...
Project Architecture                                    49Wednesday, March 9, 2011
System Directories               system/                 config.php    # config file                 config/       # confi...
Source vs. Include               include/       # single include-path for PHP files                 Solar/       # symlink...
Typical Page and View                     framework/             # libraries                     application/           # ...
Solar Page And View                Solar/               # libraries                Vendor/              # same include pat...
Performance Indicators                           54Wednesday, March 9, 2011
Benchmarks                   • Google “web framework benchmarks”                   • Amazon EC2 “Large” instance          ...
Results Table                 Framework                | relative | average                 ---------------------    | ---...
Results Graph                            lithium/0.9.9            0.08                              solar/1.1.1           ...
• Be suspicious of benchmarks                   • Only one data-point in decision making                   • Measures only...
Conclusion                   • Foundational concepts and techniques                   • Dispatch cycle                   •...
Aura Project                           for PHP 5.3+                            aka Solar version 2.0                      ...
Changes                   • Aura (Solar vs Solr)                   • PHP 5.3+ (namespaces, closures, etc)                 ...
Packages                   •       Autoloader                   •       DI (constructor/setter)                   •       ...
Where?                   • https://github.com/auraphp                   • http://groups.google.com/group/auraphp          ...
Thanks!            http://solarphp.com         http://auraphp.github.com                  http://joind.in/2814            ...
Upcoming SlideShare
Loading in …5
×

The Solar Framework for PHP

5,482 views

Published on

  • Be the first to comment

The Solar Framework for PHP

  1. 1. Solar for PHP 5 Paul M. Jones ConFoo, Montréal 09 Mar 2011 http://joind.in/2814 1Wednesday, March 9, 2011
  2. 2. Read This 2Wednesday, March 9, 2011
  3. 3. 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 3Wednesday, March 9, 2011
  4. 4. “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) 4Wednesday, March 9, 2011
  5. 5. Overview • Foundational concepts and techniques • Dispatch cycle • Package highlights • Project architecture • Performance indicators 5Wednesday, March 9, 2011
  6. 6. Foundations 6Wednesday, March 9, 2011
  7. 7. PEAR Standards • pear.php.net • 10+ years of public usage and vetting • Coding style guide • Class-to-file naming convention (PSR-0) <?php class Vendor_Foo_Bar {   // Vendor/Foo/Bar.php } 7Wednesday, March 9, 2011
  8. 8. 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 8Wednesday, March 9, 2011
  9. 9. Techniques • Unified constructor • Unified configuration • Unified factory • Lazy-loading registry • Dependency management 9Wednesday, March 9, 2011
  10. 10. Typical Constructor 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;   } } 10Wednesday, March 9, 2011
  11. 11. Unified Constructor 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"];   } } 11Wednesday, March 9, 2011
  12. 12. Typical Config File webhost                  = www.example.com database.adapter         = pdo_mysql database.params.host     = db.example.com database.params.username = dbuser database.params.password = secret database.params.dbname   = dbname # what class uses them? # what if different classes use "database" as their key? 12Wednesday, March 9, 2011
  13. 13. Solar Config File <!-- /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"; ?> 13Wednesday, March 9, 2011
  14. 14. Unified Configuration • Given unique class names, and unified constructor ... • ... configuration keys map directly to class names ... • ... unified configuration mechanism for all classes. $config = array(   "Vendor_Foo" => array(     "foo" => "zim",   ), ); // default values from config file $foo = new Vendor_Foo($config[Vendor_Foo]); 14Wednesday, March 9, 2011
  15. 15. Unified Factory • Solar::factory() instead of `new` keyword // 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", )); 15Wednesday, March 9, 2011
  16. 16. Typical Registry // populate registry entries $db = new DB::factory(mysql); Registry::set(db, $db); // later, elsewhere $db = Registry::get(db); 16Wednesday, March 9, 2011
  17. 17. Lazy-Loading Registry // 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"); 17Wednesday, March 9, 2011
  18. 18. Dependency Management • How to maintain dependencies on other objects? • How to configure, replace, or test the other object? • “Dependency injection” or “service locator” // typical dependency creation public function __construct() {   $this->db = new DB(); } 18Wednesday, March 9, 2011
  19. 19. Dependency Injection // 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; } 19Wednesday, March 9, 2011
  20. 20. Service Locator // 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"); } 20Wednesday, March 9, 2011
  21. 21. Solar::dependency() • Combined service locator and dependency injector • Uses a registry key (which may be lazy-loaded) ... • ... or a directly-passed dependency object. Solar::dependency($class_hint, $specification); 21Wednesday, March 9, 2011
  22. 22. Solar::dependency() 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")); 22Wednesday, March 9, 2011
  23. 23. Dependency Config <!-- /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"); 23Wednesday, March 9, 2011
  24. 24. Dynamic Dispatch Cycle 24Wednesday, March 9, 2011
  25. 25. Web Server + PHP Framework Boot Front Page Action View 25Wednesday, March 9, 2011
  26. 26. Bootstrap $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(); 26Wednesday, March 9, 2011
  27. 27. 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 27Wednesday, March 9, 2011
  28. 28. Front Controller • Stack of class prefixes, e.g. Vendor_App • /foo/bar/baz => Vendor_App_Foo 28Wednesday, March 9, 2011
  29. 29. Front Controller • Dynamic rewriter • Static routing $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", ); 29Wednesday, March 9, 2011
  30. 30. Front Controller • Named actions <?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)); 30Wednesday, March 9, 2011
  31. 31. 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() 31Wednesday, March 9, 2011
  32. 32. Package Highlights 32Wednesday, March 9, 2011
  33. 33. • SQL adapters • ORM system • Form generation • CLI tooling • Auth, Role, Access 33Wednesday, March 9, 2011
  34. 34. 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 34Wednesday, March 9, 2011
  35. 35. Prepared Statements • Named placeholders and bound values $sql = Solar::factory("Solar_Sql"); $stmt = "SELECT * FROM students WHERE name = :name"; $bind = array(   "name"  => "Bob; DROP TABLE students;--", ); $list = $sql->fetchAll($stmt, $bind); 35Wednesday, March 9, 2011
  36. 36. Query Builder • Programmatically build complex SELECT statements $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 36Wednesday, March 9, 2011
  37. 37. MysqlReplicated • Adapter picks master or slave • Switch to/from non-replicated with no code changes • Automatic “gap” (GET-after-POST) management $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"),   ), ); 37Wednesday, March 9, 2011
  38. 38. 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 38Wednesday, March 9, 2011
  39. 39. Model Setup 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");   } } 39Wednesday, March 9, 2011
  40. 40. Relationship Definitions 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. 40Wednesday, March 9, 2011
  41. 41. Fetching $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(...)); 41Wednesday, March 9, 2011
  42. 42. Records $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(); 42Wednesday, March 9, 2011
  43. 43. 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 <?php class 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");   } } 43Wednesday, March 9, 2011
  44. 44. Form Generation $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 44Wednesday, March 9, 2011
  45. 45. 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 45Wednesday, March 9, 2011
  46. 46. CLI Tooling $ ./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, ... 46Wednesday, March 9, 2011
  47. 47. 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 47Wednesday, March 9, 2011
  48. 48. Auth, Role, Access • Auth adapters (SQL, LDAP, email, etc.) • Role adapters (File, SQL) • Access adapters (File, SQL) • User class is composed of these 48Wednesday, March 9, 2011
  49. 49. Project Architecture 49Wednesday, March 9, 2011
  50. 50. System Directories 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 50Wednesday, March 9, 2011
  51. 51. Source vs. Include 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 51Wednesday, March 9, 2011
  52. 52. Typical Page and View framework/          # libraries application/           # 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/ 52Wednesday, March 9, 2011
  53. 53. Solar Page And View Solar/               # libraries Vendor/              # 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 53Wednesday, March 9, 2011
  54. 54. Performance Indicators 54Wednesday, March 9, 2011
  55. 55. 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 55Wednesday, March 9, 2011
  56. 56. Results Table Framework | relative | average --------------------- | -------- | -------- baseline/html | 1.2823 | 3220.56 baseline/php | 1.0000 | 2511.54 lithium/0.9.9 | 0.0827 | 207.62 solar/1.1.1 | 0.1609 | 404.17 symfony/1.4.8 | 0.0784 | 196.91 symfony/2.0.0pr4 | 0.0897 | 225.22 yii/1.1.5 | 0.1891 | 474.82 zend/1.10.2-minapp | 0.1136 | 285.28 zend/1.10.2-project | 0.0832 | 208.84 56Wednesday, March 9, 2011
  57. 57. Results Graph lithium/0.9.9 0.08 solar/1.1.1 0.16 symfony/1.4.8 0.08 symfony/2.0.0pr4 0.09 yii/1.1.5 0.19 zend/1.10.2-minapp 0.11 zend/1.10.2-project 0.08 0 0.05 0.10 0.15 0.20 57Wednesday, March 9, 2011
  58. 58. • Be suspicious of benchmarks • Only one data-point in decision making • Measures only what you test • https://github.com/pmjones/php- framework-benchmarks 58Wednesday, March 9, 2011
  59. 59. Conclusion • Foundational concepts and techniques • Dispatch cycle • Package highlights • Project architecture • Performance indicators • Stable, mature, proven 59Wednesday, March 9, 2011
  60. 60. Aura Project for PHP 5.3+ aka Solar version 2.0 60Wednesday, March 9, 2011
  61. 61. 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 61Wednesday, March 9, 2011
  62. 62. Packages • Autoloader • DI (constructor/setter) • Signal slots / event handler • Web router • CLI controllers • “System” • Next: web controllers and views 62Wednesday, March 9, 2011
  63. 63. Where? • https://github.com/auraphp • http://groups.google.com/group/auraphp • #auraphp on Freenode 63Wednesday, March 9, 2011
  64. 64. Thanks! http://solarphp.com http://auraphp.github.com http://joind.in/2814 64Wednesday, March 9, 2011

×