Attributeを極める

公開日:

オンラインZoomウェビナーで開催された『PHPerKaigi 2023』でライトニングトーク(5分)として発表しました。

Download PDF

スライドテキスト

Page 1

Attributeを極める

増補版

Mastering Attributes

pixiv Inc.
USAMI Kenta

2022-03-25

Page 2

お前誰よ

  • うさみけんた (@tadsan) / Zonu.EXE / にゃんだーすわん
  • ピクシブ株式会社 pixiv事業本部 エンジニア
    • 最近はピクシブ百科事典(dic.pixiv.net)を開発しています
  • Emacs Lisper, PHPer
    • Emacs PHP Modeを開発しています (2017年-)
  • プログラミング言語にちょっとこだわりのある素人

Page 3

ゴランノスポンサー

Page 4

先におことわり:
20分ではピクシブ百科事典の
事例紹介までたどりつけませんでした
(話はするけどあっさりめ)

Page 5

さて

Page 6

PHPのアップデートサイクル

  • PHPは年に一回バージョンアップされている
    • https://www.php.net/supported-versions.php
    • 最新バージョンは2022年12月リリースの8.2.x系
  • いわゆるセマンティックバージョニングではない
    • 8.2.0… MAJOR.MINOR.RELASEの3レベル構成
    • 影響の大きなBC BreakはMAJOR

Page 7

PHP8.0で
変わったこと

Page 8

Page 9

すべての機能を
使えていますか?

Page 10

その中でも存在感が
ありながら
「何に使うの」度が高い

Page 11

Page 12

Page 13

ア ト リ ビ ュ ー ト

Attributes

Page 14

実はPHPerKaigiでは

Page 15

Page 16

Page 17

毎年なにかと
言及されている

Page 18

アトリビュートってなんだ?

  • 日本語での定訳としては「属性」
  • もっとも知られているであろう用語としてはHTMLタグ
  • あるもの(要素)の性質を表す情報のこと
  • MDNではHTMLの属性について次のように説明している
    • 「属性は要素を拡張し、動作を変更したりメタデータを提供したりします。」
      Attribute (属性) - MDN Web Docs 用語集 より

Page 19

<a href="?foo">

Page 20

<a href="?foo">

属性名

Page 21

<a href="?foo">

属性名 属性値

Page 22

<a href="?foo">

属性名 属性 属性値

Page 23

<a href="?foo">

要素名

Page 24

<a href="?foo">

要素名 (開始)タグ

Page 25

<a href="?foo">
text
</a>

<a>要素

Page 26

今回はHTMLは 本題ではないので
ここまで

Page 27

ソースコードにも
付加的な情報を
付与したい!

Page 28

付加的な情報…
われわれは知っている

Page 29

Docコメント

