SimpleXML - echo / print_r return different values - php

I am trying to convert some xml into a json object using PHP.
This should be working, yet for some bizarre reason it is failing.
Could someone provide some input.
// Loop Through images and return the right one.
$i = 1;
foreach($page->image as $image) {
if ($i == $_GET['id']) {
echo json_encode(array(
'background' => $image['bgColor'],
'image' => $image['source'],
'caption' => $image['caption']
));
}
$i++;
}
This code returns the following.
{"background":{"0":"000033"},
"image":"0":"0210e849f02646e2f5c08738716ce7e8b3c1169112790078351021245495.jpg"},
"caption": {"0":"Frog"}}
print_r($image['bgColor']); shows 'SimpleXMLElement Object ( [0] => 000033 )'
echo $image['bgColor']; shows '000033'
How do I parse values like the echo statement instead of the print_r statement. Why are these different?

Why are these different
Because these variables are not strings internally, but SimpleXMLElement type objects that get converted into strings when output by echo.
To use the values elsewhere,I usually do an explicit cast:
$bg_color = (string) $image['bgColor'];
A canonical question regarding casting a simplexml element into a string is here:
Forcing a SimpleXML Object to a string, regardless of context

Related

PHP json_encode($_POST) and mySQL issue

I am trying to json_encode($_POST) in PHP, but I have one small problem.
On my $_POST sometimes I get some encoded JSON as example below:
Array
(
[module] => {"media":true}
)
Where module contains a JSON string. My problem is when I use json_encode($POST); I get a result like this:
{"module":"{\"petMedia\":true}"}
Trying to insert into mySQL JSON column, I get this error
Invalid JSON text: "Missing a comma or '}' after an object member.
It's not possible for me to decode the string before because not always I get encoded JSON.
Thanks.
If I had POST variables coming in where sometimes they have json encoded in it, or not... one way to handle it, would be a cleanup loop.
Lets say this is the POST in:
$_POST -> 'var1' = 'some string'
-> 'var2' = '2315'
-> 'var3' = '{"some":"json"}'
Now, I would setup a little cleaner, because you cannot just json_encode($_POST) as you found out. It double encodes the var3.
$clean = array();
foreach($_POST as $key => $val) {
json_decode($val);// test
if (json_last_error() == JSON_ERROR_NONE) {
$clean[$key] = json_decode($val);// $val is json, so pre-decode it
} else {
$clean[$key] = $val;// its not real json, so assign straight
}
}
print_r($clean);
print_r(json_encode($clean));
Results in:
Array (
[var1] => some string
[var2] => 2315
[var3] => stdClass Object
(
[some] => json
)
)
// this then is a json encoded string, which is good:
{"var1":"some string","var2":2315,"var3":{"some":"json"}}
Basically, this then makes a full object which you can now store into the database safely, since all variables and their values are json encodable.
I hope this helps you get started, and you dont actually have normal variables inside json strings, that are not json encoded, and and and and ;)
A variation on a theme perhaps
/* emulate POST */
$_POST=array(
'php'=>'fantastic',
'javascript'=>'awesome',
'asp'=>'awful',
'module'=>json_encode( array( 'media' => true ) )
);
/* process the POST array and decode any json data within */
array_walk( $_POST, function( &$value ){
$tmp=json_decode( $value );
if( json_last_error() == 0 )$value=$tmp;
});
/* encode the POST data */
$encoded=json_encode( $_POST );
Outputs:
{"php":"fantastic","javascript":"awesome","asp":"awful","module":{"media":true}}

Reading JSON file in PHP

