Home  >  Article  >  Backend Development  >  Things to note about __construct() and __destory() in PHP

Things to note about __construct() and __destory() in PHP

Guanhui
Guanhuiforward
2020-05-29 09:47:232187browse

Things to note about __construct() and __destory() in PHP

Basically all programming languages ​​have the concepts of constructors and destructors in classes. The constructor can be used to do some initialization work when a function instance is created, while the destructor can do some cleanup work before the instance is destroyed. Relatively speaking, we use constructors a lot, while destructors are generally used to release resources, such as database links, file reading and writing handles, etc.

The use of constructors and destructors

Let’s first look at the use of normal constructors and destructors:

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

The above Is there something in the code that is different from what we expected? It’s okay, let’s look at it one by one:

If a subclass overrides the constructor or destructor of the parent class, and if the parent class’s constructor is not explicitly called using parent::__constuct(), then the parent class will The constructor of the class will not be executed. For example, if a C subclass does not override the constructor or destructor, the destructor of the parent class will be called by default. If the variable is not explicitly set to NULL or unset() is used, it will It is called after the script execution is completed. The calling order in the test code is similar to the stack first in last out (C->B->A, C is destructed first), but it is not necessarily the case in the server environment. That is to say, the order is not necessarily fixed

Reference problem of destructor

When the object contains its own mutual references, you want to set it to NULL or unset( ) may cause problems when calling the destructor.

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;

Similar to this code, $e1 and $e2 are both objects of class E, and they each hold references to each other. In fact, to put it simply, similar problems will occur if you hold your own references.

$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;

If we do not turn on the comment on the gc_collect_cycles() line, the order in which the destructor is executed is as follows:

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

If we turn on the comment on gc_collect_cycles(), the destructor's The execution order is:

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

It can be seen that PHP must be recycled once using gc, and the destructor of the class will not be executed until all references to the object have been released. If the reference is not released, the destructor will not be executed.

Compatibility issues with low version constructors

Before PHP5, PHP's constructor was a method with the same name as the class name. That is to say, if I have a class F, then the function F(){} method is its constructor. In order to be compatible with lower versions, PHP still retains this feature. After PHP7, if there is a method with the same name as the class name, a timeout warning will be reported, but it will not affect program execution.

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();

If __construc() and a class method with the same name exist at the same time, __construct() will be used first. Another thing to note is that function names are not case-sensitive, so the F() and f() methods are the same and will become constructors. Similarly, because they are not case-sensitive, f() and F() cannot exist at the same time. Of course, we do not recommend using a function with the same name as a constructor. After all, it is an outdated feature and may be canceled one day.

Constructor overloading

PHP does not run method overloading. It only supports rewriting, that is, subclasses override parent class methods, but cannot define multiple methods. Methods with the same name but different parameters. In languages ​​such as Java, overloading methods is very convenient, especially when instantiating a class, to easily implement polymorphic capabilities.

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

Just like the above code, if you try to define multiple __construct(), PHP will tell you directly that it cannot run. So is there any other way to achieve the function of the above code? Of course there is, otherwise we wouldn’t write it.

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

Relatively speaking, it is more troublesome than languages ​​like Java, but it does achieve the same function.

Access restrictions on constructors and destructors

Constructors and destructors are public by default, the same as the default values ​​of other methods in the class. Of course they can also be set to private and protected. If you make the constructor non-public, you will not be able to instantiate the class. This is widely used in the singleton mode. Let's look directly at the code of a singleton mode.

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

When $s3 wants to be instantiated, an error is reported directly. Regarding the question of why the singleton pattern makes it impossible to instantiate externally, we can look at the singleton pattern in the previous design pattern system article.

Summary

I didn’t expect that the constructor we use every day can play so many tricks. What needs to be paid attention to in daily development is subclass inheritance. The problem is the calling problem of the parent class constructor when the constructor is overridden and the destruction problem when the reference is made.

Recommended tutorial: "PHP Tutorial"

The above is the detailed content of Things to note about __construct() and __destory() in PHP. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete
Previous article:PHP life cycleNext article:PHP life cycle