This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 8 years ago.
<?php
echo '<pre>';
error_reporting(E_ALL);
$pid = '129';
$families = array
(
"Griffin"=>array
(
"PTY"=>"Peter",
"STY"=>"X",
"QTY"=>"A|F"
),
"Quagmire"=>array
(
"NTY"=>"Glenn"
),
"Brown"=>array
(
"FTY"=>"Cleveland",
"OTY"=>"Q|G|T|Y",
"PTY"=>"Junior"
)
);
global $allid;
$allid = array();
function buildid($pid,$key,$val){
if (preg_match("/\|/",$val)){
$val = explode("|",$val);
foreach($val as $val1){
$id = $pid.'-'.$key.'-'.$val1;
$allid[] = $id;
}
}
}
print_r($allid);
foreach($families as $familieskey=>$familiesvalue){
foreach($familiesvalue as $skey=>$sval){
buildid($pid,$skey,$sval);
}
}
echo '</pre>';
?>
Expected output for the above code:
Case1:
Array
(
[0] => 129-QTY-A
[1] => 129-QTY-F
)
Array
(
[0] => 129-OTY-Q
[1] => 129-OTY-G
[2] => 129-OTY-T
[3] => 129-OTY-Y
)
Case2:
Array
(
[0] => 129-QTY-A
[1] => 129-QTY-F
[2] => 129-OTY-Q
[3] => 129-OTY-G
[4] => 129-OTY-T
[5] => 129-OTY-Y
)
The global $allid; goes into the function itself, not outside, i.e.
function buildid($pid,$key,$val){
global $allid;
if (preg_match("/\|/",$val)){
...
The documentation points out that
Using global keyword outside a function is not an error. It can be used if the file is included from inside a function.
--> Unless you do include it from inside a function, it has no effect.
Edit to add: You also need to put the print_r after you run the code (i.e. right before echo '</pre>';) - currently, you are showing the content of the array right after you initialize it, then you fill it with data, and then your program ends.
You are outputting array before setting its value. So code should be like this.
Your function buildid() is defined but not called before outputting array.
so print_r($allid); should be called afters its value are filled in foreach loop.
<?php
echo '<pre>';
error_reporting(E_ALL);
$pid = '129';
$families = array
(
"Griffin"=>array
(
"PTY"=>"Peter",
"STY"=>"X",
"QTY"=>"A|F"
),
"Quagmire"=>array
(
"NTY"=>"Glenn"
),
"Brown"=>array
(
"FTY"=>"Cleveland",
"OTY"=>"Q|G|T|Y",
"PTY"=>"Junior"
)
);
$allid = array();
function buildid($pid,$key,$val){
//Global should be inside function like this.
global $allid;
if (preg_match("/\|/",$val)){
$val = explode("|",$val);
foreach($val as $val1){
$id = $pid.'-'.$key.'-'.$val1;
$allid[] = $id;
}
}
}
foreach($families as $familieskey=>$familiesvalue){
foreach($familiesvalue as $skey=>$sval){
buildid($pid,$skey,$sval);
}
}
print_r($allid);
echo '</pre>';
?>
First. You make print_r($allid) before setting to this array the data. You did it only in "foreach($families as $familieskey=>$familiesvalue){..."]
Second.Done.
TRY
error_reporting(E_ALL);
$pid = '129';
$families = array
(
"Griffin"=>array
(
"PTY"=>"Peter",
"STY"=>"X",
"QTY"=>"A|F"
),
"Quagmire"=>array
(
"NTY"=>"Glenn"
),
"Brown"=>array
(
"FTY"=>"Cleveland",
"OTY"=>"Q|G|T|Y",
"PTY"=>"Junior"
)
);
function buildid($pid,$key,$val){
global $allid;
if (preg_match("/\|/",$val)){
$val = explode("|",$val);
foreach($val as $val1){
$id = $pid.'-'.$key.'-'.$val1;
$allid[] = $id;
}
}
}
foreach($families as $familieskey=>$familiesvalue){
foreach($familiesvalue as $skey=>$sval){
buildid($pid,$skey,$sval);
}
}
print_r($allid);
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 =.
I'm working with dynamic data, so I'm trying to put that data into a bidimensional array.
I need a structure like this:
$array['something1'] = array ( 'hi1' => 'there1' , 'hi2' => 'there2' );
All this data is dynamically generated by a foreach, ex.:
$list = array ( 0 => 'something;hi1;there1' , 1 => 'something;hi2;there2' );
foreach ( $list as $key => $data )
{
// extract data
$columns = explode ( ';' , $data );
// set data
$items[$columns[0]] = array ( $columns[1] => $columns[2] );
}
How Can I do the previously described?
Right now the script is stepping over the previous key getting something like this:
$array['something1'] = array ( 'hi2' => 'there2' );
I hope you can help me.
Thanks.
The problem is that you are overwriting the value for the key when it already exists. You should modify to something like:
foreach ( $list as $key => $data )
{
// extract data
$columns = explode ( ';' , $data );
$outer_array_key = $columns[0];
$key = $columns[1];
$value = $columns[2];
// set data
$items[$outer_array_key][$key] = $value;
}
Here is how it can be done:
$list = array ( 0 => 'something;hi1;there1' , 1 => 'something;hi2;there2' );
$newlist =array();
foreach($list as $k=>$v){
$items = explode(';',$v);
$newlist[$items[0]][$items[1]]=$items[2];
}
echo "<pre>";
print_r($newlist);
echo "</pre>";
//output
/*
Array
(
[something] => Array
(
[hi1] => there1
[hi2] => there2
)
)*/
?>
Change your set data with something like this :
if(!array_key_exists($columns[0], $items))
$items[$columns[0]] = array();
$items[$columns[0]][$columns[1]] = $columns[2];
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to modify an array's values by a foreach loop?
Why doesn't this work?
$user_list_array = array(
1 => array( "first_name" => "Jim" ),
2 => array( "first_name" => "Bob" )
)
foreach ($user_list_array as $item ) {
echo $item["first_name"];
$item["last_name"] = "test";
}
var_dump($user_list_array );
I can get the "first_name"s back, but can't add the "last_name";
You're modifying $item, which is a copy of the relevant entry fro $user_list_array
EITHER:
(modify the original array)
foreach ($user_list_array as $key => $item ) {
echo $item["first_name"];
$user_list_array[$key]["last_name"] = "test";
}
OR:
(by reference)
foreach ($user_list_array as &$item ) {
echo $item["first_name"];
$item["last_name"] = "test";
}
unset($item);
foreach ($user_list_array as &$item ) {
echo $item["first_name"];
$item["last_name"] = "test";
}
Adding & before $item will pass the array by reference which means that any modifications you make to it will be saved.
It didn't work because you were not modifying the actual array, this should do the trick.
$user_list_array = array(
1 => array( "first_name" => "Jim" ),
2 => array( "first_name" => "Bob" )
)
foreach ($user_list_array as $id => $item ) {
echo $item["first_name"];
$user_list_array[$id]["last_name"] = "test";
}
You should be modifying the original array, not the tmp variable $item the loop creates. You could do it like that
foreach ($user_list_array as $key = $val) {
echo $val["first_name"];
$user_list_array[$key]["last_name"] = "test";
}
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
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.