]> granicus.if.org Git - php/commitdiff
* initial commit of PEAR method autoloader
authorStig Bakken <ssb@php.net>
Mon, 10 Dec 2001 01:50:11 +0000 (01:50 +0000)
committerStig Bakken <ssb@php.net>
Mon, 10 Dec 2001 01:50:11 +0000 (01:50 +0000)
pear/PEAR/Autoloader.php [new file with mode: 0644]
pear/tests/pear_autoloader.phpt [new file with mode: 0644]

diff --git a/pear/PEAR/Autoloader.php b/pear/PEAR/Autoloader.php
new file mode 100644 (file)
index 0000000..6068fdc
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+
+if (!extension_loaded("overload")) {
+    // die hard without ext/overload
+    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
+}
+
+require_once "PEAR.php";
+
+/**
+ * This class is for objects where you want to separate the code for
+ * some methods into separate classes.  This is useful if you have a
+ * class with not-frequently-used methods that contain lots of code
+ * that you would like to avoid always parsing.
+ *
+ * The PEAR_Autoloader class provides autoloading and aggregation.
+ * The autoloading lets you set up in which classes the separated
+ * methods are found.  Aggregation is the technique used to import new
+ * methods, an instance of each class providing separated methods is
+ * stored and called every time the aggregated method is called.
+ *
+ * @author Stig Sæther Bakken <ssb@fast.no>
+ */
+class PEAR_Autoloader extends PEAR
+{
+    /**
+     * Map of methods and classes where they are defined
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_autoload_map = array();
+
+    /**
+     * Map of methods and aggregate objects
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_method_map = array();
+
+    /**
+     * Add one or more autoload entries.
+     *
+     * @param $method     string  which method to autoload
+     *
+     * @param $classname  string  (optional) which class to find the method in.
+     *                            If the $method parameter is an array, this
+     *                            parameter may be omitted (and will be ignored
+     *                            if not), and the $method parameter will be
+     *                            treated as an associative array with method
+     *                            names as keys and class names as values.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAutoload($method, $classname = null)
+    {
+       if (is_array($method)) {
+           $this->_autoload_map = array_merge($this->_autoload_map, $method);
+       } else {
+           $this->_autoload_map[$method] = $classname;
+       }
+    }
+
+    /**
+     * Remove an autoload entry.
+     *
+     * @param $method  string  which method to remove the autoload entry for
+     *
+     * @return bool TRUE if an entry was removed, FALSE if not
+     *
+     * @access public
+     */       
+    function removeAutoload($method)
+    {
+       $ok = isset($this->_autoload_map[$method]);
+       unset($this->_autoload_map[$method]);
+       return $ok;
+    }
+
+    /**
+     * Add an aggregate object to this object.  If the specified class
+     * is not defined, loading it will be attempted following PEAR's
+     * file naming scheme.  All the methods in the class will be
+     * aggregated, except private ones (name starting with an
+     * underscore) and constructors.
+     *
+     * @param $classname  string  what class to instantiate for the object.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAggregateObject($classname)
+    {
+       $classname = strtolower($classname);
+       if (!class_exists($classname)) {
+           $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
+           include_once $include_file;
+       }
+       $obj =& new $classname;
+       $methods = get_class_methods($classname);
+       foreach ($methods as $method) {
+           // don't import priviate methods and constructors
+           if ($method{0} != '_' && $method != $classname) {
+               $this->_method_map[$method] = $obj;
+           }
+       }
+    }
+
+    /**
+     * Remove an aggregate object.
+     *
+     * @param $classname  string  the class of the object to remove
+     *
+     * @return bool  TRUE if an object was removed, FALSE if not
+     *
+     * @access public
+     */
+    function removeAggregateObject($classname)
+    {
+       $ok = false;
+       $classname = strtolower($classname);
+       reset($this->_method_map);
+       while (list($method, $obj) = each($this->_method_map)) {
+           if (get_class($obj) == $classname) {
+               unset($this->_method_map[$method]);
+               $ok = true;
+           }
+       }
+       return $ok;
+    }
+
+    /**
+     * Overloaded object call handler, called each time an
+     * undefined/aggregated method is invoked.  This method repeats
+     * the call in the right aggregate object and passes on the return
+     * value.
+     *
+     * @param $method  string  which method that was called
+     *
+     * @param $args  array  An array of the parameters passed in the
+     *                      original call
+     *
+     * @return mixed  The return value from the aggregated method, or a PEAR
+     *                error if the called method was unknown.
+     */
+    function __call($method, $args)
+    {
+       if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
+           $this->addAggregateObject($this->_autoload_map[$method]);
+       }
+       if (isset($this->_method_map[$method])) {
+           return call_user_func_array(array($this->_method_map[$method], $method), $args);
+       }
+       return $this->raiseError("undefined method: $method");
+    }
+}
+
+overload("PEAR_Autoloader");
+
+?>
diff --git a/pear/tests/pear_autoloader.phpt b/pear/tests/pear_autoloader.phpt
new file mode 100644 (file)
index 0000000..d6c780d
--- /dev/null
@@ -0,0 +1,80 @@
+--TEST--
+PEAR_Autoloader
+--SKIPIF--
+<?php if (!extension_loaded("overload")) die("skip\n"); ?>
+--FILE--
+<?php
+
+require "../PEAR/Autoloader.php";
+
+class test1 extends PEAR_Autoloader {
+    function test1() {
+       $this->addAutoload(array(
+           'testfunc1' => 'testclass1',
+           'testfunca' => 'testclass1',
+           'testfunc2' => 'testclass2',
+           'testfuncb' => 'testclass2',
+       ));
+    }
+}
+
+class testclass1 {
+    function testfunc1($a) {
+       print "testfunc1 arg=";var_dump($a);
+       return 1;
+    }
+    function testfunca($a) {
+       print "testfunca arg=";var_dump($a);
+       return 2;
+    }
+}
+
+class testclass2 {
+    function testfunc2($b) {
+       print "testfunc2 arg=";var_dump($b);
+       return 3;
+    }
+    function testfuncb($b) {
+       print "testfuncb arg=";var_dump($b);
+       return 4;
+    }
+}
+
+function dump($obj) {
+    print "mapped methods:";
+    foreach ($obj->_method_map as $method => $object) {
+       print " $method";
+    }
+    print "\n";
+}
+
+function call($msg, $retval) {
+    print "calling $msg returned $retval\n";
+}
+
+$obj = new test1;
+dump($obj);
+call("testfunc1", $obj->testfunc1(2));
+dump($obj);
+call("testfunca", $obj->testfunca(2));
+dump($obj);
+call("testfunc2", $obj->testfunc2(2));
+dump($obj);
+call("testfuncb", $obj->testfuncb(2));
+dump($obj);
+
+?>
+--EXPECT--
+mapped methods:
+testfunc1 arg=int(2)
+calling testfunc1 returned 1
+mapped methods: testfunc1 testfunca
+testfunca arg=int(2)
+calling testfunca returned 2
+mapped methods: testfunc1 testfunca
+testfunc2 arg=int(2)
+calling testfunc2 returned 3
+mapped methods: testfunc1 testfunca testfunc2 testfuncb
+testfuncb arg=int(2)
+calling testfuncb returned 4
+mapped methods: testfunc1 testfunca testfunc2 testfuncb