深入理解PHP中七个预定义接口-php教程

资源魔 26 0

深化了解预约义接口

场景:平时工作中写的都是营业模块,很少会去完成这样的接口,然而正在框架外面用的却是不少。

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中七个预约义接口的具体内容,更多请存眷资源魔其它相干文章!

标签: php php开发教程 php开发资料 php开发自学

抱歉,评论功能暂时关闭!