PHP

技术栈
后端框架
phpscriptingweb-developmentserver-sidedynamic-typing

概览

PHP

PHP(Hypertext Preprocessor)是广泛使用的开源服务器端脚本语言,由 Rasmus Lerdorf 于 1994 年创建。专为 Web 开发设计,可嵌入 HTML 中执行。支撑了全球约 75% 的网站(含 WordPress、Facebook 早期版本)。核心特性:动态弱类型、丰富的内置函数库、C 风格语法、跨平台部署、出色的数据库集成能力。

安装

环境准备

  • 操作系统:Linux (Ubuntu 20.04+ / Debian / CentOS 7+)、macOS (Homebrew)、Windows (WSL2 推荐 / 手动安装)
  • 运行时版本:PHP 8.2+(推荐,寿命到 2026-12);PHP 7.4+ 仍可用但已 EOL
  • Web 服务器(可选):Apache 2.4+(mod_php)、Nginx 1.18+(php-fpm)、Caddy、或内置开发服务器
  • 数据库驱动(可选):mysqlnd、pgsql、sqlite3
  • 依赖项:libxml2、libcurl、OpenSSL、libzip、oniguruma(mbstring)

安装命令

Ubuntu/Debian

# 添加 PPA(获取最新版本)
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update

# 安装 PHP 8.3 + 常用扩展
sudo apt install -y php8.3 php8.3-cli php8.3-fpm php8.3-mbstring \
  php8.3-xml php8.3-curl php8.3-mysql php8.3-sqlite3 \
  php8.3-zip php8.3-gd php8.3-intl php8.3-bcmath

# 验证
php -v

macOS (Homebrew)

brew install php@8.3

# 扩展
brew install php@8.3-mbstring php@8.3-xml php@8.3-curl

Windows

推荐两种方式:

  1. XAMPPhttps://www.apachefriends.org/ — 一键安装 Apache + PHP + MariaDB
  2. PHP for Windowshttps://windows.php.net/download — 下载 ZIP,解压,设置 PATH

Docker(推荐用于开发)

docker run -d -p 8080:80 --name my-php-app \
  -v "$PWD":/var/www/html php:8.3-apache

常见安装问题

"configure: error: Package requirements ... not found"

缺少编译依赖。Ubuntu 下运行:

sudo apt build-dep php

扩展未加载

检查 php.ini 位置:

php --ini

确保 extension=mbstring(等行)未被注释。

权限问题(Permission Denied)

  • Linux/macOS:确保 php-fpm 用户对项目目录有读权限
  • SELinux(CentOS):chcon -R -t httpd_sys_content_t /var/www/html

版本切换

# Ubuntu
sudo update-alternatives --config php

# macOS
brew unlink php@8.2 &;& brew link php@8.3 --force

示例

PHP Hello World 例程

目标

运行第一个 PHP 程序:输出 "Hello, World!" 并在浏览器中验证。

方式一:CLI 命令行

创建 hello.php

<?php
echo "Hello, World!\n";
echo "今天的日期是:" . date('Y-m-d') . "\n";

运行:

php hello.php

预期输出:

Hello, World!
今天的日期是:2026-01-15

方式二:内置 Web 服务器

在同一目录运行:

php -S localhost:8000

浏览器访问 http://localhost:8000/hello.php,页面显示:

Hello, World!
今天的日期是:2026-01-15

方式三:嵌入 HTML

创建 index.php

<!DOCTYPE html>
<html>
<head><title>PHP Hello World</title></head>
<body>
    <h1><?php echo "Hello, World!"; ?></h1>
    <p>服务器时间:<?php echo date('H:i:s'); ?></p>
    <p>PHP 版本:<?php echo phpversion(); ?></p>
</body>
</html>

关键说明

  • <?php ... ?> 是 PHP 代码块标记,可嵌入 HTML 任意位置
  • echo 是最常用的输出语句
  • 每条语句以分号 ; 结尾
  • PHP 文件默认扩展名为 .php

教程

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 从零构建动态网站的核心能力。

参考资料

  1. [1] The PHP Group. PHP 官方手册. 2024. https://www.php.net/manual/
  2. [2] Josh Lockhart, Phil Sturgeon. PHP: The Right Way. 2023. https://phptherightway.com/
  3. [3] Josh Lockhart. Modern PHP. 2015.
  4. [4] Jon Duckett. PHP & MySQL: Server-side Web Development. 2022.