问题
I am getting an error I cannot reproduce.
The following code is part of a module which protects against attacks. This particular snippet is tracking how many hits from a particular bot user agent I am getting.
After many years of trouble free use, I am all of a sudden getting the error:
A non well formed numeric value encountered;
This is occurring at the line:
$seconds = time() - $time;
The value of $time is 2016-10-02 19:33:42
the function safefilename() returns:
Mozilla-5-0-compatible-spbot-5-0-3-http-OpenLinkProfiler-org-bot
The name of the file being written to and read is:
bot_2016-10-02--19-33-42_Mozilla-5-0-compatible-spbot-5-0-3-http-Open_104.131.179.5.log
Methodology
The code below targets bots and writes to a filename which is based on the user agent and the time the file was created. Each time that user agent is used, it adds an "X" to the file so I can keep track of how many times that agent has visited. If a bot is targeting me more than a certain number of times, I block it.
The code below produces the desire outcome in testing and in production - except of course when this error is thrown. The file mentioned has 6 bytes written to it, so it has been read and written to successfully 5 times before.
The php error was logged at 06:37:04 and my server log file shows these hits:
104.131.63.140 - - [10/Dec/2016:06:36:59 -0800] "GET /robots.txt HTTP/1.1" 301 257 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"
104.131.63.140 - - [10/Dec/2016:06:36:59 -0800] "GET /robots.txt HTTP/1.1" 200 1460 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"
104.131.63.140 - - [10/Dec/2016:06:37:04 -0800] "GET / HTTP/1.1" 403 937 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"
104.131.63.140 - - [10/Dec/2016:06:37:05 -0800] "GET / HTTP/1.1" 301 247 "-" "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )"
PHP Code I've extracted the following code which can be run stand alone to test.
// this is my site address
define("STATIC_SITE_ROOT", "http://static");
$agent = "Mozilla/5.0 (compatible; spbot/5.0.3; +http://OpenLinkProfiler.org/bot )";
$ip = '127.0.0.1';
$t = new test();
$t->testAgent($agent, $ip);
class test {
public $agent;
public $ip;
public $maxbadpages = 100;
function testAgent($agent, $ip){
$this->agent = $agent;
$this->ip = $ip;
if (strlen($badbot = $this->badbot($this->agent)) > 0){
$new = FALSE;
$path = $_SERVER['DOCUMENT_ROOT'] . "/logs";
// $filename = "bot-" . time() . "-" . safefilename(substr($this->agent, 0, 50));
$safefilename = safefilename(substr($this->agent, 0, 50));
$filename = "bot_" . date("Y-m-d--H-i-s") . "_" . $safefilename . "_" . $this->ip . ".log";
$filter = $safefilename;
$afiles = getDirArray($path, $filter);
if (count($afiles) > 0){
// bot file already exists
$filename = $afiles[0];
} else {
// add time to filename if crating new file
$new = TRUE;
}
$fullfilename = "$path/$filename";
// log a counter (# bytes in file)
file_put_contents($fullfilename, "X", FILE_APPEND);
// number of hits == size of file
$size = filesize($fullfilename);
// count hits to determine if block via htaccess
// if > # entries in log from a useragent, ban it
if ($size > $this->maxbadpages){
$this->blockagent($this->agent, $this->ip, "> $this->maxbadpages hits");
} elseif (! $new) {
// test for hits per second
$blockagent = FALSE;
$parts = explode("_", $filename);
// 2nd part is the time
// $time = strtotime($parts[1]);
$parts2 = explode("--", $parts[1]);
$time = $parts2[0] . " " . str_replace("-",":",$parts2[1]);
// seconds is time elapsed
$seconds = time() - $time;
// check for various scenarios
if ($size > $seconds * 2){
// more than average of 2 hits per second for any period
$blockagent = TRUE;
$reason = "$size (hits) > $seconds (seconds) * 2";
}
if ($seconds >= 10 && $size > $seconds * 1){
// more than 1 hit per second over 10 seconds
$blockagent = TRUE;
$reason = "$seconds (seconds) >= 10 && $size (hits) > $seconds (seconds) * 1";
}
if ($blockagent){
$this->blockagent($this->agent, $this->ip, $reason);
}
}
$this->blockAccess("bad bot: ". $badbot);
}
}
function blockAgent($message){
die("Block Agent: " . $message);
}
function blockAccess($message){
die("Block Access: " . $message);
}
function badbot($agent) {
if (stripos($agent, "bot") !==FALSE){
return "match 'bot' in agent: ($agent)";
} elseif (stripos($agent, "spider") !==FALSE){
return "match 'spider' in agent: ($agent)";
} elseif (stripos($agent, "crawl") !==FALSE){
return "match 'crawl' in agent: ($agent)";
}
$badbots = array(
"007AC9",
"2Bone",
"404 Checker",
"There are many more bad bots contained in this array...");
foreach ($badbots as $bot) {
//If the spider text is found in the current user agent, then return true
if (stripos($agent, $bot) !== false){
return "$bot ($agent)";
return "match: $bot in agent: ($agent)";
}
}
//If it gets this far then no bot was found!
return "";
}
}
function safefilename($string){
// convert entities e.g. Á => Á
$string = htmlentities($string, ENT_QUOTES, 'UTF-8');
// replace the entities with letter equivalents
$string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string);
// return entities which did not have letter equivalents back to entities
$string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');
// replace non valid chars with dash and multiple dashes with only one
$string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), '-', $string);
return trim($string, ' -');
}
function getDirArray($path = "./", $filter = ".*", $exclude = '', $sorted = true, $optfilter2 = '') {
// for server directories, can't use the static url
$path = str_replace(STATIC_SITE_ROOT, $_SERVER['DOCUMENT_ROOT'], $path);
if (file_exists($path) == false) {
if (mkdir($path, 0777, true) == false) {
die($path);
exit;
}
}
$handle = opendir($path);
$dir = array();
while ($file = readdir($handle)) {
if (is_file("$path/$file") && preg_match("/$filter/", $file) && (strlen($exclude) == 0 ? TRUE : !preg_match("/$exclude/", $file))) {
if ($optfilter2 == '') {
// No 2n filter
$dir[] = $file;
} else {
$pos = strpos($file, $optfilter2);
if ($pos === false) {
// Not found
} else {
$dir[] = $file;
}
}
}
}
closedir($handle);
if ($sorted == true) {
sort($dir);
}
return $dir;
}
回答1:
The issue was you were using a datetime string rather than a unix timestamp. You needed to use, as suggested in my comment, strtotime($time)
to fix this, but you don't seem to understand why.
From the documentation for time
:
Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
This means when you are executing time()
it is returning the number of seconds-- an integer-- since New Years of 1970 in GMT timezone.
On the other hand, you had $time
, which was a string. This string is a more user-friendly string that can be read rather than an integer representing a number of seconds. There are some cases where you need this string rather than a unix timestamp, although that was not the case this time.
You were trying to subtract $time
(string) from time()
(integer). This obviously will not work, as you cannot subtract a letter from a number, and that's why you were getting that error. strtotime
is a function that is able to parse a date as a string such as the one you provided and convert it into an integer of how many seconds since New Years of 1970.
In your comment you said, after encasing $time
in strtotime()
, that you are now getting 5937340
as the result. This is the difference in seconds between the current time and $time
. Hopefully this is what you were looking for. It is equivalent to approximately 68.7 days. If this is not the result you expected, then I can try to help you further.
It is also possible to subtract two date strings from each other using the DateTime
class but it is more complicated and unnecessary in your situation in my opinion. You cannot, though, subtract an integer date from a string date. They have to be converted to be the same type. Hopefully I helped clear this up for you.
来源:https://stackoverflow.com/questions/41079104/a-non-well-formed-numeric-value-encountered-can-not-reproduce