I've never done this before, so I need some input.
Code (in general):
$cofig = Configure::read('config');
if ($config['stuff'] == 1){
$this->Session->setFlash('it is already done this month');
$this->redirect('/to/some/where');
}
elseif ($config['stuff'] == 2){
$this->Session->setFlash('it is already running');
$this->redirect('/to/some/where');
}
else {
SomeComponent::SomeFunction(); //this I need to launch in background while user continues further
$this->Session->setFlash('you have launched it');
$this->redirect('/to/some/where');
}
"SomeComponent" contains several functions. I need to launch a specific function "SomeFunction()" in the bacground while user continues further.
Function "SomeComponent::SomeFunction()" generates bunch of pdfs, interacts with database and uses Cakephp specific methods'n'crap to do all that. Users receive output via database, so I don't need to retrieve it form the function itself.
So i'm not clear which method can do that, which best to use and what are/may be drawbacks of each one (security issues in particular).
I hope I explained everything in a understandable way. If you have read this far - thanks.
Is that process "SomeComponent::SomeFunction()" taking too long? If the answer is yes then I would wrap that function in a shell and if you want to take it to the next level I would use an Observer in order to dispatch that background process.
Here is an introduction to Event Handling in CakePHP.
Related
I have 2 functions, let's call them login and doSomething and currently, I implemented them this way,
$member=$this->login();
$this->doSomething($member);
//show welcome page
When a user logs in, I want to do some stuff but it takes around 20 seconds or more to complete. Is there any ways where after login() is run, it will show the welcome page immediately while the method doSomething() is being executed separately. The method doSomething() doesn't return any values thus does not affect the welcome page.
Please try the following.
ob_start();
$member = $this->login();
ob_end_flush();
ob_flush();
flush();
$this->doSomething($member);
If you do not want to print anything after login, you can use:
ob_start();
$this->doSomething($member);
ob_end_clean();
Also using Ajax from the front site's login page(after loading), you can start processing
$this->doSomething($member);
in another ajax call in the back end silently.
There are other ways for achieving threading, pseudo threading like behaviour.
But these are the easiest one for your scenerio :)
You can check WorkerThreads also.
Their implementation example documentation are available in the net.
If you really, really want to run it in parallel, then you need to run it in a sperate process. That means you are running it in different scope, so while the code you invoke might contain $this->doSomething($member), that "this" won't be this "this".
Assuming that is possible, then your question is a duplicate of this one (but beware - the accepted answer is not good). Note that you will run in blocking problems if both parts of the script depend on a session.
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.
Is there a way in PHP to check if a function has completed processing before allowing it to run again?
I have a function that on page loads/reloads/timer event checks if DB items should be expired based on an end date (date less than now) and if date is less than now makes a duplicate of the record with the exact same information but adds 10 days to the end date. The script then sets the original record status to inactive. This is required to keep an original copy of the item in the DB and the process continues for every record.
Sometimes the script will create multiple duplicates of the same item so it seems like the script is not setting the status to inactive quick enough and when the page is reloaded/visited etc another instance of the script is run producing another duplicate record.
So is there a way to check if a function is currently running and if so ignore the new call to only ever have a single instance running?
Many many thanks
Sounds like you need a "mutex". You can try using this, or implement one yourself by creating a shared resource, such as writing an empty file to disk and then checking for its existence, and removing it when you're done.
A better solution for your specific problem though would be to set up a cron job to periodically run your database maintenance scripts rather than relying on random user requests to your page. This will ensure it won't run too often and reduce the processing per request.
For PHP:
$Running = false;
function functionName() {
if (!$Running) {
$Running= true;
//Your logic implementation here
$Running= false;
}
}
You can use same principle for jQuery functions also and cron job too.
Use a try catch
function blah() {
try {
}
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
}
I have a JavaScript functions which calls a PHP function through AJAX.
The PHP function has a set_time_limit(0) for its purposes.
Is there any way to stop that function when I want, for example with an HTML button event?
I want to explain better the situation:
I have a php file which uses a stream_copy_to_stream($src, $dest) php function to retrieve a stream in my local network. The function has to work until I want: I can stop it at the end of the stream or when I want. So I can use a button to start and a button to stop. The problem is the new instance created by the ajax call, in fact I can't work on it because it is not the function that is recording but it is another instance. I tried MireSVK's suggest but it doesn't worked!
Depending on the function. If it is a while loop checking for certain condition every time, then you could add a condition that is modifiable from outside the script (e.g. make it check for a file, and create / delete that file as required)
It looks like a bad idea, however. Why you want to do it?
var running = true;
function doSomething(){
//do something........
}
setInterval(function(){if(running){doSomething()}},2000); ///this runs do something every 2 seconds
on button click simply set running = false;
Your code looks like:
set_time_limit(0);
while(true==true){//infinite loop
doSomething(); //your code
}
Let's upgrade it
set_time_limit(0);
session_start();
$_SESSION['do_a_loop'] = true;
function should_i_stop_loop(){
#session_start();
if( $_SESSION['do_a_loop'] == false ) {
//let's stop a loop
exit();
}
session_write_close();
}
while(true==true){
doSomething();
should_i_stop_loop(); //your new function
}
Create new file stopit.php
session_start();
$_SESSION['do_a_loop'] = false;
All you have to do now is create a request on stopit.php file (with ajax or something)
Edit code according to your needs, this is point. One of many solutions.
Sorry for my English
Sadly this isn't possible (sort of).
Each time you make an AJAX call to a PHP script the script spawns a new instance of itself. Thus anything you send to it will be sent to a new operation, not the operation you had previously started.
There are a number of workarounds.
Use readystate 3 in AJAX to create a non closing connection to the PHP script, however that isn't supported cross browser and probably won't work in IE (not sure about IE 10).
Look into socket programming in PHP, which allows you to create a script with one instance that you can connect to multiple times.
Have PHP check a third party. I.E have one script running in a loop checking a file or a database, then connect to another script to modify that file or database. The original script can be remotely controlled by what you write to the file/database.
Try another programming language (this is a silly option, but I'm a fan of node). Node.js does this sort of thing very very easily.
I want to write a small management tool to oversee my server processes, my problem is now, how could i wait for user input and at same time update the screen with current stats? Is it even possible with PHP-CLI or are there any tricks for doing this I missing currently?
I have looked into newt and ncurses PECL extensions but both doesn't seem to fit my needs.
Go for libevent http://www.php.net/manual/en/book.libevent.php
You can run your main loop while listening to console with a code roughly like this one:
<?php
// you need libevent, installable via PEAR
$forever=true;
$base=event_base_new();
$console=event_buffer_new(STDIN,"process_console");
event_buffer_base_set($console,$base);
event_buffer_enable($console,EV_READ);
while ($forever) {
event_base_loop($base,EVLOOP_NONBLOCK); // Non blocking poll to console listener
//Do your video update process
}
event_base_free($base); //Cleanup
function process_console($buffer,$id) {
global $base;
global $forever;
$message='';
while ($read = event_buffer_read($buffer, 256)) {
$message.=$read;
}
$message=trim($message);
print("[$message]\n");
if ($message=="quit") {
event_base_loopexit($base);
$forever=false;
}
else {
//whatever.....
}
}
I don't think you can do it with PHP CLI. As I know, when interpret the script with PHP, you can only view the final output.
I think you do want ncurses. If you can convert the simple example C code here, which you should be able to with the PHP wrapper, you'd have your "bootstrap" for solving your problem.
Make sure to blog your code somewhere! :)
My advice would be to try to avoid any solutions that talk about leaving processes running whilst exiting PHP. Here is a really simple example of how to do it with a bit of jQuery:
window.setInterval(checkstat, 10000); //10 second interval
function checkstat() {
//Change a div with id stat to show updating (don't need this but it's nice)
$('#stat').html('Updating...');
$.get('/getmystats.php?option=blah', function(data) {
//Update the results when the data is returned.
$('#stat').html(data);
});
}
If you are need to update more than one area on your page, you can do one call but return JSON or XML and then populate the bits as required.