PHP 20 个实用开发技巧(完整详解)

PHP 20 个实用开发技巧(完整详解)
你想要的是这 20 个 PHP 实用技巧的完整梳理和可落地实现,下面我将每个技巧补充完整代码示例和细节说明,方便你直接应用到项目中:
1. 不要使用相对路径,优先使用绝对路径
问题
相对路径会查找 PHP 包含路径和当前目录,存在路径混乱(被其他脚本包含时、定时任务运行时)的问题。
解决方案
php
运行
// 不推荐(相对路径)
include “config.php”;
include “lib/func.php”;

// 推荐1:写死绝对路径(不灵活)
include “/var/www/project/config.php”;

// 推荐2:使用__FILE__常量(灵活,迁移无需修改)
// __FILE__ 获取当前文件的完整路径和文件名
// dirname() 获取文件所在目录
define(‘PROJECT_ROOT’, dirname(__FILE__));
include PROJECT_ROOT . “/config.php”;
include PROJECT_ROOT . “/lib/func.php”;

// PHP 5.3+ 可使用 __DIR__(直接获取当前文件目录,等价于 dirname(__FILE__))
define(‘PROJECT_ROOT’, __DIR__);
include PROJECT_ROOT . “/config.php”;
2. 不要直接使用 require/include,封装加载函数
问题
直接多次调用 require/include 可读性差,后续修改文件路径需全局替换。
解决方案
php
运行
// 不推荐(原始用法)
require “config.php”;
require “lib/db.php”;
require “lib/validate.php”;

// 推荐:封装助手函数
function load_file($file_path) {
$full_path = PROJECT_ROOT . “/” . $file_path;
if (file_exists($full_path)) {
require $full_path;
} else {
die(“文件 {$full_path} 不存在!”);
}
}

// 调用(可读性更强)
load_file(“config.php”);
load_file(“lib/db.php”);
load_file(“lib/validate.php”);

// 扩展:支持多目录查找
function load_file_ext($file_name, $dirs = []) {
// 默认添加项目根目录
$dirs[] = PROJECT_ROOT;
foreach ($dirs as $dir) {
$full_path = rtrim($dir, “/”) . “/” . $file_name;
if (file_exists($full_path)) {
require $full_path;
return true;
}
}
die(“文件 {$file_name} 未在指定目录中找到!”);
}

// 调用:查找多个目录
load_file_ext(“db.php”, [PROJECT_ROOT . “/lib”, PROJECT_ROOT . “/core”]);
3. 为应用保留调试代码,区分开发 / 生产环境
问题
调试代码注释 / 删除后,后续问题排查需重新编写,效率低下。
解决方案
php
运行
// 1. 定义环境常量(建议在配置文件中统一配置)
// 开发环境
define(‘APP_ENV’, ‘development’);
// 生产环境
// define(‘APP_ENV’, ‘production’);

// 2. 封装调试函数
function debug_log($data) {
if (APP_ENV === ‘development’) {
// 打印调试信息(可根据需求格式化)
echo ”

";
        var_dump($data);
        echo "

“;
}
// 生产环境不输出,可选择写入日志文件
else {
$log_str = is_array($data) || is_object($data) ? json_encode($data) : $data;
error_log(date(‘Y-m-d H:i:s’) . ” – ” . $log_str . PHP_EOL, 3, PROJECT_ROOT . “/logs/debug.log”);
}
}

// 调用:保留调试代码,无需注释/删除
$db_sql = “SELECT * FROM user WHERE id = 1”;
debug_log(“数据库查询语句:” . $db_sql);
$user_info = $db->query($db_sql);
debug_log(“用户信息:” . $user_info);
4. 使用可跨平台的函数执行系统命令
问题
system/exec/passthru/shell_exec 可能被共享主机禁用,逐个判断繁琐。
解决方案
php
运行
/**
* 跨平台执行系统命令
* @param string $cmd 系统命令
* @return string 命令执行结果
*/
function exec_system_cmd($cmd) {
$result = ”;
// 依次判断可用的执行函数
if (function_exists(‘system’)) {
ob_start(); // 捕获输出
system($cmd, $return_var);
$result = ob_get_clean();
} elseif (function_exists(‘exec’)) {
exec($cmd, $output, $return_var);
$result = implode(PHP_EOL, $output);
} elseif (function_exists(‘passthru’)) {
ob_start();
passthru($cmd, $return_var);
$result = ob_get_clean();
} elseif (function_exists(‘shell_exec’)) {
$result = shell_exec($cmd);
} else {
die(“没有可用的系统命令执行函数!”);
}
return $result;
}

