Building an advanced ActiveRecord Model in PHP – Part 1 1

Posted by Vic Cherubini on October 26, 2009

This is a valid article, and is considered technically accurate up to Feb. 21, 2010

As Ruby on Rails took off, so did the idea of the ActiveRecord design pattern. ActiveRecord allows you to define a class that models a single table in the database. This works well for small tables that don’t rely on other tables for writing and loading, however, the patterns doesn’t work as well with a table that depends on many other tables.

This series will start of examining a class to handle small tables, and then progress through several parts to handling validation, using a separate data recorder, and finally to handle relationships. The code in here will all use PHP5.2+ and will be available on our GitHub account and to download at the end of each article.

For example, in our eCommerce application, SpEEdy Cart, the table that stores global, editable configuration data is very straightforward.

CREATE TABLE `config` (
  `config_id` smallint(3) NOT NULL AUTO_INCREMENT,
  `group` varchar(32) collate utf8_unicode_ci NOT NULL,
  `name` varchar(64) collate utf8_unicode_ci NOT NULL,
  `value` text collate utf8_unicode_ci NOT NULL,
  `required` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY  (`config_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Each piece of configuration data is independent of any other piece and only modifies the execution of the software. There are no other tables that store configuration information, so this table is a prime candidate to have an ActiveRecord Model written for it.

The ActiveRecord pattern allows the programmer to create a new Config object, set the data, and write it to the database. This is done through the interface of the class itself, and the database writing is hidden from the user.

/* Load up configuration record ID #10, set some new data, and write it. */
$config = new Config(10);
$config->setGroup('category')
  ->setName('category_depth')
  ->setValue(3)
  ->write();
 
/* Create an empty configuration object, load the data from an array, and then write a new record. */
$cfg = array('group' => 'category', 'name' => 'category_depth', 'value' => 3);
$config = new Config();
$config->loadFromArray($cfg)->write();

The write() method knows if the object is loaded or not (if the primary key of the object is set). If its loaded, an UPDATE command is issued, otherwise, an INSERT command is sent.

Surprisingly, the Config class is empty.

class Config extends Model {
}

All of the work takes place in the class Model. First, take the time to analyze the class, and then it will be broken up into individual pieces. The Doxygen comments in this class have been omitted to reduce the number of lines and to improve the readability.

abstract class Model {
  protected $_id = 0;
  protected $_pkey = NULL;
  protected $_model = array();
  protected $_table = NULL;
  protected $_methodCache = array();
  protected $_forceLoad = false;
 
  const TABLE_ROOT = 'artisan_';
 
  public function __construct($id=0) {
    $class = strtolower(get_class($this));
    $this->_table = self::TABLE_ROOT . $class;
    $this->_pkey = $class . '_id';
 
    $this->load($id);
  }
 
  public function __destruct() {
    $this->_id = 0;
    $this->_model = array();
  }
 
  public function __call($method, $argv) {
    $argc = count($argv);
    if ( 0 === $argc && true === isset($this->_methodCache[$method]) ) {
      return $this->_methodCache[$method];
    } else {
      $k = substr($method, 3);
      $k = strtolower(substr($k, 0, 1)) . substr($k, 1);
      $k = preg_replace('/[A-Z]/', '_\\0', $k);
      $k = strtolower($k);
 
      if ( 0 === $argc ) {
        /* If the length is 0, assume this is a get() */
        $v = $this->__get($k);
        $this->_methodCache[$method] = $v;
        return $v;
      } else {
        /* Else assume its a set with the first element of $argv. */
        $this->__set($k, current($argv));
        return $this;
      }
    }
  }
 
  public function __set($k, $v) {
    $this->_model[$k] = $v;
    return true;
  }
 
  public function __get($k) {
    if ( true === isset($this->_model[$k]) ) {
      return $this->_model[$k];
    }
    return NULL;
  }
 
  public function enabled() {
    if ( false === isset($this->_model['status']) ) {
      return true;
    }
 
    if ( 1 == $this->_model['status'] ) {
      return true;
    }
    return false;
  }
 
  public function getModelAsArray() {
    return $this->_model;
  }
 
  public function getId() {
    return $this->_id;
  }
 
  public function getPkey() {
    return $this->_pkey;
  }
 
  public function getTable() {
    return $this->_table;
  }
 
  public function setPkey($pkey) {
    $this->_pkey = $pkey;
    return $this;
  }
 
  public function setTable($table) {
    $this->_table = $table;
    return $this;
  }
 
  public function loadFromArray($array) {
    $this->_model = $array;
 
    if ( true === isset($array[$this->_pkey]) ) {
      if ( true === $this->_forceLoad ) {
        $this->load($array[$this->_pkey]);
      } else {
        $this->_id = $array[$this->_pkey];
      }
    }
 
    return clone $this;
  }
 
  public function write() {
    if ( $this->_id > 0 ) {
      $this->_update();
    } else {
      $this->_insert();
    }
    return $this->_id;
  }
 
  public function load($id) {
    $id = intval($id);
    if ( $id > 0 ) {
      $result = LN::getDb()->select()
        ->from($this->_table)
        ->where($this->_pkey . ' = ?', $id)
        ->query();
      if ( 1 == $result->numRows() ) {
        $this->_model = $result->fetch();
        $this->_id = $this->_model[$this->_pkey];
      }
 
      return true;
    }
 
    return false;
  }
 
  protected function _insert() {
    LN::getDb()->insert()
      ->into($this->_table)
      ->values($this->_model)
      ->query();
 
    if ( 1 == LN::getDb()->affectedRows() ) {
      $this->_id = LN::getDb()->insertId();
      return true;
    }
 
    return false;
  }
 
  protected function _update() {
    LN::getDb()->update()
      ->table($this->_table)
      ->set($this->_model)
      ->where($this->_pkey . ' = ?', $this->_id)
      ->query();
 
    if ( 1 == LN::getDb()->affectedRows() ) {
      return true;
    }
 
    return false;
  }
}

All of the members of the class are protected so they can be accessed in extended classes. This class assumes the primary key (ID) of the table is an integer. However, load() could be updated to assume its any data type.

The member variable $_pkey stores the name of the primary key of the table. It is automatically created by taking the name of the class and appending “_id” after it. It can be overwritten via setPkey().

$_model stores the actual data itself. It’s simply a key/value array with each key corresponding to a field of the table. $_table is the name of the table being worked on, and is automatically determined based on lowercasing the name of the class. Thus, if the class was named Product_Description, the value of $_table would be product_description and the value of $_pkey would be product_description_id.

$_methodCache stores a list of get*() methods and their resulting values for quick lookups later in __call(), and finally $_forceLoad causes the loadFromArray() method to load the data freshly from the database, regardless of if the data already exists in the model array.

The constructor takes an optional argument for the ID of the object to load. After the default table and primary key are established, the data is loaded (if possible). The loading takes place in load() which attempts to load the data based on the primary key.

load() is public in the case that it needs to be called if the primary key or table are changed.

loadFromArray() takes an array of key/value pairs and loads it into the object. This is useful if a list of objects is needed from a single query. For example, one could select all configuration options, and set them to a list of Config objects with a single query.

$config_list = array();
$config = new Config();
$result_config = LN::getDb()->select()->from('config')->query();
while ( $cfg = $result_config->fetch() ) {
  $config_list[] = $config->loadFromArray($cfg);
}

Because each $cfg contains the primary key of the Config object (`config_id`) the object will be fully loaded and the $config_list array will contain a list of Config objects. Note: loadFromArray() returns a clone of $this. This ensures each element of $config_list will have a new Zend refcount value and will be an entirely different object.

The other methods, excluding __call() are fairly evident in their purpose. __call() is where a lot of the work takes place, so understanding it fully is crucial.

__call() is a Magic Method in PHP, meaning it is present in classes and is called silently if defined. It takes two arguments, the name of the method called and the arguments passed to that method.

__call() is executed in the case that a method that isn’t written as a member of the class is called. If __call() doesn’t exist in the class and a method is called that doesn’t exist as well, the normal error handling routines will throw the appropriate warning.

As a result, any object can now easily handle the get*() and set*() methods that are suitable only for that object.

The full name of the method, without the () is the value of $method in __call(). First, the value is checked against the $_methodCache array. If its found, that value is returned immediately and no extra processing is required.

However, if the call is a set*() type, or a get*() type that hasn’t been cached, further processing is necessary.

Fortunately, “get” and “set” are both three characters, so they are stripped off. Next, the first letter of the string is lowercased. Thus, “setModuleName” becomes “moduleName”. With the power of a simple regular expression, uppercased characters have an underscore appended before them. “moduleName” becomes “module_Name”. Finally, the entire string is lowercased, becoming “module_name”. This is the key that will be used to get or set a property of the model.

If there are no arguments, the class assumes this is a get*() method, and if that key corresponds to a value in $_model, the data is returned. It is also at this point that the method and the data is cached for future lookups.

If there are 1 or more arguments, a set*() method is assumed, and all but the first arguments are discarded. The first argument is the new value of the property, and it is set. $this is returned for chainability.

This simple class is very powerful for handling database tables easily. As shown earlier, setting up a class to handle global configuration data is simple.

Keep in mind, the ActiveRecord class described defines a 1:1 relationship between a record and its representation in your code. It should not represent a list or multiple objects. A separate class, or an Iterator is required to handle that case.

It should be evident where successive parts of this article series will go. An issue to resolve are classes that need to load/modify multiple other objects. For example, a Product class may need to load a list of Prices, Images, and Attributes. Each of these are separate tables, and thus have a separate class to handle them, however, in a read-only system (such as the front end of a shopping cart), knowing about a Product_Price is fairly meaningless without the context of the Product. Thus, it is the responsibility of the Product object to manage the Product_Price object.

The next article will cover using a separate recorder adapter to save the data in a different location, and to abstract the saving of data away from the Model object itself. From there, relationships like the scenario described above will be closely examined.

Subscribe to Leftnode’s RSS feed to read more about this topic in the upcoming days!

Developers and Marketing 2

Posted by Vic Cherubini on October 10, 2009

I recently attended a conference put on by the TechExecs Network for the CIO Invitational Forum. The conference itself was very interesting, and I met some nice people there. One of the topics brought up by the main speaker was Marketing. Marketing has always left an odd feeling in my stomach, as there’s so many ways to be disingenuous about it.

The late comedian Bill Hicks had something to say about this:

By the way, if anyone here is in advertising or marketing, kill yourself. Thank you, thank you. Just a little thought. I’m just trying to plant seeds. Maybe one day they’ll take root. I don’t know. You try. You do what you can. Kill yourselves. Seriously though, if you are, do. No really, there’s no rationalization for what you do, and you are Satan’s little helpers, OK? Kill yourselves, seriously. You’re the ruiner of all things good. Seriously, no, this is not a joke. “There’s gonna be a joke coming…” There’s no fucking joke coming, you are Satan’s spawn, filling the world with bile and garbage, you are fucked and you are fucking us, kill yourselves, it’s the only way to save your fucking soul. Kill yourself, kill yourself, kill yourself now. Now, back to the show.

“You know what Bill’s doing now, he’s going for the righteous indignation dollar, that’s a big dollar, a lot of people are feeling that indignation, we’ve done research, huge market. He’s doing a good thing.” Goddammit, I’m not doing that, you scumbags, quit putting a goddamn dollar sign on every fucking thing on this planet!

I first heard that when I was an impressionable youth, but part of it resonated with me. There’s so much scummy marketing in the world, it should make any developer with the slightest sense of integrity insane. Whether its a cigarette company advertising its product to impressionable kids, a company attempting to take advantage of an elderly person’s life savings, or an insurance company trying to show you it cares about your health, and then denying you benefits for something you’ve paid into for 30 years, all of it is the worst, most despicable use of technology and creativity ever.

Thus, the question for any like minded developer becomes, “how to manage my disdain for disingenuous advertising, and still wanting to sell my product?” Fortunately, as creative software developers, there are ways to do it. Furthermore, companies do it all the time, do it well, and it actually benefits others!

Recent media darling Mint.com just sold for around $170 million dollars after only 2.5 years since their launch. Thats pretty impressive, and just about the best example of overnight success as you could get. Everyone loves Mint.com, and they should! They’re an honest to goodness great company that helps people. You couldn’t ask for more in a company. And they got that way by marketing to you.

How? Through their extensive blog, their beautiful, dazzling infographics on the economy and money, and by providing a great service. All are examples of marketing and advertising, but all are accepted, and even requested, by the community.

Mint.com isn’t the first or only company to do this, of course. 37Signals is very well liked, by both programmers and companies. As a result of their work, they’ve created jobs (and millions of blog posts) for up and coming programmers. Subtle as it may be, giving your software away for free as Open Source is certainly a great way to market yourself.

The point is, its entirely possible to market your product in a responsible, and even helpful way. As a developer, its your task to find these (and new) ways to market your product to try to bring some of the ridiculous marketing back to Earth.

PS. I know, a company blog is only one way of marketing, but I try to do it responsibly.

Introducing the Artisan System SDK 2

Posted by Vic Cherubini on October 07, 2009

While the content of this article is accurate, the Git repository is no long valid as of Feb. 21, 2010

I released the Artisan System Framework in November 2008. I was very excited when it released, got some good feedback, got some bad feedback, but was very happy with my work. It went through about 8 months of development, and was rewritten twice. Ultimately I derived a lot of pleasure from releasing my first open source product.

With the 0.4 release coming in the next week, I felt it was appropriate to launch the SDK, or software development kit, so developers can become quickly acclimated with how the framework operates. The feedback I received from explaining how the Model-View Controller works in Artisan System was that it wasn’t as intuitive as I had thought. Rather than updating the post, I decided the best way to show its intuitiveness would be to release the SDK.

Because Leftnode is moving to git for some projects, I decided to release the SDK on github.com. You can find it at http://github.com/leftnode/artisan-sdk. It includes version 0.3beta of Artisan System, but will be upgraded to 0.4 when it is released.

The default SDK includes all of the code necessary to build a small website. It includes database access (for the contact form mail page), the Model-View Controller, and a global static class for passing objects between each other.

Installation is very simple.

  • Clone the repository in a web-serveable directory
  • Create a new MySQL database named `artisan` and run the artisan.sql file against it.
  • Copy configure.template.php to configure.php
  • Open configure.php in your editor
  • On a development server, set DEBUG_MODE to 1, on a live server, set it to 0.
  • Set the value for the DIR_ROOT define for the root location. Essentially, this is the directory where index.php resides.
  • Set the keys server, username, password, and database in the $config_artisan_db variable to your appropriate database settings.
  • Set the values of site_root and site_root_secure in the $config_artisan_router variable.
  • If your web server has mod_rewrite (or similar) enabled, keep rewrite in $config_artisan_router as true, otherwise, set it to false.
  • Set the email_list value in $config_form to an array of email addresses the form should submit to.

This concludes the installation. Currently, it is done manually, but an automatic installation will be available in the future.

To access the initial site, if you have http://localhost/ set up properly, you can go to http://localhost/directory/ where directory is the name of the directory you placed all of the files in.

After you’ve verified the installation worked properly, and navigated through the small website, we’ll discuss the overall architecture of the SDK.

Application Directory Structure

The first directory you should pay attention to is app/. Open it, and you’ll see two sub-directories, Page/ and Root/. In Root/, you will see one PHP file, Root.php and a directory, View/. Opening Root.php shows a small class file.

<?php
 
class Root_Controller extends Artisan_Controller {
  protected $_layout = 'index';
 
  public function renderLayout($view) {
    $this->view->css_artisan = DIR_CSS . 'artisan.css';
 
    $this->render('root/header', 'header');
    $this->render('root/menu', 'menu');
 
    $this->render($view, 'body');
 
    $this->render('root/footer', 'footer');
  }
 
  protected function _redirect($url) {
    header("Location: " . $url);
    exit;
  }
}

This is the root Controller class. All Controllers in Artisan System must extend from the class Artisan_Controller. Because this Root_Controller extends Artisan_Controller, all subsequent Controllers will extend it. All Controllers must also be named {Name}_Controller, where {Name} is the actual name of the Controller.

All Controllers reside in a central directory, with sub-directories corresponding to each Controller.

app/
  Root/
    Root.php
    View/
      header.phtml
      footer.phtml
  Index/
    Index.php
    Model/
      Index.php
    View/
      index.phtml
      alternate.phtml
  Account
    Account.php
    Model/
      Account.php
    View/
      account.phtml
      update.phtml
      friends.phtml
      contacts.phtml
  Profile
    Profile.php
    Model/
      Profile.php
    View/
      profile.phtml

With this structure, any Controller can load a View of any other Controller. Additionally, the file in the Model/ directory handles the validation rules for different sections of each Controller.

Rendering Views in a Controller is easy with the render() method. It takes two optional paramters, $view_file and $content_block. $view_file defaults to the name of the method you’re calling with the URL (see URL Routing below), and $content_block defaults to no content block, meaning the view is rendered directly to the output stream.

By passing a full path for the $view_file parameter in render(), it will load up that View file within that Controller directory. If no path is passed, the View file from that Controller directory is loaded.

Because an application does not know of the concept of a header or footer (or other repeated content on the page), the Root_Controller was added. Each subsequent Controller extends Root_Controller, and every method in that Controller would call $this->renderLayout() rather than render() directly. As a result, the header, menu, and footer Views are always loaded in. Without this, one would have to load in each of these at every method. Backing up a directory and opening to Page/Page.php shows how this works.

<?php
 
require_once 'app/Root/Root.php';
 
class Page_Controller extends Root_Controller {
  // ##### POST Methods ##### //
 
  public function contactPost() {
    $form_id = intval($this->getParam('form_id'));
    $contact = $this->getParam('contact');
 
    try {
      $contactor = Artisan::getContactor();
      $contactor->setFormId($form_id)->setFieldList($contact)->send();
 
      $this->contactGet();
    } catch ( Artisan_Exception $e ) {
      // Determine what to do...
    }
  }
 
  // ##### GET Methods ##### //
 
  public function indexGet() {
    $this->renderLayout('index');
  }
 
  public function contactGet() {
    $this->renderLayout('contact');
  }
 
  public function aboutusGet() {
    $this->renderLayout('aboutus');
  }
 
  public function servicesGet() {
    $this->renderLayout('services');
  }
}

Because Page_Controller extends Root_Controller, it can call renderLayout() with the name of the View to load. Focusing on the *Get() methods for now, you see how simple they are. The method indexGet() loads up the index.phtml View, which is found in app/Page/View/.

Library Files

In the lib/ directory, you’ll see on sub-directory, Artisan/ and two PHP files. The Artisan/ sub-directory contains the entire Artisan System framework. Artisan.php is a static class that serves as a simple entry point into the system. Most of the methods within here could be combined into a single chunk of code in index.php, however, by putting them in separate static methods in a single class, they can be loaded in different areas of an application, making deployment easy and avoiding globalizing variables. Finally, Contactor.php is a simple class for managing a Contact Us form.

URL Routing

Controllers are broken into different methods that are accessible through the URL. With mod_rewrite turned on, URL’s look like: http://website.com/controller/method/arg1/arg2/argN. The first value after the base URL is the name of the Controller to load. Next, is the method within that Controller. Finally, any additional values, separated by forward slashes, are passed in as arguments to the method. Which method is called in the Controller is determined by the request type. GET requests route to methodGet(), and POST requests route to methodPost(). Thus, going to http://website.com/account/update would attempt to load Account_Controller::updateGet(), while posting a form to that same URL would attempt to load Account_Controller::updatePost().

Public Facing Files

Navigating to the root directory and then to public/ shows several sub-directories: css/, image/, layout/, and locale/. The names should be relatively intuitive as to what they do. One note: layout/ holds the different layout files. Layout files are what are loaded automatically after a view is rendered if the $_layout property of the Artisan_Controller class is defined. Because it is defined as ‘index’ in the code above, the index.phtml file is loaded. This small file is parsed by the Controller itself, with each content block being rendered to the appropriate area.

Download

You can download the initial release of the Artisan System SDK using Git from github.com. The public clone URL is git://github.com/leftnode/artisan-sdk.git

Model-View Controller in Artisan System 1

Posted by Vic Cherubini on October 02, 2009

This is a valid article, and is considered technically accurate up to Feb. 21, 2010

When Artisan System was first started, I had a vague idea of what the Model-View Controller pattern (MVC) was. As a result, the initial Artisan System framework had a hacked together inefficient MVC layout. It was burdensome and unintuitive. After researching more as to what MVC really is, I rewrote it from scratch. Twice. Now, the latest version of Artisan System, 0.4, has a complete MVC engine that’s efficient and very easy to use.

Models, Views, and Controllers

Finding an easy to understand idea of what MVC is was hard to do. Most contained the original layout of the idea proposed by Xerox in the 1970’s. The idea has changed since then, especially for web development. The overall idea of MVC is to separate business logic (what is done with the data) with the display logic (how the data is viewed).

Your model is your data. It can come from anywhere: flat files, XML, a database, a key/value store. The view is what determines how the data will be displayed. Views can generally have some logic in them, but it should be entirely confined to displaying data. A simple example would be to display a different greeting depending on a user type.

<?php if ( $user->type == USER_TYPE_BUSINESS ): ?>
  <?php echo _('Welcome to the website, Business User.'); ?>
<?php else: ?>
  <?php echo _('Welcome to the website, Consumer User.'); ?>
<?php endif; ?>

The Controller determines what to do with the data sent to it, how it affects the Model, and how to render the data in the View. When broken into their individual terms, understanding the MVC pattern becomes quite clear.

Artisan System contains a series of mostly abstract classes to help define each sub-section of MVC. An abstract Artisan_Controller class is meant to be extended for each of a websites controllers. Each controller has an Artisan_View object as a member. This object is what is responsible for rendering one or more views associated with a Controller. Finally, each Controller has an extended Artisan_Model object that defines validation rules for any forms associated with that View. Generally, each view should have a single form, if any, but multiple forms per View are easily supported.

On the filesystem, this is laid out intuitively as well.

application/
  Root/
    View/
      header.phtml
      menu.phtml
      footer.phtml
    Root.php
  Index/
    Model/
      Index.php
    View/
      about.phtml
      index.phtml
    Index.php

The main files in each sub directory are the Controller files, the file in the Model directory is the Model validation file, and all of the files in the View directory correspond to a View. Because they are .phtml files, the views themselves can contain PHP code. The alternative PHP syntax should be used for them, as it is a built in templating language and much more suited for integration with HTML.

Routing and Redirection

As of this writing, the Controllers are URL based. This means the URL specifies which Controller and View to load. In a typical Artisan System application, the index.php file is very small and handles this.

<?php
 
require_once 'Artisan/Controller/Router.php';
 
$config_artisan_router = new Artisan_Config_Array(array(
  'site_root' => 'http://www.example.com',
  'site_root_secure' => 'https://www.example.com/',
  'root_dir' => 'application',
  'layout_dir' => 'layout',
  'default_controller' => 'Index',
  'default_method' => 'index',
  'default_layout' => 'index',
  'rewrite' => true
  )
);
 
$artisanRouter = new Artisan_Controller_Router($config_artisan_router);
echo $artisanRouter->dispatch();
 
exit;

Basic URL’s without using any rewrite’s are in the form: index.php?u=controller/view/arg1/arg2/argN

Thus, if the $_REQUEST variable u is used for anything, the application will not work. In this example, the value passed to u is exploded based on the slashes. The first value is the name of the Controller to load, the next is the name of the method within that controller to load. Any values after the frist two are passed in as arguments to that method.

Thus, the URL index.php?u=profile/update/1 called through a POST request will cause the following actions to occur.

  1. Profile/Profile.php is loaded
  2. Profile_Controller::updatePost() is found
  3. Profile_Controller::updatePost(1) is called (albeit, not statically)

If the request was made by GET, the method updateGet() would be used instead.

If the method is not found, an exception is thrown, which can be handled by the router.

Because each Controller extends the base Artisan_Controller class, they can easily render the view associated with that Controller. By default, the Controller will attempt to render a view of the same name as the Controller, however, its generally best to specify one.

<?php
 
class Index_Controller extends Artisan_Controller {
  public function indexGet($page) {
    $page_data = load_page_data($page);
    $this->page_data = $page_data;
    $this->render('index');
 
    return true;
  }
 
  public function updatePost($page) {
    $data = $this->getInput('page');
    update_page_data($page, $data);
 
    $this->redirect('index');
  }
}

The above code is generic in nature, and obviously the methods load_page_data() and update_page_data() need to be defined, but it gives a general layout for a controller.

In future revisions, the routing will remain URL based by default, but can easily be overwritten with a rewrite table.

By default, the controller will render that data directly to the browser. This is useful for simple AJAX requests where one simply wants basic HTML, JSON, or XML returned. However, on non-AJAX requests, the full HTML page needs to be rendered. Artisan System makes use of Layouts and Content Blocks to facilitate this.

Layouts and Content Blocks

Artisan System uses a layout file to handle the final layout of a page. The layout file is mainly intended for designers to be able to re-theme an application easily.

Layout files are .phtml files as well, but live in a directory named layout (by default). Each has a unique name and is referenced by the Controller calling it. Thus, each Controller could have a different layout, although its generally not necessary.

The root Artisan_Controller class defines a protected member variable $_layout. The value of this variable is the name of the layout file, minus the extension, to be loaded. Additionally, a method, public Artisan_Controller::setLayout($layout) is available to set the layout without having to reference the variable directly. By default, no layout is set, and thus each view is rendered directly to the browser. However, the render() method of the Artisan_Controller class takes an optional second parameter, the name of the content block to render to. After the view is rendered to a specific content block, that block can be loaded in the layout (or another view).

<?php
 
class Index_Controller extends Artisan_Controller {
  protected $_layout = 'default';
 
  public function indexGet() {
    // $title is now a variable that can be used directly in the view.
    $this->title = _('Welcome to my site!');
 
    // Renders the index.phtml view to the 'body' content block.
    $this->render('index', 'body');
    return true;
  }
}

The index.phtml view is found in application/Index/View/ and is very straightforward.

<?php echo $title; ?>
 
<p>Thank you for taking the time to read my website.</p>

However, because this method renders to a specific content block, body, nothing will be displayed until the appropriate layout is loaded. The loading and rendering of the layout file happens automatically by the Artisan_Controller file in the load() method.

The layout file, default.phtml is located in public/layout and is equally as straightforward.

<!doctype html>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <title>Welcome to my Site!</title>
</head>
<body>
 
<?php echo $this->getBlock('body'); ?>
 
</body>
</html>

The default.phtml layout file now loads the body content block and the resulting concatenation of both is rendered to the browser.

Conclusion

Separation of content from design from logic was always a difficult design decision to make. However, with Artisan System, this issue is effortless and intuitive.

PHP5 Database Iterators Performance 1

Posted by Vic Cherubini on October 01, 2009

This is a valid article, and is considered technically accurate up to Feb. 21, 2010

The previous article about introducing PHP5 Database Iterators received a lot of good discussion, particularly on Hacker News. Whenever an article generates a lot of discussion (the link had a 2:1 ratio of comments to upvotes), a great deal is learned and passed on. Rather than responding to each individual comment, I decided to write an entire blog post. This article will be primarily focused on performance, both in speed and space complexities.

One concern was the overhead of having an entire class devoted to simply looping through a result from the database. If the Db_Iterator class simply did that, it would be hard to argue my point. However, careful analysis of what the iterator actual does proves it to be both faster and more memory efficient.

Result Sets

An important concept in querying a database is the result set that is returned. When a query is executed against a database (MySQL will be used for this article), it returns a pointer to the first result in the set.

<?php
 
$db = mysqli_connect('localhost', 'username', 'password', 'iterator_test');
 
$sql = "SELECT * FROM `user` u WHERE u.email_address LIKE '%@gmail.com%'";
$result_set = mysqli_query($db, $sql);
 
var_dump($result_set);
 
mysqli_close($db);

This small script shows that the $result_set variable is of type mysqli_result.

object(mysqli_result)#2 (0) {
}

When PHP passes the query to MySQL and data is returned, the data is oriented into sets of rows. The result set points to the beginning row. (If the query is not of type SELECT, $result_set is boolean of value true on a successful query, false otherwise). Further investigation of the PHP source code describes what is behind the actual result set.

PHP Internals

Download the latest 5.2.x release (5.2.11 at the time of publication) and un-tar it to a local directory. In your editor, open up the file ext/mysqli/mysqli_nonapi.c +232. All of the following assumes a basic SELECT query that returns more than 1 rows and everything executes properly.

In PHP, new functions are generally registered through the preprocessor directive PHP_FUNCTION(). The argument to the directive is the name of the function, in this case mysqli_query.

The first 20 or so lines are not incredibly important, they just set up some local variables and do some error checking. Line 255 fetches the connection resource into the local mysql_link variable. Line 259 performs the actual query. If everything goes well, and the query is of type SELECT, the result_mode is checked. Because mysqli_query() takes an optional third parameter for the result mode, and none is specified, MYSQLI_STORE_RESULT is used by default. Thus, the method, mysql_store_result() is called, passing in the result set from MySQL.

Lines 284 through 287 is where the result set is created and returned.

mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
mysqli_resource->ptr = (void *)result;
mysqli_resource->status = MYSQLI_STATUS_VALID;
MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);

mysqli_resource->ptr is the result set pointer to the first row returned. This pointer to the first row returned is what allows iterating (whether through a normal while() loop, or through an iterator) to happen. During each mysqli_fetch_assoc() (or similar method call), the pointer is adjusted to the next row, until no more rows are found, and NULL is returned. The directive MYSQLI_RETURN_RESOURCE() is called, returning the resource, which is passed into the above PHP script as the variable $result_set.

Back to PHP

The usefulness of this should be evident: rather than returning an array of all the rows found, a pointer to the first matched row is returned. This pointer is then stored in the Db_Iterator class.

/* Assuming the above query was executed properly. */
$iterator = new Db_Iterator($result_set, new User());
 
/* Iterate through a list of User objects now. */
foreach ( $iterator as $user ) {
  /* Do whatever with $user. */
}

The memory footprint of this result set pointer is tiny compared to storing all rows matched as an array. Many queries can easily return hundreds of thousands or millions of rows. Storing each of these, even without using an object for each element, is incredibly inefficient, both in space and time complexity. The space complexity is many times as large because an array of N elements must be created to store each result.

Another nice feature of using this pointer idea is being able to easily jump from row to row by referencing it’s index.

<?php
 
$db = mysqli_connect('localhost', 'username', 'password', 'iterator_test');
 
$sql = "SELECT * FROM `user` u WHERE u.email_address LIKE '%@gmail.com%'";
$result_set = mysqli_query($db, $sql);
 
/* Jump to the 11th actual row, row 0 points to the first matched row. */
mysqli_data_seek($result_set, 10);
 
while ( $row = mysqli_fetch_assoc($result_set) ) {
  echo $row['email_address'] . '<br>';
}
 
mysqli_close($db);

The useful method, mysqli_data_seek() allows the programmer to easily jump to a specific row, similar to jumping to an array index (which, in C, is syntactic sugar for pointers).

Race Conditions with SELECT’s

What happens when a row that is matched in a result set is updated by another query? For example, say one wants to fetch all comments by a specific user. The query executes successfully, and returns a result set to PHP. Because that result set is a reference to rows, and not the rows themselves, another query could easily come and update (or delete) one of those matched rows resulting in inaccurate results. Fortunately, the database ensures that the rows matching a result set have the values at query time returned specifically. This occurrence, known as a race condition in multithreaded applications, is quite common, and a source of many developer frustrations. However, MySQL handles it for the developer.

Conclusion

Understanding PHP’s internals is necessary for every developer. It can give helpful insights as to better design your programs. One can think of a result set as a pointer in C. While it doesn’t create memory on the heap, it does allow for easy navigation through a list of elements.