← All results

$class::$prop when $class is a class-string, not a bare class name

Group: Cross-analyzer regressions · Category: regression · File: regressions_variable_class_static_property.php

Holder::$count is a public int. Through class-string<Holder> the same read should be int, a string write should be rejected, and the private $secret should stay inaccessible. get_class($holder) is the same class-string; Psalm 6.x used to analyse that read as a made-up object property (InvalidPropertyFetch on $__fake_var). Source: vimeo/psalm#12040, from #3609 / #4198. References: - psalm#12040

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.10Phan 6.0.10 php-ast version 1.1.3 PHP version used to run Phan: 8.5.11 Enforced
regressions_variable_class_static_property.php:52: Assigning 'no' of type string to property but \Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$count is int [PhanTypeMismatchPropertyReal]
regressions_variable_class_static_property.php:60: Cannot access private property \Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/regressions_variable_class_static_property.php:26 [PhanAccessPropertyPrivate]
  • Recognition: spelling resolved
  • Enforcement: 4/4 of the expected violations reported
phpstan 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Partly enforced (2/4)

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 2/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
Line 60: Expected 1 error(s)
psalm 6.20.0Psalm 6.20.0@804f03c0c183d149fcd4b8c7c0c9b3fbf836d34c
next: 7.0.0-rc2
Enforced (pzoom≠)
regressions_variable_class_static_property.php:52: Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$count with declared type 'int' cannot be assigned type ''no'' [InvalidPropertyAssignmentValue]
regressions_variable_class_static_property.php:60: Cannot access private property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret [InaccessibleProperty]
pzoom (Psalm port)
regressions_variable_class_static_property.php:60: Cannot access private property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret [InaccessibleProperty]
regressions_variable_class_static_property.php:66: Cannot access property $count on class-string<Conformance\Tests\RegressionsVariableClassStaticProperty\Holder> [InvalidPropertyFetch]
regressions_variable_class_static_property.php:66: Argument 1 of takesInt cannot be mixed, expecting int [MixedArgument]
  • Recognition: spelling resolved
  • Enforcement: 4/4 of the expected violations reported
mago 1.57.0mago 1.57.0 Partly enforced (3/4)
regressions_variable_class_static_property.php:52: Invalid property assignment value [invalid-property-assignment-value]
regressions_variable_class_static_property.php:60: Cannot read private property `$secret` from class `Conformance\Tests\RegressionsVariableClassStaticProperty\Holder`. [invalid-property-read]
regressions_variable_class_static_property.php:60: Argument #1 passed to function `Conformance\Tests\RegressionsVariableClassStaticProperty\takesInt` has type `never`, meaning it cannot produce a value. [no-value]
regressions_variable_class_static_property.php:66: Invalid argument type for argument #1 of `Conformance\Tests\RegressionsVariableClassStaticProperty\takesInt`: expected `int`, but found `mixed`. [mixed-argument]
  • Recognition: spelling resolved
  • Enforcement: 3/4 of the expected violations reported
Expectation diff
Line 66: Expected silence (quiet probe), got ["Invalid argument type for argument #1 of `Conformance\\Tests\\RegressionsVariableClassStaticProperty\\takesInt`: expected `int`, but found `mixed`. [mixed-argument]"]
mir 0.87.0mir 0.87.0 Partly enforced (3/4)
regressions_variable_class_static_property.php:52: InvalidPropertyAssignment: Property $count expects 'int', cannot assign '"no"' [MIR0206]
regressions_variable_class_static_property.php:60: InaccessibleProperty: Cannot access property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret [MIR0014]
regressions_variable_class_static_property.php:66: MixedArgument: Argument $value of takesInt() is mixed [MIR1200]
  • Recognition: spelling resolved
  • Enforcement: 3/4 of the expected violations reported
Expectation diff
Line 66: Expected silence (quiet probe), got ["MixedArgument: Argument $value of takesInt() is mixed [MIR1200]"]
phpantom 0.11.1phpantom_lsp 0.11.1 Partly enforced (3/4)
regressions_variable_class_static_property.php:60: Cannot access private static property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret from outside its declaring class [invalid_member_access]
  • Recognition: spelling resolved
  • Enforcement: 3/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
intelephense 1.18.5intelephense 1.18.5 Partly enforced (1/4)
regressions_variable_class_static_property.php:43: Expected type 'int'. Found 'mixed'. [P1006]
regressions_variable_class_static_property.php:60: Expected type 'int'. Found 'mixed'. [P1006]
regressions_variable_class_static_property.php:60: Cannot access private property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret from global scope. [P1080]
regressions_variable_class_static_property.php:66: Expected type 'int'. Found 'mixed'. [P1006]
  • Recognition: spelling resolved
  • Enforcement: 1/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
