Crafty Documentation

Following, is a single page document covering the usage and the philosophy behind using Crafty.

Document contents

Introduction

One of the biggest problems with software source code stems from the degree of coupling between modules or components. Coupling means that one single piece of code cannot easily exist without one or more other pieces of code accompanying it. The degree of coupling relates to how tightly one unit of code relies on other units.

Examine the following, perfectly logical sample of code:

<?php

class MySqlDb {
  public function __construct($username, $password, $host) {
    // .. snip ..
  }
  public function executeSql($sql) {
    // .. snip ..
  }
}

class BookReader {
  private $_db;
  public function __construct(Db $db) {
    $this->_db = new MySqlDb(DB_USER, DB_PASS, DB_HOST);
  }
  public function getChapters() {
    return $this->_db->executeSql('SELECT name FROM chapter');
  }
}

$book = new BookReader();
$chapters = $book->getChapers();
 

Obviously it's not a real-world example but there's nothing inherently wrong with the code (except maybe the placement of the credentials), it's just got one significant weak point - tight coupling! The BookReader class uses a MySqlDb which is fair enough, but the fact that the BookReader class uses that Db class means you need to take the same Db everywhere the BookReader class goes. With a few lines of code like this the impact is negligible, but fill an application with code like this and you end up with a complete nightmare. There may come a time when you want to take a small component to use in another application. As a result you'd end up having to take a significant portion of your code base simply to use that small portion. This is the real issue with tight coupling.

If your BookReader needs a Db then you'll never completely relieve coupling all together, but you can certainly bring it down to a significantly weaker level where all the BookReader knows is how to use a Db and not specifically what the Db is.

Take a look at the following modification to the above code:

<?php

interface Db {
  public function executeSql($sql);
}

class MySqlDb implements Db {
  public function __construct($username, $password, $host) {
    // .. snip ..
  }
  public function executeSql($sql) {
    // .. snip ..
  }
}

class BookReader {
  private $_db;
  public function __construct(Db $db) {
    $this->_db = $db;
  }
  public function getChapters() {
    return $this->_db->executeSql('SELECT name FROM chapter');
  }
}

$book = new BookReader(new MySqlDb(DB_USER, DB_PASS, DB_HOST));
$chapters = $book->getChapers();
 

All we've done above is moved the instantiation of MySqlDb outside of the class which depends on it. We did throw in the Db interface by means of constraining the behaviour in the API which is passed to it. The interface is optional however; what is important is the movement of the MySqlDb instantiation away from the internals of the BookReader class. This is what we term "constructor based dependency injection".

The advantages may not be immediately obvious but they are huge. You can move the BookReader class without the MySqlDb. Granted, you'd need some sort of a Db, but you can provide one that fits your needs more appropriately (maybe an SQLite one?).

Another approach you can take when injection dependencies is to use a a setter method. This is generally less desirable until the number of dependencies reaches too many to reasonably pass to a constructor.

<?php

interface Db {
  public function executeSql($sql);
}

class MySqlDb implements Db {
  public function __construct($username, $password, $host) {
    // .. snip ..
  }
  public function executeSql($sql) {
    // .. snip ..
  }
}

class BookReader {
  private $_db;
  public function setDb(Db $db) {
    $this->_db = $db;
  }
  public function getChapters() {
    return $this->_db->executeSql('SELECT name FROM chapter');
  }
}

$book = new BookReader();
$book->setDb(new MySqlDb(DB_USER, DB_PASS, DB_HOST));
$chapters = $book->getChapers();
 

That's it, you've seen it now; this is dependency injection. Easy to do manually right? True. But tedious when you've got complex classes which have dependencies and those dependencies each in turn have their own dependencies. This is where Crafty comes in. All Crafty does is pulls all knowledge about these dependencies into one location. It saves you from having to do all that injection manually and as a result it encourages you to write really flexible, loosely-coupled classes.

Because Crafty's job is simply to handle all your dependency injection requirements, you can stop using it quite happily and just go back to those tedious old days when you did it all manually. Your classes know nothing about Crafty, Crafty knows about your classes.

Unpacking the archive

The archive you downloaded will either be a GZipped tarball (.tar.gz) or a Zip archive (.zip). Once you extract this file you will get a new directory created named Crafty-XYZ where XYZ is the version number of Crafty you got. Inside this directory will be a small collection of sub-directories. The most significant directory here is the lib directory which contains the class files to run Crafty. Upload this entire directory to your server in a place which is accessible to the PHP scripts that will use it.

Dependency Maps

The way you tell Crafty what to inject and where to inject it is through something called dependency maps. Dependency maps can take various forms (you choose a format) but they all provide the same information. A map will contain information on:

  • Class names
  • Constructor depedencies (or values)
  • Setter dependencies (or values)
  • Object scope (shared or not)

With the exception of the programmatic mapping using PHP, these mappings are read by something called ComponentSpecFinders in Crafty. You need to load a ComponentSpecFinder for the relevant mapping:

