Home  >  Article  >  Backend Development  >  In-depth understanding of the seven predefined interfaces in PHP

In-depth understanding of the seven predefined interfaces in PHP

藏色散人
藏色散人forward
2020-01-29 14:44:211830browse

In-depth understanding of the seven predefined interfaces in PHP

In-depth understanding of predefined interfaces

Scenario: In normal work, I write business modules, rarely To implement such an interface, it is used a lot in the framework.

1. Traversable interface

This interface cannot be directly implemented by a class. If you directly write a normal class to implement the traversal The interface will directly report a fatal error, prompting the use of Iterator (iterator interface) or IteratorAggregate (aggregation iterator interface) to implement. These two interfaces will be introduced later; under normal circumstances, we will only use it to judge the class Is it possible to use foreach to traverse?

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

The above roughly means that if you want to implement this interface, you must implement it with Iterator or IteratorAggregate

Correct approach:

When we want to determine whether a class can be traversed using foreach, we only need to determine whether it is an instance of traversable

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

2. Iterator interface

Iteration The actual implementation principle of the device interface is similar to the movement of pointers. When we write a class, we can implement the corresponding five methods: key(), current(), next(), rewind(), and valid(). To realize the iterative movement of data, please see the following code for details

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

        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 . &#39;:&#39; . $test->current() . PHP_EOL;
        $test->next();
    }

## The output result:      

        0: one
        1: two
        2: three

After looking at this principle, we know that the actual iterative movement method is: rewind()-> valid()->key() -> current() -> next() -> valid()-> key() ....-> valid();

Okay, after understanding the above, we open the Iterator interface and find that it implements the Traversable interface. Next, let’s prove it:

var_dump($test instanceOf Traversable);

The result returns true, proving that the object of this class is It can be traversed.

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

The result of this is the same as the pattern implemented by the while loop.

3. IteratorAggregate (aggregation iterator) interface

The principles of aggregate iterators and iterators are the same , but the aggregation iterator has already implemented the iterator principle. You only need to implement a getIterator() method to implement iteration. See the following code for details

<?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() . &#39; : &#39;  .  $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 . &#39; : &#39; . $value  .  PHP_EOL;
  }

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

 class Test implements IteratorAggregate
 {
    public $data;

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

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

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

Many PHPers are advanced when There are always some problems and bottlenecks. I write too much business code and have no sense of direction. I don’t know where to start to improve. I have compiled some information on this, including but not limited to: distributed architecture, high scalability, and high performance. , high concurrency, server performance tuning, TP6, laravel, YII2, Redis, Swoole, Swoft, Kafka, Mysql optimization, shell scripts, Docker, microservices, Nginx and many other knowledge points. If you need advanced knowledge, you can share it for free. Everyone, if you need to join the group (click →) 677079770

4. ArrayAccess (array access) interface

Normally, we will see this ['name '] Such usage, but we know that $this is an object, how to access it using array method? The answer is to implement the data group access interface ArrayAccess. The specific code is as follows

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

        public function __construct()
        {
            $this->container = [
                &#39;one&#39; => 1,
                &#39;two&#39; => 2,
                &#39;three&#39;  => 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[&#39;one&#39;]));
   var_dump($test[&#39;two&#39;]);
   unset($test[&#39;two&#39;]);
   var_dump(isset($test[&#39;two&#39;]));
   $test[&#39;two&#39;] = 22;
   var_dump($test[&#39;two&#39;]);
   $test[] = 4;
   var_dump($test);
   var_dump($test[0]);

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

   class Test implements ArrayAccess
   {
        public $name;

        public function __construct()
        {
            $this->name = &#39;gabe&#39;;  
        }

        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[&#39;name&#39;]));
  var_dump($test[&#39;name&#39;]);
  var_dump($test[&#39;age&#39;]);
  $test[1] = &#39;22&#39;;
  var_dump($test);
  unset($test[&#39;name&#39;]);
  var_dump(isset($test[&#39;name&#39;]));
  var_dump($test);
  $test[] = &#39;hello world&#39;;
  var_dump($test);

5. Serializable (serialization) interface

Normally, If magic methods, sleep() and wakeup () are defined in our class, when we serialize (), we will first call the magic method of sleep (). We return an array to define which properties of the object Properties are serialized. Similarly, when we call the unserialize () method, we will also call the wakeup () magic method first. We can initialize, such as assigning values ​​to the properties of an object; but if This class implements the serialization interface, so we must implement the serialize() method and unserialize () method. At the same time, the two magic methods sleep() and wakeup () will no longer be supported at the same time. The specific code is as follows;

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

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

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

        public function __sleep()
        {        
            var_dump(__METHOD__);
            return [&#39;name&#39;];    
        }
    }

    $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 = &#39;gabe&#39;;
        $this->age = 25;
    } 

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

    public function __sleep()
    {
        var_dump(__METHOD__);
        return [&#39;name&#39;];
    }

    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 class

is a class used to represent anonymous functions. All anonymous functions actually return an instance of the Closure closure class. This class There are two main methods, bindTo() and bind(). By looking at the source code, you can find that the two methods have the same goal, but bind() is a static method. The specific usage is as follows;

<?php
    $closure = function () {
        return &#39;hello world&#39;;
    }

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

Through the above example, you can see that the first one printed is an instance of Closure, and the second one prints out the hello world string returned by the anonymous function; the next step is to use the method of this anonymous class , the purpose of these two methods is to bind anonymous functions to a class;

bindTo()

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

    $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 = &#39;hello world&#39;;
    }

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

bind()

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

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

$func();

namespace demo2;
class Test 
{
    private static  $name = &#39;hello world&#39;;
}

$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 . &#39;: &#39; . $value . PHP_EOL;
}
echo &#39;memory: &#39; . memory_get_usage() . &#39; time: &#39;. (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 . &#39;: &#39; . $value . PHP_EOL;
}
echo &#39;memory: &#39; . memory_get_usage() . &#39; time: &#39;. (microtime(true) - $start_time);

输出:

memory: 6680312 time: 0.10804104804993

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

The above is the detailed content of In-depth understanding of the seven predefined interfaces in PHP. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete