Developer

【PHP応用】コンストラクタとデストラクタ 
2021.04.13
Lv1

【PHP応用】コンストラクタとデストラクタ 

コンストラクタとデストラクタ

1.コンストラクタはオブジェクト生成時、デストラクタはオブジェクト破棄時に実行されます。
2.コンストラクタとデストラクタはオーバーロードできません。

コンストラクタとは

オブジェクトが生成される度に、呼び出されるメソッドの事。
クラスをインスタンス化してオブジェクトを生成する際に、
そのプロパティの初期値を設定するときなどに使うことができる。

デストラクタとは

オブジェクトの参照がなくなったときに呼び出されるメソッドの事。
また、スクリプト終了時にも順不同で呼び出される。

コンストラクターとデストラクターの構文

コンストラクタには引数を指定することが出来ます。
但し、引数の数が異なる複数のコンストラクタを定義すること(オーバーロード)は出来ません。
デストラクタには引数を指定できません。

コンストラクタの定義構文:
function __construct(){
    コンストラクタ内の処理
}
function __construct(引数1,引数2・・・){
    コンストラクタ内の処理
}


デストラクタの定義構文:
function __destruct(){
    デストラクタ内の処理
}

実例

以下のコードが、コンストラクタとデストラクタの定義と呼び出し例です。

class Sample1_5_1
{
    function __construct()
    {
        echo "construct";
    }
    function __destruct()
    {
        echo "destruct";
    }
    public $x = 100;
}
$s = new Sample1_5_1();
echo $s->x;
$s = null;
echo $s->x; //←ここでUndefinedエラーがおこります。

発展: コンストラクターでプロパティ代入を行う際の注意点

コンストラクターは実際には引数のデータを、
オブジェクトのプロパティに代入していく作業が主となります。
その際重要なのは、
PHP7まででは、コンストラクター内で定義、代入したプロパティは、
publicになってしまい、
値の取得、改変が自由になることです。

<?php
// PHP7かそれ以前の推奨パターン
class Employee_Ver7_OrBefore{   
    //あらかじめprivateとしてプロパティを宣言 
    private $name;
    private $email;
    private $job;
    function __construct(String $name, String $email, String $job)
    {
        $this->name = $name;
        $this->email = $email;
        $this->job = $job;
    }
}

//データが改ざんされない
$employee_1 = new Employee_Ver7_OrBefore("Alice", "alees@zmail.com", "CTO");
echo "Correct: ".$employee_1->name."<br/>";
//nameがAliceでなく、__BROKEN__にならない。アクセスできずエラーが出る。
$employee_1->name = "__BROKEN__";
echo "After vandalism: ".$employee_1->name;
?>

これを防ぐために、プロパティをあらかじめクラス直下に宣言し、
コンストラクターでは代入のみを行います。
値の取得のためのgetterメソッドだけを定義します。
そうすると、直接代入で値を改変しようとすると、エラーが出ます。

<?php
// PHP7かそれ以前の推奨パターン
class Employee_Ver7_OrBefore{   
    //あらかじめprivateとしてプロパティを宣言 
    private $name;
    private $email;
    private $job;
    //コンストラクターでは代入のみ
    function __construct(String $name, String $email, String $job)
    {
        $this->name = $name;
        $this->email = $email;
        $this->job = $job;
    }
    //専用のgetterを通じてのみ値を取得
    public function get(String $property_name)    {
        $valid_names = ["name", "email", "job"];
        foreach($valid_names as $valid_name)
        {
            if($valid_name === $property_name)
            {
                return $this->$property_name;
            }
        }
        return null;
    }
}

//データが改ざんされない
$employee_1 = new Employee_Ver7_OrBefore("Alice", "alees@zmail.com", "CTO");
echo "Correct: ".$employee_1->get("name")."<br/>";
//nameがAliceでなく、__BROKEN__にならない。アクセスできずエラーが出る。
$employee_1->name = "__BROKEN__";
echo "After vandalism: ".$employee_1->get("name");
?>


PHP8からは、コンストラクター引数を自動的にアクセス制限付きプロパティにすることができます。

<?php
// PHP8
class Employee_Ver8{
    function __construct(
        private String $name, 
        private String $email, 
        private String $job,
    )
    {
        $this->name = $name;
        $this->email = $email;
        $this->job = $job;
    }
    //専用のgetterを通じてのみ値を取得
    public function get(String $property_name)    {
        $valid_names = ["name", "email", "job"];
        foreach($valid_names as $valid_name)
        {
            if($valid_name === $property_name)
            {
                return $this->$property_name;
            }
        }
        return null;
    }
}

//データが改ざんされない
$employee_1 = new Employee_Ver8("Alice", "alees@zmail.com", "CTO");
echo "Correct: ".$employee_1->get("name")."<br/>";
//nameがAliceでなく、__BROKEN__にならない。アクセスできずエラーが出る。
$employee_1->name = "__BROKEN__";
echo "After vandalism: ".$employee_1->get("name");
?>

発展: コンストラクターのオーバーロードの代替品

PHPのコンストラクターではオーバーロードができないので、
ラッパー関数を使ってオーバーロードの代替品を用意します。

<?php
class Product
{
    private $id;
    private $name;
    private $price;
    private $description;
    
    //コンストラクター
    function __construct(int $id, String $name, int $price, String $description)
    {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
        $this->description = $description;
    }
    
    //画面表示関数
    public function echo()
    {
        $id = $this->id;
        $name = $this->name;
        $price = $this->price;
        $description = $this->description;
        echo "製品ID {$id}, 製品名 {$name}, 価格 {$price}, 詳細 {$description}.<br/>";
    }
    
    // プレーンデータからオブジェクト生成
    public static function create_from_plain_data(int $id, String $name, int $price, String $description)
    {
        return new Product($id, $name, $price, $description);
    }
    // jsonからオブジェクト生成
    public static function create_from_json(String $json)
    {
        //クラス名なしのオブジェクトに変換されるjson
        $params = json_decode($json);
        //オブジェクトの各プロパティをコンストラクターに渡す
        return new Product($params->id, $params->name, $params->price, $params->description);
    }
}

$apple_tea = Product::create_from_plain_data(
    1, 
    "apple_tea", 
    200, "500ml, 
    apple juice 30%"
);
$honey_tea = Product::create_from_json(<<<JSON
{
    "id" : 2,
    "name" : "honey_tea",
    "price" : 200,
    "description" : "500ml, honey 10%"
}
JSON);

$apple_tea->echo();
$honey_tea->echo();
?>