$crafty->registerSpecFinder('xml',
  new Crafty_ComponentSpecFinder_XmlSpecFinder('/path/to/map.xml')
  );

XML based mapping (recommended)

Let's take a look at our BookReader/MySqlDb example's XML dependency map, carefully reading the descriptive comments from top to bottom.

<?xml version="1.0" ?>
<components>
 
  <!-- Each component has a component block -->
  <component>
    <!-- ... and a name -->
    <name>db</name>
    <!-- ... and a className -->
    <className>MySqlDb</className>
    <!-- Constructor based injection is done with the constructor tag -->
    <constructor>
      <!-- Each argument has an arg tag -->
      <arg>
        <!-- ... and it's value has either a value, componentRef
          or collection element -->

        <value>the-username</value>
      </arg>
      <arg>
        <value>the-password</value>
      </arg>
      <arg>
        <value>the-hostname</value>
      </arg>
    </constructor>
    <!-- Shared instances mean all components reference the same object -->
    <shared>true</shared>
  </component>
 
  <component>
    <name>bookReader</name>
    <className>BookReader</className>
    <constructor>
      <arg>
        <!-- componentRef elements refer to another component (dependency) -->
        <componentRef>db</componentRef>
      </arg>
    </constructor>
  </component>
 
</components>
 

The above XML sample shows us the mapping we'd write for the constructor-based injection example seen earlier. You see the two components for BookReader and MySqlDb declared as <component> blocks, and the use of <componentRef> to show where dependencies lie.

The code also shows the use of the <shared> tag which makes the database become a sort of singleton whereby the same instance is accessed by all other components which use it. This is a common design pattern for database access in order to save on overheads.

Now let's take a look at the same map but using setter-based injection.

<?xml version="1.0" ?>
<components>
 
  <!-- ... snip ... -->
 
  <component>
    <name>bookReader</name>
    <className>BookReader</className>
    <!-- All object properties are contained in the properties element -->
    <properties>
      <!-- Each property has it's own tag -->
      <property>
        <!-- Where the key translates to the setter method setKey(), or setDb()
          in this example -->

        <key>db</key>
        <!-- componentRef elements refer to another component (dependency) -->
        <componentRef>db</componentRef>
      </property>
    </properties>
  </component>
 
</components>
 

The code is almost identical to the constructor-based injection map. The only real difference being we've changed <constructor> to <properties>, <arg> to <property> and added the <key> tag. With setter-based dependency injection Crafy assumes a JavaBean-esque naming scheme with your setters. If you refer to a property called 'foo' then Crafty looks for the setter 'setFoo()'. Crafty doesn't care if the property actually exists; it just wants to use the setter.

The only tag which hasn't had a mention in our examples is the <collection> tag. The <collection> tag can be used as the value inside an constructor or property element. It simply creates an array in PHP, populated with the values you give it (<componentRef> or <value> tags). Imagine our BookReader could take an array of Db instances for redundancy:

<?xml version="1.0" ?>
<components>
 
  <!-- ... snip ... -->
 
  <component>
    <name>bookReader</name>
    <className>BookReader</className>
    <constructor>
      <arg>
        <!-- A collection just creates an array -->
        <collection>
          <!-- Any number of componentRef or value tags can be added -->
          <componentRef>db1</componentRef>
          <componentRef>db2</componentRef>
          <componentRef>db3</componentRef>
        </collection>
      </arg>
    </constructor>
  </component>
 
</components>
 

YAML based mapping

If you don't understand YAML there's probably not much point in reading this section without first reading this. I'll go through the YAML map for our examples in a less verbose manner than I did for XML because it almost entirely translates into the same structure.

#Start with a components key
components:
 
  #Each component has a named key
  db:
    # ... and a className
    className: MySqlDb
    #The constructor is defined by a constructor key
    constructor:
      #Each argument is a new unamed element
      # ... and each has a value or componentRef key
      - { value: the-username }
      - { value: the-password }
      - { value: the-hostname }
    #Shared instances have a shared key with a boolean true (or yes) value
    shared: true
 
  bookReader:
    className: BookReader
    constructor:
      - { componentRef: db }
 

The above shows our constructor-based injection example in YAML. Here's the setter-based version:

components:
 
  # ... snip ...
 
  bookReader:
    className: BookReader
    #All properties go under the properties key
    properties:
      #Each property has a unique key
      # ... and contains a value or componentRef
      db: { componentRef: db }
 

The availability of the <collection> tag in the XML version makes XML more expressive for Crafty compared with YAML. In YAML you just create a non-associative array to make a collection. It's harder to read though:

components:
 
  # ... snip ...
 
  bookReader:
    className: BookReader
    constructor:
      #Start a non-associative array with a single dash
      -
        # ... and nest in a level
        - { componentRef: db1 }
        - { componentRef: db2 }
        - { componentRef: db2 }
 

Array based mapping

Mapping with a standard PHP array takes the exact same structure as mapping with YAML. Quite literally.

<?php