Line 43: Expected silence (quiet probe), got ["Expected type 'int'. Found 'mixed'. [P1006]"]
Line 66: Expected silence (quiet probe), got ["Expected type 'int'. Found 'mixed'. [P1006]"]
phpactor 2026.10.07.0Phpactor 2026.10.07.0 Partly enforced (2/4)

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 2/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
Line 60: Expected 1 error(s)
phpy 1.0.19409phpy 1.0.19409 Enforced
regressions_variable_class_static_property.php:52: Cannot implicitly convert 'string' to 'int'
regressions_variable_class_static_property.php:60: Member has private visibility and is not accessible from the current context.
  • Recognition: spelling resolved
  • Enforcement: 4/4 of the expected violations reported
qodana 262.10968.76PhpStorm 262.10968.76 Partly enforced (3/4)
regressions_variable_class_static_property.php:52: Property must be of type 'int', 'string' provided [PhpStrictTypeCheckingInspection]
  • Recognition: spelling resolved
  • Enforcement: 3/4 of the expected violations reported
Expectation diff
Line 60: Expected 1 error(s)
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Unrecognized
regressions_variable_class_static_property.php:41: param $class miss matched with phpdoc type <<class-string<Holder>>> [funcParamTypeMissMatch]
regressions_variable_class_static_property.php:49: param $class miss matched with phpdoc type <<class-string<Holder>>> [funcParamTypeMissMatch]
regressions_variable_class_static_property.php:58: param $class miss matched with phpdoc type <<class-string<Holder>>> [funcParamTypeMissMatch]
  • Recognition: spelling not resolved — reported on declaration line(s) 41, 49, 58
  • Enforcement: 2/4 of the expected violations reported — incidental, since the spelling was not resolved
Expectation diff
Line 52: Expected 1 error(s)
Line 60: Expected 1 error(s)
steins 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) Partly enforced (2/4)

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 2/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
Line 60: Expected 1 error(s)
phpstan-strict 2.3.1PHPStan - PHP Static Analysis Tool 2.3.1 Partly enforced (2/4)

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 2/4 of the expected violations reported
Expectation diff
Line 52: Expected 1 error(s)
Line 60: Expected 1 error(s)
psalm-next 7.0.0-rc2Psalm 7.0.0-rc2@20339e3fe5b8d03bcd8061030eb2a83ad50e5783 Enforced
regressions_variable_class_static_property.php:52: Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$count with declared type 'int' cannot be assigned type ''no'' [InvalidPropertyAssignmentValue]
regressions_variable_class_static_property.php:60: Cannot access private property Conformance\Tests\RegressionsVariableClassStaticProperty\Holder::$secret [InaccessibleProperty]
  • Recognition: spelling resolved
  • Enforcement: 4/4 of the expected violations reported

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\RegressionsVariableClassStaticProperty;

/**
 * `$class::$prop` when `$class` is a class-string, not a bare class name.
 *
 * `Holder::$count` is a public int. Through `class-string<Holder>` the same
 * read should be int, a string write should be rejected, and the private
 * `$secret` should stay inaccessible. `get_class($holder)` is the same
 * class-string; Psalm 6.x used to analyse that read as a made-up object
 * property (`InvalidPropertyFetch` on `$__fake_var`).
 *
 * Source: vimeo/psalm#12040, from #3609 / #4198.
 *
 * References:
 * - https://github.com/vimeo/psalm/pull/12040
 */

final class Holder
{
    public static int $count = 1;

    private static int $secret = 2;

    public static function secret(): int
    {
        return self::$secret;
    }
}

function takesInt(int $value): void
{
}

/**
 * @param class-string<Holder> $class
 */
function readCount(string $class): void // T: class-string<Holder>
{
    takesInt($class::$count); // Q: class-string<Holder>::$count is int
}

/**
 * @param class-string<Holder> $class
 */
function writeCount(string $class): void // T: class-string<Holder>
{
    $class::$count = 2; // V: int write matches the declared type
    $class::$count = 'no'; // E: string is not int
}

/**
 * @param class-string<Holder> $class
 */
function readSecret(string $class): void // T: class-string<Holder>
{
    takesInt($class::$secret); // E: $secret is private
}

function fromGetClass(Holder $holder): void
{
    $class = get_class($holder);
    takesInt($class::$count); // Q: get_class() is class-string<Holder>
}

takesInt(Holder::$count); // V: the same read with a bare class name