PHP 語言

PHP 學習的筆記

規範

關鍵字區分大小寫
保留字不區分大小寫
敍述以分號;結束
<? statements ?> 或 <?php statements ?>
命名與引用之變數前綴 $
數值只有有號數,無無號數
變數 $a=10;$b=&$a;$b=11;則 $a==11
變數 $a=10;$b=“a”;則 $$b==10
用 define();定義常數,例如 define(“PI”, 3.14);

註解

註解

/**/
//
#

整數

整數

類型表示法數值
十進制1111
八進制01210
十六進制0x1319
二進制0b113
整數大小/bytesPHP_INT_SIZE
最小整數PHP_INT_MIN
最大整數PHP_INT_MAX

浮點數

小數點後有效位數 14 位,超過者四捨五入。或用 E or e 表示 10 為底的指數

布林

空字串“”與 NULL 都會轉成 FALSE,也就是說布林變數沒有 NULL
反向地 NULL 可轉成 FALSE,整數 0,空字串

字串

單引號內只有“\”有轉義效力可轉單引號及自身成一般符號。
雙引號內的轉義符號:\n, \r, \t, \v, \e, \f, \\, \$, \”
可用 $str[i] 存取字串中個別元素。陣列索引值從 0 開始。

檢查資料類型函式

資料類型檢查函式

函式作用
gettype(arg)回傳值:integer, double, boolean, string, NULL, resource, array, object, unknown type
is_integer(arg), is_int(arg), is_long(arg),
is_float(arg), is_real(arg),
is_bool(arg)
is_string(arg)
is_null(arg)
is_resource(arg)
is_array(arg)
is_object(arg)
is_numeric(arg)數值或數值字串

資料轉型函式

資料轉型函式

函式說明
settype(var, type)integer, double, boolean, string, NULL, array, object,
變數自身就會轉型。例如 settype($var, "integer");
intval(var), floatval(var), strval(var),變數自身就會轉型。
變數處理:
isset(arg)若 arg 不是 NULL 回傳 TRUE,NULL 回傳 FALSE
unset(arg)設成 NULL(釋放 arg 的變數空間配置)
empty(arg)除了是 not isset(arg)/NULL 回傳 TRUE 外,0 值也會回傳 TRUE

基本函式

echo($str1, $str2, ...); // 無回傳值
print($str); // 回傳 1 表成功,0 表失敗
var_dump($var1, $var2, ...); // 會輸出型別及值
exit($var) or die($var); // 若 $var 介於 0~254,其用於表示錯誤碼,程式結束時用於指示。若 $var 是字串,將於前台輸出。
以下是後來追加的。
我們常會想要知道運算的結果,其存於某變數或陣列。但用筆者目前已知的函式覺得不就手,故就寫個簡單的 print 函式,應可更簡便的運用:只要一個函式呼叫就可印出多個變數並且可換行及可以 HTML 格式(即轉了換行及空格)輸出。

function Mypr(...$objs){
// print variables.
// variables can't be yv/nv/nl
// invoke ex: mypr("nv", $var1, "nl", "yv", $var1, "nl");
// nl: put a new blank line
// nv: default, newline and space are not literally visable.
// yv: !nv, for HTML printing.

static $ident=0;
static $space="&nbsp;";
static $newline="<br>";

foreach ($objs as $var)

switch (gettype($var)){
    case "integer":
    case "double":
    case "string":
        switch ($var){
            case "yv": // default is nv; nonvisible ifs;
                $space="&amp;nbsp;";
                $newline="&lt;br&gt;";

                // note: "continue 2" will continue foreach
                // which is second layer loop
                continue 2;
            case "nv":
                $space="&nbsp";
                $newline="<br>";
                continue 2;
            case "nl":
                print("<br>");
                continue 2;
            default:
        }
    case "boolean":
    case "NULL":
        print(str_repeat($space, $ident).gettype($var).": (".$var.")".$newline);
        break;
    case "array":
        print(str_repeat($space, $ident)."array(".sizeof($var)."){".$newline);
        $ident+=4;
        foreach ($var as $i => $x){
                print(str_repeat($space, $ident).gettype($i)."[".$i."]=".$newline);
                $ident+=4;
                Mypr($x);
                $ident-=4;
        }
        $ident-=4;
        print(str_repeat($space, $ident)."}".$newline);
        break;
    default:
        print(gettype($var)." is non-printable.".$newline);
    }
}

用法範例如下:
Mypr("start", "nl");

$yt=array();
$xt=array(1.8, true, $yt, "i" => 0x3c, NULL, 49, "ttty");

Mypr(NULL, "yv", $xt, false, "nv", "nl", $yt, "nl", array(2, $xt, "3"));

Mypr("done", "nl");

輸出結果:
string: (start)

