PHP : 從零開始

這是一篇關於PHP從0開始的小電子書!

前言:PHP 在做什麼

PHP(PHP: Hypertext Preprocessor)是在伺服器端執行的程式語言。使用者用瀏覽器請求一個 .php 網址,Web 伺服器執行 PHP,通常查資料庫、組裝邏輯,最後輸出 HTML / JSON 回傳。

sequenceDiagram
  participant B as 瀏覽器
  participant S as Apache/Nginx + PHP
  participant D as MySQL

  B->>S: GET /users.php
  S->>D: SELECT * FROM users
  D->>S: 資料列
  S->>S: PHP 組裝回應
  S->>B: HTML 或 JSON

PHP 並非輸出HTML而是嵌入HTML標籤中,以特殊的<?php?>標籤來宣告PHP片段。


第 0 章:開發環境

快速開始

前往codesandbox線上編輯器,建立帳號後建立php專案

本地環境安裝

以下提供如果想要在自己本地安裝環境:

元件作用
PHP 8.2+語言執行環境(建議 8.1 以上)
Web ServerApache 或 Nginx(或用 PHP 內建伺服器開發)
MySQL / MariaDB / SQLite儲存資料(進階章節)
編輯器VS Code、PhpStorm

一鍵安裝包(擇一):

  • XAMPP(Windows / macOS / Linux)
  • Laragon(Windows,輕量好用)
  • Docker:php:8.3-apache 映像

在專案目錄執行:

php -S localhost:8000Code language: CSS (css)

瀏覽 http://localhost:8000/index.php 即可。適合學習,正式環境請用 Apache/Nginx。

info.php

<?php
phpinfo();Code language: HTML, XML (xml)

看到紫色資訊頁代表成功。正式環境務必刪除

CLI 模式

PHP 不只能跑網頁,也能在終端機執行:

php -r "echo 2 + 2;"
php script.phpCode language: JavaScript (javascript)

建議 VS Code 擴充

  • PHP Intelephense
  • PHP Debug(Xdebug)
  • EditorConfig

第 1 章:第一支 PHP 程式

基本規則

  1. 程式寫在 <?php ... ?> 內(檔尾可省略 ?>,建議省略避免多餘空白)
  2. 陳述以 ; 結尾
  3. 副檔名 .php
<?php
echo 'Hello, World!';Code language: HTML, XML (xml)

與 HTML 混寫

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <title><?= '我的網站' ?></title>
</head>
<body>
  <h1><?php echo '歡迎'; ?></h1>
  <p>現在時間:<?php echo date( 'Y-m-d H:i' ); ?></p>
</body>
</html>Code language: HTML, XML (xml)

<?= ... ?><?php echo ... ?> 的簡寫。

註解

// 單行註解
# 也是單行

/*
  多行
  註解
*/Code language: PHP (php)

第 2 章:變數、型別與運算子

變數
$ 開頭,區分大小寫:

<?php
$name = 'Alice';
$age  = 25;
$price = 19.99;
$active = true;Code language: HTML, XML (xml)

常見型別

型別範例
string'hello'
int42
float3.14
booltrue / false
array[1, 2, 3]
nullnull
objectnew User()

PHP 8+ 可宣告型別:

function add( int $a, int $b ): int {
    return $a + $b;
}Code language: PHP (php)

寬鬆轉型

'5' + 2;   // 7(字串自動轉數字)Code language: JavaScript (javascript)

實務上應明確轉型

$id = (int) $_GET['id'];
$price = (float) $row['price'];Code language: PHP (php)

重要運算子

$a + $b;    // 加
$a . $b;    // 字串連接(極常用)
$a === $b;  // 嚴格相等(型別+值)
$a ?? $b;   // $a 為 null 則用 $b
$a ?: $b;   // $a 為假值則用 $b
$a <=> $b;  // 太空船:比較大小回傳 -1/0/1Code language: PHP (php)

字串(兩種引號)

$name = 'Bob';
echo "Hello, $name";   // 雙引號可插值
echo 'Hello, $name';   // 單引號大部分原樣輸出Code language: PHP (php)

第 3 章:流程控制

if / elseif / else

<?php
$score = 85;

if ( $score >= 90 ) {
    echo 'A';
} elseif ( $score >= 80 ) {
    echo 'B';
} else {
    echo 'C';
}Code language: HTML, XML (xml)

替代語法(模板常用)

<?php if ( $logged_in ) : ?>
    <p>歡迎回來</p>
<?php else : ?>
    <p>請登入</p>
<?php endif; ?>Code language: HTML, XML (xml)

switch、match(PHP 8+)

<?php
$role = 'editor';

switch ( $role ) {
    case 'admin':
        $level = 3;
        break;
    case 'editor':
        $level = 2;
        break;
    default:
        $level = 1;
}

// match 更簡潔,必須涵蓋所有情況或 default
$label = match ( $role ) {
    'admin'   => '管理員',
    'editor'  => '編輯',
    default   => '訪客',
};Code language: HTML, XML (xml)

迴圈

<?php
for ( $i = 0; $i < 5; $i++ ) {
    echo $i;
}

$items = [ 'PHP', 'MySQL', 'HTML' ];
foreach ( $items as $item ) {
    echo $item;
}

foreach ( $user as $key => $value ) {
    echo "$key: $value";
}

while ( $row = $stmt->fetch() ) {
    // 處理每一列
}Code language: HTML, XML (xml)

break / continue

foreach ( $items as $item ) {
    if ( $item === 'skip' ) {
        continue; // 跳過本次
    }
    if ( $item === 'stop' ) {
        break;    // 結束迴圈
    }
}Code language: PHP (php)

第 4 章:函式

定義與回傳

<?php
function greet( string $name ): string {
    return 'Hello, ' . $name;
}

echo greet( 'World' );Code language: HTML, XML (xml)

預設參數與命名參數

function paginate( int $page = 1, int $per_page = 10 ): array {
    return [ 'page' => $page, 'per_page' => $per_page ];
}

paginate( per_page: 20, page: 2 ); // PHP 8 命名參數Code language: PHP (php)

可變參數

function sum( int ...$nums ): int {
    return array_sum( $nums );
}

sum( 1, 2, 3 ); // 6Code language: PHP (php)

防止重複定義

if ( ! function_exists( 'my_helper' ) ) {
    function my_helper() {
        // ...
    }
}Code language: JavaScript (javascript)

匿名函式與閉包

$multiplier = 3;
$fn = function ( int $n ) use ( $multiplier ): int {
    return $n * $multiplier;
};

echo $fn( 4 ); // 12Code language: PHP (php)

箭頭函式(PHP 7.4+)

$double = fn( int $n ) => $n * 2;Code language: PHP (php)

第 5 章:陣列

索引與關聯陣列

<?php
$list = [ 'apple', 'banana' ];
$user = [
    'id'   => 1,
    'name' => 'Alice',
    'role' => 'admin',
];

echo $user['name'];Code language: HTML, XML (xml)

常用函式

count( $arr );
empty( $arr );
isset( $arr['key'] );
array_key_exists( 'key', $arr );

$arr[] = 'new';              // 追加
array_push( $arr, 'a', 'b' );
array_pop( $arr );
array_map( fn( $x ) => $x * 2, [ 1, 2, 3 ] );
array_filter( $arr, fn( $x ) => $x > 0 );
in_array( 'apple', $list, true ); // 嚴格模式Code language: PHP (php)

解構

[ $first, $second ] = [ 10, 20 ];
[ 'name' => $name ] = $user;Code language: PHP (php)

** Spread**

$merged = [ ...$a, ...$b ];Code language: PHP (php)

第 6 章:字串處理

strlen( $s );
mb_strlen( $s, 'UTF-8' );     // 中文用這個
strpos( $haystack, $needle );
str_contains( $haystack, 'x' ); // PHP 8+
str_starts_with( $s, 'pre' );
str_ends_with( $s, 'suf' );
str_replace( 'old', 'new', $s );
preg_match( '/^\d+$/', $s );  // 正規表示式
trim( $s );
explode( ',', $s );
implode( '-', $arr );
sprintf( '%s 有 %d 筆', $name, $count );
number_format( 12345.6, 2 );  // 1,234.56Code language: PHP (php)

輸出跳脫(安全基礎)

echo htmlspecialchars( $text, ENT_QUOTES, 'UTF-8' );Code language: PHP (php)

防止 XSS:任何來自使用者或資料庫、要顯示在 HTML 的字串都應跳脫。


