1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Vend\Statsd; |
4
|
|
|
|
5
|
|
|
|
6
|
|
|
/** |
7
|
|
|
* StatsD Client |
8
|
|
|
* |
9
|
|
|
* Holds and sends a queue of metric instances to a socket. Uses the provided socket and factory to do so. Exposes |
10
|
|
|
* methods on the factory via __call, which is a little dynamic for some tastes, but also damn convenient. |
11
|
|
|
* |
12
|
|
|
* @method MetricInterface counter(string $key, int $delta) |
13
|
|
|
* @method MetricInterface increment(string $key) |
14
|
|
|
* @method MetricInterface decrement(string $key) |
15
|
|
|
* @method MetricInterface gauge(string $key, int|float $value) |
16
|
|
|
* @method MetricInterface timer(string $key, float $value) |
17
|
|
|
* @method MetricInterface set(string $key, mixed $value) |
18
|
|
|
*/ |
19
|
|
|
class Client |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* An ordered list of metrics yet to be sent |
23
|
|
|
* |
24
|
|
|
* @var MetricInterface[] |
25
|
|
|
*/ |
26
|
|
|
protected $queue = []; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var Socket |
30
|
|
|
*/ |
31
|
|
|
protected $socket; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var FactoryInterface |
35
|
|
|
*/ |
36
|
|
|
protected $factory; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Client constructor |
40
|
|
|
* |
41
|
|
|
* @param Socket $socket |
42
|
|
|
* @param FactoryInterface $factory |
43
|
|
|
*/ |
44
|
3 |
|
public function __construct(Socket $socket, FactoryInterface $factory) |
45
|
|
|
{ |
46
|
3 |
|
$this->socket = $socket; |
47
|
3 |
|
$this->factory = $factory; |
48
|
3 |
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Forward methods to the factory |
52
|
|
|
* |
53
|
|
|
* @param string $name |
54
|
|
|
* @param array $arguments |
55
|
|
|
* @return Metric |
56
|
|
|
*/ |
57
|
2 |
|
public function __call($name, $arguments) |
58
|
|
|
{ |
59
|
2 |
|
if (method_exists($this->factory, $name)) { |
60
|
1 |
|
$metric = call_user_func_array([$this->factory, $name], $arguments); |
61
|
1 |
|
$this->add($metric); |
62
|
1 |
|
return $metric; |
63
|
|
|
} |
64
|
|
|
|
65
|
1 |
|
throw new \BadMethodCallException('No such StatsD factory method: ' . $name); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Enqueues a metric to be sent on the next flush |
70
|
|
|
* |
71
|
|
|
* @param MetricInterface $metric |
72
|
|
|
*/ |
73
|
1 |
|
public function add(MetricInterface $metric) |
74
|
|
|
{ |
75
|
1 |
|
$this->queue[] = $metric; |
76
|
1 |
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Flushes the queued metrics |
80
|
|
|
*/ |
81
|
1 |
|
public function flush() |
82
|
|
|
{ |
83
|
1 |
|
$this->socket->open(); |
84
|
|
|
|
85
|
1 |
|
$metrics = array_map(function (MetricInterface $metric) { |
86
|
1 |
|
return $metric->getData(); |
87
|
1 |
|
}, $this->queue); |
88
|
|
|
|
89
|
1 |
|
$packets = $this->fillPackets($metrics, Socket::MAX_DATAGRAM_SIZE); |
|
|
|
|
90
|
|
|
|
91
|
1 |
|
foreach ($packets as $packet) { |
92
|
1 |
|
$this->socket->write($packet); |
93
|
1 |
|
} |
94
|
|
|
|
95
|
1 |
|
$this->queue = []; |
96
|
1 |
|
$this->socket->close(); |
97
|
1 |
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Splits an array of pieces of data into combined pieces no larger than the given max |
101
|
|
|
* |
102
|
|
|
* @param String[] $data |
103
|
|
|
* @return array<array<int,string>> |
|
|
|
|
104
|
|
|
*/ |
105
|
2 |
|
protected function fillPackets(array $data) |
106
|
|
|
{ |
107
|
2 |
|
$maxLength = Socket::MAX_DATAGRAM_SIZE; |
108
|
2 |
|
$glue = "\n"; |
109
|
|
|
|
110
|
|
|
// The result array of strings, each shorter than $maxLength (unless a piece is larger on its own) |
111
|
2 |
|
$result = ['']; |
112
|
|
|
|
113
|
|
|
// The index, in the result array, of the piece we're currently appending to |
114
|
2 |
|
$index = 0; |
115
|
|
|
|
116
|
|
|
// The size of the current piece |
117
|
2 |
|
$size = 0; |
118
|
|
|
|
119
|
2 |
|
foreach ($data as $metric) { |
120
|
2 |
|
$len = strlen($glue . $metric); |
121
|
|
|
|
122
|
2 |
|
if (($size + $len) > $maxLength) { |
123
|
1 |
|
$result[++$index] = $metric; // Fill the next part of the result |
124
|
1 |
|
$size = $len; |
125
|
1 |
|
} else { |
126
|
2 |
|
$result[$index] .= ($size != 0 ? $glue : '') . $metric; |
127
|
2 |
|
$size += $len; |
128
|
|
|
} |
129
|
2 |
|
} |
130
|
|
|
|
131
|
2 |
|
return $result; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.