├── .env.travis ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md ├── SECURITY.md └── workflows │ └── main.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── config └── config.php ├── phpunit.xml ├── sonar-project.properties └── src ├── Console └── InstallPingPackageCommand.php ├── Exceptions ├── InvalidIPAddressException.php ├── MaxValueException.php ├── NegativeValueException.php ├── PingFailedException.php ├── TimerNotStartedException.php └── UnknownOSException.php ├── Facades └── PingFacade.php ├── IPAddress.php ├── Interfaces ├── PingCommand.php └── PingParserInterface.php ├── Parsers ├── PingParser.php ├── PingParserForNix.php └── PingParserForWindows.php ├── Ping.php ├── PingCommandBuilder.php ├── ServiceProviders └── PingServiceProvider.php ├── System.php └── Timer.php /.env.travis: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY=base64:aYpD7YNDNY8QmslWAyJOG+zi8m4WSpRqGHAGqbt05TA= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at angel.campos.m@outlook.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.blockchain.com/btc/address/3DWxSQg2HNijAADowkQWFJsDHHpKo8Ec2z'] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug reports 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ### Reporting a Vulnerability 4 | 5 | If you discover any security related issues, please email angel.campos.m@outlook.com instead of using the issue tracker. 6 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: PHP-CI 4 | 5 | on: 6 | push: 7 | branches: [ master ] 8 | pull_request: 9 | branches: [ master ] 10 | types: [ opened, synchronize, reopened ] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | 17 | tests: 18 | 19 | name: PHP-Unit 20 | runs-on: ubuntu-latest 21 | steps: 22 | 23 | - uses: actions/checkout@v2 24 | with: 25 | fetch-depth: 0 26 | 27 | - name: Validate composer.json and composer.lock 28 | run: composer validate --strict || composer update 29 | 30 | - name: Cache Composer packages 31 | id: composer-cache 32 | uses: actions/cache@v2 33 | with: 34 | path: vendor 35 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 36 | restore-keys: | 37 | ${{ runner.os }}-php- 38 | 39 | - name: Install dependencies 40 | run: composer install --prefer-dist --no-progress 41 | 42 | - name: Run test suite 43 | env: 44 | XDEBUG_MODE: coverage 45 | run: composer run-script test 46 | 47 | - name: Prepare for SonarCloud Scan - sed on clover.xml 48 | run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace@g' build/clover.xml 49 | 50 | - name: Prepare for SonarCloud Scan - sed on junit.xml 51 | run: sed -i 's@'$GITHUB_WORKSPACE'@/github/workspace@g' build/junit.xml 52 | 53 | - name: SonarCloud Scan 54 | uses: SonarSource/sonarcloud-github-action@master 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 58 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `ping` will be documented in this file 4 | 5 | ## 2.1.3 - 2021-12-04 6 | 7 | Small fixes: 8 | 9 | - Fixed ping:install command, now when ping configuration is published the ping:install is not displayed. 10 | 11 | Small refactoring 12 | 13 | ## 2.1.2 - 2021-11-28 14 | 15 | Small fixes: 16 | 17 | - Removed ending slash on url hosts. 18 | - Sanitized ping command execution results. 19 | 20 | ## 2.1.1 - 2021-02-07 21 | 22 | Small fixes: 23 | 24 | - An error in the README.md file has been corrected. 25 | - Fixed a bug when parsing the result of a ping to an unreachable host. 26 | 27 | ## 2.1.0 - 2021-01-01 28 | 29 | Added package install command, now you can do: 30 | 31 | ```bash 32 | php artisan ping:install 33 | ``` 34 | 35 | To publish the package configuration file. 36 | 37 | ## 2.0.0 - 2020-08-09 38 | 39 | In this version there are important changes, because I have rewritten the class, adding a class to generate the ping commands. With this class you can also create commands for IPv4 and IPv6 addresses, and it allows you to ping domain names. 40 | 41 | - The System.php class has been added to detect the type of Operating System. 42 | - The IPAddress.php class has been added to perform the verification and validation of IPv4 and IPv6 addresses. 43 | - Exceptions have been added for error handling. 44 | 45 | In this version, an instance of PingCommandBuilder has to be created and passed to the Ping class, then execute the run method of the Ping class to obtain an object with the results. 46 | 47 | ## 1.0.0 - 2020-04-13 48 | 49 | - Added support for Windows based servers, now it's possible to do PING on Linux/Windows based servers... 50 | 51 | ## 0.0.1 - 2020-04-12 52 | 53 | - initial release 54 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Angel Campos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PING for Laravel 2 | 3 | [![License](https://poser.pugx.org/acamposm/ping/license)](https://packagist.org/packages/acamposm/ping) 4 | [![Latest Stable Version](https://poser.pugx.org/acamposm/ping/v/stable)](https://packagist.org/packages/acamposm/ping) 5 | [![StyleCI](https://github.styleci.io/repos/255138468/shield?branch=master)](https://github.styleci.io/repos/255138468) 6 | [![Total Downloads](https://poser.pugx.org/acamposm/ping/downloads)](https://packagist.org/packages/acamposm/ping) 7 | 8 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ping&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=ping) 9 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ping&metric=coverage)](https://sonarcloud.io/summary/new_code?id=ping) 10 | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ping&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=ping) 11 | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ping&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=ping) 12 | 13 | This ping class allow making ping request from Laravel applications, it is based on PING command from the linux iputils package. 14 | 15 | ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway. ECHO_REQUEST datagrams (pings) have an IP and ICMP header, followed by a struct timeval and then an arbitrary number ofpadbytes used to fill out the packet. 16 | 17 | - [Installation](#installation) 18 | - [Usage](#usage) 19 | - [Change Count](#change-count) 20 | - [Change Interval](#change-interval) 21 | - [Change Packet Size](#change-packet-size) 22 | - [Change Timeout](#change-timeout) 23 | - [Change Time To Live](#change-time-to-live) 24 | - [Sample output](#sample-outputs) 25 | - [Testing](#testing) 26 | - [Changelog](#changelog) 27 | - [Contributing](#contributing) 28 | - [Security & Vulnerabilities](#security--vulnerabilities) 29 | - [Standards](#standards) 30 | - [Credits](#credits) 31 | - [License](#license) 32 | 33 | ## Installation 34 | 35 | You can install the package via [composer](https://getcomposer.org/) and then publish the assets: 36 | 37 | Prior to Ping 2.1.0 version you can install with: 38 | 39 | ```bash 40 | composer require acamposm/ping 41 | 42 | php artisan vendor:publish --provider="Acamposm\Ping\PingServiceProvider" 43 | ``` 44 | 45 | From Ping 2.1.0 version you can install with: 46 | 47 | ```bash 48 | composer require acamposm/ping 49 | 50 | php artisan ping:install 51 | ``` 52 | 53 | ## Usage 54 | 55 | For basic usage you only need to create with an ip address as a first argument and run... 56 | 57 | ```php 58 | use Acamposm\Ping\Ping; 59 | use Acamposm\Ping\PingCommandBuilder; 60 | 61 | // Create an instance of PingCommand 62 | $command = (new PingCommandBuilder('192.168.1.1')); 63 | 64 | // Pass the PingCommand instance to Ping and run... 65 | $ping = (new Ping($command))->run(); 66 | ``` 67 | 68 | ### Change Count 69 | 70 | Stop after sending count ECHO_REQUEST packets. With deadline option, ping waits for count ECHO_REPLY packets, until the timeout expires. 71 | 72 | ```php 73 | use Acamposm\Ping\Ping; 74 | use Acamposm\Ping\PingCommandBuilder; 75 | 76 | // Change the number of packets to send to 10 77 | $command = (new PingCommandBuilder('192.168.1.1'))->count(10); 78 | 79 | $ping = (new Ping($command))->run(); 80 | ``` 81 | 82 | ### Change Interval 83 | 84 | Wait interval seconds between sending each packet. The default is to wait for one second between each packet normally, or not to wait in flood mode. Only super-user may set interval to values less than 0.2 seconds. 85 | 86 | ```php 87 | use Acamposm\Ping\Ping; 88 | use Acamposm\Ping\PingCommandBuilder; 89 | 90 | // Change interval to 0.5 seconds between each packet 91 | $command = (new PingCommandBuilder('192.168.1.1'))->interval(0.5); 92 | 93 | $ping = (new Ping($command))->run(); 94 | ``` 95 | 96 | ### Change Packet Size 97 | 98 | Specifies the number of data bytes to be sent. The default is 56, which translates into 64 ICMP data bytes when combined with the 8 bytes of ICMP header data. 99 | 100 | ```php 101 | use Acamposm\Ping\Ping; 102 | use Acamposm\Ping\PingCommandBuilder; 103 | 104 | // Change packet size to 128 105 | $command = (new PingCommandBuilder('192.168.1.1'))->packetSize(128); 106 | 107 | $ping = (new Ping($command))->run(); 108 | ``` 109 | 110 | ### Change Timeout 111 | 112 | Time to wait for a response, in seconds. The option affects only timeout in absence of any responses, otherwise ping waits for two RTTs. 113 | 114 | ```php 115 | use Acamposm\Ping\Ping; 116 | use Acamposm\Ping\PingCommandBuilder; 117 | 118 | // Change timeout to 10 seconds 119 | $command = (new PingCommandBuilder('192.168.1.1'))->timeout(10); 120 | 121 | $ping = (new Ping($command))->run(); 122 | ``` 123 | 124 | ### Change Time To Live 125 | 126 | ping only. Set the IP Time to Live. 127 | 128 | ```php 129 | use Acamposm\Ping\Ping; 130 | use Acamposm\Ping\PingCommandBuilder; 131 | 132 | // Change Time To Live to 128 133 | $command = (new PingCommandBuilder('192.168.1.1'))->ttl(128); 134 | 135 | $ping = (new Ping($command))->run(); 136 | ``` 137 | 138 | ## Sample outputs 139 | 140 | Here you can see three output samples of the ping command... 141 | - The first with domain. 142 | - The second with an IPv4 Address 143 | - The third with an IPv6 Address 144 | 145 | ### Sample output on Windows based server to https://google.com 146 | 147 | ```php 148 | use Acamposm\Ping\Ping; 149 | use Acamposm\Ping\PingCommandBuilder; 150 | 151 | $command = (new PingCommandBuilder('https://google.com'))->count(10)->packetSize(200)->ttl(128); 152 | 153 | // Sample output from Windows based server 154 | $ping = (new Ping($command))->run(); 155 | 156 | dd($ping); 157 | ``` 158 | ```json 159 | { 160 | "host_status": "Ok", 161 | "raw": [ 162 | "", 163 | "Haciendo ping a google.com [216.58.213.142] con 200 bytes de datos:", 164 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 165 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 166 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 167 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=37ms TTL=115", 168 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=37ms TTL=115", 169 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 170 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 171 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 172 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 173 | "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 174 | "", 175 | "Estadísticas de ping para 216.58.213.142:", 176 | " Paquetes: enviados = 10, recibidos = 10, perdidos = 0", 177 | " (0% perdidos),", 178 | "Tiempos aproximados de ida y vuelta en milisegundos:", 179 | " Mínimo = 36ms, Máximo = 37ms, Media = 36ms", 180 | ], 181 | "latency": 0.036, 182 | "rtt": { 183 | "avg": 0.036, 184 | "min": 0.036, 185 | "max": 0.037, 186 | }, 187 | "sequence": { 188 | "0": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 189 | "1": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 190 | "2": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 191 | "3": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=37ms TTL=115", 192 | "4": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=37ms TTL=115", 193 | "5": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 194 | "6": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 195 | "7": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 196 | "8": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 197 | "9": "Respuesta desde 216.58.213.142: bytes=68 (enviados 200) tiempo=36ms TTL=115", 198 | }, 199 | "statistics": { 200 | "packets_transmitted": 10, 201 | "packets_received": 10, 202 | "packets_lost": 0, 203 | "packets_lost": 0, 204 | }, 205 | "options": { 206 | "host": "google.com", 207 | "count": 10, 208 | "packet_size": 200, 209 | "ttl": 120, 210 | }, 211 | "time": { 212 | "start": { 213 | "as_float": 1596984650.5006, 214 | "human_readable": "09-08-2020 14:50:50.500600", 215 | }, 216 | "stop": { 217 | "as_float": 1596984659.5802, 218 | "human_readable": "09-08-2020 14:50:59.580200", 219 | }, 220 | "time": 9.08, 221 | }, 222 | } 223 | 224 | ``` 225 | 226 | ### Sample output from Windows based server to local gateway IPv4 227 | 228 | ```php 229 | use Acamposm\Ping\Ping; 230 | use Acamposm\Ping\PingCommandBuilder; 231 | 232 | $command = (new PingCommandBuilder('192.168.10.254'))->count(10)->packetSize(200)->ttl(120); 233 | 234 | $ping = (new Ping($command))->run(); 235 | 236 | dd($ping); 237 | ``` 238 | ```json 239 | { 240 | "host_status": "Ok", 241 | "raw": [ 242 | "", 243 | "Haciendo ping a 192.168.10.254 con 200 bytes de datos:", 244 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 245 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 246 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 247 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 248 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 249 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 250 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 251 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 252 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 253 | "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 254 | "", 255 | "Estadísticas de ping para 192.168.10.254:", 256 | " Paquetes: enviados = 10, recibidos = 10, perdidos = 0", 257 | " (0% perdidos),", 258 | "Tiempos aproximados de ida y vuelta en milisegundos:", 259 | " Mínimo = 0ms, Máximo = 0ms, Media = 0ms", 260 | ], 261 | "latency": 0.0, 262 | "rtt": { 263 | "avg": 0.0, 264 | "min": 0.0, 265 | "max": 0.0, 266 | }, 267 | "sequence": { 268 | "0": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 269 | "1": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 270 | "2": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 271 | "3": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 272 | "4": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 273 | "5": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 274 | "6": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 275 | "7": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 276 | "8": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 277 | "9": "Respuesta desde 192.168.10.254: bytes=200 tiempo<1m TTL=255", 278 | }, 279 | "statistics": { 280 | "packets_transmitted": 10, 281 | "packets_received": 10, 282 | "packets_lost": 0, 283 | "packets_lost": 0, 284 | }, 285 | "options": { 286 | "host": "192.168.10.254", 287 | "count": 10, 288 | "packet_size": 200, 289 | "ttl": 120, 290 | "version": 4, 291 | }, 292 | "time": { 293 | "start": { 294 | "as_float": 1596985359.7592, 295 | "human_readable": "09-08-2020 15:02:39.759200", 296 | }, 297 | "stop": { 298 | "as_float": 1596985368.7952, 299 | "human_readable": "09-08-2020 15:02:48.795200", 300 | }, 301 | "time": 9.036, 302 | }, 303 | } 304 | ``` 305 | 306 | #### Sample output from Windows based server to link local IPv6 address 307 | 308 | ```php 309 | use Acamposm\Ping\Ping; 310 | use Acamposm\Ping\PingCommandBuilder; 311 | 312 | $command = (new PingCommandBuilder('fe80::6c42:407d:af01:9567'))->count(10)->packetSize(200)->ttl(120); 313 | 314 | $ping = (new Ping($command))->run(); 315 | 316 | dd($ping); 317 | ``` 318 | ```json 319 | { 320 | "host_status": "Ok", 321 | "raw": [ 322 | "", 323 | "Haciendo ping a fe80::6c42:407d:af01:9567 con 200 bytes de datos:", 324 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 325 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 326 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 327 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 328 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 329 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 330 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 331 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 332 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 333 | "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 334 | "", 335 | "Estadísticas de ping para fe80::6c42:407d:af01:9567:", 336 | " Paquetes: enviados = 10, recibidos = 10, perdidos = 0", 337 | " (0% perdidos),", 338 | "Tiempos aproximados de ida y vuelta en milisegundos:", 339 | " Mínimo = 0ms, Máximo = 0ms, Media = 0ms", 340 | ], 341 | "latency": 0.0, 342 | "rtt": { 343 | "avg": 0.0, 344 | "min": 0.0, 345 | "max": 0.0, 346 | }, 347 | "sequence": { 348 | "0": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 349 | "1": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 350 | "2": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 351 | "3": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 352 | "4": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 353 | "5": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 354 | "6": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 355 | "7": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 356 | "8": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 357 | "9": "Respuesta desde fe80::6c42:407d:af01:9567: tiempo<1m", 358 | }, 359 | "statistics": { 360 | "packets_transmitted": 10, 361 | "packets_received": 10, 362 | "packets_lost": 0, 363 | "packets_lost": 0, 364 | }, 365 | "options": { 366 | "host": "fe80::6c42:407d:af01:9567", 367 | "count": 10, 368 | "packet_size": 200, 369 | "ttl": 120, 370 | "version": 6, 371 | }, 372 | "time": { 373 | "start": { 374 | "as_float": 1596985675.4344, 375 | "human_readable": "09-08-2020 15:07:55.434400", 376 | }, 377 | "stop": { 378 | "as_float": 1596985684.4659, 379 | "human_readable": "09-08-2020 15:08:04.465900", 380 | }, 381 | "time": 9.032, 382 | }, 383 | } 384 | ``` 385 | 386 | ### Testing 387 | 388 | For running the tests, in the console run: 389 | 390 | ```bash 391 | composer test 392 | ``` 393 | 394 | ## Changelog 395 | 396 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 397 | 398 | ## Contributing 399 | 400 | Thank you for considering contributing to the improvement of the package. Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details. 401 | 402 | ## Security & Vulnerabilities 403 | 404 | If you discover any security related issues, please send an e-mail to Angel Campos via angel.campos.m@outlook.com instead of using the issue tracker. All security vulnerabilities will be promptly addressed. 405 | 406 | ## Standards 407 | 408 | The php package IPv4 Address Converter, comply with the next standards: 409 | 410 | - [PSR-1 - Basic Coding Standard](http://www.php-fig.org/psr/psr-1/) 411 | - [PSR-4 - Autoloading Standard](http://www.php-fig.org/psr/psr-4/) 412 | - [PSR-12 - Extended Coding Style Guide](https://www.php-fig.org/psr/psr-12/) 413 | 414 | ## Credits 415 | 416 | - [Angel Campos](https://github.com/angelcamposm) 417 | 418 | ## License 419 | 420 | The package Ping is open-source package and is licensed under The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 421 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "acamposm/ping", 3 | "version": "2.1.3", 4 | "description": "Ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit an ICMP ECHO_RESPONSE from a host or gateway.", 5 | "keywords": [ 6 | "icmp", 7 | "laravel", 8 | "php", 9 | "ping" 10 | ], 11 | "homepage": "https://github.com/angelcamposm/ping", 12 | "readme": "https://github.com/angelcamposm/ping/blob/master/README.md", 13 | "license": "MIT", 14 | "type": "library", 15 | "authors": [ 16 | { 17 | "name": "Angel Campos", 18 | "email": "angel.campos.m@outlook.com", 19 | "role": "Developer" 20 | } 21 | ], 22 | "require": { 23 | "php": "^8.0" 24 | }, 25 | "require-dev": { 26 | "orchestra/testbench": "^6.23", 27 | "phpunit/phpunit": "^9" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Acamposm\\Ping\\": "src" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Acamposm\\Ping\\Tests\\": "tests" 37 | } 38 | }, 39 | "scripts": { 40 | "test": "phpunit --colors=always --testdox", 41 | "test-coverage": "phpunit --coverage-html coverage" 42 | }, 43 | "config": { 44 | "sort-packages": true 45 | }, 46 | "extra": { 47 | "laravel": { 48 | "providers": [ 49 | "Acamposm\\Ping\\ServiceProviders\\PingServiceProvider" 50 | ], 51 | "aliases": { 52 | "Ping": "Acamposm\\Ping\\Facades\\PingFacade" 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | 11 | | @requires PHP 8.0 12 | | @version 2.1.2 13 | */ 14 | 15 | return [ 16 | 17 | /* 18 | |------------------------------------------------------------------------- 19 | | Number of Ping Packets 20 | |------------------------------------------------------------------------- 21 | | 22 | | Stop after sending count ECHO_REQUEST packets. 23 | | 24 | */ 25 | 'count' => 5, 26 | 27 | /* 28 | |------------------------------------------------------------------------- 29 | | Interval between packets 30 | |------------------------------------------------------------------------- 31 | | 32 | | Wait interval seconds between sending each packet. (min: 0.2) 33 | | The default is to wait for one second between each packet normally. 34 | | Only super-user may set interval to values less than 0.2 seconds. 35 | | 36 | */ 37 | 'interval' => 1, 38 | 39 | /* 40 | |------------------------------------------------------------------------- 41 | | Packet size 42 | |------------------------------------------------------------------------- 43 | | 44 | | Specifies the number of data bytes to be sent. 45 | | The default is 56, which translates into 64 ICMP data bytes when The 46 | | default is 56, which translates into 64 ICMP data bytes when combined 47 | | with the 8 bytes of ICMP header data. 48 | | 49 | */ 50 | 'packet_size' => 64, 51 | 52 | /* 53 | |------------------------------------------------------------------------- 54 | | Timeout 55 | |------------------------------------------------------------------------- 56 | | 57 | | Time to wait for a response, in seconds. 58 | | The option affects only timeout in absence of any responses, otherwise 59 | | ping waits for two RTTs. 60 | | 61 | */ 62 | 'timeout' => 8, 63 | 64 | /* 65 | |------------------------------------------------------------------------- 66 | | Time to Live 67 | |------------------------------------------------------------------------- 68 | | 69 | | Set the IP Time to Live. 70 | | The TTL value of an IP packet represents the maximum number of IP 71 | | routers that the packet can go through before being thrown away. 72 | | In current practice you can expect each router in the Internet to 73 | | decrement the TTL field by exactly one. 74 | | 75 | */ 76 | 'ttl' => 60, 77 | 78 | ]; 79 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | src/ 17 | 18 | 19 | src/Console 20 | src/Exceptions 21 | src/Facades 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | tests/Unit 35 | 36 | 37 | tests/Feature 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.organization=angelcamposm 2 | sonar.projectBaseDir=/github/workspace 3 | sonar.projectKey=ping 4 | sonar.projectName=Ping for Laravel 5 | sonar.projectDescription=Ping tool for Laravel 6 | sonar.projectVersion=2.1.3 7 | sonar.links.homepage=https://github.com/angelcamposm/ping 8 | sonar.sources=/github/workspace/src 9 | sonar.sourceEncoding=UTF-8 10 | sonar.php.coverage.reportPaths=build/clover.xml 11 | sonar.php.exclusions=vendor/** 12 | sonar.php.tests.reportPath=build/junit.xml 13 | -------------------------------------------------------------------------------- /src/Console/InstallPingPackageCommand.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Console; 17 | 18 | use Acamposm\Ping\ServiceProviders\PingServiceProvider; 19 | use Illuminate\Console\Command; 20 | 21 | class InstallPingPackageCommand extends Command 22 | { 23 | protected $signature = 'ping:install'; 24 | 25 | protected $description = 'Install the Ping package'; 26 | 27 | public function handle() 28 | { 29 | $this->info('Installing PingPackage...'); 30 | 31 | $this->info('Publishing configuration...'); 32 | 33 | $this->call('vendor:publish', [ 34 | '--provider' => PingServiceProvider::class, 35 | '--tag' => 'config', 36 | ]); 37 | 38 | $this->info('Installed PingPackage'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidIPAddressException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class InvalidIPAddressException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('Invalid IP Address'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/MaxValueException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class MaxValueException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('TTL must not exceed 255 value.'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/NegativeValueException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class NegativeValueException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('The value of the count parameter must be positive value.'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/PingFailedException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class PingFailedException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('Ping execution failed.'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/TimerNotStartedException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class TimerNotStartedException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('Timer not started.'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Exceptions/UnknownOSException.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Exceptions; 17 | 18 | use Exception; 19 | 20 | class UnknownOSException extends Exception 21 | { 22 | public function __construct() 23 | { 24 | parent::__construct('Unknown OS'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Facades/PingFacade.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Facades; 17 | 18 | use Illuminate\Support\Facades\Facade; 19 | 20 | class PingFacade extends Facade 21 | { 22 | /** 23 | * Get the registered name of the component. 24 | * 25 | * @return string 26 | */ 27 | protected static function getFacadeAccessor(): string 28 | { 29 | return 'ping'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/IPAddress.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping; 17 | 18 | class IPAddress 19 | { 20 | public const IPV4_SEPARATOR = '.'; 21 | public const IPV6_SEPARATOR = ':'; 22 | 23 | /** 24 | * Performs the validation of the IP Address. 25 | * 26 | * @param string $ip_address 27 | * 28 | * @return bool 29 | */ 30 | public static function Validate(string $ip_address): bool 31 | { 32 | if (str_contains($ip_address, IPAddress::IPV4_SEPARATOR)) { 33 | return self::validateIPv4Address($ip_address); 34 | } 35 | 36 | if (str_contains($ip_address, IPAddress::IPV6_SEPARATOR)) { 37 | return self::validateIPv6Address($ip_address); 38 | } 39 | 40 | return false; 41 | } 42 | 43 | /** 44 | * Performs a IPv4 validation. 45 | * 46 | * Validate an IPv4 address 47 | * 48 | * @param string $ip_address 49 | * 50 | * @return bool 51 | */ 52 | private static function validateIPv4Address(string $ip_address): bool 53 | { 54 | if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { 55 | return false; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | /** 62 | * Performs a IPv6 validation. 63 | * 64 | * @param string $ip_address 65 | * 66 | * @return bool 67 | */ 68 | private static function validateIPv6Address(string $ip_address): bool 69 | { 70 | if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { 71 | return false; 72 | } 73 | 74 | return true; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Interfaces/PingCommand.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Interfaces; 17 | 18 | use Acamposm\Ping\Exceptions\MaxValueException; 19 | use Acamposm\Ping\Exceptions\NegativeValueException; 20 | use Acamposm\Ping\Exceptions\UnknownOSException; 21 | use Acamposm\Ping\PingCommandBuilder; 22 | 23 | interface PingCommand 24 | { 25 | /** 26 | * Stop after sending count ECHO_REQUEST packets. With deadline option, ping 27 | * waits for count ECHO_REPLY packets, until the timeout expires. 28 | * 29 | * @param int $count 30 | * 31 | * @throws NegativeValueException 32 | * 33 | * @return PingCommandBuilder 34 | */ 35 | public function count(int $count): PingCommandBuilder; 36 | 37 | /** 38 | * Wait interval seconds between sending each packet. The default is to wait 39 | * for one second between each packet normally, or not to wait in flood mode. 40 | * Only super-user may set interval to values less than 0.2 seconds. 41 | * 42 | * @param float $interval 43 | * 44 | * @return PingCommandBuilder 45 | */ 46 | public function interval(float $interval): PingCommandBuilder; 47 | 48 | /** 49 | * Specifies the number of data bytes to be sent. The default is 56, which 50 | * translates into 64 ICMP data bytes when combined with the 8 bytes of ICMP 51 | * header data. 52 | * 53 | * @param int $packet_size 54 | * 55 | * @return PingCommandBuilder 56 | */ 57 | public function packetSize(int $packet_size): PingCommandBuilder; 58 | 59 | /** 60 | * ping only. Set the IP Time to Live. 61 | * 62 | * @param int $ttl 63 | * 64 | * @throws MaxValueException 65 | * 66 | * @return PingCommandBuilder 67 | */ 68 | public function ttl(int $ttl): PingCommandBuilder; 69 | 70 | /** 71 | * Time to wait for a response. The option affects only timeout in absence 72 | * of any responses, otherwise ping waits for two RTTs. 73 | * (Seconds for Linux OS, Milliseconds for Windows). 74 | * 75 | * @param int $timeout 76 | * 77 | * @return PingCommandBuilder 78 | */ 79 | public function timeout(int $timeout): PingCommandBuilder; 80 | 81 | /** 82 | * Return the Ping Command. 83 | * 84 | * @throws UnknownOSException 85 | * 86 | * @return string 87 | */ 88 | public function get(): string; 89 | } 90 | -------------------------------------------------------------------------------- /src/Interfaces/PingParserInterface.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Interfaces; 17 | 18 | interface PingParserInterface 19 | { 20 | /** 21 | * Returns the Ping execution result parsed as object. 22 | * 23 | * @return object 24 | */ 25 | public function parse(): object; 26 | } 27 | -------------------------------------------------------------------------------- /src/Parsers/PingParser.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @requires PHP 8.0 13 | * 14 | * @version 2.1.2 15 | */ 16 | 17 | namespace Acamposm\Ping\Parsers; 18 | 19 | use Acamposm\Ping\Interfaces\PingParserInterface; 20 | 21 | class PingParser implements PingParserInterface 22 | { 23 | protected string $host_status = ''; 24 | protected array $sequence = []; 25 | protected array $statistics = []; 26 | protected array $round_trip_time = []; 27 | 28 | /** 29 | * PingParser constructor. 30 | * 31 | * @param array $results 32 | */ 33 | public function __construct(protected array $results = []) 34 | { 35 | } 36 | 37 | /** 38 | * Returns the Ping execution result parsed as object. 39 | * 40 | * @return object 41 | */ 42 | public function parse(): object 43 | { 44 | return (object) $this->results; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Parsers/PingParserForNix.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @requires PHP 8.0 13 | * 14 | * @version 2.1.2 15 | */ 16 | 17 | namespace Acamposm\Ping\Parsers; 18 | 19 | use Acamposm\Ping\System; 20 | 21 | final class PingParserForNix extends PingParser 22 | { 23 | protected bool $is_unreachable; 24 | 25 | /** 26 | * PingParserForNix constructor. 27 | * 28 | * @param array $ping 29 | */ 30 | public function __construct(array $ping) 31 | { 32 | parent::__construct($ping); 33 | 34 | $this->is_unreachable = $this->isUnreachable($ping); 35 | 36 | $this->host_status = 'Unreachable'; 37 | 38 | if (preg_match('/^--- (.+) ---$/i', $ping[count($ping) - 2])) { 39 | $this->setStatistics($ping[count($ping) - 1]); 40 | 41 | if ($this->is_unreachable === false) { 42 | // $this->setRoundTripTime($ping[count($ping)-1]); 43 | $this->setSequence(); 44 | $this->setHostStatus(); 45 | } 46 | } else { 47 | $this->setStatistics($ping[count($ping) - 2]); 48 | 49 | if ($this->is_unreachable === false) { 50 | $this->setRoundTripTime($ping[count($ping) - 1]); 51 | $this->setSequence(); 52 | $this->setHostStatus(); 53 | } 54 | } 55 | } 56 | 57 | private function cleanStatisticsRecord(string $row): array 58 | { 59 | $search = explode( 60 | '|', 61 | 'packets|transmitted|received|+|errors|%|packet|loss|time|ms' 62 | ); 63 | 64 | $row = trim(str_replace($search, '', $row)); 65 | 66 | return array_map('trim', explode(', ', $row)); 67 | } 68 | 69 | private function isUnreachable($ping): bool 70 | { 71 | $unreachable = false; 72 | 73 | foreach ($ping as $row) { 74 | if ($unreachable === strpos($row, '100% packet loss')) { 75 | break; 76 | } 77 | } 78 | 79 | return $unreachable !== false; 80 | } 81 | 82 | /** 83 | * Get the host status. 84 | * 85 | * @return string 86 | */ 87 | private function getHostStatus(): string 88 | { 89 | return ($this->statistics['packets_received'] == $this->statistics['packets_transmitted']) ? 'Ok' 90 | : ($this->statistics['packets_lost'] == $this->statistics['packets_transmitted'] 91 | ? 'Unreachable' : 'High Latency'); 92 | } 93 | 94 | /** 95 | * Return an array with the Ping sequence and latency. 96 | * 97 | * @return array 98 | */ 99 | private function getSequence(): array 100 | { 101 | $ping = $this->results; 102 | 103 | // Remove unnecessary index 104 | unset($ping[0]); 105 | unset($ping[count($ping) - 4]); 106 | unset($ping[count($ping) - 3]); 107 | unset($ping[count($ping) - 2]); 108 | unset($ping[count($ping) - 1]); 109 | 110 | $sequence = []; 111 | 112 | foreach ($ping as $row) { 113 | if (str_contains($row, 'Unreachable') && !empty($row)) { 114 | $data = explode(': ', str_replace(' ms', '', $row)); 115 | $items = explode(' ', $data[1]); 116 | 117 | $key = (int) explode('=', $items[0])[1]; 118 | $value = (float) explode('=', $items[2])[1]; 119 | 120 | $sequence[$key] = $value; 121 | } 122 | } 123 | 124 | return $sequence; 125 | } 126 | 127 | /** 128 | * Return an object with Ping Round Trip Time. 129 | * 130 | * @param string $row 131 | * 132 | * @return array 133 | */ 134 | private function parseRoundTripTime(string $row): array 135 | { 136 | if (!str_contains($row, 'rtt')) { 137 | return []; 138 | } 139 | 140 | $row = trim(str_replace(['ms', System::isLinux() ? 'rtt' : 'round-trip'], '', $row)); 141 | 142 | $rtt = explode(' = ', $row); 143 | 144 | $keys = explode('/', $rtt[0]); 145 | 146 | $values = array_map('floatval', explode('/', $rtt[1])); 147 | 148 | return array_combine($keys, $values); 149 | } 150 | 151 | /** 152 | * Return an object with Ping Statistics. 153 | * 154 | * @param string $row 155 | * 156 | * @return array 157 | */ 158 | private function parseStatistics(string $row): array 159 | { 160 | $statistics = $this->cleanStatisticsRecord($row); 161 | 162 | $results = [ 163 | 'packets_transmitted' => (int) $statistics[0], 164 | 'packets_received' => (int) $statistics[1], 165 | 'packets_lost' => (int) ($statistics[0] - $statistics[1]), 166 | ]; 167 | 168 | if (count($statistics) === 5 && $this->is_unreachable) { 169 | $results['errors'] = (int) $statistics[2]; 170 | } 171 | 172 | if (count($statistics) === 5) { 173 | $results['packets_lost'] = (float) $statistics[3]; 174 | $results['time'] = (int) $statistics[4]; 175 | } 176 | 177 | if (count($statistics) === 4) { 178 | $results['packets_lost'] = (float) $statistics[2]; 179 | $results['time'] = (int) $statistics[3]; 180 | } 181 | 182 | return $results; 183 | } 184 | 185 | /** 186 | * Set the host status. 187 | */ 188 | private function setHostStatus(): void 189 | { 190 | $this->host_status = $this->getHostStatus(); 191 | } 192 | 193 | /** 194 | * Set the Round Trip Time statistics. 195 | * 196 | * @param string $rtt 197 | */ 198 | private function setRoundTripTime(string $rtt): void 199 | { 200 | $this->round_trip_time = $this->parseRoundTripTime($rtt); 201 | } 202 | 203 | /** 204 | * Set the Ping sequence. 205 | */ 206 | private function setSequence(): void 207 | { 208 | $this->sequence = $this->getSequence(); 209 | } 210 | 211 | /** 212 | * Set the Statistics. 213 | * 214 | * @param string $statistics 215 | */ 216 | private function setStatistics(string $statistics): void 217 | { 218 | $this->statistics = $this->parseStatistics($statistics); 219 | } 220 | 221 | /** 222 | * Return the Ping execution result parsed as object. 223 | * 224 | * @return object 225 | */ 226 | public function parse(): object 227 | { 228 | $parsed = [ 229 | 'host_status' => $this->host_status, 230 | 'raw' => (object) $this->results, 231 | ]; 232 | 233 | if (count($this->round_trip_time) > 0) { 234 | $parsed['latency'] = $this->round_trip_time['avg']; 235 | $parsed['rtt'] = (object) $this->round_trip_time; 236 | } 237 | 238 | if (count($this->sequence) > 0) { 239 | $parsed['sequence'] = (object) $this->sequence; 240 | } 241 | 242 | if (count($this->statistics) > 0) { 243 | $parsed['statistics'] = (object) $this->statistics; 244 | } 245 | 246 | return (object) $parsed; 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/Parsers/PingParserForWindows.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\Parsers; 17 | 18 | final class PingParserForWindows extends PingParser 19 | { 20 | private bool $unreachable; 21 | 22 | /** 23 | * PingParserForWindows constructor. 24 | * 25 | * @param array $ping 26 | */ 27 | public function __construct(array $ping) 28 | { 29 | parent::__construct($ping); 30 | 31 | $this->unreachable = $this->isUnreachable($ping); 32 | 33 | if ($this->unreachable) { 34 | $this->setStatistics($ping[count($ping) - 2]); 35 | } else { 36 | $this->setRoundTripTime($ping[count($ping) - 1]); 37 | $this->setSequence(); 38 | $this->setStatistics($ping[count($ping) - 4]); 39 | } 40 | 41 | $this->setHostStatus(); 42 | } 43 | 44 | /** 45 | * Get the host status. 46 | * 47 | * @return string 48 | */ 49 | private function getHostStatus(): string 50 | { 51 | return ($this->statistics['packets_received'] == $this->statistics['packets_transmitted']) ? 'Ok' 52 | : ($this->statistics['packets_lost'] == $this->statistics['packets_transmitted'] 53 | ? 'Unreachable' : 'High Latency'); 54 | } 55 | 56 | /** 57 | * Return an array with the Ping sequence and latency. 58 | * 59 | * @return array 60 | */ 61 | private function getSequence(): array 62 | { 63 | $ping = $this->results; 64 | 65 | $items_count = count($ping); 66 | 67 | // First remove items from final of the array 68 | for ($i = 6; $i > 0; $i--) { 69 | unset($ping[$items_count - $i]); 70 | } 71 | 72 | // Then remove first items 73 | unset($ping[1]); 74 | unset($ping[0]); 75 | 76 | $key = 0; 77 | 78 | $sequence = []; 79 | 80 | foreach ($ping as $row) { 81 | $sequence[$key] = $row; 82 | 83 | $key++; 84 | } 85 | 86 | return $sequence; 87 | } 88 | 89 | /** 90 | * Check if the last element of the array has 100% value string. 91 | * 92 | * @param array $ping 93 | * 94 | * @return bool 95 | */ 96 | private function isUnreachable(array $ping): bool 97 | { 98 | $needles = 'perdidos|lost'; 99 | 100 | $result = $ping[count($ping) - 1]; 101 | 102 | $unreachable = false; 103 | 104 | foreach (explode('|', $needles) as $needle) { 105 | $search = strpos($result, '100% '.$needle); 106 | 107 | if ($search !== false) { 108 | $unreachable = true; 109 | break; 110 | } 111 | } 112 | 113 | return $unreachable; 114 | } 115 | 116 | /** 117 | * Return an object with Ping Round Trip Time. 118 | * 119 | * @param string $row 120 | * 121 | * @return array 122 | */ 123 | private function parseRoundTripTime(string $row): array 124 | { 125 | $rtt = explode(',', str_replace('ms', '', $row)); 126 | 127 | $min = (float) explode(' = ', $rtt[0])[1] / 1000; 128 | $max = (float) explode(' = ', $rtt[1])[1] / 1000; 129 | $avg = (float) explode(' = ', $rtt[2])[1] / 1000; 130 | 131 | return [ 132 | 'avg' => $avg, 133 | 'min' => $min, 134 | 'max' => $max, 135 | ]; 136 | } 137 | 138 | /** 139 | * Return an object with Ping Statistics. 140 | * 141 | * @param string $row 142 | * 143 | * @return array 144 | */ 145 | private function parseStatistics(string $row): array 146 | { 147 | $ping_statistics = explode(', ', explode(':', $row)[1]); 148 | 149 | $transmitted = (int) explode(' = ', $ping_statistics[0])[1]; 150 | 151 | $received = (int) explode(' = ', $ping_statistics[1])[1]; 152 | 153 | $lost = (int) explode(' = ', $ping_statistics[2])[1]; 154 | 155 | return [ 156 | 'packets_transmitted' => $transmitted, 157 | 'packets_received' => $received, 158 | 'packets_lost' => $lost, 159 | 'packets_lost' => (int) (100 - (($received * 100) / $transmitted)), 160 | ]; 161 | } 162 | 163 | /** 164 | * Set the host status. 165 | */ 166 | private function setHostStatus(): void 167 | { 168 | $this->host_status = $this->getHostStatus(); 169 | } 170 | 171 | /** 172 | * Set the Round Trip Time statistics. 173 | * 174 | * @param string $rtt 175 | */ 176 | private function setRoundTripTime(string $rtt): void 177 | { 178 | $this->round_trip_time = $this->parseRoundTripTime($rtt); 179 | } 180 | 181 | /** 182 | * Set the Ping sequence. 183 | */ 184 | private function setSequence(): void 185 | { 186 | $this->sequence = $this->getSequence(); 187 | } 188 | 189 | /** 190 | * Set the Statistics. 191 | * 192 | * @param string $statistics 193 | */ 194 | private function setStatistics(string $statistics): void 195 | { 196 | $this->statistics = $this->parseStatistics($statistics); 197 | } 198 | 199 | /** 200 | * Return the Ping execution result parsed as object. 201 | * 202 | * @return object 203 | */ 204 | public function parse(): object 205 | { 206 | $parsed = [ 207 | 'host_status' => $this->host_status, 208 | 'raw' => $this->results, 209 | ]; 210 | 211 | if (count($this->round_trip_time) > 0) { 212 | $parsed['latency'] = $this->round_trip_time['avg']; 213 | $parsed['rtt'] = (object) $this->round_trip_time; 214 | } 215 | 216 | if (count($this->sequence) > 0) { 217 | $parsed['sequence'] = (object) $this->sequence; 218 | } 219 | 220 | if (count($this->statistics) > 0) { 221 | $parsed['statistics'] = (object) $this->statistics; 222 | } 223 | 224 | return (object) $parsed; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/Ping.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @requires PHP 8.0 13 | * 14 | * @version 2.1.2 15 | */ 16 | 17 | namespace Acamposm\Ping; 18 | 19 | use Acamposm\Ping\Exceptions\PingFailedException; 20 | use Acamposm\Ping\Exceptions\UnknownOSException; 21 | use Acamposm\Ping\Parsers\PingParserForNix; 22 | use Acamposm\Ping\Parsers\PingParserForWindows; 23 | use Exception; 24 | 25 | class Ping 26 | { 27 | /** 28 | * @var PingCommandBuilder 29 | */ 30 | protected PingCommandBuilder $command; 31 | 32 | /** 33 | * @var Timer 34 | */ 35 | protected Timer $timer; 36 | 37 | /** 38 | * Ping constructor. 39 | * 40 | * @param PingCommandBuilder $command 41 | */ 42 | public function __construct(PingCommandBuilder $command) 43 | { 44 | $this->command = $command; 45 | 46 | $this->timer = new Timer(); 47 | $this->timer->Start(); 48 | } 49 | 50 | /** 51 | * Parse the ping result and return an object. 52 | * 53 | * @param array $ping 54 | * 55 | * @throws UnknownOSException 56 | * 57 | * @return object 58 | */ 59 | protected function parse(array $ping): object 60 | { 61 | if (System::isLinux() || System::isOSX()) { 62 | return (new PingParserForNix($ping))->parse(); 63 | } 64 | 65 | if (System::isWindows()) { 66 | return (new PingParserForWindows($ping))->parse(); 67 | } 68 | 69 | throw new UnknownOSException(); 70 | } 71 | 72 | /** 73 | * Remove binary casting from the beginning of the strings. 74 | * 75 | * @param array $ping 76 | * 77 | * @return array 78 | */ 79 | private function cleanBinaryString(array $ping): array 80 | { 81 | $cleaned = []; 82 | 83 | foreach ($ping as $row) { 84 | $cleaned[] = preg_replace('/[[:^print:]]/', '', $row); 85 | } 86 | 87 | return $cleaned; 88 | } 89 | 90 | /** 91 | * @throws Exception 92 | * 93 | * @return object 94 | */ 95 | public function run(): object 96 | { 97 | $ping = $this->executePing(); 98 | 99 | // Return the result if lines count are less than three. 100 | if (count($ping) < 3) { 101 | return (object) $ping; 102 | } 103 | 104 | $ping_object = $this->parse($ping); 105 | 106 | $ping_object->options = $this->command->getOptions(); 107 | $ping_object->time = $this->timer->getResults(); 108 | 109 | return $ping_object; 110 | } 111 | 112 | /** 113 | * Return the result of the execution of ping command. 114 | * 115 | * @throws Exception 116 | * 117 | * @return array 118 | */ 119 | private function executePing(): array 120 | { 121 | exec($this->command->get(), $exec_result); 122 | 123 | if (!is_array($exec_result)) { 124 | throw new PingFailedException(); 125 | } 126 | 127 | return $this->cleanBinaryString($exec_result); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/PingCommandBuilder.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @requires PHP 8.0 13 | * 14 | * @version 2.1.2 15 | */ 16 | 17 | namespace Acamposm\Ping; 18 | 19 | use Acamposm\Ping\Exceptions\InvalidIPAddressException; 20 | use Acamposm\Ping\Exceptions\MaxValueException; 21 | use Acamposm\Ping\Exceptions\NegativeValueException; 22 | use Acamposm\Ping\Exceptions\UnknownOSException; 23 | use Acamposm\Ping\Interfaces\PingCommand; 24 | 25 | class PingCommandBuilder implements PingCommand 26 | { 27 | protected int $count; 28 | protected ?string $host; 29 | protected float $interval; 30 | protected int $packet_size; 31 | protected int $ttl; 32 | protected int $timeout; 33 | protected int $version; 34 | 35 | /**- 36 | * PingCommand constructor. 37 | * 38 | * @param string|null $host 39 | * @throws InvalidIPAddressException 40 | */ 41 | public function __construct(?string $host = null) 42 | { 43 | if ($this->isValidIPAddress($host)) { 44 | $this->host = $host; 45 | $this->setIPAddressVersion(); 46 | } else { 47 | if (str_ends_with($host, '/')) { 48 | $host = substr($host, 0, -1); 49 | } 50 | 51 | // We assume that is an URL... 52 | //TODO: Needs URL validation 53 | $pattern = '/^http:\/\/|^https:\/\//'; 54 | 55 | if (preg_match($pattern, $host)) { 56 | $this->host = preg_replace($pattern, '', $host); 57 | } else { 58 | $this->host = $host; 59 | } 60 | } 61 | 62 | $this->count = config('ping.count'); 63 | $this->interval = config('ping.interval'); 64 | $this->packet_size = config('ping.packet_size'); 65 | $this->timeout = config('ping.timeout'); 66 | $this->ttl = config('ping.ttl'); 67 | } 68 | 69 | /** 70 | * Validates an IP Address. 71 | * 72 | * @param string|null $ip_address 73 | * 74 | * @throws InvalidIPAddressException 75 | * 76 | * @return bool 77 | */ 78 | private function isValidIPAddress(?string $ip_address = null): bool 79 | { 80 | return IPAddress::Validate($ip_address); 81 | } 82 | 83 | /** 84 | * Select the appropriate IP version. 85 | */ 86 | private function setIPAddressVersion(): void 87 | { 88 | if (strpos($this->host, IPAddress::IPV4_SEPARATOR) > 0) { 89 | $this->useIPv4(); 90 | } elseif (strpos($this->host, IPAddress::IPV6_SEPARATOR) > 0) { 91 | $this->useIPv6(); 92 | } 93 | } 94 | 95 | /** 96 | * Set IP protocol version 4. 97 | */ 98 | private function useIPv4(): void 99 | { 100 | $this->version = 4; 101 | } 102 | 103 | /** 104 | * Set IP protocol version 6. 105 | */ 106 | private function useIPv6(): void 107 | { 108 | $this->version = 6; 109 | } 110 | 111 | /** 112 | * Stop after sending count ECHO_REQUEST packets. With deadline option, ping 113 | * waits for count ECHO_REPLY packets, until the timeout expires. 114 | * 115 | * @param int $count 116 | * 117 | * @throws NegativeValueException 118 | * 119 | * @return PingCommandBuilder 120 | */ 121 | public function count(int $count): PingCommandBuilder 122 | { 123 | if ($count < 0) { 124 | throw new NegativeValueException(); 125 | } 126 | 127 | $this->count = $count; 128 | 129 | return $this; 130 | } 131 | 132 | /** 133 | * Wait interval seconds between sending each packet. The default is to wait 134 | * for one second between each packet normally, or not to wait in flood mode. 135 | * Only super-user may set interval to values less than 0.2 seconds. 136 | * 137 | * @param float $interval 138 | * 139 | * @return PingCommandBuilder 140 | */ 141 | public function interval(float $interval): PingCommandBuilder 142 | { 143 | $this->interval = $interval; 144 | 145 | return $this; 146 | } 147 | 148 | /** 149 | * Specifies the number of data bytes to be sent. The default is 56, which 150 | * translates into 64 ICMP data bytes when combined with the 8 bytes of ICMP 151 | * header data. 152 | * 153 | * @param int $packet_size 154 | * 155 | * @return PingCommandBuilder 156 | */ 157 | public function packetSize(int $packet_size): PingCommandBuilder 158 | { 159 | $this->packet_size = $packet_size; 160 | 161 | return $this; 162 | } 163 | 164 | /** 165 | * ping only. Set the IP Time to Live. 166 | * 167 | * @param int $ttl 168 | * 169 | * @throws MaxValueException 170 | * 171 | * @return PingCommandBuilder 172 | */ 173 | public function ttl(int $ttl): PingCommandBuilder 174 | { 175 | if ($ttl > 255) { 176 | throw new MaxValueException(); 177 | } 178 | 179 | $this->ttl = $ttl; 180 | 181 | return $this; 182 | } 183 | 184 | /** 185 | * Time to wait for a response. The option affects only timeout in absence 186 | * of any responses, otherwise ping waits for two RTTs. 187 | * (Seconds for Linux OS, Milliseconds for Windows). 188 | * 189 | * @param int $timeout 190 | * 191 | * @return PingCommandBuilder 192 | */ 193 | public function timeout(int $timeout): PingCommandBuilder 194 | { 195 | $this->timeout = $timeout; 196 | 197 | return $this; 198 | } 199 | 200 | /** 201 | * Returns the Linux Ping Command. 202 | * 203 | * @return string 204 | */ 205 | private function getLinuxCommand(): string 206 | { 207 | $command = ['ping -n']; 208 | 209 | (!isset($this->version)) ?: array_push($command, '-'.$this->version); 210 | (!isset($this->count)) ?: array_push($command, '-c '.$this->count); 211 | (!isset($this->interval)) ?: array_push($command, '-i '.$this->interval); 212 | (!isset($this->packet_size)) ?: array_push($command, '-s '.$this->packet_size); 213 | (!isset($this->timeout)) ?: array_push($command, '-W '.$this->timeout); 214 | (!isset($this->ttl)) ?: array_push($command, '-t '.$this->ttl); 215 | 216 | $command[] = $this->host; 217 | 218 | return implode(' ', $command); 219 | } 220 | 221 | /** 222 | * Returns the OSX Ping Command. 223 | * 224 | * @return string 225 | */ 226 | private function getOSXCommand(): string 227 | { 228 | $command = ['ping']; 229 | 230 | // (!isset($this->version)) ?: array_push($command, '-'.$this->version); 231 | (!isset($this->count)) ?: array_push($command, '-c '.$this->count); 232 | (!isset($this->interval)) ?: array_push($command, '-i '.$this->interval); 233 | (!isset($this->packet_size)) ?: array_push($command, '-s '.$this->packet_size); 234 | (!isset($this->timeout)) ?: array_push($command, '-t '.$this->timeout); 235 | (!isset($this->ttl)) ?: array_push($command, '-m '.$this->ttl); 236 | 237 | $command[] = $this->host; 238 | 239 | return implode(' ', $command); 240 | } 241 | 242 | /** 243 | * Returns the Windows Ping Command. 244 | * 245 | * @return string 246 | */ 247 | private function getWindowsCommand(): string 248 | { 249 | $command = ['ping']; 250 | 251 | (!isset($this->version)) ?: array_push($command, '-'.$this->version); 252 | (!isset($this->count)) ?: array_push($command, '-n '.$this->count); 253 | (!isset($this->packet_size)) ?: array_push($command, '-l '.$this->packet_size); 254 | (!isset($this->timeout)) ?: array_push($command, '-w '.($this->timeout * 1000)); 255 | (!isset($this->ttl)) ?: array_push($command, '-i '.$this->ttl); 256 | 257 | $command[] = $this->host; 258 | 259 | return implode(' ', $command); 260 | } 261 | 262 | /** 263 | * Return an object with the options. 264 | * 265 | * @return object 266 | */ 267 | public function getOptions(): object 268 | { 269 | $options = [ 270 | 'host' => $this->host, 271 | ]; 272 | 273 | (!isset($this->count)) ?: $options['count'] = $this->count; 274 | (!isset($this->interval)) ?: $options['interval'] = $this->interval; 275 | (!isset($this->packet_size)) ?: $options['packet_size'] = $this->packet_size; 276 | (!isset($this->timeout)) ?: $options['timeout'] = $this->timeout; 277 | (!isset($this->ttl)) ?: $options['ttl'] = $this->ttl; 278 | (!isset($this->version)) ?: $options['version'] = $this->version; 279 | 280 | return (object) $options; 281 | } 282 | 283 | /** 284 | * Return the Ping Command. 285 | * 286 | * @throws UnknownOSException 287 | * 288 | * @return string 289 | */ 290 | public function get(): string 291 | { 292 | if (System::isLinux()) { 293 | return $this->getLinuxCommand(); 294 | } 295 | 296 | if (System::isOSX()) { 297 | return $this->getOSXCommand(); 298 | } 299 | 300 | if (System::isWindows()) { 301 | return $this->getWindowsCommand(); 302 | } 303 | 304 | throw new UnknownOSException(); 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/ServiceProviders/PingServiceProvider.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping\ServiceProviders; 17 | 18 | use Acamposm\Ping\Console\InstallPingPackageCommand; 19 | use Acamposm\Ping\Ping; 20 | use Illuminate\Support\ServiceProvider; 21 | 22 | class PingServiceProvider extends ServiceProvider 23 | { 24 | /** 25 | * Bootstrap the application services. 26 | */ 27 | public function boot() 28 | { 29 | if ($this->app->runningInConsole()) { 30 | $this->publishCommands(); 31 | $this->publishConfiguration(); 32 | } 33 | } 34 | 35 | /** 36 | * Register the application services. 37 | */ 38 | public function register() 39 | { 40 | // Automatically apply the package configuration 41 | $this->mergeConfigFrom(__DIR__.'/../../config/config.php', 'ping'); 42 | 43 | // Register the main class to use with the facade 44 | $this->app->singleton('ping', function () { 45 | return new Ping(); 46 | }); 47 | } 48 | 49 | /** 50 | * Publishes console commands. 51 | * 52 | * @return void 53 | */ 54 | private function publishCommands(): void 55 | { 56 | if (!file_exists(config_path('ping.php'))) { 57 | $this->commands([ 58 | InstallPingPackageCommand::class, 59 | ]); 60 | } 61 | } 62 | 63 | /** 64 | * Publishes package configuration files. 65 | * 66 | * @return void 67 | */ 68 | private function publishConfiguration(): void 69 | { 70 | $this->publishes([ 71 | __DIR__.'/../../config/config.php' => config_path('ping.php'), 72 | ], 'config'); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/System.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @requires PHP 8.0 13 | * 14 | * @version 2.1.2 15 | */ 16 | 17 | namespace Acamposm\Ping; 18 | 19 | class System 20 | { 21 | /** 22 | * Return TRUE if OS is Linux. 23 | * 24 | * @return bool 25 | */ 26 | public static function isLinux(): bool 27 | { 28 | return PHP_OS_FAMILY === 'Linux'; 29 | } 30 | 31 | /** 32 | * Return TRUE if OS is Apple OSX. 33 | * 34 | * @return bool 35 | */ 36 | public static function isOSX(): bool 37 | { 38 | return PHP_OS_FAMILY === 'OSX' || PHP_OS_FAMILY === 'Darwin'; 39 | } 40 | 41 | /** 42 | * Return TRUE if OS is Solaris. 43 | * 44 | * @return bool 45 | */ 46 | public static function isSolaris(): bool 47 | { 48 | return PHP_OS_FAMILY === 'Solaris'; 49 | } 50 | 51 | /** 52 | * Return TRUE if OS is Windows. 53 | * 54 | * @return bool 55 | */ 56 | public static function isWindows(): bool 57 | { 58 | return PHP_OS_FAMILY === 'Windows'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Timer.php: -------------------------------------------------------------------------------- 1 | 11 | * @requires PHP 8.0 12 | * 13 | * @version 2.1.2 14 | */ 15 | 16 | namespace Acamposm\Ping; 17 | 18 | use Acamposm\Ping\Exceptions\TimerNotStartedException; 19 | use DateTime; 20 | 21 | /** 22 | * Utility Class to control time elapsed in commands. 23 | */ 24 | class Timer 25 | { 26 | /** 27 | * Format for the timestamps. 28 | */ 29 | public const FORMAT = 'd-m-Y H:i:s.u'; 30 | 31 | /** 32 | * Timer START. 33 | * 34 | * @var float 35 | */ 36 | protected float $start; 37 | 38 | /** 39 | * Timer END. 40 | * 41 | * @var float 42 | */ 43 | protected float $stop; 44 | 45 | /** 46 | * Timer constructor. 47 | */ 48 | public function __construct(protected string $format = self::FORMAT) 49 | { 50 | return $this; 51 | } 52 | 53 | /** 54 | * Start the Timer. 55 | * 56 | * @return float 57 | */ 58 | public function Start(): float 59 | { 60 | return $this->start = microtime(true); 61 | } 62 | 63 | /** 64 | * Stop the Timer. 65 | * 66 | * @throws TimerNotStartedException 67 | * @retun float 68 | */ 69 | public function Stop(): float 70 | { 71 | if (!isset($this->start)) { 72 | throw new TimerNotStartedException(); 73 | } 74 | 75 | return $this->stop = microtime(true); 76 | } 77 | 78 | /** 79 | * Returns an object with the Timer details. 80 | * 81 | * @return object 82 | */ 83 | public function GetResults(): object 84 | { 85 | if (!isset($this->stop)) { 86 | $this->stop = microtime(true); 87 | } 88 | 89 | return (object) [ 90 | 'start' => $this->getTimeObject($this->start), 91 | 'stop' => $this->getTimeObject($this->stop), 92 | 'time' => $this->getTimeElapsed(), 93 | ]; 94 | } 95 | 96 | /** 97 | * Returns a DateTime instance from timestamp. 98 | * 99 | * @param float $timestamp 100 | * 101 | * @return DateTime 102 | */ 103 | private static function getDateTimeObjectFromTimeStamp(float $timestamp): DateTime 104 | { 105 | return DateTime::createFromFormat('U.u', $timestamp); 106 | } 107 | 108 | /** 109 | * Returns an object with the timestamp as a float and as a human-readable. 110 | * 111 | * @param float $timestamp 112 | * 113 | * @return object 114 | */ 115 | private function getTimeObject(float $timestamp): object 116 | { 117 | $date_time = self::getDateTimeObjectFromTimeStamp($timestamp); 118 | 119 | return (object) [ 120 | 'as_float' => $timestamp, 121 | 'human_readable' => $date_time->format($this->format), 122 | ]; 123 | } 124 | 125 | /** 126 | * Returns the elapsed time between start and stop. 127 | * 128 | * @return float 129 | */ 130 | private function getTimeElapsed(): float 131 | { 132 | $time_elapsed = $this->stop - $this->start; 133 | 134 | return round($time_elapsed, 3, PHP_ROUND_HALF_DOWN); 135 | } 136 | } 137 | --------------------------------------------------------------------------------