<NME> TokenArrayScanner.php
<BEF> <?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Code\Scanner;

use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;

use function array_shift;
use function array_slice;
use function in_array;
use function is_array;
use function is_int;
use function is_string;

class TokenArrayScanner implements ScannerInterface
{
    /**
     * @var bool
     */
    protected $isScanned = false;


    /**
     * @param null|array $tokens
     * @param null|array $options
     */
    public function __construct($tokens = null)
    {
     * @var null
     */
    protected $docComment;

    /**
     * @var NameInformation
     */
    protected $nameInformation;

    /**
     * @var array
     */
    protected $infos = [];

        $this->reset();
    }

    public function getNamespaces($returnScannerClass = false)
    {
        $this->scan();
        
        if (!$returnScannerClass) {
            $namespaces = array();
            foreach ($this->infos as $info) {
                if ($info['type'] == 'namespace') {
                    $namespaces[] = $info['namespace'];
                }
            }
            return $namespaces;
        } else {
            if ($returnScannerClass === true) {
                $returnScannerClass = 'Zend\Code\Scanner\NamespaceScanner';
            }
            $scannerClass = new $returnScannerClass;
            // @todo
        }
    }
    
    public function getUses($returnScannerClass = false)
    {
        $this->scan();
        
        if (!$returnScannerClass) {
            $namespaces = array();
            foreach ($this->infos as $info) {
                if ($info['type'] == 'uses') {
                    $namespaces[] = $info['uses'];
                }
            }
            return $namespaces;
        }
    }
    
