SPL标准库专题10DatastructuresSplObjectStorage

PHP SPL SplObjectStorage是用来存储一组对象的,特别是当你需要唯一标识对象的时候。PHP SPL SplObjectStorage类实现了Countable,Iterator,Serializable,ArrayAccess四个接口。可实现统计、迭代、序列化、数组式访问等功能。 类摘要SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess { /* 方法 */ public void addAll ( SplObjectStorage $storage ) // ↓↓加入对象 public void attach ( object $object [, mixed $data = NULL ] ) // ↓↓检查是否包含指定对象 public bool contains ( object $object ) // ↓↓移除对象 public void detach ( object $object ) // ↓↓返回一串哈希值,每次调用的时候该串哈希值都在改变 public string getHash ( object $object ) public mixed getInfo ( void ) public int count ( void ) public object current ( void ) public int key ( void ) public void next ( void ) public bool offsetExists ( object $object ) public mixed offsetGet ( object $object ) public void offsetSet ( object $object [, mixed $data = NULL ] ) public void offsetUnset ( object $object ) public void removeAll ( SplObjectStorage $storage ) public void removeAllExcept ( SplObjectStorage $storage ) public void rewind ( void ) public string serialize ( void ) public void setInfo ( mixed $data ) public void unserialize ( string $serialized ) public bool valid ( void )}Example# Example1:class A { public $i; public function __construct($i) { $this->i = $i; }}$a1 = new A(1);$a2 = new A(2);$a3 = new A(3);$a4 = new A(4);$container = new SplObjectStorage();//SplObjectStorage::attach 添加对象到Storage中$container->attach($a1);$container->attach($a2);$container->attach($a3);//SplObjectStorage::detach 将对象从Storage中移除$container->detach($a2);//SplObjectStorage::contains用于检查对象是否存在Storage中var_dump($container->contains($a1)); //truevar_dump($container->contains($a4)); //false//遍历$container->rewind();while($container->valid()) { var_dump($container->current()); var_dump($container->getInfo()); $container->next();}参考http://php.net/manual/zh/clas... ...

May 25, 2019 · 1 min · jiezi

SPL标准库专题9DatastructuresSplFixedArray

SplFixedArray主要是处理数组相关的主要功能,与普通php array不同的是,它是固定长度的,且以数字为键名的数组,优势就是比普通的数组处理更快。 类摘要SplFixedArray implements Iterator , ArrayAccess , Countable { /* 方法 */ public __construct ([ int $size = 0 ] ) public int count ( void ) public mixed current ( void ) //↓↓导入PHP数组,返回SplFixedArray对象; public static SplFixedArray fromArray ( array $array [, bool $save_indexes = true ] ) //↓↓把SplFixedArray对象数组导出为真正的数组; public array toArray ( void ) public int getSize ( void ) public int key ( void ) public void next ( void ) public bool offsetExists ( int $index ) public mixed offsetGet ( int $index ) public void offsetSet ( int $index , mixed $newval ) public void offsetUnset ( int $index ) public void rewind ( void ) public int setSize ( int $size ) public bool valid ( void ) public void __wakeup ( void )}Example# Example1:$arr = new SplFixedArray(4);try{ $arr[0] = 'php'; $arr[1] = 'Java'; $arr[3] = 'javascript'; $arr[5] = 'mysql';}catch(RuntimeException $e){ //由于是定长数组,所以$arr超过定长4;就会抛出异常。 echo $e->getMessage(),' : ',$e->getCode();}#Example2:// public static SplFixedArray fromArray ( array $array [, bool $save_indexes = true ] )$arr = [ '4' => 'php', '5' => 'javascript', '0' => 'node.js', '2' => 'linux'];//第二参数默认为true的话,保持原索引,如果为false的话,就重组索引;//如下,如果重组了索引,那数组长度就为4;如果不重组长度就为6;$arrObj = SplFixedArray::fromArray($arr);$arrObj->rewind();while($arrObj->valid()){ echo $arrObj->key(),'=>',$arrObj->current(); echo PHP_EOL; $arrObj->next();}//↓↓由定长数组对象转换为真正的数组$arr = $arrObj->toArray();print_r($arr);

May 24, 2019 · 1 min · jiezi

SPL标准库专题8DatastructuresSplHeap-SplMaxHeap-SplMinHeap

堆(Heap)就是为了实现优先队列而设计的一种数据结构,它是通过构造二叉堆(二叉树的一种)实现。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。二叉堆还常用于排序(堆排序)。 类摘要abstract SplHeap implements Iterator , Countable { /* 方法 */public __construct ( void )abstract protected int compare ( mixed $value1 , mixed $value2 )public int count ( void )public mixed current ( void )public mixed extract ( void )public void insert ( mixed $value )public bool isEmpty ( void )public mixed key ( void )public void next ( void )public void recoverFromCorruption ( void )public void rewind ( void )public mixed top ( void )public bool valid ( void )}从上面可以看到由于类中包含一个compare的抽象方法,所以该类必须为抽象类(不可实例化,只能被继承使用); ...

May 23, 2019 · 1 min · jiezi

SPL标准库专题7DatastructuresSplPriorityQueue

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头取出。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先取出。优先队列具有最高级先出 (largest-in,first-out)的行为特征。总结下来就是普通队列有先进先出原则,优先级队列有优先级高先出原则,这个优先级可以设置; 类摘要// 1. 没有实现ArrayAccess接口,所以不能像数组那样操作;SplPriorityQueue implements Iterator , Countable { /* 方法 */ public __construct ( void ) // 比较方法,内部应该用到了冒泡排序,对于权重值来说,返回0代表相等,返回正整数就代表大于,返回负整数就代表小于; // 默认是权重值越优先,也可以让其被子类覆盖改为权重值越小越优先 public int compare ( mixed $priority1 , mixed $priority2 ) public mixed extract ( void ) //恢复到上一个被破坏的节点? 测试无用; public void recoverFromCorruption ( void ) public void setExtractFlags ( int $flags ) public void insert ( mixed $value , mixed $priority ) public int count ( void ) public mixed current ( void ) public bool isEmpty ( void ) public mixed key ( void ) public void next ( void ) public void rewind ( void ) public mixed top ( void ) public bool valid ( void )}Exampleclass PQtest extends SplPriorityQueue{ //覆盖父类,更改其优先规则为权重值越小越优先; public function compare($priority1, $priority2) { if ($priority1 === $priority2) return 0; return $priority1 > $priority2 ? -1 : 1; }}$pq = new PQtest();// 设置值与优先值$pq->insert('a', 10);$pq->insert('b', 1);$pq->insert('c', 8);/** * 设置元素出队模式 * SplPriorityQueue::EXTR_DATA 仅提取值 * SplPriorityQueue::EXTR_PRIORITY 仅提取优先级 * SplPriorityQueue::EXTR_BOTH 提取数组包含值和优先级 */$pq->setExtractFlags(PQtest::EXTR_BOTH);//从顶部取出一个节点,该节点下面的节点移上为顶部节点;print_r( $pq->extract());/* [data] => b [priority] => 1 */$pq->recoverFromCorruption();//拿出顶部节点print_r( $pq->extract());/* [data] => c [priority] => 8 */// 还原自上一个节点? 没什么效果?$pq->recoverFromCorruption();print_r( $pq->current());$pq->rewind();while($pq->valid()){ print_r($pq->current()); echo PHP_EOL; $pq -> next();}

May 22, 2019 · 1 min · jiezi

SPL标准库专题6DatastructuresSplStack-SplQueue

这两个类都是继承自SplDoublyLinkedList,分别派生自SplDoublyLinkedList的堆栈模式和队列模式;所以放在一起来介绍; 堆栈SplStack # 类摘要SplStack extends SplDoublyLinkedList implements Iterator , ArrayAccess , Countable { /* 方法 */ __construct(void) // 重写了父类SplDoublyLinkedList,固定为堆栈模式,然后此处只需要传IT_MODE_DELETE或者IT_MODE_KEEP。 void setIteratorMode(int $mode ) /* 继承自SplDoublyLinkedList的方法 */ ... }//把栈想象成一个颠倒的数组$stack = new SplStack();/** * 可见栈和双链表的区别就是IteratorMode改变了而已,栈的IteratorMode只能为: * (1)SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP (默认值,迭代后数据保存) * (2)SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_DELETE (迭代后数据删除) */$stack->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_DELETE);$stack->push('a');$stack->push('b');$stack->push('c');$stack->offsetSet(0, 'first');//index 为0的是最后一个元素,后入后出$stack->pop(); //出栈foreach($stack as $item) { echo $item . PHP_EOL; // first a}print_R($stack); //测试IteratorMode队列SplQueue # 类摘要 SplQueue extends SplDoublyLinkedList implements Iterator , ArrayAccess , Countable { /* 方法 */ __construct ( void ) // 出队 mixed dequeue ( void ) // 入队 void enqueue ( mixed $value ) // 重写了父类SplDoublyLinkedList,固定为堆栈模式,然后此处只需要传IT_MODE_DELETE或者IT_MODE_KEEP。 void setIteratorMode ( int $mode ) //其他继承的方法 }$q = new SplQueue();$q->setIteratorMode(SplQueue::IT_MODE_DELETE);//可以放任何数据类型到队列里面$q->enqueue('item1');//每次放入都是只占一个队列的位置$q->enqueue(array("FooBar", "foo"));$q->enqueue(new stdClass());$q->rewind();while($q->valid()){ print_r($q->current()); echo "\n"; $q->next();}// 出队,先入先出,因为队列为空,所以此处报错;$q->dequeue();

May 21, 2019 · 1 min · jiezi

SPL标准库专题4Exceptions

嵌套异常了解SPL异常之前,我们先了解一下嵌套异常。嵌套异常顾名思义就是异常里面再嵌套异常,一个异常抛出,在catch到以后再抛出异常,这时可以通过Exception基类的getPrevious方法可以获得嵌套异常; <?phpclass DBException extends Exception{}class Database{ /** * @var PDO object setup during construction */ protected $_pdoResource = null; public function executeQuery($sql) { try { throw new PDOException('PDO error'); } catch (PDOException $e) { throw new DBException('Query was unexecutable', null, $e); } return $numRows; }}try { $sql = 'select * from user'; $db = new Database('PDO', $connectionParams); $db->executeQuery($sql);} catch (DBException $e) { echo 'General Error: ' . $e->getMessage() . "\n"; // 调用被捕获异常的getPrevious()获得嵌套异常 $pdoException = $e->getPrevious(); echo 'PDO Specific error: ' . $pdoException->getMessage() . "\n";}SPL异常简要的说一下SPL异常的优点: ...

May 19, 2019 · 2 min · jiezi

SPL标准库专题3Classes

我把SPL分为五个部分:Iterator,Classes,Exceptions,Datastructures,Function;而其中classes是就是做一些类的介绍(Iterator与Datastructures相关的类在各自文章内),在介绍这些类之前,先介绍几个接口: ArrayAccess(数组式访问)接口http://php.net/manual/zh/clas... 只要实现了这个接口,就可以使得object像array那样操作。ArrayAccess界面包含四个必须部署的方法,这四个方法分别传入的是array的key和value,: * offsetExists($offset)This method is used to tell php if there is a value for the key specified by offset. It should return true or false.检查一个偏移位置是否存在* offsetGet($offset)This method is used to return the value specified by the key offset.获取一个偏移位置的值* offsetSet($offset, $value)This method is used to set a value within the object, you can throw an exception from this function for a read-only collection. 获取一个偏移位置的值* offsetUnset($offset)This method is used when a value is removed from an array either through unset() or assigning the key a value of null. In the case of numerical arrays, this offset should not be deleted and the array should not be reindexed unless that is specifically the behavior you want. 复位一个偏移位置的值/** * A class that can be used like an array */class Article implements ArrayAccess{ public $title; public $author; public $category; function __construct($title , $author , $category){ $this->title = $title; $this->author = $author; $this->category = $category; } /** * Defined by ArrayAccess interface * Set a value given it's key e.g. $A['title'] = 'foo'; * @param mixed key (string or integer) * @param mixed value * @return void */ function offsetSet($key , $value){ if (array_key_exists($key , get_object_vars($this))) { $this->{$key} = $value; } } /** * Defined by ArrayAccess interface * Return a value given it's key e.g. echo $A['title']; * @param mixed key (string or integer) * @return mixed value */ function offsetGet($key){ if (array_key_exists($key , get_object_vars($this))) { return $this->{$key}; } } /** * Defined by ArrayAccess interface * Unset a value by it's key e.g. unset($A['title']); * @param mixed key (string or integer) * @return void */ function offsetUnset($key){ if (array_key_exists($key , get_object_vars($this))) { unset($this->{$key}); } } /** * Defined by ArrayAccess interface * Check value exists, given it's key e.g. isset($A['title']) * @param mixed key (string or integer) * @return boolean */ function offsetExists($offset){ return array_key_exists($offset , get_object_vars($this)); }}// Create the object$A = new Article('SPL Rocks','Joe Bloggs', 'PHP');// Check what it looks likeecho 'Initial State:<div>';print_r($A);echo '</div>';// Change the title using array syntax$A['title'] = 'SPL _really_ rocks';// Try setting a non existent property (ignored)$A['not found'] = 1;// Unset the author fieldunset($A['author']);// Check what it looks like againecho 'Final State:<div>';print_r($A);echo '</div>';Serializable接口接口摘要 ...

May 18, 2019 · 4 min · jiezi

SPL标准库专题1SPL简介

什么是SPLSPL是Standard PHP Library(PHP标准库)的缩写。 根据官方定义,它是"a collection of interfaces and classes that are meant to solve standard problems" SPL是用于解决典型问题(standard problems)的一组接口与类的集合。 但是,目前在使用中,SPL更多地被看作是一种使object(物体)模仿array(数组)行为的interfaces和classes。SPL的核心概念就是Iterator。 在我的理解中,SPL以及后面要说的设计模式专题都是用于同一个目的: 构建优雅简洁易于扩展和维护的代码,有时候我们看上去写了更多的代码,但事实上却让代码的扩展性和维护性变得更强。 另外本专题属于PHP进阶课程。在本专题中给出的一些Example,看上去是有更简单的替代方案,但在实际上在更复杂的开发中,看似更多的代码却使得程序的可插拔性,可维护性变得更强,SPL以及设计模式都算是面向对象中的精髓了,所以面向对象的基础一定要掌握得非常牢才更能理解;Iterator迭代器有时又称光标(cursor)是程式设计的软件设计模式,可在容器物件(container,例如list或vector)上遍访的接口,设计人员无需关心容器物件的内容。PHP5开始支持了接口, 并且内置了Iterator接口, 所以如果你定义了一个类,并实现了Iterator接口,那么你的这个类对象就是ZEND_ITER_OBJECT,否则就是ZEND_ITER_PLAIN_OBJECT.对于ZEND_ITER_PLAIN_OBJECT的类,foreach会通过HASH_OF获取该对象的默认属性数组,然后对该数组进行foreach。而对于ZEND_ITER_OBJECT的类对象,则会通过调用对象实现的Iterator接口相关函数来进行foreach。通俗地说,Iterator能够使许多不同的数据结构,都能有统一的操作界面,比如一个数据库的结果集、同一个目录中的文件集、或者一个文本中每一行构成的集合。 如果按照普通情况,遍历一个MySQL的结果集,程序需要这样写: // Fetch the "aggregate structure"$result = mysql_query("SELECT * FROM users");// Iterate over the structurewhile ( $row = mysql_fetch_array($result) ) { // do stuff with the row here}读出一个目录中的内容,需要这样写: // Fetch the "aggregate structure"$dh = opendir('/home/harryf/files');// Iterate over the structurewhile ( $file = readdir($dh) ) { // do stuff with the file here}读出一个文本文件的内容,需要这样写: ...

May 16, 2019 · 1 min · jiezi

PHP 优先级队列:SplPriorityQueue

PHP 的 SPL 库内置了 SplPriorityQueue优先级队列,是以Heap堆特性实现的,默认为MaxHeap模式,即priority越大越优先出队,同时可以通过重写compare方法来使用MinHeap(优先级越低越优先出队,场景貌似很少吧)。SplPriorityQueue堆特性这里需要注意并理解:SplPriorityQueue是以堆数据结构来实现的,当我们出队时会拿出堆顶的元素,此时堆的特性被破坏,堆会进行相应的调整至稳定态(MaxHeap or MinHeap),即会将最后一个元素替换到堆顶,然后进行稳定态验证,不符合堆特性则继续调整,或者我们就得到了一个稳定态的堆,所以当优先级相同,出队顺序并不会按照入队顺序。源码示例:<?php$splPriorityQueue = new \SplPriorityQueue();// 设定返回数据的meta信息// \SplPriorityQueue::EXTR_DATA 默认 只返回数// \SplPriorityQueue::EXTR_PRIORITY 只返回优先级// \SplPriorityQueue::EXTR_BOTH 返回数据和优先级// $splPriorityQueue->setExtractFlags(\SplPriorityQueue::EXTR_DATA);$splPriorityQueue->insert(“task1”, 1);$splPriorityQueue->insert(“task2”, 1);$splPriorityQueue->insert(“task3”, 1);$splPriorityQueue->insert(“task4”, 1);$splPriorityQueue->insert(“task5”, 1);echo $splPriorityQueue->extract() . PHP_EOL;echo $splPriorityQueue->extract() . PHP_EOL;echo $splPriorityQueue->extract() . PHP_EOL;echo $splPriorityQueue->extract() . PHP_EOL;echo $splPriorityQueue->extract() . PHP_EOL;//执行结果task1task5task4task3task2可以看到,虽然 5 个任务的优先级相同,但队列并没有按照入队顺序返回数据,因为堆的特性使然:1、入队 task1, task2, task3, task4, task5,因为优先级相同,所以堆一直处于稳定态。2、出队,得 task1,堆先将结构调整为 task5, task2, task3, task4,已然达到了稳定态。3、出队,得 task5,堆先将结构调整为 task4, task2, task3,已然达到了稳定态。4、出队,得 task4,堆先将结构调整为 task3, task2,已然达到了稳定态。5、出队,得 task3,堆先将结构调整为 task2,已然达到了稳定态。4、出队,得 task2。Iterator, CountableSplPriorityQueue实现了 Iterator, Countable接口,所以我们可以foreach/count函数操作它,或者使用rewind,valid,current,next/count方法。注意,因为是堆实现,所以rewind方法是一个no-op没有什作用的操作,因为头指针始终指向堆顶,即current始终等于top,不像List只是游走指针,出队是会删除堆元素的,extract = current + next(current出队,从堆中删除)。<?php$splPriorityQueue = new \SplPriorityQueue();$splPriorityQueue->insert(“task1”, 1);$splPriorityQueue->insert(“task2”, 2);$splPriorityQueue->insert(“task3”, 1);$splPriorityQueue->insert(“task4”, 4);$splPriorityQueue->insert(“task5”, 5);echo “Countable: " . count($splPriorityQueue) . PHP_EOL;// 迭代的话会删除队列元素 current 指针始终指向 top 所以 rewind 没什么意义for ($splPriorityQueue->rewind(); $splPriorityQueue->valid();$splPriorityQueue->next()) { var_dump($splPriorityQueue->current()); var_dump($splPriorityQueue->count()); $splPriorityQueue->rewind();}var_dump(“is empty:” . $splPriorityQueue->isEmpty());Extract出队extract 出队更为友好,即始终返回优先级最高的元素,优先级相投时会以堆调整的特性返回数据。<?php$splPriorityQueue = new \SplPriorityQueue();// data priority$splPriorityQueue->insert(“task1”, 1);$splPriorityQueue->insert(“task2”, 2);$splPriorityQueue->insert(“task3”, 1);$splPriorityQueue->insert(“task4”, 4);$splPriorityQueue->insert(“task5”, 5);echo “Countable: " . count($splPriorityQueue) . PHP_EOL;while (! $splPriorityQueue->isEmpty()) { var_dump($splPriorityQueue->extract()); echo $splPriorityQueue->count() . PHP_EOL;}自定义优先级处理方式重写compare方法定义自己的优先级处理机制。<?phpclass CustomedSplPriorityQueue extends SplPriorityQueue{ public function compare($priority1, $priority2): int { // return $priority1 - $priority2;//高优先级优先 return $priority2 - $priority1;//低优先级优先 }}$splPriorityQueue = new \CustomedSplPriorityQueue();$splPriorityQueue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);$splPriorityQueue->insert(“task1”, 1);$splPriorityQueue->insert(“task2”, 2);$splPriorityQueue->insert(“task3”, 1);$splPriorityQueue->insert(“task4”, 4);$splPriorityQueue->insert(“task5”, 5); echo “Countable: " . count($splPriorityQueue) . PHP_EOL;while (!$splPriorityQueue->isEmpty()) { var_dump($splPriorityQueue->extract());} ...

March 25, 2019 · 1 min · jiezi