PHP 从零到 Web 应用

知识库
知识库文档
/tech-stacks/php/tutorial/PHP 从零到 Web 应用.md

文档

PHP 从零到 Web 应用

1. 背景与定位

PHP 最初代表 "Personal Home Page"(Rasmus Lerdorf,1994),后来演变为 "PHP: Hypertext Preprocessor"。它设计之初就是为了让开发者在 HTML 里嵌入动态逻辑。

核心哲学

  • 共享无状态:每个请求独立处理,启动、执行、消亡
  • 模板原生:HTML 和 PHP 无缝混合
  • 电池内置:无需第三方库即可操作数据库、处理表单、发送邮件

2. 语言基础速览

变量与类型

<?php
$name = "Vibe";           // 字符串
$count = 42;              // 整数
$price = 19.99;           // 浮点数
$isAdmin = true;          // 布尔
$items = ["A", "B", "C"]; // 数组
$user = null;             // null

// 类型检查
var_dump($count);         // int(42)
echo gettype($price);     // double

字符串操作

// 双引号解析变量
echo "Hello, $name!";     // Hello, Vibe!

// 连接符 .(不是 +)
echo "Hello, " . $name;

// Heredoc(多行)
$html = <<<HTML
<div>
    <p>Welcome, $name</p>
</div>
HTML;

数组

// 索引数组
$colors = ["red", "green", "blue"];

// 关联数组(相当于字典)
$user = [
    "name" => "Alice",
    "email" => "alice@example.com",
];

// 遍历
foreach ($user as $key => $value) {
    echo "$key: $value\n";
}

// 实用函数
count($colors);           // 3
in_array("red", $colors); // true
array_merge([1,2], [3,4]);// [1,2,3,4]

函数

function greet(string $name, string $title = "Mr."): string
{
    return "Hello, $title $name!";
}

echo greet("Smith");                // Hello, Mr. Smith!
echo greet("Jane", "Ms.");          // Hello, Ms. Jane!

// 箭头函数(PHP 7.4+)
$double = fn($x) => $x * 2;
echo $double(5);                    // 10

3. 处理 Web 请求

GET 请求

// URL: /search.php?q=php&page=1
$query = $_GET['q'] ?? 'default';
$page  = (int)($_GET['page'] ?? 1);

echo "搜索: $query, 第 $page 页";

POST 请求

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name = htmlspecialchars($_POST['name'] ?? '');
    $email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
    
    if ($email === false) {
        die("无效的 Email");
    }
    
    echo "已接收: $name ($email)";
}

文件上传

if (isset($_FILES['avatar']) && $_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
    $tmpPath  = $_FILES['avatar']['tmp_name'];
    $destPath = 'uploads/' . basename($_FILES['avatar']['name']);
    move_uploaded_file($tmpPath, $destPath);
    echo "文件已保存至 $destPath";
}

4. 数据库操作(PDO)

$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'root', '');

// 查询(防 SQL 注入 — 预处理)
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => 1]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

// 插入
$stmt = $pdo->prepare('INSERT INTO posts (title, body) VALUES (:title, :body)');
$stmt->execute([
    'title' => 'Hello PHP',
    'body'  => 'This is my first post.',
]);
echo "新纪录 ID: " . $pdo->lastInsertId();

5. Session 与 Cookie

session_start();

// 登录
$_SESSION['user_id'] = 42;
$_SESSION['username'] = 'alice';

// 读取
if (isset($_SESSION['user_id'])) {
    echo "欢迎回来,{$_SESSION['username']}";
}

// Cookie(7 天后过期)
setcookie('theme', 'dark', time() + 7 * 86400, '/');
echo $_COOKIE['theme'] ?? 'light';

6. 错误处理

// 异常
try {
    $pdo = new PDO('mysql:host=localhost;dbname=nonexistent', 'root', '');
} catch (PDOException $e) {
    error_log($e->getMessage());
    http_response_code(500);
    echo "数据库连接失败,请稍后重试";
}

// 生产环境配置
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php_errors.log');

7. 分步实践:留言板

步骤 1 — 数据库

CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    author VARCHAR(100) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

步骤 2 — 前端表单

<!-- index.php -->
<form method="POST" action="post.php">
    <input name="author" placeholder="你的名字" required>
    <textarea name="content" placeholder="想说点什么..." required></textarea>
    <button type="submit">发布</button>
</form>

步骤 3 — 处理提交

<!-- post.php -->
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'root', '');
$stmt = $pdo->prepare('INSERT INTO messages (author, content) VALUES (:author, :content)');
$stmt->execute([
    'author'  => htmlspecialchars($_POST['author']),
    'content' => htmlspecialchars($_POST['content']),
]);
header('Location: /');

步骤 4 — 展示消息

<!-- index.php 继续 -->
<?php
$stmt = $pdo->query('SELECT * FROM messages ORDER BY created_at DESC LIMIT 20');
while ($msg = $stmt->fetch()): ?>
    <div class="message">
        <strong><?= htmlspecialchars($msg['author']) ?></strong>
        <small><?= $msg['created_at'] ?></small>
        <p><?= nl2br(htmlspecialchars($msg['content'])) ?></p>
    </div>
<?php endwhile; ?>

8. 思考题

  1. 为什么用 htmlspecialchars() 如果去掉会有什么安全隐患?
  2. PDO::prepare 为什么必要? 写一个易受 SQL 注入攻击的代码,再用预处理修复。
  3. Session vs Cookie:用户关闭浏览器后,哪个还在?为什么登录通常用 Session?

掌握以上内容,你就具备了用 PHP 从零构建动态网站的核心能力。

信息

路径
/tech-stacks/php/tutorial/PHP 从零到 Web 应用.md
更新时间
2026/5/31