Completed
Pull Request — master (#18)
by
unknown
39:59
created

Broadcast::channelName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace yiicod\socketio;
4
5
use Exception;
6
use Yii;
7
use yii\base\InvalidConfigException;
8
use yii\di\NotInstantiableException;
9
use yii\helpers\ArrayHelper;
10
use yii\helpers\HtmlPurifier;
11
use yii\helpers\Json;
12
use yiicod\base\helpers\LoggerMessage;
13
use yiicod\socketio\drivers\RedisDriver;
14
use yiicod\socketio\events\EventPolicyInterface;
15
use yiicod\socketio\events\EventPubInterface;
16
use yiicod\socketio\events\EventRoomInterface;
17
use yiicod\socketio\events\EventSubInterface;
18
19
/**
20
 * Class Broadcast
21
 *
22
 * @package yiicod\socketio
23
 */
24
class Broadcast
25
{
26
    protected static $channels = [];
27
28
	/**
29
	 * Subscribe to event from client
30
	 *
31
	 * @param string $event
32
	 * @param array $data
33
	 * @param string $id
34
	 *
35
	 * @throws InvalidConfigException
36
	 * @throws NotInstantiableException
37
	 */
38
    public static function on(string $event, array $data, string $id)
39
    {
40
        // Clear data
41
        array_walk_recursive($data, function (&$item, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
42
            $item = HtmlPurifier::process($item);
43
        });
44
45
        Yii::info(Json::encode([
46
            'type' => 'on',
47
            'name' => $event,
48
            'data' => $data,
49
            'id' => $id,
50
        ]), 'socket.io');
51
52
        $eventClassName = self::getManager()->getList()[$event] ?? null;
53
        if (null === $eventClassName) {
54
            Yii::error(LoggerMessage::trace("Can not find $event", [Json::encode($data)]));
55
        }
56
57
        Yii::$container->get(Process::class)->run($eventClassName, $data, $id);
58
    }
59
60
	/**
61
	 * Handle process from client
62
	 *
63
	 * @param string $handler
64
	 * @param array $data
65
	 * @param string $id
66
	 */
67
    public static function process(string $handler, array $data, string $id)
68
    {
69
        try {
70
            /** @var EventSubInterface|EventPolicyInterface $event */
71
            $event = new $handler($data);
72
73
            if (false === $event instanceof EventSubInterface) {
74
                throw new Exception('Event should implement EventSubInterface');
75
            }
76
77
            Yii::$app->db->close();
78
            Yii::$app->db->open();
79
80
            if (true === $event instanceof EventPolicyInterface && false === $event->can($data, $id)) {
81
                return;
82
            }
83
84
            $event->handle($data, $id);
85
        } catch (Exception $e) {
86
            Yii::error(LoggerMessage::log($e, Json::encode($data)));
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
87
        }
88
    }
89
90
	/**
91
	 * Emit event to client
92
	 *
93
	 * @param string $event
94
	 * @param array $data
95
	 *
96
	 * @param string|null $id
97
	 */
98
    public static function emit(string $event, array $data, string $id = null)
99
    {
100
        $eventClassName = self::getManager()->getList()[$event] ?? null;
101
        try {
102
            if (null === $eventClassName) {
103
                throw new Exception("Can not find $event");
104
            }
105
106
            /** @var EventPubInterface|EventRoomInterface $event */
107
            $event = new $eventClassName($data);
108
109
            if (false === $event instanceof EventPubInterface) {
110
                throw new Exception('Event should implement EventPubInterface');
111
            }
112
113
            $data = $event->fire($data);
114
115
            if (true === $event instanceof EventRoomInterface) {
116
                $data['room'] = $event->room();
117
            } else {
118
				if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
119
					$data['id'] = $id;
120
				}
121
			}
122
123
            Yii::info(Json::encode([
124
                'type' => 'emit',
125
                'name' => $event,
126
                'data' => $data,
127
            ]), 'socket.io');
128
            foreach ($eventClassName::broadcastOn() as $channel) {
129
                static::publish(static::channelName($channel), [
130
                    'name' => $eventClassName::name(),
131
                    'data' => $data,
132
                ]);
133
            }
134
        } catch (Exception $e) {
135
            Yii::error(LoggerMessage::log($e));
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
136
        }
137
    }
138
	
139
	/**
140
	 * @param string $event
141
	 * @param string $id
142
	 * @throws Exception
143
	 */
144
	public static function closeConnection(string $event, string $id)
145
	{
146
		$eventClassName = self::getManager()->getList()[$event] ?? null;
147
		if (null === $eventClassName) {
148
			throw new Exception("Can not find $event");
149
		}
150
		foreach ($eventClassName::broadcastOn() as $channel) {
151
			static::publish(static::channelName($channel), [
152
				'name' => 'close',
153
				'data' => compact('id'),
154
			]);
155
		}
156
		
157
    }
158
159
    /**
160
     * Prepare channel name
161
     *
162
     * @param $name
163
     *
164
     * @return string
165
     */
166
    public static function channelName($name)
167
    {
168
        return $name . self::getManager()->nsp;
169
    }
170
171
    /**
172
     * Publish data to redis channel
173
     *
174
     * @param string $channel
175
     * @param array $data
176
     */
177
    public static function publish(string $channel, array $data)
178
    {
179
        static::getDriver()->getConnection(true)->publish($channel, Json::encode($data));
180
    }
181
182
    /**
183
     * Redis channels names
184
     *
185
     * @return array
186
     */
187
    public static function channels(): array
188
    {
189
        if (empty(self::$channels)) {
190
            foreach (self::getManager()->getList() as $eventClassName) {
191
                self::$channels = ArrayHelper::merge(self::$channels, $eventClassName::broadcastOn());
192
            }
193
            self::$channels = array_unique(self::$channels);
194
195
            self::$channels = array_map(function ($channel) {
196
                return static::channelName($channel);
197
            }, self::$channels);
198
            //Yii::info(Json::encode(self::$channels));
199
        }
200
201
        return self::$channels;
202
    }
203
204
    /**
205
     * @return RedisDriver
206
     */
207
    public static function getDriver()
208
    {
209
        return Yii::$app->broadcastDriver;
210
    }
211
212
    /**
213
     * @return EventManager
214
     */
215
    public static function getManager()
216
    {
217
        return Yii::$app->broadcastEvents;
218
    }
219
}
220