Completed
Push — 3.0 ( 6d983c...443341 )
by Vermeulen
01:58
created

anonymous//src/Subject.php$0   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 9
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 0
Metric Value
wmc 1
lcom 0
cbo 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BFW;
4
5
use \Exception;
6
use \SplSubject;
7
use \SplObserver;
8
9
/**
10
 * Class to manage subject in observers systems
11
 */
12
class Subject implements SplSubject
13
{
14
    /**
15
     * @const ERR_OBSERVER_NOT_FOUND Exception code if the observer to detach
16
     * has not been found.
17
     */
18
    const ERR_OBSERVER_NOT_FOUND = 1112001;
19
    
20
    /**
21
     * @var \SplObserver[] $observers List of all observers
22
     */
23
    protected $observers = [];
24
    
25
    /**
26
     * @var object[] $notifyHeap List of notify to send
27
     */
28
    protected $notifyHeap = [];
29
    
30
    /**
31
     * @var string $action The current action to send to observers
32
     */
33
    protected $action = '';
34
    
35
    /**
36
     * @var mixed $context The current context to send to observers
37
     */
38
    protected $context = null;
39
    
40
    /**
41
     * Return list of all observers
42
     * 
43
     * @return \SplObserver[]
44
     */
45
    public function getObservers(): array
46
    {
47
        return $this->observers;
48
    }
49
    
50
    /**
51
     * Return list of all notify to send
52
     * 
53
     * @return object[]
54
     */
55
    public function getNotifyHeap(): array
56
    {
57
        return $this->notifyHeap;
58
    }
59
    
60
    /**
61
     * Return the action
62
     * 
63
     * @return string
64
     */
65
    public function getAction(): string
66
    {
67
        return $this->action;
68
    }
69
    
70
    /**
71
     * Return the context
72
     * 
73
     * @return mixed
74
     */
75
    public function getContext()
76
    {
77
        return $this->context;
78
    }
79
80
    /**
81
     * Attach a new observer to the list
82
     * 
83
     * @param \SplObserver $observer The new observer
84
     * 
85
     * @return \BFW\Subject The current instance of this class
86
     */
87
    public function attach(SplObserver $observer): self
88
    {
89
        $this->observers[] = $observer;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Detach a observer to the list
96
     * 
97
     * @param \SplObserver $observer The observer instance to detach
98
     * 
99
     * @return \BFW\Subject The current instance of this class
100
     */
101
    public function detach(SplObserver $observer): self
102
    {
103
        $key = array_search($observer, $this->observers, true);
104
        
105
        if ($key === false) {
106
            throw new Exception(
107
                'The observer has not been found.',
108
                self::ERR_OBSERVER_NOT_FOUND
109
            );
110
        }
111
        
112
        unset($this->observers[$key]);
113
114
        return $this;
115
    }
116
117
    /**
118
     * Send a notification to all observers
119
     * 
120
     * @return \BFW\Subject The current instance of this class
121
     */
122
    public function notify(): self
123
    {
124
        \BFW\Application::getInstance()
0 ignored issues
show
Documentation Bug introduced by
The method getMonolog does not exist on object<BFW\Application>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
125
            ->getMonolog()
126
            ->getLogger()
127
            ->debug(
128
                'Subject notify event',
129
                ['action' => $this->action]
130
            );
131
        
132
        foreach ($this->observers as $observer) {
133
            $observer->update($this);
134
        }
135
        
136
        return $this;
137
    }
138
    
139
    /**
140
     * Read the notify heap list and send each notify into the list.
141
     * 
142
     * @return $this
143
     */
144
    public function readNotifyHeap(): self
145
    {
146
        foreach ($this->notifyHeap as $notifyIndex => $notifyDatas) {
147
            $this->action  = $notifyDatas->action;
148
            $this->context = $notifyDatas->context;
149
            
150
            $this->notify();
151
            
152
            //Remove the current notification from list
153
            unset($this->notifyHeap[$notifyIndex]);
154
        }
155
        
156
        //Some new notifications has been added during the loop
157
        if (count($this->notifyHeap) > 0) {
158
            $this->readNotifyHeap();
159
        }
160
161
        return $this;
162
    }
163
    
164
    /**
165
     * Add a new notification to the list of notification to send.
166
     * If there is only one notification into the list, it will be send now.
167
     * Else, a notification is currently sent, so we wait it finish and the
168
     * current notification will be sent.
169
     * 
170
     * @param string $action The action to send
171
     * @param mixed $context (default null) The context to send
172
     * 
173
     * @return \BFW\Subject The current instance of this class
174
     */
175
    public function addNotification(string $action, $context = null): self
176
    {
177
        $this->notifyHeap[] = new class($action, $context) {
178
            public $action;
179
            public $context;
180
            
181
            public function __construct($action, $context) {
182
                $this->action  = $action;
183
                $this->context = $context;
184
            }
185
        };
186
        
187
        if (count($this->notifyHeap) === 1) {
188
            $this->readNotifyHeap();
189
        }
190
        
191
        return $this;
192
    }
193
}
194