php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #68605 $_FILES should not only be filled when POST
Submitted: 2014-12-14 17:20 UTC Modified: 2014-12-15 06:44 UTC
From: lewiscowles at me dot com Assigned:
Status: Closed Package: PHP Language Specification
PHP Version: Irrelevant OS: ALL
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: lewiscowles at me dot com
New email:
PHP Version: OS:

 

 [2014-12-14 17:20 UTC] lewiscowles at me dot com
Description:
------------
basically I am going to have to switch a major part of my app to Python because I cannot PUT multiple files via CURL custom request. Made worse by the fact PHP seems to have a bug that cuts off php:/input after the first file (probably to do with C strings and EOF)

Test script:
---------------
// server receiving data (putEcho.php was used)
<?php $reqMeth = ( isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( $_SERVER['REQUEST_METHOD'] ) : 'GET' ); ?>
<?php
$_PUT = array();
if( $reqMeth == 'PUT' ) {
	$putF = fopen( "php://input", "r" );
	$tmp = fread( $putF, MAX_POST_SIZE*10 ); // limit maximum PUT size to maximum POST size (seems reasonable)
	fclose($putF);
	$_PUT =  parse_raw_http_request( $tmp );
}
?>
put:
<?php var_dump( $_PUT ); ?>
file:
<?php var_dump( $_FILES ); ?>
<?php
//
// Functions
//
function parse_raw_http_request($input) {
	//var_dump($input);
	$a_data = array();
	// grab multipart boundary from content type header
	$matches = explode( str_ireplace( 'multipart/form-data; boundary=', '', $_SERVER['CONTENT_TYPE'] ), $input ); //preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
	// content type is probably regular form-encoded
	if (!count($matches)) {
		// we expect regular puts to containt a query string containing data
		parse_str(urldecode($input), $a_data);
		return $a_data;
	}
	$boundary = $matches[0];
	// split content by boundary and get rid of last -- element
	$a_blocks = explode($boundary,$input);//preg_split("/-+$boundary/", $input);
	//array_pop($a_blocks);
	// loop data blocks
	foreach ($a_blocks as $id => $block) {
		if (empty($block)) { continue; }
		//var_dump( $block );
		// you'll have to var_dump $block to understand this and maybe replace \n or \r with a visibile char
		// parse uploaded files
		if (mb_strpos($block, 'application/octet-stream') !== FALSE) {
			//var_dump( $block );
			// match "name", then everything after "stream" (optional) except for prepending newlines
			preg_match("/name=\"([^\"]*)\".*filename=\"([^\"]*)\".*stream[\n|\r]+([^\n\r].*)?$/ms", $block, $matches);
			$a_data['files'][$matches[1]]['filename'] = basename( $matches[2] ); //for some reason CURL can send files with an absolute path
			$a_data['files'][$matches[1]]['data'] = $matches[3];
		} else { // parse all other fields
			// match "name" and optional value in between newline sequences
			preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/su', $block, $matches);
			$a_data[$matches[1]] = $matches[2];
		}
	}
	return $a_data;
}

// client to send data (name does not matter)
<?php

$data = array(
	'text'=>'bob
    bob
	漢字</textarea>
	ghghg
表外字?
表外字?表外字?',
	'file1' =>	new CURLFile( __DIR__.'/need a second monicle.jpg' ),
	'kanji'=>'漢字表外字?表外字?表外字?',
	'file2' => new CURLFile( __DIR__.'/10620539_301923573265336_9157682394656180014_n.jpg' )
);
?>
<pre><?php var_dump( json_decode( CURL_REQ( 'http://censored/putEcho.php', 'PUT', $data/*$_POST*/ ), true ) ); ?></pre>
<?php
//
// CURL lightweight helper Function...
//
function CURL_REQ( $url, $method, &$data ) {
	$ch = curl_init( $url );
	curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
	curl_setopt( $ch, CURLOPT_CUSTOMREQUEST, $method );
	curl_setopt( $ch, CURLOPT_POSTFIELDS, $data );
	$response = curl_exec( $ch );
	return $response;
}

Expected result:
----------------
I expected (although I think I understand why) to be able to manually parse the results of my request, even if PHP did not support populating the $_FILES array...

Basically within 15minutes I was able to get this to work with Python, but a large part of my system is PHP. I am always telling people PHP is a great language and developers are part of the problem, but it seems to me that the decision to not populate $_FILES is a bit mad

for desired outcome, I like how python FLASK is able to give me 
request.args ($_GET)
request.form ($_PUT or $_POST)
request.files ($_FILES)

and wondered if it was something that could be easily patched into PHP.

In any case I have the C code and will try custom compiling, then patching, but my weekend is now dead, so it may be some time before I come up with something and after 20mins python working, it might just be easier for me to convert HTTP methods to python =(

Actual result:
--------------
well in the example given everything after the first file is missing from php://input stream, so not even the second var gets through... I can mitigate this by moving the posted form field values easily, but I never get more than one file, and I am unsure if it is complete (seems to end if 255 and 0 if I run ord on the last two bytes)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-12-15 06:44 UTC] lewiscowles at me dot com
-Status: Open +Status: Closed
 [2014-12-15 06:44 UTC] lewiscowles at me dot com
Bug closed due to extreme stupidity... 

switched to 
stream_get_contents instead of file_get_contents and everything just works...

must have been a long weekend really sorry
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC