Skip to content

Commit

Permalink
Merge pull request #1 from hotmeteor/ips
Browse files Browse the repository at this point in the history
Add format and detection for IPv4 and IPv6
  • Loading branch information
hotmeteor authored Sep 7, 2021
2 parents 9e6511e + a2613b8 commit 3b0a180
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 4 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ Regex::isUuid($subject)
```
Checks if the value is a UUID. Does not allow for whitespace.

***

```php
Regex::isIp($subject) // or
Regex::isIpv4($subject)
```
Checks if the value is an IPv4 address. Does not allow for whitespace.

***

```php
Regex::isIpv6($subject)
```
Checks if the value is an IPv6 address. Does not allow for whitespace.

### Replace

Replace methods replace anything in the subject value that doesn't match the pattern with the provided replacement.
Expand Down Expand Up @@ -110,4 +125,17 @@ Replaces all characters in the subject except numeric values, including decimals
Regex::uuid($subject)
```
Replaces all characters in the subject to form it into a UUID. Does not accept a replacement value.


***

```php
Regex::ip($subject) // or
Regex::ipv4($subject)
```
Replaces all characters in the subject to form it into an IPv4 address. Does not accept a replacement value.

```php
Regex::ipv6($subject)
```
Replaces all characters in the subject to form it into an IPv6 address. Does not accept a replacement value.

121 changes: 118 additions & 3 deletions src/Regex.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class Regex
const PATTERN_ALPHADASH = '\pL\pM\pN._-';
const PATTERN_DIGITS = '0-9';
const PATTERN_NUMERIC = '-?\d*(\.\d+)?';
const PATTERN_UUID = '\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}';

/**
* Filter to only alpha.
Expand Down Expand Up @@ -88,13 +87,72 @@ public static function uuid($subject)
return preg_replace("/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/i", '$1-$2-$3-$4-$5', $subject);
}

/**
* Filter to only IPv4 format.
*
* @param $subject
* @return string
*/
public static function ip($subject): string
{
return static::ipv4($subject);
}

/**
* Filter to only IPv4 format.
*
* @param $subject
* @return string
*/
public static function ipv4($subject): string
{
$subject = static::digits($subject);

$pattern = str_repeat('(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]|[0|1])', 4);

return static::glue($subject, $pattern, '.');
}

/**
* Filter to only IPv6 format.
*
* @param $subject
* @return string
*/
public static function ipv6($subject): string
{
$subject = str_replace(' ', '', static::alphanumeric($subject));

$count = floor(strlen($subject) / 4);

$pattern = str_repeat('([0-9a-fA-F]{1,4})', $count);

preg_match(self::wrapMatchPattern($pattern, false, '/', '/'), $subject, $matches);

// Get rid of full subject.
array_shift($matches);

$prefix = null;

if ($count < 8) {
$prefix = implode('::', array_slice($matches, 0, 2));
$matches = array_slice($matches, 2);
}

$matches = implode(':', $matches);

return implode(':', array_filter([$prefix, $matches]));
}

/**
* @param $subject
* @param $pattern
* @param string $replace
* @param string $prefix
* @param string $suffix
* @return array|string|string[]|null
*/
public static function replace($subject, $pattern, string $replace = '', $prefix = '/[^', $suffix = ']+/u')
public static function replace($subject, $pattern, string $replace = '', string $prefix = '/[^', string $suffix = ']+/u')
{
return preg_replace(self::wrapReplacePattern($pattern, $prefix, $suffix), $replace, $subject);
}
Expand Down Expand Up @@ -169,23 +227,78 @@ public static function isUuid($subject): bool
return static::match($subject, '/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iD', false, '', '');
}

/**
* Check if it's an IPv4 address.
*
* @param $subject
* @return bool
*/
public static function isIp($subject): bool
{
return static::isIpv4($subject);
}

/**
* Check if it's an IPv4 address.
*
* @param $subject
* @return bool
*/
public static function isIpv4($subject): bool
{
return static::match($subject, '/^((2[0-4]|1\d|[1-9])?\d|25[0-5])(\.(?1)){3}\z/', false, '', '');
}

/**
* Check if it's an IPv6 address.
*
* @param $subject
* @return bool
*/
public static function isIpv6($subject): bool
{
return static::match($subject, '/^(((?=(?>.*?(::))(?!.+\3)))\3?|([\dA-F]{1,4}(\3|:(?!$)|$)|\2))(?4){5}((?4){2}|((2[0-4]|1\d|[1-9])?\d|25[0-5])(\.(?7)){3})\z/i', false, '', '');
}

