From fc0488b0433e0299ddc2d734595fb39f3c3f24e8 Mon Sep 17 00:00:00 2001 From: Eric Sizemore Date: Sat, 10 Jun 2023 21:06:37 -0400 Subject: [PATCH] Version bump, PHP version requirement upped Mostly small updates to documentation. New helper functions were added to improve the getIpAddress function. --- README.md | 4 +- counter.php | 142 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 77d5e49..6ab10be 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Usage is fairly simple once installed. Simply add the following code to the page ### Requirements -- Simple Counter works with PHP 7.0.0 or above. +- Simple Counter works with PHP 8.0.0 or above. ### Submitting bugs and feature requests @@ -81,7 +81,7 @@ branches for both of these features and send two requests. ### Author -Eric Sizemore - - +Eric Sizemore - - ### License diff --git a/counter.php b/counter.php index 2eca3ce..81852c9 100644 --- a/counter.php +++ b/counter.php @@ -1,53 +1,66 @@ -* @package SV's Simple Counter -* @link http://www.secondversion.com/downloads/ -* @version 4.0.3 -* @copyright (C) 2006 - 2021 Eric Sizemore -* @license GNU Lesser General Public License -* -* SV's Simple Counter is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -* details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this program. If not, see . -*/ + * SV's Simple Counter - A simple web hit counter. + * + * @author Eric Sizemore + * @package SV's Simple Counter + * @link https://www.secondversion.com/ + * @version 4.0.4 + * @copyright (C) 2006 - 2023 Eric Sizemore + * @license GNU Lesser General Public License + */ namespace Esi\SimpleCounter; +use Exception; + /** - * Main class that processes the counter. + * SV's Simple Counter - A simple web hit counter. + * + * @author Eric Sizemore + * @package SV's Simple Counter + * @link https://www.secondversion.com/ + * @version 4.0.4 + * @copyright (C) 2006 - 2023 Eric Sizemore + * @license GNU Lesser General Public License + * + * Copyright (C) 2006 - 2023 Eric Sizemore. All rights reserved. + * + * SV's Simple Counter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . */ class Counter { /** User Configuration **/ // Log file locations/paths. - const COUNT_FILE = 'counter/logs/counter.txt'; - const IP_FILE = 'counter/logs/ips.txt'; + private const COUNT_FILE = 'counter/logs/counter.txt'; + private const IP_FILE = 'counter/logs/ips.txt'; // Use file locking? - const USE_FLOCK = true; + private const USE_FLOCK = true; // Count only unique visitors? - const ONLY_UNIQUE = true; + private const ONLY_UNIQUE = true; // Show count as images? - const USE_IMAGES = false; + private const USE_IMAGES = false; // Path to the images. - const IMAGE_DIR = 'counter/images/'; + private const IMAGE_DIR = 'counter/images/'; // Image extension. - const IMAGE_EXT = '.gif'; + private const IMAGE_EXT = '.gif'; /** End User Configuration **/ @@ -88,9 +101,10 @@ public static function getInstance(): self */ private function getIpAddress(bool $trustProxyHeaders = false): string { + // Pretty self-explanatory. Try to get an 'accurate' IP $ip = $_SERVER['REMOTE_ADDR']; - if ($trustProxyHeaders === false) { + if ($trustProxyHeaders) { return $ip; } @@ -106,15 +120,13 @@ private function getIpAddress(bool $trustProxyHeaders = false): string if (!empty($ips)) { foreach ($ips AS $val) { - if ( - \inet_ntop(\inet_pton($val)) == $val - AND !\preg_match("#^(10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|192\.168\.|fe80:|fe[c-f][0-f]:|f[c-d][0-f]{2}:)#i", $val) - ) { + if (\inet_ntop(\inet_pton($val)) == $val AND self::isPublicIp($val)) { $ip = $val; break; } } } + unset($ips); if (!$ip AND isset($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; @@ -122,6 +134,54 @@ private function getIpAddress(bool $trustProxyHeaders = false): string return $ip; } + // --- getIpAddress helpers --- // + /** + * isPrivateIp() + * + * Determines if an IP address is within the private range. + * + * @param string $ipaddress IP address to check. + * @return bool + */ + private function isPrivateIp(string $ipaddress): bool + { + return !(bool)\filter_var( + $ipaddress, + \FILTER_VALIDATE_IP, + \FILTER_FLAG_IPV4 | \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_PRIV_RANGE + ); + } + + /** + * isReservedIp() + * + * Determines if an IP address is within the reserved range. + * + * @param string $ipaddress IP address to check. + * @return bool + */ + private function isReservedIp(string $ipaddress): bool + { + return !(bool)\filter_var( + $ipaddress, + \FILTER_VALIDATE_IP, + \FILTER_FLAG_IPV4 | \FILTER_FLAG_IPV6 | \FILTER_FLAG_NO_RES_RANGE + ); + } + + /** + * isPublicIp() + * + * Determines if an IP address is not within the private or reserved ranges. + * + * @param string $ipaddress IP address to check. + * @return bool + */ + private function isPublicIp(string $ipaddress): bool + { + return (bool)(!self::isPrivateIp($ipaddress) AND !self::isReservedIp($ipaddress)); + } + /** * We use this function to open and read/write to files. * @@ -129,26 +189,28 @@ private function getIpAddress(bool $trustProxyHeaders = false): string * @param string $mode Mode (r, w, a, etc..) * @param string $data If writing to the file, the data to write * @return mixed + * + * @throws Exception */ - private function readWriteFile(string $file, string $mode, string $data = '') + private function readWriteFile(string $file, string $mode, string $data = ''): mixed { if (!\file_exists($file) OR !\is_writable($file)) { - throw new \Exception(\sprintf("'%s' does not exist or is not writable.", $file)); + throw new Exception(\sprintf("'%s' does not exist or is not writable.", $file)); } if (!($fp = \fopen($file, $mode))) { - throw new \Exception(\sprintf("'%s' could not be opened.", $file)); + throw new Exception(\sprintf("'%s' could not be opened.", $file)); } $return = null; - if (self::USE_FLOCK AND \flock($fp, LOCK_EX)) { + if (self::USE_FLOCK AND \flock($fp, \LOCK_EX)) { if ($mode == 'r') { $return = \fread($fp, \filesize($file)); } else { \fwrite($fp, $data); } - \flock($fp, LOCK_UN); + \flock($fp, \LOCK_UN); } else { if ($mode == 'r') { $return = \fread($fp, \filesize($file)); @@ -176,7 +238,7 @@ public function process() $ip = self::getIpAddress(); $ips = \trim(self::readWriteFile(self::IP_FILE, 'r')); - $ips = \preg_split("#\n#", $ips, -1, PREG_SPLIT_NO_EMPTY); + $ips = \preg_split("#\n#", $ips, -1, \PREG_SPLIT_NO_EMPTY); // They've not visited before if (!\in_array($ip, $ips)) { @@ -191,7 +253,7 @@ public function process() // Do we want to display the # visitors as graphics? if (self::USE_IMAGES) { - $count = \preg_split('##', $count, -1, PREG_SPLIT_NO_EMPTY); + $count = \preg_split('##', $count, -1, \PREG_SPLIT_NO_EMPTY); $length = \count($count); for ($i = 0; $i < $length; $i++) {