1<?php 2 3namespace Gettext\Utils; 4 5use Gettext\Extractors\PhpCode; 6 7class PhpFunctionsScanner extends FunctionsScanner 8{ 9 /** 10 * PHP tokens of the code to be parsed. 11 * 12 * @var array 13 */ 14 protected $tokens; 15 16 /** 17 * If not false, comments will be extracted. 18 * 19 * @var string|false|array 20 */ 21 protected $extractComments = false; 22 23 /** 24 * Enable extracting comments that start with a tag (if $tag is empty all the comments will be extracted). 25 * 26 * @param mixed $tag 27 */ 28 public function enableCommentsExtraction($tag = '') 29 { 30 $this->extractComments = $tag; 31 } 32 33 /** 34 * Disable comments extraction. 35 */ 36 public function disableCommentsExtraction() 37 { 38 $this->extractComments = false; 39 } 40 41 /** 42 * Constructor. 43 * 44 * @param string $code The php code to scan 45 */ 46 public function __construct($code) 47 { 48 $this->tokens = array_values( 49 array_filter( 50 token_get_all($code), 51 function ($token) { 52 return !is_array($token) || $token[0] !== T_WHITESPACE; 53 } 54 ) 55 ); 56 } 57 58 /** 59 * {@inheritdoc} 60 */ 61 public function getFunctions(array $constants = []) 62 { 63 $count = count($this->tokens); 64 /* @var ParsedFunction[] $bufferFunctions */ 65 $bufferFunctions = []; 66 /* @var ParsedComment[] $bufferComments */ 67 $bufferComments = []; 68 /* @var array $functions */ 69 $functions = []; 70 71 for ($k = 0; $k < $count; ++$k) { 72 $value = $this->tokens[$k]; 73 74 if (is_string($value)) { 75 if (isset($bufferFunctions[0])) { 76 switch ($value) { 77 case ',': 78 $bufferFunctions[0]->nextArgument(); 79 break; 80 case ')': 81 $functions[] = array_shift($bufferFunctions)->close(); 82 break; 83 case '.': 84 break; 85 default: 86 $bufferFunctions[0]->stopArgument(); 87 break; 88 } 89 } 90 continue; 91 } 92 93 switch ($value[0]) { 94 case T_CONSTANT_ENCAPSED_STRING: 95 //add an argument to the current function 96 if (isset($bufferFunctions[0])) { 97 $bufferFunctions[0]->addArgumentChunk(PhpCode::convertString($value[1])); 98 } 99 break; 100 101 case T_STRING: 102 if (isset($bufferFunctions[0])) { 103 if (isset($constants[$value[1]])) { 104 $bufferFunctions[0]->addArgumentChunk($constants[$value[1]]); 105 break; 106 } 107 108 if (strtolower($value[1]) === 'null') { 109 $bufferFunctions[0]->addArgumentChunk(null); 110 break; 111 } 112 113 $bufferFunctions[0]->stopArgument(); 114 } 115 116 //new function found 117 for ($j = $k + 1; $j < $count; ++$j) { 118 $nextToken = $this->tokens[$j]; 119 120 if (is_array($nextToken) && $nextToken[0] === T_COMMENT) { 121 continue; 122 } 123 124 if ($nextToken === '(') { 125 $newFunction = new ParsedFunction($value[1], $value[2]); 126 127 // add comment that was on the line before. 128 if (isset($bufferComments[0])) { 129 $comment = $bufferComments[0]; 130 131 if ($comment->isRelatedWith($newFunction)) { 132 $newFunction->addComment($comment->getComment()); 133 } 134 } 135 136 array_unshift($bufferFunctions, $newFunction); 137 $k = $j; 138 } 139 break; 140 } 141 break; 142 143 case T_COMMENT: 144 $comment = $this->parsePhpComment($value[1], $value[2]); 145 146 if ($comment) { 147 array_unshift($bufferComments, $comment); 148 149 // The comment is inside the function call. 150 if (isset($bufferFunctions[0])) { 151 $bufferFunctions[0]->addComment($comment->getComment()); 152 } 153 } 154 break; 155 156 default: 157 if (isset($bufferFunctions[0])) { 158 $bufferFunctions[0]->stopArgument(); 159 } 160 break; 161 } 162 } 163 164 return $functions; 165 } 166 167 /** 168 * Extract the actual text from a PHP comment. 169 * 170 * If set, only returns comments that match the prefix(es). 171 * 172 * @param string $value The PHP comment. 173 * @param int $line Line number. 174 * 175 * @return null|ParsedComment Comment or null if comment extraction is disabled or if there is a prefix mismatch. 176 */ 177 protected function parsePhpComment($value, $line) 178 { 179 if ($this->extractComments === false) { 180 return null; 181 } 182 183 //this returns a comment or null 184 $comment = ParsedComment::create($value, $line); 185 186 $prefixes = array_filter((array) $this->extractComments); 187 188 if ($comment && $comment->checkPrefixes($prefixes)) { 189 return $comment; 190 } 191 192 return null; 193 } 194} 195