Project: ApiGen
Code Location: git://github.com/apigen/apigenmaster
Browse
/
Outline
Download File
apigen.php
#!/usr/bin/env php
<?php

/**
 * ApiGen 2.8.0 - API documentation generator for PHP 5.3+
 *
 * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com)
 * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich)
 * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville)
 *
 * For the full copyright and license information, please view
 * the file LICENSE.md that was distributed with this source code.
 */

namespace ApiGen;

use Nette\Diagnostics\Debugger;
use TokenReflection;

// Safe locale and timezone
setlocale(LC_ALL, 'C');
if (!ini_get('date.timezone')) {
	date_default_timezone_set('UTC');
}

if (false === strpos('@php_dir@', '@php_dir')) {
	// PEAR package

	@include '@php_dir@/Nette/loader.php';
	@include '@php_dir@/Texy/texy.php';

	spl_autoload_register(function($class) {
		$class = trim($class, '\\');
		require sprintf('%s.php', str_replace('\\', DIRECTORY_SEPARATOR, $class));
	});
} elseif (is_file($composerAutoload = __DIR__ . '/../../autoload.php')) {
	// Composer package

	@include $composerAutoload;
} else {
	// Standalone package

	set_include_path(
		__DIR__ . PATH_SEPARATOR .
		__DIR__ . '/libs/FSHL' . PATH_SEPARATOR .
		__DIR__ . '/libs/TokenReflection' . PATH_SEPARATOR .
		get_include_path()
	);

	@include __DIR__ . '/libs/Nette/Nette/loader.php';
	@include __DIR__ . '/libs/Texy/texy/texy.php';

	spl_autoload_register(function($class) {
		$class = trim($class, '\\');
		require sprintf('%s.php', str_replace('\\', DIRECTORY_SEPARATOR, $class));
	});
}

