/** * Locate every function or static method call. * * @param array $tokens * @param int $functionLevel Internal constant used for parsing. */ private function locateCalls(array $tokens, $functionLevel = 0) { //Multiple "(" and ")" statements nested. $parenthesisLevel = 0; //Skip all tokens until next function $skipTokens = false; //Were function was found $functionTID = $line = 0; //Parsed arguments and their first token id $arguments = []; $argumentsTID = false; //Tokens used to re-enable token detection $stopTokens = [T_STRING, T_WHITESPACE, T_DOUBLE_COLON, T_NS_SEPARATOR]; foreach ($tokens as $TID => $token) { $tokenType = $token[self::TOKEN_TYPE]; //We are not indexing function declarations or functions called from $objects. if ($tokenType == T_FUNCTION || $tokenType == T_OBJECT_OPERATOR || $tokenType == T_NEW) { //Not a call, function declaration, or object method if (!$argumentsTID) { $skipTokens = true; continue; } } elseif ($skipTokens) { if (!in_array($tokenType, $stopTokens)) { //Returning to search $skipTokens = false; } continue; } //We are inside function, and there is "(", indexing arguments. if ($functionTID && $tokenType == '(') { if (!$argumentsTID) { $argumentsTID = $TID; } $parenthesisLevel++; if ($parenthesisLevel != 1) { //Not arguments beginning, but arguments part $arguments[$TID] = $token; } continue; } //We are inside function arguments and ")" met. if ($functionTID && $tokenType == ')') { $parenthesisLevel--; if ($parenthesisLevel == -1) { $functionTID = false; $parenthesisLevel = 0; continue; } //Function fully indexed, we can process it now. if (!$parenthesisLevel) { $source = ''; for ($tokenID = $functionTID; $tokenID <= $TID; $tokenID++) { //Collecting function usage source $source .= $tokens[$tokenID][self::TOKEN_CODE]; } //Will be fixed in future $class = $this->fetchClass($tokens, $functionTID, $argumentsTID); $call = null; if ($class != 'self' && $class != 'static') { $call = new ReflectionCall($this->filename, $line, $class, $this->fetchName($tokens, $functionTID, $argumentsTID), ReflectionArgument::fetchArguments($arguments), $source, $functionTID, $TID, $functionLevel); } //Nested functions can be function in usage arguments. $this->locateCalls($arguments, $functionLevel + 1); !empty($call) && ($this->calls[] = $call); //Closing search $arguments = []; $argumentsTID = $functionTID = false; } else { //Not arguments beginning, but arguments part $arguments[$TID] = $token; } continue; } //Still inside arguments. if ($functionTID && $parenthesisLevel) { $arguments[$TID] = $token; continue; } //Nothing valuable to remember, will be parsed later. if ($functionTID && in_array($tokenType, $stopTokens)) { continue; } //Seems like we found function/method call if ($tokenType == T_STRING || $tokenType == T_STATIC || $tokenType == T_NS_SEPARATOR) { $functionTID = $TID; $line = $token[self::TOKEN_LINE]; $parenthesisLevel = 0; $argumentsTID = false; continue; } //Returning to search $functionTID = false; $arguments = []; } }
/** * Registering invocation. * * @param int $invocationID * @param int $argumentsID * @param int $endID * @param array $arguments * @param int $invocationLevel */ private function registerInvocation($invocationID, $argumentsID, $endID, array $arguments, $invocationLevel) { //Nested invocations $this->locateInvocations($arguments, $invocationLevel + 1); list($class, $operator, $name) = $this->fetchContext($invocationID, $argumentsID); if (!empty($operator) && empty($class)) { //Non detectable return; } $this->invocations[] = new ReflectionInvocation($this->filename, $this->lineNumber($invocationID), $class, $operator, $name, ReflectionArgument::locateArguments($arguments), $this->getSource($invocationID, $endID), $invocationLevel); }