アクセス権

プロパティ、メソッドまたは (PHP 7.1.0 以降) 定数のアクセス権 (visibility) は、 キーワード: public, protected または private を指定することにより定義できます。 public 宣言されたクラスのメンバーには、どこからでもアクセス可能です。 protected 宣言されたメンバーには、 そのクラス自身、そのクラスを継承したクラス、および親クラスからのみアクセスできます。 private 宣言されたメンバーには、そのメンバーを定義したクラスからのみアクセスできます。

プロパティのアクセス権

クラスのプロパティは、public, private, または protected として定義することができます。 アクセス権を明示的に指定しない場合、 そのプロパティは public として定義されます。

例1 プロパティの宣言

<?php
/**
 * MyClass の定義
 */
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // 動作します
echo $obj->protected; // Fatal エラー
echo $obj->private; // Fatal エラー
$obj->printHello(); // Public、Protected そして Private を表示します


/**
 * MyClass2 の定義
 */
class MyClass2 extends MyClass
{
    // public および protected プロパティは再定義できますが、
    // private はできません。
    public $public = 'Public2';
    protected $protected = 'Protected2';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj2 = new MyClass2();
echo $obj2->public; // 動作します
echo $obj2->protected; // Fatal エラー
echo $obj2->private; // 未定義です
$obj2->printHello(); // Public2, Protected2, Undefined を表示します

?>

非対称可視性プロパティ

PHP 8.4 から、プロパティは非対称に 可視性を設定できるようになりました。 読み取り(get)と書き込み(set)に 異なるスコープを設定できます。具体的には、set の可視性が メインの可視性より広くならない限り、 別々に指定できます。

例2 非対称可視性プロパティ

<?php
class Book
{
    public function __construct(
        public private(set) string $title,
        public protected(set) string $author,
        protected private(set) int $pubYear,
    ) {}
}

class SpecialBook extends Book
{
    public function update(string $author, int $year): void
    {
        $this->author = $author; // OK
        $this->pubYear = $year; // 致命的なエラー
    }
}

$b = new Book('How to PHP', 'Peter H. Peterson', 2024);

echo $b->title; // OK
echo $b->author; // OK
echo $b->pubYear; // 致命的なエラー

$b->title = 'How not to PHP'; // 致命的なエラー
$b->author = 'Pedro H. Peterson'; // 致命的なエラー
$b->pubYear = 2023; // 致命的なエラー
?>

非対称可視性プロパティにはいくつかの注意点があります:

  • 型付きプロパティのみが、set の可視性を個別に指定できます。
  • set の可視性は、get と同じか、 より厳しくなければなりません。つまり、 public protected(set) および protected protected(set) は許可されますが、protected public(set) は構文エラーになります。
  • プロパティが public の場合、メインの可視性は 省略できます。つまり、public private(set)private(set) は 同じ結果になります。
  • private(set) の可視性を持つプロパティは 自動的に final となり、子クラスで再宣言できません。
  • プロパティへのリファレンスを取得する場合、get ではなく set の可視性に従います。 これは、リファレンスを使用してプロパティの値を変更できるためです。
  • 同様に、配列プロパティへの書き込みを試みると、内部的には getset の両方の操作が行われます。そのため、常により厳しい set の可視性に従います。

クラスが別のクラスを拡張するとき、子クラスは final でない任意のプロパティを再定義できます。その際、 新しい可視性が親クラスと同じか広い場合に限り、メインの可視性または set の可視性を広げることができます。ただし、 private プロパティがオーバーライドされると、 実際には親のプロパティを変更するのではなく、 異なる内部名を持つ新しいプロパティを作成することに注意してください。

例3 非対称可視性プロパティの継承

<?php
class Book
{
    protected string $title;
    public protected(set) string $author;
    protected private(set) int $pubYear;
}

class SpecialBook extends Book
{
    public protected(set) $title; // OK。読み取りの可視性が広く、書き込みは同じだから。
    public string $author; // OK。読み取りの可視性は同じで、書き込みが広いから。
    public protected(set) int $pubYear; // 致命的なエラー。private(set) プロパティは final です。
}
?>

メソッドのアクセス権

クラスメソッドは、public, private, または protected として定義します。アクセス権を明示せずに宣言したメソッドは、 public となります。

例4 メソッドの宣言

<?php
/**
 * MyClass の定義
 */
class MyClass
{
    // public コンストラクタの宣言
    public function __construct() { }

