3.8. Observer
3.8.1. Rôle
Implémenter un comportement de publication/abonnement à un objet, chaque fois qu’un objet « Sujet » change d’état, les « Observateurs » attachés seront notifiés. Il est utilisé pour réduire le nombre d’objets couplés et utilise plutôt un couplage souple.
3.8.2. Exemples
Un système de file d’attente de messages est observé pour montrer la progression d’un travail dans une interface graphique.
3.8.3. Note
PHP définit déjà deux interfaces qui peuvent aider à implémenter ce modèle : SplObserver et SplSubject.
3.8.4. Diagramme UML

3.8.5. Code
Vous pouvez également trouver ce code sur GitHub
User.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer;
6
7use SplSubject;
8use SplObjectStorage;
9use SplObserver;
10
11/**
12 * User implements the observed object (called Subject), it maintains a list of observers and sends notifications to
13 * them in case changes are made on the User object
14 */
15class User implements SplSubject
16{
17 private SplObjectStorage $observers;
18 private $email;
19
20 public function __construct()
21 {
22 $this->observers = new SplObjectStorage();
23 }
24
25 public function attach(SplObserver $observer): void
26 {
27 $this->observers->attach($observer);
28 }
29
30 public function detach(SplObserver $observer): void
31 {
32 $this->observers->detach($observer);
33 }
34
35 public function changeEmail(string $email): void
36 {
37 $this->email = $email;
38 $this->notify();
39 }
40
41 public function notify(): void
42 {
43 /** @var SplObserver $observer */
44 foreach ($this->observers as $observer) {
45 $observer->update($this);
46 }
47 }
48}
UserObserver.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer;
6
7use SplObserver;
8use SplSubject;
9
10class UserObserver implements SplObserver
11{
12 /**
13 * @var SplSubject[]
14 */
15 private array $changedUsers = [];
16
17 /**
18 * It is called by the Subject, usually by SplSubject::notify()
19 */
20 public function update(SplSubject $subject): void
21 {
22 $this->changedUsers[] = clone $subject;
23 }
24
25 /**
26 * @return SplSubject[]
27 */
28 public function getChangedUsers(): array
29 {
30 return $this->changedUsers;
31 }
32}
3.8.6. Test
Tests/ObserverTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer\Tests;
6
7use DesignPatterns\Behavioral\Observer\User;
8use DesignPatterns\Behavioral\Observer\UserObserver;
9use PHPUnit\Framework\TestCase;
10
11class ObserverTest extends TestCase
12{
13 public function testChangeInUserLeadsToUserObserverBeingNotified()
14 {
15 $observer = new UserObserver();
16
17 $user = new User();
18 $user->attach($observer);
19
20 $user->changeEmail('foo@bar.com');
21 $this->assertCount(1, $observer->getChangedUsers());
22 }
23}