上一节的 Apache 只会「把已经写好的文件送出去」;本节装上 PHP,它就变成了「每次都现算一遍再送」。 这一节你会第一次看到服务器端代码长什么样、它和网页里的 HTML 有什么本质区别,以及新手最容易搞混的PHP 两套运行方式。
5-1 装好了 Apache(负责「接待客人」),本节装 PHP(负责「现场炒菜」)。 下一节(5-3)装数据库(负责「去仓库取料」)——这三样凑齐就是大名鼎鼎的 LAMP(Linux + Apache + MySQL/MariaDB + PHP)。 本课 1-5 里提过「Python 和 PHP 都是脚本语言」,本节就是 PHP 的第一次实操。
一、先说清「静态」和「动态」的区别
这是本节的概念地基,比命令重要得多。
| 静态网页 | 动态网页 | |
|---|---|---|
| 服务器做什么 | 把文件原样发出去 | 先跑一段程序,把结果拼成 HTML 再发 |
| 文件后缀 | .html |
.php(跑完后浏览器看不到 PHP 代码,只看到 HTML) |
| 内容会变吗 | 不会,谁看都一样 | 会——看时间、看你是谁、看数据库里有什么 |
| 例子 | 你写的「这是我的服务器」 | 淘宝首页(每个人看到的商品都不一样) |
Apache 是「服务员」,PHP 是「厨师」。
客人点 /index.php,服务员不直接把菜端上去,而是把单子递给厨师,厨师做完再交回服务员端出去。
客人(浏览器)永远见不到厨房——这就是为什么你在浏览器里「查看源代码」永远看不到 PHP 代码,这也是 PHP 能安全地藏住数据库密码的原理。
二、PHP 是什么,它跑在哪里
PHP = Hypertext Preprocessor(超文本预处理器),一门专门为网页设计的服务器端脚本语言。
三个「不是」要先记牢:
- 不是在浏览器里跑的(那是 JavaScript 的活儿);
- 不是编译成 exe 的(它是「脚本」——每次被访问时由服务器现场解释执行,来自 4-1 讲的「脚本」概念);
- 不是只能搭配 Apache(nginx 也能跑,只是配法不同)。
它诞生的理由:1994 年有人想在自己的主页上放一个「访问计数」,用 C 写太麻烦,就随手写了个小程序——这就是 PHP 的起点。所以它的长项一直是:「在网页里嵌一段小程序,算点东西出来」,非常顺手。
PHP 代码长什么样:它直接嵌在 HTML 里,用 <?php ... ?> 圈起来:
<!DOCTYPE html>
<html>
<body>
<h1>现在时间</h1>
<p><?php echo date("Y-m-d H:i:s"); ?></p>
</body>
</html>
注意读法:<h1> 那些是普通的 HTML,浏览器拿到什么样就是什么样;只有 <?php ... ?> 中间那段会被服务器执行掉,执行结果替换掉原位置,再发出去。
三、装 PHP:一行命令,但有几个包要认清
sudo apt update
sudo apt install php libapache2-mod-php php-mysql -y
三个包分别管什么(别只背命令,要知为什么):
| 包 | 作用 | 少了它会怎样 |
|---|---|---|
php |
PHP 本体(含命令行解释器 php-cli) |
什么都没有 |
libapache2-mod-php |
把 PHP 接进 Apache 的接头 | 浏览器会直接下载 .php 文件(源码泄露!) |
php-mysql |
PHP 连数据库的驱动 | 下一节的数据库连不上 |
libapache2-mod-php 是「Apache」和「PHP」之间的转接头。
没装它、或者装完没启用模块,你访问 info.php 时 Apache 不认识 .php,会把它当成一个未知文件发给你——浏览器就会弹下载框,或者把 PHP 源码当纯文本显示出来。
看到「下载 .php 文件」或「页面上直接显示源码」,第一个要查的就是这个模块。
装完重启并验证:
sudo systemctl restart apache2
php -v
php -v 期望输出第一行类似:
PHP 8.4.x (cli) (built: ...)
Debian 13(Trixie)这一代配套的是 PHP 8.4(上一代 Bookworm 是 8.2)。
本文示例里的 8.4 只是举例——具体版本一律以 php -v 的实际输出为准,后面的路径也跟着它变。
四、新手最大的迷惑:PHP 有「两套」
这一节的知识点里,这一条最值钱。
装完之后,你的系统里其实有两套 PHP 在工作:
| CLI 那一套 | Apache 模块那一套 | |
|---|---|---|
| 给谁用 | 你在终端里敲 php xxx.php |
浏览器访问 xxx.php |
| 配置文件在哪 | /etc/php/8.4/cli/php.ini |
/etc/php/8.4/apache2/php.ini |
| 版本命令 | php -v 看的是它 |
phpinfo() 看的才是它 |
| 扩展装在哪 | /etc/php/8.4/mods-available/(两套共享) |
同左 |
你在命令行里 php -v 显示一切正常,不代表浏览器那一套也正常。 两者只有 .ini 是分开的,扩展是共享的。
所以典型误判是:「我终端里测过没问题的啊?」 —— 因为那是另一套。
统一判据:浏览器这套的真相,只有一个地方能看到 —— phpinfo() 页面(下一节马上就用)。
装完就有一堆现成的 PHP 模块(Debian 用 phpenmod / phpdismod 开关,跟 Apache 的 a2enmod 是一个思路):
php -m # 列出所有已加载的扩展(不看版本号的话,这条更快)
五、第一张 PHP 页面:info.php
在网页目录里新建(注意:这次用 sudo 是因为 5-1 讲过的权限):
sudo nano /var/www/html/info.php
内容只有一行:
<?php phpinfo(); ?>
保存(Ctrl + O → 回车 → Ctrl + X),然后在浏览器打开:
http://树莓派的IP/info.php
期望看到:一整页紫色的表格,包含 PHP 版本、加载的配置、已启用的扩展、环境变量等等。
这一页就是「Apache 那套 PHP」的体检报告。 你要看四样:
- 最上面那行 PHP Version —— 是不是你刚装的版本;
- Loaded Configuration File —— 告诉我它实际读了哪个
php.ini(就是上面表格里的路径,用它来验证你的判断); - Server API —— 应该写着
Apache 2.0 Handler(证明是模块方式在跑); - 往下找
$_SERVER段 —— 能看到 Apache 传过来的各种信息。
phpinfo() 会把服务器上所有配置细节摊开给人看,包括路径、模块版本、编译参数——这东西等于把家底贴在门口,对攻击者是极好的情报。
验收完就删:
sudo rm /var/www/html/info.php
要用的时候再临时建一次。这是本节最该记住的一条安全习惯。
六、常用扩展:装一次,后面不折腾
PHP 本体很精简,很多能力要单独装扩展。这一课后面会用到这几个,建议一次装齐:
sudo apt install php-mbstring php-curl php-gd php-xml php-zip -y
sudo systemctl restart apache2
| 扩展 | 干什么 | 不装的后果 |
|---|---|---|
php-mbstring |
正确处理多字节字符(中文!) | 中文截断变乱码——跟 4-6 装字体是一个道理 |
php-curl |
PHP 主动去访问别的网址 | 调外部接口失败 |
php-gd |
图片处理(缩略图、水印) | 图片功能报错 |
php-xml |
读写 XML/一部分 API 用它 | 很多库依赖它 |
php-zip |
压缩包操作 | 上传 zip 解压失败 |
php-mbstring 这个名字里的 mb = multi-byte(多字节)。
一个汉字在 UTF-8 里占 3 个字节,而 PHP 里老式的 strlen() 是按字节算的——不装 mbstring,你以为在处理「10 个字」,实际在处理「30 个字节」,截一半就成乱码。
(这和我们写 C++ 笔记时踩过的那个坑一模一样:UTF-8 下按字节切分会把汉字切碎。)
七、动手 Demo:写一个「会变」的页面
目标:做一个每次刷新都会显示不同内容的页面——亲眼看见「动态」是什么意思。
sudo nano /var/www/html/hello.php
输入:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>PHP 测试</title></head>
<body>
<h1>这个页面是服务器现算出来的</h1>
<p>服务器时间:<?php echo date("Y-m-d H:i:s"); ?></p>
<p>PHP 版本:<?php echo phpversion(); ?></p>
<p>一个随机数:<?php echo rand(100, 999); ?></p>
<p>你的浏览器是:<?php echo $_SERVER["HTTP_USER_AGENT"]; ?></p>
<p>你的 IP 是:<?php echo $_SERVER["REMOTE_ADDR"]; ?></p>
</body>
</html>
保存后,在浏览器打开 http://树莓派的IP/hello.php。
验收(三条都做到才算成功):
- 能看到「服务器时间」且时间秒数在走;
- 按
F5刷新,随机数每次都变 —— 证明服务器在每次请求时都重新算了一遍; - 在浏览器里「查看源代码」 —— 期望看到的是浏览器收到的那份 HTML(时间、随机数都是具体数字),绝对看不到
<?php那几个字。
读结果:
- 第 ③ 条如果你真的看到了
<?php echo ... ?>原样出现在源码里 → 说明 PHP 模块没生效(Apache 把文件当纯文本发出来了),回去查libapache2-mod-php与a2enmod php8.4; - 页面上出现乱码 → 缺
php-mbstring,或你没写<meta charset="utf-8">; - 页面一片空白 → PHP 报错了。PHP 的报错默认不显示在页面上(安全设计),要去日志看(下一条)。
八、30 秒实验:故意让它出错,学会看报错
PHP 出错时页面往往是白的(这叫「白屏」),新手在这最容易以为「什么都没发生」。真相在日志里:
# 一边盯着日志
sudo tail -f /var/log/apache2/error.log
另开一个终端,把 hello.php 里故意写错一句(比如把 echo 写成 echoo),保存,然后刷新浏览器。
期望在日志里看到类似:
PHP Parse error: syntax error, unexpected ... in /var/www/html/hello.php on line 8
看到带文件名和行号的 Parse error 就算成功。改回来,日志不再新增。
你以后写 PHP 的绝大部分时间,都是在「刷新页面 → 看日志 → 改代码」这个循环里。 先把这条循环走通,比背语法重要。
九、常见误区
- 「PHP 是前端的,跟 JavaScript 一样在浏览器里跑」——错。PHP 完全在服务器上跑,浏览器只拿到它输出的 HTML。这也是为什么「查看源代码」永远看不到 PHP 逻辑。
- 「
.php文件应该能看到源码」——恰恰相反。能看到源码说明配置错了(模块没生效),属于安全问题,要马上修。 - 「页面白屏 = 没运行」——错。白屏几乎总是「运行了但报错了」。去看
error.log。 - 「
php -v正常就万事大吉」——错。那是 CLI 那一套。浏览器用的是 Apache 模块那一套,判据是phpinfo()。(见第四节) - 「
info.php建着方便,以后还要用」——危险。它是情报泄露页,用完立刻删。需要时再临时建。 - 「改完
php.ini直接生效」——错。Apache 那套改完要sudo systemctl restart apache2;CLI 那套下次执行自动生效。顺序反了会以为自己改错了。
- 装
php5/php7—— 包名里带版本号的老写法全失效了,现在统一叫php,装到哪一代由系统仓库决定; - 改
/etc/php5/apache2/php.ini—— 路径里的版本号现在跟着实际版本走(本节是/etc/php/8.4/apache2/php.ini),照抄老路径会No such file; - 用短标签
<? ... ?>—— 老教程里写<? echo $x; ?>能跑,但短标签在新版默认关闭,照抄必然失败。一律写完整的<?php ... ?>,这一条能省你半小时; /etc/init.d/apache2 restart—— 老式服务管理写法,现在统一systemctl。
自测三问(点开对答案)
1. 浏览器里「查看源代码」看不到 PHP 代码,说明了什么?(**说明 PHP 是在服务器上执行的,输出给浏览器的只有它生成的 HTML**。如果真看到了 PHP 源码,那是配置错误、属于源码泄露) 2. `php -v` 显示一切正常,但浏览器里的页面白屏——这两件事矛盾吗?(**不矛盾**。`php -v` 是 **CLI 那一套**,浏览器用的是 **Apache 模块那一套**,两套的 `php.ini` 是分开的。要去 `error.log` 或 `phpinfo()` 里查真正的那一套) 3. `libapache2-mod-php` 这个包的作用是什么?不装会有什么现象?(**它是 Apache 认领 `.php` 文件的接头**;不装的话 Apache 不认识 `.php`,会把它当普通文件发出去——**浏览器弹下载框,或者直接把 PHP 源码显示成纯文本**)- ☐ 下一节(5-3 安装设置 MySQL 数据库系统)
相关笔记
- 架设 Web 服务器 —— 上一节:Apache 负责接待,PHP 负责现算
- 使用命令行安装和卸载应用程序 ——
apt install、purge、查已装包 - Linux 的常用指令 —— 本节排错用的
tail -f与日志 - 安装中文输入法 —— 「中文要专门处理」在 PHP 这边就是
mbstring - 在 Raspberry Pi OS 执行命令 —— nano 的存盘退出、
>重定向在这里都用得到 - 🍓 树莓派 · 索引 —— 全课路线图