ICT Elective B · Web Application Development ICT 選修乙 · 網絡應用程式開發

Chapter 4: Connecting to Database 第四章:連接數據庫

PHP III — Learn to connect web servers to databases, execute SQL queries, retrieve data, and protect against SQL injection attacks. PHP III — 學習如何將網絡伺服器連接到數據庫、執行 SQL 查詢、取得數據,並防範 SQL 注入攻擊。

5
Key API Functions核心API函數
12
Quiz Questions測驗題目
20
Flashcards閃卡
6
Past Questions歷屆試題

Chapter Contents 章節目錄

Click any card to navigate to that section 點擊任意卡片跳轉至對應章節

Core Concepts核心概念
Web server & database architecture網絡伺服器與數據庫架構
mysqli APImysqli API
Connect, query, fetch functions連接、查詢、取得數據函數
SQL InjectionSQL 注入
Security risks & prevention安全風險與防範
Quiz互動測驗
12 questions, auto-graded12題,自動評分
Flashcards閃卡練習
20 cards, 3D flip20張卡片,3D翻轉
Past Questions歷屆試題
Exam practice with answers考試練習附參考答案

Core Concepts 核心概念

4.1 Introduction — What is Database Connectivity? 4.1 簡介 — 什麼是數據庫連接?

In previous chapters, we learned HTML forms, HTTP requests, data validation, and cookies. Now we reach the most powerful part of server-side scripting — connecting our web server to a database. 在前幾章中,我們學習了 HTML 表單、HTTP 請求、數據驗證和 Cookie。現在我們進入伺服器端腳本最強大的部分 — 將網絡伺服器連接到數據庫。

A database allows us to store information such as usernames, passwords, product listings, and much more. This enables us to build real-world applications like booking systems, discussion forums, shopping sites, and online surveys. 數據庫讓我們能夠存儲用戶名、密碼、產品列表等各種信息。這使我們能夠構建現實世界的應用程序,如預訂系統、討論論壇、購物網站和在線調查。

Prerequisites 前置知識
You should be familiar with SQL queries from the compulsory curriculum (Compulsory A2, Chapter 5 — Information Processing). Review SELECT, WHERE, LIKE, ORDER BY, and aggregate functions (COUNT, SUM) before proceeding. 你應已熟悉必修課程中的 SQL 查詢(必修 A2 第5章 — 資訊處理)。在繼續之前,請複習 SELECT、WHERE、LIKE、ORDER BY 以及聚合函數(COUNT、SUM)。

4.2 Web Server and Database as Separate Entities 4.2 網絡伺服器與數據庫是獨立的實體

A web server and a database are two separate entities that operate independently. While clients and servers communicate via HTTP, a web server uses an Application Programming Interface (API) to communicate with a database. 網絡伺服器和數據庫是兩個獨立的實體,各自獨立運行。客戶端和伺服器通過 HTTP 通信,而網絡伺服器則通過應用程式介面 (API) 與數據庫通信。

Request-Response Flow: 請求-回應流程:


Client客戶端
HTTP RequestHTTP 請求
→

Web Server網絡伺服器
Runs PHP執行 PHP
→

Database數據庫
SQL QuerySQL 查詢
→

HTML ResponseHTML 回應
Sent to client發送至客戶端
● Client-side客戶端 ● Server-side伺服器端 ● Database-side數據庫端

4 Steps of a Database-Backed Web Request 數據庫支持的網絡請求的4個步驟

  • 1

    Client sends HTTP request客戶端發送 HTTP 請求

    The user's browser sends a request to the web server (e.g., visiting a PHP page).用戶的瀏覽器向網絡伺服器發送請求(例如,訪問一個 PHP 頁面)。

  • 2

    Web server executes PHP and sends SQL query網絡伺服器執行 PHP 並發送 SQL 查詢

    The PHP script runs on the server. It forms an SQL query and sends it to the database using the mysqli API.PHP 腳本在伺服器上運行。它形成一個 SQL 查詢,並使用 mysqli API 將其發送到數據庫。

  • 3

    Database returns query result數據庫返回查詢結果

    The database executes the SQL and returns the result set back to the web server.數據庫執行 SQL 並將結果集返回給網絡伺服器。

  • 4

    Web server generates HTML and sends response網絡伺服器生成 HTML 並發送回應

    The PHP script uses the query result data to generate an HTML page, which is returned to the client's browser.PHP 腳本使用查詢結果數據生成 HTML 頁面,然後返回給客戶端的瀏覽器。