try {

	// Check dependencies
	foreach (array('json', 'iconv', 'mbstring', 'tokenizer') as $extension) {
		if (!extension_loaded($extension)) {
			printf("Required extension missing: %s\n", $extension);
			die(1);
		}
	}
	if (!class_exists('Nette\\Diagnostics\\Debugger')) {
		echo "Required dependency missing: Nette Framework\n";
		die(1);
	}
	if (!class_exists('Texy')) {
		echo "Required dependency missing: Texy library\n";
		die(1);
	}
	if (!class_exists('FSHL\\Highlighter')) {
		echo "Required dependency missing: FSHL library\n";
		die(1);
	}
	if (!class_exists('TokenReflection\\Broker')) {
		echo "Required dependency missing: TokenReflection library\n";
		die(1);
	}

	Debugger::$strictMode = true;
	Debugger::$onFatalError[] = function() {
		echo "\nFor more information turn on the debug mode using the --debug option.\n";
	};
	Debugger::enable(Debugger::PRODUCTION, false);

	$start = new \DateTime();

	$options = $_SERVER['argv'];
	array_shift($options);

	$config = new Config();
	$config->processCliOptions($options);
	$generator = new Generator($config);

	// Help
	if ($config->isHelpRequested()) {
		echo $generator->colorize($generator->getHeader());
		echo $generator->colorize($config->getHelp());
		die();
	}

	// Prepare configuration
	$config->prepare();

	if ($config->debug) {
		Debugger::$onFatalError = array();
		Debugger::enable(Debugger::DEVELOPMENT, false);
	}

	$generator->output($generator->getHeader());

	// Check for update (only in production mode)
	if ($config->updateCheck && !$config->debug) {
		ini_set('default_socket_timeout', 5);
		$latestVersion = @file_get_contents('http://pear.apigen.org/rest/r/apigen/latest.txt');
		if (false !== $latestVersion && version_compare(trim($latestVersion), Generator::VERSION, '>')) {
			$generator->output(sprintf("New version @header@%s@c available\n\n", $latestVersion));
		}
	}

	// Scan
	if (count($config->source) > 1) {
		$generator->output(sprintf("Scanning\n @value@%s@c\n", implode("\n ", $config->source)));
	} else {
		$generator->output(sprintf("Scanning @value@%s@c\n", $config->source[0]));
	}
	if (count($config->exclude) > 1) {
		$generator->output(sprintf("Excluding\n @value@%s@c\n", implode("\n ", $config->exclude)));
	} elseif (!empty($config->exclude)) {
		$generator->output(sprintf("Excluding @value@%s@c\n", $config->exclude[0]));
	}

	$parsed = $generator->parse();

	if (count($parsed->errors) > 1) {
		$generator->output(sprintf("@error@Found %d errors@c\n\n", count($parsed->errors)));

		$no = 1;
		foreach ($parsed->errors as $e) {

			if ($e instanceof TokenReflection\Exception\ParseException) {
				$generator->output(sprintf("@error@%d.@c The TokenReflection library threw an exception while parsing the file @value@%s@c.\n", $no, $e->getFileName()));
				if ($config->debug) {
					$generator->output("\nThis can have two reasons: a) the source code in the file is not valid or b) you have just found a bug in the TokenReflection library.\n\n");
					$generator->output("If the license allows it please send the whole file or at least the following fragment describing where exacly is the problem along with the backtrace to apigen@apigen.org. Thank you!\n\n");

					$token = $e->getToken();
					$sender = $e->getSender();
					if (!empty($token)) {
						$generator->output(
							sprintf(
								"The cause of the exception \"%s\" was the @value@%s@c token (line @count@%d@c) in following part of %s source code:\n\n",
								$e->getMessage(),
								$e->getTokenName(),
								$e->getExceptionLine(),
								$sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the'
							)
						);
					} else {
						$generator->output(
							sprintf(
								"The exception \"%s\" was thrown when processing %s source code:\n\n",
								$e->getMessage(),
								$sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the'
							)
						);
					}

					$generator->output($e->getSourcePart(true) . "\n\nThe exception backtrace is following:\n\n" . $e->getTraceAsString() . "\n\n");
				}
			} elseif ($e instanceof TokenReflection\Exception\FileProcessingException) {
				$generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage()));
				if ($config->debug) {
					$generator->output("\n" . $e->getDetail() . "\n\n");
				}
			} else {
				$generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage()));
				if ($config->debug) {
					$trace = $e->getTraceAsString();
					while ($e = $e->getPrevious()) {
						$generator->output(sprintf("\n%s", $e->getMessage()));
						$trace = $e->getTraceAsString();
					}
					$generator->output(sprintf("\n%s\n\n", $trace));
				}
			}

			$no++;
		}

		if (!$config->debug) {
			$generator->output("\nEnable the debug mode (@option@--debug@c) to see more details.\n\n");
		}
	}

	$generator->output(sprintf("Found @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes\n", $parsed->classes, $parsed->constants, $parsed->functions, $parsed->internalClasses));
	$generator->output(sprintf("Documentation for @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes will be generated\n", $parsed->documentedClasses, $parsed->documentedConstants, $parsed->documentedFunctions, $parsed->documentedInternalClasses));

	// Generating
	$generator->output(sprintf("Using template config file @value@%s@c\n", $config->templateConfig));

	if ($config->wipeout && is_dir($config->destination)) {
		$generator->output("Wiping out destination directory\n");
		if (!$generator->wipeOutDestination()) {
			throw new \RuntimeException('Cannot wipe out destination directory');
		}
	}

	$generator->output(sprintf("Generating to directory @value@%s@c\n", $config->destination));
	$skipping = array_merge($config->skipDocPath, $config->skipDocPrefix);
	if (count($skipping) > 1) {
		$generator->output(sprintf("Will not generate documentation for\n @value@%s@c\n", implode("\n ", $skipping)));
	} elseif (!empty($skipping)) {
		$generator->output(sprintf("Will not generate documentation for @value@%s@c\n", $skipping[0]));
	}
	$generator->generate();

	// End
	$end = new \DateTime();
	$interval = $end->diff($start);
	$parts = array();
	if ($interval->h > 0) {
		$parts[] = sprintf('@count@%d@c hours', $interval->h);
	}
	if ($interval->i > 0) {
		$parts[] = sprintf('@count@%d@c min', $interval->i);
	}
	if ($interval->s > 0) {
		$parts[] = sprintf('@count@%d@c sec', $interval->s);
	}
	if (empty($parts)) {
		$parts[] = sprintf('@count@%d@c sec', 1);
	}

	$duration = implode(' ', $parts);
	$generator->output(sprintf("Done. Total time: %s, used: @count@%d@c MB RAM\n", $duration, round(memory_get_peak_usage(true) / 1024 / 1024)));

} catch (ConfigException $e) {
	// Configuration error
	echo $generator->colorize($generator->getHeader() . sprintf("\n@error@%s@c\n\n", $e->getMessage()) . $config->getHelp());

	die(2);
} catch (\Exception $e) {
	// Everything else
	if ($config->debug) {
		do {
			echo $generator->colorize(sprintf("\n%s(%d): @error@%s@c", $e->getFile(), $e->getLine(), $e->getMessage()));
			$trace = $e->getTraceAsString();
		} while ($e = $e->getPrevious());

		printf("\n\n%s\n", $trace);
	} else {
		echo $generator->colorize(sprintf("\n@error@%s@c\n", $e->getMessage()));
	}

	die(1);
}