2013年7月20日

Callback 回呼類型

Callback 回呼類型

自PHP 5.4起可用callable型態指定回呼型態callback。
一些函數如call_user_func()或usort()可以接受使用者自行定義回呼函數作為參數。
回呼函數可以是簡單函數,也可以是物件的方法,包括靜態類別方法。

傳遞
一個PHP的函數以字串型態的方式傳遞其回呼函式的名稱。
可以使用任何內建或使用者自定義函數,但是除了語言結構例如array(),echo,empty(),eval(),exit(),isset(),list(),print()或unset()。
一個已經實體化物件的方法做為陣列傳遞,陣列索引0代表該物件,陣列索引1代表方法名稱。
靜態類別方法也可不經實體化該物件而傳遞,只要在陣列索引0中包含類別名稱而不是物件。自PHP 5.2.3起,也可以傳遞'ClassName::methodName'。
除了普通的使用者自定義函數外,create_function()可以用來建立一個匿名回呼函數。自PHP 5.3.0起也可以傳遞closure給回呼函數

<?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 "An";
    }
}
 
class B extends A {
    public static function who() {
        echo "Bn";
    }
}
 
call_user_func(array('B', 'parent::who')); // A
?>

使用Closure的範例
<?php
// Our closure
$double = function($a) {
    return $a * 2;
};
 
// This is our range of numbers
$numbers = range(1, 5);
 
// Use the closure as a callback here to 
// double the size of each element in our 
// range
$new_numbers = array_map($double, $numbers);
 
print implode(' ', $new_numbers);
?>

Note:在PHP4中,需要使用一個參照來建立一個指向具體物件的回呼函數,而不是一個複製物件,參閱參照的解釋

