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