Requesting CURL_HTTP_VERSION_2 makes curl send an h2c Upgrade request on
plain http URLs, which some servers mishandle. PHP 7.3's built-in server
drops the connection, which failed the curl tests on PHP 7.3. Use
CURL_HTTP_VERSION_2TLS instead, curl's own default since 7.62: https
still negotiates HTTP/2.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
proc_open() only accepts an array command as of PHP 7.4, so on 7.3 the
test server never started. Use a command string, with exec so that
proc_terminate() stops the server rather than just the shell.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Guard::resolve() combined gethostbynamel(), which is IPv4 only, with a
DNS AAAA query, which skips the hosts file. IPv6 addresses from the
hosts file, such as ::1 for localhost, were never found, so safe mode
couldn't reach a server listening only on them. Use getaddrinfo via
socket_addrinfo_lookup() when ext-sockets is available, falling back to
the previous lookups otherwise.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
curl keeps only the last CURLOPT_RESOLVE entry for a given host and
port, so passing one entry per address meant a host with both IPv6 and
IPv4 addresses was only ever tried on the last one. A server reachable
on just the other address (e.g. listening only on ::1) failed to
connect. Pass all the addresses in a single entry instead, which curl
has supported since 7.59.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Guard relied on FILTER_FLAG_GLOBAL_RANGE, which only exists from PHP
8.2. It now checks addresses against its own list taken from the IANA
IPv4 and IPv6 special-purpose registries (plus multicast), so every PHP
version refuses the same addresses. The list also covers ranges the PHP
flag let through, such as 2001:db8::/32 and 3fff::/20.
Declare php >= 7.1 (the nullable parameter type needs it), so older PHP
versions keep resolving to 0.1.x. Tests run on PHPUnit 9.6 as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
set_safe_mode(true) refuses anything but http and https, refuses hosts
that resolve to loopback, private, link-local or reserved addresses,
pins curl to the addresses that were checked (no DNS rebinding), and
follows redirects one hop at a time so each is checked, dropping
credentials when a redirect changes origin. Hosts or ranges can be
allowed explicitly for development servers.
Off by default. Adds a PHPUnit suite.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>