2016-12-12 05:41:11 +01:00
|
|
|
<?php
|
|
|
|
namespace Psalm\Tests;
|
|
|
|
|
2019-06-26 22:52:29 +02:00
|
|
|
use function class_exists;
|
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class ClassTest extends TestCase
|
2016-12-12 05:41:11 +01:00
|
|
|
{
|
2018-11-06 03:57:36 +01:00
|
|
|
use Traits\InvalidCodeAnalysisTestTrait;
|
|
|
|
use Traits\ValidCodeAnalysisTestTrait;
|
2017-04-25 05:45:02 +02:00
|
|
|
|
2018-07-17 02:52:58 +02:00
|
|
|
/**
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function testExtendsMysqli()
|
|
|
|
{
|
|
|
|
if (class_exists('mysqli') === false) {
|
|
|
|
$this->markTestSkipped('Cannot run test, base class "mysqli" does not exist!');
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->addFile(
|
|
|
|
'somefile.php',
|
|
|
|
'<?php
|
|
|
|
class db extends mysqli {
|
|
|
|
public function close()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function prepare(string $sql)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function commit(?int $flags = null, ?string $name = null)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function real_escape_string(string $string)
|
|
|
|
{
|
|
|
|
return "escaped";
|
|
|
|
}
|
|
|
|
}'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,assertions?:array<string,string>,error_levels?:string[]}>
|
2017-04-25 05:45:02 +02:00
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
public function providerValidCodeParse()
|
2017-04-25 05:45:02 +02:00
|
|
|
{
|
|
|
|
return [
|
|
|
|
'overrideProtectedAccessLevelToPublic' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
protected function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B extends A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'reflectedParents' => [
|
|
|
|
'<?php
|
|
|
|
$e = rand(0, 10)
|
|
|
|
? new RuntimeException("m")
|
|
|
|
: null;
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
if ($e instanceof Exception) {
|
|
|
|
echo "good";
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'namespacedAliasedClassCall' => [
|
|
|
|
'<?php
|
|
|
|
namespace Aye {
|
|
|
|
class Foo {}
|
|
|
|
}
|
|
|
|
namespace Bee {
|
|
|
|
use Aye as A;
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
new A\Foo();
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'abstractExtendsAbstract' => [
|
|
|
|
'<?php
|
|
|
|
abstract class A {
|
|
|
|
/** @return void */
|
|
|
|
abstract public function foo();
|
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
abstract class B extends A {
|
|
|
|
/** @return void */
|
|
|
|
public function bar() {
|
|
|
|
$this->foo();
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'missingParentWithFunction' => [
|
|
|
|
'<?php
|
|
|
|
class B extends C {
|
|
|
|
public function fooA() { }
|
|
|
|
}',
|
|
|
|
'assertions' => [],
|
|
|
|
'error_levels' => [
|
|
|
|
'UndefinedClass',
|
2017-05-27 02:05:57 +02:00
|
|
|
'MissingReturnType',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'subclassWithSimplerArg' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class E1 {
|
|
|
|
/**
|
|
|
|
* @param A|B|null $a
|
|
|
|
*/
|
|
|
|
public function __construct($a) {
|
|
|
|
}
|
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class E2 extends E1 {
|
|
|
|
/**
|
|
|
|
* @param A|null $a
|
|
|
|
*/
|
|
|
|
public function __construct($a) {
|
|
|
|
parent::__construct($a);
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2019-02-07 18:25:57 +01:00
|
|
|
'subclassOfInvalidArgumentExceptionWithSimplerArg' => [
|
2017-04-25 05:45:02 +02:00
|
|
|
'<?php
|
|
|
|
class A extends InvalidArgumentException {
|
|
|
|
/**
|
|
|
|
* @param string $message
|
|
|
|
* @param int $code
|
|
|
|
* @param Throwable|null $previous_exception
|
|
|
|
*/
|
|
|
|
public function __construct($message, $code, $previous_exception) {
|
|
|
|
parent::__construct($message, $code, $previous_exception);
|
|
|
|
}
|
2017-05-27 02:05:57 +02:00
|
|
|
}',
|
|
|
|
],
|
2018-04-04 05:14:23 +02:00
|
|
|
'classStringInstantiation' => [
|
|
|
|
'<?php
|
|
|
|
class Foo {}
|
|
|
|
class Bar {}
|
|
|
|
$class = mt_rand(0, 1) === 1 ? Foo::class : Bar::class;
|
|
|
|
$object = new $class();',
|
|
|
|
'assertions' => [
|
2019-10-17 07:14:33 +02:00
|
|
|
'$object' => 'Bar|Foo',
|
2018-04-04 05:14:23 +02:00
|
|
|
],
|
|
|
|
],
|
|
|
|
'instantiateClassAndIsA' => [
|
|
|
|
'<?php
|
|
|
|
class Foo {
|
|
|
|
public function bar() : void{}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string|null
|
|
|
|
*/
|
|
|
|
function getFooClass() {
|
|
|
|
return mt_rand(0, 1) === 1 ? Foo::class : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$foo_class = getFooClass();
|
|
|
|
|
|
|
|
if (is_string($foo_class) && is_a($foo_class, Foo::class, true)) {
|
|
|
|
$foo = new $foo_class();
|
|
|
|
$foo->bar();
|
|
|
|
}',
|
|
|
|
],
|
2018-04-05 18:03:36 +02:00
|
|
|
'returnStringAfterIsACheckWithClassConst' => [
|
|
|
|
'<?php
|
|
|
|
class Foo{}
|
|
|
|
function bar(string $maybeBaz) : string {
|
|
|
|
if (!is_a($maybeBaz, Foo::class, true)) {
|
|
|
|
throw new Exception("not Foo");
|
|
|
|
}
|
|
|
|
return $maybeBaz;
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'returnStringAfterIsACheckWithString' => [
|
|
|
|
'<?php
|
|
|
|
class Foo{}
|
|
|
|
function bar(string $maybeBaz) : string {
|
|
|
|
if (!is_a($maybeBaz, "Foo", true)) {
|
|
|
|
throw new Exception("not Foo");
|
|
|
|
}
|
|
|
|
return $maybeBaz;
|
|
|
|
}',
|
|
|
|
],
|
2018-06-06 21:32:03 +02:00
|
|
|
'assignAnonymousClassToArray' => [
|
|
|
|
'<?php
|
|
|
|
/**
|
|
|
|
* @param array<string, object> $array
|
|
|
|
*/
|
|
|
|
function foo(array $array, string $key) : void {
|
|
|
|
foreach ($array as $i => $item) {
|
|
|
|
$array[$key] = new class() {};
|
|
|
|
|
|
|
|
if ($array[$i] === $array[$key]) {}
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-07-13 15:52:15 +02:00
|
|
|
'getClassSelfClass' => [
|
|
|
|
'<?php
|
|
|
|
class C {
|
|
|
|
public function work(object $obj): string {
|
|
|
|
if (get_class($obj) === self::class) {
|
|
|
|
return $obj->baz();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
public function baz(): string {
|
|
|
|
return "baz";
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-07-18 04:50:30 +02:00
|
|
|
'staticClassComparison' => [
|
|
|
|
'<?php
|
|
|
|
class C {
|
|
|
|
public function foo1(): string {
|
|
|
|
if (static::class === D::class) {
|
|
|
|
return $this->baz();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function foo2(): string {
|
|
|
|
if (static::class === D::class) {
|
|
|
|
return static::bat();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class D extends C {
|
|
|
|
public function baz(): string {
|
|
|
|
return "baz";
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function bat(): string {
|
|
|
|
return "baz";
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
|
|
|
'isAStaticClass' => [
|
|
|
|
'<?php
|
|
|
|
class C {
|
|
|
|
public function foo1(): string {
|
|
|
|
if (is_a(static::class, D::class, true)) {
|
|
|
|
return $this->baz();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function foo2(): string {
|
|
|
|
if (is_a(static::class, D::class, true)) {
|
|
|
|
return static::bat();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class D extends C {
|
|
|
|
public function baz(): string {
|
|
|
|
return "baz";
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function bat(): string {
|
|
|
|
return "baz";
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-07-17 04:48:53 +02:00
|
|
|
'typedMagicCall' => [
|
|
|
|
'<?php
|
|
|
|
class B {
|
|
|
|
public function __call(string $methodName, array $args) : string {
|
|
|
|
return __METHOD__;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class A {
|
|
|
|
public function __call(string $methodName, array $args) : B {
|
|
|
|
return new B;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$a = (new A)->zugzug();
|
|
|
|
$b = (new A)->bar()->baz();',
|
|
|
|
'assertions' => [
|
|
|
|
'$a' => 'B',
|
|
|
|
'$b' => 'string',
|
|
|
|
],
|
|
|
|
],
|
2018-11-11 01:05:51 +01:00
|
|
|
'abstractCallToInterfaceMethod' => [
|
|
|
|
'<?php
|
|
|
|
interface I {
|
|
|
|
public function fooBar(): array;
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class A implements I
|
|
|
|
{
|
|
|
|
public function g(): array {
|
|
|
|
return $this->fooBar();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2018-12-30 18:28:15 +01:00
|
|
|
'noCrashWhenIgnoringUndefinedClass' => [
|
|
|
|
'<?php
|
|
|
|
class A extends B {
|
|
|
|
public function foo() {
|
|
|
|
parent::bar();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
'assertions' => [],
|
|
|
|
'error_levels' => [
|
2019-03-23 19:27:54 +01:00
|
|
|
'UndefinedClass',
|
2018-12-30 18:28:15 +01:00
|
|
|
],
|
|
|
|
],
|
2019-01-05 18:59:12 +01:00
|
|
|
'noCrashWhenIgnoringUndefinedParam' => [
|
|
|
|
'<?php
|
|
|
|
function bar(iterable $_i) : void {}
|
|
|
|
function foo(C $c) : void {
|
|
|
|
bar($c);
|
|
|
|
}',
|
|
|
|
'assertions' => [],
|
|
|
|
'error_levels' => [
|
|
|
|
'UndefinedClass',
|
|
|
|
'InvalidArgument',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'noCrashWhenIgnoringUndefinedReturnIterableArg' => [
|
|
|
|
'<?php
|
|
|
|
function bar(iterable $_i) : void {}
|
|
|
|
function foo() : D {
|
|
|
|
return new D();
|
|
|
|
}
|
|
|
|
bar(foo());',
|
|
|
|
'assertions' => [],
|
|
|
|
'error_levels' => [
|
|
|
|
'UndefinedClass',
|
|
|
|
'MixedInferredReturnType',
|
|
|
|
'InvalidArgument',
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'noCrashWhenIgnoringUndefinedReturnClassArg' => [
|
|
|
|
'<?php
|
|
|
|
class Exists {}
|
|
|
|
function bar(Exists $_i) : void {}
|
|
|
|
function foo() : D {
|
|
|
|
return new D();
|
|
|
|
}
|
|
|
|
bar(foo());',
|
|
|
|
'assertions' => [],
|
|
|
|
'error_levels' => [
|
|
|
|
'UndefinedClass',
|
|
|
|
'MixedInferredReturnType',
|
|
|
|
'InvalidArgument',
|
|
|
|
],
|
|
|
|
],
|
2019-01-04 18:28:00 +01:00
|
|
|
'allowAbstractInstantiationOnPossibleChild' => [
|
|
|
|
'<?php
|
|
|
|
abstract class A {}
|
|
|
|
|
|
|
|
function foo(string $a_class) : void {
|
|
|
|
if (is_a($a_class, A::class, true)) {
|
|
|
|
new $a_class();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2019-05-19 21:56:04 +02:00
|
|
|
'interfaceExistsCreatesClassString' => [
|
|
|
|
'<?php
|
|
|
|
function funB(string $className) : ?ReflectionClass {
|
|
|
|
if (class_exists($className)) {
|
|
|
|
return new ReflectionClass($className);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interface_exists($className)) {
|
|
|
|
return new ReflectionClass($className);
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}',
|
|
|
|
],
|
2019-05-21 02:57:59 +02:00
|
|
|
'allowClassExistsAndInterfaceExists' => [
|
|
|
|
'<?php
|
|
|
|
function foo(string $s) : void {
|
|
|
|
if (class_exists($s) || interface_exists($s)) {}
|
2019-07-05 22:24:00 +02:00
|
|
|
}',
|
2019-05-21 02:57:59 +02:00
|
|
|
],
|
2019-05-26 22:26:47 +02:00
|
|
|
'classExistsWithFalseArg' => [
|
2019-05-26 19:16:44 +02:00
|
|
|
'<?php
|
|
|
|
/**
|
|
|
|
* @param class-string $class
|
2019-05-26 19:34:07 +02:00
|
|
|
* @return string
|
2019-05-26 19:16:44 +02:00
|
|
|
*/
|
|
|
|
function autoload(string $class) : string {
|
|
|
|
if (class_exists($class, false)) {
|
|
|
|
return $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $class;
|
2019-07-05 22:24:00 +02:00
|
|
|
}',
|
2019-05-26 19:16:44 +02:00
|
|
|
],
|
2019-05-28 19:16:09 +02:00
|
|
|
'classExistsWithFalseArgInside' => [
|
|
|
|
'<?php
|
|
|
|
function foo(string $s) : void {
|
|
|
|
if (class_exists($s, false)) {
|
|
|
|
/** @psalm-suppress MixedMethodCall */
|
|
|
|
new $s();
|
|
|
|
}
|
2019-07-05 22:24:00 +02:00
|
|
|
}',
|
2019-05-28 19:16:09 +02:00
|
|
|
],
|
2019-07-04 21:46:11 +02:00
|
|
|
'classAliasOnNonexistantClass' => [
|
|
|
|
'<?php
|
|
|
|
if (!class_exists(\PHPUnit\Framework\TestCase::class)) {
|
|
|
|
/** @psalm-suppress UndefinedClass */
|
|
|
|
class_alias(\PHPUnit_Framework_TestCase::class, \PHPUnit\Framework\TestCase::class);
|
|
|
|
}
|
|
|
|
|
|
|
|
class T extends \PHPUnit\Framework\TestCase {
|
|
|
|
|
|
|
|
}',
|
|
|
|
[],
|
2019-07-05 22:24:00 +02:00
|
|
|
['PropertyNotSetInConstructor'],
|
2019-07-04 21:46:11 +02:00
|
|
|
],
|
2019-08-15 16:17:27 +02:00
|
|
|
'classAliasNoException' => [
|
|
|
|
'<?php
|
|
|
|
class_alias("Bar\F1", "Bar\F2");
|
|
|
|
|
|
|
|
namespace Bar {
|
|
|
|
class F1 {
|
|
|
|
public static function baz() : void {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
Bar\F2::baz();
|
|
|
|
}',
|
|
|
|
],
|
2019-10-01 04:15:48 +02:00
|
|
|
'classAliasEcho' => [
|
|
|
|
'<?php
|
|
|
|
class A { }
|
|
|
|
class_alias("A", "A_A");
|
|
|
|
|
|
|
|
echo A_A::class;'
|
|
|
|
],
|
2019-11-29 07:21:00 +01:00
|
|
|
'classAliasTrait' => [
|
|
|
|
'<?php
|
|
|
|
trait FeatureV1 {}
|
|
|
|
class_alias(FeatureV1::class, Feature::class);
|
|
|
|
class App { use Feature; }
|
|
|
|
'
|
2020-01-01 23:23:13 +01:00
|
|
|
],
|
|
|
|
'classAliasParent' => [
|
|
|
|
'<?php
|
|
|
|
class NewA {}
|
|
|
|
class_alias(NewA::class, OldA::class);
|
|
|
|
function action(NewA $_m): void {}
|
|
|
|
|
|
|
|
class OldAChild extends OldA {}
|
|
|
|
action(new OldA());
|
|
|
|
action(new OldAChild());'
|
|
|
|
],
|
2020-02-12 04:34:34 +01:00
|
|
|
'resourceAndNumericSoftlyReserved' => [
|
|
|
|
'<?php
|
|
|
|
namespace Foo {
|
|
|
|
class Resource {}
|
|
|
|
class Numeric {}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace Bar {
|
|
|
|
use \Foo\Resource;
|
|
|
|
use \Foo\Numeric;
|
|
|
|
|
|
|
|
new \Foo\Resource();
|
|
|
|
new \Foo\Numeric();
|
|
|
|
|
|
|
|
new Resource();
|
|
|
|
new Numeric();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Resource $r
|
|
|
|
* @param Numeric $n
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
function foo(Resource $r, Numeric $n) : void {}
|
|
|
|
}'
|
|
|
|
],
|
2020-02-13 23:44:16 +01:00
|
|
|
'inheritInterfaceFromParent' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class AChild extends A {}
|
|
|
|
|
|
|
|
interface IParent {
|
|
|
|
public function get(): A;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IChild extends IParent {
|
|
|
|
/**
|
|
|
|
* @psalm-return AChild
|
|
|
|
*/
|
|
|
|
public function get(): A;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Concrete implements IChild {
|
|
|
|
public function get(): A {
|
|
|
|
return new AChild;
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2020-02-19 01:46:05 +01:00
|
|
|
'noErrorsAfterClassExists' => [
|
|
|
|
'<?php
|
|
|
|
if (class_exists(A::class)) {
|
|
|
|
if (method_exists(A::class, "method")) {
|
|
|
|
echo A::method();
|
|
|
|
}
|
|
|
|
|
|
|
|
echo A::class;
|
|
|
|
/** @psalm-suppress MixedArgument */
|
|
|
|
echo A::SOME_CONST;
|
|
|
|
}'
|
|
|
|
],
|
2020-03-09 19:24:19 +01:00
|
|
|
'noCrashOnClassExists' => [
|
|
|
|
'<?php
|
|
|
|
if (!class_exists(ReflectionGenerator::class)) {
|
|
|
|
class ReflectionGenerator {
|
|
|
|
private $prop;
|
|
|
|
}
|
|
|
|
}',
|
2020-03-11 22:41:05 +01:00
|
|
|
],
|
|
|
|
'extendException' => [
|
|
|
|
'<?php
|
|
|
|
class ME extends Exception {
|
|
|
|
/** @var string */
|
|
|
|
protected $message = "hello";
|
|
|
|
}',
|
2020-03-24 23:00:20 +01:00
|
|
|
],
|
2020-03-24 23:14:10 +01:00
|
|
|
'allowFinalReturnerForStatic' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
/** @return static */
|
|
|
|
public static function getInstance() {
|
|
|
|
return new static();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final class AChild extends A {
|
|
|
|
public static function getInstance() {
|
|
|
|
return new AChild();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-01 21:55:20 +01:00
|
|
|
* @return iterable<string,array{string,error_message:string,2?:string[],3?:bool,4?:string}>
|
2017-04-25 05:45:02 +02:00
|
|
|
*/
|
2018-11-06 03:57:36 +01:00
|
|
|
public function providerInvalidCodeParse()
|
2017-04-25 05:45:02 +02:00
|
|
|
{
|
|
|
|
return [
|
|
|
|
'undefinedClass' => [
|
|
|
|
'<?php
|
|
|
|
(new Foo());',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'UndefinedClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'wrongCaseClass' => [
|
|
|
|
'<?php
|
|
|
|
class Foo {}
|
|
|
|
(new foo());',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'InvalidClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2017-10-13 01:46:00 +02:00
|
|
|
'wrongCaseClassWithCall' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
needsA(new A);
|
2018-01-11 21:50:45 +01:00
|
|
|
function needsA(a $x): void {}',
|
2017-10-13 01:46:00 +02:00
|
|
|
'error_message' => 'InvalidClass',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
'invalidThisFetch' => [
|
|
|
|
'<?php
|
|
|
|
echo $this;',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'InvalidScope',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'invalidThisArgument' => [
|
|
|
|
'<?php
|
|
|
|
$this = "hello";',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'InvalidScope',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'undefinedConstant' => [
|
|
|
|
'<?php
|
|
|
|
echo HELLO;',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'UndefinedConstant',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'undefinedClassConstant' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
echo A::HELLO;',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'UndefinedConstant',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2018-03-04 18:24:50 +01:00
|
|
|
'overridePublicAccessLevelToPrivate' => [
|
2017-04-25 05:45:02 +02:00
|
|
|
'<?php
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B extends A {
|
2018-01-11 21:50:45 +01:00
|
|
|
private function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'OverriddenMethodAccess',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'overridePublicAccessLevelToProtected' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
public function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B extends A {
|
2018-01-11 21:50:45 +01:00
|
|
|
protected function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'OverriddenMethodAccess',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'overrideProtectedAccessLevelToPrivate' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
2018-01-11 21:50:45 +01:00
|
|
|
protected function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2017-04-25 05:45:02 +02:00
|
|
|
class B extends A {
|
2018-01-11 21:50:45 +01:00
|
|
|
private function fooFoo(): void {}
|
2017-04-25 05:45:02 +02:00
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'OverriddenMethodAccess',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2018-03-04 18:24:50 +01:00
|
|
|
'overridePublicPropertyAccessLevelToPrivate' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
/** @var string|null */
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B extends A {
|
|
|
|
/** @var string|null */
|
|
|
|
private $foo;
|
|
|
|
}',
|
|
|
|
'error_message' => 'OverriddenPropertyAccess',
|
|
|
|
],
|
|
|
|
'overridePublicPropertyAccessLevelToProtected' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
/** @var string|null */
|
|
|
|
public $foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B extends A {
|
|
|
|
/** @var string|null */
|
|
|
|
protected $foo;
|
|
|
|
}',
|
|
|
|
'error_message' => 'OverriddenPropertyAccess',
|
|
|
|
],
|
|
|
|
'overrideProtectedPropertyAccessLevelToPrivate' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
/** @var string|null */
|
|
|
|
protected $foo;
|
|
|
|
}
|
|
|
|
|
|
|
|
class B extends A {
|
|
|
|
/** @var string|null */
|
|
|
|
private $foo;
|
|
|
|
}',
|
|
|
|
'error_message' => 'OverriddenPropertyAccess',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
'classRedefinition' => [
|
|
|
|
'<?php
|
|
|
|
class Foo {}
|
|
|
|
class Foo {}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'DuplicateClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'classRedefinitionInNamespace' => [
|
|
|
|
'<?php
|
|
|
|
namespace Aye {
|
|
|
|
class Foo {}
|
|
|
|
class Foo {}
|
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'DuplicateClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'classRedefinitionInSeparateNamespace' => [
|
|
|
|
'<?php
|
|
|
|
namespace Aye {
|
|
|
|
class Foo {}
|
|
|
|
}
|
|
|
|
namespace Aye {
|
|
|
|
class Foo {}
|
|
|
|
}',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'DuplicateClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
|
|
|
'abstractClassInstantiation' => [
|
|
|
|
'<?php
|
|
|
|
abstract class A {}
|
|
|
|
new A();',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'AbstractInstantiation',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2017-06-30 16:24:47 +02:00
|
|
|
'abstractClassMethod' => [
|
|
|
|
'<?php
|
2018-07-22 02:50:42 +02:00
|
|
|
abstract class A {
|
2019-07-29 01:44:36 +02:00
|
|
|
abstract public function foo() : void;
|
2018-07-22 02:50:42 +02:00
|
|
|
}
|
2017-06-30 16:24:47 +02:00
|
|
|
|
2018-07-22 02:50:42 +02:00
|
|
|
class B extends A { }',
|
|
|
|
'error_message' => 'UnimplementedAbstractMethod',
|
|
|
|
],
|
|
|
|
'abstractReflectedClassMethod' => [
|
|
|
|
'<?php
|
|
|
|
class DedupeIterator extends FilterIterator {
|
|
|
|
public function __construct(Iterator $i) {
|
|
|
|
parent::__construct($i);
|
|
|
|
}
|
|
|
|
}',
|
2017-06-30 16:24:47 +02:00
|
|
|
'error_message' => 'UnimplementedAbstractMethod',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
'missingParent' => [
|
|
|
|
'<?php
|
|
|
|
class A extends B { }',
|
2017-05-27 02:05:57 +02:00
|
|
|
'error_message' => 'UndefinedClass',
|
2017-04-25 05:45:02 +02:00
|
|
|
],
|
2017-12-07 21:50:25 +01:00
|
|
|
'lessSpecificReturnStatement' => [
|
2017-04-25 05:45:02 +02:00
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
class B extends A {}
|
2017-06-29 16:22:49 +02:00
|
|
|
|
2018-01-11 21:50:45 +01:00
|
|
|
function foo(A $a): B {
|
2017-04-25 05:45:02 +02:00
|
|
|
return $a;
|
|
|
|
}',
|
2017-12-07 21:50:25 +01:00
|
|
|
'error_message' => 'LessSpecificReturnStatement',
|
2017-05-27 02:05:57 +02:00
|
|
|
],
|
2017-12-22 18:56:59 +01:00
|
|
|
'circularReference' => [
|
|
|
|
'<?php
|
|
|
|
class A extends A {}',
|
|
|
|
'error_message' => 'CircularReference',
|
|
|
|
],
|
2019-01-04 18:28:00 +01:00
|
|
|
'preventAbstractInstantiationDefiniteClasss' => [
|
|
|
|
'<?php
|
|
|
|
abstract class A {}
|
|
|
|
|
|
|
|
function foo(string $a_class) : void {
|
|
|
|
if ($a_class === A::class) {
|
|
|
|
new $a_class();
|
|
|
|
}
|
|
|
|
}',
|
|
|
|
'error_message' => 'AbstractInstantiation',
|
|
|
|
],
|
2019-03-07 17:16:40 +01:00
|
|
|
'preventExtendingInterface' => [
|
|
|
|
'<?php
|
|
|
|
interface Foo {}
|
|
|
|
|
|
|
|
class Bar extends Foo {}',
|
|
|
|
'error_message' => 'UndefinedClass',
|
|
|
|
],
|
|
|
|
'preventImplementingClass' => [
|
|
|
|
'<?php
|
|
|
|
class Foo {}
|
|
|
|
|
|
|
|
class Bar implements Foo {}',
|
|
|
|
'error_message' => 'UndefinedInterface',
|
|
|
|
],
|
2019-07-18 02:33:44 +02:00
|
|
|
'classAliasAlreadyDefinedClass' => [
|
|
|
|
'<?php
|
|
|
|
class A {}
|
|
|
|
|
|
|
|
class B {}
|
|
|
|
|
|
|
|
if (false) {
|
|
|
|
class_alias(A::class, B::class);
|
|
|
|
}
|
|
|
|
|
|
|
|
function foo(A $a, B $b) : void {
|
|
|
|
if ($a === $b) {}
|
|
|
|
}',
|
|
|
|
'error_message' => 'TypeDoesNotContainType',
|
|
|
|
],
|
2020-03-11 02:26:02 +01:00
|
|
|
'cannotOverrideFinalType' => [
|
|
|
|
'<?php
|
|
|
|
class P {
|
|
|
|
public final function f() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
class C extends P {
|
|
|
|
public function f() : void {}
|
|
|
|
}',
|
|
|
|
'error_message' => 'MethodSignatureMismatch',
|
|
|
|
],
|
2020-03-24 23:00:20 +01:00
|
|
|
'preventFinalOverriding' => [
|
|
|
|
'<?php
|
|
|
|
class A {
|
|
|
|
/** @return static */
|
|
|
|
public static function getInstance() {
|
|
|
|
return new static();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class AChild extends A {
|
|
|
|
public static function getInstance() {
|
|
|
|
return new AChild();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class AGrandChild extends AChild {
|
|
|
|
public function foo() : void {}
|
|
|
|
}
|
|
|
|
|
|
|
|
AGrandChild::getInstance()->foo();',
|
|
|
|
'error_message' => 'LessSpecificReturnStatement',
|
|
|
|
],
|
2017-04-25 05:45:02 +02:00
|
|
|
];
|
2017-03-30 17:44:38 +02:00
|
|
|
}
|
2016-12-12 05:41:11 +01:00
|
|
|
}
|