Merge pull request #465 from isfedorov/master
New architecture of test suite
This commit is contained in:
commit
b643efdde0
|
@ -11,9 +11,6 @@ rest-client.private.env.json
|
|||
.idea/workspace.xml
|
||||
.idea/shelf
|
||||
|
||||
#Test temporary file
|
||||
tests/stub.json
|
||||
|
||||
#Composer
|
||||
vendor
|
||||
composer.lock
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 = [];
|
||||
}
|
|
@ -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, "\\");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 [];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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": [
|
||||
{
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue