Merge pull request #465 from isfedorov/master

New architecture of test suite
This commit is contained in:
Maxim Kolmakov 2018-12-11 17:37:48 +01:00 committed by GitHub
commit b643efdde0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1874 additions and 815 deletions

3
.gitignore vendored
View File

@ -11,9 +11,6 @@ rest-client.private.env.json
.idea/workspace.xml
.idea/shelf
#Test temporary file
tests/stub.json
#Composer
vendor
composer.lock

View File

@ -2,7 +2,7 @@
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="StubTests\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />

View File

@ -13,7 +13,6 @@ php:
install:
- composer install
script:
- docker-compose -f tests/docker-compose.yml pull
- docker-compose -f tests/docker-compose.yml build
- docker-compose -f tests/docker-compose.yml run php > ./tests/stub.json
- phpunit tests/TestStubs.php
- docker-compose -f docker-compose.yml pull
- docker-compose -f docker-compose.yml build
- docker-compose -f docker-compose.yml run php

View File

@ -26,8 +26,7 @@ The set of extensions enabled by default in PhpStorm can change anytime without
### How to run tests
1. Execute `composer install`
2. Execute `docker-compose -f tests/docker-compose.yml run php > tests/stub.json`
3. Run the test `phpunit tests/TestStubs.php`
2. Execute `docker-compose -f docker-compose.yml run php`
### License
[Apache 2]

View File

