Passed
Push — master ( 837657...1a0eba )
by Xu
05:31
created

NotificationManager   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 237
rs 10
c 0
b 0
f 0
wmc 29

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getChannels() 0 3 2
B get() 0 17 6
A setChannels() 0 4 2
A __isset() 0 7 2
B set() 0 21 6
A has() 0 3 2
B send() 0 20 6
A clear() 0 3 1
A __get() 0 7 2
1
<?php
2
/**
3
 * @link http://www.tintsoft.com/
4
 * @copyright Copyright (c) 2012 TintSoft Technology Co. Ltd.
5
 * @license http://www.tintsoft.com/license/
6
 */
7
8
namespace yuncms\notifications;
9
10
use Closure;
11
use Yii;
12
use yii\base\Component;
13
use yii\base\InvalidConfigException;
14
use yuncms\notifications\channels\Channel;
15
16
/**
17
 * 通知管理
18
 *
19
 * ```php
20
 * [
21
 *     'components' => [
22
 *         'notification' => [
23
 *             'channels' => [
24
 *                 'db' => [
25
 *                     'class' => 'yuncms\notifications\channels\DBChannel',
26
 *                 ],
27
 *                 'email' => [
28
 *                     'class' => 'yuncms\notifications\channels\EmailChannel',
29
 *                     'message' => [
30
 *                         'from' => '[email protected]',
31
 *                     ],
32
 *                 ],
33
 *             ],
34
 *         ],
35
 *     ],
36
 * ]
37
 * ```
38
 * @author Tongle Xu <[email protected]>
39
 * @since 3.0
40
 */
