Skip to content

特殊函数

1. preg_match()函数

preg_match 函数简介(PHP 正则表达式匹配)

preg_match 是 PHP 中用于执行 正则表达式(Regular Expression) 匹配的核心函数,主要用于检查字符串是否匹配特定模式,并可以提取匹配的内容。

(1) 基本语法

php
preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0)

参数说明

参数说明
$pattern正则表达式模式(如 "/\d+/" 匹配数字)
$subject要搜索的目标字符串
&$matches(可选)存储匹配结果的数组
$flags(可选)控制匹配行为(如 PREG_OFFSET_CAPTURE 返回匹配位置)
$offset(可选)从字符串的哪个位置开始搜索

返回值

  • 1:匹配成功(模式匹配到内容)。
  • 0:未匹配到内容。
  • false:发生错误(如正则表达式语法错误)。

(2) 基本用法

① 检查字符串是否匹配

php
if (preg_match("/php/i", "PHP is great!")) {
    echo "匹配成功!";
} else {
    echo "未匹配!";
}
// 输出:匹配成功!(不区分大小写 `/i`)

② 提取匹配的内容

php
$text = "Date: 2023-10-05";
if (preg_match("/\d{4}-\d{2}-\d{2}/", $text, $matches)) {
    echo "找到日期:" . $matches[0]; // 2023-10-05
}

$matches 数组结构

  • $matches[0]:完整匹配的字符串。
  • $matches[1]$matches[2]...:捕获组(括号 () 内的部分)。

③ 使用捕获组提取特定部分

php
$email = "user@example.com";
if (preg_match("/^([a-z]+)@([a-z]+\.com)$/i", $email, $matches)) {
    echo "用户名:" . $matches[1] . "<br>";  // user
    echo "域名:" . $matches[2];            // example.com
}

