Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9289b65
Add issue category comments to AnalyserIntegrationTest bug methods
claude Mar 11, 2026
fa1afb3
Extract PHPStanTestCaseTrait and add AbstractBenchCase for phpbench
claude Mar 11, 2026
2902fcf
Restore $container in bootstrap closure, move AbstractBenchCase to te…
claude Mar 11, 2026
ca8dd2a
Move performance tests to phpbench RegressionBench
claude Mar 11, 2026
3024c44
Add .phpbench to .gitignore
claude Mar 11, 2026
1859b17
Exclude tests/bench/data from linting
ondrejmirtes Mar 11, 2026
0c94d28
No need to exclude old PHP versions, it will always run on PHP 8.5
ondrejmirtes Mar 11, 2026
1710adb
Improve phpbench config
ondrejmirtes Mar 11, 2026
7e6e94d
phpbench storage
ondrejmirtes Mar 11, 2026
0f1f480
Moved "infinite run" tests
ondrejmirtes Mar 11, 2026
d92d73b
Fix CS
ondrejmirtes Mar 11, 2026
bc282bc
Move more tests
ondrejmirtes Mar 11, 2026
e4155d5
Higher RetryThreshold because of CI
ondrejmirtes Mar 11, 2026
77838bf
Bench CI workflow
ondrejmirtes Mar 11, 2026
02f1a86
Fix PHPStan
ondrejmirtes Mar 11, 2026
70788c8
Fix workflow
ondrejmirtes Mar 11, 2026
5fb14ad
Upload my baseline temporarily
ondrejmirtes Mar 11, 2026
cd74f0d
Move phpbench
ondrejmirtes Mar 11, 2026
50b0f2f
baseline.xml from GitHub Actions
ondrejmirtes Mar 11, 2026
081f445
Fix workflow
ondrejmirtes Mar 11, 2026
1be0d93
Sophisticated assertion
ondrejmirtes Mar 11, 2026
00f0ba7
Fix workflow
ondrejmirtes Mar 11, 2026
e8787d0
Use ParamProviders
ondrejmirtes Mar 11, 2026
2cb623a
Fix tests workflow
ondrejmirtes Mar 11, 2026
2e011a5
Do not analyse benches
ondrejmirtes Mar 11, 2026
1c473c4
Fix config file
ondrejmirtes Mar 11, 2026
f9e97eb
Update baseline file
ondrejmirtes Mar 11, 2026
e852e8f
Exclude RegressionBench from linting because it has multi-line attrib…
ondrejmirtes Mar 11, 2026
98b76f0
Fix baseline
ondrejmirtes Mar 11, 2026
753fecf
More lenient phpbench assertions
ondrejmirtes Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: "Benchmark"

on:
pull_request:
paths-ignore:
- 'compiler/**'
- 'apigen/**'
- 'changelog-generator/**'
- 'issue-bot/**'
push:
branches:
- "2.1.x"
paths-ignore:
- 'compiler/**'
- 'apigen/**'
- 'changelog-generator/**'
- 'issue-bot/**'

concurrency:
group: bench-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read

jobs:
baseline:
name: "Baseline"
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit

- name: "Checkout base branch"
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
Comment on lines +38 to +39

Check warning

Code scanning / zizmor

credential persistence through GitHub Actions artifacts Warning

credential persistence through GitHub Actions artifacts

- name: "Install PHP"
uses: "shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1" # v2
with:
coverage: "none"
php-version: "8.5"
tools: pecl
extensions: ds,mbstring
ini-file: development
ini-values: memory_limit=-1

- uses: "ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520" # v3

Check warning

Code scanning / zizmor

detects commit SHAs that don't match their version comment tags Warning

detects commit SHAs that don't match their version comment tags
- uses: "ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520" # v3

Check warning

Code scanning / zizmor

detects commit SHAs that don't match their version comment tags Warning

detects commit SHAs that don't match their version comment tags
with:
working-directory: "tests/"

- name: "Run phpbench baseline"
run: "tests/vendor/bin/phpbench run --dump-file=tests/bench/storage/baseline.xml"

- name: "Upload baseline artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: phpbench-baseline
path: tests/bench/storage/baseline.xml

test:
name: "Test"
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
with:
egress-policy: audit

- name: "Checkout"
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
Comment on lines +76 to +77

Check warning

Code scanning / zizmor

credential persistence through GitHub Actions artifacts Warning

credential persistence through GitHub Actions artifacts

- name: "Install PHP"
uses: "shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1" # v2
with:
coverage: "none"
php-version: "8.5"
tools: pecl
extensions: ds,mbstring
ini-file: development
ini-values: memory_limit=-1

- uses: "ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520" # v3

Check warning

Code scanning / zizmor

detects commit SHAs that don't match their version comment tags Warning

detects commit SHAs that don't match their version comment tags
- uses: "ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520" # v3

