☰
PHP写第一个网页为什么总是空白
2026/10/3 14:40:38 网站建设 项目流程

前言

第一次写 PHP 的人,几乎都会经历这样一个下午:照着教程敲完代码,保存成文件,用浏览器打开——一片空白。更让人困惑的是,浏览器什么错都不报,你反复检查代码,觉得每个字符都对。

其实"空白"根本不是一种情况,而是至少五种完全不同的故障共用同一个表象:


  • 你的文件根本没有被 PHP 执行,而是被当成普通文本发给了浏览器;

  • 代码执行了,但里面没有任何输出语句——PHP 是"你不说输出,它就不输出"的语言;

  • 代码里有致命错误,但错误信息被配置关掉了,浏览器只拿到一个空的 500 响应;

  • 你打开的是本地磁盘路径而不是 HTTP 地址,浏览器把 PHP 文件当文本下载或渲染;

  • 代码进了exit,在输出之前就结束了。


本文不假设你懂服务器,只教你用最短的路径判断出到底是哪一类问题,然后给出一个可以直接跑通的最小页面和一份自检脚本。

一、先分清你遇到的是哪一种"空白"

不要急着读代码。先做一件事:在浏览器里按查看源代码(大多数浏览器是 Ctrl+U),看看到底返回了什么。

你看到的现象查看源代码看到的内容真正的原因
完全空白,什么都不显示也是空的代码没输出、被exit中断,或者致命错误被隐藏
完全空白有一堆<?php ... ?>原文文件没被 PHP 执行(打开方式或扩展名不对)
浏览器弹出"下载文件"不适用服务器把.php当成普通文件返回了
页面显示要下载,或者直接问"用什么打开"不适用你打开的是file:///本地路径,不是 HTTP 地址
页面顶部多出几行英文能看到英文提示那是警告或错误信息,不是真的"空白"
空白,且开发者工具显示状态码 500空的脚本发生了致命错误,但display_errors关着

这里有一条极其重要的判断标准:

如果在"查看源代码"里看到了<?php这几个字符,说明PHP 根本没有运行,你看到的是文件的原始内容。这时候排查方向是"环境",不是"代码"。

反之,如果源码里什么都没有,说明 PHP 很可能运行了,只是没有产生输出。这时候排查方向是"代码逻辑 + 错误配置"。

二、最容易被忽略的三个原因

原因一:文件扩展名不是.php

服务器是根据扩展名决定"要不要交给 PHP 解析"的。你把代码写在一个.html文件里,服务器只会原样把它发给浏览器。

浏览器看到<?php开头的文本时,会把它当成一个"无法识别的标签"直接跳过,结果是页面上什么都看不见,源代码里却能完整看到 PHP 代码——这就是很多人遇到的"空白"。

同理,用记事本保存时如果没注意,Windows 可能给你存成了hello.php.txt。打开文件所在的文件夹,确认扩展名显示的是.php,而不是.php.txt(记得先在资源管理器里打开"显示文件扩展名"选项)。

原因二:短标签没被识别

PHP 标签有两种写法:

<?php echo '这一种永远有效';
<? echo '这一种依赖 short_open_tag 配置';

第二种叫短标签(short open tag),它只有在php.ini里把short_open_tag设为On时才生效,而这个选项默认就是关闭的(因为<?和 XML 的声明开头冲突)。

短标签被关闭时,PHP 不会报错,它只会把整段内容当作纯文本原样输出。于是你的脚本"跑了",但输出的是一段变成了普通文本的 PHP 代码——如果这段文本被浏览器当成无法识别的标记跳过,页面上就是一片空白。

有一个例外要记住:<?=这种"短输出标签"从 PHP 5.4 起是永远可用的,跟short_open_tag无关。所以下面这行在任何配置下都能正常输出:

<?= 'Hello' ?>

结论很简单:永远用<?php和<?=,不要用<?。

原因三:代码里没有输出语句