④ 获取匹配位置(PREG_OFFSET_CAPTURE

php
preg_match("/\d+/", "ID: 12345", $matches, PREG_OFFSET_CAPTURE);
print_r($matches);

输出

Array
(
    [0] => Array
        (
            [0] => 12345  // 匹配的字符串
            [1] => 4      // 匹配的起始位置(从 0 开始)
        )
)

(3) 注意事项

  1. 正则定界符:通常使用 /(如 "/pattern/"),但也可以用 #~ 等。
  2. 性能优化:避免过于复杂的正则,尤其是长字符串匹配时。
  3. 错误处理:检查返回值是否为 false(如 preg_match() 返回 false 可能是正则语法错误)。
  4. 全局匹配preg_match 只匹配第一个结果,如果需要所有匹配,用 preg_match_all

(4) 相关函数

  • preg_match_all:匹配所有符合条件的结果。
  • preg_replace:正则替换字符串。
  • preg_split:用正则分割字符串。

(5)示例:匹配并提取所有数字

php
$text = "Age: 25, Height: 180cm";
preg_match_all("/\d+/", $text, $matches);
print_r($matches[0]); // Array ( [0] => 25 [1] => 180 )

(6)绕过方法

1.数组绕过,即传入的参数为数组

源代码如下:

image-20250329231457442

效果: image-20250329231529601

成功绕过preg_match()

2.利用PCRE回溯次数限制绕过

源代码:

image-20250329234016669

让回溯次数超过最大限制就可以使preg_match()函数返回false,从而绕过限制,中文的回溯次数在100万次就会崩溃,这个回溯保护使PHP为了防止关于正则表达式的DDOS

结果:

POC(概念验证):

image-20250329231734260

image-20250329231748435

成功绕过了preg_match()

脚本:

python
import requests
data = {"xdmtql": "sys nb" + "aaaaa" * 1000000}
res = requests.post('http://6fe942bf-0245-4683-a76c-ecbd54086f15.www.polarctf.com:8090/',
                    data=data, allow_redirects=False)
print(res.content)
# pre_match函数处理的字符长度有限,如果超过这个长度就会返回false也就是没有匹配到
# allow_redirects=False 表示禁止自动处理重定向。如果服务器返回重定向响应(如 302),requests 不会自动跟随重定向。

3.换行符绕过.不会匹配换行符

源代码:

image-20250329232048740

结果: image-20250329232146963

成功绕过

2. is_array()函数

is_array() 是 PHP 中的一个内置函数,用于检测给定的变量是否是一个数组(array)

(1) 基本语法

php
bool is_array(mixed $var)

参数

  • $var:要检查的变量(可以是任意类型)。

返回值

  • true:如果 $var 是数组。
  • false:如果 $var 不是数组。

(2) 使用示例

① 检查变量是否为数组

php
$arr = [1, 2, 3];
if (is_array($arr)) {
    echo "这是一个数组";
} else {
    echo "这不是数组";
}
// 输出:这是一个数组

② 检查非数组变量

php
$str = "Hello";
var_dump(is_array($str)); // 输出:bool(false)

$num = 123;
var_dump(is_array($num)); // 输出:bool(false)

③ 用于函数参数验证

php
function processData($data) {
    if (!is_array($data)) {
        throw new InvalidArgumentException("参数必须是数组");
    }
    // 处理数组...
}

(3)注意事项

  1. 空数组也会返回 true

    php
    $emptyArr = [];
    var_dump(is_array($emptyArr)); // 输出:bool(true)
  2. 不适用于其他可迭代对象(如 ArrayObject):

    php
    $obj = new ArrayObject([1, 2, 3]);
    var_dump(is_array($obj)); // 输出:bool(false)

    如果需要检查,可以用 $var instanceof Traversable

  3. gettype() 的区别

    php
    $arr = [1, 2, 3];
    echo gettype($arr); // 输出:"array"

    gettype() 返回变量类型的字符串表示,而 is_array() 直接返回布尔值。

(4)相关函数

函数说明
is_string()检查变量是否为字符串
is_int()检查变量是否为整数
is_object()检查变量是否为对象
is_null()检查变量是否为 null
empty()检查变量是否为空(包括空数组 []

(5)实际应用场景

① 表单数据处理

php
$userInput = $_POST['hobbies']; // 可能是一个数组(如多选框)
if (is_array($userInput)) {
    foreach ($userInput as $hobby) {
        // 处理每个选项
    }
}

② API 响应验证

php
$apiResponse = json_decode($jsonStr, true); // 返回关联数组
if (is_array($apiResponse)) {
    // 安全地遍历数据
}

③ 动态参数处理

php
function mergeConfigs($config) {
    if (!is_array($config)) {
        $config = [];
    }
    return array_merge($defaultConfig, $config);
}

(6)总结

  • is_array() 是 PHP 中用于类型安全的关键函数,尤其在处理动态数据时非常有用。
  • 仅对纯数组返回 true,不适用于 ArrayObject 等实现了数组接口的对象。
  • 在函数或方法中,使用 is_array() 可以避免因类型错误导致的运行时异常。

如果需要检查更复杂的结构(如“是否可遍历”),可以结合 is_iterable()(PHP 7.1+)或 instanceof 使用。

3. strpos()函数

strpos() 是 PHP 中用于 查找字符串首次出现位置 的内置函数,区分大小写。如果找到子字符串,返回它的 数字索引位置(从 0 开始);如果未找到,返回 false

(1)基本语法

php
mixed strpos(string $haystack, string $needle, int $offset = 0)

参数说明

参数描述
$haystack要搜索的 主字符串
$needle要查找的 子字符串
$offset可选,从主字符串的哪个位置开始搜索(默认 0)

返回值

  • int:子字符串首次出现的索引位置(从 0 开始)。
  • false:如果未找到子字符串。

(2)基础用法示例

① 检查子字符串是否存在

php
$text = "Hello, world!";
$position = strpos($text, "world");

if ($position !== false) {
    echo "找到 'world',位置在索引 $position"; // 输出:找到 'world',位置在索引 7
} else {
    echo "未找到";
}

注意:必须用 !== false 判断,因为 0 也可能是有效位置(如 strpos("abc", "a") 返回 0)。

② 使用 $offset 跳过部分内容

php
$text = "apple, banana, apple";
$position = strpos($text, "apple", 5); // 从索引 5 开始搜索
echo $position; // 输出:14(第二个 "apple" 的位置)

③ 区分大小写

php
$text = "PHP is Fun!";
$position = strpos($text, "fun"); // 返回 false(大小写不匹配)

(3)注意事项

① 严格判断返回值

php
// 错误写法(如果子字符串在开头,0 会被视为 false):
if (strpos($text, "abc")) { ... } 

// 正确写法:
if (strpos($text, "abc") !== false) { ... }

② 多字节字符(如中文)

strpos() 对多字节字符(如 UTF-8)可能不准确,建议用 mb_strpos()

php
$text = "你好,世界!";
$position = mb_strpos($text, "世界"); // 返回 3(每个中文字符算 1 个位置)

$needle 为空字符串

php
$result = strpos("abc", ""); // 返回 0(PHP 8.0+ 会抛出错误)

(4)实际应用场景

① 检查关键词是否存在

php
$content = "This is a sample text.";
if (strpos($content, "sample") !== false) {
    echo "内容包含关键词 'sample'";
}

② 提取子字符串后的内容

php
$url = "https://example.com/page";
if (strpos($url, "://") !== false) {
    $protocol = substr($url, 0, strpos($url, "://"));
    echo "协议:$protocol"; // 输出:https
}

③ 替换首次匹配项

php
$text = "cat, dog, cat";
$pos = strpos($text, "cat");
if ($pos !== false) {
    $text = substr_replace($text, "lion", $pos, strlen("cat"));
}
echo $text; // 输出:lion, dog, cat

(5)相关函数对比

函数描述区别
stripos()不区分大小写的查找大小写不敏感
strrpos()查找子字符串最后一次出现的位置反向搜索
mb_strpos()多字节安全的查找支持中文等字符
str_contains() (PHP 8+)检查是否包含子字符串直接返回 bool

(6)总结

  • strpos()区分大小写 的字符串搜索函数,返回 位置索引false
  • 关键点:始终用 !== false 判断结果
  • 多字节字符用 mb_strpos(),不区分大小写用 stripos()
  • PHP 8+ 推荐 str_contains() 简化存在性检查。

4. include()函数

image-20250323221800270

在 PHP 中,include 是一个用于将外部文件的内容插入到当前脚本中的语言构造。它通常用于代码复用和模块化开发,让你能够在多个 PHP 文件中共享代码,如函数、类、配置文件或 HTML 结构等。

(1)基本语法

php
include 'file.php';

工作原理

  • 当 PHP 执行 include 时,它会读取指定的文件,并将文件的内容插入到调用 include 的位置。
  • 如果文件无法找到或包含失败,PHP 会发出一个 警告(Warning),但脚本会继续执行。

示例

php
// header.php
echo "<h1>欢迎访问我的网站</h1>";

// main.php
include 'header.php';  // 包含 header.php 文件
echo "<p>这是页面的主要内容</p>";
  • 输出:
html
<h1>欢迎访问我的网站</h1>
<p>这是页面的主要内容</p>

(2)includerequire 的区别

  • include:如果文件无法找到,PHP 会发出警告(Warning),但脚本会继续执行。
  • require:如果文件无法找到,PHP 会发出致命错误(Fatal Error),并停止执行脚本。

(3)动态包含文件

include 也可以通过变量指定文件路径,这允许你根据条件动态选择要包含的文件。

php
$file = 'header.php';
include $file;  // 动态包含文件

(5)安全性注意

  • 使用 include 时,确保文件路径正确,以防止路径泄漏或恶意文件包含。
  • 为了避免目录遍历攻击,可以使用绝对路径或进行路径验证。

5. in_array()函数

in_array() 是 PHP 中一个常用的数组函数,用于检查某个值是否存在于数组中。

(1)基本语法

php
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

参数说明

  • $needle:要搜索的值
  • $haystack:要搜索的数组
  • $strict(可选):是否使用严格模式(类型检查),默认为 FALSE

返回值

  • 如果找到值则返回 true
  • 如果未找到则返回 false

(2)使用示例

基本用法

php
$fruits = ['apple', 'banana', 'orange'];

if (in_array('apple', $fruits)) {
    echo '找到了苹果!';
}

严格模式

php
$numbers = [1, 2, 3, '4'];

// 非严格模式 - 会匹配
var_dump(in_array('4', $numbers)); // 输出: bool(true)

// 严格模式 - 不会匹配
var_dump(in_array('4', $numbers, true)); // 输出: bool(false)

(3)实际应用

php
$allowed_extensions = ['jpg', 'png', 'gif'];
$file_extension = 'pdf';

if (!in_array($file_extension, $allowed_extensions)) {
    echo '不允许的文件类型!';
}

(4)注意事项

  1. 在大型数组上使用 in_array() 可能会影响性能,因为它是线性搜索
  2. 对于键值对数组,如果只需要检查键是否存在,使用 array_key_exists() 更高效
  3. 严格模式会同时比较值和类型
  4. 对于对象比较,严格模式下会比较是否为同一个实例

(5)替代方案

如果需要进行多次存在性检查,可以考虑先将数组转换为关联数组(使用值作为键),然后使用 isset()array_key_exists(),这样效率更高。

php
$values = ['a', 'b', 'c'];
$flipped = array_flip($values);

if (isset($flipped['a'])) {
    echo '存在';
}

6. call_user_func_array() 函数

call_user_func_array() 是 PHP 中一个非常有用的函数,它允许你动态调用一个回调函数,并将参数作为数组传递。

(1)基本语法

php
mixed call_user_func_array ( callable $callback , array $param_arr )

参数说明

  • $callback:要调用的回调函数,可以是:

    • 函数名的字符串
    • 包含方法名和对象的数组 [$object, 'methodName']
    • 包含类名和静态方法名的数组 ['ClassName', 'staticMethod']
    • 匿名函数(闭包)
  • $param_arr:要传递给回调函数的参数数组,数组中的元素会作为单独的参数按顺序传递

返回值

返回回调函数的执行结果。

(2)使用示例

调用普通函数

php
function sum($a, $b) {
    return $a + $b;
}

$result = call_user_func_array('sum', [5, 3]);
echo $result; // 输出 8

调用对象方法

php
class Calculator {
    public function multiply($x, $y) {
        return $x * $y;
    }
}

$calc = new Calculator();
$result = call_user_func_array([$calc, 'multiply'], [4, 5]);
echo $result; // 输出 20

调用静态方法

php
class MathUtils {
    public static function subtract($a, $b) {
        return $a - $b;
    }
}

$result = call_user_func_array(['MathUtils', 'subtract'], [10, 4]);
echo $result; // 输出 6

使用匿名函数

php
$callback = function($name, $greeting) {
    return "$greeting, $name!";
};

$message = call_user_func_array($callback, ['World', 'Hello']);
echo $message; // 输出 "Hello, World!"

(3)实际应用场景

  1. 动态调用函数:当函数名存储在变量中时

    php
    $functionName = 'doSomething';
    call_user_func_array($functionName, $args);
  2. 事件处理系统:将事件分发给不同的处理器

    php
    $eventHandlers = [
        'login' => [$userService, 'handleLogin'],
        'logout' => [$userService, 'handleLogout']
    ];
    
    $event = 'login';
    $data = ['user_id' => 123];
    
    if (isset($eventHandlers[$event])) {
        call_user_func_array($eventHandlers[$event], [$data]);
    }
  3. 中间件或管道模式:将请求通过一系列处理器传递

    php
    $middlewares = [$middleware1, $middleware2, $middleware3];
    $request = new Request();
    
    foreach ($middlewares as $middleware) {
        $request = call_user_func_array($middleware, [$request]);
    }

(4)注意事项

  1. 参数数组中的元素会按顺序传递给回调函数

  2. 如果参数数量不匹配,可能会导致错误

  3. 在 PHP 5.6+ 中,可以使用 ... 参数解包操作符作为替代:

    php
    $callback(...$param_arr);
  4. 性能考虑:call_user_func_array() 比直接调用函数稍慢

(5)与类似函数的比较

  • call_user_func():类似,但参数是单独传递而不是数组
  • forward_static_call_array():用于静态方法的调用,并保持后期静态绑定

call_user_func_array() 提供了极大的灵活性,特别适合需要动态决定调用哪个函数或方法的场景。

7. intval()函数

intval() 是 PHP 中用于获取变量整数值的函数,可以将各种类型的变量转换为整数。

(1)基本语法

php
int intval ( mixed $var [, int $base = 10 ] )

参数说明

  • $var:要转换为整数的变量
  • $base(可选):转换的进制基数(仅当 $var 是字符串时有效)
    • 取值范围:2-36
    • 默认值:10

返回值

返回 $var 的整数值,转换失败时返回 0。

(2)使用示例

基本转换

php
echo intval(42);          // 输出: 42
echo intval(4.2);         // 输出: 4
echo intval('42');        // 输出: 42
echo intval('+42');       // 输出: 42
echo intval('-42');       // 输出: -42
echo intval(042);         // 输出: 34 (八进制转十进制)
echo intval('042');       // 输出: 42
echo intval(1e10);        // 输出: 10000000000
echo intval('1e10');      // 输出: 1

布尔值转换

php
echo intval(true);        // 输出: 1
echo intval(false);       // 输出: 0

NULL 转换

php
echo intval(null);        // 输出: 0

数组转换

php
echo intval([]);          // 输出: 0
echo intval([1, 2, 3]);   // 输出: 1 (PHP 7+)

对象转换

php
class Test {}
$obj = new Test();
echo intval($obj);        // 输出: 1 (PHP 5+)

使用不同基数

php
echo intval('1a', 16);    // 输出: 26 (16进制)
echo intval('1011', 2);   // 输出: 11 (二进制)
echo intval('ff', 16);    // 输出: 255
echo intval('077', 8);    // 输出: 63

(3)实际应用场景

  1. 表单数据处理

    php
    $age = intval($_POST['age']);
  2. 数据库查询参数处理

    php
    $id = intval($_GET['id']);
    $sql = "SELECT * FROM users WHERE id = $id";
  3. 类型安全比较

    php
    if (intval($input) === 1) {
        // 严格比较
    }
  4. 进制转换

    php
    $hexValue = 'a1';
    $decimal = intval($hexValue, 16); // 161

(4)注意事项

  1. 浮点数转换:会直接截断小数部分(不是四舍五入)

    php
    echo intval(4.9); // 输出4
  2. 字符串转换规则

    • 从字符串开头解析数字,遇到非数字字符停止
    php
    echo intval('42abc'); // 输出42
    echo intval('abc42'); // 输出0
  3. 大数处理

    • 32位系统上最大值为2147483647
    • 64位系统上最大值为9223372036854775807
    • 超出会返回这些最大值
  4. PHP版本差异

    • PHP 5: 空数组返回0,非空数组返回1
    • PHP 7+: 所有数组都返回0
  5. 替代方案

    • 类型转换:(int)$var
    • 类型强制:(integer)$var
    • intval() 可以指定进制

(5)最佳实践

  1. 对于用户输入,建议先过滤再转换:

    php
    $id = intval(filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT));
  2. 对于需要四舍五入的情况,先用 round()floor()

    php
    $value = intval(round(4.6)); // 5
  3. 重要数据应考虑使用 filter_var() 进行更严格的验证:

    php
    $options = ['options' => ['min_range' => 1]];
    $id = filter_var($_GET['id'], FILTER_VALIDATE_INT, $options);

intval() 是处理整数转换的便捷工具,但在安全敏感场景中,建议结合其他过滤函数使用。

8. substr()函数

substr() 是 PHP 中用于截取字符串的函数,可以从字符串中返回指定的一部分。

(1)基本语法

php
string substr ( string $string , int $start [, int $length ] )

参数说明

  • $string:输入的字符串
  • $start:开始截取的位置
    • 正数:从字符串开头第 $start 个字符开始(0为第一个字符)
    • 负数:从字符串末尾倒数第 $start 个字符开始
  • $length(可选):截取的长度
    • 正数:返回最多 $length 个字符
    • 负数:从字符串末尾忽略 $length 个字符
    • 省略:截取到字符串末尾

返回值

返回字符串的截取部分,失败时返回 false(PHP 8.0+ 返回空字符串)。

(2)使用示例

基本用法

php
$str = "Hello, World!";

echo substr($str, 0, 5);    // 输出: Hello
echo substr($str, 7);       // 输出: World!
echo substr($str, -6);      // 输出: World!
echo substr($str, -6, 3);   // 输出: Wor
echo substr($str, 7, -1);   // 输出: World

负数位置示例

php
$str = "abcdef";

echo substr($str, -3, 1);   // 输出: d
echo substr($str, -3, -1);  // 输出: de
echo substr($str, 2, -1);   // 输出: cde

(3)实际应用场景

  1. 截取文件扩展名

    php
    $filename = "example.jpg";
    $ext = substr($filename, strrpos($filename, '.') + 1);
    echo $ext; // 输出: jpg
  2. 显示文章摘要

    php
    $content = "这是一段很长的文章内容...";
    $summary = substr($content, 0, 100) . '...';
  3. 处理手机号显示

    php
    $phone = "13800138000";
    $display = substr($phone, 0, 3) . '****' . substr($phone, -4);
    echo $display; // 输出: 138****8000
  4. 提取字符串中间部分

    php
    $str = "Name: John; Age: 30;";
    $age = substr($str, strpos($str, "Age: ") + 5, 2);
    echo $age; // 输出: 30

(4)注意事项

  1. 多字节字符问题

    • substr() 不识别多字节字符(如中文)
    • 对于UTF-8等编码,应使用 mb_substr()
    php
    $chinese = "你好世界";
    echo substr($chinese, 0, 2);      // 可能输出乱码
    echo mb_substr($chinese, 0, 2);   // 正确输出: 你好
  2. PHP版本差异

    • PHP 7.0-:substr('abc', 5) 返回 false
    • PHP 8.0+:substr('abc', 5) 返回空字符串 ""
  3. 边界情况

    php
    echo substr("abc", 1, 10); // 输出: bc(不会填充空字符)
    echo substr("abc", -10);   // 输出: abc(自动调整起始位置)
  4. 性能考虑

    • 对于大字符串操作,substr() 效率较高
    • 但频繁调用可能影响性能

(5)替代方案

  1. mb_substr():处理多字节字符

    php
    echo mb_substr("こんにちは", 0, 2); // 输出: こん
  2. 字符串访问语法(PHP 7.1+):

    php
    $str = "Hello";
    echo $str[1]; // 输出: e
    echo $str[-1]; // 输出: o
  3. 正则表达式:更复杂的提取需求

    php
    preg_match('/\d+/', 'abc123def', $matches);
    echo $matches[0]; // 输出: 123

(6)最佳实践

  1. 对于UTF-8等编码,始终使用 mb_substr()

  2. 检查返回值有效性:

    php
    $result = substr($str, $start, $length);
    if ($result !== false) {
        // 处理有效结果
    }
  3. 结合其他字符串函数使用:

    php
    // 安全截取并添加省略号
    function safeSubstr($str, $len) {
        if (mb_strlen($str) > $len) {
            return mb_substr($str, 0, $len) . '...';
        }
        return $str;
    }

9. 数据类型判断函数

PHP 提供了一系列用于判断变量数据类型的函数,这些函数在变量处理和类型检查中非常有用。

基本类型判断函数

(1) is_int() / is_integer() / is_long()

判断是否为整数

php
is_int(42);          // true
is_int("42");        // false
is_int(4.2);         // false

(2) is_float() / is_double()

判断是否为浮点数

php
is_float(3.14);      // true
is_float(42);        // false
is_float("3.14");    // false

(3) is_string()

判断是否为字符串

php
is_string("hello");   // true
is_string(123);       // false

(4) is_bool()

判断是否为布尔值

php
is_bool(true);        // true
is_bool(false);       // true
is_bool(0);           // false

(5) is_array()

判断是否为数组

php
is_array([]);         // true
is_array([1, 2, 3]);  // true
is_array("array");    // false

(6) is_object()

判断是否为对象

php
is_object(new stdClass()); // true
is_object("object");      // false

(7) is_null()

判断是否为null

php
is_null(null);        // true
is_null(0);           // false
is_null("null");      // false

(8) is_resource()

判断是否为资源类型

php
$file = fopen('test.txt', 'r');
is_resource($file);   // true
fclose($file);
is_resource($file);   // false

复合类型判断函数

(9) is_scalar()

判断是否为标量类型(int, float, string, bool)

php
is_scalar(123);       // true
is_scalar("abc");     // true
is_scalar([]);        // false

(10) is_callable()

判断是否可调用(函数名、方法、闭包)

php
is_callable('strlen');        // true
is_callable(function() {});   // true
is_callable([$obj, 'method']);// 如果方法存在则为true
is_callable('no_function');   // false

(11) is_iterable() (PHP 7.1+)

判断是否可迭代(数组或实现Traversable接口的对象)

php
is_iterable([]);              // true
is_iterable(new ArrayObject); // true
is_iterable("string");        // false

特殊类型判断函数

(12) is_numeric()

判断是否为数字或数字字符串

php
is_numeric(123);      // true
is_numeric("123");    // true
is_numeric("12.3");   // true
is_numeric("12a");    // false

(13) is_countable() (PHP 7.3+)

判断是否可计数(数组或实现Countable接口的对象)

php
is_countable([]);             // true
is_countable(new ArrayObject); // true
is_countable("string");       // false

(14) is_finite() / is_infinite() / is_nan()

浮点数特殊判断

php
is_finite(1.0);       // true
is_infinite(log(0));  // true
is_nan(acos(2));      // true

(15)最佳实践

  1. 类型安全比较

    php
    if (is_int($value) && $value === 42) {
        // 严格类型检查
    }
  2. 处理用户输入

    php
    $age = $_POST['age'];
    if (!is_numeric($age)) {
        die("年龄必须是数字");
    }
    $age = (int)$age;
  3. 方法参数验证

    php
    public function process(array $data) {
        if (!is_array($data)) {
            throw new InvalidArgumentException("参数必须是数组");
        }
        // ...
    }
  4. 使用类型提示替代(PHP 7.0+):

    php
    public function calculate(int $a, float $b): float {
        return $a * $b;
    }
  5. 多类型检查

    php
    if (is_string($value) || is_numeric($value)) {
        // 处理字符串或数字
    }

(16)注意事项

  1. 这些函数返回布尔值(true/false)
  2. 大多数函数有对应的"is not"形式,如!is_string()
  3. 对于对象,还可以使用instanceof检查特定类
  4. PHP 8.0+ 引入了更严格的类型系统,建议尽可能使用类型声明

10. count()函数

count() 是 PHP 中用于计算数组或对象中元素数量的核心函数。

(1)基本语法

php
int count ( mixed $array_or_countable [, int $mode = COUNT_NORMAL ] )

参数说明

  • $array_or_countable:要计数的数组或实现 Countable 接口的对象
  • $mode(可选):计数模式
    • COUNT_NORMAL(默认):常规计数,不递归统计多维数组
    • COUNT_RECURSIVE:递归统计多维数组的所有元素

返回值

返回元素的数量:

  • 对于非数组且非可计数对象,返回 1(PHP 7.2+ 返回 0)
  • 对于未初始化的变量,返回 0(PHP 7.2+)
  • 对于数组或可计数对象,返回元素个数

(2)使用示例

基本用法

php
$fruits = ['apple', 'banana', 'orange'];
echo count($fruits);  // 输出: 3

$empty = [];
echo count($empty);   // 输出: 0

多维数组计数

php
$food = [
    'fruits' => ['apple', 'banana', 'orange'],
    'veggies' => ['carrot', 'cucumber']
];

// 常规模式
echo count($food);        // 输出: 2(只统计第一维)

// 递归模式
echo count($food, COUNT_RECURSIVE);  // 输出: 6(2+3+1)

对象计数

php
class MyCountable implements Countable {
    public function count() {
        return 5;
    }
}

$obj = new MyCountable();
echo count($obj);  // 输出: 5

特殊值处理

php
echo count(null);      // 输出: 0(PHP 7.2+)
echo count(false);     // 输出: 1(PHP 7.2+ 返回 0)
echo count("string");  // 输出: 1(PHP 7.2+ 返回 0)

(3)实际应用场景

  1. 检查数组是否为空

    php
    if (count($array) > 0) {
        // 数组不为空
    }
  2. 遍历前检查

    php
    $users = getUsers();
    if (count($users)) {
        foreach ($users as $user) {
            // 处理用户
        }
    }
  3. 统计多维数组深度

    php
    function array_depth($array) {
        $max_depth = 1;
        foreach ($array as $value) {
            if (is_array($value)) {
                $depth = array_depth($value) + 1;
                $max_depth = max($max_depth, $depth);
            }
        }
        return $max_depth;
    }
  4. 批量处理数据时分批

    php
    $batchSize = 100;
    $totalItems = count($items);
    $batches = ceil($totalItems / $batchSize);

(4)注意事项

  1. PHP版本差异

    • PHP 7.2 之前:对非数组和非可计数对象返回 1
    • PHP 7.2+:对非数组和非可计数对象返回 0
  2. 性能考虑

    • count() 对数组的时间复杂度是 O(1)
    • 递归计数大数组可能影响性能
  3. 替代方案

    • 空数组检查:empty() 函数更快

      php
      if (!empty($array)) { ... }
    • 数组大小:sizeof()count() 的别名

  4. 最佳实践

    • 优先使用类型提示(PHP 7.0+)

      php
      function processItems(array $items) {
          // 确保参数是数组
      }
    • 对可能为null的值先检查

      php
      $count = $items ? count($items) : 0;
  5. 与empty()的区别

    php
    $a = [];
    count($a);  // 0
    empty($a);  // true
    
    $b = [0];
    count($b);  // 1
    empty($b);  // false

11. preg_replace()函数

preg_replace() 是 PHP 中一个强大的正则表达式替换函数,用于执行基于正则表达式的搜索和替换操作。

(1)基本语法

php
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

参数说明

  1. $pattern - 要搜索的模式,可以是字符串或字符串数组

    • 可以是一个正则表达式字符串,如 '/pattern/'
    • 也可以是一个包含多个正则表达式的数组
  2. $replacement - 用于替换的字符串或字符串数组

    • 可以包含反向引用(如 \\1$1
    • 如果是数组,则与 $pattern 数组一一对应
  3. $subject - 要进行搜索和替换的字符串或字符串数组

  4. $limit (可选) - 每个模式在每个 $subject 上进行替换的最大次数,默认为 -1(无限制)

  5. &$count (可选) - 如果指定,将被填充为完成的替换次数

返回值

  • 如果 $subject 是数组,则返回数组
  • 其他情况返回字符串
  • 发生错误时返回 NULL

(2)使用示例

基本替换

php
$string = "April 15, 2023";
$pattern = '/(\w+) (\d+), (\d+)/i';
$replacement = '$2 $1 $3';
echo preg_replace($pattern, $replacement, $string);
// 输出: 15 April 2023

使用数组模式

php
$patterns = array('/quick/', '/brown/', '/fox/');
$replacements = array('slow', 'black', 'bear');
$string = 'The quick brown fox jumps over the lazy dog.';
echo preg_replace($patterns, $replacements, $string);
// 输出: The slow black bear jumps over the lazy dog.

移除空白字符

php
$text = "This   is    some text \t with \n whitespace.";
$cleaned = preg_replace('/\s+/', ' ', $text);
echo $cleaned;
// 输出: This is some text with whitespace.

限制替换次数

php
$string = "a a a a a";
echo preg_replace('/a/', 'b', $string, 3);
// 输出: b b b a a

(3)注意事项

  1. 正则表达式模式通常需要分隔符,如 /pattern/
  2. 使用 preg_quote() 可以转义正则表达式中的特殊字符
  3. 对于简单字符串替换,str_replace() 效率更高
  4. 反向引用可以使用 \\n$n 形式
  5. 替换字符串中的 $ 需要转义为 \$

(4)性能考虑

  • 复杂的正则表达式可能影响性能
  • 对于大量数据处理,考虑使用 preg_replace_callback()

preg_replace() 是处理复杂文本替换的强大工具,特别适合需要模式匹配的场景。

12. preg_match_all()函数

preg_match_all 是 PHP 中一个强大的正则表达式匹配函数,用于执行全局正则表达式匹配。

(1)基本语法

php
int preg_match_all(
    string $pattern,
    string $subject,
    array &$matches = null,
    int $flags = 0,
    int $offset = 0
)

参数说明

  1. $pattern - 要搜索的正则表达式模式
  2. $subject - 要搜索的输入字符串
  3. $matches - 用于存储匹配结果的数组(引用传递)
  4. $flags - 控制匹配结果的排列方式(可选)
    • PREG_PATTERN_ORDER(默认) - 按模式分组
    • PREG_SET_ORDER - 按匹配分组
    • PREG_OFFSET_CAPTURE - 同时返回匹配位置的偏移量
  5. $offset - 从目标字符串的哪个位置开始搜索(可选)

返回值

  • 返回完整匹配的次数(可能是0)
  • 如果发生错误,返回false

(2)使用示例

示例1:基本用法

php
$subject = "a1b2c3d4";
preg_match_all('/\d/', $subject, $matches);
print_r($matches);

输出:

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => 2
            [2] => 3
            [3] => 4
        )
)

