PHP提供了几个预定义接口,用于解决一般性的问题
- 遍历(Travsersable接口)
- 迭代器
- 聚合式迭代器
- 数组式访问
- 序列化
- closure
- 生成器
罗列出来好像有点陌生,妈呀,这是什么鬼,接下来我们来剖析这个鬼东西!
Traversable遍历接口
检测一个类是否可以使用 foreach 进行遍历的接口。无法被单独实现的基本抽象接口。
无法被单独实现即是不能继承,但是可用于判断是否可以遍历
if($class instanceof Traversable){
foreach ($class as $attr){
print_r($attr);
}
}
Iterator迭代器
可在内部迭代自己的外部迭代器或类的接口。
这个听起来好像有点绕,其实就是该接口提供了php内部实现迭代器时用的接口,通过继承这些接口便可实现类迭代
接口定义如下:
Iterator
extends
Traversable {
/* 方法 */
}
沿用一下manual上的栗子:
class myIterator implements Iterator {
private $position = 0;
private $array = array(
"firstelement",
"secondelement",
"lastelement",
);
public function __construct() {
$this->position = 0;
}
function rewind() {
var_dump(__METHOD__);
$this->position = 0;
}
function current() {
var_dump(__METHOD__);
return $this->array[$this->position];
}
function key() {
var_dump(__METHOD__);
return $this->position;
}
function next() {
var_dump(__METHOD__);
++$this->position;
}
function valid() {
var_dump(__METHOD__);
return isset($this->array[$this->position]);
}
}
$it = new myIterator;
foreach($it as $key => $value) {
var_dump($key, $value);
echo "\n";
}
//打印
string(18) "myIterator::rewind"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(0)
string(12) "firstelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(1)
string(13) "secondelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
string(19) "myIterator::current"
string(15) "myIterator::key"
int(2)
string(11) "lastelement"
string(16) "myIterator::next"
string(17) "myIterator::valid"
IteratorAggregate聚合式迭代器
创建外部迭代器接口;其实就是可以通过实现这个接口,返回其他可迭代的迭代器,如spl提供的日常迭代器
接口定义如下:
}
举个栗子:
class myClass implements IteratorAggregate {
public $property1 = 'public property1';
public $property2 = 'public property2';
private $property3 = 'private property3';
protected $property4 = 'protected property4';
public function __construct(){
$this->property2 = 'change property2';
$this->property5 = 'dynamic append property5';
}
public function testFunc(){
var_dump('testfunc');
}
public function getIterator(){
return new ArrayIterator($this);
}
}
$myClass = new myClass();
foreach ($myClass as $key => $val){
echo '<pre>';
var_dump($key,$val);
echo "</pre>";
}
echo ('--------------------------------------------------------');
class myClass2 extends myClass {
public $property1 = 'myClass2 public property1';
public $property2 = 'myClass2 public property2';
private $property3 = 'myClass2 private property3';
protected $property4 = 'myClass2 protected property4';
}
$myClass = new myClass2();
foreach ($myClass as $key => $val){
echo '<pre>';
var_dump($key,$val);
echo "</pre>";
}
//输出,从输出可以看出来,迭代器只可以迭代public的属性,继承之后也可以继续迭代
string(9) "property1"
string(16) "public property1"
string(9) "property2"
string(16) "change property2"
string(9) "property5"
string(24) "dynamic append property5"
--------------------------------------------------------
string(9) "property1"
string(25) "myClass2 public property1"
string(9) "property2"
string(16) "change property2"
string(9) "property5"
string(24) "dynamic append property5"
ArrayAccess数组式访问接口
提供像访问数组一样访问对象的能力的接口,这个接口应该好理解,就是一个实例化对象,可以像访问数组的形式访问它
接口定义如下:
ArrayAccess {
/* 方法 */
}
依旧拿manual中的栗子:
class obj implements arrayaccess {
private $container = array();
public function __construct() {
$this->container = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
}
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
$obj = new obj;
var_dump(isset($obj["two"]));
var_dump($obj["two"]);
unset($obj["two"]);
var_dump(isset($obj["two"]));
$obj["two"] = "A value";
var_dump($obj["two"]);
$obj[] = 'Append 1';
$obj[] = 'Append 2';
$obj[] = 'Append 3';
print_r($obj);
//输出
bool(true)
int(2)
bool(false)
string(7) "A value"
obj Object
(
[container:obj:private] => Array
(
[one] => 1
[three] => 3
[two] => A value
[0] => Append 1
[1] => Append 2
[2] => Append 3
)
)
像这种把对象当做数组来访问的写法,在很多地方懂有用到,如swoole中访问配置文件config下的内容
Swoole::$php->config['redis']
源码是这样写的:
//swoole类中的定义是这样的
//定义了一个public的公共属性$config
//然后在加载配置文件的时候实例化Swoole\Config类
class Swoole{
.....
public config;
.....
.....
//将此目录作为App命名空间的根目录
Swoole\Loader::addNameSpace('App', self::$app_path . '/classes');
$this->load = new Swoole\Loader($this);
$this->model = new Swoole\ModelLoader($this);
$this->config = new Swoole\Config;
$this->config->setPath(self::$app_path . '/configs');
//添加默认路由器
$this->addRouter(new Swoole\Router\Rewrite());
$this->addRouter(new Swoole\Router\MVC);
}
//Swoole\Config类的定义,Config类继承了ArrayObject,而ArrayObject实现了ArrayAccess接口
namespace Swoole;
class Config extends\ArrayObject
{
protected $config;
......
function setPath($dir)
{
.......
}
function offsetGet($index)
{
if (!isset($this->config[$index]))
{
$this->load($index);
}
return isset($this->config[$index]) ? $this->config[$index] : false;
}
function load($index)
{
......
}
function offsetSet($index, $newval)
{
$this->config[$index] = $newval;
}
function offsetUnset($index)
{
unset($this->config[$index]);
}
function offsetExists($index)
{
return isset($this->config[$index]);
}
}
自定义序列化
自定义序列化的接口。实现此接口的类将不再支持 __sleep() 和 __wakeup()(想了解这两个魔术方法的可以看
这里)。不论何时,只要有实例需要被序列化,serialize 方法都将被调用。它将不会调用 __destruct() 或有其他影响,除非程序化地调用此方法。当数据被反序列化时,类将被感知并且调用合适的 unserialize() 方法而不是调用 __construct()。如果需要执行标准的构造器,你应该在这个方法中进行处理
接口定义如下:
Serializable {
/* 方法 */
}
Closure类
用于代表 匿名函数(闭包函数) 的类.匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象。
在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情。
自 PHP 5.4 起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制。
除了此处列出的方法,还有一个 __invoke 方法。
这是为了与其他实现了 __invoke()魔术方法 的对象保持一致性,但调用匿名函数的过程与它无关。
类定义如下:
除了上面bind()和bindTo()方法,其实还有call()方法和
__invoke()方法
bindTo()方法其实和bind()的实现是一样的,只不过bind()是静态调用方法
bindTo()有什么作用呢? 它可以动态的为匿名函数绑定参数,让参数也可以在闭包内使用,也可以用use关键字来绑定
举个栗子:
//绑定参数
$arr = [1,2];
$closure = function ($arg1){
echo '<pre>';
var_dump($arg1);
echo '</pre>';
};
$closure($arr);
绑定参数
$closure1 = function ()use($arr){
echo '<pre>';
var_dump($arr);
echo '</pre>';
};
$closure1();
//绑定类对象
class testClass {
public $property = 'test property';
}
$closure2 = function ($name){
echo '<pre>';
var_dump($name . $this->property);
echo '</pre>';
};
$obj = new testClass();
//绑定类实例,第一个参数$obj是要绑定的对象,第二个参数$obj是
//关联到匿名函数的类作用域,或者 'static' 保持当前状态。
//如果是一个对象,则使用这个对象的类型为心得类作用域。
// 这会决定绑定的对象的 保护、私有成员 方法的可见性
$closure2 = $closure2->bindTo($obj,$obj);
$closure2('sasd');
//输出
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
string(17) "sasdtest property"
生成器
generator对象是从generators返回的,generator对象不能通过new来实例化
生成器提供更容易的方法来实现简单的对象迭代,相比编写类来实现Iterator迭代器,生成器的性能开销和复杂性小很多
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组--
那会使你的内存达到上限,或者会占据可观的处理时间
接口定义如下:
Generator implements
Iterator {
/* 方法 */
}
上面接口定义是生成器的内部实现接口,但是你实现生成器的时候并不是要继承这个类,
生成器的语法如下:
参考manual说明
生成器函数实现的核心:yield关键字
它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数
举个栗子:
function generator_fun(){
for($i=0; $i<10;$i++){
yield $i*$i;
}
}
foreach (generator_fun() as $value){
echo ' ' . $value;
}
//输出
0 1 4 9 16 25 36 49 64 81
php这些预定义接口,看似很陌生,但其实只是在业务代码上用处不多,但是,稍微看过点框架源码的人就会比较熟悉
不要沉迷于高层业务,也该瞄瞄底层实现