You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
9.7 KiB

23 years ago
  1. <?php
  2. class php_function extends php_element {
  3. function __construct($name, $summary, $proto, $desc="", $code="", $role="") {
  4. $this->name = $name;
  5. $this->summary = $summary;
  6. $this->desc = empty($desc) ? "&warn.undocumented.func;" : $desc;
  7. $this->code = $code;
  8. $this->role = empty($role) ? "public" : $role;
  9. if ($this->role === "public") {
  10. $this->status = $this->parse_proto($proto);
  11. }
  12. }
  13. function parse_proto($proto) {
  14. // 'tokenize' it
  15. $len=strlen($proto);
  16. $name="";
  17. $tokens=array();
  18. for ($n=0;$n<$len;$n++) {
  19. $char = $proto{$n};
  20. if (ereg("[a-zA-Z0-9_]",$char)) {
  21. $name.=$char;
  22. } else {
  23. if ($name) $tokens[]=$name;
  24. $name="";
  25. if (trim($char)) $tokens[]=$char;
  26. }
  27. }
  28. if ($name) $tokens[]=$name;
  29. $n=0;
  30. $opts=0;
  31. $params=array();
  32. $return_type = ($this->is_type($tokens[$n])) ? $tokens[$n++] : "void";
  33. $function_name = $tokens[$n++];
  34. if ($return_type === "resource" && $tokens[$n] !== "(") {
  35. $return_subtype = $function_name;
  36. $function_name = $tokens[$n++];
  37. }
  38. if (! $this->is_name($function_name)) {
  39. return("$function_name is not a valid function name");
  40. }
  41. if ($function_name != $this->name) {
  42. return "proto function name is '$function_name' instead of '{$this->name}'";
  43. }
  44. if ($tokens[$n]!='(') return("'(' expected instead of '$tokens[$n]'");
  45. if ($tokens[++$n]!=')') {
  46. for ($param=0;$tokens[$n];$n++,$param++) {
  47. if ($tokens[$n]=='[') {
  48. $params[$param]['optional']=true;
  49. $opts++;
  50. $n++;
  51. if ($param>0) {
  52. if ($tokens[$n]!=',') return("',' expected after '[' instead of '$token[$n]'");
  53. $n++;
  54. }
  55. }
  56. if (!$this->is_type($tokens[$n])) return("type name expected instead of '$tokens[$n]'");
  57. $params[$param]['type']=$tokens[$n];
  58. $n++;
  59. if ($tokens[$n] == "&") {
  60. $params[$param]['by_ref'] = true;
  61. $n++;
  62. }
  63. if ($this->is_name($tokens[$n])) {
  64. $params[$param]['name']=$tokens[$n];
  65. $n++;
  66. }
  67. if ($tokens[$n] == "&") {
  68. $params[$param]['by_ref'] = true;
  69. $n++;
  70. }
  71. if ($params[$param]['type'] === "resource" && $this->is_name($tokens[$n])) {
  72. $params[$param]['subtype'] = $params[$param]['name'];
  73. $params[$param]['name'] = $tokens[$n];
  74. $n++;
  75. }
  76. if ($tokens[$n]=='[') {
  77. $n--;
  78. continue;
  79. }
  80. if ($tokens[$n]==',') continue;
  81. if ($tokens[$n]==']') break;
  82. if ($tokens[$n]==')') break;
  83. }
  84. }
  85. $numopts=$opts;
  86. while ($tokens[$n]==']') {
  87. $n++;
  88. $opts--;
  89. }
  90. if ($opts!=0) return ("'[' / ']' count mismatch");
  91. if ($tokens[$n] != ')') return("')' expected instead of '$tokens[$n]'");
  92. $this->name = $function_name;
  93. $this->returns = $return_type;
  94. if (isset($return_subtype)) {
  95. $this->returns .= " $return_subtype";
  96. }
  97. $this->params = $params;
  98. $this->optional = $numopts;
  99. return true;
  100. }
  101. function c_code($extension) {
  102. $code = "";
  103. $returns = explode(" ", $this->returns);
  104. switch ($this->role) {
  105. case "public":
  106. $code .= "\n/* {{{ proto {$this->returns} {$this->name}(";
  107. if (isset($this->params)) {
  108. foreach ($this->params as $key => $param) {
  109. if (!empty($param['optional']))
  110. $code.=" [";
  111. if ($key)
  112. $code.=", ";
  113. $code .= $param['type']." ";
  114. if (isset($param['subtype'])) {
  115. $code .= $param['subtype']." ";
  116. }
  117. if ($param['type'] !== 'void') {
  118. if (isset($param['by_ref'])) {
  119. $code .= "&";
  120. }
  121. $code .= $param['name'];
  122. }
  123. }
  124. }
  125. for ($n=$this->optional; $n>0; $n--) {
  126. $code .= "]";
  127. }
  128. $code .= ")\n ";
  129. if (!empty($this->summary)) {
  130. $code .= $this->summary;
  131. }
  132. $code .= " */\n";
  133. $code .= "PHP_FUNCTION({$this->name})\n";
  134. $code .= "{\n";
  135. if ($returns[0] === "resource" && isset($returns[1])) {
  136. $resource = $extension->resources[$returns[1]];
  137. if ($resource->alloc === "yes") {
  138. $payload = $resource->payload;
  139. $code .= " $payload * return_res = ($payload *)emalloc(sizeof($payload));\n";
  140. }
  141. }
  142. if (isset($this->params) && count($this->params)) {
  143. $arg_string="";
  144. $arg_pointers=array();
  145. $optional=false;
  146. $res_fetch="";
  147. foreach ($this->params as $key => $param) {
  148. if ($param["type"] === "void") continue;
  149. $name = $param['name'];
  150. $arg_pointers[]="&$name";
  151. if (isset($param['optional'])&&!$optional) {
  152. $optional=true;
  153. $arg_string.="|";
  154. }
  155. switch ($param['type']) {
  156. case "bool":
  157. $arg_string.="b";
  158. $code .= " zend_bool $name = 0;\n";
  159. break;
  160. case "int":
  161. $arg_string.="l";
  162. $code .= " long $name = 0;\n";
  163. break;
  164. case "float":
  165. $arg_string.="d";
  166. $code .= " double $name = 0.0;\n";
  167. break;
  168. case "string":
  169. $arg_string.="s";
  170. $code .= " char * $name = NULL;\n";
  171. $code .= " int {$name}_len = 0;\n";
  172. $arg_pointers[]="&{$name}_len";
  173. break;
  174. case "array":
  175. $arg_string.="a";
  176. $code .= " zval * $name = NULL;\n";
  177. break;
  178. case "object":
  179. $arg_string.="o";
  180. $code .= " zval * $name = NULL;\n";
  181. break;
  182. case "resource":
  183. $arg_string.="r";
  184. $code .= " zval * $name = NULL;\n";
  185. $code .= " int {$name}_id = -1;\n";
  186. // dummfug? $arg_pointers[]="&{$name}_id";
  187. if (isset($param['subtype'])) {
  188. $resource = $extension->resources[$param['subtype']];
  189. $varname = "res_{$name}";
  190. $code .= " {$resource->payload} * $varname;\n";
  191. $res_fetch.=" if ($name) {\n"
  192. ." ZEND_FETCH_RESOURCE($varname, {$resource->payload} *, &$name, {$name}_id, \"$param[subtype]\", le_$param[subtype]);\n"
  193. ." }\n";
  194. } else {
  195. $res_fetch.=" if ($name) {\n"
  196. ." ZEND_FETCH_RESOURCE(???, ???, $name, {$name}_id, \"???\", ???_rsrc_id);\n"
  197. ." }\n";
  198. }
  199. break;
  200. case "stream":
  201. $arg_string .= "r";
  202. $code .= " zval * _z$name = NULL;�\n";
  203. $code .= " php_stream * $name = NULL:\n";
  204. $res_fetch.= " php_stream_from_zval($name, &_z$name);\n";
  205. break;
  206. case "mixed":
  207. case "callback":
  208. $arg_string.="z";
  209. $code .= " zval * $name = NULL;\n";
  210. break;
  211. }
  212. }
  213. }
  214. if (isset($arg_string) && strlen($arg_string)) {
  215. $code .= " int argc = ZEND_NUM_ARGS();\n\n";
  216. $code .= " if (zend_parse_parameters(argc TSRMLS_CC, \"$arg_string\", ".join(", ",$arg_pointers).") == FAILURE) return;\n";
  217. if ($res_fetch) $code.="\n$res_fetch\n";
  218. } else {
  219. $code .= " if (ZEND_NUM_ARGS()>0) { WRONG_PARAM_COUNT; }\n\n";
  220. }
  221. if ($this->code) {
  222. $code .= " {\n$this->code }\n"; // {...} for local variables ...
  223. } else {
  224. $code .= " php_error(E_WARNING, \"{$this->name}: not yet implemented\"); RETURN_FALSE;\n\n";
  225. switch ($returns[0]) {
  226. case "void":
  227. break;
  228. case "bool":
  229. $code .= " RETURN_FALSE;\n";
  230. break;
  231. case "int":
  232. $code .= " RETURN_LONG(0);\n";
  233. break;
  234. case "float":
  235. $code .= " RETURN_DOUBLE(0.0);\n";
  236. break;
  237. case "string":
  238. $code .= " RETURN_STRINGL(\"\", 0, 1);\n";
  239. break;
  240. case "array":
  241. $code .= " array_init(return_value);\n";
  242. break;
  243. case "object":
  244. $code .= " object_init(return_value)\n";
  245. break;
  246. case "resource":
  247. if (isset($returns[1])) {
  248. $code .= " ZEND_REGISTER_RESOURCE(return_value, return_res, le_$returns[1]);\n";
  249. } else {
  250. $code .= " /* RETURN_RESOURCE(...); /*\n";
  251. }
  252. break;
  253. case "stream":
  254. $code .= " /* php_stream_to_zval(stream, return_value); */\n";
  255. break;
  256. case "mixed":
  257. $code .= " /* RETURN_...(...); /*\n";
  258. break;
  259. default:
  260. $code .= " /* UNKNOWN RETURN TYPE '$this->returns' /*\n";
  261. break;
  262. }
  263. }
  264. $code .= "}\n/* }}} */\n\n";
  265. break;
  266. case "internal":
  267. if (!empty($this->code)) {
  268. $code .= " {\n";
  269. $code .= $this->code."\n";
  270. $code .= " }\n";
  271. }
  272. break;
  273. case "private":
  274. $code .= $this->code."\n";
  275. break;
  276. }
  277. return $code;
  278. }
  279. function docbook_xml() {
  280. $xml =
  281. '<?xml version="1.0" encoding="iso-8859-1"?>
  282. <!-- '.'$'.'Revision: 1.0 $ -->
  283. <refentry id="function.'.strtolower(str_replace("_","-",$this->name)).'">
  284. <refnamediv>
  285. <refname>'.$this->name.'</refname>
  286. <refpurpose>'.$this->summary.'</refpurpose>
  287. </refnamediv>
  288. <refsect1>
  289. <title>Description</title>
  290. <methodsynopsis>
  291. ';
  292. $xml .= " <type>{$this->returns}</type><methodname>{$this->name}</methodname>\n";
  293. if (empty($this->params) || $this->params[0]["type"]==="void") {
  294. $xml .= " <void/>\n";
  295. } else {
  296. foreach ($this->params as $key => $param) {
  297. if (isset($param['optional'])) {
  298. $xml .= " <methodparam choice='opt'>";
  299. } else {
  300. $xml .= " <methodparam>";
  301. }
  302. $xml .= "<type>$param[type]</type><parameter>";
  303. if (isset($param['by_ref'])) {
  304. $xml .= "&amp;";
  305. }
  306. $xml .= "$param[name]</parameter>";
  307. $xml .= "</methodparam>\n";
  308. }
  309. }
  310. $desc = $this->desc;
  311. if (!strstr($this->desc,"<para>")) {
  312. $desc = " <para>\n$desc </para>\n";
  313. }
  314. $xml .=
  315. ' </methodsynopsis>
  316. '.$desc.'
  317. </refsect1>
  318. </refentry>
  319. ';
  320. $xml .= $this->docbook_editor_settings(4);
  321. return $xml;
  322. }
  323. function write_test($extension) {
  324. $fp = fopen("{$extension->name}/tests/{$this->name}.phpt", "w");
  325. fputs($fp,
  326. "--TEST--
  327. {$this->name}() function
  328. --SKIPIF--
  329. <?php if (!extension_loaded('{$extension->name}')) print 'skip'; ?>
  330. --POST--
  331. --GET--
  332. --FILE--
  333. <?php
  334. echo 'no test case for {$this->name}() yet';
  335. ?>
  336. --EXPECT--
  337. no test case for {$this->name}() yet");
  338. fclose($fp);
  339. }
  340. }
  341. ?>