Files
archived-doc-base/scripts/docgen/docgen.php

1321 lines
43 KiB
PHP

<?php
/*
+----------------------------------------------------------------------+
| Copyright (c) 1997-2023 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt. |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net, so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Felipe Pena <felipe@php.net> |
| Philip Olson <philip@php.net> |
+----------------------------------------------------------------------+
$Id$
*/
if (!(extension_loaded('reflection') && extension_loaded('pcre'))) {
die("ERROR: Docgen requires the Reflection and PCRE extensions.\n");
}
/* Constants */
define('DOC_METHOD', 1<<0);
define('DOC_CONSTRUCTOR', 1<<1);
define('DOC_PROPERTY', 1<<2);
define('DOC_CLASS', 1<<3);
define('DOC_EXTENSION', 1<<4);
define('DOC_FUNCTION', 1<<5);
/* Templates */
$TEMPLATE = array(
DOC_METHOD => 'method.tpl',
DOC_CONSTRUCTOR => 'constructor.tpl',
DOC_PROPERTY => 'property.tpl',
DOC_CLASS => 'class.tpl',
DOC_FUNCTION => 'function.tpl'
);
/* Default files for extensions */
$DOC_EXT = array(
'book.xml' => 'book.tpl',
'setup.xml' => 'setup.tpl',
'constants.xml' => 'constants.tpl',
'configure.xml' => 'configure.tpl',
'reference.xml' => 'reference.tpl',
'ini.xml' => 'ini.tpl',
'versions.xml' => 'versions.tpl',
);
function usage() { /* {{{ */
$v = PHP_VERSION;
print <<<USAGE
Usage:
php docgen.php <options>
Example options:
-e dom (an entire extension)
-f preg_replace (a single function)
-c simplexmlelement -m xpath -m asxml (a couple class methods)
Notes:
Long options are supported with PHP 5.3.0+, and you use $v
Be sure the desired extension to document is loaded into PHP
Options:
-c,--class -- class name
-e,--extension -- extension name
-f,--function -- function name
-h,--help -- show this help
-i,--include -- includes a PHP file
(shortcut for: php -dauto_prepend_file=streams.php docgen.php)
-m,--method -- method name (require -c)
-o,--output -- output dir
-p,--pecl -- is a PECL extension
-d,--phpdoc -- path to files in phpdoc directory
-a,--copy -- copy created files to phpdoc directory
-s,--seealso -- add empty see also sections
-x,--example -- add empty example sections
-q,--quiet -- quiet mode
-v,--version -- show the version
-V,--verbose -- disable show progress
-t,--test -- Test/debug mode (affects a few activities)
USAGE;
}
/* }}} */
function find_function($ext, ?ReflectionMethod $method = NULL, ?ReflectionFunction $func = NULL) { /* {{{ */
$ext_name = strtolower($ext);
$ext = new ReflectionExtension($ext);
if ($method) {
preg_match_all('/[A-Z][a-z]+|[a-z]+/', $method->name, $parts);
$possibleNames = array(
$ext_name .'_'. strtolower(implode('_', $parts[0])),
$ext_name .'_'. strtolower(implode('_', array_reverse($parts[0])))
);
foreach ($ext->getFunctions() as $function) {
if (in_array($function->name, $possibleNames)) {
return $function;
}
}
} else {
$func_name = preg_replace('/^'. $ext_name .'_/', '', $func->name);
preg_match_all('/[a-z]+/', $func_name, $parts);
$possibleNames = array(
strtolower(implode($parts[0])),
strtolower(implode(array_reverse($parts[0])))
);
foreach ($ext->getClasses() as $class) {
foreach ($class->getMethods() as $method) {
if (in_array(strtolower($method->name), $possibleNames)) {
return $method;
}
}
}
}
return false;
}
/* }}} */
function add_warning($err_msg) { /* {{{ */
global $WARNING, $INFO;
$WARNING[$INFO['actual_file']][] = $err_msg;
}
/* }}} */
function create_markup_to_modifiers(int $modifierFlags): array { /* {{{ */
$modifiers = Reflection::getModifierNames($modifierFlags);
$result = [];
foreach ($modifiers as $modifier) {
$result[] = '<modifier>'. $modifier .'</modifier>';
}
return $result;
}
/* }}} */
function format_id($name) { /* {{{ */
return preg_replace(array('/[^[:alnum:]]/', '/^-+/'), array('-', ''), strtolower($name));
}
/* }}} */
function format_filename($name) { /* {{{ */
$name = strtolower(trim($name));
$name = ltrim($name, '_');
$name = str_replace(array('_', '\\'), array('-', '.'), $name);
return $name;
}
/* }}} */
function format_config($name) { /* {{{ */
return preg_replace('/_/', '-', $name);
}
/* }}} */
function get_ident_size($placeholder, $content) { /* {{{ */
preg_match('/^([[:blank:]]*)\{'. $placeholder .'\}/m', $content, $match);
return isset($match[1]) ? strlen($match[1]) : false;
}
/* }}} */
function save_file($filename, $content) { /* {{{ */
global $OPTION;
$filename = str_replace("\\", DIRECTORY_SEPARATOR, $filename);
file_put_contents($filename, $content);
if ($OPTION['verbose']) {
printf("%s\n", $filename);
}
}
/* }}} */
/** Returns an xml type tag or an entity to use in the generated xml for a PHP constant */
function get_xml_type_tag_or_entity($value, bool $allow_entity) { /* {{{ */
// Entities from entities/global.ent are recommended for freeform text.
if (is_int($value)) {
return $allow_entity ? '&integer;' : '<type>int</type>';
} elseif (is_float($value)) {
return $allow_entity ? '&float;' : '<type>float</type>';
} elseif (is_null($value)) {
return '<type>null</type>';
} elseif (is_bool($value)) {
return $allow_entity ? '&boolean;' : '<type>bool</type>';
}
if ($allow_entity) {
if (is_array($value)) {
return '&array;';
} elseif (is_object($value)) {
return '&object;';
} elseif (is_resource($value)) {
return '&resource;';
}
}
// Use <type>$type</type> for other types
return '<type>' . gettype($value) . '</type>';
}
/* }}} */
function get_type_by_string($str) { /* {{{ */
if (is_numeric($str)) {
if ($str && intval($str) == $str) {
return 'int';
} else if ($str && floatval($str) == $str) {
return 'float';
} else {
return 'int';
}
} else {
return 'string';
}
}
/* }}} */
/** @return string|null */
function get_type_as_xml_string(?ReflectionType $type = null) { /* {{{ */
if ($type instanceof ReflectionNamedType) {
$ret = "<type>{$type->getName()}</type>";
if ($type->allowsNull()) {
$ret = "<type class=\"union\">{$ret}<type>null</type></type>";
}
return $ret;
}
if ($type instanceof ReflectionUnionType) {
$types = array_map(function($type) {return "<type>{$type->getName()}</type>";}, $type->getTypes());
$ret = implode('', $types);
if (count($types) > 1) {
$ret = "<type class=\"union\">{$ret}</type>";
}
return $ret;
}
if ($type instanceof ReflectionType) {
$ret = (string) $type;
return "<type>{$ret}</type>";
}
return null;
}
/* }}} */
function create_dir($path) { /* {{{ */
global $OPTION;
$path = str_replace("\\", DIRECTORY_SEPARATOR, $path);
if (!file_exists($path)) {
if ($OPTION['verbose']) {
printf("- Creating directory `%s'\n", $path);
}
if (!mkdir($path, 0777, true)) {
add_warning("chmod: Permission denied `{$path}'");
return;
}
}
}
/* }}} */
function create_markup_to_params(array $params, $ident) { /* {{{ */
$count = 1;
$markup = "";
foreach ($params as $param) {
/* Parameter type */
$type = get_type_as_xml_string($param->getType());
if ($type === null) {
$type = '<type>mixed</type>';
if (!$param->getName()) {
add_warning(sprintf("Parameter name not found, param%d used", $count));
}
add_warning(sprintf("Type hint for parameter `%s' not found, 'mixed' used", ($param->getName() ? $param->getName() : $count)));
}
$markup .= sprintf("%s<methodparam%s>%s<parameter%s>%s</parameter></methodparam>". PHP_EOL,
($markup ? str_repeat(' ', $ident) : ''),
($param->isOptional() ? ' choice="opt"' : ''),
$type,
($param->isPassedByReference() ? ' role="reference"' : ''),
($param->getName() ? $param->getName() : 'param'. $count));
$count++;
}
return rtrim($markup, PHP_EOL);
}
/* }}} */
function global_check($content) { /* {{{ */
global $INFO, $OPTION;
if (!$INFO['actual_extension']) {
$INFO['actual_extension'] = 'EXTENSION_NAME_HERE';
}
/* {EXT_NAME_ID} */
$content = preg_replace('/\{EXT_NAME_ID\}/', format_id($INFO['actual_extension']), $content);
/* {EXT_NAME} */
$content = preg_replace('/\{EXT_NAME\}/', ucwords($INFO['actual_extension']), $content);
/* {EMPTY_REVISION_KEYWORD} */
$content = str_replace('{EMPTY_REVISION_KEYWORD}', '<!-- '. chr(36) .'Revision$ -->', $content);
return $content;
}
/* }}} */
function create_markup_to_parameter_section(Reflector $obj, $content) { /* {{{ */
$param_name = ($obj instanceof ReflectionMethod ? 'METHOD' : 'FUNCTION'). '_PARAMETERS';
/* {PARAMETERS} */
if ($obj->getNumberOfParameters()) {
$ident = get_ident_size($param_name, $content);
$parameters = $obj->getParameters();
$content = preg_replace('/\{'. $param_name .'\}/', create_markup_to_params($parameters, $ident), $content);
/* {PARAMETERS_DESCRIPTION} */
if ($ident = get_ident_size('PARAMETERS_DESCRIPTION', $content)) {
$count = 1;
$markup = "<variablelist>". PHP_EOL;
foreach ($parameters as $param) {
$markup .= str_repeat(' ', $ident + 1) ."<varlistentry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) .'<term><parameter>'. ($param->getName() ? $param->getName() : 'param'. $count) ."</parameter></term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 4) . PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."</varlistentry>". PHP_EOL;
$count++;
}
$markup .= str_repeat(' ', $ident) ."</variablelist>";
$content = preg_replace('/\{PARAMETERS_DESCRIPTION\}/', $markup, $content, 1);
}
} else {
$content = preg_replace('/\{'. $param_name .'\}/', '<void/>', $content, 1);
$content = preg_replace('/\{PARAMETERS_DESCRIPTION\}/', '&no.function.parameters;', $content, 1);
}
return $content;
}
/* }}} */
function gen_function_markup(ReflectionFunction $function, $content) { /* {{{ */
/* {FUNCTION_NAME_ID} */
$content = preg_replace('/\{FUNCTION_NAME_ID\}/', format_id($function->getName()), $content);
/* {FUNCTION_NAME} */
$content = preg_replace('/\{FUNCTION_NAME\}/', $function->getName(), $content);
/* {RETURN_TYPE} */
$type = get_type_as_xml_string($function->getReturnType());
if ($type === null) {
$type = '<type>mixed</type>';
add_warning(sprintf("Return type hint for function `%s' not found, 'mixed' used", $function->getName()));
}
$content = preg_replace('/\{RETURN_TYPE\}/', $type, $content, 1);
/* {FUNCTION_PARAMETERS}, {PARAMETERS_DESCRIPTION} */
$content = create_markup_to_parameter_section($function, $content);
return $content;
}
/* }}} */
function gen_method_markup(ReflectionMethod $method, $content) { /* {{{ */
/* {METHOD_PARAMETERS}, {PARAMETERS_DESCRIPTION} */
$content = create_markup_to_parameter_section($method, $content);
/* {CLASS_NAME_ID} */
$content = preg_replace('/\{CLASS_NAME_ID\}/', format_id($method->class), $content);
/* {METHOD_NAME_ID} */
$content = preg_replace('/\{METHOD_NAME_ID\}/', format_id($method->name), $content);
/* {FULL_METHOD_NAME} */
$content = preg_replace('/\{FULL_METHOD_NAME\}/', $method->class .'::'. $method->name, $content);
/* {METHOD_NAME} */
$content = preg_replace('/\{METHOD_NAME\}/', $method->name, $content);
/* {MODIFIERS} */
$content = preg_replace('/\{MODIFIERS\}/', implode(" ", create_markup_to_modifiers($method->getModifiers())), $content, 1);
/* {RETURN_TYPE} */
if (!$method->isConstructor()) {
$type = get_type_as_xml_string($method->getReturnType());
if ($type === null) {
$type = '<type>mixed</type>';
add_warning(sprintf("Return type hint for method `%s' not found, 'mixed' used", $method->getName()));
}
$content = preg_replace('/\{RETURN_TYPE\}/', $type, $content, 1);
} else {
$content = preg_replace('/\{RETURN_TYPE\}/', '', $content, 1);
}
return $content;
}
/* }}} */
function gen_mapping_markup(ReflectionMethod $method, ReflectionFunction $function, $content) { /* {{{ */
if ($method->getNumberOfParameters() || $function->getNumberOfParameters()) {
/* {PARAMETERS_DESCRIPTION} */
if ($ident = get_ident_size('PARAMETERS_DESCRIPTION', $content)) {
$count = 1;
$func_params = array();
$method_params = array();
foreach ($function->getParameters() as $param) {
$func_params[] = $param->getName();
}
foreach ($method->getParameters() as $param) {
$method_params[] = $param->getName();
}
$markup = str_repeat(' ', $ident) ."<variablelist>". PHP_EOL;
foreach ($func_params as $param) {
$markup .= str_repeat(' ', $ident + 1) ."<varlistentry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) .'<term><parameter>'. ($param ? $param : 'param'. $count) ."</parameter></term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 4) . PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."</varlistentry>". PHP_EOL;
$count++;
}
$diff_params = array_diff($method_params, $func_params);
foreach ($method_params as $param) {
if (!($param && in_array($param, $diff_params))) {
continue;
}
$markup .= str_repeat(' ', $ident + 1) ."<varlistentry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) .'<term><parameter>'. ($param ? $param : 'param'. $count) ."</parameter></term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 4) . PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."</varlistentry>". PHP_EOL;
$count++;
}
$markup .= str_repeat(' ', $ident) ."</variablelist>". PHP_EOL;
$content = preg_replace('/\{PARAMETERS_DESCRIPTION\}/', $markup, $content, 1);
}
} else {
$content = preg_replace('/\{PARAMETERS\}/', '<void/>', $content, 1);
$content = preg_replace('/\{PARAMETERS_DESCRIPTION\}/', '&no.function.parameters;', $content, 1);
}
$content = gen_method_markup($method, $content);
$content = gen_function_markup($function, $content);
return $content;
}
/* }}} */
/* TODO This is outdated */
function gen_class_markup(ReflectionClass $class, $content) { /* {{{ */
$id = format_id($class->getName());
$escapedName = addslashes($class->getName());
/* {CLASS_NAME} */
$content = preg_replace('/\{CLASS_NAME\}/', $class->getName(), $content);
/* {CLASS_TYPE} */
$type = ($class->isInterface() ? 'interface' : 'class');
$content = preg_replace('/\{CLASS_TYPE\}/', $type, $content);
/* {CLASS_NAME_ID} */
$content = preg_replace('/{CLASS_NAME_ID\}/', $id, $content);
/* {EXTENDS} */
if ($parent = $class->getParentClass()) {
$ident = get_ident_size('EXTENDS', $content);
$markup = PHP_EOL;
$markup .= str_repeat(' ', $ident) . "<ooclass>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."<modifier>extends</modifier>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) .'<classname>'. $parent->getName() ."</classname>". PHP_EOL;
$markup .= str_repeat(' ', $ident) .'</ooclass>';
$content = preg_replace('/\{EXTENDS\}/', $markup, $content, 1);
} else {
$content = preg_replace('/^\s*\{EXTENDS\}.*?\n/m', '', $content, 1);
}
/* {IMPLEMENTS} */
if ($interfaces = $class->getInterfaces()) {
$ident = get_ident_size('IMPLEMENTS', $content);
// Don't get inherited interfaces, e.g. Traversable if we already have Iterator.
foreach ($interfaces as $interface) {
foreach (array_keys($interface->getInterfaces()) as $inherited_interface) {
unset($interfaces[$inherited_interface]);
}
}
$markup = PHP_EOL;
foreach ($interfaces as $interface) {
$markup .= str_repeat(' ', $ident) ."<oointerface>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) .'<interfacename>'. $interface->getName() ."</interfacename>". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."</oointerface>". PHP_EOL . PHP_EOL;
}
$content = preg_replace('/\{IMPLEMENTS\}/', rtrim($markup, PHP_EOL), $content, 1);
} else {
$content = preg_replace('/^\s*\{IMPLEMENTS\}.*?\n/m', '', $content, 1);
}
/* {CONSTANTS_LIST} */
if ($constants = $class->getConstants()) {
$ident = get_ident_size('CONSTANTS_LIST', $content);
$markup = "<classsynopsisinfo role=\"comment\">Constants</classsynopsisinfo>". PHP_EOL;
foreach ($constants as $constant => $value) {
$markup .= str_repeat(' ', $ident) ."<fieldsynopsis>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."<modifier>const</modifier>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) .get_xml_type_tag_or_entity($value, false). PHP_EOL; // For the class synopsis we use explicit <type> elements
$markup .= str_repeat(' ', $ident + 1) .'<varname linkend="'. $id .'.constants.'. format_id($constant) .'">'. $class->getName() .'::'. $constant ."</varname>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) .'<initializer>'. $value ."</initializer>". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."</fieldsynopsis>". PHP_EOL;
}
$content = preg_replace('/\{CONSTANTS_LIST\}/', $markup, $content, 1);
} else {
$content = preg_replace('/^\s*\{CONSTANTS_LIST\}.*?\n/m', '', $content, 1);
}
/* {CONSTANTS} */
if ($constants) {
$ident = get_ident_size('CONSTANTS', $content);
$markup = PHP_EOL ."<!-- {{{ ". $class->getName() ." constants -->". PHP_EOL;
$markup .= str_repeat(' ', $ident) .'<section xml:id="'. $id .".constants\">". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."&reftitle.constants;". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."<variablelist>". PHP_EOL . PHP_EOL;
foreach ($constants as $constant => $value) {
$markup .= str_repeat(' ', $ident + 2) .'<varlistentry xml:id="'. $id .".constants.". format_id($constant) ."\">". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) .'<term><constant>'. $class->getName() .'::'. $constant ."</constant></term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 4) ."<simpara></simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</varlistentry>". PHP_EOL . PHP_EOL;
}
$markup .= str_repeat(' ', $ident + 1) ."</variablelist>". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."</section>". PHP_EOL;
$markup .= "<!-- }}} -->". PHP_EOL;
$content = preg_replace('/\{CONSTANTS\}/', $markup, $content, 1);
} else {
$content = preg_replace('/^\s*\{CONSTANTS\}.*?\n/m', '', $content, 1);
}
/* {PROPERTIES_LIST} */
if ($properties = $class->getProperties()) {
$ident = get_ident_size('PROPERTIES_LIST', $content);
$inherited = array();
$markup = "";
foreach ($properties as $key => $property) {
/* Don't get inherited properties */
$declaring_class = $property->getDeclaringClass()->name;
if ($declaring_class != $class->name) {
$inherited[$declaring_class] = $declaring_class;
unset($properties[$key]);
continue;
}
$markup .= str_repeat(' ', $ident) ."<fieldsynopsis>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) .
implode(
str_repeat(' ', $ident + 1) . PHP_EOL,
create_markup_to_modifiers($property->getModifiers())
) . PHP_EOL;
if (PHP_VERSION_ID >= 74000 && $property->hasType()) {
$markup .= str_repeat(' ', $ident + 1) . get_type_as_xml_string($property->getType()). PHP_EOL;
}
$markup .= str_repeat(' ', $ident + 1) . '<varname linkend="'. $id .'.props.'. format_id($property->getName()) .'">'. $property->getName() ."</varname>". PHP_EOL;
$markup .= str_repeat(' ', $ident) . "</fieldsynopsis>". PHP_EOL;
}
if ($markup) {
$markup = PHP_EOL . str_repeat(' ', $ident) . "<classsynopsisinfo role=\"comment\">&Properties;</classsynopsisinfo>". PHP_EOL . $markup;
}
if ($inherited) {
if ($markup) {
$markup .= PHP_EOL . str_repeat(' ', $ident);
}
$markup .= '<classsynopsisinfo role="comment">&InheritedProperties;</classsynopsisinfo>'. PHP_EOL;
foreach ($inherited as $declaring_class) {
$markup .= str_repeat(' ', $ident) ."<xi:include xpointer=\"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('" . strtolower($declaring_class) . ".synopsis')/descendant::db:fieldsynopsis)\">" . PHP_EOL . str_repeat(' ', $ident + 1) . "<xi:fallback/>" . PHP_EOL . str_repeat(' ', $ident) . "</xi:include>". PHP_EOL;
}
}
$content = preg_replace('/\{PROPERTIES_LIST\}/', $markup, $content, 1);
} else {
$content = preg_replace('/^\s*\{PROPERTIES_LIST\}.*?\n/m', '', $content, 1);
}
/* {PROPERTIES} */
if ($properties) {
$ident = get_ident_size('PROPERTIES', $content);
$markup = PHP_EOL ."<!-- {{{ ". $class->getName() ." properties -->". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."<section xml:id=\"". $id .".props\">". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."&reftitle.properties;". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."<variablelist>". PHP_EOL;
foreach ($properties as $property) {
$markup .= str_repeat(' ', $ident + 2) .'<varlistentry xml:id="'. $id .'.props.'. format_id($property->getName()) ."\">". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) .'<term><varname>'. $property->getName() ."</varname></term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 4) ."<simpara></simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</varlistentry>". PHP_EOL;
}
$markup .= str_repeat(' ', $ident + 1) ."</variablelist>". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."</section>". PHP_EOL;
$markup .= "<!-- }}} -->". PHP_EOL;
$content = preg_replace('/\{PROPERTIES\}/', $markup, $content, 1);
} else {
$content = preg_replace('/^\s*\{PROPERTIES\}.*?\n/m', '', $content, 1);
}
/* {PROPERTY_XINCLUDE} */
$content = preg_replace('/\{PROPERTY_XINCLUDE\}/',
"<xi:include xpointer=\"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.". $id ."')/db:refentry/db:refsect1[@role='description']/descendant::db:fieldsynopsis[1])\"><xi:fallback/></xi:include>". PHP_EOL,
$content, 1);
/* {METHOD_XINCLUDE} */
$ident = get_ident_size('METHOD_XINCLUDE', $content);
$content = preg_replace('/\{METHOD_XINCLUDE\}/',
PHP_EOL . str_repeat(' ', $ident) . "<classsynopsisinfo role=\"comment\">&Methods;</classsynopsisinfo>". PHP_EOL.
str_repeat(' ', $ident) ."<xi:include xpointer=\"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.". $id ."')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[@role='" . $escapedName . "'])\">" . PHP_EOL . str_repeat(' ', $ident + 1) . "<xi:fallback/>" . PHP_EOL . str_repeat(' ', $ident) . "</xi:include>",
$content, 1);
/* {INHERITED_XINCLUDE} */
if ($parent) {
$ident = get_ident_size('INHERITED_XINCLUDE', $content);
$content = preg_replace('/\{INHERITED_XINCLUDE\}/',
PHP_EOL . str_repeat(' ', $ident) ."<classsynopsisinfo role=\"comment\">&InheritedMethods;</classsynopsisinfo>". PHP_EOL.
str_repeat(' ', $ident) ."<xi:include xpointer=\"xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.". format_id($parent->getName()) ."')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[@role='" . $escapedName . "'])\">" . PHP_EOL . str_repeat(' ', $ident + 1) . "<xi:fallback/>" . PHP_EOL . str_repeat(' ', $ident) . "</xi:include>". PHP_EOL,
$content, 1);
} else {
$content = preg_replace('/^\s*\{INHERITED_XINCLUDE\}.*?\n/m', '', $content, 1);
}
return $content;
}
/* }}} */
function gen_extension_markup(ReflectionExtension $obj, $content, $xml_file) { /* {{{ */
global $INFO, $OPTION;
switch ($xml_file) {
case 'ini.xml':
if ($ini = ini_get_all($obj->name)) {
$visibility = array(
INI_USER => 'INI_USER',
INI_PERDIR => 'INI_PERDIR',
INI_SYSTEM => 'INI_SYSTEM',
INI_ALL => 'INI_ALL',
);
$ident = get_ident_size('INI_ENTRIES', $content);
$markup = "<tbody>". PHP_EOL;
$markup2 = '';
foreach ($ini as $config => $value) {
$id = "ini.". format_config($config);
$markup .= str_repeat(' ', $ident + 1) ."<row>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<entry><link linkend=\"". $id ."\">". $config ."</link></entry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<entry>". $value['global_value'] ."</entry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<entry>" . (isset($visibility[$value['access']]) ? ('<constant>' . $visibility[$value['access']] . '</constant>') : $value['access']) . "</entry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<entry><!-- leave empty, this will be filled by an automatic script --></entry>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."</row>". PHP_EOL;
$markup2 .= ($markup2 ? str_repeat(' ', $ident) : '') ."<varlistentry xml:id=\"". $id ."\">". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 1) ."<term>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 2) ."<parameter>". $config ."</parameter>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 2) ."<type>". get_type_by_string($value['global_value']) ."</type>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 1) ."</term>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 1) ."<listitem>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 2) ."<simpara>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 3) . PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 2) ."</simpara>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident + 1) ."</listitem>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident) ."</varlistentry>". PHP_EOL;
}
$markup .= str_repeat(' ', $ident) ."</tbody>";
/* {INI_ENTRIES} */
$content = preg_replace('/\{INI_ENTRIES\}/', $markup, $content, 1);
/* {INI_ENTRIES_DESCRIPTION} */
$content = preg_replace('/\{INI_ENTRIES_DESCRIPTION\}/', $markup2, $content, 1);
} else {
return false; /* Abort */
}
break;
case 'constants.xml':
if ($constants = $obj->getConstants()) {
$ident = get_ident_size('CONSTANTS', $content);
$markup = "&extension.constants;". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."<variablelist>". PHP_EOL;
foreach ($constants as $name => $value) {
$markup .= str_repeat(' ', $ident + 1) .'<varlistentry xml:id="constant.'. format_id($name) .'">'. PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<constant>". $name ."</constant>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."(". get_xml_type_tag_or_entity($value, true) .")". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</term>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."<listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 3) ."</simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 2) ."</listitem>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."</varlistentry>". PHP_EOL;
}
$markup .= str_repeat(' ', $ident) ."</variablelist>". PHP_EOL;
$content = preg_replace('/\{CONSTANTS\}/', $markup, $content, 1);
} else {
$content = preg_replace('/\{CONSTANTS\}/', '&no.constants;', $content, 1);
}
break;
case 'configure.xml':
$ident = get_ident_size('EXT_INSTALL_MAIN', $content);
$ident2 = get_ident_size('EXT_INSTALL_WIN', $content);
$markup = '';
$markup2 = '';
if ($OPTION['pecl'] === true) {
$markup .= "<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."&pecl.info;". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."<link xlink:href=\"&url.pecl.package;{EXT_NAME_ID}\">&url.pecl.package;{EXT_NAME_ID}</link>". PHP_EOL;
$markup .= str_repeat(' ', $ident) . "</simpara>". PHP_EOL;
/*
$markup2 .= "<simpara>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident2 + 1) ."The latest PECL/{EXT_NAME_ID} Win32 DLL is available here:". PHP_EOL;
$markup2 .= str_repeat(' ', $ident2 + 1) ."<link xlink:href=\"&url.pecl.win.ext;php_{EXT_NAME_ID}.dll\">php_{EXT_NAME_ID}.dll</link>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident2) ."</simpara>". PHP_EOL;
*/
} else {
$markup .= "<simpara>". PHP_EOL;
$markup .= str_repeat(' ', $ident + 1) ."Use <option role=\"configure\">--with-{EXT_NAME_ID}[=DIR]</option> when compiling PHP.". PHP_EOL;
$markup .= str_repeat(' ', $ident) ."</simpara>". PHP_EOL;
$markup2 .= "<simpara>". PHP_EOL;
$markup2 .= str_repeat(' ', $ident2 + 1) ."Windows users should include <filename>php_{EXT_NAME_ID}.dll</filename> into &php.ini;". PHP_EOL;
$markup2 .= str_repeat(' ', $ident2) ."</simpara>". PHP_EOL;
}
$content = str_replace('{EXT_INSTALL_MAIN}', $markup, $content);
$content = str_replace('{EXT_INSTALL_WIN}', $markup2, $content);
break;
case 'versions.xml':
$version_default = 'PHP 5 &gt;= Unknown';
if ($OPTION['pecl'] === true) {
$version_default = 'PECL {EXT_NAME_ID} &gt;= Unknown';
}
$markup = "";
/* Function list */
if ($functions = $obj->getFunctions()) {
$markup .= "<!-- Functions -->". PHP_EOL;
foreach ($functions as $function) {
$markup .= " <function name='". strtolower($function->getName()) ."' from='$version_default'/>". PHP_EOL;
}
}
/* Method list */
if ($classes = $obj->getClasses()) {
$markup .= " <!-- Classes and Methods -->". PHP_EOL;
foreach ($classes as $class) {
$markup .= PHP_EOL;
$markup .= " <function name='". strtolower($class->name) ."' from='$version_default'/>". PHP_EOL;
foreach ($class->getMethods() as $method) {
$markup .= " <function name='". strtolower($class->name .'::'. $method->getName()) ."' from='$version_default'/>". PHP_EOL;
}
}
}
$content = preg_replace('/\{VERSIONS\}/', rtrim($markup), $content);
break;
}
return $content;
}
/* }}} */
function write_doc(Reflector $obj, $type) { /* {{{ */
global $OPTION, $INFO, $TEMPLATE, $DOC_EXT;
switch ($type) {
case DOC_EXTENSION:
foreach ($DOC_EXT as $xml_file => $tpl_file) {
$filename = $OPTION['output'] .'/'. format_filename($xml_file);
$INFO['actual_file'] = $filename;
$content = file_get_contents(dirname(__FILE__) .'/'. $tpl_file);
if ($content = gen_extension_markup($obj, $content, $xml_file)) {
save_file($filename, global_check($content));
}
}
break;
/* Methods */
case DOC_METHOD:
case DOC_CONSTRUCTOR:
$path = $OPTION['output'] .'/'. strtolower($obj->class);
$filename = $path .'/'. format_filename($obj->name) .'.xml';
create_dir($path);
$INFO['actual_file'] = $filename;
$INFO['mappeds'][] = $filename;
/* Mappeds */
if ($function = find_function($INFO['actual_extension'], $obj, NULL)) {
$content = file_get_contents(dirname(__FILE__) .'/mapping.tpl');
$content = gen_mapping_markup($obj, $function, $content);
$content = str_replace('{DEFAULT_EXAMPLE}', get_default_role('example_mapping', "{$obj->class}::{$obj->name}", $OPTION['example']), $content);
} else {
$content = file_get_contents(dirname(__FILE__) .'/'. $TEMPLATE[$type]);
$content = gen_method_markup($obj, $content);
$content = str_replace('{DEFAULT_EXAMPLE}', get_default_role('example', "{$obj->class}::{$obj->name}", $OPTION['example']), $content);
}
$content = str_replace('{DEFAULT_SEEALSO}', get_default_role('seealso', "{$obj->class}::{$obj->name}", $OPTION['seealso']), $content);
save_file($filename, global_check($content));
break;
/* Properties */
case DOC_PROPERTY:
/* Doesn't exists separated file documenting property, actually
* they are documented in DOC_METHOD */
break;
/* Classes */
case DOC_CLASS:
$path = $OPTION['output'];
$filename = $path .'/'. format_filename($obj->getName()) .'.xml';
$INFO['actual_file'] = $filename;
$content = file_get_contents(dirname(__FILE__) .'/'. $TEMPLATE[$type]);
$content = gen_class_markup($obj, $content);
/* classname.xml */
save_file($filename, global_check($content));
break;
case DOC_FUNCTION:
if ($method = find_function($INFO['actual_extension'], NULL, $obj)) {
$path = $OPTION['output'] .'/'. strtolower($method->class);
$filename = $path .'/'. format_filename($method->name) .'.xml';
if (in_array($filename, $INFO['mappeds'])) {
return;
}
create_dir($path);
$INFO['actual_file'] = $filename;
$content = file_get_contents(dirname(__FILE__) .'/mapping.tpl');
$content = gen_mapping_markup($method, $obj, $content);
$content = str_replace('{DEFAULT_EXAMPLE}', get_default_role('example_mapping', $obj->getName(), $OPTION['example']), $content);
} else {
$path = $OPTION['output'] .'/functions';
$filename = $path .'/'. format_filename($obj->getName()) .'.xml';
create_dir($path);
$INFO['actual_file'] = $filename;
$content = file_get_contents(dirname(__FILE__) .'/'. $TEMPLATE[$type]);
$content = gen_function_markup($obj, $content);
$content = str_replace('{DEFAULT_EXAMPLE}', get_default_role('example', $obj->getName(), $OPTION['example']), $content);
}
$content = str_replace('{DEFAULT_SEEALSO}', get_default_role('seealso', $obj->getName(), $OPTION['seealso']), $content);
save_file($filename, global_check($content));
break;
}
}
/* }}} */
function gen_docs($name, $type) { /* {{{ */
global $OPTION, $INFO;
if ($type & DOC_EXTENSION) {
try {
$extension = new ReflectionExtension($name);
$INFO['actual_extension'] = $name;
write_doc($extension, DOC_EXTENSION);
foreach ($extension->getClasses() as $class) {
gen_docs($class->name, DOC_CLASS);
}
foreach ($extension->getFunctions() as $function) {
gen_docs($function->name, DOC_FUNCTION);
}
} catch (Exception $e) {
die('Error: '. $e->getMessage() ."\n");
}
} else if ($type & DOC_FUNCTION) {
try {
$function = new ReflectionFunction($name);
if (!$INFO['actual_extension']) {
if ($extname = $function->getExtensionName()) {
$INFO['actual_extension'] = $extname;
} else {
add_warning("The function {$name} lacks Reflection information");
}
}
write_doc($function, DOC_FUNCTION);
} catch (Exception $e) {
die('Error: '. $e->getMessage() ."\n");
}
} else if ($type & DOC_METHOD) {
try {
$class = new ReflectionClass($OPTION['class']);
if (!$INFO['actual_extension']) {
if ($extname = $class->getExtensionName()) {
$INFO['actual_extension'] = $extname;
} else {
add_warning("The method {$name} lacks Reflection information");
}
}
foreach ($class->getMethods() as $method) {
/* Don't get the inherited methods */
if ($method->getDeclaringClass()->name == $class->name &&
((is_array($OPTION['method']) && in_array(strtolower($method->getName()), $OPTION['method']))
|| $OPTION['method'] == strtolower($method->getName()))) {
write_doc($method, $method->isConstructor() ? DOC_CONSTRUCTOR : DOC_METHOD);
}
}
} catch (Exception $e) {
die('Error: '. $e->getMessage() ."\n");
}
} else if ($type & DOC_CLASS) {
try {
$class = new ReflectionClass($name);
if (!$INFO['actual_extension']) {
if ($extname = $class->getExtensionName()) {
$INFO['actual_extension'] = $extname;
} else {
add_warning("The class {$name} lacks Reflection information");
}
}
write_doc($class, DOC_CLASS);
foreach ($class->getMethods() as $method) {
/* Don't get the inherited methods */
if ($method->getDeclaringClass()->name == $class->name) {
write_doc($method, $method->isConstructor() ? DOC_CONSTRUCTOR : DOC_METHOD);
}
}
} catch (Exception $e) {
die('Error: '. $e->getMessage() ."\n");
}
}
}
/* }}} */
function get_default_role ($role, $funcname, $default) {
$out = '';
if (!$default) {
return $out;
}
if ($role === 'seealso') {
$out = <<<ROLE
<refsect1 role="seealso">
&reftitle.seealso;
<simplelist>
<member></member>
</simplelist>
</refsect1>
ROLE;
}
if ($role === 'example') {
$out = <<<ROLE
<refsect1 role="examples">
&reftitle.examples;
<example>
<title><function>$funcname</function> example</title>
<programlisting role="php">
<![CDATA[
<?php
/* ... */
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
...
]]>
</screen>
</example>
</refsect1>
ROLE;
}
if ($role === 'example_mapping') {
$out = <<<ROLE
<refsect1 role="examples">
&reftitle.examples;
<example>
<title>&style.oop;</title>
<programlisting role="php">
<![CDATA[
<?php
/* ... */
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
...
]]>
</screen>
</example>
<example>
<title>&style.procedural;</title>
<programlisting role="php">
<![CDATA[
<?php
/* ... */
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
...
]]>
</screen>
</example>
</refsect1>
ROLE;
}
return PHP_EOL . $out . PHP_EOL;
}
$OPTION = array();
$INFO = array('actual_extension' => false, 'mappeds' => array());
$WARNING = array();
$OPTION['extension'] = NULL;
$OPTION['method'] = NULL;
$OPTION['class'] = NULL;
$OPTION['function'] = NULL;
$OPTION['output'] = getcwd() . '/output';
$OPTION['verbose'] = true;
$OPTION['quiet'] = false;
$OPTION['pecl'] = false;
$OPTION['copy'] = false;
$OPTION['test'] = false;
$OPTION['phpdoc'] = NULL;
$OPTION['docbase'] = NULL;
$OPTION['seealso'] = false;
$OPTION['example'] = false;
$arropts = array(
'verbose' => 'v', /* verbose */
'quiet' => 'q', /* quiet */
'include:' => 'i:', /* include */
'help' => 'h', /* help */
'pecl' => 'p', /* pecl */
'phpdoc:' => 'd:', /* phpdoc dir*/
'docbase:' => 'b:', /* doc-base dir */
'copy' => 'a', /* copy */
'test' => 't', /* test */
'example' => 'x', /* example */
'seealso' => 's', /* seealso */
'output:' => 'o:', /* output dir */
'class:' => 'c:', /* classname */
'extension:' => 'e:', /* extension */
'function:' => 'f:', /* function */
'method:' => 'm:' /* method */
);
$options = @getopt(implode($arropts), array_keys($arropts));
if (!$options) {
usage();
exit;
}
foreach ($options as $opt => $value) {
switch ($opt) {
case 'v':
case 'verbose':
$OPTION['verbose'] = false;
break;
case 'h':
case 'help':
usage();
break;
case 'e':
case 'extension':
$OPTION['extension'] = $value;
break;
case 'f':
case 'function':
$OPTION['function'] = $value;
break;
case 'm':
case 'method':
if (!array_key_exists('c', $options) && !array_key_exists('class', $options)) {
die("Error: The class name should be supplied (i.e. -c classname)\n");
}
$OPTION['method'] = is_array($value) ? array_map('strtolower', $value) : strtolower($value);
break;
case 'c':
case 'class':
$OPTION['class'] = $value;
break;
case 'o':
case 'output':
$OPTION['output'] = $value;
break;
case 'p':
case 'pecl':
$OPTION['pecl'] = true;
break;
case 'd':
case 'phpdoc':
$OPTION['phpdoc'] = realpath($value);
break;
case 'b':
case 'docbase':
$OPTION['docbase'] = realpath($value);
break;
case 'a':
case 'copy':
$OPTION['copy'] = true;
break;
case 't':
case 'test':
$OPTION['test'] = true;
break;
case 'q':
case 'quiet':
$OPTION['quiet'] = true;
break;
case 's':
case 'seealso':
$OPTION['seealso'] = true;
break;
case 'x':
case 'example':
$OPTION['example'] = true;
break;
case 'i':
case 'include':
foreach((array)$value as $filename) {
if (stream_is_local($filename) && file_exists($filename)) {
include $filename;
} else {
echo "- Cannot include '$filename': ", stream_is_local($filename) ? "doesn't exist" : "isn't local file", "\n";
}
}
break;
}
}
if (!empty($OPTION['example'])) {
$DOC_EXT['examples.xml'] = 'examples.tpl';
}
if (!file_exists($OPTION['output']) || !is_writable($OPTION['output'])) {
if (mkdir($OPTION['output'])) {
echo "- Created output directory: $OPTION[output]\n";
} else {
die("Error: The output directory ($OPTION[output]) must be writable\n");
}
}
if (!empty($OPTION['extension'])) {
if (is_array($OPTION['extension'])) {
foreach ($OPTION['extension'] as $extension) {
gen_docs($extension, DOC_EXTENSION);
}
} else {
gen_docs($OPTION['extension'], DOC_EXTENSION);
}
}
if (!empty($OPTION['function'])) {
gen_docs($OPTION['function'], DOC_FUNCTION);
}
if (!empty($OPTION['method'])) {
gen_docs($OPTION['method'], DOC_METHOD);
}
if (empty($OPTION['method']) && !empty($OPTION['class'])) {
if (is_array($OPTION['class'])) {
foreach ($OPTION['class'] as $classname) {
gen_docs($classname, DOC_CLASS);
}
} else {
gen_docs($OPTION['class'], DOC_CLASS);
}
}
// Copy new skeletons to the appropriate directory
// Example: --output out --class domdocument --copy --phpdoc ../../../en/reference/dom/
// That will copy over new domdocument files, while not overwriting any
if (!empty($OPTION['copy']) && !empty($OPTION['phpdoc'])) {
if (!is_dir($OPTION['phpdoc'])) {
echo "ERROR: The provided phpdoc path is not a directory: $OPTION[phpdoc]\n";
exit;
}
if (!empty($OPTION['test'])) {
echo "INFO: Test mode, so will not copy over files.\n";
}
$count_gen = 0;
$count_copy = 0;
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($OPTION['output'])) as $file) {
$filepathname = $file->getPathName();
$filename = $file->getBaseName();
$count_gen++;
if (!$file->isFile() || pathinfo($filename, PATHINFO_EXTENSION) !== 'xml') {
continue;
}
// fileid is equal in docgen output and phpdoc
$fileid = str_replace($OPTION['output'], '', $filepathname);
// Do not overwrite
if (file_exists($OPTION['phpdoc'] . $fileid)) {
echo "INFO: will not overwrite: $OPTION[phpdoc]$fileid\n";
continue;
}
if (empty($OPTION['test'])) {
// Hack to create the directory
$dir = str_replace($filename, '', $OPTION['phpdoc'] . $fileid);
if (!is_dir($dir)) {
echo "INFO: Created directory $dir\n";
create_dir($dir);
}
// Do the copy
copy($OPTION['output'] . $fileid, $OPTION['phpdoc'] . $fileid);
} else {
echo "DEBUG: $OPTION[output]$fileid TO $OPTION[phpdoc]$fileid\n";
}
$count_copy++;
}
echo "INFO: Copied over $count_copy files, after generating $count_gen files.\n";
if ($count_copy > 0) {
echo "INFO: Be sure to add version information to $OPTION[phpdoc]/version.xml, because I am unsure how to do that (yet).\n";
}
}
/* Warnings */
if (!empty($WARNING) && !$OPTION['quiet']) {
print "\nWarnings:\n";
foreach ($WARNING as $file => $messages) {
printf("- %s\n", realpath($file));
foreach ($messages as $message) {
printf("\t-- %s\n", $message);
}
}
}
// vim: set noet ts=4 sw=4: