Here's my problem. I want to create a function that takes an outside variable that contains an xpath and once the function runs I want to add to that same variable to create a counter.
So I have the outside variable:
$node = $xmlDoc->xpath('//a:Order');
Then the function with a single argument that will take the outside variable ($node). Like so:
function loopXML($node) {
i=1; //counter variable
}
Now I want to add a counter to $node so that it goes through all of the children of "Order". Outside of the function, I would use:
$child = $xmlDoc->xpath('//a:Order['.$i.']/*');
But inside of the function, I have no idea how to concat it. Does anyone have any idea how I could do this?
EDIT:
Also, it should be noted that I created an arbitrary namespace already:
foreach($xmlDoc->getDocNamespaces() as $strPrefix => $strNamespace) {
if(strlen($strPrefix)==0) {
$strPrefix="a"; //Assign an arbitrary namespace prefix.
}
$xmlDoc->registerXPathNamespace($strPrefix,$strNamespace);
}
SimpleXMLElement::xpath() uses the node associated with the SimpleXML element as the context so you can do something like:
foreach ($xmlDoc->xpath('//a:Order') as $order) {
foreach ($order->xpath('*') as $field) {
...
}
}
But SimpleXMLElement::children() is a list of the element child nodes so it returns the same as the Xpath expression * or to be more exact '*[namespace-uri == ""]'. The first argument is the namespace of the children you would like to fetch.
foreach ($xmlDoc->xpath('//a:Order') as $order) {
foreach ($order->children() as $field) {
...
}
}
This can be easily refactored into a function.
function getRecord(SimpleXMLelement $order, $namespace) {
$result = [];
foreach ($order->children($namespace) as $field) {
$result[$field->getName()] = (string)$field;
}
return $result;
}
You should always depend on the actual namespace, never on the prefix. Prefixes can change and are optional.
Put all together:
$xml = <<<'XML'
<a:orders xmlns:a="urn:a">
<a:order>
<a:foo>bar</a:foo>
<a:answer>42</a:answer>
</a:order>
</a:orders>
XML;
$namespace = 'urn:a';
$orders = new SimpleXMLElement($xml);
$orders->registerXpathNamespace('a', $namespace);
function getRecord(SimpleXMLelement $order, $namespace = NULL) {
$result = [];
foreach ($order->children($namespace) as $field) {
$result[$field->getName()] = (string)$field;
}
return $result;
}
foreach ($orders->xpath('//a:order') as $order) {
var_dump(getRecord($order, $namespace));
}
Output:
array(2) {
["foo"]=>
string(3) "bar"
["answer"]=>
string(2) "42"
}
So I figured it out with a lot of Googling and the help of ThW. So to all that helped, thank you. Here's how I got it to work:
$orderPNode = '//a:Order';
$amazonRawXML = 'AmazonRaw.xml';
$amazonRawCSV = 'AmazonRaw.csv';
function loopXML($xmlDoc, $node, $writeCsv) {
$i = 1;
$xmlDocs = simplexml_load_file($xmlDoc);
$result = [];
foreach($xmlDocs->getDocNamespaces() as $strPrefix => $strNamespace) {
if(strlen($strPrefix)==0) {
$strPrefix="a"; //Assign an arbitrary namespace prefix.
}
$xmlDocs->registerXPathNamespace($strPrefix,$strNamespace);
}
file_put_contents($writeCsv, ""); // Clear contents of csv file after each go
$nodeP = $xmlDocs->xpath($node);
foreach ($nodeP as $n) {
$nodeC = $xmlDocs->xpath($node.'['.$i.']/*');
if($nodeC) {
foreach ($nodeC as $value) {
$values[] = $value;
}
$write = fopen($writeCsv, 'a');
fputcsv($write, $values);
fclose($write);
$values = [];
$i++;
} else {
$result[] = $n;
$i++;
}
}
return $result;
}
loopXML($amazonRawXML, $orderPNode, $amazonRawCSV);
Related
I have links with a different paths, and trying retrieve data from those links. So I don't want to do it separate. Made a query list, and used foreach on that list.
function passPath($list){
$list = [
"//li[#class='out']/a[1]",
"//ul[#class='ul right_ul clearfix']/li[2]/a",
"//ul[#class='ul right_ul clearfix']/li[2]/a"
];
foreach($list as $val){
return $val;
}
}
Then used that function inside DOMXpath's query.
function getPath($urls){
foreach($urls as $k => $val){
$url = $urls;
$html = content($val);
$path = new \DOMXPath($html);
$xPath = passPath($val);
$route = $path->query($xPath);
foreach($route as $value){
if ($value->nodeValue != false) {
$urls [] = trim($value->getAttribute('href'));
unset($urls[$k]);
}
}
}
return array_unique($urls);
}
it's running without an error. But there is foreach problem here. because it's just retrieving one element's data. not keep going other elements... What I am missing here?
$data = getPath($urls)
var_dump($data)
by the way: content() is file_get_content/loadHTML function.
I changed your code for earning list of href.
# You want to parse all pages using url list. So you created function named `getPath($urls)`.
function getPath($urls) {
# I suggest you'd rather declare $ret for storing values to return.
$ret = [];
# Using foreach, you can parse all url.
foreach ($urls as $k => $url) { # $val is url value of $urls. And I changed $val to $url.
# content() is file_get_content/loadHTML function.
$html = content($url);
# Create new DOMXPath object using $html.
$path = new \DOMXPath($html);
# This function is not required.
# By the way, second element and third element of $xPathList are equal. I think the third element is not required.
// $xPath = passPath($url);
$xPathList = [
"//li[#class='out']/a[1]",
"//ul[#class='ul right_ul clearfix']/li[2]/a",
"//ul[#class='ul right_ul clearfix']/li[2]/a"
];
foreach ($xPathList as $xPath) {
$nodes = $path->query($xPath);
foreach ($nodes as $node) {
if ($node->nodeValue != false) {
$ret[] = trim($node->getAttribute('href'));
}
}
}
}
return array_unique($ret);
}
$data = getPath($urls);
var_dump($data);
Why am I getting this PHP Warning?
Invalid argument supplied for foreach()
Here is my code:
// look for text file for this keyword
if (empty($site["textdirectory"])) {
$site["textdirectory"] = "text";
}
if (file_exists(ROOT_DIR.$site["textdirectory"].'/'.urlencode($q).'.txt')) {
$keywordtext =
file_get_contents(ROOT_DIR.$site["textdirectory"].'/'.urlencode($q).'.txt');
}
else {
$keywordtext = null;
}
$keywordsXML = getEbayKeywords($q);
foreach($keywordsXML->PopularSearchResult as $item) {
$topicsString = $item->AlternativeSearches;
$relatedString = $item->RelatedSearches;
if (!empty($topicsString)) {
$topics = split(";",$topicsString);
}
if (!empty($relatedString)) {
$related = split(";",$relatedString);
}
}
$node = array();
$node['keywords'] = $q;
2
$xml = ebay_rss($node);
$ebayItems = array();
$totalItems = count($xml->channel->item);
$totalPages = $totalItems / $pageSize;
$i = 0;
foreach ($xml->channel->item as $item) {
$ebayRss =
$item->children('http://www.ebay.com/marketplace/search/v1/services');
if ($i>=($pageSize*($page-1)) && $i<($pageSize*$page)) {
$newItem = array();
$newItem['title'] = $item->title;
$newItem['link'] = buyLink($item->link, $q);
$newItem['image'] = ebay_stripImage($item->description);
$newItem['currentbid'] = ebay_convertPrice($item->description);
$newItem['bidcount'] = $ebayRss->BidCount;
$newItem['endtime'] = ebay_convertTime($ebayRss->ListingEndTime);
$newItem['type'] = $ebayRss->ListingType;
if (!empty($ebayRss->BuyItNowPrice)) {
$newItem['bin'] = ebay_convertPrice($item->description);
}
array_push($ebayItems, $newItem);
}
$i++;
}
$pageNumbers = array();
for ($i=1; $i<=$totalPages; $i++) {
array_push($pageNumbers, $i);
}
3
// get user guides
$guidesXML = getEbayGuides($q);
$guides = array();
foreach ($guidesXML->guide as $guideXML) {
$guide = array();
$guide['url'] = makeguideLink($guideXML->url, $q);
$guide['title'] = $guideXML->title;
$guide['desc'] = $guideXML->desc;
array_push($guides,$guide);
}
What causes this warning?
You should check that what you are passing to foreach is an array by using the is_array function
If you are not sure it's going to be an array you can always check using the following PHP example code:
if (is_array($variable)) {
foreach ($variable as $item) {
//do something
}
}
This means that you are doing a foreach on something that is not an array.
Check out all your foreach statements, and look if the thing before the as, to make sure it is actually an array. Use var_dump to dump it.
Then fix the one where it isn't an array.
How to reproduce this error:
<?php
$skipper = "abcd";
foreach ($skipper as $item){ //the warning happens on this line.
print "ok";
}
?>
Make sure $skipper is an array.
Because, on whatever line the error is occurring at (you didn't tell us which that is), you're passing something to foreach that is not an array.
Look at what you're passing into foreach, determine what it is (with var_export), find out why it's not an array... and fix it.
Basic, basic debugging.
Try this.
if(is_array($value) || is_object($value)){
foreach($value as $item){
//somecode
}
}
I would like to split an array:
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
based on the color attribute of each item, and fill corresponding sub arrays
$a = array("green", "yellow", "blue");
function isGreen($var){
return($var->color == "green");
}
$greens = array_filter($o, "isGreen");
$yellows = array_filter($o, "isYellow");
// and all possible categories in $a..
my $a has a length > 20, and could increase more, so I need a general way instead of writing functions by hand
There doesn't seem to exist a function array_split to generate all filtered arrays
or else I need a sort of lambda function maybe
You could do something like:
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$greens = array_filter($o, function($item) {
if ($item->color == 'green') {
return true;
}
return false;
});
Or if you want to create something really generic you could do something like the following:
function filterArray($array, $type, $value)
{
$result = array();
foreach($array as $item) {
if ($item->{$type} == $value) {
$result[] = $item;
}
}
return $result;
}
$o = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$greens = filterArray($o, 'color', 'green');
$yellows = filterArray($o, 'color', 'yellow');
In my second example you could just pass the array and tell the function what to filter (e.g. color or some other future property) on based on what value.
Note that I have not done any error checking whether properties really exist
I would not go down the road of creating a ton of functions, manually or dynamically.
Here's my idea, and the design could be modified so filters are chainable:
<?php
class ItemsFilter
{
protected $items = array();
public function __construct($items) {
$this->items = $items;
}
public function byColor($color)
{
$items = array();
foreach ($this->items as $item) {
// I don't like this: I would prefer each item was an object and had getColor()
if (empty($item->color) || $item->color != $color)
continue;
$items[] = $item;
}
return $items;
}
}
$items = json_decode('[{"id":"1","color":"green"},{"id":"2","color":"green"},{"id":"3","color":"yellow"},{"id":"4","color":"green"}]');
$filter = new ItemsFilter($items);
$greens = $filter->byColor('green');
echo '<pre>';
print_r($greens);
echo '</pre>';
If you need more arguments you could use this function:
function splitArray($array, $params) {
$result = array();
foreach ($array as $item) {
$status = true;
foreach ($params as $key => $value) {
if ($item[$key] != $value) {
$status = false;
continue;
}
}
if ($status == true) {
$result[] = $item;
}
}
return $result;
}
$greensAndID1 = splitArray($o, array('color' => 'green', 'id' => 1));
Let's assume we want to process this Feed: http://tools.forestview.eu/xmlp/xml_feed.php?aid=1094&cid=1000
I'm trying to show the nodes of an XML file this way:
deals->deal->dealsite
deals->deal->deal_id
deals->deal->deal_title
This is in order to be able to process feeds that we don't know what their XML tags are. So we will let the user choose that deals->deal->deal_title is the Deal Title and will recognize it that way.
I have been trying ages to do this with this code:
class HandleXML {
var $root_tag = false;
var $xml_tags = array();
var $keys = array();
function parse_recursive(SimpleXMLElement $element)
{
$get_name = $element->getName();
$children = $element->children(); // get all children
if (empty($this->root_tag)) {
$this->root_tag = $this->root_tag.$get_name;
}
$this->xml_tags[] = $get_name;
// only show children if there are any
if(count($children))
{
foreach($children as $child)
{
$this->parse_recursive($child); // recursion :)
}
}
else {
$key = implode('->', $this->xml_tags);
$this->xml_tags = array();
if (!in_array($key, $this->keys)) {
if (!strstr('>', $key) && count($this->keys) > 0) { $key = $this->root_tag.'->'.$key; }
if (!in_array($key, $this->keys)) {
$this->keys[] = $key;
}
}
}
}
}
$xml = new SimpleXMLElement($feed_url, null, true);
$handle_xml = new HandleXML;
$handle_xml->parse_recursive($xml);
foreach($handle_xml->keys as $key) {
echo $key.'<br />';
}
exit;
but here's what I get instead:
deals->deal->dealsite
deals->deal_id
deals->deal_title
See on 2nd and 3rd line the deal-> part is missing.
I have also tried with this code: http://pastebin.com/FkPWXF64 but it's definitely not the best way to go and it doesn't always work.
No matter how many times I couldn't do it.
In one of my sites I use a little different approach to handle xml feed. In your case it would look like:
$xml = simplexml_load_file("http://tools.forestview.eu/xmlp/xml_feed.php?aid=1094&cid=1000");
foreach($xml->{'deal'} as $deal)
{
$dealsite = $deal->{'dealsite'};
$dael_id = $deal->{'dael_id'};
$deal_title = $deal->{'deal_title'};
$deal_url = $deal->{'deal_url'};
$deal_city = $deal->{'deal_city'};
$deal_category = $deal->{'deal_category'};
// and so on for the rest
// do some stuff with the variables like insert into MySQL
}
Why am I getting this PHP Warning?
Invalid argument supplied for foreach()
Here is my code:
// look for text file for this keyword
if (empty($site["textdirectory"])) {
$site["textdirectory"] = "text";
}
if (file_exists(ROOT_DIR.$site["textdirectory"].'/'.urlencode($q).'.txt')) {
$keywordtext =
file_get_contents(ROOT_DIR.$site["textdirectory"].'/'.urlencode($q).'.txt');
}
else {
$keywordtext = null;
}
$keywordsXML = getEbayKeywords($q);
foreach($keywordsXML->PopularSearchResult as $item) {
$topicsString = $item->AlternativeSearches;
$relatedString = $item->RelatedSearches;
if (!empty($topicsString)) {
$topics = split(";",$topicsString);
}
if (!empty($relatedString)) {
$related = split(";",$relatedString);
}
}
$node = array();
$node['keywords'] = $q;
2
$xml = ebay_rss($node);
$ebayItems = array();
$totalItems = count($xml->channel->item);
$totalPages = $totalItems / $pageSize;
$i = 0;
foreach ($xml->channel->item as $item) {
$ebayRss =
$item->children('http://www.ebay.com/marketplace/search/v1/services');
if ($i>=($pageSize*($page-1)) && $i<($pageSize*$page)) {
$newItem = array();
$newItem['title'] = $item->title;
$newItem['link'] = buyLink($item->link, $q);
$newItem['image'] = ebay_stripImage($item->description);
$newItem['currentbid'] = ebay_convertPrice($item->description);
$newItem['bidcount'] = $ebayRss->BidCount;
$newItem['endtime'] = ebay_convertTime($ebayRss->ListingEndTime);
$newItem['type'] = $ebayRss->ListingType;
if (!empty($ebayRss->BuyItNowPrice)) {
$newItem['bin'] = ebay_convertPrice($item->description);
}
array_push($ebayItems, $newItem);
}
$i++;
}
$pageNumbers = array();
for ($i=1; $i<=$totalPages; $i++) {
array_push($pageNumbers, $i);
}
3
// get user guides
$guidesXML = getEbayGuides($q);
$guides = array();
foreach ($guidesXML->guide as $guideXML) {
$guide = array();
$guide['url'] = makeguideLink($guideXML->url, $q);
$guide['title'] = $guideXML->title;
$guide['desc'] = $guideXML->desc;
array_push($guides,$guide);
}
What causes this warning?
You should check that what you are passing to foreach is an array by using the is_array function
If you are not sure it's going to be an array you can always check using the following PHP example code:
if (is_array($variable)) {
foreach ($variable as $item) {
//do something
}
}
This means that you are doing a foreach on something that is not an array.
Check out all your foreach statements, and look if the thing before the as, to make sure it is actually an array. Use var_dump to dump it.
Then fix the one where it isn't an array.
How to reproduce this error:
<?php
$skipper = "abcd";
foreach ($skipper as $item){ //the warning happens on this line.
print "ok";
}
?>
Make sure $skipper is an array.
Because, on whatever line the error is occurring at (you didn't tell us which that is), you're passing something to foreach that is not an array.
Look at what you're passing into foreach, determine what it is (with var_export), find out why it's not an array... and fix it.
Basic, basic debugging.
Try this.
if(is_array($value) || is_object($value)){
foreach($value as $item){
//somecode
}
}