示例2:使用命名捕获组

php
$html = '<p>段落1</p><p>段落2</p>';
preg_match_all('/<p>(?<content>.*?)<\/p>/', $html, $matches);
print_r($matches['content']);

输出:

Array
(
    [0] => 段落1
    [1] => 段落2
)

示例3:使用 PREG_SET_ORDER 标志

php
$subject = "name: John, age: 30; name: Jane, age: 25";
preg_match_all('/name: (\w+), age: (\d+)/', $subject, $matches, PREG_SET_ORDER);
print_r($matches);

输出:

Array
(
    [0] => Array
        (
            [0] => name: John, age: 30
            [1] => John
            [2] => 30
        )
    [1] => Array
        (
            [0] => name: Jane, age: 25
            [1] => Jane
            [2] => 25
        )
)

(3)注意事项

  1. preg_match 更强大,可以获取所有匹配而不仅是第一个
  2. 对于大型字符串,可能消耗较多内存
  3. 正则表达式应使用分隔符(如 /pattern/
  4. 如果只需要检查是否匹配而不需要所有结果,preg_match 效率更高

preg_match_all 是处理复杂文本匹配和提取的强大工具,特别适合需要从字符串中提取多个匹配项的场景。

13. exec()函数

exec 是一个用于执行外部程序的函数,在多种编程环境中存在,包括 PHPNode.js,但行为有所不同。下面分别介绍:

(1)PHP 中的 exec()

基本语法

php
string exec(string $command [, array &$output [, int &$return_var]])

参数说明

  • $command: 要执行的 shell 命令
  • &$output (可选): 数组,用于存储命令的每一行输出
  • &$return_var (可选): 存储命令的返回值(通常 0 表示成功)

返回值

  • 返回命令执行的 最后一行输出(如果要获取全部输出,需使用 $output 参数)

示例

php
// 执行简单的 shell 命令
exec('ls -l', $output, $return_var);

// 打印所有输出行
print_r($output);

// 检查是否执行成功
if ($return_var === 0) {
    echo "命令执行成功";
} else {
    echo "命令执行失败";
}

安全注意事项

  • 避免直接将用户输入拼接到命令中(有 命令注入 风险)
  • 建议使用 escapeshellarg()escapeshellcmd() 对参数进行转义

(2) Node.js 中的 exec()

在 Node.js 中,execchild_process 模块的一个函数,用于执行 shell 命令。

基本语法

javascript
const { exec } = require('child_process');

exec(command [, options], callback);

参数说明

  • command: 要执行的 shell 命令
  • options (可选): 配置对象(如 cwd, env, timeout 等)
  • callback: 执行完成后的回调函数,格式: (error, stdout, stderr) => { ... }

返回值

  • 返回一个 ChildProcess 实例(可用于控制进程)

示例

javascript
const { exec } = require('child_process');

exec('ls -l', (error, stdout, stderr) => {
    if (error) {
        console.error(`执行错误: ${error}`);
        return;
    }
    console.log(`输出:\n${stdout}`);
    if (stderr) {
        console.error(`错误输出:\n${stderr}`);
    }
});

安全注意事项

  • 同样存在 命令注入 风险,避免直接拼接用户输入
  • 推荐使用 execFilespawn 替代,安全性更高

主要区别

特性PHP exec()Node.js exec()
返回值最后一行输出通过回调返回全部输出 (stdout)
输出获取需传递 &$output 数组直接在回调中接收 stdout
异步/同步同步执行异步执行
错误处理通过 $return_var 判断通过回调的 error 参数处理

常见用途

  1. 调用系统命令(如压缩文件、处理图片)
  2. 执行脚本(Python、Bash 等)
  3. 与外部程序交互(如 Git、FFmpeg)
  4. 自动化任务(备份、部署)

替代方案

  • PHP: 考虑 shell_exec()passthru()proc_open()
  • Node.js: 更安全的 execFile()spawn()

14. str_replace()函数

str_replacePHP 中一个常用的字符串替换函数,用于在字符串中查找并替换指定的内容。它支持 简单替换数组批量替换多对多替换,功能强大且灵活。

(1)基本语法

php
str_replace($search, $replace, $subject, [&$count])
  • $search:要查找的字符串或字符串数组(旧值)。
  • $replace:替换成的字符串或字符串数组(新值)。
  • $subject:被搜索的原始字符串或字符串数组。
  • $count(可选):如果提供,函数会返回替换发生的次数(引用传递)。

返回值:替换后的字符串或数组。

(2) 使用示例

简单替换(单个字符串)

php
$text = "Hello World!";
$newText = str_replace("World", "PHP", $text);
echo $newText; // 输出:Hello PHP!

数组批量替换(多对一)

php
$text = "apple, banana, cherry";
$fruits = ["apple", "banana", "cherry"];
$newText = str_replace($fruits, "fruit", $text);
echo $newText; // 输出:fruit, fruit, fruit

多对多替换(数组对数组)

php
$text = "I like cats and dogs.";
$search = ["cats", "dogs"];
$replace = ["dogs", "birds"];
$newText = str_replace($search, $replace, $text);
echo $newText; // 输出:I like dogs and birds.

统计替换次数

php
$text = "foo bar foo baz foo";
$count = 0;
$newText = str_replace("foo", "FOO", $text, $count);
echo $newText; // 输出:FOO bar FOO baz FOO
echo "替换次数:" . $count; // 输出:3

(3)特点与注意事项

  1. 区分大小写

    • str_replace大小写敏感 的,如果需要不区分大小写,使用 str_ireplace

    • php
      $text = "Hello World!";
      $newText = str_ireplace("world", "PHP", $text); // 匹配 "World"(不区分大小写)
      echo $newText; // 输出:Hello PHP!
  2. 数组替换规则

    • 如果 $search$replace 都是数组,则 按索引一一对应替换
    • 如果 $replace$search 短,多出的 $search 会用空字符串 "" 替换。
  3. 递归替换

    • str_replace 不会递归替换(即不会对替换后的内容再次替换)。

    • php
      $text = "a b c";
      $newText = str_replace(["a", "b"], ["b", "c"], $text);
      echo $newText; // 输出:b c c(不会变成 c c c)
  4. 性能优化

    • 如果替换大量数据,strtr() 可能比 str_replace 更快:

      php
      $trans = ["hello" => "hi", "world" => "earth"];
      $text = "hello world";
      echo strtr($text, $trans); // 输出:hi earth

(4)常见应用场景

  1. 模板变量替换(如邮件模板):

    php
    $template = "Hello {name}, your code is {code}";
    $data = ["{name}" => "Alice", "{code}" => "1234"];
    $message = str_replace(array_keys($data), array_values($data), $template);
    echo $message; // 输出:Hello Alice, your code is 1234
  2. 敏感词过滤

    php
    $text = "This is a bad word and another bad word.";
    $badWords = ["bad", "another"];
    $filtered = str_replace($badWords, "***", $text);
    echo $filtered; // 输出:This is a *** word and *** *** word.
  3. URL 或文件名处理

    php
    $filename = "image name with spaces.jpg";
    $safeName = str_replace(" ", "-", $filename);
    echo $safeName; // 输出:image-name-with-spaces.jpg

(4)与 preg_replace 的区别

函数匹配方式适用场景
str_replace固定字符串简单替换,性能更高
preg_replace正则表达式复杂模式匹配(如 /a.*b/
php
// 使用 preg_replace 替换所有数字
$text = "abc123def456";
$newText = preg_replace("/\d+/", "X", $text);
echo $newText; // 输出:abcXdefX

(5)总结

  • str_replace 是 PHP 中最常用的字符串替换函数,适合 固定字符串替换
  • 支持 单次替换、批量替换、多对多替换,但不支持正则。
  • 如果需要 不区分大小写正则匹配,改用 str_ireplacepreg_replace

适用于模板渲染、敏感词过滤、数据清洗等场景!

15. trim()函数

trim() 是 PHP 中一个常用的字符串处理函数,用于去除字符串首尾的空白字符(或其他指定字符)。

(1)基本语法

php
trim(string $str, string $character_mask = " \t\n\r\0\x0B") : string

参数说明

  • $str: 要处理的输入字符串
  • $character_mask (可选): 指定要删除的字符列表,默认删除以下空白字符:
    • 普通空格 ()
    • 制表符 (\t)
    • 换行符 (\n)
    • 回车符 (\r)
    • 空字节 (\0)
    • 垂直制表符 (\x0B)

返回值

返回去除首尾指定字符后的字符串

(2)使用示例

基本用法(去除空格)

php
$text = "   Hello World!   ";
echo trim($text);  // 输出: "Hello World!"

去除特定字符

php
$text = "Hello World!";
echo trim($text, "Hed!");  // 输出: "llo Worl"
// 去除了首尾的 H、e、d、! 字符

处理用户输入

php
$username = trim($_POST['username']);  // 去除用户输入首尾空格

与文件上传结合使用

php
$file_name = trim($_FILES['upload_file']['name']);

这段代码的作用是去除上传文件名首尾的空白字符,防止因意外空格导致的问题。

(3)相关函数

  • ltrim() - 只去除字符串开头的空白字符(或其他字符)
  • rtrim() (或 chop()) - 只去除字符串末尾的空白字符(或其他字符)

(4)注意事项

  1. trim() 不会修改字符串中间的空白字符
  2. 对于多字节字符(如中文),可能需要使用 mb_trim() 替代
  3. 在处理文件路径时,过度使用 trim() 可能导致路径问题

16. str_ireplace()函数

str_ireplace() 是 PHP 中用于字符串替换的函数,不区分大小写地执行查找替换操作。

(1)基本语法

php
str_ireplace(
    mixed $search,
    mixed $replace,
    mixed $subject,
    int &$count = null
): mixed

参数说明

参数说明
$search要查找的值(可以是字符串或数组)
$replace替换的值(可以是字符串或数组)
$subject被搜索的字符串或数组
&$count可选参数,如果指定,将被设置为替换发生的次数

返回值

返回替换后的字符串或数组。

(2)使用示例

基本字符串替换(不区分大小写)

php
$text = "Hello World! PHP is great!";
$result = str_ireplace("php", "Python", $text);
echo $result; // 输出: "Hello World! Python is great!"

数组替换

php
$text = "The quick brown fox jumps over the lazy dog";
$search = ["quick", "brown", "fox"];
$replace = ["slow", "black", "bear"];
$result = str_ireplace($search, $replace, $text);
echo $result; // 输出: "The slow black bear jumps over the lazy dog"

统计替换次数

php
$text = "Cat, cat, CAT, dog";
$count = 0;
$result = str_ireplace("cat", "tiger", $text, $count);
echo $result; // 输出: "tiger, tiger, tiger, dog"
echo $count;  // 输出: 3

(3)在文件上传安全过滤中的应用(有缺陷)

php
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
$file_name = str_ireplace($deny_ext, "", $file_name);

这段代码试图移除文件名中的危险扩展名,但存在安全问题(下文会分析)。

(4)与 str_replace() 的区别

特性str_ireplace()str_replace()
大小写敏感不敏感敏感
性能稍慢(因为不区分大小写)更快

(5)安全问题分析

在文件上传过滤中使用 str_ireplace() 存在严重安全隐患:

替换逻辑缺陷

php
$filename = "test.pHp";
echo str_ireplace(["php"], "", $filename); 
// 输出: "test." (只移除了"php"部分)

双扩展名绕过

php
$filename = "test.php.jpg";
echo str_ireplace(["php"], "", $filename);
// 输出: "test..jpg" (仍可能被解析为PHP文件)

部分匹配问题(双写绕过)

php
$filename = "test.pphphp";
echo str_ireplace(["php"], "", $filename);
// 输出: "test.pphp" → 处理后变为 "test.php"

(6)安全建议

  1. 改用白名单机制:只允许已知安全的扩展名
  2. 使用路径信息函数pathinfo() 获取真实扩展名
  3. 完全重命名文件:不使用用户提供的文件名
  4. 验证文件内容:检查实际文件类型而非扩展名

(7)安全示例代码

php
// 安全的白名单方式
$allowed_ext = ['jpg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));

if (!in_array($ext, $allowed_ext)) {
    die("不允许的文件类型");
}

// 完全重命名文件
$new_filename = uniqid() . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], '/uploads/' . $new_filename);

17. move_uploaded_file() 函数

move_uploaded_file() 是 PHP 中专门用于处理 HTTP 上传文件移动的函数,它是文件上传过程中最关键的安全函数之一。

(1)基本语法

php
move_uploaded_file(string $from, string $to): bool

参数说明

参数说明
$from文件的路径(即 $_FILES['file']['tmp_name']
$to目标路径(包括新文件名)

返回值

  • 成功返回 true
  • 失败返回 false

(2)核心特点

  1. 安全验证:会额外检查文件是否是通过 HTTP POST 上传的合法文件
  2. 原子性操作:移动操作是原子的,要么完全成功,要么完全失败
  3. 自动清理:移动成功后,临时文件会被自动删除

(3)使用示例

基本用法

php
if (move_uploaded_file(
    $_FILES['userfile']['tmp_name'], 
    '/uploads/' . basename($_FILES['userfile']['name'])
)) {
    echo "文件上传成功";
} else {
    echo "文件上传失败";
}

安全用法(推荐)

php
// 定义上传目录
$upload_dir = '/var/www/uploads/';

// 生成唯一文件名
$filename = uniqid('img_') . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$destination = $upload_dir . $filename;

// 验证并移动文件
if (move_uploaded_file($_FILES['file']['tmp_name'], $destination)) {
    echo "文件已安全上传";
} else {
    echo "文件上传失败";
}

(4)安全注意事项

  1. 路径安全

    • 必须检查目标路径是否在允许的目录内
    • 防止目录遍历攻击(如 ../../../etc/passwd
    php
    $upload_dir = '/var/www/uploads/';
    $filename = basename($_FILES['file']['name']); // 移除路径信息
    $destination = realpath($upload_dir) . DIRECTORY_SEPARATOR . $filename;
    
    // 验证目标路径是否在允许的目录内
    if (strpos(realpath($destination), realpath($upload_dir)) !== 0) {
        die("非法路径");
    }
  2. 文件覆盖

    • 如果目标文件已存在,会被静默覆盖
    • 建议使用唯一文件名防止覆盖
  3. 权限设置

    • 确保 PHP 进程对目标目录有写权限
    • 上传目录不应有执行权限(防止脚本执行)
    bash
    chmod 755 /var/www/uploads  # 目录可读可写不可执行
  4. 配合其他验证

    • 应先验证文件类型、大小等再移动文件
    • 不要仅依赖 move_uploaded_file() 做安全检查

(5)常见错误处理

  1. 检查错误代码

    php
    if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
        die("上传错误: " . $_FILES['file']['error']);
    }
  2. 验证临时文件

    php
    if (!is_uploaded_file($_FILES['file']['tmp_name'])) {
        die("非法文件来源");
    }
  3. 检查移动结果

    php
    if (!move_uploaded_file(/*...*/)) {
        error_log("文件移动失败: " . print_r(error_get_last(), true));
        die("文件保存失败");
    }

(6)完整安全示例

php
// 配置
$upload_dir = '/var/www/uploads/';
$allowed_types = ['image/jpeg', 'image/png'];
$max_size = 2 * 1024 * 1024; // 2MB

// 验证上传错误
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
    die("上传错误: " . $_FILES['file']['error']);
}

// 验证文件大小
if ($_FILES['file']['size'] > $max_size) {
    die("文件大小超过限制");
}

// 验证文件类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);

if (!in_array($mime, $allowed_types)) {
    die("不允许的文件类型");
}

// 生成安全文件名
$ext = [
    'image/jpeg' => 'jpg',
    'image/png' => 'png'
][$mime] ?? 'bin';

$filename = uniqid('img_') . '.' . $ext;
$destination = $upload_dir . $filename;

// 验证目标路径
if (strpos(realpath($upload_dir) . '/', realpath($upload_dir)) !== 0) {
    die("非法路径");
}

// 移动文件
if (!move_uploaded_file($_FILES['file']['tmp_name'], $destination)) {
    die("文件保存失败");
}

echo "文件上传成功: " . htmlspecialchars($filename, ENT_QUOTES);

18.basename()函数

basename() 是 PHP 中用于获取路径中的文件名部分的函数,它会返回路径中的最后一个名称部分(即文件名或最后一级目录名)。

(1)基本语法

php
basename(string $path, string $suffix = ""): string

参数说明

参数说明
$path文件路径(可以是相对路径或绝对路径)
$suffix可选参数,如果文件名以这个后缀结尾,则去除该后缀

返回值

返回路径中的文件名部分。

(2)使用示例

基本用法

php
echo basename("/var/www/index.html");  // 输出: "index.html"
echo basename("images/avatar.jpg");    // 输出: "avatar.jpg"
echo basename("../files/data.txt");    // 输出: "data.txt"

去除后缀

php
echo basename("/var/www/index.html", ".html");  // 输出: "index"
echo basename("report.pdf", ".pdf");           // 输出: "report"

处理不同操作系统路径

php
// 在Windows系统上
echo basename("C:\xampp\htdocs\index.php");  // 输出: "index.php"

// 在Unix/Linux系统上
echo basename("/home/user/file.txt");        // 输出: "file.txt"

(3)安全应用

在文件上传中的安全使用

php
// 不安全的方式(可能包含路径遍历)
$unsafe_name = $_FILES['file']['name']; // 可能包含 "../../etc/passwd"

// 安全的方式
$safe_name = basename($_FILES['file']['name']);

配合路径安全检查

php
$upload_dir = '/var/www/uploads/';
$filename = basename($_FILES['file']['name']);

// 生成完整路径
$destination = $upload_dir . $filename;

// 验证路径是否在允许的目录内
if (strpos(realpath($destination), realpath($upload_dir)) !== 0) {
    die("非法路径尝试");
}

(4)注意事项

  1. 不是万能的basename() 只去除路径信息,不验证文件名本身是否安全
  2. 多字节字符:对于包含非ASCII字符的文件名,可能需要使用 mb_basename() 替代
  3. 符号链接:不会解析符号链接,直接返回链接名
  4. 性能:是一个轻量级函数,性能开销很小

(5)常见问题

问题:为什么 basename() 处理后仍然不安全?

回答:basename() 只解决了路径遍历问题,但:

  • 不检查文件名是否包含特殊字符
  • 不验证扩展名是否合法
  • 不防止文件名冲突

(6)安全文件上传的完整示例

php
// 配置
$upload_dir = '/var/www/uploads/';
$allowed_ext = ['jpg', 'png', 'pdf'];
$max_size = 5 * 1024 * 1024; // 5MB

// 获取安全文件名
$original_name = basename($_FILES['file']['name']);
$ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));

