SoFunction
Updated on 2025-03-02

Comparison of member variable acquisition speed for PHP code optimization

There are 4 code examples below, what do you think is the ordering of how quickly they create objects and get member variables?

1: Set member variables to public, assign values ​​to member variables through assignment operations, and directly obtain variables

Copy the codeThe code is as follows:

<?php
class Foo {
    public $id;
}
$data = new Foo;
$data->id = 10;
echo $data->id;
?>

2: Set member variable to public, set the value of member variable through the constructor, and directly obtain the variable
Copy the codeThe code is as follows:

<?php
class Foo2 {
 public $id;
 public function __construct($id) {
  $this->id = $id;
 }
}

$data = new Foo2(10);
echo $data->id;
?>


3: Set member variable to protected, set the value of member variable through constructor, and obtain the variable through magic method
Copy the codeThe code is as follows:

<?php
class Foo3 {
 protected $id;
 public function __construct($id) {
  $this->id = $id;
 }

 public function getId() {
  return $this->id;
 }
}
$data = new Foo3(10);
echo $data->getId();
?>


4: Set member variable to protected, set the value of member variable through constructor, and obtain the variable through member method
<?php
class Foo4 {
  protected $id;
  public function __construct($id) {
   $this->id = $id;
  }

  public function __get($key) {
   return $this->id;
  }
}
$data = new Foo4(10);
echo $data->id;
?>
Sort by execution speed: 1243
Let's look at its opcode first:
1:

Copy the codeThe code is as follows:

1  ZEND_FETCH_CLASS 4  :4  'Foo'
2  NEW         $5 :4
3  DO_FCALL_BY_NAME   0         
4  ASSIGN         !0, $5
5  ZEND_ASSIGN_OBJ   !0, 'id'
6  ZEND_OP_DATA    10
7  FETCH_OBJ_R   $9 !0, 'id'
8  ECHO            $9

2:
Copy the codeThe code is as follows:

1  ZEND_FETCH_CLASS 4  :10 'Foo2'
2  NEW               $11 :10
3  SEND_VAL           10
4  DO_FCALL_BY_NAME  1
5  ASSIGN        !1, $11
6  FETCH_OBJ_R   $14 !1, 'id'
7  ECHO            $14

3:
Copy the codeThe code is as follows:

1  ZEND_FETCH_CLASS 4  :15 'Foo3'
2  NEW            $16 :15
3  SEND_VAL        10
4  DO_FCALL_BY_NAME   1         
5  ASSIGN         !2, $16
6  ZEND_INIT_METHOD_CALL !2, 'getId'
7  DO_FCALL_BY_NAME  0  $20    
8  ECHO           $20

4:
Copy the codeThe code is as follows:

1  ZEND_FETCH_CLASS 4  :21 'Foo4'
2  NEW            $22 :21
3  END_VAL         10
4  DO_FCALL_BY_NAME  1         
5  ASSIGN           !3, $22
6  FETCH_OBJ_R    $25 !3, 'id'
7   ECHO      $25


According to the above opcode, referring to its opcode implementation in the zend_vm_execute.h file, what can we discover?

1. The process of creating objects in the PHP kernel is divided into three steps:

ZEND_FETCH_CLASS Get the variables of the storage class based on the class name, which is implemented as a hashtalbe EG(class_table) search operation
NEW Initialize the object and point EX(call)->fbc to the constructor pointer.
Calling the constructor, its calls are the same as other function calls, both zend_do_fcall_common_helper_SPEC

2. The call of the magic method is triggered by conditions, not directly, such as the acquisition of the member variable id in our example.

(zend_std_read_property), the steps are:
Get the object's attributes. If there is, turn to the second step; if there is no relevant attributes, turn to the third step.
Find out whether the property corresponding to the name exists from the object's properties. If it exists, return the result. If it does not exist, go to the third step
If the __get magic method exists, then this method is called to get the variable. If it does not exist, an error will be reported.
Go back to the sorting question:

1. What is the difference between the first and the second one?

The second opcode is less than the first one, but is slower than the first one, because the constructor has more parameters and one more opcode for processing. Parameter processing is a time-consuming operation. When we are doing code optimization, some unnecessary parameters can be removed if they can be removed; when a function has multiple parameters, you can consider encapsulating it through an array and passing it in.

2. Why is the third one the slowest?

Because its acquisition parameter is essentially a call to an object member method, the cost of calling a method is higher than that of a variable

3. Why is the fourth faster than the third?

Because the fourth operation essentially obtains variables, it only implements the call of magic methods internally, compared with user-defined methods, the call of internal functions will be more efficient. Therefore, when we have some methods implemented by PHP kernel that can be called, don't repeatedly invent the wheel.
4. Why is the fourth one slower than the second one?
Because in the process of obtaining variables in PHP objects, when the member variable is not in the class definition, PHP's unique magic method __get will be called, and there is an additional call to the magic method.

To summarize:

1. Use PHP built-in functions
2. It is not something that must be object-oriented (OOP). Object-oriented is often very expensive, and each method and object call consumes a lot of memory.
3. Try to use magic methods as little as possible - do not use frames unless necessary, because frames have a large number of magic methods to use.
4. In performance-first application scenarios, member variables are a better method, when you need to use OOP.
5. Do not use functions if you can use PHP syntax structure. Do not write them by yourself if you can use built-in functions. Do not use objects if you can use functions.