195 lines
8.0 KiB
PHP
195 lines
8.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace StubTests;
|
|
|
|
use PhpParser\Node\Expr\BinaryOp\BitwiseOr;
|
|
use PhpParser\Node\Expr\ClassConstFetch;
|
|
use PhpParser\Node\Expr\ConstFetch;
|
|
use PhpParser\Node\Expr\UnaryMinus;
|
|
use PhpParser\Node\Scalar\DNumber;
|
|
use PhpParser\Node\Scalar\LNumber;
|
|
use PhpParser\Node\Scalar\String_;
|
|
use PHPUnit\Framework\Exception;
|
|
use PHPUnit\Framework\TestCase;
|
|
use RuntimeException;
|
|
use StubTests\Model\PHPClass;
|
|
use StubTests\Model\PHPConst;
|
|
use StubTests\Model\PHPFunction;
|
|
use StubTests\Model\PHPInterface;
|
|
use StubTests\Parsers\ParserUtils;
|
|
use StubTests\TestData\Providers\PhpStormStubsSingleton;
|
|
use StubTests\TestData\Providers\ReflectionStubsSingleton;
|
|
use function array_filter;
|
|
use function array_pop;
|
|
use function property_exists;
|
|
use function strval;
|
|
|
|
abstract class BaseStubsTest extends TestCase
|
|
{
|
|
public static function setUpBeforeClass(): void
|
|
{
|
|
parent::setUpBeforeClass();
|
|
PhpStormStubsSingleton::getPhpStormStubs();
|
|
ReflectionStubsSingleton::getReflectionStubs();
|
|
}
|
|
|
|
/**
|
|
* @throws Exception|RuntimeException
|
|
*/
|
|
public static function getStringRepresentationOfDefaultParameterValue(mixed $defaultValue, PHPClass|PHPInterface $contextClass = null): float|bool|int|string|null
|
|
{
|
|
if ($defaultValue instanceof ConstFetch) {
|
|
$defaultValueName = (string)$defaultValue->name;
|
|
if ($defaultValueName !== 'false' && $defaultValueName !== 'true' && $defaultValueName !== 'null') {
|
|
$constants = array_filter(
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getConstants(),
|
|
fn (PHPConst $const) => $const->name === (string)$defaultValue->name
|
|
);
|
|
/** @var PHPConst $constant */
|
|
$constant = array_pop($constants);
|
|
$value = $constant->value;
|
|
} else {
|
|
$value = $defaultValueName;
|
|
}
|
|
} elseif ($defaultValue instanceof String_ || $defaultValue instanceof LNumber || $defaultValue instanceof DNumber) {
|
|
$value = strval($defaultValue->value);
|
|
} elseif ($defaultValue instanceof BitwiseOr) {
|
|
if ($defaultValue->left instanceof ConstFetch && $defaultValue->right instanceof ConstFetch) {
|
|
$constants = array_filter(
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getConstants(),
|
|
fn (PHPConst $const) => property_exists($defaultValue->left, 'name') &&
|
|
$const->name === (string)$defaultValue->left->name
|
|
);
|
|
/** @var PHPConst $leftConstant */
|
|
$leftConstant = array_pop($constants);
|
|
$constants = array_filter(
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getConstants(),
|
|
fn (PHPConst $const) => property_exists($defaultValue->right, 'name') &&
|
|
$const->name === (string)$defaultValue->right->name
|
|
);
|
|
/** @var PHPConst $rightConstant */
|
|
$rightConstant = array_pop($constants);
|
|
$value = $leftConstant->value|$rightConstant->value;
|
|
}
|
|
} elseif ($defaultValue instanceof UnaryMinus && property_exists($defaultValue->expr, 'value')) {
|
|
$value = '-' . $defaultValue->expr->value;
|
|
} elseif ($defaultValue instanceof ClassConstFetch) {
|
|
$class = (string)$defaultValue->class;
|
|
if ($class === 'self' && $contextClass !== null) {
|
|
$class = $contextClass->name;
|
|
}
|
|
$parentClass = PhpStormStubsSingleton::getPhpStormStubs()->getClass($class) ??
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getInterface($class);
|
|
if ($parentClass === null) {
|
|
throw new Exception("Class $class not found in stubs");
|
|
}
|
|
if ((string)$defaultValue->name === 'class') {
|
|
$value = (string)$defaultValue->class;
|
|
} else {
|
|
$constants = array_filter($parentClass->constants, fn (PHPConst $const) => $const->name === (string)$defaultValue->name);
|
|
/** @var PHPConst $constant */
|
|
$constant = array_pop($constants);
|
|
$value = $constant->value;
|
|
}
|
|
} else {
|
|
$value = strval($defaultValue);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
public static function getParameterRepresentation(PHPFunction $function): string
|
|
{
|
|
$result = '';
|
|
foreach ($function->parameters as $parameter) {
|
|
$types = array_unique($parameter->typesFromSignature + Model\CommonUtils::flattenArray($parameter->typesFromAttribute, false));
|
|
if (!empty($types)) {
|
|
$result .= implode('|', $types) . ' ';
|
|
}
|
|
if ($parameter->is_passed_by_ref) {
|
|
$result .= '&';
|
|
}
|
|
if ($parameter->is_vararg) {
|
|
$result .= '...';
|
|
}
|
|
$result .= '$' . $parameter->name . ', ';
|
|
}
|
|
return rtrim($result, ', ');
|
|
}
|
|
|
|
/**
|
|
* @throws RuntimeException
|
|
*/
|
|
protected static function getDuplicatedFunctions(array $filtered): array
|
|
{
|
|
$duplicatedFunctions = array_filter($filtered, function (PHPFunction $value, int|string $key) {
|
|
$duplicatesOfFunction = self::getAllDuplicatesOfFunction($value->name);
|
|
$functionVersions[] = ParserUtils::getAvailableInVersions(
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getFunction($value->name)
|
|
);
|
|
array_push($functionVersions, ...array_values(array_map(
|
|
fn (PHPFunction $function) => ParserUtils::getAvailableInVersions($function),
|
|
$duplicatesOfFunction
|
|
)));
|
|
$hasDuplicates = false;
|
|
$current = array_pop($functionVersions);
|
|
$next = array_pop($functionVersions);
|
|
while ($next !== null) {
|
|
if (!empty(array_intersect($current, $next))) {
|
|
$hasDuplicates = true;
|
|
}
|
|
$current = array_merge($current, $next);
|
|
$next = array_pop($functionVersions);
|
|
}
|
|
return $hasDuplicates;
|
|
}, ARRAY_FILTER_USE_BOTH);
|
|
return array_unique(array_map(fn (PHPFunction $function) => $function->name, $duplicatedFunctions));
|
|
}
|
|
|
|
protected static function getAllDuplicatesOfFunction(?string $name): array
|
|
{
|
|
return array_filter(
|
|
PhpStormStubsSingleton::getPhpStormStubs()->getFunctions(),
|
|
fn ($duplicateValue, $duplicateKey) => $duplicateValue->name === $name && str_contains($duplicateKey, 'duplicated'),
|
|
ARRAY_FILTER_USE_BOTH
|
|
);
|
|
}
|
|
|
|
public static function isReflectionTypesMatchSignature(array $reflectionTypes, array $typesFromSignature): bool
|
|
{
|
|
$diff = array_merge(array_diff($reflectionTypes, $typesFromSignature), array_diff($typesFromSignature, $reflectionTypes));
|
|
return empty($diff);
|
|
}
|
|
|
|
public static function ifReflectionTypesExistInAttributes(array $reflectionTypes, array $typesFromAttribute): bool
|
|
{
|
|
return !empty(array_filter(
|
|
$typesFromAttribute,
|
|
fn (array $types) => empty(array_merge(
|
|
array_diff($reflectionTypes, $types),
|
|
array_diff($types, $reflectionTypes)
|
|
))
|
|
));
|
|
}
|
|
|
|
public static function getStringRepresentationOfTypeHintsFromAttributes(array $typesFromAttribute): string
|
|
{
|
|
$resultString = '';
|
|
foreach ($typesFromAttribute as $types) {
|
|
$resultString .= '[' . implode('|', $types) . ']';
|
|
}
|
|
return $resultString;
|
|
}
|
|
|
|
public static function convertNullableTypesToUnion($typesToProcess, array &$resultArray)
|
|
{
|
|
array_walk($typesToProcess, function (string $type) use (&$resultArray) {
|
|
if (str_contains($type, '?')) {
|
|
array_push($resultArray, 'null', ltrim($type, '?'));
|
|
} else {
|
|
array_push($resultArray, $type);
|
|
}
|
|
});
|
|
}
|
|
}
|