// 验证
if (!in_array($ext, $allowed_ext)) {
    die("不允许的文件类型");
}

if ($_FILES['file']['size'] > $max_size) {
    die("文件大小超过限制");
}

// 生成唯一文件名
$new_filename = uniqid('file_') . '.' . $ext;
$destination = $upload_dir . $new_filename;

// 移动文件
if (move_uploaded_file($_FILES['file']['tmp_name'], $destination)) {
    echo "文件上传成功: " . htmlspecialchars($new_filename);
} else {
    echo "文件上传失败";
}

(7)相关函数

  • dirname() - 获取路径中的目录部分
  • pathinfo() - 获取文件路径的各个组成部分
  • realpath() - 返回规范化的绝对路径名

18. parse_str() 函数

parse_str() 是 PHP 中用于将查询字符串(query string)解析为变量的函数,它通常用于处理 URL 中的查询参数。

(1)基本语法

php
parse_str(string $string, array &$result): void

参数说明

参数说明
$string要解析的输入字符串(通常是 key1=value1&key2=value2 格式)
&$result可选参数,如果提供,结果将存储到这个数组中而不创建变量

(2)使用方式

直接创建变量(不推荐)

php
parse_str("name=John&age=30");
echo $name; // 输出: John
echo $age;  // 输出: 30

⚠️ 注意:这种方式会直接在当前作用域创建变量,存在安全风险,不推荐使用。

存储到数组(推荐方式)

php
$data = [];
parse_str("name=John&age=30", $data);
print_r($data);
/*
输出:
Array
(
    [name] => John
    [age] => 30
)
*/

(3)主要特点

  1. URL解码:会自动对 URL 编码的值进行解码

    php
    parse_str("city=New%20York", $data);
    print_r($data); // 输出: ['city' => 'New York']
  2. 处理数组:可以解析出数组结构

    php
    parse_str("colors[]=red&colors[]=blue", $data);
    print_r($data);
    /*
    输出:
    Array
    (
        [colors] => Array
            (
                [0] => red
                [1] => blue
            )
    )
    */
  3. 处理多维数组

    php
    parse_str("user[name]=John&user[age]=30", $data);
    print_r($data);
    /*
    输出:
    Array
    (
        [user] => Array
            (
                [name] => John
                [age] => 30
            )
    )
    */

(4)安全注意事项

  1. 变量注入风险

    php
    // 危险!会创建 $id 和 $admin 变量
    parse_str("id=1&admin=1");

    推荐始终使用数组接收结果:

    php
    $data = [];
    parse_str("id=1&admin=1", $data);
  2. URL编码问题

    • 确保输入字符串是正确编码的
    • 可以使用 urldecode() 预处理字符串
  3. 输入验证

    • 解析后应对结果数据进行验证和过滤
    • 特别是当处理用户提供的查询字符串时

