La SPL au quotidien
@afup
@florianm__
@afup
@florianm__
La SPL au quotidien
en 2023*
@afup
@florianm__
Florian Merle
@florianm__
Florian-Merle
AKAWAKA
@afup
@florianm__
C'est quoi ?
@afup
@florianm__
@afup
@florianm__
Ça comprend quoi ?
- Structures de données
- Itérateurs
- Interfaces
- Exceptions
- Fonctionnalités diverses
@afup
@florianm__
@afup
@florianm__
🫶
Lecteur de musique
@afup
@florianm__
Lecteur de musique
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
}
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
return false;
}
return $this->playlist[$index];
}
}
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
return false;
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): bool {
if ($volumePercentage < 0 || $volumePercentage > 100) {
return false;
}
$this->volumePercentage = $volumePercentage;
return true;
}
}
@afup
@florianm__
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
return false;
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): bool {
if ($volumePercentage < 0 || $volumePercentage > 100) {
return false;
}
$this->volumePercentage = $volumePercentage;
return true;
}
}
<?php
// index.php
require 'player.php';
$player = new Player(
[
'Simulation Swarm',
'Flimsier',
// ...
],
80,
);
<?php
// index.php
require 'player.php';
$player = new Player(
[
'Simulation Swarm',
'Flimsier',
// ...
],
80,
);
var_dump($player->getSong(1));
// string(8) "Flimsier"
<?php
// index.php
require 'player.php';
$player = new Player(
[
'Simulation Swarm',
'Flimsier',
// ...
],
80,
);
var_dump($player->getSong(8));
// bool(false)
<?php
// index.php
require 'player.php';
$player = new Player(
[
'Simulation Swarm',
'Flimsier',
// ...
],
80,
);
var_dump($player->getSong(8));
// bool(false)
var_dump($player->setVolume(50));
// bool(true)
<?php
// index.php
require 'player.php';
$player = new Player(
[
'Simulation Swarm',
'Flimsier',
// ...
],
80,
);
var_dump($player->getSong(8));
// bool(false)
var_dump($player->setVolume(120));
// bool(false)
@afup
@florianm__
Lecteur de musique
Flimsier
King Krule
None
None
?
50 %
120 %
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
return false;
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): bool {
if ($volumePercentage < 0 || $volumePercentage > 100) {
return false;
}
$this->volumePercentage = $volumePercentage;
return true;
}
}
<?php
// exceptions.php
class SystemException extends Exception {
}
class UserException extends Exception {
}
<?php
// player.php
require 'exceptions.php'
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
throw new SystemException("Index out of range");
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): void {
if ($volumePercentage < 0 || $volumePercentage > 100) {
throw new UserException("Volume out of range");
}
$this->volumePercentage = $volumePercentage;
}
}
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
throw new RuntimeException("Index out of range");
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): void {
if ($volumePercentage < 0 || $volumePercentage > 100) {
throw new LogicException("Volume out of range");
}
$this->volumePercentage = $volumePercentage;
}
}
<?php
// player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
public function getSong(int $index): string|false {
if (!isset($this->playlist[$index])) {
throw new RangeException("Index out of range");
}
return $this->playlist[$index];
}
public function setVolume(int $volumePercentage): void {
if ($volumePercentage < 0 || $volumePercentage > 100) {
throw new OutOfRangeException("Volume out of range");
}
$this->volumePercentage = $volumePercentage;
}
}
@afup
@florianm__
Lecteur de musique
Hierachie
@afup
@florianm__
Lecteur de musique
<?php
// Player.php
class Player {
public function __construct(
public array $playlist = [],
public int $volumePercentage = 100,
) {}
// ...
}
<?php
// index.php
require 'player.php';
$player = new Player();
<?php
// index.php
spl_autoload_register(function ($class): void {
require $class . '.php';
});
$player = new Player();
@afup
@florianm__
- Exceptions
- Fonctionnalités diverses
- Structures de données
- Itérateurs
@afup
@florianm__
Structures de données
@afup
@florianm__
En informatique, une structure de données est une manière d'organiser les données pour les traiter plus facilement.
wikipedia
En informatique, une structure de données est une manière d'organiser les données pour les traiter plus facilement.
wikipedia
Structures de données
En informatique, une structure de données est une manière d'organiser les données pour les traiter plus facilement.
wikipedia
@afup
@florianm__
en C
Les tableaux en php
@afup
@florianm__
Les tableaux en php
@afup
@florianm__
Les tableaux en php
@afup
@florianm__
@afup
@florianm__
- Liste doublement chaînées (Doubly Linked Lists)
- Tas (Heaps)
- Tableaux (Arrays)
- Carte (Map)
D'autres structures de données ?
@afup
@florianm__
D'autres structures de données ?
- Liste doublement chaînées
- Tas
- Tableaux
- Carte
- SplDoublyLinkedList
- SplStack
- SplQueue
- SplHeap
- SplMaxHeap
- SplMinHeap
- SplPriorityQueue
- SplFixedArray
- SplObjectStorage
@afup
@florianm__
File d'attente
$list = new \SplDoublyLinkedList();
$list->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO);
$list->push('Galatea\'s Guitar');
$list->push('Is This It');
$list->push('The Dripping Tap');
$newSong = $list->shift(); // Galatea's Guitar
$newSong = $list->shift(); // Seaforth
var_dump($list); // [0 => 'The Dripping Tap']
@afup
@florianm__
File d'attente
$list = new \SplDoublyLinkedList();
$list->setIteratorMode(\SplDoublyLinkedList::IT_MODE_FIFO);
$list->push('Galatea\'s Guitar');
$list->push('Is This It');
$list->push('The Dripping Tap');
$newSong = $list->shift(); // Galatea's Guitar
$newSong = $list->shift(); // Seaforth
var_dump($list); // [0 => 'The Dripping Tap']
$queue = new \SplQueue();
$queue->enqueue('Galatea\'s Guitar');
$queue->enqueue('Is This It');
$queue->enqueue('The Dripping Tap');
$newSong = $queue->dequeue(); // Galatea's Guitar
$newSong = $queue->dequeue(); // Seaforth
var_dump($queue); // [0 => 'The Dripping Tap']
≡ |
@afup
@florianm__
Hit parade
$artists = [];
$artists[] = ['name' => 'King Krule', 'monthly_listeners' => 1_320_982];
$artists[] = ['name' => 'Big Thief', 'monthly_listeners' => 2_702_553];
$artists[] = ['name' => 'Kero Kero Bomito', 'monthly_listeners' => 1_182_732];
$hitParade = $artists;
usort($hitParade, fn (array $a, array $b): bool => $a['monthly_listeners'] < $b['monthly_listeners']);
foreach ($hitParade as $artist) {
// Big Thief
// King Krule
// Kero Kero Bomito
echo $artist['name'] . PHP_EOL;
}
class HitParade extends \SplHeap
{
public function compare(mixed $a, mixed $b): int
{
return $a['monthly_listeners'] < $b['monthly_listeners'] ? -1 : 1;
}
}
$hitParade = new HitParade();
$hitParade->insert(['name' => 'King Krule', 'monthly_listeners' => 1_320_982]);
$hitParade->insert(['name' => 'Big Thief', 'monthly_listeners' => 2_702_553]);
$hitParade->insert(['name' => 'Kero Kero Bomito', 'monthly_listeners' => 1_182_732]);
foreach ($hitParade as $artist) {
// Big Thief
// King Krule
// Kero Kero Bomito
echo $artist['name'] . PHP_EOL;
}
$hitParade = new SplPriorityQueue();
$hitParade->insert(['name' => 'King Krule'], 1_320_982);
$hitParade->insert(['name' => 'Big Thief'], 2_702_553);
$hitParade->insert(['name' => 'Kero Kero Bomito'], 1_182_732);
foreach ($hitParade as $artist) {
// Big Thief
// King Krule
// Kero Kero Bomito
echo $artist['name'] . PHP_EOL;
}
@afup
@florianm__
DX & Perfs
$queue = new \SplPriorityQueue();
$queue->insert('🚀', 1);
$queue->insert('🦘', 1);
$queue->insert('🥁', 1);
foreach ($queue as $v) {
echo $v . PHP_EOL;
}
@afup
@florianm__
$queue = new \SplPriorityQueue();
$queue->insert('🚀', 1);
$queue->insert('🦘', 1);
$queue->insert('🥁', 1);
foreach ($queue as $v) {
echo $v . PHP_EOL;
}
🚀
🥁
🦘
🚀
🦘
🥁
@afup
@florianm__
DX & Perfs
class SplDoublyLinkedList {
public shift(): mixed
public push(mixed $value): void
public add(int $index, mixed $value): void
public setIteratorMode(int $mode): int
// ...
}
@afup
@florianm__
DX & Perfs
class SplQueue extends SplDoublyLinkedList {
public dequeue(): mixed
public enqueue(mixed $value): void
public add(int $index, mixed $value): void
public setIteratorMode(int $mode): int
// ...
}
class SplDoublyLinkedList {
public shift(): mixed
public push(mixed $value): void
public add(int $index, mixed $value): void
public setIteratorMode(int $mode): int
// ...
}
@afup
@florianm__
DX & Perfs
@afup
@florianm__
DX & Perfs
@afup
@florianm__
DX & Perfs
@afup
@florianm__
DX & Perfs
@afup
@florianm__
DX & Perfs
$queue = new \SplPriorityQueue();
$queue->insert('🚀', 1);
$queue->insert('🦘', 1);
$queue->insert('🥁', 1);
foreach ($queue as $v) {
echo $v . PHP_EOL;
}
🚀
🥁
🦘
$queue = new Ds\PriorityQueue();
$queue->push('🚀', 1);
$queue->push('🦘', 1);
$queue->push('🥁', 1);
foreach ($queue as $v) {
echo $v . PHP_EOL;
}
🚀
🦘
🥁
PHP DS
@afup
@florianm__
- Vector
- Deque
- Queue
- Stack
- Set
- PriorityQueue
- Map
@afup
@florianm__
PHP DS
- Vector
- Deque
- Queue
- Stack
- Set
- PriorityQueue
- Map
@afup
@florianm__
PHP DS
- Exceptions
- Fonctionnalités diverses
- Structures de données
- Itérateurs
@afup
@florianm__
Iterateurs
C'est quoi ?
interface Iterator extends Traversable {
public current(): mixed
public key(): mixed
public next(): void
public rewind(): void
public valid(): bool
}
$obj->rewind();
while ($obj->valid()) {
$key = $obj->key();
$val = $obj->current();
// stuff
$obj->next();
}
foreach ($obj as $key => $val) {
// stuff
}
≡ |
@afup
@florianm__
FibonacciIterator
final class FibonacciIterator implements \Iterator
{
}
final class FibonacciIterator implements \Iterator
{
private int $previous;
private int $current;
private int $key;
public function rewind(): void
{
$this->previous = 1;
$this->current = 0;
$this->key = 0;
}
}
final class FibonacciIterator implements \Iterator
{
private int $previous;
private int $current;
private int $key;
public function rewind(): void
{
// ...
}
public function next(): void
{
$next = $this->previous + $this->current;
$this->previous = $this->current;
$this->current = $next;
$this->key += 1;
}
}
final class FibonacciIterator implements \Iterator
{
private int $previous;
private int $current;
private int $key;
public function rewind(): void
{
// ...
}
public function next(): void
{
// ...
}
public function current(): mixed
{
return $this->current;
}
public function key(): mixed
{
return $this->key;
}
}
final class FibonacciIterator implements \Iterator
{
// ...
public function rewind(): void
{
// ...
}
public function next(): void
{
// ...
}
public function current(): mixed
{
// ...
}
public function key(): mixed
{
// ...
}
public function valid(): bool
{
return true;
}
}
@afup
@florianm__
FibonacciIterator
foreach (new FibonacciIterator() as $k => $number) {
echo $number . PHP_EOL;
if ($k > 10) {
return;
}
}
@afup
@florianm__
FibonacciIterator
foreach (new FibonacciIterator() as $k => $number) {
echo $number . PHP_EOL;
if ($k > 10) {
return;
}
}
0
1
1
2
3
5
8
13
21
34
55
89
@afup
@florianm__
Iterateurs inclus dans la SPL
ArrayIterator
RecursiveArrayIterator
EmptyIterator
IteratorIterator
RecursiveCallbackFilterIterator
CachingIterator
RecursiveCachingIterator
FilterIterator
CallbackFilterIterator
AppendIterator
RecursiveFilterIterator
ParentIterator
RegexIterator
RecursiveRegexIterator
InfiniteIterator
NoRewindIterator
LimitIterator
MultipleIterator
RecursiveIteratorIterator
RecursiveTreeIterator
DirectoryIterator
FilesystemIterator
GlobIterator
RecursiveDirectoryIterator
@afup
@florianm__
Album
final class Album
{
public function __construct(
public readonly string $title,
public readonly string $artist,
public readonly array $songs,
) {
}
}
final class Album implements \IteratorAggregate
{
public function __construct(
public readonly string $title,
public readonly string $artist,
public readonly array $songs,
) {
}
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->songs);
}
}
@afup
@florianm__
final class Album implements \IteratorAggregate
{
public function __construct(
public readonly string $title,
public readonly string $artist,
public readonly array $songs,
) {
}
public function getIterator(): \Traversable
{
return new \ArrayIterator($this->songs);
}
}
$album = new Album(
'Futur Days',
'Can',
[
'Future Days',
'Spray',
'Moonshake',
'Bel Air',
],
);
$album = new Album(
'Futur Days',
'Can',
[
'Future Days',
'Spray',
'Moonshake',
'Bel Air',
],
);
foreach ($album as $song) {
echo $song . PHP_EOL;
}
$album = new Album(
'Futur Days',
'Can',
[
'Future Days',
'Spray',
'Moonshake',
'Bel Air',
],
);
foreach ($album as $song) {
// Future Days
// Spray
// Moonshake
// Bel Air
echo $song . PHP_EOL;
}
@afup
@florianm__
Album
$queue = new \AppendIterator();
$queue->append($album);
File d'attente
$album = new Album(
'Futur Days',
'Can',
[
'Future Days',
'Spray',
'Moonshake',
'Bel Air',
],
);
AppendIterator::append(): Argument #1 ($iterator) must be of type Iterator, Album given
$queue = new \AppendIterator();
$queue->append(new \IteratorIterator($album));
$queue = new \AppendIterator();
$iterator = $album;
if ($iterator instanceof \IteratorAggregate) {
$iterator = $iterator->getIterator();
}
$queue->append($iterator);
$queue = new \AppendIterator();
$queue = new \AppendIterator();
$queue->append(new \IteratorIterator($album));
Can
Future Days
Can
Spray
Can
Moonshake
Can
Bel Air
Angelo Badalamenti
Audrey's Dance
Angelo Badalamenti
Silencio
Now playing
Comming next
2
3
4
5
6
$queue = new \AppendIterator();
$queue->append(new \IteratorIterator($album));
$queue->append(new \ArrayIterator([
new Song("Audrey's Dance", "Angelo Badalamenti"),
new Song("Silencio", "Angelo Badalamenti"),
]));
@afup
@florianm__
Lecture continue
J Dilla
Last Donut of the Night
@florianm__
@afup
Lecture continue
$album = new Album(
'Donuts',
'J Dilla',
[
'Donuts (Outro)',
'Workinonit',
// ...
'Last Donut of the Night',
'Welcome to the Show',
],
);
$album = new Album(
'Donuts',
'J Dilla',
[
'Donuts (Outro)',
'Workinonit',
// ...
'Last Donut of the Night',
'Welcome to the Show',
],
);
$songs = new \InfiniteIterator(
new \IteratorIterator($album),
);
foreach ($songs as $song) {
echo $song['title'] . PHP_EOL;
}
J Dilla
Last Donut of the Night
@florianm__
@afup
Aléatoire
Jan Jelinek
They, Them
@florianm__
@afup
Aléatoire
$album = new Album(
'Loop-Finding-Jazz-Records',
'Jan Jelinek',
[
'Moiré (Piano & Organ)',
'Rock in the Video Age',
'They, Them',
'Them, Their',
'Tendency',
'Moiré (Strings)',
'Do Dekor',
'Drift',
],
);
Jan Jelinek
They, Them
@florianm__
@afup
Aléatoire
$album = new Album(
'Loop-Finding-Jazz-Records',
'Jan Jelinek',
[
'Moiré (Piano & Organ)',
'Rock in the Video Age',
'They, Them',
'Them, Their',
'Tendency',
'Moiré (Strings)',
'Do Dekor',
'Drift',
],
);
final class RandomIteratorAggregate implements \IteratorAggregate
{
public function __construct(
private \Traversable $iterator,
) {
}
public function getIterator(): \Traversable
{
$array = iterator_to_array($this->iterator);
shuffle($array);
return new \ArrayIterator($array);
}
}
$album = new Album(
'Loop-Finding-Jazz-Records',
'Jan Jelinek',
[
'Moiré (Piano & Organ)',
'Rock in the Video Age',
'They, Them',
'Them, Their',
'Tendency',
'Moiré (Strings)',
'Do Dekor',
'Drift',
],
);
$songs = new RandomIteratorAggregate(
new \IteratorIterator($album),
);
foreach ($songs as $song) {
echo $song['title'] . PHP_EOL;
}
Jan Jelinek
They, Them
@florianm__
@afup
Filtrer les sons explicites
black midi
Sugar / Tzu
@florianm__
@afup
Filtrer les sons explicites
$album = new Album(
'Hellfire',
'black midi',
[
new Song('Hellfire', explicit: false),
new Song('Sugar / Tzu', explicit: false),
new Song('Eat Men Eat', explicit: true),
new Song('Welcome to Hell', explicit: true),
new Song('Still', explicit: false),
new Song('Half Time', explicit: false),
new Song('The Race Is About to Begin', explicit: true),
new Song('Dangerous Liaisons', explicit: false),
new Song('The Defence', explicit: false),
new Song('27 Questions', explicit: false),
],
);
$album = new Album(
'Hellfire',
'black midi',
[
new Song('Hellfire', explicit: false),
new Song('Sugar / Tzu', explicit: false),
new Song('Eat Men Eat', explicit: true),
new Song('Welcome to Hell', explicit: true),
new Song('Still', explicit: false),
new Song('Half Time', explicit: false),
new Song('The Race Is About to Begin', explicit: true),
new Song('Dangerous Liaisons', explicit: false),
new Song('The Defence', explicit: false),
new Song('27 Questions', explicit: false),
],
);
foreach ($album as $song) {
if ($song->explicit === true) {
continue;
}
echo $song->title . PHP_EOL;
}
$album = new Album(
'Hellfire',
'black midi',
[
new Song('Hellfire', explicit: false),
new Song('Sugar / Tzu', explicit: false),
new Song('Eat Men Eat', explicit: true),
new Song('Welcome to Hell', explicit: true),
new Song('Still', explicit: false),
new Song('Half Time', explicit: false),
new Song('The Race Is About to Begin', explicit: true),
new Song('Dangerous Liaisons', explicit: false),
new Song('The Defence', explicit: false),
new Song('27 Questions', explicit: false),
],
);
$songs = new \CallbackFilterIterator(
new \IteratorIterator($album),
fn (array $song): bool => $song['explicit'] === false,
);
foreach ($songs as $song) {
echo $song->title . PHP_EOL;
}
black midi
Sugar / Tzu
@florianm__
@afup
- Exceptions
- Fonctionnalités diverses
- Structures de données
- Itérateurs
@afup
@florianm__
final class InMemoryMetadataStore implements MetadataStoreInterface
{
use GetMetadataTrait;
private array $workflowMetadata;
private array $placesMetadata;
private \SplObjectStorage $transitionsMetadata;
/**
* @param \SplObjectStorage<Transition, array>|null $transitionsMetadata
*/
public function __construct(
array $workflowMetadata = [],
array $placesMetadata = [],
\SplObjectStorage $transitionsMetadata = null
) {
$this->workflowMetadata = $workflowMetadata;
$this->placesMetadata = $placesMetadata;
$this->transitionsMetadata = $transitionsMetadata ?? new \SplObjectStorage();
}
}
class ClassLoader
{
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
}
// framework/src/Illuminate/Http/Request.php
public function session()
{
if (! $this->hasSession()) {
throw new RuntimeException('Session store not set on request.');
}
return $this->session;
}
final class SyliusRequirements implements \IteratorAggregate
{
/** @var array|RequirementCollection[] */
private array $collections = [];
public function __construct(array $requirementCollections)
{
// ...
}
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->collections);
}
}
Merci
@afup
@florianm__
[Forum PHP 2023][AFUP] SPL
By Florian David Merle
[Forum PHP 2023][AFUP] SPL
- 318