WordPress admin-ajax - defer actions until after response sent - php

I am using admin-ajax.php to respond to requests from JavaScript.
When I've pulled together the data I want and returned my response I also need to send some data to an external API and send an email to the user. Both of these things take a little bit of time.
I was hoping I could do something like this which would return the JSON response via AJAX before setting off the actions so they could essentially happen in the background after the response was back with the user:
function my_ajax_action() {
//process the $_REQUEST ajax data here
echo json_encode($results);
do_action('request_successful', $results);
wp_die();
}
...but it is obviously waiting to complete the actions before it dies (as PHP is not async) and the AJAX request doesn't complete until the response is provided.
Are there any known methods to defer these actions until after the response has been fully sent? The two functions attached to these actions are not required to deliver the response and are just adding a couple of seconds to the amount of time it takes for the results to show in the user's browser.
I am guessing I could store them in a cron job to happen very soon after but I feel it's likely that someone has already solved this issue in a better way.
thanks
Aaron

it could be related to PHP session locking and if you already have a script using session data, then the later script that will also request session data access will be put on hold until the initial script is done. Read more how to avoid it: http://konrness.com/php5/how-to-prevent-blocking-php-requests/

Related

How to stop PHP script in case the internet didn't work only for the AJAX response

I am calling the server via AJAX and I get the response of the server by checking the XmlHttp object properties like:
if(xmlHttp.readyState==4){
if(xmlHttp.status==200){ // AJAX succeeded }
}
Now, I have a timeout mechanism to wait for 10 seconds using:
setTimeout();
in Javascript for the request to be executed, in case I didn't get the status 200 for the request, then I abort the request via:
xmlHttp.abort()
and then I show a timeout message and I show a button to resend this request.
The AJAX request works perfect in 98% of time but:
The problem when my internet is shaking, I send the request successfully but when the response tries to come back, the internet doesn't work well and I lose the response, so in this case the timeout message will be triggered to user (request not completed, please try again) but actually in the server side (PHP), the request is executed successfully and this request is attached to email to be sent to user, so the user will get the email that the request is done but also a timeout message is seen for the user.
So what shall I do? When internet is off completely, timeout mechanism works fine! But when the ISP has such internet problem (shaking quickly), how can I do to prevent the PHP from executing?
Thanks for your help
You should leave as is , unless it is of critical importance to achieve this redundancy.
In case it is critical :
Instead of immediately throwing an error message, you could retry to send the request 2 or 3 times. This would give the server more chances to respond to the request.
To do that, you'd have to make sure that the same request isn't processed more then once (in your case sending the mail) - you'd have to implement a simple system in your php to cache responses and recognize requests that were already fulfilled so they wont be processed again.
Create an unique id in javascript and send it as a parameter in your ajax.
Use a session array to store the responses of your requests, keyed by this unique id.
When a request come in, check the responses array to see if it was already fulfilled, in which case you just echo back the cached response without processing.

AngularJS delayed HTTP POST to PHP

I'm using the http service in AngularJS to make requests to my PHP API. It's a Phonegap iOS app, using AngularJS as the main framework.
In one call, I'm using the Facebook Javascript API to get the friends list of a user, encode that and send it to my API, match the emails to a list of our app users, then send the data back to the app.
I do exactly the same thing with the contacts from the phone, which I access using Phonegap.
Using console.log() I've confirmed that loading the contacts and facebook friends data from the device take less than a second.
The problem is mapping the Facebook friends list via the API takes about 20 seconds, but contacts only takes about 2 seconds.
I've manually forced the facebook list to be empty, var friends = [], and the process is nearly instant, but if I send 1 or 300 facebook contacts it gets really slow.
I've check the code on the live server by processing it with hard coded data and it takes about 0.01 seconds (so no HTTP POST, just the php logic).
I've also placed console.log() commands before the http.post() and during the success function:
console.log('http.post started');
$http.post(
$rootScope.api_url + '/friends/map/',
{values: hashes.values},
).success(function(data,status,headers,config){
console.log('http.post finished');
// process data
}).error(function(data,status,headers,config){
// log error
});
And in addition to that used PHP error_log to tell me when the start of the PHP processing occurs, and just before the result is returned:
<?php
// includes
require_once("../../../configs/config.".get_current_user().".php");
function process_request() {
error_log('started processing');
// decode POST data, work out API method etc
$return_data = $caseObject->process_data( $method, $arr_rest_data, $arr_req_data, $extra_data );
error_log('data processed');
return $return_data;
}
echo GibberishAES::enc( json_encode( process_request() ), IOS_API_PASSPHRASE );
?>
So that gives me a log before the Javascript sends the data, before and after the PHP processes it and after the Javascript receives a response.
Watching the logs get output, the 20s delay is between the following:
console.log('http.post started');
error_log('started processing');
So the delay seems to be in the upload. No data has been AES decoded by that point, and I've checked and the facebook data is an encoded string about 17k characters long, and the contacts data is 22k characters long. So it's not the size of the post, at least I don't think.
To summarise, I've got an http.post request in AngualrJS that is taking about 20s, very similar to another request that takes around 2s, the PHP code itself executes in under 1 second, and the delay seems to be between the start of the javascript http.post call and the beginning of the php processing code.
Any one know how I can work out what is causing the delay? Where should I be looking to narrow this down?
For anyone landing here with a similar issue:
The delay seems to be related with $http's request pre-processing and internal handling.
Using "plain old" XHR (eliminating $http) could speed things up considerably (at the cost of lost functionality and Angular-integration, of course).

PHP cURL or header approach?

I am confused what approach to take when updating a status e.g delivery on our client's system. On normal process, they provide us URL where we will post back the delivery data.
The problem now is what if we update the delivery data, then we'll have to notify our client's system too so the change of delivery status on their system happens real time instead of the cron job (check delivery status).
Should this be a
header('Location:http://path/to/client/parse_update_delivery_status.php');
once all the updating on our system is done or a
cURL.... //http post to client's url for updating delivery status on their system based on what we passed here
approach?
I'd really appreciate your input! :) Thanks!
I'm pretty certain that would have to be done with cURL, and at the very least would work much better using curl. Does parse_update_delivery_status.php receive data from an HTTP POST?
Using a location header causes the browser to redirect to that script, and no data gets passed along with it (unless you add it to the query string). If an update was performed, a person could potentially stop their browser from loading the redirect by hitting stop fast enough. Also, chances are that script won't output anything meaningful to the user so they would be left with a blank page or data on their screen that they don't understand and would have to use the back button to return to your site.
If the update was run from a cron job or PHP CLI script, then the headers have no meaning anyway.
This should be done as a cURL operation. The reason for this is your update scripts shouldn't be held responsible for handling browser operations like redirects; their job should just be to update whatever they need to update. By using cURL you can move all of your code which handles whatever status codes are returned by the update script into something which presents that data to the user, instead of intermingling that with your update script. By using this approach, you can keep your update script clean and allow it to be called by multiple sources without worrying about misleading redirects.

Codeigniter - How to do work on server, after view file is shown?

What is the best way of spitting out the view file in codeigniter and still doing some work on the server? The situation basically is:
I have a payPal checkout where user goes to paypal and clicks Pay Now! A set_express_checkout() is called to start things off.
The user is returned to the Thank You page.
I have to call a Get_express_checkout_details() and a do_checkout() before showing him the Thank you page and this is 2 calls to a pretty slow payPal server.
My problem is, that when the user clicks on Pay Now! button, he is redirected back to my site but hangs at payPal for at least 5 seconds (while my server makes the 2 requests) before he can se anything. So the question is: Where should I make these two calls so the user doesnt have to wait so long before anything is shown to them?
I think using AJAX request is justwhat youwant. The idea is the following:
Output your page to client not performing any paypal requests
Create additional page/method that only performs paypal request and outputs data as json
On the outputted page place AJAX call to that new page
Process the response to know, if the request was successful.
For ajax calls youmight want to have a look at jQuery.ajax. Most convenient way to output json data from PHP is using json_encode PHP function.
You could enable hooks and use the 'post_system' hook to make your two calls to the slow server. See http://codeigniter.com/user_guide/general/hooks.html for more information.
However this will leave you with no easy way of showing any result of the two calls.

PHP Validation - passing http POST values forwards to a 3rd party checkout

I don't know whether this is possible, I can't seem to find any other help guides so this may not be possible...
I have a checkout page which POSTs a load of variables forwards to a 3rd party payment processor (WorldPay).
I want to know if it is possible to put a PHP script of some sort inbetween the two pages for validation purposes.
EG if an item in the basket has sold out while they were filling out the form, it could catch the customer before money is taken. Or useful if they tamper with form data.
If I do this on my own site I could use sessions to pass the POST data forward but as it's an external website, I don't know how to send the data without making another HTML page with a hidden form & refresh for instance.
Is it possible to do this 'invisibly' - not actually showing a HTML page inbetween?
Yes you can do that by hooking into the onsubmit hook of the form and sending out an Ajax call like this (using jQuery):
$('#myform')[0].onsubmit = function() {
if (form_check_elements(this.elements)) { /* ««« eg JS validator here */
data = $('#myform').serialize();
$.post('/ajax_validator.php', data, function(data, textStatus) {
$('#myform')[0].submit(); /* ««« check the textStatus before here and
eventually do not submit (wrap it in
an if-clause) */
});
return false; /* make the form not post directly */
} else {
return false; /* do not post if JS validation fails */
}
};
We use this snippet to store form data in a session before posting to a 3rd party so we have it available when the 3rd party returns to our page.
Edit: Keep in mind that this will only work with JS enabled, but it is fallback-safe: The form still submits without JS support.
EDIT:
Ashley said:
Okay, i've taken a look at the cURL
manual and written this very simple
script to forward the POST values to
the 3rd party checkout. This just
displays the contents of the checkout
page though. The URL address shows the
script currently running rather than
forwarding to the 3rd party site. Also
all their relatively linked graphics
will not work. Can 'true' forwarding
be achieved using cURL?
The short answer - no.
With the way you described your payment process if you want to step in the middle of the offsite process to do things (customize html/messages, validate data, etc.) then you need to handle the entire process which cURL would allow you to do.
With cURL, you dont "forward" the request - you sort of "proxy" the request. So the fact that the browser URL never changes and that the relative graphics dont work is expected. With the use of cURL or something similar you would never let the user end user know that they are even touching an external page. you would be handling all the requests to that external server on your server and then simply displaying the response from the external server to your user OR parsing that response so that you can use the data from it in a customized way.
Essentially this means if secure.wp3.rbsworldpay.com/wcc/purchase is returning a form that requires futher interaction from the user you have to mimic this form on your server and display that instead. Then when the user submits your form you use cURL again to make a request to the external server - this time to post the next round of data submitted by the user. So for example lets say:
secure.wp3.rbsworldpay.com/wcc/purchase shows the cart
secure.wp3.rbsworldpay.com/wcc/confirm shows a final confirmation of the payment to be made
secure.wp3.rbsworldpay.com/wcc/success and secure.wp3.rbsworldpay.com/wcc/error show whether the transaction succeeded or failed respectively.
Then you are actuall going to need to make 2 requests externally as part of you transaction process which could be summarized like so:
User shops at your site and adds items to cart
User clicks on checkout and you validate the cart/user data
If the data from #2 was valid you package up the data and post to secure.wp3.rbsworldpay.com/wcc/purchase via cURL
If the cURL response from #3 was successful you build your own confirm page using data from the cURL response and display it to the user.
The user submits the confirmation of the purchase to your server.
You package up the data submitted to your server in #5 and post it to secure.wp3.rbsworldpay.com/wcc/confirm via cURL.
If the cURL response from #6 was successful then you parse it for the expected "error" or "success" message returned from external server and display them or your own custom error messages.
Rinse and repeat in case of error ;-)
Generally speaking most payment processors have an option of processing that supports this basic process often returning easy to parse data as XML, JSON, or Plain Text instead of HTML. You might want to look in to this. A lot of times they will often have libraries built for various programming languages to help ease the integration process.
Yep it sure is... i normally use the curl extension to do stuff like this, or an http client class that utilizes curl. You might want to make it a tad easier on yourself and use one of these class libraries - for example Zend_Http_Client. It supports not only curl but also sockets and proxies.
Yes, you can. What you are looking for is the CURL function:
http://php.net/manual/en/book.curl.php
Also see:
http://php.dzone.com/news/execute-http-post-using-php-cu
I rather like the HTTP_Request2 package from PEAR, which basically wraps cURL and/or sockets in some simple objects. POSTing works great. You can use that to bounce the POST request to your validation-checker, then on to the payment processor.
I would suggest you to go like this:
Before directing a user to the form, you check (through SQL queries) whether the item in the basket has sold out. If it has been sold, redirect the user to some other page saying that this item has been sold out otherwise let him go to the form for new purchase.

Categories