Completed
Push — master ( 7a5aed...4799e1 )
by Kacper
06:25
created

XmppClient::bind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 0
cts 4
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * XMPP Library
4
 *
5
 * Copyright (C) 2016, Some right 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;
17
18
use DI\Container;
19
use DI\ContainerBuilder;
20
use Interop\Container\ContainerInterface;
21
use Kadet\Xmpp\Exception\InvalidArgumentException;
22
use Kadet\Xmpp\Module\Binding;
23
use Kadet\Xmpp\Module\ClientModuleInterface;
24
use Kadet\Xmpp\Module\SaslAuthenticator;
25
use Kadet\Xmpp\Module\StartTls;
26
use Kadet\Xmpp\Network\Connector;
27
use Kadet\Xmpp\Stream\Features;
28
use Kadet\Xmpp\Utils\Accessors;
29
use Kadet\Xmpp\Utils\filter as with;
30
use Kadet\Xmpp\Utils\ServiceManager;
31
use Kadet\Xmpp\Xml\XmlElementFactory;
32
use Kadet\Xmpp\Xml\XmlParser;
33
use Kadet\Xmpp\Xml\XmlStream;
34
use React\EventLoop\LoopInterface;
35
36
/**
37
 * Class XmppClient
38
 * @package Kadet\Xmpp
39
 *
40
 * @property-read Jid        $jid      Client's jid (Jabber Identifier) address.
41
 * @property-read Features   $features Features provided by that stream
42
 * @property-write Connector $connector
43
 */
44
class XmppClient extends XmlStream implements ContainerInterface
45
{
46
    use ServiceManager, Accessors;
47
48
    private $_attributes = [];
49
    private $_lang;
50
51
    /**
52
     * Connector used to instantiate stream connection to server.
53
     *
54
     * @var Connector
55
     */
56
    protected $_connector;
57
58
    /**
59
     * Client's jid (Jabber Identifier) address.
60
     *
61
     * @var Jid
62
     */
63
    protected $_jid;
64
65
    /**
66
     * Dependency container used as service manager.
67
     *
68
     * @var Container
69
     */
70
    protected $_container;
71
72
    /**
73
     * Features provided by that stream
74
     *
75
     * @var Features
76
     */
77
    protected $_features;
78
79
    /**
80
     * XmppClient constructor.
81
     * @param Jid   $jid
82
     * @param array $options
83
     */
84
    public function __construct(Jid $jid, array $options = [])
85
    {
86
        $options = array_merge_recursive([
87
            'parser'  => new XmlParser(new XmlElementFactory()),
88
            'lang'    => 'en',
89
            'modules' => [
90
                new StartTls(),
91
                new Binding()
92
            ]
93
        ], $options);
94
95
        parent::__construct($options['parser'], null);
96
97
        $this->_parser->factory->load(require __DIR__ . '/XmlElementLookup.php');
98
99
        $this->_lang      = $options['lang'];
100
        $this->_container = ContainerBuilder::buildDevContainer();
101
        $this->_jid       = $jid;
102
        $this->connector  = $options['connector'] ?? new Connector\TcpXmppConnector($jid->domain, $options['loop']);
103
104
        if (isset($options['password'])) {
105
            $this->register(new SaslAuthenticator($options['password']));
106
        }
107
108
        foreach ($options['modules'] as $module) {
109
            $this->register($module);
110
        }
111
112
        $this->_connector->on('connect', function (...$arguments) {
113
            return $this->emit('connect', $arguments);
114
        });
115
116
        $this->on('element', function (Features $element) {
117
            $this->_features = $element;
118
            $this->emit('features', [$element]);
119
        }, Features::class);
120
121
        $this->connect();
122
    }
123
124
    public function start(array $attributes = [])
125
    {
126
        $this->_attributes = $attributes;
127
128
        parent::start(array_merge([
129
            'xmlns'    => 'jabber:client',
130
            'version'  => '1.0',
131
            'xml:lang' => $this->_lang
132
        ], $attributes));
133
    }
134
135
    public function restart()
136
    {
137
        $this->getLogger()->debug('Restarting stream', $this->_attributes);
138
        $this->start($this->_attributes);
139
    }
140
141
    public function connect()
142
    {
143
        $this->getLogger()->debug("Connecting to {$this->_jid->domain}");
144
145
        $this->_connector->connect();
146
    }
147
148
    public function setResource(string $resource)
149
    {
150
        $this->_jid = new Jid($this->_jid->domain, $this->_jid->local, $resource);
151
    }
152
153
    public function getJid()
154
    {
155
        return $this->_jid;
156
    }
157
158
    public function bind($jid)
159
    {
160
        $this->_jid = new Jid($jid);
161
162
        $this->emit('bind', [ $jid ]);
163
    }
164
165
    private function handleConnect($stream)
166
    {
167
        $this->exchangeStream($stream);
168
169
        $this->getLogger()->info("Connected to {$this->_jid->domain}");
170
        $this->start([
171
            'from' => (string)$this->_jid,
172
            'to'   => $this->_jid->domain
173
        ]);
174
    }
175
176
    /**
177
     * @param $connector
178
     */
179
    protected function setConnector($connector)
180
    {
181
        if ($connector instanceof LoopInterface) {
182
            $this->_connector = new Connector\TcpXmppConnector($this->_jid->domain, $connector);
183
        } elseif ($connector instanceof Connector) {
184
            $this->_connector = $connector;
185
        } else {
186
            throw new InvalidArgumentException(sprintf(
187
                '$connector must be either %s or %s instance, %s given.',
188
                LoopInterface::class, Connector::class, \Kadet\Xmpp\Utils\helper\typeof($connector)
189
            ));
190
        }
191
192
        $this->_connector->on('connect', function ($stream) {
193
            $this->handleConnect($stream);
194
        });
195
    }
196
197
    public function register(ClientModuleInterface $module, $alias = true)
198
    {
199
        $module->setClient($this);
200
        if ($alias === true) {
201
            $parents = array_merge(class_implements($module), array_slice(class_parents($module), 1));
202
            foreach ($parents as $alias) {
203
                if (!$this->has($alias)) {
204
                    $this->_container->set($alias, $module);
205
                }
206
            }
207
        } else {
208
            $this->_container->set($alias === true ? get_class($module) : $alias, $module);
0 ignored issues
show
Bug introduced by
It seems like $alias === true ? get_class($module) : $alias can also be of type boolean; however, DI\Container::set() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
209
        }
210
    }
211
212
    protected function getContainer() : ContainerInterface
213
    {
214
        return $this->_container;
215
    }
216
217
    public function getFeatures()
218
    {
219
        return $this->_features;
220
    }
221
}
222