Skip to content

php.ini 配置

php.ini 是 PHP 的核心配置文件,几乎控制着 PHP 运行的所有行为。从内存限制、上传大小到错误显示方式、扩展加载,每一项都直接影响着你的 PHP 应用的运行效果和安全性。本节将全面介绍 php.ini 的查找方法、核心配置项详解、配置分区机制,以及不同环境下的配置建议。

前置知识

在阅读本节之前,你需要了解:

php.ini 文件查找

使用 php -i 命令查找

bash
# 查找当前加载的 php.ini 文件位置
php -i | grep "Loaded Configuration File"

# 输出示例:
# Loaded Configuration File => /etc/php/8.3/cli/php.ini

# 查找所有扫描的配置文件目录
php -i | grep "Scan for additional .ini files"

# 输出示例:
# Scan this dir for additional .ini files => /etc/php/8.3/cli/conf.d
# Additional .ini files parsed => /etc/php/8.3/cli/conf.d/10-opcache.ini,
# /etc/php/8.3/cli/conf.d/10-pdo.ini, /etc/php/8.3/cli/conf.d/20-calendar.ini,
# ...

使用 php --ini 快捷命令

bash
# PHP 提供了更简洁的方式查看配置文件位置
php --ini

# 输出示例:
# Configuration File (php.ini) Path: /etc/php/8.3/cli
# Loaded Configuration File:         /etc/php/8.3/cli/php.ini
# Scan for additional .ini files in: /etc/php/8.3/cli/conf.d
# Additional .ini files:             /etc/php/8.3/cli/conf.d/10-opcache.ini,
#                                  /etc/php/8.3/cli/conf.d/10-pdo.ini,
#                                  ...

不同 SAPI 的 php.ini 位置

PHP 为不同的 SAPI 可以使用不同的 php.ini 文件:

/etc/php/8.3/
├── cli/
│   ├── php.ini              ← CLI 模式使用的配置
│   └── conf.d/              ← CLI 扩展配置
├── fpm/
│   ├── php.ini              ← PHP-FPM 模式使用的配置
│   └── conf.d/              ← FPM 扩展配置
├── apache2/
│   ├── php.ini              ← Apache mod_php 使用的配置
│   └── conf.d/              ← Apache 扩展配置
└── mods-available/          ← 扩展模块文件

建议分别配置

强烈建议为 CLI 和 FPM 分别配置 php.ini。例如:

  • CLI 可以设置 memory_limit = -1(无限制)
  • FPM 应设置合理的 memory_limit = 256M
  • CLI 可以开启 display_errors = On
  • FPM 应关闭 display_errors = Off

核心配置项详解

资源限制配置

memory_limit

ini
; 脚本可使用的最大内存量
; 格式:数字 + 单位(K/M/G),-1 表示无限制
; 默认值:128M
memory_limit = 128M

; 常见设置:
; memory_limit = 256M    — 一般 Web 应用
; memory_limit = 512M    — 处理大量数据的应用
; memory_limit = 1G     — 图片/视频处理
; memory_limit = -1      — CLI 模式(无限制)
php
<?php
declare(strict_types=1);

// 查看当前内存限制
echo "memory_limit: " . ini_get('memory_limit') . "\n";

