PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 1.9.13 All 162 releases
woocommerce-pos / includes / Services / Provider.php

Provider.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.18, at includes/Services/Provider.php

329 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cloud-print provider capabilities value object.
4 *
5 * Single source of truth for per-provider knowledge: validity, the default for
6 * rows without a provider, polling, content types, poll endpoints, server
7 * diagnostics, thermal wire formats and renderable template engines.
8 *
9 * @package WCPOS\WooCommercePOS\Services
10 */
11
12 namespace WCPOS\WooCommercePOS\Services;
13
14 use WCPOS\WooCommercePOS\Interfaces\Provider_Adapter_Interface;
15 use WCPOS\WooCommercePOS\Interfaces\Poll_Provider_Adapter_Interface;
16 use WCPOS\WooCommercePOS\Services\Providers\Epson_Sdp_Adapter;
17 use WCPOS\WooCommercePOS\Services\Providers\Printnode_Adapter;
18 use WCPOS\WooCommercePOS\Services\Providers\Star_Cloudprnt_Adapter;
19 use WCPOS\WooCommercePOS\Services\Providers\Star_Online_Adapter;
20
21 /**
22 * Provider class.
23 */
24 class Provider {
25 /**
26 * Provider assumed for printer rows that predate the provider field.
27 *
28 * Star CloudPRNT was the only provider before the field existed, so a row
29 * without one is a Star CloudPRNT printer.
30 */
31 public const DEFAULT_PROVIDER = 'star-cloudprnt';
32
33 /**
34 * Per-provider capability map.
35 *
36 * @var array<string, array<string, mixed>>
37 */
38 private const CAPABILITIES = array(
39 // StarPRNT-native printers (the whole TSP100 line) cannot decode
40 // ESC/POS; Star's docs advise against octet-stream for command data,
41 // so jobs are emitted as native StarPRNT under the vnd.star type.
42 'star-cloudprnt' => array(
43 'polling' => true,
44 'content_type' => 'application/vnd.star.starprnt',
45 'poll_endpoint' => 'cloudprnt',
46 'supports_server_diagnostic' => true,
47 'thermal_wire_format' => 'starprnt',
48 'template_engines' => 'thermal',
49 'stores_job_kind' => false,
50 // The StarPRNT emitter emits a native drawer pulse (and inserts one
51 // before the trailing cut when a job asks for it), so drawer metadata
52 // has to survive to render time.
53 'supports_drawer' => true,
54 ),
55 'epson-sdp' => array(
56 'polling' => true,
57 'content_type' => 'application/xml',
58 'poll_endpoint' => 'epson-sdp',
59 'supports_server_diagnostic' => true,
60 'thermal_wire_format' => 'epos-xml',
61 'template_engines' => 'thermal',
62 'stores_job_kind' => false,
63 'supports_drawer' => true,
64 ),
65 'printnode' => array(
66 'polling' => false,
67 'content_type' => 'application/pdf',
68 'poll_endpoint' => null,
69 'supports_server_diagnostic' => false,
70 'thermal_wire_format' => null,
71 'template_engines' => 'all',
72 'stores_job_kind' => true,
73 'supports_drawer' => true,
74 ),
75 'star-online' => array(
76 'polling' => false,
77 'content_type' => 'text/vnd.star.markup',
78 'poll_endpoint' => null,
79 'supports_server_diagnostic' => false,
80 'thermal_wire_format' => 'star-markup',
81 'template_engines' => 'thermal',
82 'stores_job_kind' => false,
83 'supports_drawer' => false,
84 ),
85 );
86
87 /**
88 * List of valid provider keys.
89 *
90 * @return array<int, string>
91 */
92 public static function valid(): array {
93 return array_keys( self::CAPABILITIES );
94 }
95
96 /**
97 * Resolve a stored printer row's provider to a known provider key.
98 *
99 * Callers read `$printer['provider']` from an option that predates the
100 * field, so the value can be missing, empty, or (for hand-edited options)
101 * a key this build does not know. All three resolve to the default rather
102 * than to a silent no-provider state.
103 *
104 * @param string|null $provider Raw provider value from a printer row.
105 *
106 * @return string A key from self::valid().
107 */
108 public static function normalize( ?string $provider ): string {
109 return \in_array( $provider, self::valid(), true ) ? (string) $provider : self::DEFAULT_PROVIDER;
110 }
111
112 /**
113 * Resolve a provider adapter.
114 *
115 * An empty legacy-row value uses normalize()'s Star CloudPRNT default;
116 * non-empty unknown keys remain unknown and return null.
117 *
118 * @param string $provider Provider key or empty legacy-row value.
119 *
120 * @return Provider_Adapter_Interface|null
121 */
122 public static function adapter( string $provider ): ?Provider_Adapter_Interface {
123 $provider = '' === $provider ? self::normalize( $provider ) : $provider;
124
125 switch ( $provider ) {
126 case 'star-cloudprnt':
127 return new Star_Cloudprnt_Adapter();
128 case 'epson-sdp':
129 return new Epson_Sdp_Adapter();
130 case 'printnode':
131 return new Printnode_Adapter();
132 case 'star-online':
133 return new Star_Online_Adapter();
134 default:
135 return null;
136 }
137 }
138
139 /**
140 * Resolve the wire format and HTTP content type for a print job.
141 *
142 * @param array $printer Printer configuration.
143 * @param array $template Template configuration.
144 *
145 * @return array{kind:string, content_type:string}
146 */
147 public static function format( array $printer, array $template ): array {
148 // Printer rows saved before the provider field existed have none; they must behave as the default provider.
149 $provider = self::normalize( \is_string( $printer['provider'] ?? null ) ? $printer['provider'] : null );
150 $adapter = self::adapter( $provider );
151 if ( null === $adapter ) {
152 return array(
153 'kind' => '',
154 'content_type' => '',
155 );
156 }
157
158 return $adapter->format( $printer, $template );
159 }
160
161 /**
162 * Resolve the HTTP content type for a printer when no template is in hand.
163 *
164 * The reprint path uses this only when the template cannot be rendered and
165 * the job carries no `pn_kind`; otherwise it keeps the stored content type
166 * so the two halves cannot drift apart.
167 *
168 * PrintNode reports its PDF default even for a printer in raw mode. The
169 * reprint path's `pn_kind` condition keeps raw jobs away from this answer.
170 * Prefer format() when a template is in hand to resolve both halves together.
171 *
172 * @param array $printer Printer configuration.
173 *
174 * @return string
175 */
176 public static function printer_content_type( array $printer ): string {
177 $provider = self::normalize( \is_string( $printer['provider'] ?? null ) ? $printer['provider'] : null );
178 $adapter = self::adapter( $provider );
179
180 return null === $adapter ? 'application/octet-stream' : $adapter->content_type();
181 }
182
183 /**
184 * Resolve only polling adapters without widening the base provider contract.
185 *
186 * @param string $key Provider key.
187 * @return Poll_Provider_Adapter_Interface|null
188 */
189 public static function poll_adapter( string $key ): ?Poll_Provider_Adapter_Interface {
190 $adapter = self::adapter( $key );
191 return $adapter instanceof Poll_Provider_Adapter_Interface ? $adapter : null;
192 }
193
194 /**
195 * Whether the provider polls the server for jobs.
196 *
197 * @param string $provider Provider key.
198 *
199 * @return bool
200 */
201 public static function is_polling( string $provider ): bool {
202 return (bool) ( self::CAPABILITIES[ $provider ]['polling'] ?? false );
203 }
204
205 /**
206 * Whether the provider needs an out-of-band submit (we push jobs to it),
207 * as opposed to a polling provider that fetches jobs itself.
208 *
209 * @param string $provider Provider key.
210 *
211 * @return bool
212 */
213 public static function requires_submit( string $provider ): bool {
214 return \in_array( $provider, self::valid(), true ) && ! self::is_polling( $provider );
215 }
216
217 /**
218 * HTTP content type for the provider's job payloads.
219 *
220 * @param string $provider Provider key.
221 *
222 * @return string
223 */
224 public static function content_type( string $provider ): string {
225 return (string) ( self::CAPABILITIES[ $provider ]['content_type'] ?? 'application/octet-stream' );
226 }
227
228 /**
229 * REST poll-endpoint slug for the provider.
230 *
231 * @param string $provider Provider key.
232 *
233 * @return string|null
234 */
235 public static function poll_endpoint( string $provider ): ?string {
236 return self::CAPABILITIES[ $provider ]['poll_endpoint'] ?? null;
237 }
238
239 /**
240 * Whether the provider supports a server-built diagnostic payload.
241 *
242 * @param string $provider Provider key.
243 *
244 * @return bool
245 */
246 public static function supports_server_diagnostic( string $provider ): bool {
247 return (bool) ( self::CAPABILITIES[ $provider ]['supports_server_diagnostic'] ?? false );
248 }
249
250 /**
251 * Thermal wire format for the given provider/engine pair.
252 *
253 * Only the 'thermal' engine on a direct printer yields a wire format;
254 * any other engine, or an unknown provider, returns null.
255 *
256 * @param string $provider Provider key.
257 * @param string $engine Render engine (e.g. 'thermal', 'logicless').
258 *
259 * @return string|null
260 */
261 public static function wire_format( string $provider, string $engine ): ?string {
262 if ( 'thermal' !== $engine ) {
263 return null;
264 }
265
266 return self::CAPABILITIES[ $provider ]['thermal_wire_format'] ?? null;
267 }
268
269 /**
270 * Receipt-template engines the provider can render for automatic jobs.
271 *
272 * 'all' means every active template; 'thermal' means thermal templates
273 * only. Unknown providers are treated as thermal-only, the conservative
274 * answer for a printer we cannot render a PDF for.
275 *
276 * @param string $provider Provider key.
277 *
278 * @return string 'all' or 'thermal'.
279 */
280 public static function template_engines( string $provider ): string {
281 return (string) ( self::CAPABILITIES[ $provider ]['template_engines'] ?? 'thermal' );
282 }
283
284 /**
285 * Whether jobs for the provider persist their resolved kind separately.
286 *
287 * @param string $provider Provider key.
288 *
289 * @return bool
290 */
291 public static function stores_job_kind( string $provider ): bool {
292 return (bool) ( self::CAPABILITIES[ $provider ]['stores_job_kind'] ?? false );
293 }
294
295 /**
296 * Whether the provider supports the generic drawer metadata contract.
297 *
298 * @param string $provider Provider key.
299 *
300 * @return bool
301 */
302 public static function supports_drawer( string $provider ): bool {
303 return (bool) ( self::CAPABILITIES[ $provider ]['supports_drawer'] ?? false );
304 }
305
306 /**
307 * Per-provider facts the settings screen cannot derive, keyed by provider.
308 *
309 * Projected onto the cloud-print settings response (cf.
310 * Cloud_Print_Relay_Service::public_state()) so the admin app can read the
311 * provider table from the server instead of re-declaring it. Deliberately
312 * narrow: presentation (labels, badges) stays in the client, and facts the
313 * client already renders from its own table are not duplicated here until
314 * something reads them.
315 *
316 * @return array<string, array<string, string>>
317 */
318 public static function public_capabilities(): array {
319 $capabilities = array();
320 foreach ( self::valid() as $provider ) {
321 $capabilities[ $provider ] = array(
322 'template_engines' => self::template_engines( $provider ),
323 );
324 }
325
326 return $capabilities;
327 }
328 }
329