首页 > 文章列表 > 必须了解的9个有用的PHP函数和特性

我们每月两次重温 Nettuts+ 历史上一些读者最喜欢的帖子。

即使在使用 PHP 多年之后,我们还是会偶然发现一些我们不知道的函数和特性。其中一些可能非常有用,但未得到充分利用。考虑到这一点,我编制了一份您应该熟悉的九个非常有用的 PHP 函数和特性的列表。


<跨度>1。 具有任意数量参数的函数

您可能已经知道 PHP 允许您使用可选参数定义函数。但还有一种方法允许完全任意数量的函数参数。

首先,这是一个仅包含可选参数的示例:

// function with 2 optional arguments
function foo($arg1 = '', $arg2 = '') {

	echo "arg1: $arg1n";
	echo "arg2: $arg2n";

}


foo('hello','world');
/* prints:
arg1: hello
arg2: world
*/

foo();
/* prints:
arg1:
arg2:
*/

现在,让我们看看如何构建一个接受任意数量参数的函数。这次我们将使用 func_get_args():

// yes, the argument list can be empty
function foo() {

	// returns an array of all passed arguments
	$args = func_get_args();

	foreach ($args as $k => $v) {
		echo "arg".($k+1).": $vn";
	}

}

foo();
/* prints nothing */

foo('hello');
/* prints
arg1: hello
*/

foo('hello', 'world', 'again');
/* prints
arg1: hello
arg2: world
arg3: again
*/

<跨度>2。 使用 Glob() 查找文件

许多 PHP 函数都有很长且具有描述性的名称。然而,除非您已经从其他地方熟悉了该术语,否则可能很难说出名为 glob() 的函数的作用。

可以将其视为功能更强大的 scandir() 函数版本。它可以让您使用模式搜索文件。

// get all php files
$files = glob('*.php');

print_r($files);
/* output looks like:
Array
(
    [0] => phptest.php
    [1] => pi.php
    [2] => post_output.php
    [3] => test.php
)
*/

您可以像这样获取多种文件类型:

// get all php files AND txt files
$files = glob('*.{php,txt}', GLOB_BRACE);

print_r($files);
/* output looks like:
Array
(
    [0] => phptest.php
    [1] => pi.php
    [2] => post_output.php
    [3] => test.php
    [4] => log.txt
    [5] => test.txt
)
*/

请注意,文件实际上可以通过路径返回,具体取决于您的查询:

$files = glob('../images/a*.jpg');

print_r($files);
/* output looks like:
Array
(
    [0] => ../images/apple.jpg
    [1] => ../images/art.jpg
)
*/

如果您想获取每个文件的完整路径,只需对返回值调用 realpath() 函数即可:

$files = glob('../images/a*.jpg');

// applies the function to each array element
$files = array_map('realpath',$files);

print_r($files);
/* output looks like:
Array
(
    [0] => C:wampwwwimagesapple.jpg
    [1] => C:wampwwwimagesart.jpg
)
*/

<跨度>3。 内存使用信息

通过观察脚本的内存使用情况,您可以更好地优化代码。

PHP 有一个垃圾收集器和一个相当复杂的内存管理器。您的脚本使用的内存量。在脚本执行过程中可以上下移动。要获取当前内存使用情况,我们可以使用 memory_get_usage() 函数,要获取任意时刻的最高内存使用量,我们可以使用 memory_get_peak_usage() 函数。

echo "Initial: ".memory_get_usage()." bytes n";
/* prints
Initial: 361400 bytes
*/

// let's use up some memory
for ($i = 0; $i < 100000; $i++) {
	$array []= md5($i);
}

// let's remove half of the array
for ($i = 0; $i < 100000; $i++) {
	unset($array[$i]);
}

echo "Final: ".memory_get_usage()." bytes n";
/* prints
Final: 885912 bytes
*/

echo "Peak: ".memory_get_peak_usage()." bytes n";
/* prints
Peak: 13687072 bytes
*/

<跨度>4。 CPU 使用信息

为此,我们将使用 getrusage() 函数。请记住,这在 Windows 平台上不可用。

