Using PHP exec() send back information in steps - php

I am using PHP exec() to install Composer dependencies:
exec('php composer.phar install -d ' . dirname(__DIR__), $out);
This is triggered via an ajax post request. Everything works fine and the return from exec() is printed to the screen.
exit(json_encode($out));
However, what I am after is a way to periodically send data back to the ajax callback so I can render each bit of information rather than render the whole block at once.
Not sure if this is possible though.
I should mention that the servers where this would be ran would not have anything like NodeJS and would very likely be shared hosting.

Security issues of exec'ing on-demand aside, if you are able to write/log the status of the output to a file, you can write a simple AJAX poller (since you prefer not to use WebSockets).
In your exec, try:
add-task.php
$jobId = uniqid();
$outfile = $jobId . "-results.txt";
exec('php composer.phar install -d ' . dirname(__DIR__) . " > $outfile &", $out);
$result = array("jobId" => $jobId);
echo json_encode($result);
Ok, now send that $jobId down to the client, so that they can poll for updates. I'm using a variation of a concept project on github: https://github.com/panique/php-long-polling
server.php
$jobid = isset($_GET['jobid']) ? $_GET['jobid'] : null;
$outputfile = $jobid . "-results.txt";
$lastTimestamp = isset($_GET['timestamp']) ? (int)$_GET['timestamp'] : null;
// get timestamp of when file has been changed the last time
clearstatcache();
$lastModified = filemtime($outputfile);
// if no timestamp delivered via ajax
// or data.txt has been changed SINCE last ajax timestamp
if ($lastTimestamp == null || $lastModified > $lastTimestamp) {
// get content of data.txt
$data = file_get_contents($outputfile);
// put data.txt's content and timestamp of
// last data.txt change into array
$result = array(
'data' => $data,
'timestamp' => $lastTimestamp
);
// encode to JSON, render the result (for AJAX)
$json = json_encode($result);
} else {
// No updates in the file, just kick back the current time
// and the client will check back later
$result = array(
'timestamp' => time()
);
$json = json_encode($result);
}
header("Content-Type: application/json");
echo $json;
Then, in the browser, you just have a tiny client that polls for it's 'jobid' to check on status.
client.js
$(function () {
var jobid = '12345';
function checkForUpdates(timestamp) {
$.ajax({
type: 'GET',
url: 'http://localhost/server.php',
data: {
'timestamp': timestamp,
'jobid': jobid
},
success: function (data) {
var obj = jQuery.parseJSON(data);
if (obj.data) {
// Update status content
$('#response').text(obj.data);
}
// Check again in a second
setTimeout(function () {
checkForUpdates(obj.timestamp);
}, 1000);
}
});
}
checkForUpdates();
});
index.html
<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script type="text/javascript" src="client.js"></script>
</head>
<body>
<h1>Status:</h1>
<div><pre id="response"></pre></div>
</body>
</html>

Related

Obtain AJAX returned object in PHP

