getDocComment()) { return false; } $instance = new DocBlockReflection($this); return $instance; } /** * @param AnnotationManager $annotationManager * @return AnnotationScanner */ public function getAnnotations(AnnotationManager $annotationManager) { if (($docComment = $this->getDocComment()) == '') { return false; } if ($this->annotations) { return $this->annotations; } $cachingFileScanner = $this->createFileScanner($this->getFileName()); $nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringClass()->getName()); if (!$nameInformation) { return false; } $this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation); return $this->annotations; } /** * Get start line (position) of method * * @param bool $includeDocComment * @return int */ public function getStartLine($includeDocComment = false) { if ($includeDocComment) { if ($this->getDocComment() != '') { return $this->getDocBlock()->getStartLine(); } } return parent::getStartLine(); } /** * Get reflection of declaring class * * @return ClassReflection */ public function getDeclaringClass() { $phpReflection = parent::getDeclaringClass(); $zendReflection = new ClassReflection($phpReflection->getName()); unset($phpReflection); return $zendReflection; } /** * Get method prototype * * @return array */ public function getPrototype($format = MethodReflection::PROTOTYPE_AS_ARRAY) { $returnType = 'mixed'; $docBlock = $this->getDocBlock(); if ($docBlock) { $return = $docBlock->getTag('return'); $returnTypes = $return->getTypes(); $returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0]; } $declaringClass = $this->getDeclaringClass(); $prototype = array( 'namespace' => $declaringClass->getNamespaceName(), 'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1), 'name' => $this->getName(), 'visibility' => ($this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected')), 'return' => $returnType, 'arguments' => array(), ); $parameters = $this->getParameters(); foreach ($parameters as $parameter) { $prototype['arguments'][$parameter->getName()] = array( 'type' => $parameter->getType(), 'required' => !$parameter->isOptional(), 'by_ref' => $parameter->isPassedByReference(), 'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null, ); } if ($format == MethodReflection::PROTOTYPE_AS_STRING) { $line = $prototype['visibility'] . ' ' . $prototype['return'] . ' ' . $prototype['name'] . '('; $args = array(); foreach ($prototype['arguments'] as $name => $argument) { $argsLine = ($argument['type'] ? $argument['type'] . ' ' : '') . ($argument['by_ref'] ? '&' : '') . '$' . $name; if (!$argument['required']) { $argsLine .= ' = ' . var_export($argument['default'], true); } $args[] = $argsLine; } $line .= implode(', ', $args); $line .= ')'; return $line; } return $prototype; } /** * Get all method parameter reflection objects * * @return ParameterReflection[] */ public function getParameters() { $phpReflections = parent::getParameters(); $zendReflections = array(); while ($phpReflections && ($phpReflection = array_shift($phpReflections))) { $instance = new ParameterReflection( array($this->getDeclaringClass()->getName(), $this->getName()), $phpReflection->getName() ); $zendReflections[] = $instance; unset($phpReflection); } unset($phpReflections); return $zendReflections; } /** * Get method contents * * @param bool $includeDocBlock * @return string */ public function getContents($includeDocBlock = true) { $docComment = $this->getDocComment(); $content = ($includeDocBlock && !empty($docComment)) ? $docComment . "\n" : ''; $content .= $this->extractMethodContents(); return $content; } /** * Get method body * * @return string */ public function getBody() { return $this->extractMethodContents(true); } /** * Tokenize method string and return concatenated body * * @param bool $bodyOnly * @return string */ protected function extractMethodContents($bodyOnly = false) { $fileName = $this->getFileName(); if ((class_exists($this->class) && false === $fileName) || ! file_exists($fileName)) { return ''; } $lines = array_slice( file($fileName, FILE_IGNORE_NEW_LINES), $this->getStartLine() - 1, ($this->getEndLine() - ($this->getStartLine() - 1)), true ); $functionLine = implode("\n", $lines); $tokens = token_get_all(" $token) { $tokenType = (is_array($token)) ? token_name($token[0]) : $token; $tokenValue = (is_array($token)) ? $token[1] : $token; switch ($tokenType) { case "T_FINAL": case "T_ABSTRACT": case "T_PUBLIC": case "T_PROTECTED": case "T_PRIVATE": case "T_STATIC": case "T_FUNCTION": // check to see if we have a valid function // then check if we are inside function and have a closure if ($this->isValidFunction($tokens, $key, $this->getName())) { if ($bodyOnly === false) { //if first instance of tokenType grab prefixed whitespace //and append to body if ($capture === false) { $body .= $this->extractPrefixedWhitespace($tokens, $key); } $body .= $tokenValue; } $capture = true; } else { //closure test if ($firstBrace && $tokenType == "T_FUNCTION") { $body .= $tokenValue; continue; } $capture = false; continue; } break; case "{": if ($capture === false) { continue; } if ($firstBrace === false) { $firstBrace = true; if ($bodyOnly === true) { continue; } } $body .= $tokenValue; break; case "}": if ($capture === false) { continue; } //check to see if this is the last brace if ($this->isEndingBrace($tokens, $key)) { //capture the end brace if not bodyOnly if ($bodyOnly === false) { $body .= $tokenValue; } break 2; } $body .= $tokenValue; break; default: if ($capture === false) { continue; } // if returning body only wait for first brace before capturing if ($bodyOnly === true && $firstBrace !== true) { continue; } $body .= $tokenValue; break; } } //remove ending whitespace and return return rtrim($body); } /** * Take current position and find any whitespace * * @param array $haystack * @param int $position * @return string */ protected function extractPrefixedWhitespace($haystack, $position) { $content = ''; $count = count($haystack); if ($position+1 == $count) { return $content; } for ($i = $position-1;$i >= 0;$i--) { $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i]; //search only for whitespace if ($tokenType == "T_WHITESPACE") { $content .= $tokenValue; } else { break; } } return $content; } /** * Test for ending brace * * @param array $haystack * @param int $position * @return bool */ protected function isEndingBrace($haystack, $position) { $count = count($haystack); //advance one position $position = $position+1; if ($position == $count) { return true; } for ($i = $position;$i < $count; $i++) { $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; switch ($tokenType) { case "T_FINAL": case "T_ABSTRACT": case "T_PUBLIC": case "T_PROTECTED": case "T_PRIVATE": case "T_STATIC": return true; case "T_FUNCTION": // If a function is encountered and that function is not a closure // then return true. otherwise the function is a closure, return false if ($this->isValidFunction($haystack, $i)) { return true; } return false; case "}": case ";"; case "T_BREAK": case "T_CATCH": case "T_DO": case "T_ECHO": case "T_ELSE": case "T_ELSEIF": case "T_EVAL": case "T_EXIT": case "T_FINALLY": case "T_FOR": case "T_FOREACH": case "T_GOTO": case "T_IF": case "T_INCLUDE": case "T_INCLUDE_ONCE": case "T_PRINT": case "T_STRING": case "T_STRING_VARNAME": case "T_THROW": case "T_USE": case "T_VARIABLE": case "T_WHILE": case "T_YIELD": return false; } } } /** * Test to see if current position is valid function or * closure. Returns true if it's a function and NOT a closure * * @param array $haystack * @param int $position * @param string $functionName * @return bool */ protected function isValidFunction($haystack, $position, $functionName = null) { $isValid = false; $count = count($haystack); for ($i = $position+1; $i < $count; $i++) { $tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i]; $tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i]; //check for occurance of ( or if ($tokenType == "T_STRING") { //check to see if function name is passed, if so validate against that if ($functionName !== null && $tokenValue != $functionName) { $isValid = false; break; } $isValid = true; break; } elseif ($tokenValue == "(") { break; } } return $isValid; } /** * @return string */ public function toString() { return parent::__toString(); } /** * @return string */ public function __toString() { return parent::__toString(); } /** * Creates a new FileScanner instance. * * By having this as a seperate method it allows the method to be overridden * if a different FileScanner is needed. * * @param string $filename * * @return CachingFileScanner */ protected function createFileScanner($filename) { return new CachingFileScanner($filename); } }