必看!在開始前一定要做的事:Disable cache
進入 http://localhost:8080 頁面,打開 devtool 的 Network tab,把 Disable cache 勾起來
如果之後碰到問題時,就要打開 devtool 切到 Network tab,按下重新整理,頁面的所有資源就會被重新載入(清空 cache)
注意!一定要在 devtool 打開的情況下才會清空 cache
再次複習 PHP 執行流程
先看靜態檔案的執行流程
- 我(client 端)發了一個 request 到 server (Apache),指定要 style.css 這支檔案
- server 會去找「我指定的那個檔案 style.css」
- 找到 style.css 檔案後,回傳 response 給 client 端(檔案內容是什麼就回傳什麼)
例如:
style.css 的內容是這樣:
body{
background-color: orange;
}
當我用網頁去存取 style.css 時,檔案內容就會原封不動的傳回來
即使在 css 的檔案內容中有 php 的程式碼,但是對於 server 來說,「.css 結尾的」就是靜態檔案,因此還是會把檔案內容原封不動的傳回來
style.css:
body{
background-color: <?php echo 'orange'; ?>;
}
靜態網頁、動態網頁的差別就在於:有沒有去執行 php 程式碼
再看動態檔案的執行流程
- 我(client 端)發一個 request 到 server (Apache),指定要 index.php 這支檔案
- server 會去找「我指定的那個檔案 index.php」 ----- 到目前為止,流程都跟靜態檔案一樣
- 因為 server 發現這是一個 php 檔案,所以 server 在回傳 response 前,會先去執行這支 php 檔案
- 「執行完 php 檔產生的結果」會回到 server,server 再把這個結果當作 response 傳回 client 端
會去執行 php 程式碼,是因為 server 有特別設定過
Apache server 會回傳 php 執行後的結果,是因為 Apache 這個 server 有做一些設定,它才會去執行 php 檔案
例如:
test.php 的內容是這樣:
這段 php 程式碼執行完後,會輸出的結果是「I am Mickey!」
<?php
echo 'I am Mickey!';
?>
當我用網頁去存取 test.php 時,回傳的內容會是上面那段 php 程式碼輸出的內容「I am Mickey!」,而不會是一整段 php 的程式碼
注意!即使是在 .php 的檔案內,一定要用 <?php ?>
包住的內容才會被 php 執行,在 <?php ?>
外面的內容也是會原封不動的傳回來(以純文字的方式)
test.php:
<?php
echo 'I am Mickey!';
?>
body{
background-color: orange;
}
所以,就可以利用 php 的這個特性,來做出一個動態的網頁,例如:印出當下的時間
test.php:
<?php
echo 'I am Mickey!';
?>
<h1>Now: <?php echo date('Y-m-d H:i:s'); ?></h1>
根據 server 設定的不同,「.php 結尾的」也有可能變成「靜態檔案」
例如,如果把 test.php 檔案丟到 GitHub 上面,也會看到是「原封不動的回傳檔案內容,是純文字」,而不會是 php 執行後的結果
從前端傳資料給後端:GET 與 POST
PHP 要怎麼跟網頁前端交換資料呢?
可以用 GET 或是 POST 的方式發送 request
- 我從瀏覽器發送一個 request,在 request 會有一個欄位是 URL(就是:我想要請求的網址是什麼),例如 http://localhost:8080/huli/be101/data.php
- server 看到這個 URL,就會去尋找對應的檔案(data.php)
- 找到 data.php 後執行 PHP
- 執行完後的結果會回到 server
- server 再把這個執行後的結果當作 response 回傳到瀏覽器
方式一:GET
在 URL(前端) 用 query string 傳參數到 PHP(後端)
「用 query string 傳參數」這種形式,所用的 method 是 GET (跟 HTTP 有關)
a=1
這個 query string 會自動存到 data.php 檔案的 $_GET
變數裡面(data.php 會自動幫我準備好 $_GET
這個特殊的變數)
然後,我就可以用 $_GET
這個變數來取得我用 query string 所傳的參數
範例如下
data.php:
<?php
echo 'Great! <br>';
echo 'a: ' . $_GET['a'] . '<br>';
echo 'b: ' . $_GET['b'] . '<br>';
print_r($_GET);
?>
output:
用 isset()
這個 function 來判斷是否有設置這個 index(key)
用 isset($_GET['a'])
判斷:如果有設置 a 這個 index 的話,才執行 echo 'a: ' . $_GET['a'] . '<br>';
這段
<?php
echo 'Great! <br>';
if (isset($_GET['a'])) {
echo 'a: ' . $_GET['a'] . '<br>';
}
if (isset($_GET['b'])) {
echo 'b: ' . $_GET['b'] . '<br>';
}
print_r($_GET);
?>
output:
前端通常會用表單的方式來跟 PHP 交換資料
範例如下
我在 index.php 寫了一個 form
在 form 裡面有幾個參數要填:
- method 要填的是 GET
- action 要填的是:要把這個表單的內容傳到哪裡去
「input
的 name
屬性」就會是「變數 $_GET
所接收到的 index 值」
index.php:
<form method="GET" action="data.php">
username: <input name="username">
age: <input name="age">
<input type="submit">
</form>
填好資料,按下 submit 後,就會跳到 data.php 的頁面
data.php 的內容如下:
<?php
echo 'Great! <br>';
print_r($_GET);
?>
瀏覽器就會用 GET 的方式,自動把我剛剛在 form 填寫的值帶到 URL 去
用 Preserve log 看看發出的 request 長什麼樣子
- Preserve log 的作用就是:讓瀏覽器除了記下最後頁面的 log,也會幫我記下中間頁面跳轉的 log。如果沒有勾選 Preserve log 的話,瀏覽器就只會記住「最新的頁面」的記錄而已,中間如果有頁面跳轉的話就不會被記錄到
在 index.php 打開 devtool 的 Network tab,把 Preserve log 勾起來,一樣填好表單後按下 submit,在下方就可以看到:
form 幫我做的事情就是:用 query string 的方式把我填的值帶到 URL 去,然後送出一個 GET 的 request 到這個 URL
index.php:
<form method="GET" action="data.php">
username: <input name="username">
age: <input name="age">
<input type="submit">
</form>
錯誤寫法:不能用 isset()
來檢查欄位是否有填寫
data.php:
<?php
if (!isset($_GET['username']) || !isset($_GET['age'])) {
echo '資料有缺,請再次填寫';
} else {
echo 'Hello! ' . $_GET['username']. '<br>';
echo 'Your age is ' . $_GET['age'];
}
?>
如果用 isset()
來檢查欄位是否有填寫,會發現:即使欄位沒有填寫,按下 submit 後還是不會印出「資料有缺,請再次填寫」的訊息
原因為:
isset()
是用來檢查「此 key 是否有被設置」的,所以就算我沒有填寫該欄位(value 是空值),但是因為兩個欄位的 key 是有傳入的(有設置 username
和 age
),所以 isset($_GET['username'])
和 isset($_GET['age'])
的結果都會是 true
必須要「沒有設置這個 key」,isset()
才會是 false
正確寫法:用 empty()
來檢查是否為空值
針對這種欄位的檢查,要使用 empty()
來檢查是否為空值
另一個要補充的點是,比起上面用 else
,更推薦使用 exit()
來達到這樣的效果:出現「資料有缺,請再次填寫」後就跳開,讓程式碼不要繼續執行下去印出下面的 Hello! 這些字
data.php:
<?php
if (empty($_GET['username']) || empty($_GET['age'])) {
echo '資料有缺,請再次填寫';
exit();
}
echo 'Hello! ' . $_GET['username']. '<br>';
echo 'Your age is ' . $_GET['age'];
?>
方式二:POST
如果把 method 改成 POST
index.php:
<form method="POST" action="data.php">
username: <input name="username">
age: <input name="age">
<input type="submit">
</form>
按下 submit 後,會發現 URL 不會帶入我填寫的值,原因為:
我沒有用 GET 的方式帶入 query string
打開 devtool 可以看到:
最底下有一個 Form Data 寫著我剛剛填寫的資料,代表:我用 POST 的方式,把表單的資料送到 data.php 了,只是因為 data.php 目前還沒去處理這些資料
用 $_POST
去存取資料
已經用 POST 的方式,把資料送到 data.php 去了
URL: http://localhost:8080/saffran/data.php
method: POST
PHP 會幫我準備好一個特別的變數 $_POST
,去自動存取 Form Data 的資料
因此,我就可以用 $_POST
去存取到我用 POST 送到 data.php 的資料了
index.php:
<form method="POST" action="data.php">
username: <input name="username">
age: <input name="age">
<input type="submit">
</form>
data.php:
<?php
if (empty($_POST['username']) || empty($_POST['age'])) {
echo '資料有缺,請再次填寫';
exit();
}
echo 'Hello! ' . $_POST['username']. '<br>';
echo 'Your age is ' . $_POST['age']. '<br>';
print_r($_POST);
?>
從 PHP 連線到 MySQL 資料庫
在新增使用者帳號時
- Host name 代表:可以從哪裡連到資料庫,要從本機端連的話,就填寫 localhost 即可
- 記得要把 Global privileges 勾選 Check all
在 Operations 底下可以調整資料庫的編碼,選擇 utf8mb4_general_ci 或是 utf8mb4_unicode_ci
在 PHP 要連到 MySQL 資料庫的程式碼如下
data.php:
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db';
$conn = new mysqli($server_name, $username, $password, $db_name);
?>
$conn = new mysqli($server_name, $username, $password, $db_name);
意思就是:我要 new 一個 mysqli 的 instance 出來(用來跟資料庫建立連線),跟資料庫建立連線之後,會回傳一些資訊到 $conn
變數
conn
是 connection 的簡寫
- 第一個參數是「server 名稱」,這裡因為我是連到本機端的資料庫,所以 server 名稱是 localhost
- 第二個參數是「帳號」
- 第三個參數是「密碼」
- 第四個參數是「資料庫名稱」
當資料庫連線錯誤時
在 PHP,要存取物件屬性時,要使用 ->
這個符號
$conn
變數會是一個物件,此物件裡面有一個屬性叫做 connect_error
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db33';
$conn = new mysqli($server_name, $username, $password, $db_name);
echo $conn->connect_error;
?>
我故意輸入一個錯誤的 db 名稱,然後用 echo $conn->connect_error;
印出來看看
echo $conn->connect_error;
印出的是下方紅色箭頭那句
之所以上面還會出現那段 Warning,是因為我的 PHP 有打開這個「顯示 Warning」的設定,這樣比較方便 debug
因此,可以這樣寫:
如果 $conn->connect_error
不是空值,那就顯示「資料庫連線錯誤:...」的提示訊息
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db33';
$conn = new mysqli($server_name, $username, $password, $db_name);
if (!empty($conn->connect_error)) {
echo '資料庫連線錯誤:' . $conn->connect_error . '<br>';
}
?>
或是也可以這樣寫:
die()
當資料庫連線錯誤時,就沒必要繼續往下執行程式碼了
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db33';
$conn = new mysqli($server_name, $username, $password, $db_name);
if (!empty($conn->connect_error)) {
die('資料庫連線錯誤:' . $conn->connect_error);
}
echo 'great!';
?>
die()
的作用是:會把 die()
小括號裡面的內容輸出之後,就讓整個 PHP 死掉,底下的程式碼都不會繼續執行
因此,echo 'great!';
不會被執行到
設定編碼、時區
用 query()
這個 function 來改變資料庫的一些設定:編碼與時區
$conn->query('SET NAMES UTF8');
是「設定編碼」,如果沒有設定編碼的話,使用中文會出現亂碼$conn->query('SET time_zone = "+8:00"');
是「把資料庫的時區設為台灣的時區」
注意!上面這兩行 query()
一定要放在 if (!empty($conn->connect_error)...
的後面,原因為:
連線完之後,要先檢查連線是否有錯誤。如果連線沒有錯誤,才繼續往下執行,這時候下 query()
去設定編碼與時區才不會出錯(如果連線錯誤,下 query()
就會出錯)
data.php:
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db';
$conn = new mysqli($server_name, $username, $password, $db_name);
if (!empty($conn->connect_error) {
die('資料庫連線錯誤:' . $conn->connect_error);
}
$conn->query('SET NAMES UTF8');
$conn->query('SET time_zone = "+8:00"');
?>
連線資料庫的程式碼,完整版如下:
也可以進一步簡化,把 empty()
拿掉:
<?php
$server_name = 'localhost';
$username = 'saffran';
$password = 'rox';
$db_name = 'saffran_db';
$conn = new mysqli($server_name, $username, $password, $db_name);
if ($conn->connect_error) {
die('資料庫連線錯誤:' . $conn->connect_error);
}
$conn->query('SET NAMES UTF8');
$conn->query('SET time_zone = "+8:00"');
?>
把連線資料庫的程式碼獨立成一個檔案
通常在實務上會這樣做:
建立一個檔案叫做 conn.php,裡面只放「連線資料庫的程式碼」
當我在 data.php 裡面要連線到資料庫時,我只需要用 require_once()
引入 conn.php 即可:
<?php
require_once('conn.php');
echo 'great!';
?>
注意!conn.php 不會放到 Git 上面做版本控制
當我要把我的程式碼放到 Git 上面時,會把 conn.php 排除掉(加在 .gitignore 裡面),因為 conn.php 裡面有我資料庫的位置、帳號密碼,不可以暴露在網路上