第 7 章:表單與超全域變數

超全域變數
在任何函式內都可存取:

變數內容
$_GETURL 查詢 ?page=2
$_POSTPOST body
$_SERVER請求方法、URI、IP
$_FILES上傳檔案
$_COOKIECookie
$_SESSIONSession(需 session_start()
$_ENV環境變數

GET 範例
search.php

<?php
$query = isset( $_GET['q'] ) ? trim( $_GET['q'] ) : '';
?>
<form method="get">
  <input type="search" name="q" value="<?= htmlspecialchars( $query, ENT_QUOTES, 'UTF-8' ) ?>">
  <button type="submit">搜尋</button>
</form>

<?php if ( $query !== '' ) : ?>
  <p>你搜尋了:<?= htmlspecialchars( $query, ENT_QUOTES, 'UTF-8' ) ?></p>
<?php endif; ?>Code language: HTML, XML (xml)

POST 範例

<?php
$errors = [];
$name   = '';

if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
    $name = trim( $_POST['name'] ?? '' );

    if ( $name === '' ) {
        $errors[] = '姓名不可空白';
    }

    if ( ! $errors ) {
        // 寫入資料庫或處理邏輯
        header( 'Location: /success.php' );
        exit;
    }
}
?>Code language: HTML, XML (xml)

重導向後 exit 很重要,避免繼續執行下方程式。

$_SERVER 常用鍵

$_SERVER['REQUEST_METHOD']; // GET / POST
$_SERVER['REQUEST_URI'];    // /path?query=1
$_SERVER['HTTP_HOST'];
$_SERVER['REMOTE_ADDR'];    // 客戶端 IP(可被 proxy 影響)Code language: PHP (php)

第 8 章:Session、Cookie 與狀態

HTTP 本身是無狀態的;要記住「已登入」需 Session 或 Cookie。

Session

<?php
session_start();

$_SESSION['user_id'] = 42;
$_SESSION['username'] = 'alice';

// 讀取
$user_id = $_SESSION['user_id'] ?? null;

// 登出
session_destroy();Code language: HTML, XML (xml)

流程:

sequenceDiagram
  participant B as 瀏覽器
  participant S as PHP 伺服器

  B->>S: POST /login(帳密)
  S->>S: 驗證成功,session_start()
  S->>S: $_SESSION['user_id'] = 1
  S->>B: Set-Cookie: PHPSESSID=abc123
  B->>S: GET /dashboard(帶 Cookie)
  S->>S: 依 PHPSESSID 讀取 Session
  S->>B: 已登入頁面Code language: JavaScript (javascript)

Cookie

setcookie( 'theme', 'dark', [
    'expires'  => time() + 86400 * 30,
    'path'     => '/',
    'httponly' => true,
    'samesite' => 'Lax',
] );

$theme = $_COOKIE['theme'] ?? 'light';Code language: PHP (php)

敏感資料(如密碼)不要放 Cookie 明文;Session ID 透過 Cookie 傳即可。


第 9 章:檔案、路徑與引入

include / require

require_once __DIR__ . '/config.php';
include __DIR__ . '/partials/header.php';Code language: PHP (php)
語法找不到檔案時
includeWarning,繼續
requireFatal,停止
*_once只載入一次

__DIR__目前檔案所在目錄的絕對路徑,比相對路徑可靠。

讀寫檔案

$content = file_get_contents( 'data.txt' );
file_put_contents( 'log.txt', $line, FILE_APPEND );

$handle = fopen( 'big.csv', 'r' );
while ( ( $row = fgetcsv( $handle ) ) !== false ) {
    // 處理每一行
}
fclose( $handle );Code language: PHP (php)

JSON 檔當簡易資料庫

$data = json_decode( file_get_contents( 'users.json' ), true );
file_put_contents( 'users.json', json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) );Code language: PHP (php)

第 10 章:物件導向 PHP

類別與物件

<?php
class User {
    public int $id;
    public string $name;

    public function __construct( int $id, string $name ) {
        $this->id   = $id;
        $this->name = $name;
    }

    public function greet(): string {
        return 'Hi, ' . $this->name;
    }
}

$user = new User( 1, 'Alice' );
echo $user->greet();Code language: HTML, XML (xml)

可見性

