亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > PHP > 正文

為何說PHP引用是個坑,要慎用

2024-05-05 00:03:01
字體:
來源:轉載
供稿:網友

前言

去年我參加了很多次會議,其中八次會議里我進行了相關發言,這其中我多次談到了 PHP 的引用問題,因為很多人對它的理解有所偏差。在深入討論這個問題之前,我們先回顧一下引用的基本概念,明確什么是“引用傳遞”。

在 PHP 中引用意味著用不同的名字訪問同一個變量內容,不論你用哪個名字對變量做出了運算,其他名字訪問的內容也將改變。

讓我們通過代碼來加深對此的理解。 首先我們寫幾個簡單的語句,把一個變量賦值給另一個變量,并且改變另一個變量:

php;"><?php$a = 23;$b = $a;$b = 42;var_dump($a); // int(23)var_dump($b); // int(42)

這個腳本顯示 $a 值仍然為 23  ,而 $b 則等于 42 。出現這個情況的原因是我們得到的是一個拷貝(具體發生了什么稍后講解。。。)現在我們使用引用來做同樣的事情:

<?php$a = 23;$b = &$a;$b = 42;var_dump($a); // int(42)var_dump($b); // int(42)?>

現在 $a 的值也改變成了 42 。 事實上,$a 和 $b 之間沒有任何區別,它們都使用了同一個變量容器(又名: zval )。 將這兩者分開的唯一方法是使用 unset() 函數銷毀其中任何一個變量。

在 PHP 中,引用不僅能用在普通語句中,還能用于函數參數和返回值:

<?phpfunction &foo(&$param) { $param = 42; return $param;}$a = 23;echo "/$a before calling foo(): $a/n";$b = foo($a);echo "/$a after the call to foo(): $a/n";$b = 23;echo "/$a after touching the returned variable: $a/n";?>

你認為上面的結果是什么呢?—— 沒錯,就像下面這樣:

$a before calling foo(): 23$a after the call to foo(): 42$a after touching the returned variable: 42

這里我們初始化了一個變量,并把它作為一個引用參數傳給了一個函數。函數改變了它,它有了新值。該函數返回同一個變量,我們更改了返回的變量和它的原始值。。。 等等!它沒變,不是嗎?。?—— 沒錯,可引用就是這樣。 具體發生了如下事情:該函數返回了一個引用,引用了 $a 的變量容器 zval,并且通過 = 賦值操作符為它創建了一個副本。

為了修復這個問題,我們需要添加一個額外的 & 操作符:

$b = &foo($a);

結果和我們所期望的一樣:

$a before calling foo(): 23$a after the call to foo(): 42$a after touching the returned value: 23

總結一下: PHP 的引用就是同一個變量的別名,想要正確的使用它們可能很難。想要詳細了解引用計數,這里有份基礎資料,請參閱 手冊中的引用計數基本知識 。

PHP 5 發布時最大的變動是『對象處理方式』。一般我們理解為:

在 PHP 4 中,對象被當成變量來對待,所以當對象作為函數傳參時,他們是被復制的。但在 PHP 5 中,他們永遠是『引用傳參』。

以上的理解并不完全正確。其主要目的是遵循『面對對象模式』:對象傳參給函數或者方法后,這個函數發送一個指令給對象(例如調用了一個方法)以此來改變對象的狀態(例如對象的屬性)。因此傳參進去的對象必須為同一個。 PHP 4 的面對對象用戶使用『引用傳參』來解決這個問題,不過很難做到完美。PHP 5 引進了獨立于變量容器的『對象存儲器』。當一個對象賦值給變量時,變量不再存儲整個對象(屬性表和其他的『類』信息),而是存儲這個對象所在 存儲器的引用 —— 當我們復制一個對象變量時,我們復制的是這個『存儲器的引用』。這很容易被誤解為『引用』,但是『存儲器的引用』與『引用』是完全不同的概念。下面的示例代碼有助于我們更好地區分:

<?php// 創建一個對象和此對象的引用變量$a = new stdclass;$b = $a;$c = &$a;// 對『對象』進行操作$a->foo = 42; var_dump($a->foo); // int(42)var_dump($b->foo); // int(42)var_dump($c->foo); // int(42)// 現在直接改變變量的類型$a = 42;var_dump($a); // int(42)var_dump($b); // object(stdClass)#1719 (1) {    //   ["foo"]=>    //   int(42)    // }var_dump($c); // int(42)?>

以上代碼中,修改對象的屬性會影響到 復制 的變量 $b 和引用的變量 $c。但是在最后區塊的代碼中,當我們修改 $a 的類型時,引用的 $c 發生了變化,而復制得到的變量 $b 不會發生改變,這是個大多數有面對對象經驗的工程師所期待的。

So, 面對對象是唯一使用『引用』的理由,但是現在 PHP 4 已死,你也可以放棄此類用法了。

另一個人們使用『引用』的理由是 —— 這將讓代碼更快。但是這是錯誤的,引用并不會使代碼執行速度變快,更糟糕的是,很多時候『引用』會讓你的代碼執行效率更低。

