介紹 PHP 中的資料型態 (型別),包含字串 (String)、陣列 (Array)、物件 (Object)、資源 (Resource)、NULL,型別的判斷與轉型。

字串 (String)

語法

字串有四種使用的方式:

  • 單引號
  • 雙引號
  • Heredoc
  • Nowdoc

以下逐一進行介紹。

單引號

以兩個單引號包夾一段文字,表示字串內容,使用單引號的字串有一些特徵如下 (與雙引號比較):

  • 逸出字元 (Escape characters) 只有兩種。
  • 逸出字元 \\ 可省略寫作 \,但不可為最後一個字元 (不建議省略)。
  • 不會對字串中的變數進行解析。

所謂逸出字元是為了某些特別的用途或能以此輸入打不出來的字,而定義的一種使用方式,在此的逸出字元以反斜線 (backslash) 開頭。例如,我們使用單引號做包夾,但字串中如果出現單引號,將會誤判為字串結束單引號,所以使用 \' 來表示字串中的單引號。使用單引號的字串可用的逸出字元如下:

符號說明
'單引號字元。
\反斜線字元。

一些使用範例如下:

1
2
3
4
5
6
7
8
9
10
echo '基本用法';
echo '也可以
斷行';
echo 'You\'re a good man.'; // You're a good man.
echo 'C:\\PHP'; // C:\PHP
echo 'C:\PHP'; // C:\PHP
// 不能在最後省略,\' 會被解析為字元而缺少結尾單引號
//echo 'C:\PHP\'; // Parse error
echo 'First line \n second line'; // First line \n second line
echo 'text is $text'; // text is $text

雙引號

以兩個雙引號包夾一段文字,表示字串內容,最主要的重點是,變數會在雙引號中被解析。雙引號的逸出字元如下:

符號說明
\n換行字元(LF or 0x0A)。
\r歸位字元(CR or 0x0D)。
\t水平Tab字元(HT or 0x09)。
\v垂直tab字元(VT or 0x0B, PHP 5.2.5之後加入)。
\f跳頁字元(CR or 0x0C, PHP 5.2.5之後加入)。
\反斜線字元。
$錢號字元。
"雙引號字元。
[0-7]{1,3}以八進位法表示字元。
\x[0-9A-Fa-f]{1,2}以十六進位法表示字元。

一些使用範例如下:

1
2
3
4
5
6
7
8
9
10
echo "\"You're a good man.\" she said.";    // "You're a good man." she said.
echo "C:\\PHP"; // C:\PHP
echo "First line\nsecond line\x0Athird line";
// output:
// First line
// second line
// third line
$text = "hello";
echo "text is $text"; // text is hello
echo "text is \$text"; // text is $text

Heredoc

Heredoc使用 <<< 符號開始,後面接著一個自訂的標籤,最後以自訂的標籤作結束。

1
2
3
4
5
6
7
8
9
$text="hello";
// 標籤之後不能有任何字元
$here=<<<EOT
\t<a href="http://tw.yahoo.com">Yahoo</a><br/>\n
text is $text<br/>
EOT;
EOT;
// 標籤之前與分號之後不能有任何字元
echo $here;

結果:

1
2
3
Yahoo
text is hello
EOT;

Heredoc 與雙引號性質相似,除了 Heredoc 的雙引號視為一般字元不需使用逸出字元。

Nowdoc

在 PHP 5.3.0 之後新增 Nowdoc 用法,Nowdoc 使用 <<< 符號開始,後面接著一個使用單引號包夾的自訂標籤,最後以自訂的標籤作結束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$text = "hello";
// Nowdoc
$now=<<<'EOT'
C:\PHP\
You're a good man.\n
text is $text
EOT;
echo $now; // C:\PHP\ You're a good man.\n text is $text

// Heredoc
$here=<<<"EOT"
text is $text\n
EOT;
echo $here; // text is hello

