is there reverse function for \http_parse_headers()? - php

PHP function \http_parse_headers() parses HTTP headers into an associative array.
But is there some reverse function? Which parses associative array into HTTP headers?
Cannot find anything :(
(it's for saveing email into textual .eml file)

There isn't a function that turns associative array into text-representation of headers. The reason: this function is extremely trivial to create.
Headers are defined as key: value delimiter is \r\n.
There is another \r\n delimiter between headers and body.
Lets take an example array:
$headers = [
'Content-Length': 50,
'Content-Encoding': 'gzip'
];
The goal: provide a string that represents HTTP headers
function parse_array_to_headers(array $headers)
{
$result = [];
$delimiter = "\r\n";
foreach($headers as $name => $value)
{
$result[] = sprintf("%s: %s", $name, $value);
}
return implode($delimiter, $result);
}
Note: this function will not check the validity of array and it won't return the string with two repetitions of \r\n at the end. This example serves to show how easy it should be to add missing function. Adjust according to your needs. Also, I didn't test this so don't copy paste it! :)

Related

Replacing characters in a returned Header Location in PHP

I'm trying to filter returned header location after POST using below PHP code. The returned header location is required for further processing of the payment status and saving the status in the db. The API provider seems not supportive since they don't reply on time or fail to reply at all.
$output = curl_exec($curl);
$lines = explode("\n",$output);
$out = array();
$headers = true;
foreach ($lines as $l){
$l = trim($l);
if ($headers && !empty($l)){
if (strpos($l,'location') !== false){
$p = explode(' ',$l);
$out['Headers']['location'] = trim($p[1]);
$url = json_encode($out['Headers']['location']);
echo json_encode($out['Headers']['location']);
}
}
}
The echo output is as below:- "https:\/\/sandbox.kopokopo.com\/api\/v1\/payments\/c122c1d2-8e07-48d3-8c9d-597829447fda"
How do I make the output to be a valid url without "\" ? I'll really appreciate your valuable assistance.
Your json_encode call is causing the problem, by escaping the / values.
Demo: https://3v4l.org/S3VWD
There's no need to encode a single string like this. JSON is mainly useful when you have a more complex set of information (e.g. multiple separate data items) that you want to output in a structured way.
echo $out['Headers']['location'];
is all you need in this case.

How to expand variables that are in a text file when reading the file into a string?

In the context of processing html form input and responding by sending email via php, rather than having a lengthy heredoc assignment statement in the middle of my code, I'd like to have the email text in a file and read it in when needed, expanding the embedded variables as required.
Using appropriately prepared HTML form data input, I previously had…
$message = <<<TEXT
NAME: {$_POST["fname"]} {$_POST["lname"]}
POSTAL ADDRESS: {$_POST["postal"]}
EMAIL ADDRESS: {$_POST["email"]}
[... message body etc.]
TEXT;
Moving the text to a file I then tried…
$message = file_get_contents('filename.txt');
…but found that variables in the text are not expanded, resulting in output including the variable identifiers as literals.
Is there a way to expand the variables when reading the file into a string ?
There's probably a duplicate, if found I'll delete. But there are two ways that come to mind:
If you construct your file like this:
NAME: {fname} {lname}
Then loop the $_POST variables:
$message = file_get_contents('filename.txt');
foreach($_POST as $key => $val) {
$message = str_replace('{'.$key.'}', $val, $message);
}
If you construct your file like this:
NAME: <?= $_POST["fname"] ?> <?= $_POST["lname"] ?>
If HTML .txt or other (not .php) then:
ob_start();
$file = file_get_contents('filename.txt');
eval($file);
$message = ob_get_clean();
If it's a .php file then buffer the output and include:
ob_start();
include('filename.php');
$message = ob_get_clean();
Thanks to the help from AbraCadaver I put everything into a function which will handle the file and array, returning the composed message. I constructed the file to use [key] marking the places where data is to be inserted. Works like a charm. Thank you.
function compose_message($file, $array) {
// reads content of $file
// interpolates values from $array into text of file
// returns composed message
$message = file_get_contents($file);
$keys = array_keys($array);
foreach ($keys as $key) {
$message = str_replace("[$key]", $array[$key], $message);
}
return $message;
}

How to convince Zend Framework to send duplicate headers?

With Content-Security-Policy headers there is often a need to send more than one such header or to union merge these headers before sending them. This arises from the fact that each module/package of an application may define its own CSP.
Right now ZF3 doesn't seem to have a way to handle such a scenario. If I try to add multple CSP headers, they keep overwriting each other so that only the last added header is sent.
Code to reproduce the issue
$headers = $controller->getResponse()->getHeaders();
$headers->addHeader(new ContentSecurityPolicy($someDirectives));
$headers->addHeader(new ContentSecurityPolicy($someOtherDirectives));
Expected results
The expected result is a response with two CSP headers (OR a union merged CSP).
Actual results
The second addition overwrites the first, the response only contains that one CSP.
Question
How can I make ZF3 send multple headers with the same fieldname?
For more information about this problem, also see my own issue on github https://github.com/zendframework/zend-http/issues/159
You should be able to create a simple workaround using GenericMultipleHeader as a reference (and changing comma delimiter to semicolon):
class MultiContentSecurityPolicy extends ContentSecurityPolicy implements MultipleHeaderInterface {
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
if (strpos($fieldValue, ';')) {
$headers = [];
foreach (explode(';', $fieldValue) as $multiValue) {
$headers[] = new static($fieldName, $multiValue);
}
return $headers;
} else {
$header = new static($fieldName, $fieldValue);
return $header;
}
}
public function toStringMultipleHeaders(array $headers)
{
$name = $this->getFieldName();
$values = [$this->getFieldValue()];
foreach ($headers as $header) {
if (! $header instanceof static) {
throw new Exception\InvalidArgumentException(
'This method toStringMultipleHeaders was expecting an array of headers of the same type'
);
}
$values[] = $header->getFieldValue();
}
return $name . ': ' . implode(';', $values) . "\r\n";
}
}
Then use that class instead of ContentSecurityPolicy:
$headers = $controller->getResponse()->getHeaders();
$headers->addHeader(new MultiContentSecurityPolicy($someDirectives));
$headers->addHeader(new MultiContentSecurityPolicy($someOtherDirectives));
Since Zend checks the interface rather than the class, should work fine.
This is the accepted HTTP standard and the PHP Core upholds this. http://php.net/manual/en/function.header.php
If you set headers in PHP header("TESTHeader: Test1"); header("TESTHeader: Test2") only one will come through and this is correct to specification RFC2616 Section 4.2 Page 31&32
If you wish to send multiple values your header should construct as header("TESTHeader: Test1, Test2");. while it is possible to send multiple same name headers through PHP it is not recommended as browsers & servers receiving 2 sets of the same header should convert them to the above style this could cause problems as you will not know for certain what format they are in. header("TESTHeader: Test1", false); header("TESTHeader: Test2", false). depending on the server or clients adherence to the RFC or HTTP Version.
So this answer is the reason as to why you are not allowed to send the same header multiple times in ZF3, it can't identify when to use the overwrite or not to based on you setting the header. to get around this and use multi-valued headers you can use Jim's answer
make your own multipleheader class, add the function you need (MultipleHeaderInterface) then add your header in a multistering and finally call it in your
$headers = $controller->getResponse()->getHeaders();
(call the new function with the new fromStringHeaders)

Format CSV into json to be consumed by ui.autocomplete

I have php problem with formatting output from a CSV to make it available as json for jquery ui.autocomplete.
fruit.csv:
apple, bananna, jackfruit,
... etc
jquery from here:
http://jqueryui.com/demos/autocomplete/#default
$( "#fruits" ).autocomplete({
source: '/path/to/fruit.json'
});
PHP to convert CSV into json:
// Callback function to output CSV as json object
function _custom_json_from_csv() {
$fruit_path = '/path/to/fruit.csv';
$fruits = array_map("str_getcsv", file($fruit_path));
drupal_json_output(array(array_values($fruits)));
exit;
}
// Below are CMS codes for detailed illustration
function drupal_json_output($var = NULL) {
// We are returning JSON, so tell the browser.
drupal_add_http_header('Content-Type', 'application/json');
if (isset($var)) {
echo drupal_json_encode($var);
}
}
function drupal_json_encode($var) {
// The PHP version cannot change within a request.
static $php530;
if (!isset($php530)) {
$php530 = version_compare(PHP_VERSION, '5.3.0', '>=');
}
if ($php530) {
// Encode <, >, ', &, and " using the json_encode() options parameter.
return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
}
// json_encode() escapes <, >, ', &, and " using its options parameter, but
// does not support this parameter prior to PHP 5.3.0. Use a helper instead.
include_once DRUPAL_ROOT . '/includes/json-encode.inc';
return drupal_json_encode_helper($var);
}
// parts of json-encode.inc - drupal_json_encode_helper(), responsible for json output:
case 'array':
// Arrays in JSON can't be associative. If the array is empty or if it
// has sequential whole number keys starting with 0, it's not associative
// so we can go ahead and convert it as an array.
if (empty($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
$output = array();
foreach ($var as $v) {
$output[] = drupal_json_encode_helper($v);
}
return '[ ' . implode(', ', $output) . ' ]';
}
// Otherwise, fall through to convert the array as an object.
case 'object':
$output = array();
foreach ($var as $k => $v) {
$output[] = drupal_json_encode_helper(strval($k)) . ':' . drupal_json_encode_helper($v);
}
return '{' . implode(', ', $output) . '}';
If there is a solution to directly consume CSV by jquery, that will be great. But no clue by now.
My problem is function _custom_json_from_csv() outputs non-expected format for ui.autocomplete. Note excessive of [[[...]]]:
[[["apple", "bananna", "jackfruit"]]]
While ui.autocomplete wants:
["apple", "bananna", "jackfruit"]
Any direction to format the function as expected by jquery ui.autocomplete?
PS: I don't use #autocomplete_path form API, and instead using ui.autocomplete, for reasons:
1) the code is stored in a theme settings, no hook_menu is available by theme, I want to avoid a module for this need whenever possible.
2) There is a plan somewhere at d.o. to use ui.autocomplete, so consider this adventurous
3) My previous question from jquery viewpoint has led me to instead correct the output of json, rather than making jquery adapt to json.
4) This is more my php issue rather than drupal
Thanks
UPDATE:
Removing one array from drupal_json_output(array(array_values($fruits))); to drupal_json_output(array_values($fruits)); successfully reduced one [] (what is the name of this?). Obviously a miss from previous format with the leading group.
[["apple", "bananna", "jackfruit"]]
I need to remove one more []
I think your code is "correct", if you had:
apple, bananna, jackfruit
peanut, walnut, almont
carrot, potato, pea
then your function would end up as ... etc
[[apple, bananna, jackfruit],[peanut, walnut, almont],[carrot, potato, pea]]
Which appears sensible.
If you only want one row, why can't you just use the result of
$FileContents = file($fruit_path);
$fruits = str_getcsv($FileContents[0]);
as that will turn an array of values in the first row, not an array of arrays of all the rows
Maybe simple is using in js array[0][0]( for drupal_json_output(array(array_values($fruits)));
in php) or array[0] in js( for drupal_json_output(array_values($fruits));) in php