I have a program which is running server script on raspberry pi (client which is also a server). I'm scanning a barcode which then executes few commands (including generating XML file). When I submit the form with the 'serial' number, I want to be able to retrieve the filename (string) returned from AJAX ($_POST) method in server.php? if (isset($_POST['filename']) does not return the filename, how do I obtain filename with a single AJAX? and use it in PHP? I have no error messages, the $_POST['filename'] is empty. I tried separating the script into a different file and creating another AJAX calling that PHP script but it did not fully work and I wonder if there is a possibility to do it with a single AJAX and make PHP listen for the returned filename.
Or maybe is there a better way to obtain the filename of the external file than through client-side? (there is always single XML file waiting to be picked up).
server.php
<?php
$show_error = "";
if (isset($_POST['serial'])) {
$serialnumber = $_POST['serial'];
if ($serialnumber > 0) {
if (isset($_POST['filename'])) {
$filenamer = $_POST['filename'];
echo $filenamer;
} else {
echo "no filename returned from ajax call";
}
$remote_file_url = 'http://' . $_SERVER['REMOTE_ADDR'] . '/345.xml'; //FILENAME NEEDED
$local_file = '345.xml'; //FILENAME NEEDED
$copy = copy( $remote_file_url, $local_file );
}
?>
<html>
<body>
<form name="test" method="post">
<input type="number" name="serial" id="serial" value="1">
<input type="submit" name="">
</form>
</body>
<script type="text/javascript">
function scan(serialnumber)
{
return $.ajax({
url : 'http://localhost/test.php',
type : 'POST',
dataType : 'json',
data : { serial_no : serialnumber},
cache : false,
success : function(data) {
var filename = data[Object.keys(data)[1]];
console.log(filename);
}
});
};
scan(<?php echo $serialnumber; ?>);
</script>
</html>
test.php
<?php
header('Access-Control-Allow-Origin: *');
header('Content-type: text/json');
# Get the serial
$serial_no = $_POST['serial_no'];
$return['serial_no'] = $serial_no;
# Get the filename of the XML file
$filename = shell_exec('find /var/www/html/*.xml -printf "%f"');
$return['filename'] = $filename;
$return['scanpink'] = 1;
echo json_encode($return);
?>
As I mentioned in my comment, you don't have filename in php because your form does not include filename field. After receiveing filename from ajax you can do another ajax request with serial & filename fields or the second solution is to use a hidden field. After receiving data in ajax you cannot use them in php - You have to send it (filename) to php.

PHP MYSQL JQuery Long Polling - Not working as expected

My long polling implementation isn't working. Been having a very difficult time understanding where to look toward debugging said code.
Key Points
No Errors
Long polling working randomly (only responds to some changes in MySQL with no distinct pattern)
MySQL is updating correctly
I'm testing this via Localhost WAMP and two browsers with two different sessions
PHP Portion -
$path= $_SERVER[ 'DOCUMENT_ROOT'];
$path .= "/config.php" ;
require_once($path);
require_once(PHP_PATH . "/classes/user.php");
session_start();
require_once(PHP_PATH . "/functions/database.php");
// Return to Login if no Session
if(!isset($_SESSION['user'])){
header("Location: /login");
die();
}
$db = connectdatabase();
$timeout = 40;
// if no post ids kill the script // Should never get here
if(!isset($_POST['post_ids'])){
die();
}
if(!isset($_POST['timestamp'])){
die();
}
$last_ajax_call = $_POST['timestamp'];
$post_ids = trim(strip_tags($_POST['post_ids']));
$id = $_SESSION['user']->getID();
// Check if there are posts from the last search that need to be updated with a comments or the like number has to be updated
$query = "SELECT posts.*, users.first_name, users.last_name, users.picture
FROM posts
LEFT JOIN users
ON users.id = posts.user_id
WHERE ((UNIX_TIMESTAMP(posts.date) > :last_ajax_call OR UNIX_TIMESTAMP(posts.last_modified) > :last_ajax_call)
AND posts.parent IN (:post_ids)) OR (posts.id IN (:post_ids) AND UNIX_TIMESTAMP(posts.last_modified) > :last_ajax_call)";
while ($timeout > 0) {
$check_for_updates = $db->prepare($query);
$check_for_updates->bindParam(':post_ids', $post_ids);
$check_for_updates->bindParam(':last_ajax_call', $last_ajax_call);
$check_for_updates->execute();
$r = $check_for_updates->fetchAll();
if(!empty($r)){
// Get current date time in mysql format
$unix_timestamp = time();
// Cofigure result array to pass back
$result = array(
'timestamp' => $unix_timestamp,
'updates' => $r
);
$json = json_encode($result);
echo $json;
return;
} else {
$timeout --;
usleep( 250000 );
clearstatcache();
}
}
// you only get here if no data found
$unix_timestamp = time();
// Cofigure result array to pass back
$result = array(
'timestamp' => $unix_timestamp
);
$json = json_encode($result);
echo $json;
JQuery Ajax -
function getUpdates(timestamp) {
var post_ids = $("#newsfeed").find("#post_ids").attr('data-post-ids');
var data = {'timestamp' : timestamp,
'post_ids' : post_ids};
poll = $.ajax({
type: 'POST',
url: '/php/check_for_updates.php',
data: data,
async: true, /* If set to non-async, browser shows page as "Loading.."*/
cache: false,
success: function(data) {
try {
// put result data into "obj"
var obj = jQuery.parseJSON(data);
// put the data_from_file into #response
//$('#response').html(obj.data_from_file);
// repeat
console.log("SQL: " + obj['timestamp']);
setTimeout( function() {
// call the function again, this time with the timestamp we just got from server.php
getUpdates(obj['timestamp']);
}, 1000 );
} catch( e ) {
// repeat
// Get mysql formated date
var unix_timestamp = Math.floor(Date.now() / 1000);
console.log("JS: " + unix_timestamp);
setTimeout( function() {
getUpdates(unix_timestamp);
}, 1000 );
}
}
}
);
}
Thanks for all the help guys! I asked around a lot of people and got a bunch of great places to look to debug the code.
I finally found the answer here -
http://blog.preinheimer.com/index.php?/archives/416-PHP-and-Async-requests-with-file-based-sessions.html
http://konrness.com/php5/how-to-prevent-blocking-php-requests/
It looks like I the PHP checking for updates was blocking any updates from happening till the PHP stop checking for updates.
Couple things you can do is:
1.) Open the Chrome Developer tools and then click on the Network tab and clear everything out. Then click on submit. Look at the network tab and see what is being posted and what isn't. Then adjust accordingly from there.
2.) Echo out different steps in your php script and do the same thing with the Network tab and then click on the "results" area and see what's being echoed out and if it's as expected.
From there, you should be able to debug what's happening and figure out where it's going wrong.