/**
* @param $subject
* @param $pattern
* @param bool $allowWhitespace
* @param string $prefix
* @param string $suffix
* @return bool
*/
public static function match($subject, $pattern, bool $allowWhitespace = false, $prefix = '/^[', $suffix = ']+$/u'): bool
public static function match($subject, $pattern, bool $allowWhitespace = false, string $prefix = '/^[', string $suffix = ']+$/u'): bool
{
$result = preg_match(self::wrapMatchPattern($pattern, $allowWhitespace, $prefix, $suffix), $subject);

return is_numeric($result) ? $result === 1 : $result;
}

/**
* @param $subject
* @param $pattern
* @param string $glue
* @param bool $allowWhitespace
* @param string $prefix
* @param string $suffix
* @return string
*/
public static function glue($subject, $pattern, string $glue = '', bool $allowWhitespace = false, string $prefix = '/', string $suffix = '/'): string
{
preg_match(self::wrapMatchPattern($pattern, $allowWhitespace, $prefix, $suffix), $subject, $matches);

array_shift($matches);

return implode($glue, $matches);
}

/**
* Wrap a replace pattern with boundaries.
*
* @param $pattern
* @param $prefix
* @param $suffix
* @return string
*/
protected static function wrapReplacePattern($pattern, $prefix, $suffix): string
Expand All @@ -198,6 +311,8 @@ protected static function wrapReplacePattern($pattern, $prefix, $suffix): string
*
* @param $pattern
* @param bool $allowWhitespace
* @param $prefix
* @param $suffix
* @return string
*/
protected static function wrapMatchPattern($pattern, bool $allowWhitespace, $prefix, $suffix): string
Expand Down
79 changes: 79 additions & 0 deletions tests/RegexTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,47 @@ public function test_uuid_replace()
$this->assertSame($expected, $result);
}

public function test_ipv4_replace()
{
$examples = [
'192.168.1.1' => '192.168.1.1',
'192. 168. 1. 1' => '192.168.1.1',
'ip address: 192.168.1.1' => '192.168.1.1',
'ip address: 19216811' => '192.168.1.1',
'255.255.255.255' => '255.255.255.255',
'2 55 255.25 5255 ' => '255.255.255.255',
];

foreach ($examples as $ip => $expected) {
$result = Regex::ipv4($ip);

$this->assertSame($expected, $result);
}
}

public function test_ipv6_replace()
{
$examples = [
'2001:0db8:85a3:0000:0000:8a2e:0370:7334' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
'20 01: 0db8:85a3 : 0000:0 000:8a2e:0 370:7334 ' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334',
'FE80:0000:0000 0000:0202:B3FF:FE1E:8329' => 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329',
'F E80:0000:0 000:0 000:020 2:B3 FF FE1E: 8329' => 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329',
'FE80::8329' => 'FE80::8329',
'FE80::FFFF:8329' => 'FE80::FFFF:8329',
'FE80::B3FF:FFFF:8329' => 'FE80::B3FF:FFFF:8329',
'FE80::0202:B3FF:FFFF:8329' => 'FE80::0202:B3FF:FFFF:8329',
'FE80::0000:0202:B3FF:FFFF:8329' => 'FE80::0000:0202:B3FF:FFFF:8329',
'FE80::0000:0000:0202:B3FF:FFFF:8329' => 'FE80::0000:0000:0202:B3FF:FFFF:8329',
'FE80:0000:0000:0000:0202:B3FF:FFFF:8329' => 'FE80:0000:0000:0000:0202:B3FF:FFFF:8329',
];

foreach ($examples as $ip => $expected) {
$result = Regex::ipv6($ip);

$this->assertSame($expected, $result);
}
}

/**
* @dataProvider providesMatchSubjects
*/
Expand Down Expand Up @@ -228,4 +269,42 @@ public function test_uuid_match()

$this->assertFalse($result);
}

public function test_ipv4_match()
{
$examples = [
'192.168.1.1' => true,
'127.0.0.1' => true,
'0.0.0.0' => true,
'255.255.255.255' => true,
'256.256.256.256' => false,
'999.999.999.999' => false,
'1.2.3' => false,
'1.2.3.4' => true,
];

foreach ($examples as $ip => $expected) {
$ipResult = Regex::isIp($ip);
$ipv4Result = Regex::isIpv4($ip);

$this->assertSame($expected, $ipResult);
$this->assertSame($expected, $ipv4Result);
}
}

public function test_ipv6_match()
{
$examples = [
'2001:0db8:85a3:0000:0000:8a2e:0370:7334' => true,
'FE80:0000:0000:0000:0202:B3FF:FE1E:8329' => true,
'192.168.1.1' => false,
'test:test:test:test:test:test:test:test' => false,
];

foreach ($examples as $ip => $expected) {
$result = Regex::isIpv6($ip);

$this->assertSame($expected, $result);
}
}
}

0 comments on commit 3b0a180

Please sign in to comment.