修飾符存取範圍
public到處可用
protected類別內與子類別
private僅類別內

建構子屬性提升(PHP 8+)

class Product {
    public function __construct(
        public string $name,
        public float $price,
    ) {}
}Code language: PHP (php)

繼承與介面

interface Notifiable {
    public function notify( string $message ): void;
}

class EmailNotifier implements Notifiable {
    public function notify( string $message ): void {
        mail( 'user@example.com', 'Notice', $message );
    }
}Code language: PHP (php)

靜態、常數、列舉

class MathUtil {
    public const PI = 3.14159;

    public static function add( int $a, int $b ): int {
        return $a + $b;
    }
}

enum Status: string {
    case Draft = 'draft';
    case Published = 'published';
}Code language: PHP (php)

魔術方法

public function __toString(): string { return $this->name; }
public function __get( string $key ) { /* ... */ }Code language: PHP (php)

第 11 章:命名空間、Composer 與自動載入

命名空間
避免類別名稱衝突:

<?php
namespace App\Models;

class User {
    // ...
}Code language: HTML, XML (xml)
<?php
namespace App\Controllers;

use App\Models\User;

class UserController {
    public function show( int $id ): void {
        $user = new User( $id, 'Bob' );
    }
}Code language: HTML, XML (xml)

Composer
PHP 的套件管理器:

composer init
composer require monolog/monologCode language: JavaScript (javascript)

composer.json

{
  "autoload": {
    "psr-4": {
      "App\\": "src/"
    }
  }
}Code language: JSON / JSON with Comments (json)
composer dump-autoload

入口:

<?php
require __DIR__ . '/vendor/autoload.php';

use App\Models\User;Code language: HTML, XML (xml)

PSR 標準(了解即可)

  • PSR-4:自動載入目錄對應
  • PSR-12:程式碼風格
  • 現代 PHP 專案幾乎都用 Composer + 命名空間

第 12 章:資料庫與 PDO

關聯式資料庫概念

這裡簡單說明一下資料庫學習中常見的幾個詞彙。

名詞說明
資料表 Tableusersposts
列 Row一筆記錄
欄 Column欄位如 idemail
主鍵 Primary Key唯一識別,常為 id
外鍵 Foreign Key關聯另一張表

建立連線(PDO)

<?php
$dsn = 'mysql:host=127.0.0.1;dbname=app;charset=utf8mb4';
$user = 'root';
$pass = '';

$pdo = new PDO( $dsn, $user, $pass, [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
] );Code language: HTML, XML (xml)

查詢(Prepared Statement)

// 讀取
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE id = :id' );
$stmt->execute( [ 'id' => $user_id ] );
$row = $stmt->fetch();

// 列表
$stmt = $pdo->query( 'SELECT id, name FROM users ORDER BY id DESC' );
$users = $stmt->fetchAll();

// 新增
$stmt = $pdo->prepare( 'INSERT INTO users (name, email) VALUES (:name, :email)' );
$stmt->execute( [
    'name'  => $name,
    'email' => $email,
] );
$new_id = $pdo->lastInsertId();

// 更新
$stmt = $pdo->prepare( 'UPDATE users SET name = :name WHERE id = :id' );
$stmt->execute( [ 'name' => $name, 'id' => $id ] );

// 刪除
$stmt = $pdo->prepare( 'DELETE FROM users WHERE id = :id' );
$stmt->execute( [ 'id' => $id ] );Code language: PHP (php)

永遠用佔位符,不要拼接使用者輸入進 SQL 字串。

交易 Transaction

$pdo->beginTransaction();
try {
    $pdo->prepare( 'UPDATE accounts SET balance = balance - :amt WHERE id = 1' )
        ->execute( [ 'amt' => 100 ] );
    $pdo->prepare( 'UPDATE accounts SET balance = balance + :amt WHERE id = 2' )
        ->execute( [ 'amt' => 100 ] );
    $pdo->commit();
} catch ( Exception $e ) {
    $pdo->rollBack();
    throw $e;
}Code language: PHP (php)

簡易 Repository 模式

class UserRepository {
    public function __construct( private PDO $pdo ) {}

    public function find( int $id ): ?array {
        $stmt = $this->pdo->prepare( 'SELECT * FROM users WHERE id = :id' );
        $stmt->execute( [ 'id' => $id ] );
        $row = $stmt->fetch();
        return $row ?: null;
    }
}Code language: PHP (php)

