PHP 知识:9 个重要实用的 PHP 函数和功能

即便你已经使用PHP多年,也难免会偶尔发现一些之前从未了解过的函数和功能。其中有些非常实用,却未得到充分利用。毕竟,不是每个人都会逐页通读PHP手册和函数参考文档!下面就来为大家介绍9个重要且实用的PHP函数和功能。

1. 支持任意参数数目的函数

在PHP中,我们都知道可以定义带有可选参数的函数,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 带有2个可选参数的函数
function foo($arg1 = '', $arg2 = '') {
echo "arg1: $arg1\n";
echo "arg2: $arg2\n";
}
foo('hello', 'world');
// 输出:
// arg1: hello
// arg2: world
foo();
// 输出:
// arg1:
// arg2:

不过,PHP还提供了一种方法,允许定义能接受任意数目的函数参数。这时候就要用到func_get_args()函数,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 函数参数列表可以为空
function foo() {
// 返回包含所有传入参数的数组
$args = func_get_args();
foreach ($args as $k => $v) {
echo "arg". ($k + 1). ": $v\n";
}
}
foo();
// 无输出
foo('hello');
// 输出:
// arg1: hello
foo('hello', 'world', 'again');
// 输出:
// arg1: hello
// arg2: world
// arg3: again

2. 使用glob()查找文件

PHP中有不少函数名称很长且具有描述性,但glob()函数可能让人不太容易猜出它的功能,除非你经常使用它。可以把glob()看作是比scandir()更强大的函数,它能够按照特定模式搜索文件。
获取所有PHP文件:

1
2
3
4
5
6
7
8
9
10
11
// 获取所有php文件
$files = glob('*.php');
print_r($files);
// 输出类似:
// Array
// (
// [0] => phptest.php
// [1] => pi.php
// [2] => post_output.php
// [3] => test.php
// )

获取多种类型的文件,例如PHP文件和TXT文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取所有php文件和txt文件
$files = glob('*.{php,txt}', GLOB_BRACE);
print_r($files);
// 输出类似:
// Array
// (
// [0] => phptest.php
// [1] => pi.php
// [2] => post_output.php
// [3] => test.php
// [4] => log.txt
// [5] => test.txt
// )

glob()函数返回的文件路径取决于查询条件,例如:

1
2
3
4
5
6
7
8
$files = glob('../images/a*.jpg');
print_r($files);
// 输出类似:
// Array
// (
// [0] => ../images/apple.jpg
// [1] => ../images/art.jpg
// )

如果想要获取每个文件的完整路径,可以结合realpath()函数使用:

1
2
3
4
5
6
7
8
9
10
$files = glob('../images/a*.jpg');
// 对数组中的每个元素应用realpath函数
$files = array_map('realpath', $files);
print_r($files);
// 输出类似:
// Array
// (
// [0] => C:\wamp\www\images\apple.jpg
// [1] => C:\wamp\www\images\art.jpg
// )

3. 内存使用信息

监测脚本的内存使用情况,对优化代码很有帮助。PHP拥有垃圾收集器和复杂的内存管理器,脚本执行过程中内存使用量会有所波动。要获取当前内存使用情况,可以使用memory_get_usage()函数;若想知道脚本在执行过程中的最高内存使用量,则可以使用memory_get_peak_usage()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
echo "Initial: ". memory_get_usage(). " bytes \n";
// 输出:Initial: 361400 bytes
// 消耗一些内存
for ($i = 0; $i < 100000; $i++) {
$array [] = md5($i);
}
// 删除数组的一半元素
for ($i = 0; $i < 100000; $i++) {
unset($array[$i]);
}
echo "Final: ". memory_get_usage(). " bytes \n";
// 输出:Final: 885912 bytes
echo "Peak: ". memory_get_peak_usage(). " bytes \n";
// 输出:Peak: 13687072 bytes

4. CPU使用信息

在PHP中,可以利用getrusage()函数获取CPU使用信息,但需要注意的是,该函数不适用于Windows平台。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
print_r(getrusage());
// 输出类似:
// Array
// (
// [ru_oublock] => 0
// [ru_inblock] => 0
// [ru_msgsnd] => 2
// [ru_msgrcv] => 3
// [ru_maxrss] => 12692
// [ru_ixrss] => 764
// [ru_idrss] => 3864
// [ru_minflt] => 94
// [ru_majflt] => 0
// [ru_nsignals] => 1
// [ru_nvcsw] => 67
// [ru_nivcsw] => 4
// [ru_nswap] => 0
// [ru_utime.tv_usec] => 0
// [ru_utime.tv_sec] => 0
// [ru_stime.tv_usec] => 6269
// [ru_stime.tv_sec] => 0
// )

上述输出结果可能看起来有些晦涩,下面为大家解释每个值的含义(无需强行记忆):

  • ru_oublock:块输出操作数
  • ru_inblock:块输入操作数
  • ru_msgsnd:发送的消息数
  • ru_msgrcv:接收的消息数
  • ru_maxrss:最大驻留集大小
  • ru_ixrss:共享内存大小
  • ru_idrss:非共享数据大小
  • ru_minflt:页面回收次数
  • ru_majflt:页面错误次数
  • ru_nsignals:接收的信号数
  • ru_nvcsw:自愿上下文切换次数
  • ru_nivcsw:非自愿上下文切换次数
  • ru_nswap:交换次数
  • ru_utime.tv_usec:用户态使用的微秒数
  • ru_utime.tv_sec:用户态使用的秒数
  • ru_stime.tv_usec:系统态使用的微秒数
  • ru_stime.tv_sec:系统态使用的秒数

