Compare commits

...

3 commits

Author SHA1 Message Date
El RIDO
e1c5bd7328
don't block CI from running 2026-04-18 12:32:49 +02:00
El RIDO
c1d080e3cc
actually update composer dependencies 2026-04-18 12:30:37 +02:00
El RIDO
67baf8cca4
update DOMpurify library from 3.3.2 to 3.4.0 2026-04-18 09:28:58 +02:00
27 changed files with 248 additions and 121 deletions

View file

@ -4,7 +4,7 @@
* ADDED: Translations for Swedish & Persian
* CHANGED: Deduplicate JSON error message translations
* CHANGED: Refactored translation of exception messages
* CHANGED: Upgrading libraries to: DOMpurify 3.3.2, ip-lib 1.22.0 & polyfill-php80 1.34.0
* CHANGED: Upgrading libraries to: DOMpurify 3.4.0, ip-lib 1.22.0 & polyfill-php80 1.34.0
* CHANGED: Remove obsolete X-XSS-Protection header (#1825)
* FIXED: Some exceptions not getting translated
* FIXED: Attachment disappears after a "paste" in the message area (#1731)

View file

@ -44,6 +44,7 @@
},
"config" : {
"audit": {
"block-insecure": false,
"ignore": {
"CVE-2025-45769": "disputed on the basis that key lengths are expected to be set by an application, not by this library"
}

25
composer.lock generated
View file

@ -215,7 +215,7 @@
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/yzalis/Identicon.git",
"url": "https://github.com/yzalis/Identicon",
"reference": "ff5ed090129cab9bfa2a322857d4a01d107aa0ae"
},
"dist": {
@ -260,11 +260,6 @@
"identicon",
"image"
],
"support": {
"issues": "https://github.com/yzalis/Identicon/issues",
"source": "https://github.com/yzalis/Identicon/tree/master"
},
"abandoned": true,
"time": "2019-10-14T09:30:57+00:00"
}
],
@ -896,16 +891,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.33",
"version": "9.6.34",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "fea06253ecc0a32faf787bd31b261f56f351d049"
"reference": "b36f02317466907a230d3aa1d34467041271ef4a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fea06253ecc0a32faf787bd31b261f56f351d049",
"reference": "fea06253ecc0a32faf787bd31b261f56f351d049",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a",
"reference": "b36f02317466907a230d3aa1d34467041271ef4a",
"shasum": ""
},
"require": {
@ -979,7 +974,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.33"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34"
},
"funding": [
{
@ -1003,7 +998,7 @@
"type": "tidelift"
}
],
"time": "2026-01-27T05:25:09+00:00"
"time": "2026-01-27T05:45:00+00:00"
},
{
"name": "sebastian/cli-parser",
@ -2069,15 +2064,15 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.4 || ^8.0"
},
"platform-dev": {},
"platform-dev": [],
"platform-overrides": {
"php": "7.4"
},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.3.0"
}

View file

@ -15,7 +15,7 @@ require('./prettify');
global.prettyPrint = window.PR.prettyPrint;
global.prettyPrintOne = window.PR.prettyPrintOne;
global.showdown = require('./showdown-2.1.0');
global.DOMPurify = require('./purify-3.3.2');
global.DOMPurify = require('./purify-3.4.0');
global.baseX = require('./base-x-5.0.1').baseX;
global.Legacy = require('./legacy').Legacy;
require('./privatebin');

File diff suppressed because one or more lines are too long

2
js/purify-3.4.0.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -123,7 +123,7 @@ class Configuration
'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==',
'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
'js/privatebin.js' => 'sha512-6SwOJniNN8RBmAK7yCt4ly2qYyH8OALxB74/K1AJgw+YnZgRCfTDVq1qY1K5Y2QCxCODGGTpAjTqQRExzCqV7g==',
'js/purify-3.3.2.js' => 'sha512-I6igPVpf3xNghG92mujwqB6Zi3LpUTsni4bRuLnMThEGH6BDbsumv7373+AXHzA4OUlxGsym8ZxKFHy4xjYvkQ==',
'js/purify-3.4.0.js' => 'sha512-OGHBIOGMG1rGpL49n3jErRh8mWysG9CdyTWy+0vtcINutmY1+L9Wy4Km6aW60cesV2OraWES+KAAhLfIcbKJjw==',
'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',
'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==',
),