/**
* @param positive-int $id
*/
function Pnd(int $id): Book
{

Page 30

コードに書きたい付加情報とは何か

人間だけわかれば

  • 人間に意図を伝えるためのドキュメント(コメント)

ヨシ

人間もわかって
機械可読

  • 静的解析ツールが情報を読み取ってコードを検査するための情報
  • 実行時に可読で
    フレームワークなどが情報を読み取って振る舞いを制御する

効率高いとうれしい

  • プログラミング処理系(PHP)に特別な処理をさせるための指定

構文の一部である
必要がある

Page 31

コードに書きたい付加情報とは何か

人間だけわかれば

  • 人間に意図を伝えるためのドキュメント(コメント)

ヨシ

人間もわかって
機械可読

  • 従来Docコメントが賄っていた領域
    静的解析ツールが情報を読み取ってコードを検査するための情報
  • 実行時に可読で
    フレームワークなどが情報を読み取って振る舞いを制御する

効率高いとうれしい

  • プログラミング処理系(PHP)に特別な処理をさせるための指定

構文の一部である
必要がある

Page 32

DocCommentは自由帳

  • 事実上、なんでも書ける
  • なにを書いても怒られないが、@hogeのような形式が一般的
  • phpDocumenterスタイルの「タグ」
    • @param int $foo (スペース区切り)
  • Doctrineスタイルの「アノテーション」
    • @Annotation("arg", 1, 2, 3)

Page 33

なぜDocコメント…

Page 34

改めてDocコメント

Page 35

実行時に読める不思議なコメント

  • 関数やクラスなどの直前に /** ... */ のような形式でコメントを書くと、
    リ フ レ ク シ ョ ン
    ReXectionという機能を使って読み取れる
    • Re:ection::getComment() で文字列で取得できる
  • 実行時とかにいい感じに情報を読み取って処理をしてやるとメタプログラミ
    ングが実現できる

Page 36

Page 37

メタ?

Page 38

さっきも出てきた

Page 39

アトリビュートってなんだ?

  • 日本語での定訳としては「属性」
  • もっとも知られているであろう用語としてはHTMLタグ
  • あるもの(要素)の性質を表す情報のこと
  • MDNではHTMLの属性について次のように説明している
    • 「属性は要素を拡張し、動作を変更したりメタデータを提供したりします。」
      Attribute (属性) - MDN Web Docs 用語集 より

Page 40

Wikipedia曰く

Page 41

https://ja.wikipedia.org/wiki/メタ
2022-12-14T19:41:26版より

Page 42

https://ja.wikipedia.org/wiki/メタ
2022-12-14T19:41:26版より

Page 43

つまり?

Page 44

メタデータ
データについてのデータ

Page 45

例:
写真 に対する
(画像データ)
「撮影場所」や「日時」などの
情報

Page 46

例:
楽曲 に対する
(音声データ)
「演奏者」や「ジャンル」など
の情報

Page 47

メタプログラミング プログラムについての
プログラミング

Page 48

メタプログラミングってつまり何

  • プログラムそのものを対象にするプログラミング
    • クラスや関数などを動的に呼び出したり
    • ある処理をしたいとき、コードに直接的に書かなくても実現したりできる
    • 明示的に定義してなくてもなんか動いてたりするやつ
  • フレームワークのようなコードは大なり小なりメタプロ的な要素がある
  • ブラックマジック
    理窟がよくわからんけど動いてたりするのはこれ (魔法とか黒魔術とか…)

Page 49

メタプロするなら コードから情報を
読み取れるとうれしい

Page 50

(それが全てではない)

Page 51

ということで

Page 52

Page 53

Page 54

基本的な定義方法

use Attribute;

#[Attribute(
Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE
)]
class MyAttr {

Page 55

基本的な使いかた

use MyAttr;

#[MyAttr('foo', 'bar', 'buz')]
class Book
{

Page 56

名前付きの呼び出しも可能

use MyAttr;

#[MyAttr(a: 'foo', b: 'bar', c: 'buz')]
class Book
{

Page 57

Attributeはどこに書けるの?

  • 現在Re:ectionから読み取れるのは6箇所

宣伝

  • クラス
  • 関数
  • メソッド
  • プロパティ
  • パラメータ (仮引数)

Page 58

これでみなさまは
Attributeを定義でき
るようになりました

Page 59

ね、簡単でしょ?

Page 60

Docコメントと
何が違うの?

Page 61

比べてみよう

Page 62

実行時に読める不思議なコメント

  • 関数やクラスなどの直前に /** ... */ のような形式でコメントを書くと、
    リ フ レ ク シ ョ ン
    ReXectionという機能を使って読み取れる
    • Re:ection::getComment() で文字列で取得できる
  • 実行時とかにいい感じに情報を読み取って処理をしてやるとメタプログラミ
    ングが実現できる

Page 63

実行時に読める不思議なXXXX

  • 関数やクラスなどの直前に #[Attr()] のような形式のコードを書くと、
    リ フ レ ク シ ョ ン
    ReXectionという機能を使って読み取れる
    • Re:ection::getAttributes() でRe:ectionAttributeを取得できる
  • 実行時とかにいい感じに情報を読み取って処理をしてやるとメタプログラミ
    ングが実現できる

Page 64

Q. 何が違うの?

Page 65

A. 違うよ、全然違うよ

Page 66

実際に見てみましょう

Page 67

Page 68

ReflectionAttribute

  • getAttributes() メソッドでとれるクラス
    • $ref = new Re:ectionClass(Target::class);
    • $ref->getArguments() で値だけ取れる
    • $ref->newInstance() でアトリビュートをインスタンス化できる

Page 69

何が変わったのか

