I am trying to parse the ampersand value in a PHP string. It keeps returning blank values after I run my code and I am sure it is because of the 'ampersand' value in my variable ($area). I tried htmlspecialchars, html_entity_decode but to no avail. Please see code below:
<?php
/** Create HTTP POST */
$accomm = 'ACCOMM';
$state = '';
$city = 'Ballan';
$area = 'Daylesford & Macedon Ranges';
$page = '10';
$seek = '<parameters>
<row><param>SUBURB_OR_CITY</param><value>'. $city .'</value></row>
<row><param>AREA</param><value>'. $area .'</value></row>
</parameters>';
$postdata = http_build_query(
array(
'DistributorKey' => '******',
'CommandName' => 'QueryProducts',
'CommandParameters' => $seek)
);
$opts = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata)
);
/** Get string output of XML (In URL instance) */
$context = stream_context_create($opts);
$result = file_get_contents('http://national.atdw.com.au/soap/AustralianTourismWebService.asmx/CommandHandler?', false, $context);
?>
Pls how do I fix this
Thanks
XML is not HTML, and vice-versa. You cannot have a bare & in an XML document since it is a special character in XML documents. If you're just defining a static string like this your can replace it with & and move on with your day.
If you need to encode arbitrary strings that may or may not contain & or another XML special char, then you'll need functions like:
function xmlentity_encode($input) {
$match = array('/&/', '/</', '/>/', '/\'/', '/"/');
$replace = array('&', '>', '<', ''', '"');
return preg_replace($match, $replace, $input);
}
function xmlentity_decode($input) {
$match = array('/&/', '/>/', '/</', '/'/', '/"/');
$replace = array('&', '<', '>', '\'', '"');
return preg_replace($match, $replace, $input);
}
echo xmlentity_encode("This is testing & 'stuff\" n <junk>.") . "\n";
echo xmlentity_decode("This is testing & 'stuff" n >junk<.");
Output:
This is testing & 'stuff" n >junk<.
This is testing & 'stuff" n <junk>.
I'm fairly sure that PHP's XML libs do this for you transparently, [and also respecting the character set] but if you're manually constructing your own XML document then you have to ensure that you're aware of things like this.
Related
I am trying to validate EU VAT numbers using the script by Eugene Mihailescu that can be found here:
https://stackoverflow.com/a/29955950
Although this script was working flawlessly up until a recently, now Apache comes up with the following error:
PHP Warning: file_get_contents(http://ec.europa.eu/taxation_customs/vies/services/checkVatService): failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error
I searched and found a cUrl version of the same script by Professor Abronsius which can be found here:
https://stackoverflow.com/a/56035880
Unfortunately a similar error comes up in the console:
Object { response: false, info: {…}, errors: "The requested URL returned error: 500 Internal Server Error" } errors: "The requested URL returned error: 500 Internal Server Error" info: Object { url: "https://ec.europa.eu/taxation_customs/vies/services/checkVatService", http_code: 500, header_size: 0, … } …….
To my understanding the problem may rely in the "VIES" server itself, but there are no announcements in their website:
https://ec.europa.eu/taxation_customs/vies/#/vat-validation
Any ideas/suggestions?
Thanks to MexicanGuy for your solution, it was really useful in fixing the problem. In regard to the original question and the code used from :
https://github.com/lepoco/vies-api/blob/master/veis-api.php
by Eugene Mihailescu
It can be fixed by removing : "SOAPAction: checkVatService" from the end of the header line.
After this it returns XML content, however it is slightly different than before as it includes the XML namespace "NS2" on the relevant nodes. Adjusting the regex to the following can fix it.
$pattern = '/ns2:(%s).*?([\s\S]*)</ns2:\1/';
Then it started working again for us.
However we came to the conclusion that the solution isn't optimal in that it uses a regex to read XML data. There are bespoke XML readers and indeed SOAP extensions in PHP that can be used. There is also a potential library that seems perhaps more robust to future changes :
https://github.com/DragonBe/vies
Which we are thinking is a superior long term solution.
Ran into the same problem, searched a little bit, and it seems that soap envelope as changed.
Made some changes and included some functions to maintain previous workflow.
Hope it helps
DEFINE ( 'VIES_URL', 'http://ec.europa.eu/taxation_customs/vies/services/checkVatService' );
/**
* VIES VAT number validation
*
* #author Eugen Mihailescu
*
* #param string $countryCode
* #param string $vatNumber
* #param int $timeout
*/
function array_key_replace($item, $replace_with, array $array){
$updated_array = [];
foreach ($array as $key => $value) {
if (!is_array($value) && $key == $item) {
$updated_array = array_merge($updated_array, [$replace_with => $value]);
continue;
}
$updated_array = array_merge($updated_array, [$key => $value]);
}
return $updated_array;
}
function string_between_two_string($str, $starting_word, $ending_word)
{
$subtring_start = strpos($str, $starting_word);
//Adding the starting index of the starting word to
//its length would give its ending index
$subtring_start += strlen($starting_word);
//Length of our required sub string
$size = strpos($str, $ending_word) - $subtring_start;
// Return the substring from the index substring_start of length size
return substr($str, $subtring_start, $size);
}
function viesCheckVAT($countryCode, $vatNumber, $timeout = 30) {
$response = array ();
$pattern = '/<(%s).*?>([\s\S]*)<\/\1/';
$keys = array (
'countryCode',
'vatNumber',
'requestDate',
'valid',
'name',
'address'
);
//Changed envelope
$content = "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'
xmlns:tns1='urn:ec.europa.eu:taxud:vies:services:checkVat:types'
xmlns:impl='urn:ec.europa.eu:taxud:vies:services:checkVat'>
<soap:Header>
</soap:Header>
<soap:Body>
<tns1:checkVat xmlns:tns1='urn:ec.europa.eu:taxud:vies:services:checkVat:types'
xmlns='urn:ec.europa.eu:taxud:vies:services:checkVat:types'>
<tns1:countryCode>".$countryCode."</tns1:countryCode>
<tns1:vatNumber>".$vatNumber."</tns1:vatNumber>
</tns1:checkVat>
</soap:Body>
</soap:Envelope>";
$opts = array (
'http' => array (
'method' => 'POST',
'header' => "Content-Type: text/xml; charset=utf-8;",
'content' => sprintf ( $content ),
'timeout' => $timeout
)
);
$ctx = stream_context_create ( $opts );
$result = file_get_contents ( 'https://ec.europa.eu/taxation_customs/vies/services/checkVatService', false, $ctx );
//extract necessary values from answer string
$valid = string_between_two_string($result, '<ns2:valid>', '</ns2:valid>');
$country = string_between_two_string($result, '<ns2:countryCode>', '</ns2:countryCode>');
$vatnumber = string_between_two_string($result, '<ns2:vatNumber>', '</ns2:vatNumber>');
$address = string_between_two_string($result, '<ns2:address>', '</ns2:address>');
$name = string_between_two_string($result, '<ns2:name>', '</ns2:name>');
$dater = string_between_two_string($result, '<ns2:requestDate>', '</ns2:requestDate>');
//insert strings in array and replace index keys by name
$vat = "$country$vatnumber";
$vars = array($vat, $name, $address, $dater, $valid);
$new_array = array_key_replace('0', 'vat', $vars);
$new_array = array_key_replace('0', 'name', $new_array);
$new_array = array_key_replace('0', 'address', $new_array);
$new_array = array_key_replace('0', 'requestDate', $new_array);
$new_array = array_key_replace('0', 'status', $new_array);
return $new_array;
}
print_r (viesCheckVAT ( 'RO', '19386256' ));
Run into the same problem today. Try changing the Content-Type in the Header to text/xml. That's what solved the problem for us in Postman.
Run into the same problem today.
Thanks to EmeseMora for the solution...
I change the Content-Type in the Header to text/xml.
In this way the problem is solved in Postman.
But in my old C++ code I cannot solve.
I try in this way:
LPCWSTR header=L"Content-type: text/xml";
SIZE_T len = lstrlenW(header);
WinHttpAddRequestHeaders (hRequest,header,DWORD(len), WINHTTP_ADDREQ_FLAG_REPLACE);
I am sending a link to my user's email to activate his account by click that link.
example link:
http://test.com/welcome/make_active_user/($user_id)
then I encode $user_id in my controller, after that my link looks like the following one
http://test.com/welcome/make_active_user/17elQOjxrOXDsZdDZVFY7JFrB0TJFojdS+5OTpiIq/XXIaVrDfVWQ7xgOXXqGsURIFe/Udvm/XHrNWtbZXFA2g==
upto this everything is fine. Now i want to decode the $user_id,
but "/" symbol create problem. codeigniter took only those characters before the "/" symbol. How can i get a output of encoded user_id without "/"
I use "$this->encrypt->encode($user_id,$key);" in my controller
Please help
Just pass your encoded $user_id to the php function urlencode() before sending in a link like this:
$user_id_link = urlencode($user_id);
And decode the string you get back from the link with url decode() like this:
$user_id = urldecode($user_id_link);
You need to modify the encrypt class to encode url safe strings.
class MY_Encrypt extends CI_Encrypt
{
function encode($string, $key="", $url_safe=true)
{
$ret = parent::encode($string, $key);
if ($url_safe)
{
$ret = strtr(
$ret,
array(
'+' => '.',
'=' => '-',
'/' => '~'
)
);
}
return $ret;
}
function decode($string, $key="")
{
$string = strtr(
$string,
array(
'.' => '+',
'-' => '=',
'~' => '/'
)
);
return parent::decode($string, $key);
}
}
Then you can create a $url_safe link
Grab the encryption key from your config
$key = $this->config->item('encryption_key');
Create a url safe string by passing true as the third parameter
$encoded_url_safe_string = urlencode($this->encrypt->encode('secret', $key, true));
You will need to use rawurldecode to decode the string
$decoded_url_safe_string = rawurldecode($encoded_url_safe_string, $key);
I faced the same problem in the past, codeigniter encrypt->encode()has disallowed chars for URL but i did the following to solve the problem
first encode() your $user_id
simply replace the disallowed chars from the encrypted string with allowed one.
now before decoding replace those chars back to original one
decode() your $user_id
here is the code:
$this->encrypt->encode($user_id,$key);
$user_id = strtr($user_id,array('+' => '.', '=' => '-', '/' => '~'));
//your email body (link the user id here)
now time for decoding
$user_id = strtr($user_id,array('.' => '+', '-' => '=', '~' => '/'));
$this->encrypt->decode($user_id,$key);
I think your problem isn't with the '/', probably it's with '=' symbol.
To code and decode a url in CodeIgniter you can use any of this php native functions: http://php.net/manual/en/ref.url.php
But to avoid problems when decode the encoded url in CodeIgniter, you can add the special character that you require to use in each case (like '=' in your case) to $config['permitted_uri_chars'] in file application/config/config.php
Try This:
$query = urldecode($query);
$query = urlencode($query);
Im looking to only display certain things from pages like this: http://sc2ranks.com/api/psearch/am/MxGPezz/1t/division/Felanis%20Sierra?appKey=sentinelgaming.net . So far I am able to display something but its not even the correct number, using the php below. Can someone show me how I would display the "achivement-points" of this player from this XML web page?
$url = 'http://sc2ranks.com/api/psearch/am/MxGPezz/1t/division/Felanis%20Sierra?appKey=sentinelgaming.net';
$xml = file_get_contents($url);
echo $xml->achievement-points;
Thanks
The content-type of this file varies depending on the Accept header or the format query parameter. It seems you can retrieve at least XML or JSON.
The default you get from file_get_contents() will be JSON because it does not include an Accept request header, but the default from a browser will be XML because browsers usually include an XML mime type in their Accept request header.
To get JSON:
$url = 'http://sc2ranks.com/api/psearch/am/MxGPezz/1t/division/Felanis%20Sierra?appKey=sentinelgaming.net';
// &format=json is not strictly necessary,
// but it will give you fewer surprises
$json = file_get_contents($url.'&format=json');
$records = json_decode($json);
echo $records[0]->achievement_points, "\n";
To get XML:
$sxe = simplexml_load_file($url.'&format=xml');
echo (string) $sxe->record->{'achievement-points'}, "\n";
To use the $sxe object see this SimpleXML cheat sheet.
Instead of using the format param you could set the Accept header. You can also add some abstraction to getting a url so that you can retrieve the content type and encoding as well. See example below.
function get_url($url, $context=null) {
$response = file_get_contents($url, false, $context);
$ctypeheaders = preg_grep('/^Content-Type:\s/i', $http_response_header);
$ctype = NULL;
if ($ctypeheaders) {
$ctype = end($ctypeheaders);
$ctype = end(explode(':', $ctype, 2));
$ctype = explode(';', $ctype, 2);
$charset = isset($ctype[1]) ? $ctype[1] : '';
if ($charset && preg_match('/charset\s*=\s*([^\s]+)/i', $charset, $matches)) {
$charset = $matches[1];
}
$ctype[1] = $charset;
$ctype = array_map('trim', $ctype);
}
return array($response, $ctype);
}
You can then use get_url() like so:
// With no accept header, just see what we get:
list($content, $contenttype) = get_url($url);
list($type, $encoding) = $contenttype;
// $type will be 'application/xml' or 'application/json'
// $encoding is very handy to know too
// Or we can specify an accept header:
$opt_accept_xml = stream_context_create(array(
'http' => array(
'header' => "Accept: application/xml\r\n"
)
));
list($content, $contenttype) = get_url($url, $opt_accept_xml);
Maybe:
echo $xml->record[0]->achievement-points;
I am trying to work with the PHP API for Amazon's Flexible Payments.
Here is my PHP snippet to send a payment request:
<?php
$string_to_sign = 'GET
authorize.payments-sandbox.amazon.com
/cobranded-ui/actions/start
SignatureMethod=HmacSHA256&SignatureVersion=2&callerKey=my_access_key&callerReference=YourCallerReference&paymentReason=donation&pipelineName=SingleUse&returnUrl=http%3A%2F%2Fproblemio.com&transactionAmount=4.0';
$encoded_string_to_sign = URLEncode(Base64_Encode(hash_hmac('sha256', $string_to_sign, 'my_secret_key')));
$amazon_request_sandbox = 'https://authorize.payments-sandbox.amazon.com/cobranded-ui/actions/start?SignatureVersion=2&returnUrl='.$return_url.'&paymentReason='.$payment_reason.'&callerReference=YourCallerReference&callerKey='.$my_access_key_id.'&transactionAmount=4.0&pipelineName=SingleUse&SignatureMethod=HmacSHA256&Signature='.$encoded_string_to_sign;
// When it goes to the url, it gets the invalid signature error
header('Location: '.$amazon_request_sandbox);
?>
This seems to be following their instructions, but I can't get past that error.
Thanks!!
<?php
$method = 'GET';
$host = 'authorize.payments-sandbox.amazon.com';
$path = '/cobranded-ui/actions/start';
$params = array(
'signatureMethod' => 'HmacSHA256',
'signatureVersion' => '2',
'currencyCode' => 'USD',
'callerKey' => 'Your_Key_ID',
'callerReference' => 'YourCallerReference',
'paymentReason' => 'donation',
'pipelineName' => 'SingleUse',
'returnUrl' => 'http://yourcallback.com',
'transactionAmount'=> '5',
'version' => '2009-01-09',
);
$params = array_map('rawurlencode', $params);
$paramStringArray = array();
foreach($params as $key => $value){
$paramStringArray[] = $key . '=' . $value;
}
$paramString = implode('&', $paramStringArray);
$string_to_sign = $method . "\n"
. $host . "\n"
. $path . "\n"
. $paramString;
$signature = base64_encode(hash_hmac(
'sha256',
$string_to_sign,
'Your_Super_Secret_Key',
true
));
$amazon_request_sandbox = "https://{$host}{$path}?" . $paramString .
'&signature=' . rawurlencode($signature);
header('Location: '.$amazon_request_sandbox);
?>
Okay... using the structure from the code below, I've finally figured this whole thing out via the code above. There are three things of note to keep track of while forming your signature/URL...
It seems that the parameter "transactionAmount" is necessary for a valid Co-branded UI Pipeline, even though there's no specific instruction alluding to the issue.
If any of your parameters have/had spaces in them, and you tried to use html_build_query() in all but the latest (5.4) version of PHP, you would be given an encoding scheme that featured "+" marks for spaces instead of "%20" which is what Amazon appears to like. My code above takes care of that by implementing rawurlencode() on the entire parameter array.
The ordering of the parameters is paramount in the construction of the signature. The keys (not the values) need to be in case-insensitive alphabetical order. It's also worth noting that despite what the documentation says for the API, both the ampersands (&) and the equals (=) must be present in the creation of the query string for the signature.
Ex:
Query String for Signature: callerKey=1111111111111¤cyCode=USD&signatureVersion=2
Some Other Things I Noticed...
In the sample code included with the PHP SDK (2010-8-28), the "paymentReason" attribute in the file "CBUISingleUsePipelineSample.php" is listed as "HarryPotter 1-5 DVD set". Since this attribute has spaces in it, it throws that ever-annoying "invalid signature" error when you try to visit the generated link because html_build_query() is used to generate the query string for the URL. To fix this issue, open up "CBUIPipeline.php", and look for the following line in the constructUrl() method...
$queryString = http_build_query($parameters, '', '&');
replace it with:
$queryString = str_replace('+', '%20', http_build_query($parameters, '', '&'));
That'll solve the space-encoding problem for older versions of PHP (< 5.4). With the latest version, there's an "enc_type" flag you can set.
Last things Last...
This is my first post on StackOverflow so don't kill me if I broke protocol. Hope it helps!
Try this piece of code:
<?php
$method = 'GET';
$host = 'authorize.payments-sandbox.amazon.com';
$path = '/cobranded-ui/actions/start';
$params = array(
'SignatureMethod' => 'HmacSHA256'
'SignatureVersion' => 2,
'callerKey' => 'my_access_key',
'callerReference' => 'YourCallerReference',
'paymentReason' => 'donation',
'pipelineName' => 'SingleUse',
'returnUrl' => 'http://problemio.com&transactionAmount=4.0',
);
$string_to_sign = $method . "\n"
. $host . "\n"
. $path . "\n"
. http_build_query($params);
$signature = base64_encode(hash_hmac(
'sha256',
$string_to_sign,
'my_secret_key'
));
$params['Signature'] = $signature;
$amazon_request_sandbox = "https://{$host}{$path}?" . http_build_query($params);
header('Location: ' . $amazon_request_sandbox);
So I made a few changes:
PHP's http_build_query() to build the query string (ensure correct encoding)
trying to re-use your vars vs. duplicating the efforts (makes it easier to spot mistakes, etc.)
explicit \n - maybe your editor entered \r or \r\n
HTH
I’ve run into a limitation in the cURL bindings for PHP. It appears there is no easy way to send the same multiple values for the same key for postfields. Most of the workarounds I have come across for this have involved creating the URL encoded post fields by hand tag=foo&tag=bar&tag=baz) instead of using the associative array version of CURLOPT_POSTFIELDS.
It seems like a pretty common thing to need to support so I feel like I must have missed something. Is this really the only way to handle multiple values for the same key?
While this workaround might be considered workable (if not really annoying), my main problem is that I need to be able to do multiple values for the same key and also support file upload. As far as I can tell, file upload more or less requires to use the associate arravy version of CURLOPT_POSTFIELDS. So I feel like I am stuck.
I have posted about this problem in more detail on the cURL PHP mailing list in the hopes that someone there has some ideas about this.
Suggestions or hints on where I can look for more information on this are greatly appreciated!
I ended up writing my own function to build a custom CURLOPT_POSTFIELDS string with multipart/form-data. What a pain.
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
// $postfields is an assoc array.
// Creates a boundary.
// Reads each postfields, detects which are #files, and which values are arrays
// and dumps them into a new array (not an assoc array) so each key can exist
// multiple times.
// Sets content-length, content-type and sets CURLOPT_POSTFIELDS with the
// generated body.
}
I was able to use this method like this:
curl_setopt_custom_postfields($ch, array(
'file' => '#/path/to/file',
'tag' => array('a', 'b', 'c'),
));
I am not certain of CURLOPT_HTTPHEADER stacks, so since this method calls it, I made certain that the function would allow for the user to specify additonal headers if needed.
I have the full code available in this blog post.
If you use tag[] rather than tag for the name, PHP will generate an array for you, in other words, rather than
tag=foo&tag=bar&tag=baz
You need
tag[]=foo&tag[]=bar&tag[]=baz
Note that when urlencoded for transmission this should become
tag%5B%5D=foo&tag%5B%5D=bar&tag%5B%5D=baz
Vote for PHP Bug #51634.
Try #BeauSimensen's answer.
Guzzle can do this. See an example below.
$client = new \GuzzleHttp\Client();
$client->request('POST', $url, [
'multipart' => [
[ 'name' => 'foo', 'contents' => 'bar' ],
[ 'name' => 'foo', 'contents' => 'baz' ],
]
]);
I ran into the same issue. But I was able to solve it this way.
for($cnt = 0; $cnt < count($siteRows); $cnt++)
{
$curlParams['site_ids['.$cnt.']'] = $siteRows[$cnt]->site_id;
}
Works for files too:
for($cnt = 0; $cnt < count($imageRows); $cnt++)
{
$curlParams['product_images['.$cnt.']'] = '#'.$imageRows[$cnt]->full_path;
}
I got it working using:
curl_setopt($ch, CURLOPT_POSTFIELDS,array('tag[0]'=>'val0','tag[1]'=>'val1'));
then $_POST results in: $_POST['tag'][0] = 'val0' and $_POST['tag'][1] = 'val1'
I think the established standard for multiple values in one key (or the same key) is to have it concatenated with a delimiter, such as for multiple selections of option lists in form elements. I believe this delimiter is the tab character (\t) or the pipe symbol (|).
If the keyname is terminated with [] (like tag[]), PHP will automatically convert the values into an array for your convenience.
lImbus and paul, thank you for your input.
If I had control over the form I am posting to, I could probably find an alternate solution to this problem. However, I do not have any control over the form. And I am almost positive that the software reading the post is not PHP and does not obey the tag[] standards.
Even if it did, cURL does not seem to obey the tag[] syntax either. Basically, I tried the following and neither worked...
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '#/pathtofile', 'tag[]' => array('a', 'b', 'c'));
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '#/pathtofile', 'tag' => array('a', 'b', 'c'));
And again, I don't think that passing tag[] would work anyway as the form I am posting to is actually looking for 'tag' and not 'tag[]'.
I am really starting to get the feeling that the cURL PHP bindings really have no support for this. Which seems so surprising to me. It seems like it can do quite literally anything else, yet it is unable to do something simple like this?
DON'T USE GUZZLE:
# at your command line start php interactive
user#group:~:php -a
php > $arr=array('var' => array(1,2,3,4));
php > echo http_build_query($arr);
var%5B0%5D=1&var%5B1%5D=2&var%5B2%5D=3&var%5B3%5D=4
php > echo urldecode(http_build_query($arr));
var[0]=1&var[1]=2&var[2]=3&var[3]=4
So, you need http_build_query where you pass a hash array of key-values; your (array) variable is entered as a key with value a array instead a scalar value like 'var' => array(1,2,3,4). Now, http_build_query can format the post fields of curl command:
$fields = array('key1' => 'value1', 'var' => array(1,2,3,4));
$curlPost = \http_build_query($fields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $curlPost);
that's 3 lines of code! how many 1000s of code lines are in Guzzle? (*)
So far, I used curl to:
manage Google OAuth protocol with success
connect with APIs like mailgun
handle paypal smart buttons
that's a replacement of million of lines with some 100s!
(*): the result of http_build_query can be formatted further according your needs.
I ran into the same problem in which I had to send a parameter which has to be an array from a PHP server to another server that does not use '[]' for mixing values with the same key along with a file.
In Laravel 8 I could achieve this goal with Http client (of course Http client uses guzzle).
Here is a sample of my code.
Illuminate\Support\Facades\Http::attach('file', $fileContents, 'file-name')
->post('https://destination' , [['name' => 'tag', 'content' => 'foo'], ['name' => 'tag', 'content' => 'bar']])
I found this answer online and want to post it here before it disappears:
http://yeehuichan.wordpress.com/2011/08/07/sending-multiple-values-with-the-same-namekey-in-curl-post/
function curl_setopt_custom_postfields($ch, $postfields, $headers = null) {
$algos = hash_algos();
$hashAlgo = null;
foreach ( array('sha1', 'md5') as $preferred ) {
if ( in_array($preferred, $algos) ) {
$hashAlgo = $preferred;
break;
}
}
if ( $hashAlgo === null ) { list($hashAlgo) = $algos; }
$boundary =
'----------------------------' .
substr(hash($hashAlgo, 'cURL-php-multiple-value-same-key-support' . microtime()), 0, 12);
$body = array();
$crlf = "\r\n";
$fields = array();
foreach ( $postfields as $key => $value ) {
if ( is_array($value) ) {
foreach ( $value as $v ) {
$fields[] = array($key, $v);
}
} else {
$fields[] = array($key, $value);
}
}
foreach ( $fields as $field ) {
list($key, $value) = $field;
if ( strpos($value, '#') === 0 ) {
preg_match('/^#(.*?)$/', $value, $matches);
list($dummy, $filename) = $matches;
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name="' . $key . '"; filename="' . basename($filename) . '"';
$body[] = 'Content-Type: application/octet-stream';
$body[] = '';
$body[] = file_get_contents($filename);
} else {
$body[] = '--' . $boundary;
$body[] = 'Content-Disposition: form-data; name="' . $key . '"';
$body[] = '';
$body[] = $value;
}
}
$body[] = '--' . $boundary . '--';
$body[] = '';
$contentType = 'multipart/form-data; boundary=' . $boundary;
$content = join($crlf, $body);
$contentLength = strlen($content);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Length: ' . $contentLength,
'Expect: 100-continue',
'Content-Type: ' . $contentType,
));
curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
}
And to use it:
curl_setopt_custom_postfields($ch, array(
'file' => '#a.csv',
'name' => array('James', 'Peter', 'Richard'),
));