当前位置:网站首页>Magic methods and usage in PHP (PHP interview theory questions)

Magic methods and usage in PHP (PHP interview theory questions)

2022-07-05 14:59:00 Back end regular developers


PHP Two underscores in the middle __ The first method is called magic method (Magic methods), These methods are in PHP It plays a very important role in .
Let's explain how to use these magic methods in the form of examples .

One 、 __construct(), Class constructor

php The construction method in is the first method automatically called by the object after the object is created . There is a constructor in each class , If you don't show it , Then, there will be a constructor with no parameters and empty content in the class by default .

  1. The function of construction method

     Generally, construction methods are used to perform some useful initialization tasks , For example, give initial values to member attributes when creating objects .
    
  2. The declaration format of the construction method in the class

function __constrct([ parameter list ]){
    

   // It is usually used to initialize and assign values to member properties 
}
  1. Things to note when declaring construction methods in a class
  • Only one constructor can be declared in the same class , as a result of ,PHP Constructor overloading is not supported .
  • The constructor name starts with two underscores __construct()
    Here's an example of it :
<?php
    class Person
    {
                                                                          
            public $name;        
            public $age;        
            public $sex;        
                                                                 
        /** *  The display declares a construction method with parameters  */                                                                                       
        public function __construct($name="", $sex=" male ", $age=22)
        {
          
            $this->name = $name;
            $this->sex = $sex;
            $this->age = $age;
        }
        
        /** * say  Method  */
        public function say()
        {
     
            echo " My name is :" . $this->name . ", Gender :" . $this->sex . ", Age :" . $this->age;
        }   
                                                                                           
    }

Create objects $Person1 Without any parameters

$Person1 = new Person();
echo $Person1->say(); // Output : My name is :, Gender : male , Age :27

Create objects $Person2 And with parameters “ Xiao Ming ”

$Person2 = new Person(" Xiao Ming ");
echo $Person2->say(); // Output : My name is : Zhang San , Gender : male , Age :27

Create objects $Person3 And with three parameters

$Person3 = new Person(" Li Si "," male ",25);
echo $Person3->say(); // Output : My name is : Li Si , Gender : male , Age :25

Two 、__destruct(), Destructor of class

Through the above explanation , Now we know what a construction method is . So the corresponding construction method is the deconstruction method .

Destruct methods allow you to perform operations or perform functions before destroying a class , Like closing a file 、 Release the result set, etc .

The deconstruction method is PHP5 New content just introduced .

Declaration format and construction method of analysis method __construct() similar , It's also a way to start with two underscores __destruct() , The name of this deconstruction method is also fixed .

  1. The declaration format of the destruct method
function __destruct()
{
    
 // Method body 
}

Be careful : A destructor cannot take any arguments .

  1. The role of deconstruction

Generally speaking , The method of deconstruction is in PHP It's not very common in English , It is an optional part of the genus , It is usually used to clean up objects before they are destroyed .
Example demonstration , as follows :

<?php
class Person{
         
                                                        
    public $name;         
    public $age;         
    public $sex;         
                                                                    
    public function __construct($name="", $sex=" male ", $age=22)
    {
       
        $this->name = $name;
        $this->sex  = $sex;
        $this->age  = $age;
    }
    
    /** * say  How to speak  */
    public function say()
    {
      
        echo " My name is :".$this->name.", Gender :".$this->sex.", Age :".$this->age;
    }    
    
    /** *  Declare a destruct method  */
    public function __destruct()
    {
    
            echo " I think I can save it again , My name is ".$this->name;
    }
}

$Person = new Person(" Xiao Ming ");
unset($Person); // Destroy the object created above $Person

The above program outputs at run time :

 I think I can save it again , My name is Xiaoming 

3、 ... and 、 __call(), Called when an invocable method is invoked in an object .

This method has two parameters , The first parameter $function_name Will automatically receive method names that don't exist , the second $arguments To receive multiple parameters of a nonexistent method in the form of an array .

  1. __call() The format of the method :
