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
7 * @author Michael Spector <michael@zend.com>
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)!');
16 if (version_compare(phpversion(), "5.0.0") < 0) {
17 die ("This script requires PHP 5.0.0 or higher!\n");
27 $argv = $_SERVER["argv"];
28 $argv0 = array_shift ($argv);
29 for ($i = 0; $i < count($argv); ++$i) {
46 if ($lang[0] === '-') {
47 show_message("Invalid arg for -lang: " . $argv[$i]);
55 if ($phpDir[0] === '-') {
56 show_message("Invalid arg for -output: " . $argv[$i]);
62 $phpdocDir = $argv[$i];
70 /***************** REMOVED FUNCTIONS (START) *************************/
72 // add these functions to $removedFunctions!
74 if (!function_exists('ob_iconv_handler')) {
75 function ob_iconv_handler($contents, $status) {}
77 if (!function_exists('ob_tidyhandler')) {
78 function ob_tidyhandler($input, $mode = 0) {}
80 if (!function_exists('session_register')) {
81 function session_register($name, $_ = null) {}
83 if (!function_exists('session_unregister')) {
84 function session_unregister($name) {}
86 if (!function_exists('session_is_registered')) {
87 function session_is_registered($name) {}
89 if (!function_exists('chroot')) {
90 function chroot($directory) {}
93 /***************** REMOVED FUNCTIONS (END) *************************/
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(
105 'session_unregister',
106 'session_is_registered',
109 $functionBlackList = array(
111 'oci_lob_import' => 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,
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,
136 $methodBlackList = array(
138 $preferHtmlProperties = array(
142 $processedFunctions = $functionBlackList;
143 $processedClasses = array();
144 $processedConstants = array();
151 foreach ($extensions as $extName) {
155 print_extension (new ReflectionExtension ($extName));
157 finish_file_output("{$phpDir}/{$extName}.php");
164 $intFunctions = get_defined_functions();
165 foreach ($intFunctions["internal"] as $intFunction) {
166 if (!@$processedFunctions[strtolower($intFunction)]) {
167 print_function (new ReflectionFunction ($intFunction));
171 $intClasses = array_merge (get_declared_classes(), get_declared_interfaces(), get_declared_traits());
172 foreach ($intClasses as $intClass) {
173 if (strpos($intClass, 'NetBeans_') === 0) {
176 if (!@$processedClasses[strtolower($intClass)]) {
177 print_class (new ReflectionClass ($intClass));
182 $constants = get_defined_constants(true);
183 $intConstants = isset($constants["internal"]) ? $constants["internal"] : array();
184 // add magic constants:
185 $intConstants['__FILE__'] = null;
186 $intConstants['__LINE__'] = null;
187 $intConstants['__CLASS__'] = null;
188 $intConstants['__FUNCTION__'] = null;
189 $intConstants['__METHOD__'] = null;
190 $intConstants['__TRAIT__'] = null;
191 if (version_compare(phpversion(), "5.3.0") >= 0) {
192 $intConstants['__DIR__'] = null;
193 $intConstants['__NAMESPACE__'] = null;
195 foreach ($intConstants as $name => $value) {
196 if (!@$processedConstants[$name]) {
197 print_constant ($name, $value);
201 finish_file_output("{$phpDir}/basic.php");
207 foreach ($removedFunctions as $removedFunction) {
208 if (!@$processedFunctions[strtolower($removedFunction)]) {
209 print_function (new ReflectionFunction ($removedFunction));
213 finish_file_output("{$phpDir}/removed.php");
218 $fp = fopen ("{$phpDir}/.list", "w");
219 foreach (glob("{$phpDir}/*.php") as $f) {
220 fwrite ($fp, basename($f));
226 function findVerInfo($file)
228 $url = DOC_URL.$file.".html";
229 $search_for = '<p class="verinfo">';
230 //echo "Reading $url :\n";
232 if (!is_file($url)) {
236 $file_contents = file_get_contents($url);
238 $start_pos = strpos($file_contents, $search_for);
240 if ($start_pos !== 0) {
241 $start_pos += strlen($search_for);
242 $end_pos = strpos($file_contents, '</p>', $start_pos);
244 if ($end_pos !== 0) {
245 $verinfo = substr($file_contents, $start_pos, $end_pos - $start_pos);
246 //echo "Ver. info: $verinfo\n";
254 * Makes generic key from given function name
255 * @param name string Function name
256 * @return string generic key
258 function make_funckey_from_str ($name) {
259 $name = str_replace ("->", "::", $name);
260 $name = str_replace ("()", "", $name);
261 $name = strtolower ($name);
266 * Replaces all invalid charaters with '_' in PHP identifier
267 * @param name PHP identifier
268 * @return string PHP identifier with stripped invalid characters
270 function clean_php_identifier ($name) {
271 $name = preg_replace('/[^\$\w\_]+/', '_', $name);
275 function clean_php_value($type) {
277 $type = str_replace("&null;", "null", $type);
278 $type = str_replace("&true;", "true", $type);
279 $type = str_replace("&false;", "false", $type);
280 $type = str_replace(""", "", $type);
281 $type = strip_tags($type);
286 * Makes generic key from given function reference
287 * @param name ReflectionMethod function reference
288 * @return string generic key
290 function make_funckey_from_ref ($ref) {
291 if ($ref instanceof ReflectionMethod) {
292 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
294 $funckey = strtolower($ref->getName());
298 function make_property_from_ref ($ref) {
299 if ($ref instanceof ReflectionProperty) {
300 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
302 throw new Exception("Unexpected type: ".gettype($ref));
307 function make_classmember_ref ($className, $memberName) {
308 return strtolower($className)."::".strtolower($memberName);
313 * Parses PHP documentation
314 * @param phpdocDir string PHP.net documentation directory
315 * @return array Function information gathered from the PHP.net documentation by parsing XML files
317 function parse_phpdoc_functions ($phpdocDir, $extensions, $lang) {
318 $xml_files = array_merge (
319 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*.xml"),
320 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/predefined/*/*.xml"),
321 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*/*.xml")
323 foreach ($extensions as $extName) {
324 $extName = strtolower($extName);
325 $globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml";
326 $xml_files = array_merge (
331 $functionsDoc = array();
332 foreach ($xml_files as $xml_file) {
333 $xml = file_get_contents ($xml_file);
335 if (preg_match ('@<refentry.*?xml:id=["\'](.*?)["\'].*?>.*?<refname>(.*?)</refname>\s*(?:<refname>(.*?)</refname>)?.*?<refpurpose>(.*?)</refpurpose>@s', $xml, $match)) {
337 $refnames = array($match[2], $match[3]);
339 foreach ($refnames as $refname) {
340 $refname = trim($refname);
341 if ($refname == '') {
344 $refname = make_funckey_from_str ($refname);
345 if (array_key_exists($refname, $functionsDoc)) {
349 $functionsDoc[$refname] = array();
350 $functionsDoc[$refname]['id'] = $id;
351 $functionsDoc[$refname]['quickref'] = xml_to_phpdoc($phpdoc);
352 $functionsDoc[$refname]['deprecated'] = strpos($xml_file, "/oldaliases/") !== false;
354 if (preg_match ('@<refsect1\s+role=["\']description["\']>(.*?)</refsect1>@s', $xml, $match)) {
355 $description = $match[1];
356 $function_alias = null;
358 $has_object_style = false;
359 if (preg_match ('@^(.*?)<classsynopsis>.*?<classname>(.*)</classname>.*?<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>.*?</classsynopsis>(.*)$@s', $description, $match)) {
360 $functionsDoc[$refname]['classname'] = trim($match[2]);
361 $functionsDoc[$refname]['returntype'] = trim($match[3]);
362 $functionsDoc[$refname]['methodname'] = trim($match[4]);
363 $parameters = $match[5];
364 $description = $match[1].$match[6];
365 $has_object_style = true;
367 $methodsynopsis = null;
368 if ($refname == 'number_format') {
369 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $tmp);
371 foreach ($tmp as $key => $val) {
372 $match[$key] = $val[count($val) - 1];
375 if (strpos($refname, '::') !== false) {
376 $methodsynopsis = preg_match ('@<methodsynopsis role="oop">.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
378 if (!$methodsynopsis) {
379 $methodsynopsis = preg_match ('@<methodsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
381 if (!$methodsynopsis) {
382 $methodsynopsis = preg_match ('@<constructorsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</constructorsynopsis>@s', $description, $match);
385 if ($methodsynopsis) {
386 if ($has_object_style && make_funckey_from_str($match[2]) != $refname) {
387 $function_alias = trim($match[2]);
389 $functionsDoc[$refname]['returntype'] = trim(str_replace('-', '_', $match[1])); // e.g. OCI-Collection -> OCI_Collection
390 $functionsDoc[$refname]['methodname'] = trim($match[2]);
391 $parameters = $match[3];
395 if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
396 for ($i = 0; $i < count($match[0]); ++$i) {
398 'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
399 'name' => clean_php_identifier(trim($match[4][$i])),
401 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
402 $parameter['isoptional'] = true;
404 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
405 $parameter['isreference'] = true;
407 if (@strlen(trim($match[5][$i]))) {
408 $def = $match[5][$i];
412 $parameter['defaultvalue'] = clean_php_value($def);
413 $parameter['isoptional'] = true;
415 $functionsDoc[$refname]['parameters'][] = $parameter;
420 if (preg_match ('@<refsect1\s+role=["\']parameters["\']>(.*?)</refsect1>@s', $xml, $match)) {
421 $parameters = $match[1];
422 if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
423 for ($i = 0; $i < count($match[0]); $i++) {
424 for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
425 if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
426 $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
433 if (preg_match ('@<refsect1\s+role=["\']returnvalues["\']>(.*?)</refsect1>@s', $xml, $match)) {
434 $returnvalues = $match[1];
435 if (preg_match ('@<para>\s*(.*)</para>?@s', $returnvalues, $match)) {
436 $functionsDoc[$refname]['returndoc'] = preg_replace("@^Returns @", "", xml_to_phpdoc ($match[1]));
440 // Create information for function alias
441 if ($function_alias) {
442 $functionsDoc[$function_alias] = $functionsDoc[$refname];
447 return $functionsDoc;
451 * Parses PHP documentation
452 * @param phpdocDir string PHP.net documentation directory
453 * @return array Function information gathered from the PHP.net documentation by parsing XML files
455 function parse_phpdoc_fields ($phpdocDir, $extensions, $lang) {
456 $xml_files = array();
457 foreach ($extensions as $extName) {
458 $extName = strtolower($extName);
460 $xml_files = array_merge (
462 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml"),
463 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml")
466 foreach ($xml_files as $xml_file) {
467 $xml = file_get_contents($xml_file);
468 if (preg_match('@<classsynopsisinfo>.*?<classname>(.*)</classname>.*?</classsynopsisinfo>@s', $xml, $matchOffset, PREG_OFFSET_CAPTURE)) {
469 $classname = $matchOffset[1][0];
470 if (preg_match_all("@<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>@s", $xml, $matchOffset,null,$matchOffset[1][1])) {
471 for ($index = 0; $index < count($matchOffset[2]); $index++) {
472 $name = $matchOffset[2][$index];
473 $type= $matchOffset[1][$index];
474 $exploded = explode("::", $name);
475 if (count($exploded) > 1) {
476 $name = $exploded[1];
478 $reference = make_classmember_ref($classname, $name);
479 $fieldsDoc[$reference]['field'] = $name;
480 $fieldsDoc[$reference]['type'] = $type;
484 if (preg_match('@<classsynopsis>.*?<classname>(.*)</classname>.*?<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>.*?</classsynopsis>@s', $xml, $match)) {
485 $reference = make_classmember_ref($match[1], $match[3]);
486 $fieldsDoc[$reference]['field'] = $match[3];
487 $fieldsDoc[$reference]['type'] = $match[2];
488 //$fieldsDoc[$refname]['quickref'] = trim($match[3]);
493 if (isset($fieldsDoc)) {
500 * Parses PHP documentation
501 * @param phpdocDir string PHP.net documentation directory
502 * @return array Class information gathered from the PHP.net documentation by parsing XML files
504 function parse_phpdoc_classes ($phpdocDir, $extensions, $lang) {
505 $xml_files = array_merge (
506 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/reference.xml"),
507 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/classes.xml"),
508 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*/*.xml"),
509 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*.xml")
511 foreach ($extensions as $extName) {
512 $extName = strtolower($extName);
513 $globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml";
514 $xml_files = array_merge (
520 $classesDoc = array();
521 foreach ($xml_files as $xml_file) {
522 $xml = file_get_contents ($xml_file);
523 if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
525 $prefixId = substr($id, 0, strlen("class."));
526 $clsNamePattern = ($prefixId === "class.") ?
527 '@<ooclass><classname>(.*?)</classname></ooclass>@' :
528 '@<title><classname>(.*?)</classname></title>@';
529 if (preg_match_all ($clsNamePattern, $xml, $match)) {
530 for ($i = 0; $i < count($match[0]); ++$i) {
531 $class = $match[1][$i];
532 $refname = strtolower ($class);
533 $classesDoc[$refname]['id'] = $id;
534 $classesDoc[$refname]['name'] = $class;
535 $offsetPattern = ($prefixId === "class.") ?
536 "@xml:id=[\"'](.*?)\.intro[\"']@i" :
537 "@<title><classname>{$class}</classname></title>@";
538 if (preg_match ($offsetPattern , $xml, $matchOffset,PREG_OFFSET_CAPTURE)) {
539 $docPattern = '@<para>(.*?)</para>@s';
540 if (preg_match ($docPattern, $xml, $match2,null,$matchOffset[0][1])) {
541 $doc = xml_to_phpdoc($match2[1]);
542 $classesDoc[$refname]['doc'] = $doc;
553 * Parses PHP documentation
554 * @param phpdocDir string PHP.net documentation directory
555 * @return array Constant information gathered from the PHP.net documentation by parsing XML files
557 function parse_phpdoc_constants ($phpdocDir) {
558 exec ("find ".addslashes($phpdocDir)." -name \"*constants.xml\"", $xml_files);
559 foreach ($xml_files as $xml_file) {
560 $xml = file_get_contents ($xml_file);
562 if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
564 if (preg_match_all ('@<term>\s*<constant>([a-zA-Z_:][a-zA-Z0-9_:]*)</constant>.*?</term>.*?<listitem>(.*?)</listitem>@s', $xml, $match)) {
565 for ($i = 0; $i < count($match[0]); ++$i) {
566 $constant = $match[1][$i];
567 $constantsDoc[$constant]['id'] = $id;
568 $constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
572 '@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>\d+</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)
573 || preg_match_all ('@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)) {
575 for ($i = 0; $i < count($match[0]); ++$i) {
576 $constant = $match[1][$i];
577 $constantsDoc[$constant]['id'] = $id;
578 $constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
583 return $constantsDoc;
587 * Prints ReflectionExtension in format of PHP code
588 * @param extRef ReflectionExtension object
590 function print_extension ($extRef) {
591 global $functionBlackList;
593 print "\n// Start of {$extRef->getName()} v.{$extRef->getVersion()}\n";
596 $classesRef = $extRef->getClasses();
597 if (count ($classesRef) > 0) {
598 foreach ($classesRef as $classRef) {
599 print_class ($classRef);
604 $funcsRef = $extRef->getFunctions();
605 if (count ($funcsRef) > 0) {
606 foreach ($funcsRef as $funcName => $funcRef) {
607 if (array_key_exists($funcName, $functionBlackList)) {
610 print_function ($funcRef);
616 $constsRef = $extRef->getConstants();
617 if (count ($constsRef) > 0) {
618 print_constants ($constsRef);
622 print "// End of {$extRef->getName()} v.{$extRef->getVersion()}\n";
626 * Prints ReflectionClass in format of PHP code
627 * @param classRef ReflectionClass object
628 * @param tabs integer[optional] number of tabs for indentation
630 function print_class (ReflectionClass $classRef, $tabs = 0) {
631 global $processedClasses, $preferHtmlProperties, $methodBlackList;
632 $processedClasses [strtolower($classRef->getName())] = true;
635 print_doccomment ($classRef, $tabs);
637 if ($classRef->isFinal()) print "final ";
639 if ($classRef->isInterface()) {
641 } elseif ($classRef->isTrait()) {
644 if ($classRef->isAbstract()) {
649 print clean_php_identifier($classRef->getName())." ";
651 // print out parent class
652 $parentClassRef = $classRef->getParentClass();
653 if ($parentClassRef) {
654 print "extends {$parentClassRef->getName()} ";
657 // print out interfaces
658 $interfacesRef = $classRef->getInterfaces();
659 if (count ($interfacesRef) > 0) {
660 print $classRef->isInterface() ? "extends " : "implements ";
662 foreach ($interfacesRef as $interfaceRef) {
666 print "{$interfaceRef->getName()}";
672 $traits = $classRef->getTraits();
673 if (count($traits)) {
674 foreach ($traits as $trait => $traitInfo) {
675 print_tabs($tabs + 1);
676 print 'use ' . $trait . ';';
683 $constsRef = $classRef->getConstants();
684 if (count ($constsRef) > 0) {
685 print_class_constants ($classRef, $constsRef, $tabs + 1);
689 // process properties
690 $propertiesRef = $classRef->getProperties();
691 if (!in_array(strtolower($classRef->getName()), $preferHtmlProperties)
692 && count ($propertiesRef) > 0) {
693 foreach ($propertiesRef as $propertyRef) {
694 print_property ($propertyRef, $tabs + 1);
698 // #188245 - try to find them directly in HTML
699 $properties = parse_properties_from_html_file('class.' . strtolower($classRef->getName()) . '.html', $classRef->getName());
700 if (count($properties)) {
701 foreach ($properties as $property) {
702 print_field($property, $tabs + 1);
709 /* @var $classRef ReflectionClass */
710 $methodsRef = $classRef->getMethods();
711 if (count ($methodsRef) > 0) {
712 foreach ($methodsRef as $methodRef) {
713 /* @var $methodRef ReflectionMethod */
714 if (in_array(strtolower($methodRef->getName()), $methodBlackList)) {
717 print_method($classRef, $methodRef, $tabs + 1);
726 * Prints ReflectionProperty in format of PHP code
727 * @param ReflectionProperty $propertyRef object
728 * @param integer[optional] tabs number of tabs for indentation
730 function print_property ($propertyRef, $tabs = 0) {
731 print_doccomment ($propertyRef, $tabs);
733 print_modifiers ($propertyRef, true);
734 $name = $propertyRef->getName();
735 if (substr($name, 0, 1) !== '$') {
742 * Prints Field in format of PHP code
743 * @param NetBeans_Field $field object
744 * @param integer[optional] tabs number of tabs for indentation
746 function print_field (NetBeans_Field $field, $tabs = 0) {
748 $fieldType = $field->getType();
749 $fieldDoc = $field->getDocumentation();
750 $fieldDoc = trim(strip_tags($fieldDoc, '<p>,<strong>,<code>,<a>'));
751 // replace hyperlinks
752 $fieldDoc = preg_replace('%<(a|strong)[^>]*>%', '<b>', $fieldDoc);
753 $fieldDoc = preg_replace('%<p[^>]+>%', '<p>', $fieldDoc);
754 $fieldDoc = str_replace('</a>', '</b>', $fieldDoc);
755 $fieldDoc = str_replace('</strong>', '</b>', $fieldDoc);
756 $fieldDoc = preg_replace('%^<p>(.+)</p>$%s', '<p style="margin-top:0;">\\1</p>', $fieldDoc);
760 foreach (preg_split('%\n|\r%', $fieldDoc) as $line) {
769 print " * @var $fieldType\n";
775 $print = implode(' ', $field->getModifiers());
776 $print = str_replace("final", "", $print);
777 $print = str_replace("abstract", "", $print);
778 $print = str_replace("readonly", "", $print);
779 $print = trim($print);
786 $name = $field->getName();
787 if (substr($name, 0, 1) !== '$') {
793 function print_function ($functionRef, $tabs = 0) {
794 print_method(null, $functionRef, $tabs);
797 function print_method ($classRef, $functionRef, $tabs = 0) {
798 global $functionsDoc;
799 global $processedFunctions;
801 $funckey = make_funckey_from_ref ($functionRef);
802 $processedFunctions[$funckey] = true;
806 print_doccomment ($functionRef, $tabs);
808 if (!($functionRef instanceof ReflectionFunction)) {
809 print_modifiers ($functionRef);
810 $modifiers = Reflection::getModifierNames($functionRef->getModifiers());
814 if ($functionRef->returnsReference()) {
817 $functionName = $functionRef->getName();
818 print "$functionName(";
819 $parameters = @$functionsDoc[$funckey]['parameters'];
821 print_parameters ($parameters);
823 print_parameters_ref ($functionRef->getParameters());
826 $returntype = sanitizeType(@$functionsDoc[$funckey]['returntype']);
828 && $functionName !== '__construct') {
829 print ': ' . $returntype;
832 if ($classRef != null && $classRef->isInterface()) {
834 } elseif (is_array($modifiers)) {
835 foreach ($modifiers as $modifier) {
836 if ($modifier == "abstract") {
852 * Prints ReflectionParameter in format of PHP code
853 * @param parameters array information from PHP.net documentation
855 function print_parameters ($parameters) {
857 foreach ($parameters as $parameter) {
858 if ($parameter['name'] != "...") {
862 $type = sanitizeType($parameter['type']);
866 if (@$parameter['isreference']) {
869 print "\${$parameter['name']}";
870 if (@$parameter['isoptional']) {
871 if (array_key_exists('defaultvalue', $parameter)) {
872 $value = $parameter['defaultvalue'];
873 if ((is_numeric ($value) && $type != 'string')
874 || in_array(strtolower($value), array('true', 'false', 'null', 'array()'))
875 || (substr($value, 0, 1) == '\'' && substr($value, -1) == '\'')
876 || (substr($value, 0, 1) == '"' && substr($value, -1) == '"')
877 || (substr($value, 0, 2) == '__' && substr($value, -2) == '__')
878 || isConstant($value)) {
881 $value = "'{$value}'";
894 * Prints ReflectionParameter in format of PHP code
895 * @param paramsRef ReflectionParameter[] array of objects
897 function print_parameters_ref ($paramsRef) {
899 foreach ($paramsRef as $paramRef) {
900 /* @var $paramRef ReflectionParameter */
901 if ($paramRef->allowsNull()) {
904 if ($paramRef->isArray()) {
907 if ($className = get_parameter_classname($paramRef)) {
908 print "{$className} ";
911 $name = $paramRef->getName() ? $paramRef->getName() : "var".($i+1);
912 if ($name != "...") {
916 if ($paramRef->isPassedByReference()) {
920 if ($paramRef->allowsNull()) {
922 } else if ($paramRef->isDefaultValueAvailable()) {
923 $value = $paramRef->getDefaultValue();
924 if (!is_numeric ($value)) {
925 $value = "'{$value}'";
934 * Prints constants in format of PHP code
935 * @param constants array containing constants, where key is a name of constant
936 * @param tabs integer[optional] number of tabs for indentation
938 function print_constants ($constants, $tabs = 0) {
939 foreach ($constants as $name => $value) {
940 print_constant ($name, $value, $tabs);
944 function print_constant ($name, $value = null, $tabs = 0) {
945 global $constantsDoc;
946 global $processedConstants;
947 $processedConstants [$name] = true;
949 if ($value === null) {
950 $value = @constant ($name);
952 $value = escape_const_value ($value);
954 $doc = @$constantsDoc[$name]['doc'];
960 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
962 print " * @link ".make_url($constantsDoc[$name]['id'])."\n";
967 print "define ('{$name}', {$value});\n";
970 function escape_const_value ($value) {
971 if (is_resource($value)) {
972 $value = "\"${value}\"";
973 } else if (!is_numeric ($value) && !is_bool ($value) && $value !== null) {
974 $value = '"'.addcslashes ($value, "\"\r\n\t").'"';
975 } else if ($value === null) {
977 } else if ($value === false) {
979 } else if ($value === true) {
986 * Prints class constants in format of PHP code
987 * @param constants array containing constants, where key is a name of constant
988 * @param tabs integer[optional] number of tabs for indentation
990 function print_class_constants ($classRef, $constants, $tabs = 0) {
991 global $constantsDoc;
992 global $processedConstants;
995 //$doc = @$constantsDoc[$name]['doc'];
996 foreach ($constants as $name => $value) {
997 $value = escape_const_value ($value);
998 $clsName = $classRef->getName();
999 $idx = "$clsName::$name";
1000 $doc = @$constantsDoc[$idx]['doc'];
1006 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
1008 print " * @link ".make_url($constantsDoc[$idx]['id'])."\n";
1013 print "const {$name} = {$value};\n";
1018 * Prints modifiers of reflection object in format of PHP code
1019 * @param ref Reflection some reflection object
1021 function print_modifiers ($ref, $forFields = false) {
1022 $modifiers = Reflection::getModifierNames ($ref->getModifiers());
1023 if (count ($modifiers) > 0) {
1024 $print = implode(' ', $modifiers);
1026 $print = str_replace("final", "", $print);
1027 $print = str_replace("abstract", "", $print);
1028 $print = str_replace("readonly", "", $print);
1036 * Makes PHP Manual URL from the given ID
1037 * @param id PHP Element ID
1040 function make_url ($id) {
1042 return "http://php.net/manual/{$lang}/{$id}.php";
1046 * Prints PHPDOC comment before specified reflection object
1047 * @param ref Reflection some reflection object
1048 * @param tabs integer[optional] number of tabs for indentation
1050 function print_doccomment ($ref, $tabs = 0) {
1051 global $functionsDoc;
1054 global $methodBlackList;
1056 $docComment = $ref->getDocComment();
1059 print "{$docComment}\n";
1061 else if ($ref instanceof ReflectionClass) {
1062 $refname = strtolower($ref->getName());
1063 if (@$classesDoc[$refname]) {
1066 $doc = @$classesDoc[$refname]['doc'];
1068 $doc = newline_to_phpdoc ($doc, $tabs);
1070 print " * {$doc}\n";
1073 foreach ($ref->getMethods() as $method) {
1074 if (in_array(strtolower($method->getName()), $methodBlackList)) {
1075 print_magic_method($ref, $method, $tabs);
1078 if (@$classesDoc[$refname]['id']) {
1080 $url = make_url ($classesDoc[$refname]['id']);
1081 print " * @link {$url}\n";
1087 else if ($ref instanceof ReflectionFunctionAbstract) {
1088 $funckey = make_funckey_from_ref ($ref);
1089 $id = @$functionsDoc[$funckey]['id'];
1090 $ver_info = findVerInfo($id);
1091 $desc = @$functionsDoc[$funckey]['quickref'];
1094 if (strpos($funckey, "::__construct") === false) {
1095 $returntype = @$functionsDoc[$funckey]['returntype'];
1096 $returndoc = newline_to_phpdoc (@$functionsDoc[$funckey]['returndoc'], $tabs);
1099 $paramsRef = $ref->getParameters();
1100 $parameters = @$functionsDoc[$funckey]['parameters'];
1102 if ($desc || count ($paramsRef) > 0 || $parameters || $returntype) {
1107 print " * {$ver_info}<br/>\n";
1111 print " * {$desc}\n";
1113 if (@$functionsDoc[$funckey]['id']) {
1115 $url = make_url ($functionsDoc[$funckey]['id']);
1116 print " * @link {$url}\n";
1118 if (!@$functionsDoc[$funckey]['deprecated']) {
1120 foreach ($parameters as $parameter) {
1122 print " * @param {$parameter['type']} \${$parameter['name']}";
1123 if (@$parameter['isoptional']) {
1124 print " [optional]";
1126 $paramdoc = @$parameter['paramdoc'];
1127 if ($paramdoc && $paramdoc != "<p>\n</p>") {
1128 $paramdoc = newline_to_phpdoc(@$parameter['paramdoc'], $tabs);
1129 print " {$paramdoc}";
1135 foreach ($paramsRef as $paramRef) {
1137 $name = $paramRef->getName() ? $paramRef->getName() : "var".++$i;
1139 if($className = get_parameter_classname($paramRef)) {
1140 print " {$className}";
1141 if($paramRef->isArray()) {
1146 if($paramRef->isOptional()) {
1147 print " [optional]";
1152 if ($returntype || $returndoc) {
1154 $returntype = 'mixed';
1157 print " * @return " . trim("{$returntype} {$returndoc}") . "\n";
1163 }else if ($ref instanceof ReflectionProperty) {
1164 $property_from_ref = make_property_from_ref($ref);
1165 $fieldName = @$fieldsDoc[$property_from_ref]['field'];
1166 $fieldType = @$fieldsDoc[$property_from_ref]['type'];
1167 if (isset ($fieldName) && isset ($fieldType)) {
1171 print " * @var $fieldType\n";
1178 function print_magic_method(ReflectionClass $classRef, ReflectionMethod $methodRef, $tabs = 0) {
1179 global $functionsDoc;
1180 global $processedFunctions;
1182 $funckey = make_funckey_from_ref($methodRef);
1183 $processedFunctions[$funckey] = true;
1186 print " * @method ";
1187 $returntype = @$functionsDoc[$funckey]['returntype'];
1188 $returndoc = @$functionsDoc[$funckey]['returndoc'];
1192 $returntype = 'mixed';
1194 print $returntype . " ";
1196 print $methodRef->getName() . "(";
1197 $parameters = @$functionsDoc[$funckey]['parameters'];
1199 print_parameters($parameters);
1201 print_parameters_ref($methodRef->getParameters());
1204 $id = @$functionsDoc[$funckey]['id'];
1205 $ver_info = findVerInfo($id);
1206 $docComment = @$functionsDoc[$funckey]['quickref'];
1224 * Converts XML entities to human readable string for PHPDOC
1228 function xml_to_phpdoc ($str) {
1229 $str = str_replace ("&php.ini;", "###(i)###php.ini###(/i)###", $str); // XXX will be replaced in strip_tags_special()
1230 $str = replace_entities($str);
1231 $str = strip_tags_special ($str);
1232 $str = preg_replace ("/ */", " ", $str);
1233 $str = str_replace ("*/", "* /", $str);
1234 $str = str_replace ("“", """, $str);
1235 $str = str_replace ("”", """, $str);
1236 $str = preg_replace ("/[\r\n][\t ]/", "\n", $str);
1242 * Converts newlines to PHPDOC prefixes in the given string
1244 * @param tabs integer[optional] number of tabs for indentation
1245 * @return string PHPDOC string
1247 function newline_to_phpdoc ($str, $tabs = 0) {
1248 $str = preg_replace ("@\s*[\r\n]+@", "\n".str_repeat("\t", $tabs)." * ", $str);
1253 * Prints specified number of tabs
1254 * @param tabs integer number of tabs to print
1256 function print_tabs ($tabs) {
1257 print str_repeat("\t", $tabs);
1261 * Returns class name from given parameter reference, this method is a workaround
1262 * for the case when exception is thrown from getClass() when such classname does not exist.
1264 function get_parameter_classname(ReflectionParameter $paramRef) {
1266 if ($classRef = $paramRef->getClass()) {
1267 return $classRef->getName();
1269 } catch (Exception $e) {
1270 if (preg_match('/Class (\w+) does not exist/', $e->getMessage(), $matches)) {
1278 * Starts outputing to the new file
1280 function begin_file_output() {
1286 * Ends outputing, and dumps the output to the specified file
1287 * @param filename File to dump the output
1289 function finish_file_output($filename) {
1290 //if (file_exists ($filename)) {
1291 // rename ($filename, "{$filename}.bak");
1294 file_put_contents (str_replace(" ", "-", $filename), ob_get_contents());
1299 * Strips xml tags from the string like the standard strip_tags() function
1300 * would do, but also translates some of the docbook tags (such as tables
1301 * an paragraphs) to proper html tags
1305 function strip_tags_special ($str) {
1307 // $str = method_to_phpdoc($str);
1308 // first mask and translate the tags to preseve
1309 $str = preg_replace ("/<(\/?)table>/", "###($1table)###", $str);
1310 $str = str_replace ("<row>", "###(tr valign=\"top\")###", $str);
1311 $str = str_replace ("</row>", "###(/tr)###", $str);
1312 $str = preg_replace ("/<(\/?)entry>/", "###($1td)###", $str);
1313 $str = preg_replace ("/<(\/?)para>/", "###($1p)###", $str);
1314 $str = preg_replace ("/<(\/?)p>/", "###($1p)###", $str);
1316 $str = str_replace ("<![CDATA[", "###(pre)###", $str);
1317 $str = str_replace ("]]>", "###(/pre)###", $str);
1318 // preserve php samples; XXX sample for preg_match_all
1319 $str = str_replace ("<?php", "###(code)###", $str);
1320 $str = str_replace ("?>", "###(/code)###", $str);
1321 // handle "<pre><code>"
1322 $str = preg_replace ("/###\(pre\)###\s*\n\s*###\(code\)###/", "###(code)###", $str);
1323 $str = preg_replace ("/###\(\/code\)###\s*\n\s*###\(\/pre\)###/", "###(/code)###", $str);
1324 // constant & function etc.
1325 $str = preg_replace ("%<(/)?(constant|function|classname|methodname|methodparam)[^>]*>%", "###(\\1b)###", $str);
1326 $str = preg_replace ("%<(/)?(parameter)[^>]*>%", "###(\\1i)###", $str);
1327 // now strip the remaining tags
1328 $str = strip_tags ($str);
1329 // and restore the translated ones
1330 $str = str_replace ("###(", "<", $str);
1331 $str = str_replace (")###", ">", $str);
1335 // XXX, see set_error_handler
1336 function method_to_phpdoc($str) {
1338 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $str, $tmp);
1339 if (!$methodsynopsis) {
1342 $functionsDoc = array();
1344 for ($i = 0; $i < count($tmp); ++$i) {
1345 $refname = trim($tmp[2][$i]);
1346 $functionsDoc[$refname]['methodname'] = $refname;
1347 $parameters = $tmp[3][$i];
1349 if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
1350 for ($i = 0; $i < count($match[0]); ++$i) {
1351 $parameter = array (
1352 'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
1353 'name' => clean_php_identifier(trim($match[4][$i])),
1355 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
1356 $parameter['isoptional'] = true;
1358 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
1359 $parameter['isreference'] = true;
1361 if (@strlen(trim($match[5][$i]))) {
1362 $parameter['defaultvalue'] = clean_php_value($match[5][$i]);
1363 $parameter['isoptional'] = true;
1365 $functionsDoc[$refname]['parameters'][] = $parameter;
1368 if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
1369 for ($i = 0; $i < count($match[0]); $i++) {
1370 for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
1371 if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
1372 $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
1383 function parse_entities($phpdocDir, $lang) {
1384 $entities = array();
1385 parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-defs.ent");
1386 parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-snippets.ent");
1387 parse_entities_from_file($entities, $phpdocDir, '/doc-base/docbook/docbook-xml/ent/isonum.ent');
1388 parse_entities_from_file($entities, $phpdocDir, '/doc-base/entities/global.ent');
1391 function parse_entities_from_file(array &$entities, $phpdocDir, $filepath) {
1392 $content = file_get_contents($phpdocDir . $filepath);
1394 preg_match_all('%\!ENTITY\s+(\S+)\s+([\'"])([^\\2]+?)\\2\s*>%m', $content, $matches);
1395 if (array_key_exists(1, $matches) && count($matches[1])) {
1396 for ($i = 0; $i < count($matches[2]); $i++) {
1397 $entities['&' . $matches[1][$i] . ';'] = $matches[3][$i];
1401 function replace_entities($text) {
1404 while (preg_match_all('%(\&(?!#)\S+?\;)%', $text, $matches)) {
1405 if (count($matches[1])) {
1406 foreach ($matches[1] as $e) {
1408 if (array_key_exists($e, $entities)) {
1409 $replace = $entities[$e];
1411 if ($replace === null) {
1415 case '&reference.strings.charsets;':
1416 case '&reference.intl.inctimezoneparam;':
1417 case '&reference.intl.incfieldparam;':
1421 die('Entity "' . $e . '" not found' . "\n");
1424 $text = str_replace($e, $replace, $text);
1428 // return back < and >
1430 '&#60;' => '<',
1431 '>' => '>',
1433 return str_replace(array_keys($keep), $keep, $text);
1436 function parse_properties_from_html_file($filepath, $classname) {
1437 $file = DOC_URL . $filepath;
1438 if (!is_file($file)) {
1440 //die('Cannot parse properties from non-existing file: ' . $file);
1443 $html = new DOMDocument();
1444 $html->preserveWhiteSpace = false;
1445 @$html->loadHtmlFile($file);
1446 $xpath = new DOMXPath($html);
1449 $docNodes = $xpath->query('//div[@id="' . strtolower($classname) . '.props"]/dl');
1450 if (!$docNodes->length) {
1451 //die('Documentation not found for properties in file: ' . $file);
1452 } elseif ($docNodes->length > 1) {
1453 die('More documentations found for properties in file: ' . $file);
1455 if ($docNodes->length) {
1457 foreach ($docNodes->item(0)->childNodes as $node) {
1458 if ($node->nodeName == 'dt') {
1459 $fieldname = trim($node->nodeValue);
1460 } elseif ($node->nodeName == 'dd') {
1461 $tmp = new DOMDocument();
1462 foreach ($node->childNodes as $child) {
1463 $tmp->appendChild($tmp->importNode($child, true));
1465 $doc[$fieldname] = $tmp->saveHTML();
1467 //die('Unknown node name: ' . $node->nodeName);
1473 $fieldNodes = $xpath->query('//div[@class="classsynopsis"]//div[@class="fieldsynopsis"]');
1474 foreach ($fieldNodes as $fieldNode) {
1475 $field = new NetBeans_Field();
1477 $varnameNodes = $xpath->query('var//var[@class="varname"]', $fieldNode);
1478 if (!$varnameNodes->length) {
1479 die('Varname not found for property in file: ' . $file);
1480 } elseif ($varnameNodes->length > 1) {
1481 die('More varnames found for property in file: ' . $file);
1483 $field->setName($varnameNodes->item(0)->nodeValue);
1485 $modifierNodes = $xpath->query('span[@class="modifier"]', $fieldNode);
1486 foreach ($modifierNodes as $modifierNode) {
1487 $modifier = $modifierNode->nodeValue;
1489 if ($modifier == 'const') {
1490 // constant => do nothing
1493 $field->addModifier($modifier);
1496 $typeNodes = $xpath->query('span[@class="type"]', $fieldNode);
1497 if ($typeNodes->length > 1) {
1498 die('More types found for property ' . $field->getName() . ' in file: ' . $file);
1500 $field->setType($typeNodes->item(0)->nodeValue);
1502 if (array_key_exists($field->getName(), $doc)) {
1503 $field->setDocumentation($doc[$field->getName()]);
1511 class NetBeans_Field {
1513 private $modifiers = array();
1517 private $documentation;
1520 public function getModifiers() {
1521 return $this->modifiers;
1525 * @return \NetBeans_Field
1527 public function addModifier($modifier) {
1528 $this->modifiers[] = $modifier;
1533 * @return \NetBeans_Field
1535 public function setModifiers($modifiers) {
1536 $this->modifiers = $modifiers;
1540 public function getType() {
1545 * @return \NetBeans_Field
1547 public function setType($type) {
1548 $this->type = $type;
1552 public function getName() {
1557 * @return \NetBeans_Field
1559 public function setName($name) {
1560 $this->name = $name;
1564 public function getValue() {
1565 return $this->value;
1569 * @return \NetBeans_Field
1571 public function setValue($value) {
1572 $this->value = $value;
1576 public function getDocumentation() {
1577 return $this->documentation;
1581 * @return \NetBeans_Field
1583 public function setDocumentation($documentation) {
1584 $this->documentation = trim($documentation);
1590 function sanitizeType($type) {
1594 if (strpos($type, '|') !== false) {
1595 // ignore 'MyClass|YourClass' cases
1598 if (in_array($type, [
1611 'boolean' => 'bool',
1613 return str_replace(array_keys($convert), $convert, $type);
1616 function isConstant($value) {
1617 $values = explode(' | ', $value);
1618 foreach ($values as $v) {
1619 if (!preg_match('/^(\\w+\\:\\:)?[A-Z0-9_]+$/', $v)) {
1627 * Prints usage help to the screen, and exits from program
1629 function show_help() {
1633 USAGE: {$argv0} [options] <PHP.net documentation directory>
1637 -help Show this help.
1638 -nosplit Don't split output to different files.
1639 -split Split output to different files (one file per PHP extension).
1640 -lang Specify the language("en" by default). e.g. -lang en, -lang ja
1641 -output Output directory name("php" by default).
1647 function show_message($message) {
1648 echo $message . PHP_EOL;