// 调用
$dir_list = exec_system_cmd(“ls -l”);
echo $dir_list;
5. 灵活编写函数,支持多类型参数
问题
为单个参数和数组参数分别创建函数,造成代码冗余。
解决方案
php
运行
/**
* 灵活添加项目(支持单个值/数组值)
* @param mixed $items 单个项目或项目数组
* @param array $target_arr 目标数组
* @return array 处理后的数组
*/
function add_items($items, $target_arr = []) {
// 判断是否为数组,非数组则转为数组
if (!is_array($items)) {
$items = [$items];
}
// 循环添加(去重可选)
foreach ($items as $item) {
if (!in_array($item, $target_arr)) {
$target_arr[] = $item;
}
}
return $target_arr;
}

// 调用1:添加单个项目
$user_list = [“张三”, “李四”];
$user_list = add_items(“王五”, $user_list);

// 调用2:添加多个项目(数组)
$user_list = add_items([“赵六”, “孙七”], $user_list);

print_r($user_list); // 输出:Array ( [0] => 张三 [1] => 李四 [2] => 王五 [3] => 赵六 [4] => 孙七 )
6. 有意忽略 PHP 关闭标签
问题
PHP 关闭标签(?>)后若存在空格 / 换行 / 特殊字符,会导致「Headers already send error」(响应头已发送错误)。
解决方案
php
运行
// 不推荐:存在关闭标签,且后面有多余字符

// 推荐:省略关闭标签

7. 集中收集输出,使用输出缓冲
问题
分散输出内容,无法统一修改,且可能出现「处理与输出混合」的页面错误。
解决方案
php
运行
// 不推荐:分散输出
function show_header() {
echo ”

网站标题

“;
}
function show_content() {
echo ”

网站内容

“;
}
show_header();
show_content();

// 推荐1:手动收集输出(局部变量)
function show_header_new() {
$html = ”

网站标题

“;
return $html;
}
function show_content_new() {
$html = ”

网站内容

“;
return $html;
}
// 集中输出
$output = “”;
$output .= show_header_new();
$output .= show_content_new();
// 统一修改输出(示例:替换关键词)
$output = str_replace(“网站”, “我的网站”, $output);
echo $output;

// 推荐2:使用PHP内置输出缓冲函数
ob_start(); // 开启输出缓冲(后续echo内容不会直接发送到浏览器,而是存入缓冲区)
show_header();
show_content();
$output = ob_get_clean(); // 获取缓冲区内容并清空缓冲区
$output = str_replace(“网站”, “我的网站”, $output);
echo $output;
8. 发送正确的 MIME 类型头信息
问题
不指定 MIME 类型,浏览器可能无法正确解析内容(如 XML、JSON、JS 等)。
解决方案
php
运行
// 示例1:输出XML
header(“Content-Type: application/xml; charset=utf-8”);
echo ‘‘;
echo ‘张三25’;

// 示例2:输出JSON
header(“Content-Type: application/json; charset=utf-8”);
$user = [“name” => “张三”, “age” => 25];
echo json_encode($user, JSON_UNESCAPED_UNICODE);

// 示例3:输出JavaScript
header(“Content-Type: application/javascript; charset=utf-8”);
echo ‘var user = {name: “张三”, age: 25};’;

// 示例4:输出CSS
header(“Content-Type: text/css; charset=utf-8”);
echo ‘body {font-size: 14px; color: #333;}’;

// 示例5:输出图片(JPG)
header(“Content-Type: image/jpeg”);
$img = imagecreatefromjpeg(“test.jpg”);
imagejpeg($img);
imagedestroy($img);
9. 为 MySQL 连接设置正确的字符编码
问题
数据库表已设置 UTF-8,但 PHP 连接未指定编码,导致中文乱码。
解决方案
php
运行
// MySQLi(面向过程)
$conn = mysqli_connect(“localhost”, “root”, “123456”, “test_db”);
// 设置连接编码(关键)
mysqli_set_charset($conn, “utf8mb4”); // 推荐utf8mb4,支持emoji表情

// MySQLi(面向对象)
$conn = new mysqli(“localhost”, “root”, “123456”, “test_db”);
$conn->set_charset(“utf8mb4”);

// PDO
$dsn = “mysql:host=localhost;dbname=test_db;charset=utf8mb4”;
$conn = new PDO($dsn, “root”, “123456”);
10. 使用 htmlentities 设置正确的编码选项
问题
PHP5.4 前默认编码为 ISO-8859-1,无法正确输出中文等特殊字符,导致乱码。
解决方案
php
运行
// 不推荐:不指定编码(PHP5.4前默认ISO-8859-1)
$str = “À â 张三”;
echo htmlentities($str); // 乱码

// 推荐:指定UTF-8编码
$str = “À â 张三”;
// htmlentities:转换所有HTML特殊字符
echo htmlentities($str, ENT_QUOTES, “UTF-8”);

// 补充:htmlspecialchars(仅转换关键HTML特殊字符,性能更高)
echo htmlspecialchars($str, ENT_QUOTES, “UTF-8”);
11. 不要在应用中使用 gzip 压缩输出
说明
PHP 的 ob_gzhandler 负责应用逻辑即可,压缩优化应交给服务器(更高效)。
解决方案
php
运行
// 不推荐:使用PHP进行gzip压缩
// ob_start(“ob_gzhandler”);

// 推荐:使用Apache/Nginx进行压缩
// Apache:开启mod_gzip/mod_deflate模块(配置httpd.conf)
// Nginx:开启gzip模块(配置nginx.conf)
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
12. 使用 json_encode 输出动态 JavaScript 内容
问题
手动拼接 JavaScript 变量,易出现语法错误和引号冲突。
解决方案
php
运行
// 不推荐:手动拼接
$user = [“name” => “张三”, “age” => 25, “desc” => “他说:’Hello World'”];
echo ““;

// 推荐:使用json_encode
$user = [“name” => “张三”, “age” => 25, “desc” => “他说:’Hello World'”];
echo ““;
13. 写文件前,检查目录写权限
问题
file_put_contents 失败时,无法快速定位原因(目录不存在 / 无权限 / 文件被锁定)。
解决方案
php
运行
/**
* 安全写入文件
* @param string $file_path 文件路径
* @param string $content 文件内容
* @return bool
*/
function safe_file_put_contents($file_path, $content) {
// 获取文件所在目录
$dir = dirname($file_path);

// 检查目录是否存在
if (!is_dir($dir)) {
// 创建目录(递归创建,权限0755)
$mkdir = mkdir($dir, 0755, true);
if (!$mkdir) {
die(“目录 {$dir} 创建失败!”);
}
}

// 检查目录是否可写
if (!is_writable($dir)) {
die(“目录 {$dir} 无写入权限!”);
}

// 写入文件
$result = file_put_contents($file_path, $content);
if ($result === false) {
die(“文件 {$file_path} 写入失败(可能被锁定)!”);
}

return true;
}

// 调用
safe_file_put_contents(PROJECT_ROOT . “/data/test.txt”, “测试内容”);
14. 更改应用创建的文件权限
问题
PHP 创建的文件默认权限由系统 umask 决定,可能导致其他用户无法访问。
解决方案
php
运行
// 写入文件后设置权限
$file_path = PROJECT_ROOT . “/data/test.txt”;
file_put_contents($file_path, “测试内容”);
// 设置文件权限(0644:所有者可读可写,其他用户只读)
chmod($file_path, 0644);

// 目录创建后设置权限
$dir_path = PROJECT_ROOT . “/data/upload”;
mkdir($dir_path, 0755, true);
chmod($dir_path, 0755); // 0755:所有者可读可写可执行,其他用户可读可执行

// 封装到安全写入函数中(优化)
function safe_file_put_contents_v2($file_path, $content) {
$dir = dirname($file_path);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
chmod($dir, 0755);
}
if (!is_writable($dir)) {
die(“目录 {$dir} 无写入权限!”);
}
$result = file_put_contents($file_path, $content);
if ($result === false) {
die(“文件 {$file_path} 写入失败!”);
}
chmod($file_path, 0644); // 设置文件权限
return true;
}
15. 不要依赖 submit 按钮值
问题
多语言应用中,submit 按钮值(如 “Save”/“保存”/“Sauvegarder”)会变化,导致判断失效。
解决方案
php
运行
// 不推荐:依赖按钮值判断
if ($_POST[‘submit’] == ‘保存’ || $_POST[‘submit’] == ‘Save’) {
// 处理表单
}

// 推荐:使用隐藏域或按钮name属性判断
// HTML代码
/*


*/

// PHP代码
if (isset($_POST[‘action’]) && $_POST[‘action’] == ‘save_user’) {
// 处理表单(不受按钮显示值影响)
$user_name = $_POST[‘user_name’];
// …
}

// 另一种方式:使用按钮name属性(多个按钮)
/*


*/
if (isset($_POST[‘action_save’])) {
// 保存逻辑
} elseif (isset($_POST[‘action_delete’])) {
// 删除逻辑
}
16. 相同值的变量定义成静态变量
问题
函数每次调用都会重新初始化变量,若变量值固定,造成资源浪费。
解决方案
php
运行
// 不推荐:每次调用都重新创建数组
function get_default_config() {
$config = [“timeout” => 30, “charset” => “utf8mb4”, “debug” => false];
return $config;
}

// 推荐:使用静态变量,仅初始化一次
function get_default_config_v2() {
static $config = null; // 静态变量,函数调用结束后不销毁
if ($config === null) {
$config = [“timeout” => 30, “charset” => “utf8mb4”, “debug” => false];
}
return $config;
}
17. 不要直接使用 $_SESSION 变量
问题
同域名下多个应用共享 SESSION,可能导致 KEY 冲突(如两个应用都使用 $_SESSION [‘user’])。
解决方案
php
运行
// 不推荐:直接使用$_SESSION
$_SESSION[‘user’] = $user_info;
$_SESSION[‘token’] = $token;

// 推荐:定义应用前缀,封装SESSION操作函数
define(‘APP_SESSION_PREFIX’, ‘blog_’); // 不同应用设置不同前缀(如admin_/shop_)

// 封装SESSION设置函数
function session_set($key, $value) {
$session_key = APP_SESSION_PREFIX . $key;
$_SESSION[$session_key] = $value;
}

// 封装SESSION获取函数
function session_get($key, $default = null) {
$session_key = APP_SESSION_PREFIX . $key;
return isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : $default;
}

// 封装SESSION删除函数
function session_del($key) {
$session_key = APP_SESSION_PREFIX . $key;
unset($_SESSION[$session_key]);
}

// 调用
session_set(‘user’, $user_info);
session_set(‘token’, $token);
$user = session_get(‘user’);
$token = session_get(‘token’);
session_del(‘token’);
18. 将工具函数封装到类中
问题
全局工具函数易与 PHP 内置函数或其他库函数重名,造成冲突。
解决方案
php
运行
// 不推荐:全局工具函数
function format_date($timestamp) {
return date(‘Y-m-d H:i:s’, $timestamp);
}

function get_ip() {
return $_SERVER[‘REMOTE_ADDR’] ?? ‘未知IP’;
}

// 推荐:封装到工具类中(静态类,无需实例化)
class Tool {
/**
* 格式化时间戳
* @param int $timestamp 时间戳
* @return string
*/
public static function format_date($timestamp) {
return date(‘Y-m-d H:i:s’, $timestamp);
}

/**
* 获取用户IP
* @return string
*/
public static function get_ip() {
return $_SERVER[‘REMOTE_ADDR’] ?? ‘未知IP’;
}
}

// 调用(通过类名::方法名,避免冲突)
$format_date = Tool::format_date(time());
$user_ip = Tool::get_ip();

// 扩展:支持多版本类(命名空间区分)
/*
namespace App\V1;
class Tool { … }

namespace App\V2;
class Tool { … }

// 调用
App\V1\Tool::format_date(time());
App\V2\Tool::format_date(time());
*/
19. 一系列实用小技巧
php
运行
// 1. 使用echo取代print(echo性能更高,支持多参数)
echo “Hello”, ” “, “World”; // 支持多参数
// print “Hello World”; // 仅支持单参数,返回值始终为1

// 2. 使用str_replace取代preg_replace(简单替换性能更高)
$str = “Hello PHP”;
$str = str_replace(“PHP”, “Java”, $str); // 简单替换
// $str = preg_replace(“/PHP/”, “Java”, $str); // 正则替换(仅在需要复杂匹配时使用)

// 3. 不要使用short tag(<? ?>),使用完整标签(
// 不推荐:<? echo “Hello”; ?>
// 推荐:

// 4. 简单字符串用单引号取代双引号(双引号会解析变量,性能略低)
$name = “张三”;
$str1 = ‘我的名字是’ . $name; // 推荐
// $str2 = “我的名字是{$name}”; // 双引号(仅在需要变量解析时使用)

// 5. header重定向后记得使用exit/die(防止后续代码执行)
header(“Location: /login.php”);
exit; // 必须添加

// 6. 不要在循环中调用函数(避免重复调用开销)
// 不推荐
for ($i = 0; $i < count($arr); $i++) { // 每次循环都调用count()
// …
}
// 推荐
$arr_count = count($arr);
for ($i = 0; $i < $arr_count; $i++) { // 仅调用一次count() // … } // 7. isset比strlen快(判断字符串是否非空) $str = “”; // 不推荐 if (strlen($str) > 0) { … }
// 推荐
if (isset($str) && $str !== “”) { … }

// 8. 始终如一的格式化代码(使用PSR规范)

// 9. 不要删除循环/if-else的括号(提高可读性,避免bug)
// 不推荐
if ($a > 0)
echo “a大于0”;
// 推荐
if ($a > 0) {
echo “a大于0″;
}

// 10. 使用有语法高亮的文本编辑器(VS Code/PhpStorm等)
20. 使用 array_map 快速处理数组
问题
循环遍历数组处理每个元素(如 trim、格式化),代码冗余。
解决方案
php
运行
// 不推荐:循环处理数组元素
$arr = [” 张三 “, ” 李四 “, ” 王五 “];
for ($i = 0; $i < count($arr); $i++) { $arr[$i] = trim($arr[$i]); } // 推荐1:使用array_map(映射处理数组每个元素) $arr = [” 张三 “, ” 李四 “, ” 王五 “]; $arr = array_map(‘trim’, $arr); // 对每个元素调用trim函数 print_r($arr); // 输出:Array ( [0] => 张三 [1] => 李四 [2] => 王五 )

// 推荐2:array_map使用自定义函数
$arr = [1, 2, 3, 4, 5];
$arr = array_map(function($item) {
return $item * 2; // 每个元素乘以2
}, $arr);
print_r($arr); // 输出:Array ( [0] => 2 [1] => 4 [2] => 6 [3] => 8 [4] => 10 )

// 补充:array_walk(更适合修改原数组,支持传引用)
$arr = [” 张三 “, ” 李四 “, ” 王五 “];
array_walk($arr, function(&$item) { // &$item 传引用,直接修改原数组
$item = trim($item);
});
总结
路径与加载:优先绝对路径(DIR/FILE),封装加载函数提高灵活性;
环境与调试:保留调试代码,通过环境常量区分开发 / 生产环境;
输出与编码:使用输出缓冲、指定正确 MIME 类型和字符编码,避免乱码;
安全与兼容:封装 SESSION / 文件操作 / 系统命令执行,避免冲突和错误;
性能与规范:使用静态变量、array_map、避免循环内函数调用,遵循代码规范。
这些技巧可直接应用到 PHP 项目中,有效改善代码质量、提高开发效率和项目可维护性。