function __call(string $function_name, array $arguments)
{
    
    //  Method body 
}
  1. __call() The role of methods :

To avoid errors when the called method does not exist , And the unexpected result is that the program stops , have access to __call() Ways to avoid .

This method is called automatically when the method called does not exist , The program will continue .
Please refer to the following code :

<?php
class Person
{
                                 
    function say()
    {
      
                              
           echo "Hello, world!<br>"; 
    }      
        
    /** *  Declare this method to handle methods that do not exist in the calling object  */
    function __call($funName, $arguments)
    {
     
          echo " The function you call :" . $funName . "( Parameters :" ;  //  The output calls a method name that does not exist 
          print_r($arguments); //  Output a list of parameters when calling a nonexistent method 
          echo ") non-existent !<br>\n"; //  End line feed  
    }                                          
}
$Person = new Person();            
$Person->run("teacher"); //  Call methods that don't exist in the object , Then... In the object is automatically called __call() Method 
$Person->eat(" Xiao Ming ", " Apple ");             
$Person->say();      

Running results :

 The function you call :run( Parameters :Array ( [0] => teacher ) ) non-existent !
 The function you call :eat( Parameters :Array ( [0] =>  Xiao Ming  [1] =>  Apple  ) ) non-existent !
Hello, world!

Four 、 __callStatic(), Call in an static way when an invocable method is called

This method is similar to the above __call() In addition to __callStatic() It's not prepared for static methods , Everything else is the same .

Look at the following code :

<?php
class Person
{
    
    function say()
    {
    

        echo "Hello, world!<br>";
    }

    /** *  Declare this method to handle methods that do not exist in the calling object  */
    public static function __callStatic($funName, $arguments)
    {
    
        echo " The static method you call :" . $funName . "( Parameters :" ;  //  The output calls a method name that does not exist 
        print_r($arguments); //  Output a list of parameters when calling a nonexistent method 
        echo ") non-existent !<br>\n"; //  End line feed 
    }
}
$Person = new Person();
$Person::run("teacher"); //  Call methods that don't exist in the object , Then... In the object is automatically called __call() Method 
$Person::eat(" Xiao Ming ", " Apple ");
$Person->say();

The operation results are as follows :

 The static method you call :run( Parameters :Array ( [0] => teacher ) ) non-existent !
 The static method you call :eat( Parameters :Array ( [0] =>  Xiao Ming  [1] =>  Apple  ) ) non-existent !
Hello, world!

5、 ... and 、 __get(), Call when you get a member variable of a class

stay php In object oriented programming , The member property of the class is set to private after , If we try to call it outside, it will appear “ Cannot access a private property ” Error of . So in order to solve this problem , We can use magic __get().

Magic methods __get() The role of

While the program is running , It can get the value of private member property outside the object .
Let's go through the following __get() Let's take it a step further :

<?php
class Person
{
    
    private $name;
    private $age;

    function __construct($name="", $age=1)
    {
    
        $this->name = $name;
        $this->age = $age;
    }

    /** *  Add in the class __get() Method , It is called once automatically when getting the property value directly , Pass in the property name as a parameter and process  * @param $propertyName * * @return int */
    public function __get($propertyName)
    {
       
        if ($propertyName == "age") {
    
            if ($this->age > 30) {
    
                return $this->age - 10;
            } else {
    
                return $this->$propertyName;
            }
        } else {
    
            return $this->$propertyName;
        }
    }
}
$Person = new Person(" Xiao Ming ", 60);   //  adopt Person An object instantiated by a class , And the attribute is given initial value by construction method 
echo " full name :" . $Person->name . "<br>";   //  Direct access to private properties name, Automatically called __get() Method can indirectly obtain 
echo " Age :" . $Person->age . "<br>";    //  Automatically called __get() Method , Depending on the object itself, different values are returned 

Running results :

 full name : Xiao Ming 
 Age :50

