I'm trying to all URLs that contain http://api.example.com/data/ to https://example.com/data/
in an array from a Database while using the Fat-Free Framework, the code iv tried below gives me
Internal Server Error, Array to string conversion
$f3->route('GET /details/#itemid',
function($f3) {
$arr = array('result' => $f3->get('DBh')->exec('SELECT * FROM DB_table WHERE id=?',$f3->get('PARAMS.itemid')));
str_replace('http://api.example.com/data/', 'https://example.com/data/', $arr);
$f3->mset($arr);
echo \Template::instance()->render('views/details.html');
}
);
Well you've got a couple simple options to help with this. I'm also going to assume you mean to reference $arr['result'] and not just $arr and I'd need to assume you're only looking for one column out of the db column. I'll call this column url.
Option 1
foreach($arr['result'] as $key => $value) {
if(empty($value['url'])) {
continue;
}
$arr['result'][$key]['url'] = str_replace('http://api.example.com/data/', 'https://example.com/data/', $value['url']);
}
Option 2
foreach($arr['result'] as &$value) {
if(empty($value['url'])) {
continue;
}
$value['url'] = str_replace('http://api.example.com/data/', 'https://example.com/data/', $value['url']);
}
Option 3
$arr['result'] = array_map(function($value) {
$value['url'] = str_replace('http://api.example.com/data/', 'https://example.com/data/', $value['url']);
return $value;
}, $arr['result']);
My personal preference for readability and simplicity is Option 2. I believe foreach statements have been optimized to actually run faster than array_map which is......interesting.
Related
I'm working with a JSON string. I'm converting it to an associative array to find specific values and change those values when a certain key is found (['content']). The depth of the array is always unknown and will always vary.
Here is the function I wrote. It takes an array as an argument and passes it by reference so that the variable itself is modified rather than a copy of it scoped locally to that function.
$json_array = json_decode($json_string, true);
function replace_data(&$json_array, $data='REPLACE TEST')
{
foreach($json_array as $key => $value) {
if ($key == 'content' && !is_array($value)) {
$json_array[$key] = $data;
} else {
if (is_array($value)) {
replace_data($value, $data);
}
}
}
}
replace_data($json_array, "test test test");
var_dump($json_array);
What I'm expecting to happen is every time a key ['content'] is found at no matter what depth, it replaces with that value specified in the $data argument.
But, when I var_dump($json_array) Those values are unchanged.
What am I missing?
With array_walk_recursive:
function replace_data($json_array, $data = 'REPLACE TEST') {
array_walk_recursive($json_array, function (&$value, $key) use ($data) {
if (!is_array($value) && $key === 'content') {
// $value passed by reference
$value = $data;
}
});
return $json_array;
}
And without references:
function replace_data($json_array, $data = 'REPLACE TEST') {
foreach ($json_array as $key => $value) {
if (is_array($value)) {
$json_array[$key] = replace_data($value, $data);
} elseif ($key === 'content') {
$json_array[$key] = $data;
}
}
return $json_array;
}
To expand on my comment, you need another reference here:
foreach($json_array as $key => &$value) {
That way, a reference to the original value is passed when you make the recursive call, rather than the local copy created with the foreach loop.
From the PHP manual entry for foreach:
In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.
You'll also see in the manual that it recommends unsetting the reference to $value after the loop. Even though it probably won't cause any problems if you don't do that in your function, it's best to be in the habit of always unsetting references created in foreach loops like that. It can cause some strange looking problems if you don't.
From PHP7.4, "arrow function" syntax offers a clean and short approach.
As leafnodes are iterated, if the key is content, then replace the text, otherwise do not change the value.
There are no returns being used. All mutations are applied directly to the passed in variables prefixed with &.
function replace_data(&$array, $replaceWith = 'REPLACE TEST')
{
array_walk_recursive(
$array,
fn(&$v, $k) => $v = ($k === 'content' ? $replaceWith : $v)
);
}
replace_data($json_array, "test test test");
var_export($json_array);
If the parameter name of an URL contains squared brackets (no matter if they are url-encoded or not), any following character is ignored by PHP and is not made available to the script (e.g. via $_GET).
Example request:
...showget.php?xxx%5B1%5Dyyy=42
$_GET:
Array
(
[xxx] => Array
(
[1] => 42
)
)
As you can see, "yyy" didn't made it. ^^
(tested in PHP 5.3.28 & 5.5.10)
Does somebody know if such URLs are even syntactically valid?
Is this behaviour intended and documented (could not find anything) or should it rather be considered as a bug within PHP?
If intended: Can i change the respective behaviour by changing a special setting or so?
Thanks!
This is intended behaviour. As you saw in your example, PHP builds arrays from GET parameters if it can, that is, if it finds square brackets in the variable name. There's a FAQ entry showing how that can sometimes be useful.
In your case, PHP sees xxx[1]yyy=42 as xxx[1]=42 which becomes an array.
As far as I know, PHP's query string parsing can not be changed, but you could use $_SERVER['QUERY_STRING'] and parse that yourself.
[] in query key names is a hint to PHP that you want an array, e.g.
example.com?foo[]=bar&foo[]=baz
produces
$_GET = array(
'foo' => array('bar', 'baz')
);
This notation also lets you specify keys in the url:
example.com?foo[bar]=baz
$_GET = array(
'foo' => array('bar' => 'baz')
);
But once you get into this array notation, you're not permitted to have anything in the keyname AFTER the [] portion:
example.com?foo[bar]baz=qux
$_GET = array(
'foo' => array('bar' => 'qux')
);
Basically it's related to PHP syntax, where somethign like
$foo['bar']baz
would be a syntax error.
Came across this myself earlier too, and wrote a function to handle it from POST data, but it shouldn't take much to get it to use GET data instead. Such a large amount of code simply to account for the fact that PHP doesn't account for nested square brackets ;-)
/**
* Gets the _POST data with correct handling of nested brackets:
* "path[to][data[nested]]=value"
* "path"
* -> "to"
* -> "data[nested]" = value
* #return array
*/
function get_real_post() {
function set_nested_value(&$arr, &$keys, &$value) {
$key = array_shift($keys);
if (count($keys)) {
// Got deeper to go
if (!array_key_exists($key, $arr)) {
// Make sure we can get deeper if we've not hit this key before
$arr[$key] = array();
} elseif (!is_array($arr[$key])) {
// This should never be relevant for well formed input data
throw new Exception("Setting a value and an array with the same key: $key");
}
set_nested_value($arr[$key], $keys, $value);
} elseif (empty($key)) {
// Setting an Array
$arr[] = $value;
} else {
// Setting an Object
$arr[$key] = $value;
}
}
$input = array();
$parts = array();
$pairs = explode("&", file_get_contents("php://input"));
foreach ($pairs as $pair) {
$key_value = explode("=", $pair, 2);
preg_match_all("/([a-zA-Z0-9]*)(?:\[([^\[\]]*(?:(?R)[^\[\]]*)*)\])?/", urldecode($key_value[0]), $parts);
$keys = array($parts[1][0]);
if (!empty($parts[2][0])) {
array_pop($parts[2]); // Remove the blank one on the end
$keys = array_merge($keys, $parts[2]);
}
$value = urldecode($key_value[1]);
if ($value == "true") {
$value = true;
} else if ($value == "false") {
$value = false;
} else if (is_numeric($value)) {
if (strpos($value, ".") !== false) {
$num = floatval($value);
} else {
$num = intval($value);
}
if (strval($num) === $value) {
$value = $num;
}
}
set_nested_value($input, $keys, $value);
}
return $input;
}
I want to generate all possible combination of array elements to fill a placeholder, the placeholder size could vary.
Let say I have array $a = array(3, 2, 9, 7) and placeholder size is 6. I want to generate something like the following:
3,3,3,3,3,3
2,3,3,3,3,3
2,2,3,3,3,3
...........
...........
7,7,7,7,7,9
7,7,7,7,7,7
However (2,3,3,3,3,3) would be considered the same as (3,2,3,3,3,3) so the later one doesn't count.
Could anyone point me to the right direction? I know there is Math_Combinatorics pear package, but that one is only applicable to placeholder size <= count($a).
Edit
I am thinking that this one is similar to bits string combination though with different number base
I have no PHP source code for you but some sources that might help.
Some C code. Look at 2.1:
http://www.aconnect.de/friends/editions/computer/combinatoricode_g.html
Delphi code: combination without repetition of N elements without use for..to..do
Wiki article here
Well it took quit some time to figure this one out.
So i split the question into multiple parts
1.
I firsrt made an array with all the possible value options.
function create_all_array($placeholder, array $values)
{
if ($placeholder <= 0) {
return [];
}
$stack = [];
$values = array_unique($values);
foreach ($values as $value) {
$stack[] = [
'first' => $value,
'childs' => create_all_array($placeholder - 1, $values)
];
}
return $stack;
}
2.
Then I made a function to stransform this massive amount of data into string (no check for uniques).
function string($values, $prefix = '')
{
$stack = [];
foreach($values as $value) {
$sub_prefix = $prefix . $value['first'];
if (empty($value['childs'])) {
$stack[$sub_prefix] = (int)$sub_prefix;
} else {
$stack = array_merge($stack, string($value['childs'], $sub_prefix));
}
}
return $stack;
}
3.
Then the hard part came. Check for duplicates. This was harder than expected, but found some good anser to it and refactored it for my use.
function has_duplicate($string, $items)
{
$explode = str_split ($string);
foreach($items as $item) {
$item_explode = str_split($item);
sort($explode);
$string = implode('',$explode);
sort($item_explode);
$item = implode($item_explode);
if ($string == $item) {
return true;
}
}
return false;
}
4.
The last step was to combine the intel into a new funciton :P
function unique_string($placeholder, array $values)
{
$stack = string(create_all_array($placeholder, $values));
$check_stack = [];
foreach($stack as $key => $item) {
if (has_duplicate($item, $check_stack)) {
unset($stack[$key]);
}
$check_stack[] = $item;
}
return $stack;
}
Now you can use it simple as followed
unique_string(3 /* amount of dept */, [1,2,3] /* keys */);
Ps the code is based for PHP5.4+, to convert to lower you need to change the [] to array() but I love the new syntax so sorry :P
TL;DR
I have this data: var_export and print_r.
And I need to narrow it down to: http://pastebin.com/EqwgpgAP ($data['Stock Information:'][0][0]);
How would one achieve it? (dynamically)
I'm working with vTiger 5.4.0 CRM and am looking to implement a function that would return a particular field information based on search criteria.
Well, vTiger is pretty weakly written system, looks and feels old, everything comes out from hundreds of tables with multiple joins (that's actually not that bad) etc., but job is job.
The need arose from getting usageunit picklist from Products module, Stock Information block.
Since there is no such function as getField();, I am looking forward to filter it out from Blocks, that is actually gathering the information about fields also.
getBlocks(); then calls something close to getFields();, that again something close to getValues(); and so on.
So...
$focus = new $currentModule(); // Products
$displayView = getView($focus->mode);
$productsBlocks = getBlocks($currentModule, $displayView, $focus->mode, $focus->column_fields); // in theory, $focus->column_fields should/could be narrowed down to my specific field, but vTiger doesn't work that way
echo "<pre>"; print_r($productsBlocks); echo "</pre>"; // = http://pastebin.com/3iTDUUgw (huge dump)
As you can see, the array under the key [Stock Information:], that actually comes out from translations (yada, yada...), under [0][0] contains information for usageunit.
Now, I was trying to array_filter(); the data out from there, but only thing I've managed to get is $productsBlocks stripped down to only contain [Stock Information:] with all the data:
$getUsageUnit = function($value) use (&$getUsageUnit) {
if(is_array($value)) return array_filter($value, $getUsageUnit);
if($value == 'usageunit') return true;
};
$productsUsageUnit = array_filter($productsBlocks, $getUsageUnit);
echo "<pre>"; print_r($productsUsageUnit); echo "</pre>"; // = http://pastebin.com/LU6VRC4h (not that huge of a dump)
And, the result I'm looking forward to is http://pastebin.com/EqwgpgAP, that I've manually got by print_r($productsUsageUnit['Stock Information:'][0][0]);.
How do I achieve this? (dynamically...)
function helper($data, $query) {
$result = array();
$search = function ($data, &$stack) use(&$search, $query) {
foreach ($data as $entry) {
if (is_array($entry) && $search($entry, $stack) || $entry === $query) {
$stack[] = $entry;
return true;
}
}
return false;
};
foreach ($data as $sub) {
$parentStack = array();
if ($search($sub, $parentStack)) {
$result[] = $parentStack[sizeof($parentStack) - 2];
}
}
return $result;
}
$node = helper($data, 'usageunit');
print_r($node);
Hi I have a PHP array with a variable number of keys (keys are 0,1,2,3,4.. etc)
I want to process the first value differently, and then the rest of the values the same.
What's the best way to do this?
$first = array_shift($array);
// do something with $first
foreach ($array as $key => $value) {
// do something with $key and $value
}
I would do this:
$firstDone = FALSE;
foreach ($array as $value) {
if (!$firstDone) {
// Process first value here
$firstDone = TRUE;
} else {
// Process other values here
}
}
...but whether that is the best way is debatable. I would use foreach over any other method, because then it does not matter what the keys are.
Here is one way:
$first = true;
foreach($array as $key => $value) {
if ($first) {
// something different
$first = false;
}
else {
// regular logic
}
}
$i = 0;
foreach($ur_array as $key => $val) {
if($i == 0) {
//first index
}
else {
//do something else
}
$i++;
}
I would do it like this if you're sure the array contains at least one entry:
processFirst($myArray[0]);
for ($i=1; $i<count($myArray); $1++)
{
processRest($myArray[$i]);
}
Otherwise you'll need to test this before processing the first element
I've made you a function!
function arrayCallback(&$array) {
$callbacks = func_get_args(); // get all arguments
array_shift($callbacks); // remove first element, we only want the callbacks
$callbackindex = 0;
foreach($array as $value) {
// call callback
$callbacks[$callbackindex]($value);
// make sure it keeps using last callback in case the array is bigger than the amount of callbacks
if(count($callbacks) > $callbackindex + 1) {
$callbackindex++;
}
}
}
If you call this function, it accepts an array and infinite callback arguments. When the array is bigger than the amount of supplied functions, it stays at the last function.
You can simply call it like this:
arrayCallback($array, function($value) {
print 'callback one: ' . $value;
}, function($value) {
print 'callback two: ' . $value;
});
EDIT
If you wish to avoid using a function like this, feel free to pick any of the other correct answers. It's just what you prefer really. If you're repeatedly are planning to loop through one or multiple arrays with different callbacks I suggest to use a function to re-use code. (I'm an optimisation freak)