* @copyright 2004-2006 Sergio Carvalho
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
* @link http://pear.php.net/package/XML_RPC2
*/
class XML_RPC2_Server_Method
{
// {{{ properties
/**
* Method signature parameters
*
* @var array
*/
private $_parameters;
/**
* Method signature return type
*
* @var string
*/
private $_returns ;
/**
* Method help, for introspection
*
* @var string
*/
private $_help;
/**
* internalMethod field : method name in PHP-land
*
* @var string
*/
private $_internalMethod;
/**
* hidden field : true if the method is hidden
*
* @var boolean
*/
private $_hidden;
/**
* name Field : external method name
*
* @var string
*/
private $_name;
/**
* Number of required parameters
*
* @var int
*/
private $_numberOfRequiredParameters;
// }}}
// {{{ getInternalMethod()
/**
* internalMethod getter
*
* @return string internalMethod
*/
public function getInternalMethod()
{
return $this->_internalMethod;
}
// }}}
// {{{ isHidden()
/**
* hidden getter
*
* @return boolean hidden value
*/
public function isHidden()
{
return $this->_hidden;
}
// }}}
// {{{ getName()
/**
* name getter
*
* @return string name
*/
public function getName()
{
return $this->_name;
}
// }}}
// {{{ constructor
/**
* Create a new XML-RPC method by introspecting a PHP method
*
* @param ReflectionMethod The PHP method to introspect
* @param string default prefix
*/
public function __construct(ReflectionMethod $method, $defaultPrefix)
{
$hidden = false;
$docs = $method->getDocComment();
if (!$docs) {
$hidden = true;
}
$docs = explode("\n", $docs);
$parameters = array();
$methodname = null;
$returns = 'mixed';
$shortdesc = '';
$paramcount = -1;
$prefix = $defaultPrefix;
// Extract info from Docblock
$paramDocs = array();
foreach ($docs as $i => $doc) {
$doc = trim($doc, " \r\t/*");
if (strlen($doc) && strpos($doc, '@') !== 0) {
if ($shortdesc) {
$shortdesc .= "\n";
}
$shortdesc .= $doc;
continue;
}
if (strpos($doc, '@xmlrpc.hidden') === 0) {
$hidden = true;
}
if ((strpos($doc, '@xmlrpc.prefix') === 0) && preg_match('/@xmlrpc.prefix( )*(.*)/', $doc, $matches)) {
$prefix = $matches[2];
}
if ((strpos($doc, '@xmlrpc.methodname') === 0) && preg_match('/@xmlrpc.methodname( )*(.*)/', $doc, $matches)) {
$methodname = $matches[2];
}
if (strpos($doc, '@param') === 0) { // Save doctag for usage later when filling parameters
$paramDocs[] = $doc;
}
if (strpos($doc, '@return') === 0) {
$param = preg_split("/\s+/", $doc);
if (isset($param[1])) {
$param = $param[1];
$returns = $param;
}
}
}
$this->_numberOfRequiredParameters = $method->getNumberOfRequiredParameters(); // we don't use isOptional() because of bugs in the reflection API
// Fill in info for each method parameter
foreach ($method->getParameters() as $parameterIndex => $parameter) {
// Parameter defaults
$newParameter = array('type' => 'mixed');
// Attempt to extract type and doc from docblock
if (array_key_exists($parameterIndex, $paramDocs) &&
preg_match('/@param\s+(\S+)(\s+(.+))/', $paramDocs[$parameterIndex], $matches)) {
if (strpos($matches[1], '|')) {
$newParameter['type'] = XML_RPC2_Server_Method::_limitPHPType(explode('|', $matches[1]));
} else {
$newParameter['type'] = XML_RPC2_Server_Method::_limitPHPType($matches[1]);
}
$tmp = '$' . $parameter->getName() . ' ';
if (strpos($matches[3], '$' . $tmp) === 0) {
$newParameter['doc'] = $matches[3];
} else {
// The phpdoc comment is something like "@param string $param description of param"
// Let's keep only "description of param" as documentation (remove $param)
$newParameter['doc'] = substr($matches[3], strlen($tmp));
}
$newParameter['doc'] = preg_replace('_^\s*_', '', $newParameter['doc']);
}
$parameters[$parameter->getName()] = $newParameter;
}
if (is_null($methodname)) {
$methodname = $prefix . $method->getName();
}
$this->_internalMethod = $method->getName();
$this->_parameters = $parameters;
$this->_returns = $returns;
$this->_help = $shortdesc;
$this->_name = $methodname;
$this->_hidden = $hidden;
}
// }}}
// {{{ matchesSignature()
/**
* Check if method matches provided call signature
*
* Compare the provided call signature with this methods' signature and
* return true iff they match.
*
* @param string Signature to compare method name
* @param array Array of parameter values for method call.
* @return boolean True if call matches signature, false otherwise
*/
public function matchesSignature($methodName, $callParams)
{
if ($methodName != $this->_name) return false;
if (count($callParams) < $this->_numberOfRequiredParameters) return false;
if (count($callParams) > $this->_parameters) return false;
$paramIndex = 0;
foreach($this->_parameters as $param) {
$paramIndex++;
if ($paramIndex <= $this->_numberOfRequiredParameters) {
// the parameter is not optional
$callParamType = XML_RPC2_Server_Method::_limitPHPType(gettype($callParams[$paramIndex-1]));
if ((!($param['type'] == 'mixed')) and ($param['type'] != $callParamType)) {
return false;
}
}
}
return true;
}
// }}}
// {{{ getHTMLSignature()
/**
* Return a HTML signature of the method
*
* @return string HTML signature
*/
public function getHTMLSignature()
{
$name = $this->_name;
$returnType = $this->_returns;
$result = "($returnType) ";
$result .= "$name";
$result .= "(";
$first = true;
$nbr = 0;
while (list($name, $parameter) = each($this->_parameters)) {
$nbr++;
if ($nbr == $this->_numberOfRequiredParameters + 1) {
$result .= " [ ";
}
if ($first) {
$first = false;
} else {
$result .= ', ';
}
$type = $parameter['type'];
$result .= "($type) ";
$result .= "$name";
}
reset($this->_parameters);
if ($nbr > $this->_numberOfRequiredParameters) {
$result .= " ] ";
}
$result .= ")";
return $result;
}
// }}}
// {{{ autoDocument()
/**
* Print a complete HTML description of the method
*/
public function autoDocument()
{
$name = $this->getName();
$signature = $this->getHTMLSignature();
$id = md5($name);
$help = nl2br(htmlentities($this->_help));
print " \n";
print " Description :
\n";
print " \n";
print " $help\n";
print "
\n";
if (count($this->_parameters)>0) {
print " Parameters :
\n";
if (count($this->_parameters)>0) {
print " \n";
print " Type | Name | Documentation |
\n";
while (list($name, $parameter) = each($this->_parameters)) {
$type = $parameter['type'];
$doc = isset($parameter['doc']) ? htmlentities($parameter['doc']) : 'Method is not documented. No PHPDoc block was found associated with the method in the source code.';
print " $type | $name | $doc |
\n";
}
reset($this->_parameters);
print "
\n";
}
}
}
// }}}
// {{{ _limitPHPType()
/**
* standardise type names between gettype php function and phpdoc comments (and limit to xmlrpc available types)
*
* @var string $type
* @return string standardised type
*/
private static function _limitPHPType($type)
{
$tmp = strtolower($type);
$convertArray = array(
'int' => 'integer',
'i4' => 'integer',
'integer' => 'integer',
'string' => 'string',
'str' => 'string',
'char' => 'string',
'bool' => 'boolean',
'boolean' => 'boolean',
'array' => 'array',
'float' => 'double',
'double' => 'double',
'array' => 'array',
'struct' => 'array',
'assoc' => 'array',
'structure' => 'array',
'datetime' => 'mixed',
'datetime.iso8601' => 'mixed',
'iso8601' => 'mixed',
'base64' => 'string'
);
if (isset($convertArray[$tmp])) {
return $convertArray[$tmp];
}
return 'mixed';
}
}
?>