Stream.php

File

drupal/core/vendor/phpunit/php-token-stream/PHP/Token/Stream.php
View source
<?php

/**
 * php-token-stream
 *
 * Copyright (c) 2009-2012, Sebastian Bergmann <sb@sebastian-bergmann.de>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *   * Neither the name of Sebastian Bergmann nor the names of his
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @package   PHP_TokenStream
 * @author    Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright 2009-2012 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license   http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @since     File available since Release 1.0.0
 */

/**
 * A stream of PHP tokens.
 *
 * @author    Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @copyright 2009-2012 Sebastian Bergmann <sb@sebastian-bergmann.de>
 * @license   http://www.opensource.org/licenses/BSD-3-Clause  The BSD 3-Clause License
 * @version   Release: @package_version@
 * @link      http://github.com/sebastianbergmann/php-token-stream/tree
 * @since     Class available since Release 1.0.0
 */
class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator {

  /**
   * @var array
   */
  protected static $customTokens = array(
    '(' => 'PHP_Token_OPEN_BRACKET',
    ')' => 'PHP_Token_CLOSE_BRACKET',
    '[' => 'PHP_Token_OPEN_SQUARE',
    ']' => 'PHP_Token_CLOSE_SQUARE',
    '{' => 'PHP_Token_OPEN_CURLY',
    '}' => 'PHP_Token_CLOSE_CURLY',
    ';' => 'PHP_Token_SEMICOLON',
    '.' => 'PHP_Token_DOT',
    ',' => 'PHP_Token_COMMA',
    '=' => 'PHP_Token_EQUAL',
    '<' => 'PHP_Token_LT',
    '>' => 'PHP_Token_GT',
    '+' => 'PHP_Token_PLUS',
    '-' => 'PHP_Token_MINUS',
    '*' => 'PHP_Token_MULT',
    '/' => 'PHP_Token_DIV',
    '?' => 'PHP_Token_QUESTION_MARK',
    '!' => 'PHP_Token_EXCLAMATION_MARK',
    ':' => 'PHP_Token_COLON',
    '"' => 'PHP_Token_DOUBLE_QUOTES',
    '@' => 'PHP_Token_AT',
    '&' => 'PHP_Token_AMPERSAND',
    '%' => 'PHP_Token_PERCENT',
    '|' => 'PHP_Token_PIPE',
    '$' => 'PHP_Token_DOLLAR',
    '^' => 'PHP_Token_CARET',
    '~' => 'PHP_Token_TILDE',
    '`' => 'PHP_Token_BACKTICK',
  );

  /**
   * @var string
   */
  protected $filename;

  /**
   * @var array
   */
  protected $tokens = array();

  /**
   * @var integer
   */
  protected $position = 0;

  /**
   * @var array
   */
  protected $linesOfCode = array(
    'loc' => 0,
    'cloc' => 0,
    'ncloc' => 0,
  );

  /**
   * @var array
   */
  protected $classes;

  /**
   * @var array
   */
  protected $functions;

  /**
   * @var array
   */
  protected $includes;

  /**
   * @var array
   */
  protected $interfaces;

  /**
   * @var array
   */
  protected $traits;

  /**
   * Constructor.
   *
   * @param string $sourceCode
   */
  public function __construct($sourceCode) {
    if (is_file($sourceCode)) {
      $this->filename = $sourceCode;
      $sourceCode = file_get_contents($sourceCode);
    }
    $this
      ->scan($sourceCode);
  }

  /**
   * Destructor.
   */
  public function __destruct() {
    $this->tokens = array();
  }

  /**
   * @return string
   */
  public function __toString() {
    $buffer = '';
    foreach ($this as $token) {
      $buffer .= $token;
    }
    return $buffer;
  }

  /**
   * @return string
   * @since  Method available since Release 1.1.0
   */
  public function getFilename() {
    return $this->filename;
  }

