SSL and PHP Streams, Part 1
docnet.nu
docnet.nu
It's generally advised to set allow_url_include to off which would make this code not work. Also if you're mention curl in your technical article about code you should ask yourself why your code isn't using the curl integration in the language you're using.
Putting a "You are doing it wrong" in your title is just asking for people to point out everything that is wrong with it.
A major element of the 5.6 changes (documented in an article not yet written, but should be by early next week) is to protect Jimmy the junior dev from himself - chances are that an inexperienced dev, given the choice between a one-liner and several lines with a bunch of scary-looking options, is going to pick the former. I'm not advocating it, just stating it.
Why not? file_get_contents makes things much simpler, and there's nothing really wrong with it. Fetching a JSON file is simple, for example:
$file = file_get_contents("http://example.com/some.json");
if ($file === FALSE) {
die("Failed to get JSON file");
}
$data = json_decode($file);
if ($data === NULL) {
die("Failed to decode JSON");
}
echo htmlspecialchars($data->foo->bar);
To be honest, I think cURL is unnecessary in most cases. PHP's HTTP streams do a better job without relying on an additional extension (HTTP streams are in the PHP core, while cURL must be built separately and often installed separately), and the HTTP stream context options with their string key names are much nicer than the dreaded CURLOPT_* constants.1) The name of the function is file_get_contents, however you're not getting a file. It just annoys me, it reads wrong. stream_get_contents is better but I still wouldn't go reaching for streams for HTTP requests. 2) It provides you with a lack of control. 3) It's pretty sloppy, to do anything like POST requests with bodies you have to do hacky stuff.
I wouldn't be writing my own curl call request either I would use a library such as Guzzle.
No, you are getting a file. It's from a remote location, but it's a file nonetheless. Are ftp:// resources not files? Are ssh:// resources not files?
>2) It provides you with a lack of control.
Does it? Stream context options give you a degree of control. If file_get_contents isn't quite right for you, there's also fopen.
>3) It's pretty sloppy, to do anything like POST requests with bodies you have to do hacky stuff.
$ctx = stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $some_post_data_here
]
]);
$result = file_get_contents('http://example.com/post_endpoint', false, $ctx);
Where's the hacky stuff?It's also shorter and more readable than the equivalent in cURL (the PHP cURL extension, not the command-line, obviously) and you don't even need to install an extension!
FTP of course it's a file, it's a FILE TRANSFER PROTOCOL. HTTP is HYPERTEXT TRANSFER PROTOCOL. You're getting Hypertext. SSH:// are not files. You are getting the contents of a stream. So you're getting the stream content.
> Does it? Stream context options give you a degree of control. If file_get_contents isn't quite right for you, there's also fopen.
To be fair I've not looked into all the options for the streams in http. But does it give you the option to use global dns cache? How about save the cookies and reuse them for later requests? Will it deal with gzip for me? If you can do all the stuff curl can do I'll be all fair enough you've got a point.
> Where's the hacky stuff?
For me the fact I had to define the header.
> It's also shorter and more readable than the equivalent in cURL
It's not more readable. It says it's getting a file, you lied to me and got stream contents instead.
> and you don't even need to install an extension!
If you really don't want to install extensions and just use the core language fair enough, I would rather use the best tools available.
The responses HTTP gives are very file-like. So much so HTTP is often used for distributing files. Indeed, most pages reference several files.
> But does it give you the option to use global dns cache?
I don't believe so. I don't think most users need to, however.
> How about save the cookies and reuse them for later requests?
I can't actually remember. Possibly.
> Will it deal with gzip for me?
Yes.
> If you can do all the stuff curl can do I'll be all fair enough you've got a point.
It can do most of the stuff cURL does. It doesn't do everything, but my point is most of the time you don't necessarily need cURL, and IMO this API is nicer.
> For me the fact I had to define the header.
That's not hacky at all; sending POST requests usually requires specifying a Content-Type. cURL just automatically does that for you in some cases.
> It's not more readable. It says it's getting a file, you lied to me and got stream contents instead.
You did get a file. The file the request returned.
> If you really don't want to install extensions and just use the core language fair enough, I would rather use the best tools available.
You might be missing my point.
cURL is the functions you need to 'get' a URL, not file_get_contents.
Oh, that's a shame. Though there's nothing forcing you to use any particular shared hosts. There are plenty of awful ones, but you don't have to use them.
>cURL is the functions you need to 'get' a URL, not file_get_contents.
Well, many shared hosts have that disabled. ;)
Pardon me if I'm missing something, but that sounds like a horrible design decision, and anyone who tries to use TLS streams without reading the documentation is fooled into a false sense of security.
"We must also specify the expected peer name on the presented certificate with the `CN_match` option, it will not be inferred from the URL and if it is not specified, the name on the certificate will not be validated."
WTF? Who ever got the idea that this is something that can ever hit production?
Effectively if you write any code you're not going to deploy yourself, you have to specify the CN_match and verify_peer. Others will not know about the limitations, so this function is messed up until after 5.5 is EOLed.
Designing APIs is hard and bad defaults can hurt for many major versions after they're changed.
The next article will cover the changes in 5.6 and why you don't need to set those ctx opts any more (and a few more you might want to set instead)
You mean in the version that's not properly released or anywhere close to production ready? 5.5 still defaults to not verifying according to the documentation.
No one should be using that, if only because with the magic strings everywhere it is not guaranteed to blow up when there are breaking changes.
Are we talking about the same language? This is PHP, not Tcl.
>all of the underlying OpenSSL crap just spills up to the API.
None of it does. I mean, there's an extension for OpenSSL, but that's not what's 'spilling up'. What's happening is there were poor defaults, so people have to change them. Luckily, PHP 5.6 fixes this.
>No one should be using that, if only because with the magic strings everywhere it is not guaranteed to blow up when there are breaking changes.
"Magic strings"? Are you seriously trying to compare using string names for keys to specify options (standard practise in many languages) with magic numbers?