NULL: ()
array(7){<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[0]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;double: (1.8)<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[1]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean: (1)<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[2]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;array(0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;string[i]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;integer: (60)<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[3]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL: ()<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[4]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;integer: (49)<br>&nbsp;&nbsp;&nbsp;&nbsp;integer[5]=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string: (ttty)<br>}<br>boolean: ()<br>
array(0){
}

array(3){
    integer[0]=
        integer: (2)
    integer[1]=
        array(7){
            integer[0]=
                double: (1.8)
            integer[1]=
                boolean: (1)
            integer[2]=
                array(0){
                }
            string[i]=
                integer: (60)
            integer[3]=
                NULL: ()
            integer[4]=
                integer: (49)
            integer[5]=
                string: (ttty)
        }
    integer[2]=
        string: (3)
}
string: (done)

變數

變數

PHP 中只有變數與常數有區分大小寫
預定義變數$GLOBALS, $_SERVER, $_ENV, $_COOKIE, $_GET, $_POST, $_REQUEST, $_SESSION, $_FILES, $php_errormsg, 這些稱為超全域 superglobal,即不用宣告即可引用。

$GLOBALS[$user_defined_global_var] 引用 $user_defined_global_var 這個變數

$_SERVER["PHP_SELF"] 回傳當前文件的定位,URI。原則上就是 document-root 之後的路徑。例如 /index.php
$var2=&$var1$var2 代表著 $var1, 改變其一另一會跟著變
變數 $A 可存放另一變數 $B 的名稱, 並以變數 A 來代表變數 B 的方式, 用法為 $$A 跟 $B 同義:$A=B; $$A is $B;

常數

常數

define(name_string, value[, case_insensitive])第三個參數不寫為 FALSE,TRUE 表示不分大小寫
預定義常數PHP_VERSION, PHP_EOL, E_ERROR, E_WARNING
預定義變數__LINE__, __FILE__, __DIR__, __FUNCTION__, __CLASS__, __METHOD__, __NAMESPACE__

運算子

arithmetic+, -, *, /
%
** 次方
array
+ 相加時能確定重覆的 key 保留哪個?不建議使用。
== (key, value)全同時
!= 相反於 ==
=== 不止 (key, value),型別也須相同外,時序也須相同但啥是時序?不建議使用。
!== 相反於 ===
<> 同 !=

似乎只有 == 用起來無疑慮。詳見陣列的範例說明即可知其所以然。
assignment
=, +=, -=, *=, /=, %=, **=, &=, |=, ^=, <<=, >>=,
.=
bitwise
~, &, |, ^, <<, >>

comparison
==, !=, >, >=, <, <=,
<> 同 !=
=== 型別也須相同
!== 或若型別不同
<=> 具回傳值 -1, 0, 1
?? 由左而右取值傳回第一個非 NULL 的(變數)值
conditional? :
error control@ 加在句子前,若句子産生出錯誤訊息忽略之。
execution`` shell 的指令執行
increment/decrement++
--
logical!, &&, ||,
and, or, xor (優先權較上列低)
string. 點/前後合併

流程控制

1. if, else, for, while, do-while, switch, break, continue 之結構與用法與 C lang 同。
增加之關鍵字 elseif

2. 但是 break, continue 有新增用法:break 是跳出當前首先遇到的 {} 區塊或迴圈,continue 則是返回首先遇到的迴圈,故二者並非完全對等的。故 PHP 定義,break number,continue number 表示跳往/離的對象是 {} 巢狀的第幾層,預設是 break 1; continue 1; break; continue; 因此更外層的便逐層遞增 continue 2; 以此類推。

3. foreach
3.1. foreach ($array_var as $value){};
3.2. foreach ($array_var as $key => $value){};

陣列

1. PHP 的陣列是 ordered associative array. (待考證,vector/list/map 可能混用)
2. 陣列的索引限於整數值或字串. 因是關聯式陣列,故不再稱索引,而是鍵值 key.
3. 陣列的引用可以使用 [] 或 {},但建議只用 []. 單純這樣宣告 $arr[]; 是不合法的. 那麼我們如何宣告一個空陣列:$arr=array();
4. 若使用 NULL 作為 key,NULL 將被轉成空字串. 將 NULL 轉為陣列,將得到一個陣列是 NULL.
5. 以單一元素來初始化一個陣列,陣列將只有一個元素且其鍵值為 0.
6. 以此型式賦值,$arrvar[]=100; 其所建立的 key 值將會是此陣列的所有 key 當中的最大非負整數+1 斥或 0.
7. 陣列的 key,若指定時是字串並且是正整數字串或 "0", 將自動被轉成正整數值或 0. 若是浮點數值, 則規則是自動無條件捨位轉成整數值. 例如 key = ,-2.3 to -2, -2.8 to -2, 9.8 to 9, "0" to 0, "3" to 3, "-3" still "-3", "3.8" still "3.8", "+8" still "+8"
8. 陣列的加入元素是有序的,即有先後加入的順序/時序之分, 其用意未明.
9. 陣列的初始化
9.1. 直接指定 key 及其值,$arrvar[3]=1;
9.2. 使用 array(),例如 $arrvar=array(); 則生成一 $arrvar 空陣列.
9.3. 使用 array(val1, val2, ...),key 將從 0, 1, ..., 開始遞增.
9.4. 完整定義 $arrvar=array(key1 => val1, key2 => val2, ...);
9.5. define(arrvar_name_str, [val1, val2, ...]);
9.6. 陣列的複製:$var=$arr,則 $var 將與 $arr 一樣擁有相同對應的鍵值與元素
10. 多維陣列
10.1. $arr[0]=1; 實際是 $arr[0] 定址到 1 這個元素所存放的空間;該空間存放著元素值 1.
10.2. 二維陣列就是有一個一維陣列 Bj,其每個元素代表著不同的一維陣列 Ck(定位到該陣列 Ck,而非存放這整個陣列 Ck. 即存放著定址值);三維陣列就是有一個一維陣列 Ai,其每個元素代表著不同的二維陣列 Bj(的定址值);以此類推. 以三維為例,$cubic[i][j][k];$cubic[i] 定址到某一個二維陣列,其中的某元素 $cubic[i][j] 定址到某一個一維陣列,其中的某元素由 $cubic[i][j][k] 定址到,$cubic[i][j][k] 即代表著該空間存放著該元素值。最低維陣列所存放的是元素值(和鍵值),往上發展的各階高維度陣列,都是一維陣列,仍存放著元素值(和鍵值),只不過其元素值都是它的次一階的陣列的定位索引值/定址值。
10.3. 若 $plane['a'][0]='hello'; $plane['b']=100; 則例如 $plane['b'][0]=101; 不再合法,因為 $plane['b'] 已定址到單一整數 100 而不再是某另一陣列。如此我們可以重新賦值 $plane['b']=array(101); 來改變,將變成 $plan['b'][0]=101;
10.4. 使用 array() 賦值,以二維為例:$plane=array(array(1, 2), array(6, 7, 8, 9)); 那麼我們可以定址到元素值例如 $plane[0][1]=2; $plane[1][0]=6; $plane[1][3]=9;
11. 陣列的運算子:已補充在前面的運算子.
12. 以下範例務必明白.
# 以下為範例,test1

$arr=array();
$arr1=array(NULL);
$arr2[]=NULL;
$arr3=array(NULL => NULL);
$arr4=array(-2.3 => 1, -6.8 => 1, 9.8 => 1, "12" => 1, "-12.3" => 1, "15.8" => 1, "+8" => 1);
$arr5['a'][0]='hello'; $arr5['b']=100; $arr5['b']=array(101);
var_dump($arr, $arr1, $arr2, $arr3, $arr4, $arr5); echo("<br>");

######################### 輸出結果:
# array(0) { } array(1) { [0]=> NULL } array(1) { [0]=> NULL } array(1) { [""]=> NULL } array(7) { [-2]=> int(1) [-6]=> int(1) [9]=> int(1) [12]=> int(1) ["-12.3"]=> int(1) ["15.8"]=> int(1) ["+8"]=> int(1) } array(2) { ["a"]=> array(1) { [0]=> string(5) "hello" } ["b"]=> array(1) { [0]=> int(101) } } 

$a = array("apple", "banana");
$b = array("banana", "apple");
$c = array(1 => "banana", 0 => "apple");
$d = array(1 => "banana", "0" => "apple");
$e = array("1" => "banana", "0" => "apple");
$f = array("1" => "banana", 0 => "apple");

var_dump($a == $b); // f
var_dump($a === $b); // f
var_dump($a == $c); // t
var_dump($a === $c); // f
var_dump($b == $c); // f
var_dump($b === $c); // f
echo("<br>");
var_dump($a == $d); // t
var_dump($a === $d); // f
var_dump($a == $e); // t
var_dump($a === $e); // f
var_dump($a == $f); // t
var_dump($a === $f); // f
echo("<br>");
var_dump($c == $d); // t
var_dump($c === $d); // t
var_dump($c == $e); // t
var_dump($c === $e); // t
var_dump($c == $f); // t
var_dump($c === $f); // t
echo("<br>");

$arr = array(5 => 1, 12 => 2); $arr[] = 56; $arr["x"] = 42;
$arr1=$arr;

var_dump($arr); echo("<br>");
var_dump($arr1); echo("<br>");

var_dump($arr == $arr1); var_dump($arr === $arr1); // tt

unset($arr[5]); // This removes the key and element from the array

var_dump($arr); echo("<br>");
$arr[5] = $arr1[5];
var_dump($arr); echo("<br>");

var_dump($arr == $arr1); var_dump($arr === $arr1); // tf

echo("<br>"); echo("done");

######################### 輸出結果:
bool(false) bool(false) bool(true) bool(false) bool(false) bool(false)
bool(true) bool(false) bool(true) bool(false) bool(true) bool(false)
bool(true) bool(true) bool(true) bool(true) bool(true) bool(true)
array(4) { [5]=> int(1) [12]=> int(2) [13]=> int(56) ["x"]=> int(42) }
array(4) { [5]=> int(1) [12]=> int(2) [13]=> int(56) ["x"]=> int(42) }
bool(true) bool(true) array(3) { [12]=> int(2) [13]=> int(56) ["x"]=> int(42) }
array(4) { [12]=> int(2) [13]=> int(56) ["x"]=> int(42) [5]=> int(1) }
bool(true) bool(false)
done

創造 PHP 語言其初意是要針對網頁的後端處理,其大部份都是字串的處理,來對於其增/刪/修/示/合併/分類/比對/查詢/複製/轉換等。也因此陣列會有如此鬆散雜亂的結構/應是說具廣度延展的面向,使能夠輕易地處理各種不同的資料型態像整數,浮點數,字串等。也因此存取效率上就相對有所取捨之餘,或許避免對於陣列的程控處理/過多運算外,應是可盡量使用其提供的陣列處理函式或使用 foreach 做(也盡量避免重覆的)巡訪。故相形之下,陣列函式就相對重要了故列於下。

陣列處理函式

 點擊此開啟全頁模式以方便閱讀 

陣列處理函式

函式說明測試結果
is_array(arg)T/F
(註:官方文件中的定義與說明並非很詳盡故我們還是必需先行測過瞭解其底細才能 bugfree 地運用之)
count(arr);
sizeof(arr);
int. 回傳陣列中元素總數
in_array(value, arr)T/F. 陣列中是否有元素值 == value
# 以下為範例,test2
$arr=array(
'a' => "6", 'b' => "xx",
-8 => "yy", -3 => 7
);
$arr[]="zz";
var_dump($arr);
echo(
"<br>".count($arr)."<br>".
sizeof($arr)."<br>".
in_array(6, $arr)."<br>"
);
array(5) {
  ["a"]=>
  string(1) "6"
  ["b"]=>
  string(2) "xx"
  [-8]=>
  string(2) "yy"
  [-3]=>
  int(7)
  [-2]=>
  string(2) "zz"
}

5
5
1
array_values(arr)array. 回傳由 arr 的元素值所組成的陣列
array_search(value, arr)key. 回傳首次找到的 arr 的元素值 == value 的 key 值
(註:php 提供很多這樣的函式,實用性可能不大)
# 以下為範例,test3
$arr=array('a' => "6", 'b' => "xx", -8 => "yy", -3 => 6);
$arr[]="zz"; $arr[]="6"; $arr['c']=6; $arr[]=-6.5;
var_dump($arr);
$rval=array_search(6, $arr);
echo("<br>"."the result is: ");
var_dump($rval);
array(8) {
  ["a"]=>
  string(1) "6"
  ["b"]=>
  string(2) "xx"
  [-8]=>
  string(2) "yy"
  [-3]=>
  int(6)
  [-2]=>
  string(2) "zz"
  [-1]=>
  string(1) "6"
  ["c"]=>
  int(6)
  [0]=>
  float(-6.5)
}

the result is: string(1) "a"
array_sum(arr)numeric. 回傳陣列所有元素中的整數/浮點數/數值字串的總和
# 以下為範例,test4
$arr=array('a' => "6", 'b' => "xx", -8 => "yy", -3 => 6);
$arr[]="zz"; $arr[]="6"; $arr['c']=6; $arr[]=-6.5;
var_dump($arr);
$rval=array_sum($arr);
echo("<br>"."the result is: ");
var_dump($rval);
array(8) {
  ["a"]=>
  string(1) "6"
  ["b"]=>
  string(2) "xx"
  [-8]=>
  string(2) "yy"
  [-3]=>
  int(6)
  [-2]=>
  string(2) "zz"
  [-1]=>
  string(1) "6"
  ["c"]=>
  int(6)
  [0]=>
  float(-6.5)
}

the result is: float(17.5)
array_unique(arr)array. 回傳一新陣列,其保留來源陣列中首次出現的元素(及其鍵)。重覆出現的元素(及其鍵)都不會被寫入新陣列中。
運算中元素會有型別轉換,即 6/"6"/6.1 視為相同
# 以下為範例,test5
$arr=array(
'a' => "6", 'b' => "xx", -8 => "yy",
-3 => 6, 0 => "zz", 1=> "6", 2 => 6,
4.5 => 6.0, 3.5 => -5.8
);
echo("<br>"); var_dump($arr);
$rarr=array_unique($arr);
echo("<br>"); var_dump($arr);
echo("<br>"); var_dump($rarr);

array(9) { ["a"]=> string(1) "6" ["b"]=> string(2) "xx" [-8]=> string(2) "yy" [-3]=> int(6) [0]=> string(2) "zz" [1]=> string(1) "6" [2]=> int(6) [4]=> float(6) [3]=> float(-5.8) }
array(9) { ["a"]=> string(1) "6" ["b"]=> string(2) "xx" [-8]=> string(2) "yy" [-3]=> int(6) [0]=> string(2) "zz" [1]=> string(1) "6" [2]=> int(6) [4]=> float(6) [3]=> float(-5.8) }
array(5) { ["a"]=> string(1) "6" ["b"]=> string(2) "xx" [-8]=> string(2) "yy" [0]=> string(2) "zz" [3]=> float(-5.8) }
shuffle(arr)將 arr 中的元素值次序打亂,key 值不異動例如 [0 => 'a', 1 => 'b', 2 => 'c'] 變成
[0 => 'c', 1 => 'a', 2 => 'b']
array_push(arr, arg1[, arg2, ...]);
array_pop(arr);
array_shift(arr);
stack/queue 堆疊佇列結構的操作。
建議以新陣列及堆疊佇列目的及單純針對元素值,來使用而非所有陣列都來使用它。

array_push:在某時刻推入(存入)陣列

$val=array_pop:將最遲推入陣列的元素移出

$val=array_shift:將最早推入陣列的元素移出

注意這三個函式反覆操作時,key 值一直都保持從 0 開始遞增至 (目前元素個數-1) 結束,即它是按時間序生成的陣列。因此我們可以例如 for ($i=0; $i<元素個數; $i++) 來巡訪整個陣列。
# 以下為範例,test6
$arr=array();
array_push($arr, "6");
array_push($arr, "xx");
array_push($arr, "yy");
$val1=array_pop($arr);
$val2=array_pop($arr);
array_push($arr, 6);
array_push($arr, "zz");
$val3=array_shift($arr);
array_push($arr, "6");
array_push($arr, 6);
array_push($arr, 6.0);
array_push($arr, 5.8);
echo("<br>"); var_dump($arr);
echo("<br>".$val1."<br>".$val2."<br>".$val3."<br>");
shuffle($arr);shuffle($arr);shuffle($arr);
echo("<br>"); var_dump($arr);

array(6) { [0]=> int(6) [1]=> string(2) "zz" [2]=> string(1) "6" [3]=> int(6) [4]=> float(6) [5]=> float(5.8) }
yy
xx
6

array(6) { [0]=> int(6) [1]=> int(6) [2]=> string(2) "zz" [3]=> float(6) [4]=> float(5.8) [5]=> string(1) "6" }
asort(arr);
arsort(arr);
ksort(arr);
krsort(arr);
將 arr 排序,分為升/降冪及鍵/元素來排。而 (鍵, 元素) 會保持它們的對應關係

asort: 對元素值升冪排序
arsort: 對元素值降冪排序
ksort: 對鍵值升冪排序
krsort: 對鍵值降冪排序
 點擊此開啟全頁模式以方便閱讀 
  • 結論
  • 網站/網頁是屬於資訊內容的提供者。故作為網站後端語言的 PHP 是資訊處理導向而非資訊運算導向。因此 PHP 所定義實作的陣列,其使用的資料結構與演算法及其處理的對象與使用方法便有其適用的層面:我們不可能拿 PHP 來計算列出 10^100 中的所有質數或計算圓周率小數點後 10^100 位的數字為何。換個方式講 PHP 非通用運算平台,故我們更有理由需瞭解到 PHP 背後使用的資結演算,才能適度地使用它,但這似乎是有難度的(官方並無此方面直接的文件:與其瞭解它不如規矩地使用它/即做一般資訊處理)。故,在持續摸索 PHP 的路上,至少目前我們針對以上對陣列的嘗試得到我們或許需最小程度地運用它而有以下原則:
  • 礙於資料型別的轉換,上列陣列函式已篩選過;盡量少用不確定性的函式。
  • 取而代之是使用 foreach 巡訪陣列並透過程設方式減少巡訪次數。
  • 陣列的運算子,除了使用 ==,不建議使用其它的。若有疑問請詳看上列那些範例:資料加入陣列是有時序性的,可能易導致非預期結果。
  • PHP 除了陣列這一種資料結構,還提供哪些目前筆者未知。我們不免會遇到需即時處理資料流,可能就會用上 push/pop/shift:避免大量使用 shift,因有可能耗用不少運算資源,而 push/pop 倒是無妨。

函式

1. 觀念與 C lang 相似,於此列出差異
2. 函式定義:
[$var=]function [func-name]([arg1, arg2, ...])[: return-data-type]{[body]}
2.1. 一般定義:
function func-name([arg1, arg2, ...]){[body]}
2.2. 含資料回傳型態的定義:
function func-name([arg1, arg2, ...]): return-data-type{[body]}
2.3. 䁥名定義:
$var=function ([arg1, arg2, ...]){[body]}
呼叫時 $var([param1, param2, ...]);
2.4. 函式內的函式:很彈性,但略. 註:像這個“XYZ PHP code”外掛,才得以實現。
2.5. 可在條件內定義:那麼只有條件成立後函式才有被定義出來。否則呼叫會造成錯誤。
2.6. 未定引數/任意個數:
function func-name(...$var){[body]}
則 $var 是一個陣列依序存放參數藉以引用,例如 foreach ($var as $parami)
2.7. 可設置參數的預設值
2.8. 可限制引數的資料型別,例如:
function func-name(int arg1, float arg2){[body]}
2.9. 靜態變數:static
2.10. 函式內的全域變數,只能宣告,無法定義:
要引用須宣告,否則不可見:global $var;
且只能使用這樣的宣告。要指定值再另行指定。即 global $var=1; 這樣是不合法的。
2.11. 函式變數:將函式名稱字串指定給一變數再透過變數引用。
function a(...){...}
$b="a";
$b(...);
3. 以上用法都是 call by value,即,所引用的參數變數會複製一份進入函式內,函式內對該份複製做任何操作都不影響函式外那份變數. 若要變數本尊進入函式內,則使用 call by reference,如前面在“變數”提過,$a=&$b; 則 $a 將參考到 $b,對 $a 更動等於對 $b 更動反之亦然。我們可以視 $a 與 $b 全同;但仍有差異,a 的生命週期不比 b 長。因此反之我們可以實驗如下:
sorry 先說完函式,故其 call by reference 定義如下:

function func-name(&$var[, arg2, ...]){[body]}
呼叫的方式沒有不同:func-name($param[, ...]);
換言之,不能傳常數,會錯誤。
另外:
不論函式定義為何,這種呼叫是不合法的:func-name(&$param[, ...]);

實驗,若 $b 參考到 $c 但 $c 的生命週期較 $b 短,結果如何:

<?php
    $b=9;
    $aa=1;
    $bb=2;
    $Msg="Hello, world!";

    function myswap(&$v1, &$v2){

            global $b;
            $c=&$v1;
            //$c=3;
            echo("<br>b = [".$b."]<br>");
            $b=&$c;

            echo("before swap:".$v1."   ".$v2."   ".$b."   ".$c."]<br>");
            $tmp=$v1;
            $v1=$v2;
            $v2=$tmp;
            echo("after swap:".$v1."   ".$v2."   ".$b."   ".$c."]<br>");
            $b=6;
            global $Msg;
            $Msg="Hello, PHP!";
    }

    myswap($aa, $bb);
    echo("aa, bb, b are:".$aa."   ".$bb."   ".$b."   ".$Msg."]<br>");
?>

結果如下,why? 問作者.

b = [9]
before swap:1 2 1 1]
after swap:2 1 2 2]
aa, bb, b are:6 1 9 Hello, PHP!]

