File indexing completed on 2024-12-29 04:15:17
0001 <?php 0002 /* 0003 SPDX-FileCopyrightText: 2008 Niko Sams <niko.sams@gmail.com> 0004 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 if (!isset($_SERVER['argv'][1])) { 0010 $msg = "Usage:\n".$_SERVER['argv'][0]." [path to phpdoc]\n"; 0011 $msg .= "you may checkout from the php svn server using this command:\n"; 0012 $msg .= "svn checkout http://svn.php.net/repository/phpdoc/en/trunk ./phpdoc-en\n"; 0013 $msg .= "\nTo debug files/directories use this: ".$_SERVER['argv'][0]." --debug PATH ...\n"; 0014 file_put_contents('php://stderr', $msg); 0015 exit(-1); 0016 } 0017 0018 $skipClasses = array(); 0019 0020 $skipClasses[] = 'self'; 0021 $skipClasses[] = 'parent'; 0022 $skipClasses[] = '__php_incomplete_class'; 0023 $skipClasses[] = 'php_user_filter'; 0024 $skipClasses[] = 'static'; // O_o where does that come from? 0025 $skipClasses[] = 'componere\abstract\definition'; // invalid namespace identifier, discussed in https://github.com/krakjoe/componere/issues/5 0026 0027 // interfaces wrongly declared as classes 0028 $interfaceClasses = array(); 0029 $interfaceClasses[] = 'sessionhandlerinterface'; 0030 $interfaceClasses[] = 'yaf_route_interface'; 0031 $interfaceClasses[] = 'yaf_view_interface'; 0032 0033 $abstractClasses[] = array(); 0034 $abstractClasses[] = 'reflectionfunctionabstract'; 0035 $abstractClasses[] = 'xmldiff\base'; 0036 $abstractClasses[] = 'yaf_action_abstract'; 0037 0038 $skipComments = array(); 0039 $skipComments[] = ':'; 0040 $skipComments[] = '(method):'; 0041 $skipComments[] = 'Description here.'; 0042 $skipComments[] = 'Description here...'; 0043 $skipComments[] = 'Description'; 0044 $skipComments[] = 'The function description goes here.'; 0045 0046 $classes = array(); 0047 $constants = array(); 0048 $constants_comments = array(); 0049 $variables = array(); 0050 $existingFunctions = array(); 0051 $versions = array(); 0052 0053 if ($_SERVER['argv'][1] == '--debug') { 0054 // only debug given file 0055 define('DEBUG', true); 0056 $dirs = array(); 0057 foreach ( $_SERVER['argv'] as $i => $v ) { 0058 if ( $i <= 1 ) { 0059 continue; 0060 } else if ( is_dir($v) ) { 0061 $dirs[] = $v; 0062 } else if ( file_exists($v) ) { 0063 parseFile(new SplFileInfo($v)); 0064 } else { 0065 trigger_error("bad argument: ".$v, E_USER_ERROR); 0066 } 0067 } 0068 } else { 0069 define('DEBUG', false); 0070 0071 if (!file_exists($_SERVER['argv'][1])) { 0072 file_put_contents('php://stderr', "phpdoc path not found"); 0073 exit(-1); 0074 } 0075 0076 $dirs = array( 0077 $_SERVER['argv'][1]."/reference", 0078 $_SERVER['argv'][1]."/features", 0079 $_SERVER['argv'][1]."/appendices", 0080 $_SERVER['argv'][1]."/language/predefined/" 0081 ); 0082 } 0083 0084 foreach ($dirs as $dir) { 0085 $dirIt = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dir)); 0086 foreach ($dirIt as $file) { 0087 parseFile($file); 0088 } 0089 } 0090 0091 unset($existingFunctions); 0092 0093 /* 0094 Here be dirty hacks! PHP's documentation isn't as good as could be wished for... 0095 */ 0096 0097 // Add constant only classes 0098 foreach (array_keys($constants) as $c) { 0099 if ($pos = strpos($c, '::')) { 0100 $class = substr($c, 0, $pos); 0101 $isInterface = false; 0102 $isAbstractClass = false; 0103 newClassEntry($class); 0104 } 0105 } 0106 0107 $skipFunctions = array(); 0108 // remove delete() function which only acts as a pointer to unlink 0109 // in the documentation but does not actually exist as a alias in PHP 0110 $skipFunctions[] = 'delete'; 0111 0112 // awesome uncallable functions - noone knows wth that should be... 0113 $skipMethods = array(); 0114 $skipMethods[] = 'list'; 0115 $skipMethods[] = 'declare'; 0116 $skipMethods[] = 'do'; 0117 $skipMethods[] = 'echo'; 0118 $skipMethods[] = 'function'; 0119 0120 /* 0121 Here ends the hackings... 0122 */ 0123 0124 function constTypeValue($ctype) { 0125 if ($ctype == 'integer' || $ctype == 'int') { 0126 return "0"; 0127 } else if ($ctype == 'string') { 0128 return "''"; 0129 } else if ($ctype == 'bool') { 0130 return "false"; 0131 } else if ($ctype == 'float') { 0132 return "0.0"; 0133 } else { 0134 // default to integer const type 0135 return "0"; 0136 } 0137 } 0138 0139 function removeTag($xml, $tag) { 0140 $tag = preg_quote($tag, '#'); 0141 return trim(preg_replace('#(^<'.$tag.'[^>]*>|</'.$tag.'>$)#s', '', trim($xml))); 0142 } 0143 0144 function cleanupComment($comment) { 0145 // <function|parameter>...</> to {@link ...} 0146 $comment = preg_replace('#<(function|parameter)>(.+)</\1>#U', '{@link $2}', $comment); 0147 // remove <para> and other stuff 0148 ///TODO: support web-links, lists and tables 0149 $comment = strip_tags($comment); 0150 $comment = html_entity_decode($comment); 0151 0152 // make sure no */ occurs in a comment... 0153 $comment = preg_replace('#\*/#', '* /', $comment); 0154 0155 $comment = preg_replace('#(?<=[^\n])\n(?=[^\n])#s', ' ', $comment); 0156 0157 $comment = preg_replace('# +#', ' ', $comment); 0158 $comment = preg_replace('#^ | $#m', '', $comment); 0159 $comment = preg_replace("#\n{3,}#", "\n\n", $comment); 0160 0161 $comment = trim($comment); 0162 return $comment; 0163 } 0164 0165 function prepareComment($comment, array $more, $indent = '') { 0166 $comment = cleanupComment($comment); 0167 if (empty($comment) && empty($more)) { 0168 return ''; 0169 } 0170 $comment = wordwrap($comment, 70, "\n", false); 0171 if ( !empty($more) ) { 0172 if ( !empty($comment) ) { 0173 $comment .= "\n\n"; 0174 } 0175 foreach($more as $s) { 0176 $comment .= str_replace("\n", "\n ", // indent 0177 wordwrap(cleanupComment($s), 68, "\n", false) 0178 )."\n"; 0179 } 0180 } 0181 $comment = rtrim($comment); 0182 // add indentation and asterisk 0183 $comment = preg_replace("#^#m", $indent." * ", $comment); 0184 $comment = str_replace(" * \n", " *\n", $comment); 0185 return $indent."/**\n". 0186 $comment."\n". 0187 $indent." **/\n"; 0188 } 0189 0190 function sortByName($a, $b) { 0191 return strnatcasecmp($a['name'], $b['name']); 0192 } 0193 0194 $fileHeader = "<?php\n"; 0195 $fileHeader .= "// THIS FILE IS GENERATED\n"; 0196 $fileHeader .= "// WARNING! All changes made in this file will be lost!\n\n"; 0197 0198 $declarationCount = 0; 0199 $out = $fileHeader; 0200 0201 // make sure the output it somehow ordered to prevent huge svn diffs 0202 uksort($variables, 'strnatcasecmp'); 0203 uksort($classes, 'strnatcasecmp'); 0204 uksort($constants, 'strnatcasecmp'); 0205 0206 // put some base classes up front 0207 $baseClasses = [ 'exception', 'error', 'throwable', 'iterator', 'traversable' ]; 0208 foreach ($baseClasses as $baseClass) { 0209 if (array_key_exists($baseClass, $classes)) { 0210 $base = $classes[$baseClass]; 0211 unset($classes[$baseClass]); 0212 $classes = array_merge(array($baseClass => $base), $classes); 0213 reset($classes); 0214 } 0215 } 0216 0217 foreach ($variables as $name=>$var) { 0218 $declarationCount++; 0219 $moreDesc = array(); 0220 if ($var['deprecated']) { 0221 $moreDesc[] = "@deprecated"; 0222 } 0223 if (isset($var['superglobal']) && $var['superglobal']) { 0224 $moreDesc[] = "@superglobal"; 0225 } 0226 $out .= prepareComment($var['desc'], $moreDesc); 0227 $out .= "$name = array();\n\n"; 0228 } 0229 0230 // make skipclasses lowercase 0231 foreach ($skipClasses as &$name) { 0232 $name = strtolower($name); 0233 } 0234 0235 foreach ($classes as $class => $i) { 0236 if (in_array($class, $skipClasses)) continue; //skip those as they are documented in spl.php 0237 if ($class != 'global') { 0238 if (isset($i['desc'])) { 0239 $out .= prepareComment($i['desc'], array()); 0240 } 0241 if (!empty($i['namespace'])) { 0242 $out .= 'namespace ' . $i['namespace'] . " {\n"; 0243 } 0244 $class = $i['prettyName']; 0245 $out .= ($i['isAbstract']) ? 'abstract ' : ''; 0246 $out .= ($i['isFinal'] && !$i['isInterface']) ? 'final ' : ''; 0247 $out .= ($i['isInterface'] ? 'interface' : 'class') . " " . $class; 0248 if (isset($i['extends'])) { 0249 if (!is_array($i['extends']) && !in_array(strtolower($i['extends']), $skipClasses)) { 0250 $out .= " extends {$i['extends']}"; 0251 } elseif (is_array($i['extends'])) { 0252 $out .= " extends "; 0253 foreach ($i['extends'] as $entry) { 0254 if (!in_array(strtolower($entry), $skipClasses)) { 0255 $out .= "$entry, "; 0256 } 0257 } 0258 $out = rtrim($out, ', '); 0259 } 0260 } 0261 if (isset($i['implements'])) { 0262 $out .= " implements ".implode(", ", $i['implements']); 0263 } 0264 $out .= " {\n"; 0265 $declarationCount++; 0266 } 0267 0268 $indent = ''; 0269 if ($class != 'global') $indent = ' '; 0270 0271 if (array_key_exists('extends', $i)) { 0272 if (!is_array($i['extends'])) { 0273 $extends = [ $i['extends'] ]; 0274 } else { 0275 $extends = $i['extends']; 0276 } 0277 0278 foreach ($extends as $entry) { 0279 if (in_array(strtolower($entry), $skipClasses)) { 0280 $base = $classes[strtolower($entry)]; 0281 0282 $i['properties'] = array_merge($i['properties'], $base['properties']); 0283 $i['functions'] = array_merge($i['functions'], $base['functions']); 0284 } 0285 } 0286 } 0287 0288 foreach ($constants as $c=>$ctype) { 0289 if ($pos = strpos($c, '::')) { 0290 if (substr($c, 0, $pos) == $class) { 0291 $moreDesc = array(); 0292 $moreDesc[] = "@var {$ctype}"; 0293 if ( isset($constants_comments[$c]) ) { 0294 $out .= prepareComment($constants_comments[$c], $moreDesc, $indent); 0295 } 0296 unset($constants[$c]); 0297 $c = substr($c, $pos+2); 0298 $out .= " const $c = ".constTypeValue($ctype).";\n\n"; 0299 $declarationCount++; 0300 } 0301 } 0302 } 0303 0304 usort($i['properties'], 'sortByName'); 0305 $handled = []; 0306 foreach ($i['properties'] as $f) { 0307 if (in_array($f['name'], $handled)) { 0308 continue; 0309 } 0310 $handled[] = $f['name']; 0311 $moreDesc = array(); 0312 if ($f['type']) { 0313 $moreDesc[] = "@var {$f['type']}"; 0314 } 0315 ///HACK the directory stuff has really bad documentation 0316 if ($class != 'directory') { 0317 $out .= prepareComment($f['desc'], $moreDesc, $indent); 0318 } 0319 if ($class != 'global' && array_key_exists('modifiers', $f) && is_array($f['modifiers'])) { 0320 $f['modifiers'] = array_filter($f['modifiers'], function ($value){ return $value != 'readonly'; }); 0321 $modifiers = implode(' ', $f['modifiers']); 0322 $modifiers .= empty($modifiers) ? 'public ' : ' '; 0323 } 0324 $out .= "{$indent}{$modifiers}$".$f['name'].";\n\n"; 0325 $declarationCount++; 0326 } 0327 0328 usort($i['functions'], 'sortByName'); 0329 $handled = []; 0330 foreach ($i['functions'] as $f) { 0331 if (in_array($f['name'], $handled)) { 0332 continue; 0333 } 0334 $handled[] = $f['name']; 0335 if ( $class == 'global' && in_array($f['name'], $skipFunctions) ) { 0336 continue; 0337 } else if ( $class != 'global' && in_array($f['name'], $skipMethods) ) { 0338 continue; 0339 } 0340 if ($endpos = strrpos($f['name'], '\\')) { 0341 $out .= 'namespace ' . substr($f['name'], 0, $endpos) . " {\n\n"; 0342 $f['name'] = substr($f['name'], $endpos + 1); 0343 } 0344 $moreDesc = array(); 0345 foreach ($f['params'] as $pi=>$param) { 0346 $desc = ''; 0347 if ( isset($param['desc']) ) { 0348 $desc = trim($param['desc']); 0349 } 0350 $param_name = str_replace('$...', '...$', '$'.$param['name']); 0351 $moreDesc[] = "@param {$param['type']} $param_name $desc"; 0352 } 0353 if ($f['type']) { 0354 $moreDesc[] = rtrim("@return {$f['type']} {$f['return_desc']}"); 0355 } 0356 $version_key = strtolower(($class == 'global' ? '' : $class.'::') . $f['name']); 0357 if (isset($versions[$version_key])) { 0358 $moreDesc[] = "@since {$versions[$version_key]}"; 0359 } 0360 ///HACK the directory stuff has really bad documentation 0361 if ($class != 'directory') { 0362 $out .= prepareComment($f['desc'], $moreDesc, $indent); 0363 } 0364 if ($f['name'] == '__construct' && strtolower($class) == 'eventutil') { 0365 //HACK: PHP doesn't allow abstract final classes, so make the constructor private instead. 0366 $f['modifiers'] = [ 'private' ]; 0367 } 0368 if ($class != 'global' && array_key_exists('modifiers', $f) && is_array($f['modifiers'])) { 0369 if ($i['isInterface'] === true) { 0370 $f['modifiers'] = array_filter($f['modifiers'], function ($value){ return $value != 'abstract' && $value != 'final'; }); 0371 } 0372 $modifiers = implode(' ', $f['modifiers']); 0373 $modifiers .= empty($modifiers) ? '' : ' '; 0374 } else { 0375 $modifiers = ''; 0376 } 0377 $out .= "{$indent}{$modifiers}function ".$f['name']; 0378 $out .= "("; 0379 $first = true; 0380 $params_total = count($f['params'])-1; 0381 foreach ($f['params'] as $pi=>$param) { 0382 $param_name = str_replace('$...', '...$', '$'.$param['name']); 0383 $param_name = str_replace('"', '', $param_name); 0384 if (substr($param_name, 0, 3) == '...' && $pi < $params_total) { 0385 // varargs in the middle of arguments are invalid syntax. 0386 // print the documentation, but ignore them in the function signature 0387 continue; 0388 } 0389 if (!$first) $out .= ", "; 0390 $first = false; 0391 if ($param['isRef']) $out .= "&"; 0392 $out .= $param_name; 0393 } 0394 $out .= ")"; 0395 if ( $i['isInterface'] || (array_key_exists('modifiers', $f) && in_array('abstract', $f['modifiers'])) ) { 0396 $out .= ";"; 0397 } else { 0398 $out .= "{}"; 0399 } 0400 $out .= "\n\n"; 0401 if ($endpos) $out .= "}\n\n"; 0402 $declarationCount++; 0403 } 0404 0405 if ($class != 'global') $out .= "}\n"; 0406 if (!empty($i['namespace'])) $out .= "}\n"; 0407 } 0408 foreach ($constants as $c=>$ctype) { 0409 if (strpos($c, '::')===false) { 0410 if ( isset($constants_comments[$c]) ) { 0411 $out .= prepareComment($constants_comments[$c], array()); 0412 } 0413 $out .= "define('$c', ".constTypeValue($ctype).");\n"; 0414 $declarationCount++; 0415 } 0416 } 0417 chdir(dirname(__FILE__)); 0418 if ( !DEBUG ) { 0419 echo "saving phpfunctions.php file\n"; 0420 file_put_contents("phpfunctions.php", $out); 0421 0422 if ( shell_exec("which php-parser") ) { 0423 echo "making sure phpfunctions file is valid...\n"; 0424 system("php-parser phpfunctions.php", $ret); 0425 if ( $ret != 0 ) { 0426 die("could not parse file, aborting\n"); 0427 } 0428 } else { 0429 echo "note: put php-parser in your path and I can check the generated file directly...\n"; 0430 } 0431 0432 echo "done\n"; 0433 } else { 0434 echo "phpfunctions.php\n~~~~\n$out\n~~~~\n"; 0435 } 0436 echo "wrote ".$declarationCount." declarations\n"; 0437 0438 /** 0439 * Parse file 0440 * 0441 * @param SplFileInfo $file File handler 0442 * @return bool 0443 */ 0444 function parseFile($file, $funcOverload="", $classOverload="") { 0445 global $existingFunctions, $constants, $constants_comments, $variables, $classes, $isInterface, $isAbstractClass, $isFinalClass, $versions; 0446 0447 if (substr($file->getFilename(), -4) != '.xml') return false; 0448 if (substr($file->getFilename(), 0, 9) == 'entities.') return false; 0449 $string = file_get_contents($file->getPathname()); 0450 $isInterface = (strpos($string, '<phpdoc:classref') !== false && 0451 strpos($string, '&reftitle.interfacesynopsis;') !== false) || 0452 strpos($string, ' interface</title>') !== false; 0453 $isAbstractClass = false; 0454 $isFinalClass = false; 0455 0456 $string = str_replace('&null;', 'NULL', $string); 0457 $string = str_replace('&true;', 'TRUE', $string); 0458 $string = str_replace('&false;', 'FALSE', $string); 0459 $string = preg_replace('#(?:(&|>|<)|&[A-Za-z\\.0-9-_]+;)#', '$1', $string); 0460 $removeSections = array(); 0461 $removeSections[] = 'apd.installwin32'; 0462 $removeSections[] = 'intl.intldateformatter-constants.calendartypes'; 0463 foreach ($removeSections as $i) { 0464 $string = preg_replace('#'.preg_quote('<section xml:id="'.$i.'">').'.*?</section>#s', '', $string); 0465 } 0466 echo "reading documentation from {$file->getPathname()}\n"; 0467 0468 libxml_use_internal_errors(TRUE); 0469 $xml = simplexml_load_string($string, "SimpleXMLElement", LIBXML_NOCDATA); 0470 0471 if ($xml === false) { 0472 echo " parsing XMl failed!\n"; 0473 return false; 0474 } 0475 0476 if ( $file->getFilename() == 'versions.xml' ) { 0477 foreach ( $xml->xpath('/versions/function') as $f ) { 0478 $attrs = $f->attributes(); 0479 $versions[strtolower($attrs['name'])] = (string) $attrs['from']; 0480 } 0481 return; 0482 } 0483 0484 $xml->registerXPathNamespace('db', 'http://docbook.org/ns/docbook'); 0485 $xml->registerXPathNamespace('phpdoc', 'http://php.net/ns/phpdoc'); 0486 if ($vars = $xml->xpath('//phpdoc:varentry//db:refnamediv')) { 0487 foreach ($vars as $var) { 0488 foreach ($var->refname as $i) { 0489 $i = (string)$i; 0490 if ( isset($variables[$i]) ) { 0491 $v = $variables[$i]; 0492 } else { 0493 $v = array(); 0494 } 0495 if (substr($i, 0, 1) != '$') continue; 0496 if (substr($i, -10) == ' [removed]') continue; 0497 0498 if (substr($i, -13) == ' [deprecated]') { 0499 $i = substr($i, 0, -13); 0500 $v['deprecated'] = true; 0501 } else { 0502 $v['deprecated'] = false; 0503 } 0504 $v['desc'] = (string)$var->refpurpose; 0505 $variables[$i] = $v; 0506 } 0507 } 0508 } 0509 if ($vars = $xml->xpath("//phpdoc:varentry[@xml:id='language.variables.superglobals']//db:member/db:varname")) { 0510 foreach ($vars as $var) { 0511 $variables[(string)$var]['superglobal'] = true; 0512 } 0513 } 0514 if (isset($xml->variablelist)) { 0515 foreach ($xml->variablelist as $list) { 0516 foreach ($list->varlistentry as $i=>$varlistentry) { 0517 if ($c = (string)$varlistentry->term->constant) { 0518 if (!isset($constants[$c])) { 0519 if (strpos($c, '=')) { 0520 $c = substr($c, 0, strpos($c, '=')); 0521 } 0522 $ctype = $varlistentry->term->type; 0523 if (!$ctype) { 0524 $ctype = $varlistentry->term->link; 0525 } 0526 $constants[$c] = (string)$ctype; 0527 } 0528 } 0529 } 0530 } 0531 } 0532 // handle class constants (and their global aliases) not defined as fieldsynopsis 0533 $consts = $xml->xpath("db:partintro//db:varlistentry"); 0534 foreach ($consts as $c) { 0535 foreach ($c->term as $t) { 0536 if ($name = (string)$t->constant) { 0537 if (!isset($constants[$name])) { 0538 if (strpos($name, '=')) { 0539 $c = substr($name, 0, strpos($name, '=')); 0540 } 0541 $constants[$name] = 'mixed'; 0542 $constants_comments[$name] = cleanupComment($c->listitem->para->asXML()); 0543 } 0544 } 0545 } 0546 } 0547 // handle constants under section 0548 if ($file->getFilename() == 'constants.xml') { 0549 $consts = $xml->xpath("db:section//db:varlistentry"); 0550 foreach ($consts as $c) { 0551 if ($name = (string)$c->term->constant) { 0552 if (!isset($constants[$name])) { 0553 if (strpos($name, '=')) { 0554 $c = substr($name, 0, strpos($name, '=')); 0555 } 0556 $ctype = $c->term->type; 0557 if (!$ctype) { 0558 $ctype = $c->term->link; 0559 } 0560 $constants[$name] = (string)$ctype; 0561 } 0562 } 0563 } 0564 } 0565 // handle constants within tables 0566 if ($file->getFilename() == 'constants.xml') { 0567 $consts = $xml->xpath("db:section//db:row"); 0568 foreach ($consts as $c) { 0569 if (!$c->entry && !$c->entry[0] && !$c->entry[0]->constant) { 0570 continue; 0571 } 0572 $name = (string)$c->entry[0]->constant; 0573 switch ($name) { 0574 case '': 0575 continue 2; 0576 default: 0577 if ($c->entry[2]) { 0578 $constants[$name] = $c->term->type; 0579 $constants_comments[$name] = cleanupComment($c->entry[2]->asXML()); 0580 } else if ($c->entry[1]) { 0581 $constants[$name] = 'mixed'; 0582 $constants_comments[$name] = cleanupComment($c->entry[1]->asXML()); 0583 } 0584 } 0585 } 0586 } 0587 // handle constants within para 0588 if ($file->getFilename() == 'constants.xml') { 0589 $consts = $xml->xpath("db:para//db:varlistentry"); 0590 foreach ($consts as $c) { 0591 if ($name = (string)$c->term->constant) { 0592 if ($name == '__default') { 0593 continue; 0594 } 0595 if (!isset($constants[$name])) { 0596 if (strpos($name, '=')) { 0597 $c = substr($name, 0, strpos($name, '=')); 0598 } 0599 $ctype = $c->term->type; 0600 if (!$ctype) { 0601 $ctype = $c->term->link; 0602 } 0603 $constants[$name] = (string)$ctype; 0604 } 0605 } 0606 } 0607 } 0608 if ($file->getFilename() == 'constants.xml') { 0609 $consts = $xml->xpath("db:para//db:simpara"); 0610 foreach ($consts as $c) { 0611 if ($name = (string)$c->constant) { 0612 if ($name[0] == '"') { 0613 continue; 0614 } 0615 if (!isset($constants[$name])) { 0616 $constants[$name] = 'mixed'; 0617 } 0618 } 0619 } 0620 } 0621 if ($file->getFilename() == 'ciphers.xml') { 0622 $consts = $xml->xpath("db:para//db:simpara"); 0623 foreach ($consts as $c) { 0624 if ($name = (string)$c) { 0625 $name = explode(' ', $name)[0]; 0626 $name = explode('(', $name)[0]; 0627 if (!isset($constants[$name])) { 0628 $constants[$name] = 'mixed'; 0629 } 0630 } 0631 } 0632 } 0633 // handle constants in tables within refsect1 0634 if ($file->getFilename() != 'attr.xml') { 0635 $consts = $xml->xpath("db:refsect1//db:row"); 0636 foreach ($consts as $c) { 0637 if (!$c->entry && !$c->entry[0] && !$c->entry[0]->constant) { 0638 continue; 0639 } 0640 $name = (string)$c->entry[0]->constant; 0641 switch ($name) { 0642 case '': 0643 case '0': 0644 continue 2; 0645 case 'ABDAY_(1-7)': 0646 case 'DAY_(1-7)': 0647 for ($i=1; $i<8;$i++) { 0648 $cname = substr($name, 0, strpos($name, '_') + 1) . $i; 0649 $constants[$cname] = 'mixed'; 0650 if ($c->entry[1]) { 0651 $constants_comments[$cname] = cleanupComment($c->entry[1]->asXML()); 0652 } 0653 } 0654 break; 0655 case 'ABMON_(1-12)': 0656 case 'MON_(1-12)': 0657 for ($i=1; $i<13;$i++) { 0658 $cname = substr($name, 0, strpos($name, '_') + 1) . $i; 0659 $constants[$cname] = 'mixed'; 0660 if ($c->entry[1]) { 0661 $constants_comments[$cname] = cleanupComment($c->entry[1]->asXML()); 0662 } 0663 } 0664 break; 0665 default: 0666 $constants[$name] = 'mixed'; 0667 if ($c->entry[1]) { 0668 $constants_comments[$name] = cleanupComment($c->entry[1]->asXML()); 0669 } 0670 } 0671 } 0672 } 0673 // handle constants within file-upload.xml 0674 if ($file->getFilename() == 'file-upload.xml') { 0675 $consts = $xml->xpath("db:sect1//db:varlistentry"); 0676 foreach ($consts as $c) { 0677 if ($name = (string)$c->term->constant) { 0678 if (!isset($constants[$name])) { 0679 if (strpos($name, '=')) { 0680 $c = substr($name, 0, strpos($name, '=')); 0681 } 0682 $ctype = $c->term->type; 0683 if (!$ctype) { 0684 $ctype = $c->term->link; 0685 } 0686 $constants[$name] = (string)$ctype; 0687 $constants_comments[$name] = cleanupComment($c->listitem->para->asXML()); 0688 } 0689 } 0690 } 0691 } 0692 // handle constants within tokens.xml 0693 if ($file->getFilename() == 'tokens.xml') { 0694 $consts = $xml->xpath("db:table//db:row"); 0695 foreach ($consts as $c) { 0696 if (!$c->entry && !$c->entry[0] && !$c->entry[0]->constant) { 0697 continue; 0698 } 0699 $name = (string)$c->entry[0]->constant; 0700 switch ($name) { 0701 case '': 0702 continue 2; 0703 default: 0704 $constants[$name] = 'mixed'; 0705 } 0706 } 0707 } 0708 // handle url-stat constants 0709 if ($file->getFilename() == 'url-stat.xml') { 0710 $consts = $xml->xpath("db:refsect1//db:informaltable//db:row"); 0711 foreach ($consts as $c) { 0712 if (!$c->entry && !$c->entry[0]) { 0713 continue; 0714 } 0715 $name = (string)$c->entry[0]; 0716 switch ($name) { 0717 case '': 0718 case 'Flag': 0719 continue 2; 0720 default: 0721 $constants[$name] = 'mixed'; 0722 $constants_comments[$name] = cleanupComment($c->entry[1]->asXML()); 0723 } 0724 } 0725 } 0726 // handle constants.xml with different layout as those above 0727 if ( !isset($xml->variablelist) && $file->getFilename() == 'constants.xml' && $xml->xpath("//db:constant") ) { 0728 $consts = $xml->xpath("//db:entry"); 0729 foreach ( $consts as $i=>$p ) { 0730 if ( isset($p->constant) ) { 0731 if ( !isset($p->type) ) { 0732 // default to integer constants 0733 $p->type = 'integer'; 0734 } else { 0735 // check for comment 0736 // next entry is the value of the constant which is followed by the comment 0737 if ( isset($consts[$i+2]) && !$consts[$i+2]->children() ) { 0738 $comment = $consts[$i+2]->asXml(); 0739 if ( !empty($comment) ) { 0740 $constants_comments[(string)$p->constant] = $comment; 0741 } 0742 } 0743 } 0744 $name = (string)$p->constant; 0745 if ($name == 'LOG_LOCAL0 ... LOG_LOCAL7') { 0746 for ($i=0; $i<8; $i++) { 0747 $constants['LOG_LOCAL' . $i] = (string)$p->type; 0748 } 0749 } else { 0750 $constants[$name] = (string)$p->type; 0751 } 0752 } 0753 } 0754 } else if (!isset($xml->variablelist) && $file->getFilename() == 'commandline.xml') { 0755 // yay for non-unified xml structures :-X 0756 $consts = $xml->xpath("//db:row"); 0757 foreach ( $consts as $i=>$p ) { 0758 $constant = ""; 0759 // default to integer constants 0760 $type = "integer"; 0761 if ( isset($p->entry[0]) && isset($p->entry[0]->constant) ) { 0762 $constant = trim((string) $p->entry[0]->constant); 0763 if ( isset($p->entry[0]->constant->type) ) { 0764 $type = (string)$p->entry[0]->constant->type; 0765 } 0766 } 0767 if (empty($constant)) { 0768 continue; 0769 } 0770 // check for comment 0771 // next entry is the comment 0772 if ( isset($p->entry[1]) ) { 0773 $comment = $p->entry[1]->para->asXml(); 0774 if ( !empty($comment) ) { 0775 $constants_comments[$constant] = $comment; 0776 } 0777 } 0778 $constants[$constant] = $type; 0779 } 0780 } 0781 if ($list = $xml->xpath('//db:sect2[starts-with(@xml:id, "reserved.classes")]/db:variablelist/db:varlistentry')) { 0782 foreach ($list as $l) { 0783 $classname = newClassEntry((string)$l->term->classname); 0784 0785 $classes[$classname]['desc'] = cleanupComment(removeTag($l->listitem->asXML(), 'listitem')); 0786 } 0787 } 0788 0789 $cEls = $xml->xpath('//db:classsynopsis/db:classsynopsisinfo'); 0790 if ($cEls) { 0791 foreach ($cEls as $class) { 0792 $class->registerXPathNamespace('db', 'http://docbook.org/ns/docbook'); 0793 $className = (string)$class->ooclass->classname; 0794 if ((string)$class->ooclass->modifier === 'abstract') { 0795 $isAbstractClass = true; 0796 } 0797 if ((string)$class->ooclass->modifier === 'final') { 0798 $isFinalClass = true; 0799 } 0800 if (!$className) continue; 0801 $className = newClassEntry($className); 0802 if ($interfaces = $class->xpath('//db:oointerface/db:interfacename')) { 0803 $key = $isInterface ? 'extends' : 'implements'; 0804 foreach ($interfaces as $if) { 0805 $classes[$className][$key][] = (string)$if; 0806 } 0807 } 0808 if ($extends = $class->xpath('//db:ooclass')) { 0809 foreach ($extends as $c) { 0810 if ($c->modifier == 'extends') { 0811 if (array_key_exists('extends', $classes[$className]) && is_array($classes[$className]['extends'])) { 0812 array_unshift($classes[$className]['extends'], (string)$c->classname); 0813 } else { 0814 $classes[$className]['extends'] = (string)$c->classname; 0815 } 0816 } elseif ($c->modifier == 'implements') { 0817 if (array_key_exists('implements', $classes[$className]) && is_array($classes[$className]['implements'])) { 0818 array_unshift($classes[$className]['implements'], (string)$c->classname); 0819 } else { 0820 $classes[$className]['implements'][] = (string)$c->classname; 0821 } 0822 } 0823 } 0824 } 0825 if ($paras = $xml->xpath('//db:section[starts-with(@xml:id, "'.$className.'")]/db:para')) { 0826 foreach ($paras as $p) { 0827 $classes[$className]['desc'] .= "\n". cleanupComment(removeTag($p->asXML(), 'para')); 0828 } 0829 } elseif ($paras = $xml->xpath('//db:section[starts-with(@xml:id, "'. str_replace('_', '-', $className) .'")]/db:para')) { 0830 foreach ($paras as $p) { 0831 $classes[$className]['desc'] .= "\n". cleanupComment(removeTag($p->asXML(), 'para')); 0832 } 0833 } 0834 } 0835 } 0836 0837 $addedSomething = false; 0838 0839 if (isset($xml->partintro) && !isset($xml->refsect1)) { 0840 if (isset($xml->partintro->section[1]->classsynopsis->fieldsynopsis)) { 0841 $synopsis = $xml->partintro->section[1]->classsynopsis->fieldsynopsis; 0842 $class = $xml->partintro->section[1]->classsynopsis->ooclass->classname; 0843 0844 $classname = newClassEntry($class); 0845 0846 foreach ($synopsis as $property){ 0847 $name = $property->varname; 0848 0849 $modifiers = (array) $property->modifier; 0850 $type = isset($property->type) ? $property->type : 'mixed'; 0851 $desc = ''; 0852 0853 if (in_array('const', $modifiers)) { 0854 $constantname = strtolower(substr((string) $name, strrpos((string) $name, '::') + 2)); 0855 if ($constant_descs = $xml->xpath('//db:section[@xml:id="' . $classname .'.constants"]//db:varlistentry[@xml:id="'. $classname .'.constants.'. $constantname .'"]') ) { 0856 foreach ($constant_descs as $constant_desc) { 0857 $desc .= getDocumentationFromPartIntro($constant_desc); 0858 } 0859 } 0860 $constants_comments[(string) $name] = $desc; 0861 $constants[(string) $name] = $type; 0862 } else { 0863 if ($property_descs = $xml->xpath('//db:section[@xml:id="' . $classname .'.props"]//db:varlistentry[@xml:id="'. $classname .'.props.'. $name .'"]') ) { 0864 foreach ($property_descs as $property_desc) { 0865 $desc .= getDocumentationFromPartIntro($property_desc); 0866 } 0867 } 0868 newPropertyEntry($class, $name, $desc, $type, $modifiers); 0869 } 0870 } 0871 0872 $addedSomething = true; 0873 } 0874 } 0875 0876 if (!isset($xml->refsect1)) return $addedSomething; 0877 0878 $desc = getDocumentation($xml); 0879 0880 // file could contain function + property 0881 if (isset($xml->refsect1->classsynopsis) && isset($xml->refsect1->classsynopsis->fieldsynopsis)) { 0882 $class = (string)$xml->refsect1->classsynopsis->ooclass->classname; 0883 0884 foreach ( $xml->refsect1->classsynopsis->fieldsynopsis as $synopsis ) { 0885 newPropertyEntry($class, $synopsis->varname, $desc, $synopsis->type ); 0886 $addedSomething = true; 0887 } 0888 } 0889 if (isset($xml->refsect1->fieldsynopsis)) { 0890 $synopsis = $xml->refsect1->fieldsynopsis; 0891 $class = substr($synopsis->varname, 0, strpos($synopsis->varname, '->')); 0892 $name = substr($synopsis->varname, strpos($synopsis->varname, '->') + 2); 0893 0894 newPropertyEntry($class, $name, $desc, $synopsis->type); 0895 $addedSomething = true; 0896 } 0897 0898 if (isset($xml->refsect1->methodsynopsis)) { 0899 $skip = false; 0900 if ($funcOverload != "") { 0901 foreach( $xml->refsect1->methodsynopsis as $synopsis ) { 0902 if (isset($synopsis->methodname->replaceable)) { 0903 if ($funcOverload == (string) $synopsis->methodname->replaceable) { 0904 $skip = true; 0905 } 0906 } else { 0907 if ($funcOverload == (string) $synopsis->methodname) { 0908 $skip = true; 0909 } 0910 } 0911 } 0912 } 0913 if (!$skip) { 0914 foreach( $xml->refsect1->methodsynopsis as $synopsis ) { 0915 if (isset($synopsis->methodname->replaceable)) { 0916 newMethodEntry('global', $synopsis->methodname->replaceable, $funcOverload, $synopsis, $desc, $xml, $classOverload); 0917 } else { 0918 newMethodEntry('global', $synopsis->methodname, $funcOverload, $synopsis, $desc, $xml, $classOverload); 0919 } 0920 0921 $addedSomething = true; 0922 } 0923 } 0924 } 0925 if (isset($xml->refsect1->classsynopsis) && isset($xml->refsect1->classsynopsis->methodsynopsis)) { 0926 $methodsynopsis = $xml->refsect1->classsynopsis->methodsynopsis; 0927 $classOverloadName = $classOverload ?? $xml->refsect1->classsynopsis->ooclass->classname; 0928 if (isset($synopsis->methodname->replaceable)) { 0929 newMethodEntry($classOverloadName, $methodsynopsis->methodname->replaceable, $funcOverload, $methodsynopsis, $desc, $xml, $classOverload); 0930 } else { 0931 newMethodEntry($classOverloadName, $methodsynopsis->methodname, $funcOverload, $methodsynopsis, $desc, $xml, $classOverload); 0932 } 0933 $addedSomething = true; 0934 } 0935 if ( !$addedSomething && (isset($xml->refnamediv->refpurpose->function) || isset($xml->refnamediv->refpurpose->methodname)) ) { 0936 // This is function alias 0937 $functionNames[] = (string)$xml->refnamediv->refname; 0938 if ($xml->refnamediv->refpurpose->function) { 0939 $aliasName = (string)$xml->refnamediv->refpurpose->function; 0940 switch ($aliasName) { 0941 case 'mysqli_stmt_execute': 0942 $baseFileName = dirname($file->getPathname()).'/../mysqli_stmt/execute.xml'; 0943 $overload = "global"; 0944 break; 0945 case 'mysqli_options': 0946 $functionNames[] = 'mysqli_set_opt'; 0947 $baseFileName = dirname($file->getPathname()).'/../mysqli/options.xml'; 0948 $overload = "global"; 0949 break; 0950 case 'mysqli_real_escape_string': 0951 $baseFileName = dirname($file->getPathname()).'/../mysqli/real-escape-string.xml'; 0952 $overload = "global"; 0953 break; 0954 case 'sql_regcase': 0955 $baseFileName = dirname($file->getPathname()).'/../../regex/functions/sql-regcase.xml'; 0956 $overload = "global"; 0957 break; 0958 default: 0959 $baseFileName = dirname($file->getPathname()).'/'.str_replace('_', '-', $aliasName).'.xml'; 0960 $overload = ""; 0961 } 0962 } else { 0963 $aliasName = (string)$xml->refnamediv->refpurpose->methodname; 0964 $baseFileName = dirname($file->getPathname()).'/'.strtolower(str_replace('::', '/', $aliasName)).'.xml'; 0965 $baseFileName = str_replace('/functions/', '/', $baseFileName); 0966 $overload = "global"; 0967 } 0968 if ( $baseFileName == $file->getPathname() || !file_exists($baseFileName) ) { 0969 return false; 0970 } 0971 foreach ($functionNames as $functionName) { 0972 parseFile(new SplFileInfo($baseFileName), $functionName, $overload); 0973 } 0974 $addedSomething = true; 0975 } 0976 0977 return $addedSomething; 0978 } // end of function parseFile() 0979 0980 /** 0981 * Create a new class entry if it not exists. 0982 * 0983 * Key in $classes will be the lower-case @p $name. 0984 * The prettyName member will be @p $name, if it contains non-lowercase chars. 0985 * 0986 * Returns the lower-cased @p $name 0987 */ 0988 function newClassEntry($name) { 0989 global $classes, $isInterface, $isAbstractClass, $isFinalClass, $interfaceClasses, $abstractClasses; 0990 if (strpos($name, '\\') !== false) { 0991 $endpos = strrpos($name, '\\'); 0992 $class = substr($name, $endpos + 1); 0993 $namespace = substr($name, 0, $endpos); 0994 } else { 0995 $class = $name; 0996 $namespace = null; 0997 } 0998 // This affects OCI-Collection and OCI-Log 0999 // Technically, this creates wrong class names, but they are otherwise illegal syntax... 1000 $class = str_replace('-','',$class); 1001 $lower = strtolower($name); 1002 1003 if (!$isInterface && in_array($lower, $interfaceClasses)) { 1004 $isInterface = true; 1005 } 1006 1007 if (!$isAbstractClass && in_array($lower, $abstractClasses)) { 1008 $isAbstractClass = true; 1009 } 1010 1011 if (!isset($classes[$lower])) { 1012 $classes[$lower] = array( 1013 'functions' => array(), 1014 'properties' => array(), 1015 'namespace' => $namespace, 1016 'prettyName' => $class, 1017 'desc' => '', 1018 'isInterface' => $isInterface, 1019 'isAbstract' => $isAbstractClass, 1020 'isFinal' => $isFinalClass, 1021 ); 1022 } else { 1023 if ( $lower != $class ) { 1024 $classes[$lower]['prettyName'] = $class; 1025 } 1026 if ( $isInterface ) { 1027 $classes[$lower]['isInterface'] = true; 1028 } 1029 if ( $isAbstractClass ) { 1030 $classes[$lower]['isAbstract'] = true; 1031 } 1032 if ( $isFinalClass ) { 1033 $classes[$lower]['isFinal'] = true; 1034 } 1035 } 1036 return $lower; 1037 } 1038 1039 /** 1040 * get the documentation for an entry 1041 * @return string 1042 */ 1043 function getDocumentation(SimpleXMLElement $xml) { 1044 global $skipComments; 1045 1046 $descs = array(); 1047 1048 $purpose = $xml->refnamediv->refpurpose; 1049 1050 if (!in_array($purpose, $skipComments)) { 1051 $descs[] = $purpose; 1052 } 1053 1054 foreach ($xml->refsect1->para as $p ) { 1055 $p = removeTag($p->asXML(), 'para'); 1056 if ( stripos($p, 'procedural style') !== false || stripos($p, 'procedure style') !== false 1057 || stripos($p, 'object oriented style') !== false ) { 1058 // uninteresting 1059 continue; 1060 } 1061 if (in_array($p, $skipComments)) { 1062 continue; 1063 } 1064 if ($p == $purpose || $p == "$purpose.") { 1065 // avoid duplicate comments 1066 continue; 1067 } 1068 $descs[] = $p; 1069 } 1070 return implode("\n\n", $descs); 1071 } 1072 1073 /** 1074 * get the documentation for an entry 1075 * @return string 1076 */ 1077 function getDocumentationFromPartIntro(SimpleXMLElement $xml) { 1078 global $skipComments; 1079 1080 $descs = array(); 1081 1082 foreach ($xml->listitem->para as $p ) { 1083 $p = removeTag($p->asXML(), 'para'); 1084 if (in_array($p, $skipComments)) { 1085 continue; 1086 } 1087 $descs[] = $p; 1088 } 1089 return implode("\n\n", $descs); 1090 } 1091 1092 /** 1093 * create a new property entry for @p $class 1094 */ 1095 function newPropertyEntry($class, $name, $desc, $type, $modifiers = []) { 1096 global $classes; 1097 $class = newClassEntry($class); 1098 $classes[$class]['properties'][] = array( 1099 'name' => (string) $name, 1100 'desc' => (string) $desc, 1101 'type' => (string) $type, 1102 'modifiers' => $modifiers 1103 ); 1104 } 1105 1106 /** 1107 * create a new method entry for @p $class 1108 */ 1109 function newMethodEntry($class, $function, $funcOverload, $methodsynopsis, $desc, SimpleXMLElement $xml, $classOverload = "") { 1110 global $existingFunctions, $classes; 1111 $class = (string) $class; 1112 $function = (string) $function; 1113 $funcOverload = (string) $funcOverload; 1114 1115 if (strpos($function, '::')) { 1116 if ($classOverload == '') { 1117 $class = substr($function, 0, strpos($function, '::')); 1118 } 1119 $function = substr($function, strpos($function, '::')+2); 1120 if (strpos($funcOverload, '::')) { 1121 $class = substr($funcOverload, 0, strpos($funcOverload, '::')); 1122 $funcOverload = substr($funcOverload, strpos($funcOverload, '::')+2); 1123 } 1124 } else if (strpos($funcOverload, '::')) { 1125 $class = substr($funcOverload, 0, strpos($funcOverload, '::')); 1126 $funcOverload = substr($funcOverload, strpos($funcOverload, '::')+2); 1127 } else if (strpos($function, '->')) { 1128 if ($classOverload == '') { 1129 $class = substr($function, 0, strpos($function, '->')); 1130 } 1131 $function = substr($function, strpos($function, '->')+2); 1132 } else { 1133 if ($function == '__halt_compiler') return false; 1134 if ($function == 'exit') return false; 1135 if ($function == 'die') return false; 1136 if ($function == 'eval') return false; 1137 if ($function == 'echo') return false; 1138 if ($function == 'print') return false; 1139 if ($function == 'array') return false; 1140 if ($function == 'list') return false; 1141 if ($function == 'isset') return false; 1142 if ($function == 'unset') return false; 1143 if ($function == 'empty') return false; 1144 } 1145 1146 if (strpos($function, '-')) return false; 1147 if (strpos($class, '-')) return false; 1148 if ($function == 'isSet') return false; //todo: bug in lexer 1149 if ($function == 'clone') return false; //todo: bug in lexer 1150 if (substr($class, 0, 3) == 'DOM') $class = 'Dom'.substr($class, 3); 1151 $class = trim($class); 1152 if ($class == 'imagick') $class = 'Imagick'; 1153 if (in_array($class.'::'.($funcOverload ? $funcOverload : $function), $existingFunctions)) return false; 1154 $existingFunctions[] = $class.'::'.($funcOverload ? $funcOverload : $function); 1155 1156 $params = array(); 1157 foreach ($methodsynopsis->methodparam as $param) { 1158 $paramName = $param->parameter; 1159 if (trim($paramName) == '...') { 1160 // Add a variable name for functions taking variable arguments 1161 $paramName = '...$vararg'; 1162 } 1163 if (!trim($paramName)) continue; 1164 $paramName = str_replace('/', '', $paramName); 1165 $paramName = str_replace('-', '', $paramName); 1166 $paramName = str_replace('$', '', $paramName); 1167 $paramName = trim(trim(trim($paramName), '*'), '&'); 1168 if ($pipe_pos = strpos($paramName, '|')) $paramName = substr($paramName, 0, $pipe_pos); 1169 if (is_numeric(substr($paramName, 0, 1))) $paramName = '_'.$paramName; 1170 $params[] = array( 1171 'name' => $paramName, 1172 'type' => (string)$param->type, 1173 'isRef' => isset($param->parameter->attributes()->role) ? ($param->parameter->attributes()->role == "reference") : false, 1174 ); 1175 } 1176 // get description of params 1177 if ( $param_descs = $xml->xpath('db:refsect1[@role="parameters"]//db:varlistentry') ) { 1178 $i = 0; 1179 foreach ( $param_descs as $d ) { 1180 if ( !isset($params[$i]) ) { 1181 continue; 1182 } 1183 $paramName = (string) $d->term->parameter; 1184 $params[$i]['desc'] = ''; 1185 foreach ( $d->listitem->para as $p ) { 1186 $p = removeTag($p->asXML(), 'para'); 1187 $params[$i]['desc'] .= $p . "\n"; 1188 } 1189 ++$i; 1190 } 1191 } 1192 1193 if ($return_desc = $xml->xpath('db:refsect1[@role="returnvalues"]//db:para')) { 1194 $return_desc = removeTag($return_desc[0]->asXML(), 'para'); 1195 } else { 1196 $return_desc = ''; 1197 } 1198 1199 $class = newClassEntry($class); 1200 $classes[$class]['functions'][] = array( 1201 'name' => $funcOverload ? $funcOverload : $function, 1202 'modifiers' => (array) $methodsynopsis->modifier, 1203 'params' => $params, 1204 'type' => (string)$methodsynopsis->type, 1205 'desc' => $funcOverload ? str_replace($function, $funcOverload, $desc) : $desc, 1206 'return_desc' => $return_desc, 1207 ); 1208 } 1209 1210 /* don't add a closing ?> here, we use this file in a benchmark as well */