woocommerce/apigen/ApiGen/Backend.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());
}
}