mirror of
https://github.com/torrentpier/torrentpier-lts.git
synced 2025-02-28 15:10:54 +03:00
495 lines
14 KiB
PHP
495 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-2015 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
|
|
*/
|
|
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("<?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 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);
|
|
}
|
|
}
|