    // public メソッドの宣言
    public function MyPublic() { }

    // protected メソッドの宣言
    protected function MyProtected() { }

    // private メソッドの宣言
    private function MyPrivate() { }

    // これは public となります
    function Foo()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate();
    }
}

$myclass = new MyClass;
$myclass->MyPublic(); // 動作します
$myclass->MyProtected(); // Fatal エラー
$myclass->MyPrivate(); // Fatal エラー
$myclass->Foo(); // Public、Protected および Private が動作します


/**
 * MyClass2 の定義
 */
class MyClass2 extends MyClass
{
    // これは public となります
    function Foo2()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate(); // Fatal エラー
    }
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // 動作します
$myclass2->Foo2(); // Public および Protected は動作しますが、Private は動作しません

class Bar 
{
    public function test() {
        $this->testPrivate();
        $this->testPublic();
    }

    public function testPublic() {
        echo "Bar::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Bar::testPrivate\n";
    }
}

class Foo extends Bar 
{
    public function testPublic() {
        echo "Foo::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Foo::testPrivate\n";
    }
}

$myFoo = new Foo();
$myFoo->test(); // Bar::testPrivate 
                // Foo::testPublic
?>

定数のアクセス権

PHP 7.1.0 以降では、クラスの定数は public, private, protected として定義できるようになりました。 明示的に公開範囲のキーワードを明示的に宣言しない定数は、public として定義されます。

例5 PHP 7.1.0 以降での定数の宣言

<?php
/**
 * MyClass を定義する
 */
class MyClass
{
    // public な定数を宣言する
    public const MY_PUBLIC = 'public';

    // protected な定数を宣言する
    protected const MY_PROTECTED = 'protected';

    // private な定数を宣言する
    private const MY_PRIVATE = 'private';

    public function foo()
    {
        echo self::MY_PUBLIC;
        echo self::MY_PROTECTED;
        echo self::MY_PRIVATE;
    }
}

$myclass = new MyClass();
MyClass::MY_PUBLIC; // 動作します
MyClass::MY_PROTECTED; // Fatal エラー
MyClass::MY_PRIVATE; // Fatal エラー
$myclass->foo(); // Public, Protected, Private のいずれでも動作します


/**
 * MyClass2 を定義する
 */
class MyClass2 extends MyClass
{
    // これは public
    function foo2()
    {
        echo self::MY_PUBLIC;
        echo self::MY_PROTECTED;
        echo self::MY_PRIVATE; // Fatal エラー
    }
}

$myclass2 = new MyClass2;
echo MyClass2::MY_PUBLIC; // 動作します
$myclass2->foo2(); // Public, Protected では動作しますが、Private では動作しません
?>

他のオブジェクトからのアクセス権

同じ型のオブジェクト間では、たとえ同一のインスタンスでなくても お互いの private メンバーや protected メンバーにアクセスすることができます。 これは、そのオブジェクトの内部ではオブジェクトの実装の詳細が既知であるからです。

例6 同じ型のオブジェクトの private メンバーへのアクセス

<?php
class Test
{
    private $foo;

    public function __construct($foo)
    {
        $this->foo = $foo;
    }

    private function bar()
    {
        echo 'Accessed the private method.';
    }

    public function baz(Test $other)
    {
        // private プロパティを変更することができます
        $other->foo = 'hello';
        var_dump($other->foo);

        // private メソッドをコールすることもできます
        $other->bar();
    }
}

$test = new Test('test');

$test->baz(new Test('other'));
?>

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

string(5) "hello"
Accessed the private method.