Nowdoc 無法使用逸出字元,也不會解析變數,也就是純文字的內容。另外 PHP 5.3.0 之後,相對於 Nowdoc 的單引號,Heredoc 同時新增了使用雙引號包夾的用法。

變數解析

字串中可放入變數直接進行解析,在字串中使用變數的寫法有三種,假設有一變數 $var

  • 直接使用:"$var"
  • 使用大括號一:"${var}"
  • 使用大括號二:"{$var}"

某些情況下直接使用可能會不如預期,如下範例:

1
2
3
4
5
$beer = 'Heineken';
echo "$beer's taste is great"; // 結果正確, 變數名稱不能使用單引號, 所以變數名稱解析為 $beer
echo "He drank some $beers"; // 結果不正確, 代入成變數 $beers
echo "He drank some ${beer}s"; // 結果正確
echo "He drank some {$beer}s"; // 結果正確

所以建議使用大括號的方式為佳,使用陣列或物件的變數的時候也常會有問題:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$teletubbies = array('dindin' => '紫色', 'lala' => '黃色');

echo "拉拉是 $teletubbies[lala]."; // 結果正確
echo "拉拉是 {$teletubbies[lala]}."; // 結果正確, 因為目前沒有 lala 的常數, lala 視為字串
const lala = '黃色';
echo "拉拉是 {$teletubbies[lala]}."; // 結果不正確, lala 常數值為 '黃色', 轉成 $teletubbies['黃色'], 無此元素

// parse error.
//echo "拉拉是$teletubbies['lala'].";

echo "拉拉是 " . $teletubbies['lala'] . "."; // 結果正確
echo "拉拉是 {$teletubbies['lala']}."; // 結果正確

$obj = new stdClass;
$obj->height = 169;
echo "$obj->height cm"; // 結果正確
echo "$obj->height00 mm"; // 結果不正確, 沒有 $obj->height00 此成員
echo "{$obj->height}00 mm"; // 結果正確

字串的操作

字元的存取修改

字串可簡單視為字元的陣列,可以用陣列的索引方式取得或修改某個字元,但並不能真的完全當作陣列使用。用法範例如下:

1
2
3
4
5
6
7
8
9
$str = "I lose";
echo $str[4]; // s
$str[4] = 'v';
echo $str; // I love
var_dump($str[7]); // string(0) "", 超出陣列範圍
$str[7] = 'u'; // 中間自動補空白
echo $str; // I love u
$str[-1] = 'x';
echo $str; // I love u, 負索引不會改變內容

字串的連接

PHP中,字串使用點 (.) 來作為連接運算子,而不是使用加號 (+),如果使用加號的話,會自動將字串轉型為數字做運算。

1
2
3
4
5
$hello = "Hello";
$str = $hello . " world.";
echo $str; // Hello world.
echo $hello + "world"; // 0
echo 30 + "00mm"; // 30, 參照轉型為整數和浮點數的部份就能理解

轉型

要轉型為整數可利用以下方式

  • 利用 (string) 的強制轉型。
  • 利用 strval() 函式轉型。
  • 利用 settype() 傳入引數 "string"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var_dump((string) false);            // string(0) "", 注意 false 為空字串
var_dump((string) true); // string(1) "1"
var_dump((string) 178); // string(3) "178"
var_dump((string) 169.99); // string(6) "169.99"
var_dump((string) array()); // string(5) "Array"
var_dump((string) array(55, 66)); // string(5) "Array"
// var_dump((string) new stdClass); // PHP4: Object, PHP5: Catchable fatal error..., 發生錯誤

class MyObj {
function __toString() {
return "Hello world";
}
}
$obj = new MyObj;
echo $obj; // PHP4: Object, PHP5: Hello world
$fp = fopen("res.txt","w+");
var_dump((string) $fp); // string(14) "Resource id #3"
fclose($fp);
var_dump((string) NULL); // string(0) ""

