list型
静的解析ツールに実装された独自の型。基本的にはPHPDocに書くことで用いる。
#Psalmの型 #PHPStanの型 #Phanの型
デリファレンスによって多値を擬似的に再現するlist構文とは別
list($a, $b) = f() のような形式で返り値を受け取るパターン(タプル)とは使用箇所が異なる
リストとは、以下のような連番の要素を持つ配列を指す
code:php
$list = 1, 2, 3;
// 値が追加されることもできる
$list[] = 4;
これはリストではなく連想配列
code:php
$assoc = [
11 => 1,
30000 => 2,
5656 => 6,
];
どちらもarray<int, int>になってしまう
list<string>やlist<int>のようにジェネリクスを組み合わせて書くことができる。
配列の中にどんな要素が入るかを制約できる
どんな種類の値でも入りうる場合はmixed型を組合せてlist<mixed>とする
PHPStanでは1.9.0から正式にサポートされている
以前のバージョンのPHPStan(1.8以下)のlist<T>はarray<int, T>のエイリアスなので注意
上記のキーがintの連想配列とリストを型レベルで区別できない
フルサポートするわけではなく互換性のために最低限の検査だけしている状態
list<string>で['a', 'b']だけでなく[2 => 'x', 5 => 'y']も通してしまう
PHPStanの検査結果 https://phpstan.org/r/74b4983d-62df-4405-ae61-441225e062d7
空ではないリストをnon-empty-listとして区別できる
@return list<int>としたときreturn [];と返しても問題ないが、non-empty-listで制限できる
list型の値が空でないことが保証できると以下のような場合に役立つ
code:php
/**
* @phpstan-param non-empty-list<int> $ids
* @return non-empty-array<int, array{name:string}>
*/
function search_ids(array $ids): array {
foreach ($ids as $id) {
$val = search($id);
$result$id = ['name' => $val'name'];
}
return $result;
}
foreach が最低一回実行される
$result が未定義にならず、空ではない配列を返す
PHP RFC: Add array_is_list(array $array): boolが受理され、PHP 8.1からビルトイン関数として導入される
array_is_list()の定義は以下のPHP関数と等価だとされている
code:php
function array_is_list(array $array): bool {
$expectedKey = 0;
foreach ($array as $i => $_) {
if ($i !== $expectedKey) { return false; }
$expectedKey++;
}
return true;
}
上記 RFCより抜粋
ただしビルトイン関数版はPHPの内部表現を使うようになっているので効率化されている
https://github.com/phan/phan/pull/3353
コンピュータサイエンスで「リスト」というと連結リストのようなデータ構造を指すことがあるが、PHPではあくまで配列である。
PHPのarrayは連想配列だが、PHP 7以降の内部での表現は真の配列(動的配列)も使われるようになった
PHP7で変わること ——言語仕様とエンジンの改善ポイント
真の配列であれば高速に結果を返すように最適化されている
内部的に連想配列化されていても上記PHPコードと同等の判定があるので心配は不要
array<T>をlist<T>に変換する方法
PHP: array_values - Manual
array_values()は配列の要素だけを抽出する関数です
code:php
$a = 'a', 'b', 'c';
結果は連想配列ではなく真の配列(list型)になることを保証できます
マニュアル
Psalm
https://psalm.dev/docs/annotating_code/type_syntax/array_types/
PHPStan
https://phpstan.org/writing-php-code/phpdoc-types#lists
PHPStan 1.9ではbleedingEdgeを有効化しないと0から始まる連番かどうかの検査はできない
デフォルト設定 https://phpstan.org/r/74b4983d-62df-4405-ae61-441225e062d7
bleedingEdge有効時 https://phpstan.org/r/6263fc04-e680-46db-bbb1-600658015e52
以前はマニュアルに記載はなかったがarray<int,T>扱いとしてが最低限の実装がされていた
https://github.com/phpstan/phpstan-src/blob/0.12.77/src/PhpDoc/TypeNodeResolver.php#L224-L230
Phan
https://github.com/phan/phan/pull/3353