我必須再鄭重強調一次:是的,很多時候『引用』會讓你的代碼執行效率更低。

別的語言的工程師,他們閱讀別的語言編碼規范,會看到建議在處理大的數據結構或者字串時,使用指針來減小對內存的消耗以提高運行效率。這些工程師誤將此概念理解到『引用』上,然而『指針』與『引用』是完全不同的技術模型。PHP 解析器與其他語言不同,在 PHP 中,我們使用『寫時復制(copy-on-write)』模型。

在『寫時復制』模型里,賦值和函數傳參不會觸發 復制 動作,你可以理解為多個不同的變量指向同一個『變量容器』,只有當『寫』動作發生時,才會觸發復制動作。這意味著,即使變量看起來像是『復制』的,本質上卻不是。所以當傳參一個巨大的變量給某個函數時,并不會對性能造成多大影響。不過此時如果你使用引用傳參的話,引用傳參會關閉『寫時復制』機制,這會導致接下來那些沒有使用引用的變量傳參會被立刻復制一份。這也不是世界末日,你也可以在所有地方都引用就行了嘛。事實并非如此:PHP 的內部機制依賴于『寫時復制』模型,存在很多你無法修改的內部函數傳參。
我曾在某處看到過類似下面這樣的代碼:

<?phpfunction foo(&$data) { for ($i = 0; $i < strlen($data); $i++) {  do_something($data{$i}); }}$string = "... looooong string with lots of data .....";foo(string);?>

顯然,上面這段代碼的第一個問題是:在循環中調用 strlen() 而不是使用已經計算好的長度。也就是說調用一次 strlen($data) 就可以了的,但是他卻調用了很多次。 不同于 C 這類語言, 一般來說,PHP 的字符串都自帶了長度,因此也不用進行長度的計算。所以就 strlen() 而言,這還不算太糟糕。 但現在另一個問題是,案例中的這個開發者為了節省時間,傳遞了一個引用作為參數以顯示自己的聰明。 然而,strlen() 期望得到的是一個副本?!簩憰r復制』不能用于引用,因此 $data 將會在 strlen() 調用時被復制,strlen() 將會做一個絕對簡單的操作 —— 事實上 strlen() 本來就是 PHP 里最簡單的函數之一 —— 緊接著該副本就會被直接銷毀。

如果沒有使用引用,也就沒必要進行復制操作,代碼執行也會更快。而且就算 strlen() 支持引用,你也不會因此獲得更多好處。

總的來說:

  • 除了 PHP4 的遺留問題,不要在面向對象(OO)中使用引用。
  • 不要使用引用來提升性能。

使用引用來完成事情的第三個問題是:通過參數的引用來返回數據所導致的糟糕的 API 設計。這個問題還是因為那個開發者沒有意識到『PHP 就是 PHP 而不是其他語言』所導致的。

在 PHP 中,同一個函數可以返回不同數據類型。—— 因此,你可以在函數執行成功時返回一個字符串,而在失敗時返回一個布爾值 false,PHP 也允許返回復雜的結構類型,比如數組和對象。所以在需要返回很多東西的時候,可以將他們打包在一起。另外,異常也是函數返回的一種方式。

使用引用是一件不好的事情,除了引用本身不好,并且還會使性能下降這個事實外,使用引用這種方式會使得代碼難以維護。像下面這段代碼的函數調用:

do_something($var);

你希望 $var 發生改變嗎?—— 當然不會。然而,如果 do_something() 傳遞的參數是引用,它就可能會改變。

這類 API 的另一個問題是:函數不能鏈式調用,因而你總會遇到必須使用臨時變量的場景。鏈式調用可能會使可讀性降低,但是在許多場景下,鏈式調用使得代碼更加簡潔。

關于引用的糟糕的設計決定,我個人最喜歡的一個例子是 PHP 自帶的 sort() 函數。sort() 使用一個數組作為引用參數,然后通過引用返回一個排好序的數組。 像常規那樣通過值返回一個排好序的數組可能還更好些。當然,這么做是由于歷史的原因:sort() 比『寫時復制』更早出現?!簩憰r復制』產生于 PHP4,而 sort() 則更早,它早在 PHP 還是作為一種在 Web 上做起事來很方便的東西,而不是真正的成為自己的語言的時候就存在了。

