Я получаю ошибку, которую не могу воспроизвести.
Следующий код является частью модуля, который защищает от атак. Этот конкретный фрагмент отслеживает, сколько хитов от конкретного агента пользователя бота я получаю.
После многих лет беспроблемного использования я внезапно получаю сообщение об ошибке:
Обнаружено неверно сформированное числовое значение;
Это происходит на линии:
$seconds = time() - $time;
Значение времени $ 2016-10-02 19:33:42
функция safefilename () возвращает:
Mozilla-5-0-совместимый-spbot-5-0-3-HTTP-OpenLinkProfiler-орг-бот
Имя файла для записи и чтения:
bot_2016-10-02—19-33-42_Mozilla-5-0-совместимая-spbot-5-0-3-HTTP-Open_104.131.179.5.log
методология
Приведенный ниже код предназначен для ботов и записи в имя файла, основанное на пользовательском агенте и времени создания файла. Каждый раз, когда этот пользовательский агент используется, он добавляет «X» в файл, чтобы я мог отслеживать, сколько раз этот агент посетил. Если бот нацеливается на меня более определенного количества раз, я блокирую его.
Приведенный ниже код дает желаемый результат в тестировании и в производстве — за исключением, конечно, когда выдается эта ошибка. В упомянутый файл записано 6 байтов, поэтому он был успешно прочитан и записан 5 раз.
Ошибка php была зарегистрирована в 06:37:04, и мой файл журнала сервера показывает эти хиты:
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
Я извлек следующий код, который можно запустить самостоятельно для тестирования.
// 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;
}
Проблема заключалась в том, что вы использовали строку даты и времени, а не метку времени Unix. Вы должны были использовать, как предложено в моем комментарии, strtotime($time)
чтобы исправить это, но вы, кажется, не понимаете, почему.
Из документации для time
:
Возвращает текущее время, измеренное в секундах с начала эпохи Unix (1 января 1970 г., 00:00:00 по Гринвичу).
Это означает, что когда вы выполняете time()
он возвращает количество секунд— целое число— с нового года 1970 в часовом поясе GMT.
С другой стороны, у вас было $time
который был строка. Эта строка является более удобной для чтения строкой, которая может быть прочитана, а не целым числом, представляющим количество секунд. В некоторых случаях вам нужна эта строка, а не метка времени Unix, хотя в этот раз это было не так.
Вы пытались вычесть $time
(строка) из time()
(Целое число). Это, очевидно, не будет работать, так как вы не можете вычесть письмо из числа, и именно поэтому вы получили эту ошибку. strtotime
это функция, которая может анализировать дату как строку, например, предоставленную вами, и конвертировать ее в целое число, равное числу секунд, прошедших с Нового года 1970 года.
В своем комментарии вы сказали, после заключения $time
в strtotime()
, что вы сейчас получаете 5937340
в результате. Это разница в секундах между текущим временем и $time
, Надеюсь, это то, что вы искали. Это эквивалентно примерно 68,7 дням. Если это не тот результат, которого вы ожидали, тогда я могу попытаться помочь вам в дальнейшем.
Также возможно вычесть две строки даты друг из друга, используя DateTime
класс, но это более сложный и ненужный в вашей ситуации, на мой взгляд. Вы не можете, однако, вычесть целую дату из строковой даты. Они должны быть преобразованы, чтобы быть того же типа. Надеюсь, я помог вам разобраться с этим.
Других решений пока нет …