torrentpier-lts/library/Zend/Code/Reflection/MethodReflection.php

488 lines
14 KiB
PHP

<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionMethod as PhpReflectionMethod;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Scanner\AnnotationScanner;
use Zend\Code\Scanner\CachingFileScanner;
class MethodReflection extends PhpReflectionMethod implements ReflectionInterface
{
/**
* Constant use in @MethodReflection to display prototype as an array
*/
const PROTOTYPE_AS_ARRAY = 'prototype_as_array';
/**
* Constant use in @MethodReflection to display prototype as a string
*/
const PROTOTYPE_AS_STRING = 'prototype_as_string';
/**
* @var AnnotationScanner
*/
protected $annotations = null;
/**
* Retrieve method DocBlock reflection
*
* @return DocBlockReflection|false
*/
public function getDocBlock()
{
if ('' == $this->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|bool
*/
public function getContents($includeDocBlock = true)
{
$docComment = $this->getDocComment();
$content = ($includeDocBlock && !empty($docComment)) ? $docComment . "\n" : '';
$content .= $this->extractMethodContents();
return $content;
}
/**
* Get method body
*
* @return string|bool
*/
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->getDeclaringClass()->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("<?php ". $functionLine);
//remove first entry which is php open tag
array_shift($tokens);
if (!count($tokens)) {
return '';
}
$capture = false;
$firstBrace = false;
$body = '';
foreach ($tokens as $key => $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 $haystack
* @param $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 $haystack
* @param $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 $haystack
* @param $position
* @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;
}
public function toString()
{
return parent::__toString();
}
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);
}
}