I tried this code
$jsonlogcontents='{"buildings":"townhall","Army":{ "Paladins":{ "325648":0, "546545":4 }, "Knights":{ "325648":-2, "546545":0 } } }';
$phpArray = json_decode($jsonlogcontents, false);
echo $phpArray->buildings;
echo $phpArray->Army;
This is just a sample of my code, the JSON file is too large to include and has sensitive information. The problem I'm having is I can't get the value or print the value of
$phpArray->Army
It give's me an error. I can however print or get the value of
$phpArray->buildings
I'm thinking that when you decode a JSON file/contents in PHP, you can't get/print/store the value of a 'key' that has more set of info (more { and }) or brackets in it. You can only print/get values for keys who's value's contain only 1 value and nothing else.
If this is the case, what can I do to get the contents of the key that has more JSON codes in it. Also, how can I convert the contents of a key that has more JSON info in it into a string? the conversion is so I can display the value of that key to the page or echo it
The error is because Army is an object, and echo doesn't know how to convert it to a string for display. Use:
print_r($phpArray->Army);
or:
var_dump($phpArray->Army);
to see its contents.
P.S. $phpArray not an array but an object.
For Army however, I will need to do another json_decode() for that.
You don't. json_decode() decodes the entire structure in one call, into one large object (or array). No matter how deeply nested the data is, you call json_decode() once and you're done. That's why Army is not a JSON string any more.
When you are adding false as the second parameter to the json_encode it will updating all array to the sdClass empty objects.In this way you can the main array as the object
<?php
$json = '{
"buildings": "townhall",
"Army": {
"Paladins": {
"325648": 0,
"546545": 4
},
"Knights": {
"325648": -2,
"546545": 0
}
}
}';
$array = json_decode($json, true);
$object = (object)$array;
var_dump($object->Army);
?>
OUTPUT
array(2) {
["Paladins"]=>
array(2) {
[325648]=>
int(0)
[546545]=>
int(4)
}
["Knights"]=>
array(2) {
[325648]=>
int(-2)
[546545]=>
int(0)
}
}
Working Demo
It's because the output from your json_decode looks like this:
object(stdClass)(
'buildings' => 'townhall',
'Army' => object(stdClass)(
'Paladins' => object(stdClass)(
325648 => 0,
546545 => 4
),
'Knights' => object(stdClass)(
325648 => -2,
546545 => 0
)
)
)
Army is a standard object so it can't know how to echo it. You can however var_dump it:
var_dump($phpArray->Army);
The easiest solution is to treat it as a normal array with:
$phpArray = json_decode($jsonlogcontents, true);

PHP: Recursive htmlspecialchars on object

