<?php
/**
* @author Gaetano Giunta
- * @copyright (C) 2006-2015 G. Giunta
+ * @copyright (C) 2006-2021 G. Giunta
* @license code licensed under the BSD License: see file license.txt
*/
namespace PhpXmlRpc;
+use PhpXmlRpc\Helper\Logger;
+
/**
* PHP-XMLRPC "wrapper" class - generate stubs to transparently access xmlrpc methods as php functions and vice-versa.
* Note: this class implements the PROXY pattern, but it is not named so to avoid confusion with http proxies.
* @todo use some better templating system for code generation?
* @todo implement method wrapping with preservation of php objs in calls
* @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
+ * @todo add support for 'epivals' mode
+ * @todo allow setting custom namespace for generated wrapping code
*/
class Wrapper
{
/// used to hold a reference to object instances whose methods get wrapped by wrapPhpFunction(), in 'create source' mode
public static $objHolder = array();
+ protected static $logger;
+
+ public function getLogger()
+ {
+ if (self::$logger === null) {
+ self::$logger = Logger::instance();
+ }
+ return self::$logger;
+ }
+
+ public static function setLogger($logger)
+ {
+ self::$logger = $logger;
+ }
+
/**
* Given a string defining a php type or phpxmlrpc type (loosely defined: strings
* accepted come from javadoc blocks), return corresponding phpxmlrpc type.
* Notes:
* - for php 'resource' types returns empty string, since resources cannot be serialized;
* - for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
- * - for php arrays always return array, even though arrays sometimes serialize as json structs
+ * - for php arrays always return array, even though arrays sometimes serialize as structs...
* - for 'void' and 'null' returns 'undefined'
*
* @param string $phpType
*
* @return string
+ *
+ * @todo support notation `something[]` as 'array'
*/
public function php2XmlrpcType($phpType)
{
case 'true':
return Value::$xmlrpcBoolean;
case Value::$xmlrpcArray: // 'array':
+ case 'array[]';
return Value::$xmlrpcArray;
case 'object':
case Value::$xmlrpcStruct: // 'struct'
return '';
default:
if (class_exists($phpType)) {
+ if (is_a($phpType, 'DateTimeInterface')) {
+ return Value::$xmlrpcDateTime;
+ }
return Value::$xmlrpcStruct;
} else {
// unknown: might be any 'extended' xmlrpc type
* Since php is a typeless language, to infer types of input and output parameters,
* it relies on parsing the javadoc-style comment block associated with the given
* function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
- * in the @param tag is also allowed, if you need the php function to receive/send
+ * in the '@param' tag is also allowed, if you need the php function to receive/send
* data in that particular format (note that base64 encoding/decoding is transparently
* carried out by the lib, while datetime vals are passed around as strings)
*
}
if (is_array($callable)) {
if (count($callable) < 2 || (!is_string($callable[0]) && !is_object($callable[0]))) {
- error_log('XML-RPC: ' . __METHOD__ . ': syntax for function to be wrapped is wrong');
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': syntax for function to be wrapped is wrong');
return false;
}
if (is_string($callable[0])) {
} else if ($callable instanceof \Closure) {
// we do not support creating code which wraps closures, as php does not allow to serialize them
if (!$buildIt) {
- error_log('XML-RPC: ' . __METHOD__ . ': a closure can not be wrapped in generated source code');
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': a closure can not be wrapped in generated source code');
return false;
}
}
if (!$exists) {
- error_log('XML-RPC: ' . __METHOD__ . ': function to be wrapped is not defined: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is not defined: ' . $plainFuncName);
return false;
}
if (is_array($callable)) {
$func = new \ReflectionMethod($callable[0], $callable[1]);
if ($func->isPrivate()) {
- error_log('XML-RPC: ' . __METHOD__ . ': method to be wrapped is private: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is private: ' . $plainFuncName);
return false;
}
if ($func->isProtected()) {
- error_log('XML-RPC: ' . __METHOD__ . ': method to be wrapped is protected: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is protected: ' . $plainFuncName);
return false;
}
if ($func->isConstructor()) {
- error_log('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the constructor: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the constructor: ' . $plainFuncName);
return false;
}
if ($func->isDestructor()) {
- error_log('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the destructor: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is the destructor: ' . $plainFuncName);
return false;
}
if ($func->isAbstract()) {
- error_log('XML-RPC: ' . __METHOD__ . ': method to be wrapped is abstract: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': method to be wrapped is abstract: ' . $plainFuncName);
return false;
}
/// @todo add more checks for static vs. nonstatic?
if ($func->isInternal()) {
// Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
// instead of getparameters to fully reflect internal php functions ?
- error_log('XML-RPC: ' . __METHOD__ . ': function to be wrapped is internal: ' . $plainFuncName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': function to be wrapped is internal: ' . $plainFuncName);
return false;
}
* @param $callable
* @param array $extraOptions
* @param string $plainFuncName
- * @param string $funcDesc
+ * @param array $funcDesc
* @return \Closure
*/
protected function buildWrapFunctionClosure($callable, $extraOptions, $plainFuncName, $funcDesc)
{
+ /**
+ * @param Request $req
+ * @return mixed
+ */
$function = function($req) use($callable, $extraOptions, $funcDesc)
{
$nameSpace = '\\PhpXmlRpc\\';
* - bool debug set it to 1 or 2 to see debug results of querying server for method synopsis
* - int simple_client_copy set it to 1 to have a lightweight copy of the $client object made in the generated code (only used when return_source = true)
*
- * @return \closure|array|false false on failure, closure by default and array for return_source = true
+ * @return \closure|string[]|false false on failure, closure by default and array for return_source = true
*/
public function wrapXmlrpcMethod($client, $methodName, $extraOptions = array())
{
$client->setDebug($debug);
$response = $client->send($req, $timeout, $protocol);
if ($response->faultCode()) {
- error_log('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature from remote server for method ' . $methodName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature from remote server for method ' . $methodName);
return false;
}
}
if (!is_array($mSig) || count($mSig) <= $sigNum) {
- error_log('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature nr.' . $sigNum . ' from remote server for method ' . $methodName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method signature nr.' . $sigNum . ' from remote server for method ' . $methodName);
return false;
}
* @param Client $client
* @param string $methodName
* @param array $extraOptions
- * @param string $mSig
+ * @param array $mSig
* @return \Closure
*
* @todo should we allow usage of parameter simple_client_copy to mean 'do not clone' in this case?
* @param string $newFuncName
* @param array $mSig
* @param string $mDesc
- * @return array
+ * @return string[] keys: source, docstring
*/
public function buildWrapMethodSource($client, $methodName, array $extraOptions, $newFuncName, $mSig, $mDesc='')
{
$mDesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
}
$plist = implode(', ', $plist);
- $mDesc .= '* @return ' . $this->xmlrpc2PhpType($mSig[0]) . " (or an {$namespace}Response obj instance if call fails)\n*/\n";
+ $mDesc .= '* @return {$namespace}Response|' . $this->xmlrpc2PhpType($mSig[0]) . " (an {$namespace}Response obj instance if call fails)\n*/\n";
$innerCode .= "\$res = \${$this_}client->send(\$req, $timeout, '$protocol');\n";
if ($decodeFault) {
$req = new $reqClass('system.listMethods');
$response = $client->send($req, $timeout, $protocol);
if ($response->faultCode()) {
- error_log('XML-RPC: ' . __METHOD__ . ': could not retrieve method list from remote server');
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve method list from remote server');
return false;
} else {
$mList = $decoder->decode($mList);
}
if (!is_array($mList) || !count($mList)) {
- error_log('XML-RPC: ' . __METHOD__ . ': could not retrieve meaningful method list from remote server');
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not retrieve meaningful method list from remote server');
return false;
} else {
}
$source .= $methodWrap['source'] . "\n";
} else {
- error_log('XML-RPC: ' . __METHOD__ . ': will not create class method to wrap remote method ' . $mName);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': will not create class method to wrap remote method ' . $mName);
}
}
}
if ($allOK) {
return $xmlrpcClassName;
} else {
- error_log('XML-RPC: ' . __METHOD__ . ': could not create class ' . $xmlrpcClassName . ' to wrap remote server ' . $client->server);
+ $this->getLogger()->errorLog('XML-RPC: ' . __METHOD__ . ': could not create class ' . $xmlrpcClassName . ' to wrap remote server ' . $client->server);
return false;
}
} else {
// (this provides for future expansion or subclassing of client obj)
if ($verbatimClientCopy) {
foreach ($client as $fld => $val) {
- if ($fld != 'debug' && $fld != 'return_type') {
+ /// @todo in php 8.0, curl handles became objects, but they have no __set_state, thus var_export will
+ /// fail for xmlrpc_curl_handle. So we disabled copying it.
+ /// We should examine in depth if this change can have side effects - at first sight if the
+ /// client's curl handle is not set, all curl options are (re)set on each http call, so there
+ /// should be no loss of state...
+ if ($fld != 'debug' && $fld != 'return_type' && $fld != 'xmlrpc_curl_handle') {
$val = var_export($val, true);
$code .= "\$client->$fld = $val;\n";
}