6、 ... and 、 __set(), Called when setting a member variable of a class

__set() The role of :

__set( $property, $value )` Method is used to set private properties , When assigning a value to an undefined property , This method will be triggered , The parameters passed are the name and value of the set property .

Take a look at the demo code below :

<?php
class Person
{
    
    private $name;
    private $age;

    public function __construct($name="",  $age=25)
    {
    
        $this->name = $name;
        $this->age  = $age;
    }

    /** *  Declaring magic methods takes two parameters , Called automatically when assigning a value to a private property , And can shield some illegal assignments  * @param $property * @param $value */
    public function __set($property, $value) {
    
        if ($property=="age")
        {
    
            if ($value > 150 || $value < 0) {
    
                return;
            }
        }
        $this->$property = $value;
    }

    /** *  Declare speaking methods in a class , Name all the private properties  */
    public function say(){
    
        echo " My name is ".$this->name.", This year, ".$this->age." Year old ";
    }
}

$Person=new Person(" Xiao Ming ", 25); // Be careful , The initial value will be changed as follows 
// Automatically called __set() function , Set the property name name To the first parameter , Set property values ” Li Si ” To the second parameter 
$Person->name = " Xiaohong ";     // Assignment successful . without __set(), You make a mistake .
// Automatically called __set() function , Set the property name age To the first parameter , Set property values 26 To the second parameter 
$Person->age = 16; // Assignment successful 
$Person->age = 160; //160 It's an illegal value , The assignment is invalid 
$Person->say();  // Output : My name is Xiao Hong , This year, 16 Year old 

Running results :

 My name is Xiao Hong , This year, 16 Year old 

7、 ... and 、 __isset(), When called on an inaccessible property isset() or empty() Called when the

Before we look at this method, let's look at isset() The application of function ,isset() It is a function to determine whether a variable is set , Pass in a variable as a parameter , If the incoming variable exists, return true, Or send it back false.

So if you use it outside of an object isset() This function is used to determine whether the members in the object are set or not ?

There are two situations , If the members in the object are public , We can then use this function to determine member properties , If it's a private member property , This function doesn't work , The reason is because the private one is encapsulated , Not visible on the outside . Then we can't use it outside of the object isset() Function to determine whether the private member property is set ? Of course you can , But not immutable . You just add one to the class __isset() The method is ok , When used outside a class isset() Function to determine whether private members in an object are set , It will automatically call __isset() Methods to help us complete this operation .

__isset() The role of : When called on an inaccessible property isset() or empty() when ,__isset() Will be called .

Take a look at the code demo below :

<?php
class Person
{
    
    public $sex;
    private $name;
    private $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    /** * @param $content * * @return bool */
    public function __isset($content) {
    
        echo " When used outside a class isset() Functions determine private members {
      $content} when , Automatically call <br>";
        echo  isset($this->$content);
    }
}

$person = new Person(" Xiao Ming ", 25); //  The initial assignment 
echo isset($person->sex),"<br>";
echo isset($person->name),"<br>";
echo isset($person->age),"<br>";

The operation results are as follows :

1 // public  Sure  isset()
 When used outside a class isset() Functions determine private members name when , Automatically call  // __isset()  Inside   first echo
1 // __isset()  Inside the second echo
 When used outside a class isset() Functions determine private members age when , Automatically call  // __isset()  Inside   first echo
1 // __isset()  Inside the second echo

8、 ... and 、 __unset(), When called on an inaccessible property unset() When called .

Before we look at this method , Let's take a look at unset() function ,unset() This function removes the specified variable and returns true, The parameter is the variable to be deleted .

Then, if you delete the member attributes inside an object from outside, use unset() Is the function OK ?

Naturally, there are two situations here :

1、 If the member properties in an object are public , You can use this function to delete the public property of the object outside the object .

2、 If the member properties of an object are private , If I use this function, I don't have permission to delete .

Although there are two situations above , But I want to say the same thing if you add __unset() This method , You can delete the private member attribute of the object outside the object . Add... To the object __unset() After this method , Use... Outside the object “unset()” Function to delete private member properties inside an object , Object will automatically call __unset() Function to help us delete private member properties inside an object .

Please look at the following code :

<?php
class Person
{
    
    public $sex;
    private $name;
    private $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    /** * @param $content * * @return bool */
    public function __unset($content) {
    
        echo " When used outside a class unset() Function to delete a private member <br>";
        echo  isset($this->$content);
    }
}

$person = new Person(" Xiao Ming ", 25); //  The initial assignment 
unset($person->sex);
unset($person->name);
unset($person->age);

Running results :

 When used outside a class unset() Function to delete a private member 
1 When used outside a class unset() Function to delete a private member 
1

Nine 、 __sleep(), perform serialize() when , This function will be called first

serialize() Function to check if there is a magic method in the class __sleep(). If there is , The method is called first , Then do the serialization operation .

This feature can be used to clean up objects , And returns an array containing the names of all variables in the object that should be serialized .

If the method does not return anything , be NULL Serialized , And produce a E_NOTICE Level error .

Be careful :

__sleep() Cannot return the name of a private member of a parent class . Doing so will produce a E_NOTICE Level error . It can be used Serializable Interface to replace .
effect :

__sleep() Methods are often used to submit uncommitted data , Or similar cleaning operations . meanwhile , If there are some big objects , But it doesn't need to be all saved , This function is easy to use .
Please refer to the following code for details :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    /** * @return array */
    public function __sleep() {
    
        echo " When used outside a class serialize() It will call __sleep() Method <br>";
        $this->name = base64_encode($this->name);
        return array('name', 'age'); //  Here you have to return a number , The element inside represents the name of the returned attribute 
    }
}

$person = new Person(' Xiao Ming '); //  The initial assignment 
echo serialize($person);
echo '<br/>';

Code run results :

 When used outside a class serialize() It will call __sleep() Method 
O:6:"Person":2:{
    s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}

Ten 、 __wakeup(), perform unserialize() when , This function will be called first

if __sleep() It's white , that __wakeup() It's black .

So why ?

because :

By contrast ,unserialize() Will check if there is one __wakeup() Method . If there is , Will be called first __wakeup Method , Prepare resources needed for objects in advance .
effect :

__wakeup() Often used in deserialization operations , For example, reestablishing a database connection , Or perform other initialization operations .
Or look at the code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    /** * @return array */
    public function __sleep() {
    
        echo " When used outside a class serialize() It will call __sleep() Method <br>";
        $this->name = base64_encode($this->name);
        return array('name', 'age'); //  Here you have to return a number , The element inside represents the name of the returned attribute 
    }

    /** * __wakeup */
    public function __wakeup() {
    
        echo " When used outside a class unserialize() It will call __wakeup() Method <br>";
        $this->name = 2;
        $this->sex = ' male ';
        //  There's no need to return an array 
    }
}

$person = new Person(' Xiao Ming '); //  The initial assignment 
var_dump(serialize($person));
var_dump(unserialize(serialize($person)));

Running results :

 When used outside a class serialize() It will call __sleep() Method 
string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"  When used outside a class serialize() It will call __sleep() Method 
 When used outside a class unserialize() It will call __wakeup() Method 
object(Person)#2 (3) { ["sex"]=> string(3) " male " ["name"]=> int(2) ["age"]=> int(25) }

11、 ... and 、 __toString(), The response method when a class is treated as a string

effect :

__toString() How to respond when a class is treated as a string . for example echo $obj; What should be shown .
Be careful :

This method must return a string , Otherwise, a E_RECOVERABLE_ERROR Fatal error at level .
Warning :

Can't be in __toString() Method . Doing so can lead to fatal errors .
Code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    public function __toString()
    {
    
        return  'go go go';
    }
}

$person = new Person(' Xiao Ming '); //  The initial assignment 
echo $person;

result :

go go go

So if there is no __toString() What happens when this magic method works ? Let's test :

Code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }
    
}

$person = new Person(' Xiao Ming '); //  The initial assignment 
echo $person;

result :

Catchable fatal error: Object of class Person could not be converted to string in D:\phpStudy\WWW\test\index.php on line 18

Obviously , The page reported a fatal error , This is not allowed by grammar .

Twelve 、 __invoke(), The response method when an object is called by calling a function

effect :

When trying to call an object as a function ,__invoke() Method will be called automatically .
Be careful :

This feature only exists in PHP 5.3.0 And above are valid .
Go straight to the code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    public function __invoke() {
    
        echo ' This is an object ';
    }

}

$person = new Person(' Xiao Ming '); //  The initial assignment 
$person();

View the run results :

 This is an object 

Of course , If you insist on using objects as function methods , Then you get the following result :

Fatal error: Function name must be a string in D:\phpStudy\WWW\test\index.php on line 18

13、 ... and 、 __set_state(), call var_export() When exporting a class , This static method will be called .

effect :

since PHP 5.1.0 rise , When calling var_export() When exporting a class , This static method is called automatically .
Parameters :

The only argument to this method is an array , It includes pressing array(‘property’ => value, …) Class properties of formatting .
Now let's see if we add __set_state() The situation press , How about the code and running results :

Code up :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

}

$person = new Person(' Xiao Ming '); //  The initial assignment 
var_export($person);

Look at the results :

Person::__set_state(array( 'sex' => ' male ', 'name' => ' Xiao Ming ', 'age' => 25, ))

Obviously , Print out all the attributes in the object

added __set_state() after :

Continue up code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    public static function __set_state($an_array)
    {
    
        $a = new Person();
        $a->name = $an_array['name'];
        return $a;
    }

}

$person = new Person(' Xiao Ming '); //  The initial assignment 
$person->name = ' Xiaohong ';
var_export($person);

Keep looking at the results :

Person::__set_state(array( 'sex' => ' male ', 'name' => ' Xiaohong ', 'age' => 25, ))

fourteen 、 __clone(), Called when the object copy is complete

In most cases , We don't need to completely copy an object to get its properties . But there is one case where you really need to : If you have one GTK Window object , This object holds the window related resources . You may want to copy a new window , Keep all properties the same as the original window , But it has to be a new object ( Because if it's not a new object , Then changes in one window will affect the other ). There's another situation : If the object A Objects are stored in the B References to , When you copy objects A when , The object you want to use is no longer an object B It is B A copy of , Then you have to get the object A A copy of .

effect :

Object replication can be done by clone Keyword to complete ( If possible , This will call the object's __clone() Method ). Object __clone() Method cannot be called directly .
grammar :

$copy_of_object = clone $object;
Be careful :

When the object is copied ,PHP 5 A shallow copy of all the properties of the object (shallow copy). All reference properties It will still be a reference to the original variable .

When the copy is complete , If you define __clone() Method , The newly created object ( Copy the generated object ) Medium __clone() Method will be called , Can be used to modify the value of a property ( If necessary ).
Look at the code :

<?php
class Person
{
    
    public $sex;
    public $name;
    public $age;

    public function __construct($name="",  $age=25, $sex=' male ')
    {
    
        $this->name = $name;
        $this->age  = $age;
        $this->sex  = $sex;
    }

    public function __clone()
    {
    
        echo __METHOD__." You're cloning objects <br>";
    }

}

$person = new Person(' Xiao Ming '); //  The initial assignment 
$person2 = clone $person;

var_dump('persion1:');
var_dump($person);
echo '<br>';
var_dump('persion2:');
var_dump($person2);

Look at the results :

Person::__clone You're cloning objects 
string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) " male " ["name"]=> string(6) " Xiao Ming " ["age"]=> int(25) } 
string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) " male " ["name"]=> string(6) " Xiao Ming " ["age"]=> int(25) }
 cloned .

15、 ... and 、__autoload(), Trying to load an undefined class

effect :

You can enable automatic loading of classes by defining this function .
In magic functions __autoload() Before the method appeared , If you want to instantiate in a program file 100 Objects , Then you have to use include perhaps require Include 100 A class file , Or you put this 100 Classes are defined in the same class file —— I believe this file will be very large , And then you're in pain .

But with __autoload() Method , You don't have to worry about it in the future , This class will automatically load the specified file before you instantiate the object .

Let's look at it through examples :

Let's look at the way it used to be :

/** *  file non_autoload.php */ 
   
require_once('project/class/A.php');  
require_once('project/class/B.php');  
require_once('project/class/C.php');  
   
if ( Conditions A) {
      
    $a = new A();  
    $b = new B();  
    $c = new C();  
    // …  Business logic  
} else if ( Conditions B) {
      
    $a = newA();  
    $b = new B();  
    // …  Business logic  
}

Did you see? ? no need 100 individual , It's just 3 It seems a little annoying . And then there's a problem : If the script executes “ Conditions B” In this branch ,C.php There's no need for this file to contain . because , Any contained file , Whether or not to use , Will be php Engine compilation . If not used , But it was compiled , This can be seen as a waste of resources . Further more , If C.php Contains D.php,D.php Contains E.php. And most of the time it's done “ Conditions B” Branch , Then a part of the resources will be wasted to compile C.php,D.php,E.php Three “ It's useless ” The file of .

So if you use __autoload() The way ?

/** *  file autoload_demo.php */ 
function  __autoload($className) {
      
    $filePath = “project/class/{
    $className}.php”;  
    if (is_readable($filePath)) {
      
        require($filePath);  
    }  
}  
   
if ( Conditions A) {
      
    $a = new A();  
    $b = new B();  
    $c = new C();  
    // …  Business logic  
} else if ( Conditions B) {
      
    $a = newA();  
    $b = new B();  
    // …  Business logic  
}

ok, No matter how efficiency is used , At least the interface looks much more comfortable , There are not many redundant generations .

Let's look at the efficiency here , Let's analyze it :

When php The engine uses classes for the first time A, But when I can't find it , Automatically called __autoload Method , And the class name “A” Pass in as a parameter . therefore , We are __autoload() All you need to do in is based on the class name , Find the corresponding file , And include , If we can't find our way , that php The engine will report an error .

Be careful :

Here you can just require, Because once it's included ,php The engine encounters the class again A when , Will not call __autoload, Instead, use the classes in memory A, Does not result in multiple inclusion of .
Expand :

Actually php To this day , There are already going to be spl_autoload_register — Registers the given function as __autoload Realized. , But this is not in this article , If you are interested, you can read the manual by yourself .
sixteen 、__debugInfo(), Print the required debug information
Be careful :

The method in PHP 5.6.0 And above , If you find that the use is invalid or an error is reported , Please check your version .
Look at the code :

<?php
class C {
    
    private $prop;

    public function __construct($val) {
    
        $this->prop = $val;
    }

    /** * @return array */
    public function __debugInfo() {
    
        return [
            'propSquared' => $this->prop ** 2,
        ];
    }
}

var_dump(new C(42));

result :

object(C)#1 (1) { ["propSquared"]=> int(1764) }

Again, pay attention to :

there ** It means power , Also in PHP5.6.0 And above , Please check the details PHP manual
summary
That's all PHP I've learned about the magic method in , Common ones include __set() __get() __autoload() You should be familiar with , It doesn't matter what else you know , After all, knowledge is not afraid of much .

Okay , Interested or I didn't make it clear here , You can refer to the official documents .

PHP Online manual address :http://php.net/manual/zh/

Reprinted from :https://segmentfault.com/a/1190000007250604

原网站

版权声明
本文为[Back end regular developers]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140519024953.html