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