project_start/phpstorm-stubs/tests/Model/PHPMethod.php

199 lines
8.0 KiB
PHP

<?php
namespace StubTests\Model;
use Exception;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use PhpParser\Node\Stmt\ClassMethod;
use ReflectionMethod;
use RuntimeException;
use stdClass;
use function strlen;
class PHPMethod extends PHPFunction
{
/**
* @var string
*/
public $access;
/**
* @var bool
*/
public $isStatic;
/**
* @var bool
*/
public $isFinal;
/**
* @var string
*/
public $parentName;
/**
* @var bool
*/
public $isReturnTypeTentative;
/**
* @param ReflectionMethod $reflectionObject
* @return static
*/
public function readObjectFromReflection($reflectionObject)
{
parent::readObjectFromReflection($reflectionObject);
$this->isStatic = $reflectionObject->isStatic();
$this->isFinal = $reflectionObject->isFinal();
$this->parentName = $reflectionObject->class;
if ($reflectionObject->isProtected()) {
$access = 'protected';
} elseif ($reflectionObject->isPrivate()) {
$access = 'private';
} else {
$access = 'public';
}
$this->access = $access;
if (method_exists($reflectionObject, 'hasTentativeReturnType')) {
$this->isReturnTypeTentative = $reflectionObject->hasTentativeReturnType();
if ($this->isReturnTypeTentative) {
$returnTypes = self::getReflectionTypeAsArray($reflectionObject->getTentativeReturnType());
if (!empty($returnTypes)) {
array_push($this->returnTypesFromSignature, ...$returnTypes);
}
}
} else {
$this->isReturnTypeTentative = false;
}
return $this;
}
/**
* @param ClassMethod $node
* @return static
* @throws RuntimeException
*/
public function readObjectFromStubNode($node)
{
$this->parentName = self::getFQN($node->getAttribute('parent'));
$this->name = $node->name->name;
$typesFromAttribute = self::findTypesFromAttribute($node->attrGroups);
$this->isReturnTypeTentative = self::hasTentativeTypeAttribute($node->attrGroups);
$this->availableVersionsRangeFromAttribute = self::findAvailableVersionsRangeFromAttribute($node->attrGroups);
$this->returnTypesFromAttribute = $typesFromAttribute;
array_push($this->returnTypesFromSignature, ...self::convertParsedTypeToArray($node->getReturnType()));
$this->collectTags($node);
$this->checkIfReturnTypeIsTentative($node);
$this->checkDeprecationTag($node);
$this->checkReturnTag();
if (strncmp($this->name, 'PS_UNRESERVE_PREFIX_', 20) === 0) {
$this->name = substr($this->name, strlen('PS_UNRESERVE_PREFIX_'));
}
$index = 0;
foreach ($node->getParams() as $parameter) {
$parsedParameter = (new PHPParameter())->readObjectFromStubNode($parameter);
if (self::entitySuitsCurrentPhpVersion($parsedParameter)) {
$parsedParameter->indexInSignature = $index;
$addedParameters = array_filter($this->parameters, function (PHPParameter $addedParameter) use ($parsedParameter) {
return $addedParameter->name === $parsedParameter->name;
});
if (!empty($addedParameters)) {
if ($parsedParameter->is_vararg) {
$parsedParameter->isOptional = false;
$index--;
$parsedParameter->indexInSignature = $index;
}
}
$this->parameters[$parsedParameter->name] = $parsedParameter;
$index++;
}
}
foreach ($this->parameters as $parameter) {
$relatedParamTags = array_filter($this->paramTags, function (Param $tag) use ($parameter) {
return $tag->getVariableName() === $parameter->name;
});
/** @var Param $relatedParamTag */
$relatedParamTag = array_pop($relatedParamTags);
if ($relatedParamTag !== null) {
$parameter->isOptional = $parameter->isOptional || str_contains((string)$relatedParamTag->getDescription(), '[optional]');
$parameter->markedOptionalInPhpDoc = str_contains((string)$relatedParamTag->getDescription(), '[optional]');
}
}
$this->isFinal = $node->isFinal();
$this->isStatic = $node->isStatic();
if ($node->isPrivate()) {
$this->access = 'private';
} elseif ($node->isProtected()) {
$this->access = 'protected';
} else {
$this->access = 'public';
}
return $this;
}
/**
* @param stdClass|array $jsonData
* @throws Exception
*/
public function readMutedProblems($jsonData)
{
foreach ($jsonData as $method) {
if ($method->name === $this->name) {
if (!empty($method->problems)) {
foreach ($method->problems as $problem) {
switch ($problem->description) {
case 'parameter mismatch':
$this->mutedProblems[StubProblemType::FUNCTION_PARAMETER_MISMATCH] = $problem->versions;
break;
case 'missing method':
$this->mutedProblems[StubProblemType::STUB_IS_MISSED] = $problem->versions;
break;
case 'deprecated method':
$this->mutedProblems[StubProblemType::FUNCTION_IS_DEPRECATED] = $problem->versions;
break;
case 'absent in meta':
$this->mutedProblems[StubProblemType::ABSENT_IN_META] = $problem->versions;
break;
case 'wrong access':
$this->mutedProblems[StubProblemType::FUNCTION_ACCESS] = $problem->versions;
break;
case 'has duplicate in stubs':
$this->mutedProblems[StubProblemType::HAS_DUPLICATION] = $problem->versions;
break;
case 'has nullable typehint':
$this->mutedProblems[StubProblemType::HAS_NULLABLE_TYPEHINT] = $problem->versions;
break;
case 'has union typehint':
$this->mutedProblems[StubProblemType::HAS_UNION_TYPEHINT] = $problem->versions;
break;
case 'wrong return typehint':
$this->mutedProblems[StubProblemType::WRONG_RETURN_TYPEHINT] = $problem->versions;
break;
case 'has type mismatch in signature and phpdoc':
$this->mutedProblems[StubProblemType::TYPE_IN_PHPDOC_DIFFERS_FROM_SIGNATURE] = $problem->versions;
break;
case 'has wrong final modifier':
$this->mutedProblems[StubProblemType::WRONG_FINAL_MODIFIER] = $problem->versions;
break;
case 'has wrong static modifier':
$this->mutedProblems[StubProblemType::WRONG_STATIC_MODIFIER] = $problem->versions;
break;
default:
throw new Exception("Unexpected value $problem->description");
}
}
}
if (!empty($method->parameters)) {
foreach ($this->parameters as $parameter) {
$parameter->readMutedProblems($method->parameters);
}
}
}
}
}
}