php中trait(性状)与generator(生成器)
PHP中trait(性状)与generator(生成器)
一、trait (性状)
最近在看Josh Lockhat的《Modern PHP》,这本书很薄。但是其中给出了一个很重要的学习方法。就是写PHP程序的时候不要凡事都自己来创造,如果已经有牛人写好了组件,直接放进自己项目中用就好了,并且给出了组件的链接。这半年看技术不少,外国的程序员比较喜欢按部就班的按照规范来写程序,包括代码规范,如何保证代码的性能方面,都严格按照工具进行测试。但是我前一份工作,对于代码规范和上线规范就比较松懈。这家公司有了很大的提高,在项目组长的要求下,还开发了功能强大的上线系统。
这本书中我看到了性状,于迭代器这两个对于我来说比较新的内容。其实这两个东西都比较好理解,因此就不单独写博客介绍了,合并在一篇博客中介绍,大家看代码就好。
<?php
header("content-type:text/html;charset=utf-8");
trait BandAid
{
public function __construct()
{
echo "Hello BandAid<br/>";
}
public function getName()
{
echo "Roverliang<br/>";
}
}
trait BandAid_2
{
public function hello()
{
echo "I'm BandAid_2<br/>";
}
}
class Body
{
use BandAid;
use BandAid_2;
public function __construct()
{
echo "Hello Body<br/>";
}
public function makeDance()
{
echo "Dance<br/>";
}
}
$roverliang = new Body();
$roverliang->getName();
$roverliang->makeDance();
$roverliang->hello();
结果:
Hello Body
Roverliang
Dance
I'm BandAid_2
其实PHP manual 中关于性状讲解的更为清晰:trait:性状
其中有一个需要注意点,容易犯错代码如下:
<?php
header("content-type:text/html;charset=utf-8");
class TestClass
{
public static $bar;
}
class Foo1 extends TestClass {}
class Foo2 extends TestClass {}
Foo1::$bar = "hello";
Foo2::$bar = "World";
echo Foo1::$bar;
echo Foo2::$bar;
//result is
/**
* WorldWorld
*/
/*********************************************/
trait TestTrait
{
public static $bar;
}
class Foo3
{
use TestTrait;
}
class Foo4
{
use TestTrait;
}
Foo3::$bar = "Hello";
Foo4::$bar = "world";
echo Foo3::$bar;
echo Foo4::$bar;
//result
/**
* Helloworld
*/
由于这个markdown 工具没有代码折叠的功能,所以只能让大家看这么冗长的代码了。trait 是一种灵活的代码段。PHP5.4 版本才增加这个功能,所以使用的时候一定要先看下自己所使用的PHP版本。PHP文档中说了,trait是一种代码复用的方法。我对于trait理解是,trait就是一个创可贴,哪里需要了就贴上去,满足功能。trait的__construct 在使用的时候会被重写,因此使用trait的时候可以不用写构造函数。
generator(生成器)
关于生成器详细可以看官方文档:生成器总览
尤其要注意的是,生成器的版本要求是PHP 5 >= 5.5.0, PHP 7
示例如下:
<?php
header("content-type:text/html;charset=utf-8");
date_default_timezone_set('Asia/Shanghai');
$file = "./ip-to-country.csv";
function getFile($file)
{
$handler = fopen($file, 'rb');
if ($handler === false) {
throw new Exception();
}
while (!feof($handler)) {
yield fgetcsv($handler);
}
fclose($handler);
}
$stime = microtime(true);
foreach (getFile($file) as $val) {
//print_r($val); save to redis
}
$etime = microtime(true);
$exectime_1 = $etime - $stime;
function getFile_2($file)
{
$handler = fopen($file, 'rb');
if ($handler === false) {
throw new Exception();
}
while (!feof($handler)) {
fgetcsv($handler); //save to redis
}
fclose($handler);
}
$stime = microtime(true);
getFile_2($file);
$etime = microtime(true);
$exectime_2 = $etime - $stime;
echo $exectime_1;
echo "\r\n";
echo $exectime_2;
echo "\r\n";
//352.71399402618
//331.73138713837
本文测试的文件大小如下:
[root@roverliang html]# ls -lh
总用量 2.0G
-rwxr--r--. 1 jack jack 171 11月 17 13:23 Creator.php
-rwxr--r--. 1 jack jack 236 11月 17 13:27 GraphicFactory.php
-rwxr--r--. 1 jack jack 963 11月 20 07:53 index.php
-rwxr--r--. 1 root root 2.0G 11月 20 06:38 ip-to-country.csv
-rwxr--r--. 1 jack jack 243 11月 19 13:38 TextFactory.php
经过测试发现,迭代器在读取大文件时候并没有表现出其官网所宣称的那种效率。甚至比使用使用常规的逐行读取方法还要慢一点点,不过这种时间上的差距可以忽视。比第二种方式略慢,可能是因为迭代器内部的状态切换所带来的性能开销。
那么两种方式在系统内存和cpu占用上有何不同,看下图:
使用迭代器
不使用
由此可以看出,使用迭代器和不使用两者并没有明显不同。
虽然如此,还是需要了解迭代器,至少给予我们一种不同的方式来进行数据操作。