/** * Describe a service and all its methods * @param $data An object containing 'label' and 'data' keys */ public function describeService($data) { $className = $data['label']; //Sanitize path //$path = str_replace('..', '', $data['data']); $methodTable = MethodTable::create($this->servicePath . ($data['data'] ? DIRECTORY_SEPARATOR . $data['data'] : '') . $className . '.php', NULL, $classComment); return array($methodTable, $classComment); }
/** * Describe a service and all its methods * @param $data An object containing 'label' and 'data' keys */ function describeService($data) { $className = $data['label']; //Sanitize path $path = str_replace('..', '', $data['data']); //Generate the method table from this info $this->_path = dirname(dirname(realpath(__FILE__))) . DIRECTORY_SEPARATOR; $methodTable = MethodTable::create($this->_path . $path . $className . '.php', NULL, $classComment); return array($methodTable, $classComment); }
/** * Parse a file to find out what functions/methods exist in it, and add entries * for the remote-call-enabled functions to the database. * * The path to a file, e.g. auth/mnet/auth.php can be thought of as * type/parentname/docname * * @param string $type mod, auth or enrol * @param string $parentname Implementation of type, e.g. 'mnet' in the * case of auth/mnet/auth.php * @return bool True on success, else false */ function mnet_get_functions($type, $parentname) { global $CFG; $dataobject = new stdClass(); $docname = $type . '.php'; $publishes = array(); if ('mod' == $type) { $docname = 'rpclib.php'; $relname = '/mod/' . $parentname . '/' . $docname; $filename = $CFG->dirroot . $relname; if (file_exists($filename)) { include_once $filename; } $mnet_publishes = $parentname . '_mnet_publishes'; if (function_exists($mnet_publishes)) { (array) ($publishes = $mnet_publishes()); } } elseif ('local' == $type) { $docname = 'rpclib.php'; $relname = '/local/' . $parentname . '/' . $docname; $filename = $CFG->dirroot . $relname; if (file_exists($filename)) { include_once $filename; } $mnet_publishes = $parentname . '_mnet_publishes'; if (function_exists($mnet_publishes)) { (array) ($publishes = $mnet_publishes()); } } else { // auth or enrol $relname = '/' . $type . '/' . $parentname . '/' . $docname; $filename = $CFG->dirroot . $relname; if (file_exists($filename)) { include_once $filename; } $class = $type . ($type == 'enrol' ? 'ment' : '') . '_plugin_' . $parentname; if (class_exists($class)) { $object = new $class(); if (method_exists($object, 'mnet_publishes')) { (array) ($publishes = $object->mnet_publishes()); } } } $methodServiceArray = array(); foreach ($publishes as $service) { if (is_array($service['methods'])) { foreach ($service['methods'] as $methodname) { $methodServiceArray[$methodname][] = $service; } } } // Disable functions that don't exist (any more) in the source // Should these be deleted? What about their permissions records? $rpcrecords = get_records_select('mnet_rpc', ' parent=\'' . $parentname . '\' AND parent_type=\'' . $type . '\' ', 'function_name ASC '); if (!empty($rpcrecords)) { foreach ($rpcrecords as $rpc) { if (!array_key_exists($rpc->function_name, $methodServiceArray)) { $rpc->enabled = 0; update_record('mnet_rpc', $rpc); } } } if (!file_exists($filename)) { return false; } if (extension_loaded('tokenizer')) { include_once "{$CFG->dirroot}/{$CFG->admin}/mnet/MethodTable.php"; $functions = (array) MethodTable::create($filename, false); } foreach ($methodServiceArray as $method => $servicearray) { if (!empty($functions[$method])) { $details = $functions[$method]; $profile = $details['arguments']; if (!isset($details['returns'])) { array_unshift($profile, array('type' => 'void', 'description' => 'No return value')); } else { array_unshift($profile, $details['returns']); } $dataobject->profile = serialize($profile); $dataobject->help = addslashes($details['description']); } else { $dataobject->profile = serialize(array(array('type' => 'void', 'description' => 'No return value'))); $dataobject->help = ''; } $dataobject->function_name = $method; $dataobject->xmlrpc_path = $type . '/' . $parentname . '/' . $docname . '/' . $method; $dataobject->parent_type = $type; $dataobject->parent = $parentname; $dataobject->enabled = '0'; if ($record_exists = get_record('mnet_rpc', 'xmlrpc_path', $dataobject->xmlrpc_path)) { $dataobject->id = $record_exists->id; $dataobject->enabled = $record_exists->enabled; update_record('mnet_rpc', $dataobject); } else { $dataobject->id = insert_record('mnet_rpc', $dataobject, true); } foreach ($servicearray as $service) { $serviceobj = get_record('mnet_service', 'name', $service['name']); if (false == $serviceobj) { $serviceobj = new stdClass(); $serviceobj->name = $service['name']; $serviceobj->apiversion = $service['apiversion']; $serviceobj->offer = 1; $serviceobj->id = insert_record('mnet_service', $serviceobj, true); } if (false == get_record('mnet_service2rpc', 'rpcid', $dataobject->id, 'serviceid', $serviceobj->id)) { $obj = new stdClass(); $obj->rpcid = $dataobject->id; $obj->serviceid = $serviceobj->id; insert_record('mnet_service2rpc', $obj, true); } } } return true; }
/** * Creates the methodTable for a passed class. * * @static * @access public * @param $className(String) The name of the service class. * May also simply be __FILE__ * @param $servicePath(String) The location of the classes (optional) */ function create($className, $servicePath = NULL, &$classComment) { $methodTable = array(); if (file_exists(Inflector::underscore($className))) { //The new __FILE__ way of doing things was used // Files are underscored in cakePHP $sourcePath = Inflector::underscore($className); $className = str_replace("\\", '/', $className); $className = substr($className, strrpos($className, '/') + 1); // Class names are CamelCased in cakePHP $className = Inflector::camelize(str_replace('.php', '', $className)); } else { $className = str_replace('.php', '', $className); $fullPath = Inflector::underscore(str_replace('.', '/', $className)); $className = Inflector::camelize($fullPath); if (strpos($fullPath, '/') !== FALSE) { // Class names are CamelCased in cakePHP $className = Inflector::camelize(substr(strrchr($fullPath, '/'), 1)); } if ($servicePath == NULL) { if (isset($GLOBALS['amfphp']['classPath'])) { $servicePath = $GLOBALS['amfphp']['classPath']; } else { $servicePath = "../services/"; } } $sourcePath = $servicePath . $fullPath . ".php"; } if (!file_exists($sourcePath)) { trigger_error("The MethodTable class could not find {" . $sourcePath . "}", E_USER_ERROR); } if (class_exists('ReflectionClass')) { //PHP5 $classMethods = MethodTable::getClassMethodsReflection($sourcePath, $className, $classComment); } else { //PHP4 $classMethods = MethodTable::getClassMethodsTokenizer($sourcePath, $className, $classComment); } foreach ($classMethods as $key => $value) { if ($value['name'][0] == '_' || $value['name'] == 'beforeFilter') { continue; } if (defined("METHOD_PREFIX")) { if (METHOD_PREFIX != substr($value['name'], 0, strlen(METHOD_PREFIX))) { continue; } } $methodSignature = $value['args']; $methodName = $value['name']; $methodComment = $value['comment']; $description = MethodTable::getMethodDescription($methodComment) . " " . MethodTable::getMethodCommentAttribute($methodComment, "desc"); $description = trim($description); $access = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "access"); $roles = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "roles"); $instance = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "instance"); $returns = MethodTable::getMethodCommentAttributeFirstLine($methodComment, "returns"); $pagesize = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "pagesize"); $params = MethodTable::getMethodCommentArguments($methodComment); //description, arguments, access, [roles, [instance, [returns, [pagesize]]]] $methodTable[$methodName] = array(); //$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes $methodTable[$methodName]["description"] = $description == "" ? "No description given." : $description; $methodTable[$methodName]["arguments"] = MethodTable::getMethodArguments($methodSignature, $params); $methodTable[$methodName]["access"] = $access == "" ? "private" : $access; if ($roles != "") { $methodTable[$methodName]["roles"] = $roles; } if ($instance != "") { $methodTable[$methodName]["instance"] = $instance; } if ($returns != "") { $methodTable[$methodName]["returns"] = $returns; } if ($pagesize != "") { $methodTable[$methodName]["pagesize"] = $pagesize; } } $classComment = trim(str_replace("\r\n", "\n", MethodTable::getMethodDescription($classComment))); return $methodTable; }
/** * Prints out the headers and footers of the method testing page and * either prints a form (through _printForm) for the user to enter arguments or, if arguments * are provided, prints the result of the method (through printResult). */ function testMethod($name) { if (isset($this->_classConstruct->methodTable[$name]['arguments'])) { return $this->_classConstruct->methodTable[$name]['arguments']; } else { require_once AMFPHP_BASE . 'util/MethodTable.php'; $mt = MethodTable::create(substr($_GET['class'], strrpos('/' . $_GET['class'], '/')) . '.php'); return $mt[$name]['arguments']; } }
/** * Cleans the arguments array. * This method removes all whitespaces and the leading "$" sign from each argument * in the array. * * @static * @access private * @param $args(Array) The "dirty" array with arguments. */ function cleanArguments($args, $commentParams) { $result = array(); if (!is_array($args)) { return array(); } foreach ($args as $index => $arg) { $arg = strrstr(str_replace(array('$', '&$'), array('', '&'), $arg), '='); if (!isset($commentParams[$index])) { $result[] = trim($arg); } else { $start = trim($arg); $end = trim(str_replace('$', '', $commentParams[$index])); // Suppress Notice of 'Undefined offset' with @ @(list($word0, $word1, $tail) = preg_split("/[\\s]+/", $end, 3)); $word0 = strtolower($word0); $word1 = strtolower($word1); $wordBase0 = ereg_replace('^[&$]+', '', $word0); $wordBase1 = ereg_replace('^[&$]+', '', $word1); $startBase = strtolower(ereg_replace('^[&$]+', '', $start)); if ($wordBase0 == $startBase) { $type = str_replace(array('(', ')'), '', $word1); } elseif ($wordBase1 == $startBase) { $type = str_replace(array('(', ')'), '', $word0); } elseif (ereg('(^[&$]+)|(\\()([a-z0-9]+)(\\)$)', $word0, $regs)) { $tail = str_ireplace($word0, '', $end); $type = $regs[3]; } else { // default to string $type = 'string'; } $type = MethodTable::standardizeType($type); /* if($type == 'str') { $type = 'string'; } elseif($type == 'int' || $type == 'integer') { $type = 'int'; } elseif($type == 'bool' || $type == 'boolean') { $type = 'boolean'; } elseif($type == 'object' || $type == 'class') { // Note that this is not a valid XMLRPC type $type = 'object'; } elseif($type == 'float' || $type == 'dbl' || $type == 'double' || $type == 'flt') { $type = 'double'; } elseif($type == 'null') { // Note that this is not a valid XMLRPC type // The null type can have only one value - null. Why would // that be an argument to a function? Just in case: $type = 'null'; } elseif($type == 'mixed') { // Note that this is not a valid XMLRPC type $type = 'mixed'; } elseif($type == 'array' || $type == 'arr') { $type = 'array'; } elseif($type == 'assoc') { $type = 'struct'; } elseif($type == 'reference' || $type == 'ref') { // Note that this is not a valid XMLRPC type // As references cannot be serialized or exported, there is // no way this could be XML-RPCed. $type = 'reference'; } else { $type = 'string'; } */ $result[] = array('type' => $type, 'description' => $start . ' - ' . $tail); } } return $result; }
/** * Returns an array with the arguments of a method. * * @static * @access private * @param $methodSignature (String)The method's signatureg; */ function getMethodArguments($methodSignature, $commentParams) { if (strlen($methodSignature) < 2) { //no arguments, return an empty array $result = array(); } else { //clean the arguments before returning them $result = MethodTable::cleanArguments(explode(",", $methodSignature), $commentParams); } return $result; }
/** * * */ private static function getMethodCommentAttributeFirstWord($comment, $attribute){ $pieces = strstrafter($comment, '@' . $attribute); if($pieces !== FALSE) { $val = MethodTable::cleanComment($pieces); return trim(strrstr($val, ' ')); } return ""; }
/** * Creates the methodTable for a passed class. * * @static * @access public * @param $className(String) The name of the service class. * May also simply be __FILE__ * @param $servicePath(String) The location of the classes (optional) */ function create($className, $servicePath = NULL, &$classComment) { $methodTable = array(); if (file_exists($className)) { //The new __FILE__ way of doing things was used $sourcePath = $className; $className = str_replace("\\", '/', $className); $className = substr($className, strrpos($className, '/') + 1); $className = str_replace('.php', '', $className); } else { $className = str_replace('.php', '', $className); $fullPath = str_replace('.', '/', $className); $className = $fullPath; if (strpos($fullPath, '/') !== FALSE) { $className = substr(strrchr($fullPath, '/'), 1); } if ($servicePath == NULL) { if (isset($GLOBALS['amfphp']['classPath'])) { $servicePath = $GLOBALS['amfphp']['classPath']; } else { $servicePath = "../services/"; } } $sourcePath = $servicePath . $fullPath . ".php"; } if (!file_exists($sourcePath)) { trigger_error("The MethodTable class could not find {" . $sourcePath . "}", E_USER_ERROR); } //convert classname to cake classname $className = Inflector::camelize($className); if (class_exists('ReflectionClass')) { //PHP5 $classMethods = MethodTable::getClassMethodsReflection($sourcePath, $className, $classComment); } else { //PHP4 $classMethods = MethodTable::getClassMethodsTokenizer($sourcePath, $className, $classComment); } foreach ($classMethods as $key => $value) { if ($value['name'][0] == '_' || in_array($value['name'], array('beforeFilter', 'afterFilter', 'beforeRender', 'afterRender', 'Object', 'cakeError', 'cleanUpFields', 'constructClasses', 'flash', 'flashOut', 'generateFieldNames', 'log', 'postConditions', 'redirect', 'referer', 'render', 'requestAction', 'set', 'setAction', 'toString', 'validate', 'validateErrors', 'view'))) { continue; } $methodSignature = $value['args']; $methodName = $value['name']; $methodComment = $value['comment']; $description = MethodTable::getMethodDescription($methodComment) . " " . MethodTable::getMethodCommentAttribute($methodComment, "desc"); $description = trim($description); $access = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "access"); $roles = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "roles"); $instance = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "instance"); $returns = MethodTable::getMethodCommentAttributeFirstLine($methodComment, "returns"); $pagesize = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "pagesize"); $params = MethodTable::getMethodCommentArguments($methodComment); //description, arguments, access, [roles, [instance, [returns, [pagesize]]]] $methodTable[$methodName] = array(); //$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes $methodTable[$methodName]["description"] = $description == "" ? "No description given." : $description; $methodTable[$methodName]["arguments"] = MethodTable::getMethodArguments($methodSignature, $params); $methodTable[$methodName]["access"] = $access == "" ? "private" : $access; if ($roles != "") { $methodTable[$methodName]["roles"] = $roles; } if ($instance != "") { $methodTable[$methodName]["instance"] = $instance; } if ($returns != "") { $methodTable[$methodName]["returns"] = $returns; } if ($pagesize != "") { $methodTable[$methodName]["pagesize"] = $pagesize; } } $classComment = trim(str_replace("\r\n", "\n", MethodTable::getMethodDescription($classComment))); return $methodTable; }
<head> <title>MethodTable</title> <link rel="stylesheet" type="text/css" href="images/service-browser.css" /> </head> <body> <h1>MethodTable for <?php echo $_GET['class'] . '.php'; ?> </h1> <?php echo $feedback; ?> <p>This code was generated by the MethodTable class by looping through the class methods and reading the signature and JavaDoc of each method. Read more about it in the <a href="http://www.amfphp.org/docs/creatingclasses.html" target="_blank">amfphp docs</a>.<br/><br/>When deploying your application on a webserver it's better to have a hard-coded methodTable for faster performance. You can copy/paste this code in the constructor of your service class, or, provided you have 777 permissions in /services, click the 'save' link, and write <code>include("<?php echo substr($className, strrpos('/' . $className, '/')); ?> .methodTable.php");</code> in the constructor.</p> <div style='text-align:right'><a href='<?php echo $PHP_SELF . '?class=' . $_GET['class'] . '&action=save'; ?> '>Save to <?php echo $className; ?> .methodTable.php</a></div> <textarea class="codex" name="methodTable"><?php echo MethodTable::showCode($methodTable); ?> </textarea> </body> </html>