  /**
   * Scans the source for sequences of characters and converts them into a
   * stream of tokens.
   *
   * @param string $sourceCode
   */
  protected function scan($sourceCode) {
    $line = 1;
    $tokens = token_get_all($sourceCode);
    $numTokens = count($tokens);
    for ($i = 0; $i < $numTokens; ++$i) {
      $token = $tokens[$i];
      unset($tokens[$i]);
      if (is_array($token)) {
        $text = $token[1];
        $tokenClass = 'PHP_Token_' . substr(token_name($token[0]), 2);
      }
      else {
        $text = $token;
        $tokenClass = self::$customTokens[$token];
      }
      $this->tokens[] = new $tokenClass($text, $line, $this, $i);
      $lines = substr_count($text, "\n");
      $line += $lines;
      if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
        break;
      }
      else {
        if ($tokenClass == 'PHP_Token_COMMENT' || $tokenClass == 'PHP_Token_DOC_COMMENT') {
          $this->linesOfCode['cloc'] += $lines + 1;
        }
      }
    }
    $this->linesOfCode['loc'] = substr_count($sourceCode, "\n");
    $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - $this->linesOfCode['cloc'];
  }

  /**
   * @return integer
   */
  public function count() {
    return count($this->tokens);
  }

  /**
   * @return PHP_Token[]
   */
  public function tokens() {
    return $this->tokens;
  }

  /**
   * @return array
   */
  public function getClasses() {
    if ($this->classes !== NULL) {
      return $this->classes;
    }
    $this
      ->parse();
    return $this->classes;
  }

  /**
   * @return array
   */
  public function getFunctions() {
    if ($this->functions !== NULL) {
      return $this->functions;
    }
    $this
      ->parse();
    return $this->functions;
  }

  /**
   * @return array
   */
  public function getInterfaces() {
    if ($this->interfaces !== NULL) {
      return $this->interfaces;
    }
    $this
      ->parse();
    return $this->interfaces;
  }

  /**
   * @return array
   * @since  Method available since Release 1.1.0
   */
  public function getTraits() {
    if ($this->traits !== NULL) {
      return $this->traits;
    }
    $this
      ->parse();
    return $this->traits;
  }

  /**
   * Gets the names of all files that have been included
   * using include(), include_once(), require() or require_once().
   *
   * Parameter $categorize set to TRUE causing this function to return a
   * multi-dimensional array with categories in the keys of the first dimension
   * and constants and their values in the second dimension.
   *
   * Parameter $category allow to filter following specific inclusion type
   *
   * @param bool   $categorize OPTIONAL
   * @param string $category   OPTIONAL Either 'require_once', 'require',
   *                                           'include_once', 'include'.
   * @return array
   * @since  Method available since Release 1.1.0
   */
  public function getIncludes($categorize = FALSE, $category = NULL) {
    if ($this->includes === NULL) {
      $this->includes = array(
        'require_once' => array(),
        'require' => array(),
        'include_once' => array(),
        'include' => array(),
      );
      foreach ($this->tokens as $token) {
        switch (get_class($token)) {
          case 'PHP_Token_REQUIRE_ONCE':
          case 'PHP_Token_REQUIRE':
          case 'PHP_Token_INCLUDE_ONCE':
          case 'PHP_Token_INCLUDE':
            $this->includes[$token
              ->getType()][] = $token
              ->getName();
            break;
        }
      }
    }
    if (isset($this->includes[$category])) {
      $includes = $this->includes[$category];
    }
    else {
      if ($categorize === FALSE) {
        $includes = array_merge($this->includes['require_once'], $this->includes['require'], $this->includes['include_once'], $this->includes['include']);
      }
      else {
        $includes = $this->includes;
      }
    }
    return $includes;
  }
  protected function parse() {
    $this->interfaces = array();
    $this->classes = array();
    $this->traits = array();
    $this->functions = array();
    $class = FALSE;
    $classEndLine = FALSE;
    $trait = FALSE;
    $traitEndLine = FALSE;
    $interface = FALSE;
    $interfaceEndLine = FALSE;
    foreach ($this->tokens as $token) {
      switch (get_class($token)) {
        case 'PHP_Token_HALT_COMPILER':
          return;
          break;
        case 'PHP_Token_INTERFACE':
          $interface = $token
            ->getName();
          $interfaceEndLine = $token
            ->getEndLine();
          $this->interfaces[$interface] = array(
            'methods' => array(),
            'parent' => $token
              ->getParent(),
            'keywords' => $token
              ->getKeywords(),
            'docblock' => $token
              ->getDocblock(),
            'startLine' => $token
              ->getLine(),
            'endLine' => $interfaceEndLine,
            'package' => $token
              ->getPackage(),
            'file' => $this->filename,
          );
          break;
        case 'PHP_Token_CLASS':
        case 'PHP_Token_TRAIT':
          $tmp = array(
            'methods' => array(),
            'parent' => $token
              ->getParent(),
            'interfaces' => $token
              ->getInterfaces(),
            'keywords' => $token
              ->getKeywords(),
            'docblock' => $token
              ->getDocblock(),
            'startLine' => $token
              ->getLine(),
            'endLine' => $token
              ->getEndLine(),
            'package' => $token
              ->getPackage(),
            'file' => $this->filename,
          );
          if ($token instanceof PHP_Token_CLASS) {
            $class = $token
              ->getName();
            $classEndLine = $token
              ->getEndLine();
            $this->classes[$class] = $tmp;
          }
          else {
            $trait = $token
              ->getName();
            $traitEndLine = $token
              ->getEndLine();
            $this->traits[$trait] = $tmp;
          }
          break;
        case 'PHP_Token_FUNCTION':
          $name = $token
            ->getName();
          $tmp = array(
            'docblock' => $token
              ->getDocblock(),
            'keywords' => $token
              ->getKeywords(),
            'visibility' => $token
              ->getVisibility(),
            'signature' => $token
              ->getSignature(),
            'startLine' => $token
              ->getLine(),
            'endLine' => $token
              ->getEndLine(),
            'ccn' => $token
              ->getCCN(),
            'file' => $this->filename,
          );
          if ($class === FALSE && $trait === FALSE && $interface === FALSE) {
            $this->functions[$name] = $tmp;
          }
          else {
            if ($class !== FALSE) {
              $this->classes[$class]['methods'][$name] = $tmp;
            }
            else {
              if ($trait !== FALSE) {
                $this->traits[$trait]['methods'][$name] = $tmp;
              }
              else {
                $this->interfaces[$interface]['methods'][$name] = $tmp;
              }
            }
          }
          break;
        case 'PHP_Token_CLOSE_CURLY':
          if ($classEndLine !== FALSE && $classEndLine == $token
            ->getLine()) {
            $class = FALSE;
            $classEndLine = FALSE;
          }
          else {
            if ($traitEndLine !== FALSE && $traitEndLine == $token
              ->getLine()) {
              $trait = FALSE;
              $traitEndLine = FALSE;
            }
            else {
              if ($interfaceEndLine !== FALSE && $interfaceEndLine == $token
                ->getLine()) {
                $interface = FALSE;
                $interfaceEndLine = FALSE;
              }
            }
          }
          break;
      }
    }
  }

  /**
   * @return array
   */
  public function getLinesOfCode() {
    return $this->linesOfCode;
  }

  /**
   */
  public function rewind() {
    $this->position = 0;
  }

  /**
   * @return boolean
   */
  public function valid() {
    return isset($this->tokens[$this->position]);
  }

  /**
   * @return integer
   */
  public function key() {
    return $this->position;
  }

  /**
   * @return PHP_Token
   */
  public function current() {
    return $this->tokens[$this->position];
  }

  /**
   */
  public function next() {
    $this->position++;
  }

  /**
   * @param mixed $offset
   */
  public function offsetExists($offset) {
    return isset($this->tokens[$offset]);
  }

  /**
   * @param  mixed $offset
   * @return mixed
   */
  public function offsetGet($offset) {
    return $this->tokens[$offset];
  }

  /**
   * @param mixed $offset
   * @param mixed $value
   */
  public function offsetSet($offset, $value) {
    $this->tokens[$offset] = $value;
  }

  /**
   * @param mixed $offset
   */
  public function offsetUnset($offset) {
    unset($this->tokens[$offset]);
  }

  /**
   * Seek to an absolute position.
   *
   * @param  integer $position
   * @throws OutOfBoundsException
   */
  public function seek($position) {
    $this->position = $position;
    if (!$this
      ->valid()) {
      throw new OutOfBoundsException('Invalid seek position');
    }
  }

}

Classes

Namesort descending Description
PHP_Token_Stream A stream of PHP tokens.