Skip to content

运行时配置(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
<?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
<?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_filesize
  • post_max_size
  • open_basedir
  • disable_functions
  • session.save_path
  • max_input_vars
  • 以及所有 PHP_INI_SYSTEMPHP_INI_PERDIR 级别的配置

ini_get() 函数

基本用法

ini_get() 用于获取配置项的当前值:

php
<?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
<?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
<?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
<?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: 128M

get_cfg_var() 与 ini_get() 的区别

这是一个非常重要的区别,许多开发者容易混淆:

函数获取的值是否受 ini_set 影响
ini_get()当前生效的值是(反映 ini_set 的修改)
get_cfg_var()php.ini 中的原始值否(不受 ini_set 影响)
php
<?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
<?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 格式。

基本用法

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

限制

ini
; .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 示例

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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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
<?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 行为。

参考链接