5 * Copyright (c) 2009-2010, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
37 * @package PHP_TokenStream
38 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
39 * @copyright 2009-2010 Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41 * @since File available since Release 1.0.0
44 require_once 'PHP/Token.php';
47 * A stream of PHP tokens.
49 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
50 * @copyright 2009-2010 Sebastian Bergmann <sb@sebastian-bergmann.de>
51 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
52 * @version Release: 1.0.1
53 * @link http://github.com/sebastianbergmann/php-token-stream/tree
54 * @since Class available since Release 1.0.0
56 class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
61 protected static $customTokens = array(
62 '(' => 'PHP_Token_OPEN_BRACKET',
63 ')' => 'PHP_Token_CLOSE_BRACKET',
64 '[' => 'PHP_Token_OPEN_SQUARE',
65 ']' => 'PHP_Token_CLOSE_SQUARE',
66 '{' => 'PHP_Token_OPEN_CURLY',
67 '}' => 'PHP_Token_CLOSE_CURLY',
68 ';' => 'PHP_Token_SEMICOLON',
69 '.' => 'PHP_Token_DOT',
70 ',' => 'PHP_Token_COMMA',
71 '=' => 'PHP_Token_EQUAL',
72 '<' => 'PHP_Token_LT',
73 '>' => 'PHP_Token_GT',
74 '+' => 'PHP_Token_PLUS',
75 '-' => 'PHP_Token_MINUS',
76 '*' => 'PHP_Token_MULT',
77 '/' => 'PHP_Token_DIV',
78 '?' => 'PHP_Token_QUESTION_MARK',
79 '!' => 'PHP_Token_EXCLAMATION_MARK',
80 ':' => 'PHP_Token_COLON',
81 '"' => 'PHP_Token_DOUBLE_QUOTES',
82 '@' => 'PHP_Token_AT',
83 '&' => 'PHP_Token_AMPERSAND',
84 '%' => 'PHP_Token_PERCENT',
85 '|' => 'PHP_Token_PIPE',
86 '$' => 'PHP_Token_DOLLAR',
87 '^' => 'PHP_Token_CARET',
88 '~' => 'PHP_Token_TILDE',
89 '`' => 'PHP_Token_BACKTICK'
95 protected $tokens = array();
100 protected $position = 0;
105 protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
115 protected $functions;
120 * @param string $sourceCode
122 public function __construct($sourceCode)
124 if (is_file($sourceCode)) {
125 $sourceCode = file_get_contents($sourceCode);
128 $this->scan($sourceCode);
132 * Scans the source for sequences of characters and converts them into a
135 * @param string $sourceCode
137 protected function scan($sourceCode)
140 $tokens = token_get_all($sourceCode);
141 $numTokens = count($tokens);
143 for ($i = 0; $i < $numTokens; ++$i) {
144 $token = $tokens[$i];
147 if (is_array($token)) {
149 $tokenClass = 'PHP_Token_' . substr(token_name($token[0]), 2);
152 $tokenClass = self::$customTokens[$token];
155 $this->tokens[] = new $tokenClass($text, $line, $this, $i);
156 $lines = substr_count($text, "\n");
159 if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
163 else if ($tokenClass == 'PHP_Token_COMMENT' ||
164 $tokenClass == 'PHP_Token_DOC_COMMENT') {
165 $this->linesOfCode['cloc'] += $lines + 1;
169 $this->linesOfCode['loc'] = substr_count($sourceCode, "\n");
170 $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
171 $this->linesOfCode['cloc'];
177 public function __toString()
181 foreach ($this as $token) {
191 public function count()
193 return count($this->tokens);
197 * @return PHP_Token[]
199 public function tokens()
201 return $this->tokens;
207 public function getClasses()
209 if ($this->classes !== NULL) {
210 return $this->classes;
213 $this->parseClassesFunctions();
215 return $this->classes;
221 public function getFunctions()
223 if ($this->functions !== NULL) {
224 return $this->functions;
227 $this->parseClassesFunctions();
229 return $this->functions;
232 protected function parseClassesFunctions()
234 $this->classes = array();
235 $this->functions = array();
237 $classEndLine = FALSE;
239 foreach ($this->tokens as $token) {
240 switch (get_class($token)) {
241 case 'PHP_Token_CLASS': {
242 $class = $token->getName();
243 $classEndLine = $token->getEndLine();
245 $this->classes[$class] = array(
246 'methods' => array(),
247 'docblock' => $token->getDocblock(),
248 'startLine' => $token->getLine(),
249 'endLine' => $classEndLine
254 case 'PHP_Token_FUNCTION': {
255 $name = $token->getName();
257 'docblock' => $token->getDocblock(),
258 'signature' => $token->getSignature(),
259 'startLine' => $token->getLine(),
260 'endLine' => $token->getEndLine(),
261 'ccn' => $token->getCCN()
264 if ($class === FALSE) {
265 $this->functions[$name] = $tmp;
267 $this->classes[$class]['methods'][$name] = $tmp;
272 case 'PHP_Token_CLOSE_CURLY': {
273 if ($classEndLine !== FALSE &&
274 $classEndLine == $token->getLine()) {
276 $classEndLine = FALSE;
287 public function getLinesOfCode()
289 return $this->linesOfCode;
294 public function rewind()
302 public function valid()
304 return isset($this->tokens[$this->position]);
310 public function key()
312 return $this->position;
318 public function current()
320 return $this->tokens[$this->position];
325 public function next()
331 * @param mixed $offset
333 public function offsetExists($offset)
335 return isset($this->tokens[$offset]);
339 * @param mixed $offset
342 public function offsetGet($offset)
344 return $this->tokens[$offset];
348 * @param mixed $offset
349 * @param mixed $value
351 public function offsetSet($offset, $value)
353 $this->tokens[$offset] = $value;
357 * @param mixed $offset
359 public function offsetUnset($offset)
361 unset($this->tokens[$offset]);
365 * Seek to an absolute position.
367 * @param integer $position
368 * @throws OutOfBoundsException
370 public function seek($position)
372 $this->position = $position;
374 if (!$this->valid()) {
375 throw new OutOfBoundsException('Invalid seek position');