这是新手最容易犯、也最容易被忽略的一点。PHP 和 JavaScript 在浏览器控制台里的行为不一样:


  • JavaScript 里写2 + 3,控制台会显示结果;

  • PHP 里写2 + 3,什么都不会发生。它只是算出了 5,然后把它扔掉。


<?php declare(strict_types=1); $name = 'PHP'; strtoupper($name); // ❌ 算完了,但没输出 trim(' hello '); // ❌ 同上

上面这段代码执行成功,返回 HTTP 200,页面上什么都没有——因为它真的什么都没有输出。

必须显式地说"把它打印出来":

<?php declare(strict_types=1); $name = 'PHP'; echo $name; // ✅ echo 是语言结构,最常用 echo strtoupper($name); // ✅ 直接打印函数返回值 print trim(' hello '); // ✅ print 也可以 printf("长度: %d\n", strlen($name)); // ✅ 格式化输出

三、把藏在暗处的错误逼出来

如果你确认了文件扩展名对、用的也是<?php、代码里也有echo,但页面还是空白,那多半是有错误发生了,只是被配置藏起来了。

PHP 的默认配置是面向生产环境的:出错时不告诉用户,只写日志。这对线上网站是对的,对刚学写代码的人是灾难——你只看到白屏,看不到原因。

开发阶段请打开全部报错提示。在脚本最开头加上:

<?php declare(strict_types=1); error_reporting(E_ALL); // 报告所有级别的错误 ini_set('display_errors', '1'); // 把错误显示在页面上

如果连这两行都因为 php.ini 被限制而无效(有些环境禁止脚本修改),那就在命令行下手动检查语法——语法错误是最容易导致"纯白屏"的错误类型:

# 检查语法,不执行代码 php -l hello.php # 期望输出 # No syntax errors detected in hello.php

php -l(lint)会做语法解析,把漏掉的分号、括号不匹配、拼错的<?ph全部指出来,并告诉你第几行。遇到白屏,第一条命令就该是它。

还有一个特别好用的手段:用 PHP 自带的内置服务器把文件跑起来,完全绕开 Nginx/Apache 的配置问题。如果内置服务器能跑通,而你的 Web 服务器跑不通,那问题百分之百在服务器配置上,跟代码无关。

cd /path/to/your/project php -S localhost:8000

然后在浏览器访问http://localhost:8000/hello.php。内置服务器把当前目录当作 Web 根目录,并且默认就会把.php交给 PHP 解析。

内置服务器会把每个请求的信息和错误直接打印在终端里。这是排查白屏问题时最好的观察窗口——比任何日志都好用。

四、代码实战:一个能立刻跑通的最小页面

先把这段代码存成hello.php,用php -S localhost:8000跑起来。它输出纯文本而不是网页,因为纯文本最好排查:任何多余的字符、任何被隐藏的错误,都会立刻暴露出来。

<?php declare(strict_types=1); // 最低要求:PHP 8.0(用到了 str_starts_with) // 开发环境:把错误全部显示出来,别让它藏起来 error_reporting(E_ALL); ini_set('display_errors', '1'); // 告诉浏览器这返回的是纯文本。排查通过后改成 text/html 就能输出真正的网页 header('Content-Type: text/plain; charset=UTF-8'); echo "你好,PHP!\n"; echo '当前服务器时间:', date('Y-m-d H:i:s'), "\n"; echo 'PHP 版本:', PHP_VERSION, "\n"; echo '运行方式(SAPI):', PHP_SAPI, "\n"; // 一个最简单的表单处理,看看输入是怎么进来的 $name = $_GET['name'] ?? '世界'; echo '收到参数 name:', $name, "\n"; // 变量、字符串、函数都用一遍,确认语法没问题 $items = ['苹果', '香蕉', '橘子']; echo "购物清单共 ", count($items), " 项:\n"; foreach ($items as $index => $item) { printf(" %d. %s\n", $index + 1, $item); }

