Created
June 9, 2022 00:08
-
-
Save 3mrdev/b1fd2f2a16f0bbdd6d66f4c313e16f47 to your computer and use it in GitHub Desktop.
Odoo XMLRPC Example using PHP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// like import file | |
require_once('lib/ripcord.php'); | |
// define variables to use in future | |
$url = "http://localhost:8015"; | |
$db = "o14_api"; | |
$username = "admin"; | |
$password = "admin"; | |
$end_point = $url . "/xmlrpc/2/object"; | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once("login.php"); // import login file | |
// create | |
$name = $_POST['name']; | |
$id = $model->execute_kw( | |
$db,//database name | |
$uid, // user id | |
$password, // user password | |
"student", // model / table name | |
"create", // rpc function | |
array( | |
array( // data to insert | |
"name"=>$name, | |
"birth_date"=>"2000-08-20", | |
"number"=>6, | |
"fees"=>400, | |
"is_register"=>True, | |
) | |
) | |
); | |
// header('Location: insert.php'); | |
echo json_encode($id); // print user that created | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('login.php'); | |
// $id = (int) $_GET['id']; | |
$is_deleted = $model->execute_kw( | |
$db, | |
$uid, | |
$password, | |
"student", | |
"unlink", | |
array(6), | |
); | |
echo json_encode($is_deleted); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<body> | |
<form action="create.php" method="POST"> | |
Name: <input name="name" type="text"/> | |
<input type="submit" name="Save"/> | |
</form> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('config.php'); | |
$common = ripcord::client($url . "/xmlrpc/2/common"); | |
$uid = $common->authenticate($db, $username, $password, array()); | |
$model = ripcord::client($end_point); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Ripcord is an easy to use XML-RPC library for PHP. | |
* @package Ripcord | |
* @author Auke van Slooten <auke@muze.nl> | |
* @copyright Copyright (C) 2010, Muze <www.muze.nl> | |
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License | |
* @version Ripcord 0.9 - PHP 5 | |
*/ | |
/** | |
* The ripcord class contains a number of useful static methods. This makes it a bit easier to create a server or client, convert types | |
* and check for errors. | |
* @package Ripcord | |
*/ | |
class ripcord | |
{ | |
/** | |
* This method checks whether the given argument is an XML-RPC fault. | |
* @param mixed $fault | |
* @return bool | |
*/ | |
public static function isFault($fault) | |
{ | |
if ( isset($fault) && is_array($fault) ) { | |
return xmlrpc_is_fault($fault); | |
} else { | |
return false; | |
} | |
} | |
/** | |
* This method generates an XML-RPC fault with the given code and message. | |
* @param int $code | |
* @param string $message | |
* @return array | |
*/ | |
public static function fault($code, $message) | |
{ | |
return array('faultCode' => $code, 'faultString' => $message); | |
} | |
/** | |
* This method returns a new Ripcord server, which by default implements XML-RPC, Simple RPC and SOAP 1.1. | |
* The server will publish any methods passed through the $services argument. It can be configured through | |
* the $options argument. | |
* @param mixed $services Optional. Either an object or an array of objects. If the array has non-numeric keys, the key will be used as a namespace for the methods in the object. | |
* @param array $options Optional. An array of options to set for the Ripcord server. | |
* @see Ripcord_Server | |
*/ | |
public static function server($services = null, $options = null, $documentor = null) | |
{ | |
self::load('Ripcord_Server'); | |
if ( !isset($documentor) ) | |
{ | |
$doc = array('name', 'css', 'wsdl', 'wsdl2'); | |
$docOptions = array(); | |
foreach ( $doc as $key ) | |
{ | |
if ( isset($options[$key]) ) | |
{ | |
$docOptions[$key] = $options[$key]; | |
unset( $options[$key] ); | |
} | |
} | |
$docOptions['version'] = $options['version']; | |
$documentor = self::documentor( $docOptions ); | |
} | |
return new Ripcord_Server($services, $options, $documentor); | |
} | |
/** | |
* This method returns a new Ripcord client. By default this will be an XML-RPC client, but you can change this | |
* through the $options argument. | |
* @param string $url The url of the RPC server to connect with | |
* @param array $options Optional. An array of options to set for the Ripcord client. | |
* @see Ripcord_Client | |
*/ | |
public static function client($url, $options = null, $transport = null ) | |
{ | |
self::load('Ripcord_Client'); | |
if ( !isset($transport) ) | |
{ | |
$transport = new Ripcord_Transport_Stream(); | |
} | |
return new Ripcord_Client($url, $options, $transport); | |
} | |
/** | |
* This method returns a new Ripcord documentor object. | |
* @param array $options Optional. An array of options to set for the Ripcord documentor. | |
* @param object docCommentParser Optional. An object that parses a docComment block. Must | |
* implement the Ripcord_Documentor_CommentParser interface. | |
* @see Ripcord_Client | |
*/ | |
public static function documentor( $options = null, $docCommentParser = null ) | |
{ | |
self::load('Ripcord_Documentor'); | |
if (!$docCommentParser) { | |
$docCommentParser = new Ripcord_Documentor_Parser_phpdoc(); | |
} | |
return new Ripcord_Documentor( $options, $docCommentParser ); | |
} | |
/** | |
* This method returns an XML-RPC datetime object from a given unix timestamp. | |
* @param int $timestamp | |
* @return object | |
*/ | |
public static function datetime($timestamp) | |
{ | |
$datetime = date("Ymd\TH:i:s", $timestamp); | |
xmlrpc_set_type($datetime, 'datetime'); | |
return $datetime; | |
} | |
/** | |
* This method returns a unix timestamp from a given XML-RPC datetime object. | |
* It will throw a 'Variable is not of type datetime' Ripcord_Exception (code -6) | |
* if the given argument is not of the correct type. | |
* @param object $datetime | |
* @return int | |
*/ | |
public static function timestamp($datetime) | |
{ | |
if (xmlrpc_get_type($datetime)=='datetime') | |
{ | |
return $datetime->timestamp; | |
} else { | |
throw Ripcord_Exception('Variable is not of type datetime', -6); | |
} | |
} | |
/** | |
* This method returns an XML-RPC base64 object from a given binary string. | |
* @param string $binary | |
* @return object | |
*/ | |
public static function base64($binary) | |
{ | |
xmlrpc_set_type($binary, 'base64'); | |
return $binary; | |
} | |
/** | |
* This method returns a (binary) string from a given XML-RPC base64 object. | |
* It will throw a 'Variable is not of type base64' Ripcord_Exception (code -7) | |
* if the given argument is not of the correct type. | |
* @param object $base64 | |
* @return string | |
*/ | |
public static function binary($base64) | |
{ | |
if (xmlrpc_get_type($base64)=='base64') | |
{ | |
return $base64->scalar; | |
} else { | |
throw Ripcord_Exception('Variable is not of type base64', -7); | |
} | |
} | |
/** | |
* This method returns the type of the given parameter. This can be any of the XML-RPC data types, e.g. | |
* 'struct', 'int', 'string', 'base64', 'boolean', 'double', 'array' or 'datetime'. | |
* @param mixed $param | |
* @return string | |
*/ | |
public static function getType($param) | |
{ | |
return xmlrpc_get_type($param); | |
} | |
/** | |
* This method returns a new Ripcord client, configured to access a SOAP 1.1 server. | |
* @param string $url | |
* @param array $options Optional. | |
* @see Ripcord_Client | |
*/ | |
public static function soapClient($url, $options = null, $transport = null) | |
{ | |
$options['version'] = 'soap 1.1'; | |
return self::client($url, $options, $transport); | |
} | |
/** | |
* This method returns a new Ripcord client, configured to access an XML-RPC server. | |
* @param string $url | |
* @param array $options Optional. | |
* @return object | |
* @see Ripcord_Client | |
*/ | |
public static function xmlrpcClient($url, $options = null, $transport = null) | |
{ | |
$options['version'] = 'xmlrpc'; | |
return self::client($url, $options, $transport); | |
} | |
/** | |
* This method returns a new Ripcord client, configured to access a Simple RPC server. | |
* @param string $url | |
* @param array $options Optional. | |
* @return object | |
* @see Ripcord_Client | |
*/ | |
public static function simpleClient($url, $options = null, $transport = null) | |
{ | |
$options['version'] = 'simple'; | |
return self::client($url, $options, $transport); | |
} | |
/** | |
* This method includes a ripcord class, using require_once. Used for autoloading ripcord classes. | |
* @param string $class The name of the class to load. | |
* @return boolean | |
*/ | |
public static function load($class) | |
{ | |
if (substr($class, 0, 8)=='Ripcord_') | |
{ | |
$root = dirname(__FILE__).'/ripcord_'; | |
$class = substr($class, 8); | |
$file = str_replace('.', '', $class); | |
$file = str_replace('_', '/', $file); | |
$file = strtolower($file); | |
while ($file && $file!='.') | |
{ | |
if ( file_exists($root.$file.'.php') ) | |
{ | |
require_once($root.$file.'.php'); | |
return true; | |
} else { | |
$file = dirname($file); | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* This method creates a new Ripcord_Client_Call object, which encodes the information needed for | |
* a method call to an rpc server. This is mostly used for the system.multiCall method. | |
* @param string $method The name of the method call to encode | |
* @param mixed $args,... The remainder of the arguments are encoded as parameters to the call | |
* @return object | |
*/ | |
public static function encodeCall() | |
{ | |
self::load('Ripcord_Client'); | |
$params = func_get_args(); | |
$method = array_shift($params); | |
return new Ripcord_Client_Call( $method, $params ); | |
} | |
/* | |
* This method binds the first parameter to the output of a Ripcord client call. If | |
* the second argument is a Ripcord_Client_Call object, it binds the parameter to it, | |
* if not it simply assigns the second parameter to the first parameter. | |
* This means that doing: | |
* > ripcord::bind( $result, $client->someMethod() ) | |
* will always result in $result eventually containing the return value of $client->someMethod(). | |
* Whether multiCall mode has been enabled or not. | |
*/ | |
public function bind(&$bound, $call) | |
{ | |
if ( is_a( $call, 'Ripcord_Client_Call' ) ) | |
{ | |
$call->bound =& $bound; | |
} else { | |
$bound = $call; | |
} | |
return null; | |
} | |
/** | |
* Method {method} not found. - Thrown by the ripcord server when a requested method isn't found. | |
*/ | |
const methodNotFound = -1; | |
/** | |
* Argument {index} is not a valid Ripcord call - Thrown by the client when passing incorrect arguments to system.multiCall. | |
*/ | |
const notRipcordCall = -2; | |
/** | |
* Cannot recurse system.multiCall - Thrown by the ripcord server when system.multicall is called within itself. | |
*/ | |
const cannotRecurse = -3; | |
/** | |
* Could not access {url} - Thrown by the transport object when unable to access the given url. | |
*/ | |
const cannotAccessURL = -4; | |
/** | |
* PHP XMLRPC library is not installed - Thrown by the ripcord server and client when the xmlrpc library is not installed. | |
*/ | |
const xmlrpcNotInstalled = -5; | |
/** | |
* Variable is not of type datetime - Thrown by the ripcord timestamp method. | |
*/ | |
const notDatetime = -6; | |
/** | |
* Variable is not of type base64 - Thrown by the ripcord binary method. | |
*/ | |
const notBase64 = -7; | |
/** | |
* Variable is not a classname or an object - Thrown by the ripcord server. | |
*/ | |
const unknownServiceType = -8; | |
} | |
/** | |
* This interface is implemented by all exceptions thrown by Ripcord. | |
* @package Ripcord | |
*/ | |
interface Ripcord_Exception {} | |
/** | |
* This class is used whenever an when a method passed to the server is invalid. | |
* - ripcord::methodNotFound (-1) Method {method} not found. - Thrown by the ripcord server when a requested method isn't found. | |
* @package Ripcord | |
*/ | |
class Ripcord_BadMethodCallException extends BadMethodCallException implements Ripcord_Exception { } | |
/** | |
* This class is used whenever prerequisite requirements are not met. | |
* - ripcord::xmlrpcNotInstalled (-5) PHP XMLRPC library is not installed - Thrown by the ripcord server and client when the xmlrpc library is not installed. | |
* @package Ripcord | |
*/ | |
class Ripcord_ConfigurationException extends Exception implements Ripcord_Exception { } | |
/** | |
* This class is used whenever an argument passed to a Ripcord method is invalid for any reason. Possible exceptions thrown are: | |
* - ripcord::notRipcordCall (-2) Argument {index} is not a valid Ripcord call - Thrown by the client when passing incorrect arguments to system.multiCall. | |
* - ripcord::cannotRecurse (-3) Cannot recurse system.multiCall - Thrown by the ripcord server when system.multicall is called within itself. | |
* - ripcord::notDateTime (-6) Variable is not of type datetime - Thrown by the ripcord timestamp method. | |
* - ripcord::notBase64 (-7) Variable is not of type base64 - Thrown by the ripcord binary method. | |
* - ripcord::unknownServiceType (-8) Variable is not a classname or an object - Thrown by the ripcord server. | |
* @package Ripcord | |
*/ | |
class Ripcord_InvalidArgumentException extends InvalidArgumentException implements Ripcord_Exception { } | |
/** | |
* This class is used whenever something goes wrong in sending / receiving data. Possible exceptions thrown are: | |
* - ripcord::cannotAccessURL (-4) Could not access {url} - Thrown by the transport object when unable to access the given url. | |
* @package Ripcord | |
*/ | |
class Ripcord_TransportException extends RuntimeException implements Ripcord_Exception { } | |
/** | |
* This class is used for exceptions generated from xmlrpc faults returned by the server. The code and message correspond | |
* to the code and message from the xmlrpc fault. | |
* @package Ripcord | |
*/ | |
class Ripcord_RemoteException extends Exception implements Ripcord_Exception { } | |
if (function_exists('spl_autoload_register')) { | |
spl_autoload_register('ripcord::load'); | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Ripcord is an easy to use XML-RPC library for PHP. | |
* @package Ripcord | |
* @author Auke van Slooten <auke@muze.nl> | |
* @copyright Copyright (C) 2010, Muze <www.muze.nl> | |
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License | |
* @version Ripcord 0.9 - PHP 5 | |
*/ | |
/** | |
* Includes the static ripcord factory class and exceptions | |
*/ | |
require_once(dirname(__FILE__).'/ripcord.php'); | |
/** | |
* This class implements a simple RPC client, for XML-RPC, (simplified) SOAP 1.1 or Simple RPC. The client abstracts | |
* the entire RPC process behind native PHP methods. Any method defined by the rpc server can be called as if it was | |
* a native method of the rpc client. | |
* | |
* E.g. | |
* <code> | |
* <?php | |
* $client = ripcord::client( 'http://www.moviemeter.nl/ws' ); | |
* $score = $client->film->getScore( 'e3dee9d19a8c3af7c92f9067d2945b59', 500 ); | |
* ?> | |
* </code> | |
* | |
* The client has a simple interface for the system.multiCall method: | |
* <code> | |
* <?php | |
* $client = ripcord::client( 'http://ripcord.muze.nl/ripcord.php' ); | |
* $client->system->multiCall()->start(); | |
* ripcord::bind( $methods, $client->system->listMethods() ); | |
* ripcord::bind( $foo, $client->getFoo() ); | |
* $client->system->multiCall()->execute(); | |
* ?> | |
* </code> | |
* | |
* The soap client can only handle the basic php types and doesn't understand xml namespaces. Use PHP's SoapClient | |
* for complex soap calls. This client cannot parse wsdl. | |
* If you want to skip the ripcord::client factory method, you _must_ provide a transport object explicitly. | |
* | |
* @link http://wiki.moviemeter.nl/index.php/API Moviemeter API documentation | |
* @package Ripcord | |
*/ | |
class Ripcord_Client | |
{ | |
/** | |
* The url of the rpc server | |
*/ | |
private $_url = ''; | |
/** | |
* The transport object, used to post requests. | |
*/ | |
private $_transport = null; | |
/** | |
* A list of output options, used with the xmlrpc_encode_request method. | |
* @see Ripcord_Server::setOutputOption() | |
*/ | |
private $_outputOptions = array( | |
"output_type" => "xml", | |
"verbosity" => "pretty", | |
"escaping" => array("markup"), | |
"version" => "xmlrpc", | |
"encoding" => "utf-8" | |
); | |
/** | |
* The namespace to use when calling a method. | |
*/ | |
private $_namespace = null; | |
/** | |
* A reference to the root client object. This is so when you use namespaced sub clients, you can always | |
* find the _response and _request data in the root client. | |
*/ | |
private $_rootClient = null; | |
/** | |
* A flag to indicate whether or not to preemptively clone objects passed as arguments to methods, see | |
* php bug #50282. Only correctly set in the rootClient. | |
*/ | |
private $_cloneObjects = false; | |
/** | |
* A flag to indicate if we are in a multiCall block. Start this with $client->system->multiCall()->start() | |
*/ | |
protected $_multiCall = false; | |
/** | |
* A list of deferred encoded calls. | |
*/ | |
protected $_multiCallArgs = array(); | |
/** | |
* The exact response from the rpc server. For debugging purposes. | |
*/ | |
public $_response = ''; | |
/** | |
* The exact request from the client. For debugging purposes. | |
*/ | |
public $_request = ''; | |
/** | |
* Whether or not to throw exceptions when an xml-rpc fault is returned by the server. Default is false. | |
*/ | |
public $_throwExceptions = false; | |
/** | |
* Whether or not to decode the XML-RPC datetime and base64 types to unix timestamp and binary string | |
* respectively. | |
*/ | |
public $_autoDecode = true; | |
/** | |
* The constructor for the RPC client. | |
* @param string $url The url of the rpc server | |
* @param array $options Optional. A list of outputOptions. See {@link Ripcord_Server::setOutputOption()} | |
* @param object $rootClient Optional. Used internally when using namespaces. | |
* @throws Ripcord_ConfigurationException (ripcord::xmlrpcNotInstalled) when the xmlrpc extension is not available. | |
*/ | |
public function __construct( $url, array $options = null, $transport = null, $rootClient = null ) | |
{ | |
if ( !isset($rootClient) ) { | |
$rootClient = $this; | |
if ( !function_exists( 'xmlrpc_encode_request' ) ) | |
{ | |
throw new Ripcord_ConfigurationException('PHP XMLRPC library is not installed', | |
ripcord::xmlrpcNotInstalled); | |
} | |
$version = explode('.', phpversion() ); | |
if ( (0 + $version[0]) == 5) { | |
if ( ( 0 + $version[1]) < 2 ) { | |
$this->_cloneObjects = true; // workaround for bug #50282 | |
} | |
} | |
} | |
$this->_rootClient = $rootClient; | |
$this->_url = $url; | |
if ( isset($options) ) | |
{ | |
if ( isset($options['namespace']) ) | |
{ | |
$this->_namespace = $options['namespace']; | |
unset( $options['namespace'] ); | |
} | |
$this->_outputOptions = $options; | |
} | |
if ( isset($transport) ) { | |
$this->_transport = $transport; | |
} | |
} | |
/** | |
* This method catches any native method called on the client and calls it on the rpc server instead. It automatically | |
* parses the resulting xml and returns native php type results. | |
* @throws Ripcord_InvalidArgumentException (ripcord::notRipcordCall) when handling a multiCall and the | |
* arguments passed do not have the correct method call information | |
* @throws Ripcord_RemoteException when _throwExceptions is true and the server returns an XML-RPC Fault. | |
*/ | |
public function __call($name, $args) | |
{ | |
if ( isset($this->_namespace) ) | |
{ | |
$name = $this->_namespace . '.' . $name; | |
} | |
if ( $name === 'system.multiCall' || $name == 'system.multicall' ) | |
{ | |
if ( !$args || ( is_array($args) && count($args)==0 ) ) | |
{ | |
// multiCall is called without arguments, so return the fetch interface object | |
return new Ripcord_Client_MultiCall( $this->_rootClient, $name ); | |
} else if ( is_array( $args ) && (count( $args ) == 1) && | |
is_array( $args[0] ) && !isset( $args[0]['methodName'] ) ) | |
{ | |
// multicall is called with a simple array of calls. | |
$args = $args[0]; | |
} | |
$this->_rootClient->_multiCall = false; | |
$params = array(); | |
$bound = array(); | |
foreach ( $args as $key => $arg ) | |
{ | |
if ( !is_a( $arg, 'Ripcord_Client_Call' ) && | |
(!is_array($arg) || !isset($arg['methodName']) ) ) | |
{ | |
throw new Ripcord_InvalidArgumentException( | |
'Argument '.$key.' is not a valid Ripcord call', | |
ripcord::notRipcordCall); | |
} | |
if ( is_a( $arg, 'Ripcord_Client_Call' ) ) | |
{ | |
$arg->index = count( $params ); | |
$params[] = $arg->encode(); | |
} | |
else | |
{ | |
$arg['index'] = count( $params ); | |
$params[] = array( | |
'methodName' => $arg['methodName'], | |
'params' => isset($arg['params']) ? | |
(array) $arg['params'] : array() | |
); | |
} | |
$bound[$key] = $arg; | |
} | |
$args = array( $params ); | |
$this->_rootClient->_multiCallArgs = array(); | |
} | |
if ( $this->_rootClient->_multiCall ) { | |
$call = new Ripcord_Client_Call( $name, $args ); | |
$this->_rootClient->_multiCallArgs[] = $call; | |
return $call; | |
} | |
if ($this->_rootClient->_cloneObjects) { //workaround for php bug 50282 | |
foreach( $args as $key => $arg) { | |
if (is_object($arg)) { | |
$args[$key] = clone $arg; | |
} | |
} | |
} | |
$request = xmlrpc_encode_request( $name, $args, $this->_outputOptions ); | |
$response = $this->_transport->post( $this->_url, $request ); | |
$result = xmlrpc_decode( $response ); | |
$this->_rootClient->_request = $request; | |
$this->_rootClient->_response = $response; | |
if ( ripcord::isFault( $result ) && $this->_throwExceptions ) | |
{ | |
throw new Ripcord_RemoteException($result['faultString'], $result['faultCode']); | |
} | |
if ( isset($bound) && is_array( $bound ) ) | |
{ | |
foreach ( $bound as $key => $callObject ) | |
{ | |
if ( is_a( $callObject, 'Ripcord_Client_Call' ) ) | |
{ | |
$returnValue = $result[$callObject->index]; | |
} | |
else | |
{ | |
$returnValue = $result[$callObject['index']]; | |
} | |
if ( is_array( $returnValue ) && count( $returnValue ) == 1 ) | |
{ | |
// XML-RPC specification says that non-fault results must be in a single item array | |
$returnValue = current($returnValue); | |
} | |
if ($this->_autoDecode) | |
{ | |
$type = xmlrpc_get_type($returnValue); | |
switch ($type) | |
{ | |
case 'base64' : | |
$returnValue = ripcord::binary($returnValue); | |
break; | |
case 'datetime' : | |
$returnValue = ripcord::timestamp($returnValue); | |
break; | |
} | |
} | |
if ( is_a( $callObject, 'Ripcord_Client_Call' ) ) { | |
$callObject->bound = $returnValue; | |
} | |
$bound[$key] = $returnValue; | |
} | |
$result = $bound; | |
} | |
return $result; | |
} | |
/** | |
* This method catches any reference to properties of the client and uses them as a namespace. The | |
* property is automatically created as a new instance of the rpc client, with the name of the property | |
* as a namespace. | |
* @param string $name The name of the namespace | |
* @return object A Ripcord Client with the given namespace set. | |
*/ | |
public function __get($name) | |
{ | |
$result = null; | |
if ( !isset($this->{$name}) ) | |
{ | |
$result = new Ripcord_Client( | |
$this->_url, | |
array_merge($this->_outputOptions, array( | |
'namespace' => $this->_namespace ? | |
$this->_namespace . '.' . $name : $name | |
) ), | |
$this->_transport, | |
$this->_rootClient | |
); | |
$this->{$name} = $result; | |
} | |
return $result; | |
} | |
} | |
/** | |
* This class provides the fetch interface for system.multiCall. It is returned | |
* when calling $client->system->multiCall() with no arguments. Upon construction | |
* it puts the originating client into multiCall deferred mode. The client will | |
* gather the requested method calls instead of executing them immediately. It | |
* will them execute all of them, in order, when calling | |
* $client->system->multiCall()->fetch(). | |
* This class extends Ripcord_Client only so it has access to its protected _multiCall | |
* property. | |
*/ | |
class Ripcord_Client_MultiCall extends Ripcord_Client | |
{ | |
/* | |
* The reference to the originating client to put into multiCall mode. | |
*/ | |
private $client = null; | |
/* | |
* This method creates a new multiCall fetch api object. | |
*/ | |
public function __construct( $client, $methodName = 'system.multiCall' ) | |
{ | |
$this->client = $client; | |
$this->methodName = $methodName; | |
} | |
/* | |
* This method puts the client into multiCall mode. While in this mode all | |
* method calls are collected as deferred calls (Ripcord_Client_Call). | |
*/ | |
public function start() | |
{ | |
$this->client->_multiCall = true; | |
} | |
/* | |
* This method finally calls the clients multiCall method with all deferred | |
* method calls since multiCall mode was enabled. | |
*/ | |
public function execute() | |
{ | |
if ($this->methodName=='system.multiCall') { | |
return $this->client->system->multiCall( $this->client->_multiCallArgs ); | |
} else { // system.multicall | |
return $this->client->system->multicall( $this->client->_multiCallArgs ); | |
} | |
} | |
} | |
/** | |
* This class is used with the Ripcord_Client when calling system.multiCall. Instead of immediately calling the method on the rpc server, | |
* a Ripcord_Client_Call object is created with all the information needed to call the method using the multicall parameters. The call object is | |
* returned immediately and is used as input parameter for the multiCall call. The result of the call can be bound to a php variable. This | |
* variable will be filled with the result of the call when it is available. | |
* @package Ripcord | |
*/ | |
class Ripcord_Client_Call | |
{ | |
/** | |
* The method to call on the rpc server | |
*/ | |
public $method = null; | |
/** | |
* The arguments to pass on to the method. | |
*/ | |
public $params = array(); | |
/** | |
* The index in the multicall request array, if any. | |
*/ | |
public $index = null; | |
/** | |
* A reference to the php variable to fill with the result of the call, if any. | |
*/ | |
public $bound = null; | |
/** | |
* The constructor for the Ripcord_Client_Call class. | |
* @param string $method The name of the rpc method to call | |
* @param array $params The parameters for the rpc method. | |
*/ | |
public function __construct($method, $params) | |
{ | |
$this->method = $method; | |
$this->params = $params; | |
} | |
/** | |
* This method allows you to bind a php variable to the result of this method call. | |
* When the method call's result is available, the php variable will be filled with | |
* this result. | |
* @param mixed $bound The variable to bind the result from this call to. | |
* @return object Returns this object for chaining. | |
*/ | |
public function bind(&$bound) | |
{ | |
$this->bound =& $bound; | |
return $this; | |
} | |
/** | |
* This method returns the correct format for a multiCall argument. | |
* @return array An array with the methodName and params | |
*/ | |
public function encode() { | |
return array( | |
'methodName' => $this->method, | |
'params' => (array) $this->params | |
); | |
} | |
} | |
/** | |
* This interface describes the minimum interface needed for the transport object used by the | |
* Ripcord_Client | |
* @package Ripcord | |
*/ | |
interface Ripcord_Transport | |
{ | |
/** | |
* This method must post the request to the given url and return the results. | |
* @param string $url The url to post to. | |
* @param string $request The request to post. | |
* @return string The server response | |
*/ | |
public function post( $url, $request ); | |
} | |
/** | |
* This class implements the Ripcord_Transport interface using PHP streams. | |
* @package Ripcord | |
*/ | |
class Ripcord_Transport_Stream implements Ripcord_Transport | |
{ | |
/** | |
* A list of stream context options. | |
*/ | |
private $options = array(); | |
/** | |
* Contains the headers sent by the server. | |
*/ | |
public $responseHeaders = null; | |
/** | |
* This is the constructor for the Ripcord_Transport_Stream class. | |
* @param array $contextOptions Optional. An array with stream context options. | |
*/ | |
public function __construct( $contextOptions = null ) | |
{ | |
if ( isset($contextOptions) ) | |
{ | |
$this->options = $contextOptions; | |
} | |
} | |
/** | |
* This method posts the request to the given url. | |
* @param string $url The url to post to. | |
* @param string $request The request to post. | |
* @return string The server response | |
* @throws Ripcord_TransportException (ripcord::cannotAccessURL) when the given URL cannot be accessed for any reason. | |
*/ | |
public function post( $url, $request ) | |
{ | |
$options = array_merge( | |
$this->options, | |
array( | |
'http' => array( | |
'method' => "POST", | |
'header' => "Content-Type: text/xml", | |
'content' => $request | |
) | |
) | |
); | |
$context = stream_context_create( $options ); | |
$result = @file_get_contents( $url, false, $context ); | |
$this->responseHeaders = $http_response_header; | |
if ( !$result ) | |
{ | |
throw new Ripcord_TransportException( 'Could not access ' . $url, | |
ripcord::cannotAccessURL ); | |
} | |
return $result; | |
} | |
} | |
/** | |
* This class implements the Ripcord_Transport interface using CURL. | |
* @package Ripcord | |
*/ | |
class Ripcord_Transport_CURL implements Ripcord_Transport | |
{ | |
/** | |
* A list of CURL options. | |
*/ | |
private $options = array(); | |
/** | |
* A flag that indicates whether or not we can safely pass the previous exception to a new exception. | |
*/ | |
private $skipPreviousException = false; | |
/** | |
* Contains the headers sent by the server. | |
*/ | |
public $responseHeaders = null; | |
/** | |
* This is the constructor for the Ripcord_Transport_CURL class. | |
* @param array $curlOptions A list of CURL options. | |
*/ | |
public function __construct( $curlOptions = null ) | |
{ | |
if ( isset($curlOptions) ) | |
{ | |
$this->options = $curlOptions; | |
} | |
$version = explode('.', phpversion() ); | |
if ( ( (0 + $version[0]) == 5) && ( 0 + $version[1]) < 3 ) { // previousException supported in php >= 5.3 | |
$this->_skipPreviousException = true; | |
} | |
} | |
/** | |
* This method posts the request to the given url | |
* @param string $url The url to post to. | |
* @param string $request The request to post. | |
* @throws Ripcord_TransportException (ripcord::cannotAccessURL) when the given URL cannot be accessed for any reason. | |
* @return string The server response | |
*/ | |
public function post( $url, $request) | |
{ | |
$curl = curl_init(); | |
$options = (array) $this->options + array( | |
CURLOPT_RETURNTRANSFER => 1, | |
CURLOPT_URL => $url, | |
CURLOPT_POST => true, | |
CURLOPT_POSTFIELDS => $request, | |
CURLOPT_HEADER => true | |
); | |
curl_setopt_array( $curl, $options ); | |
$contents = curl_exec( $curl ); | |
$headerSize = curl_getinfo( $curl, CURLINFO_HEADER_SIZE ); | |
$this->responseHeaders = substr( $contents, 0, $headerSize ); | |
$contents = substr( $contents, $headerSize ); | |
if ( curl_errno( $curl ) ) | |
{ | |
$errorNumber = curl_errno( $curl ); | |
$errorMessage = curl_error( $curl ); | |
curl_close( $curl ); | |
$version = explode('.', phpversion() ); | |
if (!$this->_skipPreviousException) { // previousException supported in php >= 5.3 | |
$exception = new Ripcord_TransportException( 'Could not access ' . $url | |
, ripcord::cannotAccessURL | |
, new Exception( $errorMessage, $errorNumber ) | |
); | |
} else { | |
$exception = new Ripcord_TransportException( 'Could not access ' . $url | |
. ' ( original CURL error: ' . $errorMessage . ' ) ', | |
ripcord::cannotAccessURL | |
); | |
} | |
throw $exception; | |
} | |
curl_close($curl); | |
return $contents; | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Ripcord is an easy to use XML-RPC library for PHP. | |
* @package Ripcord | |
* @author Auke van Slooten <auke@muze.nl> | |
* @copyright Copyright (C) 2010, Muze <www.muze.nl> | |
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License | |
* @version Ripcord 0.9 - PHP 5 | |
*/ | |
/** | |
* This interface defines the minimum methods any documentor needs to implement. | |
* @package Ripcord | |
*/ | |
interface Ripcord_Documentor_Interface | |
{ | |
public function setMethodData( $methods ); | |
public function handle( $rpcServer ); | |
public function getIntrospectionXML(); | |
} | |
/** | |
* This class implements the default documentor for the ripcord server. Any request to the server | |
* without a request_xml is handled by the documentor. | |
* @package Ripcord | |
*/ | |
class Ripcord_Documentor implements Ripcord_Documentor_Interface | |
{ | |
/** | |
* The object to parse the docComments. | |
*/ | |
private $docCommentParser = null; | |
/** | |
* The name of the rpc server, used as the title and heading of the default HTML page. | |
*/ | |
public $name = 'Ripcord: Simple RPC Server'; | |
/** | |
* A url to an optional css file or a css string for an inline stylesheet. | |
*/ | |
public $css = " | |
html { | |
font-family: georgia, times, serif; | |
font-size: 79%; | |
background-color: #EEEEEE; | |
} | |
h1 { | |
font-family: 'arial black', helvetica, sans-serif; | |
font-size: 2em; | |
font-weight: normal; | |
margin: -20px -21px 0.4em -20px; | |
padding: 40px 20px 20px; | |
background: #01648E; /* for non-css3 browsers */ | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00486E', endColorstr='#09799E'); /* for IE */ | |
background: -webkit-gradient(linear, left top, left bottom, from(#00486E), to(#09799E)); /* for webkit browsers */ | |
background: -moz-linear-gradient(top, #00486E, #09799E); /* for firefox 3.6+ */ | |
color: white; | |
border-bottom: 4px solid black; | |
text-shadow: black 0.1em 0.1em 0.2em; | |
} | |
h2 { | |
font-family: arial, helvetica, sans-serif; | |
font-weight: bold; | |
font-size: 1.4em; | |
color: #444444; | |
text-shadow: #AAAAAA 0.1em 0.1em 0.2em; | |
margin-top: 2.5em; | |
border-bottom: 1px solid #09799E; | |
} | |
h3 { | |
font-family: arial, helvetica, sans-serif; | |
font-weight: normal; | |
font-size: 1.4em; | |
color: #555555; | |
text-shadow: #AAAAAA 0.1em 0.1em 0.2em; | |
margin-bottom: 0px; | |
} | |
div.signature { | |
font-family: courier, monospace; | |
margin-bottom: 1.4em; | |
} | |
ul, ol, li { | |
margin: 0px; | |
padding: 0px; | |
} | |
ul, ol { | |
color: #09799E; | |
margin-bottom: 1.4em; | |
} | |
ul li { | |
list-style: square; | |
} | |
ul li, ol li { | |
margin-left: 20px; | |
} | |
li span, li label { | |
color: black; | |
} | |
li.param label { | |
font-family: courier, monospace; | |
padding-right: 1.4em; | |
} | |
a { | |
text-decoration: none; | |
} | |
a:hover { | |
text-decoration: underline; | |
} | |
body { | |
background-color: white; | |
width: 830px; | |
margin: 10px auto; | |
padding: 20px; | |
-moz-box-shadow: 5px 5px 5px #ccc; | |
-webkit-box-shadow: 5px 5px 5px #ccc; | |
box-shadow: 5px 5px 5px #ccc; | |
} | |
code { | |
display: block; | |
background-color: #999999; | |
padding: 10px; | |
margin: 0.4em 0px 1.4em 0px; | |
color: white; | |
white-space: pre; | |
font-family: courier, monospace; | |
font-size: 1.2em; | |
} | |
.tag, .argName, .argType { | |
margin-right: 10px; | |
} | |
.argument { | |
margin-left: 20px; | |
} | |
.footer { | |
font-family: helvetica, sans-serif; | |
font-size: 0.9em; | |
font-weight: normal; | |
margin: 0px -21px -20px -20px; | |
padding: 20px; | |
background: #01648E; /* for non-css3 browsers */ | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00486E', endColorstr='#09799E'); /* for IE */ | |
background: -webkit-gradient(linear, left top, left bottom, from(#00486E), to(#09799E)); /* for webkit browsers */ | |
background: -moz-linear-gradient(top, #00486E, #09799E); /* for firefox 3.6+ */ | |
color: white; | |
} | |
.footer a { | |
color: white; | |
text-decoration: none; | |
} | |
"; | |
/** | |
* The wsdl 1.0 description. | |
*/ | |
public $wsdl = false; | |
/** | |
* The wsdl 2.0 description | |
*/ | |
public $wsdl2 = false; | |
/** | |
* Which version of the XML vocabulary the server implements. Either 'xmlrpc', 'soap 1.1', 'simple' or 'auto'. | |
*/ | |
public $version = 'auto'; | |
/** | |
* The root URL of the rpc server. | |
*/ | |
public $root = ''; | |
/** | |
* Optional header text for the online documentation. | |
*/ | |
public $header = ''; | |
/** | |
* Optional footer text for the online documentation. | |
*/ | |
public $footer = ''; | |
/** | |
* A list of method data, containing all the user supplied methods the rpc server implements. | |
*/ | |
private $methods = null; | |
/** | |
* The constructor for the Ripcord_Documentor class. | |
* @param array $options. Optional. Allows you to set the public properties of this class upon construction. | |
*/ | |
public function __construct( $options = null, $docCommentParser = null ) | |
{ | |
$check = array( 'name', 'css', 'wsdl', 'wsdl2', 'root', 'version', 'header', 'footer' ); | |
foreach ( $check as $name ) | |
{ | |
if ( isset($options[$name]) ) | |
{ | |
$this->{$name} = $options[$name]; | |
} | |
} | |
$this->docCommentParser = $docCommentParser; | |
} | |
/** | |
* This method fills the list of method data with all the user supplied methods of the rpc server. | |
* @param array $methodData A list of methods with name and callback information. | |
*/ | |
public function setMethodData( $methodData ) | |
{ | |
$this->methods = $methodData; | |
} | |
/** | |
* This method handles any request which isn't a valid rpc request. | |
* @param object $rpcServer A reference to the active rpc server. | |
*/ | |
public function handle( $rpcServer ) | |
{ | |
$methods = $rpcServer->call('system.listMethods'); | |
echo '<!DOCTYPE html>'; | |
echo '<html><head><title>' . $this->name . '</title>'; | |
if ( isset($this->css) ) | |
{ | |
if (strpos($this->css, "\n")!==false) { | |
echo '<style type="text/css">'.$this->css.'</style>'; | |
} else { | |
echo '<link rel="stylesheet" type="text/css" href="' . $this->css . '">'; | |
} | |
} | |
echo '</head><body>'; | |
echo '<div class="content">'; | |
echo '<h1>' . $this->name . '</h1>'; | |
echo $this->header; | |
echo '<p>'; | |
$showWSDL = false; | |
switch ( $this->version ) | |
{ | |
case 'xmlrpc': | |
echo 'This server implements the <a href="http://www.xmlrpc.com/spec">XML-RPC specification</a>'; | |
break; | |
case 'simple': | |
echo 'This server implements the <a href="http://sites.google.com/a/simplerpc.org/simplerpc/Home/simplerpc-specification-v09">SimpleRPC 1.0 specification</a>'; | |
break; | |
case 'auto'; | |
echo 'This server implements the <a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/">SOAP 1.1</a>, <a href="http://www.xmlrpc.com/spec">XML-RPC</a> and <a href="http://sites.google.com/a/simplerpc.org/simplerpc/Home/simplerpc-specification-v09">SimpleRPC 1.0</a> specification.'; | |
$showWSDL = true; | |
break; | |
case 'soap 1.1': | |
echo 'This server implements the <a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/">SOAP 1.1 specification</a>.'; | |
$showWSDL = true; | |
break; | |
} | |
echo '</p>'; | |
if ( $showWSDL && ( $this->wsdl || $this->wsdl2 ) ) | |
{ | |
echo '<ul>'; | |
if ($this->wsdl) | |
{ | |
echo '<li><a href="' . $this->root . '?wsdl">WSDL 1.1 Description</a></li>'; | |
} | |
if ($this->wsdl2) | |
{ | |
echo '<li><a href="' . $this->root . '?wsdl2">WSDL 2.0 Description</a></li>'; | |
} | |
echo '</ul>'; | |
} | |
$methods = $rpcServer->call( 'system.describeMethods' ); | |
$allMethods = array(); | |
$allFunctions = array(); | |
foreach( $methods['methodList'] as $index => $method ) | |
{ | |
if ( strpos( $method['name'], '.' ) !== false ) | |
{ | |
$allMethods[ $method['name'] ] = $index; | |
} else { | |
$allFunctions[ $method['name'] ] = $index; | |
} | |
} | |
ksort( $allMethods ); | |
ksort( $allFunctions ); | |
$allMethods = $allFunctions + $allMethods; | |
echo '<div class="index"><h2>Methods</h2><ul>'; | |
foreach ( $allMethods as $methodName => $methodIndex ) | |
{ | |
echo '<li><a href="#method_' . (int)$methodIndex . '">' . $methodName . '</a></li>'; | |
} | |
echo '</ul></div>'; | |
$currentClass = ''; | |
$class = ''; | |
echo '<div class="functions">'; | |
foreach ( $allMethods as $methodName => $methodIndex ) | |
{ | |
$method = $methods['methodList'][$methodIndex]; | |
$pos = strpos( $methodName, '.'); | |
if ( $pos !== false ) | |
{ | |
$class = substr( $methodName, 0, $pos ); | |
} | |
if ( $currentClass != $class ) | |
{ | |
echo '</div>'; | |
echo '<div class="class_'.$class.'">'; | |
$currentClass = $class; | |
} | |
echo '<h2 id="method_'.$methodIndex.'">' . $method['name'] . '</h2>'; | |
if ( isset( $method['signatures'] ) ) | |
{ | |
foreach ( $method['signatures'] as $signature ) | |
{ | |
echo '<div class="signature">'; | |
if ( is_array( $signature['returns'] ) ) { | |
$return = $signature['returns'][0]; | |
echo '(' . $return['type'] . ') '; | |
} | |
echo $method['name'] . '('; | |
$paramInfo = false; | |
if ( is_array( $signature['params'] ) ) | |
{ | |
$paramInfo = $signature['params']; | |
$params = ''; | |
foreach ( $signature['params'] as $param ) | |
{ | |
$params .= ', (' . $param['type'] . ') ' . $param['name'] . ' '; | |
} | |
echo substr($params, 1); | |
} | |
echo ')</div>'; | |
if ( is_array( $paramInfo ) ) | |
{ | |
echo '<div class="params"><h3>Parameters</h3><ul>'; | |
foreach ( $paramInfo as $param ) | |
{ | |
echo '<li class="param">'; | |
echo '<label>(' . $param['type'] . ') ' . $param['name'] . '</label> '; | |
echo '<span>' . $param['description'] . '</span>'; | |
echo '</li>'; | |
} | |
echo '</ul></div>'; | |
} | |
} | |
} | |
if ( $method['purpose'] ) | |
{ | |
echo '<div class="purpose">' . $method['purpose'] . '</div>'; | |
} | |
if ( isset( $method['notes'] ) && is_array( $method['notes'] ) ) | |
{ | |
echo '<div class="notes"><h3>Notes</h3><ol>'; | |
foreach ( $method['notes'] as $note ) | |
{ | |
echo '<li><span>' . $note. '</span></li>'; | |
} | |
echo '</ol></div>'; | |
} | |
if ( isset( $method['see'] ) && is_array( $method['see'] ) ) | |
{ | |
echo '<div class="see">'; | |
echo '<h3>See</h3>'; | |
echo '<ul>'; | |
foreach ( $method['see'] as $link => $description) | |
{ | |
echo '<li>'; | |
if ( isset( $allMethods[$link] ) ) | |
{ | |
echo '<a href="#method_' . (int)$allMethods[$link] .'">' . $link . '</a> <span>' . $description . '</span>'; | |
} else { | |
echo '<span>' . $link . ' ' . $description . '</span>'; | |
} | |
echo '</li>'; | |
} | |
echo '</ul></div>'; | |
} | |
} | |
echo '</div>'; | |
echo $this->footer; | |
echo '<div class="footer">'; | |
echo 'Powered by <a href="http://ripcord.googlecode.com/">Ripcord : Simple RPC Server</a>.'; | |
echo '</div>'; | |
echo '</div></body></html>'; | |
} | |
/** | |
* This method returns an XML document in the introspection format expected by | |
* xmlrpc_server_register_introspection_callback. It uses the php Reflection | |
* classes to gather information from the registered methods. | |
* Descriptions are added from phpdoc docblocks if found. | |
* @return string XML string with the introspection data. | |
*/ | |
function getIntrospectionXML() | |
{ | |
$xml = "<?xml version='1.0' ?><introspection version='1.0'><methodList>"; | |
if ( isset($this->methods) && is_array( $this->methods ) ) | |
{ | |
foreach ($this->methods as $method => $methodData ) | |
{ | |
if ( is_array( $methodData['call'] ) ) | |
{ | |
$reflection = new ReflectionMethod( | |
$methodData['call'][0], | |
$methodData['call'][1] | |
); | |
} | |
else | |
{ | |
$reflection = new ReflectionFunction( $methodData['call'] ); | |
} | |
$description = $reflection->getDocComment(); | |
if ( $description && $this->docCommentParser ) | |
{ | |
$data = $this->docCommentParser->parse( $description ); | |
if ($data['description']) | |
{ | |
$description = $data['description']; | |
} | |
} | |
if ($description) | |
{ | |
$description = '<p>' . str_replace( array( "\r\n\r\n", "\n\n") , '</p><p>', $description) | |
. '</p>'; | |
} | |
if ( is_array( $data ) ) | |
{ | |
foreach( $data as $key => $value ) | |
{ | |
switch( $key ) | |
{ | |
case 'category' : | |
case 'deprecated' : | |
case 'package' : | |
$description .= '<div class="' . $key . '"><span class="tag">' | |
. $key . '</span>' . $value .'</div>'; | |
break; | |
default : | |
break; | |
} | |
} | |
} | |
$xml .= '<methodDescription name="' . $method . '"><purpose><![CDATA[' | |
. $description . ']]></purpose>'; | |
if ( is_array( $data ) && ( isset( $data['arguments'] ) || isset( $data['return'] ) ) ) | |
{ | |
$xml .= '<signatures><signature>'; | |
if ( isset( $data['arguments'] ) && is_array($data['arguments']) ) | |
{ | |
$xml .= '<params>'; | |
foreach ( $data['arguments'] as $name => $argument ) | |
{ | |
if ( $name[0] == '$' ) | |
{ | |
$name = substr( $name, 1 ); | |
} | |
$xml .= '<value type="' . htmlspecialchars( $argument['type'] ) | |
. '" name="' . htmlspecialchars( $name ) . '"><![CDATA[' . $argument['description'] | |
. ']]></value>'; | |
} | |
$xml .= '</params>'; | |
} | |
if ( isset( $data['return'] ) && is_array( $data['return'] ) ) | |
{ | |
$xml .= '<returns><value type="' . htmlspecialchars($data['return']['type']) | |
. '"><![CDATA[' . $data['return']['description'] . ']]></value></returns>'; | |
} | |
$xml .= '</signature></signatures>'; | |
} | |
$xml .= '</methodDescription>'; | |
} | |
} | |
$xml .= "</methodList></introspection>"; | |
return $xml; | |
} | |
} | |
/** | |
* This interface describes the minimum interface needed for a comment parser object used by the | |
* Ripcord_Documentor | |
* @package Ripcord | |
*/ | |
interface Ripcord_Documentor_Parser | |
{ | |
/** | |
* This method parses a given docComment block and returns an array with information. | |
* @param string $commentBlock The docComment block. | |
* @return array The parsed information. | |
*/ | |
public function parse( $commentBlock ); | |
} | |
/** | |
* This class implements the Ripcord_Documentor_Parser interface, parsing the docComment | |
* as a phpdoc style docComment. | |
* @package Ripcord | |
*/ | |
class Ripcord_Documentor_Parser_phpdoc implements Ripcord_Documentor_Parser | |
{ | |
/** | |
* This method parses a given docComment block and returns an array with information. | |
* @param string $commentBlock The docComment block. | |
* @return array The parsed information. | |
*/ | |
public function parse( $commentBlock) | |
{ | |
$this->currentTag = 'description'; | |
$description = preg_replace('/^(\s*(\/\*\*|\*\/|\*))/m', '', $commentBlock); | |
$info = array(); | |
$lines = explode( "\n", $description ); | |
foreach ( $lines as $line ) { | |
$info = $this->parseLine( $line, $info ); | |
} | |
return $info; //array( 'description' => $description ); | |
} | |
/** | |
* This method parses a single line from the comment block. | |
*/ | |
private function parseLine( $line, $info ) | |
{ | |
$handled = false; | |
if (preg_match('/^\s*(@[a-z]+)\s(.*)$/i', $line, $matches)) | |
{ | |
$this->currentTag = substr($matches[1], 1); | |
$line = trim( substr($line, strlen($this->currentTag)+2 ) ); | |
switch( $this->currentTag ) | |
{ | |
case 'param' : | |
if ( preg_match('/^\s*([[:alpha:]|]+)\s([[:alnum:]$_]+)(.*)$/i', $line, $matches) ) | |
{ | |
if ( !isset($info['arguments']) ) { | |
$info['arguments'] = array(); | |
} | |
if ( !isset($info['arguments'][$matches[2]]) ) { | |
$info['arguments'][$matches[2]] = array('description' => ''); | |
} | |
$info['arguments'][$matches[2]]['type'] = $matches[1]; | |
$info['arguments'][$matches[2]]['description'] .= $this->parseDescription($matches[3]); | |
} | |
$handled = true; | |
break; | |
case 'return' : | |
if ( preg_match('/^\s*([[:alpha:]|]+)\s(.*)$/i', $line, $matches) ) | |
{ | |
if ( !isset($info['return']) ) { | |
$info['return'] = array( 'description' => '' ); | |
} | |
$info['return']['type'] = $matches[1]; | |
$info['return']['description'] .= $this->parseDescription($matches[2]); | |
} | |
$handled = true; | |
break; | |
} | |
} | |
if (!$handled) { | |
switch( $this->currentTag) { | |
case 'param' : | |
case 'return' : | |
if ( !isset( $info[$this->currentTag] ) ) { | |
$info[$this->currentTag] = array(); | |
} | |
$info[$this->currentTag]['description'] .= $this->parseDescription($line); | |
break; | |
default: | |
if ( !isset( $info[$this->currentTag] ) ) { | |
$info[$this->currentTag] = ''; | |
} | |
$info[$this->currentTag] .= $this->parseDescription($line); | |
break; | |
} | |
} | |
return $info; | |
} | |
/** | |
* This method parses only the text description part of a line of the comment block. | |
*/ | |
private function parseDescription( $line ) { | |
while ( preg_match('/{@([^}]*)}/', $line, $matches) ) { | |
switch( $matches[1] ) { | |
case 'internal' : | |
$line = str_replace( $matches[0], '', $line ); | |
break; | |
default : | |
$line = str_replace( $matches[0], $matches[1], $line ); | |
break; | |
} | |
} | |
$line = str_replace( array( '\@', '{@*}' ), array( '@', '*/' ), $line ); | |
return $line; | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Ripcord is an easy to use XML-RPC library for PHP. | |
* @package Ripcord | |
* @author Auke van Slooten <auke@muze.nl> | |
* @copyright Copyright (C) 2010, Muze <www.muze.nl> | |
* @license http://opensource.org/licenses/gpl-3.0.html GNU Public License | |
* @version Ripcord 0.9 - PHP 5 | |
*/ | |
/** | |
* Includes the static ripcord factory class and exceptions | |
*/ | |
require_once(dirname(__FILE__).'/ripcord.php'); | |
/** | |
* This class implements the Ripcord server. It is an OO wrapper around PHP's XML-RPC methods, with some added features. | |
* You can create an XML-RPC (or Simple RPC or a simple SOAP 1.1) server by defining a class with public methods and passing | |
* an object (or array of objects) of this class to the constructor of Ripcord_Server. Then simply call the run() method. | |
* | |
* A basic example: | |
* <code> | |
* <?php | |
* $myObject = new MyClass(); | |
* $server = ripcord::server( $myObject ); | |
* $server->run(); | |
* ?> | |
* </code> | |
* | |
* An example with namespaces in the method names and a static class as rpc service. | |
* <code> | |
* <?php | |
* $myObject = new MyClass(); | |
* $server = ripcord::server( | |
* array( | |
* 'namespace1' => $myObject, | |
* 'namespace2' => 'myOtherClass' | |
* ) | |
* ); | |
* $server->run(); | |
* ?> | |
* </code> | |
* | |
* You don't need to instantiate a class to use it with Ripcord, in the above example 'myOtherClass' is the | |
* name of a PHP class to use. In addition you may also specify functions or methods directly, in any format | |
* that matches PHP's is_callable() criteria. | |
* @package Ripcord | |
*/ | |
/* | |
TODO: | |
- create seperate interface for encoding / decoding requests | |
- create xmlrpc-epi class using xmlrpc_encode/decode for xml-rpc, simple-rpc and for now soap | |
- add json-rpc class (http://json-rpc.org/wiki/specification) | |
- pass list of protocol parsers/generators in the constructor of the server | |
- protocol must know how to handle the system.* methods | |
*/ | |
class Ripcord_Server | |
{ | |
/** | |
* Contains a reference to the Ripcord documentor object. | |
* @see Ripcord_Documentor | |
*/ | |
private $documentor = null; | |
/** | |
* Contains a reference to the XML-RPC server created with xmlrpc_server_create. | |
*/ | |
private $xmlrpc = null; | |
/** | |
* Contains a list of methods set for this server. Excludes the system.* methods automatically | |
* created by PHP's xmlrpc_server_create. | |
*/ | |
private $methods = array(); | |
/** | |
* Contains an array with outputOptions, used when calling methods on the xmlrpc server created with | |
* xmlrpc_server_create. These options can be overridden through the $options parameter of the | |
* Ripcord_Server constructor. | |
* @see Ripcord_Server::setOutputOption() | |
*/ | |
private $outputOptions = array( | |
"output_type" => "xml", | |
"verbosity" => "pretty", | |
"escaping" => array("markup"), | |
"version" => "auto", | |
"encoding" => "utf-8" | |
); | |
/** | |
* Creates a new instance of the Ripcord server. | |
* @param mixed $services. Optional. An object or array of objects. The public methods in these objects will be exposed | |
* through the RPC server. If the services array has non-numeric keys, the key for each object will define its namespace. | |
* @param array $options. Optional. Allows you to override the default server settings. Accepted key names are: | |
* - 'documentor': allows you to specify an alternative HTML documentor class, or if set to false, no HTML documentor. | |
* - 'name' : The name of the server, used by the default HTML documentor. | |
* - 'css' : An url of a css file to link to in the HTML documentation. | |
* - 'wsdl' : The wsdl 1.0 description of this service (only usefull if you run the 'soap 1.1' version, or the 'auto' version | |
* - 'wsdl2' : The wsdl 2.0 description of this service | |
* In addition you can set any of the outputOptions for the xmlrpc server. | |
* @see Ripcord_Server::setOutputOption() | |
* @throws Ripcord_InvalidArgumentException (ripcord::unknownServiceType) when passed an incorrect service | |
* @throws Ripcord_ConfigurationException (ripcord::xmlrpcNotInstalled) when the xmlrpc extension in not available. | |
*/ | |
function __construct($services = null, $options = null, $documentor = null) | |
{ | |
if ( !function_exists( 'xmlrpc_server_create' ) ) | |
{ | |
throw new Ripcord_ConfigurationException('PHP XMLRPC library is not installed', | |
ripcord::xmlrpcNotInstalled ); | |
} | |
libxml_disable_entity_loader(); // prevents XXE attacks | |
$this->xmlrpc = xmlrpc_server_create(); | |
if (isset($services)) | |
{ | |
if (is_array($services)) | |
{ | |
foreach ($services as $serviceName => $service) | |
{ | |
$this->addService($service, $serviceName); | |
} | |
} else { | |
$this->addService($services); | |
} | |
} | |
if ( isset($documentor) && is_object($documentor) ) { | |
$this->documentor = $documentor; | |
xmlrpc_server_register_introspection_callback( $this->xmlrpc, | |
array( $this->documentor, 'getIntrospectionXML') ); | |
} | |
if ( isset($options) ) | |
{ | |
$this->outputOptions = array_merge($this->outputOptions, $options); | |
} | |
} | |
/** | |
* Allows you to add a service to the server after construction. | |
* @param object $service The object or class whose public methods must be added to the rpc server. May also be a function or method. | |
* @param string $serviceName Optional. The namespace for the methods. | |
* @throws Ripcord_InvalidArgumentException (ripcord::unknownServiceType) when passed an incorrect service | |
*/ | |
public function addService($service, $serviceName = 0) | |
{ | |
if ( is_object( $service ) ) | |
{ | |
$reflection = new ReflectionObject( $service ); | |
} | |
else if ( is_string( $service ) && class_exists( $service ) ) | |
{ | |
$reflection = new ReflectionClass( $service ); | |
} | |
else if ( is_callable( $service ) ) // method passed directly | |
{ | |
$this->addMethod( $serviceName, $service ); | |
return; | |
} | |
else | |
{ | |
throw new Ripcord_InvalidArgumentException( 'Unknown service type ' . $serviceName, | |
ripcord::unknownServiceType ); | |
} | |
if ( $serviceName && !is_numeric( $serviceName ) ) | |
{ | |
$serviceName .= '.'; | |
} | |
else | |
{ | |
$serviceName = ''; | |
} | |
$methods = $reflection->getMethods(); | |
if ( is_array( $methods ) ) | |
{ | |
foreach( $methods as $method ) | |
{ | |
if ( substr( $method->name, 0, 1 ) != '_' | |
&& !$method->isPrivate() && !$method->isProtected()) | |
{ | |
$rpcMethodName = $serviceName . $method->name; | |
$this->addMethod( | |
$rpcMethodName, | |
array( $service, $method->name ) | |
); | |
} | |
} | |
} | |
} | |
/** | |
* Allows you to add a single method to the server after construction. | |
* @param string $name The name of the method as exposed through the rpc server | |
* @param callback $method The name of the method to call, or an array with classname or object and method name. | |
*/ | |
public function addMethod($name, $method) | |
{ | |
$this->methods[$name] = array( | |
'name' => $name, | |
'call' => $method | |
); | |
xmlrpc_server_register_method( $this->xmlrpc, $name, array( $this, 'call' ) ); | |
} | |
/** | |
* Runs the rpc server. Automatically handles an incoming request. | |
*/ | |
public function run() | |
{ | |
if ($this->documentor) { | |
$this->documentor->setMethodData( $this->methods ); | |
} | |
$request_xml = file_get_contents( 'php://input' ); | |
if ( !$request_xml ) | |
{ | |
if ( ( $query = $_SERVER['QUERY_STRING'] ) | |
&& isset($this->wsdl[$query]) && $this->wsdl[$query] ) | |
{ | |
header('Content-type: text/xml'); | |
header('Access-Control-Allow-Origin: *'); | |
$wsdl = $this->wsdl[$query]; | |
header('Content-Length: '.strlen($wsdl) ); | |
echo $wsdl; | |
} | |
else if ( $this->documentor ) | |
{ | |
header('Content-type: text/html; charset=' . $this->outputOptions['encoding']); | |
$this->documentor->handle( $this, $this->methods ); | |
} | |
else | |
{ | |
// FIXME: add check for json-rpc protocol, if set and none of the xml protocols are set, use that | |
header('Content-type: text/xml'); | |
header('Access-Control-Allow-Origin: *'); | |
$result = xmlrpc_encode_request( | |
null, | |
ripcord::fault( -1, 'No request xml found.' ), | |
$this->outputOptions | |
); | |
header('Content-Length: '.strlen( $result ) ); | |
echo $result; | |
} | |
} | |
else | |
{ | |
// FIXME: add check for the protocol of the request, could be json-rpc, then check if it is supported. | |
header('Content-type: text/xml'); | |
header('Access-Control-Allow-Origin: *'); | |
$result = $this->handle( $request_xml ); | |
header('Content-Length: '.strlen($result) ); | |
echo $result; | |
} | |
} | |
/** | |
* This method wraps around xmlrpc_decode_request, since it is borken in many ways. This wraps | |
* around all the ugliness needed to make it not dump core and not print expat warnings. | |
*/ | |
private function parseRequest( $request_xml ) { | |
$xml = @simplexml_load_string($request_xml); | |
if (!$xml && !$xml->getNamespaces()) { | |
// FIXME: check for protocol json-rpc | |
//simplexml in combination with namespaces (soap) lets $xml evaluate to false | |
return xmlrpc_encode_request( | |
null, | |
ripcord::fault( -3, 'Invalid Method Call - Ripcord Server accepts only XML-RPC, SimpleRPC or SOAP 1.1 calls'), | |
$this->outputOptions | |
); | |
} else { | |
// prevent segmentation fault on incorrect xmlrpc request (without methodName) | |
$methodCall = $xml->xpath('//methodCall'); | |
if ($methodCall) { //xml-rpc | |
$methodName = $xml->xpath('//methodName'); | |
if (!$methodName) { | |
return xmlrpc_encode_request( | |
null, | |
ripcord::fault( -3, 'Invalid Method Call - No methodName given'), | |
$this->outputOptions | |
); | |
} | |
} | |
} | |
$method = null; | |
ob_start(); // xmlrpc_decode echo expat errors if the xml is not valid, can't stop it. | |
$params = xmlrpc_decode_request($request_xml, $method); | |
ob_end_clean(); // clean up any xml errors | |
return array( 'methodName' => $method, 'params' => $params ); | |
} | |
/** | |
* This method implements the system.multiCall method without dumping core. The built-in method from the | |
* xmlrpc library dumps core when you have registered any php methods, fixed in php 5.3.2 | |
*/ | |
private function multiCall( $params = null ) { | |
if ( $params && is_array( $params ) ) | |
{ | |
$result = array(); | |
$params = $params[0]; | |
foreach ( $params as $param ) { | |
$method = $param['methodName']; | |
$args = $param['params']; | |
try { | |
// XML-RPC specification says that non-fault results must be in a single item array | |
$result[] = array( $this->call($method, $args) ); | |
} catch( Exception $e) { | |
$result[] = ripcord::fault( $e->getCode(), $e->getMessage() ); | |
} | |
} | |
$result = xmlrpc_encode_request( null, $result, $this->outputOptions ); | |
} else { | |
$result = xmlrpc_encode_request( | |
null, | |
ripcord::fault( -2, 'Illegal or no params set for system.multiCall'), | |
$this->outputOptions | |
); | |
} | |
return $result; | |
} | |
/** | |
* Handles the given request xml | |
* @param string $request_xml The incoming request. | |
* @return string | |
*/ | |
public function handle($request_xml) | |
{ | |
$result = $this->parseRequest( $request_xml ); | |
if (!$result || ripcord::isFault( $result ) ) | |
{ | |
return $result; | |
} | |
else | |
{ | |
$method = $result['methodName']; | |
$params = $result['params']; | |
} | |
if ( $method == 'system.multiCall' || $method == 'system.multicall' ) { | |
// php's xml-rpc server (xmlrpc-epi) crashes on multicall, so handle it ourselves... fixed in php 5.3.2 | |
$result = $this->multiCall( $params ); | |
} else { | |
try { | |
$result = xmlrpc_server_call_method( | |
$this->xmlrpc, $request_xml, null, $this->outputOptions | |
); | |
} catch( Exception $e) { | |
$result = xmlrpc_encode_request( | |
null, | |
ripcord::fault( $e->getCode(), $e->getMessage() ), | |
$this->outputOptions | |
); | |
} | |
} | |
return $result; | |
} | |
/** | |
* Calls a method by its rpc name. | |
* @param string $method The rpc name of the method | |
* @param array $args The arguments to this method | |
* @return mixed | |
* @throws Ripcord_InvalidArgumentException (ripcord::cannotRecurse) when passed a recursive multiCall | |
* @throws Ripcord_BadMethodCallException (ripcord::methodNotFound) when the requested method isn't available. | |
*/ | |
public function call( $method, $args = null ) | |
{ | |
if ( isset( $this->methods[$method] ) ) | |
{ | |
$call = $this->methods[$method]['call']; | |
return call_user_func_array( $call, $args); | |
} else { | |
if ( substr( $method, 0, 7 ) == 'system.' ) | |
{ | |
if ( $method == 'system.multiCall' ) { | |
throw new Ripcord_InvalidArgumentException( | |
'Cannot recurse system.multiCall', ripcord::cannotRecurse ); | |
} | |
// system methods are handled internally by the xmlrpc server, so we've got to create a makebelieve request, | |
// there is no other way because of a badly designed API | |
$req = xmlrpc_encode_request( $method, $args, $this->outputOptions ); | |
$result = xmlrpc_server_call_method( $this->xmlrpc, $req, null, | |
$this->outputOptions); | |
return xmlrpc_decode( $result ); | |
} else { | |
throw new Ripcord_BadMethodCallException( 'Method '.$method.' not found.', | |
ripcord::methodNotFound ); | |
} | |
} | |
} | |
/** | |
* Allows you to set specific output options of the server after construction. | |
* @param string $option The name of the option | |
* @param mixed $value The value of the option | |
* The options are: | |
* - output_type: Return data as either php native data or xml encoded. Can be either 'php' or 'xml'. 'xml' is the default. | |
* - verbosity: Determines the compactness of generated xml. Can be either 'no_white_space', 'newlines_only' or 'pretty'. | |
* 'pretty' is the default. | |
* - escaping: Determines how/whether to escape certain characters. 1 or more values are allowed. If multiple, they need | |
* to be specified as a sub-array. Options are: 'cdata', 'non-ascii', 'non-print' and 'markup'. Default is 'non-ascii', | |
* 'non-print' and 'markup'. | |
* - version: Version of the xml vocabulary to use. Currently, three are supported: 'xmlrpc', 'soap 1.1' and 'simple'. The | |
* keyword 'auto' is also recognized and tells the server to respond in whichever version the request cam in. 'auto' is | |
* the default. | |
* - encoding: The character encoding that the data is in. Can be any supported character encoding. Default is 'utf-8'. | |
*/ | |
public function setOutputOption($option, $value) | |
{ | |
if ( isset($this->outputOptions[$option]) ) | |
{ | |
$this->outputOptions[$option] = $value; | |
return true; | |
} else { | |
return false; | |
} | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('login.php'); | |
// search | |
// $result = $model->execute_kw( | |
// $db,$uid,$password,"student","search", | |
// array( | |
// array( | |
// // array("id","=",1), | |
// // array("name","=","Admin") | |
// ), | |
// ) | |
// ); | |
// read | |
// $data = $model->execute_kw( | |
// $db, | |
// $uid, | |
// $password, | |
// "student", | |
// "read", | |
// array($result), | |
// array('fields'=>array("name","age","number")) | |
// ); | |
$result2 = $model->execute_kw( | |
$db,$uid,$password,"student","search_read", | |
array( | |
array( | |
// array("id","=",1), | |
array("name","ilike","ali") | |
), | |
), | |
array('fields'=>array("name","age","number")) | |
); | |
echo json_encode($result2); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once('login.php'); // import login php file | |
// $no = (int) $_GET['id']; | |
// $name = $_GET['name']; | |
$is_update = $model->execute_kw( | |
$db, //database name | |
$uid, | |
$password, | |
"student", | |
"write", | |
array( | |
array(6), | |
array("name"=>"Alaa Ahmed Ibrahim Osman") | |
), | |
); | |
echo json_encode($is_update); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment