Slide 1

Slide 1 text

Shogo Kawase / @shogogg Oct. 11 2025 PHP Conference Hiroshima 2025 PHPに関数型の魂を宿す 〜PHP 8.5 で実現する堅牢なコードとは〜

Slide 2

Slide 2 text

2025年11月20日

Slide 3

Slide 3 text

🎉 PHP 8.5 リリース! (予定)

Slide 4

Slide 4 text

PHP 8.5 のステキ機能を広めたい!! 発表の目的

Slide 5

Slide 5 text

PHP 8.5 の新機能(抜粋) ● Pipe operator v3 ● Marking return values as important (#[\NoDiscard]) ● Make OPcache a non-optional part of PHP ● RFC 3986 and WHATWG URL compliant API ● Clone with v2 ● Grapheme cluster for levenshtein, grapheme_levenshtein function ● Support Closures in constant expressions ● First Class Callables in constant expressions

Slide 6

Slide 6 text

PHP 8.5 の新機能(抜粋) ● Pipe operator v3 ● Marking return values as important (#[\NoDiscard]) ● Make OPcache a non-optional part of PHP ● RFC 3986 and WHATWG URL compliant API ● Clone with v2 ● Grapheme cluster for levenshtein, grapheme_levenshtein function ● Support Closures in constant expressions ● First Class Callables in constant expressions 今日はこの2つについてお話します!

Slide 7

Slide 7 text

自己紹介 河瀨 翔吾 / Shogo Kawase 株式会社 PR TIMES ソフトウェアエンジニア(2024.12〜) I LOVE... 妻 / 型安全 / アジャイル / ももいろクローバーZ F1 / マリオカート shogogg shogogg

Slide 8

Slide 8 text

We are Hiring!!

Slide 9

Slide 9 text

おしながき 1 プログラミング言語の分類とPHPの歴史 2 パイプ演算子 3 NoDiscard アトリビュート 4 まとめ

Slide 10

Slide 10 text

おしながき 1 プログラミング言語の分類とPHPの歴史 2 パイプ演算子 3 NoDiscard アトリビュート 4 まとめ

Slide 11

Slide 11 text

プログラミング言語の分類(パラダイム) ● 命令型 ○ 手続き型言語 ○ オブジェクト指向言語 ● 宣言型 ○ 関数型言語 ○ 論理型言語

Slide 12

Slide 12 text

命令型プログラミング言語 ● 実行すべき命令や手続きを順に記述していくことでプログラムを表現 ● プロシージャ・サブルーチン・関数などで処理をひとまとめにする ● 代表例:C / COBOL / BASIC など 手続き型言語 ● データと手続きをまとめた「オブジェクト」を組み合わせてプログラムを表現 ● クラスを用いるのが主流だが、JavaScript などのプロトタイプ方式を用いる言語も ● 代表例:C++ / Java / JavaScript など オブジェクト指向言語

Slide 13

Slide 13 text

宣言型プログラミング言語 ● 数学的な「関数」と「式」の組み合わせでプログラムを表現 ● データの不変性や関数の参照等価性を重視し、副作用を厳密に制御する(ことが多い) ● 代表例:Haskell / LISP / OCaml / Elixir など 関数型言語 ● 事実と規則を定義し、それらに基づいて質問に答える形でプログラムを実行 ● 処理を記述するのではなく、知識を記述 ● 代表例:Prolog 論理型言語

Slide 14

Slide 14 text

マルチパラダイム言語 ● オブジェクト指向 + 関数型など、複数の特徴を併せ持つ言語 ● 現代ではオブジェクト指向に関数型のエッセンスを加えた言語が多い 例)Scala / Kotlin / Rust ● PHP もこのマルチパラダイム言語に分類される

Slide 15

Slide 15 text

関数型言語について

Slide 16

Slide 16 text

第一級関数(First-Class Functions)と高階関数 変数に関数を代入可能 / 引数に関数を渡せる / 関数の戻り値として関数を返せる 副作用の排除と参照等価性 関数は同じ引数に対して必ず同じ戻り値を返し、スコープ外の状態を変更しない データの不変性(Immutability) 一度定義した変数の書き換えや、破壊的な操作を避ける 文(Statement)ではなく式(Expression)が中心 手続きを実行するための「文」ではなく、常に値を返す「式」で構成される 関数型言語の持つ特徴

Slide 17

Slide 17 text

なぜ関数型に注目が集まるのか(1) 2000年代 ● CPU のクロック周波数が頭打ちになり、マルチコア化 が進む ● アプリケーションも処理性能向上のため並列処理・並行処理 のニーズが増加 ● 複数のコア(スレッド)から状態を参照・変更する命令型パラダイムでは 複雑な排他制御が必要となり、バグの温床に 2010年代 ● データの不変性 と副作用の排除 を原則とする関数型に注目が集まる ● 多くの言語が関数型のエッセンスを取り入れはじめる

Slide 18

Slide 18 text

なぜ関数型に注目が集まるのか(2) 2020年代 ● AI によるコーディング支援 や AI エージェント が爆発的に普及 ● 手続きを記述し、状態を管理する命令型のコードに比べ、参照等価性を持ち 宣言的に記述された関数型(またはそれに近い)コードは少ないコンテキス ト消費で理解・処理が可能 ● 完全な関数型言語でなくても、そのエッセンスを取り入れることで AI フレン ドリーなコードを書くことができる!

Slide 19

Slide 19 text

PHPの歴史

Slide 20

Slide 20 text

PHPの歴史(1) PHP Tools :1995年 ● プログラミング言語というよりも名前の通り「ツール」に近いものだった PHP/FI :1996年 ● 変数や関数の概念が登場、手続き型言語 の特徴を持ち始める PHP 3:1998年 ● if 文や for 文などが導入され、本格的な手続き型言語へと進化 ● クラスなどオブジェクト指向言語 の要素を取り入れ始める PHP 4:2000年 ● Zend Engine が導入される

Slide 21

Slide 21 text

PHPの歴史(2) PHP 5:2004年 ● メソッド・プロパティの可視性、インターフェース、抽象クラスなど 本格的なオブジェクト指向言語 として記述できるようになる PHP 6 ● 開発が断念された幻のバージョン PHP 7:2015年 ● PHP 7.4 でアロー関数式 が導入され、関数を簡潔に記述できるように PHP 8:2020年 ● readonly property/class など、データの不変性を担保する仕組みが強化

Slide 22

Slide 22 text

ここまでのまとめ ● PHP は個人のWEBサイトを拡張するツールから手続き型言語を経て オブジェクト指向言語 へと進化してきた ● PHP 7 以降、アロー関数式 や(限定的ながら)データの不変性 のサポートが 加わり、関数型言語 のエッセンスを取り入れ始めている ● AI がコードを書く時代になり、コンテキストの消費が少なく、間違いが 起こりにくい宣言的なコードの価値が向上 している

Slide 23

Slide 23 text

1 プログラミング言語の分類とPHPの歴史 おしながき 2 パイプ演算子 3 NoDiscard アトリビュート 4 まとめ

Slide 24

Slide 24 text

パイプ演算子 ● 左辺の値を右辺の関数に渡し、その結果を返す演算子 ● ML 系言語で初めて導入され、F# が採用 したことでその有用性が広まる ● Elixir にも取り入れられ、JavaScript でも導入を検討中

Slide 25

Slide 25 text

hello_world = " Hello World!! " # パイプ演算子を使わない場合 IO.puts(String.upcase(String.trim(hello_world))) # -> HELLO WORLD !! # パイプライン演算子( |>)を使った場合 hello_world |> String.trim |> String.upcase |> IO.puts # -> HELLO WORLD !! Elixirのパイプ演算子

Slide 26

Slide 26 text

$hello_world = ' Hello World!! '; // パイプ演算子を使わない場合 echo strtoupper(trim($hello_world)); // -> HELLO WORLD !! // パイプライン演算子( |>)を使った場合 echo $hello_world |> trim(...) |> strtoupper(...); // -> HELLO WORLD !! PHPのパイプ演算子

Slide 27

Slide 27 text

パイプ演算子のメリット データの流れと記述の順番が一致する ● より直感的で、読みやすいコード の記述が可能になる 中間変数を使わずに済む ● これまで可読性のために導入していた「中間変数」をなくせるようになる ● コードがシンプルになり、変更しやすいコード になる ● 人間や AI の「コンテキスト消費」を抑え、理解しやすいコード が書ける

Slide 28

Slide 28 text

データの流れと記述の順番が一致する

Slide 29

Slide 29 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo htmlentities(trim(implode(array_map(ucfirst(...), $input)))); // -> Alice & Bob 「データの流れと記述の順番が一致する」とは なんだかとっても読みづらい

Slide 30

Slide 30 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo htmlentities( trim( implode( array_map(ucfirst(...), $input) ) ) ); // -> Alice & Bob 「データの流れと記述の順番が一致する」とは 改行すれば少しはマシに……なってる?

Slide 31

Slide 31 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo htmlentities( trim( implode( array_map(ucfirst(...), $input) ) ) ); // -> Alice & Bob 「データの流れと記述の順番が一致する」とは 処理の流れと記述の順番が逆 → 読みづらい!

Slide 32

Slide 32 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo $input |> fn ($array) => array_map(ucfirst(...), $input) |> implode(...) |> trim(...) |> htmlentities(...); // -> Alice & Bob 「データの流れと記述の順番が一致する」とは 処理の流れと記述の順番が一致 → 読みやすい!

Slide 33

Slide 33 text

中間変数を使わずに済む

Slide 34

Slide 34 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo htmlentities( trim( implode( array_map(ucfirst(...), $input) ) ) ); // -> Alice & Bob 改行しただけでは読みにくいので…… 「中間変数を使わずに済む」とは

Slide 35

Slide 35 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; $tokens = array_map(ucfirst(...), $input); $joined = implode($tokens); $trimmed = trim($joined); $escaped = htmlentities($trimmed); echo $escaped; // -> Alice & Bob 「中間変数を使わずに済む」とは 中間変数を導入してコードを整理することがある

Slide 36

Slide 36 text

$input = [' ', 'alice', ' ', '&', ' ', 'bob', ' ']; echo $input |> fn ($array) => array_map(ucfirst(...), $input) |> implode(...) |> trim(...) |> htmlentities(...); // -> Alice & Bob 「中間変数を使わずに済む」とは 中間変数がなくなってスッキリ?

Slide 37

Slide 37 text

中間変数がもたらすメリットとデメリット 変数が増える=考えることが増える ● 適当な変数名では読み手に混乱を与える → 変数名を考える手間が生じる ● 変数が増えると、それぞれが「どこで」「どのように」参照されているのかを 考える必要が生じる → 認知負荷が高くなってしまう ● 特にリファクタリングでは「この変数の中身が変わったらどうなる?」「この変数を 消したらどうなる?」など確認することが増える → 変更容易性が下がる デバッグがしやすい ● 処理が複数の代入式で構成されるため、ブレークポイントが置きやすい ● 処理の途中結果が変数として参照できるため、何が起きているのか確認しやすい

Slide 38

Slide 38 text

中間変数 vs パイプ演算子 ● ブレークポイントが置きやすい ● デバッグ中に処理途中のデータが確認 しやすい 中間変数 ● 中間変数の名前を考える手間がない ● 記述がシンプルになり、変更容易性に 優れる パイプ演算子 一概にどちらが優れているとは言いづらい

Slide 39

Slide 39 text

パイプ演算子:まとめ ● F# や Elixir と同じパイプ演算子 が PHP 8.5 で導入される! ● パイプ演算子は変更容易性の高いシンプルなコード を実現する新たな手段 ● 従来の「中間変数」を用いた書き方を完全に置き換えるものではなく コードの内容や文脈によってうまく使い分けたい

Slide 40

Slide 40 text

1 プログラミング言語の分類とPHPの歴史 おしながき 2 パイプ演算子 3 NoDiscard アトリビュート 4 まとめ

Slide 41

Slide 41 text

NoDiscard アトリビュート ● #[\NoDiscard] というアトリビュートが設定された関数やメソッドの戻り 値を無視する(捨てる)と Warning が発生するようになる ● flock や DateTimeImmutable::setDate() など一部の組み込み関数・ メソッドにも NoDiscard アトリビュートが設定され、戻り値を捨てている と Warning が発生するように ● 他言語の例としてはC言語の [[nodiscard]] アトリビュートや Rust の #[must_use] アトリビュートなど

Slide 42

Slide 42 text

#[\NoDiscard('DO NOT DISCARD THE RESULT')] function hello(): string { return 'Hello World!!'; } // OK $result = hello(); // OK echo hello(); // OK (void)hello(); // Warning: The return value of function hello() should either be used or intentionally ignored by casting it as (void), DO NOT DISCARD THE RESULT hello(); NoDiscard アトリビュートの例

Slide 43

Slide 43 text

NoDiscard アトリビュートをいつ使う? 処理の成功・失敗を戻り値で返す関数 ● 失敗した場合のリカバリー処理が書かれていないコードを検知 リソース(またはそれを表すオブジェクト)を返す関数 ● 明示的な利用と解放を促す 副作用を持たない純粋関数 ● 戻り値を無視している=無駄な計算をしているコードを検知 問題のありそうなコードを検知できるようになる!

Slide 44

Slide 44 text

ところで

Slide 45

Slide 45 text

Result型、流行ってますね! ● 成功/失敗を例外ではなく戻り値で表現する Result 型 が近年流行している ● Rust が導入し、Swift も採用したことで一気に普及・流行 ● Haskell や Scala など関数型言語の Either 型 が原型 ● 他の言語でもサードパーティーライブラリとして実装する例が増えている ○ neverthrow(TypeScript) ○ graham-campbell/result-type(PHP) ○ returns(Python) ○ kotlin-result(Kotlin)

Slide 46

Slide 46 text

PHPとResult型 ● PHP には総称型(Generics)がないのでそのまま輸入しても使いづらい ● しかし工夫次第では NoDiscard アトリビュートと組み合わせることで 検査例外のような強力なエラーハンドリングが実現できるのでは?

Slide 47

Slide 47 text

検査例外? ● Java では特定の種類の例外は try ... catch で処理しないとコンパイル時点で エラーとなる(検査例外) ○ IOException(ファイル入出力時に発生するエラー) ○ SocketException(ネットワーク通信中に発生するエラー) ○ SQLException(DB 操作時に発生するエラー) ● プログラマによる実装が不十分であり、コードに問題があるような場合は catch しなくてもエラーとならない(非検査例外) ○ NullPointerException(null に対する不正な操作) ○ IllegalArgumentException(不正な引数) ○ ArrayIndexOutOfBoundsException(配列の範囲外にアクセス)

Slide 48

Slide 48 text

PHPと例外 ● PHP の例外はすべて非検査例外 であるため、catch しなくてもコンパイル時 点ではエラーとならない ● そのため、適切なリカバリー処理が記述されていないコードもそのまま実行 されてしまう

Slide 49

Slide 49 text

そこで

Slide 50

Slide 50 text

/** DoSomething 関数の結果を表す型 */ interface DoSomethingResult {} /** DoSomething 関数が成功した場合の戻り値 */ final class DoSomethingSuccess implements DoSomethingResult { public function __construct(public readonly string $value) {} } /** DoSomething 関数がファイル入出力に失敗した場合の戻り値 */ final class DoSomethingFileIOFailure implements DoSomethingResult {} /** DoSomething 関数がネットワーク通信に失敗した場合の戻り値 */ final class DoSomethingNetworkIOFailure implements DoSomethingResult {} PHP × Result型 × NoDiscard アトリビュート

Slide 51

Slide 51 text

#[/NoDiscard] function doSomething(): DoSomethingResult { // ファイルの読み書きに失敗したら DoSomethingFileIOFailure を返す // ネットワーク通信に失敗したら DoSomethingNetworkIOFailure を返す // 成功したら DoSomethingSuccess を返す } $result = doSomething(); match (true) { $result instanceof DoSomethingSuccess => // 成功時の処理 $result instanceof DoSomethingFileIOFailure => // ファイル入出力失敗時の処理 $result instanceof DoSomethingNetworkIOFailure => // ネットワーク通信失敗時の処理 }; PHP × Result型 × NoDiscard アトリビュート

Slide 52

Slide 52 text

● NoDiscard アトリビュートにより、戻り値を無視すると Warning が発生 → 戻り値の処理が強制できる ● 型を確定させるまでプロパティやメソッドへのアクセスを制限(静的解析) → 戻り値の型検査によるエラー制御を強制できる ● match 式を使うことで型検査の漏れを実行時エラーにできる → エラー制御の網羅性が担保できる ● 引数チェックなどは従来通り例外を投げることで検査例外と非検査例外を区 別する →「例外が起きたらバグ」という状態を実現できる PHP × Result型 × NoDiscard アトリビュート

Slide 53

Slide 53 text

NoDiscard アトリビュート:まとめ ● 関数やメソッドの戻り値を無視すると Warning を発生させられるように ● 戻り値を無視している=問題のありそうなコードを検知できるようになるこ とで、より安全で堅牢なコード が実現できる ● Result 型のような、戻り値でエラーを知らせる仕組みと相性がよさそう

Slide 54

Slide 54 text

1 プログラミング言語の分類とPHPの歴史 おしながき 2 パイプ演算子 4 まとめ 3 NoDiscard アトリビュート

Slide 55

Slide 55 text

本日のまとめ PHP はマルチパラダイム言語 ● PHP はオブジェクト指向を軸に様々な言語の「いいとこ」取りをしている ● 関数型の考え方をうまく適用すると AI にもやさしい読みやすいコードになる PHP 8.5 で起きること ● パイプ演算子や NoDiscard アトリビュートなど現代的なプログラミング言語 のエッセンスが追加される ● それらをうまく使うことで、これまで以上にシンプルなコード、堅牢なコード になる可能性を秘めている ● 他にもステキなアップデートがたくさん!ちゃんとバージョンアップしよ う!

Slide 56

Slide 56 text

おまけ(時間が余ったら話す)

Slide 57

Slide 57 text

$normalize = fn (string $input): string => $input |> trim(...) |> strtoupper(...) |> htmlentities(...); パイプ演算子を使って関数合成っぽいこと(?)

Slide 58

Slide 58 text

$normalize = fn (string $input): string => $input |> trim(...) |> strtoupper(...) |> htmlentities( ?, // <- これ flags: ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, encoding: 'Shift_JIS', ); 次の PHP (8.6?9.0?) で関数の部分適用が来る?