← All results

value-of<T> over a template parameter

Group: Advanced PHPDoc types · Category: ecosystem · File: phpdoc_advanced_fallback_value_of_template.php

The value-side mirror of the template key-of case: a generic function takes T of array and returns one of its values, so value-of<T> is evaluated at the call site from the inferred T. Without the evaluation the return is mixed, which satisfies everything silently — so the probe is a parameter the projected value type cannot satisfy, and the precision probe is a literal union only an unwidened T can satisfy. References: - PHPStan phpdoc-types: value-of over template parameters - Psalm utility_types.md: value-of with templates

Analyzer results

AnalyzerVersionResultDiagnostics
phan 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 Enforced
phpdoc_advanced_fallback_value_of_template.php:62: Argument 1 ($value) is firstValue(['low'=>1,'high'=>10]) of type int (value: 1)|int (value: 10) but \Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString() takes string defined at /Users/megurine/repo/php/php-typing-conformance/conformance/tests/phpdoc_advanced_fallback_value_of_template.php:43 [PhanTypeMismatchArgument]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
phpstan 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 Enforced reported Lv.5+
phpdoc_advanced_fallback_value_of_template.php:62: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString expects string, int given. [identifier=argument.type] [reported-from-level=5]
With strict-rules
phpdoc_advanced_fallback_value_of_template.php:62: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString expects string, int given. [identifier=argument.type]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
psalm 6.16.1Psalm 6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac
next: 7.0.0-beta19
Enforced
phpdoc_advanced_fallback_value_of_template.php:29: Unable to determine the type that $value is being assigned to [MixedAssignment]
phpdoc_advanced_fallback_value_of_template.php:33: Could not infer a return type [MixedReturnStatement]
phpdoc_advanced_fallback_value_of_template.php:62: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString expects string, but 1|10 provided [InvalidScalarArgument]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
  • False positives: line(s) 29
Expectation diff
Line 33: Expected valid value to be accepted, got ["Could not infer a return type [MixedReturnStatement]"]
Line 29: Unexpected errors ["Unable to determine the type that $value is being assigned to [MixedAssignment]"]
mago 1.46.0mago 1.46.0 Enforced ⚠ 1 false positive
phpdoc_advanced_fallback_value_of_template.php:29: Assigning `mixed` type to a variable may lead to unexpected behavior. [mixed-assignment]
phpdoc_advanced_fallback_value_of_template.php:62: Invalid argument type for argument #1 of `Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString`: expected `string`, but found `int(1)|int(10)`. [invalid-argument]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
  • False positives: line(s) 29
Expectation diff
Line 29: Unexpected errors ["Assigning `mixed` type to a variable may lead to unexpected behavior. [mixed-assignment]"]
mir 0.70.1mir 0.70.1 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/1 of the expected violations reported
phpantom 0.9.0phpantom_lsp 0.9.0 Incidental (1/1)
phpdoc_advanced_fallback_value_of_template.php:55: Argument 1 ($value) expects int, got value-of<array> [type_mismatch_argument]
phpdoc_advanced_fallback_value_of_template.php:59: Argument 1 ($level) expects 1|10, got value-of<array> [type_mismatch_argument]
phpdoc_advanced_fallback_value_of_template.php:62: Argument 1 ($value) expects string, got value-of<array> [type_mismatch_argument]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 55, 59
Expectation diff
Line 55: Expected valid value to be accepted, got ["Argument 1 ($value) expects int, got value-of<array> [type_mismatch_argument]"]
Line 59: Expected valid value to be accepted, got ["Argument 1 ($level) expects 1|10, got value-of<array> [type_mismatch_argument]"]
intelephense 1.18.5intelephense 1.18.5 Enforced
phpdoc_advanced_fallback_value_of_template.php:62: Expected type 'string'. Found 'int|int'. [P1006]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
phpy 0.2.0phpy 0.2.0 Not enforced

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/1 of the expected violations reported
qodana 262.8665.325Qodana 262.8665.325 Incidental (0/1) ⚠ 1 false positive
phpdoc_advanced_fallback_value_of_template.php:48: Argument type does not match the declared [PhpDocSignatureInspection]
  • Recognition: spelling resolved
  • Enforcement: 0/1 of the expected violations reported — incidental, since values the type admits were also rejected on line(s) 48
  • False positives: line(s) 48
