九个实用的PHP函数

1. 带有任意数量参数的函数

你可能知道PHP允许你定义带有任意参数的函数。不过事实上还有一种完全允许任意数量函数参数的方法。

首先这里给出一个可选参数的示例:

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

    echo "arg1: $arg1\n";
    echo "arg2: $arg2\n";

}

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).": $v\n";
	}

}

foo();
/* prints nothing */

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

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


2. 利用Glob()查找文件

很多PHP函数的函数名都很长并且带有解释性。 因此从名称来判断glob()函数的作用不是件简单事,除非你之前就已经熟悉了这个函数。

你可以把这个函数想象成scandir()函数的进化版。 glob()使你能够通过样式查找文件:

// 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:\wamp\www\images\apple.jpg
    [1] => C:\wamp\www\images\art.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,需要先了解“用户时间”和“系统时间”的值。默认单位分别是秒和微秒(百万分之一秒)。 你可以把微秒转换成秒,然后将其添加到秒值,以获取小数形式的总秒数。

下面我们来看一个例子:

// 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秒。 这是因为服务端可能还有其他进程,脚本在这三秒内无法完全占用CPU。


5. 魔术常量

PHP为获取当前行数(__LINE__)、文件路径(__FILE__)、目录路径(__DIR__)、文件名(__FUNCTION__)、类名称(__CLASS__)、方法名(__METHOD__)以及名空间(__NAMESPACE__)提供了非常实用的魔术常量。

这里只介绍其中几个作为例子。

在包含其他脚本时,利用__FILE__常量(PHP 5.3以上版本中还可以使用__DIR__)是个好办法。

// 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__会让除BUG变得更轻松。 记录行数:

// 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: $msg\n";
}

6. 生成唯一ID

有时候你需要生成一个具有唯一性的字符串。 这时很多人会用md5()函数,即使这个函数并不是为这个用途而存在。

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

事实上有一个PHP函数uniqid()就是为这个目的而创造的。

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

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

即使字符串是唯一的,他们看上去却和最初的几个字符差不多。 这时因为生成的字符串与服务器时间有关。 这样带来的一个积极效果就是,新ID会按字母表顺序生成,为排序提供了方便。
为减少获得重复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
)
*/

这种方法更紧凑,并且可以与JS以及其他语言兼容。 不过对复杂的对象时可能会丢失信息。


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.";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值