要了解脚本消耗的CPU功率,主要关注user time(用户时间)和system time(系统时间)这两个参数。这两个参数的值默认分别以秒和微秒为单位提供,我们可以将微秒部分除以100万,再加上秒数部分,得到总的十进制秒数。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
// 睡眠3秒(不占用CPU)
sleep(3);
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
// 输出:
// User time: 0.011552
// System time: 0

尽管脚本运行了约3秒,但CPU使用率非常低,因为睡眠过程中脚本实际上不消耗CPU资源。还有很多任务可能耗时较长,但不会占用CPU时间,比如等待磁盘操作。
再看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 循环1000万次(占用CPU)
for($i = 0; $i < 10000000; $i++) {
}
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
// 输出:
// User time: 1.424592
// System time: 0.004204

这个循环大约消耗了1.4秒的CPU时间,且几乎都是用户时间,因为没有系统调用。系统时间是指执行程序系统调用时的CPU开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$start = microtime(true);
// 持续调用microtime约3秒
while(microtime(true) - $start < 3) {
}
$data = getrusage();
echo "User time: ".
($data['ru_utime.tv_sec'] +
$data['ru_utime.tv_usec'] / 1000000);
echo "System time: ".
($data['ru_stime.tv_sec'] +
$data['ru_stime.tv_usec'] / 1000000);
// 输出:
// User time: 1.088171
// System time: 1.675315

在这个例子中,系统时间占用较多,这是因为脚本多次调用microtime()函数,该函数需要向操作系统请求获取时间。你可能还会发现运行时间加起来不到3秒,这是因为服务器上可能同时存在其他进程,脚本并没有100%占用CPU的3秒持续时间。

5. 魔术常量

PHP提供了一系列非常有用的魔术常量,如当前行号__LINE__、文件路径__FILE__、目录路径__DIR__、函数名__FUNCTION__、类名__CLASS__、方法名__METHOD__和命名空间__NAMESPACE__等。这里不一一详细介绍,主要分享一些它们的使用场景。
在包含其他脚本文件时,可以使用__FILE__常量(或者PHP 5.3新增的__DIR__常量):

1
2
3
4
// 这种方式相对于加载脚本的路径,在不同目录运行脚本时可能会有问题
require_once('config/database.php');
// 这种方式始终相对于当前文件的路径,无论从何处包含该文件都不会出错
require_once(dirname(__FILE__) . '/config/database.php');

__LINE__常量在调试时非常有用,它可以帮助我们追踪到具体的代码行号。

1
2
3
4
5
6
7
8
9
10
11
// 一些代码
//...
my_debug("some debug message", __LINE__);
// 输出:Line 4: some debug message
// 更多代码
//...
my_debug("another debug message", __LINE__);
// 输出:Line 11: another debug message
function my_debug($msg, $line) {
echo "Line $line: $msg";
}

6. 生成唯一标识符

在某些场景下,我们需要生成唯一的字符串。很多人会使用md5()函数,但它并非专门用于此目的。实际上,PHP提供了uniqid()函数来生成唯一标识符:

1
2
3
4
5
6
7
8
// 生成唯一字符串
echo md5(time(). mt_rand(1,1000000));
// 使用uniqid()函数生成唯一字符串
echo uniqid();
// 输出类似:4bd67c947233e
// 再次生成唯一字符串
echo uniqid();
// 输出类似:4bd67c9472340

可以发现,uniqid()生成的字符串虽然是唯一的,但前几个字符相似,这是因为生成的字符串与服务器时间相关。不过,这也有好处,新生成的ID会按字母顺序排列,方便排序。如果想进一步降低重复概率,可以传递一个前缀,或者设置第二个参数增加熵值。

1
2
3
4
5
6
7
8
9
// 带前缀
echo uniqid('foo_');
// 输出类似:foo_4bd67d6cd8b8f
// 增加熵值
echo uniqid('',true);
// 输出类似:4bd67d6cd8b926.12135106
// 同时使用前缀和增加熵值
echo uniqid('bar_',true);
// 输出类似:bar_4bd67da367b650.43684647

相比md5()函数,uniqid()生成的字符串更短,能节省一定的空间。

7. 序列化

在开发过程中,我们可能会遇到需要在数据库或文本文件中存储复杂变量的情况。PHP提供了序列化变量的功能,有两种常用的方法。
使用serialize()unserialize()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 一个复杂数组
$myvar = array(
'hello',
42,
array(1, 'two'),
'apple'
);
// 转换为字符串
$string = serialize($myvar);
echo $string;
// 输出:a:4:{i:0;s:5:"hello";i:1;i:42;i:2;a:2:{i:0;i:1;i:1;s:3:"two";}i:3;s:5:"apple";}
// 恢复原始变量
$newvar = unserialize($string);
print_r($newvar);
// 输出:
// Array
// (
// [0] => hello
// [1] => 42
// [2] => Array
// (
// [0] => 1
// [1] => two
// )
// [3] => apple
// )

随着JSON的广泛应用,PHP 5.2及以上版本加入了对JSON格式的支持,可以使用json_encode()json_decode()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 一个复杂数组
$myvar = array(
'hello',
42,
array(1, 'two'),
'apple'
);
// 转换为字符串
$string = json_encode($myvar);
echo $string;
// 输出:["hello",42,[1,"two"],"apple"]
// 恢复原始变量
$newvar = json_decode($string);
print_r($newvar);
// 输出:
// Array
// (
// [0] => hello
// [1] => 42
// [2] => Array
// (
// [0] => 1
// [1] => two
// )
// [3] => apple
// )

使用JSON序列化的方式更高效,并且与JavaScript等多种语言兼容。不过,对于复杂对象,可能会丢失一些信息。


PHP 知识:9 个重要实用的 PHP 函数和功能
https://jycpp.github.io/11-10-12-PHP中9个重要实用的函数和功能.html
作者
Jet Yan
发布于
2011年10月12日
许可协议