View file

@ -65,7 +65,7 @@ if ($MARKDOWN) :
<?php
endif;
?>
<?php $this->_scriptTag('js/purify-3.3.2.js', 'defer'); ?>
<?php $this->_scriptTag('js/purify-3.4.0.js', 'defer'); ?>
<?php $this->_scriptTag('js/legacy.js', 'defer'); ?>
<?php $this->_scriptTag('js/privatebin.js', 'defer'); ?>
<!-- icon -->

View file

@ -49,7 +49,7 @@ if ($MARKDOWN) :
<?php
endif;
?>
<?php $this->_scriptTag('js/purify-3.3.2.js', 'defer'); ?>
<?php $this->_scriptTag('js/purify-3.4.0.js', 'defer'); ?>
<?php $this->_scriptTag('js/legacy.js', 'defer'); ?>
<?php $this->_scriptTag('js/privatebin.js', 'defer'); ?>
<!-- icon -->

View file

@ -3,7 +3,7 @@
'name' => 'privatebin/privatebin',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
'reference' => '67baf8cca4b7aa1bc57e631b47ab561695c8ce2b',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -20,9 +20,9 @@
'dev_requirement' => false,
),
'mlocati/ip-lib' => array(
'pretty_version' => '1.21.0',
'version' => '1.21.0.0',
'reference' => 'b5d38cdcbfc1516604d821a1f3f4a1638f327267',
'pretty_version' => '1.22.0',
'version' => '1.22.0.0',
'reference' => '4e40ffd3bf9989db19403d89c4d8be44b87b8a91',
'type' => 'library',
'install_path' => __DIR__ . '/../mlocati/ip-lib',
'aliases' => array(),
@ -31,16 +31,16 @@
'privatebin/privatebin' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8eb39b4ffa8daed3d8ff0279c9870ee5b6b0bd95',
'reference' => '67baf8cca4b7aa1bc57e631b47ab561695c8ce2b',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'reference' => '60328e362d4c2c802a54fcbf04f9d3fb892b4cf8',
'pretty_version' => 'v1.34.0',
'version' => '1.34.0.0',
'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),

View file

@ -63,6 +63,8 @@ interface AddressInterface
* Get the type of the IP address.
*
* @return int One of the \IPLib\Address\Type::T_... constants
*
* @phpstan-return \IPLib\Address\Type::T_IPv4|\IPLib\Address\Type::T_IPv6
*/
public function getAddressType();
@ -110,7 +112,7 @@ interface AddressInterface
/**
* Get the address at a certain distance from this address.
*
* @param int|numeric-string $n the distance of the address (can be negative)
* @param int|numeric-string|mixed $n the distance of the address (can be negative)
*
* @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or NULL if $n is neither an integer nor a string containing a valid integer, or if the final address would be invalid
*

View file

@ -11,6 +11,8 @@ use IPLib\Service\NumberInChunks;
/**
* An IPv4 address.
*
* @phpstan-consistent-constructor
*/
class IPv4 implements AddressInterface
{
@ -40,14 +42,14 @@ class IPv4 implements AddressInterface
/**
* A string representation of this address than can be used when comparing addresses and ranges.
*
* @var string
* @var string|null
*/
protected $comparableString;
/**
* An array containing RFC designated address ranges.
*
* @var array|null
* @var \IPLib\Address\AssignedRange[]|null
*/
private static $reservedRanges;
@ -181,7 +183,7 @@ class IPv4 implements AddressInterface
/**
* Parse an array of bytes and returns an IPv4 instance if the array is valid, or null otherwise.
*
* @param int[]|array $bytes
* @param array<int|mixed> $bytes
*
* @return static|null
*/
@ -367,10 +369,14 @@ class IPv4 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
$exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
$subnet = Subnet::parseString($exceptionRange);
/** @var Subnet $subnet */
$exceptions[] = new AssignedRange($subnet, $exceptionType);
}
}
$reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
$subnet = Subnet::parseString($range);
/** @var Subnet $subnet */
$reservedRanges[] = new AssignedRange($subnet, $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@ -407,8 +413,10 @@ class IPv4 implements AddressInterface
public function toIPv6()
{
$myBytes = $this->getBytes();
$ipv6 = IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
/** @var IPv6 $ipv6 */
return IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
return $ipv6;
}
/**
@ -420,7 +428,10 @@ class IPv4 implements AddressInterface
*/
public function toIPv6IPv4Mapped()
{
return IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes()));
$ipv6 = IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes()));
/** @var IPv6 $ipv6 */
return $ipv6;
}
/**

View file

@ -11,6 +11,8 @@ use IPLib\Service\NumberInChunks;
/**
* An IPv6 address.
*
* @phpstan-consistent-constructor
*/
class IPv6 implements AddressInterface
{
@ -56,7 +58,7 @@ class IPv6 implements AddressInterface
/**
* An array containing RFC designated address ranges.
*
* @var array|null
* @var \IPLib\Address\AssignedRange[]|null
*/
private static $reservedRanges;
@ -144,7 +146,7 @@ class IPv6 implements AddressInterface
}
}
$result = null;
if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) {
if (strpos($address, ':') !== false && strpos($address, ':::') === false) {
if ($flags & ParseStringFlag::MAY_INCLUDE_PORT && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
$address = $matches[1];
}
@ -212,7 +214,7 @@ class IPv6 implements AddressInterface
/**
* Parse an array of bytes and returns an IPv6 instance if the array is valid, or null otherwise.
*
* @param int[]|array $bytes
* @param array<int|mixed> $bytes
*
* @return static|null
*/
@ -244,7 +246,7 @@ class IPv6 implements AddressInterface
/**
* Parse an array of words and returns an IPv6 instance if the array is valid, or null otherwise.
*
* @param int[]|array $words
* @param array<int|mixed> $words
*
* @return static|null
*/
@ -353,7 +355,7 @@ class IPv6 implements AddressInterface
if ($this->words === null) {
$this->words = array_map(
function ($chunk) {
return hexdec($chunk);
return (int) hexdec($chunk);
},
explode(':', $this->longAddress)
);
@ -441,10 +443,14 @@ class IPv6 implements AddressInterface
$exceptions = array();
if (isset($data[1])) {
foreach ($data[1] as $exceptionRange => $exceptionType) {
$exceptions[] = new AssignedRange(Subnet::parseString($exceptionRange), $exceptionType);
$subnet = Subnet::parseString($exceptionRange);
/** @var Subnet $subnet */
$exceptions[] = new AssignedRange($subnet, $exceptionType);
}
}
$reservedRanges[] = new AssignedRange(Subnet::parseString($range), $data[0], $exceptions);
$subnet = Subnet::parseString($range);
/** @var Subnet $subnet */
$reservedRanges[] = new AssignedRange($subnet, $data[0], $exceptions);
}
self::$reservedRanges = $reservedRanges;
}
@ -517,11 +523,17 @@ class IPv6 implements AddressInterface
{
$myBytes = $this->getBytes();
$ipv6Bytes = array_merge(array_slice($myBytes, 0, 12), array(0xff, 0xff, 0xff, 0xff));
$ipv6String = static::fromBytes($ipv6Bytes)->toString($ipV6Long);
$ipv6 = static::fromBytes($ipv6Bytes);
/** @var IPv6 $ipv6 */
$ipv6String = $ipv6->toString($ipV6Long);
$ipv4Bytes = array_slice($myBytes, 12, 4);
$ipv4String = IPv4::fromBytes($ipv4Bytes)->toString($ipV4Long);
$ipv4 = IPv4::fromBytes($ipv4Bytes);
/** @var IPv4 $ipv4 */
$ipv4String = $ipv4->toString($ipV4Long);
$result = preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String);
/** @var string $result */
return preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String);
return $result;
}
/**
@ -627,8 +639,11 @@ class IPv6 implements AddressInterface
$paddedBits = substr($paddedBits, $absBits) . $pad;
}
$bytes = array_map('bindec', str_split($paddedBits, 16));
/** @var int[] $bytes */
$result = static::fromWords($bytes);
/** @var IPv6 $result */
return static::fromWords($bytes);
return $result;
}
/**

View file

@ -24,7 +24,7 @@ class Type
/**
* Get the name of a type.
*
* @param int $type
* @param int|mixed $type
*
* @return string
*
@ -38,7 +38,7 @@ class Type
case static::T_IPv6:
return 'IP v6';
default:
return sprintf('Unknown type (%s)', $type);
return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', print_r($type, true));
}
}
}

View file

@ -48,35 +48,33 @@ class Factory
*/
public static function parseAddressString($address, $flags = 0)
{
$result = null;
if ($result === null) {
$result = Address\IPv4::parseString($address, $flags);
if (($result = Address\IPv4::parseString($address, $flags)) !== null) {
return $result;
}
if ($result === null) {
$result = Address\IPv6::parseString($address, $flags);
if (($result = Address\IPv6::parseString($address, $flags)) !== null) {
return $result;
}
return $result;
return null;
}
/**
* Convert a byte array to an address instance.
*
* @param int[]|array $bytes
* @param array<int|mixed> $bytes
*
* @return \IPLib\Address\AddressInterface|null
*/
public static function addressFromBytes(array $bytes)
{
$result = null;
if ($result === null) {
$result = Address\IPv4::fromBytes($bytes);
if (($result = Address\IPv4::fromBytes($bytes)) !== null) {
return $result;
}
if ($result === null) {
$result = Address\IPv6::fromBytes($bytes);
if (($result = Address\IPv6::fromBytes($bytes)) !== null) {
return $result;
}
return $result;
return null;
}
/**
@ -100,7 +98,7 @@ class Factory
/**
* Parse an IP range string.
*
* @param string $range
* @param string|mixed $range
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\RangeInterface|null
@ -110,10 +108,7 @@ class Factory
*/
public static function parseRangeString($range, $flags = 0)
{
$result = null;
if ($result === null) {
$result = Range\Subnet::parseString($range, $flags);
}
$result = Range\Subnet::parseString($range, $flags);
if ($result === null) {
$result = Range\Pattern::parseString($range, $flags);
}
@ -133,7 +128,7 @@ class Factory
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param bool $supportNonDecimalIPv4
*
* @return \IPLib\Address\AddressInterface|null
* @return \IPLib\Range\RangeInterface|null
*
* @see \IPLib\Factory::getRangeFromBoundaries()
* @since 1.2.0
@ -163,6 +158,53 @@ class Factory
return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
}
/**
* Calculate the minimal range that contains all the specified addresses.
*
* @param array<non-empty-string|\IPLib\Address\AddressInterface|mixed> $addresses
* @param int $flags
*
* @return \IPLib\Range\RangeInterface|null Returns NULL if $addresses is empty, if it contains invalid addresses, or if the addresses aren't compatible (for example, both IPv4 and IPv6 addresses)
*
* @since 1.22.0
*/
public static function getRangeFromAddresses(array $addresses, $flags = 0)
{
$min = null;
$max = null;
$numberOfBits = null;
foreach ($addresses as $address) {
if (!$address instanceof AddressInterface) {
$address = Factory::parseAddressString($address, $flags);
if ($address === null) {
return null;
}
}
if ($numberOfBits === null) {
$min = $address;
$max = $address;
$numberOfBits = $address->getNumberOfBits();
} elseif ($numberOfBits !== $address->getNumberOfBits()) {
return null;
} else {
/** @var AddressInterface $min */
/** @var AddressInterface $max */
$comparable = $address->getComparableString();
if ($min->getComparableString() > $comparable) {
$min = $address;
}
if ($max->getComparableString() < $comparable) {
$max = $address;
}
}
}
if ($numberOfBits === null) {
return null;
}
return static::rangeFromBoundaryAddresses($min, $max);
}
/**
* @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
* For upgrading:
@ -185,8 +227,8 @@ class Factory
/**
* Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
*
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
* @param string|\IPLib\Address\AddressInterface|mixed $from
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param int $flags A combination or zero or more flags
*
* @return \IPLib\Range\Subnet[]|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
@ -202,6 +244,7 @@ class Factory
}
if ($from === null || $to === null) {
$address = $from ? $from : $to;
/** @var AddressInterface $address */
return array(new Subnet($address, $address, $address->getNumberOfBits()));
}
@ -265,11 +308,11 @@ class Factory
}
/**
* @param string|\IPLib\Address\AddressInterface $from
* @param string|\IPLib\Address\AddressInterface $to
* @param string|\IPLib\Address\AddressInterface|mixed $from
* @param string|\IPLib\Address\AddressInterface|mixed $to
* @param int $flags
*
* @return \IPLib\Address\AddressInterface[]|null[]|false[]
* @return array{\IPLib\Address\AddressInterface|false|null, \IPLib\Address\AddressInterface|false|null}
*/
private static function parseBoundaries($from, $to, $flags = 0)
{
@ -277,7 +320,7 @@ class Factory
foreach (array('from', 'to') as $param) {
$value = $$param;
if (!($value instanceof AddressInterface)) {
$value = (string) $value;
$value = is_object($value) && method_exists($value, '__toString') || is_scalar($value) ? (string) $value : '';
if ($value === '') {
$value = null;
} else {
@ -289,6 +332,7 @@ class Factory
}
$result[] = $value;
}
/** @var array{\IPLib\Address\AddressInterface|false|null, \IPLib\Address\AddressInterface|false|null} $result */
if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
$result = array($result[1], $result[0]);
}

View file

@ -22,10 +22,18 @@ abstract class AbstractRange implements RangeInterface
*/
public function getRangeType()
{
/** @var \IPLib\Range\Pattern|\IPLib\Range\Subnet $this */
// @phpstan-ignore varTag.nativeType
if ($this->rangeType === null) {
$addressType = $this->getAddressType();
if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) {
$this->rangeType = Factory::getRangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType();
$fromAddress = $this->fromAddress;
/** @var IPv6 $fromAddress */
$toAddress = $this->toAddress;
/** @var IPv6 $toAddress */
$range = Factory::getRangeFromBoundaries($fromAddress->toIPv4(), $toAddress->toIPv4());
/** @var RangeInterface $range */
$this->rangeType = $range->getRangeType();
} else {
switch ($addressType) {
case AddressType::T_IPv4:
@ -36,8 +44,6 @@ abstract class AbstractRange implements RangeInterface
$defaultType = IPv6::getDefaultReservedRangeType();
$reservedRanges = IPv6::getReservedRanges();
break;
default:
throw new \Exception('@todo'); // @codeCoverageIgnore
}
$rangeType = null;
foreach ($reservedRanges as $reservedRange) {
@ -62,23 +68,20 @@ abstract class AbstractRange implements RangeInterface
{
if (is_int($n)) {
$positive = $n >= 0;
if ($positive === false) {
$nPlus1 = $n + 1;
}
} elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') {
$n = $s;
$positive = $n[0] !== '-';
if ($positive === false) {
$nPlus1 = BinaryMath::getInstance()->add1ToIntegerString($n);
}
} else {
return null;
}
if ($positive) {
$start = Factory::parseAddressString($this->getComparableStartString());
/** @var \IPLib\Address\AddressInterface $start */
$address = $start->getAddressAtOffset($n);
} else {
$end = Factory::parseAddressString($this->getComparableEndString());
/** @var \IPLib\Address\AddressInterface $end */
$nPlus1 = is_int($n) ? $n + 1 : BinaryMath::getInstance()->add1ToIntegerString($n);
$address = $end->getAddressAtOffset($nPlus1);
}
@ -156,15 +159,20 @@ abstract class AbstractRange implements RangeInterface
if ($networkPrefix > $maxPrefix) {
throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be larger than the maximum network prefix of the range ({$maxPrefix})");
}
if ($startIp instanceof IPv4) {
$one = IPv4::fromBytes(array(0, 0, 0, 1));
} elseif ($startIp instanceof IPv6) {
$one = IPv6::fromBytes(array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
switch ($startIp->getAddressType()) {
case AddressType::T_IPv4:
$one = IPv4::fromBytes(array(0, 0, 0, 1));
break;
case AddressType::T_IPv6:
$one = IPv6::fromBytes(array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
break;
}
/** @var \IPLib\Address\AddressInterface $one */
$delta = $one->shift($networkPrefix - $maxPrefix);
$result = array();
while (true) {
$range = Subnet::parseString("{$startIp}/{$networkPrefix}");
/** @var Subnet $range */
if (!$forceSubnet && $this instanceof Pattern) {
$range = $range->asPattern() ?: $range;
}

View file

@ -14,6 +14,8 @@ use IPLib\Service\BinaryMath;
*
* @example 127.0.*.*
* @example ::/8
*
* @phpstan-consistent-constructor
*/
class Pattern extends AbstractRange
{
@ -106,10 +108,20 @@ class Pattern extends AbstractRange
return null;
}
if ($range === '*.*.*.*') {
return new static(IPv4::parseString('0.0.0.0'), IPv4::parseString('255.255.255.255'), 4);
$fromAddress = IPv4::parseString('0.0.0.0');
/** @var \IPLib\Address\IPv4 $fromAddress */
$toAddress = IPv4::parseString('255.255.255.255');
/** @var \IPLib\Address\IPv4 $toAddress */
return new static($fromAddress, $toAddress, 4);
}
if ($range === '*:*:*:*:*:*:*:*') {
return new static(IPv6::parseString('::'), IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
$fromAddress = IPv6::parseString('::');
/** @var \IPLib\Address\IPv6 $fromAddress */
$toAddress = IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff');
/** @var \IPLib\Address\IPv6 $toAddress */
return new static($fromAddress, $toAddress, 8);
}
$matches = null;
if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
@ -128,6 +140,7 @@ class Pattern extends AbstractRange
$fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
$otherBytes = array_fill(0, $asterisksCount, 255);
$toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
/** @var \IPLib\Address\IPv4 $toAddress */
return new static($fromAddress, $toAddress, $asterisksCount);
}
@ -140,6 +153,7 @@ class Pattern extends AbstractRange
$fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
$otherWords = array_fill(0, $asterisksCount, 0xffff);
$toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
/** @var \IPLib\Address\IPv6 $toAddress */
return new static($fromAddress, $toAddress, $asterisksCount);
}
@ -177,6 +191,7 @@ class Pattern extends AbstractRange
$bytes = array_slice($bytes, 0, -$this->asterisksCount * 2);
$bytes = array_pad($bytes, 16, 1);
$address = IPv6::fromBytes($bytes);
/** @var IPv6 $address */
$before = substr($address->toString(false), 0, -strlen(':101') * $this->asterisksCount);
$result = $before . str_repeat(':*', $this->asterisksCount);
}

View file

@ -31,13 +31,15 @@ interface RangeInterface
* Get the type of the IP addresses contained in this range.
*
* @return int One of the \IPLib\Address\Type::T_... constants
*
* @phpstan-return \IPLib\Address\Type::T_IPv4|\IPLib\Address\Type::T_IPv6
*/
public function getAddressType();
/**
* Get the type of range of the IP address.
*
* @return int One of the \IPLib\Range\Type::T_... constants
* @return int|null One of the \IPLib\Range\Type::T_... constants, or null if this range crosses multiple range types
*
* @since 1.5.0
*/
@ -46,7 +48,7 @@ interface RangeInterface
/**
* Get the address at a certain offset of this range.
*
* @param int|numeric-string $n the offset of the address (support negative offset)
* @param int|numeric-string|mixed $n the offset of the address (support negative offset)
*
* @return \IPLib\Address\AddressInterface|null return NULL if $n is neither an integer nor a string containing a valid integer, or if the offset out of range
*

View file

@ -13,6 +13,8 @@ use IPLib\ParseStringFlag;
*
* @example 127.0.0.1
* @example ::1
*
* @phpstan-consistent-constructor
*/
class Single extends AbstractRange
{

View file

@ -14,6 +14,8 @@ use IPLib\Service\BinaryMath;
*
* @example 127.0.0.1/32
* @example ::/8
*
* @phpstan-consistent-constructor
*/
class Subnet extends AbstractRange
{
@ -146,15 +148,15 @@ class Subnet extends AbstractRange
$startSameBits = $networkPrefix % 8;
if ($startSameBits !== 0) {
$varyingByte = $addressBytes[$numSameBytes];
$differentBytesStart[0] = $varyingByte & bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT));
$differentBytesEnd[0] = $differentBytesStart[0] + bindec(str_repeat('1', 8 - $startSameBits));
$differentBytesStart[0] = $varyingByte & (int) bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT));
$differentBytesEnd[0] = $differentBytesStart[0] + (int) bindec(str_repeat('1', 8 - $startSameBits));
}
$fromAddress = Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart));
/** @var \IPLib\Address\AddressInterface $fromAddress */
$toAddress = Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd));
/** @var \IPLib\Address\AddressInterface $toAddress */
return new static(
Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart)),
Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd)),
$networkPrefix
);
return new static($fromAddress, $toAddress, $networkPrefix);
}
/**
@ -255,7 +257,9 @@ class Subnet extends AbstractRange
public static function get6to4()
{
if (self::$sixToFour === null) {
self::$sixToFour = self::parseString('2002::/16');
$subnet = self::parseString('2002::/16');
/** @var Subnet $subnet */
self::$sixToFour = $subnet;
}
return self::$sixToFour;
@ -327,10 +331,11 @@ class Subnet extends AbstractRange
$result = array();
$unitsToRemove = $maxUnits - $prefixUnits;
$initialPointer = preg_replace("/^(({$rxUnit})\.){{$unitsToRemove}}/", '', $this->getStartAddress()->getReverseDNSLookupName());
/** @var string $initialPointer */
$chunks = explode('.', $initialPointer, 2);
for ($index = 0; $index < $numVariants; $index++) {
if ($index !== 0) {
$chunks[0] = $isHex ? dechex(1 + hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]);
$chunks[0] = $isHex ? dechex(1 + (int) hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]);
}
$result[] = implode('.', $chunks);
}

