#### 第15章:
#### PHP
PHP初始全稱為Personal Home Page,現已正式更名為Hypertext Preprocessor(超文本預處理語言)。PHP算一種HTML內嵌式的語言,在服務端執行的嵌入HTML文檔的腳本語言,語法風格類似C語言,被廣泛應用于動態網站制作中。PHP支持各種數據庫和操作系統,還能使用C/C++進行程序擴展。
#### 15.1 PHP7的準備工作
##### Nginx 和 PHP 7 服務器安裝配置
1. ##### 獲取PHP7安裝包資源
- 打開瀏覽器,進入https://www.php.net/downloads
:-: 
- 選擇合適的版本,這里選擇PHP7版本
- 選擇合適的格式,這里選擇tar.gz格式
:-: 
- 復制其地址,使用wget命令下載到服務器。
:-: 
1. ##### 獲取Nginx安裝包資源
Nginx是一個高性能的HTTP和反向代理web服務器,同時也提供了IMAP/POP3/SMTP服務。其特點是占有內存少,并發能力強。
- 打開瀏覽器,進入http://nginx.org/en/download.html
:-: 
- 選擇適合的版本,這里選擇1.16
- 選擇適合的格式,這里選擇tar.gz
:-: 
- 復制其地址,使用wget命令下載到服務器。
:-: 
2. ##### 安裝Nginx和PHP
- 使用tar命令解壓nginx壓縮文件
- 進入nginx文件夾使用`./configure --prefix=/usr/local/nginx`構建安裝nginx到/usr/local/nginx目錄。再使用`make && make insgall`進行安裝(安裝過程中可能缺少依賴,使用yum進行安裝)。
- 使用tar命令解壓PHP壓縮文件
- 進入PHP文件夾使用以下命令構建安裝(構建安裝路徑、配置文件路徑、功能模塊等)
```
./configure --prefix=/usr/local/php7 \
--with-config-file-path=/usr/local/php7/etc \
--with-config-file-scan-dir=/usr/local/php7/etc/php.d \--enable-mysqlnd \
--with-mysqli \
--with-pdo-mysql \
--enable-fpm \
--with-iconv \
--with-zlib \
--enable-xml \
--enable-shmop \
--enable-sysvsem \
--enable-inline-optimization \
--enable-mbregex \
--enable-mbstring \
--with-openssl \
--enable-pcntl \
--enable-sockets \
--with-xmlrpc \
--enable-zip \
--enable-soap \
--without-pear \
--enable-session \
--with-curl \
--enable-opcache
```
再使用`make && make install`進行安裝(安裝過程中可能缺少依賴,使用yum進行安裝)。
3. ##### 建立Nginx和PHP之間的連接
在PHP和Nginx的架構里,用戶請求到達服務器后先經過Nginx處理,再由Nginx通過fastcgi協議發送給PHP的進程管理器php-fpm,最后由php-fpm分配子進程來運行PHP程序。
:-: 
? Nginx與php-fpm的交互過程
- 將PHP安裝目錄里的bin目錄中的php、phpize、php-config三個二進制文件復制到/usr/local/bin目錄,這一步是為了將這三個運行文件設置成常用命令。
- 將解壓后的PHP源文件目錄里的php.ini-development(配置文件)復制到PHP配置目錄(etc目錄),取名為php.ini。配置目錄通過`php --ini`命令查看
:-: 
? 配置文件目錄為/usr/local/php7/etc
- 找到nginx配置文件,在Nginx安裝目錄的conf目錄里。使用`vim nginx.conf`命令將以下注釋打開并修改root后面指向的未來的網站目錄。
```
location ~ \.php$ {
//這里是你未來的網站目錄
root /data/wwwroot/;
//這里配置通過fastcgi與php-fpm通信的ip和端口
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
//這里要注釋掉換成下面一種
#fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
```
- 在PHP安裝目錄里的etc目錄里,將復制粘貼`php-fpm.conf.default`為`php-fpm.conf`。并將php-fpm.d目錄里`www.conf.default`的內容復制到php-fpm.conf里。并確認[www]塊的`listen = 127.0.0.1:9000`(確保php-fpm監聽的是9000端口,這樣才能和Nginx通信)。
:-: 
? 合并后的php-fpm.conf配置文件
- 啟動Nginx,在Nginx安裝目錄下的sbin目錄里輸入命令`./nginx`;啟動php-fpm進程管理器,在PHP安裝目錄下的sbin目錄里輸入命令`./php-fpm`
此時在網站目錄下創建名為index.php的文件
```
<?php
echo "hello world";
?>
```
此時瀏覽器輸入服務器IP地址時,出現hello world說明環境配置成功。
#### 15.2 PHP的基本語法
##### PHP的標識符
PHP代碼是以`<?php`開始,以為`?>`結束的。
```
<?php
echo "這是一行PHP代碼";
?>
```
##### 編碼規范
1. 指令分隔符:PHP代碼的正常描述中,是一句為一個執行單位,而句是以`;`號結尾。
```
<?php
echo "這是一句代碼,以為分號結尾";
?>
```
1. 空白符:PHP對各種情況下的空白進行忽略。利用空白可以增強代碼的可讀性和清晰性。
2. 注釋:為了增強可讀性,程序員需要對代碼添加文字說明。這就使用到了注釋。
```
/*這是C語言的注釋風格*/
```
- C語言的注釋風格
```
//這是C++注釋風格
```
- C++語言的注釋風格
```
#這是SHELL的注釋風格
```
- SHELL風格
3. 與HTML混搭
PHP可以與HTML文檔混搭使用。
```
<html>
<head>
<title>PHP與HTML混搭</title>
</head>
<body>
<?php echo "HTML文檔嵌入了PHP代碼" ?>
</body>
</html>
```
#### 15.3 常量
常量是一類字面量,定義后就無法改變其值。
##### 聲明常量
使用define()聲明常量,通常使用大寫英文字母表示。語法:
```
define('常量名','常量值');
```
常量可以存儲數組、對象、字符、數字。例如:
```
<?php
define('NAME','星河一趟不枉雙腳立行');
echo NAME;
?>
```
:-: 
? 運行結果
##### 內置常量
PHP內置了部分常量。用以隨時調用。
1. `__FILE__`:文件的完整路徑
2. `__LINE__`:PHP文件運行行數
3. `PHP_VERSION`:PHP程序的版本
4. `TRUE`:真
5. `FALSE`:假
6. `_DIR_`:文件所在目錄
7. `PHP_OS`:運行PHP的系統
8. `E_ERROR`:最近錯誤處
9. `E_WARNING`:最近的警告處
10. `E_PARSE`:語法解析潛在問題處
11. `E_NOTICE`:發生不尋常但不一定是錯誤處
12. `_FUNCTION_`:函數名稱
13. `_CLASS_`:類名
例子:
```
<?php
echo(__FILE__);
echo '<br>';
echo (__DIR__);
echo '<br>';
echo TRUE;
echo '<br>';
?>
```
:-: 
? 運行結果
#### 15.4 變量
不同的變量類型對應著不同的數據類型。
##### 聲明變量
不同于C/C++、Java等編譯型語言在定義變量時需要指定變量的數據類型,PHP這種弱類型的語言只需要使用`$`符就可以定義任意數據類型的變量(實際交給了PHP底層進行數據類型的判斷選擇和空間申請等操作)。
合法的PHP變量以$為前綴,a~z的大小寫或者`_`下劃線開頭。
合法的變量名可以是:
```
$name
$NAME
$Name
$_name
```
不合法的變量名:
```
$!1
$1name
```
PHP中對變量的賦值有傳值和傳址:
`傳值`:直接將值賦予變量。
```
$name = '春夏';
```
`傳址`:將地址空間傳給變量,此變量得到此地址空間原變量的值,需加上`&`符號(取地址符號)。
```
$her = &$name;
```
##### 變量作用域
變量的作用域是指在代碼中能訪問到變量的范圍。
1. 內置超全局變量:在代碼中任何位置都可訪問。
2. 常數:聲明后為全局性,可以在函數內外使用。
3. 全局變量:可以在代碼內訪問,不可在函數內訪問。
4. 函數內聲明的全局變量(global關鍵字):在函數內部聲明變量是全局變量就是同名的全局變量。
5. 函數內部創建和聲明的靜態變量:外部無法訪問,靜態值會保留。
6. 在函數中創建的局部變量:只能在函數內部訪問,函數結束后釋放。
`超級全局變量`是PHP內置的,在代碼的任何地方都可以訪問:
- $GLOBALS:包含全局變量的數組
- $_GET:通過GET方法傳遞來的變量的數組
- $_POST:通過POSET方法傳遞來的變量的數組
- $_COOKIE:包含cookie變量的數組
- $_SERVER:包含服務器環境變量的數組
- $_ENV:包含環境變量的數組
- $_SESSION:包含會話變量的數組
- $_FILES:包含上傳變量的數組
全局變量例子:
```
<?php
$num = 3;
function showNum()
{
echo $num;
}
showNum();
echo $num;
?>
```
:-: 
此時設置的全局變量$num在showNum函數內部是無法訪問的。
靜態變量例子:
```
<?php
$num = 3;
function addNum()
{
static $num = 10;
$num++;
echo '$num =' . $num . PHP_EOL;
}
addNum();
addNum();
```
:-: 
此時函數將$num設置為靜態變量值為10,調用兩次+1,第一次輸出11并保留其值,則再第二次調用時輸出12。
##### 變量的銷毀
當用戶創建變量時,PHP底層處理邏輯會為變量分配一段內存空間,并對此空間進行引用計數(復雜數據類型),并引用計數+1。當其他變量被賦值為此變量或者此空間時,引用計數+1,當變量與該空間斷開聯系時,引用計數-1。引用計數是由PHP底層處理邏輯動態維護的。直到引用計數為0時,這塊空間稱為垃圾,也由PHP底層處理邏輯自動回收。
也可以使用unset()函數進行銷毀,其用于切斷變量名與分配空間的關聯關系。unset()的例子:
```
<?php
$num = 10;
unset($num);
echo $num;
?>
```
:-: 
此時$num變量已經被切斷與原來分配空間的關系,所以輸出這個沒有的變量會提示未定義變量num。
#### 15.5 數據類型
PHP變量不需要事先聲明,賦值即可聲明。PHP7的數據類型包括:
1. 字符串
2. 整型
3. 浮點型
4. 數組
5. 對象
6. NULL
7. 布爾
8. 資源(用于打開文件、數據連接、圖形畫布)
##### 字符串
字符串存儲的是一段字符,例如:
```
$str = "hello world";
```
##### 整型
整型存放整數,例如:
```
$num = 1000;
```
##### 浮點型
浮點型表示實數,例如:
```
-1.43
1E+09
0.0
```
##### 數組
數組通過list()或者array()創建,或直接賦值。存放一組變量集合,由"鍵"與"值"的對應關系組成。數組元素可以是整數、字符串等。不指明"鍵"的情況下,默認"鍵"從零開始。
```
<?php
$arr = array(
0=>"張三",
1=>"春夏",
2=>"秋冬"
);
for($i = 0; $i<count($arr);$i++){
echo $arr[$i]."</br>";
}
?>
```
:-: 
##### 對象
對象是類的實例。當一個類被實例化后,這個對象傳遞給一個變量,這個變量就是對象類型。
##### NULL
NULL類型是僅有NULL這個值。標記一個變量為空。
##### 資源類型
資源類型表示PHP的擴展,可以是一個打開的文件,也可以是一個連接或者其他。
##### 數據類型自動轉換
數據類型從一個類型轉換到另外一個類型就是數據轉換。常見強制類型轉換和自動轉換。
##### 自動數據類型轉換
```
<?php
$a = 1;
$b = '1';
$c = $a + $b;
echo $c;
?>
```
:-: 
##### 強制類型轉換
在PHP中使用settype()或者(int)、(string)、(float)、(double)、(real)等函數可以實現強制類型轉換。
```
<?php
$flo1 = 1.11;
echo settype($flo1,'int');
echo (int)$flo1;
?>
```
:-: 
##### 標量類型的聲明
默認情況下PHP文件處于弱類型校驗模式。PHP7新增了標量類型聲明的特性,標量聲明有兩種模式:強制模式(默認)和嚴格模式。語法:
```
declare(strict_types=1);
```
strict_type為1指嚴格模式,作用于函數參數和函數返回;strict_type為0指強制模式,會校對函數參數的數據類型和返回值的數據類型。
強制模式:
```
<?php
function maxNum(int $a, int $b)
{
if($a>$b){
echo $a;
}else{
echo $b;
}
}
maxNum(1,2.1); //這里會將2.1強制轉換成2
?>
```
:-: 
嚴格模式:
```
<?php
declare(strict_types=1);
function maxNum(int $a, int $b)
{
if($a>$b){
echo $a;
}else{
echo $b;
}
}
maxNum(1,2.1); //這里2.1不屬于int整型,會報錯
?>
```
:-: 
#### 15.6 運算符
##### 算數運算符
| 運算符 | 作用 | 運算符 | 作用 |
| ------ | ---- | ------ | ---- |
| + | 加法 | % | 取余 |
| - | 減法 | ++ | 累加 |
| * | 乘法 | -- | 累減 |
| / | 除法 | | |
? 常見的算數運算符
##### 字符串連接符
`.`號可以把兩個不同的字符串連接起來組成一個字符串。
```
<?php
$a = 'hello ';
$b = 'world';
$c = $a.$b;
echo $c;
?>
```
:-: 
##### 賦值運算符
| 賦值運算符 | 作用 |
| ---------- | ------------------------------ |
| = | 將右邊的值賦值給左邊的變量 |
| += | 將左邊值加右邊值再賦值給左邊 |
| -= | 將左邊值減右邊值再賦值給左邊 |
| *= | 將左邊值乘右邊值再賦值給左邊 |
| /= | 將左邊值除以右邊值再賦值給左邊 |
| .= | 將左邊值連接右邊值再賦值給左邊 |
| %= | 將左邊值取余右邊值再賦值給左邊 |
? 賦值運算符的含義
```
<?php
$a = 1;
$a+= 1;
echo $a;
?>
```
:-: 
##### 比較運算符
| 比較運算符 | 含義 | 比較運算符 | 含義 |
| ---------- | ---------- | ---------- | -------------------- |
| == | 是否相等 | >= | 是否大于等于 |
| != | 是否不等于 | <= | 是否小于等于 |
| > | 是否大于 | === | 是否全等(類型也相等) |
| < | 是否小于 | !== | 是否不全等 |
? 比較運算符的含義
```
<?php
$a = 1;
$b = 2;
if($a<$b){
echo '$a 小于 $b';
}else{
echo '$a 不小于 $b';
}
```
:-: 
##### 邏輯運算符
| 邏輯運算符 | 含義 | 邏輯運算符 | 含義 |
| ---------- | ------ | ---------- | ---------------- |
| && | 邏輯和 | ! | 邏輯否 |
| AND | 邏輯和 | NOT | 邏輯否 |
| \|\| | 邏輯或 | XOR | 邏輯異或(找不同) |
| OR | 邏輯或 | | |
? 邏輯運算符的含義
```
<?php
$a = 1;
$b = 2;
if($a == 1 && $a < $b){
echo '$a 等于 1,且$a 小于 $b';
}
?>
```
:-: 
##### 按位運算符
| 按位運算符 | 名稱 | 含義 |
| ---------- | -------- | ---------------------------------------- |
| & | 按位和 | 對應位都為1,結果為1 |
| \| | 按位或 | 對應位有1,結果為1 |
| ^ | 按位異或 | 對應位不同,結果為1 |
| ~ | 按位取反 | 0改為1,1改為0 |
| << | 左移 | 將變量的二進制位向左移動N位,右邊空0補齊 |
| >> | 右移 | 將變量的二進制位向右移動N位,左邊空0補齊 |
```
<?php
$a = 6; //二進制碼為0110
$b = 2; //二進制碼為0010
echo ($a & $b) . '<br>';
echo ($a | $b) . '<br>';
echo ($a ^ $b) . '<br>';
?>
```
:-: 
$a & $b = 0110 & 0010 = 0010 = 2
$a | $b = 0110 & 0010 = 0110 = 6
$a ^ $b = 0110 & 0010 = 0100 = 4
##### 否定控制運算符
用在`操作數`之前,判斷數字真假。
| 否定控制運算符 | 含義 |
| -------------- | ------ |
| ! | 邏輯否 |
| ~ | 按位否 |
##### 錯誤控制運算符
`@`符用來屏蔽錯誤信息的生成。
```
<?php
$err = @(1 / 0); //利用@屏蔽除數為0的錯誤
?>
```
:-: 
##### 三元運算符
在三個操作符之間,語法:
```
(expr1)? (expr2):(expr3);
```
作用:如果expr1成立,執行expr2,否則執行expr3。
```
<?php
echo (1>2)? 1 :0;
?>
```
:-: 
##### 運算符規則
- 加減乘除遵從數學原則
- 先括號內再括號外
- 賦值由右向左
#### 15.7 PHP函數
PHP函數分為`內置函數`和`自定義函數`。內置函數是PHP內部實現的函數。自定義函數是開發者可以自行定義功能的函數。
##### 內置函數
PHP提供了大量的內置函數供程序員直接使用,常見的包括數學函數、字符串函數、數組函數等。
```
<?php
$num = rand(1000,9999);
echo $num;
?>
```
rand函數是隨機數字,可以指定隨機數字的范圍。上面的例子指定隨機數從1000到9999。
:-: 
##### 自定義函數
語法:
```
function function_name(param1,param2,...)
{
code
}
```
function_name 是函數名,param1、param2是參數。
返回一個數字相加總和的自定義函數例子:
```
<?php
function numTotal($num1,$num2)
{
return $num1 + $num2;
}
echo numTotal(1,2);
?>
```
:-: 
`return`指定函數返回值。函數是封閉的程序,所以通過預定義的參數位置傳遞參數,上面的$num1和$num2就是定義了兩個參數。調用numTotal函數時將1給到$num1,將2給到$num2,就是參數的數據傳遞。
##### 對參數傳遞引用
```
<?php
$a = 1;
function echoNum(&$num) //這里定義函數的參數是傳遞的變量的引用,也就是這個變量的地址空間
{
echo $num;
}
echoNum($a);
?>
```
:-: 
這種情況下,這個地址空間里的值不銷毀,引用的值就一定是這個地址空間的值。上面的$a在這段地址空間的值為1,則函數調用時參數是引用這個$a的地址空間的值,所以最終輸出為1。
##### 對函數的引用
對函數的引用,實際上是對函數返回值的引用。
```
<?php
function &expl($a = 1)
{
return $a;
}
$b = &expl('這是一個函數引用,這里函數返回值的空間是一定的');
echo $b;
?>
```
:-: 
對比以下非引用
```
<?php
function expl($a = 1)
{
return $a;
}
$b = expl('這里的返回值重新進入了$b開辟的內存空間');
echo $b . '<br>';
$a = expl('這里的返回值重新進入了$a開辟的內存空間');
echo $a . '<br>';
echo $b;
?>
```
:-: 
##### 對函數引用取消
對函數的引用就是對函數返回值的引用,所以對函數引用的取消就是對函數返回值的引用的取消。也就是斷開變量名和變量內容的關系(斷開變量名與內存空間值的關系)。使用unset()函數,但是unset()函數只是斷開關系,并沒有回收變量內容,也就是沒有回收內存空間里面的值。
```
<?php
$a = 1;
$num = $a;
unset($num);
echo $a;
echo $num;
```
:-: 
這里的$num已經與內存空間中值1的關系已經被unset()斷開。所以再使用$num變量會提示$num函數未定義。
#### 包含文件
包含文件通過require()和include()語句。
1. require():在腳本執行前讀入包含的文件,讀取錯誤時拋出致命錯誤,并停止腳本運行。
2. include():在指定到語句時讀入包含的文件,讀取錯誤時產生一個警告,并繼續執行腳本。
通過require_once()或者include_once()語句作用是一樣的,但只加載一次文件,防止變量重新賦值或函數重定義問題。
#### 15.8 流程控制
##### if 語句
```
if(條件判斷){
執行語句;
}
```
##### if else 語句
```
if(條件判斷){
執行語句A;
}else{
執行語句B;
}
```
##### elseif 語句
```
if(條件判斷1){
執行語句A;
}elseif(條件判斷2){
執行語句B;
}else{
執行語句C;
}
```
##### switch 語句
```
switch (條件判斷語句){
case 判斷結果A:
執行語句;
break;
case 判斷結果B:
執行語句;
break;
case 判斷結果C;
執行語句;
break;
default:
默認執行語句;
}
```
例子:
```
<?php
$a = 3;
switch($a){
case 1:
echo 1;
break;
case 2:
echo 2;
break;
default:
echo '其他數字';
}
?>
```
:-: 
##### 循環語句
##### while 循環語句
```
while(判斷條件){
執行語句;
}
```
##### do while 語句
do while 語句會執行至少會執行一次。
```\
do{
執行語句;
}while(判斷條件)
```
##### for 循環語句
```
for(expr1;expr2;expr3)
{
執行語句
}
```
```
<?php
for($i = 0; $i<5;$i++){
echo 1 . "<br>";
}
?>
```
:-: 
這里$i初始化為0,每次循環自增1,$i小于5。
##### foreach 循環語句
foreach 語句是常用的一種循環,經常用來遍歷數組。格式:
```
foreach(數組 as 數組元素){
對數組元素的執行語句;
}
```
可以根據數組的情況分成兩種。
不包含鍵的:
```
foreach(數組 as 數組元素值){
對數組元素的執行語句;
}
```
包含鍵的:
```
foreach(數組 as 數組元素鍵=>數組元素值){
對數組元素的執行語句;
}
```
##### 使用break/continue跳出循環
在循環里可以使用break跳出整個循環。
在循環里可以使用continue跳出此次循環。
```
<?php
for($i=0;$i<5;$i++){
if($i==3){
continue;
}
echo $i . "<br>";
}
?>
```
:-: 
此次for循環里,當$i == 3時候,跳過了此次循環。
#### 15.9 字符串
##### 雙引號和單引號的區別
- 雙引號會解析字符串的值。單引號會直接顯示變量名稱。
- 雙引號可以通過`\`轉義符輸出特殊字符,例如\n換行、\tTab、\\反斜杠等。
單引號只能通過`\`轉義符輸出\\'單引號本身、\\\反斜杠本身。
##### 字符串連接符
`.`號可以連接兩個字符串。
```
<?php
echo '你好, ' . '中國';
?>
```
##### 手動轉義字符
手動轉義字符是通過`\`反斜杠使一些特殊字符轉義為普通字符。
##### 自動轉義字符
通過PHP的內置函數addslashes()來完成自動轉義。
##### 計算字符串的長度
使用strlen()函數。
```
<?php
$str = '這是一段字符串';
echo strlen($str);
?>
```
:-: 
這里沒有指定字符集,所以一個漢字的長度是3。
##### 字符串單詞統計
僅針對英文單詞,使用str_word_count()函數實現。
##### 清理字符串中的空格
有時候不需要空格,就需要清理。使用ltrim()、rtrim()、trim()函數。ltrim()從左邊清除字符串頭部空格,rtrim()從右邊清除字符串頭部空格,trim()清除左右兩邊字符串頭部空格。
##### 字符串切分和組合
使用explode()將字符串分成不同部分存入一個數組;使用implode()函數,將數組的元素按照一定的間隔標準組成一段字符串。
explode()例子:
```
<?php
$a = '1;2;3;4';
print_r(explode(';',$a));
?>
```
:-: 
implode()例子:
```
<?php
$arr = array(
0=>1,
1=>2,
2=>3
);
echo implode(',',$arr);
?>
```
:-: 
##### 字符串截取
使用substr()截取非中文字符串,使用m_substr()截取中文字符串。語法
```
substr(目標字符串,起始位置,截取長度)
```
例子:
```
<?php
$str = '1234556';
echo substr($str,1,3);
?>
```
:-: 
##### 字符串替換
substr_replace()函數替換字符串文本。
```
substr_replace(目標字符串,替換字符串,起始位置,替換長度)
```
例子:
```
<?php
$str = '1234567890123456';
echo substr_replace($str,'****',2,6);
?>
```
:-: 
##### 字符串查找
strstr()在一個字符串中查找另外一個字符串,并返回查找到的字符串位置到尾部的字符串。
strpos()查找一個字符串在另一個字符串中的位置。
```
<?php
$str = 'aabbcc我aabbcc';
echo strpos($str,'我');
?>
```
:-: 
##### 大小寫轉換
strtolower()函數將字符串轉為小寫,strtoupper()函數將字符串轉為大寫,ucfirst()將整個字符串首字母改為大寫,ucwords()函數將整個字符串中以空格為分隔符的單詞首字母大寫。
#### 15.10 正則表達式
正則表達式是把文本或字符串按照一定規則表示的方法,常用于文本匹配。
主要包括:
1. 方括號[]
方括號內是要用來匹配的字符。
2. 連接符-
很多情況下不能逐個列出字符就需要用到連接符,比如0到9使用`0-9`、`A-Z`、`a-z。`
3. 點號字符.
代表所有字符和數字。
4. 限定符(+*?{n,m})
- `+`表示前面的字符至少有1個。
- `*`表示前面的字符有任意個。
- `?`表示前面的字符有0個或1個。
- `{n,m}`表示前面的字符最少有n個,最多有m個。
5. 行定位符(^和$)
確定字符串所有出現的位置。例如:^a,匹配a只能在字符串頭部出現。
6. 排除符([^])
用在方括號內表示排除,例如`[^a-z]`表示除了a-z的其他所有字符。
7. 括號字符(())
將正則表達式分成不同的操作部分,一個括號為一個部分。
8. 選擇字符(|)
選擇字符表示"或"。例如:[ab|ac|ad]匹配ab或ac或ad
9. 轉義字符(\\)與反斜線(\\)
例子:
```
<?php
$str = 'abc12345bcd';
$rule = '/[0-9]?/';
preg_match_all($rule,$str,$arr);
echo "<pre>";
print_r($arr);
?>
```
preg_match_all()函數匹配所有可能的結果,每一次匹配從上一次匹配成功的位置開始,$rule是匹配規則,$str是匹配的字符串,$arr是最后的匹配結果數組。
:-: 
```
<?php
$str = 'abc12345bcd';
$rule = '/[0-9]{5}/';
$str = preg_replace($rule,';',$str);
echo $str;
?>
```
preg_replace (正則表達式, 要替換成的字符串, 匹配的字符串, 最大替換次數【默認-1,無數次】, 替換次數)。
:-: 
#### 15.11 PHP數組
數組是用來存儲一系列值的結構。數組中的每個數值稱為數據元素,每個數據元素對應一個表示稱作`鍵`。
##### 數組的類型
##### 數字索引數組
數字索引數組是鍵為數字的數組,也就是一個數字鍵key對應一個數值value,key=>value。可以分別定義鍵和值,鍵不可重復。
```
$arr = array(0=>'張三',1=>'李四',2=>'趙五',5=>'王七');
```
也可以不指定鍵,則鍵從0自動遞增。
```
$arr = array('張三','李四','趙五','王七');
```
##### 關聯索引數組
關聯索引數組是鍵不為數字的,其鍵可以是字符串。PHP底層運行時隱性地為其數組元素添加了數字索引。
```
$arr = array(''張家'=>'張三','李家'=>'李四','趙家'=>'趙五','王家'=>'王七');
```
##### 數組常量
使用const定義數組常量。
```
<?php
const arr = array('張三','李四','趙五','王七');
echo arr[2];
?>
```
:-: 
也可以使用define()定義常量數組。
```
<?php
define('ARR',['張三','李四','趙五','王七']);
echo ARR[2];
?>
```
:-: 
##### 數組結構
##### 一維數組
一維數組的每個數組元素是一個獨立的值。
```
$arr = array('張三','李四','趙五','王七');
```
##### 多維數組
```
<?php
$arr = array(
0=>['張三','李四','趙五','王七'],
1=>['空調','洗衣機','電視機'],
2=>['1000','2000','3000'],
);
echo "<pre>";
print_r($arr);
?>
```
:-: 
##### 遍歷數組
使用for循環、while循環、foreach循環可以遍歷數組。其中foreach循環遍歷數組速度最快,因為PHP底層為數組設計了迭代器和相關尋址指針能夠快速查找到下一個元素值的地址空間。
遍歷一維數組:
```
<?php
$arr = ['張三','李四','趙五','王七'];
for($i=0;$i<count($arr);$i++){
echo $arr[$i] . "<br>";
}
?>
```
:-: 
```
<?php
$arr = ['張三','李四','趙五','王七'];
foreach($arr as $key => $value){
echo 'key = ' . $key . 'value = ' . $value . '<br>';
}
?>
```
:-: 
遍歷多維數組:
```
<?php
$arr = array(
0=>['張三','李四','趙五','王七'],
1=>['空調','洗衣機','電視機'],
2=>['1000','2000','3000'],
);
for($i=0;$i<count($arr);$i++){
foreach($arr[$i] as $value){
echo $value . '<br>';
}
}
?>
```
:-: 
##### 數組排序
簡單的一維數組可以通過sort()、asort()、ksort()、rsort()、krsort()等函數進行排序,這些函數在PHP底層是由C函數zend_sort()實現的,它會判斷在何時使用快排或者插入排序等算法。
當然,除了以上函數也可以自定義個PHP的快排或者插入排序等算法進行數組排序。
使用sort()函數排序:
```
<?php
$arr = [11,9,29,2,41,213];
sort($arr);
echo "<pre>";
print_r($arr);
?>
```
:-: 
自定義排序:
```
<?php
$arr = [11,9,29,2,41,213];
for($i=1;$i<count($arr);$i++){
if($arr[$i] < $arr[$i-1]){
$a = $arr[$i-1];
$arr[$i-1] = $arr[$i];
$arr[$i] = $a;
}
}
echo "<pre>";
print_r($arr);
?>
```
這是一種簡單的冒泡排序。時間復雜度是O(n2)。
:-: 
?
##### 字符串和數組的轉換
使用explode()將字符串切分成數組;使用implode()將字符串組成數組。
##### 向數組添加刪除元素
使用push、pop、shift、unshift函數實現。
##### 添加數組元素
array_unshift()向數組頭部添加元素:
```
<?php
$arr = [1,2,3,5];
array_unshift($arr,22,3,1);
echo "<pre>";
print_r($arr);
?>
```
:-: 
array_push()向數組尾部添加元素:
```
<?php
$arr = [1,2,3,5];
array_push($arr,22,3,1);
echo "<pre>";
print_r($arr);
?>
```
:-: 
array_shift()把數組頭部元素刪除:
```
<?php
$arr = [1,2,3,5];
array_shift($arr);
echo "<pre>";
print_r($arr);
?>
```
:-: 
array_pop()把數組尾部元素刪除:
```
<?php
$arr = [1,2,3,5];
array_pop($arr);
echo "<pre>";
print_r($arr);
?>
```
:-: 
##### 統計元素個數
使用count()函數統計元素個數。語法:
```
count(expr1,[1]);
```
第二個參數設為1時表示統計所有元素,包括多維數組內的所有數組元素個數。
```
<?php
$arr = [1,1,2,2,3,45,1,];
echo count($arr);
?>
```
:-: 
##### 去掉元素值相同(刪除重復元素)
使用array_unique()函數。以元素值為準返回元素值具有唯一性的數組。
```
<?php
$arr = [1,1,1,2,3,4,5];
echo "<pre>";
print_r(array_unique($arr));
?>
```
:-: 
##### 調換數組的鍵和值
使用array_flip()函數。
```
<?php
$arr = [1=>'王',2=>'李','3'=>'趙'];
echo '<pre>';
print_r(array_flip($arr));
?>
```
:-: 
##### 序列化數組
serialize()函數將數組序列化為字符串,unserialize()函數則是其反序列化為數組。
##### 合并數組
使用array_merge()函數將兩個數組組合成一個數組。
#### 15.12 時間和日期
##### 系統時區設置
1. 在php.ini文件設置。"date.timezone="選項設置時區。
2. 在引用程序中使用date_default_timezone_set()設置時區。
##### 獲取當前時間戳
使用time()函數獲得當前時間戳。
##### 獲取當前日期和時間
使用date()函數。date()函數有很多種使用方法。
```
<?php
echo date('Y-m-d h:m:s',time()) ."<br>";
echo date('Y-m h:m',time()) ."<br>";
echo date('Y h:m:s',time()) ."<br>";
echo date('Y-m h',time()) ."<br>";
?>
```
:-: 
##### 將時間轉換為時間戳
strtotime()將時間轉換成時間戳。
```
<?php
$t = '2018-11-11 02:23:01';
echo strtotime($t);
?>
```
:-: 
##### 獲得詳細的時間信息
getdate()函數返回一個數組,包含日期和時間的各個部分。如果它的參數為空,則返回當前時間戳。
```
<?php
$t = "2018:1:1 23:01:02";
$st = strtotime($t);
$tp = getdate($st);
echo "年是".$tp['year'] . '<br>';
echo "月是".$tp['mon'] . '<br>';
echo "日是".$tp['mday'] . '<br>';
?>
```
:-: 
##### 校驗日期
使用checkdate()函數進行日期校驗。語法:
```
cheackdate(月份,日期,年份)
```
```
<?php
if(checkdate(13,31,2011))
{
echo '日期是合法的';
}else{
echo '日期不合法';
}
```
:-: 
##### 輸出格式化時間戳的日期和時間
使用strftime()函數。格式:
```
strftime(格式,時間戳)
```
```
<?php
date_default_timezone_set("PRC");
echo (strftime("%b %d %Y %X",mktime(20,0,0,11,3,95)));
?>
```
:-: 
##### 將日期和時間解析成時間戳
使用mktime()函數。語法:
```
mktime(小時,分鐘,秒,月份,日期,年份)
```
```
<?php
echo mktime(1,2,0,10,2,98);
?>
```
:-: 
#### 15.13 面向對象編程
所有的事務都看作一種`類`。一類生物,一類物品,一類車,一類樓房......
對象則是由這個類具象化產生的`實體`。
面向對象有三大特點:
1. 封裝性。開發人員不必知道類的實現過程,只需要知道如何使用類,從而提高研發效率。
2. 繼承性。A類繼承B類,則A類是B類的子類。繼承可以使子類具有父類允許傳遞的方法和屬性。也可以重寫相關方法或者屬性。
3. 多態性。不同的類的對象收到相同的信息或不同的信息時,得到不同的結果。
##### 命名空間 namespace
命名空間namespace是用來封裝各個項目的方法。常用在命名類、命名函數、命名常量名上。避免代碼沖突和管理命名空間下的類、函數、常量名、變量名。
```
<?php
namespace /controller/system/data;
function addNum($a,$b)
{
echo $a + $b;
}
?>
```
這里的addNum函數在命名空間`/controller/system/data`下。
##### 聲明類
PHP使用class關鍵字定義類。
```
<?php
namespace /controller;
public class person
{
}
```
修飾符可以對類、屬性、方法進行修飾:
- public:默認,意味著被修飾的方法或屬性可以從類外部訪問。
- protected:受保護的,意味著被修飾的方法或屬性只能從類的內部訪問,通過繼承的子類同樣可以訪問。
- private:私有的,意味著被修飾的方法或屬性只能從類的內部訪問,繼承的子類也無法訪問。
例子:
```
<?php
namespace /controller; //處于controller的命名空間
class person
{
public $name; //名字是共有的,可以從外部訪問
protected $age; //年齡是受保護的,只有自己和自己的孩子可以訪問
private $weight; //體重最私密,不讓別人訪問,只讓自己可以訪問
public function printName($n){
echo $n;
}
private function printWeight($w)
{
echo $w;
}
protected function printAge($a)
{
echo $a;
}
}
```
人是一個類。我們為人設置了姓名、年齡、體重的屬性,并為人這個類設置行為方法。
##### 類的實例(對象)
面向對象的思想是一切皆為對象。類是對一類事物的抽象,對象是具體這類中具體的那個。所以要將類實例化為對象。則對象就是類的實例化。使用new對類實例化。
```
<?php
namespace controller;
class person
{
public $name = '未命名';
protected $age;
private $weight;
public function printName(){
echo $this->name . '<br>';
}
private function printWeight()
{
echo $this->weight;
}
protected function printAge()
{
echo $this->age;
}
public function setName($name)
{
$this->name = $name;
}
}
$a = new person;
$b = new person;
$a->printName();
$b->printName();
$a->setName('小李');
$a->printName();
$b->setName('小王');
$b->printName();
```
這里實例化了兩個person類的對象,分別賦給變量$a(名字設置為小李)、$b(名字設置為小王)。
:-: 
##### 訪問類的成員屬性和方法
##### $this
`$this`存在于類的每一個成員的方法里,指向的是本實例。
```
<?php
namespace controller;
class person
{
public $name = '未命名';
protected $age;
private $weight;
public function printName(){
echo $this->name . '<br>';
}
}
$a = new person;
$a->printName();
```
:-: 
##### 操作符 ::
`::`符號可以在沒有任何聲明實例的情況下訪問類中的靜態屬性和靜態方法。語法:
```
關鍵字::變量名/常量名/方法名
```
關鍵字有:
- parent:表示可以調用父類的成員變量、常量和方法。
- self:表示可以調用當前類中的常量和靜態成員變量。
- 類名:表示可以調用此類的常量變量和方法。
```
<?php
namespace controller;
class person
{
static $name = '未命名';
protected $age;
private $weight;
public function printName(){
echo self::$name . '<br>';
}
}
$a = new person;
$a->printName();
```
這里的$name成員屬性設置為靜態成員。則使用self關鍵字可以直接調用。
##### 構造方法和析構方法
構造方法是可選的,一個類只能聲明一個構造方法。在實例化對象時會自動執行此對象的構造方法。PHP7的構造方法使用`__construct`定義。
```
<?php
namespace controller;
class person
{
static $name = '未命名';
protected $age;
private $weight;
function __construct()
{
echo '實例化時會自動執行構造方法';
}
public function printName(){
echo self::$name . '<br>';
}
}
$a = new person;
```
:-: 
析構方法使用`__destruct`定義。在對象被銷毀時候執行。PHP在請求結束后會釋放所有資源,所以析構方法實際使用較少。
```
<?php
namespace controller;
class person
{
static $name = '未命名';
protected $age;
private $weight;
function __destruct()
{
echo '銷毀對象時會自動執行構造方法';
}
public function printName(){
echo self::$name . '<br>';
}
}
$a = new person;
unset($a);
```
這里使用unset()銷毀被實例化的對象觸發了此對象的析構函數。
:-: 
##### 設置和訪問屬性
在任何時候類的屬性被操作或訪問都會觸發訪問方法`__set`和`__get`。面向對象OOP的思想不鼓勵直接從類的外部訪問類的屬性。
```
<?php
namespace controller;
class person
{
static $name = '未命名';
}
$a = new person();
$a->age = 18; //這里觸發__set
echo $a->age; //這里觸發__get
```
:-: 
##### 類的繼承
類的繼承使用`extends` 關鍵字。子類擁有父類具有的公共屬性和公共方法。
```
<?php
namespace controller;
class person
{
public $name = '未命名';
private $age = 18;
public function getAge()
{
echo $this->age();
}
public function printName()
{
echo $this->name;
}
private function getMoney()
{
echo '我可以掙錢';
}
}
class son extends person
{
}
$a = new person;
$a->getAge(); //這里可以輸出父類自己的私有屬性$age
$s = new son;
$s->printName(); //可以輸出父類公用的方法
$s->getAge; //這里無法輸出父類的私有屬性$age
```
:-: 
##### 靜態方法和屬性
使用`static`關鍵字可以定義靜態屬性和靜態方法,可以實現不用實例化對象即可訪問。靜態屬性不能通過已實例化對象訪問(即靜態屬性不可由對象通過->操作符訪問)。$this在靜態方法中不可用,因為靜態方法可以直接調用。
格式為:
```
類名::靜態屬性/靜態方法
```
例子:
```
<?php
namespace controller;
class person
{
static $name = '未命名';
private $age = 18;
static public function getAge()
{
echo '這是靜態方法';
}
}
echo person::$name;
echo "<br>";
person::getAge();
```
:-: 
##### final 類和方法
`final`關鍵字是最終的意思。由final修飾的方法表示已經是最終方法無法被重寫(子類無法重寫該方法);由final修飾的類表示已經是最終的類無法被繼承。
```
<?php
namespace controller;
class person
{
static $name = '未命名';
private $age = 18;
public final function getAge()
{
echo '這是靜態方法';
}
}
class son extends person
{
public function getAge()
{
echo "嘗試重寫父類的getAge()方法";
}
}
$a = new person();
$a->getAge();
$s = new son();
$s->getAge();
?>
```
:-: 
提示無法重寫父類的最終方法getAge()。
##### 抽象類
抽象類不能實例化,只能用來作為父類被繼承。使用`abstract`關鍵字聲明。抽象類的抽象方法也使用abstract關鍵字聲明。
```
<?php
abstract class person
{
$name = '未定義';
abstract function getName();
abstract function setName();
}
```
##### 接口(接口類)
PHP只支持單繼承,想使用多繼承時就需要用到接口。接口不能聲明變量,可以使用const聲明常量成員,且接口的方法必須是抽象方法,所有的方法都必須是public訪問權限。并且接口的方法在繼承類必須實現所有方法。定義接口使用`interface`關鍵字。
```
<?php
interface person
{
public function getName();
public function setName();
}
```
繼承接口的語法:
```
class 類名 implements 接口1,接口2,....{}
```
例子:
```
<?php
interface things
{
public function getName();
public function setName($name);
}
class person implements things
{
public $name = '未定義';
public function setName($name){
$this->name = $name;
}
public function getName(){
echo $this->name;
}
}
$a = new person;
$a->setName('小周');
$a->getName();
```
:-: 
#### 15.14 異常和錯誤處理
PHP運行時會可能發生各種錯誤:來自語法的錯誤;程序員的編碼錯誤;服務器或用戶輸出導致的錯誤等。
常見的錯誤和異常有哪些:
- 拼寫錯誤
- 單引號雙引號混用
- 括號使用混亂
- 缺少美元符號
- 調用不存在的變量與常量
- 調用不存在的文件
- 數據庫服務器連接錯誤
........
##### 錯誤處理
##### php.ini 配置錯誤處理機制
| 名稱 | 默認值 | 含義 |
| ---------------------- | ------ | ------------------------------------------------------------ |
| display_errors | On | 設置錯誤作為PHP輸出的一部分。生產環境建議設置為Off |
| error_repoting | E_ALL | E_ALL會顯示所有錯誤或者提示。設置為E_ALL & ~E_NOTICE只會顯示錯誤和不良碼。 |
| error_log | null | 設置錯誤日志 |
| html_errors | On | 錯誤日志是否采用HTML格式 |
| log_errors | Off | 是否將錯誤發送到主機服務器的日志文件 |
| display_startup_errors | Off | 是否顯示PHP啟動時的錯誤 |
| track_errors | Off | 是否保存最近一個警告或錯誤信息 |
##### 自定義錯誤和錯誤觸發器
自定義錯誤處理函數,語法:
```
error_function(error_level,error_message,error_file,error_line,error_context)
```
參數含義:
| 參數 | 含義 |
| ------------- | -------------------------------------------- |
| error_level | 必須。定義錯誤報告級別 |
| error_message | 必須。規定錯誤信息 |
| error_file | 可選。規定錯誤在其中發生的文件名 |
| error_line | 可選。規定錯誤發生的行號 |
| error_context | 可選。規定數組,包含發生錯誤時的每個變量及值 |
錯誤級別:
| 數值 | 常量 | 含義 |
| ---- | ------------------- | ------------------------------------------------------ |
| 2 | E_WARNING | 不致命的運行錯誤不停止腳本 |
| 8 | E_NOTICE | 運行通知,腳本發現可能有錯誤,但也可能在腳本運行時發生 |
| 256 | E_USER_ERROR | 致命的用戶生成的錯誤 |
| 512 | E_USER_WARNING | 非致命的用戶生成的錯誤 |
| 1024 | E_USER_NOTICE | 用戶生成的通知 |
| 4096 | E_RECOVERABLE_ERROR | 可獲得的致命錯誤,可被用戶定義的處理程序捕獲 |
| 8191 | E_ALL | 所有錯誤和警告 |
創建錯誤處理函數:
```
function getError($errorLevel,$errorMsg)
{
echo "錯誤:{errorLevel} , $errorMsg";
die();
}
```
創建錯誤處理程序后要使用set_error_handler()函數設置用戶自定義的錯誤處理函數。用于創建運行時用戶自己的錯誤處理方法。成功返回原來的錯誤處理程序,失敗返回null。語法:
```
set_error_handler(error_function,error_type)
```
error_type為可選參數,不選擇默認為E_ALL。
例子:
```
<?php
function getError($errorLevel,$errorMsg)
{
echo "錯誤:{$errorLevel} , $errorMsg";
die();
}
set_error_handler("getError");
echo $a;
?>
```
:-: 
trigger_error()函數創建用戶自定義的錯誤消息。用于用戶在自己指定的情況下觸發錯誤。
```
<?php
$a = 10;
if($a<20){
trigger_error('This number must be greater than 20');
}
```
:-: 
set_error_handler()與trigger_error()組合使用:
```
<?php
function getError($errorLevel,$errorMsg)
{
echo "錯誤:{$errorLevel} , $errorMsg";
die();
}
set_error_handler("getError");
$num = 5;
if($num > 4){
trigger_error('This number has to be less than 4',E_USER_WARNING);
}
?>
```
:-: 
##### 異常處理
異常用于發生指定錯誤時改變腳本的運行流程。異常觸發時,會發生以下動作:
- 當前代碼狀態被保存。
- 代碼切到異常處理器函數。
- 根據情況處理器也許從保存的代碼狀態重新執行代碼、終止腳本、從其他位置繼續執行腳本。
拋出異常時,沒有catch塊捕獲異常或沒有set_exception_handler()函數做處理,會發生嚴重錯誤。
##### 異常的基本處理
異常處理語法:
```
try{
throw new Exception('觸發異常');//拋出異常,應定義拋出的條件
}catch(Exception $e){
echo '這里是捕獲異常后運行的代碼'; //catch塊捕獲異常并在捕獲后運行其代碼
}
```
值得注意的是:PHP只能手動使用throw拋出異常。且一個throw必須對應一個catch塊。注意**可以多個try catch塊嵌套try catch塊。**
```
<?php
try{
$num = 10;
if($num < 20){
throw new Exception('$num 不能小于20');
}
}catch(Exception $e){
echo $e->getMessage();
echo "已捕獲異常";
}
```
:-: 
##### 自定義異常處理器
自定義的異常處理器必須繼承Exception 類,可以看作是其的擴展。
```
<?php
class numException extends Exception{
public function errMessage()
{
$msg = $this->getLine().'行,位于文件:'.$this->getFile().'錯誤信息:'.$this->getMessage();
return $msg;
}
}
try{
$num = 10;
if($num < 20){
throw new numException('這個數字不能小于20');
}
}catch(numException $e){
echo $e->errMessage();
}
```
:-: 
##### 處理多個異常
```
<?php
class numException extends Exception{
public function errMessage()
{
$msg = $this->getLine().'行,位于文件:'.$this->getFile().'錯誤信息:'.$this->getMessage();
return $msg;
}
}
try{
$num = 10;
if($num < 20){
throw new numException('這個數字不能小于20');
}
if(0<1){
throw new Exception('這是拋出一個普通的異常');
}
}catch(numException $e){
echo $e->errMessage();
}catch(Exception $e){
echo '捕獲到異常';
}
```
##### 設置頂層的異常處理函數
所有未捕獲的異常都可以通過頂層的異常處理器來處理。使用set_exception_handler()函數來設置。語法:
```
set_exception_handler(exception_function)
```
```
<?php
function exceptionHand($exception)
{
echo '發生異常:' . $exception->getMessage();
}
set_exception_handler('exceptionHand');
throw new Exception('發生一個異常');
```
:-: 
#### 15.15 PHP與頁面交互
##### 表單
PHP與頁面交互一般通過頁面表單提交信息,PHP接收信息進行處理。表單提供以下功能分類:
- 文本框
- 單選框
- 復選框
- 下拉列表
- 隱藏表單
- 重置按鈕
- 提交按鈕
##### 傳遞數據的方法
表單傳遞數據或者平時的HTTP數據傳遞數據的方法一般為POST或者GET。
POST 傳遞數據不會限制數據和變量大小限制,且不會以URL方式呈現出來。
GET傳遞數據不能超過100字節,且顯性地呈現在URL上。
##### PHP獲取表單提交數據
$_POST[]全局變量獲得POST提交的數據。
$_GET[]全局變量獲得GET提交的數據。
```
<html>
<body>
<form action='test.php' method='post'>
<input type='text' name='name' size='10'>
<input type='checkbox' name='acolor' value='red'>
<input type='checkbox' name='bcolor' value='blue'>
<input type='checkbox' name='ccolor' value='black'>
<input type='radio' name='aredio' value='1'>
<input type='radio' name='aredio' value='2'>
<input type='radio' name='aredio' value='3'>
<select name='aselect' size='1'>
<option value='xinjiang'>新疆</option>
<option value='hainan'>海南</option>
<option value='sichuan'>四川</option>
<option value='chongqing'>重慶</option>
</body>
</html>
```
test.php
```
<?php
print_r($_POST);
print_r($_POST['aradio']);
print_r($_POST['aselect']);
print_r($_POST['name']);
```
以上方法通過$_POST全局變量獲得表單通過POST方法提交的數據。
##### PHP對URL傳遞的參數進行編碼
如果需要對URL傳遞的參數進行加密使用urlencode()和 rawurlencode()函數。其反操作解密函數使用urldecode()和rawurldecode()函數。
```
<?php
$str = '這一一段 未加密的段落';
$line1 = 'index.php?str=' . urlencode($str);
$line2 = 'index.php?str=' . rawurlencode($str);
echo $line1 . '<br>';
echo $line2 . '<br>';
echo urldecode($line1) .'<br>';
echo urldecode($line2) .'<br>';
echo rawurldecode($line2);
?>
```
:-: 
#### 15.16 PHP操作MySQL
##### 準備工作
首先打開php.ini,找到`;extension=mysqli`,去掉前面的`;`號。保存php.ini文件。
:-: 
##### 使用mysqli_connect()連接MySQL服務器
```
mysqli_connect('MySQL服務器地址','用戶名','用戶密碼','需要連接的數據庫','[端口號默認3306]');
```
##### 使用mysqli_select_db()函數改變默認的數據庫
```
mysqli_select_db(連接對象,'更改后的數據庫')
```
```
<?php
$db = mysqli_connect('localhost','root','123456','test1');
mysqli_select_db($db,'test2');//將$db連接對象的數據庫更改為test2
```
##### 使用mysqli_close()函數關閉MySQL連接
```
mysqli_close(數據庫連接對象)
```
##### 使用mysqli_query()函數執行SQL語句
```
mysqli_query(數據庫連接對象,SQL語句)
```
```
<?php
$db = mysqli_connect('localhost','root','123456','test1');
$sq = 'select * from student';
$result = mysqli_query($db,$sq);
if($result){
echo '查詢成功';
}else{
echo '查詢失敗';
}
?>
```
##### 獲取查詢結果的記錄集
使用mysqli_num_rows()函數獲取查詢結果條數。只對select語句有效。
```
mysqli_num_rows(result)
```
使用mysqli_affected_rows()函數獲取查詢、插入、更新、刪除操作影響的行數。connection表示上一次操作的連接對象。返回結果為-1表示錯誤。返回其他包括0的正整數表示上次操作影響的行數。
```
mysqli_affected_rows(connection)
```
##### 取出查詢結果
使用mysqli_fetch_rows()函數取出查詢結果集。再使用循環獲得每條數據的每一列具體數據。
```
mysqli_fetch_rows(result)
```
```
<?php
$db = mysqli_connect('localhost','root','123456','test1');
$sq = 'select * from student';
$result = mysqli_query($db,$sq);
while($row = mysqli_fetch_rows($result)){
echo $row[0]; //輸出本條數據的第1列
echo $row[1]; //輸出本條數據的第2列
echo $row[2]; //輸出本條數據的第3列
echo $row[3]; //輸出本條數據的第4列
}
?>
```
##### 獲取結果集中的記錄作為對象
使用mysqli_fetch_object()函數。
```
<?php
$db = mysqli_connect('localhost','root','123456','test1');
$sq = 'select * from student';
$result = mysqli_query($db,$sq);
while($row = mysqli_fetch_object($result)){
echo $row->id;
echo $row->name;
echo $row->age;
echo $row->num;
}
?>
```
##### 使用mysqli_fetch_array()函數獲取結果集記錄
```
mysqli_fetch_array(result,[resuilt_type])
```
resuilt_type有兩種常量參數:MYSQL_ASSOC表示關聯數組,MYSQL_NUM表示數字數組,MYSQL_BOTH二者兼有(默認)
```
<?php
$db = mysqli_connect('localhost','root','123456','test1');
$sq = 'select * from student';
$result = mysqli_query($db,$sq);
while($row = mysqli_fetch_array($result)){
echo $row['id'];
echo $row['name'];
echo $row['age'];
echo $row['num'];
}
?>
```
##### 使用mysqli_free_result()函數釋放資源
```
mysqli_free_result(SQL請求返回的結果對象)
```
通過mysqli_free_result($result)使用SQL請求返回的結果對象$result占用的資源。
#### 15.17 PDO數據庫抽象類庫
PDO擴展是PHP訪問數據庫定義的一個輕量級,一致性的接口,提供了一個數據庫訪問抽象層。PHP可以使用MySQL函數操作MySQL,可以使用其他函數操作Oracle等其他函數,這樣會增加PHP程序在處理數據庫方面的難度和工作量,減少了其靈活性。PDO就是解決這樣問題的"數據庫抽象層"。
經測試,PDO操作MySQL比直接用PHP操作MySQL效率高。PDO擴展類是C語言編寫并編譯進PHP的,有興趣可以查看源碼學習。
##### 準備工作
PDO類庫是PHP自帶的類庫,在php.ini中把帶有php_mysql的分號去掉。如果要啟用Oracle數據庫,可以啟用pdo_oci。
:-: 
##### 使用PDO操作MySQL
```
PDO::__constuct(DNS,username,password,driver_options)
```
DNS是數據源,username是用戶名,password是用戶密碼,driver_options是其他參數。
DNS是一個字符串,寫法:
```
'數據庫服務器類型:host=數據庫地址;dbname=數據庫名稱'
```
driver_options參數有多種作用,有興趣的同學可以自行學習。
##### 連接MySQL
```
<?php
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
?>
```
##### 使用try catch
PDOException 繼承了Exception基礎異常類,可以使用它獲得PDO連接數據庫時候的相關錯誤信息。
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
?>
```
獲得SQL請求執行時候的錯誤:
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = 'select * from student';
$db->exec($sqlQuery);
echo $db->errorCode();//獲取執行時的錯誤碼
print_r($db->errorInfo());//獲取執行時錯誤的信息
?>
```
##### 通過foreach 遍歷PDO查詢結果對象
使用PDO查詢數據時一定要使用query()方法。其他操作使用exec()方法。
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = 'select * from student';
$result = $db->query($sqlQuery);
foreach($result as $row){
$name = $row['name'];
$id = $row['id'];
}
?>
```
##### 使用fetch()獲取返回數據
使用fetch()可以獲得一條返回結果的記錄,使用fetchAll()可以獲得返回的數據對象的所有記錄。它們可以指定將返回結果讀取為關聯數組(PDO::FETCH_ASSOC)、數字索引數組(PDO::FETCH_NUM)、關聯數組加數字索引數組(FETCH_BOTH)、對象(PDO::FETCH_OBJ)。
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = 'select * from student';
$result = $db->query($sqlQuery);
foreach($row = $result->fetch(PDO::FETCH_ASSOC)){
$name = $row['name'];
$id = $row['id'];
}
echo 'total : ' $result->rowCount();//結果一共多少條
?>
```
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = 'select * from student';
$result = $db->query($sqlQuery);
foreach($result->fetchAll){
$name = $row[0];//可以使用數字索引數組
$id = $row[1];
$text = $row['text'];//可以使用關聯數組
}
echo 'total : ' $result->rowCount();//結果一共多少條
?>
```
##### 使用PDO執行添加修改語句
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = "INSERT INTO user(id,name) VALUES(30,'小張')";
if($db->exec($sqlQuery)){
echo "添加成功";
}
?>
```
##### 使用PDO執行SQL的刪除語句
```
<?php
try{
$db = new PDO('mysql:host=localhost;dbname=test1','root','123456');
}catch(PDOException $e){
echo '連接錯誤:'.$e->getMessage();
}
$sqlQuery = "DELETE FROM student WHERE name = '小張'";
if($db->exec($sqlQuery)){
echo "刪除成功";
}
//上面可以使用try catch語句給程序添加容錯方案
?>
```
#### 15.18 Smarty模板
Smarty是使用PHP寫出來的模板引擎。它分離了內容和邏輯代碼,可以將原來與HTML代碼混寫的PHP代碼分離出來。許多框架內置或者改良Smarty模板的功能加入了其中。Smarty模板有在視圖層進行邏輯運算、流程控制等功能。
##### MVC結構
MVC是指的是`模型`、`視圖`、`控制器`。模型負責數據的組織結構,視圖負責呈現用戶界面,控制器負責流程的邏輯控制。
所以MVC結構就是把一個程序的輸入、處理過程、輸出分開。當請求來到時,'控制器'對請求做出反應,調用'模型'和'視圖',將相關的代碼和數據返回來滿足用戶需求。
##### Smarty安裝配置
1. 首先在官網下載Smarty軟件包。
2. 解壓軟件包到網站目錄下。
3. 編輯php.ini文件,添加包含Smarty類庫文件路徑。`include_path='[Smarty類庫文件路徑]'`。
PHP和HTML混寫例子:
```
<html>
<body>
<p>
<?php echo "這是HTML與PHP混寫"; ?>
</p>
</body>
</html>
```
使用Smarty模板引擎分離視圖和控制器例子:
index.php文件
```
<?php
require_once('Smarty.class.php');
$smarty = new Smarty;
$smarty->template_dir = '/data/wwwroot/demo/templates';
$smarty->config_dir = '/data/wwwroot/demo/config';
$smarty->cache_dir = '/data/wwwroot/demo/cache';
$smarty->compile_dir = '/data/wwwroot/demo/templates_c';
$smarty->assign('message','1');//渲染了message
$smarty->display('index.tpl');//渲染給了index.tpl模板文件
```
index.tpl文件
```
<html>
<body>
<p>message:{$message}</p>//這里將上面渲染的message參數展示出來
</body>
</html>
```
從上面可以看出來,Smarty模板引擎比起PHP和HTML混寫,將視圖層和控制器層分離開來。在實際開發環境中,模板引擎是非常重要的。
##### 流程控制
```
<html>
<body>
{if $message == '1'}<p>message:{$message}</p>//這里將上面渲染的message參數展示出來
{elseif $message == '2'}<p>message:2</p>
</body>
</html>
```