diff --git a/lib/pear/HTML/AJAX.php b/lib/pear/HTML/AJAX.php new file mode 100755 index 00000000000..c17520f6ecd --- /dev/null +++ b/lib/pear/HTML/AJAX.php @@ -0,0 +1,1076 @@ + + * @author Arpad Ray + * @author David Coallier + * @author Elizabeth Smith + * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ + +/** + * This is a quick hack, loading serializers as needed doesn't work in php5 + */ +require_once "HTML/AJAX/Serializer/JSON.php"; +require_once "HTML/AJAX/Serializer/Null.php"; +require_once "HTML/AJAX/Serializer/Error.php"; +require_once "HTML/AJAX/Serializer/XML.php"; +require_once "HTML/AJAX/Serializer/PHP.php"; +require_once 'HTML/AJAX/Debug.php'; + +/** + * OO AJAX Implementation for PHP + * + * @category HTML + * @package AJAX + * @author Joshua Eichorn + * @author Arpad Ray + * @author David Coallier + * @author Elizabeth Smith + * @copyright 2005-2008 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ +class HTML_AJAX +{ + /** + * An array holding the instances were exporting + * + * key is the exported name + * + * row format is + * + * array('className'=>'','exportedName'=>'','instance'=>'','exportedMethods=>'') + * + * + * @var object + * @access private + */ + var $_exportedInstances = array(); + + /** + * Set the server url in the generated stubs to this value + * If set to false, serverUrl will not be set + * @var false|string + */ + var $serverUrl = false; + + /** + * What encoding your going to use for serializing data + * from php being sent to javascript. + * + * @var string JSON|PHP|Null + */ + var $serializer = 'JSON'; + + /** + * What encoding your going to use for unserializing data sent from javascript + * @var string JSON|PHP|Null + */ + var $unserializer = 'JSON'; + + /** + * Option to use loose typing for JSON encoding + * @var bool + * @access public + */ + var $jsonLooseType = true; + + /** + * Content-type map + * + * Used in to automatically choose serializers as needed + */ + var $contentTypeMap = array( + 'JSON' => 'application/json', + 'XML' => 'application/xml', + 'Null' => 'text/plain', + 'Error' => 'application/error', + 'PHP' => 'application/php-serialized', + 'Urlencoded' => 'application/x-www-form-urlencoded' + ); + + /** + * This is the debug variable that we will be passing the + * HTML_AJAX_Debug instance to. + * + * @param object HTML_AJAX_Debug + */ + var $debug; + + /** + * This is to tell if debug is enabled or not. If so, then + * debug is called, instantiated then saves the file and such. + */ + var $debugEnabled = false; + + /** + * This puts the error into a session variable is set to true. + * set to false by default. + * + * @access public + */ + var $debugSession = false; + + /** + * Boolean telling if the Content-Length header should be sent. + * + * If your using a gzip handler on an output buffer, or run into + * any compatability problems, try disabling this. + * + * @access public + * @var boolean + */ + var $sendContentLength = true; + + /** + * Make Generated code compatible with php4 by lowercasing all + * class/method names before exporting to JavaScript. + * + * If you have code that works on php4 but not on php5 then setting + * this flag can fix the problem. The recommended solution is too + * specify the class and method names when registering the class + * letting you have function case in php4 as well + * + * @access public + * @var boolean + */ + var $php4CompatCase = false; + + /** + * Automatically pack all generated JavaScript making it smaller + * + * If your using output compression this might not make sense + */ + var $packJavaScript = false; + + /** + * Holds current payload info + * + * @access private + * @var string + */ + var $_payload; + + /** + * Holds iframe id IF this is an iframe xmlhttprequest + * + * @access private + * @var string + */ + var $_iframe; + + /** + * Holds the list of classes permitted to be unserialized + * + * @access private + * @var array + */ + var $_allowedClasses = array(); + + /** + * Holds serializer instances + */ + var $_serializers = array(); + + /** + * PHP callbacks we're exporting + */ + var $_validCallbacks = array(); + + /** + * Interceptor instance + */ + var $_interceptor = false; + + /** + * Set a class to handle requests + * + * @param object &$instance An instance to export + * @param mixed $exportedName Name used for the javascript class, + * if false the name of the php class is used + * @param mixed $exportedMethods If false all functions without a _ prefix + * are exported, if an array only the methods + * listed in the array are exported + * + * @return void + */ + function registerClass(&$instance, $exportedName = false, + $exportedMethods = false) + { + $className = strtolower(get_class($instance)); + + if ($exportedName === false) { + $exportedName = get_class($instance); + if ($this->php4CompatCase) { + $exportedName = strtolower($exportedName); + } + } + + if ($exportedMethods === false) { + $exportedMethods = $this->_getMethodsToExport($className); + } + + + $index = strtolower($exportedName); + $this->_exportedInstances[$index] = array(); + $this->_exportedInstances[$index]['className'] = $className; + $this->_exportedInstances[$index]['exportedName'] = $exportedName; + $this->_exportedInstances[$index]['instance'] =& $instance; + $this->_exportedInstances[$index]['exportedMethods'] = $exportedMethods; + } + + /** + * Get a list of methods in a class to export + * + * This function uses get_class_methods to get a list of callable methods, + * so if you're on PHP5 extending this class with a class you want to export + * should export its protected methods, while normally only its public methods + * would be exported. All methods starting with _ are removed from the export list. + * This covers PHP4 style private by naming as well as magic methods in either PHP4 or PHP5 + * + * @param string $className Name of the class + * + * @return array all methods of the class that are public + * @access private + */ + function _getMethodsToExport($className) + { + $funcs = get_class_methods($className); + + foreach ($funcs as $key => $func) { + if (strtolower($func) === $className || substr($func, 0, 1) === '_') { + unset($funcs[$key]); + } else if ($this->php4CompatCase) { + $funcs[$key] = strtolower($func); + } + } + return $funcs; + } + + /** + * Generate the client Javascript code + * + * @return string generated javascript client code + */ + function generateJavaScriptClient() + { + $client = ''; + + $names = array_keys($this->_exportedInstances); + foreach ($names as $name) { + $client .= $this->generateClassStub($name); + } + return $client; + } + + /** + * Return the stub for a class + * + * @param string $name name of the class to generated the stub for, + * note that this is the exported name not the php class name + * + * @return string javascript proxy stub code for a single class + */ + function generateClassStub($name) + { + if (!isset($this->_exportedInstances[$name])) { + return ''; + } + + $client = "// Client stub for the {$this->_exportedInstances[$name]['exportedName']} PHP Class\n"; + $client .= "function {$this->_exportedInstances[$name]['exportedName']}(callback) {\n"; + $client .= "\tmode = 'sync';\n"; + $client .= "\tif (callback) { mode = 'async'; }\n"; + $client .= "\tthis.className = '{$this->_exportedInstances[$name]['exportedName']}';\n"; + if ($this->serverUrl) { + $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'{$this->serverUrl}','{$this->unserializer}');\n}\n"; + } else { + $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,false,'{$this->unserializer}');\n}\n"; + } + $client .= "{$this->_exportedInstances[$name]['exportedName']}.prototype = {\n"; + $client .= "\tSync: function() { this.dispatcher.Sync(); }, \n"; + $client .= "\tAsync: function(callback) { this.dispatcher.Async(callback); },\n"; + foreach ($this->_exportedInstances[$name]['exportedMethods'] as $method) { + $client .= $this->_generateMethodStub($method); + } + $client = substr($client, 0, (strlen($client)-2))."\n"; + $client .= "}\n\n"; + + if ($this->packJavaScript) { + $client = $this->packJavaScript($client); + } + return $client; + } + + /** + * Returns a methods stub + * + * @param string $method the method name + * + * @return string the js code + * @access private + */ + function _generateMethodStub($method) + { + $stub = "\t{$method}: function() { return ". + "this.dispatcher.doCall('{$method}',arguments); },\n"; + return $stub; + } + + /** + * Populates the current payload + * + * @return string the js code + * @access private + */ + function populatePayload() + { + if (isset($_REQUEST['Iframe_XHR'])) { + $this->_iframe = $_REQUEST['Iframe_XHR_id']; + if (isset($_REQUEST['Iframe_XHR_headers']) && + is_array($_REQUEST['Iframe_XHR_headers'])) { + foreach ($_REQUEST['Iframe_XHR_headers'] as $header) { + + $array = explode(':', $header); + $array[0] = strip_tags(strtoupper(str_replace('-', '_', $array[0]))); + //only content-length and content-type can go in without an + //http_ prefix - security + if (strpos($array[0], 'HTTP_') !== 0 + && strcmp('CONTENT_TYPE', $array[0]) + && strcmp('CONTENT_LENGTH', $array[0])) { + $array[0] = 'HTTP_' . $array[0]; + } + $_SERVER[$array[0]] = strip_tags($array[1]); + } + } + $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) + ? $_REQUEST['Iframe_XHR_data'] : ''); + + if (isset($_REQUEST['Iframe_XHR_method'])) { + $_GET['m'] = $_REQUEST['Iframe_XHR_method']; + } + if (isset($_REQUEST['Iframe_XHR_class'])) { + $_GET['c'] = $_REQUEST['Iframe_XHR_class']; + } + } + } + + /** + * Handle a ajax request if needed + * + * The current check is if GET variables c (class) and m (method) are set, + * more options may be available in the future + * + * @return boolean true if an ajax call was handled, false otherwise + */ + function handleRequest() + { + set_error_handler(array(&$this,'_errorHandler')); + if (function_exists('set_exception_handler')) { + set_exception_handler(array(&$this,'_exceptionHandler')); + } + if (isset($_GET['px'])) { + if ($this->_iframeGrabProxy()) { + restore_error_handler(); + if (function_exists('restore_exception_handler')) { + restore_exception_handler(); + } + return true; + } + } + + $class = strtolower($this->_getVar('c')); + $method = $this->_getVar('m'); + $phpCallback = $this->_getVar('cb'); + + + if (!empty($class) && !empty($method)) { + if (!isset($this->_exportedInstances[$class])) { + // handle error + trigger_error('Unknown class: '. $class); + } + if (!in_array(($this->php4CompatCase ? strtolower($method) : $method), + $this->_exportedInstances[$class]['exportedMethods'])) { + // handle error + trigger_error('Unknown method: ' . $method); + } + } else if (!empty($phpCallback)) { + if (strpos($phpCallback, '.') !== false) { + $phpCallback = explode('.', $phpCallback); + } + if (!$this->_validatePhpCallback($phpCallback)) { + restore_error_handler(); + if (function_exists('restore_exception_handler')) { + restore_exception_handler(); + } + return false; + } + } else { + restore_error_handler(); + if (function_exists('restore_exception_handler')) { + restore_exception_handler(); + } + return false; + } + + // auto-detect serializer to use from content-type + $type = $this->unserializer; + $key = array_search($this->_getClientPayloadContentType(), + $this->contentTypeMap); + if ($key) { + $type = $key; + } + $unserializer = $this->_getSerializer($type); + + $args = $unserializer->unserialize($this->_getClientPayload(), $this->_allowedClasses); + if (!is_array($args)) { + $args = array($args); + } + + if ($this->_interceptor !== false) { + $args = $this->_processInterceptor($class, $method, $phpCallback, $args); + } + + if (empty($phpCallback)) { + $ret = call_user_func_array(array(&$this->_exportedInstances[$class]['instance'], $method), $args); + } else { + $ret = call_user_func_array($phpCallback, $args); + } + + restore_error_handler(); + $this->_sendResponse($ret); + return true; + } + + /** + * Determines the content type of the client payload + * + * @return string + * a MIME content type + */ + function _getClientPayloadContentType() + { + //OPERA IS STUPID FIX + if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) { + $type = $this->_getServer('HTTP_X_CONTENT_TYPE'); + $pos = strpos($type, ';'); + + return strtolower($pos ? substr($type, 0, $pos) : $type); + } else if (isset($_SERVER['CONTENT_TYPE'])) { + $type = $this->_getServer('CONTENT_TYPE'); + $pos = strpos($type, ';'); + + return strtolower($pos ? substr($type, 0, $pos) : $type); + } + return 'text/plain'; + } + + /** + * Send a reponse adding needed headers and serializing content + * + * Note: this method echo's output as well as setting headers to prevent caching + * Iframe Detection: if this has been detected as an iframe response, it has to + * be wrapped in different code and headers changed (quite a mess) + * + * @param mixed $response content to serialize and send + * + * @access private + * @return void + */ + function _sendResponse($response) + { + if (is_object($response) && is_a($response, 'HTML_AJAX_Response')) { + $output = $response->getPayload(); + $content = $response->getContentType(); + + } elseif (is_a($response, 'PEAR_Error')) { + $serializer = $this->_getSerializer('Error'); + $output = $serializer->serialize(array( + 'message' => $response->getMessage(), + 'userinfo' => $response->getUserInfo(), + 'code' => $response->getCode(), + 'mode' => $response->getMode() + )); + $content = $this->contentTypeMap['Error']; + + } else { + $serializer = $this->_getSerializer($this->serializer); + $output = $serializer->serialize($response); + + $serializerType = $this->serializer; + // let a serializer change its output type + if (isset($serializer->serializerNewType)) { + $serializerType = $serializer->serializerNewType; + } + + if (isset($this->contentTypeMap[$serializerType])) { + $content = $this->contentTypeMap[$serializerType]; + } + } + // headers to force things not to be cached: + $headers = array(); + //OPERA IS STUPID FIX + if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) { + $headers['X-Content-Type'] = $content; + $content = 'text/plain'; + } + + if ($this->_sendContentLength()) { + $headers['Content-Length'] = strlen($output); + } + + $headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; + $headers['Last-Modified'] = gmdate("D, d M Y H:i:s").'GMT'; + $headers['Cache-Control'] = 'no-cache, must-revalidate'; + $headers['Pragma'] = 'no-cache'; + $headers['Content-Type'] = $content.'; charset=utf-8'; + + //intercept to wrap iframe return data + if ($this->_iframe) { + $output = $this->_iframeWrapper($this->_iframe, + $output, $headers); + $headers['Content-Type'] = 'text/html; charset=utf-8'; + } + + $this->_sendHeaders($headers); + echo $output; + } + + /** + * Decide if we should send a Content-length header + * + * @return bool true if it's ok to send the header, false otherwise + * @access private + */ + function _sendContentLength() + { + if (!$this->sendContentLength) { + return false; + } + $ini_tests = array( "output_handler", + "zlib.output_compression", + "zlib.output_handler"); + foreach ($ini_tests as $test) { + if (ini_get($test)) { + return false; + } + } + return (ob_get_level() <= 0); + } + + /** + * Actually send a list of headers + * + * @param array $array list of headers to send + * + * @access private + * @return void + */ + function _sendHeaders($array) + { + foreach ($array as $header => $value) { + header($header . ': ' . $value); + } + } + + /** + * Get an instance of a serializer class + * + * @param string $type Last part of the class name + * + * @access private + * @return HTML_AJAX_Serializer + */ + function _getSerializer($type) + { + if (isset($this->_serializers[$type])) { + return $this->_serializers[$type]; + } + + $class = 'HTML_AJAX_Serializer_'.$type; + + if ( (version_compare(phpversion(), 5, '>') && !class_exists($class, false)) + || (version_compare(phpversion(), 5, '<') && !class_exists($class)) ) { + // include the class only if it isn't defined + include_once "HTML/AJAX/Serializer/{$type}.php"; + } + + //handle JSON loose typing option for associative arrays + if ($type == 'JSON') { + $this->_serializers[$type] = new $class($this->jsonLooseType); + } else { + $this->_serializers[$type] = new $class(); + } + return $this->_serializers[$type]; + } + + /** + * Get payload in its submitted form, currently only supports raw post + * + * @access private + * @return string raw post data + */ + function _getClientPayload() + { + if (empty($this->_payload)) { + if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) { + $this->_payload = $GLOBALS['HTTP_RAW_POST_DATA']; + } else if (function_exists('file_get_contents')) { + // both file_get_contents() and php://input require PHP >= 4.3.0 + $this->_payload = file_get_contents('php://input'); + } else { + $this->_payload = ''; + } + } + return $this->_payload; + } + + /** + * stub for getting get vars - applies strip_tags + * + * @param string $var variable to get + * + * @access private + * @return string filtered _GET value + */ + function _getVar($var) + { + if (!isset($_GET[$var])) { + return null; + } else { + return strip_tags($_GET[$var]); + } + } + + /** + * stub for getting server vars - applies strip_tags + * + * @param string $var variable to get + * + * @access private + * @return string filtered _GET value + */ + function _getServer($var) + { + if (!isset($_SERVER[$var])) { + return null; + } else { + return strip_tags($_SERVER[$var]); + } + } + + /** + * Exception handler, passes them to _errorHandler to do the actual work + * + * @param Exception $ex Exception to be handled + * + * @access private + * @return void + */ + function _exceptionHandler($ex) + { + $this->_errorHandler($ex->getCode(), $ex->getMessage(), $ex->getFile(), $ex->getLine()); + } + + + /** + * Error handler that sends it errors to the client side + * + * @param int $errno Error number + * @param string $errstr Error string + * @param string $errfile Error file + * @param string $errline Error line + * + * @access private + * @return void + */ + function _errorHandler($errno, $errstr, $errfile, $errline) + { + if ($errno & error_reporting()) { + $e = new stdClass(); + $e->errNo = $errno; + $e->errStr = $errstr; + $e->errFile = $errfile; + $e->errLine = $errline; + + + $this->serializer = 'Error'; + $this->_sendResponse($e); + if ($this->debugEnabled) { + $this->debug = new HTML_AJAX_Debug($errstr, $errline, $errno, $errfile); + if ($this->debugSession) { + $this->debug->sessionError(); + } + $this->debug->_saveError(); + } + die(); + } + } + + /** + * Creates html to wrap serialized info for iframe xmlhttprequest fakeout + * + * @param string $id iframe instance id + * @param string $data data to pass + * @param string $headers headers to pass + * + * @access private + * @return string html page with iframe passing code + */ + function _iframeWrapper($id, $data, $headers = array()) + { + $string = '' + . ''; + return $string; + } + + /** + * Handles a proxied grab request + * + * @return bool true to end the response, false to continue trying to handle it + * @access private + */ + function _iframeGrabProxy() + { + if (!isset($_REQUEST['Iframe_XHR_id'])) { + trigger_error('Invalid iframe ID'); + return false; + } + $this->_iframe = $_REQUEST['Iframe_XHR_id']; + $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) ? $_REQUEST['Iframe_XHR_data'] : ''); + $url = urldecode($_GET['px']); + $url_parts = parse_url($url); + $urlregex = '#^https?://#i'; + + if (!preg_match($urlregex, $url) || $url_parts['host'] != $_SERVER['HTTP_HOST']) { + trigger_error('Invalid URL for grab proxy'); + return true; + } + $method = (isset($_REQUEST['Iframe_XHR_HTTP_method']) + ? strtoupper($_REQUEST['Iframe_XHR_HTTP_method']) + : 'GET'); + // validate method + if ($method != 'GET' && $method != 'POST') { + trigger_error('Invalid grab URL'); + return true; + } + // validate headers + $headers = ''; + if (isset($_REQUEST['Iframe_XHR_headers'])) { + foreach ($_REQUEST['Iframe_XHR_headers'] as $header) { + if (strpos($header, "\r") !== false + || strpos($header, "\n") !== false) { + trigger_error('Invalid grab header'); + return true; + } + $headers .= $header . "\r\n"; + } + } + // tries to make request with file_get_contents() + if (ini_get('allow_url_fopen') && version_compare(phpversion(), '5.0.0'. '>=')) { + $opts = array( + $url_parts['scheme'] => array( + 'method' => $method, + 'headers' => $headers, + 'content' => $this->_payload + ) + ); + $ret = @file_get_contents($url, false, stream_context_create($opts)); + if (!empty($ret)) { + $this->_sendResponse($ret); + return true; + } + } + // tries to make request using the curl extension + if (function_exists('curl_setopt')) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $ret = curl_exec($ch); + if ($ret !== false) { + curl_close($ch); + $this->_sendResponse($ret); + return true; + } + } + if (isset($url_parts['port'])) { + $port = $url_parts['port']; + } else { + $port = getservbyname(strtolower($url_parts['scheme']), 'tcp'); + if ($port === false) { + trigger_error('Grab proxy: Unknown port or service, defaulting to 80', E_USER_WARNING); + $port = 80; + } + } + if (!isset($url_parts['path'])) { + $url_parts['path'] = '/'; + } + if (!empty($url_parts['query'])) { + $url_parts['path'] .= '?' . $url_parts['query']; + } + $request = "$method {$url_parts['path']} HTTP/1.0\r\n" + . "Host: {$url['host']}\r\n" + . "Connection: close\r\n" + . "$headers\r\n"; + // tries to make request using the socket functions + $fp = fsockopen($_SERVER['HTTP_HOST'], $port, $errno, $errstr, 4); + if ($fp) { + fputs($fp, $request); + + $ret = ''; + $done_headers = false; + + while (!feof($fp)) { + $ret .= fgets($fp, 2048); + if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) { + continue; + } + $done_headers = true; + $ret = substr($ret, $contentpos + 4); + } + fclose($fp); + $this->_sendResponse($ret); + return true; + } + // tries to make the request using the socket extension + $host = gethostbyname($url['host']); + if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0 + || ($connected = socket_connect($socket, $host, $port)) < 0 + || ($written = socket_write($socket, $request)) < strlen($request)) { + trigger_error('Grab proxy failed: ' . socket_strerror($socket)); + return true; + } + + $ret = ''; + $done_headers = false; + + while ($out = socket_read($socket, 2048)) { + $ret .= $out; + if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) { + continue; + } + $done_headers = true; + $ret = substr($ret, $contentpos + 4); + } + socket_close($socket); + $this->_sendResponse($ret); + return true; + } + + /** + * Add a class or classes to those allowed to be unserialized + * + * @param mixed $classes the class or array of classes to add + * + * @access public + * @return void + */ + function addAllowedClasses($classes) + { + if (!is_array($classes)) { + $this->_allowedClasses[] = $classes; + } else { + $this->_allowedClasses = array_merge($this->_allowedClasses, $classes); + } + $this->_allowedClasses = array_unique($this->_allowedClasses); + } + + /** + * Checks that the given callback is callable and allowed to be called + * + * @param callback $callback the callback to check + * + * @return bool true if the callback is valid, false otherwise + * @access private + */ + function _validatePhpCallback($callback) + { + if (!is_callable($callback)) { + return false; + } + $sig = md5(serialize($callback)); + return isset($this->_validCallbacks[$sig]); + } + + /** + * Register a callback so it may be called from JS + * + * @param callback $callback the callback to register + * + * @access public + * @return void + */ + function registerPhpCallback($callback) + { + $this->_validCallbacks[md5(serialize($callback))] = 1; + } + + /** + * Make JavaScript code smaller + * + * Currently just strips whitespace and comments, needs to remain fast + * Strips comments only if they are not preceeded by code + * Strips /*-style comments only if they span over more than one line + * Since strings cannot span over multiple lines, it cannot be defeated by a + * string containing /* + * + * @param string $input Javascript to pack + * + * @access public + * @return string packed javascript + */ + function packJavaScript($input) + { + $stripPregs = array( + '/^\s*$/', + '/^\s*\/\/.*$/' + ); + $blockStart = '/^\s*\/\/\*/'; + $blockEnd = '/\*\/\s*(.*)$/'; + $inlineComment = '/\/\*.*\*\//'; + $out = ''; + + $lines = explode("\n", $input); + $inblock = false; + foreach ($lines as $line) { + $keep = true; + if ($inblock) { + if (preg_match($blockEnd, $line)) { + $inblock = false; + $line = preg_match($blockEnd, '$1', $line); + $keep = strlen($line) > 0; + } + } elseif (preg_match($inlineComment, $line)) { + $keep = true; + } elseif (preg_match($blockStart, $line)) { + $inblock = true; + $keep = false; + } + + if (!$inblock) { + foreach ($stripPregs as $preg) { + if (preg_match($preg, $line)) { + $keep = false; + break; + } + } + } + + if ($keep && !$inblock) { + $out .= trim($line)."\n"; + } + /* Enable to see what your striping out + else { + echo $line."
"; + }//*/ + } + $out .= "\n"; + return $out; + } + + /** + * Set an interceptor class + * + * An interceptor class runs during the process of handling a request, + * it allows you to run security checks globally. It also allows you to + * rewrite parameters + * + * You can throw errors and exceptions in your intercptor methods and + * they will be passed to javascript + * + * You can add interceptors are 3 levels + * For a particular class/method, this is done by add a method to you class + * named ClassName_MethodName($params) + * For a particular class, method ClassName($methodName,$params) + * Globally, method intercept($className,$methodName,$params) + * + * Only one match is done, using the most specific interceptor + * + * All methods have to return $params, if you want to empty all of the + * parameters return an empty array + * + * @param Object $instance an instance of you interceptor class + * + * @todo handle php callbacks + * @access public + * @return void + */ + function setInterceptor($instance) + { + $this->_interceptor = $instance; + } + + /** + * Attempt to intercept a call + * + * @param string $className Class Name + * @param string $methodName Method Name + * @param string $callback Not implemented + * @param array $params Array of parameters to pass to the interceptor + * + * @todo handle php callbacks + * @access private + * @return array Updated params + */ + function _processInterceptor($className,$methodName,$callback,$params) + { + + $m = $className.'_'.$methodName; + if (method_exists($this->_interceptor, $m)) { + return $this->_interceptor->$m($params); + } + + $m = $className; + if (method_exists($this->_interceptor, $m)) { + return $this->_interceptor->$m($methodName, $params); + } + + $m = 'intercept'; + if (method_exists($this->_interceptor, $m)) { + return $this->_interceptor->$m($className, $methodName, $params); + } + + return $params; + } +} + +/** + * PHP 4 compat function for interface/class exists + * + * @param string $class Class name + * @param bool $autoload Should the autoloader be called + * + * @access public + * @return bool + */ +function HTML_AJAX_Class_exists($class, $autoload) +{ + if (function_exists('interface_exists')) { + return class_exists($class, $autoload); + } else { + return class_exists($class); + } +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Action.php b/lib/pear/HTML/AJAX/Action.php new file mode 100755 index 00000000000..77ef9f57ea8 --- /dev/null +++ b/lib/pear/HTML/AJAX/Action.php @@ -0,0 +1,357 @@ + + * @copyright 2005-2008 Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://htmlajax.org/HTML_AJAX/Using%20haSerializer + */ + +/** + * Require the response class and json serializer + */ +require_once 'HTML/AJAX/Response.php'; +require_once 'HTML/AJAX/Serializer/JSON.php'; + +/** + * Helper class to eliminate the need to write javascript functions to deal with data + * + * This class creates information that can be properly serialized and used by + * the haaction serializer which eliminates the need for php users to write + * javascript for dealing with the information returned by an ajax method - + * instead the javascript is basically created for them + * + * @category HTML + * @package AJAX + * @author Elizabeth Smith + * @copyright 2005-2008 Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://htmlajax.org/HTML_AJAX/Using%20haSerializer + */ +class HTML_AJAX_Action extends HTML_AJAX_Response +{ + + /** + * Content type for the HAA response + * + * goofy but unique content type to tell the javascript which deserializer to use + * overrides HTML_AJAX_Response + * + * @var string + * @access public + */ + var $contentType = 'application/html_ajax_action'; + + /** + * An array holding all the actions for the class + * + * these have numeric keys and each new action is added on the end, remember + * these are executed in the order added + * + * @var array + * @access private + */ + var $_actions = array(); + + /** + * Prepends data to the attribute identified by id + * + * The data will be added to the beginning of the attribute identified by the id + * sent, id must be unique + * + * $response->prependAttr('myid', 'class', 'red'); + * $response->prependAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error')); + * + * @param string $id id for a specific item on the page
+ * @param string|array $attribute either an array of attribute/data pairs or a string attribute name + * @param mixed $data should be NULL if attribute is an array, otherwise data you wish to set the attribute to + * + * @return void + * @access public + */ + function prependAttr($id, $attribute, $data = null) + { + if (!is_null($data)) { + $attribute = array($attribute => $data); + } + $this->_actions[] = array( + 'action' => 'prepend', + 'id' => $id, + 'attributes' => $attribute, + 'data' => $data, + ); + return; + } + + /** + * Appends data to the attribute identified by id + * + * The data will be added to the end of the attribute identified by the id + * sent, id must be unique + * + * $response->appendAttr('myid', 'class', 'red'); + * $response->appendAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error')); + * + * @param string $id id for a specific item on the page
+ * @param string|array $attribute either an array of attribute/data pairs or a string attribute name + * @param mixed $data should be NULL if attribute is an array, otherwise data you wish to set the attribute to + * + * @return void + * @access public + */ + function appendAttr($id, $attribute, $data = null) + { + if (!is_null($data)) { + $attribute = array($attribute => $data); + } + $this->_actions[] = array( + 'action' => 'append', + 'id' => $id, + 'attributes' => $attribute, + ); + return; + } + + /** + * Assigns data to the attribute identified by id overwriting any previous values + * + * The data will be assigned to the attribute identified by the id + * sent, id must be unique + * + * $response->assignAttr('myid', 'class', 'red'); + * $response->assignAttr('myid', array('class' => 'red', 'innerHTML' => 'this is an error')); + * + * @param string $id id for a specific item on the page
+ * @param string|array $attribute either an array of attribute/data pairs or a string attribute name + * @param mixed $data should be NULL if attribute is an array, otherwise data you wish to set the attribute to + * + * @return void + * @access public + */ + function assignAttr($id, $attribute, $data = null) + { + if (!is_null($data)) { + $attribute = array($attribute => $data); + } + $this->_actions[] = array( + 'action' => 'assign', + 'id' => $id, + 'attributes' => $attribute, + ); + return; + } + + /** + * Deletes or assigns a value of an empty string to an attribute + * + * You may send either a single attribute or an array of attributes to clear + * + * $response->clearAttr('myid', 'class'); + * $response->clearAttr('myid', array('class', 'innerHTML')); + * + * @param string $id id for a specific item on the page
+ * @param string|array $attribute either an array of attribute/data pairs or a string attribute name + * + * @return void + * @access public + */ + function clearAttr($id, $attribute) + { + if (!is_array($attribute)) { + $attribute = array($attribute); + } + $this->_actions[] = array( + 'action' => 'clear', + 'id' => $id, + 'attributes' => $attribute, + ); + return; + } + + /** + * create a dom node via javascript + * + * higher level dom manipulation - creates a new node to insert into the dom + * You can control where the new node is inserted with two things, the insertion + * type and the id/ The type should be append, prepend, insertBefore, or insertAfter + * + * The id is a sibling node - like a div in the same div you want to add more to + * If you choose to append or prepend a node it will be placed at the beginning + * or end of the node with the id you send. If you choose insertBefore or + * InsertAfter it will be put right before or right after the node you specified. + * You can send an array of attributes to apply to the new node as well, + * so you don't have to create it and then assign Attributes. + * + * + * $response->createNode('myid', 'div'); + * $response->createNode('submit', 'input', + * array('id' => 'key', + * 'name' => 'key', + * 'type' => 'hidden', + * 'value' => $id), + * 'insertBefore'); + * + * + * @param string $id id for a specific item on the page
+ * @param string $tag html node to create + * @param array $attributes array of attribute -> data to fill the node with + * @param string $type append|prepend|insertBefore|insertAfter default is append + * + * @return void + * @access public + */ + function createNode($id, $tag, $attributes, $type = 'append') + { + $types = array('append', 'prepend', 'insertBefore', 'insertAfter'); + if (!in_array($type, $types)) { + $type = 'append'; + } + settype($attributes, 'array'); + $this->_actions[] = array( + 'action' => 'create', + 'id' => $id, + 'tag' => $tag, + 'attributes' => $attributes, + 'type' => $type, + ); + return; + } + + /** + * Replace a dom node via javascript + * + * higher level dom manipulation - replaces one node with another + * This can be used to replace a div with a form for inline editing + * use innerHtml attribute to change inside text + * + * $response->replaceNode('myid', 'div', array('innerHTML' => 'loading complete')); + * $response->replaceNode('mydiv', 'form', array('innerHTML' => $form)); + * + * @param string $id id for a specific item on the page
+ * @param string $tag html node to create + * @param array $attributes array of attribute -> data to fill the node with + * + * @return void + * @access public + */ + function replaceNode($id, $tag, $attributes) + { + settype($attributes, 'array'); + $this->_actions[] = array( + 'action' => 'replace', + 'id' => $id, + 'tag' => $tag, + 'attributes' => $attributes, + ); + return; + } + + /** + * Delete a dom node via javascript + * + * $response->removeNode('myid'); + * $response->removeNode(array('mydiv', 'myform')); + * + * @param string $id id for a specific item on the page
+ * + * @return void + * @access public + */ + function removeNode($id) + { + $this->_actions[] = array( + 'action' => 'remove', + 'id' => $id, + ); + return; + } + + /** + * Send a string to a javascript eval + * + * This will send the data right to the eval javascript function, it will NOT + * allow you to dynamically add a javascript function for use later on because + * it is constrined by the eval function + * + * @param string $data string to pass to the alert javascript function + * + * @return void + * @access public + */ + function insertScript($data) + { + $this->_actions[] = array( + 'action' => 'script', + 'data' => $data, + ); + return; + } + + /** + * Send a string to a javascript alert + * + * This will send the data right to the alert javascript function + * + * @param string $data string to pass to the alert javascript function + * + * @return void + * @access public + */ + function insertAlert($data) + { + $this->_actions[] = array( + 'action' => 'alert', + 'data' => $data, + ); + return; + } + + /** + * Returns the serialized content of the response class + * + * we actually use the json serializer underneath, so we send the actions array + * to the json serializer and return the data + * + * @return string serialized response content + * @access public + */ + function getPayload() + { + $serializer = new HTML_AJAX_Serializer_JSON(); + return $serializer->serialize($this->_actions); + } + + /** + * Adds all the actions from one response object to another, feature request + * #6635 at pear.php.net + * + * @param object &$instance referenced HTML_AJAX_Action object + * + * @return array + * @access public + */ + function combineActions(&$instance) + { + $this->_actions = array_merge($this->_actions, $instance->retrieveActions()); + } + + /** + * to follow proper property access we need a way to retrieve the private + * actions array + * + * @return array + * @access public + */ + function retrieveActions() + { + return $this->_actions; + } +} +?> diff --git a/lib/pear/HTML/AJAX/Debug.php b/lib/pear/HTML/AJAX/Debug.php new file mode 100755 index 00000000000..7e11f7ddf94 --- /dev/null +++ b/lib/pear/HTML/AJAX/Debug.php @@ -0,0 +1,144 @@ + + * @copyright 2005 David Coallier + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + */ +class HTML_AJAX_Debug { + // {{{ properties + /** + * This is the error message. + * + * @access private + */ + var $errorMsg; + + /** + * The line where the error occured. + * + * @access private + */ + var $errorLine; + + /** + * The error code. + * + * @access private + */ + var $errorCode; + + /** + * The file where the error occured. + * + * @access private + */ + var $errorFile; + + /** + * Time the error occured + * + * @access private + */ + var $_timeOccured; + + /** + * The whole error itself + * + * @access private + * @see errorMsg + * @see errorLine + * @see errorFile + * @see errorCode + */ + var $error; + + /** + * The file to save the error to. + * + * @access private + * @default ajaxErrLog.xml + */ + var $file = 'ajaxErrLog.xml'; + // }}} + // {{{ constructor + /** + * The constructor. + * + * @param string $errorMsg The error message. + * @param string $errLine The line where error occured. + * @param string $errCode The error Code. + * @param string $errFile The file where error occured. + */ + function HTML_AJAX_Debug($errMsg, $errLine, $errCode, $errFile) + { + $this->errorMsg = $errMsg; + $this->errorLine = $errLine; + $this->errorCode = $errCode; + $this->errorFile = $errFile; + $this->_timeOccured = date("Y-m-d H:i:s", time()); + $this->xmlError(); + } + // }}} + // {{{ xmlError + /** + * This functions formats the error to xml format then we can save it. + * + * @access protected + * @return $this->error the main error. + */ + function xmlError() + { + $error = " {$this->_timeOccured}" . HTML_AJAX_NEWLINE; + $error .= " {$this->errorMsg}" . HTML_AJAX_NEWLINE; + $error .= " {$this->errorCode}" . HTML_AJAX_NEWLINE; + $error .= " {$this->errorLine}" . HTML_AJAX_NEWLINE; + $error .= " {$this->errorFile}" . HTML_AJAX_NEWLINE . HTML_AJAX_NEWLINE; + return $this->error = $error; + } + // }}} + // {{{ sessionError + /** + * This function pushes the array $_SESSION['html_ajax_debug']['time'][] + * with the values inside of $this->error + * + * @access public + */ + function sessionError() + { + $_SESSION['html_ajax_debug']['time'][] = $this->error; + } + // }}} + // {{{ _saveError + /** + * This function saves the error to a file + * appending to this file. + * + * @access private. + */ + function _saveError() + { + if ($handle = fopen($this->file, 'a')) { + fwrite($handle, $this->error); + } + } + // }}} +} +// }}} +?> diff --git a/lib/pear/HTML/AJAX/Helper.php b/lib/pear/HTML/AJAX/Helper.php new file mode 100755 index 00000000000..ecf54974a43 --- /dev/null +++ b/lib/pear/HTML/AJAX/Helper.php @@ -0,0 +1,186 @@ + + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + */ + +/** + * HTML/JavaScript Generation Helper + * + * @category HTML + * @package AJAX + * @author Joshua Eichorn + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ +class HTML_AJAX_Helper +{ + /** + * URL where an HTML_AJAX_Server instance is serving up clients and taking ajax requests + */ + var $serverUrl = 'server.php'; + + /** + * JS libraries to include + * + * @var array + */ + var $jsLibraries = array('Util','Main','Request','HttpClient','Dispatcher','Behavior','Loading','JSON','iframe'); + + /** + * Remote class stubs to include + */ + var $stubs = array(); + + /** + * Combine jsLibraries into a single require and remove duplicates + */ + var $combineJsIncludes = false; + + /** + * Include all needed libraries, stubs, and set defaultServer + * + * @return string + */ + function setupAJAX() + { + $libs = array(0=>array()); + $combinedLibs = array(); + + $this->jsLibraries = array_unique($this->jsLibraries); + foreach($this->jsLibraries as $library) { + if (is_array($library)) { + $library = array_unique($library); + $combinedLibs = array_merge($combinedLibs,$library); + $libs[] = implode(',',$library); + } + else { + $libs[0][] = $library; + $combinedLibs[] = $library; + } + } + $libs[0] = implode(',',$libs[0]); + + $sep = '?'; + if (strstr($this->serverUrl,'?')) { + $sep = '&'; + } + + $ret = ''; + if ($this->combineJsIncludes == true) { + $list = implode(',',$combinedLibs); + $ret .= "\n"; + } + else { + foreach($libs as $list) { + $ret .= "\n"; + } + } + + if (count($this->stubs) > 0) { + $stubs = implode(',',$this->stubs); + $ret .= "\n"; + } + $ret .= $this->encloseInScript('HTML_AJAX.defaultServerUrl = '.$this->escape($this->serverUrl)); + return $ret; + } + + /** + * Create a custom Loading message + * + * @param string $body HTML body of the loading div + * @param string $class CSS class of the div + * @param string $style style tag of the loading div + */ + function loadingMessage($body, $class = 'HTML_AJAX_Loading', + $style = 'position: absolute; top: 0; right: 0; background-color: red; width: 80px; padding: 4px; display: none') + { + return "
{$body}
\n"; + } + + /** + * Update the contents of an element using ajax + * + * @param string $id id of the element to update + * @param string|array $update Either a url to update with or a array like array('class','method') + * @param string $type replace or append + * @param boolean $enclose + */ + function updateElement($id, $update, $type, $enclose = false) { + if (is_array($update)) { + $updateStr = ""; + $comma = ''; + foreach($update as $item) { + $updateStr .= $comma.$this->escape($item); + $comma = ','; + } + } + else { + $updateStr = $this->escape($update); + } + + $ret = "HTML_AJAX.{$type}(".$this->escape($id).",{$updateStr});\n"; + if ($enclose) { + $ret = $this->encloseInScript($ret); + } + return $ret; + } + + /** + * Escape a string and add quotes allowing it to be a javascript paramater + * + * @param string $input + * @return string + * @todo do something here besides a quick hack + */ + function escape($input) { + return "'".addslashes($input)."'"; + } + + /** + * Enclose a string in a script block + * + * @param string $input + * @return string + */ + function encloseInScript($input) { + return '\n"; + } + + /** + * Generate a JSON String + * + * @param string $input + * @return string + */ + function jsonEncode($input) { + require_once 'HTML/AJAX/Serializer/JSON.php'; + + $s = new HTML_AJAX_Serializer_JSON(); + return $s->serialize($input); + } + + /** + * Check the request headers to see if this is an AJAX request + * + * @return boolean + */ + function isAJAX() { + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { + return true; + } + return false; + } +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Response.php b/lib/pear/HTML/AJAX/Response.php new file mode 100755 index 00000000000..d3deb089159 --- /dev/null +++ b/lib/pear/HTML/AJAX/Response.php @@ -0,0 +1,78 @@ + + * @copyright 2005-2006 Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + */ + +/** + * Require the main AJAX library + */ +require_once 'HTML/AJAX.php'; + +/** + * Simple base class for a response object to use as an ajax callback + * + * This is the base response class, more interesting response classes can be + * built off of this, simply give it a unique content type and override the + * getPayload method or fill the payload property with your extended classes's + * serialized content + * + * @version $Id$ + */ +class HTML_AJAX_Response +{ + + /** + * The base response class uses plain text so use that content type + * + * @var string + * @access public + */ + var $contentType = 'text/plain'; + + /** + * Assign a string to this variable to use the bare response class + * + * @var string + * @access public + */ + var $payload = ''; + + /** + * Returns the appropriate content type + * + * This normally simply returns the contentType property but can be overridden + * by an extending class if the content-type is variable + * + * @return string appropriate content type + * @access public + */ + function getContentType() + { + return $this->contentType; + } + + /** + * Returns the serialized content of the response class + * + * You can either fill the payload elsewhere in an extending class and leave + * this method alone, or you can override it if you have a different type + * of payload that needs special treatment + * + * @return string serialized response content + * @access public + */ + function getPayload() + { + return $this->payload; + } +} +?> diff --git a/lib/pear/HTML/AJAX/Serializer/Error.php b/lib/pear/HTML/AJAX/Serializer/Error.php new file mode 100755 index 00000000000..736a3e4b82b --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/Error.php @@ -0,0 +1,20 @@ + + * @copyright 2005 Joshua Eichorn + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ +class HTML_AJAX_Serializer_Error extends HTML_AJAX_Serializer_JSON +{ + +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Serializer/JSON.php b/lib/pear/HTML/AJAX/Serializer/JSON.php new file mode 100755 index 00000000000..62a70040452 --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/JSON.php @@ -0,0 +1,96 @@ + + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/PackageName + */ +// {{{ class HTMLA_AJAX_Serialize_JSON +class HTML_AJAX_Serializer_JSON +{ + // {{{ variables-properties + /** + * JSON instance + * @var HTML_AJAX_JSON + * @access private + */ + var $_json; + + /** + * use json php extension http://www.aurore.net/projects/php-json/ + * @access private + */ + var $_jsonext; + + /** + * use loose typing to decode js objects into php associative arrays + * @access public + */ + var $loose_type; + + // }}} + // {{{ constructor + function HTML_AJAX_Serializer_JSON($use_loose_type = true) + { + $this->loose_type = (bool) $use_loose_type; + $this->_jsonext = $this->_detect(); + if(!$this->_jsonext) { + $use_loose_type = ($this->loose_type) ? SERVICES_JSON_LOOSE_TYPE : 0; + $this->_json = new HTML_AJAX_JSON($use_loose_type); + } + } + // }}} + // {{{ serialize + /** + * This function serializes and input passed to it. + * + * @access public + * @param string $input The input to serialize. + * @return string $input The serialized input. + */ + function serialize($input) + { + if($this->_jsonext) { + return json_encode($input); + } else { + return $this->_json->encode($input); + } + } + // }}} + // {{{ unserialize + /** + * this function unserializes the input passed to it. + * + * @access public + * @param string $input The input to unserialize + * @return string $input The unserialized input. + */ + function unserialize($input) + { + if($this->_jsonext) { + return json_decode($input, $this->loose_type); + } else { + return $this->_json->decode($input); + } + } + // }}} + // {{{ _detect + /** + * detects the loaded extension + */ + function _detect() + { + return extension_loaded('json'); + } + // }}} +} +// }}} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Serializer/Null.php b/lib/pear/HTML/AJAX/Serializer/Null.php new file mode 100755 index 00000000000..026bd5846cd --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/Null.php @@ -0,0 +1,28 @@ + + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/PackageName + */ +class HTML_AJAX_Serializer_Null +{ + + function serialize($input) + { + return $input; + } + + function unserialize($input) + { + return $input; + } +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Serializer/PHP.php b/lib/pear/HTML/AJAX/Serializer/PHP.php new file mode 100755 index 00000000000..a3000ad0328 --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/PHP.php @@ -0,0 +1,88 @@ + + * @copyright 2005 Arpad Ray + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ +class HTML_AJAX_Serializer_PHP +{ + function serialize($input) + { + return serialize($input); + } + + /** + * Unserializes the given string + * + * Triggers an error if a class is found which is not + * in the provided array of allowed class names. + * + * @param string $input + * the serialized string to process + * @param array $allowedClasses + * an array of class names to check objects against + * before instantion + * @return mixed + * the unserialized variable on success, or false on + * failure. If this method fails it will also trigger + * a warning. + */ + function unserialize($input, $allowedClasses) + { + if (version_compare(PHP_VERSION, '4.3.10', '<') + || (substr(PHP_VERSION, 0, 1) == '5' && version_compare(PHP_VERSION, '5.0.3', '<'))) { + trigger_error('Unsafe version of PHP for native unserialization'); + return false; + } + $classes = $this->_getSerializedClassNames($input); + if ($classes === false) { + trigger_error('Invalidly serialized string'); + return false; + } + $diff = array_diff($classes, $allowedClasses); + if (!empty($diff)) { + trigger_error('Class(es) not allowed to be serialized'); + return false; + } + return unserialize($input); + } + + /** + * Extract class names from serialized string + * + * Adapted from code by Harry Fuecks + * + * @param string $string + * the serialized string to process + * @return mixed + * an array of class names found, or false if the input + * is invalidly formed + */ + function _getSerializedClassNames($string) { + // Strip any string representations (which might contain object syntax) + while (($pos = strpos($string, 's:')) !== false) { + $pos2 = strpos($string, ':', $pos + 2); + if ($pos2 === false) { + // invalidly serialized string + return false; + } + $end = $pos + 2 + substr($string, $pos + 2, $pos2) + 1; + $string = substr($string, 0, $pos) . substr($string, $end); + } + + // Pull out the class names + preg_match_all('/O:[0-9]+:"(.*)"/U', $string, $matches); + + // Make sure names are unique (same object serialized twice) + return array_unique($matches[1]); + } +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Serializer/Urlencoded.php b/lib/pear/HTML/AJAX/Serializer/Urlencoded.php new file mode 100755 index 00000000000..a26ff1e8839 --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/Urlencoded.php @@ -0,0 +1,67 @@ + + */ +if (!function_exists('http_build_query')) { + function http_build_query($formdata, $numeric_prefix = null, $key = null) + { + $res = array(); + foreach ((array)$formdata as $k => $v) { + if (is_resource($v)) { + return null; + } + $tmp_key = urlencode(is_int($k) ? $numeric_prefix . $k : $k); + if (!is_null($key)) { + $tmp_key = $key . '[' . $tmp_key . ']'; + } + $res[] = (is_scalar($v)) + ? $tmp_key . '=' . urlencode($v) + : http_build_query($v, null , $tmp_key); + } + $separator = ini_get('arg_separator.output'); + if (strlen($separator) == 0) { + $separator = '&'; + } + return implode($separator, $res); + } +} +// }}} +// {{{ class HTML_AJAX_Serialize_Urlencoded +/** + * URL Encoding Serializer + * + * @category HTML + * @package AJAX + * @author Arpad Ray + * @author David Coallier + * @copyright 2005 Arpad Ray + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/HTML_AJAX + */ +class HTML_AJAX_Serializer_Urlencoded +{ + // {{{ serialize + function serialize($input) + { + return http_build_query(array('_HTML_AJAX' => $input)); + } + // }}} + // {{{ unserialize + function unserialize($input) + { + parse_str($input, $ret); + return (isset($ret['_HTML_AJAX']) ? $ret['_HTML_AJAX'] : $ret); + } + // }}} +} +// }}} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?> diff --git a/lib/pear/HTML/AJAX/Serializer/XML.php b/lib/pear/HTML/AJAX/Serializer/XML.php new file mode 100755 index 00000000000..6856f72b167 --- /dev/null +++ b/lib/pear/HTML/AJAX/Serializer/XML.php @@ -0,0 +1,88 @@ + + * @copyright 2005-2006 Elizabeth Smith + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: 0.5.6 + * @link http://pear.php.net/package/PackageName + */ +class HTML_AJAX_Serializer_XML +{ + + /** + * Serializes a domdocument into an xml string + * + * Uses dom or domxml to dump a string from a DomDocument instance + * remember dom is always the default and this will die horribly without + * a domdocument instance + * + * @access public + * @param object $input instanceof DomDocument + * @return string xml string of DomDocument + */ + function serialize($input) + { + if(empty($input)) + { + return $input; + } + // we check for the dom extension + elseif (extension_loaded('Dom')) + { + return $input->saveXml(); + } + // then will check for domxml + elseif (extension_loaded('Domxml')) + { + return $input->dump_mem(); + } + // will throw an error + else { + $error = new HTML_AJAX_Serializer_Error(); + $this->serializerNewType = 'Error'; + return $error->serialize(array('errStr'=>"Missing PHP Dom extension direct XML won't work")); + } + } + + /** + * Unserializes the xml string sent from the document + * + * Uses dom or domxml to pump a string into a DomDocument instance + * remember dom is always the default and this will die horribly without + * one or the other, and will throw warnings if you have bad xml + * + * @access public + * @param string $input The input to serialize. + * @return object instanceofDomDocument + */ + function unserialize($input) + { + if(empty($input)) + { + return $input; + } + // we check for the dom extension + elseif (extension_loaded('Dom')) + { + $doc = new DOMDocument(); + $doc->loadXML($input); + return $doc; + } + // then we check for the domxml extensions + elseif (extension_loaded('Domxml')) + { + return domxml_open_mem($input); + } + // we give up and just return the xml directly + else + { + return $input; + } + } +} +?> diff --git a/lib/pear/HTML/AJAX/Server.php b/lib/pear/HTML/AJAX/Server.php new file mode 100755 index 00000000000..b5fc98272de --- /dev/null +++ b/lib/pear/HTML/AJAX/Server.php @@ -0,0 +1,725 @@ + + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: @package_version@ + */ + +/** + * Require the main AJAX library + */ +require_once 'HTML/AJAX.php'; + +/** + * Class for creating an external AJAX server + * + * Can be used in 2 different modes, registerClass mode where you create an instance of the server and add the classes that will be registered + * and then run handle request + * + * Or you can extend it and add init{className} methods for each class you want to export + * + * Client js generation is exposed through 2 _GET params client and stub + * Setting the _GET param client to `all` will give you all the js classes needed + * Setting the _GET param stub to `all` will give you stubs of all registered classes, you can also set it too just 1 class + * + * @category HTML + * @package AJAX + * @author Joshua Eichorn + * @copyright 2005 Joshua Eichorn + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version Release: @package_version@ + * @link http://pear.php.net/package/PackageName + */ +class HTML_AJAX_Server +{ + + /** + * Client options array if set to true the code looks at _GET + * @var bool|array + */ + var $options = true; + + /** + * HTML_AJAX instance + * @var HTML_AJAX + */ + var $ajax; + + /** + * Set to true if your extending the server to add init{className methods} + * @var boolean + * @access public + */ + var $initMethods = false; + + /** + * Location on filesystem of client javascript library + * @var false|string if false the default pear data dir location is used + */ + var $clientJsLocation = false; + + /** + * An array of options that tell the server howto Cache output + * + * The rules are functions that make etag hash used to see if the client needs to download updated content + * If you extend this class you can make your own rule function the naming convention is _cacheRule{RuleName} + * + * + * array( + * 'httpCacheClient' => true, // send 304 headers for responses to ?client=* requests + * 'ClientCacheRule' => 'File', // create a hash from file names and modified times, options: file|content + * 'ClientCacheExpects'=> 'files', // what type of content to send to the hash function, options: files|classes|content + * 'httpCacheStub' => true, // send 304 headers for responses to ?stub=* requests + * 'StubCacheRule' => 'Api', // create a hash from the exposed api, options: api|content + * 'StubCacheExpects'=> 'classes', // what type of content to send to the hash function, options: files|classes|content + * ) + * + * + * @var array + * @access public + */ + var $cacheOptions = array( + 'httpCacheClient' => true, + 'ClientCacheRule' => 'file', + 'ClientCacheExpects' => 'files', + 'httpCacheStub' => true, + 'StubCacheRule' => 'api', + 'StubCacheExpects' => 'classes', + ); + + /** + * Compression Options + * + * + * array( + * 'enabled' => false, // enable compression + * 'type' => 'gzip' // the type of compression to do, options: gzip + * ) + * + * + * @var array + * @access public + */ + var $compression = array( + 'enabled' => false, + 'type' => 'gzip' + ); + + /** + * Javascript library names and there path + * + * the return of $this->clientJsLocation(), is prepended before running readfile on them + * + * @access public + * @var array + */ + var $javascriptLibraries = array( + 'all' => 'HTML_AJAX.js', + 'html_ajax' => 'HTML_AJAX.js', + 'html_ajax_lite'=> 'HTML_AJAX_lite.js', + 'json' => 'serializer/JSON.js', + 'request' => 'Request.js', + 'main' => array('Compat.js','Main.js','clientPool.js'), + 'httpclient' => 'HttpClient.js', + 'dispatcher' => 'Dispatcher.js', + 'util' => 'util.js', + 'loading' => 'Loading.js', + 'phpserializer' => 'serializer/phpSerializer.js', + 'urlserializer' => 'serializer/UrlSerializer.js', + 'haserializer' => 'serializer/haSerializer.js', + 'clientpool' => 'clientPool.js', + 'iframe' => 'IframeXHR.js', + 'alias' => 'Alias.js', + 'queues' => 'Queue.js', + 'behavior' => array('behavior/behavior.js','behavior/cssQuery-p.js'), + + // rules to help you use a minimal library set + 'standard' => array('Compat.js','clientPool.js','util.js','Main.js','HttpClient.js','Request.js','serializer/JSON.js', + 'Loading.js','serializer/UrlSerializer.js','Alias.js','behavior/behavior.js','behavior/cssQuery-p.js'), + 'jsonrpc' => array('Compat.js','util.js','Main.js','clientPool.js','HttpClient.js','Request.js','serializer/JSON.js'), + 'proxyobjects' => array('Compat.js','util.js','Main.js','clientPool.js','Request.js','serializer/JSON.js','Dispatcher.js'), + + // BC rules + 'priorityqueue' => 'Queue.js', + 'orderedqueue' => 'Queue.js', + ); + + /** + * Custom paths to use for javascript libraries, if not set {@link clientJsLocation} is used to find the system path + * + * @access public + * @var array + * @see registerJsLibrary + */ + var $javascriptLibraryPaths = array(); + + /** + * Array of className => init methods to call, generated from constructor from initClassName methods + * + * @access protected + */ + var $_initLookup = array(); + + + /** + * Constructor creates the HTML_AJAX instance + * + * @param string $serverUrl (Optional) the url the client should be making a request too + */ + function HTML_AJAX_Server($serverUrl = false) + { + $this->ajax = new HTML_AJAX(); + + // parameters for HTML::AJAX + $parameters = array('stub', 'client'); + + // keep in the query string all the parameters that don't belong to AJAX + // we remove all string like "parameter=something&". Final '&' can also + // be '&' (to be sure) and is optional. '=something' is optional too. + $querystring = ''; + if (isset($_SERVER['QUERY_STRING'])) { + $querystring = preg_replace('/(' . join('|', $parameters) . ')(?:=[^&]*(?:&(?:amp;)?|$))?/', '', $this->ajax->_getServer('QUERY_STRING')); + } + + // call the server with this query string + if ($serverUrl === false) { + $serverUrl = htmlentities($this->ajax->_getServer('PHP_SELF')); + } + + if (substr($serverUrl,-1) != '?') { + $serverUrl .= '?'; + } + $this->ajax->serverUrl = $serverUrl . $querystring; + + $methods = get_class_methods($this); + foreach($methods as $method) { + if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) { + $this->_initLookup[strtolower($match[1])] = $method; + } + } + } + + /** + * Handle a client request, either generating a client or having HTML_AJAX handle the request + * + * @return boolean true if request was handled, false otherwise + */ + function handleRequest() + { + if ($this->options == true) { + $this->_loadOptions(); + } + //basically a hook for iframe but allows processing of data earlier + $this->ajax->populatePayload(); + if (!isset($_GET['c']) && (count($this->options['client']) > 0 || count($this->options['stub']) > 0) ) { + $this->generateClient(); + return true; + } else { + if (!empty($_GET['c'])) { + $this->_init($this->_cleanIdentifier($this->ajax->_getVar('c'))); + } + return $this->ajax->handleRequest(); + } + } + + /** + * Register method passthrough to HTML_AJAX + * + * @see HTML_AJAX::registerClass for docs + */ + function registerClass(&$instance, $exportedName = false, $exportedMethods = false) + { + $this->ajax->registerClass($instance,$exportedName,$exportedMethods); + } + + /** + * Change default serialization - important for exporting classes + * + * I wanted this for the xml serializer :) + */ + function setSerializer($type) + { + $this->ajax->serializer = $type; + $this->ajax->unserializer = $type; + } + + /** + * Register a new js client library + * + * @param string $libraryName name you'll reference the library as + * @param string|array $fileName actual filename with no path, for example customLib.js + * @param string|false $path Optional, if not set the result from jsClientLocation is used + */ + function registerJSLibrary($libraryName,$fileName,$path = false) { + $libraryName = strtolower($libraryName); + $this->javascriptLibraries[$libraryName] = $fileName; + + if ($path !== false) { + $this->javascriptLibraryPaths[$libraryName] = $path; + } + } + + /** + * Register init methods from an external class + * + * @param object $instance an external class with initClassName methods + */ + function registerInitObject(&$instance) { + $instance->server =& $this; + $methods = get_class_methods($instance); + foreach($methods as $method) { + if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) { + $this->_initLookup[strtolower($match[1])] = array(&$instance,$method); + } + } + } + + /** + * Register a callback to be exported to the client + * + * This function uses the PHP callback pseudo-type + * + */ + function registerPhpCallback($callback) + { + if (!is_callable($callback)) { + // invalid callback + return false; + } + + if (is_array($callback) && is_object($callback[0])) { + // object method + $this->registerClass($callback[0], strtolower(get_class($callback[0])), array($callback[1])); + return true; + } + + // static callback + $this->ajax->registerPhpCallback($callback); + } + + /** + * Generate client js + * + * @todo this is going to need tests to cover all the options + */ + function generateClient() + { + $headers = array(); + + ob_start(); + + // create a list list of js files were going to need to output + // index is the full file and so is the value, this keeps duplicates out of $fileList + $fileList = array(); + + if(!is_array($this->options['client'])) { + $this->options['client'] = array(); + } + foreach($this->options['client'] as $library) { + if (isset($this->javascriptLibraries[$library])) { + $lib = (array)$this->javascriptLibraries[$library]; + foreach($lib as $file) { + if (isset($this->javascriptLibraryPaths[$library])) { + $fileList[$this->javascriptLibraryPaths[$library].$file] = $this->javascriptLibraryPaths[$library].$file; + } + else { + $fileList[$this->clientJsLocation().$file] = $this->clientJsLocation().$file; + } + } + } + } + + // do needed class init if were running an init server + if(!is_array($this->options['stub'])) { + $this->options['stub'] = array(); + } + $classList = $this->options['stub']; + if ($this->initMethods) { + if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') { + $this->_initAll(); + } else { + foreach($this->options['stub'] as $stub) { + $this->_init($stub); + } + } + } + if (isset($this->options['stub'][0]) && $this->options['stub'][0] === 'all') { + $classList = array_keys($this->ajax->_exportedInstances); + } + + // if were doing stub and client we have to wait for both ETags before we can compare with the client + $combinedOutput = false; + if ($classList != false && count($classList) > 0 && count($fileList) > 0) { + $combinedOutput = true; + } + + + if ($classList != false && count($classList) > 0) { + + // were setup enough to make a stubETag if the input it wants is a class list + if ($this->cacheOptions['httpCacheStub'] && + $this->cacheOptions['StubCacheExpects'] == 'classes') + { + $stubETag = $this->_callCacheRule('Stub',$classList); + } + + // if were not in combined output compare etags, if method returns true were done + if (!$combinedOutput && isset($stubETag)) { + if ($this->_compareEtags($stubETag)) { + ob_end_clean(); + return; + } + } + + // output the stubs for all the classes in our list + foreach($classList as $class) { + echo $this->ajax->generateClassStub($class); + } + + // if were cacheing and the rule expects content make a tag and check it, if the check is true were done + if ($this->cacheOptions['httpCacheStub'] && + $this->cacheOptions['StubCacheExpects'] == 'content') + { + $stubETag = $this->_callCacheRule('Stub',ob_get_contents()); + } + + // if were not in combined output compare etags, if method returns true were done + if (!$combinedOutput && isset($stubETag)) { + if ($this->_compareEtags($stubETag)) { + ob_end_clean(); + return; + } + } + } + + if (count($fileList) > 0) { + // if were caching and need a file list build our jsETag + if ($this->cacheOptions['httpCacheClient'] && + $this->cacheOptions['ClientCacheExpects'] === 'files') + { + $jsETag = $this->_callCacheRule('Client',$fileList); + + } + + // if were not in combined output compare etags, if method returns true were done + if (!$combinedOutput && isset($jsETag)) { + if ($this->_compareEtags($jsETag)) { + ob_end_clean(); + return; + } + } + + // output the needed client js files + foreach($fileList as $file) { + $this->_readFile($file); + } + + // if were caching and need content build the etag + if ($this->cacheOptions['httpCacheClient'] && + $this->cacheOptions['ClientCacheExpects'] === 'content') + { + $jsETag = $this->_callCacheRule('Client',ob_get_contents()); + } + + // if were not in combined output compare etags, if method returns true were done + if (!$combinedOutput && isset($jsETag)) { + if ($this->_compareEtags($jsETag)) { + ob_end_clean(); + return; + } + } + // were in combined output, merge the 2 ETags and compare + else if (isset($jsETag) && isset($stubETag)) { + if ($this->_compareEtags(md5($stubETag.$jsETag))) { + ob_end_clean(); + return; + } + } + } + + + // were outputting content, add our length header and send the output + $length = ob_get_length(); + $output = ob_get_contents(); + ob_end_clean(); + + if ($this->ajax->packJavaScript) { + $output = $this->ajax->packJavaScript($output); + $length = strlen($output); + } + + if ($this->compression['enabled'] && $this->compression['type'] == 'gzip' && strpos($_SERVER["HTTP_ACCEPT_ENCODING"], "gzip") !== false) { + $output = gzencode($output,9); + $length = strlen($output); + $headers['Content-Encoding'] = 'gzip'; + } + + if ($length > 0 && $this->ajax->_sendContentLength()) { + $headers['Content-Length'] = $length; + } + $headers['Content-Type'] = 'text/javascript; charset=utf-8'; + $this->ajax->_sendHeaders($headers); + echo($output); + } + + /** + * Run readfile on input with basic error checking + * + * @param string $file file to read + * @access private + * @todo is addslashes enough encoding for js? + */ + function _readFile($file) + { + if (file_exists($file)) { + readfile($file); + } else { + $file = addslashes($file); + echo "alert('Unable to find javascript file: $file');"; + } + } + + /** + * Get the location of the client js + * To override the default pear datadir location set $this->clientJsLocation + * + * @return string + */ + function clientJsLocation() + { + if (!$this->clientJsLocation) { + $path = '@data-dir@'.DIRECTORY_SEPARATOR.'HTML_AJAX'.DIRECTORY_SEPARATOR.'js'.DIRECTORY_SEPARATOR; + if(strpos($path, '@'.'data-dir@') === 0) + { + $path = realpath(dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'js').DIRECTORY_SEPARATOR; + } + return $path; + } else { + return $this->clientJsLocation; + } + } + + /** + * Set the location of the client js + * + * @access public + * @param string $location Location + * @return void + */ + function setClientJsLocation($location) + { + $this->clientJsLocation = $location; + } + + /** + * Set the path to a Javascript libraries + * + * @access public + * @param string $library Library name + * @param string $path Path + * @return void + */ + function setJavascriptLibraryPath($library, $path) + { + $this->javascriptLibraryPaths[$library] = $path; + } + + /** + * Set the path to more than one Javascript libraries at once + * + * @access public + * @param array $paths Paths + * @return void + */ + function setJavascriptLibraryPaths($paths) + { + if (is_array($paths)) { + $this->javascriptLibraryPaths = array_merge($this->javascriptLibraryPaths, $paths); + } + } + + /** + * Load options from _GET + * + * @access private + */ + function _loadOptions() + { + $this->options = array('client'=>array(),'stub'=>array()); + if (isset($_GET['client'])) { + $clients = explode(',',$this->ajax->_getVar('client')); + $client = array(); + foreach($clients as $val) { + $cleanVal = $this->_cleanIdentifier($val); + if (!empty($cleanVal)) { + $client[] = strtolower($cleanVal); + } + } + + if (count($client) > 0) { + $this->options['client'] = $client; + } + } + if (isset($_GET['stub'])) { + $stubs = explode(',',$this->ajax->_getVar('stub')); + $stub = array(); + foreach($stubs as $val) { + $cleanVal = $this->_cleanIdentifier($val); + if (!empty($cleanVal)) { + $stub[] = strtolower($cleanVal); + } + } + + if (count($stub) > 0) { + $this->options['stub'] = $stub; + } + } + } + + /** + * Clean an identifier like a class name making it safe to use + * + * @param string $input + * @return string + * @access private + */ + function _cleanIdentifier($input) { + return trim(preg_replace('/[^A-Za-z_0-9]/','',$input)); + } + + /** + * Run every init method on the class + * + * @access private + */ + function _initAll() + { + if ($this->initMethods) { + foreach($this->_initLookup as $class => $method) { + $this->_init($class); + } + } + } + + /** + * Init one class + * + * @param string $className + * @access private + */ + function _init($className) + { + $className = strtolower($className); + if ($this->initMethods) { + if (isset($this->_initLookup[$className])) { + $method =& $this->_initLookup[$className]; + if (is_array($method)) { + call_user_func($method); + } + else { + $this->$method(); + } + } else { + trigger_error("Could find an init method for class: " . $className); + } + } + } + + /** + * Generate a hash from a list of files + * + * @param array $files file list + * @return string a hash that can be used as an etag + * @access private + */ + function _cacheRuleFile($files) { + $signature = ""; + foreach($files as $file) { + if (file_exists($file)) { + $signature .= $file.filemtime($file); + } + } + return md5($signature); + } + + /** + * Generate a hash from the api of registered classes + * + * @param array $classes class list + * @return string a hash that can be used as an etag + * @access private + */ + function _cacheRuleApi($classes) { + $signature = ""; + foreach($classes as $class) { + if (isset($this->ajax->_exportedInstances[$class])) { + $signature .= $class.implode(',',$this->ajax->_exportedInstances[$class]['exportedMethods']); + } + } + return md5($signature); + } + + /** + * Generate a hash from the raw content + * + * @param array $content + * @return string a hash that can be used as an etag + * @access private + */ + function _cacheRuleContent($content) { + return md5($content); + } + + /** + * Send cache control headers + * @access private + */ + function _sendCacheHeaders($etag,$notModified) { + header('Cache-Control: must-revalidate'); + header('ETag: '.$etag); + if ($notModified) { + header('HTTP/1.0 304 Not Modified',false,304); + } + } + + /** + * Compare eTags + * + * @param string $serverETag server eTag + * @return boolean + * @access private + */ + function _compareEtags($serverETag) { + if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + if (strcmp($this->ajax->_getServer('HTTP_IF_NONE_MATCH'),$serverETag) == 0) { + $this->_sendCacheHeaders($serverETag,true); + return true; + } + } + $this->_sendCacheHeaders($serverETag,false); + return false; + } + + /** + * Call a cache rule and return its retusn + * + * @param string $rule Stub|Client + * @param mixed $payload + * @return boolean + * @access private + * @todo decide if error checking is needed + */ + function _callCacheRule($rule,$payload) { + $method = '_cacheRule'.$this->cacheOptions[$rule.'CacheRule']; + return call_user_func(array(&$this,$method),$payload); + } +} +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +?>