百分百源码网-让建站变得如此简单! 登录 注册 签到领金币!

主页 | 如何升级VIP | TAG标签

当前位置: 主页>网站教程>网页制作> 深入了解PHP中七个预定义接口
分享文章到:

深入了解PHP中七个预定义接口

发布时间:09/01 来源:未知 浏览: 关键词:

深入懂得预定义接口

场景:平常工作中写的都是业务模块,很少会去实现这样的接口,但是在框架里面用的倒是许多。

1. Traversable(遍历)接口

该接口不克不及被类直接实现,假如直接写了一个一般类实现了该遍历接口,是会直接报致命的错误,提醒使用 Iterator(迭代器接口)或者 IteratorAggregate(聚合迭代器接口)来实现,这两个接口后面会介绍;所有平常状况下,我们只是会用来推断该类可否可以使用 foreach 来停止遍历;

class Test implements Traversable
    {
    }
    上面这个是错误示范,该代码会提醒这样的错误:
    Fatal error: Class Test must implement interface Traversable as part of either Iterator or 
    IteratorAggregate in Unknown on line 0

上面的大致意思是说如要实现这个接口,必需同Iterator或者IteratorAggregate来实现

准确的做法:

当我们要推断一个类可否可以使用foreach来停止遍历,只需要推断可否是traversable的实例

     class Test
     {
     }
     $test = new Test;
     var_dump($test instanceOf Traversable);

2. Iterator(迭代器)接口

迭代器接口其实实现的道理就是相似指针的移动,当我们写一个类的时候,通过实现对应的 5 个办法:key(),current(),next(),rewind(),valid(),就可以实现数据的迭代移动,详细看以下代码

<?php
    class Test implements Iterator
    {
        private $key;
        private $val = [
            'one',
            'two',
            'three',
        ];

        public function key()
        {
            return $this->key;
        }

        public function current()
        {
            return $this->val[$this->key];
        }

        public function next()
        {
            ++$this->key;
        }

        public function rewind()
        {
            $this->key = 0;
        }

        public function valid()
        {
            return isset($this->val[$this->key]);
        }
    }

    $test = new Test;

    $test->rewind();

    while($test->valid()) {
        echo $test->key . ':' . $test->current() . PHP_EOL;
        $test->next();
    }

## 该输出结果 :

        0: one
        1: two
        2: three

看了这个道理我们就知道,其实迭代的移动方式:rewind()-> valid()->key() -> current() -> next() -> valid()-> key() ....-> valid();

好的,懂得了上面,我们翻开Iterator的接口,发明它是实现了Traversable(遍历)接口的,接下来我们来证明下:

var_dump($test instanceOf Traversable);

结果返回的是true,证明这个类的对象是可以停止遍历的。

 foreach ($test as $key => $value){
         echo $test->key . ':' . $test->current() . PHP_EOL;
  }

这个的结果跟while轮回实现的模式是一样的。

3. IteratorAggregate(聚合迭代器) 接口

聚合迭代器和迭代器的道理是一样的,只不外聚合迭代器已经实现了迭代器道理,你只需要实现一个 getIterator()办法来实现迭代,详细看以下代码

