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

XmppClient.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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