@ -966,7 +966,8 @@ class SplTempFileObject extends SplFileObject {
* The SplDoublyLinkedList class provides the main functionalities of a doubly linked list.
* @link https://php.net/manual/en/class.spldoublylinkedlist.php
*/
class SplDoublyLinkedList implements Iterator, Countable, ArrayAccess {
class SplDoublyLinkedList implements Iterator, Countable, ArrayAccess, Serializable
{
const IT_MODE_LIFO = 2;
const IT_MODE_FIFO = 0;
const IT_MODE_DELETE = 1;

View File

@ -1,21 +1,26 @@
{
"name": "jetbrains/phpstorm-stubs",
"description": "PHP runtime & extensions header files for PhpStorm",
"homepage": "https://www.jetbrains.com/phpstorm",
"license": "Apache-2.0",
"keywords": [
"JetBrains",
"PHPStorm",
"stubs",
"autocomplete",
"type",
"inference",
"code",
"inspection"
],
"require-dev": {
"nikic/php-parser": "v4.0.1",
"phpdocumentor/reflection-docblock": "^4.3",
"phpunit/phpunit": "7.1.4"
"name": "jetbrains/phpstorm-stubs",
"description": "PHP runtime & extensions header files for PhpStorm",
"homepage": "https://www.jetbrains.com/phpstorm",
"license": "Apache-2.0",
"keywords": [
"JetBrains",
"PHPStorm",
"stubs",
"autocomplete",
"type",
"inference",
"code",
"inspection"
],
"require-dev": {
"nikic/php-parser": "v4.0.1",
"phpdocumentor/reflection-docblock": "^4.3",
"phpunit/phpunit": "7.1.4"
},
"autoload-dev": {
"psr-4": {
"StubTests\\": "tests/"
}
}
}

View File

@ -4,4 +4,4 @@ services:
image: php:7.2
volumes:
- .:/opt/project
command: php /opt/project/PHPReflectionParser.php
command: /opt/project/vendor/bin/phpunit /opt/project/tests/TestStubs.php

View File

@ -158,7 +158,8 @@ interface SessionUpdateTimestampHandlerInterface {
* @link https://php.net/manual/en/class.reflectionzendextension.php
* @since 5.4.0
*/
class SessionHandler implements SessionHandlerInterface, SessionUpdateTimestampHandlerInterface {
class SessionHandler implements SessionHandlerInterface, SessionIdInterface
{
/**
* Close the session

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
abstract class BasePHPClass extends PHPElementWithPHPDoc
{
/**
* @var PHPMethod[]
*/
public $methods = [];
/**
* @var PHPConst[]
*/
public $constants = [];
}

View File

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeAbstract;
abstract class BasePHPElement
{
public $name;
public $parseError;
/**
* @param mixed $object
*
* @return mixed
*/
abstract public function readObjectFromReflection($object);
/**
* @param mixed $node
*
* @return mixed
*/
abstract public function readObjectFromStubNode($node);
protected function getConstantFQN(NodeAbstract $node, string $nodeName): string
{
$namespace = '';
if ($node->getAttribute('parent') instanceof Namespace_ && !empty($node->getAttribute('parent')->name)) {
$namespace = '\\' . implode('\\', $node->getAttribute('parent')->name->parts) . '\\';
}
return $namespace . $nodeName;
}
protected function getFQN($node): string
{
$fqn = '';
if ($node->namespacedName === null) {
$fqn = $node->name->parts[0];
} else {
/**@var string $part*/
foreach ($node->namespacedName->parts as $part) {
$fqn .= "$part\\";
}
}
return rtrim($fqn, "\\");
}
}

80
tests/Model/PHPClass.php Normal file
View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Stmt\Class_;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionException;
use ReflectionMethod;
class PHPClass extends BasePHPClass
{
public $parentClass;
public $interfaces = [];
/**
* @param ReflectionClass $clazz
* @return $this
*/
public function readObjectFromReflection($clazz): self
{
try {
$reflectionClass = new ReflectionClass($clazz);
$this->name = $reflectionClass->getName();
$parentClass = $reflectionClass->getParentClass();
if ($parentClass !== false) {
$this->parentClass = $reflectionClass->getParentClass()->getName();
}
$this->interfaces = $reflectionClass->getInterfaceNames();
/**@var ReflectionMethod $method */
foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->methods[$method->name] = (new PHPMethod())->readObjectFromReflection($method);
}
/**@var ReflectionClassConstant $constant */
foreach ($reflectionClass->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->constants[$constant->name] = (new PHPConst())->readObjectFromReflection($constant);
}
} catch (ReflectionException $ex) {
$this->parseError = $ex;
}
return $this;
}
/**
* @param Class_ $node
* @return $this
*/
public function readObjectFromStubNode($node): self
{
$this->name = $this->getFQN($node);
$this->collectLinks($node);
if (!empty($node->extends)) {
$this->parentClass = '';
foreach ($node->extends->parts as $part) {
$this->parentClass .= "\\$part";
}
$this->parentClass = ltrim($this->parentClass, "\\");
}
if (!empty($node->implements)) {
foreach ($node->implements as $interfaceObject) {
$interfaceFQN = '';
foreach ($interfaceObject->parts as $interface) {
$interfaceFQN .= "\\$interface";
}
$this->interfaces[] = ltrim($interfaceFQN, "\\");
}
}
return $this;
}
}

54
tests/Model/PHPConst.php Normal file
View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Const_;
use PhpParser\Node\Stmt\ClassConst;
use ReflectionClassConstant;
class PHPConst extends PHPElementWithPHPDoc
{
public $parentName;
public $value;
/**
* @param ReflectionClassConstant $constant
* @return $this
*/
public function readObjectFromReflection($constant)
{
$this->name = $constant->name;
$this->value = $constant->getValue();
return $this;
}
/**
* @param Const_ $node
* @return $this
*/
public function readObjectFromStubNode($node)
{
$this->name = $this->getConstantFQN($node, $node->name->name);
$this->value = $this->getConstValue($node);
$this->collectLinks($node);
if ($node->getAttribute('parent') instanceof ClassConst) {
$this->parentName = $this->getFQN($node->getAttribute('parent')->getAttribute('parent'));
}
return $this;
}
protected function getConstValue($node)
{
if (in_array('value', $node->value->getSubNodeNames(), true)) {
return $node->value->value;
}
if (in_array('expr', $node->value->getSubNodeNames(), true)) {
return $node->value->expr->value;
}
if (in_array('name', $node->value->getSubNodeNames(), true)) {
return $node->value->name->parts[0];
}
return null;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace StubTests\Model;
use PhpParser\Node\Expr\FuncCall;
class PHPDefineConstant extends PHPConst
{
/**
* @param array $constant
* @return $this
*/
public function readObjectFromReflection($constant)
{
$this->name = utf8_encode($constant[0]);
$this->value = is_resource($constant[1]) ? 'PHPSTORM_RESOURCE' : utf8_encode($constant[1]);
return $this;
}
/**
* @param FuncCall $node
* @return $this
*/
public function readObjectFromStubNode($node)
{
$constName = $this->getConstantFQN($node, $node->args[0]->value->value);
if (in_array($constName, ['null', 'true', 'false'])) {
$constName = strtoupper($constName);
}
$this->name = $constName;
$this->value = $this->getConstValue($node->args[1]);
$this->collectLinks($node);
return $this;
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use Exception;
use phpDocumentor\Reflection\DocBlock\Tag;
use PhpParser\Node;
use StubTests\Parsers\DocFactoryProvider;
abstract class PHPElementWithPHPDoc extends BasePHPElement
{
/**
* @var Tag[]
*/
public $links = [];
/**
* @var Tag[]
*/
public $see = [];
protected function collectLinks(Node $node): void
{
if ($node->getDocComment() !== null) {
try {
$phpDoc = DocFactoryProvider::getDocFactory()->create($node->getDocComment()->getText());
$this->links = $phpDoc->getTagsByName('link');
$this->see = $phpDoc->getTagsByName('see');
} catch (Exception $e) {
$this->parseError = $e->getMessage();
}
}
}
}

100
tests/Model/PHPFunction.php Normal file
View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use Exception;
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use phpDocumentor\Reflection\Type;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Function_;
use ReflectionException;
use ReflectionFunction;
use ReflectionParameter;
use StubTests\Parsers\DocFactoryProvider;
class PHPFunction extends PHPElementWithPHPDoc
{
/**
* @var boolean $is_deprecated
*/
public $is_deprecated;
/**
* @var PHPParameter[]
*/
public $parameters = [];
/**
* @var Type $returnTag
*/
public $returnTag;
/**
* @param ReflectionFunction $function
* @return $this
*/
public function readObjectFromReflection($function)
{
try {
$reflectionFunction = new ReflectionFunction($function);
$this->name = $reflectionFunction->name;
$this->is_deprecated = $reflectionFunction->isDeprecated();
/**@var ReflectionParameter $parameter */
foreach ($reflectionFunction->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromReflection($parameter);
}
} catch (ReflectionException $ex) {
$this->parseError = $ex;
}
return $this;
}
/**
* @param Function_ $node
* @return $this
*/
public function readObjectFromStubNode($node)
{
$functionName = $this->getFQN($node);
$this->name = $functionName;
foreach ($node->getParams() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromStubNode($parameter);
}
$this->collectLinks($node);
$this->checkDeprecationTag($node);
$this->checkReturnTag($node);
return $this;
}
protected function checkDeprecationTag(FunctionLike $node): void
{
if ($node->getDocComment() !== null) {
try {
$phpDoc = DocFactoryProvider::getDocFactory()->create($node->getDocComment()->getText());
if (empty($phpDoc->getTagsByName('deprecated'))) {
$this->is_deprecated = false;
} else {
$this->is_deprecated = true;
}
} catch (Exception $e) {
$this->parseError = $e->getMessage();
}
}
}
protected function checkReturnTag(FunctionLike $node): void
{
if ($node->getDocComment() !== null) {
try {
$phpDoc = DocFactoryProvider::getDocFactory()->create($node->getDocComment()->getText());
$parsedReturnTag = $phpDoc->getTagsByName('return');
if (!empty($parsedReturnTag) && $parsedReturnTag[0] instanceof Return_) {
$this->returnTag = $parsedReturnTag[0]->getType() . '';
}
} catch (Exception $e) {
$this->parseError = $e->getMessage();
}
}
}
}

View File

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Stmt\Interface_;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionException;
use ReflectionMethod;
class PHPInterface extends BasePHPClass
{
public $parentInterfaces = [];
/**
* @param ReflectionClass $interface
* @return $this
*/
public function readObjectFromReflection($interface): self
{
try {
$reflectionInterface = new ReflectionClass($interface);
$this->name = $reflectionInterface->getName();
/**@var ReflectionMethod $method */
foreach ($reflectionInterface->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->methods[$method->name] = (new PHPMethod())->readObjectFromReflection($method);
}
$this->parentInterfaces = $reflectionInterface->getInterfaceNames();
/**@var ReflectionClassConstant $constant */
foreach ($reflectionInterface->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->constants[$constant->name] = (new PHPConst())->readObjectFromReflection($constant);
}
} catch (ReflectionException $ex) {
$this->parseError = $ex;
}
return $this;
}
/**
* @param Interface_ $node
* @return $this
*/
public function readObjectFromStubNode($node): self
{
$this->name = $this->getFQN($node);
$this->collectLinks($node);
if (!empty($node->extends)) {
foreach ($node->extends[0]->parts as $part) {
$this->parentInterfaces[] = $part;
}
}
return $this;
}
}

72
tests/Model/PHPMethod.php Normal file
View File

@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Stmt\ClassMethod;
use ReflectionParameter;
class PHPMethod extends PHPFunction
{
public $access;
public $is_static;
public $is_final;
public $parentName;
/**
* @param \ReflectionMethod $method
* @return $this
*/
public function readObjectFromReflection($method)
{
$this->name = $method->name;
$this->is_static = $method->isStatic();
$this->is_final = $method->isFinal();
/**@var ReflectionParameter $parameter */
foreach ($method->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromReflection($parameter);
}
if ($method->isProtected()) {
$access = 'protected';
} elseif ($method->isPrivate()) {
$access = 'private';
} else {
$access = 'public';
}
$this->access = $access;
return $this;
}
/**
* @param ClassMethod $node
* @return $this
*/
public function readObjectFromStubNode($node)
{
$this->parentName = $this->getFQN($node->getAttribute('parent'));
$this->name = $node->name->name;
$this->collectLinks($node);
$this->checkDeprecationTag($node);
$this->checkReturnTag($node);
if (strncmp($this->name, 'PS_UNRESERVE_PREFIX_', 20) === 0) {
$this->name = substr($this->name, strlen('PS_UNRESERVE_PREFIX_'));
}
foreach ($node->getParams() as $parameter) {
$this->parameters[] = (new PHPParameter())->readObjectFromStubNode($parameter);
}
$this->is_final = $node->isFinal();
$this->is_static = $node->isStatic();
if ($node->isPrivate()) {
$this->access = 'private';
} elseif ($node->isProtected()) {
$this->access = 'protected';
} else {
$this->access = 'public';
}
return $this;
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace StubTests\Model;
use PhpParser\Node\Param;
use ReflectionParameter;
class PHPParameter extends BasePHPElement
{
public $type = '';
public $is_vararg;
public $is_passed_by_ref;
/**
* @param ReflectionParameter $parameter
* @return $this
*/
public function readObjectFromReflection($parameter): self
{
$this->name = $parameter->name;
if (!empty($parameter->getType())) {
$this->type = $parameter->getType()->getName();
}
$this->is_vararg = $parameter->isVariadic();
$this->is_passed_by_ref = $parameter->isPassedByReference();
return $this;
}
/**
* @param Param $node
* @return $this
*/
public function readObjectFromStubNode($node): self
{
$this->name = $node->var->name;
if ($node->type !== null) {
if (empty($node->type->name)) {
if (!empty($node->type->parts)) {
$this->type = $node->type->parts[0];
}
} else {
$this->type = $node->type->name;
}
}
$this->is_vararg = $node->variadic;
$this->is_passed_by_ref = $node->byRef;
return $this;
}
}

View File

@ -1,171 +0,0 @@
<?php
$constants = [];
foreach (get_defined_constants() as $name => $value) {
$constants[] = (new PHPConst())->serialize($name, $value);
}
$data['constants'] = $constants;
$functions = [];
foreach (get_defined_functions()['internal'] as $name) {
$functions[] = (new PHPFunction())->serialize(new ReflectionFunction($name));
}
$data['functions'] = $functions;
$classes = [];
foreach (get_declared_classes() as $class) {
$classes[] = (new PHPClass())->serialize(new ReflectionClass($class));
}
$data['classes'] = $classes;
$interfaces = [];
foreach (get_declared_interfaces() as $interface) {
$interfaces[] = (new PHPInterface())->serialize(new ReflectionClass($interface));
}
$data['interfaces'] = $interfaces;
$json = json_encode($data, JSON_NUMERIC_CHECK);
echo $json;
class PHPClass
{
public $name;
public $methods = [];
public $constants = [];
public $parentClass;
public $interfaces = [];
public function serialize(ReflectionClass $reflectionClass): array
{
$this->name = $reflectionClass->getName();
$parentClass = $reflectionClass->getParentClass();
if (false !== $parentClass) {
$this->parentClass = $reflectionClass->getParentClass()->getName();
}
$this->interfaces = $reflectionClass->getInterfaceNames();
foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->methods[$method->name] = (new PHPMethod())->serialize($method);
}
foreach ($reflectionClass->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->constants[$constant->name] = (new PHPConst())->serialize($constant->name, $constant->getValue());
}
return (array)$this;
}
}
class PHPConst
{
public $name;
public $value;
public function serialize($name, $value): array
{
$this->name = utf8_encode($name);
$this->value = is_resource($value) ? 'PHPSTORM_RESOURCE' : utf8_encode($value);
return (array)$this;
}
}
class PHPFunction
{
public $name;
public $is_deprecated;
public $parameters = [];
public function serialize(ReflectionFunction $reflectionFunction): array
{
$this->name = $reflectionFunction->name;
$this->is_deprecated = $reflectionFunction->isDeprecated();
foreach ($reflectionFunction->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->serialize($parameter);
}
return (array)$this;
}
}
class PHPMethod
{
public $name;
public $is_static;
public $access;
public $is_final;
public $parameters = [];
public function serialize(ReflectionMethod $method): array
{
$this->name = $method->name;
$this->is_static = $method->isStatic();
$this->is_final = $method->isFinal();
foreach ($method->getParameters() as $parameter) {
$this->parameters[] = (new PHPParameter())->serialize($parameter);
}
if ($method->isProtected()) {
$access = 'protected';
} else if ($method->isPrivate()) {
$access = 'private';
} else {
$access = 'public';
}
$this->access = $access;
return (array)$this;
}
}
class PHPParameter
{
public $name;
public $type = '';
public $is_vararg;
public $is_passed_by_ref;
public function serialize(ReflectionParameter $parameter): array
{
$this->name = $parameter->name;
if (!empty($parameter->getType())) {
$this->type = $parameter->getType()->getName();
}
$this->is_vararg = $parameter->isVariadic();
$this->is_passed_by_ref = $parameter->isPassedByReference();
return (array)$this;
}
}
class PHPInterface
{
public $name;
public $methods = [];
public $constants = [];
public $parentInterfaces = [];
public function serialize(ReflectionClass $reflectionClass): array
{
$this->name = $reflectionClass->getName();
foreach ($reflectionClass->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->methods[$method->name] = (new PHPMethod())->serialize($method);
}
$this->parentInterfaces = $reflectionClass->getInterfaceNames();
foreach ($reflectionClass->getReflectionConstants() as $constant) {
if ($constant->getDeclaringClass()->getName() !== $this->name) {
continue;
}
$this->constants[$constant->name] = (new PHPConst())->serialize($constant->name, $constant->getValue());
}
return (array)$this;
}
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers;
use phpDocumentor\Reflection\DocBlockFactory;
class DocFactoryProvider
{
private static $docFactory;
public static function getDocFactory(): DocBlockFactory
{
if (self::$docFactory === null) {
self::$docFactory = DocBlockFactory::createInstance();
}
return self::$docFactory;
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers;
use ReflectionClass;
use ReflectionFunction;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPConst;
use StubTests\Model\PHPDefineConstant;
use StubTests\Model\PHPFunction;
use StubTests\Model\PHPInterface;
class PHPReflectionParser
{
public static function getStubs(): array
{
$data = [];
$const_groups = get_defined_constants(true);
unset($const_groups['user']);
$const_groups = Utils::flattenArray($const_groups, true);
$data[PHPConst::class] = [];
foreach ($const_groups as $name => $value) {
$data[PHPConst::class][] = (new PHPDefineConstant())->readObjectFromReflection([$name, $value]);
}
$data[PHPFunction::class] = [];
/**@var ReflectionFunction $function */
foreach (get_defined_functions()['internal'] as $function) {
$data[PHPFunction::class][] = (new PHPFunction())->readObjectFromReflection($function);
}
$data[PHPClass::class] = [];
$cl = get_declared_classes();
foreach ($cl as $clazz) {
$reflectionClass = new ReflectionClass($clazz);
if ($reflectionClass->isInternal()) {
$data[PHPClass::class][] = (new PHPClass())->readObjectFromReflection($clazz);
}
}
$data[PHPInterface::class] = [];
/**@var ReflectionClass $interface */
foreach (get_declared_interfaces() as $interface) {
$reflectionInterface = new ReflectionClass($interface);
if ($reflectionInterface->isInternal()) {
$data[PHPInterface::class][] = (new PHPInterface())->readObjectFromReflection($interface);
}
}
return $data;
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace StubTests\Parsers;
use FilesystemIterator;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitor\NameResolver;
use PhpParser\ParserFactory;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPInterface;
use StubTests\Parsers\Visitors\ASTVisitor;
use StubTests\Parsers\Visitors\ParentConnector;
class StubParser
{
public static function getPhpStormStubs(): array
{
/** @noinspection PhpUnhandledExceptionInspection */
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$nameResolver = new NameResolver;
$stubs = [];
$visitor = new ASTVisitor($stubs);
$stubsIterator =
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(__DIR__ . '/../../', FilesystemIterator::SKIP_DOTS)
);
/** @var SplFileInfo $file */
foreach ($stubsIterator as $file) {
if (strpos($file->getRealPath(), 'vendor') || strpos($file->getRealPath(), '.git') ||
strpos($file->getRealPath(), 'tests') || strpos($file->getRealPath(), '.idea')) {
continue;
}
$code = file_get_contents($file->getRealPath());
$traverser = new NodeTraverser();
$traverser->addVisitor(new ParentConnector());
$traverser->addVisitor($nameResolver);
$traverser->addVisitor($visitor);
$traverser->traverse($parser->parse($code, new StubsParserErrorHandler()));
}
/**@var PHPInterface $interface */
foreach ($stubs[PHPInterface::class] as $interface) {
$stubs[PHPInterface::class][$interface->name]->parentInterfaces = $visitor->combineParentInterfaces($interface);
}
/**@var PHPClass $class */
foreach ($stubs[PHPClass::class] as $class) {
$stubs[PHPClass::class][$class->name]->interfaces = Utils::flattenArray($visitor->combineImplementedInterfaces($class), false);
}
return $stubs;
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers;
use PhpParser\Error;
use PhpParser\ErrorHandler;
class StubsParserErrorHandler implements ErrorHandler
{
/**
* Handle an error generated during lexing, parsing or some other operation.
*
* @param Error $error The error that needs to be handled
*/
public function handleError(Error $error): void
{
$error->setRawMessage($error->getRawMessage() . "\n" . $error->getFile());
}
}

14
tests/Parsers/Utils.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace StubTests\Parsers;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
class Utils
{
public static function flattenArray(array $arr, bool $group)
{
return iterator_to_array(new RecursiveIteratorIterator(new RecursiveArrayIterator($arr)), $group);
}
}

View File

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers\Visitors;
use PhpParser\Node;
use PhpParser\Node\Const_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\NodeVisitorAbstract;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPConst;
use StubTests\Model\PHPDefineConstant;
use StubTests\Model\PHPFunction;
use StubTests\Model\PHPInterface;
use StubTests\Model\PHPMethod;
use StubTests\Parsers\Utils;
use function key_exists;
class ASTVisitor extends NodeVisitorAbstract
{
public $stubs;
public function __construct(array &$stubs)
{
$this->stubs = &$stubs;
$this->stubs[PHPFunction::class] = [];
$this->stubs[PHPConst::class] = [];
$this->stubs[PHPClass::class] = [];
$this->stubs[PHPInterface::class] = [];
}
public function enterNode(Node $node)
{
if ($node instanceof Function_) {
$function = (new PHPFunction())->readObjectFromStubNode($node);
$this->stubs[PHPFunction::class][$function->name] = $function;
} elseif ($node instanceof Const_) {
$constant = (new PHPConst())->readObjectFromStubNode($node);
if ($constant->parentName === null) {
$this->stubs[PHPConst::class][$constant->name] = $constant;
} elseif (array_key_exists($constant->parentName, $this->stubs[PHPClass::class])) {
$this->stubs[PHPClass::class][$constant->parentName]->constants[$constant->name] = $constant;
} else {
$this->stubs[PHPInterface::class][$constant->parentName]->constants[$constant->name] = $constant;
}
} elseif ($node instanceof FuncCall) {
if ($node->name->parts[0] === 'define') {
$constant = (new PHPDefineConstant())->readObjectFromStubNode($node);
$this->stubs[PHPConst::class][$constant->name] = $constant;
}
} elseif ($node instanceof ClassMethod) {
$method = (new PHPMethod())->readObjectFromStubNode($node);
if (array_key_exists($method->parentName, $this->stubs[PHPClass::class])) {
$this->stubs[PHPClass::class][$method->parentName]->methods[$method->name] = $method;
} else {
$this->stubs[PHPInterface::class][$method->parentName]->methods[$method->name] = $method;
}
} elseif ($node instanceof Interface_) {
$interface = (new PHPInterface())->readObjectFromStubNode($node);
$this->stubs[PHPInterface::class][$interface->name] = $interface;
} elseif ($node instanceof Class_) {
$class = (new PHPClass())->readObjectFromStubNode($node);
$this->stubs[PHPClass::class][$class->name] = $class;
}
}
public function combineParentInterfaces($interface): array
{
$parents = [];
if (empty($interface->parentInterfaces)) {
return $parents;
}
/**@var PHPInterface $parentInterface */
foreach ($interface->parentInterfaces as $parentInterface) {
$parents[] = $parentInterface;
if (key_exists($parentInterface, $this->stubs[PHPInterface::class])) {
/**@var PHPInterface $parentInterface */
foreach ($this->combineParentInterfaces($this->stubs[PHPInterface::class][$parentInterface]) as $value) {
$parents[] = $value;
}
}
}
return $parents;
}
public function combineImplementedInterfaces($class): array
{
$interfaces = [];
/**@var PHPInterface $interface */
foreach ($class->interfaces as $interface) {
$interfaces[] = $interface;
if (key_exists($interface, $this->stubs[PHPInterface::class])) {
$interfaces[] = $this->stubs[PHPInterface::class][$interface]->parentInterfaces;
}
}
if ($class->parentClass === null) {
return $interfaces;
}
if (key_exists($class->parentClass, $this->stubs[PHPClass::class])
&& $this->stubs[PHPClass::class][$class->parentClass] !== null
) {
$inherited = $this->combineImplementedInterfaces($this->stubs[PHPClass::class][$class->parentClass]);
$interfaces[] = Utils::flattenArray($inherited, false);
}
return $interfaces;
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace StubTests\Parsers\Visitors;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
/**
* The visitor is required to provide "parent" attribute to nodes
*/
class ParentConnector extends NodeVisitorAbstract
{
private $stack;
public function beforeTraverse(array $nodes)
{
$this->stack = [];
}
public function enterNode(Node $node)
{
if (!empty($this->stack)) {
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
}
$this->stack[] = $node;
}
public function leaveNode(Node $node)
{
array_pop($this->stack);
}
}

View File

@ -1,350 +0,0 @@
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use phpDocumentor\Reflection\DocBlockFactory;
use PhpParser\Node;
use PhpParser\Node\{Const_,
Expr\FuncCall,
FunctionLike,
Stmt\Class_,
Stmt\ClassMethod,
Stmt\Function_,
Stmt\Interface_,
Stmt\Namespace_};
use PhpParser\NodeAbstract;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\ParserFactory;
/**
* The visitor is required to provide "parent" attribute to nodes
*/
class ParentConnector extends NodeVisitorAbstract
{
private $stack;
public function beforeTraverse(array $nodes)
{
$this->stack = [];
}
public function enterNode(Node $node)
{
if (!empty($this->stack)) {
$node->setAttribute('parent', $this->stack[count($this->stack) - 1]);
}
$this->stack[] = $node;
}
public function leaveNode(Node $node)
{
array_pop($this->stack);
}
}
class ASTVisitor extends NodeVisitorAbstract
{
private $docFactory;
private $stubs;
public function __construct(DocBlockFactory $docFactory, stdClass $stubs)
{
$this->docFactory = $docFactory;
$this->stubs = $stubs;
}
public function enterNode(Node $node)
{
if ($node instanceof Function_) {
$this->visitFunction($node);
} elseif ($node instanceof Const_) {
$this->visitConstant($node);
} elseif ($node instanceof FuncCall) {
$this->visitDefine($node);
} elseif ($node instanceof ClassMethod) {
$this->visitMethod($node);
} elseif ($node instanceof Interface_) {
$this->visitInterface($node);
} elseif ($node instanceof Class_) {
$this->visitClass($node);
}
}
public function visitFunction(Function_ $node): void
{
$function = new stdClass();
$functionName = $this->getFQN($node, $node->name->name);
$function->name = $functionName;
$function->parameters = $this->parseParams($node);
$function->parseError = null;
$this->collectLinks($node, $function);
if ($node->getDocComment() !== null) {
try {
$phpDoc = $this->docFactory->create($node->getDocComment()->getText());
} catch (Exception $e) {
$function->parseError = $e->getMessage();
return;
}
if (empty($phpDoc->getTagsByName('deprecated'))) {
$function->is_deprecated = false;
} else {
$function->is_deprecated = true;
}
}
$this->stubs->functions[$functionName] = $function;
}
private function visitConstant(Const_ $node): void
{
$const = new stdClass();
$constName = $this->getFQN($node, $node->name->name);
$const->name = $constName;
$const->value = $this->getConstValue($node);
$const->parseError = null;
$this->collectLinks($node, $const);
if ($node->getAttribute('parent') instanceof Node\Stmt\ClassConst) {
$parentName = $this->getFQN($node->getAttribute('parent')->getAttribute('parent'),
$node->getAttribute('parent')->getAttribute('parent')->name->name);
if (array_key_exists($parentName, $this->stubs->classes)) {
$this->stubs->classes[$parentName]->constants[$constName] = $const;
} else {
$this->stubs->interfaces[$parentName]->constants[$constName] = $const;
}
} else {
$this->stubs->constants[$constName] = $const;
}
}
private function visitDefine(FuncCall $node): void
{
if ($node->name->parts[0] === 'define') {
$constName = $this->getFQN($node, $node->args[0]->value->value);
$const = new stdClass();
if (in_array($constName, ['null', 'true', 'false'])) {
$constName = strtoupper($constName);
}
$const->name = $constName;
$const->value = $this->getConstValue($node->args[1]);
$const->parseError = null;
$this->collectLinks($node, $const);
$this->stubs->constants[$constName] = $const;
}
}
private function collectLinks(NodeAbstract $node, stdClass $stub){
$stub->links = [];
$stub->see = [];
if ($node->getDocComment() !== null) {
try {
$phpDoc = $this->docFactory->create($node->getDocComment()->getText());
$stub->links = $phpDoc->getTagsByName('link');
$stub->see = $phpDoc->getTagsByName('see');
} catch (Exception $e) {
$stub->parseError = $e->getMessage();
return;
}
}
}
private function getConstValue($node)
{
if (in_array('value', $node->value->getSubNodeNames(), true)) {
return $node->value->value;
}
if (in_array('expr', $node->value->getSubNodeNames(), true)) {
return $node->value->expr->value;
}
if (in_array('name', $node->value->getSubNodeNames(), true)) {
return $node->value->name->parts[0];
}
return null;
}
private function visitMethod(ClassMethod $node): void
{
$parentName = $this->getFQN($node->getAttribute('parent'), $node->getAttribute('parent')->name->name);
$method = new stdClass();
$method->name = $node->name->name;
//this will test PHPDocs
$method->parseError = null;
$method->returnTag = null;
$this->collectLinks($node, $method);
if ($node->getDocComment() !== null) {
try {
$phpDoc = $this->docFactory->create($node->getDocComment()->getText());
$parsedReturnTag = $phpDoc->getTagsByName('return');
if(!empty($parsedReturnTag) && $parsedReturnTag[0] instanceof Return_){
$method->returnTag = $parsedReturnTag[0]->getType() . "";
}
} catch (Exception $e) {
$method->parseError = $e->getMessage();
}
}
if (strncmp($method->name, 'PS_UNRESERVE_PREFIX_', 20) === 0) {
$method->name = substr($method->name, strlen('PS_UNRESERVE_PREFIX_'));
}
$method->parameters = $this->parseParams($node);
$method->is_final = $node->isFinal();
$method->is_static = $node->isStatic();
if ($node->isPrivate()) {
$method->access = 'private';
} elseif ($node->isProtected()) {
$method->access = 'protected';
} else {
$method->access = 'public';
}
if (array_key_exists($parentName, $this->stubs->classes)) {
$this->stubs->classes[$parentName]->methods[$method->name] = $method;
} else {
$this->stubs->interfaces[$parentName]->methods[$method->name] = $method;
}
}
private function visitClass(Class_ $node): void
{
$class = new stdClass();
$className = $this->getFQN($node, $node->name->name);
//this will test PHPDocs
$class->parseError = null;
$this->collectLinks($node, $class);
if ($node->getDocComment() !== null) {
try {
$this->docFactory->create($node->getDocComment()->getText());
} catch (Exception $e) {
$class->parseError = $e->getMessage();
}
}
$class->name = $className;
if (empty($node->extends)) {
$class->parentClass = null;
} else {
$class->parentClass = $this->getFQN($node, $node->extends->parts[0]);
}
$class->interfaces = $node->implements;
$this->stubs->classes[$className] = $class;
$this->stubs->classes[$className]->constants = [];
$this->stubs->classes[$className]->methods = [];
}
private function visitInterface(Interface_ $node): void
{
$interface = new stdClass();
$interfaceName = $this->getFQN($node, $node->name->name);
//this will test PHPDocs
$interface->parseError = null;
$this->collectLinks($node, $interface);
if ($node->getDocComment() !== null) {
try {
$this->docFactory->create($node->getDocComment()->getText());
} catch (Exception $e) {
$interface->parseError = $e->getMessage();
}
}
$interface->name = $interfaceName;
$interface->parentInterfaces = [];
if (!empty($node->extends)) {
foreach ($node->extends[0]->parts as $part) {
array_push($interface->parentInterfaces, $this->getFQN($node, $part));
}
}
$this->stubs->interfaces[$interfaceName] = $interface;
$this->stubs->interfaces[$interfaceName]->constants = [];
$this->stubs->interfaces[$interfaceName]->methods = [];
}
public function combineParentInterfaces($interface): array
{
$parents = [];
if (empty($interface->parentInterfaces)) {
return $parents;
} else {
foreach ($interface->parentInterfaces as $parentInterface) {
array_push($parents, $parentInterface);
foreach ($this->combineParentInterfaces($this->stubs->interfaces[$parentInterface]) as $value) {
array_push($parents, $value);
}
}
}
return $parents;
}
private function parseParams(FunctionLike $node): array
{
$params = $node->getParams();
$parsedParams = [];
/** @var Node\Param $param */
foreach ($params as $param) {
$parsedParam = new stdClass();
$parsedParam->name = $param->var->name;
if ($param->type !== null) {
if (empty($param->type->name)) {
if (!empty($param->type->parts)) {
$parsedParam->type = $param->type->parts[0];
}
} else {
$parsedParam->type = $param->type->name;
}
} else {
$parsedParam->type = '';
}
$parsedParam->is_vararg = $param->variadic;
$parsedParam->is_passed_by_ref = $param->byRef;
$parsedParams[] = $parsedParam;
}
return $parsedParams;
}
private function getFQN(NodeAbstract $node, string $nodeName): string
{
$namespace = '';
if ($node->getAttribute('parent') instanceof Namespace_ && !empty($node->getAttribute('parent')->name)) {
$namespace = '\\' . implode('\\', $node->getAttribute('parent')->name->parts) . '\\';
}
return $namespace . $nodeName;
}
}
function getPhpStormStubs(): stdClass
{
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$docFactory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
$stubs = new stdClass();
$visitor = new ASTVisitor($docFactory, $stubs);
$stubsIterator =
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(__DIR__ . '/../', FilesystemIterator::SKIP_DOTS)
);
/** @var SplFileInfo $file */
foreach ($stubsIterator as $file) {
if (strpos($file->getRealPath(), 'vendor') || strpos($file->getRealPath(), '.git') || substr(dirname($file->getRealPath(), 1), -5) === 'tests') {
continue;
}
$code = file_get_contents($file->getRealPath());
try {
$ast = $parser->parse($code);
} catch (\PhpParser\Error $error){
$error->setRawMessage($error->getRawMessage() . "\n" . $file->getRealPath());
throw $error;
}
$traverser = new NodeTraverser();
$traverser->addVisitor(new ParentConnector());
$traverser->addVisitor($visitor);
$traverser->traverse($ast);
}
foreach ($stubs->interfaces as $interface) {
$interface->parentInterfaces = $visitor->combineParentInterfaces($interface);
}
return $stubs;
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace StubTests\TestData;
use stdClass;
class MutedProblems
{
/** @var stdClass */
private $mutedProblems;
public function __construct()
{
$json = file_get_contents(__DIR__ . '/mutedProblems.json');
$this->mutedProblems = json_decode($json);
}
public function getMutedProblemsForConstant(string $constantName): array
{
/**@var stdClass $constant */
foreach ($this->mutedProblems->constants as $constant) {
if ($constant->name === $constantName) {
return $constant->problems;
}
}
return [];
}
public function getMutedProblemsForFunction(string $functionName): array
{
/**@var stdClass $function */
foreach ($this->mutedProblems->functions as $function) {
if ($function->name === $functionName) {
return $function->problems;
}
}
return [];
}
public function getMutedProblemsForClass(string $className): array
{
/**@var stdClass $class */
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->problems)) {
return $class->problems;
}
}
return [];
}
public function getMutedProblemsForMethod(string $className, $methodName): array
{
/**@var stdClass $class */
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->methods)) {
/**@var stdClass $method */
foreach ($class->methods as $method) {
if ($method->name === $methodName) {
return $method->problems;
}
}
}
}
return [];
}
public function getMutedProblemsForClassConstants($className, $constantName): array
{
/**@var stdClass $class */
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->constants)) {
/**@var stdClass $constant */
foreach ($class->constants as $constant) {
if ($constant->name === $constantName) {
return $constant->problems;
}
}
}
}
return [];
}
public function getMutedProblemsForInterface($interfaceName): array
{
/**@var stdClass $interface */
foreach ($this->mutedProblems->interfaces as $interface) {
if ($interface->name === $interfaceName && !empty($interface->problems)) {
return $interface->problems;
}
}
return [];
}
}

View File

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace StubTests\TestData\Providers;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPConst;
use StubTests\Model\PHPFunction;
use StubTests\Model\PHPInterface;
use StubTests\Parsers\PHPReflectionParser;
class ReflectionTestDataProviders
{
public static function constantProvider()
{
/**@var PHPConst $constant */
foreach (ReflectionStubsSingleton::getReflectionStubs()[PHPConst::class] as $constant) {
yield "constant {$constant->name}" => [$constant];
}
}
public static function functionProvider()
{
/**@var PHPFunction $function */
foreach (ReflectionStubsSingleton::getReflectionStubs()[PHPFunction::class] as $function) {
yield "function {$function->name}" => [$function];
}
}
public static function classProvider()
{
/**@var PHPClass $class */
foreach (ReflectionStubsSingleton::getReflectionStubs()[PHPClass::class] as $class) {
//exclude classes from PHPReflectionParser
if (strncmp($class->name, 'PHP', 3) !== 0) {
yield "class {$class->name}" => [$class];
}
}
}
public static function interfaceProvider()
{
/**@var PHPInterface $interface */
foreach (ReflectionStubsSingleton::getReflectionStubs()[PHPInterface::class] as $interface) {
yield "interface {$interface->name}" => [$interface];
}
}
}
class ReflectionStubsSingleton
{
private static $reflectionStubs;
/**
* @return array
*/
public static function getReflectionStubs(): array
{
if (self::$reflectionStubs === null) {
self::$reflectionStubs = PHPReflectionParser::getStubs();
}
return self::$reflectionStubs;
}
}

View File

@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace StubTests\TestData\Providers;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPConst;
use StubTests\Model\PHPFunction;
use StubTests\Model\PHPInterface;
use StubTests\Model\PHPMethod;
use StubTests\Parsers\StubParser;
class StubsTestDataProviders
{
public static function stubClassConstantProvider()
{
/**@var PHPClass $class */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPClass::class] as $class) {
foreach ($class->constants as $constant) {
yield "constant {$class->name}::{$constant->name}" => [$class->name, $constant];
}
}
/**@var PHPInterface $interface */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPInterface::class] as $interfaceName => $interface) {
foreach ($interface->constants as $constantName => $constant) {
yield "constant {$interfaceName}::{$constantName}" => [$interfaceName, $constant];
}
}
}
public static function stubConstantProvider()
{
/**@var PHPConst $constant */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPConst::class] as $constantName => $constant) {
yield "constant {$constantName}" => [$constant];
}
}
public static function stubFunctionProvider()
{
/**@var PHPFunction $function */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPFunction::class] as $functionName => $function) {
yield "function {$functionName}" => [$function];
}
}
public static function stubClassProvider()
{
/**@var PHPClass $class */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPClass::class] as $class) {
yield "class {$class->name}" => [$class];
}
/**@var PHPInterface $interface */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPInterface::class] as $interface) {
yield "interface {$interface->name}" => [$interface];
}
}
public static function stubMethodProvider()
{
/**@var PHPClass $class */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPClass::class] as $className => $class) {
/**@var PHPMethod $method */
foreach ($class->methods as $methodName => $method) {
yield "method {$className}::{$methodName}" => [$methodName, $method];
}
}
/**@var PHPInterface $interface */
foreach (PhpStormStubsSingleton::getPhpStormStubs()[PHPInterface::class] as $interfaceName => $interface) {
/**@var PHPMethod $method */
foreach ($interface->methods as $methodName => $method) {
yield "interface {$interfaceName}::{$methodName}" => [$methodName, $method];
}
}
}
}
class PhpStormStubsSingleton
{
private static $phpstormStubs;
public static function getPhpStormStubs(): array
{
if (self::$phpstormStubs === null) {
self::$phpstormStubs = StubParser::getPhpStormStubs();
}
return self::$phpstormStubs;
}
}

