Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
17 / 17
CRAP
100.00% covered (success)
100.00%
1 / 1
Options
100.00% covered (success)
100.00%
41 / 41
100.00% covered (success)
100.00%
17 / 17
34
100.00% covered (success)
100.00%
1 / 1
 isValidOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 isCommandOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isPhpOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isValueOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isBooleanOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCommandOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCommandOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPhpOptions
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
7
 getPhpOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValueOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValueOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOptionValueByName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getOptionNameByValue
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
10
 hasOptionValueByName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasOptionNameByValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOmitOptions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isOmitOption
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Http\Client\Handler\Curl;
16
17/**
18 * HTTP client curl options class
19 *
20 * @category   Pop
21 * @package    Pop\Http
22 * @author     Nick Sagona, III <nick@popphp.org>
23 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    6.0.0
26 */
27class Options
28{
29
30    /**
31     * Curl CLI-to-PHP options
32     * @var array
33     */
34    protected static array $commandOptions = [
35        '--abstract-unix-socket'      => 'CURLOPT_ABSTRACT_UNIX_SOCKET',                                   // --abstract-unix-socket <path>
36        '--alt-svc'                   => 'CURLOPT_ALTSVC',                                                 // --alt-svc <filename>
37        '-a'                          => 'CURLOPT_APPEND',
38        '--aws-sigv4'                 => 'CURLOPT_AWS_SIGV4',                                              // --aws-sigv4 <provider1[:provider2[:region[:service]]]>
39        '--cacert'                    => 'CURLOPT_CAINFO',                                                 // --cacert <file>
40        '--capath'                    => 'CURLOPT_CAPATH',                                                 // --capath <dir>
41        '--connect-timeout'           => ['CURLOPT_CONNECTTIMEOUT', 'CURLOPT_TIMEOUT', 'CURLOPT_TIMEOUT_MS'], // --connect-timeout <fractional seconds> (CURLOPT_CONNECTTIMEOUT is the semantically-correct target and must resolve first; MS needs to be converted to seconds)
42        '--connect-to'                => 'CURLOPT_CONNECT_TO',                                             // --connect-to <HOST1:PORT1:HOST2:PORT2>
43        '-b'                          => 'CURLOPT_COOKIE',                                                 // -b, --cookie <data|filename>
44        '--cookie'                    => 'CURLOPT_COOKIE',                                                 // -b, --cookie <data|filename>
45        '-c'                          => 'CURLOPT_COOKIEJAR',                                              // -c, --cookie-jar <filename>
46        '--cookie-jar'                => 'CURLOPT_COOKIEJAR',                                              // -c, --cookie-jar <filename>
47        '--crlf'                      => 'CURLOPT_CRLF',
48        '--crlfile'                   => 'CURLOPT_CRLFILE',                                                // --crlfile <file>
49        '-X'                          => ['CURLOPT_POST', 'CURLOPT_PUT', 'CURLOPT_CUSTOMREQUEST'],         // -X, --request <method>
50        '--request'                   => ['CURLOPT_POST', 'CURLOPT_PUT', 'CURLOPT_CUSTOMREQUEST'],         // -X, --request <method>
51        '--disallow-username-in-url'  => 'CURLOPT_DISALLOW_USERNAME_IN_URL',
52        '--dns-interface'             => 'CURLOPT_DNS_INTERFACE',                                          // --dns-interface <interface>
53        '--dns-ipv4-addr'             => 'CURLOPT_DNS_LOCAL_IP4',                                          // --dns-ipv4-addr <address>
54        '--dns-ipv6-addr'             => 'CURLOPT_DNS_LOCAL_IP6',                                          // --dns-ipv6-addr <address>
55        '--dns-servers'               => 'CURLOPT_DNS_SERVERS',                                            // --dns-servers <addresses>
56        '--doh-insecure'              => ['CURLOPT_DOH_SSL_VERIFYPEER', 'CURLOPT_DOH_SSL_VERIFYHOST'],     // Verify the DNS-over-HTTPS server's SSL certificate name fields against the host name and certificate. Available as of PHP 8.2.0 and cURL 7.76.0.
57        '--no-doh-insecure'           => ['CURLOPT_DOH_SSL_VERIFYPEER', 'CURLOPT_DOH_SSL_VERIFYHOST'],     // Verify the DNS-over-HTTPS server's SSL certificate name fields against the host name and certificate. Available as of PHP 8.2.0 and cURL 7.76.0.
58        '--doh-cert-status'           => 'CURLOPT_DOH_SSL_VERIFYSTATUS',                                   // Tell cURL to verify the status of the DNS-over-HTTPS server certificate using the "Certificate Status Request" TLS extension (OCSP stapling). Available as of PHP 8.2.0 and cURL 7.76.0.
59        '--doh-url'                   => 'CURLOPT_DOH_URL',                                                // --doh-url <url> Provides the DNS-over-HTTPS URL. Available as of PHP 8.1.0 and cURL 7.62.0.
60        '--expect100-timeout'         => 'CURLOPT_EXPECT_100_TIMEOUT_MS',                                  // --expect100-timeout <seconds>
61        '-L'                          => 'CURLOPT_FOLLOWLOCATION',                                         // Not available when open_basedir is enabled.
62        '--location'                  => 'CURLOPT_FOLLOWLOCATION',                                         // Not available when open_basedir is enabled.
63        '-P'                          => 'CURLOPT_FTPPORT',                                                // -P, --ftp-port <address>
64        '--ftp-port'                  => 'CURLOPT_FTPPORT',                                                // -P, --ftp-port <address>
65        '--ftp-account'               => 'CURLOPT_FTP_ACCOUNT',                                            // --ftp-account <data>
66        '--ftp-alternative-to-user'   => 'CURLOPT_FTP_ALTERNATIVE_TO_USER',                                // --ftp-alternative-to-user <command>
67        '--ftp-create-dirs'           => 'CURLOPT_FTP_CREATE_MISSING_DIRS',
68        '--ftp-method'                => 'CURLOPT_FTP_FILEMETHOD',                                         // --ftp-method <method>
69        '--ftp-skip-pasv-ip'          => 'CURLOPT_FTP_SKIP_PASV_IP',
70        '--ftp-pasv'                  => 'CURLOPT_FTP_SKIP_PASV_IP',
71        '--ftp-ssl-ccc'               => 'CURLOPT_FTP_SSL_CCC',
72        '--disable-eprt'              => 'CURLOPT_FTP_USE_EPRT',
73        '--disable-epsv'              => 'CURLOPT_FTP_USE_EPSV',
74        '--ftp-pret'                  => 'CURLOPT_FTP_USE_PRET',
75        '--delegation'                => 'CURLOPT_GSSAPI_DELEGATION',                                      // --delegation <level>
76        '--happy-eyeballs-timeout-ms' => 'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS',                              // --happy-eyeballs-timeout-ms <milliseconds>
77        '--haproxy-protocol'          => 'CURLOPT_HAPROXYPROTOCOL',
78        '-i'                          => 'CURLOPT_HEADER',
79        '--include'                   => 'CURLOPT_HEADER',
80        '--hsts'                      => 'CURLOPT_HSTS',                                                   // --hsts <filename>
81        '--http0.9'                   => 'CURLOPT_HTTP09_ALLOWED',
82        '-H'                          => 'CURLOPT_HTTPHEADER',                                             // -H, --header <header/@file>
83        '--header'                    => 'CURLOPT_HTTPHEADER',                                             // -H, --header <header/@file>
84        '-p'                          => 'CURLOPT_HTTPPROXYTUNNEL',
85        '--proxytunnel'               => 'CURLOPT_HTTPPROXYTUNNEL',
86        '-0'                          => 'CURLOPT_HTTP_VERSION',
87        '--http1.0'                   => 'CURLOPT_HTTP_VERSION',
88        '--http1.1'                   => 'CURLOPT_HTTP_VERSION',
89        '--http2'                     => 'CURLOPT_HTTP_VERSION',
90        '--ignore-content-length'     => 'CURLOPT_IGNORE_CONTENT_LENGTH',
91        '-T'                          => 'CURLOPT_INFILE',                                                 // -T, --upload-file <file> (Used with PUT)
92        '--upload-file'               => 'CURLOPT_INFILE',                                                 // -T, --upload-file <file> (Used with PUT)
93        '--interface'                 => 'CURLOPT_INTERFACE',                                              // --interface <name>
94        '--krb'                       => 'CURLOPT_KRBLEVEL',                                               // --krb <level>
95        '--local-port'                => ['CURLOPT_LOCALPORT', 'CURLOPT_LOCALPORTRANGE'],                  // --local-port <num/range>
96        '--login-options'             => 'CURLOPT_LOGIN_OPTIONS',                                          // --login-options <options>
97        '-Y'                          => 'CURLOPT_LOW_SPEED_LIMIT',                                        // -Y, --speed-limit <speed>
98        '--speed-limit'               => 'CURLOPT_LOW_SPEED_LIMIT',                                        // -Y, --speed-limit <speed>
99        '-y'                          => 'CURLOPT_LOW_SPEED_TIME',                                         // -y, --speed-time <seconds>
100        '--speed-time'                => 'CURLOPT_LOW_SPEED_TIME',                                         // -y, --speed-time <seconds>
101        '--mail-auth'                 => 'CURLOPT_MAIL_AUTH',                                              // --mail-auth <address>
102        '--mail-from'                 => 'CURLOPT_MAIL_FROM',                                              // --mail-from <address>
103        '--mail-rcpt'                 => 'CURLOPT_MAIL_RCPT',                                              // --mail-rcpt <address>
104        '--mail-rcpt-allowfails'      => 'CURLOPT_MAIL_RCPT_ALLLOWFAILS',
105        '--max-filesize'              => 'CURLOPT_MAXFILESIZE',                                            // --max-filesize <bytes>
106        '-m'                          => 'CURLOPT_MAXLIFETIME_CONN',                                       // -m, --max-time <fractional seconds>
107        '--max-time'                  => 'CURLOPT_MAXLIFETIME_CONN',                                       // -m, --max-time <fractional seconds>
108        '--max-redirs'                => 'CURLOPT_MAXREDIRS',                                              // --max-redirs <num>
109        '-n'                          => 'CURLOPT_NETRC',
110        '--netrc'                     => 'CURLOPT_NETRC',
111        '--netrc-file'                => 'CURLOPT_NETRC_FILE',                                             // --netrc-file <filename>
112        '--no-progress-meter'         => 'CURLOPT_NOPROGRESS',
113        '--noproxy'                   => 'CURLOPT_NOPROXY',                                                // --noproxy <no-proxy-list>
114        '-u'                          => ['CURLOPT_USERPWD', 'CURLOPT_USERNAME', 'CURLOPT_PASSWORD'],      // -u, --user <user:password>
115        '--user'                      => ['CURLOPT_USERPWD', 'CURLOPT_USERNAME', 'CURLOPT_PASSWORD'],      // -u, --user <user:password>
116        '-d'                          => 'CURLOPT_POSTFIELDS',                                             // -d, --data <data>
117        '--data'                      => 'CURLOPT_POSTFIELDS',                                             // -d, --data <data> (was mistyped as '-data', a single dash, so it was never recognized as a real CLI flag by isCommandOption())
118        '-F'                          => 'CURLOPT_POSTFIELDS',                                             // -F, --form <name=content>
119        '--form'                      => 'CURLOPT_POSTFIELDS',                                             // -F, --form <name=content>
120        '-x'                          => 'CURLOPT_PROXY',                                                  // -x, --proxy [protocol://]host[:port]
121        '--proxy'                     => 'CURLOPT_PROXY',                                                  // -x, --proxy [protocol://]host[:port]
122        '--proxy-basic'               => 'CURLOPT_PROXYAUTH',
123        '--proxy-digest'              => 'CURLOPT_PROXYAUTH',
124        '--proxy-header'              => 'CURLOPT_PROXYHEADER',                                            // --proxy-header <header/@file>
125        '-U'                          => 'CURLOPT_PROXYUSERPWD',                                           // -U, --proxy-user <user:password>
126        '--proxy-user'                => 'CURLOPT_PROXYUSERPWD',                                           // -U, --proxy-user <user:password>
127        '--proxy-cacert'              => 'CURLOPT_PROXY_CAINFO',                                           // --proxy-cacert <file>
128        '--proxy-capath'              => 'CURLOPT_PROXY_CAPATH',                                           // --proxy-capath <dir>
129        '--proxy-crlfile'             => 'CURLOPT_PROXY_CRLFILE',                                          // --proxy-crlfile <file>
130        '--proxy-pass'                => 'CURLOPT_PROXY_KEYPASSWD',                                        // --proxy-pass <phrase>
131        '--proxy-pinnedpubkey'        => 'CURLOPT_PROXY_PINNEDPUBLICKEY',                                  // --proxy-pinnedpubkey <hashes>
132        '--proxy-service-name'        => 'CURLOPT_PROXY_SERVICE_NAME',                                     // --proxy-service-name <name>
133        '--proxy-cert'                => 'CURLOPT_PROXY_SSLCERT',                                          // --proxy-cert <cert[:passwd]>
134        '--proxy-cert-type'           => 'CURLOPT_PROXY_SSLCERTTYPE',                                      // --proxy-cert-type <type>
135        '--proxy-key'                 => 'CURLOPT_PROXY_SSLKEY',                                           // --proxy-key <key>
136        '--proxy-key-type'            => 'CURLOPT_PROXY_SSLKEYTYPE',                                       // --proxy-key-type <type>
137        '--proxy-ciphers'             => 'CURLOPT_PROXY_SSL_CIPHER_LIST',                                  // --proxy-ciphers <list>
138        '--proxy-insecure'            => ['CURLOPT_PROXY_SSL_VERIFYHOST', 'CURLOPT_PROXY_SSL_VERIFYPEER'],
139        '--proxy-tls13-ciphers'       => 'CURLOPT_PROXY_TLS13_CIPHERS',                                    // --proxy-tls13-ciphers <ciphersuite list>
140        '--proxy-tlspassword'         => 'CURLOPT_PROXY_TLSAUTH_PASSWORD',                                 // --proxy-tlspassword <string>
141        '--proxy-tlsauthtype'         => 'CURLOPT_PROXY_TLSAUTH_TYPE',                                     // --proxy-tlsauthtype <type>
142        '--proxy-tlsuser'             => 'CURLOPT_PROXY_TLSAUTH_USERNAME',                                 // --proxy-tlsuser <name>
143        '-Q'                          => 'CURLOPT_QUOTE',                                                  // -Q, --quote <command>
144        '--quote'                     => 'CURLOPT_QUOTE',                                                  // -Q, --quote <command>
145        '--random-file'               => 'CURLOPT_RANDOM_FILE',                                            // --random-file <file>
146        '-r'                          => 'CURLOPT_RANGE',                                                  // -r, --range <range>
147        '--range'                     => 'CURLOPT_RANGE',                                                  // -r, --range <range>
148        '-e'                          => 'CURLOPT_REFERER',                                                // -e, --referer <url>
149        '--referer'                   => 'CURLOPT_REFERER',                                                // -e, --referer <url>
150        '--resolve'                   => 'CURLOPT_RESOLVE',                                                // --resolve <[+]host:port:addr[,addr]...>
151        '--sasl-authzid'              => 'CURLOPT_SASL_AUTHZID',                                           // --sasl-authzid <identity>
152        '--sasl-ir'                   => 'CURLOPT_SASL_IR',
153        '--service-name'              => 'CURLOPT_SERVICE_NAME',                                           // --service-name <name>
154        '--socks5-basic'              => 'CURLOPT_SOCKS5_AUTH',
155        '--socks5-gssapi-nec'         => 'CURLOPT_SOCKS5_GSSAPI_NEC',
156        '--socks5-gssapi-service'     => 'CURLOPT_SOCKS5_GSSAPI_SERVICE',                                  // --socks5-gssapi-service <name>
157        '--compressed-ssh'            => 'CURLOPT_SSH_COMPRESSION',
158        '--hostpubmd5'                => 'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5',                                // --hostpubmd5 <md5>
159        '--hostpubsha256'             => 'CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256',                             // --hostpubsha256 <sha256>
160        '--pubkey'                    => 'CURLOPT_SSH_PUBLIC_KEYFILE',                                     // --pubkey <key>
161        '-E'                          => 'CURLOPT_SSLCERT',                                                // -E, --cert <certificate[:password]>
162        '--cert'                      => 'CURLOPT_SSLCERT',                                                // -E, --cert <certificate[:password]>
163        '--cert-type'                 => 'CURLOPT_SSLCERTTYPE',                                            // --cert-type <type>
164        '--engine'                    => 'CURLOPT_SSLENGINE',                                              // --engine <name>
165        '--key'                       => 'CURLOPT_SSLKEY',                                                 // --key <key>
166        '--pass'                      => 'CURLOPT_SSLKEYPASSWD',                                           // --pass <phrase>
167        '--key-type'                  => 'CURLOPT_SSLKEYTYPE',                                             // --key-type <type>
168        '-2'                          => 'CURLOPT_SSLVERSION',
169        '--sslv2'                     => 'CURLOPT_SSLVERSION',
170        '-3'                          => 'CURLOPT_SSLVERSION',
171        '--sslv3'                     => 'CURLOPT_SSLVERSION',
172        '--ciphers'                   => 'CURLOPT_SSL_CIPHER_LIST',                                        // --ciphers <list of ciphers> i.e. ECDHE-ECDSA-AES256-CCM8
173        '--curves'                    => 'CURLOPT_SSL_EC_CURVES',                                          // --curves <algorithm list>
174        '--alpn'                      => 'CURLOPT_SSL_ENABLE_ALPN',
175        '--no-alpn'                   => 'CURLOPT_SSL_ENABLE_ALPN',
176        '--npn'                       => 'CURLOPT_SSL_ENABLE_NPN',
177        '--no-npn'                    => 'CURLOPT_SSL_ENABLE_NPN',
178        '--false-start'               => 'CURLOPT_SSL_FALSESTART',
179        '--no-sessionid'              => 'CURLOPT_SSL_SESSIONID_CACHE',
180        '-k'                          => ['CURLOPT_SSL_VERIFYHOST', 'CURLOPT_SSL_VERIFYPEER'],
181        '--insecure'                  => ['CURLOPT_SSL_VERIFYHOST', 'CURLOPT_SSL_VERIFYPEER'],
182        '--cert-status'               => 'CURLOPT_SSL_VERIFYSTATUS',
183        '--stderr'                    => 'CURLOPT_STDERR',                                                 // --stderr <file>
184        '--suppress-connect-headers'  => 'CURLOPT_SUPPRESS_CONNECT_HEADERS',
185        '--tcp-fastopen'              => 'CURLOPT_TCP_FASTOPEN',
186        '--keepalive-time'            => 'CURLOPT_TCP_KEEPALIVE',                                          // --keepalive-time <seconds>
187        '--tcp-nodelay'               => 'CURLOPT_TCP_NODELAY',
188        '-t'                          => 'CURLOPT_TELNETOPTIONS',                                          // -t, --telnet-option <opt=val>
189        '--telnet-option'             => 'CURLOPT_TELNETOPTIONS',                                          // -t, --telnet-option <opt=val>
190        '--tftp-blksize'              => 'CURLOPT_TFTP_BLKSIZE',                                           // --tftp-blksize <value>
191        '--tftp-no-options'           => 'CURLOPT_TFTP_NO_OPTIONS',
192        '-z'                          => 'CURLOPT_TIMECONDITION',                                          // -z, --time-cond <time>
193        '--time-cond'                 => 'CURLOPT_TIMECONDITION',                                          // -z, --time-cond <time>
194        '--tls13-ciphers'             => 'CURLOPT_TLS13_CIPHERS',                                          // --tls13-ciphers <ciphersuite list>
195        '--tlspassword'               => 'CURLOPT_TLSAUTH_PASSWORD',                                       // --tlspassword <string>
196        '--tlsauthtype'               => 'CURLOPT_TLSAUTH_TYPE',                                           // --tlsauthtype <type>
197        '--tlsuser'                   => 'CURLOPT_TLSAUTH_USERNAME',                                       // --tlsuser <name>
198        '--tr-encoding'               => 'CURLOPT_TRANSFER_ENCODING',
199        '--no-tr-encoding'            => 'CURLOPT_TRANSFER_ENCODING',
200        '--unix-socket'               => 'CURLOPT_UNIX_SOCKET_PATH',                                       // --unix-socket <path>
201        '--url'                       => 'CURLOPT_URL',                                                    // --url <url> (Used for config files, unnecessary on the CLI)
202        '-A'                          => 'CURLOPT_USERAGENT',                                              // -A, --user-agent <name>
203        '--user-agent'                => 'CURLOPT_USERAGENT',                                              // -A, --user-agent <name>
204        '--ssl'                       => 'CURLOPT_USE_SSL',                                                // Attempts to force server to use secure connection',
205        '--ssl-reqd'                  => 'CURLOPT_USE_SSL',                                                // Attempts to force server to use secure connection',
206        '-v'                          => 'CURLOPT_VERBOSE',
207        '--verbose'                   => 'CURLOPT_VERBOSE',
208        '--oauth2-bearer'             => 'CURLOPT_XOAUTH2_BEARER',                                         // --oauth2-bearer <token>
209        '--ssl-allow-beast'           => 'CURLSSLOPT_ALLOW_BEAST',
210        '--ssl-auto-client-cert'      => 'CURLSSLOPT_AUTO_CLIENT_CERT',
211        '--ssl-no-revoke'             => 'CURLSSLOPT_NO_REVOKE',
212        '--ssl-revoke-best-effort'    => 'CURLSSLOPT_REVOKE_BEST_EFFORT',
213    ];
214
215    /**
216     * Derived PHP-to-CLI options (inverse of $commandOptions), computed once and cached
217     * @var ?array
218     */
219    protected static ?array $phpOptions = null;
220
221    /**
222     * Curl options that require a value
223     * @var array
224     */
225    protected static array $valueOptions = [
226        '--abstract-unix-socket'             => null, // --abstract-unix-socket <path>
227        '--alt-svc'                          => null, // --alt-svc <filename>
228        '--aws-sigv4'                        => null, // --aws-sigv4 <provider1[:provider2[:region[:service]]]>[
229        '--cacert'                           => null, // --cacert <file>
230        '--capath'                           => null, // --capath <dir>
231        '--connect-timeout'                  => null, // --connect-timeout <fractional seconds> (MS needs to be converted to seconds)
232        '--connect-to'                       => null, // --connect-to <HOST1:PORT1:HOST2:PORT2>
233        '-b'                                 => null, // -b, --cookie <data|filename>
234        '--cookie'                           => null, // -b, --cookie <data|filename>
235        '-c'                                 => null, // -c, --cookie-jar <filename>
236        '--cookie-jar'                       => null, // -c, --cookie-jar <filename>
237        '--crlfile'                          => null, // --crlfile <file>
238        '-X'                                 => null, // -X, --request <method>
239        '--request'                          => null, // -X, --request <method>
240        '--dns-interface'                    => null, // --dns-interface <interface>
241        '--dns-ipv4-addr'                    => null, // --dns-ipv4-addr <address>
242        '--dns-ipv6-addr'                    => null, // --dns-ipv6-addr <address>
243        '--dns-servers'                      => null, // --dns-servers <addresses>
244        '--doh-insecure'                     => null, // Verify the DNS-over-HTTPS server's SSL certificate name fields against the host name and certificate. Available as of PHP 8.2.0 and cURL 7.76.0.
245        '--no-doh-insecure'                  => null, // Verify the DNS-over-HTTPS server's SSL certificate name fields against the host name and certificate. Available as of PHP 8.2.0 and cURL 7.76.0.
246        '--doh-cert-status'                  => null, // Tell cURL to verify the status of the DNS-over-HTTPS server certificate using the "Certificate Status Request" TLS extension (OCSP stapling). Available as of PHP 8.2.0 and cURL 7.76.0.
247        '--doh-url'                          => null, // --doh-url <url> Provides the DNS-over-HTTPS URL. Available as of PHP 8.1.0 and cURL 7.62.0.
248        '--expect100-timeout'                => null, // --expect100-timeout <seconds>
249        '-P'                                 => null, // -P, --ftp-port <address>
250        '--ftp-port'                         => null, // -P, --ftp-port <address>
251        '--ftp-account'                      => null, // --ftp-account <data>
252        '--ftp-alternative-to-user'          => null, // --ftp-alternative-to-user <command>
253        '--ftp-method'                       => null, // --ftp-method <method>
254        '--delegation'                       => null, // --delegation <level>
255        '--happy-eyeballs-timeout-ms'        => null, // --happy-eyeballs-timeout-ms <milliseconds>
256        '--hsts'                             => null, // --hsts <filename>
257        '-H'                                 => null, // -H, --header <header/@file>
258        '--header'                           => null, // -H, --header <header/@file>
259        '-0'                                 => CURL_HTTP_VERSION_1_0, // CURL HTTP Version constant
260        '--http1.0'                          => CURL_HTTP_VERSION_1_0, // CURL HTTP Version constant
261        '--http1.1'                          => CURL_HTTP_VERSION_1_1, // CURL HTTP Version constant
262        '--http2'                            => CURL_HTTP_VERSION_2_0, // CURL HTTP Version constant
263        '-T'                                 => null, // -T, --upload-file <file> (Used with PUT)
264        '--upload-file'                      => null, // -T, --upload-file <file> (Used with PUT)
265        '--interface'                        => null, // --interface <name>
266        '--krb'                              => null, // --krb <level>
267        '--local-port'                       => null, // --local-port <num/range>
268        '--login-options'                    => null, // --login-options <options>
269        '-Y'                                 => null, // -Y, --speed-limit <speed>
270        '--speed-limit'                      => null, // -Y, --speed-limit <speed>
271        '-y'                                 => null, // -y, --speed-time <seconds>
272        '--speed-time'                       => null, // -y, --speed-time <seconds>
273        '--mail-auth'                        => null, // --mail-auth <address>
274        '--mail-from'                        => null, // --mail-from <address>
275        '--mail-rcpt'                        => null, // --mail-rcpt <address>
276        '--max-filesize'                     => null, // --max-filesize <bytes>
277        '-m'                                 => null, // -m, --max-time <fractional seconds>
278        '--max-time'                         => null, // -m, --max-time <fractional seconds>
279        '--max-redirs'                       => null, // --max-redirs <num>
280        '--netrc-file'                       => null, // --netrc-file <filename>
281        '--noproxy'                          => null, // --noproxy <no-proxy-list>
282        '-u'                                 => null, // -u, --user <user:password>
283        '--user'                             => null, // -u, --user <user:password>
284        '-d'                                 => null, // -d, --data <data>
285        '--data'                             => null, // -d, --data <data>
286        '-F'                                 => null, // -F, --form <name=content>
287        '--form'                             => null, // -F, --form <name=content>
288        '-x'                                 => null, // -x, --proxy [protocol://]host[:port]
289        '--proxy'                            => null, // -x, --proxy [protocol://]host[:port]
290        '--proxy-basic'                      => CURLAUTH_BASIC,  // CURL Auth constant
291        '--proxy-digest'                     => CURLAUTH_DIGEST, // CURL Auth constant
292        '--proxy-header'                     => null, // --proxy-header <header/@file>
293        '-U'                                 => null, // -U, --proxy-user <user:password>
294        '--proxy-user'                       => null, // -U, --proxy-user <user:password>
295        '--proxy-cacert'                     => null, // --proxy-cacert <file>
296        '--proxy-capath'                     => null, // --proxy-capath <dir>
297        '--proxy-crlfile'                    => null, // --proxy-crlfile <file>
298        '--proxy-pass'                       => null, // --proxy-pass <phrase>
299        '--proxy-pinnedpubkey'               => null, // --proxy-pinnedpubkey <hashes>
300        '--proxy-service-name'               => null, // --proxy-service-name <name>
301        '--proxy-cert'                       => null, // --proxy-cert <cert[:passwd]>
302        '--proxy-cert-type'                  => null, // --proxy-cert-type <type>
303        '--proxy-key'                        => null, // --proxy-key <key>
304        '--proxy-key-type'                   => null, // --proxy-key-type <type>
305        '--proxy-ciphers'                    => null, // --proxy-ciphers <list>
306        '--proxy-tls13-ciphers'              => null, // --proxy-tls13-ciphers <ciphersuite list>
307        '--proxy-tlspassword'                => null, // --proxy-tlspassword <string>
308        '--proxy-tlsauthtype'                => null, // --proxy-tlsauthtype <type>
309        '--proxy-tlsuser'                    => null, // --proxy-tlsuser <name>
310        '-Q'                                 => null, // -Q, --quote <command>
311        '--quote'                            => null, // -Q, --quote <command>
312        '--random-file'                      => null, // --random-file <file>
313        '-r'                                 => null, // -r, --range <range>
314        '--range'                            => null, // -r, --range <range>
315        '-e'                                 => null, // -e, --referer <url>
316        '--referer'                          => null, // -e, --referer <url>
317        '--resolve'                          => null, // --resolve <[+]host:port:addr[,addr]...>
318        '--sasl-authzid'                     => null, // --sasl-authzid <identity>
319        '--service-name'                     => null, // --service-name <name>
320        '--socks5-gssapi-service'            => null, // --socks5-gssapi-service <name>
321        '--hostpubmd5'                       => null, // --hostpubmd5 <md5>
322        '--hostpubsha256'                    => null, // --hostpubsha256 <sha256>
323        '--pubkey'                           => null, // --pubkey <key>
324        '-E'                                 => null, // -E, --cert <certificate[:password]>
325        '--cert'                             => null, // -E, --cert <certificate[:password]>
326        '--cert-type'                        => null, // --cert-type <type>
327        '--engine'                           => null, // --engine <name>
328        '--key'                              => null, // --key <key>
329        '--pass'                             => null, // --pass <phrase>
330        '--key-type'                         => null, // --key-type <type>
331        '-2'                                 => CURL_SSLVERSION_SSLv2, // CURL SSL Version contstant
332        '--sslv2'                            => CURL_SSLVERSION_SSLv2, // CURL SSL Version contstant
333        '-3'                                 => CURL_SSLVERSION_SSLv3, // CURL SSL Version contstant
334        '--sslv3'                            => CURL_SSLVERSION_SSLv3, // CURL SSL Version contstant
335        '--ciphers'                          => null, // --ciphers <list of ciphers> i.e. ECDHE-ECDSA-AES256-CCM8
336        '--curves'                           => null, // --curves <algorithm list>
337        '--stderr'                           => null, // --stderr <file>
338        '--keepalive-time'                   => null, // --keepalive-time <seconds>
339        '-t'                                 => null, // -t, --telnet-option <opt=val>
340        '--telnet-option'                    => null, // -t, --telnet-option <opt=val>
341        '--tftp-blksize'                     => null, // --tftp-blksize <value>
342        '-z'                                 => null, // -z, --time-cond <time>
343        '--time-cond'                        => null, // -z, --time-cond <time>
344        '--tls13-ciphers'                    => null, // --tls13-ciphers <ciphersuite list>
345        '--tlspassword'                      => null, // --tlspassword <string>
346        '--tlsauthtype'                      => null, // --tlsauthtype <type>
347        '--tlsuser'                          => null, // --tlsuser <name>
348        '--unix-socket'                      => null, // --unix-socket <path>
349        '--url'                              => null, // --url <url> (Used for config files, unnecessary on the CLI)
350        '-A'                                 => null, // -A, --user-agent <name>
351        '--user-agent'                       => null, // -A, --user-agent <name>
352        '--oauth2-bearer'                    => null, // --oauth2-bearer <token>
353        'CURLOPT_ABSTRACT_UNIX_SOCKET'       => null, // --abstract-unix-socket <path>
354        'CURLOPT_ALTSVC'                     => null, // --alt-svc <filename>
355        'CURLOPT_AWS_SIGV4'                  => null, // --aws-sigv4 <provider1[:provider2[:region[:service]]]>
356        'CURLOPT_CAINFO'                     => null, // --cacert <file>
357        'CURLOPT_CAPATH'                     => null, // --capath <dir>
358        'CURLOPT_CONNECTTIMEOUT'             => null, // --connect-timeout <fractional seconds>
359        'CURLOPT_CONNECT_TO'                 => null, // --connect-to <HOST1:PORT1:HOST2:PORT2>
360        'CURLOPT_COOKIE'                     => null, // -b, --cookie <data|filename>
361        'CURLOPT_COOKIEJAR'                  => null, // -c, --cookie-jar <filename>
362        'CURLOPT_CRLFILE'                    => null, // --crlfile <file>
363        'CURLOPT_CUSTOMREQUEST'              => null, // -X, --request <method>
364        'CURLOPT_DNS_INTERFACE'              => null, // --dns-interface <interface>
365        'CURLOPT_DNS_LOCAL_IP4'              => null, // --dns-ipv4-addr <address>
366        'CURLOPT_DNS_LOCAL_IP6'              => null, // --dns-ipv6-addr <address>
367        'CURLOPT_DNS_SERVERS'                => null, // --dns-servers <addresses>
368        'CURLOPT_DOH_URL'                    => null, // --doh-url <url> Provides the DNS-over-HTTPS URL. Available as of PHP 8.1.0 and cURL 7.62.0.
369        'CURLOPT_EXPECT_100_TIMEOUT_MS'      => null, // --expect100-timeout <seconds>
370        'CURLOPT_FTPPORT'                    => null, // -P, --ftp-port <address>
371        'CURLOPT_FTP_ACCOUNT'                => null, // --ftp-account <data>
372        'CURLOPT_FTP_ALTERNATIVE_TO_USER'    => null, // --ftp-alternative-to-user <command>
373        'CURLOPT_FTP_FILEMETHOD'             => null, // --ftp-method <method>
374        'CURLOPT_GSSAPI_DELEGATION'          => null, // --delegation <level>
375        'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS'  => null, // --happy-eyeballs-timeout-ms <milliseconds>
376        'CURLOPT_HSTS'                       => null, // --hsts <filename>
377        'CURLOPT_HTTPHEADER'                 => null, // -H, --header <header/@file>
378        'CURLOPT_HTTP_VERSION'               => [CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1, CURL_HTTP_VERSION_2_0], // CURL HTTP version constant
379        'CURLOPT_INFILE'                     => null, // -T, --upload-file <file> (Used with PUT)
380        'CURLOPT_INTERFACE'                  => null, // --interface <name>
381        'CURLOPT_KRBLEVEL'                   => null, // --krb <level>
382        'CURLOPT_LOCALPORT'                  => null, // --local-port <num/range>
383        'CURLOPT_LOCALPORTRANGE'             => null, // --local-port <num/range>
384        'CURLOPT_LOGIN_OPTIONS'              => null, // --login-options <options>
385        'CURLOPT_LOW_SPEED_LIMIT'            => null, // -Y, --speed-limit <speed>
386        'CURLOPT_LOW_SPEED_TIME'             => null, // -y, --speed-time <seconds>
387        'CURLOPT_MAIL_AUTH'                  => null, // --mail-auth <address>
388        'CURLOPT_MAIL_FROM'                  => null, // --mail-from <address>
389        'CURLOPT_MAIL_RCPT'                  => null, // --mail-rcpt <address>
390        'CURLOPT_MAXFILESIZE'                => null, // --max-filesize <bytes>
391        'CURLOPT_MAXLIFETIME_CONN'           => null, // -m, --max-time <fractional seconds>
392        'CURLOPT_MAXREDIRS'                  => null, // --max-redirs <num>
393        'CURLOPT_NETRC_FILE'                 => null, // --netrc-file <filename>
394        'CURLOPT_NOPROXY'                    => null, // --noproxy <no-proxy-list>
395        'CURLOPT_PASSWORD'                   => null, // -u, --user <user:password>
396        'CURLOPT_POST'                       => null, // -X, --request <method>
397        'CURLOPT_POSTFIELDS'                 => null, // -d, --data <data>
398        'CURLOPT_PROXY'                      => null, // -x, --proxy [protocol://]host[:port]
399        'CURLOPT_PROXYAUTH'                  => [CURLAUTH_BASIC, CURLAUTH_DIGEST], // CURL Auth Constant
400        'CURLOPT_PROXYHEADER'                => null, // --proxy-header <header/@file>
401        'CURLOPT_PROXYUSERPWD'               => null, // -U, --proxy-user <user:password>
402        'CURLOPT_PROXY_CAINFO'               => null, // --proxy-cacert <file>
403        'CURLOPT_PROXY_CAPATH'               => null, // --proxy-capath <dir>
404        'CURLOPT_PROXY_CRLFILE'              => null, // --proxy-crlfile <file>
405        'CURLOPT_PROXY_KEYPASSWD'            => null, // --proxy-pass <phrase>
406        'CURLOPT_PROXY_PINNEDPUBLICKEY'      => null, // --proxy-pinnedpubkey <hashes>
407        'CURLOPT_PROXY_SERVICE_NAME'         => null, // --proxy-service-name <name>
408        'CURLOPT_PROXY_SSLCERT'              => null, // --proxy-cert <cert[:passwd]>
409        'CURLOPT_PROXY_SSLCERTTYPE'          => null, // --proxy-cert-type <type>
410        'CURLOPT_PROXY_SSLKEY'               => null, // --proxy-key <key>
411        'CURLOPT_PROXY_SSLKEYTYPE'           => null, // --proxy-key-type <type>
412        'CURLOPT_PROXY_SSL_CIPHER_LIST'      => null, // --proxy-ciphers <list>
413        'CURLOPT_PROXY_TLS13_CIPHERS'        => null, // --proxy-tls13-ciphers <ciphersuite list>
414        'CURLOPT_PROXY_TLSAUTH_PASSWORD'     => null, // --proxy-tlspassword <string>
415        'CURLOPT_PROXY_TLSAUTH_TYPE'         => null, // --proxy-tlsauthtype <type>
416        'CURLOPT_PROXY_TLSAUTH_USERNAME'     => null, // --proxy-tlsuser <name>
417        'CURLOPT_PUT'                        => null, // -X, --request <method>
418        'CURLOPT_QUOTE'                      => null, // -Q, --quote <command>
419        'CURLOPT_RANDOM_FILE'                => null, // --random-file <file>
420        'CURLOPT_RANGE'                      => null, // -r, --range <range>
421        'CURLOPT_REFERER'                    => null, // -e, --referer <url>
422        'CURLOPT_RESOLVE'                    => null, // --resolve <[+]host:port:addr[,addr]...>
423        'CURLOPT_SASL_AUTHZID'               => null, // --sasl-authzid <identity>
424        'CURLOPT_SERVICE_NAME'               => null, // --service-name <name>
425        'CURLOPT_SOCKS5_GSSAPI_SERVICE'      => null, // --socks5-gssapi-service <name>
426        'CURLOPT_SSH_HOST_PUBLIC_KEY_MD5'    => null, // --hostpubmd5 <md5>
427        'CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256' => null, // --hostpubsha256 <sha256>
428        'CURLOPT_SSH_PUBLIC_KEYFILE'         => null, // --pubkey <key>
429        'CURLOPT_SSLCERT'                    => null, // -E, --cert <certificate[:password]>
430        'CURLOPT_SSLCERTTYPE'                => null, // --cert-type <type>
431        'CURLOPT_SSLENGINE'                  => null, // --engine <name>
432        'CURLOPT_SSLKEY'                     => null, // --key <key>
433        'CURLOPT_SSLKEYPASSWD'               => null, // --pass <phrase>
434        'CURLOPT_SSLKEYTYPE'                 => null, // --key-type <type>
435        'CURLOPT_SSLVERSION'                 => [CURL_SSLVERSION_SSLv2, CURL_SSLVERSION_SSLv2, CURL_SSLVERSION_SSLv3, CURL_SSLVERSION_SSLv3], // CURL SSL Version constant
436        'CURLOPT_SSL_CIPHER_LIST'            => null, // --ciphers <list of ciphers> i.e. ECDHE-ECDSA-AES256-CCM8
437        'CURLOPT_SSL_EC_CURVES'              => null, // --curves <algorithm list>
438        'CURLOPT_STDERR'                     => null, // --stderr <file>
439        'CURLOPT_TCP_KEEPALIVE'              => null, // --keepalive-time <seconds>
440        'CURLOPT_TELNETOPTIONS'              => null, // -t, --telnet-option <opt=val>
441        'CURLOPT_TFTP_BLKSIZE'               => null, // --tftp-blksize <value>
442        'CURLOPT_TIMECONDITION'              => null, // -z, --time-cond <time>
443        'CURLOPT_TIMEOUT'                    => null, // --connect-timeout <fractional seconds>
444        'CURLOPT_TIMEOUT_MS'                 => null, // --connect-timeout <fractional seconds> (MS needs to be converted to seconds),
445        'CURLOPT_TLS13_CIPHERS'              => null, // --tls13-ciphers <ciphersuite list>
446        'CURLOPT_TLSAUTH_PASSWORD'           => null, // --tlspassword <string>
447        'CURLOPT_TLSAUTH_TYPE'               => null, // --tlsauthtype <type>
448        'CURLOPT_TLSAUTH_USERNAME'           => null, // --tlsuser <name>
449        'CURLOPT_UNIX_SOCKET_PATH'           => null, // --unix-socket <path>
450        'CURLOPT_URL'                        => null, // --url <url> (Used for config files, unnecessary on the CLI)
451        'CURLOPT_USERAGENT'                  => null, // -A, --user-agent <name>
452        'CURLOPT_USERNAME'                   => null, // -u, --user <user:password>
453        'CURLOPT_USERPWD'                    => null, // -u, --user <user:password>
454        'CURLOPT_XOAUTH2_BEARER'             => null, // --oauth2-bearer <token>
455    ];
456
457    /**
458     * Options to omit from conversion, as they are addressed elsewhere in the conversion
459     *
460     * @var array
461     */
462    protected static array $omitOptions = [
463        'CURLOPT_CUSTOMREQUEST', 'CURLOPT_HEADER', 'CURLOPT_HTTPHEADER', 'CURLOPT_POST', 'CURLOPT_POSTFIELDS',
464        'CURLOPT_PUT', 'CURLOPT_RETURNTRANSFER', 'CURLOPT_URL', 'CURLOPT_SSL_VERIFYHOST', 'CURLOPT_SSL_VERIFYPEER',
465        '-i', '-X', '--request', '-H', '--header', '-d', '--data', '-F', '--form', '-k', '--insecure', '--url',
466    ];
467
468    /**
469     * Unresolved Curl options
470     *
471     *   These are Curl options in PHP that have not yet been mapped to a CLI option
472     *
473     * @var array
474     */
475    protected static array $unresolvedOptions = [
476        'CURLOPT_ACCEPTTIMEOUT_MS',
477        'CURLOPT_ACCEPT_ENCODING',
478        'CURLOPT_ADDRESS_SCOPE',
479        'CURLOPT_ALTSVC_CTRL',
480        'CURLOPT_AUTOREFERER',
481        'CURLOPT_BINARYTRANSFER',
482        'CURLOPT_BUFFERSIZE',
483        'CURLOPT_CAINFO_BLOB',
484        'CURLOPT_CERTINFO',
485        'CURLOPT_CONNECTTIMEOUT_MS',
486        'CURLOPT_CONNECT_ONLY',
487        'CURLOPT_COOKIEFILE',
488        'CURLOPT_COOKIELIST',
489        'CURLOPT_COOKIESESSION',
490        'CURLOPT_DEFAULT_PROTOCOL',
491        'CURLOPT_DIRLISTONLY',
492        'CURLOPT_DNS_CACHE_TIMEOUT',
493        'CURLOPT_DNS_SHUFFLE_ADDRESSES',
494        'CURLOPT_DNS_USE_GLOBAL_CACHE',
495        'CURLOPT_EGDSOCKET',
496        'CURLOPT_ENCODING',
497        'CURLOPT_FAILONERROR',
498        'CURLOPT_FILE',
499        'CURLOPT_FILETIME',
500        'CURLOPT_FNMATCH_FUNCTION',
501        'CURLOPT_FORBID_REUSE',
502        'CURLOPT_FRESH_CONNECT',
503        'CURLOPT_FTPAPPEND',
504        'CURLOPT_FTPLISTONLY',
505        'CURLOPT_FTPSSLAUTH',
506        'CURLOPT_FTP_RESPONSE_TIMEOUT',
507        'CURLOPT_FTP_SSL',
508        'CURLOPT_HEADERFUNCTION',
509        'CURLOPT_HEADEROPT',
510        'CURLOPT_HSTS_CTRL',
511        'CURLOPT_HTTP200ALIASES',
512        'CURLOPT_HTTPAUTH',
513        'CURLOPT_HTTPGET',
514        'CURLOPT_HTTP_CONTENT_DECODING',
515        'CURLOPT_HTTP_TRANSFER_DECODING',
516        'CURLOPT_INFILESIZE',
517        'CURLOPT_IPRESOLVE',
518        'CURLOPT_ISSUERCERT',
519        'CURLOPT_ISSUERCERT_BLOB', // Issuer SSL certificate from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
520        'CURLOPT_KEEP_SENDING_ON_ERROR',
521        'CURLOPT_KEYPASSWD',
522        'CURLOPT_KRB4LEVEL',
523        'CURLOPT_MAXAGE_CONN',
524        'CURLOPT_MAXCONNECTS',
525        'CURLOPT_MAXFILESIZE_LARGE',
526        'CURLOPT_MAX_RECV_SPEED_LARGE',
527        'CURLOPT_MAX_SEND_SPEED_LARGE',
528        'CURLOPT_NEW_DIRECTORY_PERMS',
529        'CURLOPT_NEW_FILE_PERMS',
530        'CURLOPT_NOBODY',
531        'CURLOPT_NOSIGNAL',
532        'CURLOPT_PATH_AS_IS',
533        'CURLOPT_PINNEDPUBLICKEY',
534        'CURLOPT_PIPEWAIT',
535        'CURLOPT_PORT',
536        'CURLOPT_POSTQUOTE',
537        'CURLOPT_POSTREDIR',
538        'CURLOPT_PREQUOTE',
539        'CURLOPT_PRE_PROXY',
540        'CURLOPT_PRIVATE',
541        'CURLOPT_PROGRESSFUNCTION',
542        'CURLOPT_PROTOCOLS',
543        'CURLOPT_PROXYPASSWORD',
544        'CURLOPT_PROXYPORT',
545        'CURLOPT_PROXYTYPE',
546        'CURLOPT_PROXYUSERNAME',
547        'CURLOPT_PROXY_SSLVERSION',
548        'CURLOPT_PROXY_CAINFO_BLOB',
549        'CURLOPT_PROXY_ISSUERCERT', // Proxy issuer SSL certificate filename. Available as of PHP 8.1.0 and cURL 7.71.0.
550        'CURLOPT_PROXY_ISSUERCERT_BLOB', // Proxy issuer SSL certificate from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
551        'CURLOPT_PROXY_SSLCERT_BLOB', // SSL proxy client certificate from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
552        'CURLOPT_PROXY_SSLKEY_BLOB', // Private key for proxy cert from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
553        'CURLOPT_PROXY_SSL_OPTIONS',
554        'CURLOPT_PROXY_TRANSFER_MODE',
555        'CURLOPT_READDATA',
556        'CURLOPT_READFUNCTION',
557        'CURLOPT_REDIR_PROTOCOLS',
558        'CURLOPT_REQUEST_TARGET',
559        'CURLOPT_RESUME_FROM',
560        'CURLOPT_RETURNTRANSFER',
561        'CURLOPT_RTSP_CLIENT_CSEQ',
562        'CURLOPT_RTSP_REQUEST',
563        'CURLOPT_RTSP_SERVER_CSEQ',
564        'CURLOPT_RTSP_SESSION_ID',
565        'CURLOPT_RTSP_STREAM_URI',
566        'CURLOPT_RTSP_TRANSPORT',
567        'CURLOPT_SAFE_UPLOAD',
568        'CURLOPT_SHARE',
569        'CURLOPT_SSH_AUTH_TYPES',
570        'CURLOPT_SSH_KNOWNHOSTS',
571        'CURLOPT_SSH_PRIVATE_KEYFILE',
572        'CURLOPT_SSLCERTPASSWD',
573        'CURLOPT_SSLCERT_BLOB', // SSL client certificate from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
574        'CURLOPT_SSLENGINE_DEFAULT',
575        'CURLOPT_SSLKEY_BLOB', // Private key for client cert from memory blob. Available as of PHP 8.1.0 and cURL 7.71.0.
576        'CURLOPT_SSL_OPTIONS',
577        'CURLOPT_STREAM_WEIGHT',
578        'CURLOPT_TCP_KEEPIDLE',
579        'CURLOPT_TCP_KEEPINTVL',
580        'CURLOPT_TIMEVALUE',
581        'CURLOPT_TIMEVALUE_LARGE',
582        'CURLOPT_TRANSFERTEXT',
583        'CURLOPT_UNRESTRICTED_AUTH',
584        'CURLOPT_UPKEEP_INTERVAL_MS',
585        'CURLOPT_UPLOAD',
586        'CURLOPT_UPLOAD_BUFFERSIZE',
587        'CURLOPT_WILDCARDMATCH',
588        'CURLOPT_WRITEFUNCTION',
589        'CURLOPT_WRITEHEADER',
590        'CURLOPT_XFERINFOFUNCTION',
591        'CURLSSLOPT_NATIVE_CA',
592        'CURLSSLOPT_NO_PARTIALCHAIN',
593    ];
594
595    /**
596     * Check if the option is valid
597     *
598     * @param  string $option
599     * @return bool
600     */
601    public static function isValidOption(string $option): bool
602    {
603        return (array_key_exists($option, self::$commandOptions) || array_key_exists($option, self::getPhpOptions()));
604    }
605
606    /**
607     * Check if the option is a valid CLI option
608     *
609     * @param  string $option
610     * @return bool
611     */
612    public static function isCommandOption(string $option): bool
613    {
614        return array_key_exists($option, self::$commandOptions);
615    }
616
617    /**
618     * Check if the option is a valid PHP option
619     *
620     * @param  string $option
621     * @return bool
622     */
623    public static function isPhpOption(string $option): bool
624    {
625        return array_key_exists($option, self::getPhpOptions());
626    }
627
628    /**
629     * Check if the option requires a value
630     *
631     * @param  string $option
632     * @return bool
633     */
634    public static function isValueOption(string $option): bool
635    {
636        return array_key_exists($option, self::$valueOptions);
637    }
638
639    /**
640     * Check if the option is a boolean option
641     *
642     * @param  string $option
643     * @return bool
644     */
645    public static function isBooleanOption(string $option): bool
646    {
647        return !array_key_exists($option, self::$valueOptions);
648    }
649
650    /**
651     * Get the CLI options
652     *
653     * @return array
654     */
655    public static function getCommandOptions(): array
656    {
657        return self::$commandOptions;
658    }
659
660    /**
661     * Get CLI option
662     *
663     * @return string|array|null
664     */
665    public static function getCommandOption(string $option): string|array|null
666    {
667        return self::$commandOptions[$option] ?? null;
668    }
669
670    /**
671     * Get the PHP options, derived from $commandOptions so the two tables can never drift apart
672     *
673     * @return array
674     */
675    public static function getPhpOptions(): array
676    {
677        if (self::$phpOptions === null) {
678            self::$phpOptions = [];
679            foreach (self::$commandOptions as $flag => $constants) {
680                $flag = (string)$flag; // Ensure flag is a string (numeric string keys auto-cast to int)
681                foreach ((array)$constants as $constant) {
682                    if (!isset(self::$phpOptions[$constant])) {
683                        self::$phpOptions[$constant] = $flag;
684                    } else if (is_array(self::$phpOptions[$constant])) {
685                        self::$phpOptions[$constant][] = $flag;
686                    } else if (self::$phpOptions[$constant] !== $flag) {
687                        self::$phpOptions[$constant] = [self::$phpOptions[$constant], $flag];
688                    }
689                }
690            }
691        }
692
693        return self::$phpOptions;
694    }
695
696    /**
697     * Get PHP option
698     *
699     * @return string|array|null
700     */
701    public static function getPhpOption(string $option): string|array|null
702    {
703        return self::getPhpOptions()[$option] ?? null;
704    }
705
706    /**
707     * Get the value options
708     *
709     * @return array
710     */
711    public static function getValueOptions(): array
712    {
713        return self::$valueOptions;
714    }
715
716    /**
717     * Get value option
718     *
719     * @return mixed
720     */
721    public static function getValueOption(string $option): mixed
722    {
723        return self::$valueOptions[$option] ?? null;
724    }
725
726    /**
727     * Cached value-to-name reverse lookup, built once from the actual defined
728     * curl constants rather than a hand-maintained table.
729     * @var ?array
730     */
731    protected static ?array $optionNamesByValue = null;
732
733    /**
734     * Get the option value by name, using the real PHP constant directly
735     * instead of a hand-maintained table that could silently drift.
736     *
737     * @return int|null
738     */
739    public static function getOptionValueByName(string $curlOption): int|null
740    {
741        return defined($curlOption) ? constant($curlOption) : null;
742    }
743
744    /**
745     * Get the option name by value
746     *
747     * @return string|null
748     */
749    public static function getOptionNameByValue(int $curlValue): string|null
750    {
751        if (self::$optionNamesByValue === null) {
752            self::$optionNamesByValue = [];
753
754            // First pass: constants with a known CLI-flag mapping (i.e. present in
755            // $phpOptions/$commandOptions) win any value collision. get_defined_constants()
756            // iteration order is not guaranteed, and several CURLOPT_*/CURLSSLOPT_* names
757            // alias the same integer value (e.g. CURLOPT_SSLVERSION and
758            // CURLSSLOPT_AUTO_CLIENT_CERT both equal 32) - only one of those is an actual
759            // settable curl option with a CLI equivalent, so that's the one toCurlCommand()
760            // needs back, not whichever alias happens to be iterated first.
761            foreach (self::getPhpOptions() as $name => $flag) {
762                if (defined($name) && !isset(self::$optionNamesByValue[constant($name)])) {
763                    self::$optionNamesByValue[constant($name)] = $name;
764                }
765            }
766
767            // Second pass: fill in the remaining curl constants (those with no CLI-flag
768            // mapping) for any value not already claimed by the first pass. Sorted
769            // alphabetically by name before assignment (rather than relying on
770            // get_defined_constants()'s registration-order iteration, which is
771            // unspecified and does not match alphabetical order) so that any residual
772            // collision between two equally-unmapped aliases resolves the same way the
773            // old hand-typed table did, since that table was itself declared
774            // alphabetically and array_search() picked its first (alphabetically
775            // earliest) match.
776            $remainingConstants = [];
777            foreach (get_defined_constants() as $name => $value) {
778                if (str_starts_with($name, 'CURLOPT_') || str_starts_with($name, 'CURLSSLOPT_')) {
779                    $remainingConstants[$name] = $value;
780                }
781            }
782            ksort($remainingConstants);
783            foreach ($remainingConstants as $name => $value) {
784                if (!isset(self::$optionNamesByValue[$value])) {
785                    self::$optionNamesByValue[$value] = $name;
786                }
787            }
788        }
789
790        return self::$optionNamesByValue[$curlValue] ?? null;
791    }
792
793    /**
794     * Has the option value by name
795     *
796     * @return bool
797     */
798    public static function hasOptionValueByName(string $curlOption): bool
799    {
800        return self::getOptionValueByName($curlOption) !== null;
801    }
802
803    /**
804     * Has the option name by value
805     *
806     * @return bool
807     */
808    public static function hasOptionNameByValue(int $curlValue): bool
809    {
810        return self::getOptionNameByValue($curlValue) !== null;
811    }
812
813    /**
814     * Get the omit options
815     *
816     * @return array
817     */
818    public static function getOmitOptions(): array
819    {
820        return self::$omitOptions;
821    }
822
823    /**
824     * Is omit option
825     *
826     * @return bool
827     */
828    public static function isOmitOption(string $option): bool
829    {
830        return in_array($option, self::$omitOptions);
831    }
832
833}