Produced by Fourier

PHP標準のアトリビュートについて

Kyouhei Horizumi icon Kyouhei Horizumi

はじめに

最近Laravelのコードを見ると、引数にアトリビュートが付いていることがあります。

例えば Illuminate\Auth\SessionGuard::rehashPasswordIfRequired のコードは以下の通りです。

protected function rehashPasswordIfRequired(AuthenticatableContract $user, #[\SensitiveParameter] array $credentials)
{
    if ($this->rehashOnLogin) {
        $this->provider->rehashPasswordIfRequired($user, $credentials);
    }
}

引数の $credentials#[\SensitiveParameter] というアトリビュート付いていることが分かります。

このようなPHP標準アトリビュートが他にもないかということで、調査してみました。

PHP 8.4現在の標準アトリビュート例

#[\SensitiveParameter]

#[\SensitiveParameter] を付けたパラメーターは、パラメーター値をエラートレースやログに表示しないようになります。

function test(string $name, #[\SensitiveParameter] string $secret) {
    throw new Exception("Error occurred");
}

try {
    test('Alice', 'super-secret');
} catch (Throwable $e) {
    echo $e;
}
Exception: Error occurred in script.php:3
Stack trace:
#0 script.php(7): test('Alice', Object(SensitiveParameterValue))

機密情報が含まれるパラメーターに対してこの設定を付けることで、「機密データがログに漏れないこと」を担保できます。 例えばクレジットカード番号をDBに保存しないのは当然ですが、ログの方に記録されていたというような事故を減らせます。

#[\ Override ]

#[\Override] を付けたメソッドは、親クラスやインターフェースに 同名のメソッドが存在しなければ 、PHPがFatal Errorを出します。

class ParentClass {
    public function greet() {}
}

class ChildClass extends ParentClass {
    #[\Override]
    public function grete() {} // ← 名前を間違えた
}
Fatal error: Method ChildClass::grete() has #[\Override] attribute, but no matching parent method found

Java 5の頃を思い出す、なじみ深い機能です。PHP 8.3(2023年)になってついに登場です。

#[\ Deprecated ]

PHP 8.4から、 #[Deprecated] アトリビュートを使って、機能の実行時にDeprecatedを出すことが出来ます。

#[\Deprecated(message: "Use doSomethingNew() instead", since: "8.4")]
function doSomethingOld() {
    // legacy code
}

以前は静的解析ツール用にPHPDocでマークするのが基本でしたが、ついに実行時に警告を出すことが出るようになりました。

/**
 * @deprecated Use doSomethingNew() instead.
 */
function doSomethingOld() {
    // legacy code
}

#[\ReturnTypeWillChange]

PHP 8.1から、インターフェースと戻り値の型があっていなかった場合にDeprectedが出るようになりました。それを抑制することが出来ます。

class MyClass implements \ArrayAccess {
    #[\ReturnTypeWillChange]
    public function offsetGet($offset) {
        // 実装
    }
}

一開発者からすると「使わずに型定義をすれば良いだけなのでは?」と思うかもしれませんが、これを使うと、PHP 8.0以下でも動く上にPHP 8.1でDeprecatedが出ないコードを書く事が出来ます。

#[ReturnTypeWillChange] // PHP 8.1 用
public function offsetGet($offset) /* PHP 8.0 以前の型なし互換 */

#[\ AllowDynamicProperties ]

PHP 8.2から、動的プロパティを追加するとDeprecatedが出るようになりました。それを抑制することが出来ます。

#[\AllowDynamicProperties]
class MyClass {}

$obj = new MyClass();
$obj->foo = 'bar';  // OK

今時の子は嫌だと思うかも知れませんが、PHPの特徴でもある柔軟性が必要であると考える人も一定数います。

この先は動的プロパティは非推奨になっていますが、明示的に許可すれば使えるということになります。

昔のPHP(特に5系)は、めちゃくちゃに書いても動く・誰でも書けるという印象が強かったのですが、そうでは無い方向性になりそうですね。 これはスコープや封じ込めなどを導入し始めている、CSSと同じ方向性を感じます。

さいごに

ライブラリで定義されたアトリビュートの活躍の場が増えていますが、PHP標準として定義済みのアトリビュートもいくつかあります。

今のところ数は少ないですが、覚えておいて損はないかと思います。