Skip to content

PHP8時代にarray型と向き合う

公開日:

オンラインYouTube Liveで開催された『PHPerKaigi petit - PHP8.1リリース祝賀会』で発表枠(20分)として発表しました。

Download PDF

スライドテキスト

Page 1

PHP8時代に

array型と向き合う

Faced with array type in the PHP 8.x era

pixiv Inc.

USAMI Kenta

2021-12-21 PHPerKaigi petit - PHP8.1リリース祝賀会

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • ピクシブ株式会社 pixiv事業本部 エンジニア
  • 最近はピクシブ百科事典(dic.pixiv.net)を開発しています (PSR-15!)
  • Emacs Lisper, PHPer
  • Emacs PHP Modeを開発しています (2017年-)
  • PHPカンファレンス実行委員、PHPerKaigi コアスタッフ

Page 3

tadsanのあれこれが読める場所

  • https://tadsan.fanbox.cc/
  • https://scrapbox.io/php/
  • https://www.phper.ninja/
  • https://zenn.dev/tadsan
  • https://qiita.com/tadsan
  • https://github.com/bag2php

Page 4

WEB+DB PRESS総集編

Page 5

Software Design 11月号

Page 6

前回までのあらすじ

Page 7

最後のPHP勉強会(2020年)

Page 8

PHPカンファレンス沖縄2021

Page 9

PHPカンファレンス2021

Page 10

なんかいろいろ話してきました

Page 11

PHPの進化は型宣言の進化

Page 12

型がついていない関数(PHP5)

function add($a, $b) {return $a + $b;}

Page 13

スカラー型宣言(PHP7)

int + intって本当にintなの?

function add(int $a, int $b): int {return $a + $b;}

Page 14

広い値をとるにはfloatが必要

ひとつの解決策ではあるが… 不必要にfloatを強制するのか

function add(float $a, float $b): float {return $a + $b;}

Page 15

PHPDocの型注釈(アノテーション)

あえて型宣言を省略する

/*** @param int|float $a* @param int|float $b* @return int|float*/function add($a, $b) {return $a + $b;}

Page 16

ユニオン型宣言 (PHP8.0)

function add(int|float $a, int|float $b): int|float {return $a + $b;}

Page 17

クラスの型もPHP7→8で進化

Page 18

著者を持つBookクラス

class Book{/** @var Author */private $author public function __construct(Author $author) {$this->author = $author;}}

Page 19

PHP 7.4でプロパティ型宣言

class Book{/** @var Author */private Author $author public function __construct(Author $author) {$this->author = $author;}}

Page 20

コンストラクタプロモーション(PHP8)

class Book{function __construct(private Author $author) {// 何も書かなくてもプロパティに代入される

}}

Page 21

readonlyプロパティ(PHP8.1)

class Book{function __construct(public readonly Author $author) {// 何も書かなくてもプロパティに代入される

}}

Page 22

かくしてPHP PHPDocの型が不要な世界に

Page 23

Bookクラスは著者を持つ

上司「共著を想定できてる?」

class Book{function __construct(private Author $author) {// 何も書かなくてもプロパティに代入される

}}

Page 24

Bookクラスは複数の著者を持つ

arrayにすることで複数であることを表現

class Book{function __construct(private array $authors) {// 何も書かなくてもプロパティに代入される

Authorクラスの情報が減ってる…

}}

Page 25

著者一覧を出力したい

Book::$authors : array

中身が何かわからない!

Authorクラスのメソッドを補完できない

class Book{function printAuthors($fp): void {foreach ($this->authors as $author) {fwrite($fp, $author->getName());}}}

Page 26

何か大事なものを失ってしまった気がする

Page 27

今回は配列に絞って話します

Page 28

PHPの配列は万能

Page 29

PHPの配列はなんでもできる

Page 30

PHPマニュアルより引用

Page 31

PHPの配列はハッシュテーブル

Page 32

JavaScript/Rubyの場合

Page 33

PHPの場合

Page 34

何が違うのか

Page 35

他の言語における配列(array)

  • C言語における配列はメモリ上の連続した領域に並べたもの

(ポインタとかアドレスとかの概念が絡んでくる)

  • インデックスは0が始点、長さ(値の数)は最初に決める
  • JavaScriptやRubyでの配列は複数の値を格納できるオブジェクト
  • インデックスは0が始点、動的に拡張される
  • 長さ以上のインデックスに代入されると、間の値はnull的な値で埋められる

Page 36

PHPの配列はハッシュテーブル

  • PHPの配列は基本的にハッシュテーブルというデータ構造
  • 任意の int または string の値をキーにできる
  • 長さ以上に代入しても間がnullで埋められることはない
  • 連続していない前提の配列を特に「連想配列」と呼ぶ
  • 英語ではassociative array (略してassoc)などと呼ばれる
  • PHPだけではなくLuaという言語も似た仕様

Page 37

PHP7の内部実装から学ぶ性能改善テクニック

Page 38

PHP7から内部的に「配列」が入った

  • 2015年リリースのPHP 7.0ではデータ構造に大きく手が入った
  • $a = [1, 2, 3] のような値は(C言語的な意味)の配列として保持される
  • 既存の配列に $a['str'] = 'val' のように追加するとき

ハッシュテーブルに詰め替えられる

Page 39

連想配列 vs リスト

  • PHPでは区別されないが、たとえば値をjson_encode()する時など

連想配列と連続した値を厳密に比較したいことはありうる

  • JSONでは歯抜けの値があると [] ではなく {} になってしまう
  • array_filter() の結果など
  • array_values() で歯抜けのない配列に変換できる
  • PHP 8.1で array_is_list() で判定できる

Page 40

array_is_list()について

  • PHP 8.1で追加された関数
  • 従来は 0 から順にforeachして歯抜けがないかチェックする必要があった
  • 静的解析ツール(PHPStan, Psalmなど)ではPHPDocに

array<int,string> の代りに list<string>と書ける

  • 最近のPhpStormでは(検査は不十分だが)認識してくれる
  • Intelephenseは未実装

Page 41

Authorsはどうなればよかったのか

Page 42

Bookクラスは複数の著者を持つ

class Book{function __construct(

private array $authors) {// 何も書かなくてもプロパティに代入される

}}

Page 43

PHPDocの帰還

class Book{function __construct(/** @var list<Authors> */private array $authors) {// 何も書かなくてもプロパティに代入される

}}

Page 44

配列はひとつ!じゃない!!

list<int>

array<int,string>

array{id:int, name:string}

  • ユーザIDがならんだリスト [123, 456]
  • ユーザIDと名前の対応表 [123 => '野比のび太', 234 => '源静香']
  • ユーザを表す構造体 ['id' => 123, 'name' => '野比のび太']
  • ユーザを表す構造体がならんだリスト

[['id' => 123, 'name' => '野比のび太'],['id' => 234, 'name' => '源静香'],]

list<array{id:int, name:string}>