访问http://localhost:8000/hello.php?name=张三,你会在浏览器里看到:

你好,PHP! 当前服务器时间:2026-09-29 14:32:07 PHP 版本:8.5.0 运行方式(SAPI):cli-server 收到参数 name:张三 购物清单共 3 项: 1. 苹果 2. 香蕉 3. 橘子

看到这段输出,说明你的 PHP 环境、文件扩展名、语法、输出全部都是对的。接下来如果你还是遇到白屏,问题一定在 Web 服务器配置上,而不是代码。

顺便说一句:要把它变成"网页",只需要把Content-Type换成text/html,然后用字符串拼接出 HTML 标记即可。在排查阶段坚持输出纯文本,能让你少受很多"标记被浏览器吞掉了"的干扰。

下面是排查白屏时最实用的自检脚本,存成check.php访问一次就能拿到答案:

<?php declare(strict_types=1); // 最低要求:PHP 8.0 error_reporting(E_ALL); ini_set('display_errors', '1'); header('Content-Type: text/plain; charset=UTF-8'); function report(bool $ok, string $label, string $hint = ''): void { printf("[%s] %s\n", $ok ? ' OK ' : 'FAIL', $label); if (!$ok && $hint !== '') { printf(" → %s\n", $hint); } } // 最先做这件事:在任何输出之前检查 HTTP 头是否还能发送 // 如果这里就已经失败了,说明在本脚本运行之前就有内容被发出去了(典型原因是 BOM) $canSendHeader = !headers_sent($sentFile, $sentLine); echo "PHP 白屏自检\n", str_repeat('=', 44), "\n"; // 1. 这个文件是否带 UTF-8 BOM $content = (string) file_get_contents(__FILE__); $hasBom = str_starts_with($content, "\xEF\xBB\xBF"); report(!$hasBom, '文件不带 UTF-8 BOM', '用编辑器把文件另存为"UTF-8 无 BOM"。BOM 会在任何输出之前吐出 3 个字节,导致 header() 报 headers already sent'); // 2. 错误显示是否真的生效 report( (bool) ini_get('display_errors'), 'display_errors 已开启', '在 php.ini 里设 display_errors=On,或在脚本开头 ini_set' ); report( error_reporting() === E_ALL, sprintf('error_reporting 为 E_ALL(当前值 %d)', error_reporting()), '设成 E_ALL 才能看到 Deprecated / Warning' ); // 3. 短标签配置 report( !(bool) ini_get('short_open_tag'), 'short_open_tag 关闭(正常,说明你应该写完整的 PHP 开标签)', '如果你坚持用短标签,需要把 short_open_tag 设为 On' ); // 4. 关键扩展 foreach (['mbstring', 'json'] as $ext) { report(extension_loaded($ext), sprintf('扩展 %s 已加载', $ext)); } // 5. 输出时机(结果在脚本最开头就采集好了) report( $canSendHeader, '脚本启动时尚可发送 HTTP 头', sprintf('已在 %s 第 %d 行产生输出,检查该文件开头是否有 BOM、结尾是否有空行', $sentFile ?? '?', $sentLine ?? 0) ); echo str_repeat('=', 44), "\n"; printf("SAPI: %s\n", PHP_SAPI); printf("PHP: %s\n", PHP_VERSION); printf("时区: %s\n", date_default_timezone_get());

其中headers_sent($file, $line)这个用法值得单独说:它不但告诉你"已经输出过了",还会通过引用参数回填"是哪个文件的哪一行先输出的"。排查"明明没写输出,却报 headers already sent"这类问题时,它是最直接的证据。

常见坑点

1. 用file:///本地路径打开 PHP 文件

❌ 错误写法:在浏览器地址栏输入file:///C:/project/hello.php。

✅ 正确写法:必须通过 HTTP 访问,也就是地址要以http://或https://开头。PHP 是服务端语言,它的代码必须由 Web 服务器(或内置服务器)执行。直接用文件协议打开,浏览器只会把它当普通文件——你看到的要么是源码,要么是下载提示,永远不会是执行结果。