$map = array(
 
  //Each component has a key (name)
  'db' => array(
    // ... and a className
    'className' => 'MySqlDb',
    //The constructor values have value or componentRef keys
    'constructor' => array(
      array('value' => 'the-username'),
      array('value' => 'the-password'),
      array('value' => 'the-hostname')
    ),
    'shared' => true
  ),
 
  'bookReader' => array(
    'className' => 'BookReader',
    'constructor' => array(
      array('componentRef' => 'db')
    )
  )
 
);
 

Programmatic mapping with PHP

This is the way Crafty interprets all the other methods. It uses objects to map out specifications. You can create the entire map using methods from the main Crafty_ComponentFactory class.

<?php

$crafty = new Crafty_ComponentFactory();

//Pass the name of the component as first param
$crafty->setComponentSpec('db',
  //Create a new component spec as second param
  $crafty->newComponentRef(
    //className      //constructor args                              //Props   //shared
    'MySqlDb', array('the-username', 'the-password', 'the-hostname'), array(), true
    )
  );
 
$crafty->setComponentSpec('bookReader',
  $crafty->newComponentSpec(
    //Use the referenceFor() method to refer to another component
    'BookReader', array($crafty->referenceFor('db'))
    )
  );
 

The above example shows the constructor-based injection example with our BookReader class. The setComponentSpec() method take two parameters; component name and component spec. To generate a component spec you use the newComponentSpec() method which takes parameters in this order: className, constructor, properties, shared. References to other components are specified by using the referenceFor() method.

Let's take a look at the setter-based injection example using PHP alone.

<?php

// .. snip ..

$crafty->setComponentSpec('bookReader'
  $crafty->newComponentSpec(
    //Provide properties as an associative array
    'BookReader', array(), array('db' => $crafty->referenceFor('db'))
    )
  );
 

Collections (arrays) are easily handled programmatically too by simply passing the array where it's needed.

<?php

// .. snip ..

$crafty->setComponentSpec('bookReader',
  $crafty->newComponentSpec(
    'BookReader', array( //Constructor
      array( //is collection
        $crafty->referenceFor('db1'),
        $crafty->referenceFor('db2'),
        $crafty->referenceFor('db3')
        )
      )
    )
  );
 

Programmatic loading of dependency maps does create objects which provide mapping data. Although Crafty uses this underlying implementation itself it only loads new maps from the original format (XML, YAML etc) as they are requested. Doing it manually will most probably use more resources in large systems where your dependency maps are large. I'd strongly advise sticking with an XML or YAML map.

Class Locators

Class locating using Crafty is entirely optional since you may already be using an autoloader or some other means of loading classes. Crafty does provide a basic class locating strategy however should you choose to use it.

Class locators must implement the Crafty_ClassLocator interface which includes two basic methods: classExists() and includeClass(). One is provided with Crafty for Zend's preferred class naming scheme -- PEAR naming.

To register a new class locator into Crafty it's a simple call.

<?php

$crafty->registerClassLocator('pear-loader',
  new Crafty_ClassLocator_PearClassLocator('./classes')
  );
 

You can register any number of class locators you like and Crafty will try them all in turn until one works, or all have been exhausted.

If the Pear style one provided does not fit your needs you'll need to write your own, but it's easy to do. Take the example where all classes reside in a single directory called 'classes' and are named className.class.php.

<?php

class DotClassLocator implements Crafty_ClassLocator {
  private $_path;
  public function __construct($path) {
    $this->_path = $path;
  }
  public function classExists($className) {
    return is_file($this->_path . '/' . $className . '.class.php');
  }
  public function includeClass($className) {
    require_once $this->_path . '/' . $className . '.class.php';
  }
}

$crafty->registerClassLocator('dot-class',
  new DotClassLocator('./classes')
  );
 

Instantiating Objects

Heck! If you're actually still reading this I take my hat off to you ;) All that XML, YAML and dependency map talk is completely useless to you if you don't know how to get objects back out of Crafty! Thankfully it's a painless procedure though.

Basic object creation

<?php

//Crafty_ComponentFactory contains a create() method
$bookReader = $crafty->create('bookReader');
 

The Crafty_ComponentFactory class has a create() method which takes the component name as an argument. This will create an instance of the class for that component, along with all its dependencies. Easy!

Overriding constructor parameters

If you need to override the defaults in your dependency map when you create your object there are two options: 1. pass an array to create()'s second parameter or 2. use the classOf() method.

<?php

//Using create()'s second parameter
$bookReader = $crafty->create('bookReader', array(new MyOtherDb()));

//Using classOf() -- preferred
$bookReader = $crafty->classOf('bookReader')->newInstance(new MyOtherDb());
 

create()'s second parameter takes an array like the dependency maps take, but if you use the classOf() method you can instantiate via Reflection (Crafty's underlying implementation) using newInstance() and passing a variable-length list of arguments (e.g. you can do $crafty->classOf('something')->newInstance($arg1, $arg2, $arg3...)).

Further notes (light reading)

I'll get around to writing some notes here eventually :P