Want to perform php using OnClick function without clearing the current web page

This is the js script at the bottom of my wp post.
<script type="text/javascript" src="jquery-1.10.2.min.js">
</script>
<script type="text/javascript">
var id = 'downloadid';
var data_from_ajax;
$.post('download.php', {id : id}) .done(function(data) {
data_from_ajax = data;
});
function hey() {
document.write(data_from_ajax);
}
</script>
Function hey was being called from a link OnClick function. When using this, the page would successfully perform the php code on download php (update a db then download a file) although it would clear the current page I was on. What I wanted to do was perform the php and keep the current page template. So next I tried using
document.getElementById("download").innerHTML = data_from_ajax;
instead of document.write. I made a div with the id download. Now when I click it, it simply won't perform the php. when I replace the data_from_ajax with a string, it gladly puts it in the div though.
Any help would be great.
EDIT:
my html is
download
<div id='download'>&nbsp</div>
http://jsfiddle.net/7smJE/
From PHP code which you've provided, I think you should replace document.write() in your code with $('#download').html(). This way you don't need to put the returned result in your download div anymore because when PHP page gets loaded it'll do this for you and you have to put your $.post in hey() function too because you need this to perform when your link gets clicked.
PHP:
<?php
$fileid = $id;
if (is_file('d84ue9d/' . $fileid . '.apk'))
{
$ip = $_SERVER['REMOTE_ADDR'];
$con=mysqli_connect("localhost","docvet95_check","%tothemax%","docvet95_downcheck");
$result = mysqli_query($con,"SELECT * FROM `download-check` where ip = '$ip'");
while ($row = mysqli_fetch_array($result))
{
$files = $row['files'];
$downloads = $row['downloads'];
}
if ($downloads > 4)
{
print "$('#download').html(unescape('%3C%73%63%72%69%70%74%20%74%79%70%65%3D%22%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%22%3E%0A%61%6C%65%72%74%28%27%59%6F%75%5C%27%76%65%20%64%6F%77%6E%6C%6F%61%64%65%64%20%66%69%76%65%20%6F%72%20%6D%6F%72%65%20%66%69%6C%65%73%2E%20%46%6F%72%20%72%69%67%68%74%20%6E%6F%77%2C%20%74%68%69%73%20%69%73%20%6F%6B%61%79%2E%20%49%6E%20%74%68%65%20%66%75%74%75%72%65%2C%20%79%6F%75%20%77%69%6C%6C%20%6E%65%65%64%20%74%6F%20%63%6F%6D%70%6C%65%74%65%20%61%20%73%75%72%76%65%79%20%69%6E%20%6F%72%64%65%72%20%74%6F%20%63%6F%6E%74%69%6E%75%65%20%64%6F%77%6E%6C%6F%61%64%69%6E%67%2E%20%54%68%61%6E%6B%20%79%6F%75%20%66%6F%72%20%75%73%69%6E%67%20%6F%75%72%20%77%65%62%73%69%74%65%27%29%3B%20%0A%77%69%6E%64%6F%77%2E%6F%70%65%6E%28%27%2F%61%70%6B%73%2F%64%38%34%75%65%39%64%2F". $fileid . "%2E%61%70%6B%27%2C%27%5F%73%65%6C%66%27%29%0A%3C%2F%73%63%72%69%70%74%3E'));";
}
else
{
$downloadq = $downloads + 1;
$there = $result->num_rows;
if ($there <1)
{
$addidnip = mysqli_query($con,"INSERT INTO `download-check` (ip, files, downloads) VALUES ('$ip', '$fileid', 1)");
}
else
{
$idtoarray = explode(",", $files);
if (!in_array($fileid, $idtoarray))
{
array_push($idtoarray, $fileid);
$newfile = implode(",", $idtoarray);
$adddw = mysqli_query($con,"UPDATE `download-check` SET downloads=$downloadq, files='$newfile' where ip = '$ip'");
}
}
print "<script type=\"text/javascript\">";
print "$('#download').html(unescape('%3C%73%63%72%69%70%74%20%74%79%70%65%3D%22%74%65%78%74%2F%6A%61%76%61%73%63%72%69%70%74%22%3E%0A%77%69%6E%64%6F%77%2E%6F%70%65%6E%28%27%64%38%34%75%65%39%64%2F". $fileid . "%2E%61%70%6B%27%2C%27%5F%73%65%6C%66%27%29%0A%3C%2F%73%63%72%69%70%74%3E'));";
print "</script>";
}
}
else
{ echo 'Whoops, looks like we couldn\'t find that file. You could try searching for it?'; }
?>
JavaScript:
var id = 'downloadid';
var data_from_ajax;
function hey() {
$.post('download.php', {id : id});
}
But I recommend you to return the exact data from your PHP without any extra tag and then use it this way:
var id = 'downloadid';
function hey() {
$.post('download.php', {id : id}).done(function(data) {
$("#download").html(unescape(data));
});
}
From what I can see without the fiddle:
The hey function is probably fired before the done function is ready. Why don't you call hey() from within done()?