在 PHP 5 之後的物件必須要實作 __toString() 的函式才能轉為字串,否則會出錯。

常見問題

常數誤用

在 PHP 中,變數使用錢號做開頭,而常數則無錢號,也因此造成許多情況下,未知的字詞就被視為常數處理,但若又無此常數,就會視為字串使用 (如變數解析中出現的情況)。

1
2
3
4
5
6
7
$text = Hello;         // 無 Hello 常數, 視為字串
echo $text; // Hello
const world = "word";
$text = world;
echo $text; // word
$user = "emn178";
echo "Hello " . user; // Hello user, 有時少打錢號造成結果錯誤, 但解析又不會出錯

字串比較

如果直接拿兩個字串來比較的話,程式的邏輯上預設是逐字以字母順序來作比較,例如 b 大於 a 和 ab 大於 aab (比對到第二個字元 b > a),但在 PHP 中,他會先判斷兩者是否為數字內容 (is_numerictrue),如果是的話就轉成數字比較:

1
2
3
var_dump('1.22' > '01.23');                     // bool(false), 兩者皆為數字, 轉為數字比較
var_dump('1.22.10' > '01.23.10'); // bool(true), 不為數字, 以字母比較, 第一個字元 1 > 0
var_dump((float)'1.22.10' > (float)'01.23.10'); // bool(false)

空字串修改

字元的存取修改中提到,以陣列方式去修改超出長度的字元時,會自動填入空白至該位置,但是當變數為空字串時為例外情況,他會被轉為陣列。

1
2
3
$str = "";
$str[10] = "a";
var_dump($str); // array(1) { [10]=> string(1) "a" }

陣列 (Array)

PHP 中的陣列是有順序性的 Map 資料結構,所以他不只可以作為一般的陣列使用外,搭配相關函式還可以將他作為各種資料結構使用,例如:Stack, Queue, Hashtable…等

語法

在 PHP 中,使用 array() 來建立陣列,括號中使用 Key => Value 的鍵與值或 Value 來建立陣列元素,以逗號隔開元素,其中 Key 可為整數或字串:

1
2
3
4
array(  key =>  value
, value
, ...
)

一些範例如下:

1
2
3
4
5
6
7
8
9
10
11
var_dump(array(178, 169.99, "30cm", array(9527)));
// array(4) { [0]=> int(178) [1]=> float(169.99) [2]=> string(4) "30cm" [3]=> array(1) { [0]=> int(9527) } }
// 可放入各種型別

var_dump(array('dindin' => '紫色', 'lala' => '黃色', 'teletubbies'));
// array(3) { ["dindin"]=> string(4) "紫色" ["lala"]=> string(4) "黃色" [0]=> string(11) "teletubbies" }
// 無指定 Key 值, 自動指派下一個數字索引

var_dump(array(3 => 44, 66, 3 => 55));
// array(2) { [3]=> int(55) [4]=> int(66) }
// 重複 Key, 由最後的覆蓋前面

陣列中的元素也可以是陣列,而成為多維陣列的應用。另外,print_r() 函式與 var_dump() 類似,也可將變數內容顯示出來,但較為簡潔,以下部分改使用 print_r() 來呈現。

陣列存取

我們使用中括號 ([]) 加上 Key 來表示陣列中的元素,例如:$array['Key'],以此方式可以取得或修改陣列元素的內容,要清除陣列元素則用 unset() 函式。以下為存取陣列的相關範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ar = array();
$ar[] = 55; // 自動指派索引, $ar[0]
$ar[] = 66;
$ar[5] = 178;
$ar[] = '30cm'; // $ar[6]
$ar['dindin'] = "紫色";
print_r($ar); // Array ( [0] => 55 [1] => 66 [5] => 178 [6] => 30cm [dindin] => 紫色 )
$ar[1] = null; // null 無法移除原素
unset($ar[6]); // 移除 $ar[6] 的元素
$ar[] = "169.99cm"; // $ar[7], 注意不是 $ar[6]
$ar[2] = 66; // 注意 $ar[2] 的順序在 $ar[7] 之後
print_r($ar); // Array ( [0] => 55 [1] => [5] => 178 [dindin] => 紫色 [7] => 169.99cm [2] => 66 )

