I'm discarding the old question for a better formulated question.
I'm using the twitter stream api via php like the script below.
When I run it via the command line. The script keeps running and when I hit ctrl+c the script stops.
Thats great and all, but i'd like to run it in the background. When a user creates a new search, the script below gets activated and stays running till a signal is given to sop. Just like the cli version, but in the background.
How do I achieve this?
Ok, here's a piece of code:
$opts = array(
'http' => array(
'method' => 'POST',
'content' => 'track=ipad'
)
);
$context = stream_context_create($opts);
$stream = fopen('http://test#stream.twitter.com/1/statuses/filter.json','r', false, $context);
while (!feof($stream)) {
if (!($line = stream_get_line($stream, 200000, "\n"))) {
continue;
}
$tweet = json_decode($line);
// mysql query
}
I'm running it
This should be solved by running the php script as a daemon from commandline, there are alot of things to conisder tho.
what when the system reboots?
what when an error occurs?
what about memory management?
what about monitoring?
A good start is Jeroen Keppens' presentation PHP in the dark he did on multiple php conference.
You might want to look into ajax or other web-browser methods like comet or websockets to achieve what you want.
update
So if you want it to run on the server side you should use php's exec function something like:
exec("php-cli runForever.php");
I'm trying to do the same thing with this tutorial. It uses the System_Daemon pear package and provides start/stop functions.
Related
I'm building an integration that communicates data to several different systems via API (REST). I need to process data as quickly as possible. This is a basic layout:
Parse and process data (probably into an array as below)
$data = array( Title => "Title", Subtitle => "Test", .....
Submit data into service (1) $result1 = $class1->functionservice1($data);
Submit data into service (2) $result2 = $class2->functionservice2($data);
Submit data into service (3) $result3 = $class3->functionservice3($data);
Report completion echo "done";
Run in a script as above I'll need to wait for each function to finish before it starts the next one (taking 3 times longer).
Is there an easy way to run each service function asynchronously but wait for all to complete before (5) reporting completion. I need to be able to extract data from each $result and return that as one post to a 4th service.
Sorry if this is an easy question - I'm a PHP novice
Many thanks, Ben
Yes, there are multiple ways.
The most efficient is to use an event loop that leverages non-blocking I/O to achieve concurrency and cooperative multitasking.
One such event loop implementation is Amp. There's an HTTP client that works with Amp, it's called Artax. An example is included in its README. You should have a look at how promises and coroutines work. There's Amp\wait to mix synchronous code with async code.
<?php
Amp\run(function() {
$client = new Amp\Artax\Client;
// Dispatch two requests at the same time
$promises = $client->requestMulti([
'http://www.google.com',
'http://www.bing.com',
]);
try {
// Yield control until all requests finish
list($google, $bing) = (yield Amp\all($promises));
var_dump($google->getStatus(), $bing->getStatus());
} catch (Exception $e) {
echo $e;
}
});
Other ways include using threads and or processes to achieve concurrency. Using multiple processes is the easiest way if you want to use your current code. However, spawning processes isn't cheap and using threads in PHP isn't really a good thing to do.
You can also put your code in another php file and call it using this :
exec("nohup /usr/bin/php -f your script > /dev/null 2>&1 &");
If you want to use asynchronicity like you can do in other languages ie. using threads, you will need to install the pthreads extension from PECL, because PHP does not support threading out of the box.
You can find an explaination on how to use threads with this question :
How can one use multi threading in PHP applications
the situation is like this. I have created multiple php files with the name operation.php and is hosted in my domains. Something like this:
example.com/operation.php
example123.com/operation.php
example1234.com/operation.php
Okay so now what I want is to code 1 single PHP script that will be the mother of all these operation.php scripts. Something like if I execute motherexample.com/operaterun.php from my browser then all these php scripts will run one by one.
Is this possible? Will this result in server outage? I do not want to run all these at once, maybe take a gap of 10seconds between each script execution.
Need help!
UPDATE I'm not sure whether you Guys are getting it or not but here's another example..
Let's say you have 100 sites each having maintenance.php located at example001.com/maintenance.php now it is not possible to load and run each of those 100 maintenance.php in the browser one by one. So this is why I want 1 single mother-maintenance.php that when run from the browser will execute each of those maintenance.php one by one or maybe with some time gap!
If a user is doing it, I will recommend AJAX.
Otherwise you can try a code like that.
<?php
$somearg = escapeshellarg('blah');
exec("php file2.php $somearg > /dev/null &");
Found on (https://stackoverflow.com/a/1110260/4268741)
However you need to make some changes to it to get it work on your project.
You need to run something like a wget call from the mother motherexample.com/operaterun.php.
wget is for linux, if you are using a windows domain check for alternatives
example
wget http://example.com/operation.php
wget http://example123.com/operation.php
wget http://example1234.com/operation.php
You can use AJAX request. Just make request from your mother script to child pages.
Or you can use simple way - use iframe for each child. If you have 100 child pages then just generate on mother page 100 iframe with src to child. Or you can create this iframe by javascript and add it to page. Then you can make delay between requests.
I think file_get_contents can help you:
<?php
//index.php
set_time_limit(0);
$op_list = array(
"example.com/operation.php",
"example123.com/operation.php",
"example1234.com/operation.php",
);
$opts = array(
"http" => array(
"method" => "get",
"timeout" => 60,
)
);
$context = stream_context_create($opts);
foreach($op_list as $key => $value)
{
if($response = file_get_contents($value, null, $context))
{
$json_decode = json_decode($response, true);
echo "{$value}: time_start: {$json_decode['time_start']}, time_end: {$json_decode['time_end']} <br />";
}
else
{
echo "{$value}: fail <br />";
}
}
?>
Here is a complete example:
http://try.valinv.com/php/21a6092d20601c77982f7c1ec6b2cc23.html
Hi i'm trying to execute a LONG RUNNING request (action) in background.
function actionRequest($id){
//execute very long process here in background but continue redirect
Yii::app()->user->setFlash('success', "Currently processing your request you may check it from time to time.");
$this->redirect(array('index', 'id'=>$id));
}
What i'm trying to achieve is to NOT have the user waiting for the request to be processed since it generally takes 5-10min, and the request usually goes to a timeout, and even if I set the timeout longer, waiting for 5-10 min. isn't a good user experience.
So I want to return to the page immediately notifying the user that his/her request is being processed, while he can still browse, and do other stuff in the application, he/she can then go back to the page and see that his/her request was processed.
I've looked into Yii extensions backjob, It works, the redirect is executed immediately (somehow a background request), but when doing other things, like navigating in the site, it doesn't load, and it seems that the request is still there, and i cannot continue using the application until the request is finished.
A similar extension runactions promises the same thing, but I could not even get it to work, it says it 'touches a url', like a fire and forget job but doesn't work.
I've also tried to look into message queuing services like Gearman, RabbitMQ, but is really highly technical, I couldn't even install Gearman in my windows machine so "farming" services won't work for me. Some answers to background processing includes CRON and AJAX but that doesn't sound too good, plus a lot of issues.
Is there any other workaround to having asynchronous background processing? I've really sought hard for this, and i'm really not looking for advanced/sophisticated solutions like "farming out work to several machines" and the likes. Thank You very much!
If you want to be able to run asynchronous jobs via Yii, you may not have a choice but to dabble with some AJAX in order to retrieve the status of the job asynchronously. Here are high-level guidelines that worked for me. Hopefully this will assist you in some way!
Setting up a console action
To run background jobs, you will need to use Yii's console component. Under /protected/commands, create a copy of your web controller that has your actionRequest() (e.g. /protected/commands/BulkCommand.php).
This should allow you to go in your /protected folder and run yiic bulk request.
Keep in mind that if you have not created a console application before, you will need to set up its configuration similar to how you've done it for the web application. A straight copy of /protected/config/main.php into /protected/config/console.php should do 90% of the job.
Customizing an extension for running asynchronous console jobs
What has worked for me is using a combination of two extensions: CConsole and TConsoleRunner. TConsoleRunner uses popen to run shell scripts, which worked for me on Windows and Ubuntu. I simply merged its run() code into CConsole as follows:
public function popen($shell, $redirectOutput = '')
{
$shell = $this->resolveCommandLine($shell, false, $redirectOutput);
$ret = self::RETURN_CODE_SUCCESS;
if (!$this->displayCommands) {
ob_start();
}
if ($this->isWindows()) {
pclose(popen('start /b '.$shell, 'r'));
}
else {
pclose(popen($shell.' > /dev/null &', 'r'));
}
if (!$this->displayCommands) {
ob_end_clean();
}
return $ret;
}
protected function isWindows()
{
if(PHP_OS == 'WINNT' || PHP_OS == 'WIN32')
return true;
else
return false;
}
Afterwards, I changed CConsole's runCommand() to the following:
public function runCommand($command, $args, $async = false, &$outputLines = null, $executor = 'popen')
{
...
switch ($executor) {
...
case 'popen':
return $this->popen($shell);
...
}
}
Running the asynchronous job
With the above set up, you can now use the following snippet of code to call yiic bulk request we created earlier.
$console = new CConsole();
$console->runCommand('bulk request', array(
'--arg1="argument"',
'--arg2="argument"',
'--arg3="argument"',
));
You would insert this in your original actionRequest().
Checking up on the status
Unfortunately, I'm not sure what kind of work your bulk request is doing. For myself, I was gathering a whole bunch of files and putting them in a folder. I knew going in how many files I expected, so I could easily create a controller action that verifies how many files have been created so far and give a % of the status as a simple division.
I've created a little command line tool to help me launch sites. It uses Symfony2 console to help create the commands and add some structure. What I'm trying to figure out is if there is a way, I can crate a "blank" or a default command so if you don't put in a command it just defaults to this. An example might help explain:
A "normal" console command would look like this:
php launch site foo
I want to make this do the exact same thing as above:
php launch foo
The only thing I can think of is to sort-of short circuit the application->run process and check if "foo" is in my own command list, if it's not then force the console to run site foo.
The crappy thing about that is if you just typo'ed a different command, the system would just try and run as a site and instead of an error message you'd get an error saying it can't launch that site (which is an error, but the wrong error and not a helpful one to a user).
Maybe I missed something in the console docs, but is there a way to do what I'm trying here?
So what I ended up doing was just attempt my own match, if I can find the command, then I run the application as normal, if not, I try to launch the site:
if (!array_key_exists($argv[1], $list))
{
$cmd = $this->application->find('launch');
$args = array(
'command' => 'launch',
'alias' => $argv[1]
);
$input = new ArrayInput($args);
$output = new ConsoleOutput();
$cmd->run($input, $output);
}
else
{
$this->application->run();
}
It works fine, it just feels a little meh, I'm open to other suggestions if anyone has them.
There is a vbscript that we must run to consolidate information gathered in a custom web application into our management software. The .vbs is in the same folder as the web application which is built in CodeIgniter 2.
Here is the controller code:
public function saveToPM( $budgetType ){
// run it
$obj = new COM( 'WScript.Shell' );
if ( is_object ( $obj ) ) {
$obj->Run( 'cmd /C wscript.exe D:\pamtest\myload.vbs', 0, true );
var_dump($obj->Run);
} else {
echo 'can not create wshell object';
} // end if
$obj = null;
//$this->load->view('goodPush');
} // end saveToPM function
We have enabled DCon in the php.ini file and used dcomcnfg to enable permissions for the user.
I borrowed the code from http://www.sitepoint.com/forums/showthread.php?505709-run-a-vbs-from-php.
The screen echos "Code executed" but the vbscript does not run.
We have been fighting with this for a while so any help is GREATLY appreciated.
It's a bit messy. PHP calls WScript.Shell.Run which will call cmd (with /c - i.e terminate cmd.exe when it's done its thing) which will call cscript.exe to run and interpret a .vbs. As you can see quite a few things that have to go right! :)
What if you 'wait' for the WScript.Shell.Run call to end (your $wait variable) before continuing execution of the wsh script which will in turn allow PHP to continue execution etc?
Since you're not waiting for the call to finish, PHP thinks its all good and continues onto the next line (interpreted language).
Also, maybe have the .vbs create an empty text file? Just so you have an indication that it has actually run.
Just take a step back, have a beer and it'll come to you! Gogo troubleshoot!
And - http://ss64.com/vb/run.html
If bWaitOnReturn is set to TRUE, the Run method returns any error code returned by the application.
I've tested your code with a stand-alone PHP script (without Codeigniter) on a Windows XP machine, with the PHP 5.4.4 built-in web server, and I've noticed that the vbscript gets executed, but PHP dies (the event viewer shows a generic "Application Error" with ID 1000).
However I've also discovered that removing the "cmd /C" from the command string solves the problem.
Here is the simple script that I've used for my test:
<?php
$obj = new COM('WScript.Shell');
if (is_object($obj)) {
//$obj->Run('cmd /C wscript.exe test.vbs', 0, true); // This does'nt work
$obj->Run('wscript.exe test.vbs', 0, true); // This works
var_dump($obj->Run);
} else {
echo 'can not create wshell object';
}
$obj = null;
?>
And this is my simple "test.vbs" script:
WScript.Echo "vbscript is running"
Another solution that seems to be working (at least on my platform) is the "system" call:
system('wscript.exe test.vbs');
Unfortunately I don't have a 64-bit IIS system to test with, so I can't really say if there are specific problems on this platform, but I hope this helps.