Hi can somebody help me with building an extended addslashes function, which will work with mixed combination of objects and arrays. For example i have this Object:
$object = new stdClass;
$object2 = new stdClass;
$object2->one = "st'r2";
$object3 = new stdClass;
$object3->one = "st'r3";
$object->one = "s'tr";
$object->two = array($object2);
$object->obj = $object3;
And i would like to get this object back escaped and with the same structure.
I have started some experiments and i get something like this:
function addslashes_extended($arr_r){
if(is_array($arr_r)){
foreach ($arr_r as $key => $val){
is_array($val) ? addslashes_extended($val):$arr_r[$key]=addslashes($val);
}
unset($val);
}else if(is_object($arr_r)){
$objectProperties = get_object_vars($arr_r);
foreach($objectProperties as $key => $value){
is_object($value) ? addslashes_extended($value):$arr_r->{$key}=addslashes($value);
}
}
return $arr_r;
}
But this is not going to work, i have to work with passing by reference i think, but i have no clue how, other solutions would be nice to have too, thanks in advance!
Try this (using array_walk):
error_reporting(E_ALL ^ E_STRICT);
ini_set('display_errors', 'on');
$data = array(
"fo'o",
'bar' => "foo'bar",
'foobar' => array(
1, 2, 'someObj' => json_decode('{"prop1": "a", "prop2": "b\'c"}')
)
);
class Util
{
public static function addslashes_extended(&$mixed) {
if (is_array($mixed) || is_object($mixed)) {
array_walk($mixed, 'Util::addslashes_extended');
}
elseif (is_string($mixed)) {
$mixed = addslashes($mixed);
}
}
}
Util::addslashes_extended($data);
print_r($data);
Output ( http://codepad.org/nUUYKWrn ):
Array
(
[0] => fo\'o
[bar] => foo\'bar
[foobar] => Array
(
[0] => 1
[1] => 2
[someObj] => stdClass Object
(
[prop1] => a
[prop2] => b\'c
)
)
)
To pass by reference, you use & before the variable name, quick example:
function inc(&$var) {
$var++;
}
$x = 5;
inc($x);
echo $x; //Prints: 6
Related
I'm using PHP-PArser to find the AST of PHP program. For example:
code
<?php
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;
$code = <<<'CODE'
<?php
$variable = $_POST['first'];
$new = $nonexist;
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";
The AST result of the above example as following:
array( 0: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: variable ) expr: Expr_ArrayDimFetch( var: Expr_Variable( name: _POST_first_symbol ) dim: Scalar_String( value: first ) ) ) ) 1: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: new ) expr: Expr_Variable( name: nonexist ) ) ) )
What I'm trying to find is the variable = _POST AND new = nonexist
I used leavenode function to reach _POST and variable. my code to find _POSTand variable as following:
public function leaveNode(Node $node)
{
$collect_to_print= array();
if ($node instanceof ArrayDimFetch
&& $node->var instanceof Variable
&& $node->var->name === '_POST')
{
$variableName = (string) $node->var->name;
$collect_to_print[$node->dim->value] = $node->var->name; // will store the variables in array in a way to print them all later such as variable => _POST , how to get the name `variable` in this case
return $node;
}
else
if ($node instanceof Variable
&& !($node->var->name === '_POST' ))
{
$collect_to_print[$node->name] = 'Empty' ;
}
}
My results until now show every variable in separate line as following:
variable =>
first => _POST // This _POST should be the value of variable (above)
new => Empty
nonexist => Empty
However, I expect the result to be:
variable => _POST
new => Empty
nonexist => Empty
any help please
This is a lot more complicated than other questions you've asked, but it has been interesting to learn about how to write it.
I've put comments through the code, but basically it analyses the code and looks for assignments (instances of PhpParser\Node\Expr\Assign nodes). It then splits it into left and right parts and recursively extracts any variables in either parts.
The code allows for nested variables on either side of the expression, I've changed the example code to provide some broader examples.
Comments in code (assumes some knowledge of how the parser works with nodes etc.)...
$traverser = new NodeTraverser;
class ExtractVars extends NodeVisitorAbstract {
private $prettyPrinter = null;
private $variables = [];
private $expressions = [];
public function __construct() {
$this->prettyPrinter = new PhpParser\PrettyPrinter\Standard;
}
public function leaveNode(Node $node) {
if ( $node instanceof PhpParser\Node\Expr\Assign ) {
$assignVars = $this->extractVarRefs ( $node->var );
// Get string of what assigned to actually is
$assign = $this->prettyPrinter->prettyPrintExpr($node->var);
// Store the variables associated with the left hand side
$this->expressions[$assign]["to"] = $assignVars;
// Store variables from right
$this->expressions[$assign][] = $this->extractVarRefs ( $node->expr );
}
}
private function extractVarRefs ( Node $node ) : array {
$variableList = [];
// If it's a variable, store the name
if ( $node instanceof PhpParser\Node\Expr\Variable ) {
$variable = $this->prettyPrinter->prettyPrintExpr($node);
$this->variables[] = $variable;
$variableList[] = $variable;
}
// Look for any further variables in the node
foreach ( $node->getSubNodeNames() as $newNodeName ) {
$newNode = $node->$newNodeName;
if ( $newNode instanceof Node && $newNode->getSubNodeNames()) {
// Recursive call to extract variables
$toAdd = $this->extractVarRefs ( $newNode );
// Add new list to current list
$variableList = array_merge($variableList, $toAdd);
}
}
return $variableList;
}
public function getVariables() : array {
return array_unique($this->variables);
}
public function getExpressions() : array {
return $this->expressions;
}
}
$varExtract = new ExtractVars();
$traverser->addVisitor ($varExtract);
$traverser->traverse($ast);
print_r($varExtract->getVariables());
print_r($varExtract->getExpressions());
Which gives the list of variables as...
Array
(
[0] => $_POST
[1] => $b
[3] => $new
[4] => $nonexist
)
And the list of expressions as
Array
(
[$_POST[$b]] => Array
(
[to] => Array
(
[0] => $_POST
[1] => $b
)
[0] => Array
(
[0] => $_POST
)
)
[$new] => Array
(
[to] => Array
(
[0] => $new
)
[0] => Array
(
[0] => $nonexist
)
[1] => Array
(
[0] => $_POST
[1] => $b
)
)
)
note that the [to] element of the array contains any variables involved on the left of the =.
This question already has answers here:
PHP rename array keys in multidimensional array
(10 answers)
Closed last month.
When I var_dump on a variable called $tags (a multidimensional array) I get this:
Array
(
[0] => Array
(
[name] => tabbing
[url] => tabbing
)
[1] => Array
(
[name] => tabby ridiman
[url] => tabby-ridiman
)
[2] => Array
(
[name] => tables
[url] => tables
)
[3] => Array
(
[name] => tabloids
[url] => tabloids
)
[4] => Array
(
[name] => taco bell
[url] => taco-bell
)
[5] => Array
(
[name] => tacos
[url] => tacos
)
)
I would like to rename all array keys called "url" to be called "value". What would be a good way to do this?
You could use array_map() to do it.
$tags = array_map(function($tag) {
return array(
'name' => $tag['name'],
'value' => $tag['url']
);
}, $tags);
Loop through, set new key, unset old key.
foreach($tags as &$val){
$val['value'] = $val['url'];
unset($val['url']);
}
Talking about functional PHP, I have this more generic answer:
array_map(function($arr){
$ret = $arr;
$ret['value'] = $ret['url'];
unset($ret['url']);
return $ret;
}, $tag);
}
Recursive php rename keys function:
function replaceKeys($oldKey, $newKey, array $input){
$return = array();
foreach ($input as $key => $value) {
if ($key===$oldKey)
$key = $newKey;
if (is_array($value))
$value = replaceKeys( $oldKey, $newKey, $value);
$return[$key] = $value;
}
return $return;
}
foreach ($basearr as &$row)
{
$row['value'] = $row['url'];
unset( $row['url'] );
}
unset($row);
This should work in most versions of PHP 4+. Array map using anonymous functions is not supported below 5.3.
Also the foreach examples will throw a warning when using strict PHP error handling.
Here is a small multi-dimensional key renaming function. It can also be used to process arrays to have the correct keys for integrity throughout your app. It will not throw any errors when a key does not exist.
function multi_rename_key(&$array, $old_keys, $new_keys)
{
if(!is_array($array)){
($array=="") ? $array=array() : false;
return $array;
}
foreach($array as &$arr){
if (is_array($old_keys))
{
foreach($new_keys as $k => $new_key)
{
(isset($old_keys[$k])) ? true : $old_keys[$k]=NULL;
$arr[$new_key] = (isset($arr[$old_keys[$k]]) ? $arr[$old_keys[$k]] : null);
unset($arr[$old_keys[$k]]);
}
}else{
$arr[$new_keys] = (isset($arr[$old_keys]) ? $arr[$old_keys] : null);
unset($arr[$old_keys]);
}
}
return $array;
}
Usage is simple. You can either change a single key like in your example:
multi_rename_key($tags, "url", "value");
or a more complex multikey
multi_rename_key($tags, array("url","name"), array("value","title"));
It uses similar syntax as preg_replace() where the amount of $old_keys and $new_keys should be the same. However when they are not a blank key is added. This means you can use it to add a sort if schema to your array.
Use this all the time, hope it helps!
Very simple approach to replace keys in a multidimensional array, and maybe even a bit dangerous, but should work fine if you have some kind of control over the source array:
$array = [ 'oldkey' => [ 'oldkey' => 'wow'] ];
$new_array = json_decode(str_replace('"oldkey":', '"newkey":', json_encode($array)));
print_r($new_array); // [ 'newkey' => [ 'newkey' => 'wow'] ]
This doesn't have to be difficult in the least. You can simply assign the arrays around regardless of how deep they are in a multi-dimensional array:
$array['key_old'] = $array['key_new'];
unset($array['key_old']);
You can do it without any loop
Like below
$tags = str_replace("url", "value", json_encode($tags));
$tags = json_decode($tags, true);
class DataHelper{
private static function __renameArrayKeysRecursive($map = [], &$array = [], $level = 0, &$storage = []) {
foreach ($map as $old => $new) {
$old = preg_replace('/([\.]{1}+)$/', '', trim($old));
if ($new) {
if (!is_array($new)) {
$array[$new] = $array[$old];
$storage[$level][$old] = $new;
unset($array[$old]);
} else {
if (isset($array[$old])) {
static::__renameArrayKeysRecursive($new, $array[$old], $level + 1, $storage);
} else if (isset($array[$storage[$level][$old]])) {
static::__renameArrayKeysRecursive($new, $array[$storage[$level][$old]], $level + 1, $storage);
}
}
}
}
}
/**
* Renames array keys. (add "." at the end of key in mapping array if you want rename multidimentional array key).
* #param type $map
* #param type $array
*/
public static function renameArrayKeys($map = [], &$array = [])
{
$storage = [];
static::__renameArrayKeysRecursive($map, $array, 0, $storage);
unset($storage);
}
}
Use:
DataHelper::renameArrayKeys([
'a' => 'b',
'abc.' => [
'abcd' => 'dcba'
]
], $yourArray);
It is from duplicated question
$json = '[
{"product_id":"63","product_batch":"BAtch1","product_quantity":"50","product_price":"200","discount":"0","net_price":"20000"},
{"product_id":"67","product_batch":"Batch2","product_quantity":"50","product_price":"200","discount":"0","net_price":"20000"}
]';
$array = json_decode($json, true);
$out = array_map(function ($product) {
return array_merge([
'price' => $product['product_price'],
'quantity' => $product['product_quantity'],
], array_flip(array_filter(array_flip($product), function ($value) {
return $value != 'product_price' && $value != 'product_quantity';
})));
}, $array);
var_dump($out);
https://repl.it/#Piterden/Replace-keys-in-array
This is how I rename keys, especially with data that has been uploaded in a spreadsheet:
function changeKeys($array, $new_keys) {
$newArray = [];
foreach($array as $row) {
$oldKeys = array_keys($row);
$indexedRow = [];
foreach($new_keys as $index => $newKey)
$indexedRow[$newKey] = isset($oldKeys[$index]) ? $row[$oldKeys[$index]] : '';
$newArray[] = $indexedRow;
}
return $newArray;
}
Based on the great solution provided by Alex, I created a little more flexible solution based on a scenario I was dealing with. So now you can use the same function for multiple arrays with different numbers of nested key pairs, you just need to pass in an array of key names to use as replacements.
$data_arr = [
0 => ['46894', 'SS'],
1 => ['46855', 'AZ'],
];
function renameKeys(&$data_arr, $columnNames) {
// change key names to be easier to work with.
$data_arr = array_map(function($tag) use( $columnNames) {
$tempArray = [];
$foreachindex = 0;
foreach ($tag as $key => $item) {
$tempArray[$columnNames[$foreachindex]] = $item;
$foreachindex++;
}
return $tempArray;
}, $data_arr);
}
renameKeys($data_arr, ["STRATEGY_ID","DATA_SOURCE"]);
this work perfectly for me
$some_options = array();;
if( !empty( $some_options ) ) {
foreach( $some_options as $theme_options_key => $theme_options_value ) {
if (strpos( $theme_options_key,'abc') !== false) { //first we check if the value contain
$theme_options_new_key = str_replace( 'abc', 'xyz', $theme_options_key ); //if yes, we simply replace
unset( $some_options[$theme_options_key] );
$some_options[$theme_options_new_key] = $theme_options_value;
}
}
}
return $some_options;
Here is print_r output of my array:
Array
(
[0] => stdClass Object
(
[itemId] => 560639000019
[name] => Item no1
[code] => 00001
[qty] => 5
[id] => 2
)
[1] => stdClass Object
(
[itemId] => 470639763471
[name] => Second item
[code] => 76347
[qty] => 9
[id] => 4
)
[2] => stdClass Object
(
[itemId] => 56939399632
[name] => Item no 3
[code] => 39963
[qty] => 6
[id] => 7
)
)
How can I find index of object with [id] => 4 in order to remove it from array?
$found = false;
foreach($values as $key => $value) {
if ($value->id == 4) {
$found = true;
break;
}
}
if ($found) unset($values[$key]);
This is considered to be faster then any other solution since we only iterate the array to until we find the object we want to remove.
Note: You should not remove an element of an array while iterating so we do it afterwards here.
foreach($parentObj AS $key=>$element){
if ($element->id == THE_ID_YOU_ARE_LOOKING_FOR){
echo "Gottcha! The index is - ". $key;
}
}
$parentObj is obviously your root array - the one that holds all the others.
We use the foreach loop to iterate over each item and then test it's id property against what ever value you desire. Once we have that - the $key that we are on is the index you are looking for.
use array_search:
$a = new stdClass;
$b = new stdClass;
$a->id = 1;
$b->id = 2;
$arr = array($a, $b);
$index = array_search($b, $arr);
echo $index;
// prints out 1
try this
foreach($array AS $key=>$object){
if($object['id'] == 4){
$key_in_array = $key;
}
}
// chop it from the original array
array_slice($array, $key_in_array, 1);
Another way to achieve the result is to use array_filter.
$array = array(
(object)array('id' => 5),
(object)array('id' => 4),
(object)array('id' => 3)
);
$array = array_filter($array, function($item) {
return $item->id != 4;
});
print_r($array);
Here's my solution. Given, it is a bit hackish, but it will get the job done.
search(array $items, mixed $id[, &$key]);
Returns the item that was found by $id. If you add the variable $key it will give you the key of the item found.
function search($items, $id, &$key = null) {
foreach( $items as $item ) {
if( $item->id == $id ) {
$key = key($item);
return $item;
break;
}
}
return null;
}
Usage
$item = search($items, 4, $key);
unset($items[$key]);
Note: This could be modified to allow a custom key and return multiple items that share the same value.
I've created an example so you can see it in action.
A funny alternative
$getIdUnset = function($id) use ($myArray)
{
foreach($myArray as $key => $obj) {
if ($obj->id == $id) {
return $key;
}
}
return false;
};
if ($unset = $getIdUnset(4)) {
unset($myArray[$unset]);
}
Currently php does not have any supported function for this yet.
So refer to Java's Vector, or jQuery's $.inArray(), it would simply be:
public function indexOf($object, array $elementData) {
$elementCount = count($elementData);
for ($i = 0 ; $i < $elementCount ; $i++){
if ($object == $elementData[$i]) {
return $i;
}
}
return -1;
}
You can save this function as a core function for later.
In my case, this my array as $array
I was confused about this problem of my project, but some answer here helped me.
array(3) {
[0]=> float(-0.12459619130796)
[1]=> float(-0.64018439966448)
[2]=> float(0)
}
Then use if condition to stop looping
foreach($array as $key => $val){
if($key == 0){ //the key is 0
echo $key; //find the key
echo $val; //get the value
}
}
I know, after so many years this could be a useless answer, but why not?
This is my personal implementation of a possible index_of using the same code as other answers but let the programmer to choose when and how the check will be done, supporting also complex checks.
if (!function_exists('index_of'))
{
/**
* #param iterable $haystack
* #param callable $callback
* #param mixed|null &$item
* #return false|int|string
*/
function index_of($haystack, $callback, &$item = null)
{
foreach($haystack as $_key => $_item) {
if ($callback($_item, $_key) === true) {
$item = $_item;
return $_key;
}
}
return false;
}
}
foreach( $arr as $k=>&$a) {
if( $a['id'] == 4 )
unset($arr[$k]);
}
** EDIT ** What would happen if I only used arrays, e.g.
array(
array('name' => 'bla', 'distance' => '123');
array('name' => 'b123a', 'distance' => '1234214');
);
Would this be easier to find the min value ?
Hi there I'm trying to retrieve the object which has the lowest distance value from an array of objects. This is my data set below;
[0] => myObjectThing Object
(
[name:myObjectThing:private] => asadasd
[distance:myObjectThinge:private] => 0.9826368952306
)
[1] => myObjectThing Object
(
[name:myObjectThing:private] => 214gerwert24
[distance:myObjectThinge:private] => 1.5212312547306
)
[2] => myObjectThing Object
(
[name:myObjectThing:private] => abc123
[distance:myObjectThinge:private] => 0.0000368952306
)
So I'd like to be able to retieve the object which has the smallest distance value. In this case it would be object with name: abc123
Hmm for PHP >= 5.3 I would try something like this:
$Object = array_reduce($data,function($A,$B){
return $A->distance < $B->distance ? $A : $B;
})
For PHP < 5.3 the fillowing would suffice:
function user_defined_reduce($A,$B){
return $A->distance < $B->distance ? $A : $B;
}
$Object = array_reduce($data,"user_defined_reduce");
Previously suggested answers did not explain the need to include an $initial value to array_reduce(). The solution did not work because of it.
This one works for me (PHP 5.3.13):
$array = array(
array(
'name' => 'something',
'value' => 56
),
array(
'name' => 'else',
'value' => 54
),
array(
'name' => 'else',
'value' => 58
),
array(
'name' => 'else',
'value' => 78
)
);
$object = array_reduce($array, function($a, $b){
return $a['value'] < $b['value'] ? $a : $b;
}, array_shift($array));
print_r($object);
This will give me:
[0] => Array
(
[name] => else
[value] => 54
)
Whereas the previous solution gave me null. I'm assuming that PHP < 5.3 would require a similar initial value to be specified to array_reduce().
You can't flatten this as it isn't a plain old multi-dimensional array with just the values.
This should work:
$min = $object[0];
for ($i = 1; $i < count($object); $i++)
if ($object[$i]['distance'] < $min['distance'])
$min = $object[$i];
This example should give you a hint in the right direction:
<?php
$a = new stdClass();
$a->foo = 2;
$b = new stdClass();
$b->foo = 3;
$c = new stdClass();
$c->foo = 1;
$init = new stdClass();
$init->foo = 1000;
$vals = array( $a, $b, $c );
var_dump(
array_reduce(
$vals,
function ( $x, $y )
{
if ( $x->foo < $y->foo )
{
return $x;
}
else
{
return $y;
}
},
$init
)
);
?>
try:
$myObjectCollection = ...;
$minObject = $myObjectCollection[0]; // or reset($myObjectCollection) if you can't ensure numeric 0 index
array_walk($myObjectCollection, function($object) use ($minObject) {
$minObject = $object->distance < $minObject->distance ? $object : $minObject;
});
but from the looks of your dump. name and distance are private. So you want be able to access them from the object directly using ->. You'll need some kind of getter getDistance()
$objectsArray = array(...); // your objects
$distance = null;
$matchedObj = null;
foreach ( $objectsArray as $obj ) {
if ( is_null($distance) || ( $obj->distance < $distance ) ) {
$distance = $obj->distance;
$matchedObj = $obj;
}
}
var_dump($matchedObj);
AD EDIT:
If you will use arrays instead of objects, change $obj->distance to $obj['distance'].
Suppose I have an array in PHP that looks like this
array
(
array(0)
(
array(0)
(
.
.
.
)
.
.
array(10)
(
..
)
)
.
.
.
array(n)
(
array(0)
(
)
)
)
And I need all the leaf elements of this mulit-dimensional array into a linear array, how should I go about doing this without resorting recursion, such like this?
function getChild($element)
{
foreach($element as $e)
{
if (is_array($e)
{
getChild($e);
}
}
}
Note: code snippet above, horribly incompleted
Update: example of array
Array
(
[0] => Array
(
[0] => Array
(
[0] => Seller Object
(
[credits:private] => 5000000
[balance:private] => 4998970
[queueid:private] => 0
[sellerid:private] => 2
[dateTime:private] => 2009-07-25 17:53:10
)
)
)
...snipped.
[2] => Array
(
[0] => Array
(
[0] => Seller Object
(
[credits:private] => 10000000
[balance:private] => 9997940
[queueid:private] => 135
[sellerid:private] => 234
[dateTime:private] => 2009-07-14 23:36:00
)
)
....snipped....
)
)
Actually, there is a single function that will do the trick, check the manual page at: http://php.net/manual/en/function.array-walk-recursive.php
Quick snippet adapted from the page:
$data = array('test' => array('deeper' => array('last' => 'foo'), 'bar'), 'baz');
var_dump($data);
function printValue($value, $key, $userData)
{
//echo "$value\n";
$userData[] = $value;
}
$result = new ArrayObject();
array_walk_recursive($data, 'printValue', $result);
var_dump($result);
You could use iterators, for example:
$result = array();
foreach(new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY) as $value) {
$result[] = $value;
}
Use a stack:
<?php
$data = array(array(array("foo"),"bar"),"baz");
$results = array();
$process = $data;
while (count($process) > 0) {
$current = array_pop($process);
if (is_array($current)) {
// Using a loop for clarity. You could use array_merge() here.
foreach ($current as $item) {
// As an optimization you could add "flat" items directly to the results array here.
array_push($process, $item);
}
} else {
array_push($results, $current);
}
}
print_r($results);
Output:
Array
(
[0] => baz
[1] => bar
[2] => foo
)
This should be more memory efficient than the recursive approach. Despite the fact that we do a lot of array manipulation here, PHP has copy-on-write semantics so the actual zvals of the real data won't be duplicated in memory.
Try this:
function getLeafs($element) {
$leafs = array();
foreach ($element as $e) {
if (is_array($e)) {
$leafs = array_merge($leafs, getLeafs($e));
} else {
$leafs[] = $e;
}
}
return $leafs;
}
Edit Apparently you don’t want a recursive solution. So here’s an iterative solution that uses a stack:
function getLeafs($element) {
$stack = array($element);
$leafs = array();
while ($item = array_pop($stack)) {
while ($e = array_shift($item)) {
if (is_array($e)) {
array_push($stack, array($item));
array_push($stack, $e);
break;
} else {
$leafs[] = $e;
}
}
}
return $leafs;
}
Just got the same issue and used another method that was not mentioned. The accepted answer require the ArrayObject class to work properly. It can be done with the array primitive and the use keyword in the anonymous function (PHP >= 5.3):
<?php
$data = array(
array(1,2,3,4,5),
array(6,7,8,9,0),
);
$result = array();
array_walk_recursive($data, function($v) use (&$result) { # by reference
$result[] = $v;
});
var_dump($result);
There is no flatten function to get directly the leafs. You have to use recursion to check for each array if has more array children and only when you get to the bottom to move the element to a result flat array.