cvs: pear /PEAR_Delegator Delegator.php package.xml /PEAR_Delegator/Delegator/Exception DelegateUndefined.php Exception.php MethodUndefined.php
/PEAR_Delegator/Delegator/Internal Extension.php ForwardProxy.php /PEAR_Delegator/Delegator/Test PEAR_Delegator_Test_ShowOff.php

From: Date: Sun, 19 Mar 2006 05:42:05 +0000
Subject: cvs: pear /PEAR_Delegator Delegator.php package.xml /PEAR_Delegator/Delegator/Exception DelegateUndefined.php Exception.php MethodUndefined.php
/PEAR_Delegator/Delegator/Internal Extension.php ForwardProxy.php /PEAR_Delegator/Delegator/Test PEAR_Delegator_Test_ShowOff.php
Groups: php.pear.cvs 
Request: Send a blank email to pear-cvs+get-39188@lists.php.net to get a copy of this message
herrwitten Sun Mar 19 05:42:05 2006 UTC Added files: /pear/PEAR_Delegator Delegator.php package.xml /pear/PEAR_Delegator/Delegator/Exception DelegateUndefined.php Exception.php MethodUndefined.php /pear/PEAR_Delegator/Delegator/Internal Extension.php ForwardProxy.php /pear/PEAR_Delegator/Delegator/Test PEAR_Delegator_Test_ShowOff.php Log: Created new directory structure.

