PHP 勝手なプロパティ追加の防止法 (コラム)


PHP 勝手なプロパティ追加の防止法 (コラム)

既定の設定では、PHPのクラスオブジェクトは、
インスタンス化した後、
クラス設計にないプロパティを勝手に付け足すことができます。

追加実例

コンストラクターとプロパティ宣言を一体化させた書き方をします(PHP8に準拠)。
以下のコードでは、勝手に追加したtrue_priceというプロパティが設定されて表示されています。
クラス設計(クラスのコードに記載されている)にないプロパティが設定されることは、
クラス設計の意味を失くしてしまうので、防ぐべきです。

<?php
class Product
{
    public function __construct(
        private int $id,
        private String $name,
        private int $price,
        private String $description
    )
    {
        
    }
}

// クラス設計通りにインスタンス化
$sample_product = new Product(0, "apple juice", 100, "apple juice.");

// 勝手にプロパティを追加
$sample_product->true_price = 500;

// プロパティに値が追加されてしまっている
echo "sample_product_true_price : ".$sample_product->true_price;

?>

防ぎ方

定義されていないプロパティを勝手に追加しているとき、
実は裏側で、マジックメソッドの__set()が動いています。

自作クラスは、暗黙の継承関係があります。
ですので自作クラスには、親クラスのstdClass(スタンダードクラスの略)の__set()が、
そのまま継承されています。
この親クラスの__set()で、プロパティの自動追加がされてしまうのです。

これを防ぐために、自作クラスで、マジックメソッドの__set()を明示的にオーバーライドします。
クラス定数で許されるプロパティ名の一覧をホワイトリスト式に宣言し、
それにないプロパティに値を設定する場合は、__setメソッドの中で拒絶させます。
以下のコードが実例です。
エラーが起こって、勝手にプロパティ追加しようとしても、処理が途中で止まってしまいました。

<?php
class Product
{
    // クラス定数で、許されるプロパティ名をホワイトリスト式に宣言
    const VALID_PROPS = ["id", "name", "price", "description"];
    
    // コンストラクター
    public function __construct(
        private int $id,
        private String $name,
        private int $price,
        private String $description
    )
    {
        
    }
    
    // マジックメソッド__setをオーバーライドして、バリデーション
    public function __set(String $name, mixed $value)
    {
        if(in_array($name, self::VALID_PROPS, true))
        {
            $this->$name = $value;
            return;
        }
        throw new ErrorException("unallowed property setting.");
    }
}

// クラス設計通りにインスタンス化
$sample_product = new Product(0, "apple juice", 100, "apple juice.");

// 勝手にプロパティを追加
$sample_product->true_price = 500;

// プロパティに値が追加されない
echo "sample_product_true_price : ".$sample_product->true_price;
?>

  • このエントリーをはてなブックマークに追加

PAGE TOP