Developer

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

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

コンストラクタとデストラクタはクラスに定義された特殊なメソッドです。

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

コンストラクタとは

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

コンストラクタでオブジェクトを生成しない限り、
オブジェクトに紐づくメソッドやプロパティは使えません。

デストラクタとは

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

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

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

コンストラクタとデストラクタのメソッド名はあらかじめ決まっています。
コンストラクタは”__construct”、デストラクタは”__destruct”と正確に入力してください。

コンストラクタの定義構文:
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();
?>

PHPWEBプログラミング講座 連載目次リンク

PHPWEBプログラミング講座 連載目次