Completed
Push — master ( dd7177...241235 )
by Kacper
04:07
created

Roster::add()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Nucleus - XMPP Library for PHP
4
 *
5
 * Copyright (C) 2016, Some rights reserved.
6
 *
7
 * @author Kacper "Kadet" Donat <[email protected]>
8
 *
9
 * Contact with author:
10
 * Xmpp: [email protected]
11
 * E-mail: [email protected]
12
 *
13
 * From Kadet with love.
14
 */
15
16
namespace Kadet\Xmpp\Component;
17
18
19
use Kadet\Highlighter\Utils\Console;
20
use Kadet\Xmpp\Exception\ReadOnlyException;
21
use Kadet\Xmpp\Jid;
22
use Kadet\Xmpp\Stanza\Iq;
23
use Kadet\Xmpp\Utils\Accessors;
24
use Kadet\Xmpp\Utils\BetterEmitter;
25
use Kadet\Xmpp\Utils\filter as with;
26
use Kadet\Xmpp\XmppClient;
27
use Traversable;
28
use function Kadet\Xmpp\Utils\helper\format;
29
30
/**
31
 * Class Roster
32
 * @package Kadet\Xmpp\Component
33
 *
34
 * @property-read Iq\Query\Roster\Item[] $items Copy of all roster items
35
 */
