file_provider = new FakeFileProvider();
}
private function getProjectAnalyzerWithConfig(Config $config): ProjectAnalyzer
{
$project_analyzer = new ProjectAnalyzer(
$config,
new Providers(
$this->file_provider,
new FakeParserCacheProvider()
)
);
$project_analyzer->setPhpVersion('7.4', 'tests');
$config->setIncludeCollector(new IncludeCollector());
$config->visitComposerAutoloadFiles($project_analyzer, null);
return $project_analyzer;
}
public function testNonexistentStubFile(): void
{
$this->expectException(ConfigException::class);
$this->expectExceptionMessage('Cannot resolve stubfile path');
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
Config::loadFromXML(
dirname(__DIR__),
'
'
)
);
}
public function testStubFileClass(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo(5, "hello");
$c = \SystemClass::bar(5, "hello");
echo \SystemClass::HELLO;'
);
$this->analyzeFile($file_path, new Context());
}
/**
* @psalm-pure
*/
private function getOperatingSystemStyledPath(string $file): string
{
return implode(DIRECTORY_SEPARATOR, explode('/', $file));
}
public function testLoadStubFileWithRelativePath(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$path = $this->getOperatingSystemStyledPath('tests/fixtures/stubs/systemclass.phpstub');
$stub_files = $this->project_analyzer->getConfig()->getStubFiles();
$this->assertStringContainsString($path, reset($stub_files));
}
public function testLoadStubFileWithAbsolutePath(): void
{
$runDir = dirname(__DIR__);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
$runDir,
'
'
)
);
$path = $this->getOperatingSystemStyledPath('tests/fixtures/stubs/systemclass.phpstub');
$stub_files = $this->project_analyzer->getConfig()->getStubFiles();
$this->assertStringContainsString($path, reset($stub_files));
}
public function testStubFileConstant(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubFileParentClass(): void
{
$this->expectException(CodeException::class);
$this->expectExceptionMessage('ImplementedParamTypeMismatch');
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubFileCircularReference(): void
{
$this->expectException(CodeException::class);
$this->expectExceptionMessage('CircularReference');
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testPhpStormMetaParsingFile(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'creAte("object");
$a2 = (new \Ns\MyClass)->creaTe("exception");
$b1 = \Create("object");
$b2 = \cReate("exception");
$e2 = \creAte(\LogicException::class);
$c1 = (new \Ns\MyClass)->foo(5);
$c2 = (new \Ns\MyClass)->bar(["hello"]);
$d1 = \foO(5);
$d2 = \baR(["hello"]);
}'
);
$context = new Context();
$this->analyzeFile($file_path, $context);
}
public function testNamespacedStubClass(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo(5, "hello");
$c = Foo\SystemClass::bar(5, "hello");
echo Foo\BAR;'
);
$this->analyzeFile($file_path, new Context());
}
public function testStubRegularFunction(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubVariadicFunction(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubVariadicFunctionWrongArgType(): void
{
$this->expectExceptionMessage('InvalidScalarArgument');
$this->expectException(CodeException::class);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testUserVariadicWithFalseVariadic(): void
{
$this->expectExceptionMessage('TooManyArguments');
$this->expectException(CodeException::class);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
/**
* @runInSeparateProcess
*/
public function testPolyfilledFunction(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
/**
* @runInSeparateProcess
*/
public function testConditionalConstantDefined(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
/**
* @runInSeparateProcess
*/
public function testClassAlias(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo;
echo $a->bar("hello");
function f(): A {
return new A;
}
function getAliased(): B {
return f();
}
$d = new D();
D::bat();
$d::bat();
class E implements IAlias {}'
);
$this->analyzeFile($file_path, new Context());
}
public function testStubFunctionWithFunctionExists(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testNamespacedStubFunctionWithFunctionExists(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testNoStubFunction(): void
{
$this->expectExceptionMessage('UndefinedFunction - /src/somefile.php:2:22 - Function barBar does not exist');
$this->expectException(CodeException::class);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testNamespacedStubFunction(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testConditionalNamespacedStubFunction(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testConditionallyExtendingInterface(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'getMessage();
}
function bar(I6 $d) : void {
$d->getMessage();
}
function bat(I7 $d) : void {
$d->getMessage();
}
function baz(I8 $d) : void {
$d->getMessage();
}'
);
$this->analyzeFile($file_path, new Context());
}
public function testStubFileWithExistingClassDefinition(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
/** @return iterable */
public function versionDependentStubsProvider(): iterable
{
yield '7.0' => [
'7.0',
'something("zzz");'
];
yield '8.0' => [
'8.0',
'something();'
];
}
/** @dataProvider versionDependentStubsProvider */
public function testVersionDependentStubs(string $php_version, string $code): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$this->project_analyzer->setPhpVersion($php_version, 'tests');
$file_path = getcwd() . '/src/somefile.php';
$this->addFile($file_path, $code);
$this->analyzeFile($file_path, new Context());
}
public function testStubFileWithPartialClassDefinitionWithMoreMethods(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo(A::class);
(new PartiallyStubbedClass())->bar(5);'
);
$this->analyzeFile($file_path, new Context());
}
public function testExtendOnlyStubbedClass(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo(A::class);'
);
$this->analyzeFile($file_path, new Context());
}
public function testStubFileWithExtendedStubbedClass(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubFileWithPartialClassDefinitionWithCoercion(): void
{
$this->expectExceptionMessage('TypeCoercion');
$this->expectException(CodeException::class);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'foo("dasda");'
);
$this->analyzeFile($file_path, new Context());
}
public function testStubFileWithPartialClassDefinitionGeneralReturnType(): void
{
$this->expectExceptionMessage('InvalidReturnStatement');
$this->expectException(CodeException::class);
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testStubFileWithTemplatedClassDefinitionAndMagicMethodOverride(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context());
}
public function testInheritedMethodUsedInStub(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$this->project_analyzer->getCodebase()->reportUnusedCode();
$vendor_file_path = getcwd() . '/vendor/vendor_class.php';
$this->addFile(
$vendor_file_path,
'foo();
}
}'
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'analyzeFile($file_path, new Context(), false);
$this->project_analyzer->consolidateAnalyzedData();
}
public function testStubOverridingMissingClass(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'expectException(InvalidClasslikeOverrideException::class);
$this->analyzeFile($file_path, new Context());
}
public function testStubOverridingMissingMethod(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$file_path = getcwd() . '/src/somefile.php';
$this->addFile(
$file_path,
'expectException(InvalidMethodOverrideException::class);
$this->analyzeFile($file_path, new Context());
}
public function testStubReplacingInterfaceDocblock(): void
{
$this->project_analyzer = $this->getProjectAnalyzerWithConfig(
TestConfig::loadFromXML(
dirname(__DIR__),
'
'
)
);
$this->addFile(
getcwd() . '/vendor/doctrine/import.php',
'addFile(
$file_path,
'getReference(A::class, 1);
}'
);
$this->expectException(CodeException::class);
$this->expectExceptionMessage('A|null');
$this->analyzeFile($file_path, new Context());
}
}