第 13 章:錯誤處理與除錯

錯誤等級

等級說明
Notice可疑但不致命
Warning有問題但繼續
Fatal Error程式停止

開發環境設定
php.ini 或程式開頭:

ini_set( 'display_errors', '1' );
ini_set( 'display_startup_errors', '1' );
error_reporting( E_ALL );Code language: JavaScript (javascript)

正式環境應關閉 display_errors,改寫 log。

例外 Exception

try {
    if ( $age < 0 ) {
        throw new InvalidArgumentException( '年齡不可為負' );
    }
    $result = risky_operation();
} catch ( InvalidArgumentException $e ) {
    echo '參數錯誤:' . $e->getMessage();
} catch ( Throwable $e ) {
    error_log( $e->getMessage() );
    echo '系統忙碌中';
}Code language: PHP (php)

除錯技巧

error_log( print_r( $data, true ) );
var_dump( $var );   // 僅開發用Code language: PHP (php)

Xdebug:斷點除錯、堆疊追蹤,PhpStorm / VS Code 可連接。

常見錯誤

訊息常見原因
Parse error: syntax error少分號、括號不配對
Undefined variable變數未賦值
Call to undefined function函式名打錯或未引入
Class not found命名空間或 autoload 問題
白畫面Fatal Error + 關閉 display_errors

第 14 章:安全實務

1. 跳脫輸出(XSS 防護)

function e( string $value ): string {
    return htmlspecialchars( $value, ENT_QUOTES, 'UTF-8' );
}

echo '<p>' . e( $user_input ) . '</p>';Code language: PHP (php)

2. 預處理語句(SQL Injection 防護)

// 危險:絕對不要這樣
// "SELECT * FROM users WHERE id = {$_GET['id']}";

$stmt = $pdo->prepare( 'SELECT * FROM users WHERE id = :id' );
$stmt->execute( [ 'id' => (int) $_GET['id'] ] );Code language: PHP (php)

3. CSRF Token

session_start();

if ( empty( $_SESSION['csrf'] ) ) {
    $_SESSION['csrf'] = bin2hex( random_bytes( 32 ) );
}

// 表單內
echo '<input type="hidden" name="csrf" value="' . e( $_SESSION['csrf'] ) . '">';

// 驗證 POST
if ( ! hash_equals( $_SESSION['csrf'], $_POST['csrf'] ?? '' ) ) {
    http_response_code( 403 );
    exit( 'Invalid CSRF token' );
}Code language: PHP (php)

4. 密碼雜湊

$hash = password_hash( $password, PASSWORD_DEFAULT );

if ( password_verify( $input_password, $hash ) ) {
    // 登入成功
}Code language: PHP (php)

絕不用 MD5/SHA1 存密碼。

5. 檔案上傳

  • 檢查副檔名與 MIME
  • 重新命名檔案,不要信任使用者檔名
  • 存到 Web 根目錄外,或禁止執行

6. 最小權限
資料庫帳號只給必要權限;正式環境關閉多餘 PHP 函式。


第 15 章:HTTP、路由與 MVC 概念

HTTP 方法

方法用途範例
GET讀取列表、詳情頁
POST建立 / 提交表單註冊、登入
PUT/PATCH更新REST API
DELETE刪除REST API

狀態碼

意義
200成功
302重導向
400客戶端錯誤
401未授權
403禁止
404找不到
500伺服器錯誤
http_response_code( 404 );
header( 'Location: /login' );
header( 'Content-Type: application/json; charset=utf-8' );Code language: JavaScript (javascript)

極簡路由(單檔示範)
public/index.php

<?php
require __DIR__ . '/../vendor/autoload.php';

$uri    = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH );
$method = $_SERVER['REQUEST_METHOD'];

match ( [ $method, $uri ] ) {
    [ 'GET', '/' ]       => ( fn() => require '../views/home.php' )(),
    [ 'GET', '/users' ]  => ( fn() => ( new UserController() )->index() )(),
    [ 'POST', '/login' ] => ( fn() => ( new AuthController() )->login() )(),
    default              => ( function () {
        http_response_code( 404 );
        echo 'Not Found';
    } )(),
};Code language: HTML, XML (xml)

