Source for file cpaint2.inc.php
Documentation is available at cpaint2.inc.php
* CPAINT - Cross-Platform Asynchronous INterface Toolkit
* http://sf.net/projects/cpaint
* released under the terms of the LGPL
* see http://www.fsf.org/licensing/licenses/lgpl.txt for details
* @author Paul Sullivan <wiley14@gmail.com>
* @author Dominique Stender <dstender@st-webdevelopment.de>
* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @version $Id: cpaint2.inc.php 311 2006-09-30 08:21:25Z saloon12yrd $
//---- includes ----------------------------------------------------------------
require_once(dirname(__FILE__ ) . '/json.php');
//---- variables ---------------------------------------------------------------
$GLOBALS['__cpaint_json'] = new JSON();
//---- error reporting ---------------------------------------------------------
//---- classes -----------------------------------------------------------------
* @author Paul Sullivan <wiley14@gmail.com>
* @author Dominique Stender <dstender@st-webdevelopment.de>
* @copyright Copyright (c) 2005-2006 Paul Sullivan, Dominique Stender - http://sf.net/projects/cpaint
* @var string $response_type
* the basenode ajaxResponse.
* list of registered methods available through the CPAINT API
* @var array $api_functions
* list of registered complex types used in the CPAINT API
* @var array $api_datatypes
* whether or not the CPAINT API generates a WSDL when called with ?wsdl querystring
* @todo -o"Dominique Stender" -ccpaint implement a better debugging
$this->basenode->set_name('ajaxResponse');
$this->basenode->set_attribute('id', '');
/* $this->use_wsdl = true;
$this->complex_type(array(
'name' => 'cpaintResponseType',
'type' => 'restriction', // (restriction|complex|list)
'base_type' => 'string', // scalar type of all values, e.g. 'string', for type = (restriction|list) only
'values' => array( // for type = 'restriction' only: list of allowed values
'XML', 'TEXT', 'OBJECT', 'E4X', 'JSON',
$this->complex_type(array(
'name' => 'cpaintDebugLevel',
$this->complex_type(array(
'name' => 'cpaintDebugMessage',
$this->complex_type(array(
'name' => 'cpaintRequestHead',
0 => array('name' => 'functionName', 'type' => 'string'),
1 => array('name' => 'responseType', 'type' => 'cpaintResponseType'),
2 => array('name' => 'debugLevel', 'type' => 'cpaintDebugLevel'),
// $this->complex_type(array(
'name' => 'cpaintResponseHead',
0 => array('name' => 'success', 'type' => 'boolean'),
1 => array('name' => 'debugger', 'type' => 'cpaintDebugMessage'),
// determine response type
if (isset ($_REQUEST['cpaint_response_type'])) {
* calls the user function responsible for this specific call.
* @param string $input_encoding input data character encoding, default is UTF-8
function start($input_encoding = 'UTF-8') {
// work only if there is no API version request
if (!isset ($_REQUEST['api_query'])
&& !isset ($_REQUEST['wsdl'])) {
$this->basenode->set_encoding($input_encoding);
if ($_REQUEST['cpaint_function'] != '') {
$user_function = $_REQUEST['cpaint_function'];
if (isset ($_REQUEST['cpaint_argument'])) {
$arguments = $_REQUEST['cpaint_argument'];
// perform character conversion on every argument
foreach ($arguments as $key => $value) {
// convert from JSON string if it is an object or an array
$arguments[$key] = $GLOBALS['__cpaint_json']->parse($value);
// a valid API function is to be called
} else if ($user_function != '') {
// desired function is not registered as API function
$this->basenode->set_data('[CPAINT] A function name was passed that is not allowed to execute on this server.');
* generates and prints the response based on response type supplied by the frontend.
// 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');
// work only if there is no API version request
if (!isset ($_REQUEST['api_query'])
/* && !isset($_REQUEST['wsdl']) */) {
// trigger generation of response
echo 'ERROR: invalid response type \'' . $this->response_type . '\'';
} elseif (isset ($_REQUEST['api_query'])) {
header('Content-type: text/plain; charset=ISO-8859-1');
echo 'CPAINT v' . $this->version . '/PHP v' . phpversion();
} elseif ($this->use_wsdl == true
&& isset($_REQUEST['wsdl'])) {
if (is_file(dirname(__FILE__) . '/cpaint2.wsdl.php')
&& is_readable(dirname(__FILE__) . '/cpaint2.wsdl.php')) {
require_once(dirname(__FILE__) . '/cpaint2.wsdl.php');
if (class_exists('cpaint_wsdl')) {
// create new instance of WSDL library
$wsdl = new cpaint_wsdl();
header('Content-type: text/xml; charset=UTF-8');
echo $wsdl->generate($this->api_functions, $this->api_datatypes);
header('Content-type: text/plain; charset=ISO-8859-1');
echo 'WSDL generator is unavailable';
header('Content-type: text/plain; charset=ISO-8859-1');
echo 'WSDL generator is unavailable';
* registers a new function or method as part of the CPAINT API
* @param mixed $func function name, array(&$object, 'function_name') or array('class', 'function_name')
* @param string $alias alias name for the function. Will be used by the frontend.
* @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)
* @param string $comment description of the functionality
* @version 1.0.1 16.09.2006 23:07:44 [dstender] Added alias functionality.
function register($func, $alias = '', $input = array(), $output = array(), $comment = '') {
$alias = (string) $alias;
$output = (array) $output;
$comment = (string) $comment;
// set correct function name if alias is used.
// calling a method of an object
// set correct function name if alias is used.
// calling a standalone function
* unregisters a function that is currently part of the CPAINT API.
* proves useful when the same set of functions is to be used in the
* frontend and in some kind of administration environment. you might
* want to unregister a few (admin) functions for the frontend in this
* Note that if you supplied an alias when registering a method, you must
* use the alias to unregister the function / method.
* @param string $func function name
* @version 1.0.1 16.09.2006 23:10:58 [dstender] Extended PHPDoc for use of aliases.
* registers a complex data type
* @param array $schema schema definition for the complex type
function complex_type($schema) {
$schema = (array) $schema;
if ($schema['name'] != ''
&& in_array($schema['type'], array('restriction', 'complex', 'list'))) {
$this->api_datatypes[] = $schema;
* switches the generation of WSDL on/off. default is on
* @param boolean $state state of WSDL generation
function use_wsdl($state) {
$this->use_wsdl = (boolean) $state;
* adds a new subnode to the basenode.
* will return a reference to it for further processing.
* @param string $nodename name of the new node
* @param string $id id of the new node
function &add_node($nodename, $id = '') {
return $this->basenode->add_node($nodename, $id);
* assigns textual data to the basenode.
* @param mixed $data data to assign to this node
* returns the data assigned to the basenode.
* sets the id property of the basenode.
* @deprecated deprecated since version 2.0.0
* @param string $id the id
$this->basenode->set_attribute('id', $id);
* gets the id property of the basenode.
* @deprecated deprecated since version 2.0.0
return $this->basenode->get_attribute('id');
* adds a new attribute to the basenode.
* @param string $name attribute name
* @param mixed $value attribute value
$this->basenode->set_attribute($name, $value);
* retrieves an attribute of the basenode by name.
* @param string $name attribute name
return $this->basenode->get_attributes($name);
* set name property of the basenode.
* @param string $name the name
* get name property of the basenode.
* returns the response type as requested by the client
* a cpaint data node. Data nodes are used to build up the response.
* @author Dominique Stender <dstender@st-webdevelopment.de>
* @copyright 2005-2006 (Dominique Stender); All rights reserved
* textual data of this node.
* character encoding for input data
* adds a new subnode to this node.
* will return a reference to it for further processing.
* @param string $nodename name of the new node
* @param string $id id of the new node
function &add_node($nodename, $id = '') {
$this->composites[$composites]->set_name($nodename);
$this->composites[$composites]->set_attribute('id', $id);
$this->composites[$composites]->set_encoding($this->input_encoding);
* assigns textual data to this node.
* @param mixed $data data to assign to this node
* returns the textual data assigned to this node.
* sets the id property of this node.
* @deprecated deprecated since version 2.0.0
* @param string id the id
* returns the id property if this node.
* @deprecated deprecated since version 2.0.0
* adds a new attribute to this node.
* @param string $name attribute name
* @param mixed $value attribute value
* retrieves an attribute by name.
* @param string $name attribute name
* @param string $name the name
* sets the character encoding for this node
* @param string $encoding character encoding
$this->input_encoding = strtoupper((string) $encoding);
* returns the character encoding for this node
return $this->input_encoding;
* static class of output transformers.
* @author Dominique Stender <dstender@st-webdevelopment.de>
* @copyright 2003-2006 (Dominique Stender); All rights reserved
* toString method, used to generate response of type TEXT.
* will perform character transformation according to parameters.
* @param object $node a cpaint_node object
foreach ($node->composites as $composite) {
* XML response generator.
* will perform character transformation according to parameters.
* @param object $node a cpaint_node object
$return_value = '<' . $node->get_name();
foreach ($node->attributes as $name => $value) {
$return_value .= ' ' . $name . '="' . $node->get_attribute($name) . '"';
foreach ($node->composites as $composite) {
. '</' . $node->get_name() . '>';
* JSON response generator.
* will perform character transformation according to parameters.
* @param object $node a cpaint_node object
$JSON_node = new stdClass();
$JSON_node->attributes = $node->attributes;
foreach ($node->composites as $composite) {
if (!is_array($JSON_node->{$composite->nodename})) {
$JSON_node->{$composite->nodename} = array();
// we need to parse the JSON object again to avoid multiple encoding
$JSON_node->data = $node->data;
return $GLOBALS['__cpaint_json']->stringify($JSON_node);
* performs conversion to JavaScript-safe UTF-8 characters
* @param string $data data to convert
* @param string $encoding character encoding
function encode($data, $encoding) {
// 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()
// give up. if UTF-8 data was supplied everything is fine!
// now encode non-printable characters
for ($i = 0; $i < 32; $i++ ) {
// encode <, >, and & respectively for XML sanity
* performs conversion from JavaScript encodeURIComponent() string (UTF-8) to
* @param string $data data to convert
* @param string $encoding character encoding
function decode($data, $encoding) {
// 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()
// give up. if data was supplied in the correct format everything is fine!
* decodes a (nested) array of data from UTF-8 into the configured character set
* @param array $data data to convert
* @param string $encoding character encoding
foreach ($data as $key => $value) {
* determines the output character set
* based on input character set
* @param string $encoding character encoding
|| $encoding == 'ISO-8859-1') {
$return_value = $encoding;
|