2.<?php结束标签后面多了一个空行

❌ 错误写法:

<?php echo 'done'; ?>

(这个结束标签之后还跟着一个换行符,它会被原样输出)

✅ 正确写法:

<?php echo 'done'; // 纯 PHP 文件不要写结束标签

?>之后的任何字符(包括换行符和空格)都会被原样输出。如果这个文件是被include进来的,这段"看不见的输出"会排在任何header()前面,导致Cannot modify header information - headers already sent。纯 PHP 文件的惯例是完全不写?>。

3. 以为变量赋值会显示出来

❌ 错误写法:

<?php $total = 100; $total; // 以为这会显示 100

✅ 正确写法:

<?php $total = 100; echo $total; var_dump($total); // 调试用,会显示类型和值

var_dump()和print_r()是调试阶段的好帮手,前者连类型一起显示,后者更适合看数组结构。

4. 语法错误被display_errors=Off吞掉

❌ 错误写法:线上配置原样搬到本地开发机,display_errors关着,一个漏掉的分号就让你看到纯白屏。

✅ 正确写法:用php -l先过一遍语法;开发环境打开display_errors。白屏 + HTTP 500 + 日志里没有记录,这三件事同时出现,几乎一定是致命错误被隐藏了。

5. 文件里带了 UTF-8 BOM

❌ 错误写法:用某些编辑器保存时自动加上了 BOM(文件开头三个字节EF BB BF),这三个字节会在你写任何代码之前就被发出去。

✅ 正确写法:保存为"UTF-8 无 BOM"。症状很典型:页面最顶部出现了几个奇怪的字符,或者header()神秘地失败。上面的自检脚本已经覆盖了这一项。

6. 把exit或return用在了输出之前

❌ 错误写法:

<?php if (!$config) { exit; // 静默退出,页面就是空白 } echo '页面内容';

✅ 正确写法:

<?php if (!$config) { http_response_code(500); exit('配置未加载'); // exit 可以接收一个字符串并直接输出它 } echo '页面内容';

exit不带参数时什么也不输出,这正是"程序明明跑完了,页面却空白"的一种来源。在全局作用域里写return效果和exit类似——它会直接结束当前脚本。

7. 文件放到了 Web 根目录之外

❌ 错误写法:把hello.php放在C:\project\hello.php,而 Web 服务器的根目录是C:\project\public\。

✅ 正确写法:确认服务器配置里的文档根目录(document root),把文件放进那个目录。症状本来是 404,但某些配置下 404 页面本身就是空白的,于是你看到的是"空白"。另外记住这条通用原则:排查 PHP 问题时,终端里的信息永远比浏览器里的多——php -S启动成功会打印Development Server ... started,端口被占用也会明确报错,别只盯着浏览器刷新。

总结

现象判断依据处理方向
空白,源码里能看到<?phpPHP 没被执行改扩展名为.php,用 HTTP 访问,别用<?
空白,源码里也空有错误被隐藏或没输出php -l查语法,打开display_errors,确认有echo
空白,状态码 500致命错误看服务器错误日志,或改用php -S观察
浏览器下载文件服务器没把.php交给 PHP检查 Web 服务器配置
顶部有奇怪字符或英文有输出抢先了去 BOM,去掉?>后的空行,用headers_sent()定位
输出正常但路由不对文件不在文档根目录确认 document root 路径

写完第一个 PHP 文件遇到白屏,请不要在代码里找"哪里写错了",而是按顺序回答三个问题:文件到底有没有被 PHP 执行?代码到底有没有输出?错误到底有没有被显示?这三个问题各有一套明确的验证手段——php -S和php -l负责第一、第二个,display_errors和headers_sent()负责第三个。学会这套顺序,比记住任何一条具体规则都有用,因为它是你之后排查所有 PHP 问题的通用方法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询