Note:在函數中註冊多個回呼函數(如使用call_user_func()call_user_func_array()時,如果在前一個調用中有未捕捉的異常,其後將不再被調用。

NULL 變數型態

NULL
NULL代表一個變數沒有值。

下列的情況變數的值被認為NULL
1. 被賦值為NULL
2. 尚未被賦值
3. 變數被unset()

語法
NULL類型只有一個值,就是不區分大小寫的常數NULL。

轉換到NULL
使用(unset)$var將一個變數轉換成NULL將不會刪除該變數或unset()該值。
僅是返回NULL值而已。

Resourece 資源型態

Resourece 資源型態

資源resource是一種特殊變數,保存了外部資源的一個參照。
透過特殊的函式來建立resource。所有的函數以及相對應的資源類型請見附表

轉換成資源型態
由於資源變數存放著打開的文件、資料庫連接、圖形數據區域和其他類似處理的特殊處理器,因此轉換成資源是沒有意義的。

釋放資源
由於PHP第四代Zend引擎引進了參照計數系統,可以自動檢測到一個資源不再被參照了。
這種情況下此資源使用的所有外部資源都會被垃圾回收器所釋放,因此很少需要手動釋放。

Note:持續性資料庫連接比較特殊,它們不會被垃圾回收器所銷毀。請參閱資料庫持續連接章節

Object 物件型態

Object 物件型態

物件初始化
要創建一個新的物件object,使用new語法實體化一個類別。
<?php
class foo
{
    function do_foo()
    {
        echo "Doing foo."; 
    }
}
 
$bar = new foo;
$bar->do_foo();
?>

詳細討論請看類別與物件

轉換成物件
如果將一個物件轉換成物件,它將不會有任何變化。
如果其他變數類型的值被轉換成物件,將會建立一個內建類別stdClass的實例。
如果該值為NULL,則新的實例為空。
陣列轉換成物件將使鍵名稱成為屬性名稱並具有相對應的值。
對於任何其他的值,名為scalar的成員變數將包含該值。
<?php
$obj = (object) 'ciao';
echo $obj->scalar;  // outputs 'ciao'
?>

Array 陣列型態

Arrays 陣列
PHP的Array實際上是一個有序的映射。
映射是一種把values關連到keys的類型。

它可以被當作陣列、列表(list)、向量(vector)、雜湊表(hash table)(是映射的一種實現)、字典(dictionary)、集合(collection)、堆疊(stack), 序列(queue)以及其他更多的可能。

語法
array(
key => value
, ......
,key2 => value2
,key3 => value3
)
鍵(key)可以是一個整數或字串。
值(value)可以是任何型態。

最後一個元素之後的逗號可以省略。即使加上逗號,在計算陣列元素上不會造成影響。
<?php
// 最後一個沒加上逗號
$a = array(1, 2);
echo count($a);  // 2
 
// 最後一個加上逗號
$a = array(1, 2, );
echo count($a);  // 2
?>

自PHP5.4起可以使用簡單定義語法,用[]取代array()。
<?php
$array = array(
    "foo" => "bar",
    "bar" => "foo",
);
 
// 自 PHP 5.4 起
$array = [
    "foo" => "bar",
    "bar" => "foo",
];
?>

Key會有下列提到的強制轉換
1. 包含有合法整數型態的字串值會被轉換為整數型態。例如字串"8"實際會被儲存為整數8。但是"08"則不會被強制轉換,因為不是一個合法的十進位數值。
2. 浮點數會被轉換為整數,小數點部分無條件捨去。例如8.7會被儲存為8。
3. 布林值會被轉換成整數,true會儲存為1,false會被儲存為0。
4. Null會被轉換成空字串,null會被儲存為""。
5. 陣列和物件不能被當作key。如果使用會出現警告Illegal offset type。

注意事項
1. 一個陣列中多個元素使用同一個鍵,則後者會覆蓋前者。
2. 陣列中可以同時擁有字串以及整術類型的鍵。
3. 如果陣列沒有指定鍵,則取當前最大的整數索引值,新的鍵則是該值加一。
4. 如果指定的鍵已經有值,則該值會被覆蓋。
5. 可以只對某些元素指定鍵,其他元素不指定。
6. 取當前最大的整數索引值是指曾經存在過即可。

讀取陣列元素
透過array[key]或者array{key}

Note:試著讀取一個未定義的陣列索引與讀取任何未定義的變數一樣,會導致E_NOTICE等級的錯誤訊息,其結果為NULL。

用[]的語法新增或修改陣列
1. 直接指定該陣列內的key-value,$array[key] = value
2. 利用[]新增一個元素到陣列內,$array[] = value

第二種方法如果陣列不存在則會新建一個。但是不建議使用該方式來新建陣列。初始化變數的最好方式是直接給值。

用unset和鍵刪除某個元素
unset($array[key])

為什麼$foo[bar]錯了?
當你使用這樣的方式存取一個元素時,會出現一個E_NOTICE的錯誤訊息(Notice: Use of undefined constant name - assumed 'foo'),但是程式還是可以正常運作,那是因為PHP先試著尋找一個定義為foo的常數,找不到的時候將foo自動轉換成字串"foo"並使用它。

Note:但是這不代表你總是要將鍵加上引號。如果鍵是一個常數或變數則不需要。

轉換為陣列
對於任意整數類型、浮點數類型、字串類型、布林類型和資源類型,如果將其轉換為陣列,將得到一個僅有一個元素的陣列,其鍵為0,該元素的值為原來的值。

將物件轉換為陣列,則結果為一個陣列,其元素為該物件的屬性,鍵將為屬性變數名稱。但是有幾點要注意,整數屬性不可讀取,私有屬性前面會加上類別名稱作為前綴字,保護屬性前面會加上"*"作為前綴字。這些前綴字的前後都各有一個NULL字元,這會導致一些不可預期的行為。
<?php
// 整數屬性不可讀取
$obj = new stdClass();
$obj->{111} = 222;
var_dump($obj);
 
$arr = (array)$sobj;
var_dump($arr);
 
echo $arr[111]; // 產生 Notice: Undefined offset: 111 錯誤
echo $arr["111"]; // 產生 Notice: Undefined offset: 111 錯誤
?>

下面的例子印出後會看到兩個鍵為AA,但是實際上其中一個是"\0A\0A"。
<?php
class A {
    private $A; // This will become 'AA'
}
 
class B extends A {
    private $A; // This will become 'BA'
    public $AA; // This will become 'AA'
}
var_dump((array) new B());
?>
將NULL轉換陣列會得到一個空陣列。

比較陣列
可以用array_diff()陣列運算子來比較陣列。

直接改變陣列的值自PHP5開始可以透過參照傳遞做到。之前的版本需要採取變通的方法。
注意當使用此方法,該變數可能會影響到原有陣列。
<?php
// PHP 5
foreach ($colors as &$color) {
    $color = strtoupper($color);
}
unset($color); /* ensure that following writes to
$color will not modify the last array element */
 
// Workaround for older versions
foreach ($colors as $key => $color) {
    $colors[$key] = strtoupper($color);
}
 
print_r($colors);
?>

2013年7月14日

String 字串型態

String 字串型態
一個字串(string)就是由一系列的字元(character)組成,一個字元等於一個位元組(byte)。
這意味了PHP只支援256字元集,因此不支援unicode。

語法
一個字串可以用四種方式宣告:
1. 單引號
2. 雙引號
3. Heredoc 語法
4. Nowdoc 語法(自PHP 5.3.0起)

單引號
定義一個字串最簡單的方法是用單引號將他包圍起來。
如果字串內有使用到單引號或者是反斜線,需要在它的前面加上反斜線(\)來跳脫。
其他用反斜線所要表示的跳脫字元,反斜線只會被當作一個反斜線。
意思是當你使用例如\n或者是\r並不代表任何跳脫字元,單純的就是指這兩個字元。

Note:不像雙引號或者是heredoc語法,在單引號字串中的變數和跳脫序列中的特殊字元將不會被替換。

雙引號
如果字串是用雙引號包住,PHP將對特殊字元進行解析。

以下說明跳脫序列以及所代表的意義
\n, 換行符號 (LF or 0x0A (10) in ASCII)
\r, 回車(返回)符號 (CR or 0x0D (13) in ASCII)
\t, 水平定位點符號 (HT or 0x09 (9) in ASCII)
\v, 垂直定位點符號 (VT or 0x0B (11) in ASCII) (自 PHP 5.2.5 起)
\e, 跳脫符號 (ESC or 0x1B (27) in ASCII) (自 PHP 5.4.0 起)
\f, 換頁符號 (ASCII 字符集中的 FF 或 0x0C (12))(自 PHP 5.2.5 起)
\\, 反斜線
\$, 美元符號
\", 雙引號
\[0-7]{1,3}, 用符合該正規表示法的八進位來表示某個字元,例如\101代表A
\x[0-9A-Fa-f]{1,2}, 用符合該正規表示法的十六進位來表示某個字元,例如\x41代表A(大寫X也可以,PHP5.4版本測試)

除了上述所提到的跳脫序列,其他使用反斜線跳脫的情況,都會導致反斜線被顯示出來。P
HP5.1.1前,\{$var}中的反斜線還不會被顯示出來。
用雙引號定義的字串最重要的特點是變數的內容會被解析。

Heredoc語法
使用<<<運算子,緊接在後面加上一個識別符,接著換行。接下來就是字串String本身,最後要用前面定義的識別符號作為結束標誌。
結束時的識別符號必須在該行的第一列,而且識別符號的命名也要遵守PHP的規格,只能使用字母、數字和底線,並且必須是底線或字母開頭。

Warning
要注意的是結束識別符號這行除了可能有一個分號外,絕對不能包含其他字元,這意味識別符號不能使用縮排,分號的前後也不可以有任何空白或定位符號(\t,\v)。更重要的是結束識別符號的前面必須是一個被本地作業系統認可的換行符號,比如在UNIX系統是\n,Mac OS X是\r,而結束識別符號(可能會接著分號)之後也必須緊接著一個換行符號。

如果不遵守該規則導致結束識別符號不乾淨,PHP將認為它不是結束識別符號而繼續尋找。如果在該文件結束前沒有找到一個正確的結束識別符號,PHP將會在最後一行產生一個解析錯誤。

Heredoc語法不能用來初始化類別的屬性。自PHP 5.3起,此限制僅對 heredoc語法內有變數時有效。

Heredoc語法就像是沒有使用雙引號的雙引號字串,在Heredoc語法內的單引號不需要使用反斜線跳脫。
上面所列出的跳脫序列也可以使用,變數將被替換成變數內容,但在Heredoc語法有複雜的變數時要格外小心

<?php
$str = <<<EOD
Example of string
spanning multiple lines
using heredoc syntax.
EOD;
 
/* 含有變數的更複雜範例 */
class foo
{
    var $foo;
    var $bar;
 
    function foo()
    {
        $this->foo = 'Foo';
        $this->bar = array('Bar1', 'Bar2', 'Bar3');
    }
}
 
$foo = new foo();
$name = 'MyName';
 
echo <<<EOT
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should print a capital 'A': x41
EOT;
?>

自PHP 5.3.0起還可以用Heredoc語法用雙引號標示出識別符號

Nowdoc語法
Nowdoc語法是類似於單引號字串的。Nowdoc語法類似Heredoc語法,但是在Nowdoc語法中不進行解析操作。這種語法很適合用於嵌入PHP代碼,或其他大量文字而無需對內容中的特殊字元進行解析。與SGML的<![CDATA[]]>語法是用來宣告大量不用解析的文字段落類似。

Nowdoc語法的宣告方式跟Heredoc語法一樣,只是開始的識別符號必須用單引號括起來,即<<<'EOT',其餘規則同Heredoc語法。

<?php
$str = <<<'EOD'
Example of string
spanning multiple lines
using nowdoc syntax.
EOD;
 
/* 含有變數的更複雜的例子 */
class foo
{
    public $foo;
    public $bar;
 
    function foo()
    {
        $this->foo = 'Foo';
        $this->bar = array('Bar1', 'Bar2', 'Bar3');
    }
}
 
$foo = new foo();
$name = 'MyName';
 
echo <<<'EOT'
My name is "$name". I am printing some $foo->foo.
Now, I am printing some {$foo->bar[1]}.
This should not print a capital 'A': x41
EOT;
?>

Note:Nowdoc語法可以用在任意的靜態數據環境中,最典型的例子是用來初始化類別的屬性或者是變數。
Note:Nowdoc語法是在PHP5.3.0中加入的

變數解析
當字串用雙引號或Heredoc語法定義時,其中的變數將會被解析為變數內容。
這裡有兩種語法規則,一種簡單規則,另一種複雜規則。
複雜規則語法的標記是用大括號包圍的表達示。

簡單語法
當PHP解析器遇到一個$符號時,會去組合盡量多的字元形成一個合法的變數名稱。可以用大括號來明確表示變數名稱的範圍。

<?php
// 上述的情況會出現一個E_NOTICE的錯誤,
// 找不到一個變數名稱為$log_filename_error的變數。
 
$log_filename = "2013-07-04";
echo "log_$log_filename_error.log";
?>

複雜語法
之所以稱作複雜語法是因為它可以使用複雜的表達式。
任何具有string表達的變數,陣列單元或者是物件屬性都可以使用此語法,只需要簡單在string以外的地方寫出表達式,然後用大括號將它括起來即可。
由於{無法被解析,只有$緊接著{才會被識別。

<?php
// 顯示所有錯誤
error_reporting(E_ALL);
 
$great = 'fantastic';
 
// 無效,輸出: 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."; 
 
// 有效,只有通過花括弧語法才能正確解析帶引號的鍵名
echo "This works: {$arr['key']}";
 
// 有效
echo "This works: {$arr[4][3]}";
 
// 這是錯誤的運算式,因為就象 $foo[bar] 的格式在字串以外也是錯的一樣。
// 換句話說,只有在 PHP 能找到常量 foo 的前提下才會正常工作;這裡會產生一個
// E_NOTICE (undefined constant) 級別的錯誤。
echo "This is wrong: {$arr[foo][3]}"; 
 
// 有效,當在字串中使用多重陣列時,一定要用括弧將它括起來
echo "This works: {$arr['foo'][3]}";
 
// 有效
echo "This works: " . $arr['foo'][3];
 
echo "This works too: {$obj->values[3]->name}";
 
echo "This is the value of the var named $name: {${$name}}";
 
echo "This is the value of the var named by the return value of getName(): {${getName()}}";
 
echo "This is the value of the var named by the return value of $object->getName(): {${$object->getName()}}";
 
// 無效,輸出: This is the return value of getName(): {getName()}
echo "This is the return value of getName(): {getName()}";
?>

Note:函數、方法、靜態類別變數和類別常數只有在PHP5以後才可以在{$}中使用。指單一使用大括號({})無法處理從函數或方法的回傳值或者類別常數以及類別靜態變數的值。

存取或修改字串中的字元
string中的字元可以透過一個從0開始的偏移量,用類似array中的方括號加上對應的數字來存取或修改,比如$str[42]。

Warning
用超出字串長度的偏移量將會使得該字串變長,沒有指定的部分用空格填充。
非整數的偏移量會被轉換成整數,並產生一個E_NOTICE等級錯誤。用負數偏移量讀取字串回傳空字串。
只有第一個字元會被賦值。(指得是可能是多字節字元)
將空字串賦值給的值是NULL。

Warning
在內部,PHP的字串是位元組組合的陣列。所以透過陣列括號方式存取或修改多位元組字串是不安全的,應該只能用在單位元組編碼的字串,例如ISO-8859-1。
自PHP5.4起,字串偏移量只能使用整數或者是可被轉換成整數的字串,否則會產生警告。在此版本之前,自動轉換而不會產生警告。

Note:用[]或{}存取任何其他類型(不包含陣列或具有相對應接口的物件)的變數只會無聲地回傳NULL。

有用的函數和運算符號
字串可以用"."(點)運算符號進行連接,注意"+"(加號)運算符號沒有這個功能。

轉換成字串
一個值可以透過在前面加上(string)或用strval()函數來轉換成字串。在一個需要字串的表達式中,會自動轉換為字串。
比如在使用函數echo或print時,或在一個變數和另外一個字串進行比較時,就會發生這種轉換。

一個布林值的true被轉換成字串的"1",布林的false被轉換成""(空字串)。
一個整數或浮點數被轉換為數字的字面樣式的字串(包含浮點數中的指數部分)。使用指數表示法的浮點數(4.1E+6)也可以轉換。

Note:在指令稿的區域(category LC_NUMERIC)中定義了十進位小數點的字元,詳情參見setlocate()。

陣列array總是轉換成字串"Array",因此echo和print無法顯示出該陣列的內容。要顯示某個單元,可以用echo $arr["foo"]。要顯示整個陣列內容請看下文。
在PHP4中物件object總是被轉換成字串"Object",如果為了測試需要印出物件的值,請繼續閱讀下文。為了得到物件的類別名稱,可以用get_class()函數。自PHP5起,適當時可以用__toString方法。

資源resource總是會被轉換成字串"Resource id #1"這種結構的字串,其中的1是PHP在執行階段分配給該resource的唯一值。不要依賴此結構,可能會有變更。要得到一個resource的類型,可以用函數get_resource_type()

NULL總是被轉換成空字串。

如上所說,直接把array、object或resource轉換成string不會得到除了其類型之外的任何有用訊息。可以使用函數print_r()和var_dump()列出這些類型的內容。
大部分的PHP值可以轉變成string來永久保存,這被稱為序列化,可以用函數serialize()來實現。如果PHP引擎設定支援WDDX,PHP值也可以被序列化為隔式良好的XML文件。

字串轉換為數值
當一個字串被當作一個數值來取值,其結果和類型如下
如果該字串沒有包含"."、"e"和"E",並且其數字值在整數類型的範圍之類(由PHP_INT_MAX所定義),該字串將被當成整數來取值。其他所有情況下都被當作浮點數來取值。

該字串的開始部分決定了它的值。如果該字串以合法的數值開始,則使用該數值。否則其值為0(零)。合法數值由可選的正負號,後面跟著一個或多個數字(可能有小數點),再跟著可選的指數部分。指數部分由"E"或"e"後面跟著一個或多個數字構成。

在C語言中,直接將一個字元轉換成整數可以直接取得ASCII代碼。
但是在PHP必須使用函數ord()或chr()實現ASCII和字元間的轉換。

字串類型詳解(重要)
PHP中的string的實作是一個由位元組組成的陣列再加上一個整數指定緩衝區長度。並沒有儲存如何將位元組轉換成字元的資訊,這部分交由程式設計師來決定。
字串由什麼值來組成並無限制。特別是,其值為0("NUL  Bytes")的位元組可以處於字串任何位置(不過有幾個函數,在本手冊中被稱為非"二進制安全"的,也許會把NUL位元組之後的數據全都忽略)。

字串類型的此性質解釋了為什麼PHP中沒有單獨的"byte"類型。已經用字串來代替了。
返回非文字的函數 - 例如從網路socket讀取的任意數據 - 仍會回傳字串。
(意思是從socket傳送過來的任何數據,透過functions讀取會回傳字串。)

由於PHP並不特別指名字串的編碼,那字串到底是怎樣編碼的呢?例如字串"á"到底是等於"\xE1"(ISO-8859-1)、"\xC3\xA1"(UTF-8,C form),"\x61\xCC\x81"(UTF-8,D form)還是任何其他可能的方式呢?答案是字串會被按照該指令稿文件(PHP檔案)相同的編碼方式來編碼。因為如果一個指令稿的編碼是ISO-8859-1,則其中的字串也會被編碼為ISO-8859-1,以此類推。不過這並不適用於啟動了Zend Multibyte時。此時指令稿可以是以任何方式編碼的(明確指定或自動檢測),然後被轉換為某種內部編碼,然後字串將被用此方式編碼。注意指令稿的編碼有一些約束(如果啟動了Zend Multibyte且是其內部編碼),這意味著此編碼應該是ASCII的兼容超集,例如UTF-8或ISO-8859-1,不過要注意,依賴狀態的編碼其中相同的位元組可以用於首字母和非首字母而轉換狀態,這可能會造成問題。

PS:有沒有開啟Zend Multibyte可以透過phpifo()函式查看Configure Command欄位是否有--enable-zend-multibyte。此選項是Zend引擎的一部分,被用來自動偵測指令稿以及檔案是否為萬國碼編碼,它搜尋檔案的開始BOM來分辨是否編碼。

當然,要使之作用正確,操作文字的函數必須假設字串是如何編碼的,不幸的是,PHP關於此的函數有很多變種情況:
* 某些函數假設字串是以單位元組編碼,並不需要將位元組解釋為特定的字元,例如substr()、strpos()、strlen()和strcmp()。理解這些函數的另一種方法是它們作用於記憶體緩衝區,即按照位元組和位元組偏移量操作。
* 某些函數被傳入了字串的編碼方式,也可能會預設無此資訊。例如htmlentities()和mbstring()擴充函式庫中的大部分函數。
* 其他函數使用了目前本機設定(可看setlocate()),但是逐位元組操作。例如strcasecmp()、strtoupper() 和 ucfirst()。這意味著這些函數只能用於單位元組編碼,而且編碼要與區域符合。例如strtoupper("á") 在本機設定正確並且á是單位元組編碼時會回傳"Á"。如果是用UTF-8編碼則不會回傳正確結果,其結果根據目前本機設定有可能回傳損毀的值。
* 最後一些函數會假設字串是使用某特定編碼的,通常是UTF-8。intl擴充函式庫和PCRE(上例中僅在使用了u修飾符號)擴展函式庫中的大部分函數都是這樣。儘管這是由於特殊用途, utf8_decode()會預設UTF-8編碼,而utf8_encode()會預設ISO-8859-1編碼。

最後,要書寫能夠正確使用unicode的程序取決於很小心地避免那些可能會損壞數據的函數。要使用來自於intl和mbstring擴充函式庫的函數。
不過使用能處理unicode編碼的函數只是個開始。不管用何種語言提供的函數,最基本的還是了解unicode規格。
例如一個程序如果假設只有大寫跟小寫字母,那可是大錯特錯。

2013年6月30日

Float 浮點數型態

Float 浮點數型態

也稱作雙倍精準數(double)或實數(real)。可以用以下任一語法定義
<?php
$a = 1.234; 
$b = 1.2e3; 
$c = 7E-10;
?>

Warning 浮點數的精準度

浮點數的精準度有限,儘管取決於系統,PHP通常使用IEEE 754 雙精準度格式,則由於取整數而導致的最大相對誤差為 1.11e-16。非基本數學運算可能會給出更大誤差,並且要考慮到進行多次運算時的誤差傳遞。

此外,以十進位制能夠精確表示的有理數如0.1或者0.7,無論有多少尾數都不能被內部所使用的二進位制精確表示,因為無法在不丟失一點點精準度的情況下轉換為二進制的格式。這就會造成混亂的結果。

例如,floor((0.1+0.7)*10),通常會得到7而不是預期的8,因為該結果內部的表示其實是類似7.9999999999999991118...。

所以永遠不要相信浮點數結果精確到了最後一位數,也永遠不要比較兩個浮點數是否相等。如果確實需要更高的精準度,應該使用任意精準度數學函數或者是gmp函數


轉換為浮點數型態

如果希望了解有關何時以及如何將字串轉換成浮點數的資訊,請參閱字串轉換為數值的章節。對於其他類型的值,其情況類似於先將值轉換成整數型態,然後再轉換成浮點數型態。
請參閱轉換為整數型態章節獲得更多的資訊。自PHP5起,如果試圖將物件轉換為浮點數,會發出E_NOTICE錯誤訊息。


比較浮點數

如上述警告訊息所述,由於內部機制的原因,比較兩個浮點數是否相等是有問題的。不過還是有迂迴的方式來比較浮點數數值的。

要測試浮點數是否相等,要使用一個儘比該數值大一點的最小誤差值。該值也被稱為機器最小值(epsilon)或者最小單元取整數,是計算中所能接受的最小差別值。

$a 和 $b 在小數點後五位精準度內都是相等的。
<?php
$a = 1.23456789;
$b = 1.23456780;
$epsilon = 0.00001;
 
if(abs($a-$b) < $epsilon) {
    echo "true";
}
?>


NaN

某些數字運算的結果會回傳一個常數NAN,在浮點數的計算中,這個結果代表一個未定義或者是無法表示的數。任何值對該NAN進行鬆散或嚴謹的比較皆會回傳FALSE。

因為NAN代表著不同的任何數字,NAN不應該拿來與其他的值進行比較,包括它自己。如果你想要檢查請使用is_nan()。

2013年6月24日

Integer 整數型態

語法:可以用十進位、十六進位以及八進位來表示,PHP沒有無號整數。
PHP5.4.0可以使用二進位的方式表是一個整數。


<?php
$a = 1234; // 十進位數
$a = -123; // 負數
$a = 0123; // 八進位數 (等於十進位 83)
$a = 0x1A; // 十六進位數 (等於十進位 26)
$a = 0b111; // 二進位數
?>


八進位的怪事
當輸入的值不符合八進位的要求,則會忽略後面的部分

<?php
var_dump(01090); // 010 octal = 8 decimal
?>


根據作業系統的位元數,整數的最大值有所不同,32位元大約是20億左右,64位元最大值大約為9E18

PHP有預定義的常數 PHP_INT_MAX 跟 PHP_INT_SIZE。

<?php
echo PHP_INT_MAX; // 顯示最大的整數值 2147483647
echo PHP_INT_SIZE; // 顯示儲存整數的空間 4 Bytes
?>


整數溢出

當給訂的數字超過了 integer 的範圍,將會被解釋為 float,如果執行後的結果溢出也是會回傳 float。

絕對不要將未知的分數強制轉換為 integer ,有時候會導致非預期的錯誤

<?php
echo (int) ( (0.1+0.7) * 10 ); // 顯示 7!
?>

2013年6月23日

Boolean 布林型態

語法:要指定一個布林值,可以使用關鍵字 true 或者 false。兩者不分大小寫
例如:$flag = true;

<?php 
$var1 = TRUE; 
$var2 = FALSE; 
 
echo $var1; // 顯示數字1 
 
echo $var2; // 不會顯示任何東西
 
/* 如果你想要它顯示數字0,你必須強制轉型為整數型態 */ 
 
echo (int)$var2; // 顯示數字0
?>


PHP會在需要的時候自動轉換型態,所以在以下第三種情況要特別小心。
<?php
// Consider that the 0 could by any parameters including itself
var_dump(0 == 1); // false
var_dump(0 == (bool)'all'); // false
var_dump(0 == 'all'); // TRUE, take care
var_dump(0 === 'all'); // false
 
// To avoid this behavior, you need to cast your parameter as string like that :
var_dump((string)0 == 'all'); // false
?>

PHP的八種原始數據型態

四個純量型態
boolean(布林型態)
integer(整數型態)
float(浮點數型態,也稱作 double)
string(字串型態)

兩個複合型態
array (陣列)
object (物件)

特殊型態
resource (資源)
NULL

如果想要同時查看變數的值跟類型,可以使用 var_dump() 函數。
如果想要知道變數類型並且回傳一個人類方便閱讀的文字字串,可以使用 get_type() 函數。
但是如果是想要檢查是否為某個類型時,使用 is_type 函式會更好
例如: is_array(), is_int(), is_string() 等等

要將變數強制轉型可以使用 set_tyep() 函數或者是直接使用強制轉換寫法。

要注意的是變數的型態通常不是由程式人員來決定,更確切地說,是在執行時由PHP根據變數使用的上下文程式碼而決定。

2013年6月22日

PHP Code Style

程式碼擁有相同的code style,可閱讀性就會提升。如果又使用code formating tool,這樣光是看程式碼的排版就完全分不出來到底是團隊中哪個成員所撰寫。

以下提供兩個比較完整的參考資料

1. Zend Framework Coding Standard for PHP

2. PHP-FIG 所提出的
    2-1. PSR-0 (Autoloading Standard)
    2-2. PSR-1 (Basic Coding Standard)
    2-3. PSR-2 (Coding Style Guide)

2-1. 說明了自動載入類別的標準包含供應商、命名空間以及類別名稱該如何決定,並且寫了一個範例在下方。
2-2. 說明基本編碼標準
2-3. 編碼風格指南

PHP標記、指令分隔符、註解

PHP標記

當解析一份文件時,解析器會尋找開始標籤<?php以及結束標籤?>,這告訴了解析器開始以及停止解析兩者之間的代碼。
因為這樣的解析方式,PHP可以被嵌入在不同的檔案中,兩個標籤之間的文字會被當作PHP代碼解析,其餘則不處理。

可以使用的方式有
1. <?php  ?>
標準且常用的方式

2. <?     ?>
短標籤,簡寫,必須要在 php.ini 內將 short_open_tag 設定為 on,或者在PHP編譯時加上 --enable-short-tags 選項

3. <%     %>
ASP標籤,必須要在 php.ini 內將 asp_tags 設定為 on。

4. <script language="php"> </script>
標籤內的文字不分大小寫

NOTE

另外提到一點是如果該文件是純PHP檔案的話可以省略結尾符號,雖然結尾符號後會刪除一個換行符號,但是如果有兩個換行符號或者是空白符號,則會出現非預期的空白出現。

避免使用短標記,尤其是開發需要更新的程式,或者是你無法控制設定的環境(虛擬主機),為了確保程式碼移植與發行,不要使用短標記

在PHP5.2和之前的版本中,解析器不允許一個文件的內容只有一個開始標記"<?php"。但是在PHP5.3以後就可以,不過在開始標記後面必須要有一個或更多的空白
例如:"<?php "

PHP5.4起,短標記的echo寫法會被識別且是合法,不再管 short_open_tag 的設定如何。
例如: Hello <?=$name ?> 可以被允許
否則你必須寫成 Hello <?php echo $name ?>

單行註解遇到結束符號的情況也要特別注意,下列程式碼並沒有將結尾符號註解。
<?php
// ?>
echo 111;
?>

指令分隔符

跟C還有Perl一樣,PHP需要在每個語句的後面使用分號當作結束。
但是PHP的結束符號包含了一個行結束,下列的寫法是允許的。
<?php 
echo "Hello";
echo "This is a test" 
?>

註解

單行註解可以使用//或者是#
<?php 
//這是註解
#這是註解
?>
要注意的是單行註解後面如果接有?>結束標記,會跳離PHP模式解析。

多行註解 /* */
<?php 
/*
這是註解
這是註解
這是註解
這是註解
*/
?>
要注意的是多行註解中又使用多行註解,註解的開始符號找到第一個結束符號就結束了,所以可能會出現非預期的錯誤
<?php 
/*
這是註解
/*這是註解
這是註解*/
這是註解
*/
?>

有關PHP的小提示

關於換行

換行符號在HTML的實際意義不大,適當的使用可以增加HTML代碼的可閱讀性以及美觀。
PHP的結尾附號"?>"會順帶刪除接在後面的一個換行符號。
如果你希望後面有一個換行符號可以在該結束符號加上一個空白隔開,或者是直接利用PHP echo 輸出一個換行符號。

關於開發工具

編輯PHP檔案必須使用純文字編輯器。
例如:notepad++或者是其他支援PHP的IDE

關於副檔名

將檔案副檔名設定為指定要處理的副檔名,副檔名可以進行設定。
例如:.php, .phtml

在某些開放原始碼的專案中,會使用附檔名.inc,副檔名設定上可能會有安全性的疑慮。
另外一個問題是作業系統預設隱藏檔案的副檔名,所以你改變的可能只是主檔名。
例如:存檔時候設定index.php,實際儲存成index.php.txt

使用utf-8編碼

目前的開發大多使用utf-8編碼,確保你所儲存的檔案已經移除了該檔案的 Byte order mark (BOM,用來識別這個檔案編碼的用途),否則你的PHP執行上可能會有錯誤訊息。
例如:使用 header 或者是 session_start。

簡單的安裝可以運行PHP的環境。

如果是剛開始學習PHP,我建議使用人家直接包裝好的安裝程式。安裝好了以後會直接有網頁伺服器apache、資料庫Mysql、PHP。在Windows環境下,被稱為WAMP。如果作業系統是Linux的話,稱為LAMP。

使用這種方式的好處是你可以直接學習PHP以及PHP如何連接資料庫。專注在學習語言特性上。

1. XAMPP

這是我目前比較常使用的。網站也提供中文解說,雖然不是很完整,但是安裝到好,並且做一些基本的設定都有說明。

2. AppServ

這是我一開始最先使用的。好處是它提供了較多的選擇,因為你必須要安裝Apache、PHP以及Mysql,但是這些都會有自己的版本號,你可以有較多的選擇,使用不同版本的PHP,不同版本的Apache。

PHP能做些什麼?

1. 伺服器端指令稿 (Server-side scripting)
這是PHP最傳統也是最主要使用的領域。你需要PHP解析器(CGI或是伺服器模組)、網頁伺服器以及網頁瀏覽器使之運作。你需要在安裝好PHP的網頁伺服器上運作,並且透過網頁瀏覽器瀏覽PHP頁面去存取網頁伺服器解析PHP輸出的結果。

PS:一般的教學書籍大多是使用第一種方式。

2. 指令列指令稿 (Command line scripting)
你可以不用透過伺服器或者是瀏覽器執行PHP指令稿,你只需要PHP解析器就可以運作。這種用法在linux系統上通常用來跑cron,在win系統用來跑日常工作。通常這樣的用法用來處理一些簡單的文字處理工作。

PS:可以來寫一個交互應答的程序或者是持續運作的程序

3. 用來寫桌面應用程序
對於擁有圖形用戶介面的桌面應用程序,PHP或許不是最好的語言,但是如果你非常了解PHP,並且想要使用PHP接近的功能在你用戶端應用程序,你可以使用PHP-GTK去編寫這類型的程序

PHP可以在大多數主流的作業系統上使用,PHP同時也支援大多數的網頁伺服器,PHP在網頁伺服器上可以是一個模組也可以是一個CGI處理來運作。

你可以自由地選擇作業系統以及網頁伺服器來執行PHP。此外,你可以使用程序導向或者是物件化導向的程式設計,或者是兩者混用。

PHP並沒有限制被用來輸出HTML。你也可以用來輸出圖像、PDF檔案、甚至是FLASH (使用 libswf 或者 Ming),還能夠簡單的輸出文字,例如XHTML或者XML。
PHP可以自動產生這些檔案在伺服器端的快取內,並且將它們儲存在系統內或者是動態地輸出內容

PHP也支援相當多的資料庫,可以透過安裝擴充針對不同的資料庫進行存取。

PHP還支援 LDAP、IMAP、SNMP、NNTP、POP3、HTTP、COM(WINDOWS系統)等協議服務,還支援了WDDX複雜資料交換,也支援對Java的連接。

PHP在處理文字上也非常方便,包括了Perl正規表達式 (PCRE) 以及許多的擴展。對於XML的解析與存取也可以使用基礎的libxml2或者是後來新加入的SimpleXML、XMLReader和XMLWriter。

2013年4月7日

推薦開發用的工具


推薦幾款不錯的免費IDE

Aptana http://www.aptana.com/
下載後,馬上就可以選取PHP的專案來進行開發。

Netbeans https://netbeans.org/
可以只單獨下載PHP開發的版本。

Eclipse PDT http://www.eclipse.org/
此IDE支援非常多的開發,並可以新增外掛來進行擴充。PDT則是他支援PHP的外掛。



功能強大的免費文字編輯器
Notepad++ http://notepad-plus-plus.org/

目前我是使用notepad++來進行開發,主要的原因有

1. 使用順手,一直以來也都是用這個,途中有試過上述的幾個IDE,但是沒有明顯的方便,所以又用回notepad++。

2. 開啟程式速度快。

3. 其他IDE該有的都有,唯一一個問題就是沒有辦法在a.php檔案中快速地開啟宣告在b.php檔案中的class,method 或者 function。


List of PHP editors

2013年4月6日

Hightlight Code In Blogger

為了要讓文章內的程式碼看起來更漂亮
尋找了網路上在 google blogspot 可行的辦法主要分為三種。

1. Syntax Highlight
http://alexgorbatchev.com/SyntaxHighlighter/
2. Prettify
https://code.google.com/p/google-code-prettify/
3. 針對元素code新增css
code{
 display: block; /* fixes a strange ie margin bug */
 font-family: Courier New;
 font-size: 8pt;
 overflow:auto;
 background: #f0f0f0 url(http://dl.dropbox.com/u/18989482/Code_BG.gif) left top repeat-y;
 border: 1px solid #ccc;
 padding: 10px 10px 10px 21px;
 /*  max-height:200px; */
 line-height: 1.2em;
}
第 1 種
blogger提供了動態檢視及圖片視窗等多種的範本使用。
在動態檢視範本無法正常運作。
在圖片視窗範本會有多行導致有scrollbar出現,我不喜歡。

第 2 種
看起來跟第1種方法差不多就不打算試用了。

第 3 種
在動態檢視範本中無法使用。
目前我是選擇圖片視窗範本,然後使用第三種方法來顯示我的程式碼。
會透過手動文字顏色編輯讓程式碼更清楚分辨。
並且使用線上hightlight,http://highlight.hohli.com/
以及轉換html標籤實體,http://www.functions-online.com/htmlentities.html



PHP 前言


PHP是"PHP: Hypertext Preprocessor"的縮寫,一種被廣泛使用且開放原始碼通用型的腳本語言,特別適合用於WEB開發並且可以嵌入到HTML中。其語法接近於C、Java和Perl,簡單易學。主要目的是允許WEB開發者可以快速地寫出動態生成的網頁,但是你可以用它做更多的事情。

舉個例子說明嵌入到HTML中
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <title>Example</title>
    </head>
    <body>
        <?php
            echo "Hi, I'm a PHP script!";
        ?>
    </body>
</html>

用C或Perl來寫網頁必須使用大量的命令輸出HTML,而使用PHP,你只需要將代碼嵌入到HTML來做某些事情,在這個例子中將會輸出Hi, I'm a PHP script!,PHP程式碼用特殊的開始標籤""包住,允許進出PHP模式。

PHP與用戶端JavaScript的差異在於程式碼在伺服器端執行後,產生HTML並且送到客戶端,客戶端接收該腳本執行完的結果,並且不會知道相關的程式碼是什麼。你甚至可以設定你的網頁伺服器使用PHP處理所有的HTML檔案,這樣用戶就無法知道你做了什麼。

PHP最大的好處是它對於初學者是非常地簡單,也對專業程式設計師提供了許多進階功能。當你閱讀長長的PHP功能清單不要害怕,你可以在短時間入門並且在幾個小時內寫些簡單的腳本。