Commit | Line | Data |
---|---|---|
b4454468 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
4 | | Copyright CiviCRM LLC. All rights reserved. | | |
5 | | | | |
6 | | This work is published under the GNU AGPLv3 license with some | | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
9 | +--------------------------------------------------------------------+ | |
10 | */ | |
11 | ||
12 | namespace Civi\Pipe; | |
13 | ||
14 | /** | |
15 | * Synchronous line-oriented communication session. | |
16 | * | |
17 | * @code | |
18 | * $session = new class { | |
19 | * use LineSessionTrait; | |
20 | * protected function onRequest(string $requestLine): ?string { | |
21 | * return "Thanks"; | |
22 | * } | |
23 | * protected function onException(string $requestLine, \Throwable $t): ?string { | |
24 | * return "Oops"; | |
25 | * } | |
26 | * } | |
27 | * $session->setIO(STDIN, STDOUT)->run(); | |
28 | * @endCode | |
29 | */ | |
30 | trait LineSessionTrait { | |
31 | ||
32 | /** | |
33 | * The onConnect() method is called when a new session is opened. | |
34 | * | |
35 | * @return string|null | |
36 | * Header/welcome line, or NULL if none. | |
37 | */ | |
38 | protected function onConnect(): ?string { | |
39 | return NULL; | |
40 | } | |
41 | ||
42 | /** | |
43 | * The onRequest() method is called after receiving one line of text. | |
44 | * | |
45 | * @param string $requestLine | |
46 | * The received line of text. | |
47 | * @return string|null | |
48 | * The line to send back, or NULL if none. | |
49 | */ | |
50 | abstract protected function onRequest(string $requestLine): ?string; | |
51 | ||
52 | /** | |
53 | * The onRequest() method is called after receiving one line of text. | |
54 | * | |
55 | * @param string $requestLine | |
56 | * The received line of text - which led to the unhandled exception. | |
57 | * @param \Throwable $t | |
58 | * The unhandled exception. | |
59 | * @return string|null | |
60 | * The line to send back, or NULL if none. | |
61 | */ | |
62 | abstract protected function onException(string $requestLine, \Throwable $t): ?string; | |
63 | ||
64 | /** | |
65 | * @var resource | |
66 | * Ex: STDIN | |
67 | */ | |
68 | protected $input; | |
69 | ||
70 | /** | |
71 | * @var resource | |
72 | * Ex: STDOUT | |
73 | */ | |
74 | protected $output; | |
75 | ||
76 | /** | |
77 | * Line-delimiter. | |
78 | * | |
79 | * @var string | |
80 | */ | |
81 | protected $delimiter = "\n"; | |
82 | ||
83 | /** | |
84 | * Maximum size of the buffer for reading lines. | |
85 | * | |
86 | * Clients may need to set this if they submit large requests. | |
87 | * | |
88 | * @var int | |
89 | */ | |
90 | protected $maxLine = 524288; | |
91 | ||
92 | /** | |
93 | * A value to display immediately before the response lines. | |
94 | * | |
95 | * Clients may set this is if they want to detect and skip buggy noise. | |
96 | * | |
97 | * @var string|null | |
98 | * Ex: chr(1).chr(1) | |
99 | */ | |
100 | protected $responsePrefix = NULL; | |
101 | ||
102 | /** | |
103 | * @param resource|null $input | |
104 | * @param resource|null $output | |
105 | */ | |
106 | public function __construct($input = NULL, $output = NULL) { | |
107 | $this->input = $input; | |
108 | $this->output = $output; | |
109 | } | |
110 | ||
111 | /** | |
112 | * Run the main loop. Poll for commands on $input and write responses to $output. | |
113 | */ | |
114 | public function run() { | |
115 | $this->write($this->onConnect()); | |
116 | ||
117 | while (FALSE !== ($line = stream_get_line($this->input, $this->maxLine, $this->delimiter))) { | |
118 | $line = rtrim($line, $this->delimiter); | |
119 | if (empty($line)) { | |
120 | continue; | |
121 | } | |
122 | ||
123 | try { | |
124 | $response = $this->onRequest($line); | |
125 | } | |
126 | catch (\Throwable $t) { | |
127 | $response = $this->onException($line, $t); | |
128 | } | |
129 | $this->write($response); | |
130 | } | |
131 | } | |
132 | ||
133 | /** | |
134 | * @return int | |
135 | */ | |
136 | public function getMaxLine(): int { | |
137 | return $this->maxLine; | |
138 | } | |
139 | ||
140 | /** | |
141 | * @param int $maxLine | |
142 | * @return $this | |
143 | */ | |
144 | public function setMaxLine(int $maxLine) { | |
145 | $this->maxLine = $maxLine; | |
146 | return $this; | |
147 | } | |
148 | ||
149 | /** | |
150 | * @return string|null | |
151 | */ | |
152 | public function getResponsePrefix(): ?string { | |
153 | return $this->responsePrefix; | |
154 | } | |
155 | ||
156 | /** | |
157 | * @param string|null $responsePrefix | |
158 | * @return $this | |
159 | */ | |
160 | public function setResponsePrefix(?string $responsePrefix) { | |
161 | $this->responsePrefix = $responsePrefix; | |
162 | return $this; | |
163 | } | |
164 | ||
165 | /** | |
166 | * @param resource $input | |
167 | * @param resource $output | |
168 | * @return $this | |
169 | */ | |
170 | public function setIO($input, $output) { | |
171 | $this->input = $input; | |
172 | $this->output = $output; | |
173 | return $this; | |
174 | } | |
175 | ||
176 | protected function write(?string $response): void { | |
177 | if ($response === NULL) { | |
178 | return; | |
179 | } | |
180 | if ($this->responsePrefix !== NULL) { | |
181 | fwrite($this->output, $this->responsePrefix); | |
182 | } | |
183 | fwrite($this->output, $response); | |
184 | fwrite($this->output, $this->delimiter); | |
185 | } | |
186 | ||
187 | } |