What do you call this implicit loop syntax in PHP? - php

Scratching my head looking at this render function:
public function render($layout, $echo=false)
{
if ($this->_escape)
{
foreach ($this as $n => &$v)
{
if (0) {
} elseif (is_array($v) or is_object($v)) {
$this->htmlspecialchars_recursive($v);
} elseif (is_scalar($v)) {$v=htmlspecialchars($v,ENT_QUOTES,null,false);}
}
}
if ($echo) { $this->insert($layout); return true;
} else { ob_start(); $this->insert($layout); return ob_get_clean(); }
}
I understand that the ampersand means that we're modifying the contents of the collection (associative array in this case?) that we're iterating over, but what's up with this if (0) business?
There's some sort of implicit iteration stuff going on here, what's this called in PHP?

It's called "this refactor is incomplete".
It appears that there was originally 3 if cases used, and the first one became no longer valid. Instead of just removing it, the author changed it to if(0), which will always evaluate to false.
The correct way would be to modify it to this
if (is_array($v) or is_object($v)) {
$this->htmlspecialchars_recursive($v);
}
elseif (is_scalar($v)) {
$v=htmlspecialchars($v,ENT_QUOTES,null,false);
}
The ampersand in the foreach is the reference symbol. $this is an object, and $n will be the property names and $v will be the property value. Without the & you get a copy of the property value. With the & you get a reference to the property, ie: you are changing the actual property value.

Related

When using simplexml_load_string all the nodes are of type string... a way to convert to int, float, etc

I get an XML response from an API and I use simplexml_load_string to convert it to an object in PHP. I then store the object into a mongo database directly and it works perfect. The issue is since it comes from XML all the nodes are of type "string" in mongo. Is there a fancy or simple way to loop through all the nodes in the PHP object and convert to integer or float or boolean? If I pull out all the nodes manually I can use "intval" and "floatval" but I am looking to see if it may be possible to do this automatically based on the content. ie. Loop through all the nodes regardless of depth and do a preg_match for 0-9 to set the item to type "int". If 0-9 w/ . set to float. If true/false/0/1 set to bool. Leave the rest strings. Any ideas?
$this->response_object = simplexml_load_string($xml);
Values in SimpleXML are always of type string within PHP. And due to the magic nature of the SimpleXMLElement class, you can not change that by extending from it.
However, you can extend from SimpleXMLElement and add a function called typecastValue() (exemplary) which does the work. You then can specify with simplexml_load_string that you want to use that class instead of the default SimpleXMLElement class.
class MyElement extends SimpleXMLElement
{
public function typecastValue() {
$value = trim($this);
// check if integer, set as float
if (preg_match('/^[0-9]{1,}$/', $value)){
return floatval($value);
}
// check if float/double
if (preg_match('/^[0-9\.]{1,}$/', $value)){
return floatval($value);
}
// check if boolean
if (preg_match('/(false|true)/i', $value)){
return (boolean)$value;
}
// all else leave as string
return $value;
}
}
As you can see, the code is very similar to your typecast function above, it just uses $this to obtain the original value.
Functions like xml_object_to_array are still generally superfluous, as the parsing is already done and you're more concerned about accessing and serializing the date in the XML into an array (I suspect this is due to JSON requirements of Mongodb but I don't know). If so, PHP JSON Serialization would/could be more appropriate, for SimpleXMLElement we have existing material on the site already.
Json Encode or Serialize an XML
I think I found my own answer (unless you have a more elegant way)...
function xml_object_to_array($xml) {
if ($xml instanceof SimpleXMLElement) {
$children = $xml->children();
$return = null;
}
foreach ($children as $element => $value) {
if ($value instanceof SimpleXMLElement) {
$values = (array)$value->children();
if (count($values) > 0) {
$return[$element] = xml_object_to_array($value);
} else {
if (!isset($return[$element])) {
$return[$element] = typecast_value($value);
} else {
if (!is_array($return[$element])) {
$return[$element] = array($return[$element], typecast_value($value));
} else {
$return[$element][] = typecast_value($value);
}
}
}
}
}
if (is_array($return)) {
return $return;
} else {
return false;
}
}
// auto typecast value for mongo storage
function typecast_value($value){
$value = trim($value);
// check if integer, set as float
if(preg_match('/^[0-9]{1,}$/', $value)){
return floatval($value);
}
// check if float/double
if(preg_match('/^[0-9\.]{1,}$/', $value)){
return floatval($value);
}
// check if boolean
if(preg_match('/(false|true)/i', $value)){
return (boolean)$value;
}
// all else leave as string
return $value;
}

