Updated the 19 build version to 20101023
[moodle.git] / admin / mnet / MethodTable.php
blobe5e4b390f8291b7ce74361033f7f8e8d51a4e914
1 <?php
2 /**
3 * Adapted for Moodle from the AMFPHP Project at http://www.amfphp.org/
4 * Creates the methodTable for a service class.
6 * @usage $this->methodTable = MethodTable::create($this);
7 * @author Christophe Herreman
8 * @since 05/01/2005
9 * @version $id$
11 * Special contributions by Allessandro Crugnola and Ted Milker
14 if (!defined('T_ML_COMMENT')) {
15 define('T_ML_COMMENT', T_COMMENT);
16 } else {
17 define('T_DOC_COMMENT', T_ML_COMMENT);
20 /**
21 * Return string from start of haystack to first occurance of needle, or whole
22 * haystack, if needle does not occur
24 * @access public
25 * @param $haystack(String) Haystack to search in
26 * @param $needle(String) Needle to look for
28 function strrstr($haystack, $needle)
30 return substr($haystack, 0, strpos($haystack.$needle,$needle));
33 /**
34 * Return substring of haystack from end of needle onwards, or FALSE
36 * @access public
37 * @param $haystack(String) Haystack to search in
38 * @param $needle(String) Needle to look for
40 function strstrafter($haystack, $needle)
42 return substr(strstr($haystack, $needle), strlen($needle));
45 class MethodTable
47 /**
48 * Constructor.
50 * Since this class should only be accessed through the static create() method
51 * this constructor should be made private. Unfortunately, this is not possible
52 * in PHP4.
54 * @access private
56 function MethodTable(){
60 /**
61 * Creates the methodTable for a passed class.
63 * @static
64 * @access public
65 * @param $sourcePath(String) The path to the file you want to parse
66 * @param $containsClass(Bool) True if the file is a class definition (optional)
68 function create($sourcePath, $containsClass = false){
70 $methodTable = array();
71 if(!file_exists($sourcePath))
73 return false;
76 $source = file_get_contents($sourcePath);
77 $tokens = (array)token_get_all($source);
79 $waitingForOpenParenthesis = false;
80 $waitingForFunction = false;
81 $waitingForClassName = false;
82 $bufferingArgs = false;
83 $argBuffer = "";
84 $lastFunction = "";
85 $lastFunctionComment = "";
86 $lastComment = "";
87 $classMethods = array();
88 $realClassName = "";
90 if($containsClass) {
91 $openBraces = -10000;
93 else
95 $openBraces = 1;
98 $waitingForEndEncapsedString = false;
99 foreach($tokens as $token)
101 if (is_string($token)) {
102 if($token == '{')
104 $openBraces++;
106 if($token == '}')
108 if($waitingForEndEncapsedString)
110 $waitingForEndEncapsedString = false;
112 else
114 $lastComment = '';
115 $openBraces--;
117 if($openBraces == 0)
119 break;
123 elseif($waitingForOpenParenthesis && $token == '(')
125 $bufferingArgs = true;
126 $argBuffer = "";
127 $waitingForOpenParenthesis = false;
129 elseif($bufferingArgs)
131 if($token != ')')
133 $argBuffer .= $token;
135 else
137 if($lastFunction != $realClassName)
139 $classMethods[] = array("name" => $lastFunction,
140 "comment" => $lastFunctionComment,
141 "args" => $argBuffer);
143 $bufferingArgs = false;
144 $argBuffer = "";
145 $lastFunction = "";
146 $lastFunctionComment = "";
151 } else {
152 // token array
153 list($id, $text) = $token;
155 if($bufferingArgs)
157 $argBuffer .= $text;
159 switch ($id)
162 case T_COMMENT:
163 case T_ML_COMMENT: // we've defined this
164 case T_DOC_COMMENT: // and this
165 // no action on comments
166 $lastComment = $text;
167 break;
168 case T_FUNCTION:
169 if($openBraces >= 1)
171 $waitingForFunction = true;
173 break;
174 case T_STRING:
175 if($waitingForFunction)
177 $waitingForFunction = false;
178 $waitingForOpenParenthesis = true;
179 $lastFunction = $text;
180 $lastFunctionComment = $lastComment;
181 $lastComment = "";
183 if($waitingForClassName)
185 $waitingForClassName = false;
186 $realClassName = $text;
188 break;
189 case T_CLASS:
190 $openBraces = 0;
191 $waitingForClassName = true;
192 break;
193 case T_CURLY_OPEN:
194 case T_DOLLAR_OPEN_CURLY_BRACES:
195 $waitingForEndEncapsedString = true;
196 break;
201 foreach ($classMethods as $key => $value) {
202 $methodSignature = $value['args'];
203 $methodName = $value['name'];
204 $methodComment = $value['comment'];
206 $description = MethodTable::getMethodDescription($methodComment) . " " . MethodTable::getMethodCommentAttribute($methodComment, "desc");
207 $description = trim($description);
208 $access = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "access");
209 $roles = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "roles");
210 $instance = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "instance");
211 $returns = MethodTable::getMethodReturnValue($methodComment);
212 $pagesize = MethodTable::getMethodCommentAttributeFirstWord($methodComment, "pagesize");
213 $params = MethodTable::getMethodCommentArguments($methodComment);
216 //description, arguments, access, [roles, [instance, [returns, [pagesize]]]]
217 $methodTable[$methodName] = array();
218 //$methodTable[$methodName]["signature"] = $methodSignature; //debug purposes
219 $methodTable[$methodName]["description"] = ($description == "") ? "No description given." : $description;
220 $methodTable[$methodName]["arguments"] = MethodTable::getMethodArguments($methodSignature, $params);
221 $methodTable[$methodName]["access"] = ($access == "") ? "private" : $access;
223 if($roles != "") $methodTable[$methodName]["roles"] = $roles;
224 if($instance != "") $methodTable[$methodName]["instance"] = $instance;
225 if($returns != "") $methodTable[$methodName]["returns"] = $returns;
226 if($pagesize != "") $methodTable[$methodName]["pagesize"] = $pagesize;
229 return $methodTable;
236 function getMethodCommentServices($comment)
238 $pieces = explode('@service', $comment);
239 $args = array();
240 if(is_array($pieces) && count($pieces) > 1)
242 for($i = 0; $i < count($pieces) - 1; $i++)
244 $ps = strrstr($pieces[$i + 1], '@');
245 $ps = strrstr($ps, '*/');
246 $args[] = MethodTable::cleanComment($ps);
249 return $args;
256 function getMethodCommentArguments($comment)
258 $pieces = explode('@param', $comment);
259 $args = array();
260 if(is_array($pieces) && count($pieces) > 1)
262 for($i = 0; $i < count($pieces) - 1; $i++)
264 $ps = strrstr($pieces[$i + 1], '@');
265 $ps = strrstr($ps, '*/');
266 $args[] = MethodTable::cleanComment($ps);
269 return $args;
274 * Returns the description from the comment.
275 * The description is(are) the first line(s) in the comment.
277 * @static
278 * @private
279 * @param $comment(String) The method's comment.
281 function getMethodDescription($comment){
282 $comment = MethodTable::cleanComment(strrstr($comment, "@"));
283 return trim($comment);
288 * Returns the value of a comment attribute.
290 * @static
291 * @private
292 * @param $comment(String) The method's comment.
293 * @param $attribute(String) The name of the attribute to get its value from.
295 function getMethodCommentAttribute($comment, $attribute){
296 $pieces = strstrafter($comment, '@' . $attribute);
297 if($pieces !== FALSE)
299 $pieces = strrstr($pieces, '@');
300 $pieces = strrstr($pieces, '*/');
301 return MethodTable::cleanComment($pieces);
303 return "";
307 * Returns the value of a comment attribute.
309 * @static
310 * @private
311 * @param $comment(String) The method's comment.
312 * @param $attribute(String) The name of the attribute to get its value from.
314 function getMethodCommentAttributeFirstLine($comment, $attribute){
315 $pieces = strstrafter($comment, '@' . $attribute);
316 if($pieces !== FALSE)
318 $pieces = strrstr($pieces, '@');
319 $pieces = strrstr($pieces, "*");
320 $pieces = strrstr($pieces, "/");
321 $pieces = strrstr($pieces, "-");
322 $pieces = strrstr($pieces, "\n");
323 $pieces = strrstr($pieces, "\r");
324 $pieces = strrstr($pieces, '*/');
325 return MethodTable::cleanComment($pieces);
327 return "";
331 * Returns the value of a comment attribute.
333 * @static
334 * @private
335 * @param $comment(String) The method's comment.
336 * @param $attribute(String) The name of the attribute to get its value from.
338 function getMethodReturnValue($comment){
339 $result = array('type' => 'void', 'description' => '');
340 $pieces = strstrafter($comment, '@returns');
341 if(FALSE == $pieces) $pieces = strstrafter($comment, '@return');
342 if($pieces !== FALSE)
344 $pieces = strrstr($pieces, '@');
345 $pieces = strrstr($pieces, "*");
346 $pieces = strrstr($pieces, "/");
347 $pieces = strrstr($pieces, "-");
348 $pieces = strrstr($pieces, "\n");
349 $pieces = strrstr($pieces, "\r");
350 $pieces = strrstr($pieces, '*/');
351 $pieces = trim(MethodTable::cleanComment($pieces));
352 @list($result['type'], $result['description']) = explode(' ', $pieces, 2);
353 $result['type'] = MethodTable::standardizeType($result['type']);
355 return $result;
358 function getMethodCommentAttributeFirstWord($comment, $attribute){
359 $pieces = strstrafter($comment, '@' . $attribute);
360 if($pieces !== FALSE)
362 $val = MethodTable::cleanComment($pieces);
363 return trim(strrstr($val, ' '));
365 return "";
369 * Returns an array with the arguments of a method.
371 * @static
372 * @access private
373 * @param $methodSignature(String) The method's signature;
375 function getMethodArguments($methodSignature, $commentParams){
376 if(strlen($methodSignature) == 0){
377 //no arguments, return an empty array
378 $result = array();
379 }else{
380 //clean the arguments before returning them
381 $result = MethodTable::cleanArguments(explode(",", $methodSignature), $commentParams);
384 return $result;
388 * Cleans the function or method's return value.
390 * @static
391 * @access private
392 * @param $value(String) The "dirty" value.
394 function cleanReturnValue($value){
395 $result = array();
396 $value = trim($value);
398 list($result['type'], $result['description']) = explode(' ', $value, 2);
400 $result['type'] = MethodTable::standardizeType($result['type']);
402 return $result;
407 * Takes a string and returns the XMLRPC type that most closely matches it.
409 * @static
410 * @access private
411 * @param $type(String) The given type string.
413 function standardizeType($type) {
414 $type = strtolower($type);
415 if('str' == $type || 'string' == $type) return 'string';
416 if('int' == $type || 'integer' == $type) return 'int';
417 if('bool' == $type || 'boolean' == $type) return 'boolean';
419 // Note that object is not a valid XMLRPC type
420 if('object' == $type || 'class' == $type) return 'object';
421 if('float' == $type || 'dbl' == $type || 'double' == $type || 'flt' == $type) return 'double';
423 // Note that null is not a valid XMLRPC type. The null type can have
424 // only one value - null.
425 if('null' == $type) return 'null';
427 // Note that mixed is not a valid XMLRPC type
428 if('mixed' == $type) return 'mixed';
429 if('array' == $type || 'arr' == $type) return 'array';
430 if('assoc' == $type || 'struct' == $type) return 'struct';
432 // Note that this is not a valid XMLRPC type. As references cannot be
433 // serialized or exported, there is no way this could be XML-RPCed.
434 if('reference' == $type || 'ref' == $type) return 'reference';
435 return 'string';
439 * Cleans the arguments array.
440 * This method removes all whitespaces and the leading "$" sign from each argument
441 * in the array.
443 * @static
444 * @access private
445 * @param $args(Array) The "dirty" array with arguments.
447 function cleanArguments($args, $commentParams){
448 $result = array();
450 if(!is_array($args)) return array();
452 foreach($args as $index => $arg){
453 $arg = strrstr(str_replace(array('$','&$'), array('','&'), $arg), '=');
454 if(!isset($commentParams[$index]))
456 $result[] = trim($arg);
458 else
460 $start = trim($arg);
461 $end = trim(str_replace('$', '', $commentParams[$index]));
463 // Suppress Notice of 'Undefined offset' with @
464 @list($word0, $word1, $tail) = preg_split("/[\s]+/", $end, 3);
465 $word0 = strtolower($word0);
466 $word1 = strtolower($word1);
468 $wordBase0 = ereg_replace('^[&$]+','',$word0);
469 $wordBase1 = ereg_replace('^[&$]+','',$word1);
470 $startBase = strtolower(ereg_replace('^[&$]+','',$start));
472 if ($wordBase0 == $startBase) {
473 $type = str_replace(array('(',')'),'', $word1);
474 } elseif($wordBase1 == $startBase) {
475 $type = str_replace(array('(',')'),'', $word0);
476 } elseif( ereg('(^[&$]+)|(\()([a-z0-9]+)(\)$)', $word0, $regs) ) {
477 $tail = str_ireplace($word0, '', $end);
478 $type = $regs[3];
479 } else {
480 // default to string
481 $type = 'string';
484 $type = MethodTable::standardizeType($type);
486 if($type == 'str') {
487 $type = 'string';
488 } elseif($type == 'int' || $type == 'integer') {
489 $type = 'int';
490 } elseif($type == 'bool' || $type == 'boolean') {
491 $type = 'boolean';
492 } elseif($type == 'object' || $type == 'class') {
493 // Note that this is not a valid XMLRPC type
494 $type = 'object';
495 } elseif($type == 'float' || $type == 'dbl' || $type == 'double' || $type == 'flt') {
496 $type = 'double';
497 } elseif($type == 'null') {
498 // Note that this is not a valid XMLRPC type
499 // The null type can have only one value - null. Why would
500 // that be an argument to a function? Just in case:
501 $type = 'null';
502 } elseif($type == 'mixed') {
503 // Note that this is not a valid XMLRPC type
504 $type = 'mixed';
505 } elseif($type == 'array' || $type == 'arr') {
506 $type = 'array';
507 } elseif($type == 'assoc') {
508 $type = 'struct';
509 } elseif($type == 'reference' || $type == 'ref') {
510 // Note that this is not a valid XMLRPC type
511 // As references cannot be serialized or exported, there is
512 // no way this could be XML-RPCed.
513 $type = 'reference';
514 } else {
515 $type = 'string';
518 $result[] = array('type' => $type, 'description' => $start . ' - ' . $tail);
522 return $result;
527 * Cleans the comment string by removing all comment start and end characters.
529 * @static
530 * @private
531 * @param $comment(String) The method's comment.
533 function cleanComment($comment){
534 $comment = str_replace("/**", "", $comment);
535 $comment = str_replace("*/", "", $comment);
536 $comment = str_replace("*", "", $comment);
537 $comment = str_replace("\n", "\\n", trim($comment));
538 $comment = eregi_replace("[\r\t\n ]+", " ", trim($comment));
539 $comment = str_replace("\"", "\\\"", $comment);
540 return $comment;
546 function showCode($methodTable){
548 if(!is_array($methodTable)) $methodTable = array();
550 foreach($methodTable as $methodName=>$methodProps){
551 $result .= "\n\t\"" . $methodName . "\" => array(";
553 foreach($methodProps as $key=>$value){
554 $result .= "\n\t\t\"" . $key . "\" => ";
556 if($key=="arguments"){
557 $result .= "array(";
558 for($i=0; $i<count($value); $i++){
559 $result .= "\"" . addslashes($value[$i]) . "\"";
560 if($i<count($value)-1){
561 $result .= ", ";
564 $result .= ")";
565 }else{
566 $result .= "\"" . $value . "\"";
569 $result .= ",";
572 $result = substr($result, 0, -1);
573 $result .= "\n\t),";
576 $result = substr($result, 0, -1);
577 $result = "\$this->methodTable = array(" . $result;
578 $result .= "\n);";
580 return $result;