phantomJS : Absolute path working, but Relative path giving problems

I'm on a Linux web server. The following files are being used to create a screenshot:
ons.php
ong.js
ons2.php
All these files along with phantomJS binary are in the same folder. The folder's permission is 744
ons.php
$forMonth = date('M Y');
exec('./phantomjs ons.js '.strtotime($forMonth), $op, $er);
print_r($op);
echo $er;
ons.js
var args = require('system').args;
var dt = '';
args.forEach(function(arg, i) {
if(i == 1)
{
dt = arg;
}
});
var page = require('webpage').create();
page.open('./ons2.php?dt='+dt, function () { //<--- This is failing
page.render('./xx.png');
phantom.exit();
});
ons2.php
<!DOCTYPE html>
<html>
<head>
<title>How are you</title>
</head>
<body>
<?php
if(isset($_GET['dt']))
{
echo $_GET['dt'];
}
else
{
echo '<h1>Did not work</h1>';
}
?>
</body>
</html>
On opening ons.php in the browser, I'm getting this result:
Array ( ) 0
But no screenshot is being created.
Debugging
On debugging a lot, I found out that it has to do with paths.
--> If I put the following inside ons.js
.
.
.
var page = require('webpage').create();
page.open('http://www.abc.com/ppt/ons2.php', function () { // <-- absolute path
page.render('./xx.png');
phantom.exit();
});
The screenshot is getting created. I want to avoid using absolute paths as the application will be shifted to a different domain pretty soon.
What I don't get is why relative path is not working even if all files are in the same folder. Is my syntax of page.open('./ons2.php....') wrong?
./ons2.php implies a local file. It will not be passed through to the web server, and moreover it will fail outright because you also appended a query string - in the local file system this would be treated as part of the file name, so the file will not be located at all.
You will need to supply an absolute URL for this to work as you expect - but you can determine this dynamically in PHP (using $_SERVER) and pass it in to the JS script as a command line argument.
For example (untested):
ons.php
<?php
// Determine the absolute URL of the directory containing this script
$baseURL = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http')
. '://' . $_SERVER['HTTP_HOST']
. rtrim(dirname($_SERVER['REQUEST_URI']), '/') . '/';
$now = new DateTime('now'); // Because all the cool kids use DateTime
$cmd = './phantomjs ons.js '
. escapeshellarg($now->format('M Y')) . ' ' // Don't forget to escape args!
. escapeshellarg($baseURL)
. ' 2>&1'; // let's capture STDERR as well
// Do your thang
exec($cmd, $op, $er);
print_r($op);
echo $er;
ons.js
var args, url, page;
args = require('system').args;
if (args.length < 3) {
console.error('Invalid arguments');
phantom.exit();
}
url = args[2] + 'ons2.php?dt=' + encodeURIComponent(args[1]);
console.log('Loading page: ' + url);
page = require('webpage').create();
page.open(url, function () {
page.render('./xx.png');
phantom.exit();
});
ons2.php remains the same.
Maybe there is an issue in page.render but I don't think so. The most common case of hangs is unhandled exception.
I will suggest you 4 things to investigate the issue :
add an handler to phantom.onError and/or to page.OnError
encapsulate your code in try/catch blocks (such as for page.render)
Once the page is loaded, there is no test on callback status. It's better to check the status ('success' or 'fail')
seems to freeze when calling page.render. Have you tried a simpler filename in the current directory? Maybe the freeze is because of the security or invalid filename (invalid characters?)
Hope this will help you

