I know that this topic is very old and closed, but because of how confusing all of this was (and is!), I thought I would follow up with more details about the problem, as well as how Local has implemented a fix in 6.5.2.
So basically, in vanilla PHP, there are two ways of making requests to third-party hosts over HTTPS:
- file_get_contents()
- cURL
Here’s a simple PHP file you can save to the site root to get a rough idea for how each of those types of requests work. Simply update the githubusername
to your actual Github username in order for PHP to make successful requests:
<?php
// Get details about what PHP's openssl thinks about the certificates.
var_dump(openssl_get_cert_locations());
// save a url to an external HTTPS endpoint
$url = 'https://api.github.com/users/githubusername';
// ************************************************************
// Try using file_get_contents()
// ************************************************************
echo "<h2>Making requests with file_get_contents()</h2>";
// Create a stream with a custom header so that Github doesn't reject the request
// See: https://stackoverflow.com/a/39912696/6107112
// https://stackoverflow.com/a/2107792/6107112
// https://www.php.net/manual/en/function.file-get-contents.php#example-2207
$opts = [
"http" => [
"method" => "GET",
"header" => "User-Agent: githubusername"
]
];
$response = file_get_contents($url, false, stream_context_create($opts));
var_dump(json_decode($response));
// ************************************************************
// Try using cURL
// ************************************************************
echo "<h2>Making requests with curl_exec()</h2>";
// initialize a handle
$ch = curl_init();
// set url
curl_setopt($ch, CURLOPT_URL, $url);
// When working with Github api, set a User-Agent header as my username. See: https://stackoverflow.com/a/39912696/6107112
curl_setopt($ch, CURLOPT_USERAGENT, "githubusername");
// return transfer as string
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// write the details of the request to a file so we can read it in
$errorFile = dirname(__FILE__) . 'curl_error.txt';
$out = fopen($errorFile, "w");
// be verbose about what's happening with the request
curl_setopt($ch, CURLOPT_VERBOSE, true);
// write stderr to our file
curl_setopt($ch, CURLOPT_STDERR, $out);
// make the actual request
$output = curl_exec($ch);
// close the handle and file
curl_close($ch);
fclose($out);
// print the contents of the file to our response buffer and delete the error file
echo "<pre>";
echo htmlspecialchars(file_get_contents($errorFile));
unlink($errorFile);
echo "</pre>";
Here’s a screenshot of what that page looks like for me in a browser:
Using the above PHP file is an interesting way to explore of the problem, but how is WordPress still able to make a secure connection over HTTPS despite a missing certificate? For example, how is WordPress still able to check for Core and Plugin updates as well as download new releases? The answer is that WordPress ships with it’s own certificates, and crafts its own cURL requests using the WP_Http->request method. This screenshot should give a rough idea of how that’s done:
So back to how Local is resolving this. We decided that the best thing would be to have these low-level php functions mimic the kind of requests that WordPress’ HTTP API is making. Towards that end, the latest version of Local and the latest builds of PHP (7.4.30, 8.0.22, 8.1.9) ship with a openssl.cafile configuration that points the certificate bundle included with WordPress.
Hope that’s interesting to others and helps your custom PHP request code work reliably over HTTPS!