Source for file Server.php
Documentation is available at Server.php
* OO AJAX Implementation for PHP
* @author Joshua Eichorn <josh@bluga.net>
* @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
* @author Joshua Eichorn <josh@bluga.net>
* @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
* Client options array if set to true the code looks at _GET
* Set to true if your extending the server to add init{className methods}
* Location on filesystem of client javascript library
* @var false|stringif false the default pear data dir location is used
* 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}
* '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
'httpCacheClient' => true ,
'ClientCacheRule' => 'file',
'ClientCacheExpects' => 'files',
'StubCacheRule' => 'api',
'StubCacheExpects' => 'classes',
* Javascript library names and there path
* the return of $this->clientJsLocation(), is prepended before running readfile on them
'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',
'loading' => 'Loading.js',
'phpserializer' => 'serializer/phpSerializer.js',
'urlserializer' => 'serializer/UrlSerializer.js',
'haserializer' => 'serializer/haSerializer.js',
'clientpool' => 'clientPool.js',
'iframe' => 'IframeXHR.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'),
'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
* Array of className => init methods to call, generated from constructor from initClassName methods
* Constructor creates the HTML_AJAX instance
* @param string $serverUrl (Optional) the url the client should be making a request too
// 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.
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 ) {
if (substr($serverUrl,-1 ) != '?') {
foreach($methods as $method) {
if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
* Handle a client request, either generating a client or having HTML_AJAX handle the request
* @return boolean true if request was handled, false otherwise
//basically a hook for iframe but allows processing of data earlier
$this->ajax->populatePayload ();
if (!empty ($_GET['c'])) {
$this->_init ($this->_cleanIdentifier ($this->ajax->_getVar ('c')));
* Register method passthrough to HTML_AJAX
* @see HTML_AJAX::registerClass for docs
function registerClass(&$instance, $exportedName = false , $exportedMethods = false )
* Change default serialization - important for exporting classes
* I wanted this for the xml serializer :)
* 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
* Register init methods from an external class
* @param object $instance an external class with initClassName methods
$instance->server = & $this;
foreach($methods as $method) {
if (preg_match('/^init([a-zA-Z0-9_]+)$/',$method,$match)) {
* Register a callback to be exported to the client
* This function uses the PHP callback pseudo-type
* @todo this is going to need tests to cover all the options
// 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
$this->options['client'] = array ();
foreach($this->options['client'] as $library) {
// do needed class init if were running an init server
$classList = $this->options['stub'];
if (isset ($this->options['stub'][0 ]) && $this->options['stub'][0 ] === 'all') {
foreach($this->options['stub'] as $stub) {
if (isset ($this->options['stub'][0 ]) && $this->options['stub'][0 ] === 'all') {
// if were doing stub and client we have to wait for both ETags before we can compare with the client
if ($classList != false && count($classList) > 0 && count($fileList) > 0 ) {
if ($classList != false && count($classList) > 0 ) {
// were setup enough to make a stubETag if the input it wants is a class list
$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)) {
// output the stubs for all the classes in our list
foreach($classList as $class) {
// if were cacheing and the rule expects content make a tag and check it, if the check is true were done
// if were not in combined output compare etags, if method returns true were done
if (!$combinedOutput && isset ($stubETag)) {
if ($this->_compareEtags ($stubETag)) {
if (count($fileList) > 0 ) {
// if were caching and need a file list build our jsETag
$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)) {
// output the needed client js files
foreach($fileList as $file) {
// if were caching and need content build the etag
// if were not in combined output compare etags, if method returns true were done
if (!$combinedOutput && isset ($jsETag)) {
if ($this->_compareEtags ($jsETag)) {
// were in combined output, merge the 2 ETags and compare
else if (isset ($jsETag) && isset ($stubETag)) {
if ($this->_compareEtags (md5($stubETag. $jsETag))) {
// were outputting content, add our length header and send the output
if ($length > 0 && $this->ajax->_sendContentLength ()) {
//$headers['Content-Length'] = $length;
$headers['Content-Type'] = 'text/javascript; charset=utf-8';
$this->ajax->_sendHeaders ($headers);
* Run readfile on input with basic error checking
* @param string $file file to read
* @todo is addslashes enough encoding for js?
function _readFile ($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
$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;
* Set the location of the client js
* @param string $location Location
* Set the path to a Javascript libraries
* @param string $library Library name
* @param string $path Path
* Set the path to more than one Javascript libraries at once
* @param array $paths Paths
$this->options = array ('client'=>array (),'stub'=>array ());
if (isset ($_GET['client'])) {
$clients = explode(',',$this->ajax->_getVar ('client'));
foreach($clients as $val) {
$cleanVal = $this->_cleanIdentifier ($val);
if (count($client) > 0 ) {
$this->options['client'] = $client;
if (isset ($_GET['stub'])) {
foreach($stubs as $val) {
$cleanVal = $this->_cleanIdentifier ($val);
* Clean an identifier like a class name making it safe to use
function _cleanIdentifier ($input) {
* Run every init method on the class
* @param string $className
function _init ($className)
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
function _cacheRuleFile ($files) {
foreach($files as $file) {
* 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
function _cacheRuleApi ($classes) {
foreach($classes as $class) {
if (isset ($this->ajax->_exportedInstances[$class])) {
$signature .= $class. implode(',',$this->ajax->_exportedInstances[$class]['exportedMethods']);
* Generate a hash from the raw content
* @return string a hash that can be used as an etag
function _cacheRuleContent ($content) {
* Send cache control headers
function _sendCacheHeaders ($etag,$notModified) {
header('Cache-Control: must-revalidate');
header('HTTP/1.0 304 Not Modified',false ,304 );
* @param string $serverETag server eTag
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 );
$this->_sendCacheHeaders ($serverETag,false );
* Call a cache rule and return its retusn
* @param string $rule Stub|Client
* @todo decide if error checking is needed
function _callCacheRule ($rule,$payload) {
$method = '_cacheRule'. $this->cacheOptions[$rule. 'CacheRule'];
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
Documentation generated on Sat, 05 May 2007 18:00:22 -0400 by phpDocumentor 1.3.0. PEAR Logo Copyright © PHP Group 2004.
|