Slide 1

Slide 1 text

© 2012-2025 BASE, Inc. 1 #phperkaigi #a List とは何か? PHPerKaigi 2025(2025/03/23)

Slide 2

Slide 2 text

© 2012-2025 BASE, Inc. 2 © 2012-2025 BASE, Inc. 2 これだけ覚えて帰ってください

Slide 3

Slide 3 text

© 2012-2025 BASE, Inc. 3 #phperkaigi #a リスト(List)とは キーが0から始まる 連続した整数である配列

Slide 4

Slide 4 text

© 2012-2025 BASE, Inc. 4 #phperkaigi #a 自己紹介 meihei | Yohei Ema BASE株式会社 BASE / Product Dev / Feature Dev 1 X: @app1e_s mixi2: @meihei2 GitHub: @meihei3

Slide 5

Slide 5 text

© 2012-2025 BASE, Inc. 5 1 2 3 #phperkaigi #a 仕様から見たリスト 静的解析ツールから見たリスト PHPの処理から見たリスト 3つの視点からお話しします

Slide 6

Slide 6 text

© 2012-2025 BASE, Inc. 6 © 2012-2025 BASE, Inc. 6 仕様から見たリスト

Slide 7

Slide 7 text

© 2012-2025 BASE, Inc. 7 #phperkaigi #a 配列がリストである = array_is_list() が TRUE である

Slide 8

Slide 8 text

© 2012-2025 BASE, Inc. 8 #phperkaigi #a RFC を読むと ● PHP RFC: Add array_is_list(array $array): bool ● リスト型を導入するわけではなく、配列が0から始まる連続した整数キーで あるかどうかを判定する関数を追加するだけ ● (背景)PHPの配列は、整数キーと文字列キーの両方を持つことができ、 かつその順序が保証されている ○ [0 => 0, 1 => 1, 2 => 2] と [0 => 0, 2 => 2, 1 => 1] は異なる ● (使用例)エンコーダなどで効率的にリストを判定したい時に使う ○ [0 => 0, 1 => 1, 2 => 2] ならば ‘[0, 1, 2]’ に ○ [0 => 0, 2 => 2, 1 => 1] ならば ‘{“0”: 0, “2”: 2, “1”: 1}’ に

Slide 9

Slide 9 text

© 2012-2025 BASE, Inc. 9 #phperkaigi #a 配列がリストである例 ['りんご', 'みかん', 'バナナ'] キーをすべて省略した配列 [0 => 'りんご', 1 => 'みかん', 2 => 'バナナ'] キーが0から始まる連続した整数を明 示的にした配列 [false => 'りんご', true => 'みかん', '2' => 'バナナ'] キーが0から始まる連続した整数とみ なせる値の配列 [] 空配列 ['りんご', 1, 2, null, []] 値が何であっても、キーがリストであ る要件をみたす配列

Slide 10

Slide 10 text

© 2012-2025 BASE, Inc. 10 #phperkaigi #a 配列がリストではない例 [1 => 'みかん', 2 => 'バナナ'] 連続した整数キーが0から始まらない 配列 ['りんご', 'みかん', 99 => 'かまぼこ'] 整数キー連続していない配列 ['a' => 'apple', 'b' => 'banana'] キーが整数ではない文字列の配列

Slide 11

Slide 11 text

© 2012-2025 BASE, Inc. 11 #phperkaigi #a リストを返す配列関数(よく使うもの) array_keys, array_values, range 常にリストを返す array_chunk preserve_keys が false ならリスト を返す array_column index_key を指定しないならリストを 返す array_merge, array_map, array_slice, array_reverse 入力がリストならリストを返す

Slide 12

Slide 12 text

© 2012-2025 BASE, Inc. 12 #phperkaigi #a 配列がリストであるかは array_is_list() を使って 実行時に判定する

Slide 13

Slide 13 text

© 2012-2025 BASE, Inc. 13 #phperkaigi #a 関数呼び出し時にリストか判定する $a = [ 0 => 'りんご', 3 => 'ぶどう', 1 => 'みかん', 2 => 'バナナ', ]; echo array_is_list($a); // FALSE unset($a[3]); echo array_is_list($a); // TRUE 1回目の呼び出し時は、キーが 0,3,1,2 の順になるからリストではな い。 2回目の呼び出し時は、キーが 0,1,2 の順になるからリストとなる。

Slide 14

Slide 14 text

© 2012-2025 BASE, Inc. 14 © 2012-2025 BASE, Inc. 14 静的解析ツールから見たリスト

Slide 15

Slide 15 text

© 2012-2025 BASE, Inc. 15 #phperkaigi #a 配列がリストである = list で表される配列

Slide 16

Slide 16 text

© 2012-2025 BASE, Inc. 16 #phperkaigi #a リスト型 ● PHPの仕様上、リスト型は無い ● 静的解析ツールではPHPDoc上で扱う擬似的な型としてリスト型がある ● list は array のサブタイプとして定義される ○ list は、配列が array であることに加えて、キーが0から始まる連続し た整数である配列 ● 配列がリストであるかを、より厳格に型としてチェックできる

