ホームページ  >  記事  >  バックエンド開発  >  PHP の __construct() と __destory() に関する注意事項

PHP の __construct() と __destory() に関する注意事項

Guanhui
Guanhui転載
2020-05-29 09:47:232250ブラウズ

PHP の __construct() と __destory() に関する注意事項

基本的にすべてのプログラミング言語には、クラス内にコンストラクターとデストラクターの概念があります。コンストラクターは関数インスタンスの作成時に初期化作業を行うために使用でき、デストラクターはインスタンスが破棄される前にクリーンアップ作業を行うことができます。比較的言えば、コンストラクターはよく使用されますが、デストラクターは通常、データベース リンク、ファイルの読み取りおよび書き込みハンドルなどのリソースを解放するために使用されます。

コンストラクターとデストラクターの使用法

まず、通常のコンストラクターとデストラクターの使用法を見てみましょう:

class A
{
    public $name;
    public function __construct($name)
    {
        $this->name = $name;
        echo "A:构造函数被调用,{$this->name}", PHP_EOL;
    }
    public function __destruct()
    {
        echo "A:析构函数被调用,{$this->name}", PHP_EOL;
    }
}
$a = new A('$a');
echo '-----', PHP_EOL;
class B extends A
{
    public function __construct($name)
    {
        $this->name = $name;
        parent::__construct($name);
        echo "B:构造函数被调用,{$this->name}", PHP_EOL;
    }
    public function __destruct()
    {
        parent::__destruct();
        echo "B:析构函数被调用,{$this->name}", PHP_EOL;
    }
}
class C extends A
{
    public function __construct($name)
    {
        $this->name = $name;
        echo "C:构造函数被调用,{$this->name}", PHP_EOL;
    }
    public function __destruct()
    {
        echo "C:析构函数被调用,{$this->name}", PHP_EOL;
    }
}
class D extends A
{
}
// unset($a); // $a的析构提前
// $a = null; // $a的析构提前
$b = new B('$b');
$c = new C('$c');
$d = new D('$d');
echo '-----', PHP_EOL;exit;
// A:构造函数被调用,$a
// -----
// A:构造函数被调用,$b
// B:构造函数被调用,$b
// C:构造函数被调用,$c
// A:构造函数被调用,$d
// -----
// A:析构函数被调用,$d
// C:析构函数被调用,$c
// A:析构函数被调用,$b
// B:析构函数被调用,$b
// A:析构函数被调用,$a

上記の内容には何かありますか?予想したものと異なるコードはありませんか?問題はありません。1 つずつ見てみましょう。

サブクラスが親クラスのコンストラクターまたはデストラクターをオーバーライドし、親クラスのコンストラクターがparent::__constuct()を使用して明示的に呼び出されない場合、親クラスはクラスのコンストラクターは実行されません。たとえば、C サブクラスがコンストラクターまたはデストラクターをオーバーライドしない場合、親クラスのデストラクターがデフォルトで呼び出されます。変数が明示的に NULL またはデストラクターに設定されていない場合は、 unset() を使用すると、スクリプトの実行が完了してから呼び出されます。テストコード内の呼び出し順序はスタックの先入れ後出しと同様です (C->B->A、C が最初に破棄されます)となりますが、サーバー環境では必ずしもその順序が固定されるわけではありません

デストラクタの参照問題

オブジェクトに次の内容が含まれる場合独自の相互参照がある場合、NULL に設定すると、デストラクターを呼び出すときに unset( ) で問題が発生する可能性があります。

class E
{
    public $name;
    public $obj;
    public function __destruct()
    {
        echo "E:析构函数被调用," . $this->name, PHP_EOL;
        echo '-----', PHP_EOL;
    }
}
$e1 = new E();
$e1->name = 'e1';
$e2 = new E();
$e2->name = 'e2';
$e1->obj = $e2;
$e2->obj = $e1;

このコードと同様に、$e1 と $e2 はどちらもクラス E のオブジェクトであり、それぞれ相互への参照を保持しています。実際、簡単に言うと、自分でリファレンスを保持している場合にも同様の問題が発生します。

$e1 = new E();
$e1->name = 'e1';
$e2 = new E();
$e2->name = 'e2';
$e1->obj = $e2;
$e2->obj = $e1;
$e1 = null;
$e2 = null;
// gc_collect_cycles();
$e3 = new E();
$e3->name = 'e3';
$e4 = new E();
$e4->name = 'e4';
$e3->obj = $e4;
$e4->obj = $e3;
$e3 = null;
$e4 = null;
echo 'E destory', PHP_EOL;

gc_collect_cycles() 行のコメントをオンにしない場合、デストラクターが実行される順序は次のとおりです。

// 不使用gc回收的结果
// E destory
// E:析构函数被调用,e1
// -----
// E:析构函数被调用,e2
// -----
// E:析构函数被调用,e3
// -----
// E:析构函数被调用,e4
// -----

gc_collect_cycles() のコメントをオンにすると、 )、デストラクターの実行順序は次のとおりです:

// 使用gc回收后结果
// E:析构函数被调用,e1
// -----
// E:析构函数被调用,e2
// -----
// E destory
// E:析构函数被调用,e3
// -----
// E:析构函数被调用,e4
// -----

