DOMDocument returns empty data - php

I am not getting into this if loop. I have a copy of the code exactly on another virtual server with the same PHP version and this gets in the loop and returns the result from CURL using a webpage as a resource. Is there something I am missing that is wrong with the code snip (I am trying to be succinct, but there is more code). Any related tips would be helpful.
class News extends General
{
public $CU;
// Constructor
public function __construct()
{
$this->CU = new CURL();
$dom = new DOMDocument();
#$dom->loadHTML($this->CU->Response['Body']);
$xpath = new DOMXPath($dom);
}
$result_list = $xpath->query("//div[contains(#class, 's-search-results')]//div[contains(#class, 's-result-item')]//h2//a");
$price_list = $xpath->query("//div[contains(#class, 's-search-results')]//div[contains(#class, 's-result-item')]//span[contains(#data-a-color, 'base')]//span[contains(#class, 'a-offscreen')]");
if($result_list->length > 0 && $result_list->length==$price_list->length)
{
ECHO "In the loop";
}
}

Related

PHP DOMDocument: How to pull two(2) values from a Webpage when one value doesnt have an ID value

I would like to pull two values from a web document. The values are api_key and authenticity_token. I have written a function but the problem is it only pulls out the api_key. Can someone help me with the other piece of this function that will enable me to pull out the authenticity_token, THANKS:
Here’s my code for my function:
<?php
class apiKey{
public $apikey;
public $authenticity_token;
function getApiKey(){
//Get api_key and pass it in
$apikey = null;
$doc = new DOMDocument();
$semcatStartUrl = 'https://entryform.semcat.net/1800stonewall';
libxml_use_internal_errors(true);
$doc->loadHTMLFile("$semcatStartUrl");
libxml_clear_errors();
$apikey = $doc->getElementById('api_key')->getAttribute('value');
return $apikey;
}
function getAuthenticityToken(){
//My guesstimate this doesn’t work because the form hasn’t been / //submitted yet
$authenticity_token = $_POST['authenticity_token'];
return $authenticity_token;
}
}
?>
Any help would be greatly appreciated. Thanks Again!
The authenticity token is contained in this element:
<input name="authenticity_token" type="hidden" value="..." />
Unfortunately, as you can see, this element has no ID attribute and therefore getting at it will be slightly more complicated.
$xpath = new DOMXPath($doc);
$input = $xpath->query("input[#name=authenticity_token]")->item(0)->getAttribute("value");
Ideally you'll want some error checking in there to make sure the element exists, but this code should work fine... provided it is in the same place as your existing "get API key" function, because it uses $doc.
Here is the complete working code:
<?php
class apiKey{
public $apikey;
public $authenticity_token;
function getApiKey(){
//Get api_key and pass it in
$apikey = null;
$authenticity_token = null;
$doc = new DOMDocument();
$semcatStartUrl = 'https://entryform.semcat.net/1800stonewall';
libxml_use_internal_errors(true);
$doc->loadHTMLFile("$semcatStartUrl");
libxml_clear_errors();
$apikey = $doc->getElementById('api_key')->getAttribute('value');
//Original 2 Lines
//$xpath = new DOMXPath($doc);
//$input = $xpath->query("input[#name=authenticity_token]")->item(0)->getAttribute("value");
//Modified 2 Lines and added 2 Lines
$xpath = new DOMXPath($doc);
$input = $xpath->query("//input[#name='authenticity_token']");
$authenticity_token = $input->item(0)->getAttribute('value');
$this->authenticity_token = $authenticity_token;
return $apikey;
}
function getAuthenticityToken(){
return $this->authenticity_token;
}
}
?>
Works like a great programmer! Thanks for the Help. It really help point me in the right direction which after doing a little research and testing I was able to make some slight changes to your code to get it successfully working, error free.

How to traverse child elements by xpath in DomNodeList?

