My Xml looks like this example:
<?xml version="1.0" encoding="UTF-8"?>
<Allvotes>
<vote score="2" ip="116.971.203.221"/>
<vote score="5" ip="32.97.233.5"/>
<vote score="3" ip="212.977.233.225"/>
<vote score="5" ip="2.80.233.225"/>
</Allvotes>
When on my flash website (AS2), somebody press "vote" button, script in PHP getting his IP... What I want is run specyfic function, depends on his IP exist in xml file or not.
If his IP already exist, PHP send message: "ALREADY VOTED!", when IP doesn't exist in XML, then I want to run function which store his vote score and IP in xml.
So far I know that this PHP script not works:
$dom = new DomDocument('1.0', 'UTF-8');
$myXML = "votes.xml";
$s="";
if ($_POST['todo']=="vote"){
$ip=$_SERVER['REMOTE_ADDR'];
$dom->load($myXML);
$allVotes= $dom->getElementsByTagName('vote');
foreach ($allVotes as $vote){
if ($vote->getAttribute('ip')==$ip){
$s.="&msg= Already Voted";
echo $s;
break;
}else{
doOtherStuff
}
}
}
The problem is that this loop fire "doOtherStuff" function when IP is not in first node...
Is there any magic trick to do that?
Why your code does not work
To answer the immediate question: you need to defer the "already voted?" test until you have iterated over all the records:
$alreadyVoted = false;
foreach ($allVotes as $vote){
if ($vote->getAttribute('ip')==$ip){
$alreadyVoted = true;
break;
}
}
if($alreadyVoted) {
$s.="&msg= Already Voted";
echo $s;
}
else {
// other stuff
}
Why you should not do it this way
Storing your data in XML like this is a really inefficient way of doing things. You should move the data store to a database (MySql is typically easiest to set up and work with from PHP).
Related
I am trying to update an XML file if a value passed matches the parent value. Basically once a minute a series of IP Addresses are pinged and the result is passed to my php script like this: http://ipaddress/presence.php?ip=192.168.0.55&present=1.
XML (presence.xml):
<?xml version="1.0" encoding="UTF-8"?>
<Presence>
<Person IP="192.168.0.55">
<Name>Bob</Name>
<Present>1</Present>
<LastSeen>14/09/2014 01:48:43 pm</LastSeen>
</Person>
<Person IP="192.168.0.56">
<Name>John</Name>
<Present>0</Present>
<LastSeen>13/09/2014 08:32:19 pm</LastSeen>
</Person>
</Presence>
PHP (presence.php):
<?php
$ip = $_REQUEST['ip'];
$present = $_REQUEST['present'];
// Write latest data to XML file
$Presence = simplexml_load_file('presence.xml');
foreach ($Presence->xpath('//Person') as $Person) {
if ($Person[#IP] == $ip) {
$Person->Present = $present;
}
else {}
}
?>
The code above I had hoped would update the <Present> tag with the value passed in the URL. Yet nothing happens, I don't get any errors in the log file. I did have the $present value inside the IF statement written in the wrong case initially which did throw an error regarding undefined variable, which indicated the code worked as far as the IF statement.
Any help appreciated, I have looked at various Q & As on Stackoverflow which is how I got this far, but can't figure this out. Once I have cleared this hurdle I will hopefully be able to add the code to update the <LastSeen> tag in the XML.
The url needs to be formatted correctly (replace the $ with &):
http://ipaddress/presence.php?ip=192.168.0.55&present=1
Edit
Make sure you save it after you make modifications
$Presence->asXML('presence.xml');
Try:
foreach ($Presence->Person as $Person) {
if ($Person->attributes()->IP == $ip) {
$Person->Present = $present;
}
}
Also, make sure you're actually getting an XML string (in the format you posted), just in case your input isn't quite what you expect.
Good day i am trying to send an xml to a web service but would need to clean the xml before sending. So far I have tried different ways and now been stuck for a while.
I capture the data from a form and post it to my php file to process. If the user doesnt enter any data in the length/width/height then i would like to clean my xml and remove empty element so it can pass validation on the server where sending xml request too.
Here below is a snippet of the data cpatured from my post and build the xml file accordingly but what if the dimensions were omitted? Could I also clean other elements that are empty?
$xmlRequest = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<mailing-scenario xmlns="http://www.mysite.com/ws/ship/rate-v2">
<customer-number>{$mailedBy}</customer-number>
<parcel-characteristics>
<weight>{$weight}</weight>
<dimensions>
<length>{$length}</length>
<width>{$width}</width>
<height>{$height}</height>
</dimensions>
</parcel-characteristics>
<origin-postal-code>{$originPostalCode}</origin-postal-code>
<destination>
<domestic>
<postal-code>{$postalCode}</postal-code>
</domestic>
</destination>
</mailing-scenario>
XML;
$xmlRequest = phpquery::newDocument();
$xp = new DOMXPath($xmlRequest->getDOMDocument());
foreach($xp->query('//*[not(node()) or normalize-space() = ""]') as $node) {
$node->parentNode->removeChild($node);
}
Okay here an example with just simple dom. Maybe some points first, you'll have decide what to do if no customer number is given or a negative weight, ... .
So you have to clean the XML but sometimes cleaning it will make either the request invalid or the user might get some result he did not expect. For instance he might put 1kg as weight you remove the kg because the weight is set in g and a string there is just wrong. If you don't tell the user he might yell at you!
And also just because all nodes are valid does not mean the request is correct, as there might be some missing nodes, so you also have to check for the requirements!
One last word to efficency, if you can get all these fields from the user without XML because the user just sends one parcel at a time. Do it like that and just check if that data is correct.
If you have to use XML, put still just send one package at a time you can just fetch the data check the validity and reconstruct the validated XML.
I would just use this example if I know that these XML requests might be really extensive and/or have a complex format.
function cleanXML($data){
// ok the data is string! So get your node.
$node = simplexml_load_string($data);
// now we can iterate throught all child nodes:
foreach($node->children() as $child){
//so here we got the childrens
// All child nodes of the root should be mailing scenarios
if($child->getName() == "mailing-scenario"){
//otherwise we check if mailing scenario is valid
if(!validateMScenario($child)){
//This node seems not so valid
//you have to decide what to do now!
}
}
else{
//Here we remove all nodes that are different
unset($child[0]);
echo "Error: Needed to remove Node";
}
}
// Just give them their cleaned XML!
return $node->asXML();
}
function validateMScenario($ms){
// These var's safe if the requirements are fullfilled
$is_customer_number_set = 0
$is_weight_set = 0
$is_package_set = 0
// Again iterate over nodes
foreach($ms->children as $child){
//check for customer number
if($child->getName() == "customerNumber"){
if($is_customer_number_set == 1){
echo "You just need one customer number I guess?!"
return -1
}
value = (string) $child;
// Check if customer number is existing
if(strlen(value) == 0 || !is_int(value) || intval(value) == -1){
echo "Dude you need a number!";
return -1
}
$is_customer_number_set = 0;
}
else if($node->getName() == "parcel-characteristics"){
//Ok I hope it should be pretty clear what to do here!
//...
}
else{
//Remove node again?
}
}
// All requirements fullfilled?
return ($is_customer_number_set && $is_weight_set && $is_package_set);
}
I am attempting to only run a loop if xml results actually exist. I am getting the xml results via:
$albums = simplexml_load_string(curl_get($api_url . '/videos.xml'));
What I want to be able to do is that on the next line say:
if($albums = hasAValue())
// Loop
Any ideas? Or a way to check before I load the XML data?
Side note: This is using the Vimeo API.
No, you need to further go down with the resultant with the namespace, reach till body give the xpath and work on.
$albums->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
To be specific, let me know the XML response you are getting i will let you the output.
UPDATED
$albums = simplexml_load_string("#your response#");
echo count($xml->children());
The dirty way:
$albums = #simplexml_load_string(curl_get($api_url . '/videos.xml'));
if ($albums)
{
...
}
This is dirty because of the Error Control Operator # which is used to "deal" with the error cases (e.g. problem fetching the remote location).
The alternative is to differentiate more here:
$xml = curl_get($api_url . '/videos.xml');
$albums = NULL;
if ($xml)
{
$albums = simplexml_load_string($xml);
}
if ($albums)
{
...
}
I'm fairly new to php although I've been programming for a couple years.
I'm working on a project and the end goal is to load certain elements of an xml file into an oracle table on a nightly basis. I have a script which runs nightly and saves a the file on my local machine. I've searched endlessly for answers but have been unsuccessful.
Here is an aggregated example of the xml file.
<?xml version="1.0" encoding="UTF-8" ?>
<Report account="7869" start_time="2012-02-23T00:00:00+00:00" end_time="2012-02-23T15:27:59+00:00" user="twilson" more_sessions="false">
<Session id="ID742247692" realTimeID="4306650378">
<Visitor id="5390643113837">
<ip>128.XXX.XX.XX</ip>
<agent>MSIE 8.0</agent>
</Visitor>
</Session>
<Session id="ID742247695" realTimeID="4306650379">
<Visitor id="7110455516320">
<ip>173.XX.XX.XXX</ip>
<agent>Chrome 17.0.963.56</agent>
</Visitor>
</Session>
</Report>
One thing to note is that the xml file will contain several objects which I will need to load into my table and the above example would just be for two rows of data. I'm familiar with the whole process of connecting and loading data into oracle and have setup similar scripts which perform ETL of txt. and csv. files using php. Unfortunately for me in this case the data is stored in xml. The approach I've taken when loading a csv. file is to load the data into an array and proceed from there.
I'm pretty certain that I can use something similar and perhaps create variable for each or something similar but am not really too sure how to do that with an xml. file.
$xml = simplexml_load_file('C:/Dev/report.xml');
echo $xml->Report->Session->Visitor->agent;
In the above code i'm trying to just return the agent associated with each visitor. This returns an error 'Trying to get property of non-object in C:\PHP\chatTest.php on line 11'
The end result would be for me to load the data into a table similar to the example I provided would be to load two rows into my table which would look similar to below however I think I can handle that if i'm able to get the data into an array or something similar.
IP|AGENT
128.XXX.XX.XX MSIE 8.0
173.XX.XX.XXX Chrome 17.0.963.56
Any help would be greatly appreciated.
Revised Code:
$doc = new DOMDocument();
$doc->load( 'C:/Dev/report.xml' );
$sessions = $doc->getElementsByTagName( "Session" );
foreach( $sessions as $session )
{
$visitors = $session->getElementsByTagName( "Visitor" );
foreach( $visitors as $visitor )
$sessionid = $session->getAttribute( 'realTimeID' );
{
$ips = $visitor->getElementsByTagName( "ip" );
$ip = $ips->item(0)->nodeValue;
$agents = $visitor->getElementsByTagName( "agent" );
$agent = $ips->item(0)->nodeValue;
echo "$sessionid- $ip- $agent\n";
}}
?>
The -> operator in PHP means that you are trying to invoke a field or method on an object. Since Report is not a method within $xml, you are receiving the error that you are trying to invoke a property on a non-object.
You can try something like this (don't know if it works, didn't test it and haven't written PHP for a long time, but you can google it):
$doc = new DOMDocument();
$doc->loadXML($content);
foreach ($doc->getElementsByTagName('Session') as $node)
{
$agent = $node->getElementsByTagName('Visitor')->item(0)->getElementsByTagName('agent')->item(0)->nodeValue;
}
edit:
Adding stuff to an array in PHP is easy as this:
$arr = array();
$arr[] = "some data";
$arr[] = "some more data";
The PHP arrays should be seen as a list, since they can be resized on the fly.
I was able to figure this out using simplexml_load_file rather than the DOM approach. Although DOM works after modifying the Leon's suggestion the approach below is what I would suggest.
$xml_object = simplexml_load_file('C:/Dev/report.xml');
foreach($xml_object->Session as $session) {
foreach($session->Visitor as $visitor) {
$ip = $visitor->ip;
$agent = $visitor->agent;
}
echo $ip.','.$agent."\n";
}
I created an ios app that parses an xml document. If a user logs in, their information will be added to the xml file. I would like to be able to remove a user if they are logging out or cancelling their logins. Essentially, I need to figure out how to delete an xml object (a bartender, in this case) that looks like this:
<Bars>
<Bar>
<bar_id>0</bar_id>
<Bartenders>
<Bartender>
<imageURL>unique URL</imageURL>
<shift>20:30</shift>
</Bartender>
</Bartenders>
</Bar>
<Bar>
<bar_id>1</bar_id>
<Bartenders>
<Bartender>
<imageURL>aURL</imageURL>
<shift>a shift</shift>
</Bartender>
<Bartender>
<imageURL>aURL</imageURL>
<shift>a shift</shift>
</Bartender>
</Bartenders>
</Bar>
For example, I want to remove a user based on a unique imageURL... I will also know the bar_id. This is the php that I have so far, but I am completely open to suggestions on doing it a different way
$newimageURL = $row['imageURL'];
$newBar_ID = $row['Bar_ID'];
$xmlUrl = "Bars.xml"; // XML
$xmlStr = file_get_contents($xmlUrl);
$xml = new SimpleXMLElement($xmlStr);
$bartenders = $xml->xpath('//Bartenders');
// something needs to happen here to remove the child
$xml->asXML('Bars.xml');
I was told that I could use something of this sort:
$bartenders->removeChild($bartenders[$newBar_ID]);
Or xpath but I am not sure how to get the path right to the correct bartender with just the unique imageXML. I know I should have planned/designed this better, but I am crunched for time and this will have to do.
Sorry I am so bad with php...
Thanks for your help.
There is a very nice script at http://php.net/manual/fr/ref.simplexml.php that works perfectly fine
function removeNode($xml, $path, $multi='one')
{
$result = $xml->xpath($path);
# for wrong $path
if (!isset($result[0])) return false;
switch ($multi) {
case 'all':
$errlevel = error_reporting(E_ALL & ~E_WARNING);
foreach ($result as $r) unset ($r[0]);
error_reporting($errlevel);
return true;
case 'child':
unset($result[0][0]);
return true;
case 'one':
if (count($result[0]->children())==0 && count($result)==1) {
unset($result[0][0]);
return true;
}
default:
return false;
}
}
Please click on the link for more information