php/sigfiles/generate.php
author Tomas Mysik <tmysik@netbeans.org>
Fri, 09 Jun 2017 09:16:56 +0200
changeset 6390 73661dbdafd6
parent 6389 8b57d477a2db
child 6391 640ddcff4333
permissions -rw-r--r--
Remove blacklisted methods

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