I have a cronjob system with PHP. I have a file named cron.php and wanna to check if it is not loaded, load it. it is very important to me that this file run only one time and I need the way how define it is already running. I can't use any system functions like exec,system,... do you have any solutions?
NOTE: I run my script trough CURL and include_one and require_once don't work for this case.
You could use flock() to lock the php file itself, like this:
<?php
class Lock{
private $fp;
function __construct(){
$this->fp=fopen(__FILE__,'r');
if (!flock($this->fp,LOCK_EX|LOCK_NB)) {
die('already running !'.PHP_EOL);
}
}
function __destruct(){
flock($this->fp,LOCK_UN);
fclose($this->fp);
}
}
$lock=new Lock();
// simulate some processing
sleep(60);
echo "END";
?>
Can you just use require_once or include_once?
require_once will throw a PHP fatal error (will stop execution) if the file cannot be evaluated.
include_once will throw a PHP warning (execution may continue).
// Require tasks.php to run once
require_once 'path/to/tasks.php';
// Attempt to run tasks.php and only once
include_once 'path/to/tasks.php';
Your problem is essentially equivalent to "Check if a php script is still running"
Please refer this
Check if a php script is still running
if I understand you correctly, you want to prevent your cron.php script from getting started a second time by cron, it is not called from another PHP script? (in that case, require_once would be the right answer)
as I understand it, you need to store a marker that indicates that your script is running and remove that marker at the end of your script.
depending on your environment, you could either create a small file, i.e. .lock or store a status = locked entry in your database.
edit: here is a small code example using the file method:
<?php
// cron.php
$path = '/path/to/your/data/directory/';
if (file_exists($path . '.lock') {
die('cron.php is already running');
}
// if script reaches this point, it is not locked -> create a lock
file_put_contents($path . '.lock', 'lockfile created at ' . now());
//... your code....
//unlocking
unlink($path . '.lock');
?>
If you are using cURL then I believe your are using cURL to request a page such as http://domain.com/cron.php. The machine requesting the script via cURL/wget/browser/etc has no way of knowing if the script is already being executed on the server. However, you can configure your cron.php script to run only once:
<?php
// attempt to obtain a lock
$fp = fopen(basename(__FILE__) . ".lock", "w");
if (flock($fp, LOCK_EX | LOCK_NB) === false) {
echo basename(__FILE__) . ": already running\n";
exit(-1);
}
// code goes here
echo date("Y-m-d H:i:s") . ": cron job started\n";
sleep(30);
echo date("Y-m-d H:i:s") . ": cron job ended\n";
// release the lock
flock($fp, LOCK_UN);
fclose($fp);
The sample code uses PHP flock function. The LOCK_EX flag tells PHP that it needs to obtain an exclusive lock; i.e. no other process is allowed to access the file. The LOCK_NB tells PHP that it should not block (wait for the lock to be released) and return false immediately. Together, the two switches assure that a second process cannot lock the file while the first one has it locked.
you can use require_once or include_once
The general syntax of both the include and require statements are as follows:
include "file_to_load.php";
include_once "file_to_load.php";
When the include_once/require_once statements are used, the file cannot be loaded or executed multiple times. If an attempt is made to load a file twice using one of these two methods, it will be ignored. Because it is unacceptable to define the same function multiple times within a script, these functions allow the developer to include a script as needed without having to check whether it has been previously loaded.
NOTE The capability to return values from external files is limited only to the include_once statements. The require_once statements cannot be used in this fashion.
include_once('class.php');
php.net states
The include_once statement includes and evaluates the specified file
during the execution of the script. This is a behavior similar to the
include statement, with the only difference being that if the code
from a file has already been included, it will not be included again.
As the name suggests, it will be included just once.
You can use either require_once or include_once.
if you are confuse what to use then difference between this two is that
require_once will stop executing preceding code, trwos fatal error, if file mentioned is not found
so if you want preceding code to continue even file is not found then don't use it.
where as the include_once will continue executing preceding code.
You can use Database for this case, make an entry of the page in database and second column for checking whether is it loaded or not (eg. '0' if not loaded yet and '1' it is loaded). Initially keep value of that row as '0' when the page is loaded update that column as '1'.
Related
I am collecting a series of php files and testing to see if a single function returns valid output. To facilitate the process, all their functions are named identically. So then I can run:
foreach($fileList as $file) {
require($file);
echo $testFunction();
}
The problem is that php throws an error 'Cannot redeclare function' since the second file's function is named the same as the first. What I want to do is 'undeclare' a function after I test its output but I know this isn't possible and I'm trying to handle this procedurally. unlink($file) does not remove the instance of the function, unfortunately. Is there a simple way to handle this without using an OOP approach?
UPDATE #1
Using exec() instead of shell_exec() allows me to check err status (which is #2). CHMOD was necessary as user/group prevented execution (security settings on this offline server to be updated once the script is functioning). At this point, it does not echo anything since shell_exec() is returning an error (at least I think so since the output from shell_exec is empty and since exec is returning error #2). Here is an updated test:
$fileList = array('test.php');
foreach($fileList as $file) {
// load code from the current file into a $code variable,
// and append a call to the function held in the $testFunction variable
$code = file_get_contents($file) . "\n" . 'testFunction();';
// save this to a temporary file
file_put_contents('test-file.php', $code);
// execute the test file in a separate php process,
// storing the output in the $output variable for examination
//*************** */
$output=null;
$retval=null;
$absPath = realpath('test-file.php');
chmod($absPath,0777);
echo $absPath;
exec($absPath, $output, $retval);
echo "Returned with status $retval and output:\n";
print_r($output);
}
UPDATE #2
While you can't undeclare a function, you can repeatedly assign different functions to the same var. For example:
$listOfFunctionNames = array('function1', 'function2', 'function3);
foreach($listOfFunctionNames as $func) {
$funxion = $func;
$funxion();
}
You can execute the files in another process, for example (assuming $testFunction is defined in the files), you could do something like this (assuming you are running on Linux):
foreach($fileList as $file) {
// load code from the current file into a $code variable,
// and append a call to the function held in the $testFunction variable
$code = file_get_contents($file) . "\n" . '$testFunction();';
// save this to a temporary file
file_put_contents('/tmp/test-file.php', $code);
// execute the test file in a separate php process,
// storing the output in the $output variable for examination
$output = shell_exec('php /tmp/test-file.php');
// examine output as you wish
}
unlink('/tmp/test-file.php');
EDIT:
Since testFunction does not echo, and instead returns the output to be examined, we can simply modify the test file to echo testFunction();.
$code = file_get_contents($file) . "\n" . 'echo testFunction();'; // <- NOTE: the semi-colon after testFunction();
I noticed my original answer was lacking a semi-colon in the test file, which is probably where the error was coming from. What you can do to ensure it's correct is have this script generate the first test file and terminate early. You can then manually inspect the file for correctness and also use PHP to ensure it's parse-able, from the command line:
php -l /tmp/test-file.php
Note also there are more sophisticated ways you could check correctness of each test file, however I am trying to keep the answer concise, as that is starting to stray into a separate question.
EDIT: I need to provide more detail, not sure what is going on.
I seem to be having a problem where PHP treats a require_once in a separate process as a repeat of the require in the outer process.
Suppose I have this file which I will run on the CLI:
<?php
require_once 'includeme.php';
$command = "php runme.php";
$handle = popen($command, 'r');
$read = fread($handle, 2096);
$exit = pclose($handle);
print_r($read);
This does the following:
include a file
run a child process, which will also try to include that file.
The file includeme.php is this:
<?php
print "I was included";
Including it should just cause it to return that string.
The runme.php file is this:
<?php
$result = require_once 'includeme.php';
print $result;
Running the main script produces this output:
I was included
1
What seems to be happening is that the runme.php script is getting a 1 for the require_once statement, which is what require_once returns if the script has already been included.
But runme.php is a separate process. How can PHP be thinking it's already included includeme.php?
From the docs:
Successful includes, unless overridden by the included file, return 1
The 1 in your output does not indicate it was already previously included, just that it was included successfully.
Regardless of the return value, you can use require_once everywhere and trust it's working as expected.
Do note that require_once (like, require, include...) is a construct, not PHP function. So, returned 1 probably means, OK, file is included. No idea why echoed I was included was not caught.
Heyas,
So this simple exec() script runs fine for the first two times, in trying to generate a PDF file from a webpage (using wkhtmltopdf).
It first) deletes the existing file, and second) creates the new PDF file in its place. If I run the script a second time, it deletes the file again, and then creates a new one, as expected. However, if I run it one more time, it deletes the files, creates a new one, but then the script seems to hang until the 30-second 504 timeout error is given. The script, when it works, only takes about 3 seconds to run/return. It also kills the entire server (any other local PHP sites no longer work). If I restart the PHP server, everything still hangs (with no success). Interestingly, if I run the script once, and then restart the PHP server, I can keep doing this without issue (but only generating the PDF up to two times). No PHP errors are logged.
Why would it be stalling out subsequent times?
$filePath = 'C:\\wtserver\\tmp\\order_' . $orderId . '.pdf';
// delete an existing file
if(file_exists($filePath)) {
if(!unlink($filePath)) {
echo 'Error deleting existing file: ' . $filePath;
return;
}
}
// generates PDF file at C:\wtserver\tmp\order_ID.pdf
exec('wkhtmltopdf http://google.com ' . $filePath);
I've tried a simple loop to check for the script's completion (successful output), and then try to exit, but it still hangs:
while(true) {
if(file_exists($filePath)) {
echo 'exit';
exit(); // have also tried die()
break;
}
//todo: add time check/don't hang
}
If I can't figure this bit out, for now, is there a way to kill the exec script, wrapping it somehow? The PDF is still generated, so the script is working, but I need to kill it and return a response to the user.
Solution:
Have to redirect standard output AND standard error, to end the process immediately, ie. in Windows:
exec('wkhtmltopdf http://google.com ' . $filePath . ' > NUL 2> NUL');
do you know that you can run the executable in background, like this
exec($cmd . " > /dev/null &");
This way you can immediately come out of it.
I have an index.php script that I use as a post-commit URL on a Google Code site. This script clones a directory and builds a project that may take some work. I want to avoid having this script running more than one time in parallel.
Is there a mechanism I can use to avoid executing that script if another one is already in session?
You can use flock with LOCK_EX to gain an exclusive lock on a file.
E.g.:
<?php
$fp = fopen('/tmp/php-commit.lock', 'r+');
if (!flock($fp, LOCK_EX | LOCK_NB)) {
exit;
}
// ... do stuff
fclose($fp);
?>
For PHP versions after 5.3.2 you need to manually release the lock using
flock($fp, LOCK_UN);
how long does it take to run.
could use memcache
<?php
$m = new Memcache(); // check the constructor call
if( $m->get( 'job_running' ) ) exit;
else $m->set( 'job_running', true );
//index code here
//at the end of the script
$m->delete( 'job_running' );
?>
If the task fails you will need to clear from memcache. Flock is a good option too... probably better actually.
Only if you save the state of the running script and check when the script starts if an other script is currently active.
For example to save if a script is running you could do something like this:
$state = file_get_contents('state.txt');
if (!$state) {
file_put_contents('state.txt', 'RUNNING, started at '.time());
// Do your stuff here...
// When your stuff is finished, empty file
file_put_contents('state.txt', '');
}
Is there any way to check id a file is being accessed or modified by another process from a php script. i have attempted to use the filemtime(), fileatime() and filectime() functions but i have the script in a loop which is checking continuously but it seems once the script has been executed it will only take the time from the first time the file was checked.. an example would be uploading files to a FTP or SMB share i attempted this below
while(1==1)
{
$LastMod = filemtime("file");
if(($LastMod +60) > time())
{
echo "file in use please wait... last modified : $LastMod";
sleep(10);
}else{
process file
}
}
I know the file is constantly changing but the $LastMod variable is not updating but end process and execute again will pick up a new $LastMod from the file but dosnt seem to update each time the file is checked in the loop
I have also attempted this with looking at filesize() but get the same symptoms i also looked into flock() but as the file is created or modified outside PHP I don't see how this would work.
If anyone has any solutions please let me know
thanks Vip32
PS. using PHP to process the files as requires interaction with mysql and querying external websites
The file metadata functions all work off stat() output, which caches its data, as a stat() call is a relatively expensive function. You can empty that cache to force stat() to fetch fresh data with clearstatcache()
There are other mechanisms that allow you to monitor for file changes. Instead of doing a loop in PHP and repeatedly stat()ing, consider using an external monitoring app/script which can hook into the OS-provided mechanism and call your PHP script on-demand when the file truly does change.
Add clearstatcache(); to your loop:
while(true)
{
$LastMod = filemtime("file");
clearstatcache();
if(($LastMod +60) > time())
{
echo "file in use please wait... last modified : $LastMod";
sleep(10);
}else{
process file
}
}