]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/PHP/Token/Stream.php
Release 6.2.0
[Github/sugarcrm.git] / tests / PHPUnit / PHP / Token / Stream.php
1 <?php
2 /**
3  * php-token-stream
4  *
5  * Copyright (c) 2009-2010, Sebastian Bergmann <sb@sebastian-bergmann.de>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *
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
18  *     distribution.
19  *
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.
23  *
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.
36  *
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
42  */
43
44 require_once 'PHP/Token.php';
45
46 /**
47  * A stream of PHP tokens.
48  *
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
55  */
56 class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
57 {
58     /**
59      * @var array
60      */
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'
90     );
91
92     /**
93      * @var array
94      */
95     protected $tokens = array();
96
97     /**
98      * @var integer
99      */
100     protected $position = 0;
101
102     /**
103      * @var array
104      */
105     protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
106
107     /**
108      * @var array
109      */
110     protected $classes;
111
112     /**
113      * @var array
114      */
115     protected $functions;
116
117     /**
118      * Constructor.
119      *
120      * @param string $sourceCode
121      */
122     public function __construct($sourceCode)
123     {
124         if (is_file($sourceCode)) {
125             $sourceCode = file_get_contents($sourceCode);
126         }
127
128         $this->scan($sourceCode);
129     }
130
131     /**
132      * Scans the source for sequences of characters and converts them into a
133      * stream of tokens.
134      *
135      * @param string $sourceCode
136      */
137     protected function scan($sourceCode)
138     {
139         $line      = 1;
140         $tokens    = token_get_all($sourceCode);
141         $numTokens = count($tokens);
142
143         for ($i = 0; $i < $numTokens; ++$i) {
144             $token = $tokens[$i];
145             unset($tokens[$i]);
146
147             if (is_array($token)) {
148                 $text       = $token[1];
149                 $tokenClass = 'PHP_Token_' . substr(token_name($token[0]), 2);
150             } else {
151                 $text       = $token;
152                 $tokenClass = self::$customTokens[$token];
153             }
154
155             $this->tokens[] = new $tokenClass($text, $line, $this, $i);
156             $lines          = substr_count($text, "\n");
157             $line          += $lines;
158
159             if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
160                 break;
161             }
162
163             else if ($tokenClass == 'PHP_Token_COMMENT' ||
164                 $tokenClass == 'PHP_Token_DOC_COMMENT') {
165                 $this->linesOfCode['cloc'] += $lines + 1;
166             }
167         }
168
169         $this->linesOfCode['loc']   = substr_count($sourceCode, "\n");
170         $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
171                                       $this->linesOfCode['cloc'];
172     }
173
174     /**
175      * @return string
176      */
177     public function __toString()
178     {
179         $buffer = '';
180
181         foreach ($this as $token) {
182             $buffer .= $token;
183         }
184
185         return $buffer;
186     }
187
188     /**
189      * @return integer
190      */
191     public function count()
192     {
193         return count($this->tokens);
194     }
195
196     /**
197      * @return PHP_Token[]
198      */
199     public function tokens()
200     {
201         return $this->tokens;
202     }
203
204     /**
205      * @return array
206      */
207     public function getClasses()
208     {
209         if ($this->classes !== NULL) {
210             return $this->classes;
211         }
212
213         $this->parseClassesFunctions();
214
215         return $this->classes;
216     }
217
218     /**
219      * @return array
220      */
221     public function getFunctions()
222     {
223         if ($this->functions !== NULL) {
224             return $this->functions;
225         }
226
227         $this->parseClassesFunctions();
228
229         return $this->functions;
230     }
231
232     protected function parseClassesFunctions()
233     {
234         $this->classes   = array();
235         $this->functions = array();
236         $class           = FALSE;
237         $classEndLine    = FALSE;
238
239         foreach ($this->tokens as $token) {
240             switch (get_class($token)) {
241                 case 'PHP_Token_CLASS': {
242                     $class        = $token->getName();
243                     $classEndLine = $token->getEndLine();
244
245                     $this->classes[$class] = array(
246                       'methods'   => array(),
247                       'docblock'  => $token->getDocblock(),
248                       'startLine' => $token->getLine(),
249                       'endLine'   => $classEndLine
250                     );
251                 }
252                 break;
253
254                 case 'PHP_Token_FUNCTION': {
255                     $name = $token->getName();
256                     $tmp  = array(
257                       'docblock'  => $token->getDocblock(),
258                       'signature' => $token->getSignature(),
259                       'startLine' => $token->getLine(),
260                       'endLine'   => $token->getEndLine(),
261                       'ccn'       => $token->getCCN()
262                     );
263
264                     if ($class === FALSE) {
265                         $this->functions[$name] = $tmp;
266                     } else {
267                         $this->classes[$class]['methods'][$name] = $tmp;
268                     }
269                 }
270                 break;
271
272                 case 'PHP_Token_CLOSE_CURLY': {
273                     if ($classEndLine !== FALSE &&
274                         $classEndLine == $token->getLine()) {
275                         $class        = FALSE;
276                         $classEndLine = FALSE;
277                     }
278                 }
279                 break;
280             }
281         }
282     }
283
284     /**
285      * @return array
286      */
287     public function getLinesOfCode()
288     {
289         return $this->linesOfCode;
290     }
291
292     /**
293      */
294     public function rewind()
295     {
296         $this->position = 0;
297     }
298
299     /**
300      * @return boolean
301      */
302     public function valid()
303     {
304         return isset($this->tokens[$this->position]);
305     }
306
307     /**
308      * @return integer
309      */
310     public function key()
311     {
312         return $this->position;
313     }
314
315     /**
316      * @return PHP_Token
317      */
318     public function current()
319     {
320         return $this->tokens[$this->position];
321     }
322
323     /**
324      */
325     public function next()
326     {
327         $this->position++;
328     }
329
330     /**
331      * @param mixed $offset
332      */
333     public function offsetExists($offset)
334     {
335         return isset($this->tokens[$offset]);
336     }
337
338     /**
339      * @param  mixed $offset
340      * @return mixed
341      */
342     public function offsetGet($offset)
343     {
344         return $this->tokens[$offset];
345     }
346
347     /**
348      * @param mixed $offset
349      * @param mixed $value
350      */
351     public function offsetSet($offset, $value)
352     {
353         $this->tokens[$offset] = $value;
354     }
355
356     /**
357      * @param mixed $offset
358      */
359     public function offsetUnset($offset)
360     {
361         unset($this->tokens[$offset]);
362     }
363
364     /**
365      * Seek to an absolute position.
366      *
367      * @param  integer $position
368      * @throws OutOfBoundsException
369      */
370     public function seek($position)
371     {
372         $this->position = $position;
373
374         if (!$this->valid()) {
375             throw new OutOfBoundsException('Invalid seek position');
376         }
377     }
378 }