PHP は gc を使用して一度リサイクルする必要があり、オブジェクトへのすべての参照が解放されるまでクラスのデストラクターは実行されないことがわかります。参照が解放されない場合、デストラクターは実行されません。

低バージョンのコンストラクターとの互換性の問題

PHP5 より前の PHP のコンストラクターは、クラス名と同じ名前のメソッドでした。つまり、クラス F がある場合、関数 F(){} メソッドがそのコンストラクターになります。 PHP では下位バージョンとの互換性を保つためこの機能が残されており、PHP7 以降でもクラス名と同名のメソッドが存在する場合、タイムアウト警告が報告されますが、プログラムの実行には影響しません。

class F
{
    public function f() 
    {
        // Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; F has a deprecated constructor 
        echo "F:这也是构造函数,与类同名,不区分大小写", PHP_EOL;
    }
    // function F(){
    //     // Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; F has a deprecated constructor 
    //     echo "F:这也是构造函数,与类同名", PHP_EOL;
    // }
    // function __construct(){
    //     echo "F:这是构造函数,__construct()", PHP_EOL;
    // }
}
$f = new F();

__construc() と同じ名前のクラス メソッドが同時に存在する場合、__construct() が最初に使用されます。もう 1 つ注意すべき点は、関数名では大文字と小文字が区別されないため、F() メソッドと f() メソッドは同じであり、コンストラクターになることです。同様に、大文字と小文字が区別されないため、f() と F() は同時に存在できません。もちろん、同じ名前の関数をコンストラクターとして使用することはお勧めしません。結局のところ、これは古い機能であり、いつか廃止される可能性があります。

コンストラクターのオーバーロード

PHP はメソッドのオーバーロードを実行しません。書き換えのみサポートします。つまり、サブクラスが親クラスのメソッドをオーバーライドしますが、複数のメソッドを定義することはできません。同じ名前ですが、パラメータが異なります。 Java などの言語では、メソッドのオーバーロードは、特にクラスをインスタンス化するときに、ポリモーフィック機能を簡単に実装するために非常に便利です。

$r1 = new R(); // 默认构造函数
$r2 = new R('arg1'); // 默认构造函数 一个参数的构造函数重载,arg1
$r3 = new R('arg1', 'arg2'); // 默认构造函数 两个参数的构造函数重载,arg1,arg2

上記のコードと同様に、複数の __construct() を定義しようとすると、PHP は実行できないことを直接通知します。では、上記のコードの機能を実現する他の方法はあるのでしょうか?もちろんそれはありますが、そうでなければ書きません。

class R
{
    private $a;
    private $b;
    public function __construct()
    {
        echo '默认构造函数', PHP_EOL;
        $argNums = func_num_args();
        $args = func_get_args();
        if ($argNums == 1) {
            $this->constructA(...$args);
        } elseif ($argNums == 2) {
            $this->constructB(...$args);
        }
    }
    public function constructA($a)
    {
        echo '一个参数的构造函数重载,' . $a, PHP_EOL;
        $this->a = $a;
    }
    public function constructB($a, $b)
    {
        echo '两个参数的构造函数重载,' . $a . ',' . $b, PHP_EOL;
        $this->a = $a;
        $this->b = $b;
    }
}
$r1 = new R(); // 默认构造函数
$r2 = new R('arg1'); // 默认构造函数 一个参数的构造函数重载,arg1
$r3 = new R('arg1', 'arg2'); // 默认构造函数 两个参数的构造函数重载,arg1,arg2

比較的言えば、Java などの言語よりも面倒ですが、同じ機能を実現します。

コンストラクターとデストラクターのアクセス制限

コンストラクターとデストラクターはデフォルトでパブリックであり、クラス内の他のメソッドのデフォルト値と同じです。もちろん、非公開に設定して保護することもできます。コンストラクターを非パブリックにすると、クラスをインスタンス化できなくなります。これはシングルトン モードで広く使用されているので、シングルトン モードのコードを直接見てみましょう。

class Singleton
{
    private static $instance;
    public static function getInstance()
    {
        return self::$instance == null ? self::$instance = new Singleton() : self::$instance;
    }
    private function __construct()
    {
    }
}
$s1 = Singleton::getInstance();
$s2 = Singleton::getInstance();
echo $s1 === $s2 ? 's1 === s2' : 's1 !== s2', PHP_EOL;
// $s3 = new Singleton(); // Fatal error: Uncaught Error: Call to private Singleton::__construct() from invalid context

$s3 をインスタンス化したい場合、エラーが直接報告されます。シングルトン パターンによって外部からのインスタンス化が不可能になる理由については、以前のデザイン パターン システムの記事でシングルトン パターンについて説明しました。

まとめ

普段使っているコンストラクターでこんなにも色々なことができるとは思いませんでした 日々の開発で気を付けなければならないのはサブクラスの継承です. 問題は親クラスのコンストラクタをオーバーライドする際の呼び出し問題と参照時の破壊問題です。

推奨チュートリアル: 「PHP チュートリアル

以上がPHP の __construct() と __destory() に関する注意事項の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。