php array in_array oddity

of all the languages i know im the weakest in php...
I have a script... that takes a csv file and does some stuff with it... fairly simple.
the issue i am having:
in_array('username', $headers) ... returns null...
while...
print_r ($headers); shows username being the first entry in the csv.
thoughts? mistakes i may have made?
TIA
code here
/// Turn the string into an array
$rows = explode("\n", $this->raw_data);
/// First row includes headers
$headers = $rows[0];
$headers = explode($this->delimiter, $headers);
/// Trim spaces from $headers
$headers = array_map('trim', $headers);
/// Check that there are no empty headers. This can happen if there are delimiters at the end of the file
foreach($headers as $header){
if(!empty($header)){
$headers2[] = $header;
}
}
$headers = $headers2;
if(! in_array('password', $headers)){
/// Add password column for generated passwords
$headers[] = 'password';
}
/// Add status column to the headers
$headers[] = 'status';
$this->headers = $headers;
/// Check that at least username, name and email are provided in the headers
if(!in_array('username', $headers) ||
!in_array('name', $headers) ||
!in_array('email', $headers)){
echo "error\n";
return false;
}
You can use the built in str_getcsv() function. Try replacing the $headers variable assignment with
$headers = str_getcsv($rows[0], $this->delimiter);
Then find the value(column) you want and loop through the rest of the $rows using the same str_getcsv() function to get the matches you need.
You may want to use the file() function to grab the file in an array delimited by newlines to begin with, as well.
Check the first three functions in this list . Your problem can arise from several causes. Start by elimination of unnecessary parsing by using the built in CSV function.
I don't see code that sets $headers2 to be an array. Is the first assignment to that variable getting lost once the second assignment happens which turns it into an array?

Categories