Removing XML Elements Created Within Loop At The End Of Each Loop - php

I have a script that scans every item laptop in my Snipe IT inventory, compares it against that same instance in my Jamf Pro server, and for any fields that do not match, it creates an XML and sends that XML over to Jamf. What I need is for the XML to have all of its elements "wiped" after sending the payload over. I have tried countless --- countless --- ways to make this happen, but even when I "print" the supposedly "wiped" payload at the end of the script and confirm that the element was removed, it still appears the next time around when I inspect the payload that was sent to Jamf. If I have three laptops with info that gets sent over, I get three fields. Four, I get four. And so on. I really need to figure out how to reset this XML to make this script cleaner.
Here is where I am creating the XML, if the information does not match.
// let's compare the DEVICE ASSIGNMENT across our two systems, knowing that itassets is our SOT
if ($jamf_assignment !== $itassets_assignment) {
echo "\n" . "ALERT: The device assignment is not in sync across our systems!" . "\n";
if (!isset($location)) {
$location = $jamf_xml->createElement('location');
$location->appendChild($jamf_xml->createElement('real_name',$itassets_assignment));
$process_xml = 1;
} else {
$location->appendChild($jamf_xml->createElement('real_name',$itassets_assignment));
$process_xml = 1;
}
} else {
echo "\n" . "DEVICE ASSIGNMENT CHECKS OUT! ALL GOOD HERE!" . "\n";
}
And here is where I am turning it into a payload, about to be sent.
if (!empty($location)) {
$computer->appendChild($location);
}
$jamf_xml->appendChild($computer);
$jamf_payload = $jamf_xml->saveXML();
And finally here is where I am trying to reset that XML, and start fresh with the next loop.
// and finally let's destroy the XML file
if (!empty($general)) {
$computer->removeChild($general);
}
if (!empty($location)) {
$computer->removeChild($location);
}
$jamf_xml->appendChild($computer);
$jamf_payload = $jamf_xml->saveXML();

Related

Testing if a combined variable name exists

I am sure this has been asked 100 times but cannot find the form of words to get at the answer either here or on Google.
I have a variable number of messages. They arrive as
$_GET['message1']; $_GET['message2']; $_GET['messageX']; etc
where X can be 1 to 100.
I need to test if they exist and then push them out to a DB. I tried
$i=1;
while (isset(parse_str("message$i")))
{
echo parse_str("output=message$i");
echo "<h1>This is test $output </h1>";
$i++;
}
which does not work. I thought the middle part worked but just re-tested and that is wrong too.
I am new to parse_str(). I thought I understood it and I understand the problem (it is a void function so cannot be used as a test) but cannot work out a solution for getting through the variables.
parse_str parses a string. What do you expect in a string "message$i"?
If you're sure that all your messages come from $_GET, use $_GET:
$i = 1;
while (isset($_GET['message' . $i])) {
echo $_GET['message' . $i];
$i++;
}
But obviously for storing such data, arrays are move convenient.

Possible to overide a foreach variable parameter within itself?

I have made a small script which uses the Twitch API. The API only allows a maximum of 100 results per query. I would like to have this query carry on until there are no more results.
My theory behind this, is to run a foreach or while loop and increment the offset by 1 each time.
My problem however, is that I cannot change the foreach parameters within itself.
Is there anyway of executing this efficiently without causing an infinite loop?
Here is my current code:
<?php
$newcurrentFollower = 0;
$offset=0;
$i = 100;
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
foreach ($json->follows as $follow)
{
echo $follow->user->name . ' (' . $newcurrentFollower . ')' . "<br>";
$newcurrentFollower++;
$offset++;
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
}
?>
Using a While loop:
while($i < $total)
{
$json = json_decode(file_get_contents("https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=25&offset=".$offset));
echo $json->follows->user->name . ' (' . $newcurrentFollower . ')' . "<br>";
$newcurrentFollower++;
$offset++;
$i++;
}
Ends up echoing this (No names are successfully being grabbed):
Here is the API part for $json->follows:
https://github.com/justintv/Twitch-API/blob/master/v2_resources/channels.md#get-channelschannelfollows
You can use this:
$offset = 0;
$count = 1;
do {
$response = json_decode(file_get_contents(
'https://api.twitch.tv/kraken/channels/greatbritishbg/follows?limit=100&offset=' . $offset
));
foreach($response->follows as $follow) {
echo $follow->user->name . ' (' . ($count++) . ')' . "</br>";
}
$offset+=25;
} while (!empty($response->follows));
You want to use a while loop here, not just a foreach. Basically:
while (the HTTP request returns results)
{
foreach ($json->follows as $follow)
{
do stuff
}
increment offset so the next request returns the next one not already processed
}
The trickiest part is going to be getting the while condition right so that it returns false when the request gets no more results, and will depend on what the API actually returns if there are no more results.
Also important, the cleanest way would be to have the HTTP request occur as part of the while condition, but if you need to do some complicated computation of the JSON return to check the condition, you can put an initial HTTP request before the loop, and then do another request at the end of each while loop iteration.
The problem is you're only capturing the key not the value. Place it into a datastructure to access the information.
Honestly I find a recursive function much more effective than a iterative/loop approach then just update a datatable or list before the next call. It's simple, uses cursors, lightweight and does the job. Reusable if you use generics on it too.
This code will be in c#, however I know with minor changes you'll be able to get it working in php with ease.
query = //follower object get request//
private void doProcessFollowers(string query)
{
HTTPParse followerData = new HTTPParse(); //custom json wrapper. using the basic is fine. Careful with your cons though
var newRoot = followerData.createFollowersRoot(query); // generates a class populated by json
if (newRoot[0]._cursor != null)
{
populateUserDataTable(newRoot); //update dataset
doProcessFollowers(newRoot[0]._links.next); //recurse
}
}
Anyway - This just allows you to roll through the cursors without needing to worry about indexes - unless you specifically want them for whatever reason. If you're working with generics you can just reuse this code without issue. Find a generic example below. All you need to do to make it reuseable is pass the correct class within the <> of the method call. Can work for any custom class that you use to parse json data with. Which is basically what the 'createfollowerroot()' is in the above code, except that's hard typed.
Also I know it's in c# and the topic is php, with a few minor changes to syntax you'll get it working easily.
Anyway Hope this helped somebody
Generic example:
public static List<T> rootSerialize<T>(JsonTextReader reader)
{
List<T> outputData = new List<T>();
while (reader.Read())
{
JsonSerializer serializer = new JsonSerializer();
var tempData = serializer.Deserialize<T>(reader);
outputData.Add(tempData);
}
return outputData;
}

