从零开始认识 .php 文件:结构、语法和运行方式

从零开始认识 .php 文件:结构、语法和运行方式

从零开始认识 .php 文件:结构、语法和运行方式

看到网址末尾的 .php,或者在项目目录里发现一个 PHP 文件,初学者很容易把它理解成“网页文件的一种”。这只说对了一部分:PHP 文件通常由服务器端的 PHP 运行时解释执行,程序可以读取请求、访问文件或数据库,再把结果作为 HTML、JSON 等内容返回。浏览器通常收到的是结果,而不是 PHP 源代码。

本文从文件结构和基础语法讲起,再说明如何在命令行或本地 Web 环境运行 PHP,并用可执行示例介绍表单、数据库与输出安全。示例以现代 PHP 为背景,建议使用受支持的 PHP 8.x 版本;不同版本和扩展配置可能影响具体行为。

一、什么是 .php 文件

.php 是常见的 PHP 源文件扩展名,但扩展名本身不会让文件自动运行。文件能否被 PHP 解释,取决于运行方式和服务器配置。

一个 PHP 程序可以处理多种任务:

  • 生成网页:读取数据后输出 HTML,常见于传统的动态网站。
  • 处理请求:接收表单或 API 请求,校验数据并返回结果。
  • 访问数据库:通过 PDO 等接口读写数据库。
  • 命令行任务:执行批处理、导入导出、定时任务或开发辅助脚本。

PHP 适合需要服务端逻辑的 Web 项目,也可以作为命令行脚本语言。它并不负责浏览器里的交互逻辑;页面中的 JavaScript 通常由浏览器运行,PHP 通常由服务器或命令行中的 PHP 程序运行。两者可以配合,但执行环境不同。

二、准备运行环境

运行 PHP 至少需要 PHP 解释器。若程序还要连接数据库、处理图片或使用其他功能,也可能需要安装并启用相应扩展。检查 PHP 是否可用,可以在终端执行:

php -v

如果系统提示找不到命令,通常表示 PHP 未安装,或其可执行文件没有加入系统的 PATH。安装方式取决于操作系统,也可以通过 Docker、虚拟机或开发环境工具管理版本。部署前应确认运行环境使用了项目要求的 PHP 版本和扩展。

创建一个名为 hello.php 的文件:

<?php

echo "你好,PHP!";

进入文件所在目录,在终端运行:

php hello.php

这会通过命令行运行 PHP 文件。它不会自动启动网站,也不会把文件发布到浏览器中。要通过 HTTP 访问,通常需要配置 Web 服务器与 PHP 运行时,或在本地开发时使用 PHP 自带的开发服务器。

在项目目录中运行以下命令,可以启动本地开发服务器:

php -S 127.0.0.1:8000

然后访问 http://127.0.0.1:8000/hello.php。这个内置服务器用于开发和测试,不应直接当作面向互联网的生产服务器。它不会替代完整的生产部署方案,也不能仅凭本地测试结果推断生产环境的并发能力、权限和安全配置。

三、PHP 文件的基本结构

PHP 代码通常写在 <?php 开始标记之后:

<?php

$message = "欢迎";
echo $message;

PHP 可以和 HTML 写在同一个文件中:

<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8">
<title>PHP 示例</title>
<body>
  <h1>当前页面</h1>
  <p><?php echo "由 PHP 生成"; ?></p>
</body>
</html>

服务器会执行 PHP 代码,并把输出与普通 HTML 一起作为响应内容发送。浏览器不会替服务器执行这段 PHP。若 Web 服务器没有正确配置 PHP 处理,访问结果可能不是预期页面,甚至可能暴露源码,因此不要把未验证的 PHP 文件随意放到公开目录中。

纯 PHP 文件通常不需要写结束标记 ?>。省略它可以减少结束标记之后意外输出空白字符的风险。若文件中还要直接写 HTML,才会在 PHP 代码后使用结束标记切换回 HTML。

四、初学者先掌握的语法

1. 语句、分号与注释

多数 PHP 语句以分号结束。注释可以写成单行或多行:

<?php

// 单行注释

/*
 * 多行注释
 */
echo "一条语句需要分号";

在 PHP 代码块内部漏写分号,常会引发语法错误。错误提示中的行号指向报错位置附近,不一定就是漏写符号的那一行。

2. 变量和字符串

变量以美元符号 $ 开头,变量名区分大小写。PHP 会根据值处理类型,但这不表示可以忽略类型和输入校验。

<?php

$name = "小林";
$count = 3;
$isReady = true;

echo $name;
echo "数量:{$count}";

字符串可用单引号或双引号。双引号字符串支持变量插值;更复杂的拼接也可以使用点号运算符:

<?php

$name = "小林";
$message = "你好," . $name;

echo $message;

echo 用来输出内容。它输出到当前运行环境:在命令行中通常显示在终端,在 Web 请求中通常成为响应正文。输出本身不会替你进行 HTML 转义。

3. 条件和循环

<?php

$score = 82;

if ($score >= 60) {
    echo "通过";
} else {
    echo "未通过";
}

$items = ["苹果", "梨", "橙子"];

foreach ($items as $item) {
    echo $item . PHP_EOL;
}

在命令行示例中,PHP_EOL 表示当前平台常用的换行符。它适合命令行输出;生成 HTML 时,换行符不会自动变成浏览器中的换行或段落结构。

4. 数组

PHP 的数组既可以作为带数字索引的列表,也可以作为键值映射:

<?php

$colors = ["红", "绿", "蓝"];
$user = [
    "name" => "小林",
    "role" => "编辑",
];

echo $colors[0];
echo $user["name"];

使用数组键前,应确认该键存在;处理不可信数据时尤其如此。对于可能不存在的键,可以先用 isset() 或其他适合场景的检查方式判断。

5. 函数

把重复逻辑放进函数,有助于明确输入和输出:

<?php

function makeGreeting(string $name): string
{
    return "你好," . $name;
}

echo makeGreeting("小林");

参数和返回值类型声明能帮助表达预期,也能让 PHP 在不符合类型约束时报告错误。类型声明不是对外部输入的完整校验方案:请求参数仍应按业务规则检查,不能因为参数写了类型就认定用户数据可信。

五、从请求中读取数据

在 Web 请求中,PHP 提供了诸如 $_GET、$_POST 和 $_SERVER 等超全局变量。下面的文件可以接收查询参数 name:

<?php

$name = $_GET["name"] ?? "访客";
$safeName = htmlspecialchars(
    $name,
    ENT_QUOTES | ENT_SUBSTITUTE,
    "UTF-8"
);

echo "<p>你好," . $safeName . "!</p>";

访问类似 hello.php?name=小林 的地址时,代码会读取查询参数并输出问候语。这里的空合并运算符 ?? 用于在键不存在或值为 null 时提供默认值。

查询参数仍然是不可信输入。htmlspecialchars() 是针对 HTML 文本或属性等上下文进行 HTML 特殊字符转义的常用函数,可降低这类位置上的脚本注入风险,但它不是所有输出场景通用的“万能安全函数”。例如,JavaScript、CSS、URL、SQL 等上下文各有不同规则,不能把 HTML 转义当成这些场景的替代措施。

处理表单

下面是一个最小表单示例。将两段代码保存为同一个文件,例如 form.php:

<?php

$result = "";

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

    if ($name === "") {
        $result = "请输入姓名。";
    } else {
        $safeName = htmlspecialchars(
            $name,
            ENT_QUOTES | ENT_SUBSTITUTE,
            "UTF-8"
        );
        $result = "收到姓名:" . $safeName;
    }
}
?>
<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8">
<title>简单表单</title>
<body>
  <form method="post" action="">
    <label>
      姓名:
      <input type="text" name="name">
    </label>
    <button type="submit">提交</button>
  </form>
  <p><?php echo $result; ?></p>
</body>
</html>

示例展示了几个重要步骤:确认请求方法、读取字段、处理空值、校验业务输入,并在输出到 HTML 前转义。实际表单通常还要考虑错误提示、长度与格式限制、业务权限和数据保存方式。对于会改变用户数据的操作,也要结合会话和 CSRF 防护等措施,不能只依赖“使用了 POST”。

六、请求参数有多大限制

GET 参数通常出现在 URL 中,因此会受到浏览器、代理、Web 服务器和其他网络组件对 URL 长度的限制;具体上限取决于环境,不能把某个固定数字当成适用于所有系统的标准。POST 数据放在请求正文中,也不代表它没有大小限制。

PHP 和服务器配置可能限制请求正文、上传文件或可解析的输入变量。例如,post_max_size 与 POST 请求正文大小有关,upload_max_filesize 与单个上传文件大小有关,max_input_vars 会影响可接受输入变量的数量。实际行为还可能受 Web 服务器、反向代理和应用自身限制影响。应根据部署环境检查 PHP 配置和服务器配置,并为应用设置合理的业务上限。

不要将这些配置限制误解成安全校验。把参数长度限制设得很大,不会使内容可信;设得较小,也不能替代输入校验、权限检查和安全输出。对于文件上传,还应验证文件大小、允许的内容类型和实际文件内容,并将上传文件放在不允许直接执行脚本的位置,避免仅凭用户提供的文件名或扩展名判断安全性。

七、访问数据库:使用参数化查询

PHP 可通过 PDO 等接口连接数据库。下面示例演示使用预处理语句传递查询参数。它需要 PDO SQLite 扩展可用;运行时会在当前目录创建或打开 demo.sqlite 数据库文件。

<?php

$pdo = new PDO("sqlite:" . __DIR__ . "/demo.sqlite");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->exec(
    "CREATE TABLE IF NOT EXISTS notes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        body TEXT NOT NULL
    )"
);

$insert = $pdo->prepare("INSERT INTO notes (body) VALUES (:body)");
$insert->execute(["body" => "第一条记录"]);

$query = $pdo->prepare(
    "SELECT id, body FROM notes WHERE body = :body"
);
$query->execute(["body" => "第一条记录"]);

foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
    echo $row["id"] . ": " . $row["body"] . PHP_EOL;
}

将用户提供的值作为参数绑定,可以避免把值直接拼进 SQL 语句所带来的 SQL 注入风险。预处理参数用于值,并不能自动让任意表名、列名或 SQL 片段变安全;这些结构部分通常需要由程序固定,或通过明确的允许列表选择。还应使用权限适当的数据库账号,并妥善管理连接凭据。

数据库输出如果最终显示在网页中,仍要根据 HTML 等输出上下文进行转义。SQL 参数化解决的是 SQL 查询中的输入处理问题,不等于 HTML 输出安全,也不替代授权检查。

八、文件组织与引入其他 PHP 文件

项目通常会把配置、业务逻辑和页面拆分到多个文件。PHP 提供 require、include 等语句引入文件;找不到文件时,require 会导致错误,include 通常发出警告并继续执行。对于必须存在的依赖文件,常用 require_once 避免同一文件被重复引入。

<?php
// 文件:config.php

return [
    "site_name" => "示例站点",
];
<?php
// 文件:index.php

$config = require __DIR__ . "/config.php";

echo htmlspecialchars(
    $config["site_name"],
    ENT_QUOTES | ENT_SUBSTITUTE,
    "UTF-8"
);

__DIR__ 表示当前文件所在目录,用它构造相对路径,通常比依赖当前工作目录更稳妥。不要把用户提交的字符串直接拼成待引入的文件路径,否则可能引入不该执行的文件。路径应由程序控制,或经过严格的允许列表校验。

配置文件不宜存放数据库密码等敏感信息到公开可访问的位置。生产环境中常通过受控的环境变量或专门的密钥管理方式传递配置,并确保日志、错误页面和代码仓库不会泄露凭据。

九、常见错误与排查方式

1. 直接双击 PHP 文件

双击文件通常只会用编辑器打开,或由系统关联的程序处理;它不会自动启动 PHP Web 服务。先用 php 文件名.php 验证命令行脚本,再按实际需求配置 Web 服务器或本地开发服务器。

2. 通过浏览器访问时看到源码或下载文件

这通常说明 Web 服务器没有把该请求交给 PHP 运行时处理,或站点配置有误。不要把“浏览器能打开文件”当成 PHP 已经正确执行。应检查服务器配置,并在确认源码不会被公开后再进行部署。

3. 页面空白,或出现 500 错误

可能原因包括语法错误、运行时异常、缺少扩展或文件权限问题。开发环境可以查看 PHP 和 Web 服务器的错误日志;必要时在开发配置中打开错误报告。生产环境不宜把详细错误堆栈直接显示给访客,以免泄露路径、配置或内部数据。

4. 中文出现乱码

应检查源文件编码、数据库连接编码、HTTP 响应编码和 HTML 文档声明是否一致。网页通常使用 UTF-8,并可在 HTML 中声明 <meta charset="utf-8">;响应头也应正确声明字符集。只改其中一处未必能解决所有乱码问题。

5. 变量或数组键不存在

访问尚未设置的变量或不存在的数组键,可能触发提示或警告,具体表现与 PHP 版本和错误配置有关。为可选输入提供默认值,并对必须字段做明确校验。不要简单关闭所有错误提示来掩盖程序问题。

6. 把 HTML 和 PHP 标记混淆

<?php 和 ?> 用于切换 PHP 与普通文本;HTML 标签则是输出内容的一部分。写在 PHP 代码块内部的 HTML 标签必须作为字符串输出,或先结束 PHP 代码块再写 HTML。语法高亮编辑器和命令行检查都能帮助发现标记未闭合等问题。

7. 认为 POST、加密或类型声明足以保证安全

POST 只是 HTTP 请求方法,不是访问控制或加密机制;连接安全通常还需要正确配置 HTTPS。类型声明表达程序预期,并不等于业务校验。安全应按具体边界处理:校验业务输入、检查用户权限、使用参数化 SQL、按输出上下文转义,并安全管理文件、会话和密钥。

十、从小脚本走向真实项目

上面的示例有意保持简单,方便理解语法,但生产项目还需要考虑错误处理、日志、配置管理、自动化测试、依赖管理和部署方式。随着代码增加,可以使用 Composer 管理 PHP 依赖,采用清晰的目录结构,并通过框架或成熟组件处理路由、会话、验证和数据库访问。

学习时可以按以下顺序练习:

  1. 用命令行运行只包含 PHP 代码的脚本,熟悉变量、条件、循环和函数。
  2. 启动本地开发服务器,练习输出 HTML,并理解服务器端执行与浏览器端执行的区别。
  3. 编写一个表单,检查请求方法和字段,验证输入并对 HTML 输出进行转义。
  4. 连接本地数据库,使用预处理语句读写数据。
  5. 检查 PHP 版本、扩展和相关配置,练习查看错误日志和修复错误。

理解 PHP 文件的关键,不是记住几个标签,而是分清楚代码在哪里执行、数据从哪里来、结果将进入什么上下文。掌握这些边界后,语法本身并不复杂;而输入校验、输出转义、数据库参数化、权限控制与部署配置,才是把示例变成可靠程序时不可省略的部分。

© 版权声明

相关文章