Is there any reason why this cron job wouldn't be working

EDIT: Cron line: /usr/bin/php /usr/local/yy/yy/yy/webspace/httpdocs/test.mysite.ie/test.php > /dev/null 2>&1
I have written a script that functions as it should when i navigate to it in the browser. This is my first time trying to use a cron job so i'm not overly familiar with how they work. The script is below. As i said, the script works as it should if i navigate to the url in a web browser.
test.php
<script src="jquery.min.js"></script>
<script>
//SET UP JS VARIABLES
var allMatchedNumbers = new Array();
var matchedthingyNumbers;
var matchedthingyPlus1Numbers;
var matchedthingyPlus2Numbers;
var winningthingyNumbers = new Array();
var winningBonusNumber;
var winningthingyPlus1Numbers = new Array();
var winningPlus1BonusNumber;
var winningthingyPlus2Numbers = new Array();
var winningPlus2BonusNumber;
var thingyList;
var thingyListItems;
var thingyPlus1List;
var thingyPlus1ListItems;
var thingyPlus2List;
var thingyPlus2ListItems;
var userNumbers = new Array();
var displayCounter = 1;
var drawDate;
var thingyNumbers;
var thingyBonus;
var thingyPlus1;
var thingyPlus1Bonus;
var thingyPlus2;
var thingyPlus2Bonus;
//GET RESULTS & DATE FOR thingy, PLUS1, PLUS2 FROM THE DOM OBJECT ONLY
$(document).ready(function fetchResults(){
$.ajax({
url: "scrape_page.php",
success: function(data) {
}
});
$.ajax({
url: "latest_results.txt",
success: function(data) {
var dom = $(data);
//GET thingy DATE
drawDate = dom.find('.date-heading.fooRegular').contents().first().text();
//GET thingy NUMBERS
thingyNumbers = dom.find('.result-block').eq(0).find('.thingy-winning-numbers');
thingyBonus = dom.find('.result-block').eq(0).find('.thingy-bonus');
thingyPlus1 = dom.find('.result-block').eq(1).find('.thingy-winning-numbers');
thingyPlus1Bonus = dom.find('.result-block').eq(1).find('.thingy-bonus');
thingyPlus2 = dom.find('.result-block').eq(2).find('.thingy-winning-numbers');
thingyPlus2Bonus = dom.find('.result-block').eq(2).find('.thingy-bonus');
populateWinningNumbers();
}
});
});
//PUT WINNING NUMBERS IN ARRAY
function populateWinningNumbers() {
//MAIN thingy NUMBERS
thingyList = thingyNumbers;
thingyListItems = thingyList.find('li');
thingyPlus1List = thingyPlus1;
thingyPlus1ListItems = thingyPlus1List.find('li');
thingyPlus2List = thingyPlus2;
thingyPlus2ListItems = thingyPlus2List.find('li');
thingyListItems.each(function(index) {
winningthingyNumbers[index] = parseInt($(this).text());
});
//winningBonusNumber = parseInt($('#mainthingyBonus').find('li').text());
winningBonusNumber = parseInt($(thingyBonus).find('li').text());
winningthingyNumbers.push(winningBonusNumber);
//thingy PLUS NUMBERS
thingyPlus1ListItems.each(function(index) {
winningthingyPlus1Numbers[index] = parseInt($(this).text());
});
winningPlus1BonusNumber = parseInt($(thingyPlus1Bonus).find('li').text());
winningthingyPlus1Numbers.push(winningPlus1BonusNumber);
//PLUS 2
thingyPlus2ListItems.each(function(index) {
winningthingyPlus2Numbers[index] = parseInt($(this).text());
});
winningPlus2BonusNumber = parseInt($(thingyPlus1Bonus).find('li').text());
winningthingyPlus2Numbers.push(winningPlus2BonusNumber);
postDataToDB();
}
//POST OFFICIAL thingy NUMBERS TO DATABASE
function postDataToDB() {
$.ajax({
url: "postToDB.php",
type: "post",
data: {thingyNums:winningthingyNumbers, thingyPlus1Nums: winningthingyPlus1Numbers, thingyPlus2Nums: winningthingyPlus2Numbers, drawDate:drawDate},
// callback handler that will be called on success
success: function (data) {
}
});
}
</script>
scrape_page.php
<?php
include 'simple_html_dom.php';
$html = file_get_html('http://www.site.com');
$file = 'latest_results.txt';
file_put_contents($file, $html);
?>
postToDB.php
<?php
$winningNums = $_POST['thingyNums'];
$winningPlus1Nums = $_POST['thingyPlus1Nums'];
$winningPlus2Nums = $_POST['thingyPlus2Nums'];
$drawDate = $_POST['drawDate'];
$thingyToSave = implode(',', $winningNums);
$plus1ToSave = implode(',', $winningPlus1Nums);
$plus2ToSave = implode(',', $winningPlus2Nums);
//CONNECT TO REMOTE
$con = mysql_connect("172.xx.xx.xx","user","pass");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
//SELECT thingy DB
mysql_select_db("App", $con);
//CHECK IF DATE ALREADY EXISTS IN DB
$date_check = mysql_query("SELECT drawDate FROM thingy WHERE drawDate='$drawDate'");
$do_date_check = mysql_num_rows($date_check);
if($do_date_check > 0){
//DATE ALREADY IN DB
die("Entries already exist");
} else {
mysql_query("INSERT INTO thingy (drawDate) VALUES ('$drawDate')");
mysql_query("UPDATE thingy SET thingyRes = '$thingyToSave' WHERE drawDate = '$drawDate'");
mysql_query("UPDATE thingy SET thingyPlus1Res = '$plus1ToSave' WHERE drawDate = '$drawDate'");
mysql_query("UPDATE thingy SET thingyPlus2Res = '$plus2ToSave' WHERE drawDate = '$drawDate'");
echo "Success";
}
mysql_close($con);
?>
The script you're trying to run contains Javascript - which is executed in a browser. Cron will execute the PHP script on the server, and send the output nowhere (as you're directing it to /dev/null).
There's nothing in that scenario that will interpret and execute the Javascript.
You need to essentially port the logic in your Javascript (which makes requests to the two related PHP scripts) to PHP. (You could possibly run some server side javascript interpreter/php extension, but in this case that would seem a bit crazy.)
If you're calling test.php via wget or similar, that tool php doesn't have a JavaScript engine in it. So naturally any JavaScript-dependent features of the page won't run.
There are tools that will load the page and execute the JavaScript therein. They're called "headless" browsers. For example, PhantomJS, which is a headless browser based on WebKit with a JavaScript engine in it. There are also headless versions of Firefox and such.
You'd have your web server running as normal and point the headless browser at the URL for the page, which would both trigger the PHP (just as though the page had been requested by a browser) and process the client-side JavaScript in the page.

Categories