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()); } }