// 查看当前脚本内存使用情况
echo "已使用: " . round(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n";
echo "峰值: " . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . " MB\n";

// 当内存不足时会触发 Fatal Error
// Allowed memory size of 134217728 bytes exhausted

max_execution_time

ini
; 脚本最大执行时间(秒)
; 0 表示无限制
; 默认值:30
; 注意:此配置在 CLI 模式下默认无效(默认为 0)
max_execution_time = 30

; 对于耗时的操作可以临时增加
; 建议使用队列处理耗时任务
php
<?php
declare(strict_types=1);

// 查看最大执行时间
echo "max_execution_time: " . ini_get('max_execution_time') . "\n";

// 运行时临时修改(不能超过 php.ini 中的设置上限)
set_time_limit(60); // 设置为 60 秒
set_time_limit(0);  // 无限制(需要确认安全)

// 对于需要长时间运行的操作,推荐使用队列
// $job = dispatch(new ProcessLargeFile($filePath));

max_input_time

ini
; PHP 解析输入数据(POST/GET/文件上传)的最大时间(秒)
; 默认值:-1(使用 max_execution_time 的值)
; 如果表单提交或文件上传较大,可以适当增加
max_input_time = 60

文件上传配置

upload_max_filesize

ini
; 单个上传文件的最大允许大小
; 必须小于等于 post_max_size
; 默认值:2M
upload_max_filesize = 20M

post_max_size

ini
; POST 请求体的最大大小(包括所有表单数据和上传文件)
; 必须大于等于 upload_max_filesize
; 如果同时上传多个文件,需要根据文件数量调整
; 默认值:8M
post_max_size = 25M

; 计算公式:
; post_max_size >= upload_max_filesize * 最大同时上传文件数 + 表单数据大小

upload 相关配置

ini
; 最大上传文件数(PHP 8.2+ 废弃,使用 upload_max_filesize 和 post_max_size 控制)
; max_file_uploads = 20

; 文件上传的临时目录
; 默认使用系统临时目录
upload_tmp_dir = /tmp

; 是否启用 HTTP 文件上传
; 默认值:On
file_uploads = On

; 上传文件的最大大小(0 表示无限制,PHP 8.2+)
; max_file_uploads = 20

文件上传配置关系

upload_max_filesizepost_max_sizememory_limit 三者必须协调:

memory_limit >= post_max_size + 脚本运行所需内存
post_max_size >= upload_max_filesize * 文件数量 + POST 数据大小
upload_max_filesize >= 实际需要上传的文件大小

常见错误配置导致的问题:

  • upload_max_filesize = 20Mpost_max_size = 8M → 上传超过 8M 的文件时 POST 为空
  • post_max_size = 25Mmemory_limit = 128M → 多个并发上传可能导致内存溢出

错误处理配置

display_errors

ini
; 是否将错误信息输出到屏幕(HTML 输出)
; 生产环境必须设为 Off
; 默认值:On
display_errors = Off    ; 生产环境
; display_errors = On   ; 开发环境

error_reporting

ini
; 控制报告哪些级别的错误
; 默认值:E_ALL & ~E_DEPRECATED & ~E_STRICT
; 推荐生产环境设置:
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE

; 推荐开发环境设置:
; error_reporting = E_ALL

; 可用的错误级别:
; E_ERROR           — 致命运行时错误
; E_WARNING         — 运行时警告
; E_PARSE           — 编译时解析错误
; E_NOTICE          — 运行时通知
; E_CORE_ERROR      — PHP 启动时的致命错误
; E_CORE_WARNING    — PHP 启动时的警告
; E_COMPILE_ERROR   — 编译时的致命错误
; E_COMPILE_WARNING — 编译时的警告
; E_USER_ERROR      — 用户触发的错误
; E_USER_WARNING    — 用户触发的警告
; E_USER_NOTICE     — 用户触发的通知
; E_DEPRECATED      — 弃用警告
; E_STRICT          — 兼容性建议
; E_RECOVERABLE_ERROR — 可恢复的致命错误
; E_ALL             — 所有错误(不包括 E_STRICT)
php
<?php
declare(strict_types=1);

// 运行时修改错误报告级别
error_reporting(E_ALL);

// 触发不同级别的错误示例
trigger_error("这是一个 Notice", E_USER_NOTICE);
trigger_error("这是一个 Warning", E_USER_WARNING);
trigger_error("这是一个 Error", E_USER_ERROR);

log_errors

ini
; 是否将错误信息记录到日志文件
; 生产环境必须开启
; 默认值:On
log_errors = On

; 错误日志文件路径
error_log = /var/log/php/error.log

; 注意:在 CLI 模式下,如果 error_log 为空,错误会输出到 STDERR
; CLI 模式下可以指定:
; error_log = /var/log/php/cli-error.log

error_log

ini
; 错误日志文件路径
; 可以是文件路径或 syslog
error_log = /var/log/php/error.log

; 使用系统日志(syslog)
; error_log = syslog

; 如果设置为空字符串,错误输出到 SAPI 标准错误输出
; error_log =
php
<?php
declare(strict_types=1);

// 使用 error_log() 函数记录日志
error_log("这是一条普通日志");
error_log("这是一条警告日志", 3, "/var/log/php/app.log");

// 记录数组或对象
error_log("调试信息: " . print_r($_SERVER, true));

日期时区配置

date.timezone

ini
; 设置默认时区
; 默认为空(PHP 会根据系统设置猜测,但会产生警告)
; 必须明确设置
; 常见值:
date.timezone = Asia/Shanghai    ; 中国(东八区)
; date.timezone = Asia/Tokyo      ; 日本
; date.timezone = America/New_York ; 纽约
; date.timezone = UTC            ; 协调世界时
; date.timezone = Europe/London  ; 伦敦

; 查看所有支持的时区列表
; php -r 'print_r(timezone_identifiers_list());'
php
<?php
declare(strict_types=1);

// 查看当前时区
echo "时区: " . date_default_timezone_get() . "\n";

// 运行时修改时区
date_default_timezone_set('Asia/Shanghai');

echo "当前时间: " . date('Y-m-d H:i:s') . "\n";
echo "时间戳: " . time() . "\n";

时区警告

如果 date.timezone 未设置,PHP 在使用日期函数时会抛出警告:

Warning: date(): It is not safe to rely on the system's timezone settings.
You are required to use the date.timezone setting or the date_default_timezone_set() function.

session 配置

ini
; Session 存储处理器
session.save_handler = files

; Session 文件保存路径
session.save_path = "/var/lib/php/sessions"

; Session 名称(Cookie 名称)
session.name = PHPSESSID

; Session 在 Cookie 中的生命周期(秒),0 表示浏览器关闭即失效
session.cookie_lifetime = 0

; Session 服务端文件的生命周期(秒)
session.gc_maxlifetime = 1440    ; 24 分钟

; Session 使用严格模式(PHP 7.1+)
session.use_strict_mode = 1

; 仅通过 Cookie 传递 Session ID(防止 Session 固定攻击)
session.use_only_cookies = 1

; Cookie 中的 Session ID 仅通过 HTTP 访问(防止 JS 窃取)
session.cookie_httponly = 1

; Cookie 中的 Session ID 仅通过 HTTPS 传输(PHP 7.1+)
; session.cookie_secure = 1    ; HTTPS 环境开启
session.cookie_secure = 0       ; HTTP 环境关闭

; Cookie 的 SameSite 属性(PHP 7.3+)
session.cookie_samesite = Lax    ; Strict / Lax / None

; Session 自动启动(建议关闭,手动控制)
session.auto_start = 0

OPcache 配置

ini
[OPcache]
; 是否启用 OPcache(强烈建议开启)
; CLI 模式默认关闭,FPM 模式默认开启
opcache.enable = 1
opcache.enable_cli = 1

; OPcache 共享内存大小(MB)
; 生产环境建议 128M+
opcache.memory_consumption = 128

; 存储缓存的内部字符串数量
opcache.interned_strings_buffer = 16

; 最大缓存文件数
opcache.max_accelerated_files = 20000

; 检查脚本时间戳的频率(秒)
; 0 表示不检查(生产环境推荐,性能最佳)
; 1 表示每次请求都检查(开发环境推荐)
opcache.revalidate_freq = 0

; 开发环境下关闭 OPcache(确保代码变更即时生效)
; opcache.enable = 0

; 开启 JIT(PHP 8.0+)
; opcache.jit = 1205
; opcache.jit_buffer_size = 64M

配置分区(PHP_INI_*)

PHP 配置项按可修改范围分为四个级别,这决定了配置项可以在哪些层面被修改:

分区级别说明

级别常量名说明
ALLPHP_INI_ALL可在所有地方修改(php.ini、.htaccess、ini_set、.user.ini)
PERDIRPHP_INI_PERDIR可在 php.ini、.htaccess、.user.ini 中修改,不可用 ini_set
SYSTEMPHP_INI_SYSTEM只能在 php.ini 或 httpd.conf 中修改
MODALPHP_INI_MODAL仅在 php.ini 中可设置(通常为只读信息)

常见配置项的分区

php
<?php
declare(strict_types=1);

/**
 * 各配置项所属分区一览
 */

$configScopes = [
    // PHP_INI_ALL — 可用 ini_set() 修改
    'display_errors'        => 'ALL',
    'error_reporting'       => 'ALL',
    'error_log'             => 'ALL',
    'date.timezone'         => 'ALL',
    'memory_limit'          => 'ALL',
    'max_execution_time'    => 'ALL',
    'include_path'          => 'ALL',
    'auto_prepend_file'     => 'ALL',
    'auto_append_file'      => 'ALL',
    'default_socket_timeout'=> 'ALL',
    'upload_max_filesize'   => 'ALL',

    // PHP_INI_PERDIR — 不可用 ini_set() 修改
    'upload_max_filesize'   => 'PERDIR',
    'post_max_size'         => 'PERDIR',
    'file_uploads'          => 'PERDIR',
    'max_input_time'        => 'PERDIR',
    'max_input_vars'        => 'PERDIR',
    'session.save_path'     => 'PERDIR',
    'session.name'          => 'PERDIR',
    'session.cookie_lifetime' => 'PERDIR',
    'session.cookie_httponly'  => 'PERDIR',
    'session.cookie_secure'    => 'PERDIR',
    'session.use_strict_mode'  => 'PERDIR',
    'mbstring.func_overload'   => 'PERDIR',

    // PHP_INI_SYSTEM — 只能在 php.ini 中设置
    'disable_functions'     => 'SYSTEM',
    'disable_classes'       => 'SYSTEM',
    'open_basedir'          => 'SYSTEM',
    'extension_dir'         => 'SYSTEM',
    'zend_extension'         => 'SYSTEM',
    'opcache.enable'         => 'SYSTEM',
    'opcache.enable_cli'     => 'SYSTEM',
    'opcache.memory_consumption' => 'SYSTEM',
];

// 使用 ini_get_all() 查看配置项的访问级别
$allConfigs = ini_get_all();

// 查找特定配置项的访问级别
$memoryLimitScope = $allConfigs['memory_limit']['access'] ?? 'unknown';
echo "memory_limit access level: {$memoryLimitScope}\n";
// 输出: memory_limit access level: 7
// PHP_INI_ALL=7, PHP_INI_USER=1, PHP_INI_PERDIR=2, PHP_INI_SYSTEM=4

检查配置项是否可修改

php
<?php
declare(strict_types=1);

/**
 * 检查某个 ini 配置项是否可以通过 ini_set() 修改
 */
function isIniSettable(string $configName): bool
{
    // PHP_INI_USER = 1
    // PHP_INI_PERDIR = 2
    // PHP_INI_SYSTEM = 4
    // PHP_INI_ALL = 7 (USER | PERDIR)
    $scope = ini_get_all($configName, false)[$configName]['access'] ?? 0;

    // 只有 USER 标记(bit 0)的配置才可用 ini_set() 修改
    return ($scope & 1) === 1;
}

// 测试
$testConfigs = [
    'display_errors',    // ALL → 可修改
    'memory_limit',      // ALL → 可修改
    'upload_max_filesize', // PERDIR → 不可修改
    'post_max_size',     // PERDIR → 不可修改
    'open_basedir',     // SYSTEM → 不可修改
    'disable_functions', // SYSTEM → 不可修改
];

foreach ($testConfigs as $name) {
    $result = isIniSettable($name) ? '可修改' : '不可修改';
    echo "ini_set('{$name}'): {$result}\n";
}

实战示例:生产环境 php.ini 模板

ini
; ================================
; PHP 8.3 生产环境 php.ini 配置模板
; ================================

[PHP]
; ========== 资源限制 ==========
memory_limit = 256M
max_execution_time = 30
max_input_time = 60
max_input_vars = 3000

; ========== 错误处理 ==========
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE
log_errors = On
error_log = /var/log/php/error.log
html_errors = Off

; ========== 文件上传 ==========
file_uploads = On
upload_max_filesize = 20M
post_max_size = 25M
upload_tmp_dir = /tmp
max_file_uploads = 20

; ========== 日期时区 ==========
date.timezone = Asia/Shanghai

; ========== Session ==========
session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.use_strict_mode = 1
session.use_only_cookies = 1
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = Lax
session.gc_maxlifetime = 1440
session.auto_start = 0

; ========== 安全 ==========
; 限制 PHP 可访问的目录
open_basedir = /var/www/html:/tmp:/usr/share/php:/var/lib/php/sessions

; 禁用危险函数
disable_functions = exec,passthru,shell_exec,system,proc_open,popen,
                    curl_multi_exec,parse_ini_file,show_source
disable_classes =

; 隐藏 PHP 版本信息
expose_php = Off

; 防止 HTTP 请求体过大攻击
max_input_nesting_level = 64

; 防止包含远程文件
allow_url_include = Off
allow_url_fopen = On

; 防止 CGI 注入
cgi.fix_pathinfo = 0

[Date]
date.timezone = Asia/Shanghai

[Session]
session.save_handler = files
session.save_path = "/var/lib/php/sessions"
session.use_strict_mode = 1

[OPcache]
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.revalidate_freq = 0
opcache.validate_timestamps = 0
opcache.save_comments = 1
opcache.jit = 1205
opcache.jit_buffer_size = 64M

[mbstring]
mbstring.internal_encoding = UTF-8
mbstring.http_input = UTF-8
mbstring.http_output = UTF-8

[curl]
; 限制 cURL 重定向次数,防止 SSRF 攻击
curl.max_redirs = 5

注意事项

1. 配置生效顺序

PHP 配置可能被多个层面覆盖,了解优先级非常重要:

php.ini < 扩展 .ini < .user.ini < httpd.conf < .htaccess < ini_set() < 脚本内设置
      ↑                                                            ↑
     低优先级                                                   高优先级

但注意:PHP_INI_SYSTEM 级别的配置只能在 php.ini 中设置,后续层面无法覆盖。

2. 修改配置后重启

修改 php.ini 后,需要根据 SAPI 类型重启对应的服务:

bash
# CLI:无需重启,下次执行新脚本时生效

# PHP-FPM:优雅重载
sudo systemctl reload php8.3-fpm
# 或
sudo service php8.3-fpm reload

# Apache mod_php:重启 Apache
sudo systemctl restart apache2
# 或
sudo systemctl restart httpd

3. 查看最终生效的配置值

bash
# 使用 php -i 查看所有配置
php -i | grep memory_limit

# 使用 ini_get() 在脚本中查看
php -r 'echo ini_get("memory_limit");'

# 创建 phpinfo.php 在 Web 环境查看
# phpinfo.php
# <?php phpinfo();

phpinfo() 安全

phpinfo() 会暴露所有 PHP 配置信息,包括服务器路径、模块版本等敏感信息。生产环境务必删除或限制访问 phpinfo.php

最佳实践

1. 分环境管理 php.ini

# 使用分离的 php.ini 文件
/etc/php/8.3/
├── fpm/
│   ├── php.ini              ← 生产配置(关闭 display_errors 等)
│   ├── php.ini.development  ← 开发配置(开启 display_errors 等)
│   └── php.ini.production   ← 生产配置的备份
├── cli/
│   ├── php.ini              ← CLI 专用配置
│   └── conf.d/

切换环境时使用软链接:

bash
# 开发环境
sudo ln -sf /etc/php/8.3/fpm/php.ini.development /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm

# 生产环境
sudo ln -sf /etc/php/8.3/fpm/php.ini.production /etc/php/8.3/fpm/php.ini
sudo systemctl reload php8.3-fpm

2. 配置验证

php
<?php
declare(strict_types=1);

/**
 * PHP 配置检查工具
 * 用于检查关键配置项是否符合生产环境要求
 */

function checkPhpConfig(): array
{
    $checks = [
        [
            'name'     => 'display_errors',
            'expected' => '0',
            'operator' => '==',
            'severity' => 'critical',
        ],
        [
            'name'     => 'expose_php',
            'expected' => '',
            'operator' => '==',
            'severity' => 'warning',
        ],
        [
            'name'     => 'allow_url_include',
            'expected' => '0',
            'operator' => '==',
            'severity' => 'critical',
        ],
        [
            'name'     => 'memory_limit',
            'expected' => '128M',
            'operator' => '>=',
            'severity' => 'info',
        ],
        [
            'name'     => 'date.timezone',
            'expected' => 'Asia/Shanghai',
            'operator' => '==',
            'severity' => 'warning',
        ],
        [
            'name'     => 'opcache.enable',
            'expected' => '1',
            'operator' => '==',
            'severity' => 'warning',
        ],
    ];

    $results = [];
    foreach ($checks as $check) {
        $actual = ini_get($check['name']);
        $passed = match ($check['operator']) {
            '==' => $actual == $check['expected'],
            '>=' => version_compare($actual, $check['expected'], '>='),
            default => false,
        };

        $results[] = [
            'name'     => $check['name'],
            'actual'   => $actual,
            'expected' => $check['expected'],
            'operator' => $check['operator'],
            'passed'   => $passed,
            'severity' => $check['severity'],
        ];
    }

    return $results;
}

// 执行检查并输出报告
$report = checkPhpConfig();
foreach ($report as $item) {
    $status = $item['passed'] ? '[PASS]' : '[FAIL]';
    echo "{$status} {$item['name']}: actual={$item['actual']} expected={$item['operator']}{$item['expected']}\n";
}

3. 不要在 php.ini 中硬编码路径

ini
; 推荐:使用相对路径或变量
upload_tmp_dir = /tmp

; 避免:硬编码特定项目路径
; upload_tmp_dir = /home/user/project/tmp

; 项目特定的配置应在 PHP-FPM pool 配置或 .user.ini 中设置

下一节

你已经掌握了 php.ini 核心配置项的用法和分区机制,接下来将学习运行时配置(ini_set()),了解如何在 PHP 脚本中动态修改配置。

参考链接