View File

@ -1,5 +1,533 @@
{
"constants": [
{
"name": "E_ALL",
"problems": [
"wrong value"
]
},
{
"name": "DEBUG_BACKTRACE_PROVIDE_OBJECT",
"problems": [
"wrong value"
]
},
{
"name": "DEBUG_BACKTRACE_IGNORE_ARGS",
"problems": [
"wrong value"
]
},
{
"name": "TRUE",
"problems": [
"wrong value"
]
},
{
"name": "FALSE",
"problems": [
"wrong value"
]
},
{
"name": "ZEND_THREAD_SAFE",
"problems": [
"wrong value"
]
},
{
"name": "ZEND_DEBUG_BUILD",
"problems": [
"wrong value"
]
},
{
"name": "NULL",
"problems": [
"wrong value"
]
},
{
"name": "PHP_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "PHP_MAJOR_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "PHP_MINOR_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "PHP_RELEASE_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "PHP_EXTRA_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "PHP_VERSION_ID",
"problems": [
"wrong value"
]
},
{
"name": "DEFAULT_INCLUDE_PATH",
"problems": [
"wrong value"
]
},
{
"name": "PEAR_INSTALL_DIR",
"problems": [
"wrong value"
]
},
{
"name": "PEAR_EXTENSION_DIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_PREFIX",
"problems": [
"wrong value"
]
},
{
"name": "PHP_BINDIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_MANDIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_LIBDIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_DATADIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_SYSCONFDIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_LOCALSTATEDIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_EXTENSION_DIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_CONFIG_FILE_PATH",
"problems": [
"wrong value"
]
},
{
"name": "PHP_CONFIG_FILE_SCAN_DIR",
"problems": [
"wrong value"
]
},
{
"name": "PHP_INT_MIN",
"problems": [
"wrong value"
]
},
{
"name": "PHP_BINARY",
"problems": [
"wrong value"
]
},
{
"name": "PHP_OUTPUT_HANDLER_CONT",
"problems": [
"wrong value"
]
},
{
"name": "PHP_OUTPUT_HANDLER_END",
"problems": [
"wrong value"
]
},
{
"name": "STDIN",
"problems": [
"wrong value"
]
},
{
"name": "STDOUT",
"problems": [
"wrong value"
]
},
{
"name": "STDERR",
"problems": [
"wrong value"
]
},
{
"name": "DATE_COOKIE",
"problems": [
"wrong value"
]
},
{
"name": "LIBXML_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "LIBXML_DOTTED_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "LIBXML_LOADED_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "LIBXML_BIGLINES",
"problems": [
"wrong value"
]
},
{
"name": "OPENSSL_VERSION_TEXT",
"problems": [
"wrong value"
]
},
{
"name": "OPENSSL_VERSION_NUMBER",
"problems": [
"wrong value"
]
},
{
"name": "OPENSSL_DEFAULT_STREAM_CIPHERS",
"problems": [
"wrong value"
]
},
{
"name": "PCRE_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_ENCODING_RAW",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_VERNUM",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_ERRNO",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_STREAM_ERROR",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_DATA_ERROR",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_BUF_ERROR",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_VERSION_ERROR",
"problems": [
"wrong value"
]
},
{
"name": "ZLIB_MEM_ERROR",
"problems": [
"wrong value"
]
},
{
"name": "CURLINFO_LASTONE",
"problems": [
"wrong value"
]
},
{
"name": "CURLM_CALL_MULTI_PERFORM",
"problems": [
"wrong value"
]
},
{
"name": "CURLAUTH_ANY",
"problems": [
"wrong value"
]
},
{
"name": "CURLAUTH_ANYSAFE",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_MAX_RECV_SPEED_LARGE",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_MAX_SEND_SPEED_LARGE",
"problems": [
"wrong value"
]
},
{
"name": "CURLSSH_AUTH_ANY",
"problems": [
"wrong value"
]
},
{
"name": "CURLSSH_AUTH_DEFAULT",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_CERTINFO",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_PROTOCOLS",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_REDIR_PROTOCOLS",
"problems": [
"wrong value"
]
},
{
"name": "CURLPROTO_ALL",
"problems": [
"wrong value"
]
},
{
"name": "CURLOPT_SAFE_UPLOAD",
"problems": [
"wrong value"
]
},
{
"name": "FILEINFO_EXTENSION",
"problems": [
"wrong value"
]
},
{
"name": "FILTER_SANITIZE_FULL_SPECIAL_CHARS",
"problems": [
"wrong value"
]
},
{
"name": "FTP_AUTORESUME",
"problems": [
"wrong value"
]
},
{
"name": "ICONV_VERSION",
"problems": [
"wrong value"
]
},
{
"name": "POSIX_RLIMIT_AS",
"problems": [
"wrong value"
]
},
{
"name": "POSIX_RLIMIT_MEMLOCK",
"problems": [
"wrong value"
]
},
{
"name": "POSIX_RLIMIT_NOFILE",
"problems": [
"wrong value"
]
},
{
"name": "POSIX_RLIMIT_NPROC",
"problems": [
"wrong value"
]
},
{
"name": "POSIX_RLIMIT_INFINITY",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv2_CLIENT",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv3_CLIENT",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv23_CLIENT",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_TLS_CLIENT",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv2_SERVER",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv3_SERVER",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_SSLv23_SERVER",
"problems": [
"wrong value"
]
},
{
"name": "STREAM_CRYPTO_METHOD_TLS_SERVER",
"problems": [
"wrong value"
]
},
{
"name": "FNM_NOESCAPE",
"problems": [
"wrong value"
]
},
{
"name": "FNM_PATHNAME",
"problems": [
"wrong value"
]
},
{
"name": "IMAGETYPE_COUNT",
"problems": [
"wrong value"
]
},
{
"name": "DNS_ALL",
"problems": [
"wrong value"
]
},
{
"name": "SODIUM_CRYPTO_BOX_SEALBYTES",
"problems": [
"wrong value"
]
},
{
"name": "SODIUM_CRYPTO_PWHASH_SCRYPTSALSA208SHA256_OPSLIMIT_INTERACTIVE",
"problems": [
"wrong value"
]
}
],
"functions": [
{

View File

@ -1,391 +1,316 @@
<?php
namespace StubTests;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\Link;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
use phpDocumentor\Reflection\DocBlock\Tags\See;
use PHPUnit\Framework\TestCase;
include __DIR__ . '/StubParser.php';
include __DIR__ . '/../vendor/autoload.php';
class ReflectionStubsSingleton
{
private static $reflectionStubs;
public static function getReflectionStubs(): stdClass
{
if (self::$reflectionStubs === null) {
$json = file_get_contents(__DIR__ . '/stub.json');
self::$reflectionStubs = json_decode($json);
}
return self::$reflectionStubs;
}
}
class PhpStormStubsSingleton
{
private static $phpstormStubs;
public static function getPhpStormStubs(): stdClass
{
if (self::$phpstormStubs === null) {
self::$phpstormStubs = getPhpStormStubs();
}
return self::$phpstormStubs;
}
}
class MutedProblems
{
/** @var stdClass */
private $mutedProblems;
public function __construct()
{
$json = file_get_contents(__DIR__ . '/mutedProblems.json');
$this->mutedProblems = json_decode($json);
}
public function getMutedProblemsForConstant(string $constantName): array
{
foreach ($this->mutedProblems->constants as $constant) {
if ($constant->name === $constantName) {
return $constant->problems;
}
}
return [];
}
public function getMutedProblemsForFunction(string $functionName): array
{
foreach ($this->mutedProblems->functions as $function) {
if ($function->name === $functionName) {
return $function->problems;
}
}
return [];
}
public function getMutedProblemsForClass(string $className): array
{
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->problems)) {
return $class->problems;
}
}
return [];
}
public function getMutedProblemsForMethod(string $className, $methodName): array
{
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->methods)) {
foreach ($class->methods as $method) {
if ($method->name === $methodName) {
return $method->problems;
}
}
}
}
return [];
}
public function getMutedProblemsForClassConstants($className, $constantName)
{
foreach ($this->mutedProblems->classes as $class) {
if ($class->name === $className && !empty($class->constants)) {
foreach ($class->constants as $constant) {
if ($constant->name === $constantName) {
return $constant->problems;
}
}
}
}
return [];
}
public function getMutedProblemsForInterface($interfaceName)
{
foreach ($this->mutedProblems->interfaces as $interface) {
if ($interface->name === $interfaceName && !empty($interface->problems)) {
return $interface->problems;
}
}
return [];
}
}
use StubTests\Model\BasePHPClass;
use StubTests\Model\PHPClass;
use StubTests\Model\PHPConst;
use StubTests\Model\PHPFunction;
use StubTests\Model\PHPInterface;
use StubTests\Model\PHPMethod;
use StubTests\TestData\MutedProblems;
use StubTests\TestData\providers\PhpStormStubsSingleton;
class TestStubs extends TestCase
{
/** @var MutedProblems */
private static $mutedProblems;
public static function setUpBeforeClass()/* The :void return type declaration that should be here would cause a BC issue */
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
self::$mutedProblems = new MutedProblems();
}
public function constantProvider()
{
foreach (ReflectionStubsSingleton::getReflectionStubs()->constants as $constant) {
yield "constant {$constant->name}" => [$constant];
}
}
/**
* @dataProvider constantProvider
* @dataProvider \StubTests\TestData\Providers\ReflectionTestDataProviders::constantProvider
* @param PHPConst $constant
*/
public function testConstants(stdClass $constant)
public function testConstants(PHPConst $constant): void
{
$constantName = $constant->name;
$constantValue = $constant->value;
$stubConstants = PhpStormStubsSingleton::getPhpStormStubs()->constants;
$stubConstants = PhpStormStubsSingleton::getPhpStormStubs()[PHPConst::class];
if (in_array('missing constant', self::$mutedProblems->getMutedProblemsForConstant($constantName), true)) {
$this->markTestSkipped('constant is excluded');
}
$this->assertArrayHasKey($constantName, $stubConstants, "Missing constant: const $constantName = $constantValue\n");
}
public function functionProvider()
{
foreach (ReflectionStubsSingleton::getReflectionStubs()->functions as $function) {
yield "function {$function->name}" => [$function];
static::markTestSkipped('constant is excluded');
}
static::assertArrayHasKey(
$constantName,
$stubConstants,
"Missing constant: const $constantName = $constantValue\n"
);
}
/**
* @dataProvider functionProvider
* @dataProvider \StubTests\TestData\Providers\ReflectionTestDataProviders::constantProvider
* @param PHPConst $constant
*/
public function testFunctions(stdClass $function)
public function testConstantsValues(PHPConst $constant): void
{
$constantName = $constant->name;
$constantValue = $constant->value;
/**@var PHPConst[] $stubConstants */
$stubConstants = PhpStormStubsSingleton::getPhpStormStubs()[PHPConst::class];
if (in_array('wrong value', self::$mutedProblems->getMutedProblemsForConstant($constantName), true)) {
static::markTestSkipped('constant is excluded');
}
static::assertEquals(
$constantValue,
$stubConstants[$constantName]->value,
"Constant value mismatch: const $constantName \n
Expected value: $constantValue but was {$stubConstants[$constantName]->value}"
);
}
/**
* @dataProvider \StubTests\TestData\Providers\ReflectionTestDataProviders::functionProvider
* @param PHPFunction $function
*/
public function testFunctions(PHPFunction $function): void
{
$functionName = $function->name;
$stubFunctions = PhpStormStubsSingleton::getPhpStormStubs()->functions;
$stubFunctions = PhpStormStubsSingleton::getPhpStormStubs()[PHPFunction::class];
$params = $this->getParameterRepresentation($function);
if (in_array('missing function', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
$this->markTestSkipped('function is excluded');
static::markTestSkipped('function is excluded');
}
$this->assertArrayHasKey($functionName, $stubFunctions, "Missing function: function $functionName($params){}");
static::assertArrayHasKey($functionName, $stubFunctions, "Missing function: function $functionName($params){}");
/**@var PHPFunction $phpstormFunction */
$phpstormFunction = $stubFunctions[$functionName];
if (!in_array('deprecated function', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
$this->assertFalse($function->is_deprecated && $phpstormFunction->is_deprecated !== true, "Function $functionName is not deprecated in stubs");
static::assertFalse(
$function->is_deprecated && $phpstormFunction->is_deprecated !== true,
"Function $functionName is not deprecated in stubs"
);
}
if (!in_array('parameter mismatch', self::$mutedProblems->getMutedProblemsForFunction($functionName), true)) {
$this->assertSameSize($function->parameters, $phpstormFunction->parameters,
"Parameter number mismatch for function $functionName. Expected: " . $this->getParameterRepresentation($function));
}
}
public function classProvider()
{
foreach (ReflectionStubsSingleton::getReflectionStubs()->classes as $class) {
//exclude classes from PHPReflectionParser
if (0 !== strncmp($class->name, 'PHP', 3)) {
yield "class {$class->name}" => [$class];
}
static::assertSameSize(
$function->parameters,
$phpstormFunction->parameters,
"Parameter number mismatch for function $functionName.
Expected: " . $this->getParameterRepresentation($function)
);
}
}
/**
* @dataProvider classProvider
* @dataProvider \StubTests\TestData\Providers\ReflectionTestDataProviders::classProvider
* @param PHPClass $class
*/
public function testClasses(stdClass $class)
public function testClasses(PHPClass $class): void
{
$className = $class->name;
$stubClasses = PhpStormStubsSingleton::getPhpStormStubs()->classes;
$stubClasses = PhpStormStubsSingleton::getPhpStormStubs()[PHPClass::class];
if (in_array('missing class', self::$mutedProblems->getMutedProblemsForClass($className), true)) {
$this->markTestSkipped('class is skipped');
static::markTestSkipped('class is skipped');
}
$this->assertArrayHasKey($className, $stubClasses, "Missing class $className: class $className {}");
static::assertArrayHasKey($className, $stubClasses, "Missing class $className: class $className {}");
/**@var PHPClass $stubClass */
$stubClass = $stubClasses[$className];
if (!in_array('wrong parent', self::$mutedProblems->getMutedProblemsForClass($className), true)) {
$this->assertEquals($class->parentClass, $stubClass->parentClass, "Class $className should extend {$class->parentClass}");
static::assertEquals(
$class->parentClass,
$stubClass->parentClass,
"Class $className should extend {$class->parentClass}"
);
}
foreach ($class->constants as $constant) {
if (!in_array('missing constant', self::$mutedProblems->getMutedProblemsForClassConstants($className, $constant->name), true)) {
$this->assertArrayHasKey($constant->name, $stubClass->constants, "Missing constant $className::{$constant->name}");
static::assertArrayHasKey(
$constant->name,
$stubClass->constants,
"Missing constant $className::{$constant->name}"
);
}
}
foreach ($class->methods as $method) {
$params = $this->getParameterRepresentation($method);
$methodName = $method->name;
if (!in_array('missing method', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
$this->assertArrayHasKey($methodName, $stubClass->methods, "Missing method $className::$methodName($params){}");
static::assertArrayHasKey(
$methodName,
$stubClass->methods,
"Missing method $className::$methodName($params){}"
);
$stubMethod = $stubClass->methods[$methodName];
if (!in_array('not final', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
$this->assertEquals($method->is_final, $stubMethod->is_final, "Method $className::$methodName final modifier is incorrect");
static::assertEquals(
$method->is_final,
$stubMethod->is_final,
"Method $className::$methodName final modifier is incorrect"
);
}
if (!in_array('not static', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
$this->assertEquals($method->is_static, $stubMethod->is_static, "Method $className::$methodName static modifier is incorrect");
static::assertEquals(
$method->is_static,
$stubMethod->is_static,
"Method $className::$methodName static modifier is incorrect"
);
}
if (!in_array('access modifiers', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
$this->assertEquals($method->access, $stubMethod->access, "Method $className::$methodName access modifier is incorrect");
static::assertEquals(
$method->access,
$stubMethod->access,
"Method $className::$methodName access modifier is incorrect"
);
}
if (!in_array('parameter mismatch', self::$mutedProblems->getMutedProblemsForMethod($className, $methodName), true)) {
$this->assertSameSize($method->parameters, $stubMethod->parameters, "Parameter number mismatch for method $className::$methodName. Expected: " . $this->getParameterRepresentation($method));
static::assertSameSize(
$method->parameters,
$stubMethod->parameters,
"Parameter number mismatch for method $className::$methodName.
Expected: " . $this->getParameterRepresentation($method)
);
}
}
}
}
public function interfaceProvider()
{
foreach (ReflectionStubsSingleton::getReflectionStubs()->interfaces as $interface) {
yield "interface {$interface->name}" => [$interface];
foreach ($class->interfaces as $interface) {
if (!in_array('wrong interface', self::$mutedProblems->getMutedProblemsForClass($className), true)) {
static::assertContains(
$interface,
$stubClass->interfaces,
"Class $className doesn't implement interface $interface"
);
}
}
}
/**
* @dataProvider interfaceProvider
* @dataProvider \StubTests\TestData\Providers\ReflectionTestDataProviders::interfaceProvider
* @param PHPInterface $interface
*/
public function testInterfaces(stdClass $interface)
public function testInterfaces(PHPInterface $interface): void
{
$interfaceName = $interface->name;
$stubInterfaces = PhpStormStubsSingleton::getPhpStormStubs()->interfaces;
/**@var PHPInterface[] $stubInterfaces */
$stubInterfaces = PhpStormStubsSingleton::getPhpStormStubs()[PHPInterface::class];
if (in_array('missing interface', self::$mutedProblems->getMutedProblemsForInterface($interfaceName), true)) {
$this->markTestSkipped('interface is skipped');
static::markTestSkipped('interface is skipped');
}
$this->assertArrayHasKey($interfaceName, $stubInterfaces, "Missing interface $interfaceName: interface $interfaceName {}");
static::assertArrayHasKey(
$interfaceName,
$stubInterfaces,
"Missing interface $interfaceName: interface $interfaceName {}"
);
$stubInterface = $stubInterfaces[$interfaceName];
if (!in_array('wrong parent', self::$mutedProblems->getMutedProblemsForInterface($interfaceName), true)) {
$this->assertEquals($stubInterface->parentInterfaces, $interface->parentInterfaces);
static::assertEquals($stubInterface->parentInterfaces, $interface->parentInterfaces);
}
foreach ($interface->constants as $constant) {
if (!in_array('missing constant', self::$mutedProblems->getMutedProblemsForClassConstants($interfaceName, $constant->name), true)) {
$this->assertArrayHasKey($constant->name, $stubInterface->constants, "Missing constant $interfaceName::{$constant->name}");
static::assertArrayHasKey(
$constant->name,
$stubInterface->constants,
"Missing constant $interfaceName::{$constant->name}"
);
}
}
foreach ($interface->methods as $method) {
$params = $this->getParameterRepresentation($method);
$methodName = $method->name;
if (!in_array('missing method', self::$mutedProblems->getMutedProblemsForMethod($interfaceName, $methodName), true)) {
$this->assertArrayHasKey($methodName, $stubInterface->methods, "Missing method $interfaceName::$methodName($params){}");
static::assertArrayHasKey(
$methodName,
$stubInterface->methods,
"Missing method $interfaceName::$methodName($params){}"
);
$stubMethod = $stubInterface->methods[$methodName];
if (!in_array('not final', self::$mutedProblems->getMutedProblemsForMethod($interfaceName, $methodName), true)) {
$this->assertEquals($method->is_final, $stubMethod->is_final, "Method $interfaceName::$methodName final modifier is incorrect");
static::assertEquals(
$method->is_final,
$stubMethod->is_final,
"Method $interfaceName::$methodName final modifier is incorrect"
);
}
if (!in_array('not static', self::$mutedProblems->getMutedProblemsForMethod($interfaceName, $methodName), true)) {
$this->assertEquals($method->is_static, $stubMethod->is_static, "Method $interfaceName::$methodName static modifier is incorrect");
static::assertEquals(
$method->is_static,
$stubMethod->is_static,
"Method $interfaceName::$methodName static modifier is incorrect"
);
}
if (!in_array('access modifiers', self::$mutedProblems->getMutedProblemsForMethod($interfaceName, $methodName), true)) {
$this->assertEquals($method->access, $stubMethod->access, "Method $interfaceName::$methodName access modifier is incorrect");
static::assertEquals(
$method->access,
$stubMethod->access,
"Method $interfaceName::$methodName access modifier is incorrect"
);
}
if (!in_array('parameter mismatch', self::$mutedProblems->getMutedProblemsForMethod($interfaceName, $methodName), true)) {
$this->assertSameSize($method->parameters, $stubMethod->parameters, "Parameter number mismatch for method $interfaceName::$methodName. Expected: " . $this->getParameterRepresentation($method));
static::assertSameSize(
$method->parameters,
$stubMethod->parameters,
"Parameter number mismatch for method $interfaceName::$methodName.
Expected: " . $this->getParameterRepresentation($method)
);
}
}
}
}
public function stubClassConstantProvider(){
foreach (PhpStormStubsSingleton::getPhpStormStubs()->classes as $className => $class) {
foreach ($class->constants as $constantName => $constant) {
yield "Constant {$className}::{$constantName}" => [$className, $constant];
}
}
foreach (PhpStormStubsSingleton::getPhpStormStubs()->interfaces as $interfaceName => $interface) {
foreach ($interface->constants as $constantName => $constant) {
yield "Constant {$interfaceName}::{$constantName}" => [$interfaceName, $constant];
}
}
}
/**
* @dataProvider stubClassConstantProvider
* @dataProvider \StubTests\TestData\Providers\StubsTestDataProviders::stubClassConstantProvider
* @param string $className
* @param PHPConst $constant
*/
public function testClassConstantsPHPDocs(string $className, stdClass $constant)
public function testClassConstantsPHPDocs(string $className, PHPConst $constant): void
{
$this->assertNull($constant->parseError, $constant->parseError ?: "");
static::assertNull($constant->parseError, $constant->parseError ?: '');
$this->checkLinks($constant, "constant $className::$constant->name");
}
public function stubConstantProvider()
{
foreach (PhpStormStubsSingleton::getPhpStormStubs()->constants as $constantName => $constant) {
yield "constant {$constantName}" => [$constant];
}
}
/**
* @dataProvider stubConstantProvider
* @dataProvider \StubTests\TestData\Providers\StubsTestDataProviders::stubConstantProvider
* @param PHPConst $constant
*/
public function testConstantsPHPDocs(stdClass $constant)
public function testConstantsPHPDocs(PHPConst $constant): void
{
$this->assertNull($constant->parseError, $constant->parseError ?: "");
static::assertNull($constant->parseError, $constant->parseError ?: '');
$this->checkLinks($constant, "function $constant->name");
}
public function stubFunctionProvider()
{
foreach (PhpStormStubsSingleton::getPhpStormStubs()->functions as $functionName => $function) {
yield "function {$functionName}" => [$function];
}
}
/**
* @dataProvider stubFunctionProvider
* @dataProvider \StubTests\TestData\Providers\StubsTestDataProviders::stubFunctionProvider
* @param PHPFunction $function
*/
public function testFunctionPHPDocs(stdClass $function)
public function testFunctionPHPDocs(PHPFunction $function): void
{
$this->assertNull($function->parseError, $function->parseError ?: "");
static::assertNull($function->parseError, $function->parseError ?: '');
$this->checkLinks($function, "function $function->name");
}
public function stubClassProvider()
{
foreach (PhpStormStubsSingleton::getPhpStormStubs()->classes as $className => $class) {
yield "class {$className}" => [$class];
}
foreach (PhpStormStubsSingleton::getPhpStormStubs()->interfaces as $interfaceName => $interface) {
yield "interface {$interfaceName}" => [$interface];
}
}
/**
* @dataProvider stubClassProvider
* @dataProvider \StubTests\TestData\Providers\StubsTestDataProviders::stubClassProvider
* @param BasePHPClass $class
*/
public function testClassesPHPDocs(stdClass $class)
public function testClassesPHPDocs(BasePHPClass $class): void
{
$this->assertNull($class->parseError, $class->parseError ?: "");
static::assertNull($class->parseError, $class->parseError ?: '');
$this->checkLinks($class, "class $class->name");
}
public function stubMethodProvider()
{
foreach (PhpStormStubsSingleton::getPhpStormStubs()->classes as $className => $class) {
foreach ($class->methods as $methodName => $method) {
yield "Method {$className}::{$methodName}" => [$methodName, $method];
}
}
foreach (PhpStormStubsSingleton::getPhpStormStubs()->interfaces as $interfaceName => $interface) {
foreach ($interface->methods as $methodName => $method) {
yield "Method {$interfaceName}::{$methodName}" => [$methodName, $method];
}
}
}
/**
* @dataProvider stubMethodProvider
* @dataProvider \StubTests\TestData\Providers\StubsTestDataProviders::stubMethodProvider
* @param string $methodName
* @param PHPMethod $method
*/
public function testMethodsPHPDocs(string $methodName, stdClass $method)
public function testMethodsPHPDocs(string $methodName, PHPMethod $method): void
{
if ($methodName === "__construct") {
$this->assertNull($method->returnTag, "@return tag for __construct should be omitted");
if ($methodName === '__construct') {
static::assertNull($method->returnTag, '@return tag for __construct should be omitted');
}
$this->assertNull($method->parseError, $method->parseError ?: "");
static::assertNull($method->parseError, $method->parseError ?: '');
$this->checkLinks($method, "method $methodName");
}
private function getParameterRepresentation(stdClass $function): string
private function getParameterRepresentation(PHPFunction $function): string
{
$result = '';
foreach ($function->parameters as $parameter) {
@ -401,22 +326,27 @@ class TestStubs extends TestCase
$result .= '$' . $parameter->name . ', ';
}
$result = rtrim($result, ', ');
return $result;
}
private function checkLinks($element, $elementName): void
{
/**@var Tag $link */
foreach ($element->links as $link) {
if ($link instanceof Link) {
$this->assertStringStartsWith('https', $link->getLink(), "In $elementName @link doesn't start with https");
static::assertStringStartsWith(
'https',
$link->getLink(),
"In $elementName @link doesn't start with https"
);
}
}
/**@var Tag $see */
foreach ($element->see as $see) {
if ($see instanceof See && $see->getReference() instanceof Url) {
if (strpos($see, 'http') === 0) {
$this->assertStringStartsWith('https', $see, "In $elementName @see doesn't start with https");
}
if ($see instanceof See && $see->getReference() instanceof Url && strncmp($see, 'http', 4) === 0) {
static::assertStringStartsWith('https', $see, "In $elementName @see doesn't start with https");
}
}
}
}
}