mysqli APImysqli API

A PHP extension that provides functions to connect and interact with MySQL databases. "mysqli" stands for MySQL Improved.一個 PHP 擴展,提供連接和與 MySQL 數據庫交互的函數。"mysqli" 代表 MySQL Improved(改進版 MySQL)。

API Function vs Built-in FunctionAPI 函數 vs 內置函數

API functions like mysqli_connect() involve network communications and can fail unexpectedly (e.g., database offline). Always implement error-handling mechanisms.像 mysqli_connect() 這樣的 API 函數涉及網絡通信,可能意外失敗(例如數據庫離線)。務必實現錯誤處理機制。

Query Result Object查詢結果對象

mysqli_query() returns a mysqli_result object for SELECT queries — think of it as a table representing the query results. You need mysqli_fetch_assoc() to access individual rows.mysqli_query() 為 SELECT 查詢返回一個 mysqli_result 對象 — 可以把它想像成代表查詢結果的表格。你需要 mysqli_fetch_assoc() 來訪問各行數據。

Associative Array關聯數組

mysqli_fetch_assoc() converts each row into an associative array where column names are the keys. E.g., $row["name"] accesses the "name" column value.mysqli_fetch_assoc() 將每行轉換為關聯數組,其中列名是鍵。例如,$row["name"] 訪問 "name" 列的值。

mysqli API Functions mysqli API 函數

Quick Reference: Commonly Used Functions 快速參考:常用函數

Function函數 Purpose用途 Returns (Success)返回值(成功) Returns (Fail)返回值(失敗)
mysqli_connect() Connect web server to database連接網絡伺服器到數據庫 mysqli object ($link)mysqli 對象 ($link) false
mysqli_query() Execute SQL query on database在數據庫上執行 SQL 查詢 mysqli_result object (SELECT) / true (INSERT/UPDATE/DELETE)mysqli_result 對象(SELECT)/ true(INSERT/UPDATE/DELETE) false
mysqli_fetch_assoc() Fetch next row as associative array以關聯數組形式獲取下一行 Associative array (one row)關聯數組(一行) NULL
mysqli_num_rows() Count rows in query result統計查詢結果中的行數 Integer (number of rows)整數(行數) —
mysqli_real_escape_string() Sanitise string for SQL query清理字符串以用於 SQL 查詢 Sanitised string已清理的字符串 —

mysqli_connect()

Used to establish a connection between a PHP script and a MySQL database. Requires 4 parameters. 用於在 PHP 腳本和 MySQL 數據庫之間建立連接。需要4個參數。

<?php $server = 'db.mysite.net'; // ① Hostname of database server $username = 'web_server_01'; // ② Username $password = 'CastleInTheSky'; // ③ Password $database = 'afflatus_mall'; // ④ Database name $link = mysqli_connect($server, $username, $password, $database); if (!$link) { die("ERROR: Web server could not connect to database!"); } ?>
Common Parameter Values常見參數值
  • 'localhost' / '127.0.0.1': Database is on the same machine as the web server (common in development with XAMPP)數據庫與網絡伺服器在同一台機器上(使用 XAMPP 開發時常見)
  • 'root': The highest-privilege database account — only use in local development, NEVER in production!最高權限的數據庫賬戶 — 僅在本地開發中使用,生產環境中絕對不要使用!

mysqli_query()

Executes an SQL statement on the connected database. Takes the connection object and the SQL string as parameters. 在已連接的數據庫上執行 SQL 語句。以連接對象和 SQL 字符串作為參數。

// Step 1: Form the SQL query as a string $query = "SELECT * FROM restaurants WHERE category = 'Asian'"; // Step 2: Execute the query using the $link "ticket" $result = mysqli_query($link, $query); // Using aggregate function — define an alias! $query2 = "SELECT COUNT(*) AS num_res FROM restaurants"; $result2 = mysqli_query($link, $query2);
Important: Use lowercase column names重要:使用小寫列名
When using the wildcard (*) in SQL, all column names in the result are lowercase. Always use lowercase when accessing $row indexes to avoid bugs. 在 SQL 中使用通配符 (*) 時,結果中所有列名都是小寫的。訪問 $row 索引時務必使用小寫,以避免錯誤。