Check warning

Code scanning / zizmor

detects commit SHAs that don't match their version comment tags Warning

detects commit SHAs that don't match their version comment tags
with:
working-directory: "tests/"

- name: "Run phpbench test"
run: "tests/vendor/bin/phpbench run --file=tests/bench/storage/baseline.xml --report=aggregate"
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ jobs:

- name: "Downgrade PHPUnit with Paratest"
shell: bash
run: "composer require --dev phpunit/phpunit:^9.6 brianium/paratest:^6.5 symfony/console:^5.4 symfony/process:^5.4 doctrine/instantiator:^1.0 --update-with-dependencies --ignore-platform-reqs --working-dir=tests"
run: "composer require --dev phpbench/phpbench:^1.2.15 phpunit/phpunit:^9.6 brianium/paratest:^6.5 symfony/console:^5.4 symfony/process:^5.4 doctrine/instantiator:^1.0 --update-with-dependencies --ignore-platform-reqs --working-dir=tests"

- uses: ./.github/actions/downgrade-code
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
/tests/PHPStan/Reflection/data/golden/
tmp/.memory_limit
e2e/bashunit
/.phpbench
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ tests-golden-reflection:
lint:
XDEBUG_MODE=off php vendor/bin/parallel-lint --colors \
--exclude tests/PHPStan/Analyser/data \
--exclude tests/bench/data \
--exclude tests/bench/RegressionBench.php \
--exclude tests/PHPStan/Analyser/nsrt \
--exclude tests/PHPStan/Rules/Methods/data \
--exclude tests/PHPStan/Rules/Functions/data \
Expand Down
1 change: 1 addition & 0 deletions build/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ parameters:
- ../tests/PHPStan/Analyser/nsrt/*
- ../tests/PHPStan/Analyser/traits/*
- ../tests/notAutoloaded/*
- ../tests/bench/*
- ../tests/PHPStan/Reflection/UnionTypesTest.php
- ../tests/PHPStan/Reflection/MixedTypeTest.php
- ../tests/e2e/magic-setter/*
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
},
"classmap": [
"tests/e2e",
"tests/PHPStan"
"tests/PHPStan",
"tests/bench"
]
},
"repositories": [
Expand Down
7 changes: 7 additions & 0 deletions phpbench.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema":"./tests/vendor/phpbench/phpbench/phpbench.schema.json",
"runner.bootstrap": "vendor/autoload.php",
"runner.path": "tests/bench",
"runner.file_pattern": "*Bench.php",
"storage.xml_storage_path": "tests/bench/storage"
}
3 changes: 2 additions & 1 deletion phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
</rule>
<rule ref="SlevomatCodingStandard.Functions.UnusedInheritedVariablePassedToClosure.UnusedInheritedVariable">
<exclude-pattern>src/Command/CommandHelper.php</exclude-pattern>
<exclude-pattern>src/Testing/PHPStanTestCase.php</exclude-pattern>
<exclude-pattern>src/Testing/PHPStanTestCaseTrait.php</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException">
<exclude-pattern>tests</exclude-pattern>
Expand All @@ -65,6 +65,7 @@
<element key="compiler/tests" value="PHPStan\Compiler"/>
<element key="src" value="PHPStan"/>
<element key="tests/PHPStan" value="PHPStan"/>
<element key="tests/bench" value="PHPStan\Benchmark"/>
<element key="tests/e2e" value="PHPStan\Tests"/>
<element key="apigen/src" value="PHPStan\ApiGen"/>
<element key="changelog-generator/src" value="PHPStan\ChangelogGenerator"/>
Expand Down
71 changes: 1 addition & 70 deletions src/Testing/PHPStanTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@
use PHPStan\Analyser\ScopeFactory;
use PHPStan\Analyser\TypeSpecifier;
use PHPStan\BetterReflection\Reflector\Reflector;
use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\ContainerFactory;
use PHPStan\DependencyInjection\Reflection\ClassReflectionExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\ExpressionTypeResolverExtensionRegistryProvider;
use PHPStan\DependencyInjection\Type\OperatorTypeSpecifyingExtensionRegistryProvider;
use PHPStan\File\FileHelper;
use PHPStan\Internal\DirectoryCreator;
use PHPStan\Internal\DirectoryCreatorException;
use PHPStan\Node\Printer\ExprPrinter;
use PHPStan\Parser\Parser;
use PHPStan\Php\ComposerPhpVersionFactory;
Expand All @@ -32,76 +27,17 @@
use PHPStan\Type\UsefulTypeAliasResolver;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;
use function array_merge;
use function count;
use function hash;
use function implode;
use function rtrim;
use function sprintf;
use function sys_get_temp_dir;
use const DIRECTORY_SEPARATOR;
use const PHP_VERSION_ID;

/** @api */
abstract class PHPStanTestCase extends TestCase
{

/** @var array<string, Container> */
private static array $containers = [];

/** @api */
public static function getContainer(): Container
{
$additionalConfigFiles = [__DIR__ . '/TestCase.neon'];
foreach (static::getAdditionalConfigFiles() as $configFile) {
$additionalConfigFiles[] = $configFile;
}
$cacheKey = hash('sha256', implode("\n", $additionalConfigFiles));

if (!isset(self::$containers[$cacheKey])) {
$tmpDir = sys_get_temp_dir() . '/phpstan-tests';
try {
DirectoryCreator::ensureDirectoryExists($tmpDir, 0777);
} catch (DirectoryCreatorException $e) {
self::fail($e->getMessage());
}

$rootDir = __DIR__ . '/../..';
$fileHelper = new FileHelper($rootDir);
$rootDir = $fileHelper->normalizePath($rootDir, '/');
$containerFactory = new ContainerFactory($rootDir);
$container = $containerFactory->create($tmpDir, array_merge([
$containerFactory->getConfigDirectory() . '/config.level8.neon',
], $additionalConfigFiles), []);
self::$containers[$cacheKey] = $container;

foreach ($container->getParameter('bootstrapFiles') as $bootstrapFile) {
(static function (string $file) use ($container): void {
require_once $file;
})($bootstrapFile);
}

if (PHP_VERSION_ID >= 80000) {
require_once __DIR__ . '/../../stubs/runtime/Enum/UnitEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/BackedEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumUnitCase.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumBackedCase.php';
}
} else {
ContainerFactory::postInitializeContainer(self::$containers[$cacheKey]);
}

return self::$containers[$cacheKey];
}

/**
* @return string[]
*/
public static function getAdditionalConfigFiles(): array
{
return [];
}
use PHPStanTestCaseTrait;

public static function getParser(): Parser
{
Expand Down Expand Up @@ -188,11 +124,6 @@ protected function shouldTreatPhpDocTypesAsCertain(): bool
return true;
}

public static function getFileHelper(): FileHelper
{
return self::getContainer()->getByType(FileHelper::class);
}

/**
* Provides a DIRECTORY_SEPARATOR agnostic assertion helper, to compare file paths.
*
Expand Down
81 changes: 81 additions & 0 deletions src/Testing/PHPStanTestCaseTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php declare(strict_types = 1);

namespace PHPStan\Testing;

use PHPStan\DependencyInjection\Container;
use PHPStan\DependencyInjection\ContainerFactory;
use PHPStan\File\FileHelper;
use PHPStan\Internal\DirectoryCreator;
use PHPStan\Internal\DirectoryCreatorException;
use RuntimeException;
use function array_merge;
use function hash;
use function implode;
use function sys_get_temp_dir;
use const PHP_VERSION_ID;

trait PHPStanTestCaseTrait
{

/** @var array<string, Container> */
private static array $containers = [];

public static function getContainer(): Container
{
$additionalConfigFiles = [__DIR__ . '/TestCase.neon'];
foreach (static::getAdditionalConfigFiles() as $configFile) {
$additionalConfigFiles[] = $configFile;
}
$cacheKey = hash('sha256', implode("\n", $additionalConfigFiles));

if (!isset(self::$containers[$cacheKey])) {
$tmpDir = sys_get_temp_dir() . '/phpstan-tests';
try {
DirectoryCreator::ensureDirectoryExists($tmpDir, 0777);
} catch (DirectoryCreatorException $e) {
throw new RuntimeException($e->getMessage(), previous: $e);
}

$rootDir = __DIR__ . '/../..';
$fileHelper = new FileHelper($rootDir);
$rootDir = $fileHelper->normalizePath($rootDir, '/');
$containerFactory = new ContainerFactory($rootDir);
$container = $containerFactory->create($tmpDir, array_merge([
$containerFactory->getConfigDirectory() . '/config.level8.neon',
], $additionalConfigFiles), []);
self::$containers[$cacheKey] = $container;

foreach ($container->getParameter('bootstrapFiles') as $bootstrapFile) {
(static function (string $file) use ($container): void {
require_once $file;
})($bootstrapFile);
}

if (PHP_VERSION_ID >= 80000) {
require_once __DIR__ . '/../../stubs/runtime/Enum/UnitEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/BackedEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnum.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumUnitCase.php';
require_once __DIR__ . '/../../stubs/runtime/Enum/ReflectionEnumBackedCase.php';
}
} else {
ContainerFactory::postInitializeContainer(self::$containers[$cacheKey]);
}

return self::$containers[$cacheKey];
}

/**
* @return string[]
*/
public static function getAdditionalConfigFiles(): array
{
return [];
}

public static function getFileHelper(): FileHelper
{
return self::getContainer()->getByType(FileHelper::class);
}

}
Loading
Loading