print_r(getrusage());
/* prints
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 功率,我们需要查看“用户时间”和“系统时间”值。默认情况下,秒和微秒部分是单独提供的。您可以将微秒值除以 100 万,然后将其与秒值相加,以获得十进制数形式的总秒数。

让我们看一个例子:

// sleep for 3 seconds (non-busy)
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);

/* prints
User time: 0.011552
System time: 0
*/

尽管脚本运行时间大约为 3 秒,但 CPU 使用率非常非常低。因为在睡眠操作期间,脚本实际上并不消耗CPU资源。还有许多其他任务可能需要实时处理,但可能不使用 CPU 时间,例如等待磁盘操作。正如您所看到的,CPU 使用率和运行时的实际长度并不总是相同。

这是另一个例子:

// loop 10 million times (busy)
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);

/* prints
User time: 1.424592
System time: 0.004204
*/

这花费了大约 1.4 秒的 CPU 时间,几乎全部都是用户时间,因为没有系统调用。

系统时间是 CPU 代表程序为内核执行系统调用所花费的时间。这是一个例子:

$start = microtime(true);
// keep calling microtime for about 3 seconds
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);

/* prints
User time: 1.088171
System time: 1.675315
*/

现在我们有相当多的系统时间使用。这是因为脚本多次调用 microtime() 函数,该函数通过操作系统执行请求来获取时间。

此外,您可能会注意到这些数字加起来并不完全等于 3 秒。这是因为服务器上可能还有其他进程,并且脚本在整个 3 秒的持续时间内没有使用 100% CPU。


<跨度>5。 魔法常量

PHP 提供了有用的魔法常量,用于获取当前行号 (__LINE__)、文件路径 (__FILE__)、目录路径 (__DIR__)、函数名称 (__FUNCTION__)、类名称 (__CLASS__) )、方法名称(__METHOD__)和命名空间(__NAMESPACE__)。

我们不会在本文中逐一介绍这些内容,但我将向您展示一些用例。

当包含其他脚本时,最好使用 __FILE__ 常量(或者 __DIR__ ,从 PHP 5.3 开始):

// this is relative to the loaded script's path
// it may cause problems when running scripts from different directories
require_once('config/database.php');

// this is always relative to this file's path
// no matter where it was included from
require_once(dirname(__FILE__) . '/config/database.php');

使用 __LINE__ 使调试更加容易。您可以追踪行号:

// some code
// ...
my_debug("some debug message", __LINE__);
/* prints
Line 4: some debug message
*/

// some more code
// ...
my_debug("another debug message", __LINE__);
/* prints
Line 11: another debug message
*/

function my_debug($msg, $line) {
	echo "Line $line: $msgn";
}

<跨度>6。 生成唯一 ID

在某些情况下,您可能需要生成唯一的字符串。我看到很多人为此使用 md5() 函数,尽管它并不完全用于此目的:

// generate unique string
echo md5(time() . mt_rand(1,1000000));

实际上有一个名为 uniqid() 的 PHP 函数就是用于此目的的。

// generate unique string
echo uniqid();
/* prints
4bd67c947233e
*/

// generate another unique string
echo uniqid();
/* prints
4bd67c9472340
*/

您可能会注意到,尽管字符串是唯一的,但它们的前几个字符看起来很相似。这是因为生成的字符串与服务器时间相关。这实际上有一个很好的副作用,因为每个新生成的 id 按字母顺序排列在后面,因此可以对它们进行排序。

为了减少获得重复的机会,您可以传递前缀或第二个参数来增加熵:

// with prefix
echo uniqid('foo_');
/* prints
foo_4bd67d6cd8b8f
*/

// with more entropy
echo uniqid('',true);
/* prints
4bd67d6cd8b926.12135106
*/

// both
echo uniqid('bar_',true);
/* prints
bar_4bd67da367b650.43684647
*/

此函数将生成比 md5() 更短的字符串,这也会节省一些空间。


<跨度>7。 序列化

您是否曾经需要将复杂变量存储在数据库或文本文件中?您不必想出一个奇特的解决方案来将数组或对象转换为格式化字符串,因为 PHP 已经具有用于此目的的函数。

