php/sigfiles/generate.php
author Tomas Mysik <tmysik@netbeans.org>
Mon, 12 Jun 2017 09:13:11 +0200
changeset 6391 640ddcff4333
parent 6390 73661dbdafd6
permissions -rw-r--r--
Return blacklisted methods back

No blacklisted methods right now but keep the code for possible future usage.
     1 <?php
     2 /**
     3  * This script can be used for generating PHP model for PDT.
     4  * It builds PHP functions according to the loaded extensions in running PHP,
     5  * using complementary information gathered from PHP.net documentation
     6  *
     7  * @author Michael Spector <michael@zend.com>
     8  */
     9 
    10 define("BRANCH_DIR", ""); // can be e.g. "/trunk" (do not forget slash!)
    11 define("DOC_URL", "./html/");      // PHP documentation, separate HTML files
    12 if (!is_dir(DOC_URL)) {
    13     die('Incorrect directory for separated HTML files ("./html/" expected)!');
    14 }
    15 
    16 if (version_compare(phpversion(), "5.0.0") < 0) {
    17 	die ("This script requires PHP 5.0.0 or higher!\n");
    18 }
    19 
    20 $splitFiles = true;
    21 $phpdocDir = null;
    22 
    23 $phpDir = "php";
    24 $lang = "en";
    25 
    26 // Parse arguments:
    27 $argv = $_SERVER["argv"];
    28 $argv0 = array_shift ($argv);
    29 for ($i = 0; $i < count($argv); ++$i) {
    30 	switch ($argv[$i]) {
    31 		case "-nosplit":
    32 			$splitFiles = false;
    33 			break;
    34 
    35 		case "-split":
    36 			$splitFiles = true;
    37 			break;
    38 
    39 		case "-help":
    40 			show_help();
    41 			break;
    42 
    43 		case "-lang":
    44 			++$i;
    45 			$lang = $argv[$i];
    46                         if ($lang[0] === '-') {
    47 				show_message("Invalid arg for -lang: " . $argv[$i]);
    48 				show_help();
    49                         }
    50 			break;
    51 
    52 		case "-output":
    53 			++$i;
    54 			$phpDir = $argv[$i];
    55                         if ($phpDir[0] === '-') {
    56 				show_message("Invalid arg for -output: " . $argv[$i]);
    57 				show_help();
    58                         }
    59 			break;
    60 
    61 		default:
    62 			$phpdocDir = $argv[$i];
    63 	}
    64 }
    65 
    66 if (!$phpdocDir) {
    67 	show_help();
    68 }
    69 
    70 /***************** REMOVED FUNCTIONS (START) *************************/
    71 
    72 // add these functions to $removedFunctions!
    73 
    74 if (!function_exists('ob_iconv_handler')) {
    75     function ob_iconv_handler($contents, $status) {}
    76 }
    77 if (!function_exists('ob_tidyhandler')) {
    78     function ob_tidyhandler($input, $mode = 0) {}
    79 }
    80 if (!function_exists('session_register')) {
    81     function session_register($name, $_ = null) {}
    82 }
    83 if (!function_exists('session_unregister')) {
    84     function session_unregister($name) {}
    85 }
    86 if (!function_exists('session_is_registered')) {
    87     function session_is_registered($name) {}
    88 }
    89 if (!function_exists('chroot')) {
    90     function chroot($directory) {}
    91 }
    92 
    93 /***************** REMOVED FUNCTIONS (END) *************************/
    94 
    95 $entities = parse_entities($phpdocDir, $lang);
    96 $extensions = get_loaded_extensions();
    97 $functionsDoc = parse_phpdoc_functions ($phpdocDir, $extensions, $lang);
    98 $fieldsDoc = parse_phpdoc_fields ($phpdocDir, $extensions, $lang);
    99 $classesDoc = parse_phpdoc_classes ($phpdocDir, $extensions, $lang);
   100 $constantsDoc = parse_phpdoc_constants ($phpdocDir);
   101 $removedFunctions = array(
   102     'ob_iconv_handler',
   103     'ob_tidyhandler',
   104     'session_register',
   105     'session_unregister',
   106     'session_is_registered',
   107     'chroot',
   108 );
   109 $functionBlackList = array(
   110     'oci_lob_save' => 1,
   111     'oci_lob_import' => 1,
   112     'oci_lob_size' => 1,
   113     'oci_lob_load' => 1,
   114     'oci_lob_read' => 1,
   115     'oci_lob_eof' => 1,
   116     'oci_lob_tell' => 1,
   117     'oci_lob_truncate' => 1,
   118     'oci_lob_erase' => 1,
   119     'oci_lob_flush' => 1,
   120     'ocisetbufferinglob' => 1,
   121     'ocigetbufferinglob' => 1,
   122     'oci_lob_rewind' => 1,
   123     'oci_lob_write' => 1,
   124     'oci_lob_append' => 1,
   125     'oci_lob_export' => 1,
   126     'oci_lob_seek' => 1,
   127     'oci_free_collection' => 1,
   128     'oci_collection_append' => 1,
   129     'oci_collection_element_get' => 1,
   130     'oci_collection_element_assign' => 1,
   131     'oci_collection_size' => 1,
   132     'oci_collection_max' => 1,
   133     'oci_collection_trim' => 1,
   134     'oci_collection_assign' => 1,
   135 );
   136 $methodBlackList = array(
   137 );
   138 $preferHtmlProperties = array(
   139     'mysqli',
   140 );
   141 
   142 $processedFunctions = $functionBlackList;
   143 $processedClasses = array();
   144 $processedConstants = array();
   145 
   146 @mkdir ($phpDir);
   147 
   148 if (!$splitFiles) {
   149 	begin_file_output();
   150 }
   151 foreach ($extensions as $extName) {
   152 	if ($splitFiles) {
   153 		begin_file_output();
   154 	}
   155 	print_extension (new ReflectionExtension ($extName));
   156 	if ($splitFiles) {
   157 		finish_file_output("{$phpDir}/{$extName}.php");
   158 	}
   159 }
   160 
   161 if ($splitFiles) {
   162 	begin_file_output();
   163 }
   164 $intFunctions = get_defined_functions();
   165 foreach ($intFunctions["internal"] as $intFunction) {
   166 	if (!@$processedFunctions[strtolower($intFunction)]) {
   167 		print_function (new ReflectionFunction ($intFunction));
   168 	}
   169 }
   170 
   171 $intClasses = array_merge (get_declared_classes(), get_declared_interfaces(), get_declared_traits());
   172 foreach ($intClasses as $intClass) {
   173     if (strpos($intClass, 'NetBeans_') === 0) {
   174         continue;
   175     }
   176 	if (!@$processedClasses[strtolower($intClass)]) {
   177 		print_class (new ReflectionClass ($intClass));
   178 	}
   179 }
   180 
   181 print "\n";
   182 $constants = get_defined_constants(true);
   183 $intConstants = isset($constants["internal"]) ? $constants["internal"] : array();
   184 // add magic constants:
   185 $intConstants['__FILE__'] = null;
   186 $intConstants['__LINE__'] = null;
   187 $intConstants['__CLASS__'] = null;
   188 $intConstants['__FUNCTION__'] = null;
   189 $intConstants['__METHOD__'] = null;
   190 $intConstants['__TRAIT__'] = null;
   191 if (version_compare(phpversion(), "5.3.0") >= 0) {
   192 	$intConstants['__DIR__'] = null;
   193 	$intConstants['__NAMESPACE__'] = null;
   194 }
   195 foreach ($intConstants as $name => $value) {
   196 	if (!@$processedConstants[$name]) {
   197 		print_constant ($name, $value);
   198 	}
   199 }
   200 
   201 finish_file_output("{$phpDir}/basic.php");
   202 
   203 // removed functions
   204 if ($splitFiles) {
   205     begin_file_output();
   206 }
   207 foreach ($removedFunctions as $removedFunction) {
   208 	if (!@$processedFunctions[strtolower($removedFunction)]) {
   209 		print_function (new ReflectionFunction ($removedFunction));
   210 	}
   211 }
   212 if ($splitFiles) {
   213     finish_file_output("{$phpDir}/removed.php");
   214 }
   215 
   216 
   217 // Create .list file
   218 $fp = fopen ("{$phpDir}/.list", "w");
   219 foreach (glob("{$phpDir}/*.php") as $f) {
   220 	fwrite ($fp, basename($f));
   221 	fwrite ($fp, "\n");
   222 }
   223 fclose($fp);
   224 
   225 
   226 function findVerInfo($file)
   227  {
   228     $url = DOC_URL.$file.".html";
   229     $search_for = '<p class="verinfo">';
   230     //echo "Reading $url :\n";
   231 
   232     if (!is_file($url)) {
   233         return;
   234     }
   235 
   236     $file_contents = file_get_contents($url);
   237 
   238     $start_pos = strpos($file_contents, $search_for);
   239 
   240     if ($start_pos !== 0) {
   241        $start_pos += strlen($search_for);
   242        $end_pos = strpos($file_contents, '</p>', $start_pos);
   243 
   244        if ($end_pos !== 0) {
   245           $verinfo = substr($file_contents, $start_pos, $end_pos - $start_pos);
   246           //echo "Ver. info: $verinfo\n";
   247           return $verinfo;
   248        }
   249     }
   250  }
   251 
   252 // === Functions ===
   253 /**
   254  * Makes generic key from given function name
   255  * @param name string Function name
   256  * @return string generic key
   257  */
   258 function make_funckey_from_str ($name) {
   259 	$name = str_replace ("->", "::", $name);
   260 	$name = str_replace ("()", "", $name);
   261 	$name = strtolower ($name);
   262 	return $name;
   263 }
   264 
   265 /**
   266  * Replaces all invalid charaters with '_' in PHP identifier
   267  * @param name PHP identifier
   268  * @return string PHP identifier with stripped invalid characters
   269  */
   270 function clean_php_identifier ($name) {
   271 	$name = preg_replace('/[^\$\w\_]+/', '_', $name);
   272 	return $name;
   273 }
   274 
   275 function clean_php_value($type) {
   276     $type = trim($type);
   277     $type = str_replace("&null;", "null", $type);
   278     $type = str_replace("&true;", "true", $type);
   279     $type = str_replace("&false;", "false", $type);
   280     $type = str_replace("&quot;", "", $type);
   281     $type = strip_tags($type);
   282     return $type;
   283 }
   284 
   285 /**
   286  * Makes generic key from given function reference
   287  * @param name ReflectionMethod function reference
   288  * @return string generic key
   289  */
   290 function make_funckey_from_ref ($ref) {
   291 	if ($ref instanceof ReflectionMethod) {
   292 		$funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
   293 	} else {
   294 		$funckey = strtolower($ref->getName());
   295 	}
   296 	return $funckey;
   297 }
   298 function make_property_from_ref ($ref) {
   299 	if ($ref instanceof ReflectionProperty) {
   300 		$funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
   301 	} else {
   302 		throw new Exception("Unexpected type: ".gettype($ref));
   303 	}
   304 	return $funckey;
   305 }
   306 
   307 function make_classmember_ref ($className, $memberName) {
   308 	return strtolower($className)."::".strtolower($memberName);
   309 }
   310 
   311 
   312 /**
   313  * Parses PHP documentation
   314  * @param phpdocDir string PHP.net documentation directory
   315  * @return array Function information gathered from the PHP.net documentation by parsing XML files
   316  */
   317 function parse_phpdoc_functions ($phpdocDir, $extensions, $lang) {
   318 	$xml_files = array_merge (
   319 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*.xml"),
   320 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/predefined/*/*.xml"),
   321 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*/*.xml")
   322 	);
   323 	foreach ($extensions as $extName) {
   324 		$extName = strtolower($extName);
   325 		$globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml";
   326 		$xml_files = array_merge (
   327 			$xml_files,
   328 			glob ($globPattern)
   329 		);
   330 	}
   331     $functionsDoc = array();
   332 	foreach ($xml_files as $xml_file) {
   333 		$xml = file_get_contents ($xml_file);
   334 
   335 		if (preg_match ('@<refentry.*?xml:id=["\'](.*?)["\'].*?>.*?<refname>(.*?)</refname>\s*(?:<refname>(.*?)</refname>)?.*?<refpurpose>(.*?)</refpurpose>@s', $xml, $match)) {
   336             $id = $match[1];
   337             $refnames = array($match[2], $match[3]);
   338             $phpdoc = $match[4];
   339             foreach ($refnames as $refname) {
   340                 $refname = trim($refname);
   341                 if ($refname == '') {
   342                     continue;
   343                 }
   344                 $refname = make_funckey_from_str ($refname);
   345                 if (array_key_exists($refname, $functionsDoc)) {
   346                     // already there
   347                     continue;
   348                 }
   349                 $functionsDoc[$refname] = array();
   350                 $functionsDoc[$refname]['id'] = $id;
   351                 $functionsDoc[$refname]['quickref'] = xml_to_phpdoc($phpdoc);
   352                 $functionsDoc[$refname]['deprecated'] = strpos($xml_file, "/oldaliases/") !== false;
   353 
   354                 if (preg_match ('@<refsect1\s+role=["\']description["\']>(.*?)</refsect1>@s', $xml, $match)) {
   355                     $description = $match[1];
   356                     $function_alias = null;
   357                     $parameters = null;
   358                     $has_object_style = false;
   359                     if (preg_match ('@^(.*?)<classsynopsis>.*?<classname>(.*)</classname>.*?<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>.*?</classsynopsis>(.*)$@s', $description, $match)) {
   360                         $functionsDoc[$refname]['classname'] = trim($match[2]);
   361                         $functionsDoc[$refname]['returntype'] = trim($match[3]);
   362                         $functionsDoc[$refname]['methodname'] = trim($match[4]);
   363                         $parameters = $match[5];
   364                         $description = $match[1].$match[6];
   365                         $has_object_style = true;
   366                     }
   367                     $methodsynopsis = null;
   368                     if ($refname == 'number_format') {
   369                         $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $tmp);
   370                         $match = array();
   371                         foreach ($tmp as $key => $val) {
   372                             $match[$key] = $val[count($val) - 1];
   373                         }
   374                     } else {
   375                         if (strpos($refname, '::') !== false) {
   376                             $methodsynopsis = preg_match ('@<methodsynopsis role="oop">.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
   377                         }
   378                         if (!$methodsynopsis) {
   379                             $methodsynopsis = preg_match ('@<methodsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
   380                         }
   381                         if (!$methodsynopsis) {
   382                             $methodsynopsis = preg_match ('@<constructorsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</constructorsynopsis>@s', $description, $match);
   383                         }
   384                     }
   385                     if ($methodsynopsis) {
   386                         if ($has_object_style && make_funckey_from_str($match[2]) != $refname) {
   387                             $function_alias = trim($match[2]);
   388                         } else {
   389                             $functionsDoc[$refname]['returntype'] = trim(str_replace('-', '_', $match[1])); // e.g. OCI-Collection -> OCI_Collection
   390                             $functionsDoc[$refname]['methodname'] = trim($match[2]);
   391                                                     $parameters = $match[3];
   392                         }
   393                     }
   394                     if ($parameters) {
   395                         if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
   396                             for ($i = 0; $i < count($match[0]); ++$i) {
   397                                 $parameter = array (
   398                                     'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
   399                                     'name' => clean_php_identifier(trim($match[4][$i])),
   400                                 );
   401                                 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
   402                                     $parameter['isoptional'] = true;
   403                                 }
   404                                 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
   405                                     $parameter['isreference'] = true;
   406                                 }
   407                                 if (@strlen(trim($match[5][$i]))) {
   408                                     $def = $match[5][$i];
   409                                     if ($def == '"\"') {
   410                                         $def = '"\\\\"';
   411                                     }
   412                                     $parameter['defaultvalue'] = clean_php_value($def);
   413                                                                     $parameter['isoptional'] = true;
   414                                 }
   415                                 $functionsDoc[$refname]['parameters'][] = $parameter;
   416                             }
   417                         }
   418                     }
   419                 }
   420                 if (preg_match ('@<refsect1\s+role=["\']parameters["\']>(.*?)</refsect1>@s', $xml, $match)) {
   421                     $parameters = $match[1];
   422                     if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
   423                         for ($i = 0; $i < count($match[0]); $i++) {
   424                             for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
   425                                 if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
   426                                     $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
   427                                     break;
   428                                 }
   429                             }
   430                         }
   431                     }
   432                 }
   433                 if (preg_match ('@<refsect1\s+role=["\']returnvalues["\']>(.*?)</refsect1>@s', $xml, $match)) {
   434                     $returnvalues = $match[1];
   435                     if (preg_match ('@<para>\s*(.*)</para>?@s', $returnvalues, $match)) {
   436                         $functionsDoc[$refname]['returndoc'] = preg_replace("@^Returns @", "", xml_to_phpdoc ($match[1]));
   437                     }
   438                 }
   439 
   440                 // Create information for function alias
   441                 if ($function_alias) {
   442                     $functionsDoc[$function_alias] = $functionsDoc[$refname];
   443                 }
   444             }
   445 		}
   446 	}
   447 	return $functionsDoc;
   448 }
   449 
   450 /**
   451  * Parses PHP documentation
   452  * @param phpdocDir string PHP.net documentation directory
   453  * @return array Function information gathered from the PHP.net documentation by parsing XML files
   454  */
   455 function parse_phpdoc_fields ($phpdocDir, $extensions, $lang) {
   456 	$xml_files = array();
   457 	foreach ($extensions as $extName) {
   458 		$extName = strtolower($extName);
   459 
   460 		$xml_files = array_merge (
   461 			$xml_files,
   462 			glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml"),
   463                         glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml")
   464 		);
   465 	}
   466         foreach ($xml_files as $xml_file) {
   467             $xml = file_get_contents($xml_file);
   468             if (preg_match('@<classsynopsisinfo>.*?<classname>(.*)</classname>.*?</classsynopsisinfo>@s', $xml, $matchOffset, PREG_OFFSET_CAPTURE)) {
   469                 $classname = $matchOffset[1][0];
   470                 if (preg_match_all("@<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>@s", $xml, $matchOffset,null,$matchOffset[1][1])) {
   471                     for ($index = 0; $index < count($matchOffset[2]); $index++) {
   472                         $name = $matchOffset[2][$index];
   473                         $type= $matchOffset[1][$index];
   474                         $exploded = explode("::", $name);
   475                         if (count($exploded) > 1) {
   476                             $name = $exploded[1];
   477                         }
   478                         $reference = make_classmember_ref($classname, $name);
   479                         $fieldsDoc[$reference]['field'] = $name;
   480                         $fieldsDoc[$reference]['type'] = $type;
   481                     }
   482                 }
   483             } else {
   484                 if (preg_match('@<classsynopsis>.*?<classname>(.*)</classname>.*?<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>.*?</classsynopsis>@s', $xml, $match)) {
   485                     $reference = make_classmember_ref($match[1], $match[3]);
   486                     $fieldsDoc[$reference]['field'] = $match[3];
   487                     $fieldsDoc[$reference]['type'] = $match[2];
   488                     //$fieldsDoc[$refname]['quickref'] = trim($match[3]);
   489                 }
   490             }
   491 
   492         }
   493         if (isset($fieldsDoc)) {
   494             return $fieldsDoc;
   495         }
   496         return array();
   497 }
   498 
   499 /**
   500  * Parses PHP documentation
   501  * @param phpdocDir string PHP.net documentation directory
   502  * @return array Class information gathered from the PHP.net documentation by parsing XML files
   503  */
   504 function parse_phpdoc_classes ($phpdocDir, $extensions, $lang) {
   505 	$xml_files = array_merge (
   506 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/reference.xml"),
   507 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/classes.xml"),
   508 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*/*.xml"),
   509 		glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*.xml")
   510 	);
   511 	foreach ($extensions as $extName) {
   512 		$extName = strtolower($extName);
   513 		$globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml";
   514 		$xml_files = array_merge (
   515 			$xml_files,
   516 			glob ($globPattern)
   517 		);
   518 	}
   519 
   520     $classesDoc = array();
   521 	foreach ($xml_files as $xml_file) {
   522 		$xml = file_get_contents ($xml_file);
   523 		if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
   524 			$id = $match[1];
   525 			$prefixId = substr($id, 0, strlen("class."));
   526 			$clsNamePattern = ($prefixId === "class.") ?
   527 			'@<ooclass><classname>(.*?)</classname></ooclass>@' :
   528 			'@<title><classname>(.*?)</classname></title>@';
   529 			if (preg_match_all ($clsNamePattern, $xml, $match)) {
   530 				for ($i = 0; $i < count($match[0]); ++$i) {
   531 					$class = $match[1][$i];
   532 					$refname = strtolower ($class);
   533 					$classesDoc[$refname]['id'] = $id;
   534 					$classesDoc[$refname]['name'] = $class;
   535 					$offsetPattern = ($prefixId === "class.") ?
   536 						"@xml:id=[\"'](.*?)\.intro[\"']@i" :
   537 						"@<title><classname>{$class}</classname></title>@";
   538 					if (preg_match ($offsetPattern , $xml, $matchOffset,PREG_OFFSET_CAPTURE)) {
   539 						$docPattern = '@<para>(.*?)</para>@s';
   540 						if (preg_match ($docPattern, $xml, $match2,null,$matchOffset[0][1])) {
   541 							$doc = xml_to_phpdoc($match2[1]);
   542 							$classesDoc[$refname]['doc'] = $doc;
   543 						}
   544 					}
   545 				}
   546 			}
   547 		}
   548 	}
   549 	return $classesDoc;
   550 }
   551 
   552 /**
   553  * Parses PHP documentation
   554  * @param phpdocDir string PHP.net documentation directory
   555  * @return array Constant information gathered from the PHP.net documentation by parsing XML files
   556  */
   557 function parse_phpdoc_constants ($phpdocDir) {
   558 	exec ("find ".addslashes($phpdocDir)." -name \"*constants.xml\"", $xml_files);
   559 	foreach ($xml_files as $xml_file) {
   560 		$xml = file_get_contents ($xml_file);
   561 
   562 		if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
   563 			$id = $match[1];
   564 			if (preg_match_all ('@<term>\s*<constant>([a-zA-Z_:][a-zA-Z0-9_:]*)</constant>.*?</term>.*?<listitem>(.*?)</listitem>@s', $xml, $match)) {
   565 				for ($i = 0; $i < count($match[0]); ++$i) {
   566 					$constant = $match[1][$i];
   567 					$constantsDoc[$constant]['id'] = $id;
   568 					$constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
   569 				}
   570 			}
   571 			if (preg_match_all (
   572 				'@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>\d+</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)
   573 				|| preg_match_all ('@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)) {
   574 
   575 				for ($i = 0; $i < count($match[0]); ++$i) {
   576 					$constant = $match[1][$i];
   577 					$constantsDoc[$constant]['id'] = $id;
   578 					$constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
   579 				}
   580 			}
   581 		}
   582 	}
   583 	return $constantsDoc;
   584 }
   585 
   586 /**
   587  * Prints ReflectionExtension in format of PHP code
   588  * @param extRef ReflectionExtension object
   589  */
   590 function print_extension ($extRef) {
   591     global $functionBlackList;
   592 
   593 	print "\n// Start of {$extRef->getName()} v.{$extRef->getVersion()}\n";
   594 
   595 	// process classes:
   596 	$classesRef = $extRef->getClasses();
   597 	if (count ($classesRef) > 0) {
   598 		foreach ($classesRef as $classRef) {
   599 			print_class ($classRef);
   600 		}
   601 	}
   602 
   603 	// process functions
   604 	$funcsRef = $extRef->getFunctions();
   605 	if (count ($funcsRef) > 0) {
   606 		foreach ($funcsRef as $funcName => $funcRef) {
   607                     if (array_key_exists($funcName, $functionBlackList)) {
   608                         continue;
   609                     }
   610 			print_function ($funcRef);
   611 		}
   612 		print "\n";
   613 	}
   614 
   615 	// process constants
   616 	$constsRef = $extRef->getConstants();
   617 	if (count ($constsRef) > 0) {
   618 		print_constants ($constsRef);
   619 		print "\n";
   620 	}
   621 
   622 	print "// End of {$extRef->getName()} v.{$extRef->getVersion()}\n";
   623 }
   624 
   625 /**
   626  * Prints ReflectionClass in format of PHP code
   627  * @param classRef ReflectionClass object
   628  * @param tabs integer[optional] number of tabs for indentation
   629  */
   630 function print_class (ReflectionClass $classRef, $tabs = 0) {
   631 	global $processedClasses, $preferHtmlProperties, $methodBlackList;
   632 	$processedClasses [strtolower($classRef->getName())] = true;
   633 
   634 	print "\n";
   635 	print_doccomment ($classRef, $tabs);
   636 	print_tabs ($tabs);
   637 	if ($classRef->isFinal()) print "final ";
   638 
   639         if ($classRef->isInterface()) {
   640             print "interface ";
   641         } elseif ($classRef->isTrait()) {
   642             print "trait ";
   643         } else {
   644             if ($classRef->isAbstract()) {
   645                 print "abstract ";
   646             }
   647             print "class ";
   648         }
   649 	print clean_php_identifier($classRef->getName())." ";
   650 
   651 	// print out parent class
   652 	$parentClassRef = $classRef->getParentClass();
   653 	if ($parentClassRef) {
   654 		print "extends {$parentClassRef->getName()} ";
   655 	}
   656 
   657 	// print out interfaces
   658 	$interfacesRef = $classRef->getInterfaces();
   659 	if (count ($interfacesRef) > 0) {
   660 		print $classRef->isInterface() ? "extends " : "implements ";
   661 		$i = 0;
   662 		foreach ($interfacesRef as $interfaceRef) {
   663 			if ($i++ > 0) {
   664 				print ", ";
   665 			}
   666 			print "{$interfaceRef->getName()}";
   667 		}
   668 	}
   669 	print " {\n";
   670 
   671 	// print out traits
   672         $traits = $classRef->getTraits();
   673         if (count($traits)) {
   674             foreach ($traits as $trait => $traitInfo) {
   675                 print_tabs($tabs + 1);
   676                 print 'use ' . $trait . ';';
   677 		print "\n";
   678             }
   679             print "\n";
   680         }
   681 
   682 	// process constants
   683 	$constsRef = $classRef->getConstants();
   684 	if (count ($constsRef) > 0) {
   685 		print_class_constants ($classRef, $constsRef, $tabs + 1);
   686 		print "\n";
   687 	}
   688 
   689 	// process properties
   690 	$propertiesRef = $classRef->getProperties();
   691 	if (!in_array(strtolower($classRef->getName()), $preferHtmlProperties)
   692                 && count ($propertiesRef) > 0) {
   693 		foreach ($propertiesRef as $propertyRef) {
   694 			print_property ($propertyRef, $tabs + 1);
   695 		}
   696 		print "\n";
   697 	} else {
   698         // #188245 - try to find them directly in HTML
   699         $properties = parse_properties_from_html_file('class.' . strtolower($classRef->getName()) . '.html', $classRef->getName());
   700         if (count($properties)) {
   701             foreach ($properties as $property) {
   702                 print_field($property, $tabs + 1);
   703             }
   704             print "\n";
   705         }
   706     }
   707 
   708 	// process methods
   709 	/* @var $classRef ReflectionClass */
   710 	$methodsRef = $classRef->getMethods();
   711 	if (count ($methodsRef) > 0) {
   712 		foreach ($methodsRef as $methodRef) {
   713             /* @var $methodRef ReflectionMethod */
   714             if (in_array(strtolower($methodRef->getName()), $methodBlackList)) {
   715                 continue;
   716             }
   717 			print_method($classRef, $methodRef, $tabs + 1);
   718 		}
   719 		print "\n";
   720 	}
   721 	print_tabs ($tabs);
   722 	print "}\n";
   723 }
   724 
   725 /**
   726  * Prints ReflectionProperty in format of PHP code
   727  * @param ReflectionProperty $propertyRef  object
   728  * @param integer[optional] tabs  number of tabs for indentation
   729  */
   730 function print_property ($propertyRef, $tabs = 0) {
   731 	print_doccomment ($propertyRef, $tabs);
   732 	print_tabs ($tabs);
   733 	print_modifiers ($propertyRef, true);
   734         $name = $propertyRef->getName();
   735         if (substr($name, 0, 1) !== '$') {
   736             $name = '$' . $name;
   737         }
   738         print $name . ";\n";
   739 }
   740 
   741 /**
   742  * Prints Field in format of PHP code
   743  * @param NetBeans_Field $field  object
   744  * @param integer[optional] tabs  number of tabs for indentation
   745  */
   746 function print_field (NetBeans_Field $field, $tabs = 0) {
   747     // documentation
   748     $fieldType = $field->getType();
   749     $fieldDoc = $field->getDocumentation();
   750     $fieldDoc = trim(strip_tags($fieldDoc, '<p>,<strong>,<code>,<a>'));
   751     // replace hyperlinks
   752     $fieldDoc = preg_replace('%<(a|strong)[^>]*>%', '<b>', $fieldDoc);
   753     $fieldDoc = preg_replace('%<p[^>]+>%', '<p>', $fieldDoc);
   754     $fieldDoc = str_replace('</a>', '</b>', $fieldDoc);
   755     $fieldDoc = str_replace('</strong>', '</b>', $fieldDoc);
   756     $fieldDoc = preg_replace('%^<p>(.+)</p>$%s', '<p style="margin-top:0;">\\1</p>', $fieldDoc);
   757 
   758     print_tabs ($tabs);
   759     print "/**\n";
   760     foreach (preg_split('%\n|\r%', $fieldDoc) as $line) {
   761         $line = trim($line);
   762         if (!$line) {
   763             continue;
   764         }
   765         print_tabs($tabs);
   766         print " * $line\n";
   767     }
   768     print_tabs($tabs);
   769     print " * @var $fieldType\n";
   770     print_tabs($tabs);
   771     print " */\n";
   772     // tabs
   773 	print_tabs($tabs);
   774     // modifiers
   775     $print = implode(' ', $field->getModifiers());
   776     $print = str_replace("final", "", $print);
   777     $print = str_replace("abstract", "", $print);
   778     $print = str_replace("readonly", "", $print);
   779     $print = trim($print);
   780     if (!$print) {
   781         // no modifiers
   782         $print = 'public';
   783     }
   784     print $print;
   785     print " ";
   786     $name = $field->getName();
   787     if (substr($name, 0, 1) !== '$') {
   788         $name = '$' . $name;
   789     }
   790     print $name . ";\n";
   791 }
   792 
   793 function print_function ($functionRef, $tabs = 0) {
   794     print_method(null, $functionRef, $tabs);
   795 }
   796 
   797 function print_method ($classRef, $functionRef, $tabs = 0) {
   798 	global $functionsDoc;
   799 	global $processedFunctions;
   800 
   801 	$funckey = make_funckey_from_ref ($functionRef);
   802 	$processedFunctions[$funckey] = true;
   803 
   804 	print "\n";
   805         $modifiers = null;
   806 	print_doccomment ($functionRef, $tabs);
   807 	print_tabs ($tabs);
   808 	if (!($functionRef instanceof ReflectionFunction)) {
   809 		print_modifiers ($functionRef);
   810                 $modifiers = Reflection::getModifierNames($functionRef->getModifiers());
   811 	}
   812 
   813 	print "function ";
   814 	if ($functionRef->returnsReference()) {
   815 		print "&";
   816 	}
   817 	$functionName = $functionRef->getName();
   818 	print "$functionName(";
   819 	$parameters = @$functionsDoc[$funckey]['parameters'];
   820 	if ($parameters) {
   821 		print_parameters ($parameters);
   822 	} else {
   823 		print_parameters_ref ($functionRef->getParameters());
   824 	}
   825 	print ")";
   826         $returntype = sanitizeType(@$functionsDoc[$funckey]['returntype']);
   827         if ($returntype
   828                 && $functionName !== '__construct') {
   829             print ': ' . $returntype;
   830         }
   831         $body = true;
   832         if ($classRef != null && $classRef->isInterface()) {
   833             $body = false;
   834         } elseif (is_array($modifiers)) {
   835             foreach ($modifiers as $modifier) {
   836                 if ($modifier == "abstract") {
   837                     $body = false;
   838                     break;
   839                 }
   840             }
   841         }
   842         if ($body) {
   843             print " {}";
   844         } else {
   845             print ";";
   846         }
   847 	print "\n";
   848 }
   849 
   850 
   851 /**
   852  * Prints ReflectionParameter in format of PHP code
   853  * @param parameters array information from PHP.net documentation
   854  */
   855 function print_parameters ($parameters) {
   856 	$i = 0;
   857 	foreach ($parameters as $parameter) {
   858 		if ($parameter['name'] != "...") {
   859 			if ($i++ > 0) {
   860 				print ", ";
   861 			}
   862                         $type = sanitizeType($parameter['type']);
   863 			if ($type) {
   864 				print "{$type} ";
   865 			}
   866 			if (@$parameter['isreference']) {
   867 				print "&";
   868 			}
   869                         print "\${$parameter['name']}";
   870 			if (@$parameter['isoptional']) {
   871 				if (array_key_exists('defaultvalue', $parameter)) {
   872 					$value = $parameter['defaultvalue'];
   873                                         if ((is_numeric ($value) && $type != 'string')
   874                                                 || in_array(strtolower($value), array('true', 'false', 'null', 'array()'))
   875                                                 || (substr($value, 0, 1) == '\'' && substr($value, -1) == '\'')
   876                                                 || (substr($value, 0, 1) == '"' && substr($value, -1) == '"')
   877                                                 || (substr($value, 0, 2) == '__' && substr($value, -2) == '__')
   878                                                 || isConstant($value)) {
   879                                             // no apostrophes
   880                                         } else {
   881                                             $value = "'{$value}'";
   882                                         }
   883                                         print " = {$value}";
   884 				} else {
   885                                         print " = null";
   886 				}
   887 			}
   888 
   889 		}
   890 	}
   891 }
   892 
   893 /**
   894  * Prints ReflectionParameter in format of PHP code
   895  * @param paramsRef ReflectionParameter[] array of objects
   896  */
   897 function print_parameters_ref ($paramsRef) {
   898 	$i = 0;
   899 	foreach ($paramsRef as $paramRef) {
   900             /* @var $paramRef ReflectionParameter */
   901             if ($paramRef->allowsNull()) {
   902                 echo '?';
   903             }
   904 		if ($paramRef->isArray()) {
   905 			print "array ";
   906 		} else {
   907 			if ($className = get_parameter_classname($paramRef)) {
   908 				print "{$className} ";
   909 			}
   910 		}
   911 		$name = $paramRef->getName() ? $paramRef->getName() : "var".($i+1);
   912 		if ($name != "...") {
   913 			if ($i++ > 0) {
   914 				print ", ";
   915 			}
   916 			if ($paramRef->isPassedByReference()) {
   917 				print "&";
   918 			}
   919 			print "\${$name}";
   920 			if ($paramRef->allowsNull()) {
   921 				print " = null";
   922 			} else if ($paramRef->isDefaultValueAvailable()) {
   923 				$value = $paramRef->getDefaultValue();
   924 				if (!is_numeric ($value)) {
   925 					$value = "'{$value}'";
   926 				}
   927 				print " = {$value}";
   928 			}
   929 		}
   930 	}
   931 }
   932 
   933 /**
   934  * Prints constants in format of PHP code
   935  * @param constants array containing constants, where key is a name of constant
   936  * @param tabs integer[optional] number of tabs for indentation
   937  */
   938 function print_constants ($constants, $tabs = 0) {
   939 	foreach ($constants as $name => $value) {
   940 		print_constant ($name, $value, $tabs);
   941 	}
   942 }
   943 
   944 function print_constant ($name, $value = null, $tabs = 0) {
   945 	global $constantsDoc;
   946 	global $processedConstants;
   947 	$processedConstants [$name] = true;
   948 
   949 	if ($value === null) {
   950 		$value = @constant ($name);
   951 	}
   952 	$value = escape_const_value ($value);
   953 
   954 	$doc = @$constantsDoc[$name]['doc'];
   955 	if ($doc) {
   956 		print "\n";
   957 		print_tabs ($tabs);
   958 		print "/**\n";
   959 		print_tabs ($tabs);
   960 		print " * ".newline_to_phpdoc($doc, $tabs)."\n";
   961 		print_tabs ($tabs);
   962 		print " * @link ".make_url($constantsDoc[$name]['id'])."\n";
   963 		print_tabs ($tabs);
   964 		print " */\n";
   965 	}
   966 	print_tabs ($tabs);
   967 	print "define ('{$name}', {$value});\n";
   968 }
   969 
   970 function escape_const_value ($value) {
   971 	if (is_resource($value)) {
   972 		$value = "\"${value}\"";
   973 	} else if (!is_numeric ($value) && !is_bool ($value) && $value !== null) {
   974 		$value = '"'.addcslashes ($value, "\"\r\n\t").'"';
   975 	} else if ($value === null) {
   976 		$value = "null";
   977 	} else if ($value === false) {
   978 		$value = "false";
   979 	} else if ($value === true) {
   980 		$value = "true";
   981 	}
   982 	return $value;
   983 }
   984 
   985 /**
   986  * Prints class constants in format of PHP code
   987  * @param constants array containing constants, where key is a name of constant
   988  * @param tabs integer[optional] number of tabs for indentation
   989  */
   990 function print_class_constants ($classRef, $constants, $tabs = 0) {
   991     global $constantsDoc;
   992     global $processedConstants;
   993 
   994 
   995     //$doc = @$constantsDoc[$name]['doc'];
   996     foreach ($constants as $name => $value) {
   997         $value = escape_const_value ($value);
   998         $clsName = $classRef->getName();
   999         $idx = "$clsName::$name";
  1000         $doc = @$constantsDoc[$idx]['doc'];
  1001         if ($doc) {
  1002             print "\n";
  1003             print_tabs ($tabs);
  1004             print "/**\n";
  1005             print_tabs ($tabs);
  1006             print " * ".newline_to_phpdoc($doc, $tabs)."\n";
  1007             print_tabs ($tabs);
  1008             print " * @link ".make_url($constantsDoc[$idx]['id'])."\n";
  1009             print_tabs ($tabs);
  1010             print " */\n";
  1011         }
  1012         print_tabs ($tabs);
  1013         print "const {$name} = {$value};\n";
  1014     }
  1015 }
  1016 
  1017 /**
  1018  * Prints modifiers of reflection object in format of PHP code
  1019  * @param ref Reflection some reflection object
  1020  */
  1021 function print_modifiers ($ref, $forFields = false) {
  1022 	$modifiers = Reflection::getModifierNames ($ref->getModifiers());
  1023 	if (count ($modifiers) > 0) {
  1024                 $print = implode(' ', $modifiers);
  1025                 if ($forFields) {
  1026                     $print = str_replace("final", "", $print);
  1027                     $print = str_replace("abstract", "", $print);
  1028                     $print = str_replace("readonly", "", $print);
  1029                 }
  1030 		print trim($print);
  1031 		print " ";
  1032 	}
  1033 }
  1034 
  1035 /**
  1036  * Makes PHP Manual URL from the given ID
  1037  * @param id PHP Element ID
  1038  * @return URL
  1039  */
  1040 function make_url ($id) {
  1041 	global $lang;
  1042 	return "http://php.net/manual/{$lang}/{$id}.php";
  1043 }
  1044 
  1045 /**
  1046  * Prints PHPDOC comment before specified reflection object
  1047  * @param ref Reflection some reflection object
  1048  * @param tabs integer[optional] number of tabs for indentation
  1049  */
  1050 function print_doccomment ($ref, $tabs = 0) {
  1051 	global $functionsDoc;
  1052 	global $classesDoc;
  1053         global $fieldsDoc;
  1054         global $methodBlackList;
  1055 
  1056 	$docComment = $ref->getDocComment();
  1057 	if ($docComment) {
  1058 		print_tabs ($tabs);
  1059 		print "{$docComment}\n";
  1060 	}
  1061 	else if ($ref instanceof ReflectionClass) {
  1062 		$refname = strtolower($ref->getName());
  1063 		if (@$classesDoc[$refname]) {
  1064 			print_tabs ($tabs);
  1065 			print "/**\n";
  1066 			$doc = @$classesDoc[$refname]['doc'];
  1067 			if ($doc) {
  1068 				$doc = newline_to_phpdoc ($doc, $tabs);
  1069 				print_tabs ($tabs);
  1070 				print " * {$doc}\n";
  1071 			}
  1072                         // @method
  1073                         foreach ($ref->getMethods() as $method) {
  1074                             if (in_array(strtolower($method->getName()), $methodBlackList)) {
  1075                                 print_magic_method($ref, $method, $tabs);
  1076                             }
  1077                         }
  1078 			if (@$classesDoc[$refname]['id']) {
  1079 				print_Tabs ($tabs);
  1080 				$url = make_url ($classesDoc[$refname]['id']);
  1081 				print " * @link {$url}\n";
  1082 			}
  1083 			print_tabs ($tabs);
  1084 			print " */\n";
  1085 		}
  1086 	}
  1087 	else if ($ref instanceof ReflectionFunctionAbstract) {
  1088 		$funckey = make_funckey_from_ref ($ref);
  1089                 $id = @$functionsDoc[$funckey]['id'];
  1090                 $ver_info = findVerInfo($id);
  1091                 $desc = @$functionsDoc[$funckey]['quickref'];
  1092 		$returntype = "";
  1093                 $returndoc = "";
  1094                 if (strpos($funckey, "::__construct") === false) {
  1095                     $returntype = @$functionsDoc[$funckey]['returntype'];
  1096                     $returndoc = newline_to_phpdoc (@$functionsDoc[$funckey]['returndoc'], $tabs);
  1097                 }
  1098 
  1099 		$paramsRef = $ref->getParameters();
  1100 		$parameters = @$functionsDoc[$funckey]['parameters'];
  1101 
  1102 		if ($desc || count ($paramsRef) > 0 || $parameters || $returntype) {
  1103 			print_tabs ($tabs);
  1104 			print "/**\n";
  1105                         if($ver_info) {
  1106                             print_tabs ($tabs);
  1107                             print " * {$ver_info}<br/>\n";
  1108                         }
  1109                         if ($desc) {
  1110 				print_tabs ($tabs);
  1111 				print " * {$desc}\n";
  1112 			}
  1113 			if (@$functionsDoc[$funckey]['id']) {
  1114 				print_tabs ($tabs);
  1115 				$url = make_url ($functionsDoc[$funckey]['id']);
  1116 				print " * @link {$url}\n";
  1117 			}
  1118                         if (!@$functionsDoc[$funckey]['deprecated']) {
  1119                             if($parameters) {
  1120                                 foreach ($parameters as $parameter) {
  1121                                     print_tabs($tabs);
  1122                                     print " * @param {$parameter['type']} \${$parameter['name']}";
  1123                                     if (@$parameter['isoptional']) {
  1124                                         print " [optional]";
  1125                                     }
  1126                                     $paramdoc = @$parameter['paramdoc'];
  1127                                     if ($paramdoc && $paramdoc != "<p>\n</p>") {
  1128                                         $paramdoc = newline_to_phpdoc(@$parameter['paramdoc'], $tabs);
  1129                                         print " {$paramdoc}";
  1130                                     }
  1131                                     print "\n";
  1132                                 }
  1133                             } else {
  1134                                 $i = 0;
  1135                                 foreach ($paramsRef as $paramRef) {
  1136                                     print_tabs($tabs);
  1137                                     $name = $paramRef->getName() ? $paramRef->getName() : "var".++$i;
  1138                                     print " * @param";
  1139                                     if($className = get_parameter_classname($paramRef)) {
  1140                                         print " {$className}";
  1141                                         if($paramRef->isArray()) {
  1142                                             print "[]";
  1143                                         }
  1144                                     }
  1145                                     print " \${$name}";
  1146                                     if($paramRef->isOptional()) {
  1147                                         print " [optional]";
  1148                                     }
  1149                                     print "\n";
  1150                                 }
  1151                             }
  1152                             if ($returntype || $returndoc) {
  1153                                 if (!$returntype) {
  1154                                     $returntype = 'mixed';
  1155                                 }
  1156                                     print_tabs ($tabs);
  1157                                     print " * @return " . trim("{$returntype} {$returndoc}") . "\n";
  1158                             }
  1159                         }
  1160 			print_tabs ($tabs);
  1161 			print " */\n";
  1162 		}
  1163 	}else if ($ref instanceof ReflectionProperty) {
  1164             $property_from_ref = make_property_from_ref($ref);
  1165             $fieldName = @$fieldsDoc[$property_from_ref]['field'];
  1166             $fieldType = @$fieldsDoc[$property_from_ref]['type'];
  1167             if (isset ($fieldName) && isset ($fieldType)) {
  1168                 print_tabs ($tabs);
  1169                 print "/**\n";
  1170                 print_tabs ($tabs);
  1171                 print " * @var $fieldType\n";
  1172                 print_tabs ($tabs);
  1173                 print " */\n";
  1174             }
  1175         }
  1176 }
  1177 
  1178 function print_magic_method(ReflectionClass $classRef, ReflectionMethod $methodRef, $tabs = 0) {
  1179     global $functionsDoc;
  1180     global $processedFunctions;
  1181 
  1182     $funckey = make_funckey_from_ref($methodRef);
  1183     $processedFunctions[$funckey] = true;
  1184 
  1185     print_tabs($tabs);
  1186     print " * @method ";
  1187     $returntype = @$functionsDoc[$funckey]['returntype'];
  1188     $returndoc = @$functionsDoc[$funckey]['returndoc'];
  1189     if ($returntype
  1190             || $returndoc) {
  1191         if (!$returntype) {
  1192             $returntype = 'mixed';
  1193         }
  1194         print $returntype . " ";
  1195     }
  1196     print $methodRef->getName() . "(";
  1197     $parameters = @$functionsDoc[$funckey]['parameters'];
  1198     if ($parameters) {
  1199         print_parameters($parameters);
  1200     } else {
  1201         print_parameters_ref($methodRef->getParameters());
  1202     }
  1203     print ")";
  1204     $id = @$functionsDoc[$funckey]['id'];
  1205     $ver_info = findVerInfo($id);
  1206     $docComment = @$functionsDoc[$funckey]['quickref'];
  1207     if ($ver_info
  1208             || $docComment) {
  1209         print " ";
  1210         if ($ver_info) {
  1211             print $ver_info;
  1212         }
  1213         if ($docComment) {
  1214             if ($ver_info) {
  1215                 print "<br/>";
  1216             }
  1217             print $docComment;
  1218         }
  1219     }
  1220     print "\n";
  1221 }
  1222 
  1223 /**
  1224  * Converts XML entities to human readable string for PHPDOC
  1225  * @param str string
  1226  * @return string
  1227  */
  1228 function xml_to_phpdoc ($str) {
  1229     $str = str_replace ("&php.ini;", "###(i)###php.ini###(/i)###", $str); // XXX will be replaced in strip_tags_special()
  1230     $str = replace_entities($str);
  1231         $str = strip_tags_special ($str);
  1232 	$str = preg_replace ("/  */", " ", $str);
  1233 	$str = str_replace ("*/", "* /", $str);
  1234 	$str = str_replace ("“", "&quot;", $str);
  1235 	$str = str_replace ("”", "&quot;", $str);
  1236 	$str = preg_replace ("/[\r\n][\t ]/", "\n", $str);
  1237         $str = trim($str);
  1238 	return $str;
  1239 }
  1240 
  1241 /**
  1242  * Converts newlines to PHPDOC prefixes in the given string
  1243  * @param str string
  1244  * @param tabs integer[optional] number of tabs for indentation
  1245  * @return string PHPDOC string
  1246  */
  1247 function newline_to_phpdoc ($str, $tabs = 0) {
  1248 	$str = preg_replace ("@\s*[\r\n]+@", "\n".str_repeat("\t", $tabs)." * ", $str);
  1249 	return $str;
  1250 }
  1251 
  1252 /**
  1253  * Prints specified number of tabs
  1254  * @param tabs integer number of tabs to print
  1255  */
  1256 function print_tabs ($tabs) {
  1257 	print str_repeat("\t", $tabs);
  1258 }
  1259 
  1260 /**
  1261  * Returns class name from given parameter reference, this method is a workaround
  1262  * for the case when exception is thrown from getClass() when such classname does not exist.
  1263  */
  1264 function get_parameter_classname(ReflectionParameter $paramRef) {
  1265 	try {
  1266 		if ($classRef = $paramRef->getClass()) {
  1267 			return $classRef->getName();
  1268 		}
  1269 	} catch (Exception $e) {
  1270 		if (preg_match('/Class (\w+) does not exist/', $e->getMessage(), $matches)) {
  1271 			return $matches[1];
  1272 		}
  1273 	}
  1274 	return null;
  1275 }
  1276 
  1277 /**
  1278  * Starts outputing to the new file
  1279  */
  1280 function begin_file_output() {
  1281 	ob_start();
  1282 	print "<?php\n";
  1283 }
  1284 
  1285 /**
  1286  * Ends outputing, and dumps the output to the specified file
  1287  * @param filename File to dump the output
  1288  */
  1289 function finish_file_output($filename) {
  1290 	//if (file_exists ($filename)) {
  1291 	//	rename ($filename, "{$filename}.bak");
  1292 	//}
  1293 	print "?>\n";
  1294 	file_put_contents (str_replace(" ", "-", $filename), ob_get_contents());
  1295 	ob_end_clean();
  1296 }
  1297 
  1298 /**
  1299  * Strips xml tags from the string like the standard strip_tags() function
  1300  * would do, but also translates some of the docbook tags (such as tables
  1301  * an paragraphs) to proper html tags
  1302  * @param str string
  1303  * @return string
  1304  */
  1305 function strip_tags_special ($str) {
  1306     // methodsynopsis
  1307 //    $str = method_to_phpdoc($str);
  1308     // first mask and translate the tags to preseve
  1309     $str = preg_replace ("/<(\/?)table>/", "###($1table)###", $str);
  1310     $str = str_replace ("<row>", "###(tr valign=\"top\")###", $str);
  1311     $str = str_replace ("</row>", "###(/tr)###", $str);
  1312     $str = preg_replace ("/<(\/?)entry>/", "###($1td)###", $str);
  1313     $str = preg_replace ("/<(\/?)para>/", "###($1p)###", $str);
  1314     $str = preg_replace ("/<(\/?)p>/", "###($1p)###", $str);
  1315     // remove cdata
  1316     $str = str_replace ("<![CDATA[", "###(pre)###", $str);
  1317     $str = str_replace ("]]>", "###(/pre)###", $str);
  1318     // preserve php samples; XXX sample for preg_match_all
  1319     $str = str_replace ("<?php", "###(code)###", $str);
  1320     $str = str_replace ("?>", "###(/code)###", $str);
  1321     // handle "<pre><code>"
  1322     $str = preg_replace ("/###\(pre\)###\s*\n\s*###\(code\)###/", "###(code)###", $str);
  1323     $str = preg_replace ("/###\(\/code\)###\s*\n\s*###\(\/pre\)###/", "###(/code)###", $str);
  1324     // constant & function etc.
  1325     $str = preg_replace ("%<(/)?(constant|function|classname|methodname|methodparam)[^>]*>%", "###(\\1b)###", $str);
  1326     $str = preg_replace ("%<(/)?(parameter)[^>]*>%", "###(\\1i)###", $str);
  1327     // now strip the remaining tags
  1328     $str = strip_tags ($str);
  1329     // and restore the translated ones
  1330     $str = str_replace ("###(", "<", $str);
  1331     $str = str_replace (")###", ">", $str);
  1332     return $str;
  1333 }
  1334 
  1335 // XXX, see set_error_handler
  1336 function method_to_phpdoc($str) {
  1337     $tmp = array();
  1338     $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $str, $tmp);
  1339     if (!$methodsynopsis) {
  1340         return $str;
  1341     }
  1342     $functionsDoc = array();
  1343     $parameters = null;
  1344     for ($i = 0; $i < count($tmp); ++$i) {
  1345         $refname = trim($tmp[2][$i]);
  1346         $functionsDoc[$refname]['methodname'] = $refname;
  1347         $parameters = $tmp[3][$i];
  1348         if ($parameters) {
  1349                 if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
  1350                         for ($i = 0; $i < count($match[0]); ++$i) {
  1351                                 $parameter = array (
  1352                                         'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
  1353                                         'name' => clean_php_identifier(trim($match[4][$i])),
  1354                                 );
  1355                                 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
  1356                                         $parameter['isoptional'] = true;
  1357                                 }
  1358                                 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
  1359                                         $parameter['isreference'] = true;
  1360                                 }
  1361                                 if (@strlen(trim($match[5][$i]))) {
  1362                                         $parameter['defaultvalue'] = clean_php_value($match[5][$i]);
  1363                                         $parameter['isoptional'] = true;
  1364                                 }
  1365                                 $functionsDoc[$refname]['parameters'][] = $parameter;
  1366                         }
  1367                 }
  1368                 if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
  1369                     for ($i = 0; $i < count($match[0]); $i++) {
  1370                         for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
  1371                             if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
  1372                                 $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
  1373                                 break;
  1374                             }
  1375                         }
  1376                     }
  1377                 }
  1378         }
  1379     }
  1380     return $str;
  1381 }
  1382 
  1383 function parse_entities($phpdocDir, $lang) {
  1384     $entities = array();
  1385     parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-defs.ent");
  1386     parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-snippets.ent");
  1387     parse_entities_from_file($entities, $phpdocDir, '/doc-base/docbook/docbook-xml/ent/isonum.ent');
  1388     parse_entities_from_file($entities, $phpdocDir, '/doc-base/entities/global.ent');
  1389     return $entities;
  1390 }
  1391 function parse_entities_from_file(array &$entities, $phpdocDir, $filepath) {
  1392     $content = file_get_contents($phpdocDir . $filepath);
  1393     $matches = array();
  1394     preg_match_all('%\!ENTITY\s+(\S+)\s+([\'"])([^\\2]+?)\\2\s*>%m', $content, $matches);
  1395     if (array_key_exists(1, $matches) && count($matches[1])) {
  1396         for ($i = 0; $i < count($matches[2]); $i++) {
  1397             $entities['&' . $matches[1][$i] . ';'] = $matches[3][$i];
  1398         }
  1399     }
  1400 }
  1401 function replace_entities($text) {
  1402     global $entities;
  1403     $matches = array();
  1404     while (preg_match_all('%(\&(?!#)\S+?\;)%', $text, $matches)) {
  1405         if (count($matches[1])) {
  1406             foreach ($matches[1] as $e) {
  1407                 $replace = null;
  1408                 if (array_key_exists($e, $entities)) {
  1409                     $replace = $entities[$e];
  1410                 }
  1411                 if ($replace === null) {
  1412                     switch ($e) {
  1413                         case '&$a));':
  1414                             // code sample
  1415                         case '&reference.strings.charsets;':
  1416                         case '&reference.intl.inctimezoneparam;':
  1417                         case '&reference.intl.incfieldparam;':
  1418                             // entity not found
  1419                             break;
  1420                         default:
  1421                             die('Entity "' . $e . '" not found' . "\n");
  1422                     }
  1423                 }
  1424                 $text = str_replace($e, $replace, $text);
  1425             }
  1426         }
  1427     }
  1428     // return back &lt; and &gt;
  1429     $keep = array(
  1430         '&#38;#60;' => '&lt;',
  1431         '&#x0003E;' => '&gt;',
  1432     );
  1433     return str_replace(array_keys($keep), $keep, $text);
  1434 }
  1435 
  1436 function parse_properties_from_html_file($filepath, $classname) {
  1437     $file = DOC_URL . $filepath;
  1438     if (!is_file($file)) {
  1439         return array();
  1440         //die('Cannot parse properties from non-existing file: ' . $file);
  1441     }
  1442     $fields = array();
  1443     $html = new DOMDocument();
  1444     $html->preserveWhiteSpace = false;
  1445     @$html->loadHtmlFile($file);
  1446     $xpath = new DOMXPath($html);
  1447     // documentation
  1448     $doc = array();
  1449     $docNodes = $xpath->query('//div[@id="' . strtolower($classname) . '.props"]/dl');
  1450     if (!$docNodes->length) {
  1451         //die('Documentation not found for properties in file: ' . $file);
  1452     } elseif ($docNodes->length > 1) {
  1453         die('More documentations found for properties in file: ' . $file);
  1454     }
  1455     if ($docNodes->length) {
  1456         $fieldname = null;
  1457         foreach ($docNodes->item(0)->childNodes as $node) {
  1458             if ($node->nodeName == 'dt') {
  1459                 $fieldname = trim($node->nodeValue);
  1460             } elseif ($node->nodeName == 'dd') {
  1461                 $tmp = new DOMDocument();
  1462                 foreach ($node->childNodes as $child) {
  1463                     $tmp->appendChild($tmp->importNode($child, true));
  1464                 }
  1465                 $doc[$fieldname] = $tmp->saveHTML();
  1466             } else {
  1467                 //die('Unknown node name: ' . $node->nodeName);
  1468             }
  1469         }
  1470     }
  1471 
  1472     // fields
  1473     $fieldNodes = $xpath->query('//div[@class="classsynopsis"]//div[@class="fieldsynopsis"]');
  1474     foreach ($fieldNodes as $fieldNode) {
  1475         $field = new NetBeans_Field();
  1476         // name
  1477         $varnameNodes = $xpath->query('var//var[@class="varname"]', $fieldNode);
  1478         if (!$varnameNodes->length) {
  1479             die('Varname not found for property in file: ' . $file);
  1480         } elseif ($varnameNodes->length > 1) {
  1481             die('More varnames found for property in file: ' . $file);
  1482         }
  1483         $field->setName($varnameNodes->item(0)->nodeValue);
  1484         // modifiers
  1485         $modifierNodes = $xpath->query('span[@class="modifier"]', $fieldNode);
  1486         foreach ($modifierNodes as $modifierNode) {
  1487             $modifier = $modifierNode->nodeValue;
  1488             // XXX
  1489             if ($modifier == 'const') {
  1490                 // constant => do nothing
  1491                 return array();
  1492             }
  1493             $field->addModifier($modifier);
  1494         }
  1495         // type
  1496         $typeNodes = $xpath->query('span[@class="type"]', $fieldNode);
  1497         if ($typeNodes->length > 1) {
  1498             die('More types found for property ' . $field->getName() . ' in file: ' . $file);
  1499         }
  1500         $field->setType($typeNodes->item(0)->nodeValue);
  1501         // documentation
  1502         if (array_key_exists($field->getName(), $doc)) {
  1503             $field->setDocumentation($doc[$field->getName()]);
  1504         }
  1505         // all ok
  1506         $fields[] = $field;
  1507     }
  1508     return $fields;
  1509 }
  1510 
  1511 class NetBeans_Field {
  1512 
  1513     private $modifiers = array();
  1514     private $type;
  1515     private $name;
  1516     private $value;
  1517     private $documentation;
  1518 
  1519 
  1520     public function getModifiers() {
  1521         return $this->modifiers;
  1522     }
  1523 
  1524     /**
  1525      * @return \NetBeans_Field
  1526      */
  1527     public function addModifier($modifier) {
  1528         $this->modifiers[] = $modifier;
  1529         return $this;
  1530     }
  1531 
  1532     /**
  1533      * @return \NetBeans_Field
  1534      */
  1535     public function setModifiers($modifiers) {
  1536         $this->modifiers = $modifiers;
  1537         return $this;
  1538     }
  1539 
  1540     public function getType() {
  1541         return $this->type;
  1542     }
  1543 
  1544     /**
  1545      * @return \NetBeans_Field
  1546      */
  1547     public function setType($type) {
  1548         $this->type = $type;
  1549         return $this;
  1550     }
  1551 
  1552     public function getName() {
  1553         return $this->name;
  1554     }
  1555 
  1556     /**
  1557      * @return \NetBeans_Field
  1558      */
  1559     public function setName($name) {
  1560         $this->name = $name;
  1561         return $this;
  1562     }
  1563 
  1564     public function getValue() {
  1565         return $this->value;
  1566     }
  1567 
  1568     /**
  1569      * @return \NetBeans_Field
  1570      */
  1571     public function setValue($value) {
  1572         $this->value = $value;
  1573         return $this;
  1574     }
  1575 
  1576     public function getDocumentation() {
  1577         return $this->documentation;
  1578     }
  1579 
  1580     /**
  1581      * @return \NetBeans_Field
  1582      */
  1583     public function setDocumentation($documentation) {
  1584         $this->documentation = trim($documentation);
  1585         return $this;
  1586     }
  1587 
  1588 }
  1589 
  1590 function sanitizeType($type) {
  1591     if (!trim($type)) {
  1592         return '';
  1593     }
  1594     if (strpos($type, '|') !== false) {
  1595         // ignore 'MyClass|YourClass' cases
  1596         return '';
  1597     }
  1598     if (in_array($type, [
  1599         'mixed',
  1600         'object',
  1601         'callback',
  1602         'resource',
  1603         'bitmask',
  1604         'name',
  1605         'number',
  1606         'scalar',
  1607     ])) {
  1608         return '';
  1609     }
  1610     $convert = [
  1611         'boolean' => 'bool',
  1612     ];
  1613     return str_replace(array_keys($convert), $convert, $type);
  1614 }
  1615 
  1616 function isConstant($value) {
  1617     $values = explode(' | ', $value);
  1618     foreach ($values as $v) {
  1619         if (!preg_match('/^(\\w+\\:\\:)?[A-Z0-9_]+$/', $v)) {
  1620             return false;
  1621         }
  1622     }
  1623     return true;
  1624 }
  1625 
  1626 /**
  1627  * Prints usage help to the screen, and exits from program
  1628  */
  1629 function show_help() {
  1630 	global $argv0;
  1631 
  1632 	die (<<<EOF
  1633 USAGE: {$argv0} [options] <PHP.net documentation directory>
  1634 
  1635 Where options are:
  1636 
  1637 -help		Show this help.
  1638 -nosplit	Don't split output to different files.
  1639 -split		Split output to different files (one file per PHP extension).
  1640 -lang		Specify the language("en" by default). e.g. -lang en, -lang ja
  1641 -output 	Output directory name("php" by default).
  1642 
  1643 EOF
  1644 	);
  1645 }
  1646 
  1647 function show_message($message) {
  1648 	echo $message . PHP_EOL;
  1649 }
  1650 
  1651 ?>