Hooks in Zesk

What are hooks?

A hook is the ability to run code at a specific point in time during a program. Hooks in Zesk allow you to:

Hooks in zesk take a few forms, but ultimately are a combination of (we hope) the best worlds of Drupal function name space ease and Wordpress' registration and invocation system.

List of standard Zesk hooks

Autoloader and object self-registration

Any class which is loaded by Zesk's autoloader is automatically registered in Zesk's system and will have a static method called hooks called if it exists in the class.

So:

class MyClass {
    public static function hooks(zesk\Application $application) {
        $application->hooks->add("exit", array(__CLASS__, "onexit"));
    }

    public static function onexit(zesk\Application $application) {
        // Clean up some stuff
    }
}

This method should be used to register hooks for your class or object globally.

Registering your hooks

TODO

So, there are basically two places to register and call hooks, in zesk:

Hook naming

Call a hook

zesk()->hooks->call($hook, ...);
zesk()->hooks->call_arguments($hook, array $arguments, $default=null);

And in any object which inherits "Hookable" which ... is most of them:

/* @var $u User */
$u = User::instance();
if ($u->call_hook("allowed_request", $request)) {
    // fun stuff
}

You'll note that all hook methods tend to have two forms, one for ease, and one which allows for a default value to be returned when no hook exists:

/* @var $u User */
$u = User::instance();
if ($u->call_hook_arguments("allowed_request", array($request), true)) {
    // fun stuff
}

Hookable call order

Conceptually, a hook attached to an object implements hook_foo and then whenever the foo hook is called, the function gets invoked.

class Project extends Hookable {
    function hook_finish() {
        $this->clean_up_files();
        $this->put_away_resources();
        $this->zip_up_files();
        $this->move_to_archive_folder();
    }
    ...
}

Now, Captain Obvious would point out that "You can use regular PHP inheritance for that!" And Captain Obvious would be correct. However, the hook system allows for something which regular PHP inheritance does not allow:

Let's handle these each in order:

Interception of hooks on a per-object basis

You'll note that zesk\Hookable inherits from zesk\Options so it inherits the ability to set arbitrary options on any Hookable object. There's a special option called "hooks" which allows the user to define a hook to be called. You can then turn this on and off for a specific object if you wish:

$this->option_append("hooks", "delete", "project_deleted");

Then the method project_deleted will be called with our object upon deletion.

External access to internal hooks

Note as well that we can also TODO

Registration based vs. function and method name space

How do you get your hook called, you say? In one of three ways:

zesk()->hooks->call and zesk()->hooks->call_arguments are essentially just buckets where you can register your hook. Zesk global hooks are registration-based.

Object-hooks are method space declaration. Meaning if you create a method in your object which is named

class Foo extends Hookable {
    function hook_dee() {
        echo "Dee called";
    }
}
$foo = new Foo();
$foo->call_hook("dee");

Then your method will be called at the appropriate time.

The final mechanism allows for hooks to be registered as static class methods, and just by registering your class will you be able to be called at the appropriate place.

class Foo extends zesk\Module {
    static function add_to_cart() {
        echo "added to cart";
    }
}

$product = ORM::factory('Product')->fetch(23);
zesk()->hooks->call_all("Module::add_to_cart", $product);

Calls method "add_to_cart" in all classes of type "Module" (if the method exists)

Configuring and manipulating arguments and return values

Typically, calling a hook requires a list of arguments to be passed to each hook, and then each hook may optionally return a value.

Things to consider is that a single hook may be a single function, or multiple functions which are called in order.

The question is: How do we handle these cases for single, multiple hooks, and return values?

The hooks system is designed to handle common cases for this.

Return values

TODO