$ar2[] = 169.99; // 未宣告變數 $ar2, 自動轉為陣列
$ar2['lala'] = "黃色";
print_r($ar2); // Array ( [0] => 169.99 [lala] => 黃色 )

$ar3 = array('dindin' => array('id' => 9527, 'color' => '紫色'),
'lala' => array('id' => 5566, 'color' => '黃色'));
echo $ar3['lala']['color']; // 黃色
$user = "dindin";
echo $ar3[$user]['id']; // 9527, Key 也可使用變數

轉型

要轉型為陣列可利用以下方式

  • 利用 (array) 的強制轉型。
  • 利用 settype() 傳入引數 "array"

將其他型別轉為陣列型別通常的行為是產生一個陣列,索引值 0 的位置為原型別內容,但 NULL 和 object 型別則例外,object 型別轉換的規則如下:

  • 只轉換變數成員,函式成員不被轉換。
  • public 成員以 [成員名稱] 為陣列 Key 值。
  • protected 成員以 [*成員名稱] 為陣列 Key 值。
  • private 成員以 [\0類別名稱\0成員名稱] 為陣列 Key 值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var_dump((array) true);    // array(1) { [0]=> bool(true) }
var_dump((array) 178); // array(1) { [0]=> int(178) }
var_dump((array) 169.99); // array(1) { [0]=> float(169.99) }
var_dump((array) "30cm"); // array(1) { [0]=> string(4) "30cm" }
var_dump((array) NULL); // array(0) { }, 轉為空陣列, 而不是array(1) {[0] => NULL}

// 物件變數成員會轉換成陣列索引值
class X {
private $A = 1; // 將轉成 "\0X\0A"
protected $B = 2; // 將轉成 "*B"
public $C = 3; // 將轉成 "C"
}
class Y extends X {
private $A = 4; // 將轉成 "\0Y\0A"
protected $B = 5; // 將轉成 "*B", 蓋過父類別之 $B
public $C = 6; // 將轉成 "C", 蓋過父類別之 $C
var $XA = 7; // 將轉成 "XA"
public function D() {}
}

$ar = (array) new Y();
var_dump($ar); // array(5) { ["YA"]=> int(4) ["*B"]=> int(5) ["C"]=> int(6) ["XA"]=> int(7) ["XA"]=> int(1) }, 看到兩個 "XA", 但其實一個是 "\0X\0A"
var_dump($ar['YA']); // NULL
var_dump($ar["\0Y\0A"]); // int(4) , 要上加 \0 才能正確取得

$fp = fopen("res.txt","w+");
var_dump((array) $fp); // array(1) { [0]=> resource(3) of type (stream) }
fclose($fp);

物件(Object)

語法

物件使用 new 關鍵字來建立,物件一般由類別 (Class) 來定義內容,也可以使用基本的物件類別 stdClass,使用範例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A {
public $M = 10;
function F() {
echo "Hello";
}
}

$a = new A(); // 或者簡寫為 $a = new A;
echo $a->M; // 10
$a->F(); // Hello
print_r($a); // A Object ( [M] => 10 )

$obj = new stdClass;
print_r($obj); // stdClass Object ( )
$obj->x = 1;
$obj->y = 2;
print_r($obj); // stdClass Object ( [x] => 1 [y] => 2 )

物件存取

如上面範例所示,物件使用 -> 符號來叫用或存取成員。要注意的地方是,如果將物件指派給其他變數,他是以傳址 (Pass by reference) 的方式指派,而不是傳值 (Pass by value) 的方式複製一份,如果要進行複製,則可使用 clone 關鍵字,但是物件下的物件並不會遞迴複製。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$obj = new stdClass;
$obj->x = 1;
$obj->y = 2;
$obj->o = new stdClass;
$obj->o->z = 3;