Slide 17

Slide 17 text

© 2012-2025 BASE, Inc. 17 #phperkaigi #a 静的解析ツールによる型チェック /** * @param list $x **/ function hello(array $x): void {...} $a = ['りんご', 'みかん', 99 => 'かまぼこ']; hello($a); // Error: Parameter #1 $x of function hello expects list, array{0: 'りんご', 1: ' みかん', 99: 'かまぼこ'} given. PHPを実行する分にはエラーは発生し ない。 静的解析ツールを使うと、厳格に配列 とリストを区別するので、エラーが発 生する(PHPStan Level 5)。

Slide 18

Slide 18 text

© 2012-2025 BASE, Inc. 18 #phperkaigi #a インライン @var タグもチェックする /** @var list $a **/ $a = [ 0 => 'りんご', 1 => 'みかん', 99 => 'かまぼこ', ]; // Error: PHPDoc tag @var with type list is not subtype of native type array{0: 'りんご', 1: 'みかん', 99: 'かまぼこ'}. コードの途中でローカル変数を定義す る場合でも、list 型をチェックし てくれる(PHPStan 1.10, Level 2)。 以前は負の整数のキーに非対応だった が、つい先々週修正された。 phpstan/phpstan#12708 phpstan/phpstan-src#3870

Slide 19

Slide 19 text

© 2012-2025 BASE, Inc. 19 © 2012-2025 BASE, Inc. 19 PHPの処理から見たリスト

Slide 20

Slide 20 text

© 2012-2025 BASE, Inc. 20 #phperkaigi #a Packed Array かつ空き領域 (holes)を含んでいない配列 ならば 配列がリストである

Slide 21

Slide 21 text

© 2012-2025 BASE, Inc. 21 #phperkaigi #a PHPの配列は2つある Hash Table ● キーからハッシュ表を 使ってデータアクセス する ● 連想配列を実装する Packed Array ● 純粋な配列としてデータ アクセスする ● メモリ効率が良く処理が高速

Slide 22

Slide 22 text

© 2012-2025 BASE, Inc. 22 #phperkaigi #a Packed Array とリスト ● Packed Array は、配列のキーが0から始まる連続した整数である時に 自動的に最適化された配列の内部構造 ● 基本的にはリストと条件が同じである ● しかし、unset などで空き領域(holes)が生まれることがある ○ その場合も Packed Array では在り続けるので、Packed Array だけどリストではな い状態が生まれる。 ● Packed Array から Hash Table へ自動的に変更されることはあるが、 Hash Table から Packed Array への自動的な変更されることはない ○ リストだけど Packed Array ではない(Hash Table である)状態が存在する 参照: PHP8.2から見る、2つの配列 / PHP Conference Japan 2023 https://speakerdeck.com/meihei3/php-conference-japan-2023

Slide 23

Slide 23 text

© 2012-2025 BASE, Inc. 23 #phperkaigi #a 配列がリストである多くの場合 // Packed Array $a = ['りんご', 'みかん', 'バナナ']; echo array_is_list($a); // TRUE 配列のキーが0から始まる連続した整 数であるので、自動的に Packed Array として最適化される。 この状態はメモリ効率が良くて、処理 も高速。

Slide 24

Slide 24 text

© 2012-2025 BASE, Inc. 24 #phperkaigi #a Packed Array だがリストではない場合 // Packed Array $a = ['りんご', 'みかん', 'バナナ']; unset($a[1]); echo array_is_list($a); // FALSE unset で要素を削除されただけでは、 Hash Table への自動的な変更は行わ れない。 ただ空き領域が生まれる。

Slide 25

Slide 25 text

© 2012-2025 BASE, Inc. 25 #phperkaigi #a リストだが Packed Array ではない場合 // Hash Table $a = [ 0 => 'りんご', 3 => 'ぶどう', 1 => 'みかん', 2 => 'バナナ', ]; unset($a[3]); echo array_is_list($a); // TRUE 配列のキーが連番となっていないの で、Hash Table となる。 配列の再生成が行われない限り、 Packed Array から Hash Table へ自 動的に変更されることはない。

Slide 26

Slide 26 text

© 2012-2025 BASE, Inc. 26 #phperkaigi #a まとめ ● リストとは、キーが0から始まる連続した整数である配列 ● PHPの仕様では、リスト型は存在せず array_is_list が TRUE で ある配列のことをリストと言う ● 静的解析ではリストを list 型として表現する ● 内部実装では Packed Array かつ空き領域(holes)を含んでいない配列な らば、配列がリストである ● 多くの場合ではリストは高速でメモリ効率も良い ● みんなリストを使おう!

Slide 27

Slide 27 text

© 2012-2025 BASE, Inc. 27 #phperkaigi #a We are hiring! https://binc.jp/jobs