Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Fixed

- `IP::plus()` and `IP::minus()` no longer misinterpret 4-character numeric strings (e.g. `'1000'`) as binary "packed" strings, which caused `IPBlock` iteration to yield addresses outside of the block [#74](https://github.com/rlanvin/php-ip/issues/74)

## [3.0.0] - 2022-03-02

### Backward Compatibility Breaking Changes
Expand Down
18 changes: 16 additions & 2 deletions src/IP.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,14 @@ public function plus($value): IP
}

if (!$value instanceof self) {
$value = new static($value);
if (is_string($value) && ctype_digit($value)) {
// numeric strings must bypass the constructor's auto-detection,
// which would treat a string of NB_BYTES characters (e.g. "1000")
// as a binary "packed" string
$value = new static(static::initGmpFromNumericString($value));
} else {
$value = new static($value);
}
}

$result = gmp_add($this->ip, $value->ip);
Expand Down Expand Up @@ -438,7 +445,14 @@ public function minus($value): IP
}

if (!$value instanceof self) {
$value = new static($value);
if (is_string($value) && ctype_digit($value)) {
// numeric strings must bypass the constructor's auto-detection,
// which would treat a string of NB_BYTES characters (e.g. "1000")
// as a binary "packed" string
$value = new static(static::initGmpFromNumericString($value));
} else {
$value = new static($value);
}
}

$result = gmp_sub($this->ip, $value->ip);
Expand Down
2 changes: 1 addition & 1 deletion src/IPBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ public function getIterator(): \Generator
$position = gmp_init(0);

while (gmp_cmp($position, 0) >= 0 && gmp_cmp($position, $this->getNbAddresses()) < 0) {
yield $this->first_ip->plus(gmp_strval($position));
yield $this->first_ip->plus($position);
$position = gmp_add($position, 1);
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/IPTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ public function validOperations()
['ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', -1, null, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe'],
['::', 1, null, '::1'],
['::', null, -1, '::1'],
// numeric strings of 4 characters must not be mistaken
// for binary "packed" strings (see issue #74)
['0.0.0.0', '1000', null, '0.0.3.232'],
['0.0.3.232', null, '1000', '0.0.0.0'],
];
}

Expand Down
18 changes: 18 additions & 0 deletions tests/IPv4BlockTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ public function testIterator()
$this->assertEquals($expectation, iterator_to_array($subnet->getIterator()));
}

/**
* Iterating past the 1000th address used to yield addresses outside of
* the block, because the numeric position was mistaken for a binary
* "packed" string (see issue #74).
*/
public function testIteratorStaysWithinBlock()
{
$subnet = new IPv4Block('35.35.32.0/22');

$ips = iterator_to_array($subnet->getIterator());

$this->assertCount(1024, $ips);
$this->assertEquals('35.35.32.0', (string) $ips[0]);
$this->assertEquals('35.35.35.231', (string) $ips[999]);
$this->assertEquals('35.35.35.232', (string) $ips[1000]);
$this->assertEquals('35.35.35.255', (string) $ips[1023]);
}

public function testGetPrivateBlocks()
{
$private_blocks = IPv4Block::getPrivateBlocks();
Expand Down