How to clean xml request before sending to web service

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);
}

Passing Object Operators As Strings (PHP)

I'm building a script that takes the contents of several (~13) news feeds and parses the XML data and inserts the records into a database. Since I don't have any control over the structure of the feeds, I need to tailor an object operator for each one to drill down into the structure in order to get the information I need.
The script works just fine if the target node is one step below the root, but if my string contains a second step, it fails ( 'foo' works, but 'foo->bar' fails). I've tried escaping characters and eval(), but I feel like I'm missing something glaringly obvious. Any help would be greatly appreciated.
// Roadmaps for xml navigation
$roadmap[1] = "deal"; // works
$roadmap[2] = "channel->item"; // fails
$roadmap[3] = "deals->deal";
$roadmap[4] = "resource";
$roadmap[5] = "object";
$roadmap[6] = "product";
$roadmap[8] = "channel->deal";
$roadmap[13] = "channel->item";
$roadmap[20] = "product";
$xmlSource = $xmlURL[$fID];
$xml=simplexml_load_file($xmlSource) or die(mysql_error());
if (!(empty($xml))) {
foreach($xml->$roadmap[$fID] as $div) {
include('./_'.$incName.'/feedVars.php');
include('./_includes/masterCategory.php.inc');
$test = sqlVendors($vendorName);
} // end foreach
echo $vUpdated." records updated.<br>";
echo $vInserted." records Inserted.<br><br>";
} else {
echo $xmlSource." returned an empty set!";
} // END IF empty $xml result
While Fosco's solution will work, it is indeed very dirty.
How about using xpath instead of object properties?
$xml->xpath('deals/deal');
PHP isn't going to magically turn your string which includes -> into a second level search.
Quick and dirty hack...
eval("\$node = \"\$xml->" . $roadmap[$fID] . "\";");
foreach($node as $div) {

Processing multi-dimensional arrays without loop

I need your advice and help
i fetched an array of data from the database and i want to process each element one by one without using foreach loop, something like
pop element a and process it, when finished
pop element b and process it, when finished
pop element b and process it
until the array become empty then the script can exit
currently i`m looping through the data using foreach loop but things are not working find.
$loaded_message = $this->lib->load_queued_messages();
if(count($loaded_message) == 0) {
die ('Nothing to do');
}
foreach($loaded_message as $tosend)
{
if($this->lib->send_sms($tosend['from'], $tosend['msg'], explode(',', $tosend['numbers']), $tosend['owner'], $tosend['qid']))
{
// Remove the message from queue
$this->lib->remove_msg_from_queued_message($tosend['qid']);
$this->lib->log('message #' . $tosend['qid']. ' sent and removed from queue', $tosend['owner']);
}else{
$this->lib->log('SENDING_ERROR: message #' . $tosend['qid']. ' not sent and remain in the queue for#', $tosend['owner']);
}
}
Inside the log table i discovered that entry was made for wrong message id and it seems like message was sent to wrong number but it does not.
hi mate you can use something like
while(sizeof($yourarray)) {
$result = array_pop(yourarray);
...yourprocessing_here(...);
}
hope this helps :)

Categories