TS3Response::unescape()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 2
1
<?php
2
/**
3
 * Class TS3Response
4
 *
5
 * @created      09.10.2016
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2016 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\Teamspeak;
12
13
use stdClass;
14
use function array_map, array_pop, chr, explode, is_numeric, preg_match, property_exists, str_replace, strpos, trim;
15
16
/**
17
 * @property string    $commandline
18
 * @property array     $commandlist
19
 * @property string    $raw
20
 * @property array     $data
21
 * @property \stdClass $error
22
 */
23
final class TS3Response{
24
25
	private string $commandline;
26
	private array $commandlist = [];
27
	private string $raw;
28
	private array $data = [];
29
	private stdClass $error;
30
31
	/**
32
	 * TS3Response constructor.
33
	 */
34
	public function __construct(string $command, string $raw){
35
		$this->commandline = $command;
36
		$this->raw         = $raw;
37
38
		$this->error       = new stdClass;
39
		$this->error->id   = null;
40
		$this->error->msg  = null;
41
42
		$this->parse_response();
43
	}
44
45
	/**
46
	 * @return mixed|null
47
	 */
48
	public function __get(string $property){
49
50
		if(property_exists($this, $property)){
51
			return $this->{$property};
52
		}
53
54
		return null;
55
	}
56
57
	/**
58
	 *
59
	 */
60
	private function parse_response():void{
61
		$this->commandlist = array_map(fn($c) => trim($c, "\r\n\t- "), explode(' ', trim($this->commandline)));
62
		$this->data        = array_map(fn($d) => trim($d), explode("\n", trim($this->raw)));
63
64
		if(preg_match('/^(:?error id=)(?P<id>[\d]+)(:? msg=)(?P<msg>.*)$/isU', array_pop($this->data), $match) > 0){
65
			$this->error->id  = (int)$match['id'];
66
			$this->error->msg = $match['msg'];
67
		}
68
69
	}
70
71
	/**
72
	 * @return \stdClass[]|\stdClass
73
	 */
74
	public function parseList(){
75
		return self::parse_list($this->data[0] ?? '');
76
	}
77
78
	/**
79
	 * parse space separated k/v pairs
80
	 *
81
	 * key1=val1 key2=val2 ...
82
	 *
83
	 * @param string $str
84
	 *
85
	 * @return \stdClass
86
	 */
87
	public static function parse_kv(string $str):stdClass{
88
		$str = explode(' ', $str);
89
		$r   = new stdClass;
90
91
		foreach($str as $pair){
92
			$kv  = explode('=', $pair);
93
			$val = isset($kv[1]) && !empty($kv[1]) ? self::unescape($kv[1]) : null;
94
95
			if(is_numeric($val)){
96
				$val += 0;
97
			}
98
99
			$r->{$kv[0]} = $val;
100
		}
101
102
		return $r;
103
	}
104
105
	/**
106
	 * parse pipe separated lines
107
	 *
108
	 * key1=val1 | key2=val2 | ...
109
	 *
110
	 * @param string $str
111
	 *
112
	 * @return \stdClass[]|\stdClass
113
	 */
114
	public static function parse_list(string $str){
115
116
		if(strpos($str, '|') !== false){
117
			$str = explode('|', $str);
118
			$arr = [];
119
120
			foreach($str as $line){
121
				$arr[] = self::parse_kv($line);
122
			}
123
124
			return $arr;
125
		}
126
127
		return self::parse_kv($str);
128
	}
129
130
	/**
131
	 * @phan-suppress PhanTypeMismatchArgumentNullableInternal
132
	 */
133
	public static function unescape(string $str = null, bool $reverse = null):string{
134
		$search  = ["\\\\", "\/", "\s", "\p", "\a", "\b", "\f", "\n", "\r", "\t", "\v"];
135
		$replace = [chr(92), chr(47), chr(32), chr(124), chr(7), chr(8), chr(12), chr(10), chr(3), chr(9), chr(11)];
136
137
		return $reverse
138
			? str_replace($replace, $search, $str)
139
			: str_replace($search, $replace, $str);
140
	}
141
142
}
143