I'm writing a simple code to simply show to clients, data that is actually loaded from another HTTP server. The problem is that loading it from the remote server can take up to multiple seconds, and I don't want that much page load delay. So, I make my server cache a copy of this data. So that whenever a client sends a request to my server, it sends the ready-loaded copy and then loads a new copy from the remote server to update the local copy in case any changes were made.
So here's my pseudo code:
if(file_exists($cache_path)){
echo file_get_contents($cache_path);
// I need to end the HTTP request and close the connection here while continuing with the code.
$uptodate_content = file_get_contents("https://docs.google.com/document/export?format=pdf&id=$id");
// I don't want the user to wait for nothing, until this line.
}
else {
$uptodate_content = file_get_contents("https://someremotehost.com/someresource");
echo $uptodate_content;
}
echo file_put_contents($cache_path, $uptodate_content);
Hi I think the best solution is using a queue For example if you use the the queue, you can send it to the queue and then your consumer can pick it from the queue when it has time and user do not need to wait for it
This link is helpful
And this link will help you to use redis for this problem
This is a bad practice.
The connection can never end and you should be careful with such code
The better method is to run a cron job/queue every houerget data from remote server, or alternatively the remote server will trigger a trigger when updating data.
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort();
ob_start();
//your code
//your code
//your code
echo "response foo bar";
$obSize = ob_get_length();
header("Content-Length: $obSize");
ob_end_flush();
flush();
session_write_close();
// Do processing here
request_to_remote_server();
One way of doing it:
First, create a new PHP file, let's call it update.php, and write the following:
if (isset($argv[1])) {
storeDocumentToCache($argv[1]);
}
And in your current file, change the code to:
echo readDocumentFromCache($id) ?? storeDocumentToCache($id);
In old PHP versions (<7) it should be:
$content = readDocumentFromCache($id);
echo isset($content) ? $content : storeDocumentToCache($id);
Then require the following helper functions in both files (and set $cache_path):
function readDocumentFromCache($id, $fetch = true)
{
$cache_path = "?";
if (file_exists($cache_path)) {
return file_get_contents($cache_path);
}
if ($fetch) {
execInBackground("php " . __DIR__ . "/update.php $id");
}
return null;
}
funciton storeDocumentToCache($id)
{
$cache_path = "?";
$uptodate_content = file_get_contents("https://docs.google.com/document/export?format=pdf&id=$id");
file_put_contents($cache_path, $uptodate_content);
return $uptodate_content;
}
function execInBackground($cmd)
{
if (substr(php_uname(), 0, 7) == "Windows") {
pclose(popen("start /B " . $cmd, "r"));
} else {
exec($cmd . ' > /dev/null 2>/dev/null &');
}
}
Related
I'm working on a one-way messaging system using server-sent events. I have a file (server.html) which sends the contents of a textarea to a PHP file (handler.php).
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
This works (alert(val) displays the text in the textarea).
My handler.php code looks like this:
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$stringData = $_POST['s'];
echo "data: Data is {$stringData}\n\n";
flush();
And the relevant part of my SSE receiver file (client.html) is as follows:
if(typeof(EventSource) !== "undefined") {
var source = new EventSource("handler.php");
source.onmessage = function(event) {
var textarea = document.getElementById('subtitles');
textarea.value += event.data + "<br>";
textarea.scrollTop = textarea.scrollHeight;
};
} else {
document.getElementById("subtitles").value = "Server-sent events not supported.";
}
The problem is that client.html only displays "data: Data is", so the text from server.html is getting lost somewhere along the way. I imagine it's the PHP code that's falling over, but I can't work out what's wrong. If anyone can help, I'd appreciate it.
EDIT
I chose to use SSE as opposed to websockets as I only need one-way communication: server.html should push the contents of its textarea to client.html whenever it changes. All the examples of SSE that I've looked at (and I've looked at a lot!) send "automatic" time-based data. I haven't seen any that use real-time user input. So perhaps I should clarify my original question and ask, "How can I use SSE to update a DIV (or whatever) in web page B whenever the user types in a textarea in web page A?"
UPDATE
I've narrowed the issue down to the while loop in the PHP file and have therefore asked a new question: Server-side PHP event page not loading when using while loop
Assuming you want to send a value from server.html and a value at client.html will be automatically updated...
You will need to store the new value somewhere because multiple instances of a script do not share variables just like that. This new value can be stored in a file, database or as a session variable, etc.
Steps:
Send new value to phpScript1 with clientScript1.
Store new value with phpScript1.
Connect clientScript2 to phpScript2.
Send stored value to clientScript2 if it is changed.
Getting the new value 'on the fly' means phpScript2 must loop execution and send a message to clientScript2 whenever the value has been changed by clientScript1.
Of course there are more and different approaches to achieve the same results.
Below there's some code from a scratchpad I've used in previous project.
Most parts come from a class (which is in development) so I had to adopt quite a lot of code. Also I've tried to fit it into your existing code.
Hopefully I didn't introduce any errors.
Do note I did not take any validation of your value into account! Also the code isn't debugged or optimized, so it's not ready for production.
Client side (send new value, e.g. your code):
function sendSubtitle(val) {
var xhr = new XMLHttpRequest();
var url = "handler.php";
var postdata = "s=" + val;
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(postdata);
//alert(val);
}
Server side (store new value):
<?php
session_start();
$_SESSION['s'] = $_POST['s'];
Client side (get new value):
//Check for SSE support at client side.
if (!!window.EventSource) {
var es = new EventSource("SSE_server.php");
} else {
console.log("SSE is not supported by your client");
//You could fallback on XHR requests.
}
//Define eventhandler for opening connection.
es.addEventListener('open', function(e) {
console.log("Connection opened!");
}, false);
//Define evenhandler for failing SSE request.
es.addEventListener('error', function(event) {
/*
* readyState defines the connection status:
* 0 = CONNECTING: Connecting
* 1 = OPEN: Open
* 2 = CLOSED: Closed
*/
if (es.readyState == EventSource.CLOSED) {
// Connection was closed.
} else {
es.close(); //Close to prevent a reconnection.
console.log("EventSource failed.");
}
});
//Define evenhandler for any response recieved.
es.addEventListener('message', function(event) {
console.log('Response recieved: ' + event.data);
}, false);
// Or define a listener for named event: event1
es.addEventListener('event1', function(event) {
var response = JSON.parse(event.data);
var textarea = document.getElementById("subtitles");
textarea.value += response + "<br>";
textarea.scrollTop = textarea.scrollHeight;
});
Server side (send new value):
<?php
$id = 0;
$event = 'event1';
$oldValue = null;
session_start();
//Validate the clients request headers.
if (headers_sent($file, $line)) {
header("HTTP/1.1 400 Bad Request");
exit('Headers already sent in %s at line %d, cannot send data to client correctly.');
}
if (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] != 'text/event-stream') {
header("HTTP/1.1 400 Bad Request");
exit('The client does not accept the correct response format.');
}
//Disable time limit
#set_time_limit(0);
//Initialize the output buffer
if(function_exists('apache_setenv')){
#apache_setenv('no-gzip', 1);
}
#ini_set('zlib.output_compression', 0);
#ini_set('implicit_flush', 1);
while (ob_get_level() != 0) {
ob_end_flush();
}
ob_implicit_flush(1);
ob_start();
//Send the proper headers
header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); // Disables FastCGI Buffering on Nginx
//Record start time
$start = time();
//Keep the script running
while(true){
if((time() - $start) % 300 == 0){
//Send a random message every 300ms to keep the connection alive.
echo ': ' . sha1( mt_rand() ) . "\n\n";
}
//If a new value hasn't been sent yet, set it to default.
session_start();
if (!array_key_exists('s', $_SESSION)) {
$_SESSION['s'] = null;
}
//Check if value has been changed.
if ($oldValue !== $_SESSION['s']) {
//Value is changed
$oldValue = $_SESSION['s'];
echo 'id: ' . $id++ . PHP_EOL; //Id of message
echo 'event: ' . $event . PHP_EOL; //Event Name to trigger the client side eventhandler
echo 'retry: 5000' . PHP_EOL; //Define custom reconnection time. (Default to 3000ms when not specified)
echo 'data: ' . json_encode($_SESSION['s']) . PHP_EOL; //Data to send to client side eventhandler
//Note: When sending html, you might need to encode with flags: JSON_HEX_QUOT | JSON_HEX_TAG
echo PHP_EOL;
//Send Data in the output buffer buffer to client.
#ob_flush();
#flush();
}
//Close session to release the lock
session_write_close();
if ( connection_aborted() ) {
//Connection is aborted at client side.
break;
}
if((time() - $start) > 600) {
//break if the time exceeds the limit of 600ms.
//Client will retry to open the connection and start this script again.
//The limit should be larger than the time needed by the script for a single loop.
break;
}
//Sleep for reducing processor load.
usleep(500000);
}
You called handler.php first time in the server.html and again in client.html. Both are different processes. The variable state won't be retained in the web server. You need to store it somewhere if you want that value in another PHP process. May be you can use sessions or database.
While using sessions you can store the values in two files like:
<?php
//server.php
session_start();
$_SESSION['s'] = $_POST['s'];
And in client.php
<?php
//client.php
session_start();
echo "data: Data is ".$_SESSION['s']."\n\n";
I'm creating a button on my web page.I want that when someone presses this button an execution of a process of Linux commands on my first server (like "cd file" "./file_to_execute"..) when these commandes are done and finished i want to connect on another server by ssh and to execute another commands.
the probleme is how can i know that the commands before are already finished to proceed to the second part which is to connect on another server .
to resume :
first step connect on the first server , execute some commands
=> when these commands are done ( the part i dont know how to do it )
second step : to connect on another server and execute some others commands.
I'm searching for a way that will allows me to add some pop up to inform the user of my web page that he finished the first step and he started the second.
<?php
$hostname = '192.177.0.252';
$username = 'pepe';
$password = '*****';
$commande = 'cd file && ./file_one.sh';
if (false === $connection_first = ssh2_connect($hostname, 22)) {
echo 'failed<br />';
exit();
}
else {
echo 'Connected<br />';
}
if (false === ssh2_auth_password($connection_first, $username, $password)) {
echo 'failed<br />';
exit();
}
else {
echo 'done !<br />';
}
if (false === $stream = ssh2_exec($connection_first, $commande)) {
echo "error<br />";
}
?>
Thanks
PS: sorry for my English, I'm from Barcelone
To handle events where an exception occurs i would recommend using a try/catch statement, like the one below:
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
When you're trying to handle events and need to know when they finish, there are a few ways to achieve this. You can either set a boolean to true after the command has been executed (like what you are already doing). OR you can return output from the command by printing the output to a txt file and then echoing out the returns of this file. See code below:
exec ('/usr/bin/flush-cache-mage > /tmp/.tmp-mxadmin');
$out = nl2br(file_get_contents('/tmp/.tmp-mxadmin'));
echo $out;
At this point you can create conditions based off of what is returned in the $out variable.
I'm currently putting together a small web-based GUI to generate kickstart-scripts. I got a confirmation page that's sending the relevant data via POST to the PHP-page where the actual shell script is called to build the iso. So far it's working, but the page seems to execute the script before it outputs anything else (for example, the 'echo' I put in at the beginning of the page ...), and I'm absolutely clueless why. Would anyone care to enlighten me?
Here's the code to the PHP-page that's executing the shell script ...
echo 'Generating your ISO; this might take a while...';
sleep(20);
if (!isset($_POST['auth'])) {
$ad = 'N';
}
else {
$ad = 'Y';
}
if (!isset($_POST['oracle'])) {
$oracle = 'N';
}
else {
$oracle = 'Y';
}
if ((!isset($_POST['ip'])) or (!isset($_POST['hostname'])) or (!isset($_POST['rhsel'])) or (!isset($_POST['submit'])) or (!isset($_POST['gw'])) or (!isset($_POST['nm']))) {
die('Please use the correct form !');
}
if (isset($_POST['ip'])) {
$ip = trim($_POST['ip']);
}
if (isset($_POST['gw'])) {
$gw = trim($_POST['gw']);
}
if (isset($_POST['nm'])) {
$nm = trim($_POST['nm']);
}
if (isset($_POST['hostname'])) {
$hostname = trim($_POST['hostname']);
}
if (isset($_POST['rhsel'])) {
$rhsel = $_POST['rhsel'];
}
passthru("/usr/bin/sudo /data/skripte/webconfig.sh $rhsel $oracle $ad $ip $gw $nm $hostname 2>&1");
PHP scripts accessed via a browser are request-response, meaning all processing is done on the server prior to headers and content being sent to the client. This means you will not get a continually updating output like you would see on the command line. There is no way around this. Sorry.
How can you mimic a command line run of a script with arguements inside a PHP script? Or is that not possible?
In other words, let's say you have the following script:
#!/usr/bin/php
<?php
require "../src/php/whatsprot.class.php";
function fgets_u($pStdn) {
$pArr = array($pStdn);
if (false === ($num_changed_streams = stream_select($pArr, $write = NULL, $except = NULL, 0))) {
print("\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n");
return FALSE;
} elseif ($num_changed_streams > 0) {
return trim(fgets($pStdn, 1024));
}
}
$nickname = "WhatsAPI Test";
$sender = ""; // Mobile number with country code (but without + or 00)
$imei = ""; // MAC Address for iOS IMEI for other platform (Android/etc)
$countrycode = substr($sender, 0, 2);
$phonenumber=substr($sender, 2);
if ($argc < 2) {
echo "USAGE: ".$_SERVER['argv'][0]." [-l] [-s <phone> <message>] [-i <phone>]\n";
echo "\tphone: full number including country code, without '+' or '00'\n";
echo "\t-s: send message\n";
echo "\t-l: listen for new messages\n";
echo "\t-i: interactive conversation with <phone>\n";
exit(1);
}
$dst=$_SERVER['argv'][2];
$msg = "";
for ($i=3; $i<$argc; $i++) {
$msg .= $_SERVER['argv'][$i]." ";
}
echo "[] Logging in as '$nickname' ($sender)\n";
$wa = new WhatsProt($sender, $imei, $nickname, true);
$url = "https://r.whatsapp.net/v1/exist.php?cc=".$countrycode."&in=".$phonenumber."&udid=".$wa->encryptPassword();
$content = file_get_contents($url);
if(stristr($content,'status="ok"') === false){
echo "Wrong Password\n";
exit(0);
}
$wa->Connect();
$wa->Login();
if ($_SERVER['argv'][1] == "-i") {
echo "\n[] Interactive conversation with $dst:\n";
stream_set_timeout(STDIN,1);
while(TRUE) {
$wa->PollMessages();
$buff = $wa->GetMessages();
if(!empty($buff)){
print_r($buff);
}
$line = fgets_u(STDIN);
if ($line != "") {
if (strrchr($line, " ")) {
// needs PHP >= 5.3.0
$command = trim(strstr($line, ' ', TRUE));
} else {
$command = $line;
}
switch ($command) {
case "/query":
$dst = trim(strstr($line, ' ', FALSE));
echo "[] Interactive conversation with $dst:\n";
break;
case "/accountinfo":
echo "[] Account Info: ";
$wa->accountInfo();
break;
case "/lastseen":
echo "[] Request last seen $dst: ";
$wa->RequestLastSeen("$dst");
break;
default:
echo "[] Send message to $dst: $line\n";
$wa->Message(time()."-1", $dst , $line);
break;
}
}
}
exit(0);
}
if ($_SERVER['argv'][1] == "-l") {
echo "\n[] Listen mode:\n";
while (TRUE) {
$wa->PollMessages();
$data = $wa->GetMessages();
if(!empty($data)) print_r($data);
sleep(1);
}
exit(0);
}
echo "\n[] Request last seen $dst: ";
$wa->RequestLastSeen($dst);
echo "\n[] Send message to $dst: $msg\n";
$wa->Message(time()."-1", $dst , $msg);
echo "\n";
?>
To run this script, you are meant to go to the Command Line, down to the directory the file is in, and then type in something like php -s "whatsapp.php" "Number" "Message".
But what if I wanted to bypass the Command Line altogether and do that directly inside the script so that I can run it at any time from my Web Server, how would I do that?
First off, you should be using getopt.
In PHP it supports both short and long formats.
Usage demos are documented at the page I've linked to. In your case, I suspect you'll have difficulty detecting whether a <message> was included as your -s tag's second parameter. It will probably be easier to make the message a parameter for its own option.
$options = getopt("ls:m:i:");
if (isset($options["s"] && !isset($options["m"])) {
die("-s needs -m");
}
As for running things from a web server ... well, you pass variables to a command line PHP script using getopt() and $argv, but you pass variables from a web server using $_GET and $_POST. If you can figure out a sensible way to map $_GET variables your command line options, you should be good to go.
Note that a variety of other considerations exist when taking a command line script and running it through a web server. Permission and security go hand in hand, usually as inverse functions of each other. That is, if you open up permissions so that it's allowed to do what it needs, you may expose or even create vulnerabilities on your server. I don't recommend you do this unless you'll more experienced, or you don't mind if things break or get attacked by script kiddies out to 0wn your server.
You're looking for backticks, see
http://php.net/manual/en/language.operators.execution.php
Or you can use shell_exec()
http://www.php.net/manual/en/function.shell-exec.php
I am having problem with my IRC Bot script, I have implemented it into my Curl transfer method.
I have a problem, once the IRC bot sends a message to the IRC channel, all of the "echo" at the end of the script does not show and the page hangs. The whole Apache hangs.
<?php
$ircServer = "///";
$ircPort = "6667";
$ircChannel = "#bots";
set_time_limit(0);
$msg = $_POST['msg'];
$paper = $_POST['paper'];
$sizzor = $_POST['sizzor'];
$hand = $_POST['hand'];
$ircSocket = fsockopen($ircServer, $ircPort, $eN, $eS);
if ($ircSocket)
{
fwrite($ircSocket, "USER Lost rawr.test lol :code\n");
fwrite($ircSocket, "NICK Rawr" . rand() . "\n");
fwrite($ircSocket, "JOIN " . $ircChannel . "\n");
ignore_user_abort(TRUE); // Noob Close down page
fwrite($ircSocket, "PRIVMSG " . $ircChannel . " :" . $msg . "\n");
while(1)
{
while($data = fgets($ircSocket, 128))
{
echo nl2br($data);
flush();
// Separate all data
$exData = explode(' ', $data);
// Send PONG back to the server
if($exData[0] == "PING")
{
fwrite($ircSocket, "PONG ".$exData[1]."\n");
}
}
echo $eS . ": " . $eN;
}
}
?>
if ($bootcontents == 'success') {
echo '<center><marquee behavior="alternate" direction="left">Spinning xxx at ' . $power . '% power.</marquee></center>';
This part does not show during the script:
if ($bootcontents == 'success') {
echo '<center><marquee behavior="alternate" direction="left">Spinning xxx at ' . $power . '% power.</marquee></center>';
The page just hangs, if I add the exit(); function onto near the top the whole "echo" info does not show.
Please can someone help.
You are creating an infinite loop:
while (1)
// ...
This loop can never finish, since you did not use an exit statement (like break). Therefore the code after the infinite loop is never executed.
Furthermore is it a busy loop (using a lot of CPU resources), so the whole apache (and computer) will hang.
You're leaving some lines out of the <?php ?> tags, so whatever is outside them will be treated as plain text. You fix it moving the closing ?> tags further down:
[this is the while(1) closing bracket]
}
// code past this line will never run, see below for details
echo $eS . ": " . $eN;
}
}
if ($bootcontents == 'success') {
echo '<center><marquee behavior="alternate" direction="left">Spinning xxx at ' . $power . '% power.</marquee></center>';
}
?> <!-- closing tag goes here -->
The page would anyway not work properly because the while(1) loop is missing an exit condition:
while(1) {
while($data = fgets($ircSocket, 128)) {
// ...
}
}
After the inner while finishes, your script keeps looping, ending up trapped in an empty, infinite loop (which would hang the server up, if it's not configured to detect and kill this kind of loophole).
On a final notice, PHP isn't probably the best tool for the job: you would be much better off with a stand-alone application.
while($data = fgets($ircSocket, 128))
This part blocks the script running until it receives data, and if somehow you're not getting data through that socket... well you're stuck there... forever... lol ok, stuck until the PHP script times out.
If that part doesn't catch, you're still stuck inside the while loop and so there is no way of ever running the part of your code that echos stuff out... so both apfelbox and Alex are correct, just not explained fully...
In order to have a infinite loop but also be able to run code outside, you would need to catch the "event" in which you want to capture and run code. All the events you want to capture would need to sit inside the while loop, or at least dispatched from the while loop to a function that would parse the input from server and respond correctly.
An even better way to do this is to utilise the observer pattern.
I really wouldn't make an IRC bot with PHP, even if you run it via commandline... PHP isn't meant to run as a long-running application.