數學常數

數學常數

常數代表
M_PIpi
M_1_PI1/pi
M_Ee
M_LOG2Elog2(e)
M_LOG10Elog10(e)
M_LN2loge(2)
M_LN10loge(10)
M_LNPIloge(pi)
M_EULEREuler Constant
M_SQRT2sqrt(2)
M_SQRT3sqrt(3)

數學函數

數學函數

函數說明
y=abs(x);絕對值
y=sin(x); cos(x); tan(x);
asin(x); acos(x); atan(x);
x,徑度
y=bindec(x); decbin(x);
hexdec(x); dechex(x);
二進制轉十進制;十進制轉二進制
十六進制轉十進制;十進制轉十六進制
y=ceil(x);
y=floor(x);
y=round(x[, precision]);
x 是實數,y 是 >= x 的最小整數
x 是實數,y 是 <= x 的最大整數
對 x 四捨五入。precision 是保留小數點後幾位,預設值是 0
r=fmod(x, y)取浮點餘數。x=qy+r,
其中 q 是整數且 0 <= r < abs(y) 且 qyr>=0
商是整數且餘數唯一
(實際操作就是正數運算結果再冠上正負號)
(檢驗 5/ 2.2, (q, r)=( 2, 0.6), ( 3, -1.6))
(檢驗 5/-2.2, (q, r)=(-2, 0.6), (-3, -1.6))
(檢驗 -5/-2.2, (q, r)=( 2, -0.6), ( 3, 1.6))
(檢驗 -5/ 2.2, (q, r)=(-2, -0.6), (-3, 1.6))
y=sqrt(x)平方根
y=pow(base, exp)base 的 exp 次方
y=exp(x)e 的 x 次方
y=log(x);
log10(x);
x 的自然對數
x 的底10對數
is_infinite(x);
is_nan(x);
此數是否無限
此數是否無法表示
y=max(x1, x2[, x3, ...]); max(arr1[, arr2, ...]);
min(x1, x2[, x3, ...]); min(arr1[, arr2, ...]);
當中的最大值
當中的最小值
y=getrandmax()獲取最大亂數值,假設叫 RANDMAX
y=rand([min, max])亂數産生器
若 y=rand(),則回傳 0 ~ RANDMAX 間之一整數
若 y=rand(-RANDMAX, RANDMAX),則回傳 -RANDMAX ~ RANDMAX 間之一整數

