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 $preferHtmlProperties = array(
140 $processedFunctions = $functionBlackList;
141 $processedClasses = array();
142 $processedConstants = array();
149 foreach ($extensions as $extName) {
153 print_extension (new ReflectionExtension ($extName));
155 finish_file_output("{$phpDir}/{$extName}.php");
162 $intFunctions = get_defined_functions();
163 foreach ($intFunctions["internal"] as $intFunction) {
164 if (!@$processedFunctions[strtolower($intFunction)]) {
165 print_function (new ReflectionFunction ($intFunction));
169 $intClasses = array_merge (get_declared_classes(), get_declared_interfaces(), get_declared_traits());
170 foreach ($intClasses as $intClass) {
171 if (strpos($intClass, 'NetBeans_') === 0) {
174 if (!@$processedClasses[strtolower($intClass)]) {
175 print_class (new ReflectionClass ($intClass));
180 $constants = get_defined_constants(true);
181 $intConstants = isset($constants["internal"]) ? $constants["internal"] : array();
182 // add magic constants:
183 $intConstants['__FILE__'] = null;
184 $intConstants['__LINE__'] = null;
185 $intConstants['__CLASS__'] = null;
186 $intConstants['__FUNCTION__'] = null;
187 $intConstants['__METHOD__'] = null;
188 $intConstants['__TRAIT__'] = null;
189 if (version_compare(phpversion(), "5.3.0") >= 0) {
190 $intConstants['__DIR__'] = null;
191 $intConstants['__NAMESPACE__'] = null;
193 foreach ($intConstants as $name => $value) {
194 if (!@$processedConstants[$name]) {
195 print_constant ($name, $value);
199 finish_file_output("{$phpDir}/basic.php");
205 foreach ($removedFunctions as $removedFunction) {
206 if (!@$processedFunctions[strtolower($removedFunction)]) {
207 print_function (new ReflectionFunction ($removedFunction));
211 finish_file_output("{$phpDir}/removed.php");
216 $fp = fopen ("{$phpDir}/.list", "w");
217 foreach (glob("{$phpDir}/*.php") as $f) {
218 fwrite ($fp, basename($f));
224 function findVerInfo($file)
226 $url = DOC_URL.$file.".html";
227 $search_for = '<p class="verinfo">';
228 //echo "Reading $url :\n";
230 if (!is_file($url)) {
234 $file_contents = file_get_contents($url);
236 $start_pos = strpos($file_contents, $search_for);
238 if ($start_pos !== 0) {
239 $start_pos += strlen($search_for);
240 $end_pos = strpos($file_contents, '</p>', $start_pos);
242 if ($end_pos !== 0) {
243 $verinfo = substr($file_contents, $start_pos, $end_pos - $start_pos);
244 //echo "Ver. info: $verinfo\n";
252 * Makes generic key from given function name
253 * @param name string Function name
254 * @return string generic key
256 function make_funckey_from_str ($name) {
257 $name = str_replace ("->", "::", $name);
258 $name = str_replace ("()", "", $name);
259 $name = strtolower ($name);
264 * Replaces all invalid charaters with '_' in PHP identifier
265 * @param name PHP identifier
266 * @return string PHP identifier with stripped invalid characters
268 function clean_php_identifier ($name) {
269 $name = preg_replace('/[^\$\w\_]+/', '_', $name);
273 function clean_php_value($type) {
275 $type = str_replace("&null;", "null", $type);
276 $type = str_replace("&true;", "true", $type);
277 $type = str_replace("&false;", "false", $type);
278 $type = str_replace(""", "", $type);
279 $type = strip_tags($type);
284 * Makes generic key from given function reference
285 * @param name ReflectionMethod function reference
286 * @return string generic key
288 function make_funckey_from_ref ($ref) {
289 if ($ref instanceof ReflectionMethod) {
290 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
292 $funckey = strtolower($ref->getName());
296 function make_property_from_ref ($ref) {
297 if ($ref instanceof ReflectionProperty) {
298 $funckey = make_classmember_ref($ref->getDeclaringClass()->getName(), $ref->getName());
300 throw new Exception("Unexpected type: ".gettype($ref));
305 function make_classmember_ref ($className, $memberName) {
306 return strtolower($className)."::".strtolower($memberName);
311 * Parses PHP documentation
312 * @param phpdocDir string PHP.net documentation directory
313 * @return array Function information gathered from the PHP.net documentation by parsing XML files
315 function parse_phpdoc_functions ($phpdocDir, $extensions, $lang) {
316 $xml_files = array_merge (
317 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*.xml"),
318 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/predefined/*/*.xml"),
319 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/functions/*/*.xml")
321 foreach ($extensions as $extName) {
322 $extName = strtolower($extName);
323 $globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml";
324 $xml_files = array_merge (
329 $functionsDoc = array();
330 foreach ($xml_files as $xml_file) {
331 $xml = file_get_contents ($xml_file);
333 if (preg_match ('@<refentry.*?xml:id=["\'](.*?)["\'].*?>.*?<refname>(.*?)</refname>\s*(?:<refname>(.*?)</refname>)?.*?<refpurpose>(.*?)</refpurpose>@s', $xml, $match)) {
335 $refnames = array($match[2], $match[3]);
337 foreach ($refnames as $refname) {
338 $refname = trim($refname);
339 if ($refname == '') {
342 $refname = make_funckey_from_str ($refname);
343 if (array_key_exists($refname, $functionsDoc)) {
347 $functionsDoc[$refname] = array();
348 $functionsDoc[$refname]['id'] = $id;
349 $functionsDoc[$refname]['quickref'] = xml_to_phpdoc($phpdoc);
350 $functionsDoc[$refname]['deprecated'] = strpos($xml_file, "/oldaliases/") !== false;
352 if (preg_match ('@<refsect1\s+role=["\']description["\']>(.*?)</refsect1>@s', $xml, $match)) {
353 $description = $match[1];
354 $function_alias = null;
356 $has_object_style = false;
357 if (preg_match ('@^(.*?)<classsynopsis>.*?<classname>(.*)</classname>.*?<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>.*?</classsynopsis>(.*)$@s', $description, $match)) {
358 $functionsDoc[$refname]['classname'] = trim($match[2]);
359 $functionsDoc[$refname]['returntype'] = trim($match[3]);
360 $functionsDoc[$refname]['methodname'] = trim($match[4]);
361 $parameters = $match[5];
362 $description = $match[1].$match[6];
363 $has_object_style = true;
365 $methodsynopsis = null;
366 if ($refname == 'number_format') {
367 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $tmp);
369 foreach ($tmp as $key => $val) {
370 $match[$key] = $val[count($val) - 1];
373 if (strpos($refname, '::') !== false) {
374 $methodsynopsis = preg_match ('@<methodsynopsis role="oop">.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
376 if (!$methodsynopsis) {
377 $methodsynopsis = preg_match ('@<methodsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $description, $match);
379 if (!$methodsynopsis) {
380 $methodsynopsis = preg_match ('@<constructorsynopsis>.*?(?:<type>(.*?)</type>.*?)?<methodname>(.*?)</methodname>(.*?)</constructorsynopsis>@s', $description, $match);
383 if ($methodsynopsis) {
384 if ($has_object_style && make_funckey_from_str($match[2]) != $refname) {
385 $function_alias = trim($match[2]);
387 $functionsDoc[$refname]['returntype'] = trim(str_replace('-', '_', $match[1])); // e.g. OCI-Collection -> OCI_Collection
388 $functionsDoc[$refname]['methodname'] = trim($match[2]);
389 $parameters = $match[3];
393 if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
394 for ($i = 0; $i < count($match[0]); ++$i) {
396 'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
397 'name' => clean_php_identifier(trim($match[4][$i])),
399 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
400 $parameter['isoptional'] = true;
402 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
403 $parameter['isreference'] = true;
405 if (@strlen(trim($match[5][$i]))) {
406 $def = $match[5][$i];
410 $parameter['defaultvalue'] = clean_php_value($def);
411 $parameter['isoptional'] = true;
413 $functionsDoc[$refname]['parameters'][] = $parameter;
418 if (preg_match ('@<refsect1\s+role=["\']parameters["\']>(.*?)</refsect1>@s', $xml, $match)) {
419 $parameters = $match[1];
420 if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
421 for ($i = 0; $i < count($match[0]); $i++) {
422 for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
423 if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
424 $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
431 if (preg_match ('@<refsect1\s+role=["\']returnvalues["\']>(.*?)</refsect1>@s', $xml, $match)) {
432 $returnvalues = $match[1];
433 if (preg_match ('@<para>\s*(.*)</para>?@s', $returnvalues, $match)) {
434 $functionsDoc[$refname]['returndoc'] = preg_replace("@^Returns @", "", xml_to_phpdoc ($match[1]));
438 // Create information for function alias
439 if ($function_alias) {
440 $functionsDoc[$function_alias] = $functionsDoc[$refname];
445 return $functionsDoc;
449 * Parses PHP documentation
450 * @param phpdocDir string PHP.net documentation directory
451 * @return array Function information gathered from the PHP.net documentation by parsing XML files
453 function parse_phpdoc_fields ($phpdocDir, $extensions, $lang) {
454 $xml_files = array();
455 foreach ($extensions as $extName) {
456 $extName = strtolower($extName);
458 $xml_files = array_merge (
460 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml"),
461 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*/*.xml")
464 foreach ($xml_files as $xml_file) {
465 $xml = file_get_contents($xml_file);
466 if (preg_match('@<classsynopsisinfo>.*?<classname>(.*)</classname>.*?</classsynopsisinfo>@s', $xml, $matchOffset, PREG_OFFSET_CAPTURE)) {
467 $classname = $matchOffset[1][0];
468 if (preg_match_all("@<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>@s", $xml, $matchOffset,null,$matchOffset[1][1])) {
469 for ($index = 0; $index < count($matchOffset[2]); $index++) {
470 $name = $matchOffset[2][$index];
471 $type= $matchOffset[1][$index];
472 $exploded = explode("::", $name);
473 if (count($exploded) > 1) {
474 $name = $exploded[1];
476 $reference = make_classmember_ref($classname, $name);
477 $fieldsDoc[$reference]['field'] = $name;
478 $fieldsDoc[$reference]['type'] = $type;
482 if (preg_match('@<classsynopsis>.*?<classname>(.*)</classname>.*?<fieldsynopsis>.*?<type>(.*?)</type>.*?<varname.*?>(.*?)</varname>.*?</classsynopsis>@s', $xml, $match)) {
483 $reference = make_classmember_ref($match[1], $match[3]);
484 $fieldsDoc[$reference]['field'] = $match[3];
485 $fieldsDoc[$reference]['type'] = $match[2];
486 //$fieldsDoc[$refname]['quickref'] = trim($match[3]);
491 if (isset($fieldsDoc)) {
498 * Parses PHP documentation
499 * @param phpdocDir string PHP.net documentation directory
500 * @return array Class information gathered from the PHP.net documentation by parsing XML files
502 function parse_phpdoc_classes ($phpdocDir, $extensions, $lang) {
503 $xml_files = array_merge (
504 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/reference.xml"),
505 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/*/classes.xml"),
506 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*/*.xml"),
507 glob ("{$phpdocDir}/{$lang}" . BRANCH_DIR . "/language/*.xml")
509 foreach ($extensions as $extName) {
510 $extName = strtolower($extName);
511 $globPattern = "{$phpdocDir}/{$lang}" . BRANCH_DIR . "/reference/{$extName}/*.xml";
512 $xml_files = array_merge (
518 $classesDoc = array();
519 foreach ($xml_files as $xml_file) {
520 $xml = file_get_contents ($xml_file);
521 if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
523 $prefixId = substr($id, 0, strlen("class."));
524 $clsNamePattern = ($prefixId === "class.") ?
525 '@<ooclass><classname>(.*?)</classname></ooclass>@' :
526 '@<title><classname>(.*?)</classname></title>@';
527 if (preg_match_all ($clsNamePattern, $xml, $match)) {
528 for ($i = 0; $i < count($match[0]); ++$i) {
529 $class = $match[1][$i];
530 $refname = strtolower ($class);
531 $classesDoc[$refname]['id'] = $id;
532 $classesDoc[$refname]['name'] = $class;
533 $offsetPattern = ($prefixId === "class.") ?
534 "@xml:id=[\"'](.*?)\.intro[\"']@i" :
535 "@<title><classname>{$class}</classname></title>@";
536 if (preg_match ($offsetPattern , $xml, $matchOffset,PREG_OFFSET_CAPTURE)) {
537 $docPattern = '@<para>(.*?)</para>@s';
538 if (preg_match ($docPattern, $xml, $match2,null,$matchOffset[0][1])) {
539 $doc = xml_to_phpdoc($match2[1]);
540 $classesDoc[$refname]['doc'] = $doc;
551 * Parses PHP documentation
552 * @param phpdocDir string PHP.net documentation directory
553 * @return array Constant information gathered from the PHP.net documentation by parsing XML files
555 function parse_phpdoc_constants ($phpdocDir) {
556 exec ("find ".addslashes($phpdocDir)." -name \"*constants.xml\"", $xml_files);
557 foreach ($xml_files as $xml_file) {
558 $xml = file_get_contents ($xml_file);
560 if (preg_match ('@xml:id=["\'](.*?)["\']@', $xml, $match)) {
562 if (preg_match_all ('@<term>\s*<constant>([a-zA-Z_:][a-zA-Z0-9_:]*)</constant>.*?</term>.*?<listitem>(.*?)</listitem>@s', $xml, $match)) {
563 for ($i = 0; $i < count($match[0]); ++$i) {
564 $constant = $match[1][$i];
565 $constantsDoc[$constant]['id'] = $id;
566 $constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
570 '@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>\d+</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)
571 || preg_match_all ('@<entry>\s*<constant>([a-zA-Z_][a-zA-Z0-9_]*)</constant>.*?</entry>\s*<entry>(.*?)</entry>@s', $xml, $match)) {
573 for ($i = 0; $i < count($match[0]); ++$i) {
574 $constant = $match[1][$i];
575 $constantsDoc[$constant]['id'] = $id;
576 $constantsDoc[$constant]['doc'] = xml_to_phpdoc($match[2][$i]);
581 return $constantsDoc;
585 * Prints ReflectionExtension in format of PHP code
586 * @param extRef ReflectionExtension object
588 function print_extension ($extRef) {
589 global $functionBlackList;
591 print "\n// Start of {$extRef->getName()} v.{$extRef->getVersion()}\n";
594 $classesRef = $extRef->getClasses();
595 if (count ($classesRef) > 0) {
596 foreach ($classesRef as $classRef) {
597 print_class ($classRef);
602 $funcsRef = $extRef->getFunctions();
603 if (count ($funcsRef) > 0) {
604 foreach ($funcsRef as $funcName => $funcRef) {
605 if (array_key_exists($funcName, $functionBlackList)) {
608 print_function ($funcRef);
614 $constsRef = $extRef->getConstants();
615 if (count ($constsRef) > 0) {
616 print_constants ($constsRef);
620 print "// End of {$extRef->getName()} v.{$extRef->getVersion()}\n";
624 * Prints ReflectionClass in format of PHP code
625 * @param classRef ReflectionClass object
626 * @param tabs integer[optional] number of tabs for indentation
628 function print_class (ReflectionClass $classRef, $tabs = 0) {
629 global $processedClasses, $preferHtmlProperties;
630 $processedClasses [strtolower($classRef->getName())] = true;
633 print_doccomment ($classRef, $tabs);
635 if ($classRef->isFinal()) print "final ";
637 if ($classRef->isInterface()) {
639 } elseif ($classRef->isTrait()) {
642 if ($classRef->isAbstract()) {
647 print clean_php_identifier($classRef->getName())." ";
649 // print out parent class
650 $parentClassRef = $classRef->getParentClass();
651 if ($parentClassRef) {
652 print "extends {$parentClassRef->getName()} ";
655 // print out interfaces
656 $interfacesRef = $classRef->getInterfaces();
657 if (count ($interfacesRef) > 0) {
658 print $classRef->isInterface() ? "extends " : "implements ";
660 foreach ($interfacesRef as $interfaceRef) {
664 print "{$interfaceRef->getName()}";
670 $traits = $classRef->getTraits();
671 if (count($traits)) {
672 foreach ($traits as $trait => $traitInfo) {
673 print_tabs($tabs + 1);
674 print 'use ' . $trait . ';';
681 $constsRef = $classRef->getConstants();
682 if (count ($constsRef) > 0) {
683 print_class_constants ($classRef, $constsRef, $tabs + 1);
687 // process properties
688 $propertiesRef = $classRef->getProperties();
689 if (!in_array(strtolower($classRef->getName()), $preferHtmlProperties)
690 && count ($propertiesRef) > 0) {
691 foreach ($propertiesRef as $propertyRef) {
692 print_property ($propertyRef, $tabs + 1);
696 // #188245 - try to find them directly in HTML
697 $properties = parse_properties_from_html_file('class.' . strtolower($classRef->getName()) . '.html', $classRef->getName());
698 if (count($properties)) {
699 foreach ($properties as $property) {
700 print_field($property, $tabs + 1);
707 /* @var $classRef ReflectionClass */
708 $methodsRef = $classRef->getMethods();
709 if (count ($methodsRef) > 0) {
710 foreach ($methodsRef as $methodRef) {
711 print_method($classRef, $methodRef, $tabs + 1);
720 * Prints ReflectionProperty in format of PHP code
721 * @param ReflectionProperty $propertyRef object
722 * @param integer[optional] tabs number of tabs for indentation
724 function print_property ($propertyRef, $tabs = 0) {
725 print_doccomment ($propertyRef, $tabs);
727 print_modifiers ($propertyRef, true);
728 $name = $propertyRef->getName();
729 if (substr($name, 0, 1) !== '$') {
736 * Prints Field in format of PHP code
737 * @param NetBeans_Field $field object
738 * @param integer[optional] tabs number of tabs for indentation
740 function print_field (NetBeans_Field $field, $tabs = 0) {
742 $fieldType = $field->getType();
743 $fieldDoc = $field->getDocumentation();
744 $fieldDoc = trim(strip_tags($fieldDoc, '<p>,<strong>,<code>,<a>'));
745 // replace hyperlinks
746 $fieldDoc = preg_replace('%<(a|strong)[^>]*>%', '<b>', $fieldDoc);
747 $fieldDoc = preg_replace('%<p[^>]+>%', '<p>', $fieldDoc);
748 $fieldDoc = str_replace('</a>', '</b>', $fieldDoc);
749 $fieldDoc = str_replace('</strong>', '</b>', $fieldDoc);
750 $fieldDoc = preg_replace('%^<p>(.+)</p>$%s', '<p style="margin-top:0;">\\1</p>', $fieldDoc);
754 foreach (preg_split('%\n|\r%', $fieldDoc) as $line) {
763 print " * @var $fieldType\n";
769 $print = implode(' ', $field->getModifiers());
770 $print = str_replace("final", "", $print);
771 $print = str_replace("abstract", "", $print);
772 $print = str_replace("readonly", "", $print);
773 $print = trim($print);
780 $name = $field->getName();
781 if (substr($name, 0, 1) !== '$') {
787 function print_function ($functionRef, $tabs = 0) {
788 print_method(null, $functionRef, $tabs);
791 function print_method ($classRef, $functionRef, $tabs = 0) {
792 global $functionsDoc;
793 global $processedFunctions;
795 $funckey = make_funckey_from_ref ($functionRef);
796 $processedFunctions[$funckey] = true;
800 print_doccomment ($functionRef, $tabs);
802 if (!($functionRef instanceof ReflectionFunction)) {
803 print_modifiers ($functionRef);
804 $modifiers = Reflection::getModifierNames($functionRef->getModifiers());
808 if ($functionRef->returnsReference()) {
811 $functionName = $functionRef->getName();
812 print "$functionName(";
813 $parameters = @$functionsDoc[$funckey]['parameters'];
815 print_parameters ($parameters);
817 print_parameters_ref ($functionRef->getParameters());
820 $returntype = sanitizeType(@$functionsDoc[$funckey]['returntype']);
822 && $functionName !== '__construct') {
823 print ': ' . $returntype;
826 if ($classRef != null && $classRef->isInterface()) {
828 } elseif (is_array($modifiers)) {
829 foreach ($modifiers as $modifier) {
830 if ($modifier == "abstract") {
846 * Prints ReflectionParameter in format of PHP code
847 * @param parameters array information from PHP.net documentation
849 function print_parameters ($parameters) {
851 foreach ($parameters as $parameter) {
852 if ($parameter['name'] != "...") {
856 $type = sanitizeType($parameter['type']);
860 if (@$parameter['isreference']) {
863 print "\${$parameter['name']}";
864 if (@$parameter['isoptional']) {
865 if (array_key_exists('defaultvalue', $parameter)) {
866 $value = $parameter['defaultvalue'];
867 if ((is_numeric ($value) && $type != 'string')
868 || in_array(strtolower($value), array('true', 'false', 'null', 'array()'))
869 || (substr($value, 0, 1) == '\'' && substr($value, -1) == '\'')
870 || (substr($value, 0, 1) == '"' && substr($value, -1) == '"')
871 || (substr($value, 0, 2) == '__' && substr($value, -2) == '__')
872 || isConstant($value)) {
875 $value = "'{$value}'";
888 * Prints ReflectionParameter in format of PHP code
889 * @param paramsRef ReflectionParameter[] array of objects
891 function print_parameters_ref ($paramsRef) {
893 foreach ($paramsRef as $paramRef) {
894 /* @var $paramRef ReflectionParameter */
895 if ($paramRef->allowsNull()) {
898 if ($paramRef->isArray()) {
901 if ($className = get_parameter_classname($paramRef)) {
902 print "{$className} ";
905 $name = $paramRef->getName() ? $paramRef->getName() : "var".($i+1);
906 if ($name != "...") {
910 if ($paramRef->isPassedByReference()) {
914 if ($paramRef->allowsNull()) {
916 } else if ($paramRef->isDefaultValueAvailable()) {
917 $value = $paramRef->getDefaultValue();
918 if (!is_numeric ($value)) {
919 $value = "'{$value}'";
928 * Prints constants in format of PHP code
929 * @param constants array containing constants, where key is a name of constant
930 * @param tabs integer[optional] number of tabs for indentation
932 function print_constants ($constants, $tabs = 0) {
933 foreach ($constants as $name => $value) {
934 print_constant ($name, $value, $tabs);
938 function print_constant ($name, $value = null, $tabs = 0) {
939 global $constantsDoc;
940 global $processedConstants;
941 $processedConstants [$name] = true;
943 if ($value === null) {
944 $value = @constant ($name);
946 $value = escape_const_value ($value);
948 $doc = @$constantsDoc[$name]['doc'];
954 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
956 print " * @link ".make_url($constantsDoc[$name]['id'])."\n";
961 print "define ('{$name}', {$value});\n";
964 function escape_const_value ($value) {
965 if (is_resource($value)) {
966 $value = "\"${value}\"";
967 } else if (!is_numeric ($value) && !is_bool ($value) && $value !== null) {
968 $value = '"'.addcslashes ($value, "\"\r\n\t").'"';
969 } else if ($value === null) {
971 } else if ($value === false) {
973 } else if ($value === true) {
980 * Prints class constants in format of PHP code
981 * @param constants array containing constants, where key is a name of constant
982 * @param tabs integer[optional] number of tabs for indentation
984 function print_class_constants ($classRef, $constants, $tabs = 0) {
985 global $constantsDoc;
986 global $processedConstants;
989 //$doc = @$constantsDoc[$name]['doc'];
990 foreach ($constants as $name => $value) {
991 $value = escape_const_value ($value);
992 $clsName = $classRef->getName();
993 $idx = "$clsName::$name";
994 $doc = @$constantsDoc[$idx]['doc'];
1000 print " * ".newline_to_phpdoc($doc, $tabs)."\n";
1002 print " * @link ".make_url($constantsDoc[$idx]['id'])."\n";
1007 print "const {$name} = {$value};\n";
1012 * Prints modifiers of reflection object in format of PHP code
1013 * @param ref Reflection some reflection object
1015 function print_modifiers ($ref, $forFields = false) {
1016 $modifiers = Reflection::getModifierNames ($ref->getModifiers());
1017 if (count ($modifiers) > 0) {
1018 $print = implode(' ', $modifiers);
1020 $print = str_replace("final", "", $print);
1021 $print = str_replace("abstract", "", $print);
1022 $print = str_replace("readonly", "", $print);
1030 * Makes PHP Manual URL from the given ID
1031 * @param id PHP Element ID
1034 function make_url ($id) {
1036 return "http://php.net/manual/{$lang}/{$id}.php";
1040 * Prints PHPDOC comment before specified reflection object
1041 * @param ref Reflection some reflection object
1042 * @param tabs integer[optional] number of tabs for indentation
1044 function print_doccomment ($ref, $tabs = 0) {
1045 global $functionsDoc;
1049 $docComment = $ref->getDocComment();
1052 print "{$docComment}\n";
1054 else if ($ref instanceof ReflectionClass) {
1055 $refname = strtolower($ref->getName());
1056 if (@$classesDoc[$refname]) {
1059 $doc = @$classesDoc[$refname]['doc'];
1061 $doc = newline_to_phpdoc ($doc, $tabs);
1063 print " * {$doc}\n";
1066 foreach ($ref->getMethods() as $method) {
1067 print_magic_method($ref, $method, $tabs);
1069 if (@$classesDoc[$refname]['id']) {
1071 $url = make_url ($classesDoc[$refname]['id']);
1072 print " * @link {$url}\n";
1078 else if ($ref instanceof ReflectionFunctionAbstract) {
1079 $funckey = make_funckey_from_ref ($ref);
1080 $id = @$functionsDoc[$funckey]['id'];
1081 $ver_info = findVerInfo($id);
1082 $desc = @$functionsDoc[$funckey]['quickref'];
1085 if (strpos($funckey, "::__construct") === false) {
1086 $returntype = @$functionsDoc[$funckey]['returntype'];
1087 $returndoc = newline_to_phpdoc (@$functionsDoc[$funckey]['returndoc'], $tabs);
1090 $paramsRef = $ref->getParameters();
1091 $parameters = @$functionsDoc[$funckey]['parameters'];
1093 if ($desc || count ($paramsRef) > 0 || $parameters || $returntype) {
1098 print " * {$ver_info}<br/>\n";
1102 print " * {$desc}\n";
1104 if (@$functionsDoc[$funckey]['id']) {
1106 $url = make_url ($functionsDoc[$funckey]['id']);
1107 print " * @link {$url}\n";
1109 if (!@$functionsDoc[$funckey]['deprecated']) {
1111 foreach ($parameters as $parameter) {
1113 print " * @param {$parameter['type']} \${$parameter['name']}";
1114 if (@$parameter['isoptional']) {
1115 print " [optional]";
1117 $paramdoc = @$parameter['paramdoc'];
1118 if ($paramdoc && $paramdoc != "<p>\n</p>") {
1119 $paramdoc = newline_to_phpdoc(@$parameter['paramdoc'], $tabs);
1120 print " {$paramdoc}";
1126 foreach ($paramsRef as $paramRef) {
1128 $name = $paramRef->getName() ? $paramRef->getName() : "var".++$i;
1130 if($className = get_parameter_classname($paramRef)) {
1131 print " {$className}";
1132 if($paramRef->isArray()) {
1137 if($paramRef->isOptional()) {
1138 print " [optional]";
1143 if ($returntype || $returndoc) {
1145 $returntype = 'mixed';
1148 print " * @return " . trim("{$returntype} {$returndoc}") . "\n";
1154 }else if ($ref instanceof ReflectionProperty) {
1155 $property_from_ref = make_property_from_ref($ref);
1156 $fieldName = @$fieldsDoc[$property_from_ref]['field'];
1157 $fieldType = @$fieldsDoc[$property_from_ref]['type'];
1158 if (isset ($fieldName) && isset ($fieldType)) {
1162 print " * @var $fieldType\n";
1169 function print_magic_method(ReflectionClass $classRef, ReflectionMethod $methodRef, $tabs = 0) {
1170 global $functionsDoc;
1171 global $processedFunctions;
1173 $funckey = make_funckey_from_ref($methodRef);
1174 $processedFunctions[$funckey] = true;
1177 print " * @method ";
1178 $returntype = @$functionsDoc[$funckey]['returntype'];
1179 $returndoc = @$functionsDoc[$funckey]['returndoc'];
1183 $returntype = 'mixed';
1185 print $returntype . " ";
1187 print $methodRef->getName() . "(";
1188 $parameters = @$functionsDoc[$funckey]['parameters'];
1190 print_parameters($parameters);
1192 print_parameters_ref($methodRef->getParameters());
1195 $id = @$functionsDoc[$funckey]['id'];
1196 $ver_info = findVerInfo($id);
1197 $docComment = @$functionsDoc[$funckey]['quickref'];
1215 * Converts XML entities to human readable string for PHPDOC
1219 function xml_to_phpdoc ($str) {
1220 $str = str_replace ("&php.ini;", "###(i)###php.ini###(/i)###", $str); // XXX will be replaced in strip_tags_special()
1221 $str = replace_entities($str);
1222 $str = strip_tags_special ($str);
1223 $str = preg_replace ("/ */", " ", $str);
1224 $str = str_replace ("*/", "* /", $str);
1225 $str = str_replace ("“", """, $str);
1226 $str = str_replace ("”", """, $str);
1227 $str = preg_replace ("/[\r\n][\t ]/", "\n", $str);
1233 * Converts newlines to PHPDOC prefixes in the given string
1235 * @param tabs integer[optional] number of tabs for indentation
1236 * @return string PHPDOC string
1238 function newline_to_phpdoc ($str, $tabs = 0) {
1239 $str = preg_replace ("@\s*[\r\n]+@", "\n".str_repeat("\t", $tabs)." * ", $str);
1244 * Prints specified number of tabs
1245 * @param tabs integer number of tabs to print
1247 function print_tabs ($tabs) {
1248 print str_repeat("\t", $tabs);
1252 * Returns class name from given parameter reference, this method is a workaround
1253 * for the case when exception is thrown from getClass() when such classname does not exist.
1255 function get_parameter_classname(ReflectionParameter $paramRef) {
1257 if ($classRef = $paramRef->getClass()) {
1258 return $classRef->getName();
1260 } catch (Exception $e) {
1261 if (preg_match('/Class (\w+) does not exist/', $e->getMessage(), $matches)) {
1269 * Starts outputing to the new file
1271 function begin_file_output() {
1277 * Ends outputing, and dumps the output to the specified file
1278 * @param filename File to dump the output
1280 function finish_file_output($filename) {
1281 //if (file_exists ($filename)) {
1282 // rename ($filename, "{$filename}.bak");
1285 file_put_contents (str_replace(" ", "-", $filename), ob_get_contents());
1290 * Strips xml tags from the string like the standard strip_tags() function
1291 * would do, but also translates some of the docbook tags (such as tables
1292 * an paragraphs) to proper html tags
1296 function strip_tags_special ($str) {
1298 // $str = method_to_phpdoc($str);
1299 // first mask and translate the tags to preseve
1300 $str = preg_replace ("/<(\/?)table>/", "###($1table)###", $str);
1301 $str = str_replace ("<row>", "###(tr valign=\"top\")###", $str);
1302 $str = str_replace ("</row>", "###(/tr)###", $str);
1303 $str = preg_replace ("/<(\/?)entry>/", "###($1td)###", $str);
1304 $str = preg_replace ("/<(\/?)para>/", "###($1p)###", $str);
1305 $str = preg_replace ("/<(\/?)p>/", "###($1p)###", $str);
1307 $str = str_replace ("<![CDATA[", "###(pre)###", $str);
1308 $str = str_replace ("]]>", "###(/pre)###", $str);
1309 // preserve php samples; XXX sample for preg_match_all
1310 $str = str_replace ("<?php", "###(code)###", $str);
1311 $str = str_replace ("?>", "###(/code)###", $str);
1312 // handle "<pre><code>"
1313 $str = preg_replace ("/###\(pre\)###\s*\n\s*###\(code\)###/", "###(code)###", $str);
1314 $str = preg_replace ("/###\(\/code\)###\s*\n\s*###\(\/pre\)###/", "###(/code)###", $str);
1315 // constant & function etc.
1316 $str = preg_replace ("%<(/)?(constant|function|classname|methodname|methodparam)[^>]*>%", "###(\\1b)###", $str);
1317 $str = preg_replace ("%<(/)?(parameter)[^>]*>%", "###(\\1i)###", $str);
1318 // now strip the remaining tags
1319 $str = strip_tags ($str);
1320 // and restore the translated ones
1321 $str = str_replace ("###(", "<", $str);
1322 $str = str_replace (")###", ">", $str);
1326 // XXX, see set_error_handler
1327 function method_to_phpdoc($str) {
1329 $methodsynopsis = preg_match_all ('@<methodsynopsis>.*?<type>(.*?)</type>.*?<methodname>(.*?)</methodname>(.*?)</methodsynopsis>@s', $str, $tmp);
1330 if (!$methodsynopsis) {
1333 $functionsDoc = array();
1335 for ($i = 0; $i < count($tmp); ++$i) {
1336 $refname = trim($tmp[2][$i]);
1337 $functionsDoc[$refname]['methodname'] = $refname;
1338 $parameters = $tmp[3][$i];
1340 if (preg_match_all ('@<methodparam\s*(.*?)>.*?<type>(.*?)</type>.*?<parameter\s*(.*?)>(.*?)</parameter>(?:<initializer>(.+?)</initializer>)?.*?</methodparam>@s', $parameters, $match)) {
1341 for ($i = 0; $i < count($match[0]); ++$i) {
1342 $parameter = array (
1343 'type' => trim(str_replace('-', '_', $match[2][$i])), // e.g. OCI-Collection -> OCI_Collection
1344 'name' => clean_php_identifier(trim($match[4][$i])),
1346 if (preg_match ('@choice=[\'"]opt[\'"]@', $match[1][$i])) {
1347 $parameter['isoptional'] = true;
1349 if (preg_match ('@role=[\'"]reference[\'"]@', $match[3][$i])) {
1350 $parameter['isreference'] = true;
1352 if (@strlen(trim($match[5][$i]))) {
1353 $parameter['defaultvalue'] = clean_php_value($match[5][$i]);
1354 $parameter['isoptional'] = true;
1356 $functionsDoc[$refname]['parameters'][] = $parameter;
1359 if (preg_match_all('@<varlistentry\s*.*?>.*?<parameter>(.*?)</parameter>.*?<listitem\s*.*?>(.*?)</listitem>.*?</varlistentry>@s', $parameters, $match)) {
1360 for ($i = 0; $i < count($match[0]); $i++) {
1361 for ($j = 0; $j < count(@$functionsDoc[$refname]['parameters']); $j++) {
1362 if (clean_php_identifier(trim($match[1][$i])) == $functionsDoc[$refname]['parameters'][$j]['name']) {
1363 $functionsDoc[$refname]['parameters'][$j]['paramdoc'] = xml_to_phpdoc ($match[2][$i]);
1374 function parse_entities($phpdocDir, $lang) {
1375 $entities = array();
1376 parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-defs.ent");
1377 parse_entities_from_file($entities, $phpdocDir, "/{$lang}/language-snippets.ent");
1378 parse_entities_from_file($entities, $phpdocDir, '/doc-base/docbook/docbook-xml/ent/isonum.ent');
1379 parse_entities_from_file($entities, $phpdocDir, '/doc-base/entities/global.ent');
1382 function parse_entities_from_file(array &$entities, $phpdocDir, $filepath) {
1383 $content = file_get_contents($phpdocDir . $filepath);
1385 preg_match_all('%\!ENTITY\s+(\S+)\s+([\'"])([^\\2]+?)\\2\s*>%m', $content, $matches);
1386 if (array_key_exists(1, $matches) && count($matches[1])) {
1387 for ($i = 0; $i < count($matches[2]); $i++) {
1388 $entities['&' . $matches[1][$i] . ';'] = $matches[3][$i];
1392 function replace_entities($text) {
1395 while (preg_match_all('%(\&(?!#)\S+?\;)%', $text, $matches)) {
1396 if (count($matches[1])) {
1397 foreach ($matches[1] as $e) {
1399 if (array_key_exists($e, $entities)) {
1400 $replace = $entities[$e];
1402 if ($replace === null) {
1406 case '&reference.strings.charsets;':
1407 case '&reference.intl.inctimezoneparam;':
1408 case '&reference.intl.incfieldparam;':
1412 die('Entity "' . $e . '" not found' . "\n");
1415 $text = str_replace($e, $replace, $text);
1419 // return back < and >
1421 '&#60;' => '<',
1422 '>' => '>',
1424 return str_replace(array_keys($keep), $keep, $text);
1427 function parse_properties_from_html_file($filepath, $classname) {
1428 $file = DOC_URL . $filepath;
1429 if (!is_file($file)) {
1431 //die('Cannot parse properties from non-existing file: ' . $file);
1434 $html = new DOMDocument();
1435 $html->preserveWhiteSpace = false;
1436 @$html->loadHtmlFile($file);
1437 $xpath = new DOMXPath($html);
1440 $docNodes = $xpath->query('//div[@id="' . strtolower($classname) . '.props"]/dl');
1441 if (!$docNodes->length) {
1442 //die('Documentation not found for properties in file: ' . $file);
1443 } elseif ($docNodes->length > 1) {
1444 die('More documentations found for properties in file: ' . $file);
1446 if ($docNodes->length) {
1448 foreach ($docNodes->item(0)->childNodes as $node) {
1449 if ($node->nodeName == 'dt') {
1450 $fieldname = trim($node->nodeValue);
1451 } elseif ($node->nodeName == 'dd') {
1452 $tmp = new DOMDocument();
1453 foreach ($node->childNodes as $child) {
1454 $tmp->appendChild($tmp->importNode($child, true));
1456 $doc[$fieldname] = $tmp->saveHTML();
1458 //die('Unknown node name: ' . $node->nodeName);
1464 $fieldNodes = $xpath->query('//div[@class="classsynopsis"]//div[@class="fieldsynopsis"]');
1465 foreach ($fieldNodes as $fieldNode) {
1466 $field = new NetBeans_Field();
1468 $varnameNodes = $xpath->query('var//var[@class="varname"]', $fieldNode);
1469 if (!$varnameNodes->length) {
1470 die('Varname not found for property in file: ' . $file);
1471 } elseif ($varnameNodes->length > 1) {
1472 die('More varnames found for property in file: ' . $file);
1474 $field->setName($varnameNodes->item(0)->nodeValue);
1476 $modifierNodes = $xpath->query('span[@class="modifier"]', $fieldNode);
1477 foreach ($modifierNodes as $modifierNode) {
1478 $modifier = $modifierNode->nodeValue;
1480 if ($modifier == 'const') {
1481 // constant => do nothing
1484 $field->addModifier($modifier);
1487 $typeNodes = $xpath->query('span[@class="type"]', $fieldNode);
1488 if ($typeNodes->length > 1) {
1489 die('More types found for property ' . $field->getName() . ' in file: ' . $file);
1491 $field->setType($typeNodes->item(0)->nodeValue);
1493 if (array_key_exists($field->getName(), $doc)) {
1494 $field->setDocumentation($doc[$field->getName()]);
1502 class NetBeans_Field {
1504 private $modifiers = array();
1508 private $documentation;
1511 public function getModifiers() {
1512 return $this->modifiers;
1516 * @return \NetBeans_Field
1518 public function addModifier($modifier) {
1519 $this->modifiers[] = $modifier;
1524 * @return \NetBeans_Field
1526 public function setModifiers($modifiers) {
1527 $this->modifiers = $modifiers;
1531 public function getType() {
1536 * @return \NetBeans_Field
1538 public function setType($type) {
1539 $this->type = $type;
1543 public function getName() {
1548 * @return \NetBeans_Field
1550 public function setName($name) {
1551 $this->name = $name;
1555 public function getValue() {
1556 return $this->value;
1560 * @return \NetBeans_Field
1562 public function setValue($value) {
1563 $this->value = $value;
1567 public function getDocumentation() {
1568 return $this->documentation;
1572 * @return \NetBeans_Field
1574 public function setDocumentation($documentation) {
1575 $this->documentation = trim($documentation);
1581 function sanitizeType($type) {
1585 if (strpos($type, '|') !== false) {
1586 // ignore 'MyClass|YourClass' cases
1589 if (in_array($type, [
1602 'boolean' => 'bool',
1604 return str_replace(array_keys($convert), $convert, $type);
1607 function isConstant($value) {
1608 $values = explode(' | ', $value);
1609 foreach ($values as $v) {
1610 if (!preg_match('/^(\\w+\\:\\:)?[A-Z0-9_]+$/', $v)) {
1618 * Prints usage help to the screen, and exits from program
1620 function show_help() {
1624 USAGE: {$argv0} [options] <PHP.net documentation directory>
1628 -help Show this help.
1629 -nosplit Don't split output to different files.
1630 -split Split output to different files (one file per PHP extension).
1631 -lang Specify the language("en" by default). e.g. -lang en, -lang ja
1632 -output Output directory name("php" by default).
1638 function show_message($message) {
1639 echo $message . PHP_EOL;