有两种流行的序列化变量的方法。以下是使用serialize() 和unserialize() 的示例:

// a complex array
$myvar = array(
	'hello',
	42,
	array(1,'two'),
	'apple'
);

// convert to a string
$string = serialize($myvar);

echo $string;
/* prints
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";}
*/

// you can reproduce the original variable
$newvar = unserialize($string);

print_r($newvar);
/* prints
Array
(
    [0] => hello
    [1] => 42
    [2] => Array
        (
            [0] => 1
            [1] => two
        )

    [3] => apple
)
*/

这是本机 PHP 序列化方法。然而,由于 JSON 近年来变得如此流行,他们决定在 PHP 5.2 中添加对它的支持。现在您也可以使用 json_encode()json_decode() 函数:

// a complex array
$myvar = array(
	'hello',
	42,
	array(1,'two'),
	'apple'
);

// convert to a string
$string = json_encode($myvar);

echo $string;
/* prints
["hello",42,[1,"two"],"apple"]
*/

// you can reproduce the original variable
$newvar = json_decode($string);

print_r($newvar);
/* prints
Array
(
    [0] => hello
    [1] => 42
    [2] => Array
        (
            [0] => 1
            [1] => two
        )

    [3] => apple
)
*/

它更加紧凑,最重要的是,它与 javascript 和许多其他语言兼容。然而,对于复杂的对象,一些信息可能会丢失。


<跨度>8。 压缩字符串

当谈论压缩时,我们通常会想到文件,例如 ZIP 存档。可以在 PHP 中压缩长字符串,而不涉及任何存档文件。

在下面的示例中,我们将使用 gzcompress() 和 gzuncompress() 函数:

$string =
"Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Nunc ut elit id mi ultricies
adipiscing. Nulla facilisi. Praesent pulvinar,
sapien vel feugiat vestibulum, nulla dui pretium orci,
non ultricies elit lacus quis ante. Lorem ipsum dolor
sit amet, consectetur adipiscing elit. Aliquam
pretium ullamcorper urna quis iaculis. Etiam ac massa
sed turpis tempor luctus. Curabitur sed nibh eu elit
mollis congue. Praesent ipsum diam, consectetur vitae
ornare a, aliquam a nunc. In id magna pellentesque
tellus posuere adipiscing. Sed non mi metus, at lacinia
augue. Sed magna nisi, ornare in mollis in, mollis
sed nunc. Etiam at justo in leo congue mollis.
Nullam in neque eget metus hendrerit scelerisque
eu non enim. Ut malesuada lacus eu nulla bibendum
id euismod urna sodales. ";

$compressed = gzcompress($string);

echo "Original size: ". strlen($string)."n";
/* prints
Original size: 800
*/



echo "Compressed size: ". strlen($compressed)."n";
/* prints
Compressed size: 418
*/

// getting it back
$original = gzuncompress($compressed);

我们能够实现近 50% 的尺寸减小。此外,函数 gzencode() 和 gzdecode() 通过使用不同的压缩算法实现了类似的结果。


<跨度>9。 注册关机功能

有一个名为 register_shutdown_function() 的函数,它可以让您在脚本完成运行之前执行一些代码。

假设您希望在脚本执行结束时捕获一些基准统计数据,例如运行时间:

// capture the start time
$start_time = microtime(true);

// do some stuff
// ...

// display how long the script took
echo "execution took: ".
		(microtime(true) - $start_time).
		" seconds.";

乍一看,这似乎微不足道。您只需将代码添加到脚本的最底部,它就会在完成之前运行。但是,如果您调用 exit() 函数,该代码将永远不会运行。另外,如果出现致命错误,或者用户终止脚本(通过按浏览器中的“停止”按钮),它可能再次无法运行。

当您使用register_shutdown_function()时,无论脚本为何停止运行,您的代码都会执行:

$start_time = microtime(true);

register_shutdown_function('my_shutdown');

// do some stuff
// ...


function my_shutdown() {
	global $start_time;

	echo "execution took: ".
			(microtime(true) - $start_time).
			" seconds.";
}

结论

您是否知道任何其他不广为人知但非常有用的 PHP 功能?请在评论中与我们分享。感谢您的阅读!