View file

@ -110,7 +110,7 @@ class Type
/**
* Get the name of a type.
*
* @param int $type
* @param int|mixed $type
*
* @return string
*/
@ -146,7 +146,7 @@ class Type
case static::T_CGNAT:
return 'Carrier-grade NAT';
default:
return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', $type);
return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', print_r($type, true));
}
}
}

View file

@ -9,6 +9,9 @@ namespace IPLib\Service;
*/
class BinaryMath
{
/**
* @var \IPLib\Service\BinaryMath|null
*/
private static $instance;
/**
@ -119,7 +122,7 @@ class BinaryMath
*
* @param int $exponent The non-negative exponent
*
* @return int|string
* @return int|numeric-string
*/
public function pow2string($exponent)
{
@ -138,14 +141,16 @@ class BinaryMath
$digits[] = $carry;
}
}
$result = implode('', array_reverse($digits));
/** @var numeric-string $result */
return implode('', array_reverse($digits));
return $result;
}
/**
* @param numeric-string|mixed $value
*
* @return string empty string if $value is not a valid numeric string
* @return numeric-string|'' empty string if $value is not a valid numeric string
*/
public function normalizeIntegerString($value)
{
@ -160,14 +165,19 @@ class BinaryMath
if (!preg_match('/^0*([0-9]+)$/', $value, $matches)) {
return '';
}
$numericString = $matches[1];
if ($sign === '-' && $numericString !== '0') {
$numericString = '-' . $numericString;
}
/** @var numeric-string $numericString */
return ($sign === '-' && $matches[1] !== '0' ? $sign : '') . $matches[1];
return $numericString;
}
/**
* @param numeric-string $value a string that has been normalized with normalizeIntegerString()
*
* @return string
* @return numeric-string
*/
public function add1ToIntegerString($value)
{
@ -189,8 +199,10 @@ class BinaryMath
if ($imploded[0] === '0') {
$imploded = substr($imploded, 1);
}
$result = '-' . $imploded;
/** @var numeric-string $result */
return '-' . $imploded;
return $result; // @phpstan-ignore varTag.nativeType
}
$digits = str_split($value);
$carry = 1;
@ -205,8 +217,10 @@ class BinaryMath
array_unshift($digits, (string) $carry);
}
}
$result = implode('', $digits);
/** @var numeric-string $result */
return implode('', $digits);
return $result;
}
/**
@ -215,7 +229,7 @@ class BinaryMath
* @param string $num1
* @param string $num2
*
* @return string[],int[] The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits
* @return array{string, string, int} The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits
*/
private function toSameLength($num1, $num2)
{

View file

@ -88,7 +88,8 @@ class NumberInChunks
$negative = $int < 0;
if ($negative) {
$positiveInt = -$int;
if (is_float($positiveInt)) { // -PHP_INT_MIN is bigger than PHP_INT_MAX
/** @var int|float $positiveInt may be float because -PHP_INT_MIN is bigger than PHP_INT_MAX */
if (is_float($positiveInt)) {
return self::fromNumericString((string) $int, $chunkSize);
}
$int = $positiveInt;
@ -197,7 +198,7 @@ class NumberInChunks
* @param int[] $subtrahend
* @param int $chunkSize
*
* @return array
* @return array{bool, int[]}
*/
private static function substractChunks(array $minuend, array $subtrahend, $chunkSize)
{

View file

@ -80,6 +80,8 @@ class RangesFromBoundaryCalculator
* Set the number of bits used to represent addresses (32 for IPv4, 128 for IPv6).
*
* @param int $numBits
*
* @return void
*/
private function setNumBits($numBits)
{
@ -102,6 +104,8 @@ class RangesFromBoundaryCalculator
* @param string $end the end address (represented in reduced bit form)
* @param int $position the number of bits in the mask we are comparing at this cycle
* @param \IPLib\Range\Subnet[] $result found ranges will be added to this variable
*
* @return void
*/
private function calculate($start, $end, $position, array &$result)
{
@ -110,6 +114,7 @@ class RangesFromBoundaryCalculator
return;
}
$startMasked = '';
for ($index = $position - 1; $index >= 0; $index--) {
$startMasked = $this->math->andX($start, $this->masks[$index]);
$endMasked = $this->math->andX($end, $this->masks[$index]);
@ -140,10 +145,12 @@ class RangesFromBoundaryCalculator
$bits = str_pad($bits, $this->numBits, '0', STR_PAD_LEFT);
$bytes = array();
foreach (explode("\n", trim(chunk_split($bits, 8, "\n"))) as $byteBits) {
$bytes[] = bindec($byteBits);
$bytes[] = (int) bindec($byteBits);
}
$result = Factory::addressFromBytes($bytes);
/** @var AddressInterface $result */
return Factory::addressFromBytes($bytes);
return $result;
}
/**

View file

@ -67,6 +67,7 @@ class UnsignedIntegerMath
$value = str_pad($value, $valueLength + 8 - $remainderBits, '0', STR_PAD_LEFT);
}
$bytes = array_map('bindec', str_split($value, 8));
/** @var int[] $bytes */
return array_pad($bytes, -$numBytes, 0);
}
@ -86,14 +87,14 @@ class UnsignedIntegerMath
'',
array_map(
function ($octalDigit) {
return str_pad(decbin(octdec($octalDigit)), 3, '0', STR_PAD_LEFT);
return str_pad(decbin((int) octdec($octalDigit)), 3, '0', STR_PAD_LEFT);
},
str_split($value, 1)
)
);
$bits = ltrim($bits, '0');
return $bits === '' ? array_fill(0, $numBytes, 0) : static::getBytesFromBits($bits, $numBytes);
return $bits === '' ? array_fill(0, $numBytes, 0) : self::getBytesFromBits($bits, $numBytes);
}
/**
@ -165,6 +166,7 @@ class UnsignedIntegerMath
}
$value = str_pad($value, $valueLength + $valueLength % 2, '0', STR_PAD_LEFT);
$bytes = array_map('hexdec', str_split($value, 2));
/** @var int[] $bytes */
return array_pad($bytes, -$numBytes, 0);
}

View file

@ -60,7 +60,7 @@ final class Php80
public static function get_resource_id($res): int
{
if (!\is_resource($res) && null === @get_resource_type($res)) {
throw new \TypeError(sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
throw new \TypeError(\sprintf('Argument 1 passed to get_resource_id() must be of the type resource, %s given', get_debug_type($res)));
}
return (int) $res;

View file

@ -29,7 +29,7 @@ class PhpToken implements \Stringable
public $text;
/**
* @var int
* @var -1|positive-int
*/
public $line;
@ -38,6 +38,9 @@ class PhpToken implements \Stringable
*/
public $pos;
/**
* @param -1|positive-int $line
*/
public function __construct(int $id, string $text, int $line = -1, int $position = -1)
{
$this->id = $id;
@ -80,7 +83,7 @@ class PhpToken implements \Stringable
}
/**
* @return static[]
* @return list<static>
*/
public static function tokenize(string $code, int $flags = 0): array
{