diff --git a/composer.json b/composer.json index 6500e7ccbf8af..a393da6b71bde 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "php": ">=7.4" }, "suggest": { - "ext-dom": "*", + "ext-curl": "*", + "ext-dom": "*" "ext-mysqli": "*" }, "require-dev": { diff --git a/docker-compose.yml b/docker-compose.yml index cc2ed8d94975e..1082bd1bf3553 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,8 +51,17 @@ services: - ./tools/local-env/php-config.ini:/usr/local/etc/php/conf.d/php-config.ini - ./:/var/www - # Copy or delete the Memcached dropin plugin file as appropriate. - command: /bin/sh -c "if [ $LOCAL_PHP_MEMCACHED = true ]; then cp -n /var/www/tests/phpunit/includes/object-cache.php /var/www/src/wp-content/object-cache.php; else rm -f /var/www/src/wp-content/object-cache.php; fi && exec php-fpm" + # Install the loopback-request shim (see tools/local-env/mu-plugins/fix-docker-loopback.php), then + # copy or delete the Memcached dropin plugin file as appropriate. + command: >- + /bin/sh -c " + mkdir -p /var/www/${LOCAL_DIR-src}/wp-content/mu-plugins && cp -f /var/www/tools/local-env/mu-plugins/fix-docker-loopback.php /var/www/${LOCAL_DIR-src}/wp-content/mu-plugins/fix-docker-loopback.php; + if [ $LOCAL_PHP_MEMCACHED = true ]; then + cp -n /var/www/tests/phpunit/includes/object-cache.php /var/www/src/wp-content/object-cache.php; + else + rm -f /var/www/src/wp-content/object-cache.php; + fi && exec php-fpm + " # The init directive ensures the command runs with a PID > 1, so Ctrl+C works correctly. init: true diff --git a/tools/local-env/mu-plugins/fix-docker-loopback.php b/tools/local-env/mu-plugins/fix-docker-loopback.php new file mode 100644 index 0000000000000..6c8dbaac03707 --- /dev/null +++ b/tools/local-env/mu-plugins/fix-docker-loopback.php @@ -0,0 +1,54 @@ + $args The HTTP request arguments. + * @param string $url The request URL. + */ +function resolve_loopback_to_host_gateway( $handle, array $args, string $url ): void { + $host = wp_parse_url( $url, PHP_URL_HOST ); + if ( ! is_string( $host ) ) { + return; + } + + // Only rewrite requests aimed at this site (loopback), not arbitrary outbound requests. + $home_host = wp_parse_url( home_url(), PHP_URL_HOST ); + if ( $host !== $home_host ) { + return; + } + + // host.docker.internal resolves to the host gateway, which reaches the published web-server port. + $gateway = gethostbyname( 'host.docker.internal' ); + if ( 'host.docker.internal' === $gateway ) { + return; // Not running under Docker Desktop / gateway unavailable. + } + + $port = wp_parse_url( $url, PHP_URL_PORT ); + if ( ! $port ) { + $port = ( 'https' === wp_parse_url( $url, PHP_URL_SCHEME ) ) ? 443 : 80; + } + + curl_setopt( $handle, CURLOPT_RESOLVE, array( "{$host}:{$port}:{$gateway}" ) ); +}