Passed
Push — main ( 3c140b...f1678e )
by smiley
02:52
created

TS3Client::disconnect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 6
c 2
b 0
f 0
dl 0
loc 13
rs 10
cc 2
nc 2
nop 0
1
<?php
2
/**
3
 * Class TS3Client
4
 *
5
 * @created      02.10.2016
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2016 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\Teamspeak;
12
13
use chillerlan\Settings\SettingsContainerInterface;
14
use Psr\Log\{LoggerAwareInterface, LoggerAwareTrait, LoggerInterface, NullLogger};
15
use ErrorException, Throwable;
16
17
use function fclose, fsockopen, fwrite, is_resource, restore_error_handler,
18
	set_error_handler, stream_get_contents, stream_set_timeout, trim;
19
use function implode;
20
use function is_int;
21
22
class TS3Client implements LoggerAwareInterface{
23
	use LoggerAwareTrait;
24
25
	/**
26
	 * @var \chillerlan\Teamspeak\TS3Config
27
	 */
28
	protected SettingsContainerInterface $config;
29
30
	/**
31
	 * @var resource|null
32
	 */
33
	protected $socket;
34
35
	/**
36
	 * TS3Client constructor.
37
	 *
38
	 * @param \chillerlan\Settings\SettingsContainerInterface $config
39
	 * @param \Psr\Log\LoggerInterface|null                   $logger
40
	 */
41
	public function __construct(SettingsContainerInterface $config, LoggerInterface $logger = null){
42
		$this->config = $config;
0 ignored issues
show
Documentation Bug introduced by
$config is of type chillerlan\Settings\SettingsContainerInterface, but the property $config was declared to be of type chillerlan\Teamspeak\TS3Config. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
43
		$this->logger = $logger ?? new NullLogger;
44
	}
45
46
	/**
47
	 *
48
	 */
49
	public function __destruct(){
50
		$this->disconnect();
51
	}
52
53
	/**
54
	 * @throws \chillerlan\Teamspeak\TS3ClientException
55
	 */
56
	public function connect():TS3Client{
57
58
		if(is_resource($this->socket)){
59
			return $this;
60
		}
61
62
		/** @phan-suppress-next-line PhanTypeMismatchArgumentInternal stupid inconsistent callables... */
63
		set_error_handler(function($severity, $msg, $file, $line){
64
			throw new ErrorException($msg, 0, $severity, $file, $line);
65
		});
66
67
		try{
68
			$this->socket = fsockopen($this->config->host, $this->config->port);
69
		}
70
		catch(Throwable $e){
71
			throw new TS3ClientException('could not connect: #'.$e->getMessage());
72
		}
73
74
		restore_error_handler();
75
		stream_set_timeout($this->socket, 1);
76
77
		$this->send('login', [
78
			'client_login_name'     => $this->config->query_user,
79
			'client_login_password' => $this->config->query_password,
80
		]);
81
82
		$this->send('use', ['sid' => $this->config->vserver]);
83
84
		return $this;
85
	}
86
87
	/**
88
	 * @return \chillerlan\Teamspeak\TS3Client
89
	 */
90
	public function disconnect():TS3Client{
91
92
		if(is_resource($this->socket)){
93
			$this->send('logout');
94
			$this->send('quit');
95
96
			fclose($this->socket);
97
98
			// just in case the destructor runs into this when the socket is already closed.
99
			$this->socket = null;
100
		}
101
102
		return $this;
103
	}
104
105
	/**
106
	 * @throws \chillerlan\Teamspeak\TS3ClientException
107
	 */
108
	public function send(string $command, array $params = null):TS3Response{
109
110
		if(!$this->socket){
111
			throw new TS3ClientException('not connected');
112
		}
113
114
		$command = trim($command);
115
116
		if(empty($command)){
117
			throw new TS3ClientException('empty command');
118
		}
119
120
		if(!empty($params)){
121
			$args = [];
122
123
			foreach($params as $k => $param){
124
				$args[] = is_int($k)
125
					? '-'.trim($param)
126
					: trim($k).'='.$param;
127
			}
128
129
			$command .= ' '.implode(' ', $args);
130
		}
131
132
		$this->logger->debug('command: '.$command);
0 ignored issues
show
Bug introduced by
The method debug() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
		$this->logger->/** @scrutinizer ignore-call */ 
133
                 debug('command: '.$command);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
133
134
		if(fwrite($this->socket, $command."\n") !== false){
135
			$response = stream_get_contents($this->socket);
136
137
			$this->logger->debug('response: '.$response);
138
139
			return new TS3Response($command, $response);
140
		}
141
142
		throw new TS3ClientException('could not send command: '.$command);
143
	}
144
145
}
146