http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator.php +++ pear/PEAR_Delegator/Delegator.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file defines PEAR_Delegator, which provides delegation capabilities. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: Delegator.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ /** * This includes highly internal parts. */ require_once 'Delegator/Internal/ForwardProxy.php'; /** * Base class for objects that allow delegation. * * <b>Introduction</b> * <br> * It is first necessary to discuss the role of delegates in PEAR. * With the advent of PHP 5, a whole host of new programming techniques * were introduced. Among them were class interfaces. These interfaces * provide much of the benefits of multiple inheritance (with regard to * methods) without adulterating the inheritance hierarchy. That is, an * object can inherit from a parent class as usual, but still possess * methods that qualify it as a member of another group of objects, related * by certain behavior but still exclusively separate in the hierarchy. * The interfaces, however, only define the protocol to which each adopting class * must adhere, but they do not define the implementation. This is very * useful in many instances, but for some purposes, the implementation * remains viritually the same for all adopting parties. This is where * delegation enters. * * A delegate is a class that defines methods which are intended to be * called by an adopting object as if they were members of that object. * For instance, * <code> * class Foo extends PEAR_Delegator * { * public function _construct() * { * parent::_construct(); * } * * public function _destruct() * { * parent::_destruct(); * } * } * * $foo = new Foo(); * $foo->bar() //This results in a runtime error, * //Foo has no such method. * * //Now define a delegate * class Delegate * { * public function _construct() * { * parent::_construct(); * } * * public function _destruct() * { * parent::_destruct(); * } * * public function bar() * { * echo 'bar'; * } * } * * foo->addDelegate(new Delegate); //add the delegate; see also Static Delegates * foo->bar(); //This will be called as if it * //were a member of Foo. * </code> * * There are two types of delegates: <i>true delegates</i> and <i>false delegates</i>. * * <b>True Delegates</b> * <br> * These delegates are designed to be used as delegates. As such, they * follow an explicit delegate protocol. To simulate an actual method * call, the forwarding mechanism transparently inserts a reference to * the calling delegator as a the first argument. Consquently, such a * delegate's method signatures must be of the form: * <code> * accesslevel function functionName($owner, ...); * </code> * Note: * 1. $owner can be any suitable variable name. * 2. $owner is unncessary if the method takes no variables and does not * access the delegator. * 3. The user of the method need only consider those parameters that * follow the $owner argument. * Delegates of this kind are the default. However, the argument <var>true</var> * can be passed to explicitly denote the following list as a set of true delegates: * <code> * $delegator->addDelegate($delegate1, $delegate2, ...); * //or * $delegator->addDelegate(true, $delegate1, $delegate2, ...); * </code> * * <b>False Delegates</b> * <br> * These delegates are designed without any intention to be used as delegates. * In essence, these are regular objects in themselves that would be useful * as delegates. They have no knowledge of delegators and no reason to access * a forwarding delegator. Consequently, such a delegate's method signatures are * not guaranteed to be of the form described above. Their signatures can be * of any form, but they will not have access to the calling delegator. * * Delegates of this kind must be explicitly denoted by passing a <var>false</var> * as the preceding argument to a list of false delegates: * <code> * $delegator->addDelegate(false, $delegate1, $delegate2, ...); * </code> * * Indeed, a mixed list of delegates can be called upon: * <code> * $delegator->addDelegate($delegate1, $delegate2, false, $delegate3, $delegate4, true $delegate1 ...); * </code> * * <b>Mixed Delegates</b> * <br> * Since delegates can themselves can be delegators, the two kinds of delegates * can be mixed. This is achieved by creating a true delegate that has added to it * a false delegate. Then, any delegator can make calls to true delegate methods * and false delegate methods through the same delegate. * * There are two further categorizations: <i>class delegates</i> and <i>instance delegates</i>. * * <b>Class Delegates</b> * <br> * These delegates are classes whose methods are to be called statically. * Hence, classes themselves (specified by classnames) can be delegates * * <b>Instance Delegates</b> * <br> * These delegates are instances of classes. * * <b>Delegate Hierarchies</b> * <br> * One of the benefits of this scheme is the ability to have delegate hierarchies. * That is, a delegator could have a delegate that is a delegator, and * the PEAR_Delegator class recognizes this by viewing such delegates as <i>subdelegates</i>, * treating such subdelegates as subclasses would be treated. This allows for such capabilities * as pseudo-overriding: * <code> * //In our Foo class we could define a bar() method as follows: * public function bar() * { * $args = func_get_args(); * * //Note that it may be better to call $this->getDelegateForMethod() * //instead of $this->hasDelegate(), since the latter may return true * //even though no delegate may be able to respond. * if ($this->hasDelegate()) * $this->forwardMethod('bar', $args); * * echo 'foobar'; * } *</code> * Now, the delegate's implementation would be called as well. This of course means that * you can also completely override the delegate method, and not even call it. * * <b>Traditional Delegation</b> * <br> * In truth, this mode of delegation is unorthodox. The traditional model * of delegation is for an object to delegate selected methods, calling its * own version unless one delegate is present. This feature is, in fact, a * subset of the scheme presented here. In otherwords, you can achieve the * same effect by pseudo-overriding as described above. * * <b>Implementation</b> * <br> * 1. <b>Performance Impact</b> * In actuality, there should be little extra overhead after the first call to a delegated * method. This is due to a caching scheme: When methods are called upon a * delegator, the delegator checks an associated array that contains method names as * keys and the proper delegates as values. If the key (method name) is cached * in this manner, then the method is immediatly invoked on the proper delegate. If it * does not exist, then each delegate is searched until one that can respond is found * and this relationship is cached, otherwise, a fatal error is produced. Thus no * matter how many delegates a class has, all calls after the first should * only have a small latency. * <b>Note</b>: Subdelegators cache their own delegates, so calls are passed down the hiearchy * until the implementing delegate handles the call; * 2. <b>Flexibility of Errors</b>. * This is not a trouble at all. In fact, the error output of this class is more direct in * many cases than PHP's own error output. It will give you the file and line of the error * in user code and its messages are modeled after those of PHP. * * <b>Terminology</b> * <br> * 1. <b>owner</b> : a delegator. * 2. <b>subdelegate</b> : A delegator that is a delegate. * 3. <b>native delegate</b> : A delegate that is an immediate delegate, not a delegate * of a delegate. * 4. <b>true delegate</b> : A delegate that follows the $owner model. * 5. <b>false delegate</b> : A delegate that does not follow the $owner model; any * regular class/object * 4. <b>class delegate</b> : A delegate that is simply the class. * 5. <b>instance delegate</b> : A delegate that is an instantiated class. * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator { /** * An associative array with delegate classnames as keys * and objects as values. * @var array */ public $_delegates = array(); /** * An associative array with false delegate classnames as keys * and objects as values. * @var array */ public $_delegatesFalse = array(); /** * An associative array with delegated methods as keys * and delegate objects as values. * @var array */ public $_method_map = array(); /** * An internal variable for turning on extensions. * @var boolean */ protected static $_addExtensions = false; /** * Constructs a delegator. * * This adds the extensions if they have been loaded. */ public function __construct() { if (self::$_addExtensions) { self::addExtensions($this); } } /** * Destroys a delegator. * * This does nothing */ public function __destruct() { } /** * Add extensions to every new delegator. * * This loads the extensions and gives every new delegator the extensions. * Delegators created before this call can be passed in to have the extensions * added to them; */ public static function addExtensions() { if (!self::$_addExtensions) { require_once 'Delegator/Internal/Extension.php'; self::$_addExtensions = true; } $args = func_get_args(); foreach ($args as $delegator) { $delegator->addDelegate('PEAR_Delegator_Internal_Extension'); } } /** * Adds delegates to the calling object. * * This method takes a list of classnames or objects. If an argument is * a classname, then the method determines if it is defined. If it is, * the class is added as a static delegate, otherwise a fatal error * is raised. If it is an object, it is stored as a delegate; thus, * there are two types of delegates: static and dynamic delegates. * * @see PEAR_Delegator::setDelegate() * * @param mixed $delegate,... This specifies either a classname or an object. */ public function addDelegate($delegate) { $args = func_get_args(); $falseDelegate = false; foreach ($args as $delegate) { if (is_bool($delegate)) { $falseDelegate = !$delegate; continue; } if (is_string($delegate)) { if (!class_exists($delegate)) { require_once 'Delegator/Exception/DelegateUndefined.php'; $exception = new PEAR_Delegator_Exception_DelegateUndefined($delegate); die('<b>Fatal error</b>: ' . $exception->getMessage()); } $delegateClass = $delegate; } else { $delegateClass = get_class($delegate); } if ($falseDelegate) { $this->_delegatesFalse[$delegateClass] = $delegate; } $this->_delegates[$delegateClass] = $delegate; } } /** * Sets the delegator's one delegate. * * This method takes a classname or an object and makes it the only delegate. * In actuality, it removes all of the delegates and then adds the specified * delegate. This is useful for using the delegation method for the traditional * delegate model. * * @see PEAR_Delegator::addDelegate() * @uses PEAR_Delegator::removeAllDelegates(), PEAR_Delegator::addDelegate() * @param mixed $delegate This specifies either a classname or an object. */ public function setDelegate($delegate) { $this->removeAllDelegates(); if ($delegate) { $this->addDelegate($delegate); } } /** * Gets the associated array of delegate classes => delegates. * * Note: Cloning may not work after this. * @see PEAR_Delegator::getAllDelegates(), PEAR_Delegator::getDelegate(), * PEAR_Delegator::getDelegateExact(), PEAR_Delegator_Internal_Extension::getDelegateRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact() * @return array A reference to the _delegates array. */ public function &getAllDelegates() { return $this->_delegates; } /** * Gets the delegate objects that are instances of the specified class. * * This method returns instances of the specified classname as well as * child instances of the specified classnames, including subdelegates, * which are native to the caller. That is, if one of the delegates is * a delegator and it contains a delegate of the specified type, * it will be returned regardless of its own class type. * * @see PEAR_Delegator::getAllDelegates(), PEAR_Delegator::getDelegate(), * PEAR_Delegator::getDelegateExact(), PEAR_Delegator_Internal_Extension::getDelegateRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact() * @param class $classname1 This specifies a delegate classname. Any * number of arguments after this is acceptable. * @return array <pre>The result is an * associative array of the form: * Array * ( * [classname1] = Array * ( * delegate11 * delegate12 * ... * ) * [classnamei] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single classname is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegate($classname1) { $args = func_get_args(); $result = null; foreach ($args as $classname) { foreach ($this->_delegates as $delegate) { if (PEAR_Delegator::isA($delegate, $classname)) { $result[$classname][] = $delegate; continue; } } } if ($result) { return (count($args) > 1) ? $result : $result[$classname1]; } else { return null; } } /** * Gets the delegate object that is an instance of the specified class. * * This method returns classes of the specified type. This does not return * subdelegates. That is, it uses only the actual class structures. * * @see PEAR_Delegator::getAllDelegates(), PEAR_Delegator::getDelegate(), * PEAR_Delegator_Internal_Extension::getDelegateRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact() * @param class $classname1 This specifies a delegate classname. Any * number of arguments after this is acceptable. * @return array <pre> The result is an * associative array of the form: * Array * ( * [classname1] = Array * ( * delegate11 * delegate12 * ... * ) * [classnamei] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single classname is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateExact($classname1) { $result = null; $args = func_get_args(); foreach ($args as $classname) { foreach ($this->_delegates as $delegate) { if (PEAR_Delegator::isAExact($delegate, $classname)) { $result[$classname][] = $delegate; } } } if ($result) { return (count($args) > 1) ? $result : $result[$classname1]; } else { return null; } } /** * Gets the native delegate objects that respond to a certain method. * * The method returns delegates native to the calling delegator, which can * respond to the method in question, whether it be defined in the native delegate * or in a delegate deeper in the hierarchy. * * @see PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursive(), PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursiveExact(), * PEAR_Delegator_Internal_Extension::getDelegateForMethodFirst(), PEAR_Delegator::methodExists() * @uses PEAR_Delegator::methodExists() * @param string $method1,... This specifies the method for whose responder is searched. * @return array <pre>The result is an * associative array of the form: * Array * ( * [method1] = Array * ( * delegate11 * delegate12 * ... * ) * [methodi] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single method name is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateForMethod($method1) { $result = array(); $args = func_get_args(); foreach ($args as $method) { foreach ($this->_delegates as $delegate) { if (PEAR_Delegator::methodExists($delegate, $method)) { $result[$method][] = $delegate; } } } if (count($result)) { return (count($args) > 1) ? $result : $result[$method1]; } else { return null; } } /** * Determines whether or not the calling object adopts a particular delegate. * * This returns the availability of a delegate not including subdelegates. * Note: This should be used for delegates that don't use hierarchies. * * @see PEAR_Delegator::hasDelegate() * @param class $specifier This specifies a delegate classname. * @return bool If the calling object has adopted the specifed classname */ public function hasDelegateExact($specifier = null) { if (array_key_exists($specifier, $this->_delegates)) { return true; } foreach ($this->_delegates as $delegate) { if (PEAR_Delegator::isAExact($delegate, $specifier)) { return true; } } return false; } /** * Determines whether or not the calling object adopts a particular delegate. * * This returns the availability of a delegate, including subdelegates. * * @see PEAR_Delegator::hasDelegateExact(), PEAR_Delegator::isA(); * @param mixed $specifier This specifies a delegate classname or object. If * $delegate is a string, then it adheres to the tests * of getDelegate(). If $delegate is an object, the _delegates * array is searched for the object. If $specifier is null, * then this returns whether or not the caller has any delegates. * @return bool If the calling object has adopted the specifed class name */ public function hasDelegate($specifier = null) { if ($specifier == null) { return (count($this->_delegates)) ? true : false; } elseif (is_string($specifier)) { $specifier = $specifier; if (array_key_exists($specifier, $this->_delegates)) { return true; } foreach ($this->_delegates as $delegate) { if (PEAR_Delegator::isAExact($delegate, $specifier)) { return true; } if (is_object($delegate) && ($delegate instanceof PEAR_Delegator && $delegate->hasDelegate($specifier))) { return true; } } } else { foreach ($this->_delegates as $delegate) { if ($delegate === $specifier) { return true; } if (is_object($delegate) && ($delegate instanceof PEAR_Delegator && $delegate->hasDelegate($specifier))) { return true; } } } return false; } /** * Removes all delegates. * * This completely cleans the calling object of any delegates. * * @see PEAR_Delegator::removeDelegate(), PEAR_Delegator_Internal_Extension::removeDelegateRecursiveExact() */ public function removeAllDelegates() { unset($this->_method_map); unset($this->_delegates); unset($this->_delegatesFalse); $this->_method_map = array(); $this->_delegates = array(); $this->_delegatesFalse = array(); } /** * Removes the unwanted entries from _method_map. * * This method cleans the _method_map array when a call to removeDelegate*() * is made. * * @param object $filterDelegate Specifies the delegate instance, whose information is * is to be removed. * @return array The method map without the $filterdelegate */ public function filterMethodMapWithDelegate($filterDelegate) { $result = array(); $method_map_keys = array_keys($this->_method_map); $method_map_values = array_values($this->_method_map); for ($i = 0, $count = count($method_map_values); $i < $count; $i++) { $delegate = $method_map_values[$i]; if ($delegate === $filterDelegate) { continue; } $result[$method_map_keys[$i]] = $delegate; } return $result; } /** * Removes the specified delegate. * * Takes a list of delegate classnames and delegate objects and removes them * from the calling object. * * @param mixed $specifier,... Specifies the delegate, whose information is * is to be removed. If it is a string, then it * adheres to the tests of getDelegate(). If it * is an object, then it searches the for that * delegate to remove. * @see PEAR_Delegator::removeAllDelegates(), PEAR_Delegator_Internal_Extension::removeDelegateRecursiveExact() * @uses PEAR_Delegator::getDelegate(), PEAR_Delegator::filterMethodMapWithDelegate() */ public function removeDelegate($specifier) { $args = func_get_args(); $delegates = call_user_func_array(array($this, 'getDelegate'), $args); foreach ($delegates as $delegateArray) { foreach ($delegateArray as $delegate) { $key = is_string($delegate) ? $delegate : get_class($delegate); unset($this->_delegates[$key]); unset($this->_delegatesFalse[$key]); $this->_method_map = $this->filterMethodMapWithDelegate($delegate); } } } /** * Determines if a class or instance object is of the given type. * * This method is analogous to the is_a() method of PHP. However, * it handles classes too. * * @see PEAR_Delegator::isA() * @param mixed $specifier Specifies the delegate with either * a class or instantiated object. * @param class $classname The classname type to check against. * @return bool true if it is, false if it is not. */ public static function isAExact($specifier, $classname) { if (is_string($specifier)) { if ((new $specifier) instanceof $classname) { return true; } } elseif ($specifier instanceof $classname) { return true; } return false; } /** * Determines if a class or instance object is of the given type. * * This method is an extension of the isAExact() method. It also * handles subdelegates, so it returns true if a delegator * is passed in and has a delegate of type $classname, whether * or not the delegator is of type $classname. * * @uses PEAR_Delegator::hasDelegate() * @param mixed $specifier Specifies the delegate with either * a class or instantiated object. * @param class $classname The classname type to check against. * @return bool true if it is, false if it is not. */ public static function isA($specifier, $classname) { if (PEAR_Delegator::isAExact($specifier, $classname)) { return true; } elseif (is_object($specifier) && (($specifier instanceof PEAR_Delegator) && $specifier->hasDelegate($classname))) { return true; } return false; } /** * Determines if a class or instance object responds to a method. * * This method is an extension of the method_exists function; * It also handles subdelegates, so it returns true if a delegator * is passed in and has a delegate that can implement $method, * whether or not the delegator can implement the $method. * * @param mixed $specifier Specifies the delegate with either * a class or instantiated object. * @param string $method The method to look for. * @return bool true if it is, false if it is not. */ public static function methodExists($specifier, $method) { if (method_exists($specifier, $method)) { return true; } elseif (is_object($specifier) && ($specifier instanceof PEAR_Delegator)) { foreach ($specifier->_delegates as $delegate) { if (PEAR_Delegator::methodExists($delegate, $method)) { return true; } } } return false; } /** * Finds whether or not the object or one of its delegates implements a method * * Finds whether or not the calling object can perform the given method name. * The calling object can perform the given method if it or one of its delegates * can do so. This means that it also searches delegates that are themselves * delegators. * * @see PEAR_Delegator::methodExists() * @uses PEAR_Delegator::methodExists() * @param string $method The method name that is to be searched for availability. * @return bool true if it does, false if it does not. */ public function respondsToMethod($method) { return PEAR_Delegator::methodExists($this, $method); } /** * Stores the relationship between method names and delegates. * * Takes a method name, searches for the delegate that can handle * it, and stores the relationship in the _method_map array. This * method is called when the __call() method reveives an unrecognized * method. This caching of methods speeds up delegation. If the method * cannot be handled by any of the adopted delegates, then an Exception * is thrown. * * Note: This caches the first responding delegate. * * This is an internal method and should not be invoked. * * @param string $method The method name that is to be cached. This must * be a lowercase string. * @throws PEAR_Delegator_Exception_MethodUndefined */ protected function cacheMethod($method) { $delegates = $this->getDelegateForMethod($method); if ($delegates && ($delegate = $delegates[0])) { $this->_method_map[$method] = $delegate; return; } require_once 'Delegator/Exception/MethodUndefined.php'; throw new PEAR_Delegator_Exception_MethodUndefined($method); } /** * This can be used by delegators for pseudo-method-overriding a method. * * This is the public interface to the __call() method, and it allows * for a method to be forwarded to the delegation system, so that * pseudo-method-overriding can occur. * * @see PEAR_Delegator::__call() * @param string $method See the PHP documentation. * @param string $args See the PHP documentation */ public function forwardMethod($method, $args = array()) { return $this->__call($method, $args); } /** * Processes unrecognized method signatures. * * This checks the _method_map array for a cached relationship * between the method and any delegate. If one exists, the method * is immediately called and the result returned. If it does not, * then it calls the cacheMethod() method to find and cache the * method, after which is calls the unrecognized method on the * proper delegate or kills the PHP with an error. * * This is an internal method and should not be invoked. * * @see PEAR_Delegator::forwardMethod(), PEAR_Delegator::cacheMethod() * @param string $method See the PHP documentation. * @param string $args See the PHP documentation. * @return mixed the result of the called method. * @uses PEAR_Delegator::cacheMethod() */ public function __call($method, $args = array(null)) { //It is necessary to convert the string to lowercase since PHP doesn't //differentiate between case when calling methods. This distills the //situation to one case. $method = strtolower($method); if (!array_key_exists($method, $this->_method_map)) { try { $this->cacheMethod($method); } catch(PEAR_Delegator_Exception_MethodUndefined $exception) { die('<b>Fatal error</b>: ' . $exception->getMessage()); } } $forwardingDelegator = (isset($args[0]) ? $args[0] : null); if (!(is_object($forwardingDelegator)) || !($forwardingDelegator instanceof PEAR_Delegator_Internal_ForwardProxy)) { $forwardingDelegator = new PEAR_Delegator_Internal_ForwardProxy($this); $args = array_merge(array($forwardingDelegator), $args); } $delegate = $this->_method_map[$method]; if (($delegate instanceof PEAR_Delegator) && !(method_exists($delegate, $method))) { return $delegate->forwardMethod($method, $args); } if (array_key_exists(is_string($delegate) ? $delegate : get_class($delegate), $this->_delegatesFalse)) { unset($args[0]); } else { $args[0] = $forwardingDelegator->getDelegator(); } return call_user_func_array(array($delegate, $method), $args); } } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/package.xml?view=markup&rev=1.1 Index: pear/PEAR_Delegator/package.xml +++ pear/PEAR_Delegator/package.xml <?xml version="1.0" encoding="UTF-8"?> <package packagerversion="1.4.6" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> <name>PEAR_Delegator</name> <channel>pear.php.net</channel> <summary>Delegation for PHP</summary> <description>This package implements traditional and unorthodox delegation in PHP. This allows for pseudo multiple inheritance and other interesting design paradigms. </description> <lead> <name>Michael Witten</name> <user>herrwitten</user> <email>herrwitten@php.net</email> <active>yes</active> </lead> <date>2006-03-18</date> <time>20:58:17</time> <version> <release>0.1.2</release> <api>0.1.2</api> </version> <stability> <release>alpha</release> <api>alpha</api> </stability> <license uri="http://www.php.net/license">PHP License</license> <notes> Though categorized as alpha, this code should be quite stable. However, semantics and organization may change in this stage. </notes> <contents> <dir name="/"> <dir name="Delegator"> <dir name="Exception"> <file baseinstalldir="PEAR" name="DelegateUndefined.php" role="php" /> <file baseinstalldir="PEAR" name="Exception.php" role="php" /> <file baseinstalldir="PEAR" name="MethodUndefined.php" role="php" /> </dir><!-- //Delegator/Exception --> <dir name="Internal"> <file baseinstalldir="PEAR" name="Extension.php" role="php" /> <file baseinstalldir="PEAR" name="ForwardProxy.php" role="php" /> </dir><!-- //Delegator/Internal --> </dir><!-- //Delegator --> <file baseinstalldir="PEAR" name="Delegator.php" role="php" /> </dir><!-- / --> </contents> <dependencies> <required> <php> <min>5.0.0</min> <max>6.0.0</max> </php> <pearinstaller> <min>1.4.0b1</min> <max>6.0.0</max> </pearinstaller> </required> </dependencies> <phprelease /> </package> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Exception/DelegateUndefined.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Exception/DelegateUndefined.php +++ pear/PEAR_Delegator/Delegator/Exception/DelegateUndefined.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file defines the undefined delegate exception. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: DelegateUndefined.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ require_once 'Exception.php'; /** * This class defines the exception that is thrown when an undefined class delegate is referenced. * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator_Exception_DelegateUndefined extends PEAR_Delegator_Exception { /** * Constructs a PEAR_Delegator_Exception_DelegateUndefined. * * This exception is thrown when adding a static delegate that has not been defined * * @see PEAR_Delegator::addDelegate() * @param mixed $delegate This is the undefined delegate. It can be a string. * @param string $method The offending method. */ public function __construct($delegate, $func = 'addDelegate') { $backtraceArray = $this->getTrace(); $file = 'unknown file'; $line = 'unknown'; foreach ($backtraceArray as $index => $fileRecord) { $function = $fileRecord['function']; if (strtolower($function) == strtolower($func)) { if (array_key_exists('file', $fileRecord)) { $file = $backtraceArray[++$index]['file']; $line = $backtraceArray[$index]['line']; } $class = $fileRecord['class']; break; } } parent::__construct("Class '$delegate' not found in <b>$file</b> on line <b>$line</b>"); } } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Exception/Exception.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Exception/Exception.php +++ pear/PEAR_Delegator/Delegator/Exception/Exception.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file defines the main exceptio class. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: Exception.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ /** * This includes the base exception class. */ require_once 'PEAR/Exception.php'; /** * This class is the base class for all delegator exceptions. * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator_Exception extends PEAR_Exception { } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Exception/MethodUndefined.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Exception/MethodUndefined.php +++ pear/PEAR_Delegator/Delegator/Exception/MethodUndefined.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file defines the undefined method exception. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: MethodUndefined.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ require_once 'Exception.php'; /** * This class defines the exception that is thrown when an undefined method is called. * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator_Exception_MethodUndefined extends PEAR_Delegator_Exception { /** * Constructs a PEAR_Delegator_Exception_MethodUndefined. * * This exception is thrown when the forwarding mechanism cannot find an implementor * for the given method. * * @see PEAR_Delegator::__call(), PEAR_Delegator::cacheMethod() * @param string $method This is the undefined method. */ public function __construct($method) { $backtraceArray = $this->getTrace(); $file = 'unknown file'; $line = 'unknown'; foreach ($backtraceArray as $fileRecord) { $function = $fileRecord['function']; if (strtolower($function) == strtolower($method)) { if (array_key_exists('file', $fileRecord)) { $file = $fileRecord['file']; $line = $fileRecord['line']; } $class = $fileRecord['class']; break; } } parent::__construct("Call to undefined method $class::$function() in <b>$file</b> on line <b>$line</b>"); } } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Internal/Extension.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Internal/Extension.php +++ pear/PEAR_Delegator/Delegator/Internal/Extension.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file defines PEAR_Delegator extensions, which are added to any delegator with the addExtensions() method. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: Extension.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ /** * This class provides a static delegate that adds methods to a PEAR_Delegator instance * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator_Internal_Extension { /** * Gets the delegates that is of the specified class. * * This method returns instances of the specified classname as well as * child instances of the specified classname, including subdelegates, * which are anywhere in the delegate hierarchy. This method is provided, * because it may be useful, but its use is discouraged, as objects should * logically only have access to their native delegates. * * @see PEAR_Delegator::getAllDelegates(), PEAR_Delegator::getDelegate(), * PEAR_Delegator_Internal_Extension::getDelegateRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact() * @param class $classname1 This specifies a delegate classname. Any * number of arguments after this is acceptable. * @return array <pre> The result is an * associative array of the form: * Array * ( * [classname1] = Array * ( * delegate11 * delegate12 * ... * ) * [classnamei] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single classname is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateRecursive($owner, $classname1) { $args = func_get_args(); unset($args[0]); $result = array(); foreach ($args as $arg) { foreach ($owner->_delegates as $delegate) { if (is_object($delegate) && $delegate instanceof PEAR_Delegator) { if ($others = PEAR_Delegator_Internal_Extension::getDelegateRecursive($delegate, $arg)) { $others = array($arg => $others); $result = array_merge_recursive($result, $others); $result[$arg][] = $delegate; continue; } } if (PEAR_Delegator::isAExact($delegate, $arg)) { $result[$arg][] = $delegate; } } } if (count($result)) { return (count($args) > 1) ? $result : $result[$classname1]; } else { return null; } } /** * Gets the delegate that is of the specified class. * * This method returns classes of the specified type. This does not return * subdelegates. This method is provided, because it may be useful, but its * use is discouraged, as objects should logically only have access to their * native delegates. * * @see PEAR_Delegator::getAllDelegates(), PEAR_Delegator::getDelegate(), * PEAR_Delegator_Internal_Extension::getDelegateRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact() * @param class $classname1 This specifies a delegate classname. Any * number of arguments after this is acceptable. * @return array <pre> The result is an * associative array of the form: * Array * ( * [classname1] = Array * ( * delegate11 * delegate12 * ... * ) * [classnamei] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single classname is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateRecursiveExact($owner, $classname1) { $result = array(); $args = func_get_args(); unset($args[0]); foreach ($args as $classname) { if ($native = $owner->getDelegateExact($owner, $classname)) { $native = array($classname => $native); $result = array_merge_recursive($result, $native); continue; } foreach ($owner->_delegates as $delegate) { if (is_object($delegate) && ($delegate instanceof PEAR_Delegator) && ($delegateNative = PEAR_Delegator_Internal_Extension::getDelegateRecursiveExact($delegate, $classname))) { $delegateNative = array($classname => $delegateNative); $result = array_merge_recursive($result, $delegateNative); } } } if (count($result)) { return (count($args) > 1) ? $result : $result[$classname1]; } else { return null; } } /** * Gets the delegate objects that respond to a certain method. * * This method returns delegates native to the calling delegator as well * as delegates of delegate owning delegates (it's recursive). This method is * provided, because it may be useful, but its use is discouraged, as objects * should logically only have access to their native delegates. * * @see PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursiveExact() * @uses PEAR_Delegator::methodExists() * @param string $method1,... This specifies the method for whose responder is searched. * @return array <pre>The result is an * associative array of the form: * Array * ( * [method1] = Array * ( * delegate11 * delegate12 * ... * ) * [methodi] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single method is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateForMethodRecursive($owner, $method1) { $result = array(); $args = func_get_args(); unset($args[0]); foreach ($args as $method) { foreach ($owner->_delegates as $delegate) { if (is_object($delegate) && $delegate instanceof PEAR_Delegator) { if ($others = PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursive($delegate, $method)) { $result = array_merge_recursive($result, array($method => $others)); $result[$method][] = $delegate; continue; } } if (PEAR_Delegator::methodExists($delegate, $method)) { $result[$method][] = $delegate; } } } if (count($result)) { return (count($args) > 1) ? $result : $result[$method1]; } else { return null; } } /** * Gets the delegate object (native or otherwise) that implements the method in question. * * This method returns the delegate in the delegate hierarchy * which actually implements the method. This method is provided, because it may be useful, * but its use is discouraged, as objects should logically only have access to their native * delegates. * * @see PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursive() * @param string $method1,... This specifies the method for whose implementor is searched. * @return array <pre> An array of the form: * Array * ( * [method1] = Array * ( * delegate11 * delegate12 * ... * ) * [methodi] = Array * ( * delegate1i * delegate1i * ... * ) * ... * ) * Note: If a single method is passed, a traditional array with numbered * elements is returned. * </pre> */ public function getDelegateForMethodRecursiveExact($owner, $method1) { $result = array(); $args = func_get_args(); unset($args[0]); foreach ($args as $method) { foreach ($owner->_delegates as $delegate) { if (method_exists($delegate, $method)) { $result[$method][] = $delegate; } if (is_object($delegate) && $delegate instanceof PEAR_Delegator) { if ($match = PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursiveExact($delegate, $method)) { $result = array_merge_recursive($result, array($method => $match)); } } } } if (count($result)) { return (count($args) > 1) ? $result : $result[$method1]; } else { return null; } } /** * Gets the first native delegate that responds to a certain method. * * The method returns the first delegate native to the calling delegator, which can * respond to each method in question. Moreover, this method returns delegates that * actually implement methods over those that inherit them from delegates. * * @see PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursive(), * PEAR_Delegator_Internal_Extension::getDelegateForMethodRecursiveExact() * @uses PEAR_Delegator::methodExists() * @param string $method,... This specifies the method for whose responder is searched. * @return array <pre>The result is an * associative array of the form: * Array * ( * [method1] = $delegate * [methodi] = $delegatei * ... * ) * Note: If a single classname is passed, the actual object is returned. * </pre> */ public function getDelegateForMethodFirst($owner, $method) { $args = func_get_args(); unset($args[0]); $delegates = call_user_func_array(array($owner, 'getDelegateForMethod'), $args); if ($delegates) { $delegateArrayImplementors = array(); $delegateArrayNonimplementors = array(); if (count($args) > 1) { foreach ($delegates as $method => $delegateArray) { foreach ($delegateArray as $delegate) { if (is_string($delegate) || method_exists($delegate, $method)) { $delegateArrayImplementors[] = $delegate; } else { $delegateArrayNonimplementors[] = $delegate; } } $delegateArray = array_merge($delegateArrayImplementors, $delegateArrayNonimplementors); $delegates[$method] = $delegateArray[0]; } } else { foreach ($delegates as $delegate) { if (is_string($delegate) || method_exists($delegate, $method)) { $delegateArrayImplementors[] = $delegate; } else { $delegateArrayNonimplementors[] = $delegate; } } $delegates = array_merge($delegateArrayImplementors, $delegateArrayNonimplementors); $delegates = $delegates[0]; } } return $delegates; } /** * Removes the specified delegate recursively * * Only exact delegates are removed, as it is otherwise superfluous. * * @see PEAR_Delegator::removeAllDelegates(), PEAR_Delegator::removeDelegate() * @param mixed $specifier,... Specifies the delegate, whose information is * is to be removed. If it is a string, then it * adheres to the tests of getDelegateExact(). If it * is an object, then it searches the for that * delegate to remove. * @uses filterMethodMapWithDelegate() */ public function removeDelegateRecursiveExact($owner, $specifier) { $args = func_get_args(); unset($args[0]); foreach ($args as $arg) { foreach ($owner->_delegates as $delegate) { if (is_object($arg)) { if ($delegate === $arg) { unset($owner->_delegates[get_class($delegate)]); $owner->_method_map = $owner->filterMethodMapWithDelegate($delegate); } } else { if (PEAR_Delegator::isAExact($delegate, $arg)) { unset($owner->_delegates[is_string($delegate) ? $delegate : get_class($delegate)]); $owner->_method_map = $owner->filterMethodMapWithDelegate($delegate); } } if ($delegate instanceof PEAR_Delegator) { PEAR_Delegator_Internal_Extension::removeDelegateRecursiveExact($delegate, $arg); } } } } /** * Removes an entry from the _method_map array. * * This method is for removing entries in the _method_map array. * it simply tests for the entry for existence and removes it. * * This is an internal method. * * @see PEAR_Delegator::cacheMethod() * @param string $method This method must be in lowercase. * @return bool If entry exists true; false otherwise. */ protected function uncacheMethod($owner, $method) { $ok = false; if ($ok = isset($owner->_method_map[$method])) unset($owner->_method_map[$method]); return $ok; } } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Internal/ForwardProxy.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Internal/ForwardProxy.php +++ pear/PEAR_Delegator/Delegator/Internal/ForwardProxy.php <?php /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ /** * This file provides the ForwardProxy class. * * PHP version 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category PEAR * @package PEAR_Delegator * @author Michael Witten <herrwitten@php.net> * @copyright 2004-2005 Michael Witten * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: ForwardProxy.php,v 1.1 2006/03/19 05:42:05 herrwitten Exp $ * @link http://pear.php.net/package/PEAR_Delegator */ /** * This class is a flag for the forwarding mechanism. * * This class serves as a flag and information carrier for the forwarding * mechanism. It tells delegators deep in the delegate hierarchy which * delegator is to be taken as the caller. Note: This is an internal structure. * * @author Michael Witten <herrwitten@php.net> 4100 85 * @see http://pear.php.net/manual/ * @package PEAR_Delegator */ class PEAR_Delegator_Internal_ForwardProxy { public $_delegator; /** * Constructs a PEAR_Delegator_Internal_ForwardProxy. * * @param mixed $delegator The delegator that is to be regarded * as the caller of the delegated method. */ public function __construct($delegator) { $this->_delegator = $delegator; } /** * Gets the delegator that is to be regarded as the initial caller. */ public function getDelegator() { return $this->_delegator; } } ?> http://cvs.php.net/viewcvs.cgi/pear/PEAR_Delegator/Delegator/Test/PEAR_Delegator_Test_ShowOff.php?view=markup&rev=1.1 Index: pear/PEAR_Delegator/Delegator/Test/PEAR_Delegator_Test_ShowOff.php +++ pear/PEAR_Delegator/Delegator/Test/PEAR_Delegator_Test_ShowOff.php <HTML> <TITLE>Show Off</TITLE> <BODY> <?php require_once "PEAR.php"; require_once "PEAR/Delegator.php"; echo " <pre> We will create 12 classes: A ADelegate1 ADelegate2 B BDelegate1 BDelegate2. Extra1 Extra2 Traditional Delegate DelegateFalse DelegateMixed; They are defined as follows: class A extends PEAR_Delegator { var \$message; public function __construct() { parent::__construct(); \$this->addDelegate('ADelegate1'); \$this->addDelegate('ADelegate2'); \$this->addDelegate(new B); } public function __destruct() { parent::__destruct(); } } class ADelegate1 extends PEAR { public function __construct() { } public function __destruct() { } public function setMessage1(\$owner, \$message) { \$owner->message = \$message; } } class ADelegate2 extends PEAR { public function __construct() { } public function __destruct() { } public function getMessage1(\$owner) { echo \$owner->message; } } class B extends PEAR_Delegator { public function __construct() { parent::__construct(); \$this->addDelegate('BDelegate1'); \$this->addDelegate(new BDelegate2); } public function __destruct() { parent::__destruct(); } function foo() { echo get_class(\$this) . \": I will now call my foo delegate without knowing which one it is< BR>\"; \$args = func_get_args(); \$this->forwardMethod(\"foo\", \$args); } function bar() { echo get_class(\$this) . \": I will now call my bar delegate without knowing which one it is< BR>\"; \$args = func_get_args(); \$this->forwardMethod(\"bar\", \$args); } public function setMessage2(\$owner, \$message) { \$this->message = \$message; } public function getMessage2() { echo \$this->message; } } class BDelegate1 extends PEAR { public function __construct() { } public function __destruct() { } public function foo() { echo \"BDelegate1:\" . \": foo< BR>\"; } } class BDelegate2 extends PEAR { public function __construct() { } public function __destruct() { } public function bar() { echo \"BDelegate2\" . \": bar< BR>\"; } } class Extra1 extends PEAR_Delegator { public function __construct() { parent::__construct(); } public function __destruct() { parent::__destruct(); } public function awesome1() { echo \"Isn't this Awesome!\"; } } class Extra2 { public function __construct() { } public function __destruct() { } public function awesome2() { echo \"Really, isn't this Awesome!\"; } } class Traditional extends PEAR_Delegator { public function __construct() { parent::__construct(); } public function __destruct() { parent::__destruct(); } public function showList() { \$args = func_get_args(); if (\$this->hasDelegate()) { return \$this->forwardMethod(\"showList\", \$args); } echo \$args[0]; unset(\$args[0]); foreach (\$args as \$arg) { echo \", \$arg\"; } } } class Delegate { var \$_multiplier; public function __construct(\$number) { \$this->_multiplier = \$number; } public function __destruct() { } public function showList(\$owner) { \$args = func_get_args(); echo \$args[1] * \$this->_multiplier; unset(\$args[0]); unset(\$args[1]); foreach (\$args as \$arg) { echo \", \" . \$arg * \$this->_multiplier; } } } class DelegateFalse { public function showListFalse(\$multiplier) { \$args = func_get_args(); echo \$args[1] * \$multiplier; unset(\$args[0]); unset(\$args[1]); foreach (\$args as \$arg) { echo ", " . \$arg * \$multiplier; } } } class DelegateMixed extends PEAR_Delegator { public function __construct() { \$this->addDelegate(false, 'DelegateFalse'); } public function showList(\$owner) { echo \"My owner, \$owner, says \\\"Show the list!\\\" But I don't want to show no stinking list!\"; } } </pre> "; class A extends PEAR_Delegator { var $message; public function __construct() { parent::__construct(); $this->addDelegate('ADelegate1'); $this->addDelegate('ADelegate2'); $this->addDelegate(new B); } public function __destruct() { parent::__destruct(); } } class ADelegate1 extends PEAR { public function __construct() { } public function __destruct() { } public function setMessage1($owner, $message) { $owner->message = $message; } } class ADelegate2 extends PEAR { public function __construct() { } public function __destruct() { } public function getMessage1($owner) { echo $owner->message; } } class B extends PEAR_Delegator { public function __construct() { parent::__construct(); $this->addDelegate('BDelegate1'); $this->addDelegate(new BDelegate2); } public function __destruct() { parent::__destruct(); } function foo() { echo get_class($this) . ": I will now call my foo delegate without knowing which one it is<BR>"; $args = func_get_args(); $this->forwardMethod("foo", $args); } function bar() { echo get_class($this) . ": I will now call my bar delegate without knowing which one it is<BR>"; $args = func_get_args(); $this->forwardMethod("bar", $args); } public function setMessage2($owner, $message) { $this->message = $message; } public function getMessage2() { echo $this->message; } } class BDelegate1 extends PEAR { public function __construct() { } public function __destruct() { } public function foo() { echo "BDelegate1:" . ": foo<BR>"; } } class BDelegate2 extends PEAR { public function __construct() { } public function __destruct() { } public function bar() { echo "BDelegate2" . ": bar<BR>"; } } class Extra1 extends PEAR_Delegator { public function __construct() { parent::__construct(); } public function __destruct() { parent::__destruct(); } public function awesome1() { echo "Isn't this Awesome!"; } } class Extra2 { public function __construct() { } public function __destruct() { } public function awesome2() { echo "Really, isn't this Awesome!"; } } class Traditional extends PEAR_Delegator { public function __construct() { parent::__construct(); } public function __destruct() { parent::__destruct(); } public function showList() { $args = func_get_args(); if ($this->hasDelegate()) { return $this->forwardMethod("showList", $args); } echo $args[0]; unset($args[0]); foreach ($args as $arg) { echo ", $arg"; } } } class Delegate { var $_multiplier; public function __construct($number) { $this->_multiplier = $number; } public function __destruct() { } public function showList($owner) { $args = func_get_args(); echo $args[1] * $this->_multiplier; unset($args[0]); unset($args[1]); foreach ($args as $arg) { echo ", " . $arg * $this->_multiplier; } } } class DelegateFalse { public function showListFalse($multiplier) { $args = func_get_args(); echo $args[1] * $multiplier; unset($args[0]); unset($args[1]); foreach ($args as $arg) { echo ", " . $arg * $multiplier; } } } class DelegateMixed extends PEAR_Delegator { public function __construct() { $this->addDelegate(false, 'DelegateFalse'); } public function showList($owner) { echo "My owner, $owner, says \"Show the list!\" But I don't want to show no stinking list!"; } } echo " <PRE> Before we get into the really cool stuff, here is the traditional delegate model: We instantiate our Traditional class and call its showList method: \$Traditional = new Traditional; \$Traditional->showList(1, 2, 3, 4, 5); Output: </PRE> "; $Traditional = new Traditional; $Traditional->showList(1, 2, 3, 4, 5); echo " <PRE> Let us now augment this method with our Delegate. We instantiate the Delegate class: \$Delegate = new Delegate(2); //value 2 sets multiplier of list; In the traditional model, only one delegate can be used, so: \$Traditional->setDelegate(\$Delegate); //Note, static delegates are also applicable. \$Traditional->showList(1, 2, 3, 4, 5); Output: </PRE> "; $Delegate = new Delegate(2); $Traditional->setDelegate($Delegate); $Traditional->showList(1, 2, 3, 4, 5); echo " <PRE> Thus, the delegate method is called instead. There is also support for existing classes, called false delegates, whose methods don't take the delegator as their first arguments: \$Traditional->addDelegate(false, 'DelegateFalse'); \$Traditional->showListFalse(3, 1, 2, 3, 4, 5); </PRE> "; $Traditional->addDelegate(false, 'DelegateFalse'); $Traditional->showListFalse(3, 1, 2, 3, 4, 5); echo " <PRE> In this way, mixed false and true methods can be used. First, lets set the delegate to the mixed version: \$Traditional->setDelegate(DelegateMixed); \$Traditional->showListFalse(3, 1, 2, 3, 4, 5); </PRE> "; $Traditional->setDelegate(new DelegateMixed); $Traditional->showListFalse(3, 1, 2, 3, 4, 5); echo " <PRE> \$Traditional->showList(1, 2, 3, 4, 5); </PRE> "; $Traditional->showList(1, 2, 3, 4, 5); echo " <PRE> We add the extensions so that every new delegator will have them: PEAR_Delegator::addExtensions(); If this is done after objects are created, simply call PEAR_Delegator::addExtensions(\$object1, \$object2, ...); to add the delegates. We now instantiate an A object: \$A = new A; You'll note that the A class defines no useable method. Nevertheless, when we call \$A->setMessage1(\"Hello\"), we get no error. To prove that it worked, let's recall the message with \$A->getMessage1(); Output: </PRE> "; PEAR_Delegator::addExtensions(); $A = new A; $A->setMessage1("Hello"); $A->getMessage1(); echo " <PRE> Let's now set and get the second message: \$A->setMessage2(\"World\"); \$A->getMessage2(); Output: </PRE> "; $A->setMessage2("World"); $A->getMessage2(); echo " <PRE> You'll note that the second message is not even stored in the A object! Now, let's try some of the other methods: //This calls the method foo in B which calls the method in BDelegate1, albeit transparently. \$A->foo(); Output: </PRE> "; $A->foo(); echo " <PRE> //This calls the method bar in B which calls the method in BDelegate2, albeit transparently. \$A->bar(); Output: </PRE> "; $A->bar(); echo " <PRE> You'll also notice that we simply instantiated A, which added the delegates to itself in its constructor. This is convenient, but it is certainly not the only way to add delegates. In fact, delegates can be added to a delegator at any time, and with classname or object. Let's add another delegate to A through an object: //We add this as an object, because it needs access to instance variables. If a delegate only //supplies static methods, then it can be added statically (by classname), in which no object //is created. \$Extra1 = new Extra1; \$A->addDelegate(\$Extra1); We can now call the methods in Extra1 on the A object as follows: \$A->awesome1(); Output: </PRE> "; $Extra1 = new Extra1; $A->addDelegate($Extra1); $A->awesome1(); echo " <PRE> Here, we add a new delegate to the Extra1 object by classname: \$Extra1->addDelegate(Extra2); Now we can use the methods in Extra2 on the A object as follows: \$A->awesome2(); Output: </PRE> "; $Extra1->addDelegate(new Extra2); $A->awesome2(); echo "<PRE> Now, let's explore the features of the PEAR_Delegate class. This class deals solely with the method forwarding mechanism and the delegate class hierarchy. Thus, all of its methods reflect that function. We have demonstrated the ability to add more delegates and so on, so we will now sequentially go though the other methods. It is generally necessary to have access to the delegate hierarchy, so we have getDelegate*() methods. To view the results, we will define a few functions along the way. function print_r_ElementTypes(\$array) { foreach (\$array as \$key => \$element) echo \"[\$key] => \" . get_class(\$element) . \"< BR>\"; } print_r_ElementTypes(\$A->getAllDelegates()); Output: </PRE> "; function print_r_ElementTypes($array) { foreach ($array as $key => $element) echo "[$key] => " . (is_string($element) ? $element : get_class($element)) . "<BR>"; } print_r_ElementTypes($A->getAllDelegates()); echo "<PRE> We can immediately see that this is the correct output of native delegates to the A Object. Now let's search for one particular kind of native delegate: print_r_ElementTypes(\$A->getDelegate('PEAR')); output: </PRE> "; print_r_ElementTypes($A->getDelegate('PEAR')); echo "<PRE> Or perhaps: print_r_ElementTypes(\$A->getDelegate('PEAR_Delegator')); output: </PRE> "; print_r_ElementTypes($A->getDelegate('PEAR_Delegator')); echo "<PRE> We can even search for multiple classes at a time: function print_r_ElementTypesR(\$array) { foreach (\$array as \$key => \$element) { echo \"[\$key]\\n\"; foreach (\$element as \$delegate) { echo \"\t\" . get_class(\$delegate) . \"\\n\"; } } } echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegate('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo \"< /PRE>\\n\"; output: </PRE> "; function print_r_ElementTypesR($array) { foreach ($array as $key => $element) { echo "[$key]\n"; foreach ($element as $delegate) { echo "\t" . (is_string($delegate) ? $delegate : get_class($delegate)) . "\n"; } } } echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegate('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo "</PRE>\n"; echo "<PRE> You'll notice that BDelegate1 returned B. This is an example of delegate subclassing. Before we continue, lets look at the structure of A: echo \"< PRE>\"; print_r(\$A); echo \"< /PRE>\"; </PRE> "; echo "<PRE>"; print_r($A); echo "</PRE>"; echo "<PRE> We should get the same thing even if we clone the delegator. \$clone = clone \$A echo \"< PRE>\"; print_r(\$clone); echo \"< /PRE>\"; </PRE> "; echo "<PRE>"; $clone = clone $A; print_r($clone); echo "</PRE>"; echo "<PRE> To make this clear, lets fetch the B Delegate from both and see if they are the same object: \$AB = \$A->getDelegate('B'); \$cloneB = \$clone->getDelegate('B'); echo (\$AB === \$cloneB) ? \"yes\" : \"no\"; Output: </PRE> "; $AB = $A->getDelegate('B'); $cloneB = $clone->getDelegate('B'); echo ($AB === $cloneB) ? "yes" : "no"; echo "<PRE> We can do the same thing for a delegate of the exact type: echo \$A->getDelegateExact('BDelegate1'); Output: </PRE> "; echo $A->getDelegateExact('BDelegate1'); echo "<PRE> Nothing! There is no delegate directly of the type BDelegate1, so nothing is returned. Conversely, print_r_ElementTypesR(\$A->getDelegateExact('PEAR')); Output: </PRE> "; print_r_ElementTypes($A->getDelegateExact('PEAR')); echo "<PRE> We can ask for multiple classes as well: echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegateExact('PEAR', 'B', 'ADelegate1', 'ADelegate2')); echo \"< /PRE>\\n\"; Output: </PRE> "; echo "<PRE>"; print_r_ElementTypesR($A->getDelegateExact('PEAR', 'B', 'ADelegate1', 'ADelegate2')); echo "</PRE>"; echo "<PRE> While one should only have access to the native delegates, methods are provided for delving deeper into the inheritance hierarchy. For instance: print_r_ElementTypes(\$A->getDelegateRecursive('PEAR')); </PRE> "; print_r_ElementTypes($A->getDelegateRecursive('PEAR')); echo "<PRE> This also handles requests for multiple classes: echo \"< PRE>\\n\"; print_r_ElementTypes(\$A->getDelegateRecursive('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo \"< /PRE>\\n\"; output: </PRE> "; echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegateRecursive('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo "</PRE>\n"; echo "<PRE> We likewise have a recursive exact method: echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegateRecursiveExact('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo \"< /PRE>\\n\"; Output: </PRE> "; echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegateRecursiveExact('PEAR', 'PEAR_Delegator', 'B', 'BDelegate1')); echo "</PRE>\n"; echo "<PRE> We can also fetch the native delegate that can resond to a particular method: echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegateForMethod(\"foo\", \"bar\", \"awesome1\", \"awesome2\", \"addDelegate\")); echo \"< /PRE>\\n\"; Output: </PRE> "; echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegateForMethod("foo", "bar", "awesome1", "awesome2", "addDelegate")); echo "</PRE>\n"; echo "<PRE> You can also get the first native responder found (native delegates are return in favor of nonnative): print_r_ElementTypes(\$A->getDelegateForMethodFirst(\"addDelegate\", \"notAMethod\")); Output: </PRE> "; print_r_ElementTypes($A->getDelegateForMethodFirst("addDelegate", "notAMethod")); echo "</PRE>\n"; echo "<PRE> The second argument makes the output an array, but if one argument is passed, the object is returned: echo get_class(\$A->getDelegateForMethodFirst(\"awesome2\")); Output: </PRE> "; echo get_class($A->getDelegateForMethodFirst("awesome2")); echo "<PRE> There are also recursive methods for getting delegates that respond to a particular method: echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegateForMethodRecursive(\"foo\",\"bar\", \"awesome1\", \"awesome2\", \"addDelegate\")); echo \"< /PRE>\\n\"; Output: </PRE> "; echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegateForMethodRecursive("foo", "bar", "awesome1", "awesome2", "addDelegate")); echo "</PRE>\n"; echo "<PRE> We can get the delegates that actually implement (or naturally inherit) the methods too: echo \"< PRE>\\n\"; print_r_ElementTypesR(\$A->getDelegateForMethodRecursiveExact(\"foo\",\"bar\", \"awesome1\", \"awesome2\", \"addDelegate\")); echo \"< /PRE>\\n\"; Output: </PRE> "; echo "<PRE>\n"; print_r_ElementTypesR($A->getDelegateForMethodRecursiveExact("foo", "bar", "awesome1", "awesome2", "addDelegate")); echo "</PRE>\n"; echo "<PRE> We can get the delegates that actually implement one method too: echo \"< PRE>\\n\"; print_r_ElementTypes(\$A->getDelegateForMethodRecursiveExact(\"foo\")); echo \"< /PRE>\\n\"; Output: </PRE> "; print_r_ElementTypes($A->getDelegateForMethodRecursiveExact("foo")); echo "<PRE> Much like you can use the instanceof operator to test for the kind of class, you can test for the kind of delegate inheritance an object has: echo (\$A->hasDelegate('B')) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->hasDelegate('B')) ? "yes" : "no"; echo "<PRE> This also takes into account delegates lower in the hierarchy: echo (\$A->hasDelegate('BDelegate1')) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->hasDelegate('BDelegate1')) ? "yes" : "no"; echo "<PRE> This works with objects too: echo (\$A->hasDelegate(\$A->getDelegateExact('BDelegate2'))) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->hasDelegate($A->getDelegateExact('BDelegate2'))) ? "yes" : "no"; echo "<PRE> You can also determine if a delegate of a specific class is available: echo (\$A->hasDelegateExact('BDelegate1')) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->hasDelegateExact('BDelegate1')) ? "yes" : "no"; echo "<PRE> But, echo (\$A->hasDelegateExact('B')) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->hasDelegateExact('B')) ? "yes" : "no"; echo "<PRE> These methods only test the delegates though. You can actually invoke a delegate-aware version of the is_a() function on any object: echo (PEAR_Delegator::isA(\$A, 'PEAR_Delegator')) ? \"yes\" : \"no\"; Output: </PRE> "; echo (PEAR_Delegator::isA($A, 'PEAR_Delegator')) ? "yes" : "no"; echo "<PRE> or: echo (PEAR_Delegator::isA(\$A, 'Extra2')) ? \"yes\" : \"no\"; Output: </PRE> "; echo (PEAR_Delegator::isA($A, 'PEAR_Delegator')) ? "yes" : "no"; echo "<PRE> This method also recognizes classes: echo (PEAR_Delegator::isA('PEAR_Delegator', 'PEAR')) ? \"yes\" : \"no\"; Output: </PRE> "; echo (PEAR_Delegator::isA('PEAR_Delegator', 'PEAR')) ? "yes" : "no"; echo "<PRE> There is also the is_aExact() method, but this is invoked when is_a() is called, so it is mostly used internally. You can also test whether or not an object implements a certain method, whether native or not: echo (\$A->respondsToMethod(\"awesome3\")) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->respondsToMethod("awesome3")) ? "yes" : "no"; echo "<PRE> Or, echo (\$A->respondsToMethod(\"awesome2\")) ? \"yes\" : \"no\"; Output: </PRE> "; echo ($A->respondsToMethod("awesome2")) ? "yes" : "no"; echo "<PRE> This actually calls an extension of method_exists(), which handles class objects as well: echo (PEAR_Delegator::methodExists('Extra2', \"awesome2\")) ? \"yes\" : \"no\"; Output: </PRE> "; echo (PEAR_Delegator::methodExists('Extra2', "awesome2")) ? "yes" : "no"; echo "<PRE> There are also methods for removing delegates. For instance: \$A->removeDelegate('Extra2', 'ADelegate2'); echo \"< PRE>\"; print_r(\$A->getAllDelegates()) echo \"< /PRE>\"; Output: </PRE> "; $A->removeDelegate('Extra2', 'ADelegate2'); echo "<PRE>"; print_r($A->getAllDelegates()); echo "</PRE>"; echo "<PRE> You'll notice the proper delegates are gone. We can also remove delegates recursively, though only exact delegates can be removed in this fashion, because it is superfluous otherwise: \$A->removeDelegateRecursiveExact('BDelegate2'); echo \"< PRE>\"; print_r(\$A->getAllDelegates()) echo \"< /PRE>\"; Output: </PRE> "; $A->removeDelegateRecursiveExact('BDelegate2'); echo "<PRE>"; print_r($A->getAllDelegates()); echo "</PRE>"; echo "<PRE> We can also remove all of the delegates: \$A->removeAllDelegates(); Now we can't call any delegate method (there should be a very specific error): \$A->setMessage1(); Output: </PRE> "; $A->removeAllDelegates(); $A->setMessage1(); ?> </BODY> </HTML>
« previous php.pear.cvs (#39188) next »