【PHP】継承とprotectedと参照パターン

## 普通の継承 子→親参照

参照というよりは、子クラスに $parentName が（継承されて）定義されているだけの状態

```php:ex1.php
<?php
class ParentClass1
{
    protected string $parentName = "parent\n";
}

class ChildClass1 extends ParentClass1
{
    public function getParentName(): void
    {
        echo $this->parentName;
    }
}

$child = new ChildClass1;
$child->getParentName();
```

> parent


## 静的な参照 parentとselfの違い

```php:ex2_1.php
<?php
class ParentClass2
{
    protected static string $parentName = "parent\n";
}

class ChildClass2 extends ParentClass2
{
    public static function getParentName(): void
    {
        // parent::$parentName と self::$parentName は同一の存在

        echo "test1:" . parent::$parentName;
        echo "test2:" . self::$parentName;   // 定義を継承している = 自身のプロパティとして parentName を持っている

        self::$parentName = "child\n";       // 代入
        echo "test3:" . parent::$parentName;
        echo "test4:" . self::$parentName;   // 定義を継承している = 自身のプロパティとして parentName を持っている
    }
}

ChildClass2::getParentName();
```

> test1:parent
> test2:parent
> test3:child
> test4:child


```php:ex2_2.php
<?php
class ParentClass3
{
    protected static string $parentName = "parent\n";
}

class ChildClass3 extends ParentClass3
{
    protected static string $parentName = "child\n"; // ChildClass2 との違い

    public static function getParentName ()
    {
        // 子クラスに同名プロパティを定義したとき、
        // parent::$parentNameとself::$parentNameは別物になる

        echo "test1:" . parent::$parentName;
        echo "test2:" . self::$parentName;   // 自身のプロパティとして parentName を持っている

        self::$parentName = "child2\n";      // 代入
        echo "test3:" . parent::$parentName;
        echo "test4:" . self::$parentName;   // 自身のプロパティとして parentName を持っている
    }
}

ChildClass3::getParentName();
```

> test1:parent
> test2:child
> test3:parent
> test4:child2


## 普通の継承：親から子のプロパティを参照するということ（前置き）

ex1.phpと同じ
参照というよりは、子クラスに parentFunction() が（継承されて）定義されているだけの状態

```php:ex3_1.php
<?php
class ParentClass4
{
    protected string $name = "parent\n";

    protected function parentFunction(): void
    {
        echo $this->name;
    }
}

class ChildClass4 extends ParentClass4
{
    protected string $name = "child\n";

    public function callParentFunction(): void
    {
        $this->parentFunction();
    }
}

$child = new ChildClass4;
$child->callParentFunction();
```

> child


## 静的な参照 self が指すものと遅延静的束縛

selfはその持ち主のメソッドやプロパティを、
staticは子の持つメソッドやプロパティを参照可能にする

```php:ex3_2.php
<?php
class ParentClass5
{
    protected static string $name = "parent\n";

    protected static function parentFunction(): void
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass5 extends ParentClass5
{
    protected static string $name = "child\n";

    public static function callParentFunction(): void
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        self::parentFunction();
    }
}

ChildClass5::callParentFunction();
```

> parent
> child


```php:ex3_3.php
<?php
class ParentClass6
{
    protected static string $name = "parent\n";

    protected static function parentFunction(): void
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass6 extends ParentClass6
{
    protected static string $name = "child\n";

    protected static function parentFunction(): void // ex3_2.php との違い
    {
        echo self::$name;
        echo static::$name;
    }

    public static function callParentFunction(): void
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        self::parentFunction();
    }
}

ChildClass6::callParentFunction();
```

> child
> child


## 親が子クラスの protected を利用できる ということ

相互参照できるとは書かれていても、使いどころも少ないのであまり詳細は見かけないかも
\# Factory パターンとかで使えなくもなさそうだけど、他にやりようはあるし…

### よくある誤解
\# PHPだからね

```php:ex4_0.php
<?php
class ParentClass7
{
    public function getChildName(): void
    {
        echo $this->name;
    }
}

class ChildClass7 extends ParentClass7
{
    protected string $name = "child\n";
}

$parent = new ParentClass7;
$parent->getChildName();       // ""

$parent->name = "parent_name";
var_dump($parent);             // 親クラスに public の新たなプロパティが追加された
$parent->getChildName();       // 動くようになるが、子クラスの定義は関係ない
```

> object(ParentClass7)#1 (1) {
> &nbsp;&nbsp;&nbsp;&nbsp;["name"]=>
> &nbsp;&nbsp;&nbsp;&nbsp;string(11) "parent_name"
> }
> parent_name

### インスタンスで見ると直接的な関係が無くても extends があれば protected が読め、なければ読めない

```php:ex4_1_1.php
<?php
class ParentClass8
{
    public function getChildName(): void
    {
        $child = new ChildClass8; // この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも、
        echo $child->name;        // 定義上親子であれば子の protected は参照できる
    }

    public function callChildFunction(): void
    {
        ChildClass8::childFunction(); // インスタンスじゃなくても。
    }
}

class ChildClass8 extends ParentClass8
{
    protected string $name = "child\n";

    protected static function childFunction(): void
    {
        echo "child_function\n";
    }
}

$parent = new ParentClass8;
$parent->getChildName();
$parent->callChildFunction();
```

> child
> child_function


```php:ex4_1_2.php
<?php
class ParentClass9
{
    public function getChildName(ChildClass9 $child): void
    {
        echo $child->name; // 定義上親子であれば子のprotectedは参照できる
    }

    public function callChildFunction(ChildClass9 $child): void
    {
        $child->childFunction();
    }
}

class ChildClass9 extends ParentClass9
{
    protected string $name = "child\n";

    protected function childFunction(): void
    {
        echo "child_function\n";
    }
}

$parent = new ParentClass9;
$child = new ChildClass9;
$parent->getChildName($child); // この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも
$parent->callChildFunction($child);
```

> child
> child_function

### extends がなければもちろん読めない

```php:ex4_2.php
<?php
class ParentClass10
{
    public static function getChildName(): void
    {
        $child = new ChildClass10;
        echo $child->name;
    }

    public static function callChildFunction(): void
    {
        ChildClass10::childFunction();
    }
}

class ChildClass10 /* extends ParentClass10 */
{
    protected string $name = "child\n";

    protected static function childFunction(): void
    {
        echo "child_function\n";
    }
}

ParentClass10::getChildName();
```

> Error: Cannot access protected property ChildClass10::$name


## 余談
### 同じクラスの private は、インスタンスが分かれていても読むことができる
```php:ex5.php
<?php
class Test
{
    private string $name = "";

    public function __construct(string $name = "")
    {
        $this->name = $name;
    }

    public function getName(Test $instance): void
    {
        echo $instance->name;
    }
}

$instanceA = new Test("A");
$instanceB = new Test("B");

$instanceA->getName($instanceB);
```

> B

他の言語でも大体同じなので試してみてね