Skip to content

Type modification by reference #MissingFeatureInPHPDoc #6871

@ondrejmirtes

Description

@ondrejmirtes

Feature request

Discussed in #6671

Originally posted by Geompse February 19, 2022
Hi Ondřej, hi anyone reading this,

You can skip this part if you are familiar with PHP references

In PHP one can pass variables by references to a function (or method) through its arguments. During the function execution, their values can change in the calling scope by being changed by the function (in the called function's scope).

  • This is used when a function returns multiple things (and you don't want to temporarily store them in an array), example fsockopen (resource or false + error code + error message):
/**
 * @param int $error_code
 * @param string $error_message
 * @return resource|false
 */
function fsockopen(string $hostname, int $port = -1, &$error_code = null, &$error_message = null, ?float $timeout = null)
  • This is useful when you need to control the memory used by PHP, while handling large strings or huge associative arrays:
function standard_call(string $string) { /* RAM usage : 2*strlen($string); */ }
function reference_call(string &$string) { /* RAM usage : 1*strlen($string); */ }

Ok thanks professor but in what way is this related to PHPStan ?

The issue I'm facing using PHPStan is that it does not like very much those references. Everytime I write the PHPDoc of a function using them I feel like I'm not doing the right thing.

Let's imagine we have millions of rows returned by a JSON REST API, so there is no way to split the memory work, use objects, or anything. For this example we will have an indexed array of associative arrays with keys "price_without_taxe" and "vat", and the goal is to add a new key "price_all_taxes_included".

function compute_taxes(array &$rows) : void
{
    $count = count($rows);
    for($index=0; $index<$count; $index++)
        $rows[$index]['price_all_taxes_included'] = $rows[$index]['price_without_taxe'] * ( 1 + $rows[$index]['vat'] );
}

What I do as of today feels wrong, for the passed variable never has the new keys before the call, and always has them after the call, and in the PHPDoc I'm saying "may or may not have the new key" 😥:

/**
 * @param array<int,array{'price_without_taxe':float,'vat':float,'price_all_taxes_included'?:float}> $rows
 */

So I was wondering if there is a better way? I would love to do something like that (or event better with a template, but this is already a long post) 👍:

/**
 * @phpstan-param-in array<int,array{'price_without_taxe':float,'vat':float}> $rows
 * @phpstan-param-out array<int,array{'price_without_taxe':float,'vat':float,'price_all_taxes_included':float}> $rows
 */

It would be even nicer to the standard PHP functions, because PHPStan could then warn when passing wrong "in" values and when using wrong "out" values. Example adapting the stub for fsockopen:

/**
 * @phpstan-param-in null $error_code
 * @phpstan-param-out int $error_code
 * @phpstan-param-in null $error_message
 * @phpstan-param-out string $error_message
 * @return resource|false
 */
function fsockopen(string $hostname, int $port = -1, &$error_code = null, &$error_message = null, ?float $timeout = null)
$error_code = 12345;
$error_message = null;
$resource = fsockopen($hostname, $port, $error_code, $error_message, $timeout); # PHPStan: "Parameter #3 $error_code of function fsockopen expects null, int given."
if(!is_null($error_code)) # PHPStan: "If condition is always true."
    echo $error_message;

Thanks for reading

This is my first discussion on Github, please be nice (as always) and tell me if I'm not doing it right 😅.

Have a nice day !
Geompse

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions