Skip to content

Commit

Permalink
Merge branch 'master' into parallel
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed Jan 27, 2025
2 parents 9ae5ca8 + a17fdd7 commit 3cf0e27
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.x-dev",
"dev-master": "7.x-dev",
"dev-6.x": "6.x-dev",
"dev-5.x": "5.x-dev",
"dev-4.x": "4.x-dev",
"dev-3.x": "3.x-dev",
Expand Down
50 changes: 50 additions & 0 deletions docs/contributing/editing_callmaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,56 @@ optional parameter is postfixed with `=`, references with `&`/`&r_`/`&w_`/`&rw_`
(depending on the read/write meaning of the reference param) and
variadic args are prefixed with `...`.

Callmaps also support function aliases: aliases are very useful to specify that
a certain function behaves differently according to the parameter types.

For example, the following aliases are currently in use in the callmap to specify
that the `version_compare` function can be called with an `operator` parameter,
in which case it will return a boolean; otherwise it will return an integer.

```php
[
'version_compare' => [
0 => 'bool',
'version1' => 'string',
'version2' => 'string',
'operator' => '\'!=\'|\'<\'|\'<=\'|\'<>\'|\'=\'|\'==\'|\'>\'|\'>=\'|\'eq\'|\'ge\'|\'gt\'|\'le\'|\'lt\'|\'ne\'|null',
],
'version_compare\'1' => [
0 => 'int',
'version1' => 'string',
'version2' => 'string',
],
]
```

**Note**: the above example doesn't provide almost any useful information for type inference;
in fact, the real logic for return type inference is contained in the `VersionCompareReturnTypeProvider`.

The callmap is mainly useful when treating functions and methods as *callable values*, for example:

```php
<?php
function naive_version_compare(string $a, string $b, ?string $operator = null): int|bool {
return 0;
}

$a = ["1.0", "2.0"];

// OK
usort($a, "version_compare");

// InvalidArgument: Argument 2 of usort expects callable(string, string):int, but impure-callable(string, string, null|string=):(bool|int) provided
usort($a, "naive_version_compare");
```

The first usort succeeds, because psalm chooses the correct alias to use between the two provided in the callmap.
The second usort fails (equivalent to the non-split return type of `version_compare` inferred by reflection), because the return type is a union of the two possible signatures of version_compare.

When you have multifaceted functions like these, it's a very good idea to at least define a templated stub in `stubs/` for them, or a custom return type provider for even more complex logic, not representable with templates/conditional types/etc in a stub.

Also note that `bin/gen_callmap.php` has some validation logic which will re-add back removed parameters in overridden aliased callmaps: to avoid this, explicitly whitelist aliased functions by editing `assertParameter` in `bin/gen_callmap_utils.php`, and eventually `bin/gen_callmap.php` as needed.

## Delta file format

Delta files (named `CallMap_<PHP major version><PHP minor version>_delta.php`)
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/DummyProjectWithErrors/diff_composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3cf0e27

Please sign in to comment.