1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\Testing;
4:
5: use PHPStan\Analyser\ConstantResolver;
6: use PHPStan\Analyser\DirectInternalScopeFactoryFactory;
7: use PHPStan\Analyser\Error;
8: use PHPStan\Analyser\ScopeFactory;
9: use PHPStan\Analyser\TypeSpecifier;
10: use PHPStan\BetterReflection\Reflector\Reflector;
11: use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider;
12: use PHPStan\DependencyInjection\Type\ExpressionTypeResolverExtensionRegistryProvider;
13: use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
14: use PHPStan\DependencyInjection\Type\UnaryOperatorTypeSpecifyingExtensionRegistryProvider;
15: use PHPStan\Node\Printer\ExprPrinter;
16: use PHPStan\Parser\Parser;
17: use PHPStan\Php\ComposerPhpVersionFactory;
18: use PHPStan\Php\PhpVersion;
19: use PHPStan\PhpDoc\TypeNodeResolver;
20: use PHPStan\PhpDoc\TypeStringResolver;
21: use PHPStan\Reflection\AttributeReflectionFactory;
22: use PHPStan\Reflection\InitializerExprTypeResolver;
23: use PHPStan\Reflection\ReflectionProvider;
24: use PHPStan\Reflection\ReflectionProvider\DirectReflectionProviderProvider;
25: use PHPStan\Rules\Properties\PropertyReflectionFinder;
26: use PHPStan\Type\Constant\OversizedArrayBuilder;
27: use PHPStan\Type\TypeAliasResolver;
28: use PHPStan\Type\UsefulTypeAliasResolver;
29: use PHPUnit\Framework\ExpectationFailedException;
30: use PHPUnit\Framework\TestCase;
31: use function count;
32: use function implode;
33: use function rtrim;
34: use function sprintf;
35: use const DIRECTORY_SEPARATOR;
36:
37: /** @api */
38: abstract class PHPStanTestCase extends TestCase
39: {
40:
41: use PHPStanTestCaseTrait;
42:
43: public static function getParser(): Parser
44: {
45: /** @var Parser $parser */
46: $parser = self::getContainer()->getService('defaultAnalysisParser');
47: return $parser;
48: }
49:
50: /** @api */
51: public static function createReflectionProvider(): ReflectionProvider
52: {
53: return self::getContainer()->getByType(ReflectionProvider::class);
54: }
55:
56: public static function getReflector(): Reflector
57: {
58: return self::getContainer()->getService('betterReflectionReflector');
59: }
60:
61: public static function getClassReflectionExtensionRegistryProvider(): ClassReflectionExtensionRegistryProvider
62: {
63: return self::getContainer()->getByType(ClassReflectionExtensionRegistryProvider::class);
64: }
65:
66: /**
67: * @param string[] $dynamicConstantNames
68: */
69: public static function createScopeFactory(ReflectionProvider $reflectionProvider, TypeSpecifier $typeSpecifier, array $dynamicConstantNames = []): ScopeFactory
70: {
71: $container = self::getContainer();
72:
73: if (count($dynamicConstantNames) === 0) {
74: $dynamicConstantNames = $container->getParameter('dynamicConstantNames');
75: }
76:
77: $reflectionProviderProvider = new DirectReflectionProviderProvider($reflectionProvider);
78: $composerPhpVersionFactory = $container->getByType(ComposerPhpVersionFactory::class);
79: $constantResolver = new ConstantResolver($reflectionProviderProvider, $dynamicConstantNames, phpVersion: null, composerPhpVersionFactory: $composerPhpVersionFactory, container: $container);
80:
81: $initializerExprTypeResolver = new InitializerExprTypeResolver(
82: $constantResolver,
83: $reflectionProviderProvider,
84: $container->getByType(PhpVersion::class),
85: $container->getByType(OperatorTypeSpecifyingExtensionRegistryProvider::class),
86: $container->getByType(UnaryOperatorTypeSpecifyingExtensionRegistryProvider::class),
87: new OversizedArrayBuilder(),
88: $container->getParameter('usePathConstantsAsConstantString'),
89: );
90:
91: return new ScopeFactory(
92: new DirectInternalScopeFactoryFactory(
93: $container,
94: $reflectionProvider,
95: $initializerExprTypeResolver,
96: $container->getByType(ExpressionTypeResolverExtensionRegistryProvider::class),
97: $container->getByType(ExprPrinter::class),
98: $typeSpecifier,
99: new PropertyReflectionFinder(),
100: self::getParser(),
101: $container->getByType(PhpVersion::class),
102: $container->getByType(AttributeReflectionFactory::class),
103: $container->getParameter('phpVersion'),
104: $constantResolver,
105: ),
106: );
107: }
108:
109: /**
110: * @param array<string, string> $globalTypeAliases
111: */
112: public static function createTypeAliasResolver(array $globalTypeAliases, ReflectionProvider $reflectionProvider): TypeAliasResolver
113: {
114: $container = self::getContainer();
115:
116: return new UsefulTypeAliasResolver(
117: $globalTypeAliases,
118: $container->getByType(TypeStringResolver::class),
119: $container->getByType(TypeNodeResolver::class),
120: $reflectionProvider,
121: );
122: }
123:
124: protected function shouldTreatPhpDocTypesAsCertain(): bool
125: {
126: return true;
127: }
128:
129: /**
130: * Provides a DIRECTORY_SEPARATOR agnostic assertion helper, to compare file paths.
131: *
132: */
133: protected function assertSamePaths(string $expected, string $actual, string $message = ''): void
134: {
135: $expected = $this->getFileHelper()->normalizePath($expected);
136: $actual = $this->getFileHelper()->normalizePath($actual);
137:
138: $this->assertSame($expected, $actual, $message);
139: }
140:
141: /**
142: * @param Error[]|string[] $errors
143: */
144: protected function assertNoErrors(array $errors): void
145: {
146: try {
147: $this->assertCount(0, $errors);
148: } catch (ExpectationFailedException $e) {
149: $messages = [];
150: foreach ($errors as $error) {
151: if ($error instanceof Error) {
152: $messages[] = sprintf("- %s\n in %s on line %d\n", rtrim($error->getMessage(), '.'), $error->getFile(), $error->getLine() ?? 0);
153: } else {
154: $messages[] = $error;
155: }
156: }
157:
158: $this->fail($e->getMessage() . "\n\nEmitted errors:\n" . implode("\n", $messages));
159: }
160: }
161:
162: protected function skipIfNotOnWindows(): void
163: {
164: if (DIRECTORY_SEPARATOR === '\\') {
165: return;
166: }
167:
168: self::markTestSkipped();
169: }
170:
171: protected function skipIfNotOnUnix(): void
172: {
173: if (DIRECTORY_SEPARATOR === '/') {
174: return;
175: }
176:
177: self::markTestSkipped();
178: }
179:
180: }
181: