## プロパティ

クラスのメンバ変数のことを プロパティ といいます。 それ以外に フィールド などという呼びかたを見たことがあるかもしれません。 しかし、このマニュアルでは プロパティ と呼ぶことにします。 プロパティは、少なくともひとつのキーワード (たとえば アクセス権 のキーワード、 static キーワード、 PHP 8.1.0 以降のみ readonly) のあとに、 オプションの型宣言 (PHP 7.4 以降、但し `readonly` を除く) を続け、 その後に通常の変数の宣言を続けます。 宣言時に初期値を設定することもできますが、 初期値は 定数 値でなければなりません。

> 注意:

> クラスのプロパティを宣言する代替の方法として、 `var` キーワードを使う方法があります。

> 注意: アクセス権 を宣言しない場合、プロパティを `public` として宣言したとみなされます。

クラスのメソッドからstatic でないプロパティにアクセスするには `->` (オブジェクト演算子) を使って $this->property のようにします (`property` のところにプロパティ名を指定します)。 staticプロパティへのアクセスには `::` (ダブルコロン) を使って self::$property のようにします。 staticプロパティとそうでないプロパティの違いについては、 static キーワード を参照ください。

クラスのメソッドがオブジェクトのコンテキストからコールされたときには、 擬似変数 $this が使えます。 $this は、呼び出し元のオブジェクトの値です。

例1 プロパティの宣言

    <?php
    class SimpleClass
    {
       public $var1 = 'hello ' . 'world';
       public $var2 = <<<EOD
    hello world
    EOD;
       public $var3 = 1+2;
       // 無効なプロパティ宣言
       public $var4 = self::myStaticMethod();
       public $var5 = $myVar;

       // 有効なプロパティ宣言
       public $var6 = myConstant;
       public $var7 = [true, false];

       public $var8 = <<<'EOD'
    hello world
    EOD;
       // アクセス権を付けない場合
       static $var9;
       readonly int $var10;
    }
    ?>

> 注意:

> クラスやオブジェクトを操作するための便利な関数が用意されています。 クラス/オブジェクト関数 を参照ください。

### 型宣言

PHP 7.4.0 以降は、プロパティの定義に 型宣言 を含めることができます。 但し、`callable` 型を除きます。

例2 型付きプロパティの例

    <?php

    class User
    {
        public int $id;
        public ?string $name;

        public function __construct(int $id, ?string $name)
        {
            $this->id = $id;
            $this->name = $name;
        }
    }

    $user = new User(1234, null);

    var_dump($user->id);
    var_dump($user->name);

    ?>

上の例の出力は以下となります。

    int(1234)
    NULL

型付きプロパティは、アクセスする前に初期化しなければいけません。 初期化しないと、Error がスローされます。

例3 プロパティにアクセスする

    <?php

    class Shape
    {
        public int $numberOfSides;
        public string $name;

        public function setNumberOfSides(int $numberOfSides): void
        {
            $this->numberOfSides = $numberOfSides;
        }

        public function setName(string $name): void
        {
            $this->name = $name;
        }

        public function getNumberOfSides(): int
        {
            return $this->numberOfSides;
        }

        public function getName(): string
        {
            return $this->name;
        }
    }

    $triangle = new Shape();
    $triangle->setName("triangle");
    $triangle->setNumberofSides(3);
    var_dump($triangle->getName());
    var_dump($triangle->getNumberOfSides());

    $circle = new Shape();
    $circle->setName("circle");
    var_dump($circle->getName());
    var_dump($circle->getNumberOfSides());
    ?>

上の例の出力は以下となります。

    string(8) "triangle"
    int(3)
    string(6) "circle"

    Fatal error: Uncaught Error: Typed property Shape::$numberOfSides must not be accessed before initialization

### 読み取り専用プロパティ

PHP 8.1.0 以降では、`readonly` を付けてプロパティを宣言できます。これによって、プロパティを初期化した後に値が変更されることを防止できます。 PHP 8.4.0 より前のバージョンでは、`readonly` プロパティは暗黙的に private-set であり、 同じクラス内からのみ書き込みが可能でした。 PHP 8.4.0 以降では、`readonly` プロパティは暗黙的に `protected(set)` となり、 子クラスからも設定可能です。必要に応じて明示的に オーバーライドすることもできます。

