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");
26 $argv = $_SERVER["argv"];
27 $argv0 = array_shift ($argv);
28 for ($i = 0; $i < count($argv); ++$i) {
39 $phpdocDir = $argv[$i];
47 /***************** REMOVED FUNCTIONS (START) *************************/
49 // add these functions to $removedFunctions!
51 if (!function_exists('ob_iconv_handler')) {
52 function ob_iconv_handler($contents, $status) {}
54 if (!function_exists('ob_tidyhandler')) {
55 function ob_tidyhandler($input, $mode = 0) {}
57 if (!function_exists('session_register')) {
58 function session_register($name, $_ = null) {}
60 if (!function_exists('session_unregister')) {
61 function session_unregister($name) {}
63 if (!function_exists('session_is_registered')) {
64 function session_is_registered($name) {}
66 if (!function_exists('chroot')) {
67 function chroot($directory) {}
70 /***************** REMOVED FUNCTIONS (END) *************************/
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(
83 'session_is_registered',
86 $functionBlackList = array(
88 'oci_lob_import' => 1,
94 'oci_lob_truncate' => 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,
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,
113 $methodBlackList = array(
120 $preferHtmlProperties = array(
124 $processedFunctions = $functionBlackList;
125 $processedClasses = array();
126 $processedConstants = array();
133 foreach ($extensions as $extName) {
137 print_extension (new ReflectionExtension ($extName));
139 finish_file_output("{$phpDir}/{$extName}.php");
146 $intFunctions = get_defined_functions();
147 foreach ($intFunctions["internal"] as $intFunction) {
148 if (!@$processedFunctions[strtolower($intFunction)]) {
149 print_function (new ReflectionFunction ($intFunction));
153 $intClasses = array_merge (get_declared_classes(), get_declared_interfaces(), get_declared_traits());
154 foreach ($intClasses as $intClass) {
155 if (strpos($intClass, 'NetBeans_') === 0) {
158 if (!@$processedClasses[strtolower($intClass)]) {
159 print_class (new ReflectionClass ($intClass));
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;
177 foreach ($intConstants as $name => $value) {
178 if (!@$processedConstants[$name]) {
179 print_constant ($name, $value);
183 finish_file_output("{$phpDir}/basic.php");
189 foreach ($removedFunctions as $removedFunction) {
190 if (!@$processedFunctions[strtolower($removedFunction)]) {
191 print_function (new ReflectionFunction ($removedFunction));
195 finish_file_output("{$phpDir}/removed.php");
200 $fp = fopen ("{$phpDir}/.list", "w");
201 foreach (glob("{$phpDir}/*.php") as $f) {
202 fwrite ($fp, basename($f));
208 function findVerInfo($file)
210 $url = DOC_URL.$file.".html";
211 $search_for = '<p class="verinfo">';
212 //echo "Reading $url :\n";
214 if (!is_file($url)) {
218 $file_contents = file_get_contents($url);
220 $start_pos = strpos($file_contents, $search_for);
222 if ($start_pos !== 0) {
223 $start_pos += strlen($search_for);
224 $end_pos = strpos($file_contents, '</p>', $start_pos);
226 if ($end_pos !== 0) {
227 $verinfo = substr($file_contents, $start_pos, $end_pos - $start_pos);
228 //echo "Ver. info: $verinfo\n";
236 * Makes generic key from given function name
237 * @param name string Function name
238 * @return string generic key
240 function make_funckey_from_str ($name) {
241 $name = str_replace ("->", "::", $name);
242 $name = str_replace ("()", "", $name);
243 $name = strtolower ($name);
248 * Replaces all invalid charaters with '_' in PHP identifier
249 * @param name PHP identifier
250 * @return string PHP identifier with stripped invalid characters
252 function clean_php_identifier ($name) {
253 $name = preg_replace('/[^\$\w\_]+/', '_', $name);
257 function clean_php_value($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(""", "", $type);
263 $type = strip_tags($type);
268 * Makes generic key from given function reference
269 * @param name ReflectionMethod function reference
270 * @return string generic key
272 function make_funckey_from_ref ($ref) {
273 if ($ref instanceof ReflectionMethod) {
274 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
276 $funckey = strtolower($ref->getName());
280 function make_property_from_ref ($ref) {
281 if ($ref instanceof ReflectionProperty) {
282 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
284 throw new Exception("Unexpected type: ".gettype($ref));
289 function make_classmember_ref ($className, $memberName) {
290 return strtolower($className)."::".strtolower($memberName);
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
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")
305 foreach ($extensions as $extName) {
306 $extName = strtolower($extName);
307 $globPattern = "{$phpdocDir}/en" . BRANCH_DIR . "/reference/{$extName}/*/*.xml";
308 $xml_files = array_merge (
313 $functionsDoc = array();
314 foreach ($xml_files as $xml_file) {
315 $xml = file_get_contents ($xml_file);
317 if (preg_match ('@<refentry.*?xml:id=["\'](.*?)["\'].*?>.*?<refname>(.*?)</refname>\s*(?:<refname>(.*?)</refname>)?.*?<refpurpose>(.*?)</refpurpose>@s', $xml, $match)) {
319 $refnames = array($match[2], $match[3]);
321 foreach ($refnames as $refname) {
322 $refname = trim($refname);
323 if ($refname == '') {
326 $refname = make_funckey_from_str ($refname);
327 if (array_key_exists($refname, $functionsDoc)) {
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;
336 if (preg_match ('@<refsect1\s+role=["\']description["\']>(.*?)</refsect1>@s', $xml, $match)) {
337 $description = $match[1];
338 $function_alias = 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;
349 $methodsynopsis = null;
350 if ($refname == 'number_format') {
351 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $tmp);
353 foreach ($tmp as $key => $val) {
354 $match[$key] = $val[count($val) - 1];
357 if (strpos($refname, '::') !== false) {
358 $methodsynopsis = preg_match ('@<methodsynopsis role="oop">.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
360 if (!$methodsynopsis) {
361 $methodsynopsis = preg_match ('@<methodsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
363 if (!$methodsynopsis) {
364 $methodsynopsis = preg_match ('@<constructorsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</constructorsynopsis>@s', $description, $match);
367 if ($methodsynopsis) {
368 if ($has_object_style && make_funckey_from_str($match[2]) != $refname) {
369 $function_alias = trim($match[2]);
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];
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) {
380 'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
381 'name' => clean_php_identifier(trim($match[4][$i])),
383 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
384 $parameter['isoptional'] = true;
386 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
387 $parameter['isreference'] = true;
389 if (@strlen(trim($match[5][$i]))) {
390 $def = $match[5][$i];
394 $parameter['defaultvalue'] = clean_php_value($def);
395 $parameter['isoptional'] = true;
397 $functionsDoc[$refname]['parameters'][] = $parameter;
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]);
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]));
422 // Create information for function alias
423 if ($function_alias) {
424 $functionsDoc[$function_alias] = $functionsDoc[$refname];
429 return $functionsDoc;
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
437 function parse_phpdoc_fields ($phpdocDir, $extensions) {
438 $xml_files = array();
439 foreach ($extensions as $extName) {
440 $extName = strtolower($extName);
442 $xml_files = array_merge (
444 glob ("{$phpdocDir}/en" . BRANCH_DIR . "/reference/{$extName}/*.xml"),
445 glob ("{$phpdocDir}/en" . BRANCH_DIR . "/reference/{$extName}/*/*.xml")
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];
460 $reference = make_classmember_ref($classname, $name);
461 $fieldsDoc[$reference]['field'] = $name;
462 $fieldsDoc[$reference]['type'] = $type;
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]);
475 if (isset($fieldsDoc)) {
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
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")
493 foreach ($extensions as $extName) {
494 $extName = strtolower($extName);
495 $globPattern = "{$phpdocDir}/en" . BRANCH_DIR . "/reference/{$extName}/*.xml";
496 $xml_files = array_merge (
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)) {
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;
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
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);
544 if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
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]);
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)) {
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]);
565 return $constantsDoc;
569 * Prints ReflectionExtension in format of PHP code
570 * @param extRef ReflectionExtension object
572 function print_extension ($extRef) {
573 global $functionBlackList;
575 print "\n// Start of {$extRef->getName()} v.{$extRef->getVersion()}\n";
578 $classesRef = $extRef->getClasses();
579 if (count ($classesRef) > 0) {
580 foreach ($classesRef as $classRef) {
581 print_class ($classRef);
586 $funcsRef = $extRef->getFunctions();
587 if (count ($funcsRef) > 0) {
588 foreach ($funcsRef as $funcName => $funcRef) {
589 if (array_key_exists($funcName, $functionBlackList)) {
592 print_function ($funcRef);
598 $constsRef = $extRef->getConstants();
599 if (count ($constsRef) > 0) {
600 print_constants ($constsRef);
604 print "// End of {$extRef->getName()} v.{$extRef->getVersion()}\n";
608 * Prints ReflectionClass in format of PHP code
609 * @param classRef ReflectionClass object
610 * @param tabs integer[optional] number of tabs for indentation
612 function print_class (ReflectionClass $classRef, $tabs = 0) {
613 global $processedClasses, $preferHtmlProperties, $methodBlackList;
614 $processedClasses [strtolower($classRef->getName())] = true;
617 print_doccomment ($classRef, $tabs);
619 if ($classRef->isFinal()) print "final ";
621 if ($classRef->isInterface()) {
623 } elseif ($classRef->isTrait()) {
628 print clean_php_identifier($classRef->getName())." ";
630 // print out parent class
631 $parentClassRef = $classRef->getParentClass();
632 if ($parentClassRef) {
633 print "extends {$parentClassRef->getName()} ";
636 // print out interfaces
637 $interfacesRef = $classRef->getInterfaces();
638 if (count ($interfacesRef) > 0) {
639 print $classRef->isInterface() ? "extends " : "implements ";
641 foreach ($interfacesRef as $interfaceRef) {
645 print "{$interfaceRef->getName()}";
651 $traits = $classRef->getTraits();
652 if (count($traits)) {
653 foreach ($traits as $trait => $traitInfo) {
654 print_tabs($tabs + 1);
655 print 'use ' . $trait . ';';
662 $constsRef = $classRef->getConstants();
663 if (count ($constsRef) > 0) {
664 print_class_constants ($classRef, $constsRef, $tabs + 1);
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);
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);
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)) {
696 print_method($classRef, $methodRef, $tabs + 1);
705 * Prints ReflectionProperty in format of PHP code
706 * @param ReflectionProperty $propertyRef object
707 * @param integer[optional] tabs number of tabs for indentation
709 function print_property ($propertyRef, $tabs = 0) {
710 print_doccomment ($propertyRef, $tabs);
712 print_modifiers ($propertyRef, true);
713 $name = $propertyRef->getName();
714 if (substr($name, 0, 1) !== '$') {
721 * Prints Field in format of PHP code
722 * @param NetBeans_Field $field object
723 * @param integer[optional] tabs number of tabs for indentation
725 function print_field (NetBeans_Field $field, $tabs = 0) {
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);
739 foreach (preg_split('%\n|\r%', $fieldDoc) as $line) {
748 print " * @var $fieldType\n";
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);
765 $name = $field->getName();
766 if (substr($name, 0, 1) !== '$') {
772 function print_function ($functionRef, $tabs = 0) {
773 print_method(null, $functionRef, $tabs);
776 function print_method ($classRef, $functionRef, $tabs = 0) {
777 global $functionsDoc;
778 global $processedFunctions;
780 $funckey = make_funckey_from_ref ($functionRef);
781 $processedFunctions[$funckey] = true;
785 print_doccomment ($functionRef, $tabs);
787 if (!($functionRef instanceof ReflectionFunction)) {
788 print_modifiers ($functionRef);
789 $modifiers = Reflection::getModifierNames($functionRef->getModifiers());
793 if ($functionRef->returnsReference()) {
796 $functionName = $functionRef->getName();
797 print "$functionName(";
798 $parameters = @$functionsDoc[$funckey]['parameters'];
800 print_parameters ($parameters);
802 print_parameters_ref ($functionRef->getParameters());
805 $returntype = sanitizeType(@$functionsDoc[$funckey]['returntype']);
807 && $functionName !== '__construct') {
808 print ': ' . $returntype;
811 if ($classRef != null && $classRef->isInterface()) {
813 } elseif (is_array($modifiers)) {
814 foreach ($modifiers as $modifier) {
815 if ($modifier == "abstract") {
831 * Prints ReflectionParameter in format of PHP code
832 * @param parameters array information from PHP.net documentation
834 function print_parameters ($parameters) {
836 foreach ($parameters as $parameter) {
837 if ($parameter['name'] != "...") {
841 $type = sanitizeType($parameter['type']);
845 if (@$parameter['isreference']) {
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)) {
860 $value = "'{$value}'";
873 * Prints ReflectionParameter in format of PHP code
874 * @param paramsRef ReflectionParameter[] array of objects
876 function print_parameters_ref ($paramsRef) {
878 foreach ($paramsRef as $paramRef) {
879 /* @var $paramRef ReflectionParameter */
880 if ($paramRef->allowsNull()) {
883 if ($paramRef->isArray()) {
886 if ($className = get_parameter_classname($paramRef)) {
887 print "{$className} ";
890 $name = $paramRef->getName() ? $paramRef->getName() : "var".($i+1);
891 if ($name != "...") {
895 if ($paramRef->isPassedByReference()) {
899 if ($paramRef->allowsNull()) {
901 } else if ($paramRef->isDefaultValueAvailable()) {
902 $value = $paramRef->getDefaultValue();
903 if (!is_numeric ($value)) {
904 $value = "'{$value}'";
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
917 function print_constants ($constants, $tabs = 0) {
918 foreach ($constants as $name => $value) {
919 print_constant ($name, $value, $tabs);
923 function print_constant ($name, $value = null, $tabs = 0) {
924 global $constantsDoc;
925 global $processedConstants;
926 $processedConstants [$name] = true;
928 if ($value === null) {
929 $value = @constant ($name);
931 $value = escape_const_value ($value);
933 $doc = @$constantsDoc[$name]['doc'];
939 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
941 print " * @link ".make_url($constantsDoc[$name]['id'])."\n";
946 print "define ('{$name}', {$value});\n";
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) {
956 } else if ($value === false) {
958 } else if ($value === true) {
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
969 function print_class_constants ($classRef, $constants, $tabs = 0) {
970 global $constantsDoc;
971 global $processedConstants;
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'];
985 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
987 print " * @link ".make_url($constantsDoc[$idx]['id'])."\n";
992 print "const {$name} = {$value};\n";
997 * Prints modifiers of reflection object in format of PHP code
998 * @param ref Reflection some reflection object
1000 function print_modifiers ($ref, $forFields = false) {
1001 $modifiers = Reflection::getModifierNames ($ref->getModifiers());
1002 if (count ($modifiers) > 0) {
1003 $print = implode(' ', $modifiers);
1005 $print = str_replace("final", "", $print);
1006 $print = str_replace("abstract", "", $print);
1007 $print = str_replace("readonly", "", $print);
1015 * Makes PHP Manual URL from the given ID
1016 * @param id PHP Element ID
1019 function make_url ($id) {
1020 return "http://php.net/manual/en/{$id}.php";
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
1028 function print_doccomment ($ref, $tabs = 0) {
1029 global $functionsDoc;
1032 global $methodBlackList;
1034 $docComment = $ref->getDocComment();
1037 print "{$docComment}\n";
1039 else if ($ref instanceof ReflectionClass) {
1040 $refname = strtolower($ref->getName());
1041 if (@$classesDoc[$refname]) {
1044 $doc = @$classesDoc[$refname]['doc'];
1046 $doc = newline_to_phpdoc ($doc, $tabs);
1048 print " * {$doc}\n";
1051 foreach ($ref->getMethods() as $method) {
1052 if (in_array(strtolower($method->getName()), $methodBlackList)) {
1053 print_magic_method($ref, $method, $tabs);
1056 if (@$classesDoc[$refname]['id']) {
1058 $url = make_url ($classesDoc[$refname]['id']);
1059 print " * @link {$url}\n";
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'];
1072 if (strpos($funckey, "::__construct") === false) {
1073 $returntype = @$functionsDoc[$funckey]['returntype'];
1074 $returndoc = newline_to_phpdoc (@$functionsDoc[$funckey]['returndoc'], $tabs);
1077 $paramsRef = $ref->getParameters();
1078 $parameters = @$functionsDoc[$funckey]['parameters'];
1080 if ($desc || count ($paramsRef) > 0 || $parameters || $returntype) {
1085 print " * {$ver_info}<br/>\n";
1089 print " * {$desc}\n";
1091 if (@$functionsDoc[$funckey]['id']) {
1093 $url = make_url ($functionsDoc[$funckey]['id']);
1094 print " * @link {$url}\n";
1096 if (!@$functionsDoc[$funckey]['deprecated']) {
1098 foreach ($parameters as $parameter) {
1100 print " * @param {$parameter['type']} \${$parameter['name']}";
1101 if (@$parameter['isoptional']) {
1102 print " [optional]";
1104 $paramdoc = @$parameter['paramdoc'];
1105 if ($paramdoc && $paramdoc != "<p>\n</p>") {
1106 $paramdoc = newline_to_phpdoc(@$parameter['paramdoc'], $tabs);
1107 print " {$paramdoc}";
1113 foreach ($paramsRef as $paramRef) {
1115 $name = $paramRef->getName() ? $paramRef->getName() : "var".++$i;
1117 if($className = get_parameter_classname($paramRef)) {
1118 print " {$className}";
1119 if($paramRef->isArray()) {
1124 if($paramRef->isOptional()) {
1125 print " [optional]";
1130 if ($returntype || $returndoc) {
1132 $returntype = 'mixed';
1135 print " * @return " . trim("{$returntype} {$returndoc}") . "\n";
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)) {
1149 print " * @var $fieldType\n";
1156 function print_magic_method(ReflectionClass $classRef, ReflectionMethod $methodRef, $tabs = 0) {
1157 global $functionsDoc;
1158 global $processedFunctions;
1160 $funckey = make_funckey_from_ref($methodRef);
1161 $processedFunctions[$funckey] = true;
1164 print " * @method ";
1165 $returntype = @$functionsDoc[$funckey]['returntype'];
1166 $returndoc = @$functionsDoc[$funckey]['returndoc'];
1170 $returntype = 'mixed';
1172 print $returntype . " ";
1174 print $methodRef->getName() . "(";
1175 $parameters = @$functionsDoc[$funckey]['parameters'];
1177 print_parameters($parameters);
1179 print_parameters_ref($methodRef->getParameters());
1182 $id = @$functionsDoc[$funckey]['id'];
1183 $ver_info = findVerInfo($id);
1184 $docComment = @$functionsDoc[$funckey]['quickref'];
1202 * Converts XML entities to human readable string for PHPDOC
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 ("“", """, $str);
1213 $str = str_replace ("”", """, $str);
1214 $str = preg_replace ("/[\r\n][\t ]/", "\n", $str);
1220 * Converts newlines to PHPDOC prefixes in the given string
1222 * @param tabs integer[optional] number of tabs for indentation
1223 * @return string PHPDOC string
1225 function newline_to_phpdoc ($str, $tabs = 0) {
1226 $str = preg_replace ("@\s*[\r\n]+@", "\n".str_repeat("\t", $tabs)." * ", $str);
1231 * Prints specified number of tabs
1232 * @param tabs integer number of tabs to print
1234 function print_tabs ($tabs) {
1235 print str_repeat("\t", $tabs);
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.
1242 function get_parameter_classname(ReflectionParameter $paramRef) {
1244 if ($classRef = $paramRef->getClass()) {
1245 return $classRef->getName();
1247 } catch (Exception $e) {
1248 if (preg_match('/Class (\w+) does not exist/', $e->getMessage(), $matches)) {
1256 * Starts outputing to the new file
1258 function begin_file_output() {
1264 * Ends outputing, and dumps the output to the specified file
1265 * @param filename File to dump the output
1267 function finish_file_output($filename) {
1268 //if (file_exists ($filename)) {
1269 // rename ($filename, "{$filename}.bak");
1272 file_put_contents (str_replace(" ", "-", $filename), ob_get_contents());
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
1283 function strip_tags_special ($str) {
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);
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);
1313 // XXX, see set_error_handler
1314 function method_to_phpdoc($str) {
1316 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $str, $tmp);
1317 if (!$methodsynopsis) {
1320 $functionsDoc = array();
1322 for ($i = 0; $i < count($tmp); ++$i) {
1323 $refname = trim($tmp[2][$i]);
1324 $functionsDoc[$refname]['methodname'] = $refname;
1325 $parameters = $tmp[3][$i];
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])),
1333 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
1334 $parameter['isoptional'] = true;
1336 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
1337 $parameter['isreference'] = true;
1339 if (@strlen(trim($match[5][$i]))) {
1340 $parameter['defaultvalue'] = clean_php_value($match[5][$i]);
1341 $parameter['isoptional'] = true;
1343 $functionsDoc[$refname]['parameters'][] = $parameter;
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]);
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');
1369 function parse_entities_from_file(array &$entities, $phpdocDir, $filepath) {
1370 $content = file_get_contents($phpdocDir . $filepath);
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];
1379 function replace_entities($text) {
1382 while (preg_match_all('%(\&(?!#)\S+?\;)%', $text, $matches)) {
1383 if (count($matches[1])) {
1384 foreach ($matches[1] as $e) {
1386 if (array_key_exists($e, $entities)) {
1387 $replace = $entities[$e];
1389 if ($replace === null) {
1393 case '&reference.strings.charsets;':
1394 case '&reference.intl.inctimezoneparam;':
1395 case '&reference.intl.incfieldparam;':
1399 die('Entity "' . $e . '" not found' . "\n");
1402 $text = str_replace($e, $replace, $text);
1406 // return back < and >
1408 '&#60;' => '<',
1409 '>' => '>',
1411 return str_replace(array_keys($keep), $keep, $text);
1414 function parse_properties_from_html_file($filepath, $classname) {
1415 $file = DOC_URL . $filepath;
1416 if (!is_file($file)) {
1418 //die('Cannot parse properties from non-existing file: ' . $file);
1421 $html = new DOMDocument();
1422 $html->preserveWhiteSpace = false;
1423 @$html->loadHtmlFile($file);
1424 $xpath = new DOMXPath($html);
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);
1433 if ($docNodes->length) {
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));
1443 $doc[$fieldname] = $tmp->saveHTML();
1445 //die('Unknown node name: ' . $node->nodeName);
1451 $fieldNodes = $xpath->query('//div[@class="classsynopsis"]//div[@class="fieldsynopsis"]');
1452 foreach ($fieldNodes as $fieldNode) {
1453 $field = new NetBeans_Field();
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);
1461 $field->setName($varnameNodes->item(0)->nodeValue);
1463 $modifierNodes = $xpath->query('span[@class="modifier"]', $fieldNode);
1464 foreach ($modifierNodes as $modifierNode) {
1465 $modifier = $modifierNode->nodeValue;
1467 if ($modifier == 'const') {
1468 // constant => do nothing
1471 $field->addModifier($modifier);
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);
1478 $field->setType($typeNodes->item(0)->nodeValue);
1480 if (array_key_exists($field->getName(), $doc)) {
1481 $field->setDocumentation($doc[$field->getName()]);
1489 class NetBeans_Field {
1491 private $modifiers = array();
1495 private $documentation;
1498 public function getModifiers() {
1499 return $this->modifiers;
1503 * @return \NetBeans_Field
1505 public function addModifier($modifier) {
1506 $this->modifiers[] = $modifier;
1511 * @return \NetBeans_Field
1513 public function setModifiers($modifiers) {
1514 $this->modifiers = $modifiers;
1518 public function getType() {
1523 * @return \NetBeans_Field
1525 public function setType($type) {
1526 $this->type = $type;
1530 public function getName() {
1535 * @return \NetBeans_Field
1537 public function setName($name) {
1538 $this->name = $name;
1542 public function getValue() {
1543 return $this->value;
1547 * @return \NetBeans_Field
1549 public function setValue($value) {
1550 $this->value = $value;
1554 public function getDocumentation() {
1555 return $this->documentation;
1559 * @return \NetBeans_Field
1561 public function setDocumentation($documentation) {
1562 $this->documentation = trim($documentation);
1568 function sanitizeType($type) {
1572 if (strpos($type, '|') !== false) {
1573 // ignore 'MyClass|YourClass' cases
1576 if (in_array($type, [
1589 'boolean' => 'bool',
1591 return str_replace(array_keys($convert), $convert, $type);
1594 function isConstant($value) {
1595 $values = explode(' | ', $value);
1596 foreach ($values as $v) {
1597 if (!preg_match('/^(\\w+\\:\\:)?[A-Z0-9_]+$/', $v)) {
1605 * Prints usage help to the screen, and exits from program
1607 function show_help() {
1611 USAGE: {$argv0} [options] <PHP.net documentation directory>
1615 -help Show this help.
1616 -split Split output to different files (one file per PHP extension).