I am having trouble getting associative names of my $_errors array right.
$item1 = 'password';
$item2 = 'firstname';
$this->addError(array($item1 => 'required'))
$this->addError(array($item2 => 'required'))
private function addError($error) {
$this->_errors[] = $error;
}
public function error($item) {
return array_search($item, $this->_errors);
}
When I do a print_r() on $_errors I get:
Array
(
[0] => Array
(
[password] => required
)
[1] => Array
(
[firstname] => required
)
)
But I need it to be:
Array
(
[password] => required
[firstname] => required
)
So I can call 'password' like so $this->_errors['password'];
Simply change your functions accordingly:
private function addError($element, $error) {
$this->_errors[$element] = $error;
}
$this->addError($item1, 'required');
$this->addError($item2, 'required');
Of course this scheme will not allow you to track multiple errors for the same element at the same time; if you need to do that you have to rethink your desired result.
Use a list() construct to break the array into key-value pair and then subject it to your $this->errors[] array variable.
private function addError($error) {
$this->_errors[] = $error;
}
to
private function addError($error) {
list($a,$b) = each($error); //<--------- Add the list() here
$this->_errors[$a] = $b; //<--------- Map those variables to your array
}
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 is a typical array structure:
$s = array ('etc'=>'etc', 'fields' =>
array (
0 => array (
'name'=>'year', 'description'=>'Year of ...', 'type'=>'integer',
),
1 => array (
'name'=>'label', 'description'=>'Offical short name', type'=>'string',
),
2 => array (
'name' => 'xx', 'description' => 'Xx ...', 'type' => 'string',
)
));
Here is a non-elegant way (or "not so elegant way") to reduce the big array to a simple array containing just one column:
$fields = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r)
$fields[] = $r['name'];
This works, but is it possible to do the same with only one instruction? Perhaps using like array_reduce(), but I not see how.
Here are other typical "elegance PHP problem":
$fieldsByName = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r)
$fields[$r['name']] = array(
'description' =>$r['description'],
'type' =>$r['type']
);
Is there a PHP alternative? The idea here is to use the keyword (name in the example) as an array key, and the other elements as usual fields, so, the generic non-elegant algorithm is
$fieldsByName = array();
foreach ($strut['resources'][0]['schema']['fields'] as $r){
$key = $r['name'];
unset($r['name']);
$fields[$key] = $r;
}
You can use array_column to extract all values with key name into another array
$names = array_column($strut['resources'][0]['schema']['fields'], 'name');
you could put your array thru this function:
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
it will result in just a literal sequence of just values of your multidimensional array, like so.
Array
(
[0] => etc
[1] => year
[2] => Year of ...
[3] => integer
[4] => day
[5] => Day of the ...
[6] => string
[7] => xx
[8] => Xx ...
[9] => string
)
then, as you know original structure - you can parse this how needed. 4ex: every third value could be new assoc array's key value that holds an array with arrays of first two values.., or as you wish
array_column is first logical announcement, no surprises there.
Depending on how normalized your data is and how often this issues comes up, you could implement a class around your data. You can use the ArrayAccess, Iterator and Countable to make the change completely transparent, and you would be able to implement helper methods to hide the complexity of fetching data.
Here is an example, just using ArrayAccess:
class Strut implements ArrayAccess {
private $data;
private $fieldsByName = null;
public function __construct($data) {
$this->data = $data;
}
public function fieldsByName() {
//If the result has not already been computed
if($this->fieldsByName === null) {
$this->fieldsByName = array();
foreach($this->data['resources'][0]['schema']['fields'] as $r) {
$this->fieldsByName[ $r['name'] ] = array(
'description' =>$r['description'],
'type' =>$r['type']
);
}
}
return $this->fieldsByName;
}
/**
* ArrayAccess Methods
*/
public function offsetSet($offset, $value) {
$this->data[$offset] = $value;
}
public function offsetExists($offset) {
return isset( $this->data[$offset] );
}
public function offsetUnset($offset) {
unset( $this->data[$offset] );
}
public function offsetGet($offset) {
return isset( $this->data[$offset] ) ? $this->data[$offset] : null;
}
}
Using the above code you should be able to access your data just has you have been, but you also have the option of defining additional accessors in a nice container. Note that you also have to implement the Iterator interface to be able to foreach over your data.
This doesn't address the "elegance" issue of the underlying implementation (the other solutions do a nice job of that), but this way hides the complexity completely.
First, I have tried to find solution within the source here but couldn't find what I am looking so posting as a new question. Thanks for your help
I want to convert Array to Object. Here is what I am getting output
Array
(
[0] => stdClass Object
(
[id] => 1
[username] => robustsolver
[first_name] => John
[last_name] => Smith
)
[1] => stdClass Object
(
[id] => 2
[username] => pickypacker
[first_name] => Peter
[last_name] => Packer
)
)
So if I want any column for all users than I have to write code $users[0]->first_name; which gives me only one item. But what I am looking is to use $users->first_name and this should return an array of all user's column (here is first_name)
Hope I have explain in better way. :S
You can try this where $arr is your array:
function filter_callback($element) {
return $element->first_name;
}
$result= array_map('filter_callback', $arr);
From a quick test, this seems to work. It keeps objects of the array without your wanted property but its value is set to NULL. Not sure if that's what you want but you can edit the filter_callback to remove such elements.
Maybe something like this will work (not tested):
$arr = array(...); // array of objects (in)
$obj = new Object; // object of arrays (out)
foreach($arr as $a) {
foreach(get_object_vars($a) as $k=>$v) {
if(!property_exists($obj, $k)) {
$obj->{$k} = array();
}
$obj->{$k}[] = $v;
}
}
consider defining a new class:
class User_objects
{
public $first_name;
public $username;
public $last_name;
public function __construct()
{
$this->first_name = array();
$this->username = array();
$this->last_name = array();
}
}
then:
consider $array_of_objects to be your array of objects (input).
$users = new User_objects();
foreach ($array_of_objects as $object)
{
$users->first_name[] = $object->first_name; // append to array
$users->last_name[] = $object->last_name;
$users->username[] = $object->username;
}
then you can get your array from $users->first_name
You can write a simple helper function that will aid in the columns you wish to select:
function prop_selector($prop)
{
return function($item) use ($prop) {
return $item->{$prop};
};
}
$first_names = array_map(prop_selector('first_name'), $users);
$last_names = array_map(prop_selector('last_name'), $users);
I need to access the data: 'hotelID', 'name', 'address1','city' etc. I have the following Std Object array ($the_obj) in PHP that contains the following data:
object(stdClass)[1]
public 'HotelListResponse' =>
object(stdClass)[2]
public 'customerSessionId' => string '0ABAAA87-6BDD-6F91-4292-7F90AF49146E' (length=36)
public 'numberOfRoomsRequested' => int 0
public 'moreResultsAvailable' => boolean false
public 'HotelList' =>
object(stdClass)[3]
public '#size' => string '227' (length=3)
public '#activePropertyCount' => string '227' (length=3)
public 'HotelSummary' =>
array (size=227)
0 =>
object(stdClass)[4]
public 'hotelId' => 112304
public 'name' => La Quinta Inn and Suites Seattle Downtown
public 'address1' => 2224 8th Ave
public 'city' => Seattle
public 'stateProvinceCode' => WA
public 'postalCode' => 98121
public 'countryCode' => US
public 'airportCode' => SEA
public 'propertyCategory' => 1
public 'hotelRating' => 2.5
I have tried the following for lets say to access the 'name':
echo $the_obj->HotelListResponse->HotelList->HotelSummary[0]->name;
Also I have tried to print each key and value pairs by using foreach loop but I keep on getting errors. Here is what I tried:
foreach ($the_obj->HotelListResponse->HotelList->HotelSummary[0] as $key => $value){
echo $key.' : '.$value.'<br />';
}
Here are the errors that I get:
Trying to get property of non-object
Warning: Invalid argument supplied for foreach()
Thank you everyone for answering, I have figured out the way to access the 'hotelID', 'name' and all other keys and value pairs in the deepest nest of the array.
I converted the Std Object array to an associative array, then I accessed each of the value by using the foreach loop:
foreach ($the_obj["HotelListResponse"]["HotelList"]["HotelSummary"] as $value){
echo $value["hotelId"];
echo $value["name"];
//and all other values can be accessed
}
To access both (Keys as well as values):
foreach ($the_obj["HotelListResponse"]["HotelList"]["HotelSummary"] as $key=>$value){
echo $key.'=>'.$value["hotelId"];
echo $key.'=>'.$value["name"];
//and all other keys as well as values can be accessed
}
Regarding to #Satya's answer I'd like to show simpler way for Object to array conversion, by using json functions:
$obj = ...
$tmp = json_encode($obj);
$objToArray = json_decode($tmp,true);
This way you can easily access array items. First you can dump structure...
try something like this :
$p=objectToArray($result);
recurse($p);
}
function objectToArray( $object )
{
if( !is_object( $object ) && !is_array( $object ) )
{
return $object;
}
if( is_object( $object ) )
{
$object = get_object_vars( $object );
}
return array_map( 'objectToArray', $object );
}
function recurse ($array)
{
//statements
foreach ($array as $key => $value)
{
# code...
if( is_array( $value ) )
{
recurse( $value );
}
else
{ $v=$value;
$v=str_replace("’",'\'',strip_tags($v));
$v=str_replace("–",'-',$v);
$v=str_replace("‘",'\'',strip_tags($v));
$v=str_replace("“",'"',strip_tags($v));
$v=str_replace("”",'"',strip_tags($v));
$v=str_replace("–",'-',strip_tags($v));
$v=str_replace("’",'\'',strip_tags($v));
$v=str_replace("'",'\'',strip_tags($v));
$v=str_replace(" ",'',strip_tags($v));
$v=html_entity_decode($v);
$v=str_replace("&",' and ',$v);
$v = preg_replace('/\s+/', ' ', $v);
if($key=="image")
{
if(strlen($v)==0)
{
echo '<'.$key .'>NA</'.$key.'>';
}
else
{
echo '<'.$key .'>'. trim($v) .'</'.$key.'>';
}
}
}
}
}
When I do the following:
$arUserStuff = array ('name' => 'username', 'email' => 'test#test.com');
$object = (object) $arUserStuff;
print_r($object);
The print function returns me the following:
stdClass Object ( [name] => username [email] => test#test.com )
How can I change std class object in let's say's User Object?
Create that class, then create an object of it:
class User {
public $name, $email; // public for this example, or set these by constructor
public function __construct( array $fields) {
foreach( $fields as $field => $value)
$this->$field = $value;
}
}
$object = new User;
$object->name = 'username';
$object->email = 'test#test.com';
Or, you can do:
$arUserStuff = array ('name' => 'username', 'email' => 'test#test.com');
$object = new User( $arUserStuff);
Now, from print_r( $object);, you'll get something like this:
User Object ( [name] => username [email] => test#test.com )
actually to do what you want, you should make it like:
$arUserStuff = new ArrayObject(
array (
'name' => 'username', 'email' => 'test#test.com'
)
);
to change the class name you need to create a new class.
It's a rather complex process but you can learn about it here:
http://php.net/manual/en/language.oop5.php
Here's a generic function that converts an array into any type of object, assuming the fields are public
class User { public $name, $email; }
class Dog { public $name, $breed; }
function objFromArray($className, $arr) {
$obj = new $className;
foreach(array_keys(get_class_vars($className)) as $key) {
if (array_key_exists($key, $arr)) {
$obj->$key = $arr[$key];
}
}
return $obj;
}
print_r(objFromArray('User',
array ('name' => 'username', 'email' => 'test#test.com')));
echo "<br/>";
print_r(objFromArray('Dog',
array ('name' => 'Bailey', 'breed' => 'Poodle')));
Output
User Object ( [name] => username [email] => test#test.com )
Dog Object ( [name] => Bailey [breed] => Poodle )
I wanted to make a trait out of it but don't have PHP 5.4 installed to test it. This wouldn't require the fields to be public
trait ConvertibleFromArray {
public static function fromArray($arr) {
var $cls = get_called_class();
var $obj = new $cls;
foreach($arr as $key=>$value) {
if (property_exists($obj, $arr)) {
$obj->$key = $value;
}
}
return $obj;
}
}
class User {
use ConvertibleFromArray;
public $name, $email;
}
class Dog {
use ConvertibleFromArray;
public $name, $breed;
}
print_r(User::fromArray(array ('name' => 'username', 'email' => 'test#test.com')));
print_r(Dog::fromArray(array('name' => 'Bailey', 'breed' => 'Poodle')));
?>