時間函式

 點擊此開啟全頁模式以方便閱讀 

時間函式

函式說明
y=time()回傳此時的 timestamp,形式如 1582630397,十位數字
arr=getdate([int timestamp])回傳目前時刻的時間陣列如下例。有帶參數的話是轉換 timestamp 參數
例如:
echo(implode(" ", getdate());
輸出:
17 33 19 25 2 2 2020 55 Tuesday February 1582630397
依序是:
秒 分 時 日 星期幾 月 公元 第幾天/當年 星期幾/英 幾月/英 timestamp
對應的 key 值是:
seconds minutes hours mday wday mon year yday weekday month 0
timestamp=mktime(24制時, 分, 秒, 月, 日, 西元)
new_timestamp=strtotime(op-str[, timestamp])
按照字串 op-str 的指示調整 timestamp 並回傳調整後。若不接 timestamp 會以目前時間來處理。例如:
$new_time=strtotime("+30 second +5 minute -3 hour +10 day -2 week +30 month -9 year");
使用單複數皆可例如,second/seconds。
萬年後的今天:$new_time=strtotime("+10000 years");

兩點注意:
1. 可使用但沒建議用 this Sat/next Sun/last Mon
2. 除了使用指示 +- month,會有進退位的問題,例如3月31日的上個月,應為31日,但若只有28日會進到3月3日。其他指示無疑慮。

例子:
echo(mktime(12, 00, 00, 2, 28, 2019)."<br>");
echo(mktime(12, 00, 00, 3, 3, 2019)."<br>");
$t=mktime(12, 00, 00, 7, 31, 2019);
$tmod=strtotime("-5 months", $t);
echo($tmod."<br>");
echo(implode(" ", getdate($tmod))."<br>");
輸出:
1551326400
1551585600
1551585600
0 0 12 3 0 3 2019 61 Sunday March 1551585600
備註:伺服器上的 PHP 是被動的,只當載入網頁內容時才會跑程式碼。故例如排程等,對時間的處理或許由前端做較為恰當。
扯到日期又會想到萬年曆,練習如下:
$year=2020; // this year
$month=2; // this month

// a pattern of 365-day of a year
$pattern=array(0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

// ought to premium this year?
if (!($year%4) && ($year%100 || !($year%400))) $pattern[2]++;

// how many days premiumed till last year
$premium_days=($year-1)/4-($year-1)/100+($year-1)/400;

// how many days past till last year since Birth of Christ
$last_year_past=365*($year-1)+$premium_days;

// days till last month so far
for ($i=1; $i<$month; $i++) $last_year_past+=$pattern[$i];

// the weekday of the last day of last month
$weekday=$last_year_past%7; // sunday if 0
echo ("Sn_Mn_Tu_Wd_Th_Fr_St_<br>");
if (++$weekday>=7) $weekday=0; // 1st day this month
echo(str_repeat("___", $weekday));
for ($i=1; $i<=$pattern[$month]; $i++){
echo(($i<10? "0": "").$i."_");
if (++$weekday>=7){
$weekday=0;
echo("<br>");
}
}

結果:
Sn_Mn_Tu_Wd_Th_Fr_St_
__________________01_
02_03_04_05_06_07_08_
09_10_11_12_13_14_15_
16_17_18_19_20_21_22_
23_24_25_26_27_28_29_
 點擊此開啟全頁模式以方便閱讀 

字串函式

 點擊此開啟全頁模式以方便閱讀 

字串函式

函式說明
str=strtolower(str);
str=strtoupper(str);
str=ucfirst(str);
str=ucwords(str);
轉成全小寫;
轉成全大寫;
只有第一個 word 字首大寫;
每個 word 字首大寫;
str=strrev(str)將字母順序反過來,例如 ”abc“ 變 ”cba“
int=ord(str);
str=chr(int);
回傳第一個字母的 ascii code;
回傳 ascii code 對應的字元;
int=strcmp(str1, str2);
int=strcasecmp(str1, str2);
int=strncmp(str1, str2, int length);
int=strncasecmp(str1, str2, int length);
回傳 y,y小於0 若 str1小於str2;y等於0 若 str1等於str2;y大於0 若 str1大於str2;
區分大小寫;
同前,只是多加了限定比較的長度
str_r=str_replace(str_old, str_new, str_src[, count]);
str_r=str_ireplace(str_old, str_new, str_src[, count]);
在 str_src 中尋找所有 str_old 並取代成 str_new,區分大小寫;
不分大小寫符合 str_old;
count:已執行取代的次數;

ex:
$r=str_ireplace("gf", "AA", "hphagfruhgFaiudhgfuihaurfa", $cnt);
echo($r."_".$cnt."<br>");
result:
hphaAAruhAAaiudhAAuihaurfa_3
str_r=strchr(str_src, str_pattern);
str_r=strstr(str_src, str_pattern);
str_r=stristr(str_src, str_pattern);
str_r=strrchr(str_src, str_pattern);
在 str_src 中搜尋,回傳第一次出現 str_pattern 及接於其後的子字串;
strstr 同 strchr;
stristr 不區分大小寫,i 表 ignore;
回傳最後一次出現的 pattern 及其後字串;
[int, false]=strpos(str_src, str_pattern[, offset]);
[int, false]=stripos(str_src, str_pattern[, offset]);
回傳 pattern 首次出現的絕對位置。offset:略過幾個位置才開始搜尋,預設0表略過0個位置;
不分大小寫;
註:沒找到回傳 false 造成困擾因和位置0相同。我們用 is_bool 來判別。
追加:忘了還有 === 可用
故可以實例如下:
$str_src="999999999";
$str_pattern="9";

for (
$pos=0;
!is_bool(
$pos=strpos($str_src, $str_pattern, $pos)
);
$pos++
) echo("found at: ".$pos."<br>");
結果:
found at: 0
found at: 1
found at: 2
found at: 3
found at: 4
found at: 5
found at: 6
found at: 7
found at: 8

我們若把 pattern 換成 "99":
found at: 0
found at: 1
found at: 2
found at: 3
found at: 4
found at: 5
found at: 6
found at: 7
故把 $pos++ 換成 $pos+=length(pattern) 更實際
int=strlen(str)回傳字串長度;“abc”長度 3
str_new=str_repeat(str_src, int times)回傳 str_src 重覆 times 次的新字串;("ab", 3)會是 "ababab"
str=substr(str, int pos[, length])回傳從 pos 位置開始的子字串,不指定長度就是至結尾;
例如 ("0123456", 3, 2)會是 "34"
str=ltrim(str_src, str_charlist);
str=rtrim(str_src, str_charlist);
str=trim(str_src, str_charlist);
由左而右依序刪除在 str_charlist 中出現的字元,一遇到不符合便終止;
由右而左;
兩端;

例如:
echo(trim("-=--==--==-v=545454-v=-=-=-=-=-=--==", "-=")."<br>");
結果:
v=545454-v
str=nl2br(str)將換行字元 \n, \r, \n\r 代換成 HTML 的<br>
str=implode(str_separator, arr);
arr=explode(str_separator, str[, int count]);
將陣列中的各元素依序藉 str_separator 連接彼此成一字串;
例如 array("ab", "c", "de") 和 “_”會成 "ab_c_de"

使用指定的 separator 來將 str 分解開並存入 arr;count 代表要分解出幾個子字串,可想而知分解不完的就當成最後一筆子字串。
例如 ("d1", "add1d1cdbddd1bd1cd1", 5) 成 array("ad", "", "cdbdd", "b", "cd1")
 點擊此開啟全頁模式以方便閱讀 

檔案存取函式

 點擊此開啟全頁模式以方便閱讀 

檔案存取函式

函式說明
str=basename(str_fullpath[, str_suffix])區分出路徑與檔名兩部份並且回傳檔名,即,取出文件名稱字串
str_fullpath:包含路徑與檔名的完整字串
str_suffix:檔名中(若有)的延伸檔名,將它也排除,例如 index.php,“.”點出了延伸檔名是 “php”
例子:
$fp 若完整路徑是 "/dat/myfile.php",則:
basename($fp) 得到 myfile.php
basename($fp, "php") 得到 myfile
arr=pathinfo(str_fullpath)
keys=
["dirname"]
["basename"]
["extension"]
["filename"]


例子:
若 $fp="/dat/myfile.php";
var_dump(pathinfo($fp));

array(4) {
["dirname"]=> string(4) "/dat"
["basename"]=> string(10) "myfile.php"
["extension"]=> string(3) "php"
["filename"]=> string(5) "myfile"
}
str=realpath(str_filename)回傳在當前目錄下(current directory/working directory),某文件在主機上的絕對路徑(即在同目錄的所有檔案都適用)

例子:
若主機當前目錄是 /var/web,底下有檔案 myfile.php,則:
realpath("myfile.php");
回傳 "/var/web/myfile.php"
str=getcwd()


當前的工作目錄路徑;
arr=scandir(str_path)該路徑的目錄內容;
bool=chdir(str_path)切換工作目錄至該路徑;
bool=mkdir(str_pathname[, int_mode[, bool_recursive]])建立資料夾;其中,
mode: 預設 0777,不過主機的預設權限有決定權。但後續我們仍可以 chmod 來修正成我們要的。
recursive: 路徑中不存在的資料夾是否一併建立。
bool=rmdir(str_dirname)刪除資料夾;空的才使刪。
bool=is_dir(str_dirname)是否為資料夾;
可使用絕對路徑,或工作目錄下的資料夾名稱。
bool=is_file(str_docname)是否為文件;
可使用絕對路徑,或工作目錄下的文件名稱。
bool=file_exists(str_filename)是否存在該檔案;
可使用絕對路徑,或工作目錄下的檔案名稱。
即,同時適用資料夾及文件。
bool=chmod(str_filename, int_mode)變更文件或資料夾權限模式,int_mode 使用 0iii 型式
bool=chgrp(str_filename, str_grp)變更群組,只能變更成使用者所屬的某群組
bool=chown(str_filename, str_usr)變更使用者,只有管理員才有此權限
bool=copy(str_src, str_dst)複製;既存的會被覆蓋。
(權限是始終會作用的)
bool=unlink(str_docname)刪除該文件;
可使用絕對路徑,或工作目錄下的文件名稱。
bool=rename(str_old, str_new)更名該文件。
測試的結果是不管文件擁有者是誰都可更改成功:因為此 rename 是由資料夾的權限 x 來限制的
(在 linux 下權限模式是重疊的,即 others 包含 group 包含 user;但 php 是沒交集的)
bool=is_readable(str_filename)檔案是否可讀取
bool=is_writable(str_filename)檔案是否可變更
int=filesize(str_docname)回傳文件大小(bytes)
timestamp=filectime(str_filename);
timestamp=filemtime(str_filename);
timestamp=fileatime(str_filename);
檔案建立時間;
檔案修改時間;
檔案存取時間;

建立時間不一定是(影響)修改時間,例如移動;但修改時間一定是(影響)建立時間(intrinsic);唯有開啟檔案,不管有無更動就一定影響存取時間,而移動並未開啟它故不影響存取時間。
str[, false]=file_get_contents(str_docname);
載入檔案;
bool=file_put_contents(str_docname, str);
寫入檔案;
若檔案已存在,其內容將先全數清除再做寫入;若不存在便新建及寫入。
接下來進入檔案的真正存取。檔案可以是固定有序的一段資料(因通常資料本身就是有序,例如一篇文章),也可以是一段串流。固定有序就表示於檔案內我們可以往後的位置,或往前的位置,或跳躍至某位置,來存取所在位置的資料。因此,便會有所謂的檔案指標記錄當前存取的位置。若扯到它,其使用上就相形複雜,故請參考 php 文件。以下只有關注在一次性存取,即,一個呼叫便把檔案內容全數讀取進變數。除非檔案異常大/百妹以上,不然應可應付一般需求了。
 點擊此開啟全頁模式以方便閱讀 

例外處理

1. 關鍵字 throw, try, catch

2. 例外物件 Exception, TypeError:
2.1. class Exception $ex
2.2. $ex->getMessage(); 錯誤訊息;
2.3. $ex->getCode(); 錯誤代碼;
2.4. $ex->getFile(); 産生例外的文件路徑;
2.5. $ex->getLine(); 哪一行發生例外;
2.6. class TypeError $te
2.7. $te->getMessage();
2.8. 其它例外物件:ArithmeticError, DivisionByZeroError, AssertionError(由 assert() 為 false 時), ParseError(語法錯誤).

3. 拋出例外物件:
3.1. throw new Exception(str_err_msg);

4. 嘗試可能會發生例外的區塊:
4.1. try {}

5. 攔截例外:
5.1. catch (Exception $ex){}

6. try {} catch (){} 是一組的

7. PHP 內建的錯誤回報訊息
7.1. 三種訊息:Notice, Warning, Error
7.2. function error_reporting([int_error_type]); 用來設置回報訊息的行為:
7.3. error_reporting(0); 關閉所有訊息報告
7.4. error_reporting(E_ALL); 回報所有種類訊息
7.5. error_reporting(E_ERROR | E_WARNING | E_PARSE); 一般需要的訊息
7.6. error_reporting(E_ALL ^ E_NOTICE); 回報除了 Notice 之外的所有訊息
7.7. 常用的訊息種類常數:E_ERROR, E_WARNING, E_NOTICE, E_ALL, E_USER_ERROR(使用於 trigger_error()), E_USER_WARNING(使用於 trigger_error()), E_USER_NOTICE(使用於 trigger_error()).

8. PHP 內建的錯誤記錄訊息
8.1. function error_log(str_msg[, int_msg_type[, str_dest[, str_header]]]);
8.2. str_dest(記錄的目的地)由 int_msg_type 來決定:0,預設值,web server 的 log file;1,郵件地址,可再由 str_header 做為郵件的標題;2,到某 ip address:port

9. 使用者自定義的錯誤處理機制
9.1. 通常將要先取消內建行為:error_reporting(0); 關閉所有訊息報告
9.2. 定義 error-processing callback function:function some_error_handler($error_number, $error_message, $docname, $line_number);
9.3. 呼叫,配置 error handler:invoke function set_error_handler(the_error-processing_callback_function);
9.4. 若要取消配置回復內建:invoke function restore_error_handler();
9.5. 使用者在自己程式內自觸發錯誤,使用 trigger_error(str_error_msg[, int_error_type]); 其中 int_error_type 就是 E_USER_ERROR 等那些常數。

類別

註:例如 A=B,筆者自己的習慣反常等號左右不加空格,理由是做 re 搜尋時表示法較簡單,及會真正去遍歴(大型專案)程式碼的人不多,反倒多是做搜尋。像 "){$" 就能找到所有的函式定義。

1. 定義

class class_name[ extends class_parent_name]{
    // 若無存取權修飾子 public 等,就是預設 public
    [[public|protected|private ]$property_name[=value];]
    [[public|protected|private ]function method_name(...){}]
    ...
    [const constant_name=value;] // 注意無 $.
    [static $static_var_name[=value];]
    [static function static_func_name(...){}]
    ...
}

2. 在 class 的定義內,存取成員:$this->member;

3. 建立/具現化物件:$obj=new class_name(); 或 $obj=new classname; // 不需帶建構參數時
3.1. 可在建立物件時就存取,例如 echo(new Arith($a, $b)->sum()); // 當然此物件應會馬上解構。優先權 new 比 -> 低,所以要加括號才對:echo((new Arith($a, $b))->sum());
3.2. echo((new class extends child1{function v(){return "v<br>";}})->v());

4. 存取物件內的成員:$obj->member;
4.1. 是可以無物件下存取類別中的:
4.1.1. 公開的,靜態變數/常數
4.1.2. 公開的靜態函式
4.1.3. 公開函式,但函式內無涉及物件資料成員和 $this 指標。
4.1.4. 故 php 語法沒嚴謹故必須實測,由語法錯誤來判別 php 的規範;例如抽象類別是無法實例化的,但其函式卻又有機會可以呼叫。
4.2. 抽象類別
4.2.1. 例子:
abstract class my_abs_object{
    abstract protected function print();
    public function pr(){echo("pr<br>");}
}
4.2.2. 抽象函式,表示不實作內容之函式宣告,但若有類別繼承自擁有該抽象函式的類別(此也須稱為成為抽象類別)就必須實作該函式內容。
4.2.3. 抽象類別不能實例化。
4.2.4. 抽象函式不能為 private。
4.2.5. 繼承的子類別的抽象函式實作,其存取權修飾子可以更寬鬆,例如從 protected 成 public。
4.2.6. 繼承的子類別的抽象函式實作的參數個數可以更多但要有預設值以符合抽象函式的原型。
4.3. 介面類別
4.3.1. 將類別關鍵字換成 interface 即成。
4.3.2. 只能有函式宣告或常數定義。所有函式都必須是 public
4.3.3. 使用介面,例如:
interface A{...}
class B implements A{...}
4.3.4. 介面可繼承。及可使用多個介面
interface A{...}
interface B{...}
interface C extends A, B{...}
class D implements A, B{...}
4.3.5. 介面就是在以上前題下之抽象類別。
4.4. 若要成為葉類別或葉函式而不要被繼承或覆載就在其前加上 final 關鍵字:final class,final function

5. 靜態變數常數或靜態方法的存取方式:
5.1 self::constant_name,
class_name::constant_name,
$this::constant_name(x),
$this->constant_name(x),
$this->$constant_name(x)
5.2. self::$static_var_name,
class_name::$static_var_name,
$this::$static_var_name,
$this::static_var_name(x),
$this->static_var_name(x),
$this->$static_var_name(x)
5.3. self::static_func_name(),
class_name::static_func_name(),
$this::static_func_name(),
$this->static_func_name()
5.4. 測試:
5.4.1. 相等性(沒建議常用)
class A{
    public static $u=0;
    public $v=1;
    protected function mod(){}
}

class B extends A{
    public function mod(){$this->v=9;}
}

function X($x){
    $x->mod();
    $x=NULL;
}

$a=new A;
$b=new B;
$c=new A;
var_dump($a==$b); echo("<br>");
var_dump($a==$c); echo("<br>");
var_dump($a===$b); echo("<br>");
var_dump($a===$c); echo("<br>");
B::$u++;
var_dump($a==$b); echo("<br>");
var_dump($a==$c); echo("<br>");
var_dump($a===$b); echo("<br>");
var_dump($a===$c); echo("<br>");

結果:
bool(false)
bool(true)
bool(false)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false) 

5.4.2. 承上,知其所以然儘管直觀上不然
X($b);
var_dump($b);
結果:
object(B)#2 (1) { ["v"]=> int(9) }
(why?請見 6.3. 結論二)

6. 建構子 __construct(...){},解構子 __destruct(){}。解構子最多一個,不帶參數。建構子可有零個以上,各自帶有不同參數。記得要加 function。
6.1. 例子:測試繼承與建解構子
class parent1{
    private static $cnt=0;
    private static $ida=0;
    private $id;
    protected $userid;

    function __construct(){
        $this->id=++self::$ida;
        self::$cnt++;
        $this->userid=$this->id;
        echo(self::class."[parent constructor ".$this->id."/".$this->userid." R(".self::$cnt.")]<br>");
    }

    function __destruct(){
        echo(self::class."[parent destructor ".$this->id."/".$this->userid." R(".--self::$cnt.")]<br>");
    }

    public function print(){
        echo("print parent class<br>");
    }

    public function setid($id){$this->userid=$id;}
    public function getid(){return $this->userid;}
}

class child1 extends parent1{
    private static $cnt=0;
    private static $ida=0;
    private $id;

    function __construct(){
        parent::__construct();
        $this->id=++self::$ida;
        self::$cnt++;
        echo(self::class."[child constructor ".$this->id."/".$this->userid." R(".self::$cnt.")]<br>");
    }

    function __destruct(){
        echo(self::class."[child destructor ".$this->id."/".$this->userid." R(".--self::$cnt.")]<br>");
        parent::__destruct();
    }

    public function print(){
        echo("print child class<br>");
    }
}

6.2. 結論一:
若父有預設建構子,子沒有,則建立子物件過程中會自動呼叫父預設建構子。然而若子也有預設建構子,則父的不再自動被呼叫,而是須(若需要的話)明白在子建構子中呼叫之。解構亦同。

6.3. 結論二:
$a=new child1;
$b=new child1;
$var_a=$a;
$var_b=&$b;

則 $var_a=NULL,斥或 $a=NULL 將不影響原物件。
$var_b=NULL,或 $b=NULL,原物件將被釋放解構。

$var_a,$a 都代表同一份物件,唯沒有變數代表該物件(例如 $var_a=NULL,$a=NULL 之後),該物件才會解構。
$var_b,$b 都代表同一份物件,唯任一變數解構(例如 $var_b=NULL),該物件就會解構。

// 測試:
$a=new child1;
$var_a=$a;
$var_b=&$var_a;

$var_b->setid(66);

echo($a->getid()."<br>");
echo($var_a->getid()."<br>");
echo($var_b->getid()."<br>");

$a->setid(77);

echo($a->getid()."<br>");
echo($var_a->getid()."<br>");
echo($var_b->getid()."<br>");

$var_a->setid(88);

echo($a->getid()."<br>");
echo($var_a->getid()."<br>");
echo($var_b->getid()."<br>");

// 分別嘗試這三個便可知其所以然
//$a=NULL;
//$var_a=NULL;
$var_b=NULL;

var_dump($a);
echo("<br>");
var_dump($var_a);
echo("<br>");
var_dump($var_b);
echo("<br>");

6.4. "$a=$b" 對陣列而言是複製一份出來,而對物件而言只是讓變數參考到同一份物件,假設有十個變數參考到同一份物件,則後續,任一變數都可被消除(=NULL),消除掉九個剩最後一個變數時,再消除之,物件才消除。因此物件用在函式參數時需特別留意。
6.5. 物件的複製要使用 __clone(){} 複製建構子
6.5.1. 在類別內定義 __clone() 函式,明白定義哪些 data 如何複製
6.5.2. 在類別的 __clone(){} 內使用 clone $instance; 來 piece-wise 複製物件的 data,或類別外也是使用 clone 作為物件的複製

7. 無名類別:直接在建立物件時定義與引用。
$obj=new class(參數){...};
請注意一定要有最後的分號;

8. 使用 class_name::class 或 self::class 可回傳類別定義名稱字串且包含命名空間。
8.1. 命名空間:
8.1.1. 對程式空間命名。根空間是“\”,接著可以階層式的字串建立子空間,目的以區隔相同名稱的變數,類別,函式等程式物件。例如“\A\B”就是根空間(全域空間)內的 A 子空間內的 B 子空間。在相同空間中便相互可見可作用。例如根空間中的變數 $a 要受值於 B 空間中的變數 $a:
$a=\A\B\$a;
8.1.2. 如何命名空間:
在所有程式碼之前(<?php 下一行)就需如下定義,
namespace A;
或
namespace \A;
// 則以下程式空間到 ?> 之前都是 \A 子空間。
8.1.3. 使用命名空間:
例如存在 \A,\A\B,則
namespace \A\B; 即進入該空間。
可取別名例如在 \A\B 下有類別 cx,cy,function fx, fy,const ix, iy,則
use \A\B\cx;
$obj=new cx;
或
use \A\B as AB;
$obj=new \AB\cx;
或
use \A\B\cx as CX;
$obj=new CX;
或
use \A\B\{cx, cy as CY};
use function \A\B\{fx as Fx, fy as Fy};
use const \A\B\{ix, iy};
8.1.4. 叫用命名空間,只是把該命名空間內的定義引入根空間;而程序總是在根空間中跑的;亦即,當前若在某非根空間中,則僅是正在做定義而已。

9. 子繼承父則子包含有完整父的物件但有存取級別之分,若父的是:
private,子不可存取,繼承後屬性 private
protected,子可存取,繼承後屬性 protected
public,子可存取,繼承後屬性 public
9.1. 相同類別的不同物件之間可相互存取所有成員含公開跟私有;當然存取都是正發生在定義此類別時。
9.2. 存取權修飾子只是修飾類別成員的存取權及繼承方式,故各個成員其名稱須相異/建解構子除外。故有所謂子類別方法覆載父類別方法,名稱相同其修飾子可相同或更寬鬆。那麼是否有這樣的問題,假設父子各有一個同名 function,怎知道呼叫哪個?沒問題的,因除了有定義 $this->func() 外,也定義了要呼叫父 function 得使用 parent::func()
9.3. 在類別定義內就可以建立此類別的實例物件,也可建立父類別物件。

10. 函式多載(function overloading)是指相同名稱相異簽名(函式引數)的函式群。類別繼承(函式)覆載(overriding)是指子類別定義了與父類別相同的函式。類別加載(overloading)是指在類別中無法存取的成員(inaccessible data/function)我們以 "magic method" 來加載使得 "inaccessible data/function" 變成不可能,即 "就是可存取";即其,擴充了類別的處理能力。所謂 inaccessible data/function 要不是指對外是私有保護成員,要不就是尚未定義的成員。存取私有成員?對也不對:當諸此行為發生,就由 magic methods 來因應回應。因此,需要有所回應便實作它,不需要就不管它,即所謂 overloading 擴充。一方面而言,能在執行時期擴充原類別的能力。分為對資料和對函式。
10.1. to data 的四個魔術函式:
Property overloading:
10.1.1. public __set(string $name, mixed $value): void
10.1.2. public __get(string $name): mixed
10.1.3. public __isset(string $name): bool
10.1.4. public __unset(string $name): void
10.2. to function 的二個魔術函式:
Method overloading:
10.2.1. public __call(string $name , array $arguments): mixed
10.2.2. public static __callStatic(string $name, array $arguments): mixed
10.3. 例子請詳見官方文件;有使用者評論可啟發相關運用。
10.4. PHP team 曾經號稱 PHP 是世界上最好的語言,相信他們於此必有相當深的見解與自信。

11. Magic methods:__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __set_state(), __clone(), __debugInfo(),

__sleep():It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized. If the method doesn't return anything then NULL is serialized and E_NOTICE is issued.
其有所限制是父類別的私有資料仍無法存取,故其主要用途是針對敏感資料。
(註:因是回傳變數名稱,故後續是從外部存取物件成員。故要得到所有成員便需從內部就引出的其它方法了“The Serializable interface”)
(請注意,經由實測,若沒有實作 sleep/wakeup 則 serialize/unserialize 是 byte-wise copy,即所有成員皆可被序列化。而 sleep/wakeup 便是針對所有成員中的子集合)
=== 測試
class U{
    private $supern=7;
    public function mod(){
        $this->supern=9;
    }
}

class A extends U{
    private $zero = 0;
    public $one = 1;
    public function show_one(){
        echo $this->one;
    }
    public function mod(){
        parent::mod();
        $this->zero=8;
    }
}

$a = new A;
$s = serialize($a);
$b = new A;
$b->mod();
$t = serialize($b);
$p = unserialize($s);
$q = unserialize($t);

$p->show_one();
$q->show_one();
var_dump($p);
var_dump($q);
=== 結果
11object(A)#3 (3) { ["zero":"A":private]=> int(0) ["one"]=> int(1) ["supern":"U":private]=> int(7) } object(A)#4 (3) { ["zero":"A":private]=> int(8) ["one"]=> int(1) ["supern":"U":private]=> int(9) }
===

__wakeup():相對於 __sleep(),用於 unserialize()。
__toString():當物件將被視作字串來處理時會呼叫到的函式
(註:若 printf 的 %d 關聯於 __toString 會先決定 %d 才決定 __toString 故便會出錯;即不會試著呼叫 __toString)
__invoke():實作讓物件可以以函式呼叫的介面
11.1. serialize/unserialize 用法範例:
透過 magic methods 或 Serializable interface 將物件(名/值)導出後,由此函式轉成位元組資料再寫入檔案或反之。
// 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);
  // store $s somewhere where page2.php can find it.
  file_put_contents('stored_place', $s);

// page2.php:
  // this is needed for the unserialize to work properly.
  include("classa.inc");
  $s = file_get_contents('stored_place');
  $a = unserialize($s);
  // now use the function show_one() of the $a object.
  $a->show_one();

12. Trait, Late Static Bindings, Covariance and Contravariance. 詳見 php 文件

PHP Code Snippets Powered By : XYZScripts.com