php foreach iteration over object property array not incrementing the value

Please help! I have been staring at this for too long. I have a property of an object that is an array of objects. I want to pass in an object to a method of the parent object and search through that array property for a match, and if one is found return the index. Otherwise, I need it to return -1. For some reason, it is not iterating. If I echo out what should be the $order->product property (where the index is pointing during the loop), it is unchanging. I have dumped the array and I know it contains different values. I can show you a big var dump, but I figured I would first ask if there is a simple error or something else that is obvious to you that I have missed.
public function getItemIndex($prod) {
if (isset($this->orders)){
foreach($this->orders as $key => $order) {
if ($order->product == $prod) { //if I echo this $order->product to the screen, it is unchanging
return $key;
} else { return -1; }
}
}
else {
return -1;
}
}
If anyone has any ideas, I am open to discuss and post more information as needed. Thank you for your time.
You are ALWAYS returning a value on the first iteration, either the $key or -1. Try removing the else statement that you currently have. This will allow you to fully iterate over the entire array.
public function getItemIndex($prod) {
if (isset($this->orders)){
foreach($this->orders as $key => $order) {
if ($order->product == $prod) { //if I echo this $order->product to the screen, it is unchanging
return $key;
}
}
}
return -1;
}
This will ONLY return -1 once it has iterated over everything and found nothing to match. It will still return $key if it finds a match.

PHP if() state keeps evaluating to true even though it isn't

