I'm generating an XML document with PHP and the elements have to be in a specific order. Most of them work fine, with the exception of three. The child elements looks like this:
<p>
<co>ABC</co>
<nbr>123456</nbr>
<name>short product description</name>
<desc>long product description</desc>
<kw>searchable keywords</kw>
<i-std>relative/path/to/image</i-std>
<i-lg>relative/path/to/large/image</i-lg>
<i-thmb>relative/path/to/thumbnail</i-thmb>
<mfg>manufacturer</mfg>
<a-pckCont>package contents</a-pckCont>
</p>
The code I'm using works fine, but the three image elements are out of order, which makes the content processor that consumes them choke. What I've tried most lately is this:
$newStd = 0;
foreach ($items as $row => $innerArray) {
$p = $domTree->createElement('p');
$xmlRoot->appendChild($p);
foreach ($innerArray as $innerRow => $value) {
if ($innerRow != 'key') {
if ($value != '') {
echo $innerRow . ' : ' . $value . '<br />';
if ($innerRow == 'i-std') {
$newStd = $domTree->createElement($innerRow, htmlspecialchars($value));
} else {
$p->appendChild($domTree->createElement($innerRow, htmlspecialchars($value)));
}
}
}
if ($newStd != 0) {
$thmb = $p->getElementsByTagName('i-thmb')->item(0);
$p->insertBefore($newStd, $thmb);
}
}
}
My thought was to have it write out all child elements before I have it write the element in using InsertBefore to ensure it appears before the i-thmb element, but it didn't make a difference. No matter what I do, the output I get has them in the order i-thmb, i-std, i-lg. All other elements appear in the proper order, after rearranging some of the variables in the arrays used to build the XML document. I haven't attempted to control the i-lg element yet, since i-std isn't working.
Ultimately, this will be used to combine to XML documents together, but in testing to be sure the XML processor wasn't going to choke, I found the fundamental issue is that the order of elements largely determines if it will work or not (the system I'm working with is undocumented and support is poor, to say the least).
Edit to add: echoing as I do in the inner foreach loop shows them in the correct order but they're out of order in the output file.
I think a couple of changes would make the code more robust (I've added comments to the code for specifics). This mainly involves checking of data types and resetting the replacement field in each loop...
foreach ($items as $row => $innerArray) {
$p = $domTree->createElement('p');
$xmlRoot->appendChild($p);
foreach ($innerArray as $innerRow => $value) {
$newStd = 0; // Make sure this is set each time
if ($innerRow != 'key') {
if ($value != '') {
echo $innerRow . ' : ' . $value . '<br />';
if ($innerRow == 'i-std') {
$newStd = $domTree->createElement($innerRow, htmlspecialchars($value));
} else {
$p->appendChild($domTree->createElement($innerRow, htmlspecialchars($value)));
}
}
}
// Check if a replacement element, checking type of element
if ($newStd instanceof DOMElement) {
$thmb = $p->getElementsByTagName('i-thmb')->item(0);
$p->insertBefore($newStd, $thmb);
}
}
}
Related
So a bit of background information is I'm creating a web app and I have 50~ arrays that I'm currently using what I get from an API, I've created a script to find the arrays that I don't need lets call them "bad arrays" but the problem is I'm unsure how I can filter these arrays out with the method I'm using to search through them
I'm searching through them with this script
$tagItems = [];
foreach($tags['items'] as $item) {
if (!$item['snippet']['tags'] || !is_array($item['snippet']['tags'])) {
continue;
}
foreach($item['snippet']['tags'] as $tag) {
$tag = strtolower($tag);
if (!isset($tagItems[$tag])) {
$tagItems[$tag] = 0;
}
$tagItems[$tag]++;
}
}
But let's say I didn't want it to include the 8th array and the 15th array
$tags['items'][8]['snippet']['tags'];
$tags['items'][15]['snippet']['tags'];
I want these to be removed from the original $tags array. How can i achieve this?
EDIT: This needs to be dynamic. I do not know if there are going to be 45/50 arrays that will need removing or just 2/50. the array that needs removing can be reffered to as $index
I have a script which determines what array(s) need to be removed
$i = 0;
while ($i <= 50) {
$x = 0;
while ($x <= 50) {
if ($tags['items'][$i]['snippet']['channelId'] == $tags['items'][$x]['snippet']['channelId']) {
if ($x < $i) {
break;
} else {
echo $x.", ";
break;
}
}
$x++;
}
$i++;
}
I'm going to edit this a little more to provide some extra information that may be useful. My overall goal is to use the YouTube API to remove all but the first array of tags where the channel id appears multiple times. I'm using a script which finds all the array numbers that dont need to be removed an URL.
You can check for the array key
$tagItems = [];
$toRemove = array(8,15);
foreach($tags['items'] as $key => $item) {
if(in_array($key,$toRemove)){
continue;
}
if (!$item['snippet']['tags'] || !is_array($item['snippet']['tags'])) {
continue;
}
foreach($item['snippet']['tags'] as $tag) {
$tag = strtolower($tag);
if (!isset($tagItems[$tag])) {
$tagItems[$tag] = 0;
}
$tagItems[$tag]++;
}
}
Hi I have a PHP array with a variable number of keys (keys are 0,1,2,3,4.. etc)
I want to process the first value differently, and then the rest of the values the same.
What's the best way to do this?
$first = array_shift($array);
// do something with $first
foreach ($array as $key => $value) {
// do something with $key and $value
}
I would do this:
$firstDone = FALSE;
foreach ($array as $value) {
if (!$firstDone) {
// Process first value here
$firstDone = TRUE;
} else {
// Process other values here
}
}
...but whether that is the best way is debatable. I would use foreach over any other method, because then it does not matter what the keys are.
Here is one way:
$first = true;
foreach($array as $key => $value) {
if ($first) {
// something different
$first = false;
}
else {
// regular logic
}
}
$i = 0;
foreach($ur_array as $key => $val) {
if($i == 0) {
//first index
}
else {
//do something else
}
$i++;
}
I would do it like this if you're sure the array contains at least one entry:
processFirst($myArray[0]);
for ($i=1; $i<count($myArray); $1++)
{
processRest($myArray[$i]);
}
Otherwise you'll need to test this before processing the first element
I've made you a function!
function arrayCallback(&$array) {
$callbacks = func_get_args(); // get all arguments
array_shift($callbacks); // remove first element, we only want the callbacks
$callbackindex = 0;
foreach($array as $value) {
// call callback
$callbacks[$callbackindex]($value);
// make sure it keeps using last callback in case the array is bigger than the amount of callbacks
if(count($callbacks) > $callbackindex + 1) {
$callbackindex++;
}
}
}
If you call this function, it accepts an array and infinite callback arguments. When the array is bigger than the amount of supplied functions, it stays at the last function.
You can simply call it like this:
arrayCallback($array, function($value) {
print 'callback one: ' . $value;
}, function($value) {
print 'callback two: ' . $value;
});
EDIT
If you wish to avoid using a function like this, feel free to pick any of the other correct answers. It's just what you prefer really. If you're repeatedly are planning to loop through one or multiple arrays with different callbacks I suggest to use a function to re-use code. (I'm an optimisation freak)
In php I am converting posted data from a form to objects like this:
<?php
...some code...
$post = new stdClass;
foreach ($_POST as $key => $val)
$post->$key = trim(strip_tags($_POST[$key]));
?>
Then in my page I just echo posted data like this :
<?php echo $post->Name; ?>
<?php echo $post->Address; ?>
etc...
This works fine but I have multiple checkboxes that are part of a group and I echo the results of that, like this:
<?php
$colors = $_POST['color_type'];
if(empty($colors))
{
echo("No color Type Selected.");
}
else
{
$N = count($colors);
for($i=0; $i < $N; $i++)
{
echo($colors[$i] . ", ");
}
}
?>
That works when I am just using array, but how do I write this as object syntax?
using your code
function array_to_object($arr) {
$post = new stdClass;
foreach ($arr as $key => $val) {
if(is_array($val)) {
$post->$key = post_object($val);
}else{
$post->$key = trim(strip_tags($arr[$key]));
}
}
return $post;
}
$post = array_to_object($_POST);
or more complex solution
function arrayToObject($array) {
if(!is_array($array)) {
return $array;
}
$object = new stdClass();
if (is_array($array) && count($array) > 0) {
foreach ($array as $name=>$value) {
$name = strtolower(trim($name));
if (!empty($name)) {
$object->$name = arrayToObject($value);
}
}
return $object;
}
else {
return FALSE;
}
}
from http://www.richardcastera.com/blog/php-convert-array-to-object-with-stdclass
why would you want that? What's wrong with an array?
Use Object Oriented Programming, which might be what you are looking for. Treat it as an object, by making a class called Color and doing $colors[$i] = new Color();
This way you can do whatever you want with it, and add functions to it.
Pretty simple -- when you attach the color_type key to your object, it'll become an array that's a property of your object. This is most likely what you want: you probably won't want to turn that array into its own stdClass-based object, because then you won't be able to iterate through all the values (as easily). Here's a snippet:
<?php
// putting in both of these checks prevents you from throwing an E_WARNING
// for a non-existent property. E_WARNINGs aren't dangerous, but it makes
// your error messages cleaner when you don't have to wade through a bunch
// of E_WARNINGS.
if (!isset($post->color_type) || empty($post->color_type)) {
echo 'No colour type selected.'; // apologies for the Canadian spelling!
} else {
// this loop does exactly the same thing as your loop, but it makes it a
// bit more succinct -- you don't have to store the count of array values
// in $N. Bit of syntax that speeds things up!
foreach ($post->color_type as $thisColor) {
echo $thisColor;
}
}
?>
Hope this helps! Of course, in a real-life setting, you'll want to do all sorts of data validation and cleaning -- for instance, you'll want to check that the browser actually passed an array of values for $_POST['color_type'], and you'll want to clean the output in case someone is trying to inject an exploit into your page (by going echo htmlspecialchars($thisColor); -- this turns all characters like < and > into HTML entities so they can't insert JavaScript code).
I wan't to load some data from my XML file using this function:
public function getElements()
{
$elements = array();
$element = $this->documentElement->getElementsByTagName('elements')->item(0);
// checks if it has any immunities
if( isset($element) )
{
// read all immunities
foreach( $element->getElementsByTagName('element') as $v)
{
$v = $v->attributes->item(0);
// checks if immunity is set
if($v->nodeValue > 0)
{
$elements[$v->nodeName] = $v->nodeValue;
}
}
}
return $elements;
}
I wan't to load that elements from my XML file:
<elements>
<element physicalPercent="10"/>
<element icePercent="10"/>
<element holyPercent="-10"/>
</elements>
I wan't to load only element node name and node value.
Got this code in my query loop:
$elements = $monster->getElements();
$elN = 0;
$elC = count($elements);
if(!empty($elements)) {
foreach($elements as $element => $value) {
$elN++;
$elements_string .= $element . ":".$value;
if($elC != $elN)
$elements_string .= ", ";
}
}
And finally - the output of $elements_string variable is wrong:
earthPercent:50, holyPercent:50, firePercent:15, energyPercent:5, physicalPercent:25, icePercent:30, deathPercent:30firePercent:20, earthPercent:75firePercent:20, earthPercent:75firePercent:20, earthPercent:75physicalPercent:70, holyPercent:20, deathPerce
It should rather return:
physicalPercent:10, icePercent:10, holyPercent:-10
Could you help me one more time?:)
Thank you in advance.
Well the XML-Parser doesn't magically know which elements you want to load and which you won't - you have to filter this by yourself. Then you have to decide where you want to filter your desired elements in the getElements-function you posted or in your "query loop" as you call it.
Should the getElements be some kind of general function which must return all elements? Then you should change that check if($v->nodeValue > 0) to something like if(!empty($v->nodeValue)) otherwise you wont get the "holyPercent" value since this is negative (and the old expression becomes false).
Then in your "query loop", just select your desired elements:
foreach($elements as $element => $value) {
if(in_array($element, array("physicalPercent", "icePercent", "holyPercent"))) {
$elN++;
$elements_string .= $element . ":".$value;
if($elC != $elN)
$elements_string .= ", ";
}
}
Just:
$xml = new SimpleXMLElement($xmlfile);
And then:
for($i=1;$i<Count($xml->elements);$i++)
echo $xml->elements[$i][0];
Didn't try if it works with [0], usually i use :
echo $xml->elements[$i]['attributename'];
I am trying to import a tab delimited file after upload. The meat of this is done with the following function. I'm trying to build an array of class instances. The code follows:
Import Function
$AddedProducts;
function importList($filename)
{
global $AddedProducts;
$AddedProducts=array();
$fileHandle = fopen($filename, "r");
$currentProduct = new productImport();
$line=fgets($fileHandle); $line=fgets($fileHandle); //throw away top 2 lines
echo '<hr>';
while(true)
{
$line = fgets($fileHandle);
if($line == null) break;
$cells=explode(' ', $line);
$i=0;
foreach($currentProduct as $ProductProperty)
{
if(isset($cells[$i]))
{
$ProductProperty = $cells[$i];
echo $i . '. ' . $cells[$i] . "<br>";
}
else return false;
$i++;
}
echo "<hr>";
$AddedProducts[]=$currentProduct;
}
fclose($fileHandle);
return true;
}
Array Output
<?
$i=0;
foreach($AddedProducts as $AddedProduct)
{
$i++;
echo "<hr>" . $i . "<br>";
foreach($AddedProduct as $key=>$value)
{
echo $key . ' = ' . $value . '<br>';
}
}
?>
Breakdown of Known Info
The final array length/size is correct. (Should be lines in file - 2)
It doesn't particularly matter how many properties are in the productImport class so long as it equates to the same number of tabs per line in the file being read.
importList function echos proper values for $cells[$i] which are the same values I'm missing in the array output.
The problem seems to be either the values aren't being assigned to the properties or the properties are not being read. I'm not sure of why either would be the case but I assume it is because PHP is not my primary language and likely something obvious about the foreach loops ;)
I'm using PHP v5.2.6
What's wrong with this code?
Answer:
foreach($currentProduct as $ProductProperty) becomes
foreach($currentProduct as &$ProductProperty)
I think the problem is in this section:
foreach($currentProduct as $ProductProperty)
{
if(isset($cells[$i]))
{
$ProductProperty = $cells[$i]; /* this seems to be the problem */
echo $i . '. ' . $cells[$i] . "<br>";
}
else return false;
$i++;
}
According to the php manual, Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. so the value you assign is discarded after the loop.
Edit: Apart from that, you are looping through object properties and although the manual does not explicitly state it, it seems you need foreach($class as $key => $value) instead of just foreach($class as $value)
In your foreach loops, the assigned variables such as $ProductProperty are not references, therefore they will not actually affect anything outside the loop.
i.e. $ProductProperty = $cells[$i] only affects the current iteration.
In addition to what the others are saying, it seems that you are attempting to insert property data to the same object every time, since you're not creating any new productImport instances in the loop.