<?php
    class Test implements IteratorAggregate
    {
        public $one = 1;
        public $two = 2;
        public $three = 3;

        public function __construct()
        {
            $this->four = 4;
        }

        public function getIterator()
        {
            return new AraayIterator($this);
        }
    }

    $test = (new Test())->getIterator();
    $test->rewind();
    while($test->valid()) {
        echo $test->key() . ' : '  .  $test->current() . PHP_EOL;
        $test->next();
    }

    从上面的代码,我们可以看到我们将Test类的对象传进去当做迭代器,通过while轮回的话,我们必需通过调取getIterator()办法猎取到迭代器对象,然后直接停止迭代输出,而不需要去实现相关的key()等办法。
    当然这个时候,我们必定想知道可否可以直接从foreach停止迭代轮回出去呢?那么我们来打印一下结果

    $test = new Test;
    var_dump($test instanceOf Traversable);

    结果是输出bool true,所以我们接下来是直接用foreach来实现一下。

    $test = new Test;
  foreach($test as $key => $value) {
     echo $key . ' : ' . $value  .  PHP_EOL;
  }

 接下来,我们看到是对对象停止迭代,这个时候我们可否可以数组停止迭代呢?

 class Test implements IteratorAggregate
 {
    public $data;

    public function __construct()
    {
        $this->data = [''one' =>  1 , 'two' => 2];
    }

    public function getIterator()
    {
        return new AraayIterator($this->data);
    }
 }

 同理实现的方式跟对对象停止迭代是一样的。

许多PHPer在进阶的时候总会碰到一些问题和瓶颈,业务代码写多了没有标的目的感,不知道该从那里入手去晋升,对此我整理了一些材料,包罗但不限于:分布式架构、高可扩展、高机能、高并发、效劳器机能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell足本、Docker、微效劳、Nginx等多个知识点高级进阶干货需要的可避免费分享给大家,需要的加群(点击→)677079770

4. ArrayAccess(数组式拜访)接口

平常状况下,我们会看到 this ['name'] 这样的用途,但是我们知道,$this 是一个对象,是怎样使用数组方式拜访的?答案就是实现了数据组拜访接口 ArrayAccess,详细代码如下

<?php
    class Test implements ArrayAccess
    {
        public $container;

        public function __construct()
        {
            $this->container = [
                'one' => 1,
                'two' => 2,
                'three'  => 3,
            ];
        }

        public function offsetExists($offset) 
        {
            return isset($this->container[$offset]);
        }

        public function offsetGet($offset)
        {
            return isset($this->container[$offset]) ? $this->container[$offset] : null;
        }

        public function offsetSet($offset, $value)
        {
            if (is_null($offset)) {
                $this->container[] = $value;
            } else {
                $this->container[$offset] = $value;
            }
        }

        public function offsetUnset($offset)
        {
            unset($this->container[$offset]);
        }
    }
   $test = new Test;
   var_dump(isset($test['one']));
   var_dump($test['two']);
   unset($test['two']);
   var_dump(isset($test['two']));
   $test['two'] = 22;
   var_dump($test['two']);
   $test[] = 4;
   var_dump($test);
   var_dump($test[0]);

   当然我们也有经典的一个做法就是把对象的属性当做数组来拜访

   class Test implements ArrayAccess
   {
        public $name;

        public function __construct()
        {
            $this->name = 'gabe';  
        }

        public function offsetExists($offset)
        {
            return isset($this->$offset);
        }

        public function offsetGet($offset)
        {
            return isset($this->$offset) ? $this->$offset : null;
        }

        public function offsetSet($offset, $value)
        {
            $this->$offset = $value;
        }

        public function offsetUnset($offset)
        {
            unset($this->$offset);
        }
   }

  $test = new Test;
  var_dump(isset($test['name']));
  var_dump($test['name']);
  var_dump($test['age']);
  $test[1] = '22';
  var_dump($test);
  unset($test['name']);
  var_dump(isset($test['name']));
  var_dump($test);
  $test[] = 'hello world';
  var_dump($test);

5. Serializable (序列化)接口

平常状况下,假如我们的类中定义了魔术办法,sleep(),wakeup () 的话,我们在停止 serialize () 的时候,会先调取 sleep () 的魔术办法,我们通过返回一个数组,来定义对对象的哪些属性停止序列化,同理,我们在调取反序列化 unserialize () 办法的时候,也会先调取的 wakeup()魔术办法,我们可以停止初始化,如对一个对象的属性停止赋值等操纵;但是假如该类实现了序列化接口,我们就必需实现 serialize()办法和 unserialize () 办法,同时 sleep()和 wakeup () 两个魔术办法都会同时不再支撑,详细代码看如下;

<?php
    class Test
    {    
        public $name;
        public $age;

        public function __construct()
        {
            $this->name = 'gabe';
            $this->age = 25; 
        }    

        public function __wakeup()
        {
            var_dump(__METHOD__); 
            $this->age++;
        }   

        public function __sleep()
        {        
            var_dump(__METHOD__);
            return ['name'];    
        }
    }

    $test = new Test;
    $a = serialize($test);
    var_dump($a);
    var_dump(unserialize($a));

    //实现序列化接口,发明魔术办法失效了

   class Test implements Serializable
   {    
    public $name;
    public $age;

    public function __construct()
    {        
        $this->name = 'gabe';
        $this->age = 25;
    } 

    public function __wakeup()
    { 
        var_dump(__METHOD__);
        $this->age++;
    }

    public function __sleep()
    {
        var_dump(__METHOD__);
        return ['name'];
    }

    public function serialize()
    {
        return serialize($this->name);
    } 

    public function unserialize($serialized)
    {       
        $this->name = unserialize($serialized);
        $this->age = 1;    
    }
}
$test = new Test;
$a = serialize($test);
var_dump($a);
var_dump(unserialize($a));

6. Closure 类

用于代表匿名函数的类,但凡匿名函数其实返回的都是 Closure 闭包类的一个实例,该类中主要有两个办法,bindTo()和 bind(),通过查看源码,可以发明两个办法是殊途同归,只不外是 bind () 是个静态办法,详细用途看如下;

<?php
    $closure = function () {
        return 'hello world';
    }

    var_dump($closure);
    var_dump($closure());

通过上面的例子,可以看出第一个打印出来的是 Closure 的一个实例,而第二个就是打印出匿名函数返回的 hello world 字符串;接下来是使用这个匿名类的办法,这两个办法的目的都是把匿名函数绑定一个类上使用;

bindTo()

<?php
namespace demo1;
    class Test {
        private $name = 'hello woeld';
    }

    $closure = function () {
        return $this->name;
    }

    $func = $closure->bindTo(new Test);
    $func();
    // 这个是可以拜访不到私有属性的,会报出没法拜访私有属性
    // 下面这个是准确的做法
    $func = $closure->bindTo(new Test, Test::class);
    $func();

namespace demo2;
    class Test
    {
        private statis $name = 'hello world';
    }

    $closure = function () {
        return self::$name;
    }
    $func = $closure->bindTo(null, Test::class);
    $func();

bind()

<?php
namespace demo1;
class Test 
{
    private  $name = 'hello world';
}

$func = \Closure::bind(function() {
    return $this->name;
}, new Test, Test::class);

$func();

namespace demo2;
class Test 
{
    private static  $name = 'hello world';
}

$func = \Closure::bind(function() {
    return self::$name;
}, null, Test::class);

$func()

7. Generator (生成器)

Generator 实现了 Iterator,但是他没法被继承,同时也生成实例。既然实现了 Iterator,所以正如上文所介绍,他也就有了和 Iterator 雷同的功效:rewind->valid->current->key->next...,Generator 的语法主要来自于关键字 yield。yield 就比如一次轮回的中转站,记载本次的活动轨迹,返回一个 Generator 的实例。

Generator 的长处在于,当我们要使用到大数据的遍历,或者说大文件的读写,而我们的内存不足的状况下,能够极大的减少我们关于内存的耗损,由于传统的遍历会返回所有的数据,这个数据存在内存上,而 yield 只会返回当前的值,不外当我们在使用 yield 时,其实其中会有一个处置记忆体的历程,所以实际上这是一个用时间换空间的方法。

<?php
$start_time = microtime(true);
function xrange(int $num){
    for($i = 0; $i < $num; $i++) { 
        yield $i;    
    }
}
$generator = xrange(100000);
foreach ($generator as $key => $value) { 
    echo $key . ': ' . $value . PHP_EOL;
}
echo 'memory: ' . memory_get_usage() . ' time: '. (microtime(true) - $start_time);

输出:memory: 388904 time: 0.12135100364685

<?php
$start_time = microtime(true);
function xrange(int $num){
    $arr = [];    
    for($i = 0; $i < $num; $i++) { 
        array_push($arr, $i);
    } 
    return $arr;
}
$arr = xrange(100000);
foreach ($arr as $key => $value) {
    echo $key . ': ' . $value . PHP_EOL;
}
echo 'memory: ' . memory_get_usage() . ' time: '. (microtime(true) - $start_time);

输出:

memory: 6680312 time: 0.10804104804993

更多相关php知识,请拜访php教程!

以上就是深入懂得PHP中七个预定义接口的具体内容,更多请关注百分百源码网其它相关文章!

打赏

打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

百分百源码网 建议打赏1~10元,土豪随意,感谢您的阅读!

共有152人阅读,期待你的评论!发表评论
昵称: 网址: 验证码: 点击我更换图片
最新评论

本文标签

广告赞助

能出一分力是一分吧!

订阅获得更多模板

本文标签

广告赞助

订阅获得更多模板