Streams are pretty much the forgotten features in php, I don’t see many people making use of it, partially because it works transparently under the hood when we use php’s functions to read/write from the disk or network. I shall write about how you can create custom stream handlers and protocols in php in userland which is pretty amazing, however for now I am documenting one of the crazy things I noticed in php.
I am currently writing a plugin for a web-service which allows you to post files to it. Its pretty simple, your code would use the plugin and then pass the uploaded file as an argument and it will post it to the endpoint of the web-service. You can read raw POST data using the php://input format which will give you the contents of a post looking similar to a query string, however if you wanted to upload a file, you can’t use the same to read the file directly to the stream. Part of what I was trying to accomplish with streams was to be able to write the file to an S3 server, however sadly there is no way (or atleast not that I know of to intercept the file before it gets written to the disk to avoid the wasted IO overhead).
Interestingly you can always post using the libCurl functions in php, however there is a little gottcha that I didn’t know about and that’s documented as the RFC1867-posting implementation of curl. So to upload a file using the command line curl in the same way as you would do with a multipart form you can run the following command along with the response, which is basically a dump of the $_POST and $_FILES global variables.
leonardo:foo rp$ curl -F upload=@nav_logo13.png -f http://scratch.local/foo/server.php
Array
(
)
Array
(
[upload] => Array
(
[name] => nav_logo13.png
[type] => application/octet-stream
[tmp_name] => /tmp/phpNLOAew
[error] => 0
[size] => 28736
)
)
Note: Notice the @ sign before the file name, this is where the gottcha exists. Translating this into php code.
$ch = curl_init(); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_VERBOSE, 1); // true to return the transfer as a string of the return value // of 'curl_exec' instead of outputting it directly curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)"); curl_setopt($ch, CURLOPT_URL, 'http://scratch.local/foo/server.php'); curl_setopt($ch, CURLOPT_POST, true); $post = array( 'logo' => '@nav_logo13.png', 'first_name' => 'Rajat' ); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $response = curl_exec($ch); print_r($response);
Cleanup the code to use the bits that apply in your context but this is pretty much that it involves. Pretty interesting though I wish there was a better way, one of the ways would be to possibly run my own server using stream_accept_socket and then read directly from the socket but there is all this additional layer that I can get out of HTTP that I don’t want to loose.
Does anyone have a better idea? suggestions welcome.