總之: 在 PHP 中,引用是不好的。 不要使用引用。 它們只會惹事生非,另外,不要對使用引用來提升引擎抱有希望。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到PHP教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产91对白在线播放| 久久久久久高潮国产精品视| 狠狠操狠狠色综合网| 国产精品日日做人人爱| 久久久久久18| 久久久久久久影视| 亚洲精品第一页| 国产欧美日韩免费看aⅴ视频| 欧美xxxx18性欧美| 国产精品免费看久久久香蕉| 国产亚洲精品久久| 久久97久久97精品免视看| 一本色道久久综合狠狠躁篇怎么玩| 国产亚洲欧洲高清| 国产午夜精品一区理论片飘花| 亚洲精品成人久久久| 91成人性视频| 最新91在线视频| 国产美女精彩久久| 日本在线观看天堂男亚洲| 精品久久久在线观看| 精品露脸国产偷人在视频| 国产精品99蜜臀久久不卡二区| 国产自产女人91一区在线观看| 国产精品影院在线观看| 成人性生交大片免费看小说| 久久久久久久久久亚洲| 奇门遁甲1982国语版免费观看高清| 欧美视频免费在线| 日韩欧美a级成人黄色| 亚洲一级黄色av| 欧美成人午夜剧场免费观看| 午夜精品www| 国产91露脸中文字幕在线| 日韩福利视频在线观看| 日韩欧美一区二区在线| 欧美猛交ⅹxxx乱大交视频| 亚洲一区二区三区乱码aⅴ蜜桃女| 亚洲成色777777在线观看影院| 亚洲国产小视频在线观看| 亚洲成人免费在线视频| 日韩免费av在线| 一区二区三区国产视频| 亚洲自拍小视频| 国产精品扒开腿做爽爽爽的视频| 欧美日韩福利在线观看| 日韩激情av在线免费观看| 日韩av网站大全| 九色精品免费永久在线| 国模视频一区二区三区| 欧美激情欧美激情在线五月| 日韩美女中文字幕| 中文字幕亚洲图片| 亚洲最大成人免费视频| 国产在线拍偷自揄拍精品| 日韩精品亚洲精品| 国产精品小说在线| 欧美另类在线观看| 美女撒尿一区二区三区| 欧美一乱一性一交一视频| 精品少妇一区二区30p| 日韩在线不卡视频| 亚洲国产精品资源| 国产精品久久久久久久9999| 日韩电影在线观看永久视频免费网站| 欧美日韩国产91| 日韩av一区在线| 久久精品国产亚洲精品2020| 麻豆一区二区在线观看| 久久综合伊人77777蜜臀| 亚洲视频一区二区| 成人黄色免费看| 欧美国产日韩视频| 国产精品丝袜久久久久久高清| 91久久精品国产91久久| 亚洲精品乱码久久久久久按摩观| 高跟丝袜一区二区三区| 久久人人97超碰精品888| 97香蕉久久夜色精品国产| 91久久精品国产91性色| 亚洲a中文字幕| 成人写真视频福利网| 亚洲伊人久久综合| 亚洲社区在线观看| 97视频在线观看成人| 亚洲黄色av女优在线观看| 亚洲精品suv精品一区二区| 久久精品久久久久| 亚洲电影天堂av| 亚洲直播在线一区| 亚洲人午夜色婷婷| 韩国三级日本三级少妇99| 在线观看欧美视频| 亚洲综合社区网| 中文字幕精品影院| 欧美电影免费观看电视剧大全| 国产一区二区三区在线看| 亚洲一区二区三区香蕉| 亚洲毛片在线免费观看| 亚洲精品一区二区三区不| 久久精品欧美视频| 中文字幕久热精品视频在线| 国产精品入口日韩视频大尺度| 中文字幕精品在线视频| 亚洲欧美综合精品久久成人| 国产精品一区电影| 国产精品视频网站| 亚洲最新av在线| 国产精品一二三视频| 日韩福利伦理影院免费| 国产日产久久高清欧美一区| 色偷偷噜噜噜亚洲男人| 久久久视频精品| 欧美日韩性视频在线| 久久久久女教师免费一区| 亚洲国产精品字幕| 欧美成人黄色小视频| 另类美女黄大片| 欧美诱惑福利视频| 国产精品中文在线| 国产精品丝袜久久久久久不卡| 日韩精品有码在线观看| 国产精品一区二区三区久久久| 亚洲男人天堂2024| 欧美午夜精品久久久久久人妖| 久久精品国产2020观看福利| 欧美视频在线免费看| 日韩小视频网址| 成人日韩在线电影| 国产精品高潮呻吟久久av黑人| 久久中文久久字幕| 亚洲色图日韩av| 国产精品高潮呻吟久久av野狼| 欧美高清第一页| 欧美成人午夜视频| 1769国内精品视频在线播放| 久久亚洲精品网站| 国产精品96久久久久久| 清纯唯美亚洲综合| 91免费观看网站| 精品女同一区二区三区在线播放| 国产精品久久久久福利| 久久伊人91精品综合网站| 亚洲欧美国产va在线影院| 国产原创欧美精品| 日韩美女在线观看一区| 91国语精品自产拍在线观看性色| 久久69精品久久久久久国产越南| 亚洲a成v人在线观看| 亚洲激情视频网站| 久久免费少妇高潮久久精品99| 日本成人黄色片| 91干在线观看| 国产成人福利视频| 久久人人爽人人爽人人片亚洲| 欧美特级www| 欧洲日韩成人av| 亚洲天堂av综合网| 中国china体内裑精亚洲片| 亚洲精品欧美日韩专区| 亚洲欧美在线免费| 亚洲91精品在线| 97超级碰碰人国产在线观看| 精品国产一区久久久|