$obj2 = $obj;
$obj2->x = 100;
echo $obj->x; // 100, $obj 和 $obj2 為相同的物件

$obj3 = clone $obj;
/* 這裡相當於
$obj3 = new stdClass;
$obj3->x = $obj->x;
$obj3->y = $obj->y;
$obj3->o = $obj->o;
*/
$obj3->y = 200;
echo $obj->y; // 2

$obj3->o->z = 300;
echo $obj->o->z; // 300, 物件下的物件並沒有進行複製

轉型

要轉型為物件可利用以下方式

  • 利用 (object) 的強制轉型。
  • 利用 settype() 傳入引數 "object"

將其他型別轉為物件型別通常的行為是產生一個 stdClass 物件,成員 scalar 為原型別內容,但 NULL 和 array 型別則例外,array 型別會將陣列中的 Key => Value 轉成物件成員:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var_dump((object) false);          // object(stdClass)#1 (1) { ["scalar"]=> bool(false) }
var_dump((object) 178); // object(stdClass)#1 (1) { ["scalar"]=> int(178) }
var_dump((object) 169.99); // object(stdClass)#1 (1) { ["scalar"]=> float(169.99) }
var_dump((object) "30cm"); // object(stdClass)#1 (1) { ["scalar"]=> string(4) "30cm" }

$obj = (object) array(55, "dindin" => "紫色");
var_dump($obj); // object(stdClass)#1 (2) { [0]=> int(55) ["dindin"]=> string(4) "紫色" }
echo $obj->dindin; // 紫色
//echo $obj->0; // parse error, 你無法取得名稱為數字的成員

$fp = fopen("res.txt","w+");
var_dump((object) $fp); // object(stdClass)#1 (1) { ["scalar"]=> resource(3) of type (stream) }
fclose($fp);
var_dump((object) NULL); // object(stdClass)#1 (0) { }, 為空物件, 而不是 object(stdClass)#1 (1) { ["scalar"]=> NULL }

資源 (Resource)

資源是特別的型別,為來自外部的資源參考,例如檔案串流等。由於轉型為資源並不具意義,故無法轉型為資源型別。資源的用法依據所使用的類別而定,故需各別參考該資源相關文件。

空值 (NULL)

語法

空值的值只有 NULL 一種內容,不分大小寫。

1
2
$a = NuLl;
var_dump($a == nUlL); // bool(true)

轉型

轉型為空值並沒有特別的意義,但仍可利用以下方式

  1. 利用 (unset) 的強制轉型。
  2. 利用 settype() 傳入引數 "null" 轉型。( 為 PHP 4.2.0 之後新增 )
  3. 利用 unset() 函式,注意這是將變數完全移除。
1
2
3
4
5
$ar = array(55, 66, 77, 88);
settype($ar[0], "null");
unset($ar[1]); // $ar[1] 將完全移除
var_dump((unset) $ar[2]); // NULL
var_dump($ar); // array(3) { [0]=> NULL [2]=> int(77) [3]=> int(88) }

虛擬型別

這部份的型別在文件中使用到,並非實際的型別。

混合 (Mixed)

混合型別在文件表示可以是多種的型別,但不一定是所有型別。例如 str_replace() 函式在文件中紀錄如下:

1
mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

回傳為 mixed 的型別只有 string 或 array 兩種。

數字 (Number)

數字型別表示可以是整數型別或浮點數型別。

回呼 (Callback)

Callback 型別表示某個函式的名稱,實際上傳入的型別是字串,不過在文件中對此用途特別稱為 Callback。

延伸閱讀

上一篇 PHP 教學 - 資料型態 (Data Type) - 上
下一篇 PHP 教學 - 變數 (Variables)