MVC 分工

flowchart LR
  R[Router 路由] --> C[Controller 控制器]
  C --> M[Model 模型 / 資料庫]
  C --> V[View 視圖 / HTML]Code language: CSS (css)
職責
Model資料與商業規則
View呈現 HTML
Controller接收請求、呼叫 Model、選 View

框架(Laravel、Symfony)幫你處理路由、ORM、樣板;底層仍是 PHP。


第 16 章:JSON API 與前後端分離

輸出 JSON

<?php
header( 'Content-Type: application/json; charset=utf-8' );

$data = [
    'ok'    => true,
    'items' => [
        [ 'id' => 1, 'title' => '第一則' ],
        [ 'id' => 2, 'title' => '第二則' ],
    ],
];

echo json_encode( $data, JSON_UNESCAPED_UNICODE );Code language: HTML, XML (xml)

讀取 JSON body

$raw  = file_get_contents( 'php://input' );
$body = json_decode( $raw, true );

if ( ! is_array( $body ) ) {
    http_response_code( 400 );
    echo json_encode( [ 'error' => 'Invalid JSON' ] );
    exit;
}Code language: PHP (php)

REST 風格 CRUD

方法路徑動作
GET/api/todos列表
GET/api/todos/1單筆
POST/api/todos新增
PUT/api/todos/1更新
DELETE/api/todos/1刪除

前端 React/Vue 用 fetch() 呼叫這些端點。


第 17 章:實戰迷你專案:待辦清單

整合所學:表單、PDO、Session、跳脫輸出。

資料表

CREATE TABLE todos (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
done TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Code language: PHP (php)

功能清單

1. `GET /` 顯示所有待辦
2. `POST /add` 新增(CSRF + 驗證)
3. `POST /toggle` 切換完成狀態
4. `POST /delete` 刪除
Code language: JavaScript (javascript)

目錄建議

todo-app/
public/
index.php # 唯一 Web 入口
src/
Database.php
TodoRepository.php
views/
list.php
config.php
composer.jsonCode language: PHP (php)

TodoRepository 核心邏輯

php
<?php
namespace App;
use PDO;

class TodoRepository {
public function __construct( private PDO $pdo ) {}

public function all(): array {
    return $this->pdo->query( 'SELECT * FROM todos ORDER BY id DESC' )->fetchAll();
}

public function add( string $title ): void {
    $stmt = $this->pdo->prepare( 'INSERT INTO todos (title) VALUES (:title)' );
    $stmt->execute( [ 'title' => $title ] );
}

public function toggle( int $id ): void {
    $stmt = $this->pdo->prepare( 'UPDATE todos SET done = NOT done WHERE id = :id' );
    $stmt->execute( [ 'id' => $id ] );
}

public function delete( int $id ): void {
    $stmt = $this->pdo->prepare( 'DELETE FROM todos WHERE id = :id' );
    $stmt->execute( [ 'id' => $id ] );
}
}Code language: HTML, XML (xml)

完成此專案後,你已具備閱讀多數 PHP 專案骨架的能力!!

推薦資源


尾聲:學完 PHP 之後看 WordPress

WordPress 是用 PHP 寫成的 內容管理系統(CMS),全球數百萬網站使用。它不是 PHP 本身,而是建立在 PHP + MySQL 之上的應用程式。

若你之後要維護 Wiki、部落格、企業官網等 WordPress 專案,會發現:

你已學的 PHP在 WordPress 中的對應
include / 專案結構get_header()get_template_part()
$_GET / $_POST後台表單、REST API(仍建議用 WP API)
PDO / SQL$wpdbWP_Query、外掛的資料表
htmlspecialcharsesc_html()esc_url() 等包裝函式
SessionCookie + WordPress 使用者系統
Hooks 概念(事件)add_action() / add_filter()
MVC 思維模板(View)+ 主題 PHP + 外掛邏輯

建議順序:先把本篇 PHP 基礎與小專案練熟 → 再讀 WordPress Theme Handbook → 從改模板、加 functions.php Hook 開始,而不是直接改核心檔。

WordPress 主題開發本質上是:在 CMS 提供的掛勾與 API 上,用你已學會的 PHP 輸出 HTML、查資料、處理邏輯。PHP 底子穩,看任何 WordPress 主題都會輕鬆許多。