I am working on a PHP website, and I am moving it over to a new server. The new server I am moving to does not have CRON compatibility. To compensate for this I have devised a system using time formats and database tables and more to run my code instead.
What I am having a problem with is this bit of code:
if ($lasttime < $pretime)
{
$newtime = strtotime("now");
queryMysql("UPDATE time SET time=".$newtime." WHERE time=".$lasttime);
include_once 'grabber/grabber.php';
}
Specifically it's the include_once 'grabber/grabber.php'; which is causing the problem. When the timer comes round and this code runs, it gets to the include and then the code stops, with no error provided, so the include fails. I have tried changing it to an exec() but to be honest I don't completely understand how exec() works and if it is the correct thing to do. This is how I used it:
if ($lasttime < $pretime)
{
$newtime = strtotime("now");
queryMysql("UPDATE time SET time=".$newtime." WHERE time=".$lasttime);
$grabber = $base."grabber/grabber.php";
exec($grabber);
}
This does not stop the code and seems to run but it doesn't actually work, if grabber/grabber.php runs correctly then I get an email to confirm using the PHP mail() function
If anyone could help me solve this or shed some light that would be brilliant.
Thanks.
This is most probably an issue with the file location or permissions. There should be some kind of error, or the code doesn't stop, but you don't properly check that or there is some kind of an issue with the code in grabber.php itself. Add some debugging lines - print the filename, so you can check for errors in the path/name; add error_reporting(E_ALL); ini_set('display_errors', true); somewhere above the include_once line; make sure the file is where you're trying to open it from, taking into account relative paths, etc. Make sure you have permissions to run this file.
exec() is not what you need in this case, at least not in the way that you're trying to use it.
If that doesn't help - give some more information about how you run the scripts that you've shown, what's in the grabber.php file, what errors you get, etc.
(Assuming your server is *nix) If you want to use exec() you need to place a hashbang at the top of the script that points to the PHP executable and give it execute permissions.
Or (this is probably the better/more portable approach), change
$grabber = $base."grabber/grabber.php";
exec($grabber);
to
$grabber = "php ".$base."grabber/grabber.php";
exec($grabber);
...as if you were running it from a terminal.
However, I doubt this will solve the problem - I think the answer is more likely to be one of these things:
A parse error in grabber.php. Keep in mind that there are slight syntax differences between major PHP versions - if your PHP version is different on your old/new hosts, this may be the problem.
A call to a function that was defined on your old host but not on your new host, because of a difference in PHP version or installed extensions
grabber.php was corrupted during the move between servers
Try it with the include_once, but do ini_set('display_errors',1); error_reporting(-1); to make sure you actually see any errors. How are you calling you main script? How will you see the errors? Edit the question with this info, any code from grabber.php you think may be relevant and I will expand this answer.
Related
In a PHP script intended to work on generic shared LAMP hosting, I am using require() as a function to read data from a file. The data file looks like this:
<?php
# storage.php
return [
// Rotating secrets
'last_rand' => '532e89355b78aafdb85f5f01f0eed20440d6bd9e0a2d6ae1bd17be4e1d7d21c7bb7a822a2077e3f4',
];
And I read it like this:
$data = require($root . '/storage.php');
$lastRand = $data['last_rand'];
In my script, I will read information from a configuration file using this approach. In some cases, the operation will re-write a new config file (using file_put_contents()) and then re-read it, again using require().
This has worked solidly so far, on a variety of LAMP hosts, but today I found a web host where the require() does not seem to notice changes in the file. This host is using PHP 5.6, whereas all the others I have tested are using 7+.
What's extremely odd is that require() gets the old contents of the file even though file_get_contents() can see the new version, as if require() is doing its own internal caching.
Failed fix attempts
I have even tried waiting for require() to catch up, in case some underlying cache needed time to expire:
$ok = true;
$t = microtime(true);
while ($oldRandom != $this->getFileService()->requirePhp($this->getStoragePath())['last_rand'])
{
$elapsedTime = microtime(true) - $t;
if ($elapsedTime > 4) // Wait 4 seconds
{
$ok = false;
break;
}
usleep(1000);
}
That did not work either (the timeout just expires with no change in the results brought back from require()).
However, upon the next run of the PHP script, require() suddenly sees the new file contents.
I've also tried to delete the storage.php file before re-reading it, and that has no effect either! I was really expecting that to help.
Future actions
So, I don't understand this behaviour at all. To work around it I could:
rather than modifying a new config, saving it to file and then reloading it from file, I could refactor my code so that I save the new config to memory, thus avoiding having to reload it
refactor so that I use file_get_contents() rather than require() (for rather dull reasons it's not as convenient, but I'll prefer whatever works reliably).
However, these both feel like I am ignoring a bug, and that I should investigate the cause. I happen to know also that this host (a free one) is nearly always under heavy CPU load. It's a 64 bit server running Linux on the rather old 2.6 kernel, and phpinfo() indicates the CGI/FastCGI server API is in force.
Can anything be done to mitigate this behaviour?
More attempts
Some helpful commenters suggest this problem could be related to opcaching, which seems to fit the general purpose of require(). I've added this code after the file_put_contents() that re-writes the config:
$reset = opcache_reset(); // Returns true
$invalid = opcache_invalidate($this->getStoragePath()); // Returns false
However, it makes no difference - require() stubbornly reads the same value. I have confirmed this by doing a require() before and after the file write, and also a file_get_contents() to get the true contents.
When I originally encountered this error, I was hesitant to work around it, since it felt like a critical bug that ought not be ignored. However, now that the culprit is likely to be opcaching (and thus related to require() specifically), I think working around it by keeping values in memory is not a bad solution. I shall have to remember that require() is only good for one call per HTTP request, even if that does not hold true for all PHP installations.
I am conscious also that if I did try to work around this, I would have to contend with a number of opcache invalidation mechanisms, which is more complexity than I am comfortable with.
I found the same problem with a config file, the only valid solution for now is to change the name of the config file with each modification:
1 - load config file
// get the config file name
$config_file=glob('config/config*.php'))[0] ?? null;
// load the settings
if($config_file) $settings=require_once($config_file);
2 - save the config file when needed
...
$data=['var'=>'value','other-var'=>'other value'];
$vars=var_export($data, TRUE);
// new file name
$file_name='config/config-'.time().'.php';
file_put_contents($file_name,"<?php\n return $vars;");
// delete old config file
if($config_file) #unlink($config_file);
I was having issues with another piece of code so I broke out this very simple part and put it in a new file:
Test file Location/Name
/var/www/html/php/tst/test.php
Test file Content
<?php
if(is_readable('/var/www/html/php/put/json_load.php')){echo('Worked');}
if(is_readable('/var/www/html/php/put/cr_query.php')){echo('Worked2');}
if(is_readable('/var/www/html/php/put/json_crdata_decode.php')){echo('Worked3');}
require '/var/www/html/php/put/json_load.php';
echo('Worked4');
?>
The output I get from this is
WorkedWorked2Worked3
It seems to be stopping execution at require. I am fairly new to PHP, and have searched long and hard for a solution. Any help is appreciated.
If any more information is needed I'd be glad to provide it.
There are three possibilities:
json_load.php stop script execution by calling exit() or die()
json_load.php triggers an error. Your display_error is set to 0 so
you experience no feedback
segmentation fault
I suspect option 2. Edit your php.ini and set display_errors to 1 then restart the server. If you don't know where php.ini is run phpinfo() funciton, it will tell you.
I'm upgrading a huge codebase for thousands of web pages to PHP 5.3 from an earlier version. We've dropped the use of short tags (<%, <\?=, etc...) and have them disabled in the php.ini and have made reasonable effort to find any in the code and replace them.
However, When someone creates something with or short tag or some legacy code still has one we missed, Apache returns a blank document with a 200 status. The problem is, PHP doesn't throw an error (obviously since it's not parsing them) and Apache doesn't seem to log it is an error either. This creates a problem for detecting these without visually inspecting all pages (a simple crawler is happy with the 200 the url returns).
Does anyone know of any way to get Apache or PHP to throw an error when it hits a short tag as a site is being crawled?
Can't really find a way to have PHP or Apache to issue some kind of a warning related to documents with short tags, but you could set a cron job to search all files under your server's web folder and for example send an email with the results, thus at the very least having pointed out the files with short tags on it:
Simple Example: cron job
<?php
// run grep command for '<?' that don't have an immediately p
$found = shell_exec('grep -rn "<?[^p]" *');
if ($found!='') {
// email or any other action...
}
?>
I don't have access to the apache server or whatever is running on the server. I just have a free account with a web host. I do apparently have access to the .htaccess file, but I'm not sure if I feel safe enough to temper with the "nuts and bolts" as it were. So instead I tried to enable debug printouts in my php script like so:
error_reporting(E_ALL);
I'm not seeing any errors, but I know that there is an error since the entire script isn't executing.
Is there an easy way to get error printouts to show in the code that php returns? If I tried to temper with .htaccess, what would I have to do there? It's only 36 bytes long.
DO:
error_reporting(E_ALL);
ini_set('display_errors',"On");
You could also try debug_backtrace().
var_dump(debug_backtrace());
I've recently started using APC cache on our servers. One of the most important parts of our product is a CLI (Cron/scheduled) process, whose performance is critical. Typically the batchjob consists of running some 16-32 processes in parallel for about an hour (they "restart" every few minutes).
By default, using APC cache in CLI is a waste of time due to the opcode cache not being retained between individual calls. But APC also contains apc_bin_dumpfile() and apc_load_dumpfile() functions.
I was thinking these two function might be used to make APC efficient in CLI mode by having it all compiled sometime outside the batchjob, stored in a single dumpfile and having the individual processes load the dumpfile.
Does anybody have any experience with such a scenario or can you give good reasons why it will or will not work? If any significant gains could reasonably be had, either in memory use or performance? What pitfalls are lurking in the shadows?
Disclaimer: As awesome as APC is when it works in CLI, and it is awesome, it can equally be as frustrating. Use with a healthy load of patience, be thorough, step away from the problem if you're spinning, keep in mind you are working with cache that is why it seems like its doing nothing, it is actually doing nothing. Delete dump file, start with just the basics, if that doesn't work forget it try a new machine, new OS, if it is working make a copy, piece by piece expand functionality - there are loads of things that won't work, if it is working commit or make a copy, add another piece and test again, for sanity-check recheck the copies that were working before, cliches or not; if at first you don't succeed try try again, you can't keep doing the same thing expecting new results.
Ready? This is what you've been waiting for:
Enable apc for cli
apc.enable-cli=1
it is not ideal to create, populate and destroy the APC cache on every CLI request
- previous answer by unknown poster since removed.
You're absolutely right that sucks, lets fix it shall we?
If you try and use APC under CLI and it is not enabled you will get warnings.
something like:
PHP Warning: apc_bin_loadfile(): APC is not enabled,
apc_bin_loadfile not available.
PHP Warning: apc_bin_dumpfile(): APC is not enabled,
apc_bin_dumpfile not available.
Warning: I suggest you don't enable cli in php.ini, it is not worth the frustration, you are going to forget you did it and have numerous other headaches with other scripts, trust me its not worth it, use a launcher script instead. (see below)
apc_loadfile and apc_dumpfile in cli
As per the comment by mightye php we need to disable apc.stat or you will get a warnings
something like:
PHP Warning: apc_bin_dumpfile(): Excluding some files from apc_bin_dump[file].
Cached files must be included using full path with apc.stat=0.
launcher script - php-apc.sh
We will use this script to launch our apc enabled scripts (ex. ./php-apc.sh apc-cli.php) instead of changing the properties in php.ini directly.
#/bin/sh
php -d apc.enable_cli=1 -d apc.stat=0 $1
Ready for the basic functionality? Sure you are =)
basic APC persisted - apc-cli.php
<?php
/** check if dump file exists, you don't want to use file_exists */
if (false !== $dump_file = stream_resolve_include_path('apc.dump'))
/** so where were we lets have a look see shall we */
if (false !== apc_bin_loadfile($dump_file))
/** fetch what was stored last run just for fun */
if (false !== $value = apc_fetch('my.awesome.apc.store'))
echo "$value from apc\n";
/** store what gets fetched the next run just for fun */
apc_store('my.awesome.apc.store', 'awesome in cli');
/** what a shlep lets not do that all over again shall we */
apc_bin_dumpfile(array(),null,'apc.dump');
Notice: Why not use file_exists? Because file_exists == stat you see and we want to reap the reward that is apc.stat=0 so; work within the include path; use absolute and not relative paths - as returned by stream_resolve_include_path(); avoid include_once, require_once use the non *_once counterparts; check your stat usage, when not using APC(Muchos important senor), with the help of a StreamWrapper echo for calls to method url_stat; Oops: Fatal scope over-run error! aborting notice thread. see url_stat
message: Error caused by StreamWrapper outside the scope of this discussion.
The smoke test
Using the launcher execute the basic script
./php-apc.sh apc-cli.php
A whole bunch of nothing happened that's what we want right, why else do you want to use cache? If it did output anything then it didn't work, sorry.
There should be a dump file called apc.dump see if you can find it? If you can't find it then it didn't work, sorry.
Good we have the dump file there were no errors lets run it again.
./php-apc.sh apc-cli.php
What you want to see:
awesome in cli from apc
Success! =)
There are few in PHP as satisfying as a working APC implementation.
nJoy!
I would definitely not use it in the CLI as when you restart it, it's almost as if it was never running in the first place!
The better way of using APC is to have it running on the webserver itself all the time, this way with it being active it will actually do what it's supposed to do!
I tryed with curl and APC.it works
use these commands in CLI
curl --data "param1=value2" http://testsite.com/test.php
so it will post data to test.php and you writes the code in it.