├── .github ├── FUNDING.yml └── workflows │ └── stale.yml ├── .gitignore ├── .travis.yml ├── JJG ├── Ping.php └── PingTest.php ├── LICENSE.md ├── README.md ├── Resources ├── Ping-Logo.png └── Ping-Logo.svg └── composer.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close inactive issues 3 | 'on': 4 | schedule: 5 | - cron: "55 10 * * 4" # semi-random time 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/stale@v8 15 | with: 16 | days-before-stale: 120 17 | days-before-close: 60 18 | exempt-issue-labels: bug,pinned,security,planned 19 | exempt-pr-labels: bug,pinned,security,planned 20 | stale-issue-label: "stale" 21 | stale-pr-label: "stale" 22 | stale-issue-message: | 23 | This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 24 | 25 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 26 | close-issue-message: | 27 | This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 28 | stale-pr-message: | 29 | This pr has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution! 30 | 31 | Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale. 32 | close-pr-message: | 33 | This pr has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details. 34 | repo-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | /.buildpath 3 | /.project 4 | /.settings 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # See: http://about.travis-ci.org/docs/user/languages/php/ 3 | language: php 4 | 5 | php: 6 | - 7.2 7 | - 7.3 8 | - nightly 9 | 10 | matrix: 11 | fast_finish: true 12 | allow_failures: 13 | - php: nightly 14 | 15 | script: 16 | - sudo /home/travis/.phpenv/shims/phpunit --coverage-text JJG/PingTest.php 17 | -------------------------------------------------------------------------------- /JJG/Ping.php: -------------------------------------------------------------------------------- 1 | ping(); 18 | * @endcode 19 | * 20 | * @version 1.2.1 21 | * @author Jeff Geerling. 22 | */ 23 | 24 | namespace JJG; 25 | 26 | class Ping { 27 | 28 | private $host; 29 | private $ttl; 30 | private $timeout; 31 | private $port = 80; 32 | private $data = 'Ping'; 33 | private $commandOutput; 34 | 35 | /** 36 | * Called when the Ping object is created. 37 | * 38 | * @param string $host 39 | * The host to be pinged. 40 | * @param int $ttl 41 | * Time-to-live (TTL) (You may get a 'Time to live exceeded' error if this 42 | * value is set too low. The TTL value indicates the scope or range in which 43 | * a packet may be forwarded. By convention: 44 | * - 0 = same host 45 | * - 1 = same subnet 46 | * - 32 = same site 47 | * - 64 = same region 48 | * - 128 = same continent 49 | * - 255 = unrestricted 50 | * @param int $timeout 51 | * Timeout (in seconds) used for ping and fsockopen(). 52 | * @throws \Exception if the host is not set. 53 | */ 54 | public function __construct($host, $ttl = 255, $timeout = 10) { 55 | if (!isset($host)) { 56 | throw new \Exception("Error: Host name not supplied."); 57 | } 58 | 59 | $this->host = $host; 60 | $this->ttl = $ttl; 61 | $this->timeout = $timeout; 62 | } 63 | 64 | /** 65 | * Set the ttl (in hops). 66 | * 67 | * @param int $ttl 68 | * TTL in hops. 69 | */ 70 | public function setTtl($ttl) { 71 | $this->ttl = $ttl; 72 | } 73 | 74 | /** 75 | * Get the ttl. 76 | * 77 | * @return int 78 | * The current ttl for Ping. 79 | */ 80 | public function getTtl() { 81 | return $this->ttl; 82 | } 83 | 84 | /** 85 | * Set the timeout. 86 | * 87 | * @param int $timeout 88 | * Time to wait in seconds. 89 | */ 90 | public function setTimeout($timeout) { 91 | $this->timeout = $timeout; 92 | } 93 | 94 | /** 95 | * Get the timeout. 96 | * 97 | * @return int 98 | * Current timeout for Ping. 99 | */ 100 | public function getTimeout() { 101 | return $this->timeout; 102 | } 103 | 104 | /** 105 | * Set the host. 106 | * 107 | * @param string $host 108 | * Host name or IP address. 109 | */ 110 | public function setHost($host) { 111 | $this->host = $host; 112 | } 113 | 114 | /** 115 | * Get the host. 116 | * 117 | * @return string 118 | * The current hostname for Ping. 119 | */ 120 | public function getHost() { 121 | return $this->host; 122 | } 123 | 124 | /** 125 | * Set the port (only used for fsockopen method). 126 | * 127 | * Since regular pings use ICMP and don't need to worry about the concept of 128 | * 'ports', this is only used for the fsockopen method, which pings servers by 129 | * checking port 80 (by default). 130 | * 131 | * @param int $port 132 | * Port to use for fsockopen ping (defaults to 80 if not set). 133 | */ 134 | public function setPort($port) { 135 | $this->port = $port; 136 | } 137 | 138 | /** 139 | * Get the port (only used for fsockopen method). 140 | * 141 | * @return int 142 | * The port used by fsockopen pings. 143 | */ 144 | public function getPort() { 145 | return $this->port; 146 | } 147 | 148 | /** 149 | * Return the command output when method=exec. 150 | * @return string 151 | */ 152 | public function getCommandOutput(){ 153 | return $this->commandOutput; 154 | } 155 | 156 | /** 157 | * Matches an IP on command output and returns. 158 | * @return string 159 | */ 160 | public function getIpAddress() { 161 | $out = array(); 162 | if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->commandOutput, $out)){ 163 | return $out[0]; 164 | } 165 | return null; 166 | } 167 | 168 | /** 169 | * Ping a host. 170 | * 171 | * @param string $method 172 | * Method to use when pinging: 173 | * - exec (default): Pings through the system ping command. Fast and 174 | * robust, but a security risk if you pass through user-submitted data. 175 | * - fsockopen: Pings a server on port 80. 176 | * - socket: Creates a RAW network socket. Only usable in some 177 | * environments, as creating a SOCK_RAW socket requires root privileges. 178 | * 179 | * @throws InvalidArgumentException if $method is not supported. 180 | * 181 | * @return mixed 182 | * Latency as float, in ms, if host is reachable or FALSE if host is down. 183 | */ 184 | public function ping($method = 'exec') { 185 | $latency = false; 186 | 187 | switch ($method) { 188 | case 'exec': 189 | $latency = $this->pingExec(); 190 | break; 191 | 192 | case 'fsockopen': 193 | $latency = $this->pingFsockopen(); 194 | break; 195 | 196 | case 'socket': 197 | $latency = $this->pingSocket(); 198 | break; 199 | 200 | default: 201 | throw new \InvalidArgumentException('Unsupported ping method.'); 202 | } 203 | 204 | // Return the latency. 205 | return $latency; 206 | } 207 | 208 | /** 209 | * The exec method uses the possibly insecure exec() function, which passes 210 | * the input to the system. This is potentially VERY dangerous if you pass in 211 | * any user-submitted data. Be SURE you sanitize your inputs! 212 | * 213 | * @return float 214 | * Latency, in ms. 215 | */ 216 | private function pingExec() { 217 | $latency = false; 218 | 219 | $ttl = escapeshellcmd($this->ttl); 220 | $timeout = escapeshellcmd($this->timeout); 221 | $host = escapeshellcmd($this->host); 222 | 223 | // Exec string for Windows-based systems. 224 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 225 | // -n = number of pings; -i = ttl; -w = timeout (in milliseconds). 226 | $exec_string = 'ping -n 1 -i ' . $ttl . ' -w ' . ($timeout * 1000) . ' ' . $host; 227 | } 228 | // Exec string for Darwin based systems (OS X). 229 | else if(strtoupper(PHP_OS) === 'DARWIN') { 230 | // -n = numeric output; -c = number of pings; -m = ttl; -t = timeout. 231 | $exec_string = 'ping -n -c 1 -m ' . $ttl . ' -t ' . $timeout . ' ' . $host; 232 | } 233 | // Exec string for other UNIX-based systems (Linux). 234 | else { 235 | // -n = numeric output; -c = number of pings; -t = ttl; -W = timeout 236 | $exec_string = 'ping -n -c 1 -t ' . $ttl . ' -W ' . $timeout . ' ' . $host . ' 2>&1'; 237 | } 238 | 239 | exec($exec_string, $output, $return); 240 | 241 | // Strip empty lines and reorder the indexes from 0 (to make results more 242 | // uniform across OS versions). 243 | $this->commandOutput = implode('', $output); 244 | $output = array_values(array_filter($output)); 245 | 246 | // If the result line in the output is not empty, parse it. 247 | if (!empty($output[1])) { 248 | // Search for a 'time' value in the result line. 249 | $response = preg_match("/time(?:=|<)(?