diff --git a/.gitattributes b/.gitattributes
index c540470..b67c7ca 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,10 +1,10 @@
* text=auto eol=lf
-/tests export-ignore
-.editorconfig export-ignore
-.gitattributes export-ignore
-.gitignore export-ignore
-.php_cs export-ignore
-.travis.yml export-ignore
-phpcs.xml.dist export-ignore
-phpunit.xml.dist export-ignore
+/tests export-ignore
+.editorconfig export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.php-cs-fixer.php export-ignore
+phpcs.xml.dist export-ignore
+phpunit.xml.dist export-ignore
+.phpstan.neon export-ignore
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index 327717c..5d14a59 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -1,10 +1,10 @@
name: "testing"
on:
- push:
- branches: [ master ]
- pull_request:
- branches: [ master ]
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
jobs:
qa:
@@ -13,19 +13,19 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Validate composer.json and composer.lock
run: composer validate
- name: Cache Composer packages
id: composer-cache
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
- path: vendor
- key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
- restore-keys: |
- ${{ runner.os }}-php-
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
@@ -40,19 +40,19 @@ jobs:
strategy:
matrix:
- php:
- - 7.2
- - 7.3
- - 7.4
- composer-args: [ "" ]
- include:
- - php: 8.0
- composer-args: --ignore-platform-reqs
- fail-fast: false
+ php:
+ - 7.2
+ - 7.3
+ - 7.4
+ - 8.0
+ - 8.1
+ - 8.2
+ - 8.3
+ - 8.4
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
@@ -60,17 +60,14 @@ jobs:
php-version: ${{ matrix.php }}
- name: Cache PHP dependencies
- uses: actions/cache@v2
+ uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer-
- name: Install dependencies
- run: composer install --prefer-dist --no-progress ${{ matrix.composer-args }}
+ run: composer install --prefer-dist --no-progress
- name: Tests
run: composer test
-
- - name: Tests coverage
- run: composer coverage
diff --git a/.gitignore b/.gitignore
index 364d1a4..5ae8693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@ vendor
composer.lock
coverage
*.cache
+.idea
+kit
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
new file mode 100644
index 0000000..90b12a3
--- /dev/null
+++ b/.php-cs-fixer.php
@@ -0,0 +1,10 @@
+setFinder(
+ PhpCsFixer\Finder::create()
+ ->files()
+ ->name('*.php')
+ ->in(__DIR__.'/src')
+ ->in(__DIR__.'/tests')
+ );
\ No newline at end of file
diff --git a/.phpstan.neon b/.phpstan.neon
new file mode 100644
index 0000000..7f33c04
--- /dev/null
+++ b/.phpstan.neon
@@ -0,0 +1,5 @@
+parameters:
+ level: 8
+ paths:
+ - src
+ - tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d277fc..368afdf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
+## [2.1.0] - 2025-03-21
+### Added
+- Support for PHP 8.4
+-
## [2.0.1] - 2020-12-02
### Added
- Support for PHP 8
@@ -93,6 +97,7 @@ First version
[#13]: https://github.com/middlewares/client-ip/issues/13
[#14]: https://github.com/middlewares/client-ip/issues/14
+[2.1.0]: https://github.com/middlewares/client-ip/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/middlewares/client-ip/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/middlewares/client-ip/compare/v1.3.0...v2.0.0
[1.3.0]: https://github.com/middlewares/client-ip/compare/v1.2.0...v1.3.0
diff --git a/LICENSE b/LICENSE
index 017c0cd..374fb13 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2019
+Copyright (c) 2019-2025
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/composer.json b/composer.json
index 430b725..1ff839d 100644
--- a/composer.json
+++ b/composer.json
@@ -17,16 +17,16 @@
},
"require": {
"php": "^7.2 || ^8.0",
- "psr/http-server-middleware": "^1.0"
+ "psr/http-server-middleware": "^1"
},
"require-dev": {
- "phpunit/phpunit": "^8|^9",
- "laminas/laminas-diactoros": "^2.2",
- "friendsofphp/php-cs-fixer": "^2.0",
- "squizlabs/php_codesniffer": "^3.0",
- "oscarotero/php-cs-fixer-config": "^1.0",
- "middlewares/utils": "^3.1",
- "phpstan/phpstan": "^0.12"
+ "phpunit/phpunit": "^8 || ^9",
+ "laminas/laminas-diactoros": "^2 || ^3",
+ "friendsofphp/php-cs-fixer": "^3",
+ "squizlabs/php_codesniffer": "^3",
+ "oscarotero/php-cs-fixer-config": "^2",
+ "middlewares/utils": "^2 || ^3 || ^4",
+ "phpstan/phpstan": "^1 || ^2"
},
"autoload": {
"psr-4": {
@@ -46,4 +46,4 @@
"coverage": "phpunit --coverage-text",
"coverage-html": "phpunit --coverage-html=coverage"
}
-}
+}
\ No newline at end of file
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..7814331
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,16 @@
+
+
+ Middlewares coding standard
+
+
+
+
+
+
+
+
+
+
+ src
+ tests
+
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..0e5e49a
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ tests
+
+
+
+
+
+ ./src
+
+ ./tests
+ ./vendor
+
+
+
+
diff --git a/src/ClientIp.php b/src/ClientIp.php
index abc21af..9dff01c 100644
--- a/src/ClientIp.php
+++ b/src/ClientIp.php
@@ -16,17 +16,20 @@ class ClientIp implements MiddlewareInterface
private $attribute = 'client-ip';
/**
- * @var array The trusted proxy headers
+ * @var string[] The trusted proxy headers
*/
private $proxyHeaders = [];
/**
- * @var array The trusted proxy ips
+ * @var string[] The trusted proxy ips
*/
private $proxyIps = [];
/**
* Configure the proxy.
+ *
+ * @param array $ips
+ * @param array $headers
*/
public function proxy(
array $ips = [],
@@ -72,7 +75,7 @@ private function getIp(ServerRequestInterface $request): ?string
{
$localIp = $this->getLocalIp($request);
- if (!empty($this->proxyIps) && !$this->isInProxiedIps($localIp)) {
+ if (!empty($this->proxyIps) && !empty($localIp) && !$this->isInProxiedIps($localIp)) {
// Local IP address does not point at a known proxy, do not attempt
// to read proxied IP address.
return $localIp;
@@ -98,6 +101,7 @@ private function isInProxiedIps(string $ip): bool
return true;
}
}
+
return false;
}
diff --git a/tests/ClientIpTest.php b/tests/ClientIpTest.php
index 29fea41..84076e1 100644
--- a/tests/ClientIpTest.php
+++ b/tests/ClientIpTest.php
@@ -10,6 +10,9 @@
class ClientIpTest extends TestCase
{
+ /**
+ * @return array>>
+ */
public function ipsProvider(): array
{
return [
@@ -38,8 +41,9 @@ public function ipsProvider(): array
/**
* @dataProvider ipsProvider
+ * @param array $headers
*/
- public function testClientIpProxy(array $headers, string $ip)
+ public function testClientIpProxy(array $headers, string $ip): void
{
$request = Factory::createServerRequest('GET', '/', ['REMOTE_ADDR' => '123.123.123.123']);
@@ -57,7 +61,7 @@ function ($request) {
$this->assertEquals($ip, (string) $response->getBody());
}
- public function testClientIpNotProxy()
+ public function testClientIpNotProxy(): void
{
$request = Factory::createServerRequest('GET', '/', ['REMOTE_ADDR' => '123.123.123.123'])
->withHeader('X-Forwarded', '11.11.11.11');
@@ -72,7 +76,7 @@ function ($request) {
$this->assertEquals('123.123.123.123', (string) $response->getBody());
}
- public function testClientIpV6NotProxy()
+ public function testClientIpV6NotProxy(): void
{
$request = Factory::createServerRequest('GET', '/', ['REMOTE_ADDR' => '[::1]'])
->withHeader('X-Forwarded', '2001:0db8:85a3:0000:0000:8a2e:0370:7334');
@@ -87,7 +91,7 @@ function ($request) {
$this->assertEquals('::1', (string) $response->getBody());
}
- public function testCustomAttribute()
+ public function testCustomAttribute(): void
{
$request = Factory::createServerRequest('GET', '/', ['REMOTE_ADDR' => '123.123.123.123']);
@@ -101,6 +105,9 @@ function ($request) {
$this->assertEquals('123.123.123.123', (string) $response->getBody());
}
+ /**
+ * @return array>>
+ */
public function proxyProvider(): array
{
// 4.4.4.4 is IP spoofed by cleint
@@ -127,8 +134,9 @@ public function proxyProvider(): array
/**
* @dataProvider proxyProvider
+ * @param array $headers
*/
- public function testProxyIp(array $headers)
+ public function testProxyIp(array $headers): void
{
$request = Factory::createServerRequest('GET', '/', ['REMOTE_ADDR' => '1.1.1.1']);
@@ -173,7 +181,7 @@ function ($request) {
$this->assertEquals('1.1.1.1', (string) $response->getBody());
}
- public function testNoRemoteAddr()
+ public function testNoRemoteAddr(): void
{
$request = Factory::createServerRequest('GET', '/');