(5)实际应用示例

处理当前URL的查询字符串

php
// 获取当前URL的查询部分(不含问号)
$queryString = $_SERVER['QUERY_STRING'] ?? '';

// 安全解析
$params = [];
parse_str($queryString, $params);

// 使用参数(先进行验证)
$page = filter_var($params['page'] ?? 1, FILTER_VALIDATE_INT, [
    'options' => ['min_range' => 1, 'default' => 1]
]);

echo "当前页码: " . $page;

解析Cookie值

php
$cookie = "username=JohnDoe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
parse_str(str_replace('; ', '&', $cookie), $cookieData);

print_r($cookieData);
/*
输出:
Array
(
    [username] => JohnDoe
    [expires] => Thu
    [path] => /
)
*/

(6)相关函数

  • http_build_query() - 将数组转换为URL查询字符串(与parse_str相反)
  • urldecode() - 解码URL编码的字符串
  • filter_input() - 更安全地获取外部变量

filter_var,$_SERVER

19.create_function()函数

create_function 是 PHP 中的一个函数,用于在运行时动态创建匿名函数(lambda 函数)。不过需要注意的是,这个函数在 PHP 7.2.0 中已被弃用,并在 PHP 8.0.0 中被移除,现在应该使用更现代的匿名函数语法(function(...) {...})。