I have this PHP script:
<?php
libxml_use_internal_errors(true);
/* Createa a new DomDocument object */
$dom = new DomDocument;
$dom_grep = new DomDocument;
/* Load the HTML */
$dom->loadHTMLFile("http://domain.com/catalog/0_1.html");
/* Create a new XPath object */
$xpath = new DomXPath($dom);
/* Query all <table> nodes containing specified class name */
$nodes = $xpath->query("/html/.//table[#class='right']");
/* Set HTTP response header to plain text for debugging output */
header("Content-type: text/plain");
/* How to make Xpath in code below??? */
foreach ($nodes as $i => $node) {
$child[$i]["title"] = $node->query("//tr[#class='bg3']//h3");
$child[$i]["href"] = $node->query("a['href=/catalog/details']");
}
}
?>
But I got this error in result:
"Fatal error: Call to undefined method DOMElement::query()" in $child array
How to make another xpath query in $nodes?
Thank you!

How to serialize/save a DOMElement in $_SESSION?

I'm pretty new to PHP, DOM, and the PHP DOM implementation. What I'm trying to do is save the root element of the DOMDocument in a $_SESSION variable so I can access it and modify it on subsequent page loads.
But I get an error in PHP when using $_SESSION to save state of DOMElement:
Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement
I have read that a PHP DOMDocument object cannot be saved to $_SESSION natively. However it can be saved by saving the serialization of the DOMDocument (e.g. $_SESSION['dom'] = $dom->saveXML()).
I don't know if the same holds true for saving a DOMElement to a $_SESSION variable as well, but that's what I was trying. My reason for wanting to do this is to use an extended class of DOMElement with one additional property. I was hoping that by saving the root DOMElement in $_SESSION that I could later retrieve the element and modify this additional property and perform a test like, if (additionalProperty === false) { do something; }. I've also read that by saving a DOMDocument, and later retrieving it, all elements are returned as objects from native DOM classes. That is to say, even if I used an extended class to create elements, the property that I subsequently need will not be accessible, because the variable holding reference to the extended-class object has gone out of scope--which is why I'm trying this other thing. I tried using the extended class (not included below) first, but got errors...so I reverted to using a DOMElement object to see if that was the problem, but I'm still getting the same errors. Here's the code:
<?php
session_start();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$_SESSION[$rootTag] = new DOMElement($rootTag);
}
$root = $doc->appendChild($_SESSION[$rootTag]);
//$root = $doc->appendChild($doc->importNode($_SESSION[$rootTag], true));
$child = new DOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) echo '<br/>'.$ch->tagName.' '.++$ct;
$_SESSION[$rootTag] = $doc->documentElement;
?>
This code gives the following errors (depending on whether I use appendChild directly or the commented line of code using importNode):
Warning: DOMNode::appendChild() [domnode.appendchild]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 11
Warning: DOMDocument::importNode() [domdocument.importnode]: Couldn't fetch DOMElement in C:\Program Files\wamp_server_2.2\www\test2.php on line 12
I have several questions. First, what is causing this error and how do I fix it? Also, if what I'm trying to do isn't possible, then how can I accomplish my general objective of saving the 'state' of a DOM tree while using a custom property for each element? Note that the additional property is only used in the program and is not an attribute to be saved in the XML file. Also, I can't just save the DOM back to file each time, because the DOMDocument, after a modification, may not be valid according to a schema I'm using until later when additional modificaitons/additions have been performed to the DOMDocument. That's why I need to save a temporarily invalid DOMDocument. Thanks for any advice!
EDITED:
After trying hakre's solution, the code worked. Then I moved on to trying to use an extended class of DOMElement, and, as I suspected, it did not work. Here's the new code:
<?php
session_start();
//$_SESSION = array();
$rootTag = 'root';
$doc = new DOMDocument;
if (!isset($_SESSION[$rootTag])) {
$root = new FreezableDOMElement($rootTag);
$doc->appendChild($root);
} else {
$doc->loadXML($_SESSION[$rootTag]);
$root = $doc->documentElement;
}
$child = new FreezableDOMElement('child_element');
$n = $root->appendChild($child);
$ct = 0;
foreach ($root->childNodes as $ch) {
$frozen = $ch->frozen ? 'is frozen' : 'is not frozen';
echo '<br/>'.$ch->tagName.' '.++$ct.': '.$frozen;
//echo '<br/>'.$ch->tagName.' '.++$ct;
}
$_SESSION[$rootTag] = $doc->saveXML();
/**********************************************************************************
* FreezableDOMElement class
*********************************************************************************/
class FreezableDOMElement extends DOMElement {
public $frozen; // boolean value
public function __construct($name) {
parent::__construct($name);
$this->frozen = false;
}
}
?>
It gives me the error Undefined property: DOMElement::$frozen. Like I mentioned in my original post, after saveXML and loadXML, an element originally instantiated with FreezableDOMElement is returning type DOMElement which is why the frozen property is not recognized. Is there any way around this?
You can not store a DOMElement object inside $_SESSION. It will work at first, but with the next request, it will be unset because it can not be serialized.
That's the same like for DOMDocument as you write about in your question.
Store it as XML instead or encapsulate the serialization mechanism.
You are basically facing three problems here:
Serialize the DOMDocument (you do this to)
Serialize the FreezableDOMElement (you do this to)
Keep the private member FreezableDOMElement::$frozen with the document.
As written, serialization is not available out of the box. Additionally, DOMDocument does not persist your FreezableDOMElement even w/o serialization. The following example demonstrates that the instance is not automatically kept, the default value FALSE is returned (Demo):
class FreezableDOMElement extends DOMElement
{
private $frozen = FALSE;
public function getFrozen()
{
return $this->frozen;
}
public function setFrozen($frozen)
{
$this->frozen = (bool)$frozen;
}
}
class FreezableDOMDocument extends DOMDocument
{
public function __construct()
{
parent::__construct();
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
# own objects do not persist
$doc->documentElement->setFrozen(TRUE);
printf("Element is frozen (should): %d\n", $doc->documentElement->getFrozen()); # it is not (0)
As PHP does not so far support setUserData (DOM Level 3), one way could be to store the additional information inside a namespaced attribute with the element. This can also be serialized by creating the XML string when serializing the object and loading it when unserializing (see Serializable). This then solves all three problems (Demo):
class FreezableDOMElement extends DOMElement
{
public function getFrozen()
{
return $this->getFrozenAttribute()->nodeValue === 'YES';
}
public function setFrozen($frozen)
{
$this->getFrozenAttribute()->nodeValue = $frozen ? 'YES' : 'NO';
}
private function getFrozenAttribute()
{
return $this->getSerializedAttribute('frozen');
}
protected function getSerializedAttribute($localName)
{
$namespaceURI = FreezableDOMDocument::NS_URI;
$prefix = FreezableDOMDocument::NS_PREFIX;
if ($this->hasAttributeNS($namespaceURI, $localName)) {
$attrib = $this->getAttributeNodeNS($namespaceURI, $localName);
} else {
$this->ownerDocument->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . $prefix, $namespaceURI);
$attrib = $this->ownerDocument->createAttributeNS($namespaceURI, $prefix . ':' . $localName);
$attrib = $this->appendChild($attrib);
}
return $attrib;
}
}
class FreezableDOMDocument extends DOMDocument implements Serializable
{
const NS_URI = '/frozen.org/freeze/2';
const NS_PREFIX = 'freeze';
public function __construct()
{
parent::__construct();
$this->registerNodeClasses();
}
private function registerNodeClasses()
{
$this->registerNodeClass('DOMElement', 'FreezableDOMElement');
}
/**
* #return DOMNodeList
*/
private function getNodes()
{
$xp = new DOMXPath($this);
return $xp->query('//*');
}
public function serialize()
{
return parent::saveXML();
}
public function unserialize($serialized)
{
parent::__construct();
$this->registerNodeClasses();
$this->loadXML($serialized);
}
public function saveBareXML()
{
$doc = new DOMDocument();
$doc->loadXML(parent::saveXML());
$xp = new DOMXPath($doc);
foreach ($xp->query('//#*[namespace-uri()=\'' . self::NS_URI . '\']') as $attr) {
/* #var $attr DOMAttr */
$attr->parentNode->removeAttributeNode($attr);
}
$doc->documentElement->removeAttributeNS(self::NS_URI, self::NS_PREFIX);
return $doc->saveXML();
}
public function saveXMLDirect()
{
return parent::saveXML();
}
}
$doc = new FreezableDOMDocument();
$doc->loadXML('<root><child></child></root>');
$doc->documentElement->setFrozen(TRUE);
$child = $doc->getElementsByTagName('child')->item(0);
$child->setFrozen(TRUE);
echo "Plain XML:\n", $doc->saveXML(), "\n";
echo "Bare XML:\n", $doc->saveBareXML(), "\n";
$serialized = serialize($doc);
echo "Serialized:\n", $serialized, "\n";
$newDoc = unserialize($serialized);
printf("Document Element is frozen (should be): %s\n", $newDoc->documentElement->getFrozen() ? 'YES' : 'NO');
printf("Child Element is frozen (should be): %s\n", $newDoc->getElementsByTagName('child')->item(0)->getFrozen() ? 'YES' : 'NO');
It's not really feature complete but a working demo. It's possible to obtain the full XML without the additional "freeze" data.

Problem with dom_import_simplexml on newly created SimpleXML nodes

I'm working on a class extending SimpleXMLElement:
class MyXML extends SimpleXMLElement {
public function cdata($text) {
$node = dom_import_simplexml($this);
$owner = $node->ownerDocument;
$node->appendChild($owner->createCDATASection($text));
return $this;
}
}
Since it's an SimpleXMLElement, I can dynamically create XML nodes inside it:
$xml = new MyXML('<foo/>');
$xml->bar = 'Test';
print $xml->asXML(); // <foo><bar>Test</bar></foo>
But when I try to run this:
$xml = new MyXML('<foo/>');
$xml->bar->cdata('Test');
I get:
Warning: dom_import_simplexml(): Invalid Nodetype to import in [..]
However, if I force the SimpleXMLElement node to be created before running cdata(), it works again:
$xml = new MyXML('<foo/>');
$xml->bar = '';
$xml->bar->cdata('Test');
print $xml->asXML(); // <foo><bar><![CDATA[Test]]></bar></foo>
I'm curious if what I found is a bug, and if there is any way to work around it without "priming" the node first.

PHP: Create new node inside parent

I have a root XML node called and I am trying to add a new child called to this but I am getting errors. Inside there is also children. Here is my code:
$xml = new DomDocument();
$xml->load(X_ASSETS);
$xml->formatOutput = true;
$new_id = $this->getNewAssetId();
// Root
$xpath = new DOMXPath($xml);
$assets = $xpath->query('assets');
$xml_assets = $assets->item(0);
$xml_root = $xml->createElement('asset');
// Asset Name
$xml_name = $xml->createElement('name');
$xml_name->nodeValue = $clean_name;
$xml_root->appendChild($xml_name);
// Asset URL
$xml_url = $xml->createElement('url');
$xml_url->nodeValue = '/'.$name;
$xml_root->appendChild($xml_url);
// Asset ID
$xml_id = $xml->createElement('id');
$xml_id->nodeValue = $new_id;
$xml_root->appendChild($xml_id);
// Create our document and save
$xml_assets->appendChild($xml_root);
$xml->save(X_ASSETS);
I get the following error when running this:
Fatal error: Call to a member function appendChild() on a non-object in /home/websites/zed_x/core/includes/x.inc on line 88
Does anyone know what I am doing wrong here?
Somehow your $xml_assets is not an object, and therefore you cannot call the function:
$xml_assets->appendChild($xml_root);
Are you certain positive that the following command returns an object?
$xml_assets = $assets->item(0);
Test it:
if(is_object($xml_assets))
{
echo "Object Here!";
}
This might be a good way to structure your code so you can catch errors
// .... stuff .....
$xml_assets = $assets->item(0);
// ... more stuff ....
// Check for Object
if(!is_object($xml_assests))
{
die("No Object Created!");
}
$xml_assets->appendChild($xml_root);
$xml->save(X_ASSETS);
// .... more stuff .....

Categories