I have a web service written in PHP to which an iPhone app connects to. When the app calls the service, a series of notification messages are sent to Apple's APNs server so it can then send Push Notifications to other users of the app.
This process can be time consuming in some cases and my app has to wait a long time before getting a response. The response is totally independent of the result of the notification messages being sent to the APNs server.
Therefore, I would like the web service to send the response back to the app regardless of whether the messages to APNs have been sent.
I tried using pcntl_fork to solve the problem:
<?php
...
$pid = pcntl_fork();
if($pid == -1)
{
// Could not fork (send response anyway)
echo "response";
}
else if($pid)
{
// Parent process - send response to app
echo "response";
}
else
{
// Child process - send messages to APNs then die
sendMessageAPNs($token_array);
die();
}
?> // end of script
Unfortunately, the parent process seems to wait for the child process to end before sending the response even though I do not use pcntl_wait in the parent process. Am I doing something wrong or is this normal behaviour? If this is normal then is there another way I can solve this problem?
Thank you!
If you're hosting the PHP process in Apache then you really shouldn't use this: see this for the section that says *Process Control should not be enabled within a web server environment and unexpected results may happen if any Process Control functions are used within a web server environment. *.
You should probably set up a separate daemon in your preferred language of choice and hand the APNS communication tasks off to that. If you really really really must try using ob_flush().
I think you can send the response back before doing the "long" process. Take a look at the flush() function of PHP it'll maybe help
Related
I want to subscribe the Spring framework WebSocket and receive the reply.
According to my target WebSocket server, the communication is done using STOMP publish protocol (Build based on Java Springframework API) https://stomp.github.io/stomp-specification-1.1.html
Now the client that I am working on, is build based on PHP and using https://github.com/Textalk/websocket-php/ for websocket client.
My idea to receive the server response is to follow the STOMP over Websocket technique based on this guy's answer Websocket Client not receiving any messages.
Using the current websocket client, I perform these steps
Send Connection (request?)
Send Subscription
Actively receive the reply
$client = new WebSocket\Client($ws_url);
//Step 1 Inintate connection;
$open_msg = "CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n";
//Step 2 Subscribe Request;
$client->send($open_msg);
$subs = "SUBSCRIBE\nid:0\ndestination:/user/queue\nack:auto\n\n\x00\n";
$client->send($subs);
while (true) {
try {
$message = $client->receive();
echo $message;
// Act[enter image description here][4] on received message
// Later, Break while loop to stop listening
} catch (\WebSocket\ConnectionException $e) {
// Possibly log errors
}
}
$client->close();
The connection (Step 1) is done and tested.
current send and receive result image
Since it is running on the loop, the Received is always printed.
Does anyone know why the API did not send reply?
It turns out, I have to implement the other Websocket library instead
Instead of using https://github.com/Textalk/websocket-php/ , I moved on and use https://github.com/ratchetphp/Pawl
I don't know what just happened. But I think Textalk is synchronous websocket library and ratchet is asynchronous websocket library.
My current hypothesis is whenever you want to do Stomp over websocket, make sure
Send Connection message ("CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n")
Send subscription ("SUBSCRIBE\nid:0\ndestination:/user/queue\nack:auto\n\n\x00\n")
Use the asynchronous Websocket instead of synchronous one
Have a nice day
I've setup a basic webhook php page as modeled on the stripe documentation and listed below. When I send a test event from the Stripe webhooks dashboard, stripe responds "Test webhook sent successfully" with a blankk reponse. However, the output log file is not written to, no email is sent and there is nothing logged to the http server error log or the php error log. My php version is 5.3.3. What am I doing wrong?
<?php
error_reporting(15);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
require_once('vendor/autoload.php');
\Stripe\Stripe::setApiKey("secret_test_key");
$handle = fopen("webhook.log","a");
// Retrieve the request's body and parse it as JSON
$input = file_get_contents("php://input");
$event_json = json_decode($input);
// Do something with $event_json
if (fwrite($handle, $event_json) === FALSE) {
mail("mike#example.com","Cannot write to webhook.log","");
echo "Cannot write to webhook.log";
exit;
}
mail('mike#example.com','Webhook Event',$event_json);
header(':', true, 200);
//http_response_code(200); // PHP 5.4 or greater
?>
You have a few potential problems. As a quick rule of thumb, the best way to debug this is to start by triggering the event yourself, which you can do simply by loading up your webhook url in a browser yourself. Then you can test it directly and make sure it is doing what you expect it to be doing. There are obviously two possibilities:
Stripe is not triggering your webhook handler for some reason
Your handler is not properly logging itself
The latter first: it could be that Stripe is triggering your handler but it isn't logging that fact successfully. This would mean that both your email logging and file logging are failing. That is actually quite possible. Email logging with the mail function is actually very unreliable, unless you know for a fact that it works. Mail sent with the mail function is dropped silently by most modern email systems (gmail, etc) unless you have your DNS records properly configured, which most people don't. So unless you know for sure that your mail attempt is working properly, it probably isn't. If you also happen to have a permission issue in your attempt to write to a log file (which is not uncommon for a newly setup server), your logs could simply be failing. The easiest way to check that is to load up the webhook URL in a browser yourself. That way you know it is being triggered, and can know for sure if the issue is improper logging or Stripe not calling your webhook.
If you determine for sure that stripe isn't calling your webhook, the most likely culprit would be an invalid HTTPS certificate. Is your webhook connected via HTTPS (it should be)? If so, is it a valid certificate? You can tell your browser to ignore an invalid certificate when you browse your own site, but stripe will simply refuse to send the request if it encounters an invalid certificate.
If none of the above fixes it then it will be time for more digging, but I would start with those: they are probably the most likely problems.
The solution is that $event_json is an object and the fwrite failed because it expects a string not an object. By converting to an array and then serializing I was able to both write to the log and send the email.
$event_json = (array)json_decode($input);
$event = serialize($event_json);
What I have:
foreach ($contacts as $contact) {
$this->StocklistMailer($contact, $weekOrDay, $data, $content, $itemGroup);
}
return new Response('completed', 204);
What I would like is :
foreach ($contacts as $contact) {
$this->StocklistMailer($contact, $weekOrDay, $data, $content, $itemGroup);
return new Response($contact->getEmail, 204);
}
return new Response('completed', 204);
And it returns to a AJAX call on the page, the reason I want to accomplish this is because I want realtime feedback to whom it has send an email to.
You can't send multiple responses from your application, the whole idea is that you only generate one response.
However, you can put all the information you require in one response.
$emails = [];
foreach ($contacts as $contact) {
$this->StocklistMailer($contact, $weekOrDay, $data, $content, $itemGroup);
$emails[] = $contact->getEmail;
}
return new Response(json_encode($emails), 200);
Note that I changed 204 (No content) to 200 (OK).
You would have to break it into multiple calls;
First call will return the array of contacts which you will store in your JavaScript array. (assuming you are using Client Side JS/Ajax to call this php file).
Then loop through the array and make as many calls to php as many contacts are in your array while passing each contact at a time to php.
You can show fancy progress bar as you are looping through the array :)
You can do that, but not from Symfony. Look into ReactPHP, Ratchet and related technologies.
https://blog.wyrihaximus.net/2015/03/reactphp-sockets/
http://socketo.me/docs/hello-world
You can create a websockets server that would listen on localhost for messages from your Symfony application and will be redirecting them using websockets to the browser.
The cliend would open a websocket connection to your websockets server, and send the request to your application. While the application is processing, it is sending the progress to the websocket server using a socket on the local machine. The client should be getting the progress realtime from the websocket and should be displaying it.
This way, you get a realtime interactive interface, with a long-running process.
Even better would be creating a rabbitmq worker, that would be sending the emails and reporting the progress to the websockets server. You would create the task for the worker from your Symfony application, and therefore you wouldn't be limited by the execution time limit for php requests. Another win with the rabbitmq worker is that you can have only one (or as many as you like), and therefore the tasks will queue and you won't be burning server resources by 50 processes generating and sending emails at once.
I personally don't see how this can be done even though these are my servers. But I want to know if my servers can reach external sites--ping a generic website for example--have outgoing communication. That is, I want to use execute a PHP script on one server, connecting to another of my servers, and test if the second server can ping a website, for example. So I know how to use PHP on the server my script is executing from to ping a website with fopensocket. I just don't know how to set this up to test another server's pingability. I mean I have the credentials but the only way is to have my script on each and every server and then reach the script and execute them. That is not what I want. I want to do this from the one/external server and just feed my script the ip/port/uid/pwd of the server I want to test.
An easy API would look something like:
SERVER1:
// get response from server2
$response = file_get_contents('http://www.server2.com/api.php?method=ping&ip=IP&port=PORT&uid=UID&pwd=PWD');
// do json_decode() if response is json string
SERVER2 (api.php):
// respond to API call
if (isset($_GET['method']) && $_GET['method'] == 'ping') {
// get other params and do your ping function
echo $pingresult; // perhaps a json encoded array
exit;
}
There is no security so you could send an API password or do it with OAuth or HMAC
I have a php script which is responsible for reading some request parameters from my iPhone app. Once I do some manipulations to it I save them in db and will need to send some push notification message using apple APNS. So currently its done like this in the code.
<?php
$param1 = $_POST['param1'];
$param2 = $_POST['param2'];
//saving part here
//push notifications
$pushService = new PushService();
$pushService -> init();
$pushService -> push($param1, $param2);
//json response
echo json_encode(array($success, $dbsavedid);
?>
Problem occurs with the push part. Now it takes lot of time for this push notification code chunk to execute because the table has grown with lot of data. Hence the iPhone app waits too long for this to execute (to get the success response to iPhone).
Hence is there any way to make this push part asynchronous and send a response to iPhone side using the echo other than using a separate script for push notifications? Also note that I need to get some data from saved records as well to iPhone side. So I will need the output to reach the iPhone side.
You can force PHP to send a response by using the flush() function for example. (there might be other possibilities to accomplish too)
So what you have to do is write with echo to the output buffer when your db operations finished (these should be really fast if you have 100-1000 records) and right after call the flush() function. Your client should get a response right away.
Also see this link about flush() itself, because there might be other parameters of your enviroment which prevents your response in reaching the client side as soon as expected.
http://php.net/manual/en/function.flush.php
<?php
$param1 = $_POST['param1'];
$param2 = $_POST['param2'];
//saving part here
//json response
echo json_encode(array($success, $dbsavedid);
//response should be sent right away, no need for wait on the pushservice operations
flush();
//push notifications
$pushService = new PushService();
$pushService -> init();
$pushService -> push($param1, $param2);
?>
The actual reasons for this is, my server provider has blocked port 2195 and port 2196 which is used by apple APNS. I believe once you allow it this will be fixed and should work like earlier.