* @author Dominique Stender * @copyright Copyright (c) 2005 Paul Sullivan, Dominique Stender - http://cpaint.sourceforge.net */ /** * cpaint base class. * * @package CPAINT * @access public * @author Paul Sullivan * @author Dominique Stender * @copyright Copyright (c) 2005 Paul Sullivan, Dominique Stender - http://cpaint.sourceforge.net * @version 2.0.1 */ class cpaint { /** * version number * * @access private * @var string $version */ public $version = '2.0.1'; /** * response type. * * @access protected * @var string $response_type */ public $response_type; /** * the basenode ajaxResponse. * * @access protected * @var object $basenode */ public $basenode; /** * list of registered methods available through the CPAINT API * * @access protected * @var array $api_functions */ public $api_functions; /** * PHP4 constructor. * * @access public * @return void */ public function cpaint() { $this->__construct(); } /** * PHP 5 constructor. * * @access public * @return void */ public function __construct() { // initialize properties $this->basenode = new cpaint_node(); $this->basenode->set_name('ajaxResponse'); $this->basenode->set_attribute('id', ''); $this->basenode->set_encoding('UTF-8'); $this->response_type = 'TEXT'; $this->api_functions = array(); // open output buffer so no output is sent back to the client ob_start(); // determine response type if (isset($_GET['cpaint_response_type'])) { $this->response_type = (string) $_GET['cpaint_response_type']; } elseif (isset($_POST['cpaint_response_type'])) { $this->response_type = (string) $_POST['cpaint_response_type']; } } /** * calls the user function responsible for this specific call. * * @access public * @param string $input_encoding input data character encoding, default is UTF-8 * @return void */ public function start($input_encoding = 'UTF-8') { $user_function = ''; $arguments = array(); // work only if there is no API version request if (!isset($_GET['api_query']) && !isset($_POST['api_query'])) { $this->basenode->set_encoding($input_encoding); if ($_GET['cpaint_function'] != '') { $user_function = $_GET['cpaint_function']; $arguments = $_GET['cpaint_argument']; } elseif ($_POST['cpaint_function'] != '') { $user_function = $_POST['cpaint_function']; $arguments = $_POST['cpaint_argument']; } // perform character conversion on every argument $arguments = cpaint_transformer::decode_array($arguments, $this->basenode->get_encoding()); if (is_array($this->api_functions[$user_function]) && is_callable($this->api_functions[$user_function]['call'])) { // a valid API function is to be called call_user_func_array($this->api_functions[$user_function]['call'], $arguments); } else { // desired function is not registered as API function // @todo -o"Dominique Stender" -ccpaint implement a better debugging $this->basenode->set_data('A function name was passed that is not allowed to execute on this server.'); } } // end: if } /** * generates and prints the response based on response type supplied by the frontend. * * @access public * @return void */ public function return_data() { // delete output buffer ob_end_clean(); // send appropriate headers to avoid caching header('Expires: Fri, 14 Mar 1980 20:53:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); // work only if there is no API version request if (!isset($_GET['api_query']) && !isset($_POST['api_query'])) { // trigger generation of response switch (trim(strtoupper($this->response_type))) { case 'TEXT': header('Content-type: text/plain; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding())); echo cpaint_transformer::toString($this->basenode); break; case 'OBJECT': case 'XML': header('Content-type: text/xml; charset=' . cpaint_transformer::find_output_charset($this->basenode->get_encoding())); echo 'basenode->get_encoding()) . '"?>' . cpaint_transformer::toXML($this->basenode); break; default: echo 'ERROR: invalid response type \'' . $this->response_type . '\''; } // end: switch } else { // API version request header('Content-type: text/plain; charset=ISO-8859-1'); echo 'CPAINT v' . $this->version . '/PHP v' . phpversion(); } // end: if } /** * registers a new function or method as part of the CPAINT API * * @access public * @param mixed $func function name, array(&$object, 'function_name') or array('class', 'function_name') * @param array $input function input parameters (not yet used by CPAINT and subject to change) * @param array $output function output format (not yed used by CPAINT and subject to change) * @return boolean */ public function register($func, $input = array(), $output = array()) { $return_value = false; $input = (array) $input; $output = (array) $output; if (is_array($func) && (is_object($func[0]) || is_string($func[0])) && is_string($func[1]) && is_callable($func)) { // calling a method of an object $this->api_functions[$func[1]] = array( 'call' => $func, 'input' => $input, 'output' => $output, ); $return_value = true; } elseif (is_string($func)) { // calling a standalone function $this->api_functions[$func] = array( 'call' => $func, 'input' => $input, 'output' => $output, ); $return_value = true; } // end: if return $return_value; } /** * adds a new subnode to the basenode. * * will return a reference to it for further processing. * * @access public * @param string $nodename name of the new node * @param string $id id of the new node * @return object */ public function &add_node($nodename, $id = '') { return $this->basenode->add_node($nodename, $id); } /** * assigns textual data to the basenode. * * @access public * @param string $data data to assign to this node * @return void */ public function set_data($data) { $this->basenode->set_data($data); } /** * returns the data assigned to the basenode. * * @access public * @return string */ public function get_data() { return $this->basenode->get_data(); } /** * sets the id property of the basenode. * * @deprecated deprecated since version 2.0.0 * @access public * @param string $id the id * @return void */ public function set_id($id) { $this->basenode->set_attribute('id', $id); } /** * gets the id property of the basenode. * * @deprecated deprecated since version 2.0.0 * @access public * @return string */ public function get_id() { return $this->basenode->get_attribute('id'); } /** * adds a new attribute to the basenode. * * @access public * @param string $name attribute name * @param mixed $value attribute value * @return void */ public function set_attribute($name, $value) { $this->basenode->set_attribute($name, $value); } /** * retrieves an attribute of the basenode by name. * * @access public * @param string $name attribute name * @return string */ public function get_attribute($name) { return $this->basenode->get_attributes($name); } /** * set name property of the basenode. * * @access public * @param string $name the name * @return void */ public function set_name($name) { $this->basenode->set_name($name); } /** * get name property of the basenode. * * @access public * @return string */ public function get_name() { return $this->basenode->get_name(); } } /** * a cpaint data node. Data nodes are used to build up the response. * * @package CPAINT * @access public * @author Dominique Stender * @copyright 2005 (Dominique Stender); All rights reserved * @version 2.0.0 */ class cpaint_node { /** * array of subnodes. * * @access public * @var array $composites */ public $composites; /** * node attributes. * * @access public * @var array $attributes */ public $attributes; /** * name of this node. * * @access public * @var string $nodename */ public $nodename; /** * textual data of this node. * * @access public * @var string $data */ public $data; /** * character encoding for input data * * @access private * @var $input_encoding */ public $input_encoding; /** * PHP4 constructor. * * @package CPAINT * @access public * @return void */ public function cpaint_node() { $this->__construct(); } /** * PHP 5 constructor. * * @access public * @return void */ public function __construct() { // initialize properties $this->composites = array(); $this->attributes = array(); $this->data = ''; $this->set_encoding('UTF-8'); $this->set_name(''); $this->set_attribute('id', ''); } /** * adds a new subnode to this node. * * will return a reference to it for further processing. * * @access public * @param string $nodename name of the new node * @param string $id id of the new node * @return object */ public function &add_node($nodename, $id = '') { $composites = count($this->composites); // create new node $this->composites[$composites] = new cpaint_node(); $this->composites[$composites]->set_name($nodename); $this->composites[$composites]->set_attribute('id', $id); $this->composites[$composites]->set_encoding($this->input_encoding); return $this->composites[$composites]; } /** * assigns textual data to this node. * * @access public * @param string $data data to assign to this node * @return void */ public function set_data($data) { $this->data = (string) $data; } /** * returns the textual data assigned to this node. * * @access public * @return string */ public function get_data() { return $this->data; } /** * sets the id property of this node. * * @deprecated deprecated since version 2.0.0 * @access public * @param string id the id * @return void */ public function set_id($id) { if ($id != '') { $this->set_attribute('id', $id); } // end: if } /** * returns the id property if this node. * * @deprecated deprecated since version 2.0.0 * @access public * @return string */ public function get_id() { return $this->get_attribute('id'); } /** * adds a new attribute to this node. * * @access public * @param string $name attribute name * @param mixed $value attribute value * @return void */ public function set_attribute($name, $value) { $this->attributes[$name] = (string) $value; } /** * retrieves an attribute by name. * * @access public * @param string $name attribute name * @return string */ public function get_attribute($name) { return $this->attributes[$name]; } /** * set name property. * * @access public * @param string $name the name * @return void */ public function set_name($name) { $this->nodename = (string) $name; } /** * get name property. * * @access public * @return string */ public function get_name() { return $this->nodename; } /** * sets the character encoding for this node * * @access public * @param string $encoding character encoding * @return void */ public function set_encoding($encoding) { $this->input_encoding = strtoupper((string) $encoding); } /** * returns the character encoding for this node * * @access public * @return string */ public function get_encoding() { return $this->input_encoding; } } /** * static class of output transformers. * * @package CPAINT * @access public * @author Dominique Stender * @copyright 2002-2005 (Dominique Stender); All rights reserved * @version 2.0.0 */ class cpaint_transformer { /** * toString method, used to generate response of type TEXT. * will perform character transformation according to parameters. * * @access public * @param object $node a cpaint_node object * @return string */ public function toString(&$node) { $return_value = ''; foreach ($node->composites as $composite) { $return_value .= cpaint_transformer::toString($composite); } $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding()); return $return_value; } /** * XML response generator. * will perform character transformation according to parameters. * * @access public * @param object $node a cpaint_node object * @return string */ public function toXML(&$node) { $return_value = '<' . cpaint_transformer::encode($node->get_name(), $node->get_encoding()); // handle attributes foreach ($node->attributes as $name => $value) { if ($value != '') { $return_value .= ' ' . cpaint_transformer::encode($name, $node->get_encoding()) . '="' . cpaint_transformer::encode($node->get_attribute($name), $node->get_encoding()) . '"'; } } // end: foreach $return_value .= '>'; foreach ($node->composites as $composite) { $return_value .= cpaint_transformer::toXML($composite); } $return_value .= cpaint_transformer::encode($node->get_data(), $node->get_encoding()) . 'get_name(), $node->get_encoding()) . '>'; return $return_value; } /** * performs conversion to JavaScript-safe UTF-8 characters * * @access public * @param string $data data to convert * @param string $encoding character encoding * @return string */ public function encode($data, $encoding) { // convert string if (function_exists('iconv')) { // iconv is by far the most flexible approach, try this first $return_value = iconv($encoding, 'UTF-8', $data); } elseif ($encoding == 'ISO-8859-1') { // for ISO-8859-1 we can use utf8-encode() $return_value = utf8_encode($data); } else { // give up. if UTF-8 data was supplied everything is fine! $return_value = $data; } /* end: if */ // now encode non-printable characters for ($i = 0; $i < 32; $i++) { $return_value = str_replace(chr($i), '\u00' . sprintf('%02x', $i), $return_value); } // end: for // encode <, >, and & respectively for XML sanity $return_value = str_replace(chr(0x26), '\u0026', $return_value); $return_value = str_replace(chr(0x3c), '\u003c', $return_value); $return_value = str_replace(chr(0x3e), '\u003e', $return_value); return $return_value; } /** * performs conversion from JavaScript encodeURIComponent() string (UTF-8) to * the charset in use. * * @access public * @param string $data data to convert * @param string $encoding character encoding * @return string */ public function decode($data, $encoding) { // convert string if (function_exists('iconv')) { // iconv is by far the most flexible approach, try this first $return_value = iconv('UTF-8', $encoding, $data); } elseif ($encoding == 'ISO-8859-1') { // for ISO-8859-1 we can use utf8-decode() $return_value = utf8_decode($data); } else { // give up. if data was supplied in the correct format everything is fine! $return_value = $data; } /* end: if */ return $return_value; } /** * decodes a (nested) array of data from UTF-8 into the configured character set * * @access public * @param array $data data to convert * @param string $encoding character encoding * @return array */ public function decode_array($data, $encoding) { $return_value = array(); foreach ($data as $key => $value) { if (!is_array($value)) { $return_value[$key] = cpaint_transformer::decode($value, $encoding); } else { $return_value[$key] = cpaint_transformer::decode_array($value, $encoding); } } return $return_value; } /** * determines the output character set * based on input character set * * @access public * @param string $encoding character encoding * @return string */ public function find_output_charset($encoding) { $return_value = 'UTF-8'; if (function_exists('iconv') || $encoding == 'UTF-8' || $encoding == 'ISO-8859-1') { $return_value = 'UTF-8'; } else { $return_value = $encoding; } /* end: if */ return $return_value; } }