Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 302
  • Last Modified:

the best way to EXECUTE a PHP script from another PHP script

I want to be able to execute a PHP script, send it to the background and then exit the script cleaning.

I do not want to "include" the file for other reasons.
0
skione
Asked:
skione
  • 11
  • 4
  • 3
  • +1
1 Solution
 
skioneAuthor Commented:
I am trying something like this:

$pid = pcntl_fork();
                        if($pid) {
                          // parent process runs what is here
                              exec('/usr/bin/php ' . $svcValue['service_path'].'&> /dev/null');
                        } else {
                              $log->error("[".$dbuser."] Unable to spawn processes");
                        }
0
 
Dave BaldwinFixer of ProblemsCommented:
There is no 'background' in PHP as such.  However, if you access another script with a URL like "http://www.mysite.com/thisphp.php", it will execute the PHP page instead of loading it into the current script.  The only catch to that is that all the functions I can think of expect a value to be returned and you don't want to wait for that.
0
 
skioneAuthor Commented:
Link is giving me a 404
0
What Kind of Coding Program is Right for You?

There are many ways to learn to code these days. From coding bootcamps like Flatiron School to online courses to totally free beginner resources. The best way to learn to code depends on many factors, but the most important one is you. See what course is best for you.

 
skioneAuthor Commented:
NVM, sorry I didn't see what you were writing at first. I believe you exec a PHP script just like you would from a command line. I don't want to open an HTTP connect as I want to execute the script from CLI
0
 
Dave BaldwinFixer of ProblemsCommented:
I think you're right.  Is the first script being run from the command line or from the web server?  I ask because there are two different sets of permissions there.  Also, read the notes here: http://us3.php.net/manual/en/function.exec.php
0
 
skioneAuthor Commented:
The firs script is being run from the command line via a cron
0
 
Dave BaldwinFixer of ProblemsCommented:
I'd try it then.  I don't know if there are any permission issues associated with using cron.
0
 
Ray PaseurCommented:
Can you please step back from the technical details and just tell us in plain language what you want these scripts to do?  Once we know that, we may have a good design pattern for you.  One possible pattern would be to use CURL POST or FSockOpen to start an asynchronous script.  But we really need to know the business purpose of the script to offer the best suggestions.  Thanks, ~Ray
0
 
skullnobrainsCommented:
fork + exec is not in the php way (at least exec is not)
you probably can manage to use fork + include but it is likeley to produse a mess with your existing file descriptors
fork+system('php your_script') should work but i would not recommend it for similar reasons

the way to go, is popen('php your_script 2>&1','r') or something similar, but you'll need to read the output
you may of course redirect the output to a file or /dev/null so your callee runs by itself
in this case, just poll the resource popen gave you to determine when your callee script finishes

if you need more complex IO handling, you may also use proc_open() which will let you map STDIN, STDOUT, and STDERR, and possibly any FD to files or resources in the caller script

beware these functions should not be used in many web server contexts, and may be forbidden for security reasons.
on a webserver, you can also do things like system('nohup php your_script 2>/var/log/script.stderr >/var/log/script.stdout &');
0
 
skioneAuthor Commented:
This is closer to what I need skullnobrains (nice handle).

The script has no output and has a nice logging class, can you provide me with the exact syntax that will execute the script as if it were being executed at the command line and then send all output to /dev/null?

PS Its my pizza box so I can set PHP however i need and exec() and the like is not disabled.

The script that is called is smart enough to do the right thing. I just need the main script to just launch all the other processes and then finish.

Actually what I would like to do is have the script wait for all the processes to finish and then move on. So in pseudo code I would like it to do this:

1. start script
2. loop through processes
3. exec(processes) in the back ground
4. wait for all processes to finish
5. update time stamps

I only need 3 & 4, if 4 is not possible then just 3

thanks
0
 
Ray PaseurCommented:
Just a thought... If your main script is going to wait for all the background processes to finish, why not just run them inline?
0
 
skioneAuthor Commented:
because I want all the scripts to run at the same time (in parallel) otherwise the script would take 6 times longer to finish. It gets called every minute so I need to make sure it finishes within the 60 second time frame.
0
 
Ray PaseurCommented:
OK, I understand.  I would probably use CURL POST or FSockopen to start the scripts.  But there are lots of ways to skin a cat...
0
 
skioneAuthor Commented:
CURL post would not execute the script via CLI but FCGI and that won't work but basically using passthru or exec or whatnot is esentially the same thing. I think skullnobrains has the syntax. I understand what he is saying but I am in a firedrill and don't have the time to figure out the syntax exactly and test. That is why I was looking for help
0
 
skullnobrainsCommented:
i have no time for a full php script today, but i may tomorrow

something like

$torun=array('cmd1','cmd2',...);
$running=array();
$num_running=0;
$max_running=6;

while(true){
  // launch as many processes as allowed
  while($num_running<=$max_running){
    // start a new one with redirection : $res=popen($cmd.' 2>&1 >/dev/null','r');
}
  $r=$running;
  select($r,null,null,1) or die ();
  foreach($r as $fd)
   if(feof($fd)); // remove from the running array and decrement the number of running processes
}


--------

but if your script does not do anything more, you should just use a simple xargs command
something like

cat LIST_OF_COMMANDS_TO_RUN | xargs -I % -p 5 sh %
0
 
skioneAuthor Commented:
The script after launching each child script updates a timestamp that the child scripts use when they launch next.

I do not understand your while(true) part, can you explain that?

I doubt you need to write a full script at this point I think I am just about there.
0
 