Expectation diff
Line 48: Unexpected errors ["Argument type does not match the declared [PhpDocSignatureInspection]"]
noverify 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 Unrecognized ⚠ 1 false positive
phpdoc_advanced_fallback_value_of_template.php:27: param $items miss matched with phpdoc type <<T>> [funcParamTypeMissMatch]
phpdoc_advanced_fallback_value_of_template.php:50: param $level miss matched with phpdoc type <<1|10>> [funcParamTypeMissMatch]
  • Recognition: spelling not resolved — reported on declaration line(s) 27
  • Enforcement: 0/1 of the expected violations reported — incidental, since the spelling was not resolved
  • False positives: line(s) 50
Expectation diff
Line 50: Unexpected errors ["param $level miss matched with phpdoc type <<1|10>> [funcParamTypeMissMatch]"]
steins 0.1.5steins 0.1.5 (2026-08-12 revision 39b4cc1) Widened to mixed

No diagnostics reported.

  • Recognition: spelling resolved
  • Enforcement: 0/1 of the expected violations reported

Notes: Same project_value_of catch-all as key-of<T>: a @template T type variable lowers to ContractTy::Class("t"), not a shape-bearing arm, so value-of<T> floors to Opaque and the value type is never narrowed from the template parameter.

phpstan-strict 2.2.8PHPStan - PHP Static Analysis Tool 2.2.8 Enforced
phpdoc_advanced_fallback_value_of_template.php:62: Parameter #1 $value of function Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString expects string, int given. [identifier=argument.type]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
psalm-next 7.0.0-beta19Psalm 7.0.0-beta19@7e751c06a756fa64dc4c759c09fe4a173afcb433 Enforced ⚠ 1 false positive
phpdoc_advanced_fallback_value_of_template.php:29: Unable to determine the type that $value is being assigned to [MixedAssignment]
phpdoc_advanced_fallback_value_of_template.php:33: Could not infer a return type [MixedReturnStatement]
phpdoc_advanced_fallback_value_of_template.php:62: Argument 1 of Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate\acceptsString expects string, but 1|10 provided [InvalidScalarArgument]
  • Recognition: spelling resolved
  • Enforcement: 1/1 of the expected violations reported
  • False positives: line(s) 29
Expectation diff
Line 33: Expected valid value to be accepted, got ["Could not infer a return type [MixedReturnStatement]"]
Line 29: Unexpected errors ["Unable to determine the type that $value is being assigned to [MixedAssignment]"]

Source

<?php

declare(strict_types=1);

namespace Conformance\Tests\PhpdocAdvancedFallbackValueOfTemplate;

/**
 * `value-of<T>` over a template parameter
 *
 * The value-side mirror of the template `key-of` case: a generic function
 * takes `T of array` and returns one of its values, so `value-of<T>` is
 * evaluated at the call site from the inferred T. Without the evaluation the
 * return is `mixed`, which satisfies everything silently — so the probe is a
 * parameter the projected value type cannot satisfy, and the precision probe
 * is a literal union only an unwidened T can satisfy.
 *
 * References:
 * - PHPStan phpdoc-types: value-of over template parameters
 * - Psalm utility_types.md: value-of with templates
 */

/**
 * @template T of array<array-key, mixed>
 * @param T $items
 * @return value-of<T>
 */
function firstValue(array $items) // T: value-of<T>
{
    foreach ($items as $value) {
        // A tool can evaluate the projection at call sites and still be unable
        // to prove this body produces value-of<T>; that limitation surfaces as
        // a false positive here, not as an expectation of the test.
        return $value; // V
    }

    throw new \InvalidArgumentException('empty array');
}

function acceptsInt(int $value): void
{
}

function acceptsString(string $value): void
{
}

/**
 * @param 1|10 $level
 */
function acceptsLevel(int $level): void
{
}

// With int values, the returned value is an int.
acceptsInt(firstValue(['low' => 1, 'high' => 10])); // V

// More precisely, it is the union of this argument's values: a tool that
// infers T but widens the values to `int` reports a false positive here.
acceptsLevel(firstValue(['low' => 1, 'high' => 10])); // V

// A value of this argument is never a string.
acceptsString(firstValue(['low' => 1, 'high' => 10])); // E?: the values of this argument are ints