Merge pull request #1526 from PrivateBin/pass-by-reference

Pass by reference & drop ctype
This commit is contained in:
El RIDO 2025-03-17 06:52:48 +01:00 committed by GitHub
commit bac849d98a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 391 additions and 537 deletions

View File

@ -2,6 +2,8 @@
## 1.7.7 (not yet released)
* ADDED: Switching templates using the web ui (#1501)
* CHANGED: Passing large data structures by reference to reduce memory consumption (#858)
* CHANGED: Removed use of ctype functions and polyfill library for ctype
* CHANGED: Upgrading libraries to: ip-lib 1.20.0
## 1.7.6 (2025-02-01)

View File

@ -27,8 +27,7 @@
"php": "^7.3 || ^8.0",
"jdenticon/jdenticon": "1.0.2",
"mlocati/ip-lib": "1.20.0",
"symfony/polyfill-ctype": "^1.31",
"symfony/polyfill-php80": "^1.31",
"symfony/polyfill-php80": "1.31.0",
"yzalis/identicon": "2.0.0"
},
"suggest" : {

93
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b6e6a0433b36e6c81fcb3cb58b22a269",
"content-hash": "6c7e6dea19e8bfd5641b220cb68c4b65",
"packages": [
{
"name": "jdenticon/jdenticon",
@ -126,85 +126,6 @@
],
"time": "2025-02-04T17:30:58+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.31.0",
@ -416,16 +337,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.12.1",
"version": "1.13.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
"shasum": ""
},
"require": {
@ -464,7 +385,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
},
"funding": [
{
@ -472,7 +393,7 @@
"type": "tidelift"
}
],
"time": "2024-11-08T17:47:46+00:00"
"time": "2025-02-12T12:17:51+00:00"
},
{
"name": "nikic/php-parser",

View File

@ -22,7 +22,6 @@ for more information.
### Minimal Requirements
- PHP version 7.3 or above
- ctype extension
- GD extension (when using identicon or vizhash icons, jdenticon works without it)
- zlib extension
- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php)

View File

@ -34,7 +34,7 @@ abstract class AbstractData
* @param array $paste
* @return bool
*/
abstract public function create($pasteid, array $paste);
abstract public function create($pasteid, array &$paste);
/**
* Read a paste.
@ -72,7 +72,7 @@ abstract class AbstractData
* @param array $comment
* @return bool
*/
abstract public function createComment($pasteid, $parentid, $commentid, array $comment);
abstract public function createComment($pasteid, $parentid, $commentid, array &$comment);
/**
* Read all comments of paste.
@ -199,7 +199,7 @@ abstract class AbstractData
* @param array $paste
* @return array
*/
protected static function upgradePreV1Format(array $paste)
protected static function upgradePreV1Format(array &$paste)
{
if (array_key_exists('attachment', $paste['meta'])) {
$paste['attachment'] = $paste['meta']['attachment'];

View File

@ -140,7 +140,7 @@ class Database extends AbstractData
* @param array $paste
* @return bool
*/
public function create($pasteid, array $paste)
public function create($pasteid, array &$paste)
{
$expire_date = 0;
$opendiscussion = $burnafterreading = false;
@ -297,14 +297,18 @@ class Database extends AbstractData
* @param array $comment
* @return bool
*/
public function createComment($pasteid, $parentid, $commentid, array $comment)
public function createComment($pasteid, $parentid, $commentid, array &$comment)
{
if (array_key_exists('data', $comment)) {
$version = 1;
$data = $comment['data'];
} else {
$version = 2;
$data = Json::encode($comment);
try {
$version = 2;
$data = Json::encode($comment);
} catch (Exception $e) {
return false;
}
}
list($createdKey, $iconKey) = $this->_getVersionedKeys($version);
$meta = $comment['meta'];

View File

@ -85,7 +85,7 @@ class Filesystem extends AbstractData
* @param array $paste
* @return bool
*/
public function create($pasteid, array $paste)
public function create($pasteid, array &$paste)
{
$storagedir = $this->_dataid2path($pasteid);
$file = $storagedir . $pasteid . '.php';
@ -188,7 +188,7 @@ class Filesystem extends AbstractData
* @param array $comment
* @return bool
*/
public function createComment($pasteid, $parentid, $commentid, array $comment)
public function createComment($pasteid, $parentid, $commentid, array &$comment)
{
$storagedir = $this->_dataid2discussionpath($pasteid);
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php';
@ -343,12 +343,11 @@ class Filesystem extends AbstractData
*/
private function _get($filename)
{
return Json::decode(
substr(
file_get_contents($filename),
strlen(self::PROTECTION_LINE . PHP_EOL)
)
$data = substr(
file_get_contents($filename),
strlen(self::PROTECTION_LINE . PHP_EOL)
);
return Json::decode($data);
}
/**

View File

@ -105,7 +105,7 @@ class GoogleCloudStorage extends AbstractData
* @param $payload array to store
* @return bool true if successful, otherwise false.
*/
private function _upload($key, $payload)
private function _upload($key, &$payload)
{
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
@ -136,7 +136,7 @@ class GoogleCloudStorage extends AbstractData
/**
* @inheritDoc
*/
public function create($pasteid, array $paste)
public function create($pasteid, array &$paste)
{
if ($this->exists($pasteid)) {
return false;
@ -201,7 +201,7 @@ class GoogleCloudStorage extends AbstractData
/**
* @inheritDoc
*/
public function createComment($pasteid, $parentid, $commentid, array $comment)
public function createComment($pasteid, $parentid, $commentid, array &$comment)
{
if ($this->existsComment($pasteid, $parentid, $commentid)) {
return false;
@ -219,7 +219,8 @@ class GoogleCloudStorage extends AbstractData
$prefix = $this->_getKey($pasteid) . '/discussion/';
try {
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
$comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
$data = $this->_bucket->object($key->name())->downloadAsString();
$comment = Json::decode($data);
$comment['id'] = basename($key->name());
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
$comments[$slot] = $comment;

View File

@ -165,7 +165,7 @@ class S3Storage extends AbstractData
* @param $payload array to store
* @return bool true if successful, otherwise false.
*/
private function _upload($key, $payload)
private function _upload($key, &$payload)
{
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
@ -191,7 +191,7 @@ class S3Storage extends AbstractData
/**
* @inheritDoc
*/
public function create($pasteid, array $paste)
public function create($pasteid, array &$paste)
{
if ($this->exists($pasteid)) {
return false;
@ -263,7 +263,7 @@ class S3Storage extends AbstractData
/**
* @inheritDoc
*/
public function createComment($pasteid, $parentid, $commentid, array $comment)
public function createComment($pasteid, $parentid, $commentid, array &$comment)
{
if ($this->existsComment($pasteid, $parentid, $commentid)) {
return false;

View File

@ -29,7 +29,7 @@ class FormatV2
* @param bool $isComment
* @return bool
*/
public static function isValid($message, $isComment = false)
public static function isValid(&$message, $isComment = false)
{
$required_keys = array('adata', 'v', 'ct');
if ($isComment) {

View File

@ -183,9 +183,12 @@ class I18n
// load translations
self::$_language = $match;
self::$_translations = ($match == 'en') ? array() : Json::decode(
file_get_contents(self::_getPath($match . '.json'))
);
if ($match == 'en') {
self::$_translations = array();
} else {
$data = file_get_contents(self::_getPath($match . '.json'));
self::$_translations = Json::decode($data);
}
}
/**
@ -273,7 +276,8 @@ class I18n
{
$file = self::_getPath('languages.json');
if (count(self::$_languageLabels) == 0 && is_readable($file)) {
self::$_languageLabels = Json::decode(file_get_contents($file));
$data = file_get_contents($file);
self::$_languageLabels = Json::decode($data);
}
if (count($languages) == 0) {
return self::$_languageLabels;

View File

@ -29,7 +29,7 @@ class Json
* @throws Exception
* @return string
*/
public static function encode($input)
public static function encode(&$input)
{
$jsonString = json_encode($input);
self::_detectError();
@ -45,7 +45,7 @@ class Json
* @throws Exception
* @return mixed
*/
public static function decode($input)
public static function decode(&$input)
{
$output = json_decode($input, true);
self::_detectError();

View File

@ -100,9 +100,9 @@ abstract class AbstractModel
* @param array $data
* @throws Exception
*/
public function setData(array $data)
public function setData(array &$data)
{
$data = $this->_sanitize($data);
$this->_sanitize($data);
$this->_validate($data);
$this->_data = $data;
@ -155,7 +155,7 @@ abstract class AbstractModel
*/
public static function isValidId($id)
{
return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
return (bool) preg_match('#\A[a-f0-9]{16}\z#', (string) $id);
}
/**
@ -163,9 +163,8 @@ abstract class AbstractModel
*
* @access protected
* @param array $data
* @return array
*/
abstract protected function _sanitize(array $data);
abstract protected function _sanitize(array &$data);
/**
* Validate data.
@ -174,7 +173,7 @@ abstract class AbstractModel
* @param array $data
* @throws Exception
*/
protected function _validate(array $data)
protected function _validate(array &$data)
{
}
}

View File

@ -104,7 +104,7 @@ class Comment extends AbstractModel
* @param Paste $paste
* @throws Exception
*/
public function setPaste(Paste $paste)
public function setPaste(Paste &$paste)
{
$this->_paste = $paste;
$this->_data['pasteid'] = $paste->getId();
@ -155,9 +155,8 @@ class Comment extends AbstractModel
*
* @access protected
* @param array $data
* @return array
*/
protected function _sanitize(array $data)
protected function _sanitize(array &$data)
{
// we generate an icon based on a SHA512 HMAC of the users IP, if configured
$icon = $this->_conf->getKey('icon');
@ -190,6 +189,5 @@ class Comment extends AbstractModel
$data['meta']['icon'] = $pngdata;
}
}
return $data;
}
}

View File

@ -219,11 +219,10 @@ class Paste extends AbstractModel
*
* @access protected
* @param array $data
* @return array
*/
protected function _sanitize(array $data)
protected function _sanitize(array &$data)
{
$expiration = $data['meta']['expire'];
$expiration = $data['meta']['expire'] ?? 0;
unset($data['meta']['expire']);
$expire_options = $this->_conf->getSection('expire_options');
if (array_key_exists($expiration, $expire_options)) {
@ -235,7 +234,6 @@ class Paste extends AbstractModel
if ($expire > 0) {
$data['meta']['expire_date'] = time() + $expire;
}
return $data;
}
/**
@ -245,7 +243,7 @@ class Paste extends AbstractModel
* @param array $data
* @throws Exception
*/
protected function _validate(array $data)
protected function _validate(array &$data)
{
// reject invalid or disabled formatters
if (!array_key_exists($data['adata'][1], $this->_conf->getSection('formatter_options'))) {

View File

@ -12,6 +12,7 @@
namespace PrivateBin;
use Exception;
use PrivateBin\Model\Paste;
/**
* Request
@ -84,7 +85,7 @@ class Request
foreach ($_GET as $key => $value) {
// only return if value is empty and key is 16 hex chars
$key = (string) $key;
if (($value === '') && strlen($key) === 16 && ctype_xdigit($key)) {
if (empty($value) && Paste::isValidId($key)) {
return $key;
}
}
@ -110,9 +111,8 @@ class Request
// it might be a creation or a deletion, the latter is detected below
$this->_operation = 'create';
try {
$this->_params = Json::decode(
file_get_contents(self::$_inputStream)
);
$data = file_get_contents(self::$_inputStream);
$this->_params = Json::decode($data);
} catch (Exception $e) {
// ignore error, $this->_params will remain empty
}

View File

@ -70,7 +70,7 @@ class View
$sri = array_key_exists($file, $this->_variables['SRI']) ?
' integrity="' . $this->_variables['SRI'][$file] . '"' : '';
// if the file isn't versioned (ends in a digit), add our own version
$cacheBuster = ctype_digit(substr($file, -4, 1)) ?
$cacheBuster = (bool) preg_match('#[0-9]\.js$#', (string) $file) ?
'' : '?' . rawurlencode($this->_variables['VERSION']);
echo '<script ', $attributes,
' type="text/javascript" data-cfasync="false" src="', $file,

View File

@ -288,7 +288,8 @@ class ControllerTest extends TestCase
$options = parse_ini_file(CONF, true);
$options['traffic']['limit'] = 0;
Helper::createIniFile(CONF, $options);
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$paste = Helper::getPasteJson();
$file = tempnam(sys_get_temp_dir(), 'FOO');
file_put_contents($file, $paste);
@ -538,7 +539,8 @@ class ControllerTest extends TestCase
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
ob_start();
new Controller;
$content = ob_get_contents();
@ -564,7 +566,8 @@ class ControllerTest extends TestCase
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
ob_start();
new Controller;
$content = ob_get_contents();
@ -633,8 +636,10 @@ class ControllerTest extends TestCase
$options = parse_ini_file(CONF, true);
$options['traffic']['limit'] = 0;
Helper::createIniFile(CONF, $options);
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$comment = Helper::getComment();
$this->_data->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), $comment);
$this->assertTrue($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'comment exists before posting data');
$comment = Helper::getCommentJson();
$file = tempnam(sys_get_temp_dir(), 'FOO');
@ -819,7 +824,8 @@ class ControllerTest extends TestCase
*/
public function testDelete()
{
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
$paste = $this->_data->read(Helper::getPasteId());
$_GET['pasteid'] = Helper::getPasteId();
@ -841,7 +847,8 @@ class ControllerTest extends TestCase
*/
public function testDeleteInvalidId()
{
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$_GET['pasteid'] = 'foo';
$_GET['deletetoken'] = 'bar';
ob_start();
@ -879,7 +886,8 @@ class ControllerTest extends TestCase
*/
public function testDeleteInvalidToken()
{
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$_GET['pasteid'] = Helper::getPasteId();
$_GET['deletetoken'] = 'bar';
ob_start();
@ -899,7 +907,8 @@ class ControllerTest extends TestCase
*/
public function testDeleteInvalidBurnAfterReading()
{
$this->_data->create(Helper::getPasteId(), Helper::getPaste());
$paste = Helper::getPaste();
$this->_data->create(Helper::getPasteId(), $paste);
$this->assertTrue($this->_data->exists(Helper::getPasteId()), 'paste exists before deleting data');
$file = tempnam(sys_get_temp_dir(), 'FOO');
file_put_contents($file, json_encode(array(

View File

@ -60,18 +60,22 @@ class DatabaseTest extends TestCase
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
// storing comments
$comment1 = Helper::getComment(1);
$comment2 = Helper::getComment(2);
$meta1 = $comment1['meta'];
$meta2 = $comment2['meta'];
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment(1)) !== false, 'store v1 comment');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment1) !== false, 'store v1 comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment exists after storing it');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment(2)) !== false, 'store v2 comment');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), $comment2) !== false, 'store v2 comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment exists after storing it');
$comment1 = Helper::getComment(1);
$comment1['id'] = Helper::getCommentId();
$comment1['parentid'] = Helper::getPasteId();
$comment2 = Helper::getComment(2);
$comment1['meta'] = $meta1;
$comment2['id'] = Helper::getPasteId();
$comment2['parentid'] = Helper::getPasteId();
$comment2['meta'] = $meta2;
$this->assertEquals(
array(
$comment1['meta']['postdate'] => $comment1,
@ -120,7 +124,8 @@ class DatabaseTest extends TestCase
if (in_array($key, array('y', 'z'))) {
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
} elseif ($key === 'x') {
$this->assertTrue($this->_model->create($ids[$key], Helper::getPaste()), "store $key paste");
$data = Helper::getPaste();
$this->assertTrue($this->_model->create($ids[$key], $data), "store $key paste");
} else {
$this->assertTrue($this->_model->create($ids[$key], $expired), "store $key paste");
}
@ -137,6 +142,28 @@ class DatabaseTest extends TestCase
}
}
public function testErrorDetection()
{
$this->_model->delete(Helper::getPasteId());
$paste = Helper::getPaste(2, array('expire' => "Invalid UTF-8 sequence: \xB1\x31"));
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
}
public function testCommentErrorDetection()
{
$this->_model->delete(Helper::getPasteId());
$data = Helper::getPaste();
$comment = Helper::getComment(2, array('nickname' => "Invalid UTF-8 sequence: \xB1\x31"));
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
$this->assertTrue($this->_model->create(Helper::getPasteId(), $data), 'store new paste');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist');
}
public function testGetIbmInstance()
{
$this->expectException(PDOException::class);

View File

@ -45,11 +45,11 @@ class FilesystemTest extends TestCase
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
// storing comments
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice');
$comment = Helper::getComment();
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store the same comment twice');
$comment['id'] = Helper::getCommentId();
$comment['parentid'] = Helper::getPasteId();
$this->assertEquals(
@ -94,7 +94,8 @@ class FilesystemTest extends TestCase
if (in_array($key, array('x', 'y', 'z'))) {
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
} elseif ($key === 'x') {
$this->assertTrue($this->_model->create($ids[$key], Helper::getPaste()), "store $key paste");
$data = Helper::getPaste();
$this->assertTrue($this->_model->create($ids[$key], $data), "store $key paste");
} else {
$this->assertTrue($this->_model->create($ids[$key], $expired), "store $key paste");
}
@ -124,9 +125,10 @@ class FilesystemTest extends TestCase
public function testCommentErrorDetection()
{
$this->_model->delete(Helper::getPasteId());
$data = Helper::getPaste();
$comment = Helper::getComment(1, array('nickname' => "Invalid UTF-8 sequence: \xB1\x31"));
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
$this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
$this->assertTrue($this->_model->create(Helper::getPasteId(), $data), 'store new paste');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');

View File

@ -58,11 +58,11 @@ class GoogleCloudStorageTest extends TestCase
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
// storing comments
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice');
$comment = Helper::getComment();
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store the same comment twice');
$comment['id'] = Helper::getCommentId();
$comment['parentid'] = Helper::getPasteId();
$this->assertEquals(
@ -92,7 +92,8 @@ class GoogleCloudStorageTest extends TestCase
if (in_array($key, array('x', 'y', 'z'))) {
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
} elseif ($key === 'x') {
$this->assertTrue($this->_model->create($ids[$key], Helper::getPaste()), "store $key paste");
$data = Helper::getPaste();
$this->assertTrue($this->_model->create($ids[$key], $data), "store $key paste");
} else {
$this->assertTrue($this->_model->create($ids[$key], $expired), "store $key paste");
}
@ -121,9 +122,10 @@ class GoogleCloudStorageTest extends TestCase
public function testCommentErrorDetection()
{
$this->_model->delete(Helper::getPasteId());
$data = Helper::getPaste();
$comment = Helper::getComment(1, array('nickname' => "Invalid UTF-8 sequence: \xB1\x31"));
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
$this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
$this->assertTrue($this->_model->create(Helper::getPasteId(), $data), 'store new paste');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');

View File

@ -7,10 +7,11 @@ class FormatV2Test extends TestCase
{
public function testFormatV2ValidatorValidatesCorrectly()
{
$this->assertTrue(FormatV2::isValid(Helper::getPastePost()), 'valid format');
$this->assertTrue(FormatV2::isValid(Helper::getCommentPost(), true), 'valid format');
$paste = Helper::getPastePost();
$comment = Helper::getCommentPost();
$this->assertTrue(FormatV2::isValid($paste), 'valid format');
$this->assertTrue(FormatV2::isValid($comment, true), 'valid format');
$paste = Helper::getPastePost();
$paste['adata'][0][0] = '$';
$this->assertFalse(FormatV2::isValid($paste), 'invalid base64 encoding of iv');
@ -68,6 +69,7 @@ class FormatV2Test extends TestCase
$paste['adata'][0][7] = '!#@';
$this->assertFalse(FormatV2::isValid($paste), 'invalid compression');
$this->assertFalse(FormatV2::isValid(Helper::getPaste()), 'invalid meta key');
$paste = Helper::getPaste();
$this->assertFalse(FormatV2::isValid($paste), 'invalid meta key');
}
}

View File

@ -114,7 +114,8 @@ class JsonApiTest extends TestCase
*/
public function testDelete()
{
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
$data = Helper::getPaste();
$this->_model->create(Helper::getPasteId(), $data);
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
$paste = $this->_model->read(Helper::getPasteId());
$file = tempnam(sys_get_temp_dir(), 'FOO');
@ -141,7 +142,8 @@ class JsonApiTest extends TestCase
*/
public function testDeleteWithPost()
{
$this->_model->create(Helper::getPasteId(), Helper::getPaste());
$data = Helper::getPaste();
$this->_model->create(Helper::getPasteId(), $data);
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
$paste = $this->_model->read(Helper::getPasteId());
$file = tempnam(sys_get_temp_dir(), 'FOO');
@ -166,7 +168,7 @@ class JsonApiTest extends TestCase
*/
public function testRead()
{
$paste = Helper::getPaste();
$paste = Helper::getPaste();
$this->_model->create(Helper::getPasteId(), $paste);
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
$_GET[Helper::getPasteId()] = '';

View File

@ -59,8 +59,10 @@ class MigrateTest extends TestCase
$this->_model_2->delete(Helper::getPasteId());
// storing paste & comment
$this->_model_1->create(Helper::getPasteId(), Helper::getPaste());
$this->_model_1->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment());
$data = Helper::getPaste();
$this->_model_1->create(Helper::getPasteId(), $data);
$data = Helper::getComment();
$this->_model_1->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $data);
// migrate files to database
$output = null;

View File

@ -163,7 +163,8 @@ class ModelTest extends TestCase
$this->_conf->getSection('model_options')
)
);
$comment->setPaste($this->_model->getPaste(Helper::getPasteId()));
$paste = $this->_model->getPaste(Helper::getPasteId());
$comment->setPaste($paste);
$this->assertEquals(Helper::getPasteId(), $comment->getParentId(), 'comment parent ID gets initialized to paste ID');
}
@ -316,7 +317,11 @@ class ModelTest extends TestCase
public function testPasteIdValidation()
{
$this->assertTrue(Paste::isValidId('a242ab7bdfb2581a'), 'valid paste id');
$this->assertFalse(Paste::isValidId('foo'), 'invalid hex values');
$this->assertFalse(Paste::isValidId('foo'), 'invalid hex values & length');
$this->assertFalse(Paste::isValidId('f00'), 'invalid length');
$this->assertFalse(Paste::isValidId('foo bar baz quux'), 'invalid hex values');
$this->assertFalse(Paste::isValidId("\n01234567feedcafe"), 'invalid line breaks');
$this->assertFalse(Paste::isValidId("deadbeef01234567\n"), 'invalid line breaks');
$this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack');
}

View File

@ -87,11 +87,11 @@ return array(
'PrivateBin\\Persistence\\ServerSalt' => $baseDir . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => $baseDir . '/lib/Persistence/TrafficLimiter.php',
'PrivateBin\\Request' => $baseDir . '/lib/Request.php',
'PrivateBin\\TemplateSwitcher' => $baseDir . '/lib/TemplateSwitcher.php',
'PrivateBin\\View' => $baseDir . '/lib/View.php',
'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
'PrivateBin\\YourlsProxy' => $baseDir . '/lib/YourlsProxy.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Polyfill\\Ctype\\Ctype' => $vendorDir . '/symfony/polyfill-ctype/Ctype.php',
'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',

View File

@ -6,6 +6,5 @@ $vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
);

View File

@ -7,7 +7,6 @@ $baseDir = dirname($vendorDir);
return array(
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'PrivateBin\\' => array($baseDir . '/lib'),
'Jdenticon\\' => array($vendorDir . '/jdenticon/jdenticon/src'),
'Identicon\\' => array($vendorDir . '/yzalis/identicon/src/Identicon'),

View File

@ -7,7 +7,6 @@ namespace Composer\Autoload;
class ComposerStaticInitDontChange
{
public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
);
@ -15,7 +14,6 @@ class ComposerStaticInitDontChange
'S' =>
array (
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Ctype\\' => 23,
),
'P' =>
array (
@ -37,10 +35,6 @@ class ComposerStaticInitDontChange
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Ctype\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
),
'PrivateBin\\' =>
array (
0 => __DIR__ . '/../..' . '/lib',
@ -141,11 +135,11 @@ class ComposerStaticInitDontChange
'PrivateBin\\Persistence\\ServerSalt' => __DIR__ . '/../..' . '/lib/Persistence/ServerSalt.php',
'PrivateBin\\Persistence\\TrafficLimiter' => __DIR__ . '/../..' . '/lib/Persistence/TrafficLimiter.php',
'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php',
'PrivateBin\\TemplateSwitcher' => __DIR__ . '/../..' . '/lib/TemplateSwitcher.php',
'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
'PrivateBin\\YourlsProxy' => __DIR__ . '/../..' . '/lib/YourlsProxy.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Polyfill\\Ctype\\Ctype' => __DIR__ . '/..' . '/symfony/polyfill-ctype/Ctype.php',
'Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/PhpToken.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',

View File

@ -3,7 +3,7 @@
'name' => 'privatebin/privatebin',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8b7ccb0fd4165f14ce7eab19c9213fb5c3ea7b80',
'reference' => '7825471d70c39baf6042c52a453c8fe705d9ed75',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -20,9 +20,9 @@
'dev_requirement' => false,
),
'mlocati/ip-lib' => array(
'pretty_version' => '1.18.1',
'version' => '1.18.1.0',
'reference' => '08bb43b4949069c543ebdf099a6b2c322d0172ab',
'pretty_version' => '1.20.0',
'version' => '1.20.0.0',
'reference' => 'fd45fc3bf08ed6c7e665e2e70562082ac954afd4',
'type' => 'library',
'install_path' => __DIR__ . '/../mlocati/ip-lib',
'aliases' => array(),
@ -31,21 +31,12 @@
'privatebin/privatebin' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '8b7ccb0fd4165f14ce7eab19c9213fb5c3ea7b80',
'reference' => '7825471d70c39baf6042c52a453c8fe705d9ed75',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',
'reference' => 'a3cc8b044a6ea513310cbd48ef7333b384945638',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.31.0',
'version' => '1.31.0.0',

View File

@ -153,4 +153,30 @@ interface AddressInterface
* @example for IPv6 it returns something like x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa
*/
public function getReverseDNSLookupName();
/**
* Shift the bits of the address, padding with zeroes.
*
* @param int $bits If negative the bits will be shifted left, if positive the bits will be shifted right
*
* @return self
*
* @since 1.20.0
*
* @example shifting by 1 127.0.0.1 you'll have 63.128.0.0
* @example shifting by -1 127.0.0.1 you'll have 254.0.0.2
*/
public function shift($bits);
/**
* Create a new IP address by adding to this address another address.
*
* @return self|null returns NULL if $other is not compatible with this address, or if it generates an invalid address
*
* @since 1.20.0
*
* @example adding 0.0.0.10 to 127.0.0.1 generates the IP 127.0.0.11
* @example adding 255.0.0.10 to 127.0.0.1 generates NULL
*/
public function add(AddressInterface $other);
}

View File

@ -512,4 +512,62 @@ class IPv4 implements AddressInterface
array_reverse($this->getBytes())
) . '.in-addr.arpa';
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::shift()
*/
public function shift($bits)
{
$bits = (int) $bits;
if ($bits === 0) {
return $this;
}
$absBits = abs($bits);
if ($absBits >= 32) {
return new self('0.0.0.0');
}
$pad = str_repeat('0', $absBits);
$paddedBits = $this->getBits();
if ($bits > 0) {
$paddedBits = $pad . substr($paddedBits, 0, -$bits);
} else {
$paddedBits = substr($paddedBits, $absBits) . $pad;
}
$bytes = array_map('bindec', str_split($paddedBits, 8));
return new static(implode('.', $bytes));
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::add()
*/
public function add(AddressInterface $other)
{
if (!$other instanceof self) {
return null;
}
$myBytes = $this->getBytes();
$otherBytes = $other->getBytes();
$sum = array_fill(0, 4, 0);
$carry = 0;
for ($index = 3; $index >= 0; $index--) {
$byte = $myBytes[$index] + $otherBytes[$index] + $carry;
if ($byte > 0xFF) {
$carry = $byte >> 8;
$byte &= 0xFF;
} else {
$carry = 0;
}
$sum[$index] = $byte;
}
if ($carry !== 0) {
return null;
}
return new static(implode('.', $sum));
}
}

View File

@ -605,4 +605,62 @@ class IPv6 implements AddressInterface
array_reverse(str_split(str_replace(':', '', $this->toString(true)), 1))
) . '.ip6.arpa';
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::shift()
*/
public function shift($bits)
{
$bits = (int) $bits;
if ($bits === 0) {
return $this;
}
$absBits = abs($bits);
if ($absBits >= 128) {
return new self('0000:0000:0000:0000:0000:0000:0000:0000');
}
$pad = str_repeat('0', $absBits);
$paddedBits = $this->getBits();
if ($bits > 0) {
$paddedBits = $pad . substr($paddedBits, 0, -$bits);
} else {
$paddedBits = substr($paddedBits, $absBits) . $pad;
}
$bytes = array_map('bindec', str_split($paddedBits, 16));
return static::fromWords($bytes);
}
/**
* {@inheritdoc}
*
* @see \IPLib\Address\AddressInterface::add()
*/
public function add(AddressInterface $other)
{
if (!$other instanceof self) {
return null;
}
$myWords = $this->getWords();
$otherWords = $other->getWords();
$sum = array_fill(0, 7, 0);
$carry = 0;
for ($index = 7; $index >= 0; $index--) {
$word = $myWords[$index] + $otherWords[$index] + $carry;
if ($word > 0xFFFF) {
$carry = $word >> 16;
$word &= 0xFFFF;
} else {
$carry = 0;
}
$sum[$index] = $word;
}
if ($carry !== 0) {
return null;
}
return static::fromWords($sum);
}
}

View File

@ -7,6 +7,7 @@ use IPLib\Address\IPv4;
use IPLib\Address\IPv6;
use IPLib\Address\Type as AddressType;
use IPLib\Factory;
use OutOfBoundsException;
/**
* Base class for range classes.
@ -122,4 +123,48 @@ abstract class AbstractRange implements RangeInterface
return $result;
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::split()
*/
public function split($networkPrefix, $forceSubnet = false)
{
$networkPrefix = (int) $networkPrefix;
$myNetworkPrefix = $this->getNetworkPrefix();
if ($networkPrefix === $myNetworkPrefix) {
return array(
$forceSubnet ? $this->asSubnet() : $this,
);
}
if ($networkPrefix < $myNetworkPrefix) {
throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be smaller than the network prefix of the range ({$myNetworkPrefix})");
}
$startIp = $this->getStartAddress();
$maxPrefix = $startIp::getNumberOfBits();
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));
}
$delta = $one->shift($networkPrefix - $maxPrefix);
$result = array();
while (true) {
$range = Subnet::parseString("{$startIp}/{$networkPrefix}");
if (!$forceSubnet && $this instanceof Pattern) {
$range = $range->asPattern() ?: $range;
}
$result[] = $range;
$startIp = $startIp->add($delta);
if ($startIp === null || !$this->contains($startIp)) {
break;
}
}
return $result;
}
}

View File

@ -308,9 +308,11 @@ class Pattern extends AbstractRange
}
/**
* @return float|int
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
*/
private function getNetworkPrefix()
public function getNetworkPrefix()
{
switch ($this->getAddressType()) {
case AddressType::T_IPv4:

View File

@ -157,4 +157,30 @@ interface RangeInterface
* @since 1.16.0
*/
public function getSize();
/**
* Get the "network prefix", that is how many bits of the address are dedicated to the network portion.
*
* @return int
*
* @since 1.19.0
*
* @example for 10.0.0.0/24 it's 24
* @example for 10.0.0.* it's 24
*/
public function getNetworkPrefix();
/**
* Split the range into smaller ranges.
*
* @param int $networkPrefix
* @param bool $forceSubnet set to true to always have ranges in "subnet format" (ie 1.2.3.4/5), to false to try to keep the original format if possible (that is, pattern to pattern, single to single)
*
* @throws \OutOfBoundsException if $networkPrefix is not valid
*
* @return \IPLib\Range\RangeInterface[]
*
* @since 1.19.0
*/
public function split($networkPrefix, $forceSubnet = false);
}

View File

@ -190,12 +190,7 @@ class Single extends AbstractRange
*/
public function asSubnet()
{
$networkPrefixes = array(
AddressType::T_IPv4 => 32,
AddressType::T_IPv6 => 128,
);
return new Subnet($this->address, $this->address, $networkPrefixes[$this->address->getAddressType()]);
return new Subnet($this->address, $this->address, $this->getNetworkPrefix());
}
/**
@ -241,4 +236,19 @@ class Single extends AbstractRange
{
return 1;
}
/**
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
*/
public function getNetworkPrefix()
{
switch ($this->getAddressType()) {
case AddressType::T_IPv4:
return 32;
case AddressType::T_IPv6:
return 128;
}
}
}

View File

@ -261,10 +261,9 @@ class Subnet extends AbstractRange
}
/**
* Get subnet prefix.
*
* @return int
* {@inheritdoc}
*
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
* @since 1.7.0
*/
public function getNetworkPrefix()

View File

@ -1,232 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Ctype;
/**
* Ctype implementation through regex.
*
* @internal
*
* @author Gert de Pagter <BackEndTea@gmail.com>
*/
final class Ctype
{
/**
* Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
*
* @see https://php.net/ctype-alnum
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_alnum($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
}
/**
* Returns TRUE if every character in text is a letter, FALSE otherwise.
*
* @see https://php.net/ctype-alpha
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_alpha($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
}
/**
* Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
*
* @see https://php.net/ctype-cntrl
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_cntrl($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
}
/**
* Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
*
* @see https://php.net/ctype-digit
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_digit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
}
/**
* Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
*
* @see https://php.net/ctype-graph
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_graph($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
}
/**
* Returns TRUE if every character in text is a lowercase letter.
*
* @see https://php.net/ctype-lower
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_lower($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
}
/**
* Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
*
* @see https://php.net/ctype-print
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_print($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
}
/**
* Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
*
* @see https://php.net/ctype-punct
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_punct($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
}
/**
* Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
*
* @see https://php.net/ctype-space
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_space($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
}
/**
* Returns TRUE if every character in text is an uppercase letter.
*
* @see https://php.net/ctype-upper
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_upper($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
}
/**
* Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
*
* @see https://php.net/ctype-xdigit
*
* @param mixed $text
*
* @return bool
*/
public static function ctype_xdigit($text)
{
$text = self::convert_int_to_char_for_ctype($text, __FUNCTION__);
return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
}
/**
* Converts integers to their char versions according to normal ctype behaviour, if needed.
*
* If an integer between -128 and 255 inclusive is provided,
* it is interpreted as the ASCII value of a single character
* (negative values have 256 added in order to allow characters in the Extended ASCII range).
* Any other integer is interpreted as a string containing the decimal digits of the integer.
*
* @param mixed $int
* @param string $function
*
* @return mixed
*/
private static function convert_int_to_char_for_ctype($int, $function)
{
if (!\is_int($int)) {
return $int;
}
if ($int < -128 || $int > 255) {
return (string) $int;
}
if (\PHP_VERSION_ID >= 80100) {
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
}
if ($int < 0) {
$int += 256;
}
return \chr($int);
}
}

View File

@ -1,50 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Ctype as p;
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('ctype_alnum')) {
function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print($text) { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space($text) { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
}

View File

@ -1,46 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Ctype as p;
if (!function_exists('ctype_alnum')) {
function ctype_alnum(mixed $text): bool { return p\Ctype::ctype_alnum($text); }
}
if (!function_exists('ctype_alpha')) {
function ctype_alpha(mixed $text): bool { return p\Ctype::ctype_alpha($text); }
}
if (!function_exists('ctype_cntrl')) {
function ctype_cntrl(mixed $text): bool { return p\Ctype::ctype_cntrl($text); }
}
if (!function_exists('ctype_digit')) {
function ctype_digit(mixed $text): bool { return p\Ctype::ctype_digit($text); }
}
if (!function_exists('ctype_graph')) {
function ctype_graph(mixed $text): bool { return p\Ctype::ctype_graph($text); }
}
if (!function_exists('ctype_lower')) {
function ctype_lower(mixed $text): bool { return p\Ctype::ctype_lower($text); }
}
if (!function_exists('ctype_print')) {
function ctype_print(mixed $text): bool { return p\Ctype::ctype_print($text); }
}
if (!function_exists('ctype_punct')) {
function ctype_punct(mixed $text): bool { return p\Ctype::ctype_punct($text); }
}
if (!function_exists('ctype_space')) {
function ctype_space(mixed $text): bool { return p\Ctype::ctype_space($text); }
}
if (!function_exists('ctype_upper')) {
function ctype_upper(mixed $text): bool { return p\Ctype::ctype_upper($text); }
}
if (!function_exists('ctype_xdigit')) {
function ctype_xdigit(mixed $text): bool { return p\Ctype::ctype_xdigit($text); }
}