skioneAuthor Commented:
Here is the code snippet, that I believe illustrates what I am doing. Thanks for you help:
            $services                         = $Service->GetServices();

            if (count($services) > 0) {            
                  foreach ($services as $svcKey => $svcValue) {
                        $currTime = $Service->current_time();
                        $log->trace( "[" . $dbuser . "] PROCESSING ".$svcValue['service_path']);
                        $handle[] = popen('/usr/bin/php ' . $svcValue['service_path']."> /dev/null 2>/dev/null &","r");
                        $log->trace( "[" . $dbuser . "] COMPLETED " .$svcValue['service_path'] );
                        $svcUpdate['id'] = $svcValue['service_id'];
                        $svcUpdate['id']['service_freq'] = $svcValue['service_freq'];
                        $svcUpdate['id']['next_run'] = $svcValue['service_next_run'];
                        $svcUpdate['id']['currTime'] = $currTime;
                  }      
                  
                  while (true) {
                        $end = true;
                        foreach ($handle as $h) {
                              if (!feof($handle)) {
                                    $end = false;
                              }
                        }
                        if ($end) {
                              break;
                        }
                  }
                  
                  foreach ($svcUpdate as $s) {
                        if (isset($s['next_run'])) {
                              $Service->UpdateNextRunTime($s['service_id'], $s['service_freq'],$s['next_run'], $s['currTime']);
                        }
                  }
            }      
0
 
skioneAuthor Commented:
OK so the problem I am running into is that the test feof() is never reporting that the file has ended even though it has. I think its because I am redirecting so it never knows when the file is over
0
 
skullnobrainsCommented:
the while true part was intended to build a script that will run many queries but only a specific number of parallel ones
it spawns up to max_running processes when iterating the first time, and then only spawns new ones when old ones died after that

----

if you do not select or read the file handles, feof will almost never be set properly

performing a select over the file handles prevents your master script from using up the CPU and forces the script to set feof properly
an other way to force feof detection is to use fseek(0,SEEK_CUR) which moves the pointer from a location to the same one

using select will probably solve both the feof issue and the CPU one
if you want to keep your script, you need a sleep to prevent eating up CPU and use any of the above methods to make feof work properly

in this case, you can forget about feof and use a simple fread. just ensure you set the stream timeout to zero and properly detect that fread returns false and not an empty string as will be returned while the script is running. this method is about the same as performing fseek+feof but you need to make sure the timeout works as expected which may not be the case on some OSes

----

if it still does not work as expected :
if you redirect stdout, you cannot detect feof on stdout on some (rare) OSes

many hacks can prevent this behaviour
- you can use a dummy hack like "CMD 2>&1 >/dev/null ; echo ended ;" so you can read the "ended" string
- you can use a better hack by explicitely running your command through sh -c "CMD" and let sh close the file descriptors cleanly
- you can use the parent process as a logger to make feof available
- you can open the script in 'w' mode and perform a select on the fd so the parent process will detect when the STDIN of the script is closed
... but this is quite an ugly hack and it will not work the same on all platforms if at all
- you can switch to proc_open() and use proc_get_status() to determine which process died (and the return codes and status) easily

---

happy coding
0
 
skioneAuthor Commented:
Actually I ended up using a Pear package called System_Daemon.

Here are 2 useful links:
http://kevin.vanzonneveld.net/techblog/article/create_daemons_in_php/
http://pear.php.net/package/System_Daemon

I have to say, while I didn't use your exact solution, it did help me to get the right search semantics that lead me to the actual solution.

For anyone who wants to do what I was trying to do, right a daemon in PHP you should use this package. It is simple, stable and just freaking works! I have pounded on it with many MANY tests and I have to say it has stayed up. It is not perfect but neither is trying to write a daemon in php but it takes much of the pain out of things.

I still need to get my head wrap around using process forking in PHP as I will further want to refine my daemons so that I can spawn child processes that handle less important tasks.

For instance, the group of Daemons are largely centered around messaging functions. So one though would be the send message in one process but I also need to update the status of the message so I could spawn that into a child process. This way the messages could be sent very quickly and then the updating of the status of those messages could happen slower. Now everything is linear and that limits my speed to about 3-4 messages per second. I eventually want to get it to about 10-20. If I can remove all transactions from the main thread except sending of the message it should be possible.

Again, thank you very much for your help. It still astounds me what people will do for points (which I awarded you of course). In addition you have my gratitude plus the knowledge you help me learn some stuff.

I have achieved my goals for this effort and need to move on but once I am reading to further refine my scripts I will post a new question and post a response here with the url so hopefully you can help me there as well!
0
 
skullnobrainsCommented:
thanks for the information about the package which i did not know about
maybe this lets you understand that many people are not here for points
as a matter of fact, i just want to have enough of them to be able to view other threads

since you are interested in forking
don't be afraid, daemonising and forking is perfectly supported in php and works fine
i had production servers running php powered forking deamons for years without trouble

you can demonize easily using the old doublefork method or more cleanly by forking+closing all fds + using setsid

forking is a little trickier :
most php libs are not fork-happy so you should close any fds other than stdin, stdout, and stderr just to make sure
or more likely in real life, do not open these connections before you fork
ex: mysql connections will mess up, ldap connections will not, but may behave weird performace-wise
obviously master tcp sockets can be used in forked processes so writing a forking daemon will work fine
basically anything that claims to be thread-safe will work, the rest should not be used before the forking is performed

you probably also will stumble on the select limit
in C you cannot select more than a number (usually 1024) fds at once
in php you cannot use select with a resource number greater than that number
so you need to make sure your master process does not use up fds over time if you need to use select

happy coding
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

The 14th Annual Expert Award Winners

The results are in! Meet the top members of our 2017 Expert Awards. Congratulations to all who qualified!

  • 11
  • 4
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now