Compréhension approfondie des interfaces prédéfinies
Scénario : Dans le travail normal, j'écris des modules métier, rarement Pour implémenter une telle interface, elle est beaucoup utilisée dans le framework.
1. Interface traversable
Cette interface ne peut pas être directement implémentée par une classe si vous écrivez directement une classe normale pour implémenter la traversée. L'interface signalera directement une erreur fatale, incitant à implémenter Iterator (interface itérateur) ou IteratorAggregate (interface itérateur d'agrégation). Ces deux interfaces seront présentées plus tard, dans des circonstances normales, nous ne les utilisons que pour juger la classe. utiliser foreach pour le parcours ?
class Test implements Traversable { } 上面这个是错误示范,该代码会提示这样的错误: Fatal error: Class Test must implement interface Traversable as part of either Iterator or IteratorAggregate in Unknown on line 0
La signification générale de ce qui précède est que si vous souhaitez implémenter cette interface, vous devez l'implémenter avec Iterator ou IteratorAggregate
L'approche correcte :
Lorsque nous voulons déterminer si une classe peut être parcourue à l'aide de foreach, il nous suffit de déterminer s'il s'agit d'une instance de traversable
class Test { } $test = new Test; var_dump($test instanceOf Traversable);
2. 🎜>Itération Le principe d'implémentation actuel de l'interface du périphérique est similaire au mouvement des pointeurs. Lorsque nous écrivons une classe, nous pouvons implémenter les cinq méthodes correspondantes : key(), current(), next(), rewind() et. valid(). Pour réaliser le mouvement itératif des données, regardez le code suivant
<?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
var_dump($test instanceOf Traversable);
foreach ($test as $key => $value){ echo $test->key . ':' . $test->current() . PHP_EOL; }
3. Interface IteratorAggregate (itérateur d'agrégation)
Les principes des itérateurs d'agrégation et des itérateurs sont les mêmes, mais l'itérateur agrégé a déjà implémenté le principe de l'itérateur. Il vous suffit d'implémenter une méthode getIterator() pour implémenter l'itération. Pour plus de détails, voir le code suivant
<?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); } } 同理实现的方式跟对对象进行迭代是一样的。
4. Interface ArrayAccess (accès au tableau)
Normalement, nous verra ceci ['name '] Une telle utilisation, mais nous savons que $this est un objet, comment y accéder en utilisant la méthode array ? La réponse est d'implémenter l'interface d'accès au groupe de données ArrayAccess. Le code spécifique est le suivant
<?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);
Interface sérialisable (sérialisation)
. Normalement, si les méthodes magiques, sleep() et wakeup() sont définies dans notre classe, lorsque nous sérialisons (), nous appellerons d'abord la méthode magique de sleep(). Nous renvoyons un tableau pour définir quelles sont les propriétés de l'objet. sérialisé. De même, lorsque nous appelons la méthode unserialize (), nous appellerons également d'abord la méthode magique wakeup (). Nous pouvons initialiser, comme attribuer des valeurs aux propriétés d'un objet, mais si cette classe implémente l'interface de sérialisation ; , nous devons donc implémenter la méthode serialize() et la méthode unserialize() en même temps, les deux méthodes magiques sleep() et wakeup() ne seront plus prises en charge en même temps. Le code spécifique est le suivant : 🎜><?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));
est une classe utilisée pour représenter des fonctions anonymes. Toutes les fonctions anonymes renvoient en fait une instance de la classe de fermeture Closure. Cette classe Il existe deux méthodes principales, bindTo() et bind() En regardant le code source, vous pouvez constater que les deux méthodes ont le même objectif, mais bind() est une méthode statique. L'utilisation spécifique est la suivante. ; <?php
$closure = function () {
return 'hello world';
}
var_dump($closure);
var_dump($closure());
Grâce à l'exemple ci-dessus, vous pouvez voir que le premier imprimé est une instance de Closure, et le second imprime la chaîne hello world renvoyée par la fonction anonyme ; l'étape suivante consiste à utiliser la méthode de cette classe anonyme, le but de ces deux méthodes est de lier des fonctions anonymes à une classe
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. Générateur
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教程!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!