(1)基本语法

php
create_function(string $args, string $code): string
  • $args: 函数参数的字符串表示,如 '$a, $b'
  • $code: 函数体的字符串表示,如 'return $a + $b;'
  • 返回值: 返回一个唯一的函数名称字符串

(2)基本示例

php
$add = create_function('$a, $b', 'return $a + $b;');
echo $add(2, 3); // 输出 5

这相当于现代语法:

php
$add = function($a, $b) { return $a + $b; };
echo $add(2, 3); // 输出 5

(3)特点与注意事项

  1. 安全性问题

    • 由于函数体是字符串,容易导致代码注入漏洞
    • 如果$code参数来自用户输入,可能被恶意利用
  2. 性能

    • 每次调用都会创建一个新函数,影响性能
    • 现代匿名函数语法性能更好
  3. 调试困难

    • 生成的函数名是自动生成的(如lambda_1
    • 错误信息难以追踪

(4)现代替代方案

使用匿名函数语法(PHP 5.3+):

php
// 替代create_function
$add = function($a, $b) { return $a + $b; };

// 作为回调函数
usort($array, function($a, $b) { return $a - $b; });

(5)为什么被弃用?

  1. 安全问题:字符串形式的代码容易导致注入
  2. 性能问题:不如现代匿名函数高效
  3. 维护性:现代语法更清晰易读

(6)总结

虽然create_function在旧代码中可能还会见到,但在新项目中应该使用现代的匿名函数语法。它不仅更安全、性能更好,而且代码更清晰易维护。

如果你正在维护旧代码,考虑逐步替换所有的create_function调用为匿名函数语法。

20.eval()函数

eval()是PHP中的一个语言构造器(language construct),用于执行字符串中的PHP代码。

(1)基本语法

php
mixed eval(string $code)
  • 参数:$code - 需要执行的PHP代码字符串
  • 返回值:返回执行代码的返回值,如果没有return语句则返回NULL

(2)基本用法

php
$string = 'echo "Hello, World!";';
eval($string);  // 输出: Hello, World!

$x = 10;
$result = eval('return $x * 2;');
echo $result;  // 输出: 20

(3)特点

  1. 代码执行:eval执行的代码会继承当前作用域中的变量
  2. 返回值:可以使用return语句返回一个值
  3. 错误处理:如果代码中有语法错误,eval会引发解析错误
php
$name = "Alice";
eval('echo "Hello, $name";');  // 输出: Hello, Alice

(4)安全性问题

PHP的eval同样存在严重的安全风险:

php
// 危险示例 - 永远不要这样做!
$user_code = $_GET['code'];
eval($user_code);  // 用户可以输入恶意代码

(5)安全使用建议

  1. 避免使用:尽可能寻找替代方案
  2. 输入过滤:如果必须使用,严格过滤所有输入
  3. 沙箱环境:考虑使用沙箱环境限制执行权限

(6)替代方案

对于动态代码执行,可以考虑:

  • 使用回调函数:call_user_func()
  • 使用变量函数:$func = 'functionName'; $func();
  • 设计模式:如策略模式、工厂模式等

(7)示例:安全使用eval

php
// 相对安全的用法 - 限制可执行的内容
$allowed_operations = [
    'add' => '$a + $b',
    'subtract' => '$a - $b'
];

$operation = 'add'; // 从安全来源获取
$a = 10;
$b = 5;

if (array_key_exists($operation, $allowed_operations)) {
    $result = eval('return ' . $allowed_operations[$operation] . ';');
    echo $result;  // 输出: 15
}

21.is_dir() 函数详解

is_dir() 是 PHP 中用于检查指定路径是否为目录的函数,它属于文件系统函数组。

(1)函数语法

php
bool is_dir ( string $filename )

参数说明

  • $filename:要检查的路径。如果是相对路径,则基于当前工作目录进行检查。

返回值

  • 如果指定的文件名存在并且是一个目录,则返回 true
  • 否则返回 false

(2)基本用法

php
$path = "/path/to/directory";

if (is_dir($path)) {
    echo "$path 是一个目录";
} else {
    echo "$path 不是一个目录或不存在";
}

(3)实际应用示例

示例1:检查目录是否存在

php
$folder = "uploads";

if (is_dir($folder)) {
    echo "目录 $folder 存在";
} else {
    echo "目录 $folder 不存在";
    // 可以在这里创建目录
    mkdir($folder);
}

示例2:遍历目录前检查

php
function listDirectory($dir) {
    if (!is_dir($dir)) {
        return "错误:$dir 不是有效的目录";
    }
    
    $files = scandir($dir);
    foreach ($files as $file) {
        if ($file != "." && $file != "..") {
# . 表示当前目录本身 .. 表示上级目录(父目录),当使用 scandir()、readdir() 等函数读取目录内容时,返回的数组中总会包含这两个特殊条目,在大多数实际应用中,我们只关心目录中的实际文件和子目录,不需要处理这两个特殊目录,所以排除它们
            echo $file . "<br>";
        }
    }
}

listDirectory("images");

示例3:与文件检查结合使用

php
$path = "data/config.ini";

if (is_dir($path)) {
    echo "这是一个目录";
} elseif (file_exists($path)) {
    echo "这是一个文件";
} else {
    echo "路径不存在";
}

(4)注意事项

  1. 权限问题:如果 PHP 没有权限访问该目录,is_dir() 也会返回 false

  2. 符号链接:对于符号链接,is_dir() 会检查链接指向的目标是否是目录。

  3. 性能考虑:频繁调用文件系统函数会影响性能,必要时可以考虑缓存结果。

  4. 相对路径:使用相对路径时要注意当前工作目录可能与预期不同。

  5. 错误抑制:如果不确定路径是否存在,可以使用 @ 抑制可能的警告:

    php
    if (@is_dir($unknown_path)) {
        // ...
    }
  6. 区分文件和目录:要明确区分文件和目录检查,可以结合使用 is_file()is_dir()

(5)相关函数

  • is_file() - 检查是否是文件
  • file_exists() - 检查文件或目录是否存在
  • mkdir() - 创建目录
  • rmdir() - 删除目录
  • scandir() - 列出目录中的文件和目录

is_dir() 是目录操作中最基础的检查函数,在进行任何目录操作前使用它可以避免很多错误。

22. mkdir() 函数详解

mkdir() 是 PHP 中用于创建新目录的函数,属于文件系统函数组。

(1)函数语法

php
bool mkdir ( string $pathname [, int $mode = 0777 [, bool $recursive = false [, resource $context ]]] )

参数说明

参数说明
$pathname要创建的目录路径
$mode (可选)目录权限,默认 0777(最大权限)
$recursive (可选)是否递归创建所有必需的父目录,默认 false
$context (可选)上下文资源,用于流操作

返回值

  • 成功创建目录时返回 true
  • 失败时返回 false

(2)基本用法

简单创建目录

php
$dir = "new_folder";

if (!file_exists($dir)) {
    if (mkdir($dir)) {
        echo "目录 $dir 创建成功";
    } else {
        echo "目录 $dir 创建失败";
    }
} else {
    echo "目录 $dir 已存在";
}

设置目录权限

php
mkdir("protected_folder", 0755);  // 设置权限为所有者可读写执行,组和其他可读执行

(3)高级用法

递归创建多级目录

php
$path = "path/to/nested/directory";

if (!file_exists($path)) {
    if (mkdir($path, 0777, true)) {  // 第三个参数 true 表示递归创建
        echo "多级目录创建成功";
    } else {
        echo "无法创建目录";
    }
}

结合错误处理

php
function createDirectory($path) {
    if (file_exists($path)) {
        return true;
    }
    
    $old_mask = umask(0);  // 临时修改 umask 确保权限设置准确
    
    if (!mkdir($path, 0777, true)) {
        umask($old_mask);  // 恢复原 umask
        throw new Exception("无法创建目录: $path");
    }
    
    umask($old_mask);
    return true;
}

try {
    createDirectory("secure/uploads");
} catch (Exception $e) {
    echo $e->getMessage();
}

(4)注意事项

  1. 权限问题

    • PHP 需要有目标父目录的写权限
    • 创建的目录权限受 umask 影响,实际权限 = $mode & ~umask()
  2. 目录已存在

    • 如果目录已存在,mkdir() 会失败并返回 false
    • 建议先用 file_exists()is_dir() 检查
  3. Windows 系统

    • 权限参数 $mode 在 Windows 上被忽略
    • 路径分隔符可以使用 /\
  4. 安全考虑

    • 避免使用用户输入直接作为目录名
    • 必要时使用 basename()realpath() 处理路径
  5. 性能考虑

    • 递归创建 ($recursive=true) 比单独创建每个目录效率高
    • 但会一次性占用更多资源

(5)相关函数

  • rmdir() - 删除目录
  • is_dir() - 检查是否是目录
  • file_exists() - 检查文件或目录是否存在
  • chmod() - 改变文件/目录权限
  • umask() - 改变当前的 umask

mkdir() 是目录操作的基础函数,合理使用可以构建复杂的目录结构,特别适合在需要动态创建存储空间的场景中使用。

滇ICP备2025057983号-1