KEMBAR78
The Solar Framework for PHP | PDF
Solar for PHP 5
                                Paul M. Jones
                              ConFoo, Montréal
                                09 Mar 2011

                             http://joind.in/2814
                                      1
Wednesday, March 9, 2011
Read This




                               2
Wednesday, March 9, 2011
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
“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
Overview

                   • Foundational concepts and techniques
                   • Dispatch cycle
                   • Package highlights
                   • Project architecture
                   • Performance indicators
                                       5
Wednesday, March 9, 2011
Foundations



                                6
Wednesday, March 9, 2011
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
                           }
                                        7
Wednesday, March 9, 2011
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
Techniques

                   • Unified constructor
                   • Unified configuration
                   • Unified factory
                   • Lazy-loading registry
                   • Dependency management
                                    9
Wednesday, March 9, 2011
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;
                              }
                            }
                                          10
Wednesday, March 9, 2011
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"];
                 }
               }

                                          11
Wednesday, March 9, 2011
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?




                                        12
Wednesday, March 9, 2011
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"; ?>

                                         13
Wednesday, March 9, 2011
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']);

                                         14
Wednesday, March 9, 2011
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",
      ));




                                     15
Wednesday, March 9, 2011
Typical Registry
               // populate registry entries
               $db = new DB::factory('mysql');
               Registry::set('db', $db);

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




                                  16
Wednesday, March 9, 2011
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");




                               17
Wednesday, March 9, 2011
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();
                           }
                                            18
Wednesday, March 9, 2011
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;
                  }
                                       19
Wednesday, March 9, 2011
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");
                   }
                                        20
Wednesday, March 9, 2011
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);



                                           21
Wednesday, March 9, 2011
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"));


                                           22
Wednesday, March 9, 2011
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");




                                          23
Wednesday, March 9, 2011
Dynamic Dispatch Cycle



                           24
Wednesday, March 9, 2011
Web Server + PHP
                                    Framework
                           Boot   Front   Page   Action   View




                                           25
Wednesday, March 9, 2011
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();



                                       26
Wednesday, March 9, 2011
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
Front Controller


                   • Stack of class prefixes, e.g. Vendor_App
                   • /foo/bar/baz   => Vendor_App_Foo




                                       28
Wednesday, March 9, 2011
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",
                  );

                                            29
Wednesday, March 9, 2011
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));



                                                   30
Wednesday, March 9, 2011
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
Package Highlights



                                   32
Wednesday, March 9, 2011
• SQL adapters
                   • ORM system
                   • Form generation
                   • CLI tooling
                   • Auth, Role, Access

                                          33
Wednesday, March 9, 2011
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
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);



                                      35
Wednesday, March 9, 2011
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



                                         36
Wednesday, March 9, 2011
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"),
                     ),
                   );

                                             37
Wednesday, March 9, 2011
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
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");
                 }
               }


                                         39
Wednesday, March 9, 2011
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.




                                     40
Wednesday, March 9, 2011
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(...));



                                          41
Wednesday, March 9, 2011
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();



                                         42
Wednesday, March 9, 2011
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");
                    }
                  }

                                                      43
Wednesday, March 9, 2011
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


                                       44
Wednesday, March 9, 2011
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
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, ...




                                   46
Wednesday, March 9, 2011
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
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
Project Architecture



                                    49
Wednesday, March 9, 2011
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




                                         50
Wednesday, March 9, 2011
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


                                             51
Wednesday, March 9, 2011
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/



                                             52
Wednesday, March 9, 2011
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


                                         53
Wednesday, March 9, 2011
Performance Indicators



                           54
Wednesday, March 9, 2011
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
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


                                         56
Wednesday, March 9, 2011
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
                                                57
Wednesday, March 9, 2011
• 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
Conclusion
                   • Foundational concepts and techniques
                   • Dispatch cycle
                   • Package highlights
                   • Project architecture
                   • Performance indicators
                   • Stable, mature, proven
                                       59
Wednesday, March 9, 2011
Aura Project
                           for PHP 5.3+
                            aka Solar version 2.0

                                     60
Wednesday, March 9, 2011
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
Packages
                   •       Autoloader
                   •       DI (constructor/setter)
                   •       Signal slots / event handler
                   •       Web router
                   •       CLI controllers
                   •       “System”
                   •       Next: web controllers and views

                                                 62
Wednesday, March 9, 2011
Where?

                   • https://github.com/auraphp
                   • http://groups.google.com/group/auraphp
                   • #auraphp on Freenode

                                       63
Wednesday, March 9, 2011
Thanks!
            http://solarphp.com
         http://auraphp.github.com

                  http://joind.in/2814




                                         64
Wednesday, March 9, 2011