mysqli_fetch_assoc() with while loop 配合 while 循環

Fetches one row from the query result as an associative array. Used in a while loop to iterate through all rows. 從查詢結果中以關聯數組形式獲取一行。在 while 循環中使用以遍歷所有行。

$query = "SELECT * FROM restaurants WHERE category = 'Asian'"; $result = mysqli_query($link, $query); while ($row = mysqli_fetch_assoc($result)) { // $row is an associative array for each row // Indexes = column names, Values = cell values echo "<li>{$row["name"]} on floor {$row["floor"]}</li>"; } // Output: <li>Ryan's Dim Sum on floor 3</li> // <li>Thai at Shirley on floor 2</li>

Why does the while loop work with an assignment operator (=)? 為什麼 while 循環可以使用賦值運算符 (=)?

In PHP, when an assignment statement is used in a while-loop condition: 在 PHP 中,當賦值語句用於 while 循環條件時:

  1. The value on the right (mysqli_fetch_assoc($result)) is first assigned to the variable on the left ($row).右側的值(mysqli_fetch_assoc($result))首先被賦值給左側的變量($row)。
  2. PHP then evaluates the assigned value: if it's NULL, FALSE, 0, or empty string → loop stops. Otherwise → loop continues.PHP 然後評估賦值的值:如果是 NULL、FALSE、0 或空字符串 → 循環停止。否則 → 循環繼續。
  3. mysqli_fetch_assoc() returns NULL when there are no more rows — this stops the loop automatically.當沒有更多行時,mysqli_fetch_assoc() 返回 NULL — 這會自動停止循環。

Note: mysqli_fetch_assoc() also removes the top row from the result object each time it's called (pass-by-reference). 注意:每次調用 mysqli_fetch_assoc() 時,它還會從結果對象中移除頂部行(按引用傳遞)。

mysqli_num_rows() — Check if results exist檢查是否有結果

if (mysqli_num_rows($result) > 0) { while ($row = mysqli_fetch_assoc($result)) { echo "{$row["name"]}<br>"; } } else { echo "No results found."; }

4.3 Parsing Variables into SQL Statements 4.3 將變量解析到 SQL 語句中

Since SQL statements are strings in PHP, you can insert (parse) variables to create dynamic queries with different filter or sort criteria. 由於 SQL 語句在 PHP 中是字符串,你可以插入(解析)變量來創建具有不同過濾或排序條件的動態查詢。

$c = "US"; // Enclose text values in SINGLE QUOTES in the SQL string! $query = "SELECT name, country, population FROM city_info WHERE country = '{$c}'"; // Result: SELECT name, country, population FROM city_info WHERE country = 'US'
Common Mistake常見錯誤
Always enclose text (string) filter values in single quotes inside the SQL string: WHERE country = '$c'. Missing the quotes is a painful bug that is hard to identify! 在 SQL 字符串中,文本(字符串)過濾值務必用單引號括起來:WHERE country = '$c'。漏掉引號是一個難以識別的痛苦 bug!

SQL Injection & Security SQL 注入與安全

What is SQL Injection? 什麼是 SQL 注入?

SQL injection is an attack method that exploits server scripts that use user-provided values without precautions in SQL queries. A hacker can "inject" malicious SQL commands and cause catastrophic consequences. SQL 注入是一種攻擊方法,它利用在 SQL 查詢中不加防範地使用用戶提供值的伺服器腳本。黑客可以"注入"惡意 SQL 命令,造成災難性後果。

Real-World Impact現實影響
In 2021, the Open Web Application Security Project (OWASP) classified injection attacks among the top 3 security risks for web developers. Many data breaches involving stolen credit card information exploit SQL injection. 2021年,開放式網絡應用安全項目(OWASP)將注入攻擊列為網絡開發者面臨的前三大安全風險。許多涉及信用卡信息被盜的數據洩露事件都利用了 SQL 注入。

Attack Example 攻擊示例

Suppose a hacker enters the following into a city search form: 假設黑客在城市搜索表單中輸入以下內容:

// User enters in form field: Hong Kong'; DROP TABLE city_info-- // WITHOUT sanitization, $query becomes: SELECT * FROM city_info WHERE country = 'Hong Kong'; DROP TABLE city_info-- // This DELETES the entire city_info table!
Why this works為什麼這樣有效
The single quote ' closes the string in SQL. The semicolon ; ends the first query. DROP TABLE city_info becomes a second, executed command. -- comments out the rest. 單引號 ' 在 SQL 中結束字符串。分號 ; 結束第一個查詢。DROP TABLE city_info 成為第二個被執行的命令。-- 注釋掉其餘部分。

Prevention: mysqli_real_escape_string() 防範:mysqli_real_escape_string()

Always sanitise client-sent values before using them in an SQL query. mysqli_real_escape_string() escapes special characters (like ', ") by adding a backslash, preventing them from being interpreted as SQL syntax. 在 SQL 查詢中使用客戶端發送的值之前,務必先清理這些值。mysqli_real_escape_string() 通過添加反斜杠來轉義特殊字符(如 '、"),防止它們被解釋為 SQL 語法。

// WRONG (vulnerable to SQL injection): $query = "SELECT * FROM city_info WHERE country = '$_GET["city"]'"; // CORRECT (sanitize first!): $safe_city = mysqli_real_escape_string($link, $_GET["city"]); $query = "SELECT * FROM city_info WHERE country = '{$safe_city}'";

Before sanitization:清理前:

Hong Kong'; DROP TABLE city_info--

After sanitization:清理後:

Hong Kong\'; DROP TABLE city_info--

The escaped \' is treated as a literal apostrophe character, not as SQL syntax. The DROP TABLE command is never executed. 轉義後的 \' 被視為字面上的撇號字符,而不是 SQL 語法。DROP TABLE 命令永遠不會被執行。

Golden Rule黃金法則
Always sanitise client-sent values before using them in an SQL query! For most use cases, mysqli_real_escape_string() is sufficient. For advanced security, consider parameterised prepared statements. 在 SQL 查詢中使用客戶端發送的值之前,務必先清理! 對於大多數用例,mysqli_real_escape_string() 已足夠。對於高級安全性,考慮使用參數化預處理語句。

Complete Secure Example: City Search Engine 完整安全示例:城市搜索引擎

<?php $link = mysqli_connect($server, $username, $password, $database); if (!$link) { die("ERROR: Could not connect!"); } ?> <form method="GET" action=""> <input type="text" name="city"> <input type="submit" name="submit"> </form> <?php if (isset($_GET["submit"])) { // ① Sanitise the user input $safe_city = mysqli_real_escape_string($link, $_GET["city"]); // ② Form the SQL query with sanitised value $query = "SELECT * FROM city_info WHERE name = '{$safe_city}'"; // ③ Execute and iterate results $result = mysqli_query($link, $query); while ($row = mysqli_fetch_assoc($result)) { echo "<p>{$row["name"]}: Population {$row["population"]}</p>"; } } ?>

Interactive Quiz 互動測驗

12 questions covering all key topics. Select your answers and submit to see your score. 12道題目涵蓋所有重要主題。選擇答案後提交以查看分數。

Q1. What does mysqli_connect() return when the connection to the database is successful? Q1. 當成功連接到數據庫時,mysqli_connect() 返回什麼?

Q2. Which of the following is the correct order of parameters for mysqli_connect()? Q2. 以下哪個是 mysqli_connect() 參數的正確順序?

Q3. What does mysqli_query() return when a SELECT query fails? Q3. 當 SELECT 查詢失敗時,mysqli_query() 返回什麼?

Q4. What does mysqli_fetch_assoc() return when there are no more rows in the result? Q4. 當結果中沒有更多行時,mysqli_fetch_assoc() 返回什麼?

Q5. A database table "students" has columns: id, name, grade. After running $result = mysqli_query($link, "SELECT * FROM students");, how do you access the name of the current row in the while loop? Q5. 數據庫表 "students" 有列:id, name, grade。執行 $result = mysqli_query($link, "SELECT * FROM students"); 後,如何在 while 循環中訪問當前行的 name?

Q6. Which function is used to count the number of rows in a query result? Q6. 哪個函數用於統計查詢結果中的行數?

Q7. What is SQL injection? Q7. 什麼是 SQL 注入?

Q8. What does mysqli_real_escape_string() do? Q8. mysqli_real_escape_string() 的作用是什麼?

Q9. When using the wildcard (*) in a SELECT query, what case are the column names in the returned result? Q9. 在 SELECT 查詢中使用通配符 (*) 時,返回結果中的列名是什麼大小寫?

Q10. What is the hostname value 'localhost' commonly used for in mysqli_connect()? Q10. 在 mysqli_connect() 中,主機名值 'localhost' 通常用於什麼情況?

Q11. When using an aggregate function like COUNT(*) in a PHP SQL query, what must you do to access the result? Q11. 在 PHP SQL 查詢中使用像 COUNT(*) 這樣的聚合函數時,你必須做什麼才能訪問結果?

Q12. What is the correct way to parse a variable $category into an SQL WHERE clause for a text field? Q12. 將變量 $category 解析到文本字段的 SQL WHERE 子句中的正確方式是什麼?

Flashcard Practice 閃卡練習

Click any card to flip it and reveal the answer. 20 key terms and concepts. 點擊任意卡片翻轉以查看答案。20個關鍵術語和概念。

Click cards to flip • 點擊翻轉 • 0/20 flipped已翻轉

Past Questions & Practice 歷屆試題與練習

Exam-style questions based on chapter content. Click "View Reference Answer" to see the model answer. 基於章節內容的考試風格題目。點擊「查看參考答案」查看範本答案。

Basic基礎 Connection連接 1 mark分
State the purpose of mysqli_connect() in a PHP script. 說明 PHP 腳本中 mysqli_connect() 的用途。
Reference Answer:參考答案:
mysqli_connect() is used to establish a connection between the web server (PHP script) and a MySQL database. It requires 4 parameters: hostname, username, password, and database name. It returns a mysqli object on success, or false on failure. mysqli_connect() 用於在網絡伺服器(PHP 腳本)和 MySQL 數據庫之間建立連接。它需要4個參數:主機名、用戶名、密碼和數據庫名稱。成功時返回 mysqli 對象,失敗時返回 false。
Basic基礎 SQL QuerySQL 查詢 2 marks分
Kathy is developing list_places.php. She wants to select the code, city, and priority of all places in Norway from the PLACES table. Write the SQL query string for line 18: $query = ""; Kathy 正在開發 list_places.php。她想從 PLACES 表中選取所有挪威地點的 code、city 和 priority。為第18行寫出 SQL 查詢字符串:$query = "";
Reference Answer:參考答案:
"SELECT code, city, priority FROM PLACES WHERE country = 'Norway'"

Note: Use lowercase for column names (code, city, priority). Enclose the text value 'Norway' in single quotes. 注意:列名使用小寫(code, city, priority)。文本值 'Norway' 用單引號括起來。

Medium中等 Fetch Data取得數據 3 marks分
Lily is designing a hotel booking website. The database table "rooms" has columns: room_number, room_type, price, is_available. She wants to display all available rooms (is_available = 1). Complete lines 24-29 of the code to display room_number, room_type, and price for each available room: Lily 正在設計一個酒店預訂網站。數據庫表 "rooms" 有列:room_number, room_type, price, is_available。她想顯示所有可用房間(is_available = 1)。完成第24-29行代碼,顯示每個可用房間的 room_number、room_type 和 price:
$query = "SELECT * FROM rooms WHERE is_available = 1";
$result = mysqli_query($link, $query);

while ($row = mysqli_fetch_assoc($result)) {
    echo "Room number: ___________<br>";
    echo "Room type: ___________<br>";
    echo "Price: ___________<br><br>";
}
Reference Answer:參考答案:
echo "Room number: {$row["room_number"]}<br>";
echo "Room type: {$row["room_type"]}<br>";
echo "Price: {$row["price"]}<br><br>";

Key points: (1) Use $row["column_name"] to access each column value. (2) Since wildcard (*) is used, column names are all lowercase. (3) Use curly braces {$row["..."]} for variable parsing inside strings. 要點:(1) 使用 $row["column_name"] 訪問每列的值。(2) 由於使用了通配符 (*),列名全部小寫。(3) 在字符串中解析變量時使用大括號 {$row["..."]}。

Medium中等 Security安全 2 marks分
A developer modified their code to add mysqli_real_escape_string() before using user input in an SQL query. State the benefit of this modification. 開發者修改了代碼,在 SQL 查詢中使用用戶輸入之前添加了 mysqli_real_escape_string()。說明此修改的好處。
Reference Answer:參考答案:
To prevent SQL injection attacks. By escaping special characters (such as single quotes and double quotes) in user-provided strings, malicious SQL commands cannot be injected into the query. This protects the database from unauthorized access or destruction. 防止 SQL 注入攻擊。通過轉義用戶提供字符串中的特殊字符(如單引號和雙引號),惡意 SQL 命令無法注入到查詢中。這保護數據庫免受未授權訪問或破壞。
Hard困難 Dynamic Query動態查詢 4 marks分
Gary's website has a search form for headphones with fields: name (text), brand (text), max_price (number). The form uses GET method. Complete the PHP code below to build a dynamic SQL query that filters by the non-empty fields: Gary 的網站有一個耳機搜索表單,包含字段:name(文本)、brand(文本)、max_price(數字)。表單使用 GET 方法。完成以下 PHP 代碼,構建一個根據非空字段過濾的動態 SQL 查詢:
$sql = "SELECT * FROM headphones WHERE TRUE";

if (!empty($name)) {
    $sql = $sql . ___________;
}
if (!empty($brand)) {
    $sql = $sql . ___________;
}
if (!empty($max_price)) {
    $sql = $sql . ___________;
}
$result = mysqli_query($conn, $sql);
Reference Answer:參考答案:
" AND name = '" . $name . "'"
" AND brand = '" . $brand . "'"
" AND price <= '" . $max_price . "'"

Key points: (1) Start with WHERE TRUE so additional AND conditions can be appended without issues. (2) Text values (name, brand) must be enclosed in single quotes. (3) Numeric values (max_price) can also be enclosed in single quotes for consistency. (4) In production, always sanitise with mysqli_real_escape_string() first! 要點:(1) 從 WHERE TRUE 開始,這樣可以無問題地附加額外的 AND 條件。(2) 文本值(name, brand)必須用單引號括起來。(3) 數值(max_price)也可以用單引號括起來以保持一致性。(4) 在生產環境中,務必先用 mysqli_real_escape_string() 清理!

Hard困難 Comprehensive綜合 6 marks分
A student search form has fields: name (text input), gender (select: B=Both, F=Female, M=Male). The form uses GET. (a) Suggest why GET should be used instead of POST. (1 mark) (b) Complete the PHP code to build the WHERE clause — filter by name using LIKE '%name%' and by gender if not "Both". (3 marks) (c) Explain how using mysqli_real_escape_string() on $name before forming the query helps security. (2 marks) 學生搜索表單有字段:name(文本輸入)、gender(選擇:B=兩者, F=女, M=男)。表單使用 GET。(a) 建議為什麼應使用 GET 而不是 POST。(1分) (b) 完成 PHP 代碼以構建 WHERE 子句 — 使用 LIKE '%name%' 按名稱過濾,如果不是"兩者"則按性別過濾。(3分) (c) 解釋在形成查詢之前對 $name 使用 mysqli_real_escape_string() 如何幫助安全性。(2分)
Reference Answer:參考答案:

(a) The request retrieves data (no modification to server state), so GET is appropriate. GET requests can also be bookmarked/shared via URL.該請求是取得數據(不修改伺服器狀態),因此 GET 是合適的。GET 請求也可以通過 URL 收藏/分享。

(b)

if ($_GET["gender"] == "M") { $c = "AND gender = 'M'"; }
elseif ($_GET["gender"] == "F") { $c = "AND gender = 'F'"; }
else { $c = ""; }

$n = mysqli_real_escape_string($link, $_GET["name"]);
$query = "SELECT * FROM STUDENTS WHERE name LIKE '%{$n}%' {$c}";

(c) mysqli_real_escape_string() escapes special characters (e.g., single quotes) in the user-provided name. This prevents SQL injection — a malicious user cannot inject extra SQL commands through the name field, protecting the database from unauthorized queries or data destruction.mysqli_real_escape_string() 轉義用戶提供的名稱中的特殊字符(例如單引號)。這防止了 SQL 注入 — 惡意用戶無法通過名稱字段注入額外的 SQL 命令,保護數據庫免受未授權查詢或數據破壞。