[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Remote/OpenApiDoc/ -> ClassResolver.php (source)

   1  <?php
   2  
   3  namespace dokuwiki\Remote\OpenApiDoc;
   4  
   5  class ClassResolver
   6  {
   7      /** @var ClassResolver */
   8      private static $instance;
   9  
  10      protected $classUses = [];
  11      protected $classDocs = [];
  12  
  13      /**
  14       * Get a singleton instance
  15       *
  16       * Constructor is public for testing purposes
  17       * @return ClassResolver
  18       */
  19      public static function getInstance()
  20      {
  21          if (self::$instance === null) {
  22              self::$instance = new self();
  23          }
  24          return self::$instance;
  25      }
  26  
  27      /**
  28       * Resolve a class name to a fully qualified class name
  29       *
  30       * Results are cached in the instance for reuse
  31       *
  32       * @param string $classalias The class name to resolve
  33       * @param string $context The classname in which context in which the class is used
  34       * @return string No guarantee that the class exists! No leading backslash!
  35       */
  36      public function resolve($classalias, $context)
  37      {
  38          if ($classalias[0] === '\\') {
  39              // Fully qualified class name given
  40              return ltrim($classalias, '\\');
  41          }
  42          $classinfo = $this->getClassUses($context);
  43  
  44          return $classinfo['uses'][$classalias] ?? $classinfo['ownNS'] . '\\' . $classalias;
  45      }
  46  
  47      /**
  48       * Resolve a class name to a fully qualified class name and return a DocBlockClass for it
  49       *
  50       * Results are cached in the instance for reuse
  51       *
  52       * @param string $classalias The class name to resolve
  53       * @param string $context The classname in which context in which the class is used
  54       * @return DocBlockClass|null
  55       */
  56      public function document($classalias, $context)
  57      {
  58          $class = $this->resolve($classalias, $context);
  59          if (!class_exists($class)) return null;
  60  
  61          if (isset($this->classDocs[$class])) {
  62              $reflector = new \ReflectionClass($class);
  63              $this->classDocs[$class] = new DocBlockClass($reflector);
  64          }
  65  
  66          return $this->classDocs[$class];
  67      }
  68  
  69      /**
  70       * Cached fetching of all defined class aliases
  71       *
  72       * @param string $class The class to parse
  73       * @return array
  74       */
  75      public function getClassUses($class)
  76      {
  77          if (!isset($this->classUses[$class])) {
  78              $reflector = new \ReflectionClass($class);
  79              $source = $this->readSource($reflector->getFileName(), $reflector->getStartLine());
  80              $this->classUses[$class] = [
  81                  'ownNS' => $reflector->getNamespaceName(),
  82                  'uses' => $this->tokenizeSource($source)
  83              ];
  84          }
  85          return $this->classUses[$class];
  86      }
  87  
  88      /**
  89       * Parse the use statements from the given source code
  90       *
  91       * This is a simplified version of the code by @jasondmoss - we do not support multiple
  92       * classed within one file
  93       *
  94       * @link https://gist.github.com/jasondmoss/6200807
  95       * @param string $source
  96       * @return array
  97       */
  98      private function tokenizeSource($source)
  99      {
 100  
 101          $tokens = token_get_all($source);
 102  
 103          $useStatements = [];
 104          $record = false;
 105          $currentUse = [
 106              'class' => '',
 107              'as' => ''
 108          ];
 109  
 110          foreach ($tokens as $token) {
 111              if (!is_array($token)) {
 112                  // statement ended
 113                  if ($record) {
 114                      $useStatements[] = $currentUse;
 115                      $record = false;
 116                      $currentUse = [
 117                          'class' => '',
 118                          'as' => ''
 119                      ];
 120                  }
 121                  continue;
 122              }
 123              $tokenname = token_name($token[0]);
 124  
 125              if ($token[0] === T_CLASS) {
 126                  break;  // we reached the class itself, no need to parse further
 127              }
 128  
 129              if ($token[0] === T_USE) {
 130                  $record = 'class';
 131                  continue;
 132              }
 133  
 134              if ($token[0] === T_AS) {
 135                  $record = 'as';
 136                  continue;
 137              }
 138  
 139              if ($record) {
 140                  switch ($token[0]) {
 141                      case T_STRING:
 142                      case T_NS_SEPARATOR:
 143                      case defined('T_NAME_QUALIFIED') ? T_NAME_QUALIFIED : -1: // PHP 7.4 compatibility
 144                          $currentUse[$record] .= $token[1];
 145                          break;
 146                  }
 147              }
 148          }
 149  
 150          // Return a lookup table alias to FQCN
 151          $table = [];
 152          foreach ($useStatements as $useStatement) {
 153              $class = $useStatement['class'];
 154              $alias = $useStatement['as'] ?: substr($class, strrpos($class, '\\') + 1);
 155              $table[$alias] = $class;
 156          }
 157  
 158          return $table;
 159      }
 160  
 161  
 162      /**
 163       * Read file source up to the line where our class is defined.
 164       *
 165       * @return string
 166       */
 167      protected function readSource($file, $startline)
 168      {
 169          $file = fopen($file, 'r');
 170          $line = 0;
 171          $source = '';
 172  
 173          while (!feof($file)) {
 174              ++$line;
 175  
 176              if ($line >= $startline) {
 177                  break;
 178              }
 179  
 180              $source .= fgets($file);
 181          }
 182          fclose($file);
 183  
 184          return $source;
 185      }
 186  }