The Solar Framework for PHP

  • 1.
    Solar for PHP5 Paul M. Jones ConFoo, Montréal 09 Mar 2011 http://joind.in/2814 1 Wednesday, March 9, 2011
  • 2.
    Read This 2 Wednesday, March 9, 2011
  • 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 3 Wednesday, March 9, 2011
  • 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) 4 Wednesday, March 9, 2011
  • 5.
    Overview • Foundational concepts and techniques • Dispatch cycle • Package highlights • Project architecture • Performance indicators 5 Wednesday, March 9, 2011
  • 6.
    Foundations 6 Wednesday, March 9, 2011
  • 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 } 7 Wednesday, March 9, 2011
  • 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 8 Wednesday, March 9, 2011
  • 9.
    Techniques • Unified constructor • Unified configuration • Unified factory • Lazy-loading registry • Dependency management 9 Wednesday, March 9, 2011
  • 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;   } } 10 Wednesday, March 9, 2011
  • 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"];   } } 11 Wednesday, March 9, 2011
  • 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? 12 Wednesday, March 9, 2011
  • 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"; ?> 13 Wednesday, March 9, 2011
  • 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']); 14 Wednesday, March 9, 2011
  • 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", )); 15 Wednesday, March 9, 2011
  • 16.
    Typical Registry // populate registry entries $db = new DB::factory('mysql'); Registry::set('db', $db); // later, elsewhere $db = Registry::get('db'); 16 Wednesday, March 9, 2011
  • 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"); 17 Wednesday, March 9, 2011
  • 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(); } 18 Wednesday, March 9, 2011
  • 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; } 19 Wednesday, March 9, 2011
  • 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"); } 20 Wednesday, March 9, 2011
  • 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); 21 Wednesday, March 9, 2011
  • 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")); 22 Wednesday, March 9, 2011
  • 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"); 23 Wednesday, March 9, 2011
  • 24.
    Dynamic Dispatch Cycle 24 Wednesday, March 9, 2011
  • 25.
    Web Server +PHP Framework Boot Front Page Action View 25 Wednesday, March 9, 2011
  • 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(); 26 Wednesday, March 9, 2011
  • 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 27 Wednesday, March 9, 2011
  • 28.
    Front Controller • Stack of class prefixes, e.g. Vendor_App • /foo/bar/baz => Vendor_App_Foo 28 Wednesday, March 9, 2011
  • 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", ); 29 Wednesday, March 9, 2011
  • 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)); 30 Wednesday, March 9, 2011
  • 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() 31 Wednesday, March 9, 2011
  • 32.
    Package Highlights 32 Wednesday, March 9, 2011
  • 33.
    • SQL adapters • ORM system • Form generation • CLI tooling • Auth, Role, Access 33 Wednesday, March 9, 2011
  • 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 34 Wednesday, March 9, 2011
  • 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); 35 Wednesday, March 9, 2011
  • 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 36 Wednesday, March 9, 2011
  • 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"),   ), ); 37 Wednesday, March 9, 2011
  • 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 38 Wednesday, March 9, 2011
  • 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");   } } 39 Wednesday, March 9, 2011
  • 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. 40 Wednesday, March 9, 2011
  • 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(...)); 41 Wednesday, March 9, 2011
  • 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(); 42 Wednesday, March 9, 2011
  • 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");   } } 43 Wednesday, March 9, 2011
  • 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 44 Wednesday, March 9, 2011
  • 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 45 Wednesday, March 9, 2011
  • 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, ... 46 Wednesday, March 9, 2011
  • 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 47 Wednesday, March 9, 2011
  • 48.
    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
  • 49.
    Project Architecture 49 Wednesday, March 9, 2011
  • 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 50 Wednesday, March 9, 2011
  • 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 51 Wednesday, March 9, 2011
  • 52.
    Typical Page andView 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/ 52 Wednesday, March 9, 2011
  • 53.
    Solar Page AndView 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 53 Wednesday, March 9, 2011
  • 54.
    Performance Indicators 54 Wednesday, March 9, 2011
  • 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 55 Wednesday, March 9, 2011
  • 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 56 Wednesday, March 9, 2011
  • 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 57 Wednesday, March 9, 2011
  • 58.
    • Be suspiciousof 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
  • 59.
    Conclusion • Foundational concepts and techniques • Dispatch cycle • Package highlights • Project architecture • Performance indicators • Stable, mature, proven 59 Wednesday, March 9, 2011
  • 60.
    Aura Project for PHP 5.3+ aka Solar version 2.0 60 Wednesday, March 9, 2011
  • 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 61 Wednesday, March 9, 2011
  • 62.
    Packages • Autoloader • DI (constructor/setter) • Signal slots / event handler • Web router • CLI controllers • “System” • Next: web controllers and views 62 Wednesday, March 9, 2011
  • 63.
    Where? • https://github.com/auraphp • http://groups.google.com/group/auraphp • #auraphp on Freenode 63 Wednesday, March 9, 2011
  • 64.
    Thanks! http://solarphp.com http://auraphp.github.com http://joind.in/2814 64 Wednesday, March 9, 2011