CodeIgniter 全局公共函数完全指南:从 is_php 到 function_usable 的源码级解析
【免费下载链接】CodeIgniterOpen Source PHP Framework (originally from EllisLab)项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter
CodeIgniter(本仓库为 EllisLab 起源的开源 PHP 框架)内置了一组全局定义的公共函数,它们在框架启动时随system/core/Common.php一并加载,不需要加载任何库或 Helper 即可在应用任何位置直接调用。本文以官方文档 common_functions.rst 为主线,逐一对 13 个核心公共函数进行参数说明、代码示例与底层源码剖析,并穿插本仓库的真实测试用例,帮助你彻底掌握这些"隐形基础设施",写出更健壮、更安全的 CodeIgniter 3 应用。
一、公共函数概览:它们从哪来、何时可用
CodeIgniter 的公共函数与类库不同:它们不是通过$this->load->library()加载的对象方法,而是全局命名空间下的过程式函数。其唯一实现文件位于仓库的 system/core/Common.php,由前端控制器 index.php 引导时引入(经由system/core/CodeIgniter.php初始化流程),因此从框架入口到应用退出,整个生命周期内随处可调用。
从源码结构看,Common.php中几乎每个函数都用if ( ! function_exists('xxx'))包裹定义,这意味着你可以放心地在application/core/下覆盖这些函数,框架不会产生重复定义冲突——这为高级定制留下了明确的扩展口。
官方文档将公共函数分为三大用途:
| 用途分类 | 函数 |
|---|---|
| 环境与运行时检测 | is_php()、is_cli()、is_https() |
| 文件与配置访问 | is_really_writable()、config_item()、get_mimes() |
| 安全消毒与输入处理 | remove_invisible_characters()、html_escape()、function_usable() |
| 错误处理与日志 | show_error()、show_404()、log_message() |
| HTTP 响应控制 | set_status_header() |
下文逐一展开。
二、环境与运行时检测函数
1.is_php($version):判断当前 PHP 版本
- 参数:
string $version—— 要比较的版本号字符串,例如'5.5' - 返回值:
bool—— 当前 PHP 版本大于等于指定版本时返回TRUE,否则返回FALSE
官方示例:
if (is_php('5.5')) { echo json_last_error_msg(); }源码剖析(Common.php):
function is_php($version) { static $_is_php; $version = (string) $version; if ( ! isset($_is_php[$version])) { $_is_php[$version] = version_compare(PHP_VERSION, $version, '>='); } return $_is_php[$version]; }两个关键实现细节值得注意:
- 底层基于 PHP 原生
version_compare(..., '>=')做语义化版本比较,'5.5'可正确匹配5.5.x; - 使用
static静态数组缓存比较结果,同一版本号只计算一次,避免在循环中反复调用时产生性能开销。
测试用例 Common_test.php 验证了其边界行为:
$this->assertTrue(is_php('1.2.0')); // 当前版本必然 >= 1.2.0 $this->assertFalse(is_php('9999.9.9')); // 未来版本必然不满足实战场景:当你的代码需要调用仅在特定 PHP 版本才存在的函数(如json_last_error_msg()需 PHP 5.5+)时,用它做特性探测是最稳妥的写法。
2.is_cli():判断是否运行于命令行
- 返回值:
bool—— 应用通过命令行运行时返回TRUE,否则返回FALSE
官方文档特别注明:该函数同时检查PHP_SAPI值是否为'cli',以及STDIN常量是否已定义。源码实现(Common.php)与之一一对应:
function is_cli() { return (PHP_SAPI === 'cli' OR defined('STDIN')); }STDIN是 PHP CLI 模式下预定义的常量,defined('STDIN')这一判断是为了兼容某些把php-cli包装为其他 SAPI 名(如'phpdbg'、'cli-server')的运行环境。实战场景:编写定时任务脚本、自定义 CLI 命令时,用它区分 Web 请求与命令行调用,决定输出 HTML 还是纯文本。
3.is_https():判断是否运行于 HTTPS
- 返回值:
bool—— 当前为 HTTP-over-SSL(HTTPS)连接时返回TRUE,其他任何情况(包括非 HTTP 请求)返回FALSE
源码实现(Common.php)依次检查三类服务器变量:
function is_https() { if ( ! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') { return TRUE; } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') { return TRUE; } elseif ( ! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off') { return TRUE; } return FALSE; }可以看出它兼顾了三种常见部署形态:标准 HTTPS(HTTPS变量)、反向代理/负载均衡透传(HTTP_X_FORWARDED_PROTO: https)、以及某些前端 Web 服务器(如部分 IIS 配置)使用的HTTP_FRONT_END_HTTPS。实战场景:强制跳转 HTTPS、生成安全 Cookie、判断是否应启用加密传输等逻辑的入口判断。
三、文件与配置访问函数
4.is_really_writable($file):真实可写性检测
- 参数:
string $file—— 文件或目录路径 - 返回值:
bool—— 路径确实可写返回TRUE,否则FALSE
官方文档明确指出该函数存在的意义:在 Windows 服务器上,is_writable()可能返回TRUE,但实际上无法写入——因为操作系统只在设置了只读属性时才向 PHP 报告FALSE。因此该函数通过"实际尝试写入"来判定可写性,官方建议仅在平台信息可能不可靠时使用。
源码实现(Common.php)清晰展示了两种平台的分支处理:
function is_really_writable($file) { // UNIX-like 服务器直接用 is_writable() if (DIRECTORY_SEPARATOR === '/') { return is_writable($file); } /* Windows 服务器或 safe_mode 开启时: * 实际写入一个文件再读取验证 */ if (is_dir($file)) { $file = rtrim($file, '/').'/'.md5(mt_rand()); if (($fp = @fopen($file, 'ab')) === FALSE) { return FALSE; } fclose($fp); @chmod($file, 0777); @unlink($file); return TRUE; } elseif ( ! is_file($file) OR ($fp = @fopen($file, 'ab')) === FALSE) { return FALSE; } fclose($fp); return TRUE; }注意实现细节:对目录,它会在目录内用md5(mt_rand())生成一个随机临时文件尝试以追加模式fopen,成功后删除;对文件则直接尝试打开追加。实战场景:安装向导、缓存目录检查、上传目录权限校验等场景中,用它比裸调is_writable()更可靠。
5.config_item($key):读取单个配置项
- 参数:
string $key—— 配置项键名 - 返回值:
mixed—— 配置值;键不存在时返回NULL
官方文档提醒:访问配置信息的首选方式是 Config 库(参见 Config 库文档),但config_item()可用于快速获取单个键值。
源码实现(Common.php):
function config_item($item) { static $_config; if (empty($_config)) { // 静态变量不能直接保存引用,因此包一层数组 $_config[0] =& get_config(); } return isset($_config[0][$item]) ? $_config[0][$item] : NULL; }它依赖同为公共函数的get_config()(Common.php)完成主配置加载:优先加载application/config/config.php,再合并环境目录application/config/ENVIRONMENT/config.php的覆盖项(ENVIRONMENT由 index.php 定义),并支持通过$replace参数动态追加/覆盖配置值。整个加载过程同样使用static缓存,整个请求周期只解析一次配置文件。
实战示例:
$charset = config_item('charset'); // 默认 'UTF-8' $prefix = config_item('subclass_prefix'); // 默认 'MY_'以上两个默认值均可在 application/config/config.php 中查证(charset见第 94 行,subclass_prefix见第 119 行)。
6.get_mimes():获取 MIME 类型映射表
- 返回值:
array—— 文件类型关联数组的引用
官方文档明确:该函数返回的是application/config/mimes.php中 MIME 数组的引用。源码实现(Common.php):
function &get_mimes() { static $_mimes; if (empty($_mimes)) { $_mimes = file_exists(APPPATH.'config/mimes.php') ? include(APPPATH.'config/mimes.php') : array(); if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')) { $_mimes = array_merge($_mimes, include(APPPATH.'config/'.ENVIRONMENT.'/mimes.php')); } } return $_mimes; }它同样支持环境级覆盖:application/config/ENVIRONMENT/mimes.php中的条目会通过array_merge合并到默认表之上。MIME 表本体位于 application/config/mimes.php。实战场景:Upload库校验上传文件类型、自定义下载响应时判断Content-Type。
四、安全消毒与输入处理函数
7.remove_invisible_characters($str, $url_encoded = TRUE):清除不可见字符
- 参数:
string $str—— 输入字符串;bool $url_encoded—— 是否同时清除 URL 编码形式(默认TRUE) - 返回值:
string—— 消毒后的字符串
官方文档指出:该函数用于防止在 ASCII 字符之间夹入 NULL 字符,例如把Java\0script这类输入还原为Javascript,从而阻断基于空字节注入的绕过攻击。
官方示例:
remove_invisible_characters('Java\\0script'); // 返回: 'Javascript'源码实现(Common.php)通过一组正则表达式完成清洗:
function remove_invisible_characters($str, $url_encoded = TRUE) { $non_displayables = array(); // 除换行(dec 10)、回车(dec 13)、水平制表(dec 09)外的所有控制字符 if ($url_encoded) { $non_displayables[] = '/%0[0-8bcef]/i'; // url 编码的 00-08, 11, 12, 14, 15 $non_displayables[] = '/%1[0-9a-f]/i'; // url 编码的 16-31 $non_displayables[] = '/%7f/i'; // url 编码的 127 } $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S'; // 00-08, 11, 12, 14-31, 127 do { $str = preg_replace($non_displayables, '', $str, -1, $count); } while ($count); return $str; }值得强调的细节:\x0A(换行)、\x0D(回车)、\x09(水平制表)被有意保留,因为它们是合法文本格式字符;do...while($count)循环确保嵌套/重复编码的字符也能被逐层清除。测试用例 Common_test.php 同时覆盖了 URL 编码开关两种模式:
$raw_string = 'Here is a string containing invisible'.chr(0x08).' text %0e.'; // 传入 FALSE 时仅清除原始控制字符,保留 %0e $this->assertEquals($removed_string, remove_invisible_characters($raw_string, FALSE)); // 默认模式下连 %0e、%1F 等 URL 编码字符也一并清除该函数是 Input 库过滤流程的底层组成部分,也是你处理用户输入、日志内容时的通用消毒利器。
8.html_escape($var):HTML 转义(防 XSS)
- 参数:
mixed $var—— 字符串或数组(可嵌套) - 返回值:
mixed—— 转义后的字符串或等结构数组
官方文档:该函数是 PHP 原生htmlspecialchars()的别名式封装,优势在于能够接受字符串数组(包括多维数组),用于防范跨站脚本(XSS)。
源码实现(Common.php)值得逐行拆解:
function html_escape($var, $double_encode = TRUE) { if (empty($var)) { return $var; } if (is_array($var)) { foreach (array_keys($var) as $key) { $var[$key] = html_escape($var[$key], $double_encode); } return $var; } return htmlspecialchars($var, ENT_QUOTES, config_item('charset'), $double_encode); }三个关键点:
- 数组递归:遍历数组键,对每个值递归调用自身,天然支持多维数组,且保留键名不变;
ENT_QUOTES标志:同时转义单引号和双引号,比默认行为更严格;- 字符集取自配置:使用
config_item('charset')(默认'UTF-8',见 application/config/config.php)作为htmlspecialchars的字符集参数,保证与全局配置一致;第二个参数$double_encode = FALSE可防止对已转义内容二次转义。
测试用例(Common_test.php)验证了引号转义与数组递归:
$this->assertEquals( html_escape('Here is a string containing "quoted" text.'), 'Here is a string containing "quoted" text.' ); // 多维数组原样递归转义实战场景:在视图中输出用户提交的数据前统一转义,是 CodeIgniter 应用防 XSS 的最便捷手段。
9.function_usable($function_name):函数可用性检测
- 参数:
string $function_name—— 待检测的函数名 - 返回值:
bool—— 函数存在且可用返回TRUE,否则FALSE
官方文档:该函数执行function_exists()检查,若服务器加载了 Suhosin 扩展,还会进一步检查函数是否被 Suhosin 禁用。它特别适合检测eval()、exec()这类高危函数在严格安全策略服务器上是否可调用。
源码实现(Common.php):
function function_usable($function_name) { static $_suhosin_func_blacklist; if (function_exists($function_name)) { if ( ! isset($_suhosin_func_blacklist)) { $_suhosin_func_blacklist = extension_loaded('suhosin') ? explode(',', trim(ini_get('suhosin.executor.func.blacklist'))) : array(); } return ! in_array($function_name, $_suhosin_func_blacklist, TRUE); } return FALSE; }实现要点:Suhosin 黑名单配置项suhosin.executor.func.blacklist是以逗号分隔的字符串,这里将其拆分为数组并做严格类型比较(TRUE第三个参数);黑名单结果用static缓存,只解析一次。官方文档同时说明其历史背景:Suhosin 在函数被黑名单命中时不是返回错误而是直接终止脚本执行,这曾是 Suhosin 的一个 bug(修复版本 0.9.34 迟迟未发布),因此框架提供了这一临时但长期保留的防护函数。
五、错误处理与日志函数
本节三个函数都是对system/core/Exceptions.php与system/core/Log.php中类方法的过程式封装,完整行为说明见官方 错误处理文档。
10.show_error($message, $status_code, $heading = 'An Error Was Encountered')
- 参数:
mixed $message错误消息(可为字符串或数组);int $status_codeHTTP 状态码;string $heading错误页标题 - 返回值:
void(直接输出错误页并终止脚本)
源码实现(Common.php):
function show_error($message, $status_code = 500, $heading = 'An Error Was Encountered') { $status_code = abs($status_code); if ($status_code < 100) { $exit_status = $status_code + 9; // 9 即 EXIT__AUTO_MIN $status_code = 500; } else { $exit_status = 1; // EXIT_ERROR } $_error =& load_class('Exceptions', 'core'); echo $_error->show_error($heading, $message, 'error_general', $status_code); exit($exit_status); }实现要点与官方错误文档完全吻合:
- 实际渲染由
CI_Exceptions::show_error()(Exceptions.php)完成,模板为application/views/errors/html/error_general.php或 CLI 版application/views/errors/cli/error_general.php(两套模板在仓库 application/views/errors 下); - 退出状态码的巧妙设计:当
$status_code < 100时,HTTP 状态固定为 500,而进程退出码取$status_code + EXIT__AUTO_MIN(9);否则退出码为EXIT_ERROR(1)。这些退出码常量定义于 application/config/constants.php,供 CLI 下外部进程监控脚本健康状态使用。
11.show_404($page = '', $log_error = TRUE)
- 参数:
string $page—— 未找到的 URI 字符串;bool $log_error—— 是否写入日志(默认TRUE) - 返回值:
void
源码实现(Common.php):
function show_404($page = '', $log_error = TRUE) { $_error =& load_class('Exceptions', 'core'); $_error->show_404($page, $log_error); exit(4); // EXIT_UNKNOWN_FILE }它调用CI_Exceptions::show_404()(Exceptions.php)渲染application/views/errors/html/error_404.php或 CLI 对应模板,随后以EXIT_UNKNOWN_FILE(4)退出进程。注意:当控制器找不到时,CodeIgniter 的 Router 会自动触发 404 展示;第二个参数设为FALSE可跳过 404 的日志记录(例如某些爬虫频繁触发 404 的场景)。
12.log_message($level, $message):写入日志
- 参数:
string $level—— 日志级别:'error'、'debug'或'info';string $message—— 日志内容 - 返回值:
void
源码实现(Common.php):
function log_message($level, $message) { static $_log; if ($_log === NULL) { // 静态变量不能直接保存引用,因此包一层数组 $_log[0] =& load_class('Log', 'core'); } $_log[0]->write_log($level, $message); }它是CI_Log::write_log()的别名,首次调用时通过load_class('Log', 'core')懒加载日志类,之后复用静态实例。官方错误文档给出了完整用法示例:
if ($some_var == '') { log_message('error', 'Some variable did not contain a value.'); } else { log_message('debug', 'Some variable was correctly set'); } log_message('info', 'The purpose of some variable is to provide some value.');三种级别按优先级排列:Error(真实错误,如 PHP 错误或用户错误)>Debug(辅助调试信息)>Info(最低优先级的信息类消息)。
重要前提:日志要真正落盘,需要满足两个条件——application/logs/目录可写;并且在 application/config/config.php 中正确设置log_threshold(默认值为0,即日志被完全禁用;设为 1 只记 error,2 记 debug,3 记 info,4 记全部)。这正是is_really_writable()派上用场的场景:写日志前先校验目录可写性。
六、HTTP 状态头控制:set_status_header($code, $text = '')
- 参数:
int $code—— HTTP 状态码;string $text—— 自定义状态文本(可选) - 返回值:
void
官方示例:
set_status_header(401); // 设置响应头为: Unauthorized源码实现(Common.php)内含值得深入讲解的完整逻辑:
- CLI 下直接返回:
is_cli()为真时不输出任何头(命令行无 HTTP 语义); - 参数校验:
$code必须为非空数字,否则触发show_error('Status codes must be numeric', 500); - 状态文本映射:不传
$text时,从一个内置的完整状态码-文本映射表(覆盖 100 到 511 的 50 余个标准状态码)中查取,如200 => 'OK'、301 => 'Moved Permanently'、404 => 'Not Found'、500 => 'Internal Server Error'等;查不到时触发错误提示要求自查状态码或显式传入文本; - CGI 兼容分支:
PHP_SAPI以'cgi'开头时使用header('Status: ...')语法(FastCGI 环境需要); - 协议协商:根据
$_SERVER['SERVER_PROTOCOL']在HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/2.0中选取,未知则回退HTTP/1.1,最终调用header($server_protocol.' '.$code.' '.$text, TRUE, $code)。
实战场景:在控制器或钩子中手动控制响应状态码(如 API 返回 201 Created、403 Forbidden、429 Too Many Requests),比依赖http_response_code()更契合框架的 CLI/CGI 兼容需求。
七、公共函数背后的"隐形基础设施"
官方文档只列出上述 13 个函数,但从 Common.php 的完整源码看,还有几个不直接面向业务、却支撑整个框架运转的同级函数,理解它们能加深你对公共函数体系的认识:
load_class()与is_loaded():单例类注册表
load_class($class, $directory = 'libraries', $param = NULL)(Common.php)是框架的核心单例机制:按application/优先、system/其次的顺序查找类文件,支持subclass_prefix(默认'MY_')扩展类覆盖,实例化后存入静态数组供后续调用复用;找不到类时输出 503 并以EXIT_UNKNOWN_CLASS(5)退出。is_loaded()(Common.php)则维护已加载类的登记表,供 Loader 等组件查询。
get_config():配置文件的原始读取器
前文已述,它负责加载并缓存application/config/config.php与环境覆盖文件,是config_item()的底层依赖,在 Config 类实例化之前即可工作。
_stringify_attributes():HTML 属性字符串化
_stringify_attributes($attributes, $js = FALSE)(Common.php)将字符串/数组/对象形式的属性列表转换为class="foo" id="bar"格式,$js = TRUE时输出width=800,height=600的 JS 参数风格。它在表单、HTML Helper 中广泛使用,测试用例 Common_test.php 对两种模式均有断言。
三个错误处理器:_error_handler()、_exception_handler()、_shutdown_handler()
(Common.php)分别通过set_error_handler、set_exception_handler、register_shutdown_function注册,负责把 PHP 错误/未捕获异常/致命错误统一转入框架日志与错误模板,并在致命错误时设置 500 状态头、以EXIT_ERROR(1)退出。它们与 CodeIgniter.php 的引导流程配合,构成了完整的错误处理闭环。
八、实践建议与调用规范
- 无需加载即可用:这些函数不依赖任何库或 Helper,控制器、模型、视图、钩子、甚至
application/config之外的任意业务文件中都可直接调用,无需$this->load。 - 可安全覆盖:由于
function_exists()保护,如需定制行为(例如让show_error()输出 JSON),可在application/core/Common.php(或通过application/config/autoload.php引入的扩展文件中)重新定义同名函数。 - 组合使用更佳:日志落盘前用
is_really_writable()校验目录;输出用户数据前用html_escape();调用高危函数前用function_usable();判断运行环境用is_cli()/is_https()。 - 配置联动:
html_escape()的字符集、config_item()的数据源、log_message()的开关阈值,均与 application/config/config.php 中的charset、log_threshold等配置项直接联动,理解配置与函数的关系是排查问题的关键。 - 以测试为行为契约:本仓库 tests/codeigniter/core/Common_test.php 对
is_php()、html_escape()、remove_invisible_characters()、_stringify_attributes()的行为做了可复现的断言,是理解这些函数边界行为(如数组递归、URL 编码开关、严格比较)最直观的参考。
这套全局公共函数是 CodeIgniter 一切组件协作的基石——理解了它们,你就理解了框架"开箱即用"背后的设计哲学:极小的核心、全局可用的过程式接口、可覆盖的扩展点。
【免费下载链接】CodeIgniterOpen Source PHP Framework (originally from EllisLab)项目地址: https://gitcode.com/gh_mirrors/co/CodeIgniter
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考