    public function getIncludes($returnScannerClass = false)
    {
        $this->scan();
        // @todo Implement getIncludes() in TokenArrayScanner
        foreach ($this->tokens as $token) {
            $type    = $token[0];
            $value   = $token[1];
            if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
                continue;
            } elseif ($type == T_DOC_COMMENT) {
                $this->docComment = $value;

                return $this->docComment;
            } else {
                // Only whitespace is allowed before file docblocks
                return;
            }
        }
    }

    /**
     * @return array
     */
    public function getNamespaces()
    {
        $this->scan();

        $namespaces = [];
        foreach ($this->infos as $info) {
            if ($info['type'] == 'namespace') {
                $namespaces[] = $info['namespace'];
            }
        }

        return $namespaces;
    }

    /**
     * @param  null|string $namespace
     * @return array|null
     */
    public function getUses($namespace = null)
    {
        $this->scan();

        return $this->getUsesNoScan($namespace);
    }

    /**
     * @return void
     */
    public function getIncludes()
    {
        $this->scan();
        // @todo Implement getIncludes() in TokenArrayScanner
    }

    /**
     * @return array
     */
    public function getClassNames()
    {
        $this->scan();

        $return = [];
        foreach ($this->infos as $info) {
            if ($info['type'] != 'class') {
                continue;
            }

            $return[] = $info['name'];
        }

        return $return;
    }

    /**
     * @return ClassScanner[]
     */
    public function getClasses()
    {
        $this->scan();

        $return = [];
        foreach ($this->infos as $info) {
            if ($info['type'] != 'class') {
                continue;
            }

            $return[] = $this->getClass($info['name']);
        }

        return $return;
    }

    /**
     * Return the class object from this scanner
     *
     * @param  string|int $name
     * @throws Exception\InvalidArgumentException
     * @return ClassScanner|false
     */
    public function getClass($name)
    {
        $this->scan();

        if (is_int($name)) {
            $info = $this->infos[$name];
            if ($info['type'] != 'class') {
                throw new Exception\InvalidArgumentException('Index of info offset is not about a class');
            }
        } elseif (is_string($name)) {
            $classFound = false;
            foreach ($this->infos as $info) {
            throw new Exception\RuntimeException('No tokens were provided');
        }

        $currentNamespace = null;
        $currentDocCommentIndex = false;

        for ($tokenIndex = 0; $tokenIndex < count($this->tokens); $tokenIndex++) {
            $token = $this->tokens[$tokenIndex];

            // track docblocks through whitespace tokens (they might belong to something)
            if ($currentDocCommentIndex !== false && is_array($token) && $token[0] != T_WHITESPACE) {
                $currentDocCommentIndex = false;
            }

            // tokens with some value are arrays (will have a token identifier, & line num)
            $fastForward = 0;
            switch ($token[0]) {
                case T_DOC_COMMENT:
                    $currentDocCommentIndex = $tokenIndex;
                    break;

                case T_NAMESPACE:
                    $currentNamespace = $this->scanNamespace($tokenIndex, $fastForward);
                    break;

                case T_USE:
                    // process uses
                    $this->scanUse($tokenIndex, $fastForward);
                    break;

                case T_INCLUDE:
                case T_INCLUDE_ONCE:
                case T_REQUIRE:
                case T_REQUIRE_ONCE:
                    // process include
                    $this->scanInclude($tokenIndex, $fastForward);
                    break;

                case T_FINAL:
                case T_ABSTRACT:
                case T_CLASS:
                case T_INTERFACE:
                    $this->scanClass(($currentDocCommentIndex) ?: $tokenIndex, $fastForward, $currentNamespace);
                    break;

                case T_FUNCTION:
                    $this->scanFunction(($currentDocCommentIndex) ?: $tokenIndex, $fastForward, $currentNamespace);
                    break;
            }
            if ($fastForward) {
                $tokenIndex += $fastForward - 1;
            }
        }

        $this->isScanned = true;
    }

    protected function scanNamespace($tokenIndex, &$fastForward)
    {
        $info = array(
            'type'       => 'namespace',
            'tokenStart' => $tokenIndex,
            'tokenEnd'   => null,
            'lineStart'  => $this->tokens[$tokenIndex][2],
            'lineEnd'    => null,
            'namespace'  => null,
        );

        // move past current T_NAMESPACE & following T_WHITESPACE
        $tokenIndex++;
        $fastForward++;

        while (true) {
            $tokenIndex++;
            $fastForward++;
            $token = $this->tokens[$tokenIndex];

            // BREAK ON:
            if (is_string($token) && $token == ';') {
                break;
            }

            // ANALYZE
            if (is_array($token)) {
                $info['lineEnd'] = $token[2];
                if ($token[0] == T_WHITESPACE) {
                    continue;
                }
                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
                    $info['namespace'] .= (is_string($token)) ? $token : $token[1];
                }
            }
        }

        $info['tokenEnd'] = $tokenIndex;
        $this->infos[]    = $info;

        return $info['namespace'];
    }

    protected function scanUse($tokenIndex, &$fastForward)
    {
        $info = array(
            'type'       => 'use',
            'tokenStart' => $tokenIndex,
            'tokenEnd'   => null,
            'lineStart'  => $this->tokens[$tokenIndex][2],
            'lineEnd'    => null,
            'statements' => array(),
        );

        // Static for performance purposes
        static $statementTemplate = array(
            'use'        => null,
            'as'         => null
        );

        // skip current token T_USE and following T_WHITESPACE
        $tokenIndex++;
        $fastForward++;

        $hasAs     = false;
        $sCount    = 0;
        $statement = $statementTemplate;

        while (true) {
            $tokenIndex++;
            $fastForward++;
            $token = $this->tokens[$tokenIndex];

            // BREAK ON:
            if (is_string($token) && $token == ';') {
                break;
            }

            // ANALYZE
            if (is_array($token)) {
                // store known line end
                $info['lineEnd'] = $token[2];

                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
                    if ($hasAs == false) {
                        $statement['use'] .= (is_string($token)) ? $token : $token[1];
                    } else {
                        $statement['as'] = $token[1]; // always a string
                    }
                }
                if ($token[0] == T_AS) {
                    $hasAs = true;
                }
            }

            $tokenLookahead = $this->tokens[$tokenIndex+1];

            if (is_string($tokenLookahead) && ($tokenLookahead == ',' || $tokenLookahead == ';')) {
                $info['statements'][$sCount] = $statement;
                $sCount++;
                $statement = $statementTemplate;
                $hasAs = false;
            }

        }

        $info['tokenEnd'] = $tokenIndex;
        $this->infos[]    = $info;
    }

    protected function scanInclude($tokenIndex, &$fastForward)
    {
        // Static for performance purposes
        static $types = array(
            T_INCLUDE      => 'include',
            T_INCLUDE_ONCE => 'include_once',
            T_REQUIRE      => 'require',
            T_REQUIRE_ONCE => 'require_once',
        );

        $info = array(
            'type'        => 'include',
            'tokenStart'  => $tokenIndex,
            'tokenEnd'    => null,
            'lineStart'   => $this->tokens[$tokenIndex][2],
            'lineEnd'     => null,
            'includeType' => $types[$this->tokens[$tokenIndex][0]],
            'path'        => '',
        );

        $index = $tokenIndex;

        // move past include & the required whitespace
        $fastForward++;
        $index++;

        while (true) {
            $fastForward++;
            $tokenIndex++;
            $token = $this->tokens[$index++];

            // BREAK ON
            if (is_string($token) && $token == ';') {
                break;
            }

            // ANALYZE
            if (is_array($token)) {
                $info['lineEnd'] = $token[2];
            }

            $info['path'] .= (is_string($token)) ? $token : $token[1];
        }

        $info['tokenEnd'] = $tokenIndex;
        $this->infos[] = $info;
    }

    protected function scanClass($tokenIndex, &$fastForward, $namespace = null)
    {
        $info = array(
            'type'        => 'class',
            'tokenStart'  => $tokenIndex,
            'tokenEnd'    => null,
            'lineStart'   => $this->tokens[$tokenIndex][2],
            'lineEnd'     => null,
            'namespace'   => $namespace,
            'name'        => null,
            'shortName'   => null,
        );
        
        $braceCount = 0;
        while (true) {
            $token = $this->tokens[$tokenIndex];

            // BREAK ON
            if (is_string($token) && $token == '}' && $braceCount == 1) {
                break;
            }

            // ANALYZE
            if ($token[0] === T_CLASS) {
                $info['shortName'] = $this->tokens[$tokenIndex+2][1];
                $info['name'] = (($namespace) ? $namespace . '\\' : '') . $info['shortName'];
            }

            if (is_string($token)) {
                if ($token == '{') {
                    $braceCount++;
                }
                if ($token == '}') {
                    $braceCount--;
                }
            }

            if (is_array($token)) {
                $info['lineEnd'] = $token[2];
            }

            // MOVE FORWARD
            $fastForward++;
            $tokenIndex++;
        }

        $info['tokenEnd'] = $tokenIndex;
        $this->infos[]    = $info;
    }

    protected function scanFunction($tokenIndex, &$fastForward, $namespace = null)
    {
        $info = array(
            'type'        => 'function',
            'tokenStart'  => $tokenIndex,
            'tokenEnd'    => null,
            'lineStart'   => $this->tokens[$tokenIndex][2],
            'lineEnd'     => null,
            'name'        => $namespace . '\\' . $this->tokens[$tokenIndex+2][1],
            'shortName'   => $this->tokens[$tokenIndex+2][1],
            'namespace'   => $namespace,
        );

        $braceCount = 0;
        while (true) {
            $fastForward++;
            $tokenIndex++;
            $token = $this->tokens[$tokenIndex];

            // BREAK ON
            if ($braceCount === false) {
                break;
            }

            // ANALYZE
            if (is_string($token)) {
                if ($token == '{') {
                    $context = null;
                    $braceCount++;
                }
                if ($token == '}') {
                    $braceCount = ($braceCount == 1) ? false : ($braceCount - 1);
                }
            }
            if (is_array($token)) {
                $info['lineEnd'] = $token[2];
            }
        }

        $info['tokenEnd'] = $tokenIndex;
        $this->infos[]    = $info;
    }

    // @todo hasNamespace(), getNamespace()


}
                    }
                }

                SCANNER_USE_CONTINUE:

                if ($MACRO_TOKEN_ADVANCE() === false) {
                    goto SCANNER_END;
                }
                goto SCANNER_USE_TOP;

                SCANNER_USE_END:

                $MACRO_INFO_ADVANCE();
                goto SCANNER_CONTINUE;
                // goto no break needed

            case T_INCLUDE:
            case T_INCLUDE_ONCE:
            case T_REQUIRE:
            case T_REQUIRE_ONCE:
                // Static for performance
                static $includeTypes = [
                    T_INCLUDE      => 'include',
                    T_INCLUDE_ONCE => 'include_once',
                    T_REQUIRE      => 'require',
                    T_REQUIRE_ONCE => 'require_once',
                ];

                $infos[$infoIndex] = [
                    'type'        => 'include',
                    'tokenStart'  => $MACRO_TOKEN_LOGICAL_START_INDEX(),
                    'tokenEnd'    => null,
                    'lineStart'   => $tokens[$tokenIndex][2],
                    'lineEnd'     => null,
                    'includeType' => $includeTypes[$tokens[$tokenIndex][0]],
                    'path'        => '',
                ];

                // start processing with next token
                if ($MACRO_TOKEN_ADVANCE() === false) {
                    goto SCANNER_END;
                }

                SCANNER_INCLUDE_TOP:

                if ($tokenType === null && $tokenContent === ';') {
                    goto SCANNER_INCLUDE_END;
                }

                $infos[$infoIndex]['path'] .= $tokenContent;

                SCANNER_INCLUDE_CONTINUE:

                if ($MACRO_TOKEN_ADVANCE() === false) {
                    goto SCANNER_END;
                }
                goto SCANNER_INCLUDE_TOP;

                SCANNER_INCLUDE_END:

                $MACRO_INFO_ADVANCE();
                goto SCANNER_CONTINUE;
                // goto no break needed

            case T_FUNCTION:
            case T_FINAL:
            case T_ABSTRACT:
            case T_CLASS:
            case T_INTERFACE:
            case T_TRAIT:
                $infos[$infoIndex] = [
                    'type'        => $tokenType === T_FUNCTION ? 'function' : 'class',
                    'tokenStart'  => $MACRO_TOKEN_LOGICAL_START_INDEX(),
                    'tokenEnd'    => null,
                    'lineStart'   => $tokens[$tokenIndex][2],
                    'lineEnd'     => null,
                    'namespace'   => $namespace,
                    'uses'        => $this->getUsesNoScan($namespace),
                    'name'        => null,
                    'shortName'   => null,
                ];

                $classBraceCount = 0;

                // start processing with current token

                SCANNER_CLASS_TOP:

                // process the name
                if ($infos[$infoIndex]['shortName'] == ''
                    && (($tokenType === T_CLASS
                            || $tokenType === T_INTERFACE
                            || $tokenType === T_TRAIT)
                        && $infos[$infoIndex]['type'] === 'class'
                        || ($tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function'))
                ) {
                    $infos[$infoIndex]['shortName'] = is_array($tokens[$tokenIndex + 2])
                        ? $tokens[$tokenIndex + 2][1]
                        : $tokens[$tokenIndex + 2];
                    $infos[$infoIndex]['name']      = ($namespace !== null
                        ? $namespace . '\\'
                        : '') . $infos[$infoIndex]['shortName'];
                }

                if ($tokenType === null) {
                    if ($tokenContent == '{') {
                        $classBraceCount++;
                    }
                    if ($tokenContent == '}') {
                        $classBraceCount--;
                        if ($classBraceCount === 0) {
                            goto SCANNER_CLASS_END;
                        }
                    }
                }

                SCANNER_CLASS_CONTINUE:

                if ($MACRO_TOKEN_ADVANCE() === false) {
                    goto SCANNER_END;
                }
                goto SCANNER_CLASS_TOP;

                SCANNER_CLASS_END:

                $MACRO_INFO_ADVANCE();
                goto SCANNER_CONTINUE;
                // goto no break needed
        }

        SCANNER_CONTINUE:

        if ($MACRO_TOKEN_ADVANCE() === false) {
            goto SCANNER_END;
        }
        goto SCANNER_TOP;

        SCANNER_END:

        /**
         * END FINITE STATE MACHINE FOR SCANNING TOKENS
         */
        $this->isScanned = true;
    }

    /**
     * Check for namespace
     *
     * @param string $namespace
     * @return bool
     */
    public function hasNamespace($namespace)
    {
        $this->scan();

        foreach ($this->infos as $info) {
            if ($info['type'] == 'namespace' && $info['namespace'] == $namespace) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param  string $namespace
     * @return null|array
     * @throws Exception\InvalidArgumentException
     */
    protected function getUsesNoScan($namespace)
    {
        $namespaces = [];
        foreach ($this->infos as $info) {
            if ($info['type'] == 'namespace') {
                $namespaces[] = $info['namespace'];
            }
        }

        if ($namespace === null) {
            $namespace = array_shift($namespaces);
        } elseif (! is_string($namespace)) {
            throw new Exception\InvalidArgumentException('Invalid namespace provided');
        } elseif (! in_array($namespace, $namespaces)) {
            return;
        }

        $uses = [];
        foreach ($this->infos as $info) {
            if ($info['type'] !== 'use') {
                continue;
            }
            foreach ($info['statements'] as $statement) {
                if ($info['namespace'] == $namespace) {
                    $uses[] = $statement;
                }
            }
        }

        return $uses;
    }
}

<MSG> Zend\Code - TokenArrayScanner has been converted to a proper finite state machine
NameInformation object returns namespace info

<DFF> @@ -30,7 +30,6 @@ class TokenArrayScanner implements Scanner
 
     /**
      * @param null|array $tokens
-     * @param null|array $options
      */
     public function __construct($tokens = null)
     {
@@ -51,43 +50,33 @@ class TokenArrayScanner implements Scanner
         $this->reset();
     }
 
-    public function getNamespaces($returnScannerClass = false)
+    public function getNamespaces()
     {
         $this->scan();
-        
-        if (!$returnScannerClass) {
-            $namespaces = array();
-            foreach ($this->infos as $info) {
-                if ($info['type'] == 'namespace') {
-                    $namespaces[] = $info['namespace'];
-                }
-            }
-            return $namespaces;
-        } else {
-            if ($returnScannerClass === true) {
-                $returnScannerClass = 'Zend\Code\Scanner\NamespaceScanner';
+
+        $namespaces = array();
+        foreach ($this->infos as $info) {
+            if ($info['type'] == 'namespace') {
+                $namespaces[] = $info['namespace'];
             }
-            $scannerClass = new $returnScannerClass;
-            // @todo
         }
+        return $namespaces;
     }
     
-    public function getUses($returnScannerClass = false)
+    public function getUses()
     {
         $this->scan();
-        
-        if (!$returnScannerClass) {
-            $namespaces = array();
-            foreach ($this->infos as $info) {
-                if ($info['type'] == 'uses') {
-                    $namespaces[] = $info['uses'];
-                }
+
+        $namespaces = array();
+        foreach ($this->infos as $info) {
+            if ($info['type'] == 'uses') {
+                $namespaces[] = $info['uses'];
             }
-            return $namespaces;
         }
+        return $namespaces;
     }
     
-    public function getIncludes($returnScannerClass = false)
+    public function getIncludes()
     {
         $this->scan();
         // @todo Implement getIncludes() in TokenArrayScanner
@@ -204,312 +193,308 @@ class TokenArrayScanner implements Scanner
             throw new Exception\RuntimeException('No tokens were provided');
         }
 
-        $currentNamespace = null;
-        $currentDocCommentIndex = false;
+        /**
+         * Variables & Setup
+         */
+
+        static $MACROS = null;
+
+        $tokens          = &$this->tokens; // localize
+        $infos           = &$this->infos;  // localize
+        $tokenIndex      = null;
+        $token           = null;
+        $tokenType       = null;
+        $tokenContent    = null;
+        $tokenLine       = null;
+        $namespace       = null;
+        $docCommentIndex = false;
+        $infoIndex       = 0;
+
+        /**
+         * MACRO creation
+         */
+
+        if ($MACROS === null) {
+            $MACROS = array(
+                'TOKEN_ADVANCE' => function() use (&$tokens, &$tokenIndex, &$token, &$tokenType, &$tokenContent, &$tokenLine) {
+                    $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex+1;
+                    if (!isset($tokens[$tokenIndex])) {
+                        $token        = false;
+                        $tokenContent = false;
+                        $tokenType    = false;
+                        $tokenLine    = false;
+                        return;
+                    }
+                    $token = $tokens[$tokenIndex];
+                    if (is_string($token)) {
+                        $tokenType = null;
+                        $tokenContent = $token;
+                    } else {
+                        list($tokenType, $tokenContent, $tokenLine) = $token;
+                    }
+                    return $tokenIndex;
+                },
+                'TOKEN_LOGICAL_START_INDEX' => function() use (&$tokenIndex, &$docCommentIndex) {;
+                    return ($docCommentIndex === false) ? $tokenIndex : $docCommentIndex;
+                },
+                'DOC_COMMENT_START' => function() use (&$tokenIndex, &$docCommentIndex) {
+                    $docCommentIndex = $tokenIndex;
+                    return $docCommentIndex;
+                },
+                'DOC_COMMENT_VALIDATE' => function() use (&$tokenType, &$docCommentIndex) {
+                    static $validTrailingTokens = null;
+                    if ($validTrailingTokens === null) {
+                        $validTrailingTokens = array(T_WHITESPACE, T_FINAL, T_ABSTRACT, T_INTERFACE, T_CLASS, T_FUNCTION);
+                    }
+                    if ($docCommentIndex !== false && !in_array($tokenType, $validTrailingTokens)) {
+                        $docCommentIndex = false;
+                    }
+                    return $docCommentIndex;
+                },
+                'INFO_ADVANCE' => function() use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
+                    $infos[$infoIndex]['tokenEnd'] = $tokenIndex;
+                    $infos[$infoIndex]['lineEnd'] = $tokenLine;
+                    $infoIndex++;
+                    return $infoIndex;
+                }
+            );
+        }
+
+        /**
+         * START FINITE STATE MACHINE FOR SCANNING TOKENS
+         */
 
-        for ($tokenIndex = 0; $tokenIndex < count($this->tokens); $tokenIndex++) {
-            $token = $this->tokens[$tokenIndex];
+        // Initialize token
+        $MACROS['TOKEN_ADVANCE']();
 
-            // track docblocks through whitespace tokens (they might belong to something)
-            if ($currentDocCommentIndex !== false && is_array($token) && $token[0] != T_WHITESPACE) {
-                $currentDocCommentIndex = false;
+        TOKEN_SCANNER_TOP:
+
+            if ($token === false) {
+                goto TOKEN_SCANNER_END;
             }
 
-            // tokens with some value are arrays (will have a token identifier, & line num)
-            $fastForward = 0;
-            switch ($token[0]) {
+            // Validate current doc comment index
+            $MACROS['DOC_COMMENT_VALIDATE']();
+
+            switch ($tokenType) {
+
                 case T_DOC_COMMENT:
-                    $currentDocCommentIndex = $tokenIndex;
-                    break;
+
+                    $MACROS['DOC_COMMENT_START']();
+                    goto TOKEN_SCANNER_CONTINUE;
 
                 case T_NAMESPACE:
-                    $currentNamespace = $this->scanNamespace($tokenIndex, $fastForward);
-                    break;
 
-                case T_USE:
-                    // process uses
-                    $this->scanUse($tokenIndex, $fastForward);
-                    break;
+                    $infos[$infoIndex] = array(
+                        'type'       => 'namespace',
+                        'tokenStart' => $MACROS['TOKEN_LOGICAL_START_INDEX'](),
+                        'tokenEnd'   => null,
+                        'lineStart'  => $token[2],
+                        'lineEnd'    => null,
+                        'namespace'  => null,
+                    );
 
-                case T_INCLUDE:
-                case T_INCLUDE_ONCE:
-                case T_REQUIRE:
-                case T_REQUIRE_ONCE:
-                    // process include
-                    $this->scanInclude($tokenIndex, $fastForward);
-                    break;
+                    // start processing with next token
+                    $MACROS['TOKEN_ADVANCE']();
 
-                case T_FINAL:
-                case T_ABSTRACT:
-                case T_CLASS:
-                case T_INTERFACE:
-                    $this->scanClass(($currentDocCommentIndex) ?: $tokenIndex, $fastForward, $currentNamespace);
-                    break;
+                    TOKEN_SCANNER_NAMESPACE_TOP:
 
-                case T_FUNCTION:
-                    $this->scanFunction(($currentDocCommentIndex) ?: $tokenIndex, $fastForward, $currentNamespace);
-                    break;
-            }
-            if ($fastForward) {
-                $tokenIndex += $fastForward - 1;
-            }
-        }
+                        if ($tokenType === null && $tokenContent === ';') {
+                            goto TOKEN_SCANNER_NAMESPACE_END;
+                        }
 
-        $this->isScanned = true;
-    }
+                        if ($tokenType === T_WHITESPACE) {
+                            goto TOKEN_SCANNER_NAMESPACE_CONTINUE;
+                        }
+                        
+                        if ($tokenType === T_NS_SEPARATOR || $tokenType === T_STRING) {
+                            $infos[$infoIndex]['namespace'] .= $tokenContent;
+                            $namespace = $infos[$infoIndex]['namespace'];
+                        }
 
-    protected function scanNamespace($tokenIndex, &$fastForward)
-    {
-        $info = array(
-            'type'       => 'namespace',
-            'tokenStart' => $tokenIndex,
-            'tokenEnd'   => null,
-            'lineStart'  => $this->tokens[$tokenIndex][2],
-            'lineEnd'    => null,
-            'namespace'  => null,
-        );
+                    TOKEN_SCANNER_NAMESPACE_CONTINUE:
 
-        // move past current T_NAMESPACE & following T_WHITESPACE
-        $tokenIndex++;
-        $fastForward++;
+                        $MACROS['TOKEN_ADVANCE']();
+                        goto TOKEN_SCANNER_NAMESPACE_TOP;
 
-        while (true) {
-            $tokenIndex++;
-            $fastForward++;
-            $token = $this->tokens[$tokenIndex];
+                    TOKEN_SCANNER_NAMESPACE_END:
 
-            // BREAK ON:
-            if (is_string($token) && $token == ';') {
-                break;
-            }
+                        $MACROS['INFO_ADVANCE']();
+                        goto TOKEN_SCANNER_CONTINUE;
 
-            // ANALYZE
-            if (is_array($token)) {
-                $info['lineEnd'] = $token[2];
-                if ($token[0] == T_WHITESPACE) {
-                    continue;
-                }
-                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
-                    $info['namespace'] .= (is_string($token)) ? $token : $token[1];
-                }
-            }
-        }
+                case T_USE:
 
-        $info['tokenEnd'] = $tokenIndex;
-        $this->infos[]    = $info;
+                    $infos[$infoIndex] = array(
+                        'type'       => 'use',
+                        'tokenStart' => $MACROS['TOKEN_LOGICAL_START_INDEX'](),
+                        'tokenEnd'   => null,
+                        'lineStart'  => $tokens[$tokenIndex][2],
+                        'lineEnd'    => null,
+                        'statements' => array(0 => array('use' => null, 'as' => null)),
+                    );
 
-        return $info['namespace'];
-    }
+                    $useStatementIndex = 0;
+                    $useAsContext = false;
 
-    protected function scanUse($tokenIndex, &$fastForward)
-    {
-        $info = array(
-            'type'       => 'use',
-            'tokenStart' => $tokenIndex,
-            'tokenEnd'   => null,
-            'lineStart'  => $this->tokens[$tokenIndex][2],
-            'lineEnd'    => null,
-            'statements' => array(),
-        );
+                    // start processing with next token
+                    $MACROS['TOKEN_ADVANCE']();
 
-        // Static for performance purposes
-        static $statementTemplate = array(
-            'use'        => null,
-            'as'         => null
-        );
+                    TOKEN_SCANNER_USE_TOP:
 
-        // skip current token T_USE and following T_WHITESPACE
-        $tokenIndex++;
-        $fastForward++;
+                        if ($tokenType === null) {
+                            if ($tokenContent === ';') {
+                                goto TOKEN_SCANNER_USE_END;
+                            } elseif ($tokenContent === ',') {
+                                $useAsContext = false;
+                                $useStatementIndex++;
+                                $infos[$infoIndex]['statements'][$useStatementIndex] = array('use' => null, 'as' => null);
+                            }
+                        }
 
-        $hasAs     = false;
-        $sCount    = 0;
-        $statement = $statementTemplate;
+                        // ANALYZE
+                        if ($tokenType !== null) {
 
-        while (true) {
-            $tokenIndex++;
-            $fastForward++;
-            $token = $this->tokens[$tokenIndex];
+                            if ($tokenType == T_AS) {
+                                $useAsContext = true;
+                                goto TOKEN_SCANNER_USE_CONTINUE;
+                            }
 
-            // BREAK ON:
-            if (is_string($token) && $token == ';') {
-                break;
-            }
+                            if ($tokenType == T_NS_SEPARATOR || $tokenType == T_STRING) {
+                                if ($useAsContext == false) {
+                                    $infos[$infoIndex]['statements'][$useStatementIndex]['use'] .= $tokenContent;
+                                } else {
+                                    $infos[$infoIndex]['statements'][$useStatementIndex]['as'] = $tokenContent;
+                                }
+                            }
 
-            // ANALYZE
-            if (is_array($token)) {
-                // store known line end
-                $info['lineEnd'] = $token[2];
+                        }
 
-                if ($token[0] == T_NS_SEPARATOR || $token[0] == T_STRING) {
-                    if ($hasAs == false) {
-                        $statement['use'] .= (is_string($token)) ? $token : $token[1];
-                    } else {
-                        $statement['as'] = $token[1]; // always a string
-                    }
-                }
-                if ($token[0] == T_AS) {
-                    $hasAs = true;
-                }
-            }
+                    TOKEN_SCANNER_USE_CONTINUE:
 
-            $tokenLookahead = $this->tokens[$tokenIndex+1];
+                        $MACROS['TOKEN_ADVANCE']();
+                        goto TOKEN_SCANNER_USE_TOP;
 
-            if (is_string($tokenLookahead) && ($tokenLookahead == ',' || $tokenLookahead == ';')) {
-                $info['statements'][$sCount] = $statement;
-                $sCount++;
-                $statement = $statementTemplate;
-                $hasAs = false;
-            }
+                    TOKEN_SCANNER_USE_END:
 
-        }
+                        $MACROS['INFO_ADVANCE']();
+                        goto TOKEN_SCANNER_CONTINUE;
 
-        $info['tokenEnd'] = $tokenIndex;
-        $this->infos[]    = $info;
-    }
+                case T_INCLUDE:
+                case T_INCLUDE_ONCE:
+                case T_REQUIRE:
+                case T_REQUIRE_ONCE:
 
-    protected function scanInclude($tokenIndex, &$fastForward)
-    {
-        // Static for performance purposes
-        static $types = array(
-            T_INCLUDE      => 'include',
-            T_INCLUDE_ONCE => 'include_once',
-            T_REQUIRE      => 'require',
-            T_REQUIRE_ONCE => 'require_once',
-        );
+                    // Static for performance
+                    static $includeTypes = array(
+                        T_INCLUDE      => 'include',
+                        T_INCLUDE_ONCE => 'include_once',
+                        T_REQUIRE      => 'require',
+                        T_REQUIRE_ONCE => 'require_once'
+                    );
 
-        $info = array(
-            'type'        => 'include',
-            'tokenStart'  => $tokenIndex,
-            'tokenEnd'    => null,
-            'lineStart'   => $this->tokens[$tokenIndex][2],
-            'lineEnd'     => null,
-            'includeType' => $types[$this->tokens[$tokenIndex][0]],
-            'path'        => '',
-        );
+                    $infos[$infoIndex] = array(
+                        'type'        => 'include',
+                        'tokenStart'  => $MACROS['TOKEN_LOGICAL_START_INDEX'](),
+                        'tokenEnd'    => null,
+                        'lineStart'   => $tokens[$tokenIndex][2],
+                        'lineEnd'     => null,
+                        'includeType' => $includeTypes[$tokens[$tokenIndex][0]],
+                        'path'        => '',
+                    );
 
-        $index = $tokenIndex;
+                    // start processing with next token
+                    $MACROS['TOKEN_ADVANCE']();
 
-        // move past include & the required whitespace
-        $fastForward++;
-        $index++;
+                    TOKEN_SCANNER_INCLUDE_TOP:
 
-        while (true) {
-            $fastForward++;
-            $tokenIndex++;
-            $token = $this->tokens[$index++];
+                        if ($tokenType === null && $token === ';') {
+                            goto TOKEN_SCANNER_INCLUDE_END;
+                        }
 
-            // BREAK ON
-            if (is_string($token) && $token == ';') {
-                break;
-            }
+                        $infos[$infoIndex]['path'] .= $tokenContent;
 
-            // ANALYZE
-            if (is_array($token)) {
-                $info['lineEnd'] = $token[2];
-            }
+                    TOKEN_SCANNER_INCLUDE_CONTINUE:
 
-            $info['path'] .= (is_string($token)) ? $token : $token[1];
-        }
+                        $MACROS['TOKEN_ADVANCE']();
+                        goto TOKEN_SCANNER_INCLUDE_TOP;
 
-        $info['tokenEnd'] = $tokenIndex;
-        $this->infos[] = $info;
-    }
+                    TOKEN_SCANNER_INCLUDE_END:
 
-    protected function scanClass($tokenIndex, &$fastForward, $namespace = null)
-    {
-        $info = array(
-            'type'        => 'class',
-            'tokenStart'  => $tokenIndex,
-            'tokenEnd'    => null,
-            'lineStart'   => $this->tokens[$tokenIndex][2],
-            'lineEnd'     => null,
-            'namespace'   => $namespace,
-            'name'        => null,
-            'shortName'   => null,
-        );
-        
-        $braceCount = 0;
-        while (true) {
-            $token = $this->tokens[$tokenIndex];
+                        $MACROS['INFO_ADVANCE']();
+                        goto TOKEN_SCANNER_CONTINUE;
 
-            // BREAK ON
-            if (is_string($token) && $token == '}' && $braceCount == 1) {
-                break;
-            }
+                case T_FUNCTION:
+                case T_FINAL:
+                case T_ABSTRACT:
+                case T_CLASS:
+                case T_INTERFACE:
 
-            // ANALYZE
-            if ($token[0] === T_CLASS) {
-                $info['shortName'] = $this->tokens[$tokenIndex+2][1];
-                $info['name'] = (($namespace) ? $namespace . '\\' : '') . $info['shortName'];
-            }
+                    $infos[$infoIndex] = array(
+                        'type'        => ($tokenType === T_FUNCTION) ? 'function' : 'class',
+                        'tokenStart'  => $MACROS['TOKEN_LOGICAL_START_INDEX'](),
+                        'tokenEnd'    => null,
+                        'lineStart'   => $tokens[$tokenIndex][2],
+                        'lineEnd'     => null,
+                        'namespace'   => $namespace,
+                        'name'        => null,
+                        'shortName'   => null,
+                    );
 
-            if (is_string($token)) {
-                if ($token == '{') {
-                    $braceCount++;
-                }
-                if ($token == '}') {
-                    $braceCount--;
-                }
-            }
+                    $classBraceCount = 0;
 
-            if (is_array($token)) {
-                $info['lineEnd'] = $token[2];
-            }
+                    // start processing with current token
 
-            // MOVE FORWARD
-            $fastForward++;
-            $tokenIndex++;
-        }
+                    TOKEN_SCANNER_CLASS_TOP:
 
-        $info['tokenEnd'] = $tokenIndex;
-        $this->infos[]    = $info;
-    }
+                        if ($tokenType === null && $tokenContent == '}' && $classBraceCount == 1) {
+                            goto TOKEN_SCANNER_CLASS_END;
+                        }
 
-    protected function scanFunction($tokenIndex, &$fastForward, $namespace = null)
-    {
-        $info = array(
-            'type'        => 'function',
-            'tokenStart'  => $tokenIndex,
-            'tokenEnd'    => null,
-            'lineStart'   => $this->tokens[$tokenIndex][2],
-            'lineEnd'     => null,
-            'name'        => $namespace . '\\' . $this->tokens[$tokenIndex+2][1],
-            'shortName'   => $this->tokens[$tokenIndex+2][1],
-            'namespace'   => $namespace,
-        );
+                        // process the name
+                        if ($tokenType === T_CLASS || $tokenType === T_INTERFACE ||
+                            ($tokenType === T_FUNCTION && $infos[$infoIndex]['type'] === 'function')
+                        ) {
+                            $infos[$infoIndex]['shortName'] = $tokens[$tokenIndex+2][1];
+                            $infos[$infoIndex]['name'] = (($namespace) ? $namespace . '\\' : '') . $infos[$infoIndex]['shortName'];
+                        }
 
-        $braceCount = 0;
-        while (true) {
-            $fastForward++;
-            $tokenIndex++;
-            $token = $this->tokens[$tokenIndex];
 
-            // BREAK ON
-            if ($braceCount === false) {
-                break;
-            }
 
-            // ANALYZE
-            if (is_string($token)) {
-                if ($token == '{') {
-                    $context = null;
-                    $braceCount++;
-                }
-                if ($token == '}') {
-                    $braceCount = ($braceCount == 1) ? false : ($braceCount - 1);
-                }
-            }
-            if (is_array($token)) {
-                $info['lineEnd'] = $token[2];
+                        if ($tokenType === null) {
+                            if ($tokenContent == '{') {
+                                $classBraceCount++;
+                            }
+                            if ($tokenContent == '}') {
+                                $classBraceCount--;
+                            }
+                        }
+
+                    TOKEN_SCANNER_CLASS_CONTINUE:
+
+                        $MACROS['TOKEN_ADVANCE']();
+                        goto TOKEN_SCANNER_CLASS_TOP;
+
+                    TOKEN_SCANNER_CLASS_END:
+
+                        $MACROS['INFO_ADVANCE']();
+                        goto TOKEN_SCANNER_CONTINUE;
+
             }
-        }
 
-        $info['tokenEnd'] = $tokenIndex;
-        $this->infos[]    = $info;
+        TOKEN_SCANNER_CONTINUE:
+
+            $MACROS['TOKEN_ADVANCE']();
+            goto TOKEN_SCANNER_TOP;
+
+        TOKEN_SCANNER_END:
+
+        /**
+         * END FINITE STATE MACHINE FOR SCANNING TOKENS
+         */
+
+        $this->isScanned = true;
     }
 
     // @todo hasNamespace(), getNamespace()
 
-
 }

Zend\Code - TokenArrayScanner has been converted to a proper finite state machine NameInformation object returns namespace info