Dark Corners Of The SPL - Midwest PHP 2019

Dark Corners Of The SPL - Midwest PHP 2019

PHP is a huge language, with lots of "kitchen sink" functionality for you to build data structures with. But did you know PHP includes a standard library that has built-in structures like linked lists, queues, stacks, and higher-performance arrays? This talk will cover a few of the more interesting ones in depth, including how to use them and most importantly why you'd use them over other solutions.

3d55a954b25a01f3f895eb20f7c48714?s=128

Omni Adams

March 08, 2019
Tweet

Transcript

  1. Dark Corners of the SPL Photo By: SSG Ryan Boas

    Midwest PHP 2019
  2. None
  3. None
  4. Photo By: Tom Walsh

  5. Photo By: NASA Goddard Space Flight Center

  6. Data Structures SplDoublyLinkedList SplStack SplQueue SplHeap SplMaxHeap SplMinHeap SplPriorityQueue SplFixedArray

    SplObjectStorage https://secure.php.net/manual/en/spl.datastructures.php
  7. Big-O O(1) - Constant no matter how big your data

    O(log n) - Grows slowly with your data O(n) - Grows linearly with your data O(n2) - Grows quadratically with your data O(2n) - Grows exponentially with your data O(n!) - LOL https://en.wikipedia.org/wiki/Big_O
  8. Photo By: Cmglee

  9. Performance SPL version versus simple array 1,000,000 random elements Sorting,

    iterating, or both Time and memory usage
  10. Data Structures SplDoublyLinkedList SplStack SplQueue SplPriorityQueue SplFixedArray SplObjectStorage SplHeap SplMaxHeap

    SplMinHeap
  11. Image By: Bhavikp19

  12. SplDoublyLinkedList Access: O(n) Search: O(n) Insert: O(1) Delete: O(1)

  13. $list = new SplDoublyLinkedList(); $list->push('Ash'); $list->push('Bingo'); $list->push('Charlie'); $list->push('Echo'); foreach ($list

    as $key => $character) { echo $key, ' ', $character, PHP_EOL; } echo PHP_EOL; https://github.com/omnicolor/dark-corners-of-the-spl/blob/master/SplDoublyLinkedList.php
  14. $list->push('Echo'); foreach ($list as $key => $character) { echo $key,

    ' ', $character, PHP_EOL; } echo PHP_EOL; 0 Ash 1 Bingo 2 Charlie 3 Echo
  15. 0 Ash 1 Bingo 2 Charlie 3 Echo $list->add(3, 'Delta');

    foreach ($list as $key => $character) { echo $key, ' ', $character, PHP_EOL; } echo PHP_EOL;
  16. foreach ($list as $key => $character) { echo $key, '

    ', $character, PHP_EOL; } echo PHP_EOL; 0 Ash 1 Bingo 2 Charlie 3 Delta 4 Echo
  17. 2 Charlie 3 Delta 4 Echo $list->rewind(); echo sprintf( "It's

    %s's turn! They take out %s!", $list->current(), $list[2] ), PHP_EOL; $list->offsetUnset(2); foreach ($list as $key => $character) { echo $key, ' ', $character, PHP_EOL; } echo PHP_EOL;
  18. foreach ($list as $key => $character) { echo $key, '

    ', $character, PHP_EOL; } echo PHP_EOL; It's Ash's turn! They take out Charlie! 0 Ash 1 Bingo 2 Delta 3 Echo
  19. Data Structures SplDoublyLinkedList SplStack SplQueue SplPriorityQueue SplFixedArray SplObjectStorage SplHeap SplMaxHeap

    SplMinHeap
  20. SplStack performance $startTime = microtime(true); $startSize = memory_get_usage(); $arrayStack =

    []; for ($i = 0; $i < $numItems; $i++) { array_push($arrayStack, mt_rand()); } $peakSize = memory_get_usage(); while (!empty($arrayStack)) { echo array_pop($arrayStack), ' '; } echo PHP_EOL; $startTime = microtime(true); $startSize = memory_get_usage(); $stack = new SplStack(); for ($i = 0; $i < $numItems; $i++) { $stack->push(mt_rand()); } $peakSize = memory_get_usage(); while (!$stack->isEmpty()) { echo $stack->pop(), ' '; } echo PHP_EOL; https://github.com/omnicolor/dark-corners-of-the-spl/blob/master/SplStackTest.php
  21. SplStack performance Array: 2.78 seconds 32MiB Stack: 2.79 seconds 38MiB

  22. Data Structures SplDoublyLinkedList SplStack SplQueue SplPriorityQueue SplFixedArray SplObjectStorage SplHeap SplMaxHeap

    SplMinHeap
  23. Image By: Ermishin

  24. SplHeap Access: O(log n) Search: O(log n) Insert: O(log n)

    Delete: O(log n)
  25. class Character { public $name; public $initiative; public function __construct(

    string $name, int $init ) { $this->name = $name; $this->initiative = $init; } } https://github.com/omnicolor/dark-corners-of-the-spl/blob/master/SplHeap.php
  26. class InitiativeHeap extends SplMaxHeap { public function compare($a, $b) :

    int { return $a->initiative - $b->initiative; } }
  27. $init = new InitiativeHeap(); $init->insert(new Character('Ash', random_int(1, 30))); $init->insert(new Character('Bingo',

    random_int(1, 30))); $init->insert(new Character('Charlie', random_int(1, 30))); $init->insert(new Character('Echo', random_int(1, 30))); foreach ($init as $character) { echo sprintf( '%s %d', $character->name, $character->initiative ), PHP_EOL; }
  28. foreach ($init as $character) { echo sprintf( '%s %d', $character->name,

    $character->initiative ), PHP_EOL; } Ash 30 Echo 27 Charlie 16 Bingo 12
  29. SplHeap Array: 2.84 seconds 32MiB Heap: 5.20 seconds 16MiB https://github.com/omnicolor/dark-corners-of-the-spl/blob/master/SplHeapTest.php

  30. Data Structures SplDoublyLinkedList SplStack SplQueue SplHeap SplMaxHeap SplMinHeap SplPriorityQueue SplFixedArray

    SplObjectStorage
  31. SplFixedArray Access: O(1) Search: O(n) Insert: O(1) or O(n) Delete:

    O(n)
  32. $a = [ 'foo' => 1, 'bar' => new StdClass(),

    1 => null, false => true, ]; var_dump($a); array(3) { [“foo"]=>int(1) [“bar"]=>object(stdClass)#1 (0) {} [1]=>NULL [0]=>bool(true) } $b = [ 0 => 10, 2 => 22, PHP_INT_MAX => PHP_INT_MAX, ]; var_dump($b); array(3) { [0]=>int(10) [2]=>int(22) [9223372036854775807]=> int(9223372036854775807) } https://github.com/omnicolor/dark-corners-of-the-spl/blob/master/SplFixedArray.php
  33. $c = new SplFixedArray(3); $c[0] = 10; $c[1] = 20;

    $c[2] = 42; var_dump($c); object(SplFixedArray)#2 (3) { [0]=>int(10) [1]=>int(20) [2]=>int(42) }
  34. $d = new SplFixedArray(3); $d[0] = 10; $d[1] = 20;

    $d[2] = 42; $d[3] = 99; var_dump($d); PHP Fatal error: Uncaught RuntimeException: Index invalid or out of range in SplFixedArray.php:33 Stack trace: #0 {main} thrown in SplFixedArray.php on line 33
  35. $e = new SplFixedArray(3); $e[0] = 10; $e[1] = 20;

    $e[2] = 42; $e->setSize(4); $e[3] = 99; var_dump($e); object(SplFixedArray)#5 (4) { [0]=>int(10) [1]=>int(20) [2]=>int(42) [3]=>int(99) }
  36. SplFixedArray Array: 2.58 seconds 32MiB Fixed: 2.67 seconds 15MiB

  37. Data Structures SplDoublyLinkedList SplStack SplQueue SplHeap SplMaxHeap SplMinHeap SplPriorityQueue SplFixedArray

    SplObjectStorage
  38. Data Structures SplDoublyLinkedList SplStack SplQueue SplHeap SplMaxHeap SplMinHeap SplPriorityQueue SplFixedArray

    SplObjectStorage
  39. $char = new Character(); $storage = []; $storage[$char] = [‘alive’

    => true]; Warning: Illegal offset type in SplObjectStorage.php on line 3
  40. class Character {} class Enemy {} class Buff {} class

    Ailment {}
  41. class Buff {} class Ailment {} $fighter = new Character();

    $mage = new Character(); $goblin = new Enemy(); $ogre = new Enemy(); $haste = new Buff(); $poison = new Ailment();
  42. $encounter = new SplObjectStorage(); $encounter->attach($fighter, [$haste]); $encounter->attach($mage, [$haste, $poison]); $encounter[$goblin]

    = [$poison]; $encounter->attach($ogre); foreach ($encounter as $key => $value) { print_r($key); echo PHP_EOL; print_r($value); } $poison = new Ailment();
  43. foreach ($encounter as $key => $value) { print_r($key); echo PHP_EOL;

    print_r($value); } 0 Character Object() 1 Character Object() 2 Enemy Object() 3 Enemy Object()
  44. object(SplObjectStorage)#7 (1) { ["storage":"SplObjectStorage":private]=>array(4) { ["000000005460553e00000000545bda63"]=>array(2) { ["obj"]=>object(Character)#1 (0) {}

    ["inf"]=>array(1) { [0]=>object(Buff)#5 (0) {} } } } }
  45. foreach ($encounter as $key) { print_r($key); print_r($encounter[$key]); }

  46. print_r($encounter[$key]); } Character Object() // fighter Array( [0] => Buff

    Object() // haste ) Character Object() // mage Array( [0] => Buff Object() // haste [1] => Ailment Object() // poison ) Enemy Object() // goblin Array( [0] => Ailment Object() // poison ) Enemy Object()
  47. –Lady Gaga “I just am committed wholeheartedly to theatre with

    no intermission.”
  48. SPL Interfaces Countable OuterIterator RecursiveIterator SeekableIterator SplObserver SplSubject https://secure.php.net/manual/en/spl.interfaces.php

  49. Countable interface Countable { public function count() : int; }

  50. class Countable { public function count() : int { return

    42; } } $foo = new Countable(); echo count($foo), PHP_EOL;
  51. class Countable { public function count() : int { return

    42; } } $foo = new Countable(); echo count($foo), PHP_EOL; Warning: count(): Parameter must be an array or an object that implements Countable in Countable.php on line 13 1
  52. object that implements Countable in Countable.php on line 13 1

    class ActuallyCountable implements Countable { public function count() { return 42; } } $bar = new ActuallyCountable(); echo count($bar), PHP_EOL;
  53. SplSubject interface SplSubject { function attach(SplObserver $observer): void; function detach(SplObserver

    $observer): void; function notify(): void; }
  54. SplObserver interface SplObserver { function update(SplSubject $subject): void; }

  55. // ... public function attach(\SplObserver $observer): void { $this->observers->attach($observer); }

    class Character implements \SplSubject, \SplObserver { public $name; protected $observers; protected $message; public function __construct(string $name) { $this->name = $name; $this->observers = new \SplObjectStorage(); } // ...
  56. // ... public function update(\SplSubject $subject): void { // ...

    public function attach(\SplObserver $observer): void { $this->observers->attach($observer); } public function detach(\SplObserver $observer): void { $this->observers->detach($observer); } public function notify(): void { foreach ($this->observers as $observer) { $observer->update($this); } } // ... } // ...
  57. // ... public function sendMessage(string $message): void { $this->message =

    $message; $this->notify(); foreach ($this->observers as $observer) { $observer->update($this); } } // ... // ... public function update(\SplSubject $subject): void { echo sprintf( '%s received message: %s', $this->name, $subject->readMessage() ), PHP_EOL; } // ...
  58. $subject->readMessage() ), PHP_EOL; } // ... } $fighter = new

    Character('Fighter'); $mage = new Character('Mage'); $thief = new Character('Thief'); // ... public function sendMessage(string $message): void { $this->message = $message; $this->notify(); } public function readMessage(): string { return sprintf( '%s said "%s"', $this->name, $this->message ); } }
  59. return sprintf( '%s said "%s"', $this->name, $this->message ); } }

    } $fighter = new Character('Fighter'); $mage = new Character('Mage'); $thief = new Character('Thief'); $paladin = new Character('Paladin'); // Fighter, mage, and paladin listen to everyone else. $mage->attach($fighter); $thief->attach($fighter); $paladin->attach($fighter); $fighter->attach($mage); $thief->attach($mage); $paladin->attach($mage); $fighter->attach($paladin); $mage->attach($paladin); $thief->attach($paladin); $thief->attach($paladin);
  60. // Thief doesn't pay attention to anyone else, jerk. $paladin->sendMessage(

    ’Get ready for a fight. Mage, can you buff us?’ ); $mage->sendMessage('Sure, give me a second.'); $fighter->sendMessage('LEEEEROY JENKINS!'); $thief->sendMessage('Dangit Fighter!'); $thief->attach($mage); $paladin->attach($mage); $fighter->attach($paladin); $mage->attach($paladin);
  61. Fighter received message: Paladin said "Get ready for a fight.

    Mage, can you buff us?" Mage received message: Paladin said "Get ready for a fight. Mage, can you buff us?" Fighter received message: Mage said "Sure, give me a second." Paladin received message: Mage said "Sure, give me a second." Mage received message: Fighter said "LEEEEROY JENKINS!" Paladin received message: Fighter said "LEEEEROY JENKINS!" Fighter received message: Thief said "Dangit Fighter!" Mage received message: Thief said "Dangit Fighter!" Paladin received message: Thief said "Dangit Fighter!"
  62. SPL Exceptions BadFunctionCallException BadMethodCallException DomainException InvalidArgumentException LengthException LogicException OutOfBoundsException OutOfRangeException

    OverflowException RangeException RuntimeException UnderflowException UnexpectedValueException https://secure.php.net/manual/en/spl.exceptions.php
  63. SPL File Handling SplFileInfo SplFileObject SplTempFileObject https://secure.php.net/manual/en/spl.files.php

  64. ArrayObject

  65. Slides: https://bit.ly/2EFZsea https://twitter.com/omnicolor Omni Adams omni@digitaldarkness.com https://joind.in/talk/7e11a https://github.com/omnicolor/dark-corners-of-the-spl When I

    Work