Foreach Loops in PHP - All or nothing - php

In a foreach loop, say I wanted to do the following
foreach($items as $item)
{
// charge a user for purchasing an item - IS BEING DONE THROUGH AN API CALL WITH STRIPE
// ie: \Stripe\Charge::create(...);
// save some info to a database
// using Laravel for this project so this is within a DB::transaction(...);
// send a notification email to the buyer
// Emails are being queued to avoid slowing down the request
// ie: Mail::queue(...);
}
I would like an all or nothing situation to occur. For example, if an error occurs during the iteration, then NONE of the items are charged, no information is saved to the database, and the buyer does not get a notification email. However, if the foreach loop successfully iterates over each item, then charge the user, save info to database, and send notification email. How do I do this?

I would recommend using Transactions and notifying the buyer at the end.
Mysqli Example:
$dbConn->begin_transaction();
try{
foreach($ítems as $item){
//Whatever you are going to do
}
if($dbConn->commit()){
//Notify buyer about all the transactions, the user doesn't receives the notification unless the commit was succesful
}
catch(Exception ex){
$dbConn->rollback();
}
Additionally you could verify the return values of the mysqli functions, I Just wanted to focus on the transaction use.

You have options.
Easiest way i can think of is loop over the array twice; the first time checking the condition and the 2nd only if the first run through found no problems.
$isGoodToGo = true;
foreach($items as $item) {
if (conditon met) {
$isGoodToGo = false;
break; // stop iterating over the array cause condition is met
}
}
if ($isGoodToGo) {
foreach($items as $item) {
// charge a user for purchasing an item
// save some info to a database
// send a notification email to the buyer
}
}

You might need to add some more detail as per #Terminus as well as if/why you are charging each item separately. But depending on your needs, you could build the db query during the loop without executing it until the end of the loop. You would then have to roll back any charges (as per your implementation) and then send one or more emails as appropriate.

You can count the no of items in the array and make some changes to make it work:
$result = count($items);
$c = 0;
foreach($items as $item)
{
// charge a user for purchasing an item -- instead add it to a buffer as total
//amount.
// save some info to a database ---instead prepare your db statements here
// send a notification email to the buyer.--this could be sent out for all the
//items in the order together outside the loop instead of inside the loop.
$c = $c + 1;
}
if($c == $cc)
{
//execute your prepared statements here..and also the other email and charging stuff.
}
else{
//Error code.
}

Related

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

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

ajax calls how to retrieve partial results during long executions with PHP

Good day all.
I have a page that calls a script via AJAX, the script calls a prestashop webservice and has to insert several items at a time. My problem is that the script seems "freezed" for most of the time, then after 2 or 3 minutes, starts to print out results, and continue since the end. what i would like to do is to retrieve something from the script each time it insert an item, and not to "buffer" hundreds of results and then see all of them in one time.
this is the code (stripped of unecessary parts) that I'm using.
<?php
function PS_new_product(all product attributes) {
global $webService;
try {
$opt = array('resource' => 'products');
$opt['postXml'] = $xml -> asXML();
$xml = $webService -> add($opt); //this should return each product state (if it's inserted or not)
return true;
} catch (PrestaShopWebserviceException $ex) {
return false;
}
}
function inserisciProdottiRAW(){
set_time_limit(30);
$sql_prodotti = "SELECT everything i need to import";
if ($prodotti = mysql_query($sql_prodotti)) {
while ($row = mysql_fetch_assoc($prodotti)){
$webService = new PrestaShopWebservice(PS_SHOP_PATH, PS_WS_AUTH_KEY, DEBUG);
$opt = array('resource' => 'products');
$opt['filter[reference]'] ="[".$row["modello"]."]";
$xml = $webService->get($opt);
$prodotto = $xml->children()->children();
if ($prodotto->product[#id] == ""){
PS_new_product(/*all product attributes*/)
}
}
}
echo "ok";
}
inserisciProdottiRAW();
?>
I would like something that i could catch in the page I have called it to know for example at which items it arrived at a certain time... it could be possible? or I have to implement something that count the items inserted in the database every... mh... 30 seconds?
If you need a quick and dirty solution - just include an echo after every insertion and make sure that it includes new line and is big enough to flush cache in php/apache (4KB should do it). Use this method for example:
function logProgress($message)
{
echo($messsage);
for ($i=0; $i<4096; $i++)
{
echo(" ");
}
echo("\n");
}
If you use gzip then it could not be enough and use other random white space characters as well.
If you want to show the progress to some user then you can run your insertion script in background, save state in some database table and poll it from different script.
Running a background job can be done using fork function, curl or if you need good job manager, try gearman.
Also be warned that if you use sessions you cannot have 2 scripts running in the same time - one will be waiting for the other one to finish. If you know that you won't be using session anymore in your script, you can call session_close() get rid of this locking issue.

PHP stop processing 'on button or time 'and output "so far collected array"

I have a heavy processing script which can be started from a user via frontend in our intranet. Imagine something like this:
$html = file_get_contents($url);
$pattern = '/[A-Z0-9._%+-]+(#|\(at\)|\[at\])[A-Z0-9.-]+\.[A-Z]{2,4}\b/i'; //also (at) and [at]
preg_match_all($pattern,$html,$emails);
foreach ($emails[0] as $m)
{
$m[] = $m;
}
foreach($m as $n){echo $n."<br>";}
This is just an example for illustration of the question! Don't judge it on common sense.
NOW what i want is 2 things:
Stop the process on Click on a button from the user. This means: outputting the already collected array $m[].
Stop the process (or better: stop the array-collecting process and jump to the echo of the already collected array) based on time (for example max. 1 minute collecting the array, THAN jumping to echoing.
I don't want to echo live and setting max execution time will stop the script without echoing.
Thanks for your wise advise on both subquestions
You have to run script through ajax, and button click regsiter session by ajax
foreach ($emails[0] as $m)
{
if (!isset($_SESSION['STOP'])) {
$m[] = $m;
}
else { //stop code here }
}

Zend Php Foreach Loop array

I have a input field and a array of email from DB table.
I am comparing the similarity of the input field to the array.
But some how, I am stuck with the loop.
Does that loop check compare each email with the input field?
It always bring me to google.com no matter what i input same or not
Here's the code from the controller:
if (isset($_POST['btn_free_download']))
{
// Get current input email from input field
$email = $this->getRequest()->getParam('email');
// Get all emails from the user
$referred_users = $this->_helper->user()->getReferredUsers()->toArray();
// Check the similarity which it with a loop
foreach ($referred_users as $referred_user)
{
similar_text($email, $referred_user['email'], $similar);
}
// If yes, Pop up message or redirect to some page
if ($similar < 97)
{
$this->_redirect('http://google.com');
}
// If not, redirect user to free download page
else
{
$this->_redirect('http://yahoo.com');
}
}
I think you need to check the manual . Foreach function is same wether you use it on zend or any other framework or raw php only.
$referred_users = $this->_helper->user()->getReferredUsers()->toArray();
$referred_users will probably hold an array of emails from the table
user, say:
$referred_users = array("one#email.com", "two#email.com", "three#email.com")
then when you use foreach loop it will iterate through each of the
emails in the array
foreach ($referred_users as $referred_user)
{
// for the first loop $referred_user = one#email.com, for second time $referred_user = two#email.com and goes on
similar_text($email, $referred_user['email'], $similar);
}
Now let us discuss your logic here:
// If yes, Pop up message or redirect to some page
if ($similar < 97)
{
$this->_redirect('http://google.com');
}
// If not, redirect user to free download page
else
{
$this->_redirect('http://yahoo.com');
}
Until and unless the last element in the array $referred_users is exactly equal to your $email
i.e. $email = "three#email.com"
you will always be given result for $similar less than 97% which means you will be redirected to google.
Which I assume you are not trying to do and probably not familiar with foreach function which is why you are not getting the expected result.
Assuming you are trying to do something like, check for the emails in the array if any of the email in the array matches (if array is from table check if the email entered from param is equal to any of the emails in the table) then redirect to somewhere or show some message else carry on. Solution below might be helpful to you.
$similarText = false;
foreach ($referred_users as $referred_user)
{
// for the first loop $referred_user = one#email.com, for second time $referred_user = two#email.com and goes on
similar_text($email, $referred_user['email'], $similar);
if ($similar > 97) {
$similarText = true;
break;
}
}
// If yes, Pop up message or redirect to some page
if ($similarText)
{
$this->_redirect('http://google.com');
}
// If not, redirect user to free download page
else
{
$this->_redirect('http://yahoo.com');
}
Hope you got the idea. But please do check the manual before posting a question in the future.

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