PHP中文手册1
1.入門
關(guān)于換行
PHP 會(huì)在輸出時(shí)自動(dòng)刪除其結(jié)束符??>后的一個(gè)換行。該功能主要是針對(duì)在一個(gè)頁(yè)面中嵌入多段 PHP 代碼或者包含了無(wú)實(shí)質(zhì)性輸出的 PHP 文件而設(shè)計(jì),與此同時(shí)也造成了一些疑惑。如果需要在 PHP 結(jié)束符??>?之后輸出換行的話,可以在其后加一個(gè)空格,或者在最后的一個(gè) echo/print 語(yǔ)句中加入一個(gè)換行。
<?php phpinfo();??>調(diào)用函數(shù)?phpinfo(),將會(huì)看到很多有關(guān)自己系統(tǒng)的有用信息,例如預(yù)定義變量、已經(jīng)加載的 PHP 模塊和配置信息。
echo?$_SERVER['HTTP_USER_AGENT']; 該腳本可能輸出是:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36打印來(lái)自表單的數(shù)據(jù):從表單提交姓名和年齡。
你好,<?php?echo?htmlspecialchars($_POST['name']);??>。你?<?php?echo (int)$_POST['age'];??>?歲了。
htmlspecialchars()使得 HTML 之中的特殊字符被正確的編碼,從而不會(huì)被使用者在頁(yè)面注入 HTML 標(biāo)簽或者? ?Javascript 代碼。超全局變量?$_REQUEST,它包含了所有 GET、POST、COOKIE 和 FILE 的數(shù)據(jù)。
有可能影響到老版本的代碼的最重要的兩點(diǎn)改動(dòng)分別是:
- 舊的?$HTTP_*_VARS?數(shù)組從 PHP 5.4.0 開始將不再有效。 PHP?? 4.1.0版本引入了如下超全局?jǐn)?shù)組變量:?$_GET、$_POST、$_COOKIE、?$_SERVER、$_FILES、$_ENV、?$_REQUEST?以及?$_SESSION。? ? ?
- 外部變量不再被默認(rèn)注冊(cè)為全局變量。也就是說(shuō),從 PHP?? 4.2.0版開始,php.ini?中的設(shè)置選項(xiàng) register_globals默認(rèn)值變成了?off。建議用以上提到的超全局?jǐn)?shù)組變量來(lái)訪問(wèn)這些值。但可能老的腳本、書籍以及教程都可能建立在該設(shè)置為 on 的基礎(chǔ)上。
<?php 和 ?>:這告訴 PHP 開始和停止解析二者之間的代碼。此種解析方式使得 PHP 可以被嵌入到各種不同的文檔中去。PHP 也允許使用短標(biāo)記?<??和??>,但不鼓勵(lì)使用。只有通過(guò)激活?php.ini中的?short_open_tag配置指令或者在編譯 PHP 時(shí)使用了配置選項(xiàng)?--enable-short-tags時(shí)才能使用短標(biāo)記。
如果文件內(nèi)容是純 PHP 代碼,最好在文件末尾刪除 PHP 結(jié)束標(biāo)記。這可以避免在 PHP 結(jié)束標(biāo)記之后萬(wàn)一意外加入了空格或者換行符,會(huì)導(dǎo)致 PHP 開始輸出這些空白,而腳本中此時(shí)并無(wú)輸出的意圖。
<% echo 'You may optionally use ASP-style tags'; %><%= $variable; # This is a shortcut for "<% echo . . ." %>
ASP?風(fēng)格標(biāo)記僅在通過(guò)?php.ini配置文件中的指令 asp_tags 打開后才可用。
自 PHP 5.4 起,短格式的 echo 標(biāo)記?<?=總會(huì)被識(shí)別并且合法,而不管?short_open_tag?的設(shè)置是什么。
在一個(gè) PHP 代碼段中的最后一行可以不用分號(hào)結(jié)束。如果后面還有新行,則代碼段的結(jié)束標(biāo)記包含了行結(jié)束。
2.類型PHP 支持 8 種原始數(shù)據(jù)類型。
四種標(biāo)量類型:boolean(布爾型) ? ?integer(整型) ? ?float(浮點(diǎn)型,也稱作?double) ? ?string(字符串)
兩種復(fù)合類型:array(數(shù)組) ? ?object(對(duì)象)
最后是兩種特殊類型:resource(資源) ? ?NULL(無(wú)類型)
偽類型:mixed(混合類型) ? ?number(數(shù)字類型) ? ?callback(回調(diào)類型)
以及偽變量?$...。
?
如果想查看某個(gè)表達(dá)式的值和類型,用?var_dump()?函數(shù)。如果只是想得到一個(gè)易讀懂的類型的表達(dá)方式用于調(diào)試,用?gettype()?函數(shù)。要查看某個(gè)類型,不要用?gettype(),而用?is_type?函數(shù)
如果要將一個(gè)變量強(qiáng)制轉(zhuǎn)換為某類型,可以對(duì)其使用強(qiáng)制轉(zhuǎn)換或者?settype()?函數(shù)。
要指定一個(gè)布爾值,使用關(guān)鍵字?TRUE?或?FALSE。兩個(gè)都不區(qū)分大小寫。
?
當(dāng)轉(zhuǎn)換為 boolean 時(shí),以下值被認(rèn)為是?FALSE:
布爾值?FALSE?本身,整型值 0(零),浮點(diǎn)型值 0.0(零),空字符串,以及字符串?"0"不,包括任何元素的數(shù)組,不包括任何成員變量的對(duì)象(僅 PHP 4.0 適用),特殊類型?NULL(包括尚未賦值的變量),從空標(biāo)記生成的?SimpleXML?對(duì)象。所有其它值都被認(rèn)為是?TRUE(包括任何資源)。
?
一個(gè)?integer?是集合 ? = {..., -2, -1, 0, 1, 2, ...} 中的一個(gè)數(shù)。
整型值要使用八進(jìn)制表達(dá),數(shù)字前必須加上?0(零)。要使用十六進(jìn)制表達(dá),數(shù)字前必須加上?0x。要使用二進(jìn)制表達(dá),數(shù)字前必須加上?0b。
如果向八進(jìn)制數(shù)傳遞了一個(gè)非法數(shù)字(即 8 或 9),則后面其余數(shù)字會(huì)被忽略。
var_dump(01090);?// 八進(jìn)制 010 = 十進(jìn)制 8整數(shù)溢出:
如果給定的一個(gè)數(shù)超出了 integer 的范圍,將會(huì)被解釋為 float。同樣如果執(zhí)行的運(yùn)算結(jié)果超出了 integer 范圍,也會(huì)返回 float。
?
PHP 中沒有整除的運(yùn)算符。1/2?產(chǎn)生出?float0.5。值可以舍棄小數(shù)部分強(qiáng)制轉(zhuǎn)換為?integer,或者使用round()?函數(shù)可以更好地進(jìn)行四舍五入。還可以通過(guò)函數(shù)?intval()?來(lái)將一個(gè)值轉(zhuǎn)換成整型。從布爾值轉(zhuǎn)換,FALSE?將產(chǎn)生出?0(零),TRUE?將產(chǎn)生出?1(壹)。當(dāng)從浮點(diǎn)數(shù)轉(zhuǎn)換成整數(shù)時(shí),將向下取整。決不要將未知的分?jǐn)?shù)強(qiáng)制轉(zhuǎn)換為?integer,這樣有時(shí)會(huì)導(dǎo)致不可預(yù)料的結(jié)果。
echo (int) ( (0.1+0.7) *?10?);?// 顯示 7!floor((0.1+0.7)*10)通常會(huì)返回?7?而不是預(yù)期中的?8,因?yàn)樵摻Y(jié)果內(nèi)部的表示其實(shí)是類似7.9999999999999991118...。
所以永遠(yuǎn)不要相信浮點(diǎn)數(shù)結(jié)果精確到了最后一位,也永遠(yuǎn)不要比較兩個(gè)浮點(diǎn)數(shù)是否相等。如果確實(shí)需要更高的精度,應(yīng)該使用任意精度數(shù)學(xué)函數(shù)或者 gmp 函數(shù)。要測(cè)試浮點(diǎn)數(shù)是否相等,要使用一個(gè)僅比該數(shù)值大一丁點(diǎn)的最小誤差值。
某些數(shù)學(xué)運(yùn)算會(huì)產(chǎn)生一個(gè)由常量?NAN所代表的結(jié)果。此結(jié)果代表著一個(gè)在浮點(diǎn)數(shù)運(yùn)算中未定義或不可表述的值。任何拿此值與其它任何值進(jìn)行的松散或嚴(yán)格比較的結(jié)果都是?FALSE。由于?NAN?代表著任何不同值,不應(yīng)拿?NAN?去和其它值進(jìn)行比較,包括其自身,應(yīng)該用?is_nan()?來(lái)檢查。
?
string?最大可以達(dá)到 2GB。一個(gè)字符串可以用 4 種方式表達(dá):
單引號(hào),雙引號(hào),heredoc 語(yǔ)法結(jié)構(gòu),nowdoc 語(yǔ)法結(jié)構(gòu)(自 PHP 5.3.0 起)?
要表達(dá)一個(gè)單引號(hào)自身,需在它的前面加個(gè)反斜線(\)來(lái)轉(zhuǎn)義。要表達(dá)一個(gè)反斜線自身,則用兩個(gè)反斜線(\\)。其它任何方式的反斜線都會(huì)被當(dāng)成反斜線本身:也就是說(shuō)如果想使用其它轉(zhuǎn)義序列例如?\r?或者?\n,并不代表任何特殊含義,就單純是這兩個(gè)字符本身。如果字符串是包圍在雙引號(hào)(")中, PHP 將對(duì)一些特殊的字符進(jìn)行解析:
\n:換行;\r回車;\t:水平制表符;\v垂直制表符;\e:Escape;\f:換頁(yè);\\:反斜線;\$:美元標(biāo)記;\":雙引號(hào);還有正則表達(dá)式。和單引號(hào)字符串一樣,轉(zhuǎn)義任何其它字符都會(huì)導(dǎo)致反斜線被顯示出來(lái)。用雙引號(hào)定義的字符串最重要的特征是變量會(huì)被解析。heredoc 句法結(jié)構(gòu):<<<。在該運(yùn)算符之后要提供一個(gè)標(biāo)識(shí)符,然后換行。接下來(lái)是字符串?string?本身,最后要用前面定義的標(biāo)識(shí)符作為結(jié)束標(biāo)志。任何具有?string表達(dá)的標(biāo)量變量,數(shù)組單元或?qū)ο髮傩远伎墒褂么苏Z(yǔ)法。只需簡(jiǎn)單地像在?string?以外的地方那樣寫出表達(dá)式,然后用花括號(hào)?{?和?}?把它括起來(lái)即可。由于?{?無(wú)法被轉(zhuǎn)義,只有?$緊挨著?{?時(shí)才會(huì)被識(shí)別。可以用?{\$?來(lái)表達(dá)?{$。
?
$great?=?'fantastic';// 無(wú)效,輸出: This is { fantastic}
echo?"This is {?$great}";
// 有效,輸出: This is fantastic
echo?"This is?{$great}";
echo?"This is?${great}";
// 有效
echo?"This square is?{$square->width}00 centimeters broad.";?
// 有效,只有通過(guò)花括號(hào)語(yǔ)法才能正確解析帶引號(hào)的鍵名
echo?"This works:?{$arr['key']}";
自 5.4 起可以使用短數(shù)組定義語(yǔ)法,用?[]?替代?array()。key?可以是?integer?或者?string。value?可以是任意類型。如果在數(shù)組定義中多個(gè)單元都使用了同一個(gè)鍵名,則只使用了最后一個(gè),之前的都被覆蓋了。PHP 數(shù)組可以同時(shí)含有?integer?和?string?類型的鍵名,因?yàn)?PHP 實(shí)際并不區(qū)分索引數(shù)組和關(guān)聯(lián)數(shù)組。如果對(duì)給出的值沒有指定鍵名,則取當(dāng)前最大的整數(shù)索引值,而新的鍵名將是該值加一。如果指定的鍵名已經(jīng)有了值,則該值會(huì)被覆蓋。還可以只對(duì)某些單元指定鍵名而對(duì)其它的空置。數(shù)組單元可以通過(guò)?array[key]?語(yǔ)法來(lái)訪問(wèn)。
$array?= ["foo"?=>?"bar",
"bar"?=>?"foo",
];
此外 key 會(huì)有如下的強(qiáng)制轉(zhuǎn)換:
- 包含有合法整型值的字符串會(huì)被轉(zhuǎn)換為整型。例如鍵名?"8"?實(shí)際會(huì)被儲(chǔ)存為?8。但是?"08"則不會(huì)強(qiáng)制轉(zhuǎn)換,因?yàn)槠洳皇且粋€(gè)合法的十進(jìn)制數(shù)值。
- 浮點(diǎn)數(shù)也會(huì)被轉(zhuǎn)換為整型,意味著其小數(shù)部分會(huì)被舍去。例如鍵名?8.7?實(shí)際會(huì)被儲(chǔ)存為?8。
- 布爾值也會(huì)被轉(zhuǎn)換成整型。即鍵名?true?實(shí)際會(huì)被儲(chǔ)存為?1而鍵名?false?會(huì)被儲(chǔ)存為?0。
- Null 會(huì)被轉(zhuǎn)換為空字符串,即鍵名?null?實(shí)際會(huì)被儲(chǔ)存為?""。
- 數(shù)組和對(duì)象不能被用為鍵名。堅(jiān)持這么做會(huì)導(dǎo)致警告:Illegal offset type。
?
<?php$array?= array(
? ? "foo"?=>?"bar",
? ? 42?=>?24,
? ? "multi"?=> array(
? ? "dimensional"?=> array(
? ? "array"?=>?"foo"
? ? ? ? )
? ? )
);
var_dump($array["foo"]);
var_dump($array[42]);
var_dump($array["multi"]["dimensional"]["array"]);
?> string(3) "bar" int(24) string(3) "foo"
?
自 PHP 5.4 起可以用數(shù)組間接引用函數(shù)或方法調(diào)用的結(jié)果。之前只能通過(guò)一個(gè)臨時(shí)變量。自 PHP 5.5 起可以用數(shù)組間接引用一個(gè)數(shù)組原型。
<?phpfunction?getArray() {
return array(1,?2,?3);
}
// on PHP 5.4
$secondElement?=?getArray()[1];
// previously
$tmp?=?getArray();
$secondElement?=?$tmp[1];
// or
list(,?$secondElement) =?getArray();
?>
僅對(duì)部分單元指定鍵名:
?
<?php$array?= array(
? ? ? ? "a",
? ? ? ? "b",
? ?6?=>?"c",
? ? ? ? "d",
);
var_dump($array);
?> array(4) {[0]=>string(1) "a"[1]=>string(1) "b"[6]=>string(1) "c"[7]=>string(1) "d" }
要修改某個(gè)值,通過(guò)其鍵名給該單元賦一個(gè)新值。要?jiǎng)h除某鍵值對(duì),對(duì)其調(diào)用?unset()?函數(shù)。
<?php$arr?= array(5?=>?1,?12?=>?2);
$arr[] =?56;?// This is the same as $arr[13] = 56;
$arr["x"] =?42;?// This adds a new element to
unset($arr[5]);?// This removes the element from the array
unset($arr);?// This deletes the whole array
?>
?
unset()?函數(shù)允許刪除數(shù)組中的某個(gè)鍵。但要注意數(shù)組將不會(huì)重建索引。如果需要?jiǎng)h除后重建索引,可以用array_values()?函數(shù)。
?
對(duì)于任意?integer,float,string,boolean和?resource?類型,如果將一個(gè)值轉(zhuǎn)換為數(shù)組,將得到一個(gè)僅有一個(gè)元素的數(shù)組,其下標(biāo)為 0,該元素即為此標(biāo)量的值。換句話說(shuō),(array)$scalarValue?與array($scalarValue)?完全一樣。
如果一個(gè)?object?類型轉(zhuǎn)換為?array,則結(jié)果為一個(gè)數(shù)組,其單元為該對(duì)象的屬性。鍵名將為成員變量名,不過(guò)有幾點(diǎn)例外:整數(shù)屬性不可訪問(wèn);私有變量前會(huì)加上類名作前綴;保護(hù)變量前會(huì)加上一個(gè) '*' 做前綴。這些前綴的前后都各有一個(gè) NULL 字符。這會(huì)導(dǎo)致一些不可預(yù)知的行為:
<?phpclass?A?{
private?$A;?// This will become '\0A\0A'
}
class?B?extends?A?{
private?$A;?// This will become '\0B\0A'
public?$AA;?// This will become 'AA'
}
var_dump((array) new?B());
?>
上例會(huì)有兩個(gè)鍵名為 'AA',不過(guò)其中一個(gè)實(shí)際上是 '\0A\0A'。
數(shù)組(Array)?的賦值總是會(huì)涉及到值的拷貝。使用引用運(yùn)算符通過(guò)引用來(lái)拷貝數(shù)組。
<?php$arr1?= array(2,?3);
$arr2?=?$arr1;
$arr2[] =?4;?// $arr2 is changed,
? ? ? ? ? ? // $arr1 is still array(2, 3)
$arr3?= &$arr1;
$arr3[] =?4;?// now $arr1 and $arr3 are the same
?>
要?jiǎng)?chuàng)建一個(gè)新的對(duì)象?object,使用?new?語(yǔ)句實(shí)例化一個(gè)類。如果將一個(gè)對(duì)象轉(zhuǎn)換成對(duì)象,它將不會(huì)有任何變化。如果其它任何類型的值被轉(zhuǎn)換成對(duì)象,將會(huì)創(chuàng)建一個(gè)內(nèi)置類?stdClass?的實(shí)例。如果該值為?NULL,則新的實(shí)例為空。數(shù)組轉(zhuǎn)換成對(duì)象將使鍵名成為屬性名并具有相對(duì)應(yīng)的值。對(duì)于任何其它的值,名為?scalar?的成員變量將包含該值。
<?php$obj?= (object)?'ciao';
echo?$obj->scalar;?// outputs 'ciao'
?>
資源?resource是一種特殊變量,保存了到外部資源的一個(gè)引用。資源是通過(guò)專門的函數(shù)來(lái)建立和使用的。
由于資源類型變量保存有為打開文件、數(shù)據(jù)庫(kù)連接、圖形畫布區(qū)域等的特殊句柄,因此將其它類型的值轉(zhuǎn)換為資源沒有意義。
特殊的?NULL?值表示一個(gè)變量沒有值。NULL?類型唯一可能的值就是?NULL。
?
在下列情況下一個(gè)變量被認(rèn)為是?NULL:
被賦值為?NULL。尚未被賦值。被?unset()。
?
NULL?類型只有一個(gè)值,就是不區(qū)分大小寫的常量?NULL。
使用?(unset) $var?將一個(gè)變量轉(zhuǎn)換為?null將不會(huì)刪除該變量或 unset 其值。僅是返回?NULL?值而已。
一些函數(shù)如?call_user_func()?或?usort()?可以接受用戶自定義的回調(diào)函數(shù)作為參數(shù)?;卣{(diào)函數(shù)不止可以是簡(jiǎn)單函數(shù),還可以是對(duì)象的方法,包括靜態(tài)類方法。一個(gè) PHP 的函數(shù)以?string類型傳遞其名稱??梢允褂萌魏蝺?nèi)置或用戶自定義函數(shù),但除了語(yǔ)言結(jié)構(gòu)例如:array(),echo,empty(),eval(),exit(),isset(),list(),print或?unset()。一個(gè)已實(shí)例化的對(duì)象的方法被作為數(shù)組傳遞,下標(biāo) 0 包含該對(duì)象,下標(biāo) 1 包含方法名。除了普通的用戶自定義函數(shù)外,create_function()可以用來(lái)創(chuàng)建一個(gè)匿名回調(diào)函數(shù)。
<?php?// An example callback function
function?my_callback_function() {
echo?'hello world!';
}
// An example callback method
class?MyClass?{
static function?myCallbackMethod() {
echo?'Hello World!';
? ? }
}
// Type 1: Simple callback
call_user_func('my_callback_function');?
// Type 2: Static class method call
call_user_func(array('MyClass',?'myCallbackMethod'));?
// Type 3: Object method call
$obj?= new?MyClass();
call_user_func(array($obj,?'myCallbackMethod'));
// Type 4: Static class method call (As of PHP 5.2.3)
call_user_func('MyClass::myCallbackMethod');
// Type 5: Relative static class method call (As of PHP 5.3.0)
class?A?{
public static function?who() {
echo?"A\n";
? ? }
}
class?B?extends?A?{
public static function?who() {
echo?"B\n";
? ? }
}
call_user_func(array('B',?'parent::who'));?// A
?>
在函數(shù)中注冊(cè)有多個(gè)回調(diào)內(nèi)容時(shí)(如使用call_user_func()?與?call_user_func_array()),如在前一個(gè)回調(diào)中有未捕獲的異常,其后的將不再被調(diào)用。
mixed?說(shuō)明一個(gè)參數(shù)可以接受多種不同的(但不一定是所有的)類型。例如?gettype()?可以接受所有的 PHP 類型,str_replace()?可以接受字符串和數(shù)組。number?說(shuō)明一個(gè)參數(shù)可以是?integer?或者?float。本文檔中在 PHP 5.4 引入?callable?類型之前使用 了?callback?偽類型。二者含義完全相同。void?作為返回類型意味著函數(shù)的返回值是無(wú)用的。void作為參數(shù)列表意味著函數(shù)不接受任何參數(shù)。在函數(shù)原型中,$...?表示等等的意思。當(dāng)一個(gè)函數(shù)可以接受任意個(gè)參數(shù)時(shí)使用此變量名。
?
<?php$foo?=?"0";?// $foo 是字符串 (ASCII 48)
$foo?+=?2;?// $foo 現(xiàn)在是一個(gè)整數(shù) (2)
$foo?=?$foo?+?1.3;?// $foo 現(xiàn)在是一個(gè)浮點(diǎn)數(shù) (3.3)
$foo?=?5?+?"10 Little Piggies";?// $foo 是整數(shù) (15)
$foo?=?5?+?"10 Small Pigs";?// $foo 是整數(shù) (15)
?>
?
?
自動(dòng)轉(zhuǎn)換為?數(shù)組?的行為目前沒有定義。
<?php $a?=?'car';?// $a is a string $a[0] =?'b';?// $a is still a string echo?$a;?// bar ?>允許的強(qiáng)制轉(zhuǎn)換有:注意在括號(hào)內(nèi)允許有空格和制表符
- (int), (integer) - 轉(zhuǎn)換為整形 integer
- (bool), (boolean) - 轉(zhuǎn)換為布爾類型 boolean
- (float), (double), (real) - 轉(zhuǎn)換為浮點(diǎn)型 float
- (string) - 轉(zhuǎn)換為字符串 string
- (array) - 轉(zhuǎn)換為數(shù)組 array
- (object) - 轉(zhuǎn)換為對(duì)象 object
- (unset) - 轉(zhuǎn)換為 NULL (PHP 5)
3.變量
$this?是一個(gè)特殊的變量,它不能被賦值。 ? ?使用引用賦值,簡(jiǎn)單地將一個(gè) & 符號(hào)加到將要賦值的變量前(源變量)。
<?php$foo?=?'Bob';?// 將 'Bob' 賦給 $foo
$bar?= &$foo;?// 通過(guò) $bar 引用 $foo
$bar?=?"My name is?$bar";?// 修改 $bar 變量
echo?$bar;
echo?$foo;?// $foo 的值也被修改
?>
有一點(diǎn)重要事項(xiàng)須指出,那就是只有有名字的變量才可以引用賦值。$bar?= &(24?*?7);?// 非法; 引用沒有名字的表達(dá)式
未初始化的變量具有其類型的默認(rèn)值 - 布爾類型的變量默認(rèn)值是?FALSE,整形和浮點(diǎn)型變量默認(rèn)值是零,字符串型變量(例如用于?echo中)默認(rèn)值是空字符串以及數(shù)組變量的默認(rèn)值是空數(shù)組。isset()?語(yǔ)言結(jié)構(gòu)可以用來(lái)檢測(cè)一個(gè)變量是否已被初始化。
<?php$a?=?1;
$b?=?2;
function?Sum(){
global?$a,?$b;
$b?=?$a?+?$b;
}
Sum();
echo?$b;
?> 等價(jià)于下面的(輸出為3): $GLOBALS['b'] =?$GLOBALS['a'] +?$GLOBALS['b'];
$GLOBALS是一個(gè)關(guān)聯(lián)數(shù)組,每一個(gè)變量為一個(gè)元素,鍵名對(duì)應(yīng)變量名,值對(duì)應(yīng)變量的內(nèi)容。$GLOBALS之所以在全局范圍內(nèi)存在,是因?yàn)?$GLOBALS 是一個(gè)超全局變量。變量范圍的另一個(gè)重要特性是靜態(tài)變量(static variable)。靜態(tài)變量?jī)H在局部函數(shù)域中存在,但當(dāng)程序執(zhí)行離開此作用域時(shí),其值并不丟失。如果在聲明中用表達(dá)式的結(jié)果對(duì)其賦值會(huì)導(dǎo)致解析錯(cuò)誤。static?$int?=?0;?// correct
static?$int?=?1+2;?// wrong (as it is an expression)
static?$int?=?sqrt(121);?// wrong (as it is an expression too)
可變變量:一個(gè)變量的變量名可以動(dòng)態(tài)的設(shè)置和使用。一個(gè)普通的變量通過(guò)聲明來(lái)設(shè)置。要將可變變量用于數(shù)組,必須解決一個(gè)模棱兩可的問(wèn)題。這就是當(dāng)寫下?$$a[1]時(shí),解析器需要知道是想要?$a[1]作為一個(gè)變量呢,還是想要?$$a作為一個(gè)變量并取出該變量中索引為 [1] 的值。解決此問(wèn)題的語(yǔ)法是,對(duì)第一種情況用${$a[1]},對(duì)第二種情況用?${$a}[1]。
類的屬性也可以通過(guò)可變屬性名來(lái)訪問(wèn)。可變屬性名將在該調(diào)用所處的范圍內(nèi)被解析。例如,對(duì)于?$foo->$bar?表達(dá)式,則會(huì)在本地范圍來(lái)解析?$bar并且其值將被用于?$foo?的屬性名。對(duì)于?$bar是數(shù)組單元時(shí)也是一樣。
<?phpclass?foo?{
var?$bar?=?'I am bar.';
var?$arr?= array('I am A.',?'I am B.',?'I am C.');
var?$r?=?'I am r.';
}
$foo?= new?foo();
$bar?=?'bar';
$baz?= array('foo',?'bar',?'baz',?'quux');
echo?$foo->$bar?.?"\n";
echo?$foo->$baz[1] .?"\n";
$start?=?'b';
$end?=?'ar';
echo?$foo->{$start?.?$end} .?"\n";
$arr?=?'arr';
echo?$foo->$arr[1] .?"\n";
echo?$foo->{$arr}[1] .?"\n";
?> I am bar.
I am bar.
I am bar.
I am r.
I am B.
注意,在 PHP 的函數(shù)和類的方法中,超全局變量不能用作可變變量。$this變量也是一個(gè)特殊變量,不能被動(dòng)態(tài)引用。
變量名中的點(diǎn)和空格被轉(zhuǎn)換成下劃線。例如?<input name="a.b" />?變成了?$_REQUEST["a_b"]。
4.常量可以用?define()?函數(shù)來(lái)定義常量,在 PHP 5.3.0 以后,可以使用?const關(guān)鍵字在類定義之外定義常量。一個(gè)常量一旦被定義,就不能再改變或者取消定義。常量只能包含標(biāo)量數(shù)據(jù)(boolean,integer,float和?string)??梢远x?resource?常量,但應(yīng)盡量避免,因?yàn)闀?huì)造成不可預(yù)料的結(jié)果。如果常量名是動(dòng)態(tài)的,也可以用函數(shù)?constant()?來(lái)獲取常量的值。用?get_defined_constants()?可以獲得所有已定義的常量列表。如果只想檢查是否定義了某常量,用?defined()?函數(shù)。
常量和變量有如下不同:
- 常量前面沒有美元符號(hào)($);
- 常量只能用 define() 函數(shù)定義,而不能通過(guò)賦值語(yǔ)句;
- 常量可以不用理會(huì)變量的作用域而在任何地方定義和訪問(wèn);
- 常量一旦定義就不能被重新定義或者取消定義;
- 常量的值只能是標(biāo)量。
和使用?define()?來(lái)定義常量相反的是,使用?const關(guān)鍵字定義常量必須處于最頂端的作用區(qū)域,因?yàn)橛么朔椒ㄊ窃诰幾g時(shí)定義的。這就意味著不能在函數(shù)內(nèi),循環(huán)內(nèi)以及?if?語(yǔ)句之內(nèi)用?const?來(lái)定義常量。
有八個(gè)魔術(shù)常量它們的值隨著它們?cè)诖a中的位置改變而改變。
| __LINE__ | 文件中的當(dāng)前行號(hào)。 |
| __FILE__ | 文件的完整路徑和文件名。如果用在被包含文件中,則返回被包含的文件名。 |
| __DIR__ | 文件所在的目錄。如果用在被包括文件中,則返回被包括的文件所在的目錄。 |
| __FUNCTION__ | 函數(shù)名稱(PHP 4.3.0 新加)。自 PHP 5 起本常量返回該函數(shù)被定義時(shí)的名字(區(qū)分大小寫)。在 PHP 4 中該值總是小寫字母的。 |
| __CLASS__ | 類的名稱(PHP 4.3.0 新加)。自 PHP 5 起本常量返回該類被定義時(shí)的名字(區(qū)分大小寫)。在 PHP 4 中該值總是小寫字母的。類名包括其被聲明的作用區(qū)域(例如?Foo\Bar)。注意自 PHP 5.4 起 __CLASS__ 對(duì) trait 也起作用。當(dāng)用在 trait 方法中時(shí),__CLASS__ 是調(diào)用 trait 方法的類的名字。 |
| __TRAIT__ | Trait 的名字(PHP 5.4.0 新加)。自 PHP 5.4 起此常量返回 trait 被定義時(shí)的名字(區(qū)分大小寫)。Trait 名包括其被聲明的作用區(qū)域(例如?Foo\Bar)。 |
| __METHOD__ | 類的方法名(PHP 5.0.0 新加)。返回該方法被定義時(shí)的名字(區(qū)分大小寫)。 |
| __NAMESPACE__ | 當(dāng)前命名空間的名稱(區(qū)分大小寫)。此常量是在編譯時(shí)定義的(PHP 5.3.0 新增)。 |
| 無(wú) | clone new | clone 和 new |
| 左 | [ | array() |
| 右 | ++ -- ~ (int) (float) (string) (array) (object) (bool) @ | 類型和遞增/遞減 |
| 無(wú) | instanceof | 類型 |
| 右 | ! | 邏輯運(yùn)算符 |
| 左 | * / % | 算術(shù)運(yùn)算符 |
| 左 | + - . | 算術(shù)運(yùn)算符和字符串運(yùn)算符 |
| 左 | << >> | 位運(yùn)算符 |
| 無(wú) | == != === !== <> | 比較運(yùn)算符 |
| 左 | & | 位運(yùn)算符和引用 |
| 左 | ^ | 位運(yùn)算符 |
| 左 | | | 位運(yùn)算符 |
| 左 | && | 邏輯運(yùn)算符 |
| 左 | || | 邏輯運(yùn)算符 |
| 左 | ? : | 三元運(yùn)算符 |
| 右 | = += -= *= /= .= %= &= |= ^= <<= >>= => | 賦值運(yùn)算符 |
| 左 | and | 邏輯運(yùn)算符 |
| 左 | xor | 邏輯運(yùn)算符 |
| 左 | or | 邏輯運(yùn)算符 |
| 左 | , | 多處用到 |
除法運(yùn)算符總是返回浮點(diǎn)數(shù)。只有在下列情況例外:兩個(gè)操作數(shù)都是整數(shù)(或字符串轉(zhuǎn)換成的整數(shù))并且正好能整除,這時(shí)它返回一個(gè)整數(shù)。取模運(yùn)算符的操作數(shù)在運(yùn)算之前都會(huì)轉(zhuǎn)換成整數(shù)(除去小數(shù)部分)。取模運(yùn)算符?%?的結(jié)果和被除數(shù)的符號(hào)(正負(fù)號(hào))相同。基本的賦值運(yùn)算符是"="。實(shí)際上意味著把右邊表達(dá)式的值賦給左邊的運(yùn)算數(shù)。
$a?= ($b?=?4) +?5;?// $a 現(xiàn)在成了 9,而 $b 成了 4。在 PHP 中普通的傳值賦值行為有個(gè)例外就是碰到對(duì)象?object?時(shí),在 PHP 5 中是以引用賦值的,除非明確使用了 clone 關(guān)鍵字來(lái)拷貝。PHP 支持引用賦值,使用"$var = &$othervar;"語(yǔ)法。引用賦值意味著兩個(gè)變量指向了同一個(gè)數(shù)據(jù),沒有拷貝任何東西。
位運(yùn)算符:
| $a & $b | And(按位與) | 將把?$a?和?$b?中都為 1 的位設(shè)為 1。 |
| $a | $b | Or(按位或) | 將把?$a?和?$b?中任何一個(gè)為 1 的位設(shè)為 1。 |
| $a ^ $b | Xor(按位異或) | 將把?$a?和?$b?中一個(gè)為 1 另一個(gè)為 0 的位設(shè)為 1。 |
| ~ $a | Not(按位取反) | 將?$a?中為 0 的位設(shè)為 1,反之亦然。 |
| $a << $b | Shift left(左移) | 將?$a?中的位向左移動(dòng)?$b?次(每一次移動(dòng)都表示"乘以 2")。 |
| $a >> $b | Shift right(右移) | 將?$a?中的位向右移動(dòng)?$b?次(每一次移動(dòng)都表示"除以 2")。 |
左移時(shí)右側(cè)以零填充,符號(hào)位被移走意味著正負(fù)號(hào)不被保留。右移時(shí)左側(cè)以符號(hào)位填充,意味著正負(fù)號(hào)被保留。
要注意數(shù)據(jù)類型的轉(zhuǎn)換。如果左右參數(shù)都是字符串,則位運(yùn)算符將對(duì)字符的 ASCII 值進(jìn)行操作。不要在 32 位系統(tǒng)下向右移超過(guò) 32 位。不要在結(jié)果可能超過(guò) 32 的情況下左移。使用 gmp 擴(kuò)展對(duì)超出 PHP_INT_MAX 的數(shù)值來(lái)進(jìn)行位操作。
比較運(yùn)算符:| $a == $b | 等于 | TRUE,如果類型轉(zhuǎn)換后?$a?等于?$b。 |
| $a === $b | 全等 | TRUE,如果?$a?等于?$b,并且它們的類型也相同。 |
| $a != $b | 不等 | TRUE,如果類型轉(zhuǎn)換后?$a?不等于?$b。 |
| $a <> $b | 不等 | TRUE,如果類型轉(zhuǎn)換后?$a?不等于?$b。 |
| $a !== $b | 不全等 | TRUE,如果?$a?不等于?$b,或者它們的類型不同。 |
| $a < $b | 小與 | TRUE,如果?$a?嚴(yán)格小于?$b。 |
| $a > $b | 大于 | TRUE,如果?$a?嚴(yán)格大于?$b。 |
| $a <= $b | 小于等于 | TRUE,如果?$a?小于或者等于?$b。 |
| $a >= $b | 大于等于 | TRUE,如果?$a?大于或者等于?$b。 |
var_dump("1"?==?"01");?// 1 == 1 -> true
var_dump("10"?==?"1e1");?// 10 == 10 -> true
由于浮點(diǎn)數(shù)?float?的內(nèi)部表達(dá)方式,不應(yīng)比較兩個(gè)浮點(diǎn)數(shù)是否相等。另一個(gè)條件運(yùn)算符是"?:"(或三元)運(yùn)算符 。
$action?= (empty($_POST['action'])) ??'default'?:?$_POST['action']; 表達(dá)式?(expr1) ? (expr2) : (expr3)?在?expr1?求值為?TRUE時(shí)的值為?expr2,在?expr1?求值為?FALSE?時(shí)的值為? ? ?expr3。自 PHP 5.3 起,可以省略三元運(yùn)算符中間那部分。表達(dá)式?expr1 ?: expr3?在?expr1?求值為?TRUE?時(shí)返回expr1,否則返回?expr3。
PHP 支持一個(gè)錯(cuò)誤控制運(yùn)算符:@。當(dāng)將其放置在一個(gè) PHP 表達(dá)式之前,該表達(dá)式可能產(chǎn)生的任何錯(cuò)誤信息都被忽略掉。
如果用?set_error_handler()設(shè)定了自定義的錯(cuò)誤處理函數(shù),仍然會(huì)被調(diào)用,但是此錯(cuò)誤處理函數(shù)可以(并且也應(yīng)該)調(diào)用?error_reporting(),而該函數(shù)在出錯(cuò)語(yǔ)句前有 @ 時(shí)將返回 0。如果激活了?track_errors特性,表達(dá)式所產(chǎn)生的任何錯(cuò)誤信息都被存放在變量?$php_errormsg中。此變量在每次出錯(cuò)時(shí)都會(huì)被覆蓋,所以如果想用它的話就要盡早檢查。
PHP 支持一個(gè)執(zhí)行運(yùn)算符:反引號(hào)(``)。注意這不是單引號(hào)!PHP 將嘗試將反引號(hào)中的內(nèi)容作為外殼命令來(lái)執(zhí)行,并將其輸出信息返回(即,可以賦給一個(gè)變量而不是簡(jiǎn)單地丟棄到標(biāo)準(zhǔn)輸出)。使用反引號(hào)運(yùn)算符"`"的效果與函數(shù)?shell_exec()?相同。
<?php$output?= `ls -al`;
echo?"<pre>$output</pre>";
?>
反引號(hào)運(yùn)算符在激活了安全模式或關(guān)閉了?shell_exec()?時(shí)是無(wú)效的。與其它某些語(yǔ)言不同,反引號(hào)不能在雙引號(hào)字符串中使用。
遞增或遞減布爾值沒有效果。遞增遞減運(yùn)算符:
| ++$a | 前加 | $a?的值加一,然后返回?$a。 |
| $a++ | 后加 | 返回?$a,然后將?$a?的值加一。 |
| --$a | 前減 | $a?的值減一, 然后返回?$a。 |
| $a-- | 后減 | 返回?$a,然后將?$a?的值減一。 |
| $a and $b | And(邏輯與) | TRUE,如果?$a?和?$b?都為?TRUE。 |
| $a or $b | Or(邏輯或) | TRUE,如果?$a?或?$b?任一為?TRUE。 |
| $a xor $b | Xor(邏輯異或) | TRUE,如果?$a?或?$b?任一為?TRUE,但不同時(shí)是。 |
| ! $a | Not(邏輯非) | TRUE,如果?$a?不為?TRUE。 |
| $a && $b | And(邏輯與) | TRUE,如果?$a?和?$b?都為?TRUE。 |
| $a || $b | Or(邏輯或) | TRUE,如果?$a?或?$b?任一為?TRUE。 |
"與"和"或"有兩種不同形式運(yùn)算符的原因是它們運(yùn)算的優(yōu)先級(jí)不同
有兩個(gè)字符串(string)運(yùn)算符。第一個(gè)是連接運(yùn)算符("."),它返回其左右參數(shù)連接后的字符串。第二個(gè)是連接賦值運(yùn)算符(".="),它將右邊參數(shù)附加到左邊的參數(shù)之后。
<?php$a?=?"Hello ";
$b?=?$a?.?"World!";?// now $b contains "Hello World!"
$a?=?"Hello ";
$a?.=?"World!";?// now $a contains "Hello World!"
?> 數(shù)組運(yùn)算符:
| $a + $b | 聯(lián)合 | $a?和?$b?的聯(lián)合。 |
| $a == $b | 相等 | 如果?$a?和?$b?具有相同的鍵/值對(duì)則為?TRUE。 |
| $a === $b | 全等 | 如果?$a?和?$b?具有相同的鍵/值對(duì)并且順序和類型都相同則為?TRUE。 |
| $a != $b | 不等 | 如果?$a?不等于?$b?則為?TRUE。 |
| $a <> $b | 不等 | 如果?$a?不等于?$b?則為?TRUE。 |
| $a !== $b | 不全等 | 如果?$a?不全等于?$b?則為?TRUE。 |
+?運(yùn)算符把右邊的數(shù)組元素附加到左邊的數(shù)組后面,兩個(gè)數(shù)組中都有的鍵名,則只用左邊數(shù)組中的,右邊的被忽略。
?
<?php$a?= array("apple",?"banana");
$b?= array(1?=>?"banana",?"0"?=>?"apple");
var_dump($a?==?$b);?// bool(true)
var_dump($a?===?$b);?// bool(false)
?> 類型運(yùn)算符:instanceof?用于確定一個(gè) PHP 變量是否屬于某一類?class?的實(shí)例。instanceof 也可用來(lái)確定一個(gè)變量是不是繼承自某一父類的子類的實(shí)例。檢查一個(gè)對(duì)象是否不是某個(gè)類的實(shí)例,可以使用邏輯運(yùn)算符?not。instanceof也可用于確定一個(gè)變量是不是實(shí)現(xiàn)了某個(gè)接口的對(duì)象的實(shí)例。雖然?instanceof?通常直接與類名一起使用,但也可以使用對(duì)象或字符串變量:
?
?
<?phpinterface?MyInterface{?}
class?MyClass?implements?MyInterface{?}
$a?= new?MyClass;
$b?= new?MyClass;
$c?=?'MyClass';
$d?=?'NotMyClass';
var_dump($a?instanceof?$b);?// $b is an object of class MyClass
var_dump($a?instanceof?$c);?// $c is a string 'MyClass'
var_dump($a?instanceof?$d);?// $d is a string 'NotMyClass'
?> bool(true) bool(true) bool(false)
如果被檢測(cè)的變量不是對(duì)象,instanceof 并不發(fā)出任何錯(cuò)誤信息而是返回?FALSE。不允許用來(lái)檢測(cè)常量。instanceof?運(yùn)算符是 PHP 5 引進(jìn)的。在此之前用?is_a(),但是后來(lái)?is_a()?被廢棄而用?instanceof替代了。注意自 PHP 5.3.0 起,又恢復(fù)使用?is_a()?了。
6.流程控制?
?
if?語(yǔ)句可以無(wú)限層地嵌套在其它?if?語(yǔ)句中,這給程序的不同部分的條件執(zhí)行提供了充分的彈性。
else?語(yǔ)句僅在?if?以及?elseif(如果有的話)語(yǔ)句中的表達(dá)式的值為?FALSE?時(shí)執(zhí)行。elseif?的語(yǔ)句僅在之前的?if?和所有之前?elseif?的表達(dá)式值為?FALSE,并且當(dāng)前的?elseif?表達(dá)式值為?TRUE?時(shí)執(zhí)行
?
必須要注意的是?elseif?與?else if只有在類似上例中使用花括號(hào)的情況下才認(rèn)為是完全相同。如果用冒號(hào)來(lái)定義? ?if/elseif?條件,那就不能用兩個(gè)單詞的?else if,否則 PHP 會(huì)產(chǎn)生解析錯(cuò)誤。
?
<?php/* 不正確的使用方法: */
if($a?>?$b):
? ? echo?$a." is greater than ".$b;
else if($a?==?$b):?// 將無(wú)法編譯
? ? echo?"The above line causes a parse error.";
endif;
/* 正確的使用方法: */
if($a?>?$b):
? ? echo?$a." is greater than ".$b;
elseif($a?==?$b):?// 注意使用了一個(gè)單詞的 elseif
? ? echo?$a." equals ".$b;
else:
? ? echo?$a." is neither greater than or equal to ".$b;
endif;
?>
?
?
PHP 提供了一些流程控制的替代語(yǔ)法,包括?if,while,for,foreach和?switch。替代語(yǔ)法的基本形式是把左花括號(hào)({)換成冒號(hào)(:),把右花括號(hào)(})分別換成?endif;,endwhile;,endfor;,endforeach;以及?endswitch;。
不支持在同一個(gè)控制塊內(nèi)混合使用兩種語(yǔ)法。
?
do-while?循環(huán)和?while循環(huán)非常相似,區(qū)別在于表達(dá)式的值是在每次循環(huán)結(jié)束時(shí)檢查而不是開始時(shí)。和一般的?while?循環(huán)主要的區(qū)別是?do-while的循環(huán)語(yǔ)句保證會(huì)執(zhí)行一次(表達(dá)式的真值在每次循環(huán)結(jié)束后檢查),然而在一般的?while?循環(huán)中就不一定了(表達(dá)式真值在循環(huán)開始時(shí)檢查,如果一開始就為?FALSE?則整個(gè)循環(huán)立即終止)。
foreach?語(yǔ)法結(jié)構(gòu)提供了遍歷數(shù)組的簡(jiǎn)單方式。foreach僅能夠應(yīng)用于數(shù)組和對(duì)象,如果嘗試應(yīng)用于其他數(shù)據(jù)類型的變量,或者未初始化的變量將發(fā)出錯(cuò)誤信息。有兩種語(yǔ)法:
foreach (array_expression as $value)statement foreach (array_expression as $key => $value)statement?
?
當(dāng)?foreach開始執(zhí)行時(shí),數(shù)組內(nèi)部的指針會(huì)自動(dòng)指向第一個(gè)單元。這意味著不需要在?foreach?循環(huán)之前調(diào)用?reset()。
由于?foreach?依賴內(nèi)部數(shù)組指針,在循環(huán)中修改其值將可能導(dǎo)致意外的行為。
?
?
<?php$arr?= array(1,?2,?3,?4);
foreach ($arr?as &$value) {
? ? $value?=?$value?*?2;
}
// $arr is now array(2, 4, 6, 8)
unset($value);?// 最后取消掉引用
?>
數(shù)組最后一個(gè)元素的?$value?引用在?foreach?循環(huán)之后仍會(huì)保留。建議使用?unset()?來(lái)將其銷毀。
以下的代碼功能完全相同:?
?
<?php$arr?= array("one",?"two",?"three");
reset($arr);
while (list(,?$value) =?each($arr)) {
? ? echo?"Value:?$value<br>\n";
}
foreach ($arr?as?$value) {
? ? echo?"Value:?$value<br />\n";
}
?> 或者:
?
?
<?php$arr?= array("one",?"two",?"three");
reset($arr);
while (list($key,?$value) =?each($arr)) {
? ? echo?"Key:?$key; Value:?$value<br />\n";
}
foreach ($arr?as?$key?=>?$value) {
? ? echo?"Key:?$key; Value:?$value<br />\n";
}
?>
?
?
PHP 5.5 增添了遍歷一個(gè)數(shù)組的數(shù)組的功能并且把嵌套的數(shù)組解包到循環(huán)變量中,只需將?list()?作為值提供。
<?php$array?= [
? ? [1,?2],
? ? [3,?4],
];
foreach ($array?as list($a,?$b)) {
? ? // $a contains the first element of the nested array,
? ? // and $b contains the second element.
? ? echo?"A:?$a; B:?$b\n";
}
?> A: 1; B: 2 A: 3; B: 4
list()?中的單元可以少于嵌套數(shù)組的,此時(shí)多出來(lái)的數(shù)組單元將被忽略。echo?"$a\n"; ? ?//會(huì)輸出1 3
如果?list()?中列出的單元多于嵌套數(shù)組則會(huì)發(fā)出一條消息級(jí)別的錯(cuò)誤信息。echo?"A:?$a; B:?$b; C:?$c\n";//會(huì)輸出
Notice: Undefined offset: 2 in example.php on line 7 A: 1; B: 2; C: Notice: Undefined offset: 2 in example.php on line 7 A: 3; B: 4; C:break?可以接受一個(gè)可選的數(shù)字參數(shù)來(lái)決定跳出幾重循環(huán)。
<?php$arr?= array('one',?'two',?'three',?'four',?'stop',?'five');
while (list (,?$val) =?each($arr)) {
? ? if ($val?==?'stop') {
? ? ? ? break;?/* You could also write 'break 1;' here. */
? ? }
? ? echo?"$val<br />\n";
}
/* 使用可選參數(shù) */
$i?=?0;
while (++$i) {
? ? switch ($i) {
? ? case?5:echo?"At 5<br />\n";
? ? ? ? ? ?break?1;?/* 只退出 switch. */
? ? case?10:echo?"At 10; quitting<br />\n";
? ? ? ? ? ? break?2;?/* 退出 switch 和 while 循環(huán) */
? ? default:
? ? ? ? ? ? break;
? ? ? ? }
}
?>
continue在循環(huán)結(jié)構(gòu)用用來(lái)跳過(guò)本次循環(huán)中剩余的代碼并在條件求值為真時(shí)開始執(zhí)行下一次循環(huán)。continue?接受一個(gè)可選的數(shù)字參數(shù)來(lái)決定跳過(guò)幾重循環(huán)到循環(huán)結(jié)尾。默認(rèn)值是?1,即跳到當(dāng)前循環(huán)末尾。省略?continue后面的分號(hào)會(huì)導(dǎo)致混淆。
<?phpwhile (list ($key,?$value) =?each($arr)) {
? ? if (!($key?%?2)) {?// skip odd members
? ? ? ? continue;
? ? }
? ? do_something_odd($value);
}
$i?=?0;
while ($i++ <?5) {
? ? echo?"Outer<br />\n";
? ? while (1) {
? ? ? ? echo?"Middle<br />\n";
? ? ? ? while (1) {
? ? ? ? ? ? echo?"Inner<br />\n";
? ? ? ? ? ? continue?3;
? ? ? ? }
? ? ? ? echo?"This never gets output.<br />\n";
? ? }
? ? echo?"Neither does this.<br />\n";
}
?>
?
?
注意和其它語(yǔ)言不同,continue語(yǔ)句作用到 switch 上的作用類似于?break。如果在循環(huán)中有一個(gè) switch 并希望 continue 到外層循環(huán)中的下一輪循環(huán),用?continue 2。注意 switch/case 作的是松散比較。允許使用分號(hào)代替 case 語(yǔ)句后的冒號(hào).
?
?
declare?結(jié)構(gòu)用來(lái)設(shè)定一段代碼的執(zhí)行指令。declare的語(yǔ)法和其它流程控制結(jié)構(gòu)相似:
declare (directive)statementdirective?部分允許設(shè)定?declare代碼段的行為。目前只認(rèn)識(shí)兩個(gè)指令:ticks以及?encoding。
declare?代碼段中的?statement部分將被執(zhí)行—怎樣執(zhí)行以及執(zhí)行中有什么副作用出現(xiàn)取決于?directive?中設(shè)定的指令。declare?結(jié)構(gòu)也可用于全局范圍,影響到其后的所有代碼(但如果有?declare?結(jié)構(gòu)的文件被其它文件包含,則對(duì)包含它的父文件不起作用)。
Tick(時(shí)鐘周期)是一個(gè)在?declare?代碼段中解釋器每執(zhí)行?N?條可計(jì)時(shí)的低級(jí)語(yǔ)句就會(huì)發(fā)生的事件。N的值是在?declare?中的?directive?部分用?ticks=N?來(lái)指定的。不是所有語(yǔ)句都可計(jì)時(shí)。通常條件表達(dá)式和參數(shù)表達(dá)式都不可計(jì)時(shí)。在每個(gè) tick 中出現(xiàn)的事件是由?register_tick_function()來(lái)指定的。注意每個(gè) tick 中可以出現(xiàn)多個(gè)事件。
可以用 encoding 指令來(lái)對(duì)每段腳本指定其編碼方式。對(duì)腳本指定編碼方式:
<?phpdeclare(encoding='ISO-8859-1');
// code here
?>
如果在一個(gè)函數(shù)中調(diào)用?return語(yǔ)句,將立即結(jié)束此函數(shù)的執(zhí)行并將它的參數(shù)作為函數(shù)的值返回。return也會(huì)終止?eval()?語(yǔ)句或者腳本文件的執(zhí)行。如果在全局范圍中調(diào)用,則當(dāng)前腳本文件中止運(yùn)行。如果當(dāng)前腳本文件是被?include?的或者?require的,則控制交回調(diào)用文件。此外,如果當(dāng)前腳本是被?include的,則?return?的值會(huì)被當(dāng)作?include調(diào)用的返回值。如果在主腳本文件中調(diào)用?return,則腳本中止運(yùn)行。如果當(dāng)前腳本文件是在?php.ini中的配置選項(xiàng)?auto_prepend_file?或者?auto_append_file?所指定的,則此腳本文件中止運(yùn)行。當(dāng)用引用返回值時(shí)永遠(yuǎn)不要使用括號(hào),這樣行不通。只能通過(guò)引用返回變量,而不是語(yǔ)句的結(jié)果。如果使用?return ($a);?時(shí)其實(shí)不是返回一個(gè)變量,而是表達(dá)式?($a)?的值(當(dāng)然,此時(shí)該值也正是?$a?的值)。
?
?
?
?
require?和?include?幾乎完全一樣,除了處理失敗的方式不同之外。require?在出錯(cuò)時(shí)產(chǎn)生?E_COMPILE_ERROR?級(jí)別的錯(cuò)誤。換句話說(shuō)將導(dǎo)致腳本中止而?include?只產(chǎn)生警告(E_WARNING),腳本會(huì)繼續(xù)運(yùn)行。
被包含文件先按參數(shù)給出的路徑尋找,如果沒有給出目錄(只有文件名)時(shí)則按照 include_path 指定的目錄尋找。如果在 include_path 下沒找到該文件則?include?最后才在調(diào)用腳本文件所在的目錄和當(dāng)前工作目錄下尋找。如果最后仍未找到文件則?include?結(jié)構(gòu)會(huì)發(fā)出一條警告;這一點(diǎn)和?require?不同,后者會(huì)發(fā)出一個(gè)致命錯(cuò)誤。當(dāng)一個(gè)文件被包含時(shí),其中所包含的代碼繼承了 include 所在行的變量范圍。從該處開始,調(diào)用文件在該行處可用的任何變量在被調(diào)用的文件中也都可用。不過(guò)所有在包含文件中定義的函數(shù)和類都具有全局作用域。
vars.php<?php
$color?=?'green';
$fruit?=?'apple';
?>
test.php
<?php
echo?"A?$color?$fruit";?//?A
include?'vars.php';
echo?"A?$color?$fruit";?//?A?green?apple
?>
如果 include 出現(xiàn)于調(diào)用文件中的一個(gè)函數(shù)里,則被調(diào)用的文件中所包含的所有代碼將表現(xiàn)得如同它們是在該函數(shù)內(nèi)部定義的一樣。所以它將遵循該函數(shù)的變量范圍。此規(guī)則的一個(gè)例外是魔術(shù)常量,它們是在發(fā)生包含之前就已被解析器處理的。
<?phpfunction?foo(){
????global?$color;
????include?'vars.php';
????echo?"A?$color?$fruit";
}
foo();????????????????????//?A?green?apple
echo?"A?$color?$fruit";???//?A?green
?>
處理返回值:在失敗時(shí)?include?返回?FALSE?并且發(fā)出警告。成功的包含則返回?1,除非在包含文件中另外給出了返回值??梢栽诒话ǖ奈募惺褂?return?語(yǔ)句來(lái)終止該文件中程序的執(zhí)行并返回調(diào)用它的腳本。同樣也可以從被包含的文件中返回值??梢韵衿胀ê瘮?shù)一樣獲得 include 調(diào)用的返回值。不過(guò)這在包含遠(yuǎn)程文件時(shí)卻不行,除非遠(yuǎn)程文件的輸出具有合法的 PHP 開始和結(jié)束標(biāo)記(如同任何本地文件一樣)。可以在標(biāo)記內(nèi)定義所需的變量,該變量在文件被包含的位置之后就可用了。
因?yàn)?include?是一個(gè)特殊的語(yǔ)言結(jié)構(gòu),其參數(shù)不需要括號(hào)。在比較其返回值時(shí)要注意。
return.php<?php
$var?=?'PHP';
return?$var;
?>
noreturn.php
<?php
$var?=?'PHP';
?>
testreturns.php
<?php
$foo?=?include?'return.php';
echo?$foo;?//?prints?'PHP'
$bar?=?include?'noreturn.php';
echo?$bar;?//?prints?1
?>
如果在包含文件中定義有函數(shù),這些函數(shù)不管是在?return?之前還是之后定義的,都可以獨(dú)立在主文件中使用。如果文件被包含兩次,PHP 5 發(fā)出致命錯(cuò)誤因?yàn)楹瘮?shù)已經(jīng)被定義,但是 PHP 4 不會(huì)對(duì)在?return?之后定義的函數(shù)報(bào)錯(cuò)。推薦使用?include_once?而不是檢查文件是否已包含并在包含文件中有條件返回。要在腳本中自動(dòng)包含文件,參見?php.ini?中的?auto_prepend_file?和?auto_append_file?配置選項(xiàng)。
require_once?語(yǔ)句和?require?語(yǔ)句完全相同,唯一區(qū)別是 PHP 會(huì)檢查該文件是否已經(jīng)被包含過(guò),如果是則不會(huì)再次包含。
include_once?語(yǔ)句在腳本執(zhí)行期間包含并運(yùn)行指定文件。此行為和?include?語(yǔ)句類似,唯一區(qū)別是如果該文件中已經(jīng)被包含過(guò),則不會(huì)再次包含。如同此語(yǔ)句名字暗示的那樣,只會(huì)包含一次。
goto?操作符可以用來(lái)跳轉(zhuǎn)到程序中的另一位置。該目標(biāo)位置可以用目標(biāo)名稱加上冒號(hào)來(lái)標(biāo)記,而跳轉(zhuǎn)指令是goto?之后接上目標(biāo)位置的標(biāo)記。PHP 中的?goto?有一定限制,目標(biāo)位置只能位于同一個(gè)文件和作用域,也就是說(shuō)無(wú)法跳出一個(gè)函數(shù)或類方法,也無(wú)法跳入到另一個(gè)函數(shù)。也無(wú)法跳入到任何循環(huán)或者 switch 結(jié)構(gòu)中。可以跳出循環(huán)或者 switch,通常的用法是用?goto?代替多層的?break。
goto?操作符僅在 PHP 5.3及以上版本有效。
7.函數(shù)?
?
函數(shù)無(wú)需在調(diào)用之前被定義,除非是下面例子中函數(shù)是有條件被定義時(shí)。當(dāng)一個(gè)函數(shù)是有條件被定義時(shí),其定義必須在調(diào)用之前先處理。
<?php$makefoo?=?true;
/*?不能在此處調(diào)用foo()函數(shù),因?yàn)樗€不存在,但可以調(diào)用bar()函數(shù)。*/
bar();
if?($makefoo)?{
??function?foo(){
????echo?"I?don't?exist?until?program?execution?reaches?me.\n";
??}
}
/*?現(xiàn)在可以安全調(diào)用函數(shù)?foo()了,因?yàn)?$makefoo?值為真?*/
if?($makefoo)?foo();
function?bar(){
??echo?"I?exist?immediately?upon?program?start.\n";
}
?>
?
函數(shù)中的函數(shù):?
?
<?phpfunction?foo(){
??function?bar(){
????echo?"I?don't?exist?until?foo()?is?called.\n";
??}
}
/*?現(xiàn)在還不能調(diào)用bar()函數(shù),因?yàn)樗€不存在?*/
foo();
/*?現(xiàn)在可以調(diào)用bar()函數(shù)了,因?yàn)閒oo()函數(shù)的執(zhí)行使得bar()函數(shù)變?yōu)橐讯x的函數(shù)?*/
bar();
?>
PHP 不支持函數(shù)重載,也不可能取消定義或者重定義已聲明的函數(shù)。PHP 的函數(shù)支持可變數(shù)量的參數(shù)和默認(rèn)參數(shù)。
在 PHP 中可以調(diào)用遞歸函數(shù)。但是要避免遞歸函數(shù)/方法調(diào)用超過(guò) 100-200 層,因?yàn)榭赡軙?huì)使堆棧崩潰從而使當(dāng)前腳本終止。
<?phpfunction?recursion($a){
????if?($a?<?20)?{
????????echo?"$a\n";
????????recursion($a?+?1);
????}
}
?>
PHP 支持按值傳遞參數(shù)(默認(rèn)),通過(guò)引用傳遞參數(shù)以及默認(rèn)參數(shù)。也支持可變長(zhǎng)度參數(shù)列表。默認(rèn)情況下,函數(shù)參數(shù)通過(guò)值傳遞(因而即使在函數(shù)內(nèi)部改變參數(shù)的值,它并不會(huì)改變函數(shù)外部的值)。如果希望允許函數(shù)修改它的參數(shù)值,必須通過(guò)引用傳遞參數(shù)。
如果想要函數(shù)的一個(gè)參數(shù)總是通過(guò)引用傳遞,可以在函數(shù)定義中該參數(shù)的前面加上符號(hào) &。用引用傳遞函數(shù)參數(shù):
<?phpfunction?add_some_extra(&$string){
????$string?.=?'and?something?extra.';
}
$str?=?'This?is?a?string,?';
add_some_extra($str);
echo?$str;????//?outputs?'This?is?a?string,?and?something?extra.'
?>
在函數(shù)中使用默認(rèn)參數(shù):
?
<?phpfunction?makecoffee($type?=?"cappuccino"){
????return?"Making?a?cup?of?$type.\n";
}
echo?makecoffee(); ? ?//Making a cup of cappuccino.?
echo?makecoffee(null); ? ?//Making a cup of .
echo?makecoffee("espresso"); ? ?//Making a cup of espresso.
?>
PHP 還允許使用數(shù)組?array?和特殊類型?NULL?作為默認(rèn)參數(shù):
<?phpfunction?makecoffee($types?=?array("cappuccino"),?$coffeeMaker?=?NULL){
????$device?=?is_null($coffeeMaker)???"hands"?:?$coffeeMaker;
????return?"Making?a?cup?of?".join(",?",?$types)."?with?$device.\n";
}
echo?makecoffee();
echo?makecoffee(array("cappuccino",?"lavazza"),?"teapot");
?>
?
默認(rèn)值必須是常量表達(dá)式,不能是諸如變量,類成員,或者函數(shù)調(diào)用等。注意當(dāng)使用默認(rèn)參數(shù)時(shí),任何默認(rèn)參數(shù)必須放在任何非默認(rèn)參數(shù)的右側(cè);否則,函數(shù)將不會(huì)按照預(yù)期的情況工作。自 PHP 5 起,傳引用的參數(shù)也可以有默認(rèn)值。
PHP 在用戶自定義函數(shù)中支持可變數(shù)量的參數(shù)列表。
<?phpfunction?sum(...$numbers)?{
????$acc?=?0;
????foreach?($numbers?as?$n)?{
????????$acc?+=?$n;
????}
????return?$acc;
}
echo?sum(1,?2,?3,?4); ? ?//10
?>
從函數(shù)返回一個(gè)引用,必須在函數(shù)聲明和指派返回值給一個(gè)變量時(shí)都使用引用運(yùn)算符 &:
<?phpfunction?&returns_reference(){
????return?$someref;
}
$newref?=&?returns_reference();
?>
可變函數(shù)可以用來(lái)實(shí)現(xiàn)包括回調(diào)函數(shù),函數(shù)表在內(nèi)的一些用途。
匿名函數(shù)(Anonymous functions),也叫閉包函數(shù)(closures),允許 臨時(shí)創(chuàng)建一個(gè)沒有指定名稱的函數(shù)。最經(jīng)常用作回調(diào)函數(shù)(callback)參數(shù)的值。當(dāng)然,也有其它應(yīng)用的情況。
<?phpecho?preg_replace_callback('~-([a-z])~',?function?($match)?{
????return?strtoupper($match[1]);
},?'hello-world');
//?輸出?helloWorld
?>
閉包函數(shù)也可以作為變量的值來(lái)使用。PHP 會(huì)自動(dòng)把此種表達(dá)式轉(zhuǎn)換成內(nèi)置類 Closure 的對(duì)象實(shí)例。
<?php$greet?=?function($name){
????printf("Hello?%s\r\n",?$name);
};
$greet('World');
$greet('PHP');
?>
閉包可以從父作用域中繼承變量。 任何此類變量都應(yīng)該用?use?語(yǔ)言結(jié)構(gòu)傳遞進(jìn)去。匿名函數(shù)目前是通過(guò)Closure?類來(lái)實(shí)現(xiàn)的。
8.類與對(duì)象?
?
?
如果在?new?之后跟著的是一個(gè)包含有類名的字符串,則該類的一個(gè)實(shí)例被創(chuàng)建。如果該類屬于一個(gè)名字空間,則必須使用其完整名稱。
在類定義內(nèi)部,可以用?new self?和?new parent?創(chuàng)建新對(duì)象。當(dāng)把一個(gè)對(duì)象已經(jīng)創(chuàng)建的實(shí)例賦給一個(gè)新變量時(shí),新變量會(huì)訪問(wèn)同一個(gè)實(shí)例,就和用該對(duì)象賦值一樣。此行為和給函數(shù)傳遞入實(shí)例時(shí)一樣??梢杂每寺〗o一個(gè)已創(chuàng)建的對(duì)象建立一個(gè)新實(shí)例。
<?php$instance?=?new?SimpleClass();
$assigned???=??$instance;
$reference??=&?$instance;
$instance->var?=?'$assigned?will?have?this?value';
$instance?=?null;?//?$instance?and?$reference?become?null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?> NULL NULL object(SimpleClass)#1 (1) {["var"]=>string(30) "$assigned will have this value" }
被繼承的方法和屬性可以通過(guò)用同樣的名字重新聲明被覆蓋。但是如果父類定義方法時(shí)使用了 final,則該方法不可被覆蓋。可以通過(guò) parent:: 來(lái)訪問(wèn)被覆蓋的方法或?qū)傩浴?/span>
當(dāng)覆蓋方法時(shí),參數(shù)必須保持一致否則 PHP 將發(fā)出?E_STRICT?級(jí)別的錯(cuò)誤信息。但構(gòu)造函數(shù)例外,構(gòu)造函數(shù)可在被覆蓋時(shí)使用不同的參數(shù)。
自 PHP 5.5 起,關(guān)鍵詞?class?也可用于類名的解析。使用?ClassName::class?你可以獲取一個(gè)字符串,包含了類?ClassName?的完全限定名稱。這對(duì)使用了 命名空間 的類尤其有用。
<?phpnamespace?NS?{
????class?ClassName?{
????}
????echo?ClassName::class; ? ?//NS\ClassName
}
?>
在類的成員方法里面,可以用?->(對(duì)象運(yùn)算符):$this->property(其中?property?是該屬性名)這種方式來(lái)訪問(wèn)非靜態(tài)屬性。靜態(tài)屬性則是用?::(雙冒號(hào)):self::$property?來(lái)訪問(wèn)。當(dāng)一個(gè)方法在類定義內(nèi)部被調(diào)用時(shí),有一個(gè)可用的偽變量?$this。$this?是一個(gè)到主叫對(duì)象的引用(通常是該方法所從屬的對(duì)象,但如果是從第二個(gè)對(duì)象靜態(tài)調(diào)用時(shí)也可能是另一個(gè)對(duì)象)。
跟 heredocs 不同,nowdocs 可在任何靜態(tài)數(shù)據(jù)上下文中使用,包括屬性聲明。
<?phpclass?foo?{
???//?自?5.3.0?起
???public?$bar?=?<<<'EOT'
bar
EOT;
}
?>
常量的值必須是一個(gè)定值,不能是變量,類屬性,數(shù)學(xué)運(yùn)算的結(jié)果或函數(shù)調(diào)用。和 heredoc 不同,nowdoc 可以用在任何靜態(tài)數(shù)據(jù)中。
<?phpclass?foo?{
????//?自?PHP?5.3.0?起
????const?bar?=?<<<'EOT'
bar
EOT;
}
?>
?
可以定義一個(gè)?__autoload()?函數(shù),它會(huì)在試圖使用尚未被定義的類時(shí)自動(dòng)調(diào)用。通過(guò)調(diào)用此函數(shù),腳本引擎在 PHP 出錯(cuò)失敗前有了最后一個(gè)機(jī)會(huì)加載所需的類。spl_autoload_register()?提供了一種更加靈活的方式來(lái)實(shí)現(xiàn)類的自動(dòng)加載。因此,不再建議使用?__autoload()?函數(shù),在以后的版本中它可能被棄用。如果類名比如被用于?call_user_func(),則它可能包含一些危險(xiǎn)的字符,比如?../。 建議您在這樣的函數(shù)中不要使用用戶的輸入,起碼需要在?__autoload()?時(shí)驗(yàn)證下輸入。
本例嘗試加載接口?ITest。
<?phpfunction?__autoload($name)?{
????var_dump($name);
}
class?Foo?implements?ITest?{
}
/*
string(5)?"ITest"
Fatal?error:?Interface?'ITest'?not?found?in?...
*/
?>
本例拋出一個(gè)異常并在 try/catch 語(yǔ)句塊中演示。
<?phpfunction?__autoload($name)?{
????echo?"Want?to?load?$name.\n";
????throw?new?Exception("Unable?to?load?$name.");
}
try?{
????$obj?=?new?NonLoadableClass();
}?catch?(Exception?$e)?{
????echo?$e->getMessage(),?"\n";
}
?> Want to load NonLoadableClass. Unable to load NonLoadableClass.
如果子類中定義了構(gòu)造函數(shù)則不會(huì)隱式調(diào)用其父類的構(gòu)造函數(shù)。要執(zhí)行父類的構(gòu)造函數(shù),需要在子類的構(gòu)造函數(shù)中調(diào)用?parent::__construct()。如果子類沒有定義構(gòu)造函數(shù)則會(huì)如同一個(gè)普通的類方法一樣從父類繼承(假如沒有被定義為 private 的話)。
為了實(shí)現(xiàn)向后兼容性,如果 PHP 5 在類中找不到 __construct() 函數(shù)并且也沒有從父類繼承一個(gè)的話,它就會(huì)嘗試尋找舊式的構(gòu)造函數(shù),也就是和類同名的函數(shù)。和構(gòu)造函數(shù)一樣,父類的析構(gòu)函數(shù)不會(huì)被引擎暗中調(diào)用。要執(zhí)行父類的析構(gòu)函數(shù),必須在子類的析構(gòu)函數(shù)體中顯式調(diào)用?parent::__destruct()。此外也和構(gòu)造函數(shù)一樣,子類如果自己沒有定義析構(gòu)函數(shù)則會(huì)繼承父類的。析構(gòu)函數(shù)即使在使用?exit()?終止腳本運(yùn)行時(shí)也會(huì)被調(diào)用。在析構(gòu)函數(shù)中調(diào)用?exit()?將會(huì)中止其余關(guān)閉操作的運(yùn)行。析構(gòu)函數(shù)在腳本關(guān)閉時(shí)調(diào)用,此時(shí)所有的 HTTP 頭信息已經(jīng)發(fā)出。腳本關(guān)閉時(shí)的工作目錄有可能和在 SAPI(如 apache)中時(shí)不同。試圖在析構(gòu)函數(shù)(在腳本終止時(shí)被調(diào)用)中拋出一個(gè)異常會(huì)導(dǎo)致致命錯(cuò)誤。
對(duì)屬性或方法的訪問(wèn)控制,是通過(guò)在前面添加關(guān)鍵字?public(公有),protected(受保護(hù))或?private(私有)來(lái)實(shí)現(xiàn)的。被定義為公有的類成員可以在任何地方被訪問(wèn)。被定義為受保護(hù)的類成員則可以被其自身以及其子類和父類訪問(wèn)。被定義為私有的類成員則只能被其定義所在的類訪問(wèn)。類屬性必須定義為公有,受保護(hù),私有之一。如果用?var?定義,則被視為公有。
類中的方法可以被定義為公有,私有或受保護(hù)。如果沒有設(shè)置這些關(guān)鍵字,則該方法默認(rèn)為公有。
同一個(gè)類的對(duì)象即使不是同一個(gè)實(shí)例也可以互相訪問(wèn)對(duì)方的私有與受保護(hù)成員。這是由于在這些對(duì)象的內(nèi)部具體實(shí)現(xiàn)的細(xì)節(jié)都是已知的。
<?phpclass?Test{
????private?$foo;
????public?function?__construct($foo){
????????$this->foo?=?$foo;
????}
????private?function?bar(){
????????echo?'Accessed?the?private?method.';
????}
????public?function?baz(Test?$other){
????????//?We?can change?the?private?property:
????????$other->foo?=?'hello';
????????var_dump($other->foo);
????????//?We?can?also?call?the?private?method:
????????$other->bar();
????}
}
$test?=?new?Test('test');
$test->baz(new?Test('other'));
?> string(5) "hello" Accessed the private method. 對(duì)象繼承:
?
<?phpclass?foo{
????public?function?printItem($string)?{
????????echo?'Foo:?'?.?$string?.?PHP_EOL;
????}
????public?function?printPHP(){
????????echo?'PHP?is?great.'?.?PHP_EOL;
????}
}
class?bar?extends?foo{
????public?function?printItem($string){
????????echo?'Bar:?'?.?$string?.?PHP_EOL;
????}
}
$foo?=?new?foo();
$bar?=?new?bar();
$foo->printItem('baz');?//?Output:?'Foo:?baz'
$foo->printPHP();???????//?Output:?'PHP?is?great'?
$bar->printItem('baz');?//?Output:?'Bar:?baz'
$bar->printPHP();???????//?Output:?'PHP?is?great'
?>
范圍解析操作符(::):可以用于訪問(wèn)靜態(tài)成員,類常量,還可以用于覆蓋類中的屬性和方法。當(dāng)在類定義之外引用到這些項(xiàng)目時(shí),要使用類名。自 PHP 5.3.0 起,可以通過(guò)變量來(lái)引用類,該變量的值不能是關(guān)鍵字(如? ?self,parent?和?static)。
self,parent?和?static?這三個(gè)特殊的關(guān)鍵字是用于在類定義的內(nèi)部對(duì)其屬性或方法進(jìn)行訪問(wèn)的。當(dāng)一個(gè)子類覆蓋其父類中的方法時(shí),PHP 不會(huì)調(diào)用父類中已被覆蓋的方法。是否調(diào)用父類的方法取決于子類。這種機(jī)制也作用于構(gòu)造函數(shù)和析構(gòu)函數(shù),重載以及魔術(shù)方法。
?
<?phpclass?MyClass{
????protected?function?myFunc()?{
????????echo?"MyClass::myFunc()\n";
????}
}
class?OtherClass?extends?MyClass{????//?覆蓋了父類的定義
????public?function?myFunc(){????????//?但還是可以調(diào)用父類中被覆蓋的方法
????????parent::myFunc();
????????echo?"OtherClass::myFunc()\n";
????}
}
$class?=?new?OtherClass();
$class->myFunc();
?>
聲明類屬性或方法為靜態(tài),就可以不實(shí)例化類而直接訪問(wèn)。靜態(tài)屬性不能通過(guò)一個(gè)類已實(shí)例化的對(duì)象來(lái)訪問(wèn)(但靜態(tài)方法可以)。
由于靜態(tài)方法不需要通過(guò)對(duì)象即可調(diào)用,所以偽變量?$this?在靜態(tài)方法中不可用。靜態(tài)屬性不可以由對(duì)象通過(guò) -> 操作符來(lái)訪問(wèn)。
用靜態(tài)方式調(diào)用一個(gè)非靜態(tài)方法會(huì)導(dǎo)致一個(gè)?E_STRICT?級(jí)別的錯(cuò)誤。就像其它所有的 PHP 靜態(tài)變量一樣,靜態(tài)屬性只能被初始化為文字或常量,不能使用表達(dá)式。所以可以把靜態(tài)屬性初始化為整數(shù)或數(shù)組,但不能初始化為另一個(gè)變量或函數(shù)返回值,也不能指向一個(gè)對(duì)象。
自 PHP 5.3.0 起,可以用一個(gè)變量來(lái)動(dòng)態(tài)調(diào)用類。但該變量的值不能為關(guān)鍵字?self,parent?或?static。
定義為抽象的類不能被實(shí)例化。任何一個(gè)類,如果它里面至少有一個(gè)方法是被聲明為抽象的,那么這個(gè)類就必須被聲明為抽象的。被定義為抽象的方法只是聲明了其調(diào)用方式(參數(shù)),不能定義其具體的功能實(shí)現(xiàn)。繼承一個(gè)抽象類的時(shí)候,子類必須定義父類中的所有抽象方法;另外,這些方法的訪問(wèn)控制必須和父類中一樣(或者更為寬松)。
<?phpabstract?class?AbstractClass{
????//?我們的抽象方法僅需要定義需要的參數(shù)
????abstract?protected?function?prefixName($name);
}
class?ConcreteClass?extends?AbstractClass{
????//?我們的子類可以定義父類簽名中不存在的可選參數(shù)
????public?function?prefixName($name,?$separator?=?".")?{
????????if?($name?==?"Pacman")?{
????????????$prefix?=?"Mr";
????????}?elseif?($name?==?"Pacwoman")?{
????????????$prefix?=?"Mrs";
????????}?else?{
????????????$prefix?=?"";
????????}
????????return?"{$prefix}{$separator}?{$name}";
????}
}
$class?=?new?ConcreteClass;
echo?$class->prefixName("Pacman"),?"\n";
echo?$class->prefixName("Pacwoman"),?"\n";
?>
?
Mr. Pacman Mrs. Pacwoman使用接口(interface),可以指定某個(gè)類必須實(shí)現(xiàn)哪些方法,但不需要定義這些方法的具體內(nèi)容。接口中定義的所有方法都必須是公有,這是接口的特性。要實(shí)現(xiàn)一個(gè)接口,使用?implements?操作符。類中必須實(shí)現(xiàn)接口中定義的所有方法,否則會(huì)報(bào)一個(gè)致命錯(cuò)誤。類可以實(shí)現(xiàn)多個(gè)接口,用逗號(hào)來(lái)分隔多個(gè)接口的名稱。接口也可以繼承,通過(guò)使用?extends?操作符。接口中也可以定義常量。接口常量和類常量的使用完全相同,但是不能被子類或子接口所覆蓋。接口加上類型約束,提供了一種很好的方式來(lái)確保某個(gè)對(duì)象包含有某些方法。
<?phpinterface?a{
????const?b?=?'Interface?constant';
}
echo?a::b; ? ?//?輸出接口常量
class?b?implements?a{
????const?b?=?'Class?constant'; ? ?//?錯(cuò)誤寫法,因?yàn)槌A坎荒鼙桓采w。接口常量的概念和類常量是一樣的。
}
?>
Traits 是一種為類似 PHP 的單繼承語(yǔ)言而準(zhǔn)備的代碼復(fù)用機(jī)制。Trait 為了減少單繼承語(yǔ)言的限制,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨(dú)立的類中復(fù)用方法集。Traits 和類組合的語(yǔ)義是定義了一種方式來(lái)減少?gòu)?fù)雜性,避免傳統(tǒng)多繼承和混入類(Mixin)相關(guān)的典型問(wèn)題。
?
Trait 和一個(gè)類相似,但僅僅旨在用細(xì)粒度和一致的方式來(lái)組合功能。Trait 不能通過(guò)它自身來(lái)實(shí)例化。它為傳統(tǒng)繼承增加了水平特性的組合;也就是說(shuō),應(yīng)用類的成員不需要繼承。
<?phptrait?ezcReflectionReturnInfo?{
????function?getReturnType()?{?/*1*/?}
????function?getReturnDescription()?{?/*2*/?}
}
class?ezcReflectionMethod?extends?ReflectionMethod?{
????use?ezcReflectionReturnInfo;
????/*?...?*/
}
class?ezcReflectionFunction?extends?ReflectionFunction?{
????use?ezcReflectionReturnInfo;
????/*?...?*/
}
?>
從基類繼承的成員被 trait 插入的成員所覆蓋。優(yōu)先順序是來(lái)自當(dāng)前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。
從基類繼承的成員被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆蓋。其行為 MyHelloWorld 類中定義的方法一致。優(yōu)先順序是當(dāng)前類中的方法會(huì)覆蓋 trait 方法,而 trait 方法又覆蓋了基類中的方法。
<?phpclass?Base?{
????public?function?sayHello()?{
????????echo?'Hello?';
????}
}
trait?SayWorld?{
????public?function?sayHello()?{
????????parent::sayHello();
????????echo?'World!';
????}
}
class?MyHelloWorld?extends?Base?{
????use?SayWorld;
}
$o?=?new?MyHelloWorld();
$o->sayHello();//Hello World!
?>
通過(guò)逗號(hào)分隔,在 use 聲明列出多個(gè) trait,可以都插入到一個(gè)類中。
<?phptrait?Hello?{
????public?function?sayHello()?{
????????echo?'Hello?';
????}
}
trait?World?{
????public?function?sayWorld()?{
????????echo?'World';
????}
}
class?MyHelloWorld?{
????use?Hello,?World;
????public?function?sayExclamationMark()?{
????????echo?'!';
????}
}
$o?=?new?MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();//Hello World!
?>
如果兩個(gè) trait 都插入了一個(gè)同名的方法,如果沒有明確解決沖突將會(huì)產(chǎn)生一個(gè)致命錯(cuò)誤。為了解決多個(gè) trait 在同一個(gè)類中的命名沖突,需要使用?insteadof?操作符來(lái)明確指定使用沖突方法中的哪一個(gè)。以上方式僅允許排除掉其它方法,as?操作符可以將其中一個(gè)沖突的方法以另一個(gè)名稱來(lái)引入。
正如類能夠使用 trait 一樣,其它 trait 也能夠使用 trait。在 trait 定義時(shí)使用一個(gè)或多個(gè) trait,它能夠組合其它 trait 中的部分或全部成員。
<?phptrait?Hello?{
????public?function?sayHello()?{
????????echo?'Hello?';
????}
}
trait?World?{
????public?function?sayWorld()?{
????????echo?'World!';
????}
}
trait?HelloWorld?{
????use?Hello,?World;
}
class?MyHelloWorld?{
????use?HelloWorld;
}
$o?=?new?MyHelloWorld();
$o->sayHello();
$o->sayWorld();//Hello World!
?>
為了對(duì)使用的類施加強(qiáng)制要求,trait 支持抽象方法的使用。
<?phptrait?Hello?{
????public?function?sayHelloWorld()?{
????????echo?'Hello'.$this->getWorld();
????}
????abstract?public?function?getWorld();
}
class?MyHelloWorld?{
????private?$world;
????use?Hello;
????public?function?getWorld()?{
????????return?$this->world;
????}
????public?function?setWorld($val)?{
????????$this->world?=?$val;
????}
}
?>
Traits 可以被靜態(tài)成員靜態(tài)方法定義。
<?phptrait?Counter?{
????public?function?inc()?{
????????static?$c?=?0;
????????$c?=?$c?+?1;
????????echo?"$c\n";
????}
}
class?C1?{
????use?Counter;
}
class?C2?{
????use?Counter;
}
$o?=?new?C1();?$o->inc();?//?echo?1
$p?=?new?C2();?$p->inc();?//?echo?1
?>
Trait 同樣可以定義屬性。
<?phptrait?PropertiesTrait?{
????public?$x?=?1;
}
class?PropertiesExample?{
????use?PropertiesTrait;
}
$example?=?new?PropertiesExample;
$example->x;
?>
如果 trait 定義了一個(gè)屬性,那類將不能定義同樣名稱的屬性,否則會(huì)產(chǎn)生一個(gè)錯(cuò)誤。如果該屬性在類中的定義與在 trait 中的定義兼容(同樣的可見性和初始值)則錯(cuò)誤的級(jí)別是?E_STRICT,否則是一個(gè)致命錯(cuò)誤。
<?phptrait?PropertiesTrait?{
????public?$same?=?true;
????public?$different?=?false;
}
class?PropertiesExample?{
????use?PropertiesTrait;
????public?$same?=?true;?//?Strict?Standards
????public?$different?=?true;?//?致命錯(cuò)誤
}
?>
當(dāng)調(diào)用當(dāng)前環(huán)境下未定義或不可見的類屬性或方法時(shí),重載方法會(huì)被調(diào)用。所有的重載方法都必須被聲明為?public。PHP中的"重載"與其它絕大多數(shù)面向?qū)ο笳Z(yǔ)言不同。傳統(tǒng)的"重載"是用于提供多個(gè)同名的類方法,但各方法的參數(shù)類型和個(gè)數(shù)不同。
屬性重載只能在對(duì)象中進(jìn)行。在靜態(tài)方法中,這些魔術(shù)方法將不會(huì)被調(diào)用。所以這些方法都不能被 聲明為 static
publicvoid__set?(?string$name?,?mixed$value?) publicmixed__get?(?string$name?) publicbool__isset?(?string$name?) publicvoid__unset?(?string$name?)在給不可訪問(wèn)屬性賦值時(shí),__set() 會(huì)被調(diào)用。
讀取不可訪問(wèn)屬性的值時(shí),__get() 會(huì)被調(diào)用。
當(dāng)對(duì)不可訪問(wèn)屬性調(diào)用?isset()?或?empty()?時(shí),__isset()會(huì)被調(diào)用。
當(dāng)對(duì)不可訪問(wèn)屬性調(diào)用?unset()?時(shí),__unset()會(huì)被調(diào)用。
參數(shù)?$name?是指要操作的變量名稱。__set()方法的?$value?參數(shù)指定了?$name?變量的值。
在除?isset()外的其它語(yǔ)言結(jié)構(gòu)中無(wú)法使用重載的屬性,這意味著當(dāng)對(duì)一個(gè)重載的屬性使用?empty()?時(shí),重載魔術(shù)方法將不會(huì)被調(diào)用。為避開此限制,必須將重載屬性賦值到本地變量再使用?empty()。
方法重載:?
publicmixed__call?(?string$name?,?array$arguments?) public staticmixed__callStatic?(?string$name?,?array$arguments?)在對(duì)象中調(diào)用一個(gè)不可訪問(wèn)方法時(shí),__call() 會(huì)被調(diào)用。
用靜態(tài)方式中調(diào)用一個(gè)不可訪問(wèn)方法時(shí),__callStatic() 會(huì)被調(diào)用。
$name?參數(shù)是要調(diào)用的方法名稱。$arguments參數(shù)是一個(gè)枚舉數(shù)組,包含著要傳遞給方法?$name?的參數(shù)。
PHP 5 提供了一種定義對(duì)象的方法使其可以通過(guò)單元列表來(lái)遍歷,例如用 foreach語(yǔ)句。默認(rèn)情況下,所有可見屬性都將被用于遍歷。foreach?遍歷了所有其能夠訪問(wèn)的可見屬性。
<?phpclass?MyClass{
public?$var1?=?'value 1';
public?$var2?=?'value 2';
public?$var3?=?'value 3';
protected?$protected?=?'protected var';
private?$private?=?'private var';
function?iterateVisible() {
? ? echo?"MyClass::iterateVisible:\n";
? ? foreach($this?as?$key?=>?$value) {
? ? ? ? print?"$key?=>?$value\n";
? ? ? ? }
? ? }
}
$class?= new?MyClass();
foreach($class?as?$key?=>?$value) {
? ? print?"$key?=>?$value\n";
}
echo?"\n";
$class->iterateVisible();
?> var1 => value 1 var2 => value 2 var3 => value 3MyClass::iterateVisible: var1 => value 1 var2 => value 2 var3 => value 3 protected => protected var private => private var
更進(jìn)一步,可以實(shí)現(xiàn)?Iterator接口??梢宰寣?duì)象自行決定如何遍歷以及每次遍歷時(shí)那些值可用??梢杂?IteratorAggregate接口以替代實(shí)現(xiàn)所有的?Iterator?方法。IteratorAggregate只需要實(shí)現(xiàn)一個(gè)方法IteratorAggregate::getIterator(),其應(yīng)返回一個(gè)實(shí)現(xiàn)了?Iterator?的類的實(shí)例。
9.類與對(duì)象:魔術(shù)方法
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo()等方法在 PHP 中被稱為"魔術(shù)方法"(Magic methods)。在命名自己的類方法時(shí)不能使用這些方法名,除非是想使用其魔術(shù)功能。
publicarray__sleep?(?void?) void__wakeup?(?void?)serialize()?函數(shù)會(huì)檢查類中是否存在一個(gè)魔術(shù)方法 __sleep()。如果存在,該方法會(huì)先被調(diào)用,然后才執(zhí)行序列化操作。此功能可以用于清理對(duì)象,并返回一個(gè)包含對(duì)象中所有應(yīng)被序列化的變量名稱的數(shù)組。如果該方法未返回任何內(nèi)容,則?NULL?被序列化,并產(chǎn)生一個(gè)?E_NOTICE?級(jí)別的錯(cuò)誤。__sleep()?不能返回父類的私有成員的名字。這樣做會(huì)產(chǎn)生一個(gè)?E_NOTICE?級(jí)別的錯(cuò)誤??梢杂?Serializable?接口來(lái)替代。__sleep()?方法常用于提交未提交的數(shù)據(jù),或類似的清理操作。同時(shí),如果有一些很大的對(duì)象,但不需要全部保存,這個(gè)功能就很好用。與之相反,unserialize()?會(huì)檢查是否存在一個(gè)?__wakeup()方法。如果存在,則會(huì)先調(diào)用?__wakeup?方法,預(yù)先準(zhǔn)備對(duì)象需要的資源。__wakeup()?經(jīng)常用在反序列化操作中,例如重新建立數(shù)據(jù)庫(kù)連接,或執(zhí)行其它初始化操作。
<?phpclass?Connection?{
protected?$link;
private?$server,?$username,?$password,?$db;
public function?__construct($server,?$username,?$password,?$db){
$this->server?=?$server;
$this->username?=?$username;
$this->password?=?$password;
$this->db?=?$db;
$this->connect();
}
private function?connect(){
$this->link?=?mysql_connect($this->server,?$this->username,?$this->password);
mysql_select_db($this->db,?$this->link);
}
public function?__sleep(){
return array('server',?'username',?'password',?'db');
}
public function?__wakeup(){
$this->connect();
}
}
?> publicstring__toString?(?void?)
__toString() 方法用于一個(gè)類被當(dāng)成字符串時(shí)應(yīng)怎樣回應(yīng)。例如?echo $obj;?應(yīng)該顯示些什么。此方法必須返回一個(gè)字符串,否則將發(fā)出一條?E_RECOVERABLE_ERROR?級(jí)別的致命錯(cuò)誤。不能在?__toString()方法中拋出異常。這么做會(huì)導(dǎo)致致命錯(cuò)誤。
<?phpclass?TestClass{
public?$foo;
public function?__construct($foo) {
$this->foo?=?$foo;
}
public function?__toString() {
return?$this->foo;
? ? }
}
$class?= new?TestClass('Hello');
echo?$class; ? ?//Hello
?> mixed__invoke?([?$...?] )
當(dāng)嘗試以調(diào)用函數(shù)的方式調(diào)用一個(gè)對(duì)象時(shí),__invoke() 方法會(huì)被自動(dòng)調(diào)用。
<?phpclass?CallableClass?{
? ? function?__invoke($x) {
? ? var_dump($x); ? ?//int(5)
? ? }
}
$obj?= new?CallableClass;
$obj(5);
var_dump(is_callable($obj)); ? ?//bool(true)
?> staticobject__set_state?(?array$properties?)
自 PHP 5.1.0 起當(dāng)調(diào)用?var_export()?導(dǎo)出類時(shí),此靜態(tài) 方法會(huì)被調(diào)用。本方法的唯一參數(shù)是一個(gè)數(shù)組,其中包含按?array('property' => value, ...)?格式排列的類屬性。
<?phpclass?A{
public?$var1;
public?$var2;
public static function?__set_state($an_array)?// As of PHP 5.1.0{
? ? $obj?= new?A;
? ? $obj->var1?=?$an_array['var1'];
? ? $obj->var2?=?$an_array['var2'];
? ? return?$obj;
? ? }
}
$a?= new?A;
$a->var1?=?5;
$a->var2?=?'foo';
eval('$b = '?.?var_export($a,?true) .?';');?
var_dump($b);
?> object(A)#2 (2) {["var1"]=>int(5)["var2"]=>string(3) "foo" }
__debugInfo()
<?phpclass?C?{
private?$prop;
public function?__construct($val) {
$this->prop?=?$val;
}
public function?__debugInfo() {
return [
'propSquared'?=>?$this->prop?**?2,
? ? ? ? ];
? ? }
}
var_dump(new?C(42));
?> object(C)#1 (1) {["propSquared"]=>int(1764) }
Final 關(guān)鍵字:如果父類中的方法被聲明為 final,則子類無(wú)法覆蓋該方法。如果一個(gè)類被聲明為 final,則不能被繼承。屬性不能被定義為 final,只有類和方法才能被定義為 final。
?
對(duì)象復(fù)制可以通過(guò) clone 關(guān)鍵字來(lái)完成(如果可能,這將調(diào)用對(duì)象的 __clone() 方法)。對(duì)象中的 __clone() 方法不能被直接調(diào)用。$copy_of_object = clone $object;當(dāng)對(duì)象被復(fù)制后,PHP 5 會(huì)對(duì)對(duì)象的所有屬性執(zhí)行一個(gè)淺復(fù)制(shallow copy)。所有的引用屬性 仍然會(huì)是一個(gè)指向原來(lái)的變量的引用。void__clone?(?void?)當(dāng)復(fù)制完成時(shí),如果定義了?__clone()方法,則新創(chuàng)建的對(duì)象(復(fù)制生成的對(duì)象)中的?__clone()方法會(huì)被調(diào)用,可用于修改屬性的值(如果有必要的話)。
對(duì)象比較:當(dāng)使用比較運(yùn)算符(==)比較兩個(gè)對(duì)象變量時(shí),比較的原則是:如果兩個(gè)對(duì)象的屬性和屬性值 都相等,而且兩個(gè)對(duì)象是同一個(gè)類的實(shí)例,那么這兩個(gè)對(duì)象變量相等。而如果使用全等運(yùn)算符(===),這兩個(gè)對(duì)象變量一定要指向某個(gè)類的同一個(gè)實(shí)例(即同一個(gè)對(duì)象)。
類型約束:函數(shù)的參數(shù)可以指定必須為對(duì)象(在函數(shù)原型里面指定類的名字),接口,數(shù)組(PHP 5.1 起)或者?callable(PHP 5.4 起)。不過(guò)如果使用?NULL作為參數(shù)的默認(rèn)值,那么在調(diào)用函數(shù)的時(shí)候依然可以使用?NULL?作為實(shí)參。如果一個(gè)類或接口指定了類型約束,則其所有的子類或?qū)崿F(xiàn)也都如此。類型約束不能用于標(biāo)量類型如?int?或?string。Traits?也不允許。
?
函數(shù)調(diào)用的參數(shù)與定義的參數(shù)類型不一致時(shí),會(huì)拋出一個(gè)可捕獲的致命錯(cuò)誤。類型約束允許 NULL 值:
<?php/* 接受 NULL 值 */
function?test(stdClass $obj?=?NULL) { ? ?}
test(NULL);
test(new?stdClass);
?> 后期靜態(tài)綁定:后期綁定"的意思是說(shuō),static::不再被解析為定義當(dāng)前方法所在的類,而是在實(shí)際運(yùn)行時(shí)計(jì)算的。也可以稱之為"靜態(tài)綁定",因?yàn)樗梢杂糜?#xff08;但不限于)靜態(tài)方法的調(diào)用。
?
?
使用?self::?或者?__CLASS__?對(duì)當(dāng)前類的靜態(tài)引用,取決于定義當(dāng)前方法所在的類:
<?phpclass?A?{
public static function?who() {
? ? echo?__CLASS__;
}
public static function?test() {
? ? self::who();
? ? }
}
class?B?extends?A?{
public static function?who() {
? ? echo?__CLASS__;
? ? }
}
B::test(); ? ?//A
?> <?php
class?A?{
public static function?who() {
? ? echo?__CLASS__;
}
public static function?test() {
? ? static::who();?// 后期靜態(tài)綁定從這里開始
? ? }
}
class?B?extends?A?{
public static function?who() {
? ? echo?__CLASS__;
? ? }
}
B::test(); ? ?//B
?>
?
?
在非靜態(tài)環(huán)境下,所調(diào)用的類即為該對(duì)象實(shí)例所屬的類。由于?$this->會(huì)在同一作用范圍內(nèi)嘗試調(diào)用私有方法,而?static::則可能給出不同結(jié)果。另一個(gè)區(qū)別是?static::?只能用于靜態(tài)屬性。
?
?
<?phpclass?A?{
private function?foo() {
? ? echo?"success!\n";
}
public function?test() {
? ? $this->foo();
? ? static::foo();
? ? }
}
class?B?extends?A?{
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
class?C?extends?A?{
private function?foo() {
? ? /* original method is replaced; the scope of the new one is C */
? ? }
}
$b?= new?B();
$b->test();
$c?= new?C();
$c->test();?//fails
?> success! success! success! Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9
?
?
后期靜態(tài)綁定的解析會(huì)一直到取得一個(gè)完全解析了的靜態(tài)調(diào)用為止。另一方面,如果靜態(tài)調(diào)用使用?parent::?或者?self::?將轉(zhuǎn)發(fā)調(diào)用信息。
<?php
class?A?{
public static function?foo() {
? ? static::who();
}
public static function?who() {
? ? echo?__CLASS__."\n";
? ? }
}
class?B?extends?A?{
? ? public static function?test() {
? ? ? ? A::foo();
? ? ? ? parent::foo();
? ? ? ? self::foo();
? ? }
? ? public static function?who() {
? ? ? ? echo?__CLASS__."\n";
? ? }
}
class?C?extends?B?{
? ? public static function?who() {
? ? ? ? echo?__CLASS__."\n";
? ? }
}
C::test(); ? ? ? ?//會(huì)輸出:A C C
?>
?
?
php的引用是別名,就是兩個(gè)不同的變量名字指向相同的內(nèi)容。在php5,一個(gè)對(duì)象變量已經(jīng)不再保存整個(gè)對(duì)象的值。只是保存一個(gè)標(biāo)識(shí)符來(lái)訪問(wèn)真正的對(duì)象內(nèi)容。 當(dāng)對(duì)象作為參數(shù)傳遞,作為結(jié)果返回,或者賦值給另外一個(gè)變量,另外一個(gè)變量跟原來(lái)的不是引用的關(guān)系,只是他們都保存著同一個(gè)標(biāo)識(shí)符的拷貝,這個(gè)標(biāo)識(shí)符指向同一個(gè)對(duì)象的真正內(nèi)容。
<?phpclass?A?{?public?$foo?=?1;}?
$a?= new?A;
$b?=?$a;?// $a ,$b都是同一個(gè)標(biāo)識(shí)符的拷貝?// ($a) = ($b) = <id>
$b->foo?=?2;
echo?$a->foo."\n"; ? ?//2
$c?= new?A;
$d?= &$c;?// $c ,$d是引用?// ($c,$d) = <id>
$d->foo?=?2;
echo?$c->foo."\n"; ? ?//2
$e?= new?A;
function?foo($obj) {?$obj->foo?=?2;}// ($obj) = ($e) = <id>
foo($e);
echo?$e->foo."\n"; ? ?//2
?>
所有php里面的值都可以使用函數(shù)serialize()來(lái)返回一個(gè)包含字節(jié)流的字符串來(lái)表示。unserialize()函數(shù)能夠重新把字符串變回php原來(lái)的值。 序列化一個(gè)對(duì)象將會(huì)保存對(duì)象的所有變量,但是不會(huì)保存對(duì)象的方法,只會(huì)保存類的名字。
為了能夠unserialize()一個(gè)對(duì)象,這個(gè)對(duì)象的類必須已經(jīng)定義過(guò)。如果序列化類A的一個(gè)對(duì)象,將會(huì)返回一個(gè)跟類A相關(guān),而且包含了對(duì)象所有變量值的字符串。 如果要想在另外一個(gè)文件中解序列化一個(gè)對(duì)象,這個(gè)對(duì)象的類必須在解序列化之前定義,可以通過(guò)包含一個(gè)定義該類的文件或使用函數(shù)spl_autoload_register()來(lái)實(shí)現(xiàn)。
<?php// classa.inc:
class?A?{
public?$one?=?1;
public function?show_one() {
? ? echo?$this->one;
? ? }
}
// page1.php:
include("classa.inc");
$a?= new?A;
$s?=?serialize($a);
file_put_contents('store',?$s); ? ?// 把變量$s保存起來(lái)以便文件page2.php能夠讀到
// page2.php:
include("classa.inc"); ? ?// 要正確了解序列化,必須包含下面一個(gè)文件
$s?=?file_get_contents('store');
$a?=?unserialize($s);
$a->show_one(); ? ?// 現(xiàn)在可以使用對(duì)象$a里面的函數(shù) show_one()
?>
在應(yīng)用程序中序列化對(duì)象以便在之后使用,強(qiáng)烈推薦在整個(gè)應(yīng)用程序都包含對(duì)象的類的定義。 不然有可能出現(xiàn)在解序列化對(duì)象的時(shí)候,沒有找到該對(duì)象的類的定義,從而把沒有方法的類__PHP_Incomplete_Class_Name作為該對(duì)象的類,導(dǎo)致返回一個(gè)沒有用的對(duì)象。所以在上面的例子中,當(dāng)運(yùn)行session_register("a"),把變量$a放在會(huì)話里之后,需要在每個(gè)頁(yè)面都包含文件classa.inc,而不是只有文件page1.php和page2.php。
10.命名空間?
?
在PHP中,命名空間用來(lái)解決在編寫類庫(kù)或應(yīng)用程序時(shí)創(chuàng)建可重用的代碼如類或函數(shù)時(shí)碰到的兩類問(wèn)題:
?
?
雖然任意合法的PHP代碼都可以包含在命名空間中,但只有三種類型的代碼受命名空間的影響,它們是:類,函數(shù)和常量。
命名空間通過(guò)關(guān)鍵字namespace來(lái)聲明。如果一個(gè)文件中包含命名空間,它必須在其它所有代碼之前聲明命名空間。
聲明單個(gè)命名空間:
<?phpnamespace?MyProject;
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
?> 在聲明命名空間之前唯一合法的代碼是用于定義源文件編碼方式的?declare語(yǔ)句。另外,所有非 PHP 代碼包括空白符都不能出現(xiàn)在命名空間的聲明之前。允許將同一個(gè)命名空間的內(nèi)容分割存放在不同的文件中。
PHP 命名空間也允許指定層次化的命名空間的名稱。聲明分層次的單個(gè)命名空間:
<?phpnamespace?MyProject\Sub\Level;
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
?> 上面的例子創(chuàng)建了常量MyProject\Sub\Level\CONNECT_OK,類?MyProject\Sub\Level\Connection和函數(shù)? ?MyProject\Sub\Level\Connection。
也可以在同一個(gè)文件中定義多個(gè)命名空間。定義多個(gè)命名空間,簡(jiǎn)單組合語(yǔ)法(不建議):
<?phpnamespace?MyProject;
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
namespace?AnotherProject;
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
?>
定義多個(gè)命名空間,大括號(hào)語(yǔ)法:
<?phpnamespace?MyProject?{
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
}
namespace?AnotherProject?{
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
}
?>
將全局的非命名空間中的代碼與命名空間中的代碼組合在一起,只能使用大括號(hào)形式的語(yǔ)法。全局代碼必須用一個(gè)不帶名稱的 namespace 語(yǔ)句加上大括號(hào)括起來(lái):
<?phpnamespace?MyProject?{
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
}
namespace {?// global code
session_start();
$a?=?MyProject\connect();
echo?MyProject\Connection::start();
}
?>
定義多個(gè)命名空間和不包含在命名空間中的代碼:
<?phpdeclare(encoding='UTF-8');
namespace?MyProject?{
const?CONNECT_OK?=?1;
class?Connection?{?/* ... */?}
function?connect() {?/* ... */?}
}
namespace {?// 全局代碼
session_start();
$a?=?MyProject\connect();
echo?MyProject\Connection::start();
}
?>
在文件系統(tǒng)中訪問(wèn)一個(gè)文件有三種方式:
<?phpnamespace?Foo\Bar\subnamespace;
const?FOO?=?1;
function?foo() {}
class?foo{?static function?staticmethod() {}?}
?> <?php
namespace?Foo\Bar;
include?'file1.php';
const?FOO?=?2;
function?foo() {}
class?foo{?static function?staticmethod() {}?}
/* 非限定名稱 */
foo();?// 解析為 Foo\Bar\foo resolves to function Foo\Bar\foo
foo::staticmethod();?// 解析為類 Foo\Bar\foo的靜態(tài)方法staticmethod。resolves to class Foo\Bar\foo, method staticmethod
echo?FOO;?// resolves to constant Foo\Bar\FOO
/* 限定名稱 */
subnamespace\foo();?// 解析為函數(shù) Foo\Bar\subnamespace\foo
subnamespace\foo::staticmethod();?// 解析為類 Foo\Bar\subnamespace\foo,
// 以及類的方法 staticmethod
echo?subnamespace\FOO;?// 解析為常量 Foo\Bar\subnamespace\FOO
/* 完全限定名稱 */
\Foo\Bar\foo();?// 解析為函數(shù) Foo\Bar\foo
\Foo\Bar\foo::staticmethod();?// 解析為類 Foo\Bar\foo, 以及類的方法 staticmethod
echo \Foo\Bar\FOO;?// 解析為常量 Foo\Bar\FOO
?>
注意訪問(wèn)任意全局類、函數(shù)或常量,都可以使用完全限定名稱,例如?\strlen()?或?\Exception?或?\INI_ALL。
<?phpnamespace?Foo;
function?strlen() {}
const?INI_ALL?=?3;
class?Exception?{}
$a?= \strlen('hi');?// 調(diào)用全局函數(shù)strlen
$b?= \INI_ALL;?// 訪問(wèn)全局常量 INI_ALL
$c?= new \Exception('error');?// 實(shí)例化全局類 Exception
?>
PHP 命名空間的實(shí)現(xiàn)受到其語(yǔ)言自身的動(dòng)態(tài)特征的影響。
<?phpclass?classname{?function?__construct(){?echo?__METHOD__,"\n";?}?}
function?funcname(){?echo?__FUNCTION__,"\n";?}
const?constname?=?"global";
$a?=?'classname';
$obj?= new?$a;?// prints classname::__construct
$b?=?'funcname';
$b();?// prints funcname
echo?constant('constname'),?"\n";?// prints global
?> 必須使用完全限定名稱(包括命名空間前綴的類名稱)。注意因?yàn)樵趧?dòng)態(tài)的類名稱、函數(shù)名稱或常量名稱中,限定名稱和完全限定名稱沒有區(qū)別,因此其前導(dǎo)的反斜杠是不必要的。動(dòng)態(tài)訪問(wèn)命名空間的元素: <?php
namespace?namespacename;
class?classname{?function?__construct()?{?echo?__METHOD__,"\n";?}?}
function?funcname()?{?echo?__FUNCTION__,"\n";?}
const?constname?=?"namespaced";
include?'example1.php';
$a?=?'classname';
$obj?= new?$a;?// prints classname::__construct
$b?=?'funcname';
$b();?// prints funcname
echo?constant('constname'),?"\n";?// prints global
/* note that if using double quotes, "\\namespacename\\classname" must be used */
$a?=?'\namespacename\classname';
$obj?= new?$a;?// prints namespacename\classname::__construct
$a?=?'namespacename\classname';
$obj?= new?$a;?// also prints namespacename\classname::__construct
$b?=?'namespacename\funcname';
$b();?// prints namespacename\funcname
$b?=?'\namespacename\funcname';
$b();?// also prints namespacename\funcname
echo?constant('\namespacename\constname'),?"\n";?// prints namespaced
echo?constant('namespacename\constname'),?"\n";?// also prints namespaced
?> PHP支持兩種抽象的訪問(wèn)當(dāng)前命名空間內(nèi)部元素的方法,__NAMESPACE__魔術(shù)常量和namespace關(guān)鍵字。 常量__NAMESPACE__的值是包含當(dāng)前命名空間名稱的字符串。在全局的,不包括在任何命名空間中的代碼,它包含一個(gè)空的字符串。常量?__NAMESPACE__?在動(dòng)態(tài)創(chuàng)建名稱時(shí)很有用。 <?php
namespace?MyProject;
echo?'"',?__NAMESPACE__,?'"';?// 輸出 "MyProject"
?> <?php
namespace?MyProject;
function?get($classname){
? ? $a?=?__NAMESPACE__?.?'\\'?.?$classname;
? ? return new?$a;
}
?>
關(guān)鍵字?namespace?可用來(lái)顯式訪問(wèn)當(dāng)前命名空間或子命名空間中的元素。它等價(jià)于類中的?self?操作符。
<?phpnamespace?MyProject;
use?blah\blah?as?mine;?// see "Using namespaces: importing/aliasing"
blah\mine();?// calls function MyProject\blah\mine()
namespace\blah\mine();?// calls function MyProject\blah\mine()
namespace\func();?// calls function MyProject\func()
namespace\sub\func();?// calls function MyProject\sub\func()
namespace\cname::method();?// calls static method "method" of class MyProject\cname
$a?= new namespace\sub\cname();?// instantiates object of class MyProject\sub\cname
$b?= namespace\CONSTANT;?// assigns value of constant MyProject\CONSTANT to $b
?>
允許通過(guò)別名引用或?qū)胪獠康耐耆薅Q,是命名空間的一個(gè)重要特征。這有點(diǎn)類似于在類 unix 文件系統(tǒng)中可以創(chuàng)建對(duì)其它的文件或目錄的符號(hào)連接。為類名稱使用別名,或?yàn)槊臻g名稱使用別名。注意PHP不支持導(dǎo)入函數(shù)或常量。
在PHP中,別名是通過(guò)操作符?use?來(lái)實(shí)現(xiàn)的.
<?phpnamespace?foo;
use?My\Full\Classname?as?Another;
// 下面的例子與 use My\Full\NSname as NSname 相同
use?My\Full\NSname;
// 導(dǎo)入一個(gè)全局類
use \ArrayObject;
$obj?= new namespace\Another;?// 實(shí)例化 foo\Another 對(duì)象
$obj?= new?Another;?// 實(shí)例化 My\Full\Classname 對(duì)象
NSname\subns\func();?// 調(diào)用函數(shù) My\Full\NSname\subns\func
$a?= new?ArrayObject(array(1));?// 實(shí)例化 ArrayObject 對(duì)象
// 如果不使用 "use \ArrayObject" ,則實(shí)例化一個(gè) foo\ArrayObject 對(duì)象
?> 注意對(duì)命名空間中的名稱(包含命名空間分隔符的完全限定名稱如?Foo\Bar以及相對(duì)的不包含命名空間分隔符的全局名稱如?FooBar)來(lái)說(shuō),前導(dǎo)的反斜杠是不必要的也不允許有反斜杠,因?yàn)閷?dǎo)入的名稱必須是完全限定的,不會(huì)根據(jù)當(dāng)前的命名空間作相對(duì)解析。通過(guò)use操作符導(dǎo)入/使用別名,一行中包含多個(gè)use語(yǔ)句: <?php
use?My\Full\Classname?as?Another,?My\Full\NSname;
$obj?= new?Another;?// 實(shí)例化一個(gè) My\Full\Classname 對(duì)象
$a?=?'Another';
$obj?= new?$a;?// 實(shí)際化一個(gè) Another 對(duì)象
?>
另外,導(dǎo)入操作只影響非限定名稱和限定名稱。完全限定名稱由于是確定的,故不受導(dǎo)入的影響。
<?phpuse?My\Full\Classname?as?Another,?My\Full\NSname;
$obj?= new?Another;?// instantiates object of class My\Full\Classname
$obj?= new \Another;?// instantiates object of class Another
$obj?= new?Another\thing;?// instantiates object of class My\Full\Classname\thing
$obj?= new \Another\thing;?// instantiates object of class Another\thing
?>
如果沒有定義任何命名空間,所有的類與函數(shù)的定義都是在全局空間,在名稱前加上前綴?\表示該名稱是全局空間中的名稱,即使該名稱位于其它的命名空間中時(shí)也是如此。
<?phpnamespace?A\B\C;
/* 這個(gè)函數(shù)是 A\B\C\fopen */
function?fopen() {?
/* ... */
$f?= \fopen(...);?// 調(diào)用全局的fopen函數(shù)
return?$f;
}?
?>
?
在一個(gè)命名空間中,當(dāng) PHP 遇到一個(gè)非限定的類、函數(shù)或常量名稱時(shí),它使用不同的優(yōu)先策略來(lái)解析該名稱。類名稱總是解析到當(dāng)前命名空間中的名稱。因此在訪問(wèn)系統(tǒng)內(nèi)部或不包含在命名空間中的類名稱時(shí),必須使用完全限定名稱。
<?phpnamespace?A\B\C;
class?Exception?extends \Exception?{}
$a?= new?Exception('hi');?// $a 是類 A\B\C\Exception 的一個(gè)對(duì)象
$b?= new \Exception('hi');?// $b 是類 Exception 的一個(gè)對(duì)象
$c?= new?ArrayObject;?// 致命錯(cuò)誤, 找不到 A\B\C\ArrayObject 類
?>
對(duì)于函數(shù)和常量來(lái)說(shuō),如果當(dāng)前命名空間中不存在該函數(shù)或常量,PHP 會(huì)退而使用全局空間中的函數(shù)或常量。
<?phpnamespace?A\B\C;
const?E_ERROR?=?45;
function?strlen($str){
? ? return \strlen($str) -?1;
}
echo?E_ERROR,?"\n";?// 輸出 "45"
echo?INI_ALL,?"\n";?// 輸出 "7" - 使用全局常量 INI_ALL
echo?strlen('hi'),?"\n";?// 輸出 "1"
if (is_array('hi')) {?// 輸出 "is not array"
? ? echo?"is array\n";
} else {
? ? echo?"is not array\n";
}
?> 命名空間名稱定義: 非限定名稱Unqualified name,例如?Foo;限定名稱Qualified name,例如?Foo\Bar完全限定名稱Fully qualified name,例如?\Foo\Bar。?namespace\Foo?也是一個(gè)完全限定名稱。 <?php
namespace?A;
use?B\D,?C\E?as?F;
// 函數(shù)調(diào)用
foo();?// 首先嘗試調(diào)用定義在命名空間"A"中的函數(shù)foo()
// 再嘗試調(diào)用全局函數(shù) "foo"
\foo();?// 調(diào)用全局空間函數(shù) "foo"?
my\foo();?// 調(diào)用定義在命名空間"A\my"中函數(shù) "foo"?
F();?// 首先嘗試調(diào)用定義在命名空間"A"中的函數(shù) "F"?
// 再嘗試調(diào)用全局函數(shù) "F"
// 類引用
new?B();?// 創(chuàng)建命名空間 "A" 中定義的類 "B" 的一個(gè)對(duì)象
// 如果未找到,則嘗試自動(dòng)裝載類 "A\B"
new?D();?// 使用導(dǎo)入規(guī)則,創(chuàng)建命名空間 "B" 中定義的類 "D" 的一個(gè)對(duì)象
// 如果未找到,則嘗試自動(dòng)裝載類 "B\D"
new?F();?// 使用導(dǎo)入規(guī)則,創(chuàng)建命名空間 "C" 中定義的類 "E" 的一個(gè)對(duì)象
// 如果未找到,則嘗試自動(dòng)裝載類 "C\E"
new \B();?// 創(chuàng)建定義在全局空間中的類 "B" 的一個(gè)對(duì)象
// 如果未發(fā)現(xiàn),則嘗試自動(dòng)裝載類 "B"
new \D();?// 創(chuàng)建定義在全局空間中的類 "D" 的一個(gè)對(duì)象
// 如果未發(fā)現(xiàn),則嘗試自動(dòng)裝載類 "D"
new \F();?// 創(chuàng)建定義在全局空間中的類 "F" 的一個(gè)對(duì)象
// 如果未發(fā)現(xiàn),則嘗試自動(dòng)裝載類 "F"
// 調(diào)用另一個(gè)命名空間中的靜態(tài)方法或命名空間函數(shù)
B\foo();?// 調(diào)用命名空間 "A\B" 中函數(shù) "foo"
B::foo();?// 調(diào)用命名空間 "A" 中定義的類 "B" 的 "foo" 方法
// 如果未找到類 "A\B" ,則嘗試自動(dòng)裝載類 "A\B"
D::foo();?// 使用導(dǎo)入規(guī)則,調(diào)用命名空間 "B" 中定義的類 "D" 的 "foo" 方法
// 如果類 "B\D" 未找到,則嘗試自動(dòng)裝載類 "B\D"
\B\foo();?// 調(diào)用命名空間 "B" 中的函數(shù) "foo"?
\B::foo();?// 調(diào)用全局空間中的類 "B" 的 "foo" 方法
// 如果類 "B" 未找到,則嘗試自動(dòng)裝載類 "B"
// 當(dāng)前命名空間中的靜態(tài)方法或函數(shù)
A\B::foo();?// 調(diào)用命名空間 "A\A" 中定義的類 "B" 的 "foo" 方法
// 如果類 "A\A\B" 未找到,則嘗試自動(dòng)裝載類 "A\A\B"
\A\B::foo();?// 調(diào)用命名空間 "A\B" 中定義的類 "B" 的 "foo" 方法
// 如果類 "A\B" 未找到,則嘗試自動(dòng)裝載類 "A\B"
?>
FAQ: things you need to know about namespaces
?
轉(zhuǎn)載于:https://www.cnblogs.com/echohao/p/4715425.html
總結(jié)
- 上一篇: Redis迭代查询详解及其使用:Scan
- 下一篇: moodle php代码解读_Moodl