例4 読み取り専用プロパティの例

    <?php

    class Test {
       public readonly string $prop;

       public function __construct(string $prop) {
           // 正しい初期化
           $this->prop = $prop;
       }
    }

    $test = new Test("foobar");
    // 正しいプロパティの読み取り
    var_dump($test->prop); // string(6) "foobar"

    // 不正な再代入です。同じ値を代入することは問題ありません。
    $test->prop = "foobar";
    // Error: Cannot modify readonly property Test::$prop
    ?>

> 注意:

> readonly は、 型付きプロパティ に対してのみ指定できます。 型の制約がないプロパティを読み取り専用にしたい場合、 Mixed が使えます。

> 注意:

> 読み取り専用の static プロパティはサポートされていません。

読み取り専用プロパティは、一度しか初期化できません。 初期化できるのは、 そのプロパティが宣言された場所と同じスコープに限られます。 これらのルールから外れたプロパティへの代入や変更を行った場合、 Error 例外が発生します。

例5 読み取り専用プロパティの不正な初期化

    <?php
    class Test1 {
        public readonly string $prop;
    }

    $test1 = new Test1;
    // スコープ外からの不正な初期化
    $test1->prop = "foobar";
    // Error: Cannot initialize readonly property Test1::$prop from global scope
    ?>

> 注意:

> 読み取り専用プロパティに対して、 明示的にデフォルト値を設定することはできません。 なぜなら、読み取り専用プロパティにデフォルト値を設定することは、 本質的に定数と同じであり、あまり役に立たないからです。

>     <?php

>     class Test {
>         // Fatal error: Readonly property Test::$prop cannot have default value
>         public readonly int $prop = 42;
>     }
>     ?>

> 注意:

> 読み取り専用プロパティは、 一度初期化されると unset() できません。 しかし、初期化する前であれば プロパティを宣言した同一のスコープから unset することはできます。

プロパティの変更は、単純な代入にとどまりません。 次に示すプロパティへの変更例は、 すべて Error 例外が発生します:

    <?php

    class Test {
        public function __construct(
            public readonly int $i = 0,
            public readonly array $ary = [],
        ) {}
    }

    $test = new Test;
    $test->i += 1;
    $test->i++;
    ++$test->i;
    $test->ary[] = 1;
    $test->ary[0][] = 1;
    unset($test->ary[0]);
    $ref =& $test->i;
    $test->i =& $ref;
    byRef($test->i);
    foreach ($test as &$prop);
    ?>

但し、プロパティが読み取り専用であっても、 そのプロパティの内部まで変更できなくなるわけではありません。 読み取り専用プロパティ内部のオブジェクト(またはリソース) は、 内部的に変更しても構いません:

    <?php

    class Test {
        public function __construct(public readonly object $obj) {}
    }

    $test = new Test(new stdClass);
    // 有効なオブジェクト内部の変更
    $test->obj->foo = 1;
    // 不正な再代入
    $test->obj = new stdClass;
    ?>

PHP 8.3.0 以降では、 読み取り専用プロパティは __clone() を使ったオブジェクトのクローン時に再初期化できるようになりました。

例6 読み取り専用プロパティとオブジェクトのクローン

    <?php
    class Test1 {
        public readonly ?string $prop;

        public function __clone() {
            $this->prop = null;
        }

        public function setProp(string $prop): void {
            $this->prop = $prop;
        }
    }

    $test1 = new Test1;
    $test1->setProp('foobar');

    $test2 = clone $test1;
    var_dump($test2->prop); // NULL
    ?>

### 動的なプロパティ

オブジェクトに対して、存在しないプロパティを代入しようとした場合、 PHP は自動的に対応するプロパティを作成します。 こうして作成された動的なプロパティは、 そのインスタンスで のみ 使えます。

警告

動的なプロパティは、PHP 8.2.0 以降は推奨されなくなりました。 代わりに、プロパティを宣言することを推奨します。 任意のプロパティの名前を扱うには、 クラスがマジックメソッド __get() と __set() を実装すべきです。 動的なプロパティを使うための最終手段として、 アトリビュート `#[\AllowDynamicProperties]` でクラスをマークすることができます。

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open