How Can We Help?

Search for answers or browse our knowledge base.

< All Topics
Print

Introduction to Pope

This tutorial is currently outdated and is in the process of being updated

-

It's a pleasure to introduce Pope, our new PHP component framework for PHP 5.2 and above, and our engine for NextGEN Gallery 2.0 and Photocrati 5.0. It's loosely based around Python's Zope3, which is where Pope gets it's name (PHP's Zope). Pope provides the following:

  1. ExtensibleObject
    • Provides a means for developers to add/remove/override methods dynamically at runtime, even with PHP 5.2
    • Borrows the concept of Mixins and "duck typing" from the programming language Ruby
    • Run code before or after a call to a method (pre and post hooks)
  2. Component Architecture
    • The concept of adapters, utilities, factories, and components to PHP. More on that later...
    • A class autoloader.
    • A modular design. Functionality is provided via modules.
    • A component registry, which maps programmatic interfaces to a specific implementation.
    • A component factory, an out-of-the-box factory utility used to create objects

Pope was designed to be embeddable within other frameworks or platforms, such as WordPress.

A look at ExtensibleObject

This class, which brings a lot of dynamic power at runtime to PHP 5.2, is Pope's greatest asset, and the underlying tool for providing much of Pope's functionality. ExtensibleObject instances are defined at runtime - that is, their implementation, isn't known till your PHP script has started execution. The implementation of these instances are provided through the use of Mixins. Here's an example of how we'd construct an object using ExtensibleObject:

<?php
include_once('pope/lib/class.extensibleobject.php');

/**
 * Defines what a Greeter SHOULD be able to perform - or in otherwords, which methods a Greeter implementation has to provide
**/
interface I_Greeter
{
	function say_helo($name);
	function say_goodbye($name);
}

/**
 * Provides an English implementation of the Greeter
**/
class Mixin_English_Greeter extends Mixin
{
	function say_hello($name)
	{
		echo "Hi there, {$name}!";
	}

	function say_goodbye($name)
	{
		echo "See ya later, {$name}!";
	}
}

/**
  * Provides a Greeter utility
**/
class Greeter extends ExtensibleObject
{
	function define()
	{
		$this->add_mixin('Mixin_English_Greeter');
		$this->implement('I_Greeter');
	}
}

We've created a class called Greeter, which implements the I_Greeter interface. We then provide a implementation of the I_Greeter interface through the Mixin_English_Greeter class. We can perform the following with an instance of the Greeter class:

/** Create instance **/
$greeter = new Greeter();
var_dump($greeter->has_method('say_hello')); // bool (true)
var_dump($greeter->has_method('say_goodbye')); // bool (true)
$greeter->say_hello('Mike'); // Hi there, Mike!

Things start to get exciting when you start changing the implementation at runtime:

/**
 * Provide an alternative Greeter implementation
**/
class Mixin_Redneck_Greeter extends Mixin
{
	function say_hello($name)
	{
		echo "Howdy, {$name}!n";
	}
}

// Change the implementation, dynamically
$greeter->add_mixin('Mixin_Redneck_Greeter');
var_dump($greeter->get_mixin_providing('say_hello')); // string(21) "Mixin_Redneck_Greeter"
$greeter->say_hello('Mike'); // Hi there, Mike!

Mixins are meant to encapsulate your implementation of an interface in it's entirety. As such, a mixin can modify an object's state as well - the instance object is accessible through $this->object:

<?php

include_once('pope/lib/class.extensibleobject.php');

class Mixin_BankAccount extends Mixin
{
	function deposit($money)
	{
		$this->object->_balance += abs($money);
	}

	function withdraw($money)
	{
		$this->object->_balance -= abs($money);
	}

	function print_balance()
	{
		if ($this->object->_balance >= 0)
                    printf("$%.2f", $this->object->_balance);
		else
                    printf("$(%.2f)", $this->object->_balance);
	}
}

class BankAccount extends ExtensibleObject
{
	var $_balance = 0;

	function define()
	{
		$this->add_mixin('Mixin_BankAccount');
	}

	function initialize($balance)
	{
		$this->_balance = $balance;
	}
}

$account = new BankAccount(1000);
$account->withdraw(100);
$account->deposit(25);
$account->print_balance(); // $925.00

As the example shows above, you can access the instance object within the context of a mixin through $this->mixin. Additionally, the above demonstrates how ExtensibleObject uses the define() method to define the actual implementation of the object, and initialize() to initialize state.

Hooks

ExtensibleObject also implements the Hook n' Anchor pattern, as used in popular CMS frameworks such as WordPress. ExtensibleObject hooks are associated with method calls and are configured to be run before or after the method:

class Hook_Pre_Withdraw extends Hook
{
	function withdraw($money)
	{
		printf("Before withdrawing $%.2f, you had ", $money);
		$this->object->print_balance();
	}
}

class Hook_Post_Withdraw extends Hook
{
	function withdraw($money)
	{
		printf(". Keep that up, and you'll be broke in no time. Balance: ");
	}
}

$account = new BankAccount(1000);
$account->add_pre_hook('withdraw',  'Pre-hook Example', 'Hook_Pre_Withdraw');
$account->add_post_hook('withdraw', 'Post-hook Example','Hook_Post_Withdraw');
$account->withdraw(100);
$account->deposit(25);
$account->print_balance(); // Before withdrawing $100.00, you had $1000.00. Keep that up, and you'll be broke in no time. Balance: $925.00

Wrapping existing Objects

One of the most powerful capabilities of ExtensibleObject is it's ability to wrap existing classes which don't use ExtensibleObject, and extend them using the same features that ExtensibleObject provides. This is a great tool in your arsenal, for example, when you need to alter a third-party WordPress plugin which hasn't made use of apply_filters() or do_action(). Here's an example of how we use ExtensibleObject to override a private method of another class:

<?php

include_once('pope/lib/class.extensibleobject.php');

/*** NON EXTENSIBLEOBJECT CODE ***/
class Foobar
{
	private function foo()
	{
		echo 'foo';
	}

	private function bar()
	{
		echo 'bar';
	}

	private function foo_bar()
	{
		$this->foo();
		$this->bar();
	}
}

/**
 * Provides an implementation bar() even though it's a private method
**/

class Mixin_Foobar extends Mixin
{
	function bar()
	{
		echo 'Bar';
	}
}

class My_Foobar extends ExtensibleObject
{
	function define()
	{
		$this->wrap('Foobar');
		$this->add_mixin('Mixin_Foobar');
	}
}

$obj = new My_Foobar();
$obj->foo_bar(); // fooBar

A Component Architecture

In Pope, you have several constructs that are listed below. We'll then cover how to use them together when building an application.

  1. Components
    • Must be a subclass of C_Component (which is a subclass of ExtensibleObject)
    • Provides a reusable implementation of an interface.
    • May be associated with one or more contexts, simple string values which indicate the appropriate context to use the component in
  2. Component Factory
    • If a component represents an entity of many instances, and each can have it's own state, then you'll need to use a factory to instantiate your components
    • The component factory is a utility which implements the I_Component_Factory interface
    • The factory is adapted to provide factory methods for instantiating other components
    • Read about the Factory Pattern here
  3. Adapters
    • Adapters are a special type of Mixin which are applied to components when they are instantiated
    • Adapters can conditionally applied, depending on the particular context a component has.
    • Adapters are typically used to adapt the behavior of a component to behave differently in certain situations
  4. Utilities
    • Provide particular implementations of an interface
    • Are helper components, used to assist other components to complete a task
    • Implement the singleton pattern. Read more here
  5. Modules
    • Provide specific loosely-coupled functionality for an application
    • Composed of components, factory methods, adapters, and utilities
    • Intended to be swappable for other modules which implement the same interfaces
  6. Products
    • Provide a collection of interconnected modules which provide an application
  7. Component Registry
    • Provides a central utility, similar to a database, of all the known implementations of interfaces within your application.
    • Responsible for handling the logic of conditionally applying adapters to components and utilities
    • Maintain a list of known products and modules, and which have been loaded

Pope demonstration

Below is a simple example of a Pope utiliy which writes a list of terms to a file:

<?php

include_once('pope/lib/autoload.php');

/**
 * Provides instance methods for the List_Writer component
**/
class Mixin_Fs_Writer_Instance extends Mixin
{
	function open()
	{
		$this->object->_fp = fopen($this->object->context, 'a+');
	}

	function close()
	{
		fclose($this->object->_fp);
		$this->object->_fp = NULL;
	}

	function add($vars=array())
	{
		$this->object->_perform_transaction('_add_list_item', $vars);
	}

	function _add_list_item($item)
	{
		if (is_array($item)) {
			foreach ($item as $i) $this->object->_add_list_item($i);
		}
		else {
			if (substr($item, -1) != "n") $item .= "n";
			fwrite($this->object->_fp, $item);			
		}
	}

	function is_closed()
	{
		return isset($this->object->_fp);
	}

	function display()
	{
		echo file_get_contents($this->object->context);
	}	

	function _perform_transaction()
	{
		try {
			$this->object->open();
			$args = func_get_args();
			$method = array_shift($args);
			$this->object->$method($args);
		}
		catch (Exception $ex) { 
			$this->close();
		}

		if (!$this->object->is_closed()) $this->object->close();
	}
}

/**
 * Defines the interface for a List_Writer component
**/
interface I_List_Writer
{
	function add();
	function open();
	function close();
	function is_closed();
	function display();
}

/**
 * Writes a list of words to a file
**/
class C_List_Writer extends C_Component
{
	static $_instances = array();
	var $_fp;

	function define($context=FALSE)
	{
		parent::define($context);
		$this->add_mixin('Mixin_Fs_Writer_Instance');
		$this->implement('I_List_Writer');
	}

	/**
	 * Returns an instance of the utility in the desired context
	**/
	function get_instance($context)
	{
		if (!isset(self::$_instances[$context])) {
			$klass = get_class();
			self::$_instances[$context] = new $klass($context);
		}
		return self::$_instances[$context];
	}
}

Here's how we would make use of this utility:

// Get an instance of the component registry, and register our utility
$registry = C_Component_Registry::get_instance();
$registry->add_utility('I_List_Writer', 'C_List_Writer');

// Get an instance of our utility
$list = $registry->get_utility('I_List_Writer', '/tmp/list.txt');
$list->add(array(
	'Testing',
	'One',
	'Two',
	'Three'
));
$list->display();
// Output:
// Testing
// One
// Two
// Three

We can take the example above further by introducing an adapter, which provides the option of displaying a numerical list:

/**
 * Provides the ability to output a numerical list
**/
class A_Numerical_List_Writer extends Mixin
{
	function display($not_numeric=FALSE)
	{
		if ($not_numeric) {
			$this->call_parent('display');
		}
		else {
			$lines = explode("n", file_get_contents($this->object->context));
			for ($i=0; $i<count($lines); $i++) {
				echo sprintf("%-5s %sn", ($i+1).'.', $lines[$i]);
			}			
		}
	}
}

// Get an instance of the component registry, and register our utility
$registry = C_Component_Registry::get_instance();
$registry->add_utility('I_List_Writer', 'C_List_Writer');
$registry->add_adapter('I_List_Writer', 'A_Numerical_List_Writer');

// Get an instance of our utility
$list = $registry->get_utility('I_List_Writer', '/tmp/list.txt');
$list->add(array(
	'Testing',
	'One',
	'Two',
	'Three'
));
$list->display();
// Output:
// 1.    Testing
// 2.    One
// 3.    Two
// 4.    Three

Pope has a lot to offer, but we've barely scratched the surface. We encourage you to examine the source of NextGEN Gallery 2.0 at Bitbucket. We'll be adding more tutorials in the near future, so please visit nextgen-gallery.com again soon!

This Post Has 3 Comments

  1. These ideas definitely require some looking into. At first glance it looks like these concepts are re-inventing some of the WordPress wheels but upon further thought and reading I can see how this might be used beyond WordPress. Very interesting.

  2. Are there any plans to develop, and even more fundamentally, document the framework? Trying to develop functionality without any in-depth documentation and/or working tutorials proves rather time-consuming, not to say close to impossible.

    In particular, the tutorial on how to build a Display Type for NextGen Gallery, when followed to the letter, simply does not give any functioning results. It has taken me the best part of 2 days (of trial and error, debugging, and comparison with some of the standard modules) to get to the stage where I can have just the settings page displayed properly. I dread the rest of the tutorial for the added frustration it may bring

    Are there more tutorials, is there more online documentation?

    If, as advertised, the Pope Framework is a part of the Photocrati vision for the future, it could definitely do with a lot more in terms of documentation and fully functioning tutorials than is visible at the moment.

  3. Hi Philippe,

    Thanks for the comment. Indeed, we need to update our tutorials and provide more documentation. They were very relevant for our initial launch of NextGEN Gallery 2.0, but the implementation has evolved since then and therefore our tutorials need updating now as well. Unfortunately, we’ve been swamped with addressing the remaining issues since the 2.0 launch, which has occupied most of our time. Updating our developer documentation is something very important to me, and I’ll make it a priority to do so. If you’d like to be notified of developer-related updates, please sign up at http://www.nextgen-gallery.com/developers/

    I apologize for the inconvenience. If you have anything specific you’d like to ask, don’t hesitate to get in touch and we’ll try to help.

    Thanks,
    Mike Weichert
    Chief Architect
    Photocrati Media

Comments are closed.

Table of Contents
Close Menu