I want to establish a generic sanitizer for my data that comes from various sources. With sanitizing I mean (at this stage) applying htmlspecialchars to strings. Now, the data that comes from these sources can be anything from an object to an array to a string, all nested (and complicated), and the format is always a bit different.
So I thought of a recursive htmlspecialchars function that applies itself to arrays and objects, and only applies htmlspecialchars to strings, but how do I walk an object recursively?
Thanks.
EDIT: I think I should have mentioned this - I am actually building a RIA that relies heavily on JS and JSON for client-server communication. The only thing the server does is fetching stuff from the database and returning it to the client via JSON, in the following format:
{"stat":"ok","data":{...}}
Now as I said, data could be anything, not only coming from a DB in the form of strings, but also coming from an XML
The workflow to process the JSON is as follows:
Fetch data from the DB/XML (source encoding is iso-8859-1)
Put them into the "data" array
Recursively convert from iso-8859-1 to utf-8 using
private function utf8_encode_deep(&$input) {
if (is_string($input)) {
$input = $this -> str_encode_utf8($input);
} else if (is_array($input)) {
foreach ($input as &$value) {
$this -> utf8_encode_deep($value);
}
unset($value);
} else if (is_object($input)) {
$vars = array_keys(get_object_vars($input));
foreach ($vars as $var) {
$this -> utf8_encode_deep($input -> $var);
}
}
}
Use PHP's json_encode to convert the data into JSON
Send (echo) the data to the client
Render the data using JS (e.g. putting into a table)
And somewhere in between that, the data should be somehow sanitized (at this stage only htmlspecialchars). Now the question should have been: Where to sanitize, using what method?
You can try the following
class MyClass {
public $var1 = '<b>value 1</b>';
public $var2 = '<b>value 2</b>';
public $var3 = array('<b>value 3</b>');
}
$list = array();
$list[0]['nice'] = range("A", "C");
$list[0]['bad'] = array("<div>A</div>","<div>B</div>","<div>C</div>",new MyClass());
$list["<b>gloo</b>"] = array(new MyClass(),"<b>WOW</b>");
var_dump(__htmlspecialchars($list));
Function Used
function __htmlspecialchars($data) {
if (is_array($data)) {
foreach ( $data as $key => $value ) {
$data[htmlspecialchars($key)] = __htmlspecialchars($value);
}
} else if (is_object($data)) {
$values = get_class_vars(get_class($data));
foreach ( $values as $key => $value ) {
$data->{htmlspecialchars($key)} = __htmlspecialchars($value);
}
} else {
$data = htmlspecialchars($data);
}
return $data;
}
Output Something like
array
0 =>
array
'nice' =>
array
0 => string 'A' (length=1)
1 => string 'B' (length=1)
2 => string 'C' (length=1)
'bad' =>
array
0 => string '<div>A</div>' (length=24)
1 => string '<div>B</div>' (length=24)
2 => string '<div>C</div>' (length=24)
3 =>
object(MyClass)[1]
...
array
0 =>
object(MyClass)[2]
public 'var1' => string '<b>value 1</b>' (length=26)
public 'var2' => string '<b>value 2</b>' (length=26)
public 'var3' =>
array
...
You would only want to escape when outputting into HTML. And you cannot output a complete array or object into HTML, so escaping everything seems invalid.
You have one level of indirection because of your JSON output. So you cannot decide in PHP what context the data is used for - JSON is still plain text, not HTML.
So to decide whether any data inside the JSON must be escaped for HTML we must know how your Javascript is using the JSON data.
Example: If your JSON is seen as plain text, and contains something like <b>BOLD</b>, then the expected outcome when used inside any HTML is exactly this text, including the chars that look like HTML tags, but no bold typesetting. This will only happen if your Javascript client handles this test as plain text, e.g. it DOES NOT use innerHTML() to place it on the page, because that would activate the HTML tags, but only innerText() or textContent() or any other convenience method in e.g. jQuery (.text()).
If on the other hand you expect the JSON to include readymade HTML that is fed into innerHTML(), then you have to escape this string before it is put into JSON. BUT you must escape the whole string only if you do not want to add any formatting to it. Otherwise you are in a situation that uses templates for mixing predefined formatting with user content: The user content has to be escaped when put into HTML context, but the result must not - otherwise Javascript cannot put it into innerHTML() and enable the formatting.
Basically a global escaping for everything inside your array or object most likely is wrong, unless you know for everything that it will be used in a HTML context by your Javascript.
function htmlrecursive($data){
if (is_array($data) && count($data) > 1){
foreach ($data as &$d){
$d = htmlrecursive($d);
}
} else if (!is_array($data)){
return htmlspecialchars($data);
}
else {
return htmlspecialchars($data[0])
}
}
htmlrecursive($array);
For objects you need to implement The ArrayAccess interface then you can do a array walk recursive
Also check this question Getting an object to work with array_walk_recursive in PHP

Why am I getting an array of SimpleXMLElement Objects here?