36
class Roster extends Component implements \IteratorAggregate, \ArrayAccess
37
{
38
    use BetterEmitter, Accessors;
39
40
    private $_items = [];
41
42 5
    public function setClient(XmppClient $client)
43
    {
44 5
        parent::setClient($client);
45
        $this->_client->on('init', function(\SplQueue $queue) {
46 1
            $queue->enqueue($this->_client->send(new Iq('get', ['query' => new Iq\Query\Roster()])));
47 5
        });
48
49 5
        $this->_client->on('iq', function(Iq $iq) {
50
            /** @var Roster $iq->query */
51 3
            switch ($iq->type) {
52 3
                case "result":
53 3
                    $this->handleResult($iq->query);
0 ignored issues
show
Documentation introduced by
$iq->query is of type object<Kadet\Xmpp\Component\Roster>, but the function expects a object<Kadet\Xmpp\Stanza\Iq\Query\Roster>.

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...
54 3
                    break;
55
                case "set":
56
                    $this->handleSet($iq->query);
0 ignored issues
show
Documentation introduced by
$iq->query is of type object<Kadet\Xmpp\Component\Roster>, but the function expects a object<Kadet\Xmpp\Stanza\Iq\Query\Roster>.

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...
57
                    break;
58
            }
59 5
        }, with\iq\query(Iq\Query\Roster::class));
60 5
    }
61
62 3
    private function handleSet(Iq\Query\Roster $query)
63
    {
64 3
        foreach ($query->items as $item) {
65 3
            if($item->subscription == 'remove') {
66
                $this->removeItem($item->jid);
67
            } else {
68 3
                $this->setItem($item);
69
            }
70
        }
71
72 3
        $this->emit('update');
73 3
    }
74
75 3
    private function handleResult(Iq\Query\Roster $query)
76
    {
77 3
        $this->_client->getLogger()->debug(format('Received roster (version: {version}) update with {no} roster items.', [
78 3
            'no' => count($query->items),
79 3
            'version' => $query->version
80
        ]));
81 3
        $this->_items = [];
82 3
        $this->handleSet($query);
83 3
    }
84
85
    /**
86
     * Whether a offset exists
87
     * @link  http://php.net/manual/en/arrayaccess.offsetexists.php
88
     * @param mixed $offset <p>
89
     *                      An offset to check for.
90
     *                      </p>
91
     * @return boolean true on success or false on failure.
92
     *                      </p>
93
     *                      <p>
94
     *                      The return value will be casted to boolean if non-boolean was returned.
95
     * @since 5.0.0
96
     */
97
    public function offsetExists($offset)
98
    {
99
        return array_key_exists($offset, $this->_items);
100
    }
101
102
    /**
103
     * Offset to retrieve
104
     * @link  http://php.net/manual/en/arrayaccess.offsetget.php
105
     * @param mixed $offset <p>
106
     *                      The offset to retrieve.
107
     *                      </p>
108
     * @return mixed Can return all value types.
109
     * @since 5.0.0
110
     */
111 1
    public function offsetGet($offset)
112
    {
113 1
        return clone ($this->_items[(string)$offset] ?? null);
114
    }
115
116
    /**
117
     * Offset to set
118
     * @link  http://php.net/manual/en/arrayaccess.offsetset.php
119
     * @param mixed $offset <p>
120
     *                      The offset to assign the value to.
121
     *                      </p>
122
     * @param mixed $value  <p>
123
     *                      The value to set.
124
     *                      </p>
125
     * @return void
126
     * @since 5.0.0
127
     */
128
    public function offsetSet($offset, $value)
129
    {
130
        throw new ReadOnlyException('You should not modify roster directly, use update() method.');
131
    }
132
133
    /**
134
     * Offset to unset
135
     * @link  http://php.net/manual/en/arrayaccess.offsetunset.php
136
     * @param mixed $offset <p>
137
     *                      The offset to unset.
138
     *                      </p>
139
     * @return void
140
     * @since 5.0.0
141
     */
142
    public function offsetUnset($offset)
143
    {
144
        throw new ReadOnlyException('You should not modify roster directly, use remove() method.');
145
    }
146
147 1
    public function remove($what)
148
    {
149 1
        $predicate = $what instanceof \Closure ? $what : with\property('jid', with\equals($what));
150 1
        $remove    = array_filter($this->_items, $predicate);
151
152 1
        $iq = new Iq('remove', ['query' => new Iq\Query\Roster()]);
153
        /** @var Iq\Query\Roster\Item $item */
154 1
        foreach($remove as $item) {
155 1
            $iq->query->append(new Iq\Query\Roster\Item($item->jid, ['subscription' => 'remove']));
156
        }
157
158 1
        return $this->_client->send($iq);
159
    }
160
161 2
    public function update(Iq\Query\Roster\Item $item)
162
    {
163 2
        $iq = new Iq('set', ['query' => new Iq\Query\Roster([
164 2
            'items' => [ $item ]
165
        ])]);
166 2
        return $this->_client->send($iq);
167
    }
168
169 1
    public function add(Iq\Query\Roster\Item $item)
170
    {
171 1
        return $this->update($item); // From XMPP perspective adding is same as updating non existent item.
172
    }
173
174
    /**
175
     * Retrieve an external iterator
176
     * @link  http://php.net/manual/en/iteratoraggregate.getiterator.php
177
     * @return Traversable An instance of an object implementing <b>Iterator</b> or
178
     * <b>Traversable</b>
179
     * @since 5.0.0
180
     */
181
    public function getIterator(): Traversable
182
    {
183
        return new \ArrayIterator($this->_items);
184
    }
185
186
    /**
187
     * @return Iq\Query\Roster\Item[]
188
     */
189 1
    public function getItems()
190
    {
191 1
        return \Kadet\Xmpp\Utils\helper\copy($this->_items);
192
    }
193
194
    public static function group($name)
195
    {
196
        return with\property('groups', with\contains($name));
197
    }
198
199
    /**
200
     * @param callable(Item $item) $mapper
0 ignored issues
show
Documentation introduced by
The doc-type callable(Item could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
Bug introduced by
There is no parameter named $item). Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
201
     * @return array
202
     */
203
    public function map(callable $mapper)
204
    {
205
        return array_map($mapper, $this->items);
206
    }
207
208
    /**
209
     * @param callable $predicate
210
     * @return Iq\Query\Roster\Item[]
211
     */
212
    public function filter(callable $predicate)
213
    {
214
        return array_filter($this->items, $predicate);
215
    }
216
217
    public function asArray() : array
218
    {
219
        return $this->items;
220
    }
221
222
    public static function fromArray(array $array)
0 ignored issues
show
Unused Code introduced by
The parameter $array 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...
223
    {
224
        // TODO: Implement fromArray() method.
225
    }
226
227
    /**
228
     * Count elements of an object
229
     * @link  http://php.net/manual/en/countable.count.php
230
     * @return int The custom count as an integer.
231
     * </p>
232
     * <p>
233
     * The return value is cast to an integer.
234
     * @since 5.1.0
235
     */
236
    public function count()
237
    {
238
        return count($this->items);
239
    }
240
241
242 3
    private function setItem(Iq\Query\Roster\Item $item)
243
    {
244 3
        $this->emit('item', [ $item ]);
245 3
        $this->_items[(string)$item->jid] = $item;
246 3
    }
247
248
    private function removeItem(Jid $jid)
249
    {
250
        if (!isset($this->_items[(string)$jid])) {
251
            $this->_client->getLogger()->warning(format('Trying to remove non-existing roster item {jid}', [
252
                'item' => Console::styled(['color' => 'green'], (string)$jid)
253
            ]));
254
            return;
255
        }
256
257
        $this->emit('remove', [ $this->_items[(string)$jid] ]);
258
        unset($this->_items[(string)$jid]);
259
    }
260
}
261