Documentation

Full reference documentation for DStruct can be found here.

Installation and Configuration

Before you get started, you may want to check out the requirements

DStruct needs to be one layer below your Document Root. Typically, this would mean placing the DStruct folder in a directory called 'lib', 'library', 'src' or similar. For the purposes of this documentation, we will be call our directory 'lib' and put DStruct in there. The files we will publish on the web will be in a directory called 'public'. An example structure is below:

[Document Root]
-->lib
   -->DStruct
      -->auth
      -->....
      -->tree
      -->inc_bootstrap.php
   -->SomeOtherPackage
   -->MyClassFilesEtc
-->public
   -->index.php

Next. make a copy of the clsPrefs.Template.php file in the prefs folder of DStruct and rename it clsPrefs.php. Open this in your preferred editor and take a look - all the configuration for DStruct is in this file. The only thing you need to do to start with is go to the constant APP_NAME and change it to something else representative of your project.

It is highly recommended that you change the constant APP_NAME as this identifies the application in caches etc and can save much confusion later on.

That's it. You're installed and ready to go.

Getting Started

<?php
// include the bootstrap at the top of each of your scripts (but not in your Class files etc)
require_once '../lib/DStruct/inc_bootstrap.php';

// the bootstrap does a number of things, including registering the autoloader
// ... our classes are now available without using require() etc
$myobj = new MyClass;

// This includes our static helper classes. Validate a UK Postcode:
$valid = Validate::isPostcode('W1C 8QT');
var_dump($valid); // = true
?>

The Autoloader and Writing Your Own Classes

The DStruct bootstrap registers an autoloader. If you want, the autoloader can be used to load your own classes.

To use the autoloader, you need to follow a few basic rules. Your classes need to be put in individual files which start with 'cls', then have the name of the class (case sensitive) and finally .php. So for example clsMyClass.php.

Your class needs to be put in a sub-directory of the directory in which you placed DStruct. So, for the example in the Installation and Configuration section, valid directories would include [approot]/lib/myclasses/ or [approot]/lib/mypackage/classes/ etc.

Finally, you need to include the path to your classes in the autoloader_directories array created in the Prefs::__construct() method. An example is shown below:

// in the Prefs::__construct() method, you could change this:
------------------
// Directories in 'lib' to be scanned by the autoloader should be put here
$this->props['autoloader_directories'] = array(
    'DStruct/dstruct_common',
    // snip ....
    'DStruct/tree'
);
------------------

// to this:
------------------
// Directories in 'lib' to be scanned by the autoloader should be put here
$this->props['autoloader_directories'] = array(
    'DStruct/dstruct_common',
    // snip ....
    'DStruct/tree',
    'mypackage/classes' // added path to project classes
);

The autoloader searches the directories for the class in the order they are listed in the array, so you can change the order to optimise for your particular application. The returns for doing this are limit though, as the results will usually be cached...

The results are also cached (if any default cache is available). If you change the paths to your files, or to DStruct, you may need to clear the cache to get the autoloader to find the files.

Connecting to a database

Database Access

Caching

DStruct uses caching where possible, so it is important to set up the cache options in your Prefs file correctly. It is also highly advised that you use APC on your server as this greatly increases the speed of PHP.

This is a simple example of how to use the cache object which is generated by the bootstrap to cache within your scripts:

<?php
// include the bootstrap at the top of each of your scripts (but not in your Class files etc)
require_once '../lib/DStruct/inc_bootstrap.php';

// the bootstrap does a number of things, including adding a default cache
$prefs = Prefs::getInstance(); // Prefs is a singleton class containing our preferences
$cache = $prefs->get('cache'); // Fetch the cache object created by the bootstrap

if ($cache->hasServer()) { // the cache is available
    $my_data = $cache->get('my_data'); // attempt to get data from cache
    if ($my_data == false) { // if cache did not already contain our data...
        $my_data = 'foobar'; // set our data
        $cache->set('my_data', $my_data); // put our data in cache for retrieval next time
    }
    echo $my_data; // do something with our data
} else {
    echo 'No cache available!';
}

?>

You are not limited to only using the default cache...

<?php
// include the bootstrap at the top of each of your scripts (but not in your Class files etc)
require_once '../lib/DStruct/inc_bootstrap.php';

// create a cache object manually...
$cache = APCCache::getInstance(); // APCCache impliments DStructCacheInterface, so has a standard set of methods

if ($cache->hasServer()) {
    // ... and so on like the previous example 

The ObjWatcher Class and Why You Should Use It

The ObjWatcher class tracks instances of objects in your system to prevent bugs which can be caused by multiple instances of the same object in your script.

In the the next example, we will show how a bug could be introduced into your system, and the example following will show how the ObjWatcher class can be used to prevent the issue. Finally, we will take a look at how the ObjWatcher was used.

Objects to be watched must have a method getID() which returns a unique ID for the object. Typically, this will be an integer, but could be a string.
<?php
// include the bootstrap at the top of each of your scripts (but not in your Class files etc)
require_once '../lib/DStruct/inc_bootstrap.php';

$a = Employee::loadByID(12); // load employee 12
$a->setName('Joe'); // set his name

// load employee 12 in a DIFFERENT object!
// we now have two objects with the same ID in the script.
$b = Employee::loadByID(12);

$b->setName('Brian'); // we haven't realised and set the name for employee 12 again.

// What is the correct name for employee 12???
// If, for instance, we saved the object to the database, the name for employee 12 could depend
// on the order in which we saved the objects.
$b->save();
$a->save();

// Let's fetch our data from the database again.
$c = Employee::loadByID(12);

// Name of employee 12 is now Joe, even though the last name we gave to the employee was Brian.
echo $c->getName(); // echos Joe
?>

Let's try that again, but this time, the loadByID() method will be using ObjWatcher.

<?php
// include the bootstrap at the top of each of your scripts (but not in your Class files etc)
require_once '../lib/DStruct/inc_bootstrap.php';

$a = Employee::loadByID(12); // load employee 12
$a->setName('Joe'); // set his name

// load employee 12 in a DIFFERENT object!
// In this example, loadByID is using the ObjWatcher and returns the SAME object (an object
// instance of class Employee with the ID of 12).
$b = Employee::loadByID(12);

$b->setName('Brian'); // Set the name for employee 12 again.

// As the objects are both the same object, the worst we will do by saving them is to do two
// identical saves to the database.
$b->save();
$a->save();

// Let's fetch our data from the database again.
$c = Employee::loadByID(12);

// The name is now the last one set for Employee 12, regardless of our objects.
echo $c->getName(); // echos Brian
?>

So, what was the difference between the classes? As we have seen, the difference is in the Employee::loadByID() method. Below is a closer look at the code for both methods.

// Original class method without ObjWatcher...
public static loadByID($id) {
    $obj = new __CLASS__; // create Employee object
    $obj->loadDataFromDatabase($id); // load data from the database
    return $obj; // return the populated object
}

// Method using ObjWatcher
public static loadByID($id) {
    // The first thing we do is check with the ObjWatcher whether there is an object of type Employee
    // with the id we are looking for. If we find it, just return that object
    if ($obj = ObjWatcher::exists(__CLASS__, $id)) {return $obj;}
    $obj = new __CLASS__; // If we didn't find that object, we create a new one
    ObjWatcher::add($obj); // and then add it to the ObjWatcher
    $obj->loadDataFromDatabase($id); // load data from the database
    return $obj; // return the populated object
}

The Shutdown Function and Error Reporting