  • 明示的にRe:ectionしないと何も起こらないのはDocコメントと同じ
    • Attributeを書くだけなら、ただのコメントと同じ
  • 引数の区切りがとても簡単
    • Docコメントでは自前で構文解析しないといけない
  • 静的解析ツールが自然にサポートしてくれている
    • Docコメントでは変な記述をしても気付きにくい

Page 70

これでAttributeの
機能は全部です!

Page 71

完全マスター

🎉

Page 72

機能はめっちゃ
シンプル

Page 73

あとはどういう
「想い」を載せるか

Page 74

創意工夫が大事

Page 75

とはいえ定石はある

Page 76

Page 77

DI vs Attribute

  • 山岡さんの2021年の発表で紹介された事例
  • 依存性注入したいデータを明示するために使う

Page 78

Page 79

DB vs Attribute

  • おかしょいさんの2022年の発表で紹介された事例
  • データベースとPHPのクラスのプロパティを紐付けるために使う

Page 80

バリデーション vs Attribute

  • 2022年11月に私が書いた記事
  • アトリビュートは自然に発火しないので明示的に呼び出す必要がある

Page 81

PHPUnit vs Docコメント

  • PHPUnitはこれまでDocコメントによってテストの実行を制御できた
    • @dataProvider, @testWith … パラメタライズドテスト
    • @runInSeparateProcess, @runTestsInSeparateProcess
      … プロセスを隔離して実行
    • @covers … カバレッジターゲットの制御
    • @group … テストグループの宣言
  • Doctrine式のAnnotationではなくスペース区切りが多かった

Page 82

PHPUnit 10 vs Attribute

  • PHPUnit 10でAttributeに移行、11で非推奨化、12で削除予定
  • 超大量の移行リスト… sebastianbergmann/phpunit#4502
  • その他の変更は02氏の
    発表を参照のこと

Page 83

明示 vs 黙示

Page 84

「こっそりやる」vs「堂々とやる」

  • PHPにはマクロやプリプロセッサがないので、明示的が基本ではある
  • しかし、クラスローダーを悪用してソースコードを書き換えてしまう荒技も
    • Go! AOPなどのAOPフレームワークはクラスローダーでフックする

Page 85

PHPの後方互換性 vs Attribute

  • PHPでは継承するときに型宣言を厳密に合わせないといけない
  • PHP 8ではPHPの組み込みクラスにも型宣言が追加された
  • そうするとPHP 7/8両対応のライブラリで非常に困る
  • #[ReturnTypeWillChange] と書くと局所的に無視できるようになった
    • 問題の先送りができてうれしい!

Page 86

ひっそり佇むAttribute

  • そこにあるだけでは何も起こさないというのは重要な性質
  • # はPHPのコメント行なので、PHP 7以下のコードに書いても問題ない
  • ということは… PHP 7以下でもパフォーマンスを気にしなければ
    Attributeを使える
    • spiral/attributes というライブラリがある
    • 開発用途やデプロイスクリプトなどで使う分には問題にならない

Page 87

doctrine/annotations

  • Docコメントのアノテーションを読み取るライブラリ
  • Attributeとだいたい同じ使い方できるが仕様が微妙に異なる
    • デフォルトでは引数は __construct(array $values) として
      一個の配列にまとめて渡されてしまう
    • クラスに @Annotation @namedArgumentConstructor と書くと
      Attributeと同様に個別のパラメータで受け取れるようになる

Page 88

spiral/attributesの互換性

  • PHP 7で利用できるAttributeを読み取るライブラリ
    • AnnotationとAttributeを相互運用できる
      • doctorine/annotationsのラッパーとしてPHPDocも読める
    • Annotationとの相互運用のためか、デフォルトでは引数がまとめて
      __construct(array $values) に渡されてしまう
      • クラスに #[Spiral Attributes NamedArgumentConstructor]と

\ \

書くと通常のAttributeと同じ仕様で使える

Page 89

ピクシブ百科事典 vs Attribute

  • コントローラにルーティング情報が書ける
  • これは実行時に読まず、スクリプトでファイルに書き出す方式

Page 90

キミだけの最高の
ユースケースを
見付けだせ