../ |
---|
PHP7.4で継承を使っていると、アクセス修飾子でおかしな挙動がある。バグなのか、仕様なのか分からないが、注意が必要である。
Userクラス単独でのテスト
まず、Userというクラスを定義してみる。idとnameを属性に持たせる。
<?php
namespace samples;
class User {
private $id;
private $name; // ここをprotected,publicに変えてテスト
public function __construct(string $id, string $name) {
$this->setId($id);
$this->setName($name);
}
public function getId(): string { return $this->id; }
public function getName(): string { return $this->name; }
public function setId(string $id): void { $this->id = $id; }
public function setName(string $name): void { $this->name = $name; }
}
以下のようなテストコードを実行してみる。name属性の変更を試みる。
$user = new User('U001', 'Aさん');
$user->name = '山本さん'; // private,protectedだと、ここでエラー
$user->setName('池田さん');
var_dump($user);
すると、privateな属性に代入しようとしているのでエラーになる。これは正しい動作である。
Fatal error: Uncaught Error: Cannot access private property
samples\User::$name in samples\User.php
ちなみに、宣言をprotectedに変えても同様なエラーが出る。そして、publicに変えると正常に動作して、以下のようなvar_dump()の結果が表示される。一旦、山本さんに変更され、その後で池田さんに上書きされる。正しい挙動である。
class samples\User#1 (2) {
public $id =>
string(4) "U001"
public $name =>
string(12) "池田さん"
}
ここまでは問題ない。
Identifierを継承したUserクラスでのテスト
次に、Identifierというクラスとそれを継承したUserクラスで試してみる。
まず、Identifierというクラスを作る。前述のUserをリネームして、abstractを宣言したものである。
<?php
namespace samples;
abstract class Identifier {
private $id;
private $name;
public function __construct(string $id, string $name) {
$this->setId($id);
$this->setName($name);
}
public function getId(): string { return $this->id; }
public function getName(): string { return $this->name; }
public function setId(string $id): void { $this->id = $id; }
public function setName(string $name): void { $this->name = $name; }
}
次に、Identifierを継承したUserクラスを作る。継承をイメージしやすいようにmail属性を追加してみる。
<?php
namespace samples;
require_once(__DIR__."/Identifier.php");
class User extends Identifier {
private $mail;
public function __construct(string $id, string $name, string $mail){
parent::__construct($id, $name);
$this->setMail($mail);
}
public function getMail(): string { return $this->mail; }
public function setMail(string $mail): void { $this->mail = $mail; }
}
この状況で、前述のテストコードと同等のことを行ってみる。
$user = new User('U001', 'Aさん', "aaa@xxx.com");
$user->name = '山本さん';
$user->setName('池田さん');
var_dump($user);
結果は、以下のようになる。エラーにはならない。
class samples\User#1 (4) {
private $mail =>
string(11) "aaa@xxx.com"
private $id =>
string(4) "U001"
private $name =>
string(12) "池田さん"
public $name =>
string(12) "山本さん"
}
「$user->name = '山本さん';
」の実行では、privateな属性なのでアクセスできないかと思えば、勝手に新たなpublicな$name
という属性を追加しているのである。継承元を探索できていない。また、おそらく、PHPでのクラスの実装はarrayを使っていて、アクセス修飾子+属性名で識別しているのだろう。PHPのバグなのか、仕様なのかは分からない。
PHPでクラスを扱うときは、アクセス修飾子を信用せずに、setter/getterをきちんと定義して使用しないと危険である。
../ |
---|