运行时配置(ini_set)
在实际开发中,我们经常需要在 PHP 脚本运行时动态修改配置项,而不需要修改 php.ini 文件或重启服务。PHP 提供了 ini_set()、ini_get()、ini_restore() 等函数来实现这一功能。本节将详细介绍这些函数的用法、限制,以及它们与 get_cfg_var() 的区别。
前置知识
在阅读本节之前,你需要了解:
php.ini配置文件基础(参见 php.ini 配置)- PHP 配置分区(PHP_INI_ALL / PERDIR / SYSTEM)的概念
- PHP 基本语法和函数调用
ini_set() 函数
基本用法
ini_set() 用于在运行时设置 PHP 配置项的值。它接受两个参数:配置项名称和新的值。
<?php
declare(strict_types=1);
// ini_set(string $option, string|int|float|bool $value): string|false
// 设置显示错误
ini_set('display_errors', '1');
// 设置内存限制
ini_set('memory_limit', '512M');
// 设置最大执行时间
ini_set('max_execution_time', '60');
// 设置错误日志文件
ini_set('error_log', '/var/log/php/app-error.log');
// 设置默认字符编码
ini_set('default_charset', 'UTF-8');
// 设置包含路径
ini_set('include_path', '/var/www/app/lib:/var/www/app/vendor');
// 设置自动包含文件(在每个脚本执行前自动加载)
ini_set('auto_prepend_file', '/var/www/app/bootstrap.php');
// 设置自动附加文件(在每个脚本执行后自动加载)
ini_set('auto_append_file', '/var/www/app/cleanup.php');返回值
ini_set() 返回配置项修改前的旧值。如果修改失败,返回 false:
<?php
declare(strict_types=1);
// 修改前获取旧值
$oldValue = ini_set('memory_limit', '512M');
if ($oldValue === false) {
echo "修改失败:该配置项不可在运行时修改\n";
} else {
echo "修改成功:旧值为 {$oldValue}\n";
}ini_set 的限制
并非所有配置项都可以通过 ini_set() 修改。只有 PHP_INI_USER(即 PHP_INI_ALL)级别的配置项才支持运行时修改。以下配置项不可使用 ini_set():
upload_max_filesizepost_max_sizeopen_basedirdisable_functionssession.save_pathmax_input_vars- 以及所有
PHP_INI_SYSTEM和PHP_INI_PERDIR级别的配置
ini_get() 函数
基本用法
ini_get() 用于获取配置项的当前值:
<?php
declare(strict_types=1);
// ini_get(string $option): string|false
// 获取内存限制
echo "memory_limit: " . ini_get('memory_limit') . "\n";
// 输出: memory_limit: 128M
// 获取最大执行时间
echo "max_execution_time: " . ini_get('max_execution_time') . "\n";
// 输出: max_execution_time: 30
// 获取显示错误状态
echo "display_errors: " . ini_get('display_errors') . "\n";
// 输出: display_errors: 1(布尔 On/Off 会被转换为 1/0)
// 获取错误报告级别
echo "error_reporting: " . ini_get('error_reporting') . "\n";
// 输出: error_reporting: 32767(E_ALL 的整数值)
// 获取不存在的配置项
$result = ini_get('nonexistent_config');
echo $result === false ? "配置项不存在\n" : "值: {$result}\n";
// 输出: 配置项不存在处理布尔值配置
某些配置项使用 On/Off 表示布尔值,ini_get() 会返回空字符串(Off)或 "1"(On):
<?php
declare(strict_types=1);
/**
* 安全地获取布尔类型的 ini 配置
*/
function getIniBool(string $name): bool
{
$value = ini_get($name);
return $value !== false && $value !== '' && $value !== '0' && $value !== 'Off';
}
// 使用
$displayErrors = getIniBool('display_errors');
$fileUploads = getIniBool('file_uploads');
$allowUrlFopen = getIniBool('allow_url_fopen');
echo "display_errors: " . ($displayErrors ? '开启' : '关闭') . "\n";
echo "file_uploads: " . ($fileUploads ? '开启' : '关闭') . "\n";
echo "allow_url_fopen: " . ($allowUrlFopen ? '开启' : '关闭') . "\n";处理带单位的数值
<?php
declare(strict_types=1);
/**
* 将带单位的 ini 值转换为字节数
* 例如 "128M" → 134217728
*/
function iniToBytes(string $value): int
{
$value = trim($value);
if ($value === '-1') {
return -1; // 无限制
}
$last = strtolower($value[strlen($value) - 1]);
$num = (int) $value;
return match ($last) {
'g' => $num * 1024 * 1024 * 1024,
'm' => $num * 1024 * 1024,
'k' => $num * 1024,
default => $num,
};
}
// 使用
$memoryLimit = iniToBytes(ini_get('memory_limit'));
$uploadMaxSize = iniToBytes(ini_get('upload_max_filesize'));
$postMaxSize = iniToBytes(ini_get('post_max_size'));
echo "memory_limit: " . number_format($memoryLimit) . " bytes\n";
echo "upload_max_filesize: " . number_format($uploadMaxSize) . " bytes\n";
echo "post_max_size: " . number_format($postMaxSize) . " bytes\n";
// 检查配置一致性
if ($postMaxSize < $uploadMaxSize) {
echo "警告: post_max_size ({$postMaxSize}) 小于 upload_max_filesize ({$uploadMaxSize})\n";
}ini_restore() 函数
ini_restore() 用于将配置项恢复为 php.ini 中的原始值:
<?php
declare(strict_types=1);
// ini_restore(string $option): void
// 查看原始值
echo "原始 memory_limit: " . ini_get('memory_limit') . "\n";
// 输出: 原始 memory_limit: 128M
// 修改值
ini_set('memory_limit', '512M');
echo "修改后 memory_limit: " . ini_get('memory_limit') . "\n";
// 输出: 修改后 memory_limit: 512M
// 恢复原始值
ini_restore('memory_limit');
echo "恢复后 memory_limit: " . ini_get('memory_limit') . "\n";
// 输出: 恢复后 memory_limit: 128Mget_cfg_var() 与 ini_get() 的区别
这是一个非常重要的区别,许多开发者容易混淆:
| 函数 | 获取的值 | 是否受 ini_set 影响 |
|---|---|---|
ini_get() | 当前生效的值 | 是(反映 ini_set 的修改) |
get_cfg_var() | php.ini 中的原始值 | 否(不受 ini_set 影响) |
<?php
declare(strict_types=1);
// 演示 ini_get() 和 get_cfg_var() 的区别
// 查看修改前的值
echo "修改前:\n";
echo " ini_get('display_errors'): " . ini_get('display_errors') . "\n";
echo " get_cfg_var('display_errors'): " . get_cfg_var('display_errors') . "\n";
// 使用 ini_set 修改
ini_set('display_errors', '0');
// 查看修改后的值
echo "\n修改后:\n";
echo " ini_get('display_errors'): " . ini_get('display_errors') . "\n";
echo " get_cfg_var('display_errors'): " . get_cfg_var('display_errors') . "\n";
// ini_get 返回 "0"(被修改了)
// get_cfg_var 返回 "1"(php.ini 中的原始值)何时使用哪个函数
ini_get()— 获取当前实际生效的配置值(用于业务逻辑判断)get_cfg_var()— 获取php.ini中的原始配置(用于诊断和调试)- 在排查配置问题时,对比两个函数的返回值可以判断配置是否被运行时修改过
ini_get_all() 函数
ini_get_all() 可以获取所有或指定配置项的详细信息,包括值、全局值、访问级别:
<?php
declare(strict_types=1);
// 获取单个配置项的详细信息
$memoryInfo = ini_get_all('memory_limit');
print_r($memoryInfo);
/*
Array
(
[memory_limit] => Array
(
[global_value] => 128M
[local_value] => 128M
[access] => 7
)
)
*/
// access 值的含义:
// 1 = PHP_INI_USER — 可用 ini_set() 修改
// 2 = PHP_INI_PERDIR — 可在 .htaccess 中修改
// 4 = PHP_INI_SYSTEM — 仅在 php.ini 中修改
// 7 = PHP_INI_ALL — USER | PERDIR(最灵活)
// 获取所有配置项
$allConfigs = ini_get_all(null, false);
echo "总配置项数量: " . count($allConfigs) . "\n";
// 获取所有可修改的配置项
$modifiable = array_filter(
$allConfigs,
fn(array $info) => ($info['access'] & 1) === 1
);
echo "可修改(USER 级别)的配置项数量: " . count($modifiable) . "\n";.user.ini 文件
概述
PHP 5.3 起引入了 .user.ini 文件机制,允许在目录级别覆盖 PHP 配置。它的工作方式类似 Apache 的 .htaccess,但使用 PHP 原生的 INI 格式。
基本用法
; 在项目根目录创建 .user.ini 文件
; /var/www/html/.user.ini
; 设置显示错误
display_errors = On
; 设置时区
date.timezone = Asia/Shanghai
; 设置内存限制
memory_limit = 256M
; 设置自动包含文件
auto_prepend_file = /var/www/html/bootstrap.php
; 设置错误日志
error_log = /var/www/html/logs/error.log限制
; .user.ini 文件支持两种配置指令:
; 1. 带有 PHP_INI_PERDIR 或 PHP_INI_USER 作用域的配置
; 2. 仅支持 PHP_INI_ALL 级别的配置
; .user.ini 的读取间隔由 user_ini.filename 控制
; 默认每 300 秒(5分钟)读取一次
; 通过以下配置修改读取间隔:
; 在 php.ini 中设置:
; user_ini.cache_ttl = 300 ; 读取间隔(秒)
; .user.ini 文件名可以通过以下配置修改(仅在 php.ini 中):
; user_ini.filename = .user.ini.user.ini 的注意事项
.user.ini仅适用于 PHP-FPM 或 Apache + mod_php 模式- 修改
.user.ini后不会立即生效,需要等待缓存时间过期(默认 300 秒) - 无法覆盖
PHP_INI_SYSTEM级别的配置 - 生产环境建议关闭
.user.ini功能(设置user_ini.filename = ""),防止用户自行修改配置
.user.ini 示例
; /var/www/html/.user.ini
; 开发环境配置
; 错误处理
display_errors = On
error_reporting = E_ALL
log_errors = On
error_log = /var/www/html/storage/logs/php-error.log
; 性能
max_execution_time = 120
memory_limit = 512M
; 字符编码
default_charset = UTF-8
; Session
session.save_path = "/var/www/html/storage/sessions"
session.cookie_httponly = 1
session.use_strict_mode = 1
; 自动加载
auto_prepend_file = /var/www/html/bootstrap/autoload.php
; 上传
upload_max_filesize = 50M
; 注意:post_max_size 是 PERDIR 级别,在 .user.ini 中可能无效实战示例:运行时配置管理器
下面是一个实用的运行时配置管理工具:
<?php
declare(strict_types=1);
/**
* PHP 运行时配置管理器
* 用于安全地读取和修改运行时配置
*/
class IniConfigManager
{
/**
* 获取配置值(兼容布尔和带单位值)
*/
public static function get(string $name): string|int|float|bool|null
{
$value = ini_get($name);
if ($value === false) {
return null;
}
// 处理布尔值
if (in_array(strtolower($value), ['on', '1', 'true', 'yes'], true)) {
return true;
}
if (in_array(strtolower($value), ['off', '0', 'false', 'no', ''], true)) {
return false;
}
return $value;
}
/**
* 安全设置配置值
* 返回是否设置成功
*/
public static function set(string $name, string|int|float|bool $value): bool
{
$result = ini_set($name, (string) $value);
if ($result === false) {
return false;
}
return true;
}
/**
* 获取字节值(如 "128M" → 134217728)
*/
public static function getBytes(string $name): int
{
$value = ini_get($name);
if ($value === false || $value === '') {
return 0;
}
$value = trim($value);
if ($value === '-1') {
return -1;
}
$last = strtolower($value[strlen($value) - 1]);
$num = (int) $value;
return match ($last) {
'g' => $num * 1024 * 1024 * 1024,
'm' => $num * 1024 * 1024,
'k' => $num * 1024,
default => (int) $value,
};
}
/**
* 检查配置项是否可以通过 ini_set 修改
*/
public static function isModifiable(string $name): bool
{
$allConfigs = ini_get_all($name, false);
if (!isset($allConfigs[$name])) {
return false;
}
return ($allConfigs[$name]['access'] & 1) === 1;
}
/**
* 获取配置项的原始值(php.ini 中的值)
*/
public static function getOriginal(string $name): string|false
{
return get_cfg_var($name);
}
/**
* 验证上传相关配置的一致性
*/
public static function validateUploadConfig(): array
{
$errors = [];
$postMaxSize = self::getBytes('post_max_size');
$uploadMaxSize = self::getBytes('upload_max_filesize');
$memoryLimit = self::getBytes('memory_limit');
if ($postMaxSize > 0 && $uploadMaxSize > $postMaxSize) {
$errors[] = "upload_max_filesize ({$uploadMaxSize}) 大于 post_max_size ({$postMaxSize})";
}
if ($memoryLimit > 0 && $postMaxSize > $memoryLimit * 0.8) {
$errors[] = "post_max_size ({$postMaxSize}) 接近或超过 memory_limit ({$memoryLimit}) 的 80%";
}
return $errors;
}
/**
* 生成当前配置报告
*/
public static function getReport(): array
{
$configs = [
'memory_limit',
'max_execution_time',
'max_input_time',
'upload_max_filesize',
'post_max_size',
'display_errors',
'error_reporting',
'log_errors',
'error_log',
'date.timezone',
'default_charset',
'include_path',
'session.save_handler',
'opcache.enable',
];
$report = [];
foreach ($configs as $name) {
$report[$name] = [
'current' => ini_get($name),
'original' => get_cfg_var($name) ?: 'N/A',
'modifiable' => self::isModifiable($name),
];
}
return $report;
}
}
// 使用示例
echo "=== PHP 运行时配置报告 ===\n\n";
$report = IniConfigManager::getReport();
foreach ($report as $name => $info) {
$modifiable = $info['modifiable'] ? '可修改' : '只读';
$changed = ($info['current'] !== $info['original']) ? ' [已修改]' : '';
echo " {$name}: {$info['current']} (原始: {$info['original']}) [{$modifiable}]{$changed}\n";
}
// 验证上传配置
echo "\n=== 上传配置验证 ===\n";
$errors = IniConfigManager::validateUploadConfig();
if (empty($errors)) {
echo " 上传配置正常\n";
} else {
foreach ($errors as $error) {
echo " [WARNING] {$error}\n";
}
}
// 安全修改配置
echo "\n=== 修改配置示例 ===\n";
if (IniConfigManager::isModifiable('memory_limit')) {
$oldValue = ini_get('memory_limit');
IniConfigManager::set('memory_limit', '512M');
echo " memory_limit: {$oldValue} → " . ini_get('memory_limit') . "\n";
ini_restore('memory_limit');
} else {
echo " memory_limit 不可在运行时修改\n";
}运行时配置的应用场景
场景一:临时增加内存
<?php
declare(strict_types=1);
/**
* 导出大型数据集时临时增加内存
*/
function exportLargeData(string $filePath): void
{
// 记录原始值
$originalLimit = ini_get('memory_limit');
try {
// 临时增加内存到 1GB
ini_set('memory_limit', '1024M');
ini_set('max_execution_time', '0'); // 无限制执行时间
// 执行耗时操作
$data = fetchData(); // 假设这是获取大量数据的函数
$csv = generateCsv($data);
file_put_contents($filePath, $csv);
} finally {
// 无论成功或失败,都恢复原始配置
ini_restore('memory_limit');
ini_restore('max_execution_time');
}
}场景二:开发环境调试
<?php
declare(strict_types=1);
/**
* 开发环境调试配置
*/
if (getenv('APP_DEBUG') === 'true') {
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('html_errors', '1');
ini_set('error_log', __DIR__ . '/../storage/logs/php-error.log');
} else {
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/../storage/logs/php-error.log');
}场景三:自动包含文件
<?php
declare(strict_types=1);
// 使用 auto_prepend_file 在每个请求前自动加载引导文件
// 在 .user.ini 中设置:
// auto_prepend_file = /var/www/html/bootstrap/autoload.php
// bootstrap/autoload.php
if (defined('APP_STARTED')) {
return; // 防止重复加载
}
define('APP_STARTED', true);
define('APP_PATH', realpath(__DIR__ . '/..'));
// 加载 Composer 自动加载
require APP_PATH . '/vendor/autoload.php';
// 初始化错误处理
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
// 设置时区
date_default_timezone_set('Asia/Shanghai');注意事项
1. ini_set 对已有连接无效
<?php
declare(strict_types=1);
// ini_set 修改的配置仅影响之后的操作
// 对于已经建立的连接(如数据库连接),可能不会生效
// 例如,修改 default_socket_timeout 不会影响已建立的 TCP 连接
ini_set('default_socket_timeout', '5');
$fp = fsockopen('example.com', 80); // 此连接使用新的超时设置2. PHP-FPM 配置优先级
在 PHP-FPM 环境中,配置的优先级为:
php.ini < pool.d/*.conf 中的 php_admin_value < ini_set()注意:php_admin_value 设置的配置项不能被 ini_set() 覆盖。
3. 检查是否修改成功
<?php
declare(strict_types=1);
// 某些配置项虽然 ini_set() 返回了旧值,但实际可能未修改成功
// 最安全的做法是修改后再次读取确认
$oldValue = ini_set('display_errors', '1');
$newValue = ini_get('display_errors');
if ($newValue !== '1') {
// 修改可能被更高优先级的配置覆盖
error_log("配置修改失败: display_errors = {$newValue}");
}最佳实践
1. 使用 try-finally 确保恢复配置
<?php
declare(strict_types=1);
function processHeavyTask(): void
{
$originalMemoryLimit = ini_get('memory_limit');
$originalTimeLimit = ini_get('max_execution_time');
try {
ini_set('memory_limit', '512M');
set_time_limit(300);
// 执行任务
doHeavyProcessing();
} finally {
// 确保无论是否出错都恢复配置
ini_set('memory_limit', $originalMemoryLimit);
set_time_limit((int) $originalTimeLimit);
}
}2. 不要依赖 ini_set 修改 PERDIR/SYSTEM 配置
<?php
declare(strict_types=1);
// 错误示范:尝试在运行时修改上传限制
ini_set('upload_max_filesize', '50M');
ini_set('post_max_size', '60M');
// 以上调用虽然不会报错,但实际不会生效
// 正确做法:在 php.ini、PHP-FPM pool 配置或 .htaccess 中设置3. 配置修改记录
<?php
declare(strict_types=1);
/**
* 带日志记录的 ini_set 封装
*/
function safeIniSet(string $name, string $value): bool
{
$original = ini_get($name);
$result = ini_set($name, $value);
if ($result === false) {
error_log("[CONFIG] 无法修改 {$name}: 配置项不支持运行时修改");
return false;
}
if ($result !== $value) {
error_log("[CONFIG] {$name}: {$result} → {$value}");
}
return true;
}下一节
你已经掌握了运行时配置的机制和限制,接下来将学习 .htaccess 配置,了解在 Apache 环境下如何通过目录级别的配置文件控制 PHP 行为。