|
- <?php
-
- namespace dokuwiki\Remote\OpenApiDoc;
-
- class ClassResolver
- {
- /** @var ClassResolver */
- private static $instance;
-
- protected $classUses = [];
- protected $classDocs = [];
-
- /**
- * Get a singleton instance
- *
- * Constructor is public for testing purposes
- * @return ClassResolver
- */
- public static function getInstance()
- {
- if (self::$instance === null) {
- self::$instance = new self();
- }
- return self::$instance;
- }
-
- /**
- * Resolve a class name to a fully qualified class name
- *
- * Results are cached in the instance for reuse
- *
- * @param string $classalias The class name to resolve
- * @param string $context The classname in which context in which the class is used
- * @return string No guarantee that the class exists! No leading backslash!
- */
- public function resolve($classalias, $context)
- {
- if ($classalias[0] === '\\') {
- // Fully qualified class name given
- return ltrim($classalias, '\\');
- }
- $classinfo = $this->getClassUses($context);
-
- return $classinfo['uses'][$classalias] ?? $classinfo['ownNS'] . '\\' . $classalias;
- }
-
- /**
- * Resolve a class name to a fully qualified class name and return a DocBlockClass for it
- *
- * Results are cached in the instance for reuse
- *
- * @param string $classalias The class name to resolve
- * @param string $context The classname in which context in which the class is used
- * @return DocBlockClass|null
- */
- public function document($classalias, $context)
- {
- $class = $this->resolve($classalias, $context);
- if (!class_exists($class)) return null;
-
- if (isset($this->classDocs[$class])) {
- $reflector = new \ReflectionClass($class);
- $this->classDocs[$class] = new DocBlockClass($reflector);
- }
-
- return $this->classDocs[$class];
- }
-
- /**
- * Cached fetching of all defined class aliases
- *
- * @param string $class The class to parse
- * @return array
- */
- public function getClassUses($class)
- {
- if (!isset($this->classUses[$class])) {
- $reflector = new \ReflectionClass($class);
- $source = $this->readSource($reflector->getFileName(), $reflector->getStartLine());
- $this->classUses[$class] = [
- 'ownNS' => $reflector->getNamespaceName(),
- 'uses' => $this->tokenizeSource($source)
- ];
- }
- return $this->classUses[$class];
- }
-
- /**
- * Parse the use statements from the given source code
- *
- * This is a simplified version of the code by @jasondmoss - we do not support multiple
- * classed within one file
- *
- * @link https://gist.github.com/jasondmoss/6200807
- * @param string $source
- * @return array
- */
- private function tokenizeSource($source)
- {
-
- $tokens = token_get_all($source);
-
- $useStatements = [];
- $record = false;
- $currentUse = [
- 'class' => '',
- 'as' => ''
- ];
-
- foreach ($tokens as $token) {
- if (!is_array($token)) {
- // statement ended
- if ($record) {
- $useStatements[] = $currentUse;
- $record = false;
- $currentUse = [
- 'class' => '',
- 'as' => ''
- ];
- }
- continue;
- }
- $tokenname = token_name($token[0]);
-
- if ($token[0] === T_CLASS) {
- break; // we reached the class itself, no need to parse further
- }
-
- if ($token[0] === T_USE) {
- $record = 'class';
- continue;
- }
-
- if ($token[0] === T_AS) {
- $record = 'as';
- continue;
- }
-
- if ($record) {
- switch ($token[0]) {
- case T_STRING:
- case T_NS_SEPARATOR:
- case defined('T_NAME_QUALIFIED') ? T_NAME_QUALIFIED : -1: // PHP 7.4 compatibility
- $currentUse[$record] .= $token[1];
- break;
- }
- }
- }
-
- // Return a lookup table alias to FQCN
- $table = [];
- foreach ($useStatements as $useStatement) {
- $class = $useStatement['class'];
- $alias = $useStatement['as'] ?: substr($class, strrpos($class, '\\') + 1);
- $table[$alias] = $class;
- }
-
- return $table;
- }
-
-
- /**
- * Read file source up to the line where our class is defined.
- *
- * @return string
- */
- protected function readSource($file, $startline)
- {
- $file = fopen($file, 'r');
- $line = 0;
- $source = '';
-
- while (!feof($file)) {
- ++$line;
-
- if ($line >= $startline) {
- break;
- }
-
- $source .= fgets($file);
- }
- fclose($file);
-
- return $source;
- }
- }
|