有人不建議使用”魔術方法”,會有人質疑, 魔術方法都不讓用?其實也不是,本文就和大家分析一下關於PHP 魔法函數的效能。
最佳化的建議, 是建議, 是防止大家濫用, 肆無忌憚的用. 如果你能在寫代碼的時候, 能意識到, 什麼慢, 什麼快, 從而避免一些沒有必要的對魔術方法的調用, 那就是這個最佳化建議所追求的效果了
疑惑
魔術方法真的效能比較差嗎?
PHP7裡使用魔術方法的效能還是存在問題嗎?
我們應該如何合理的使用魔術方法?
方案
面對我的疑惑,我的方案是:
目前個人能力有限,只能通過這種方式,如果你有更好的方案或者建議可以告訴我,謝謝,haha~
測試
__construct
首先我們先來看看建構函式__construct的實驗,php指令碼如下:
<?php/** * 魔術方法效能探索 * * 建構函式 * * @author TIGERB <https://github.com/TIGERB> */require('./function.php');if (!isset($argv[1])) { die('error: variable is_use_magic is empty');}$is_use_magic = $argv[1];/** * 建構函式使用類名 */class ClassOne{ public function classOne() { # code... }}/** * 建構函式使用魔術函數__construct */class ClassTwo{ public function __construct() { # code... }}$a = getmicrotime();if ($is_use_magic === 'no_magic') { new ClassOne();}else { new ClassTwo();}$b = getmicrotime();echo ($b-$a) . "\n";
// PHP5.6中連續呼叫指令碼10000次sh test 10000 no_magic php5 construct// 運行資料統計指令碼sh analysis ./logs/__construct_no_magic_php5.log 10000// 結果avg: 34μmmax: 483μmmin: 26μm
// PHP5.6中連續呼叫指令碼10000次sh test 10000 magic php5 construct// 運行資料統計指令碼sh analysis ./logs/__construct_magic_php5.log 10000// 結果avg: 28μmmax: 896μmmin: 20μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 no_magic php construct// 運行資料統計指令碼sh analysis ./logs/__construct_no_magic_php.log 10000// 結果avg: 19μmmax: 819μmmin: 13μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 magic php construct// 運行資料統計指令碼sh analysis ./logs/__construct_magic_php.log 10000// 結果avg: 14μmmax: 157μmmin: 10μm
通過上面的資料我們可以看出:
使用__construct作為建構函式的指令碼執行的平均時間是要快於使用類名作為建構函式的, 大概快5到6微秒 ,不論是在php5.6還是php7.0中。
__call
接著,我們來看看__call的實驗,php指令碼如下:
<?php/** * 魔術方法效能探索 * * 建構函式 * * @author TIGERB <https://github.com/TIGERB> */require('./function.php');if (!isset($argv[1])) { die('error: variable is_use_magic is empty');}$is_use_magic = $argv[1];/** * 建構函式使用類名 */class ClassOne{ public function __construct() { # code... } public function test() { # code... }}/** * 建構函式使用魔術函數__construct */class ClassTwo{ public function __construct() { # code... } public function __call($method, $argus) { # code... }}$a = getmicrotime();if ($is_use_magic === 'no_magic') { $instance = new ClassOne(); $instance->test();}else { $instance = new ClassTwo(); $instance->test();}$b = getmicrotime();echo ($b-$a) . "\n";
// PHP5.6中連續呼叫指令碼10000次sh test 10000 no_magic php5 call// 運行資料統計指令碼sh analysis ./logs/__call_no_magic_php5.log 10000// 結果avg: 27μmmax: 206μmmin: 20μm
// PHP5.6中連續呼叫指令碼10000次sh test 10000 magic php5 call// 運行資料統計指令碼sh analysis ./logs/__call_magic_php5.log 10000// 結果avg: 29μmmax: 392μmmin: 22μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 no_magic php call// 運行資料統計指令碼sh analysis ./logs/__call_no_magic_php.log 10000// 結果avg: 16μmmax: 256μmmin: 10μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 magic php call// 運行資料統計指令碼sh analysis ./logs/__call_magic_php.log 10000// 結果avg: 18μmmax: 2459μmmin: 11μm
通過上面的資料我們可以看出:
使用__call的指令碼執行的平均時間是要慢於不使用, 大概慢2微秒 ,不論是在php5.6還是php7.0中。
__callStatic
接著,我們來看看__callStatic的實驗,php指令碼如下:
<?php/** * 魔術方法效能探索 * * 靜態重載函數 * * @author TIGERB <https://github.com/TIGERB> */require('./function.php');if (!isset($argv[1])) { die('error: variable is_use_magic is empty');}$is_use_magic = $argv[1];/** * 存在test靜態方法 */class ClassOne{ public function __construct() { # code... } public static function test() { # code... }}/** * 使用重載實現test */class ClassTwo{ public function __construct() { # code... } public static function __callStatic($method, $argus) { # code... }}$a = getmicrotime();if ($is_use_magic === 'no_magic') { ClassOne::test();}else { ClassTwo::test();}$b = getmicrotime();echo ($b-$a) . "\n";
// PHP5.6中連續呼叫指令碼10000次sh test 10000 no_magic php5 callStatic// 運行資料統計指令碼sh analysis ./logs/__callStatic_no_magic_php5.log 10000// 結果avg: 25μmmax: 129μmmin: 19μm
// PHP5.6中連續呼叫指令碼10000次sh test 10000 magic php5 callStatic// 運行資料統計指令碼sh analysis ./logs/__callStatic_magic_php5.log 10000// 結果avg: 28μmmax: 580μmmin: 20μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 no_magic php callStatic// 運行資料統計指令碼sh analysis ./logs/__callStatic_no_magic_php.log 10000// 結果avg: 14μmmax: 130μmmin: 9μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 magic php callStatic// 運行資料統計指令碼sh analysis ./logs/__callStatic_magic_php.log 10000// 結果avg: 14μmmax: 159μmmin: 10μm
通過上面的資料我們可以看出:
在php5.6中使用__callStatic的指令碼執行的平均時間是要慢於不使用, 大概慢3微秒 ;在php7.0中使用__callStatic的指令碼執行的平均時間是要大致等於不使用__callStatic的;
__set
接著,我們來看看__set的實驗,php指令碼如下:
<?php/** * 魔術方法效能探索 * * 設定私人屬性__set * * @author TIGERB <https://github.com/TIGERB> */require('./function.php');if (!isset($argv[1])) { die('error: variable is_use_magic is empty');}$is_use_magic = $argv[1];/** * 實現公用方法設定私人屬性 */class ClassOne{ /** * 私人屬性 * * @var string */ private $someVariable = 'private'; public function __construct() { # code... } public function setSomeVariable($value = '') { $this->someVariable = $value; }}/** * 使用_set設定私人屬性 */class ClassTwo{ /** * 私人屬性 * * @var string */ private $someVariable = 'private'; public function __construct() { # code... } public function __set($name = '', $value = '') { $this->$name = $value; }}$a = getmicrotime();if ($is_use_magic === 'no_magic') { $instance = new ClassOne(); $instance->setSomeVariable('public');}else { $instance = new ClassTwo(); $instance->someVariable = 'public';}$b = getmicrotime();echo ($b-$a) . "\n";
// PHP5.6中連續呼叫指令碼10000次sh test 10000 no_magic php5 set// 運行資料統計指令碼sh analysis ./logs/__set_no_magic_php5.log 10000// 結果avg: 31μmmax: 110μmmin: 24μm
// PHP5.6中連續呼叫指令碼10000次sh test 10000 magic php5 set// 運行資料統計指令碼sh analysis ./logs/__set_magic_php5.log 10000// 結果avg: 33μmmax: 138μmmin: 25μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 no_magic php set// 運行資料統計指令碼sh analysis ./logs/__set_no_magic_php.log 10000// 結果avg: 15μmmax: 441μmmin: 11μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 magic php set// 運行資料統計指令碼sh analysis ./logs/__set_magic_php.log 10000// 結果avg: 17μmmax: 120μmmin: 11μm
通過上面的資料我們可以看出:
使用__set的指令碼執行的平均時間是要慢於不使用, 大概慢2微秒 ,不論是在php5.6還是php7.0中。
__get
接著,我們來看看__get的實驗,php指令碼如下:
<?php/** * 魔術方法效能探索 * * 讀取私人屬性__get * * @author TIGERB <https://github.com/TIGERB> */require('./function.php');if (!isset($argv[1])) { die('error: variable is_use_magic is empty');}$is_use_magic = $argv[1];/** * 實現公用方法擷取私人屬性 */class ClassOne{ /** * 私人屬性 * * @var string */ private $someVariable = 'private'; public function __construct() { # code... } public function getSomeVariable() { return $this->someVariable; }}/** * 使用_get擷取私人屬性 */class ClassTwo{ /** * 私人屬性 * * @var string */ private $someVariable = 'private'; public function __construct() { # code... } public function __get($name = '') { return $this->$name; }}$a = getmicrotime();if ($is_use_magic === 'no_magic') { $instance = new ClassOne(); $instance->getSomeVariable();}else { $instance = new ClassTwo(); $instance->someVariable;}$b = getmicrotime();echo ($b-$a) . "\n";
// PHP5.6中連續呼叫指令碼10000次sh test 10000 no_magic php5 get// 運行資料統計指令碼sh analysis ./logs/__get_no_magic_php5.log 10000// 結果avg: 28μmmax: 590μmmin: 20μm
// PHP5.6中連續呼叫指令碼10000次sh test 10000 magic php5 get// 運行資料統計指令碼sh analysis ./logs/__get_magic_php5.log 10000// 結果avg: 28μmmax: 211μmmin: 22μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 no_magic php get// 運行資料統計指令碼sh analysis ./logs/__get_no_magic_php.log 10000// 結果avg: 16μmmax: 295μmmin: 10μm
// PHP7.0中連續呼叫指令碼10000次sh test 10000 magic php get// 運行資料統計指令碼sh analysis ./logs/__get_magic_php.log 10000// 結果avg: 19μmmax: 525μmmin: 12μm
通過上面的資料我們可以看出:
在php5.6中使用__get的指令碼執行的平均時間是要大致等於不使用__get的;在php7.0中使用__get的指令碼執行的平均時間是要慢於不使用, 大概慢3微秒 。
結語
這裡主要測試了__construct(), __call(), __callStatic(), __get(), __set()這五個常用的且可有其他實現方式代替的魔法函數。通過上面的測試再回來解答我的疑惑
魔術方法真的效能比較差嗎?
答:除了使用__construct之外,這裡使用其他的魔法方法的時間大致慢10微妙以內。
PHP7裡使用魔術方法的效能還是存在問題嗎?
答:在PHP7中使用與不使用魔術方法之間的差異和在PHP5.6中近乎一致。
我們應該如何合理的使用魔術方法?
答:通過整個測試我們可以看出使不使用魔法方法這之間的執行時間差異大致都是在10微妙以內的,所以如果魔法方法可以很好的節省我們的開發成本和最佳化我們的代碼結構,我們應該可以考慮犧牲掉這不到10微妙。而__construct是要快的,所以使用__construct應該沒什麼異議。
以上內容就是關於PHP 魔法函數效能分析,希望對大家有協助。