41
class NotificationManager extends Component
42
{
43
    /**
44
     * @var array shared channel instances indexed by their IDs
45
     */
46
    private $_channels = [];
47
48
    /**
49
     * @var array channel definitions indexed by their IDs
50
     */
51
    private $_definitions = [];
52
53
    /**
54
     * Getter magic method.
55
     * This method is overridden to support accessing channels like reading properties.
56
     * @param string $name channel or property name
57
     * @return mixed the named property value
58
     * @throws InvalidConfigException
59
     * @throws \yii\base\UnknownPropertyException
60
     */
61
    public function __get($name)
62
    {
63
        if ($this->has($name)) {
64
            return $this->get($name);
65
        }
66
67
        return parent::__get($name);
68
    }
69
70
    /**
71
     * Checks if a property value is null.
72
     * This method overrides the parent implementation by checking if the named channel is loaded.
73
     * @param string $name the property name or the event name
74
     * @return bool whether the property value is null
75
     */
76
    public function __isset($name)
77
    {
78
        if ($this->has($name)) {
79
            return true;
80
        }
81
82
        return parent::__isset($name);
83
    }
84
85
    /**
86
     * Returns a value indicating whether the locator has the specified channel definition or has instantiated the channel.
87
     * This method may return different results depending on the value of `$checkInstance`.
88
     *
89
     * - If `$checkInstance` is false (default), the method will return a value indicating whether the locator has the specified
90
     *   channel definition.
91
     * - If `$checkInstance` is true, the method will return a value indicating whether the locator has
92
     *   instantiated the specified channel.
93
     *
94
     * @param string $id channel ID (e.g. `local`).
95
     * @param bool $checkInstance whether the method should check if the channel is shared and instantiated.
96
     * @return bool whether the locator has the specified channel definition or has instantiated the channel.
97
     * @see set()
98
     */
99
    public function has($id, $checkInstance = false)
100
    {
101
        return $checkInstance ? isset($this->_channels[$id]) : isset($this->_definitions[$id]);
102
    }
103
104
    /**
105
     * Returns the channel instance with the specified ID.
106
     *
107
     * @param string $id channel ID (e.g. `db`).
108
     * @param bool $throwException whether to throw an exception if `$id` is not registered with the locator before.
109
     * @return object|null the channel of the specified ID. If `$throwException` is false and `$id`
110
     * is not registered before, null will be returned.
111
     * @throws InvalidConfigException if `$id` refers to a nonexistent channel ID
112
     * @see has()
113
     * @see set()
114
     */
115
    public function get($id, $throwException = true)
116
    {
117
        if (isset($this->_channels[$id])) {
118
            return $this->_channels[$id];
119
        }
120
121
        if (isset($this->_definitions[$id])) {
122
            $definition = $this->_definitions[$id];
123
            if (is_object($definition) && !$definition instanceof Closure) {
124
                return $this->_channels[$id] = $definition;
125
            }
126
            return $this->_channels[$id] = Yii::createObject($definition);
127
        } elseif ($throwException) {
128
            throw new InvalidConfigException("Unknown channel ID: $id");
129
        }
130
131
        return null;
132
    }
133
134
    /**
135
     * Registers a filesystem definition with this locator.
136
     *
137
     * For example,
138
     *
139
     * ```php
140
     * // a class name
141
     * $locator->set('cache', 'yii\caching\FileCache');
142
     *
143
     * // a configuration array
144
     * $locator->set('db', [
145
     *     'class' => 'yii\db\Connection',
146
     *     'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
147
     *     'username' => 'root',
148
     *     'password' => '',
149
     *     'charset' => 'utf8',
150
     * ]);
151
     *
152
     * // an anonymous function
153
     * $locator->set('cache', function ($params) {
154
     *     return new \yii\caching\FileCache;
155
     * });
156
     *
157
     * // an instance
158
     * $locator->set('cache', new \yii\caching\FileCache);
159
     * ```
160
     *
161
     * If a filesystem definition with the same ID already exists, it will be overwritten.
162
     *
163
     * @param string $id filesystem ID (e.g. `db`).
164
     * @param mixed $definition the filesystem definition to be registered with this locator.
165
     * It can be one of the following:
166
     *
167
     * - a class name
168
     * - a configuration array: the array contains name-value pairs that will be used to
169
     *   initialize the property values of the newly created object when [[get()]] is called.
170
     *   The `class` element is required and stands for the the class of the object to be created.
171
     * - a PHP callable: either an anonymous function or an array representing a class method (e.g. `['Foo', 'bar']`).
172
     *   The callable will be called by [[get()]] to return an object associated with the specified filesystem ID.
173
     * - an object: When [[get()]] is called, this object will be returned.
174
     *
175
     * @throws InvalidConfigException if the definition is an invalid configuration array
176
     */
177
    public function set($id, $definition)
178
    {
179
        unset($this->_channels[$id]);
180
181
        if ($definition === null) {
182
            unset($this->_definitions[$id]);
183
            return;
184
        }
185
186
        if (is_object($definition) || is_callable($definition, true)) {
187
            // an object, a class name, or a PHP callable
188
            $this->_definitions[$id] = $definition;
189
        } elseif (is_array($definition)) {
190
            // a configuration array
191
            if (isset($definition['class'])) {
192
                $this->_definitions[$id] = $definition;
193
            } else {
194
                throw new InvalidConfigException("The configuration for the \"$id\" channel must contain a \"class\" element.");
195
            }
196
        } else {
197
            throw new InvalidConfigException("Unexpected configuration type for the \"$id\" channel: " . gettype($definition));
198
        }
199
    }
200
201
    /**
202
     * Removes the channel from the locator.
203
     * @param string $id the channel ID
204
     */
205
    public function clear($id)
206
    {
207
        unset($this->_definitions[$id], $this->_channels[$id]);
208
    }
209
210
    /**
211
     * Returns the list of the channel definitions or the loaded channel instances.
212
     * @param bool $returnDefinitions whether to return channel definitions instead of the loaded channel instances.
213
     * @return array the list of the channel definitions or the loaded channel instances (ID => definition or instance).
214
     */
215
    public function getChannels($returnDefinitions = true)
216
    {
217
        return $returnDefinitions ? $this->_definitions : $this->_channels;
218
    }
219
220
    /**
221
     * Registers a set of channel definitions in this locator.
222
     *
223
     * This is the bulk version of [[set()]]. The parameter should be an array
224
     * whose keys are channel IDs and values the corresponding channel definitions.
225
     *
226
     * For more details on how to specify channel IDs and definitions, please refer to [[set()]].
227
     *
228
     * If a channel definition with the same ID already exists, it will be overwritten.
229
     *
230
     * The following is an example for registering two channel definitions:
231
     *
232
     * ```php
233
     * [
234
     *     'local' => [
235
     *         'class' => 'yuncms\NotificationManager\channels\Local',
236
     *     ],
237
     * ]
238
     * ```
239
     *
240
     * @param array $channels channel definitions or instances
241
     * @throws InvalidConfigException
242
     */
243
    public function setChannels($channels)
244
    {
245
        foreach ($channels as $id => $channel) {
246
            $this->set($id, $channel);
247
        }
248
    }
249
250
    /**
251
     * Send a notification to all channels
252
     *
253
     * @param Notification $notification
254
     * @param array|null $channels
255
     * @return void return the channel
256
     * @throws \yii\base\InvalidConfigException
257
     */
258
    public function send($notification, array $channels = null)
259
    {
260
        if ($channels === null) {
261
            $channels = array_keys($this->getChannels(true));
262
        }
263
264
        foreach ((array)$channels as $id) {
265
            $channel = $this->get($id);
266
            if (!$notification->shouldSend($channel)) {
267
                continue;
268
            }
269
            $handle = 'to' . ucfirst($id);
270
            try {
271
                if ($notification->hasMethod($handle)) {
272
                    call_user_func([clone $notification, $handle], $channel);
273
                } else {
274
                    $channel->send(clone $notification);
275
                }
276
            } catch (\Exception $e) {
277
                Yii::warning("Notification sending by channel '$id' has failed: " . $e->getMessage(), __METHOD__);
278
            }
279
        }
280
    }
281
}