302 lines
8.5 KiB
PHP
302 lines
8.5 KiB
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 TokenReflection, TokenReflection\IReflectionConstant, TokenReflection\IReflectionFunction, TokenReflection\Broker, TokenReflection\Resolver;
|
|
use InvalidArgumentException, RuntimeException;
|
|
|
|
/**
|
|
* Customized TokenReflection broker backend.
|
|
*
|
|
* Adds internal classes from @param, @var, @return, @throws annotations as well
|
|
* as parent classes to the overall class list.
|
|
*/
|
|
class Backend extends Broker\Backend\Memory
|
|
{
|
|
/**
|
|
* Generator instance.
|
|
*
|
|
* @var \ApiGen\Generator
|
|
*/
|
|
private $generator;
|
|
|
|
/**
|
|
* Cache of processed token streams.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $fileCache = array();
|
|
|
|
/**
|
|
* Determines if token streams should be cached in filesystem.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $cacheTokenStreams = false;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param \ApiGen\Generator $generator Generator instance
|
|
* @param boolean $cacheTokenStreams If token stream should be cached
|
|
*/
|
|
public function __construct(Generator $generator, $cacheTokenStreams = false)
|
|
{
|
|
$this->generator = $generator;
|
|
$this->cacheTokenStreams = $cacheTokenStreams;
|
|
}
|
|
|
|
/**
|
|
* Destructor.
|
|
*
|
|
* Deletes all cached token streams.
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
foreach ($this->fileCache as $file) {
|
|
unlink($file);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a file to the backend storage.
|
|
*
|
|
* @param \TokenReflection\Stream\StreamBase $tokenStream Token stream
|
|
* @param \TokenReflection\ReflectionFile $file File reflection object
|
|
* @return \TokenReflection\Broker\Backend\Memory
|
|
*/
|
|
public function addFile(TokenReflection\Stream\StreamBase $tokenStream, TokenReflection\ReflectionFile $file)
|
|
{
|
|
if ($this->cacheTokenStreams) {
|
|
$this->fileCache[$file->getName()] = $cacheFile = tempnam(sys_get_temp_dir(), 'trc');
|
|
file_put_contents($cacheFile, serialize($tokenStream));
|
|
}
|
|
|
|
parent::addFile($tokenStream, $file);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of tokens for a particular file.
|
|
*
|
|
* @param string $fileName File name
|
|
* @return \TokenReflection\Stream
|
|
* @throws \RuntimeException If the token stream could not be returned.
|
|
*/
|
|
public function getFileTokens($fileName)
|
|
{
|
|
try {
|
|
if (!$this->isFileProcessed($fileName)) {
|
|
throw new InvalidArgumentException('File was not processed');
|
|
}
|
|
|
|
$realName = Broker::getRealPath($fileName);
|
|
if (!isset($this->fileCache[$realName])) {
|
|
throw new InvalidArgumentException('File is not in the cache');
|
|
}
|
|
|
|
$data = @file_get_contents($this->fileCache[$realName]);
|
|
if (false === $data) {
|
|
throw new RuntimeException('Cached file is not readable');
|
|
}
|
|
$file = @unserialize($data);
|
|
if (false === $file) {
|
|
throw new RuntimeException('Stream could not be loaded from cache');
|
|
}
|
|
|
|
return $file;
|
|
} catch (\Exception $e) {
|
|
throw new RuntimeException(sprintf('Could not return token stream for file %s', $fileName), 0, $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepares and returns used class lists.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function parseClassLists()
|
|
{
|
|
$allClasses = array(
|
|
self::TOKENIZED_CLASSES => array(),
|
|
self::INTERNAL_CLASSES => array(),
|
|
self::NONEXISTENT_CLASSES => array()
|
|
);
|
|
|
|
$declared = array_flip(array_merge(get_declared_classes(), get_declared_interfaces()));
|
|
|
|
foreach ($this->getNamespaces() as $namespace) {
|
|
foreach ($namespace->getClasses() as $name => $trClass) {
|
|
$class = new ReflectionClass($trClass, $this->generator);
|
|
$allClasses[self::TOKENIZED_CLASSES][$name] = $class;
|
|
if (!$class->isDocumented()) {
|
|
continue;
|
|
}
|
|
|
|
foreach (array_merge($trClass->getParentClasses(), $trClass->getInterfaces()) as $parentName => $parent) {
|
|
if ($parent->isInternal()) {
|
|
if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName])) {
|
|
$allClasses[self::INTERNAL_CLASSES][$parentName] = $parent;
|
|
}
|
|
} elseif (!$parent->isTokenized()) {
|
|
if (!isset($allClasses[self::NONEXISTENT_CLASSES][$parentName])) {
|
|
$allClasses[self::NONEXISTENT_CLASSES][$parentName] = $parent;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->generator->checkMemory();
|
|
}
|
|
}
|
|
|
|
foreach ($allClasses[self::TOKENIZED_CLASSES] as $class) {
|
|
if (!$class->isDocumented()) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($class->getOwnMethods() as $method) {
|
|
$allClasses = $this->processFunction($declared, $allClasses, $method);
|
|
}
|
|
|
|
foreach ($class->getOwnProperties() as $property) {
|
|
$annotations = $property->getAnnotations();
|
|
|
|
if (!isset($annotations['var'])) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($annotations['var'] as $doc) {
|
|
foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) {
|
|
if ($name = rtrim($name, '[]')) {
|
|
$name = Resolver::resolveClassFQN($name, $class->getNamespaceAliases(), $class->getNamespaceName());
|
|
$allClasses = $this->addClass($declared, $allClasses, $name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->generator->checkMemory();
|
|
}
|
|
|
|
foreach ($this->getFunctions() as $function) {
|
|
$allClasses = $this->processFunction($declared, $allClasses, $function);
|
|
}
|
|
|
|
array_walk_recursive($allClasses, function(&$reflection, $name, Generator $generator) {
|
|
if (!$reflection instanceof ReflectionClass) {
|
|
$reflection = new ReflectionClass($reflection, $generator);
|
|
}
|
|
}, $this->generator);
|
|
|
|
return $allClasses;
|
|
}
|
|
|
|
/**
|
|
* Processes a function/method and adds classes from annotations to the overall class array.
|
|
*
|
|
* @param array $declared Array of declared classes
|
|
* @param array $allClasses Array with all classes parsed so far
|
|
* @param \ApiGen\ReflectionFunction|\TokenReflection\IReflectionFunctionBase $function Function/method reflection
|
|
* @return array
|
|
*/
|
|
private function processFunction(array $declared, array $allClasses, $function)
|
|
{
|
|
static $parsedAnnotations = array('param', 'return', 'throws');
|
|
|
|
$annotations = $function->getAnnotations();
|
|
foreach ($parsedAnnotations as $annotation) {
|
|
if (!isset($annotations[$annotation])) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($annotations[$annotation] as $doc) {
|
|
foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) {
|
|
if ($name) {
|
|
$name = Resolver::resolveClassFQN(rtrim($name, '[]'), $function->getNamespaceAliases(), $function->getNamespaceName());
|
|
$allClasses = $this->addClass($declared, $allClasses, $name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($function->getParameters() as $param) {
|
|
if ($hint = $param->getClassName()) {
|
|
$allClasses = $this->addClass($declared, $allClasses, $hint);
|
|
}
|
|
}
|
|
|
|
return $allClasses;
|
|
}
|
|
|
|
/**
|
|
* Adds a class to list of classes.
|
|
*
|
|
* @param array $declared Array of declared classes
|
|
* @param array $allClasses Array with all classes parsed so far
|
|
* @param string $name Class name
|
|
* @return array
|
|
*/
|
|
private function addClass(array $declared, array $allClasses, $name)
|
|
{
|
|
$name = ltrim($name, '\\');
|
|
|
|
if (!isset($declared[$name]) || isset($allClasses[self::TOKENIZED_CLASSES][$name])
|
|
|| isset($allClasses[self::INTERNAL_CLASSES][$name]) || isset($allClasses[self::NONEXISTENT_CLASSES][$name])
|
|
) {
|
|
return $allClasses;
|
|
}
|
|
|
|
$parameterClass = $this->getBroker()->getClass($name);
|
|
if ($parameterClass->isInternal()) {
|
|
$allClasses[self::INTERNAL_CLASSES][$name] = $parameterClass;
|
|
foreach (array_merge($parameterClass->getInterfaces(), $parameterClass->getParentClasses()) as $parentClass) {
|
|
if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName = $parentClass->getName()])) {
|
|
$allClasses[self::INTERNAL_CLASSES][$parentName] = $parentClass;
|
|
}
|
|
}
|
|
} elseif (!$parameterClass->isTokenized() && !isset($allClasses[self::NONEXISTENT_CLASSES][$name])) {
|
|
$allClasses[self::NONEXISTENT_CLASSES][$name] = $parameterClass;
|
|
}
|
|
|
|
return $allClasses;
|
|
}
|
|
|
|
/**
|
|
* Returns all constants from all namespaces.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getConstants()
|
|
{
|
|
$generator = $this->generator;
|
|
return array_map(function(IReflectionConstant $constant) use ($generator) {
|
|
return new ReflectionConstant($constant, $generator);
|
|
}, parent::getConstants());
|
|
}
|
|
|
|
/**
|
|
* Returns all functions from all namespaces.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getFunctions()
|
|
{
|
|
$generator = $this->generator;
|
|
return array_map(function(IReflectionFunction $function) use ($generator) {
|
|
return new ReflectionFunction($function, $generator);
|
|
}, parent::getFunctions());
|
|
}
|
|
}
|