I’m writing a RESTful API. I’m having trouble with uploading images using the different verbs.
Consider:
I have an object which can be created/modified/deleted/viewed via a post/put/delete/get request to a URL. The request is multi part form when there is a file to upload, or application/xml when there’s just text to process.
To handle the image uploads which are associated with the object I am doing something like:
if(isset($_FILES['userfile'])) { $data = $this->image_model->upload_image(); if($data['error']){ $this->response(array('error' => $error['error'])); } $xml_data = (array)simplexml_load_string( urldecode($_POST['xml']) ); $object = (array)$xml_data['object']; } else { $object = $this->body('object'); }
The major problem here is when trying to handle a put request, obviously $_POST doesn’t contain the put data (as far as I can tell!).
For reference this is how I’m building the requests:
curl -F userfile=@./image.png -F xml="<xml><object>stuff to edit</object></xml>" http://example.com/object -X PUT
Does anyone have any ideas how I can access the xml
variable in my PUT request?
Advertisement
Answer
First of all, $_FILES
is not populated when handling PUT requests. It is only populated by PHP when handling POST requests.
You need to parse it manually. That goes for “regular” fields as well:
// Fetch content and determine boundary $raw_data = file_get_contents('php://input'); $boundary = substr($raw_data, 0, strpos($raw_data, "rn")); // Fetch each part $parts = array_slice(explode($boundary, $raw_data), 1); $data = array(); foreach ($parts as $part) { // If this is the last part, break if ($part == "--rn") break; // Separate content from headers $part = ltrim($part, "rn"); list($raw_headers, $body) = explode("rnrn", $part, 2); // Parse the headers list $raw_headers = explode("rn", $raw_headers); $headers = array(); foreach ($raw_headers as $header) { list($name, $value) = explode(':', $header); $headers[strtolower($name)] = ltrim($value, ' '); } // Parse the Content-Disposition to get the field name, etc. if (isset($headers['content-disposition'])) { $filename = null; preg_match( '/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/', $headers['content-disposition'], $matches ); list(, $type, $name) = $matches; isset($matches[4]) and $filename = $matches[4]; // handle your fields here switch ($name) { // this is a file upload case 'userfile': file_put_contents($filename, $body); break; // default for all other files is to populate $data default: $data[$name] = substr($body, 0, strlen($body) - 2); break; } } }
At each iteration, the $data
array will be populated with your parameters, and the $headers
array will be populated with the headers for each part (e.g.: Content-Type
, etc.), and $filename
will contain the original filename, if supplied in the request and is applicable to the field.
Take note the above will only work for multipart
content types. Make sure to check the request Content-Type
header before using the above to parse the body.