I have some code that pulls HTML from an external source:
$doc = new DOMDocument();
#$doc->loadHTML($html);
$xml = #simplexml_import_dom($doc); // just to make xpath more simple
$images = $xml->xpath('//img');
$sources = array();
Then, if I add all of the sources with this code:
foreach ($images as $i) {
array_push($sources, $i['src']);
}
echo "<pre>";
print_r($sources);
die();
I get this result:
Array
(
[0] => SimpleXMLElement Object
(
[0] => /images/someimage.gif
)
[1] => SimpleXMLElement Object
(
[0] => /images/en/someother.jpg
)
....
)
But when I use this code:
foreach ($images as $i) {
$sources[] = (string)$i['src'];
}
I get this result (which is what is desired):
Array
(
[0] => /images/someimage.gif
[1] => /images/en/someother.jpg
...
)
What is causing this difference?
What is so different about array_push()?
Thanks,
EDIT: While I realize the answers match what I am asking (I've awarded), I more wanted to know why whether using array_push or other notation adds the SimpleXMLElement Object and not a string when both arent casted. I knew when explicitly casting to a string I'd get a string. See follow up question here:Why aren't these values being added to my array as strings?
The difference is not caused by array_push() -- but by the type-cast you are using in the second case.
In your first loop, you are using :
array_push($sources, $i['src']);
Which means you are adding SimpleXMLElement objects to your array.
While, in the second loop, you are using :
$sources[] = (string)$i['src'];
Which means (thanks to the cast to string), that you are adding strings to your array -- and not SimpleXMLElement objects anymore.
As a reference : relevant section of the manual : Type Casting.
Sorry, just noticed better answers above, but the regex itself is still valid.
Are you trying to get all images in HTML markup?
I know you are using PHP, but you can convert use this C# example of where to go:
List<string> links = new List<string>();
if (!string.IsNullOrEmpty(htmlSource))
{
string regexImgSrc = #"<img[^>]*?src\s*=\s*[""']?([^'"" >]+?)[ '""][^>]*?>";
MatchCollection matchesImgSrc = Regex.Matches(htmlSource, regexImgSrc, RegexOptions.IgnoreCase | RegexOptions.Singleline);
foreach (Match m in matchesImgSrc)
{
string href = m.Groups[1].Value;
links.Add(href);
}
}
In your first example, you should:
array_push($sources, (string) $i['src']);
Your second example gives an array of strings because you are converting the SimpleXMLElements to strings using the (string) cast. In your first example you are not, so you get an array of SimpleXMLElements instead.

Referencing a dynamic associative array position

I'm requesting data from an online source which I then decode into json StdClass objects (using php). Once I've done this I have the following (see below). I'm trying to extract the elements in 'otherstuff' by doing echo $response->stuff->WHAT GOES HERE?->otherstuff
However I cant hard code the [2010-12] because its a date, is there any way I can call e.g. $response->stuff->nextsibling->stuff
I hope this makes sense to someone :D Currently i'm bastardising this with a $key => $value for loop and extracting the key value and using it in my $response->stuff->$key->stuff call.
stdClass Object
(
[commentary] =>
[stuff] => stdClass Object
(
**[2010-12]** => stdClass Object
(
[otherstuff] => stdClass Object
(
[otherstuffrate] => 1
[otherstufflevel] => 1
[otherstufftotal] => 1
)
)
)
)
StdClass instances can be used with some Array Functions, among them
current — Return the current element in an array and
key — Fetch a key from an array
So you can do (codepad)
$obj = new StdClass;
$obj->{"2012-10"} = 'foo';
echo current($obj); // foo
echo key($obj); // 2012-10
On a sidenote, object properties should not start with a number and they may not contain dashes, so instead of working with StdClass objects, pass in TRUE as the second argument to json_decode. Returned objects will be converted into associative arrays then.
The date key must be a string, otherwise PHP breaks ;).
echo $response->stuff['2010-12']->otherstuff
Retrieve it using a string.
Edited again: added object code also
json decode it as associative array, and use key fetched through array_keys . See it work here : http://codepad.org/X8HCubIO
<?php
$str = '{
"commentary" : null,
"stuff" : {
"ANYDATE" : {
"otherstuff": {
"otherstuffrate" : 1,
"otherstufflevel" : 1,
"otherstufftotal" : 1
}
}
}
}';
$obj = json_decode($str,true);
$reqKey = array_keys($obj["stuff"]);
$req = $obj["stuff"][$reqKey[0]]["otherstuff"];
print_r($req);
print "====================as object ============\n";
$obj = json_decode($str);
$req = current($obj->stuff)->otherstuff;
print_r($req);
?>

Categories