Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/infrastructure/log/PhabricatorProtocolLog.php
12241 views
1
<?php
2
3
final class PhabricatorProtocolLog
4
extends Phobject {
5
6
private $logfile;
7
private $mode;
8
private $buffer = array();
9
10
public function __construct($logfile) {
11
$this->logfile = $logfile;
12
}
13
14
public function didStartSession($session_name) {
15
$this->setMode('!');
16
$this->buffer[] = $session_name;
17
$this->flush();
18
}
19
20
public function didEndSession() {
21
$this->setMode('_');
22
$this->buffer[] = pht('<End of Session>');
23
$this->flush();
24
}
25
26
public function didWriteBytes($bytes) {
27
if (!strlen($bytes)) {
28
return;
29
}
30
31
$this->setMode('>');
32
$this->buffer[] = $bytes;
33
}
34
35
public function didReadBytes($bytes) {
36
if (!strlen($bytes)) {
37
return;
38
}
39
40
$this->setMode('<');
41
$this->buffer[] = $bytes;
42
}
43
44
public function didReadFrame($frame) {
45
$this->writeFrame('<*', $frame);
46
}
47
48
public function didWriteFrame($frame) {
49
$this->writeFrame('>*', $frame);
50
}
51
52
private function writeFrame($header, $frame) {
53
$this->flush();
54
55
$frame = explode("\n", $frame);
56
foreach ($frame as $key => $line) {
57
$frame[$key] = $header.' '.$this->escapeBytes($line);
58
}
59
$frame = implode("\n", $frame)."\n\n";
60
61
$this->writeMessage($frame);
62
}
63
64
private function setMode($mode) {
65
if ($this->mode === $mode) {
66
return $this;
67
}
68
69
if ($this->mode !== null) {
70
$this->flush();
71
}
72
73
$this->mode = $mode;
74
75
return $this;
76
}
77
78
private function flush() {
79
$mode = $this->mode;
80
$bytes = $this->buffer;
81
82
$this->mode = null;
83
$this->buffer = array();
84
85
$bytes = implode('', $bytes);
86
87
if (strlen($bytes)) {
88
$this->writeBytes($mode, $bytes);
89
}
90
}
91
92
private function writeBytes($mode, $bytes) {
93
$header = $mode;
94
$len = strlen($bytes);
95
96
$out = array();
97
switch ($mode) {
98
case '<':
99
$out[] = pht('%s Write [%s bytes]', $header, new PhutilNumber($len));
100
break;
101
case '>':
102
$out[] = pht('%s Read [%s bytes]', $header, new PhutilNumber($len));
103
break;
104
default:
105
$out[] = pht(
106
'%s %s',
107
$header,
108
$this->escapeBytes($bytes));
109
break;
110
}
111
112
switch ($mode) {
113
case '<':
114
case '>':
115
$out[] = $this->renderBytes($header, $bytes);
116
break;
117
}
118
119
$out = implode("\n", $out)."\n\n";
120
121
$this->writeMessage($out);
122
}
123
124
private function renderBytes($header, $bytes) {
125
$bytes_per_line = 48;
126
$bytes_per_chunk = 4;
127
128
// Compute the width of the "bytes" display section, which looks like
129
// this:
130
//
131
// > 00112233 44556677 abcdefgh
132
// ^^^^^^^^^^^^^^^^^
133
//
134
// We need to figure this out so we can align the plain text in the far
135
// right column appropriately.
136
137
// The character width of the "bytes" part of a full display line. If
138
// we're rendering 48 bytes per line, we'll need 96 characters, since
139
// each byte is printed as a 2-character hexadecimal code.
140
$display_bytes = ($bytes_per_line * 2);
141
142
// The character width of the number of spaces in between the "bytes"
143
// chunks. If we're rendering 12 chunks per line, we'll put 11 spaces
144
// in between them to separate them.
145
$display_spaces = (($bytes_per_line / $bytes_per_chunk) - 1);
146
147
$pad_bytes = $display_bytes + $display_spaces;
148
149
// When the protocol is plaintext, try to break it on newlines so it's
150
// easier to read.
151
$pos = 0;
152
$lines = array();
153
while (true) {
154
$next_break = strpos($bytes, "\n", $pos);
155
if ($next_break === false) {
156
$len = strlen($bytes) - $pos;
157
} else {
158
$len = ($next_break - $pos) + 1;
159
}
160
$len = min($bytes_per_line, $len);
161
162
$next_bytes = substr($bytes, $pos, $len);
163
164
$chunk_parts = array();
165
foreach (str_split($next_bytes, $bytes_per_chunk) as $chunk) {
166
$chunk_display = '';
167
for ($ii = 0; $ii < strlen($chunk); $ii++) {
168
$chunk_display .= sprintf('%02x', ord($chunk[$ii]));
169
}
170
$chunk_parts[] = $chunk_display;
171
}
172
$chunk_parts = implode(' ', $chunk_parts);
173
174
$chunk_parts = str_pad($chunk_parts, $pad_bytes, ' ');
175
176
177
$lines[] = $header.' '.$chunk_parts.' '.$this->escapeBytes($next_bytes);
178
179
$pos += $len;
180
181
if ($pos >= strlen($bytes)) {
182
break;
183
}
184
}
185
186
$lines = implode("\n", $lines);
187
188
return $lines;
189
}
190
191
private function escapeBytes($bytes) {
192
$result = '';
193
for ($ii = 0; $ii < strlen($bytes); $ii++) {
194
$c = $bytes[$ii];
195
$o = ord($c);
196
197
if ($o >= 0x20 && $o <= 0x7F) {
198
$result .= $c;
199
} else {
200
$result .= '.';
201
}
202
}
203
return $result;
204
}
205
206
private function writeMessage($message) {
207
$f = fopen($this->logfile, 'a');
208
fwrite($f, $message);
209
fflush($f);
210
fclose($f);
211
}
212
213
}
214
215