I'm having the toughest time figuring out this problem and I can't seem to find the answer.
Here's what I'm trying to do: I have different nav menus on my website depending on the section. I've already pre-built the different variations and on each page declare $linkbox_array, which is the array of links for my nav menu on that page. On certain pages I display horizontally and on others vertically. When vertical, I need a disclaimer to be added to the bottom of the stacked link boxes.
So my function is trying to say this: if the $linkbox_array is $general_linkboxes and the $bodyClass is "withSidebar", then echo out a disclaimer after array item 2. Otherwise, just echo out the array items.
So this is what I've written (forgive me if it sucks, I'm new to this):
function display_linkboxes($array) {
if ($linkbox_array == $general_linkboxes && $bodyClass = "withSidebar") {
foreach ($array as $linkbox) {
if ($linkbox == $array[2]) {
echo $linkbox;
global $general_disclaimer;
echo $general_disclaimer;
} else {
echo $linkbox;
}
}
} else {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
The problem is that it keeps spitting out the $general_disclaimer even when the two conditions aren't true. I tried to deconstruct the function and figure out where I was going wrong and I realized my if statement always evaluates to true even if I put in jibberish. For example:
function display_linkboxes($array) {
if ($linkbox_array == $askjfdalfjk) {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
This evaluates to true and displays the links even though $askjfdalfjk doesn't exist. Then I have the opposite problem below. This won't display the links even though the if statement should evaluate to true:
function display_linkboxes($array) {
if ($bodyClass == "withSidebar") {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
What in the world am I doing wrong?!?! Any help is greatly appreciated!
In your code:
function display_linkboxes($array) {
if ($linkbox_array == $general_linkboxes && $bodyClass = "withSidebar") {
...
$linkbox_array, $general_linkboxes, and $bodyClass are not in scope for this function, which means they are both equal to each other in that neither of them exists. You need to either pass them as variables to the function (recommended), or change your code to the following:
function display_linkboxes($array) {
global $linkbox_array, $general_linkboxes, $bodyClass;
if ($linkbox_array == $general_linkboxes && $bodyClass == "withSidebar") {
...
I've edited this answer to include the = -> == fix on $bodyClass
Probably you set $linkbox_array outside of your function, then your function doesn't knoew it, unless you ...
function display_linkboxes($array) {
global $linkbox_array; // <------ now the variable exists within the function.
if ($linkbox_array == $askjfdalfjk) {
foreach ($array as $linkbox) {
echo $linkbox;
}
}
}
Same with $bodyClass
Your if conditional just needs a bit of refactoring:
if(($linkbox_array == $general_linkboxes) && ($bodyClass == "withSidebar")) {}
A single '=' is an assignment operation and will always evaluate to true.
A == is the conditional equals operation you were looking for.
Furthermore if you are checking for array equality and order matters to you then you should use the '===' operator which checks not just for the same elements but also the same order
Lastly you have a scoping issue - if your linkbox array is evaluating as equal to a null variable then it hasn't been defined. You can use the print_r() operation to check this. If the array is defined outside the function then you should pass it to the function as a parameter.

Why does my class only return the first value?

This Class gives me a blank output even if I change return to echo, I'm not sure what the issue is but I'm obviously not that versed in dealing with Classes and Objects.
I'm sure I'm just handling the variables/arrays incorrectly, but I can't see where, maybe the variables shouldn't be declared under Class since they should only be returned if a person is created? Should I declare variables in the function, or not declare them at all since they should be handled by $args?
Updated Question: How do I get it to return every argument not just FIRSTNAME?
PHP:
class people_handler
{
public $firstname;
public $middlename;
public $lastname;
public $city;
public $province_state;
/* zip+4 is default for postcode (postal code) */
public $postcode;
public $country;
function create_people($args)
{
$fullname=array($this->firstname,$this->middlename,$this->lastname);
$normname=array($this->firstname,$this->lastname);
$fulladdress=array($this->city,$this->province_state,$this->postcode,$this->country);
if(!$args->middlename&&$args->firstname && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{
$temp_arr=array($normname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
}
else if($args->firstname && $args->middlename && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{
$temp_arr=array($fullname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
}
else
{
die ("Must enter all values excluding middlename.");
}
}
}
$p1=new people_handler;
$p1->firstname="John";
$p1->middlename="Jonah";
$p1->lastname="Jameson";
$p1->city="Lansing";
$p1->province_state="Michigan";
$p1->postcode="48876-4444";
$p1->country="USA";
echo $p1->create_people($p1);
Returns:
John
You're missing the Object self-reference: $this all over the place.
Anytime you refer to a method or property from within the class, you need to refer to $this as the current instantiation of the Object that is doing the process. So, for instance...
$fullname=array($firstname,$middlename,$lastname);
becomes
$fullname=array($this->firstname,$this->middlename,$this->lastname);
Which should work, since you assigned the values to those properties already.
EDIT: Looking at the code further, constantly returning a value through loops won't manage the echoing to the browser. You can either echo $value instead of returning it, or build an array from the values and return that and have the script handle the array to echo to the browser.
EDIT THE SECOND: To get all the values out, you need to collect them as you build them. Another option is to simply output them to the browser as part of the method. Both options work, but collecting them into an array makes it more portable, but also a fair bit more code to maintain. As well, you do not need to pass the object into itself to get the method to work.
echo $p1->create_people($p1);
Should be...
$p1->create_people();
In create_people you'll have...
function create_people()
{
$fullname=array($this->firstname,$this->middlename,$this->lastname);
$normname=array($this->firstname,$this->lastname);
$fulladdress=array($this->city, $this->province_state, $this->postcode, $this->country);
if($args->firstname && $args->lastname && $args->city && $args->province_state && $args->postcode && $args->country)
{ //Don't bother including middlename if it doesn't matter if it is filled or not...
$temp_arr = array($normname, $fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
echo $values;
}
}
} else {
die ("Must enter all values excluding middlename.");
}
}
That should work.
Apart from the self-reference problem (btw the $args is also not needed as this should be the self-reference), your loop structure is wrong.
$temp_arr=array($normname,$fulladdress);
foreach($temp_arr as $value)
{
foreach($value as $values)
{
return $values;
}
}
This will:
Loop through temp_arr, finding $normname as the first value
Treat $normname as an array and loop through it
Return the first value it finds in $normname
That concludes the function, everything else is not executed.
A function can only have one return value. If you need to return information on more than one thing, you need to return it as an array or as an object so that it is all wrapped up in one element.
At the moment I'm not quite sure what you're trying to accomplish with your class, so unfortunately I can't help you with what you need to do.
Edit: You don't need to return anything in that case. Your class makes those variables accessible to all functions within the class already. With "new" you create an instance of the object, that is you create "a people_handler". This people_handler has properties about it, which you made public, so they can be set from outside the class (which may not be a great idea in a bigger project but seems fine for this). All functions which are part of the class (that is, inside it), can access what values these properties currently have for that certain people_handler by using the self-reference, $this:
class TestClass {
public fullname; //a random "property"
function echoFullname() {
echo $this->fullname; //whatever fullname is at the moment for the TestClass object we are using
}
}
$a = new TestClass(); //Create a TestClass object
$a->fullname = "Alex"; //make its name "Alex"
$b = new TestClass(); //Create another TestClass object
$b->fullname = "Carl"; //but let's name him Carl
$a->echoFullname(); //And now output the names
$b->echoFullname();
Obviously this has no practical use but hopefully illustrates how it works.As you can see, variable passing wasn't necessary at all.
at line 14:
$fullname=array($firstname,$middlename,$lastname);
Probably should be:
$fullname=array($this->firstname,$this->middlename,$this->lastname);
same one line 16:
$fulladdress=array($city,$province_state,$postcode,$country);

php: array_replace_recursive alternative

I need a solution for array_replace_recursive, because my php-version isn't high enough. I want to use this code:
$_GET = array_replace_recursive($_GET, array("__amp__"=>"&"));
easy, isn't it?
On the PHP docs page for array_replace_recursive, someone posted the following source code to use in place of it:
<?php
if (!function_exists('array_replace_recursive'))
{
function array_replace_recursive($array, $array1)
{
function recurse($array, $array1)
{
foreach ($array1 as $key => $value)
{
// create new key in $array, if it is empty or not an array
if (!isset($array[$key]) || (isset($array[$key]) && !is_array($array[$key])))
{
$array[$key] = array();
}
// overwrite the value in the base array
if (is_array($value))
{
$value = recurse($array[$key], $value);
}
$array[$key] = $value;
}
return $array;
}
// handle the arguments, merge one by one
$args = func_get_args();
$array = $args[0];
if (!is_array($array))
{
return $array;
}
for ($i = 1; $i < count($args); $i++)
{
if (is_array($args[$i]))
{
$array = recurse($array, $args[$i]);
}
}
return $array;
}
}
?>
The code above by #Justin is ok, save for 2 things:
Function is not readily available at start of php execution be cause it is wrapped in if(). PHP docu says
When a function is defined in a conditional manner such as the two examples shown. Its definition must be processed prior to being called.
Most importantly; calling the function twice results in fatal error.
PHP docu says
All functions and classes in PHP have the global scope - they can be called outside a function even if they were defined inside and vice versa.
So I just moved the recurse function outside array_replace_recursive function and it worked well. I also removed the if() condition and renamed it to array_replace_recursive_b4php53 for fear of future upgradings

Categories