diff --git a/core/lib/Drupal/Component/Utility/Number.php b/core/lib/Drupal/Component/Utility/Number.php index b4340dd0ce6159a0a037269c380ad9126a7ef62b..38a524b5aa36dab0c7bf3d196a214bbfdc992e00 100644 --- a/core/lib/Drupal/Component/Utility/Number.php +++ b/core/lib/Drupal/Component/Utility/Number.php @@ -57,4 +57,47 @@ public static function validStep($value, $step, $offset = 0.0) { return $computed_acceptable_error >= $remainder || $remainder >= ($step - $computed_acceptable_error); } + /** + * Generates a sorting code from an integer. + * + * Consists of a leading character indicating length, followed by N digits + * with a numerical value in base 36 (alphadecimal). These codes can be sorted + * as strings without altering numerical order. + * + * It goes: + * 00, 01, 02, ..., 0y, 0z, + * 110, 111, ... , 1zy, 1zz, + * 2100, 2101, ..., 2zzy, 2zzz, + * 31000, 31001, ... + * + * @param int $i + * The integer value to convert. + * + * @return string + * The alpha decimal value. + * + * @see \Drupal\Component\Utility\Number::alphadecimalToInt + */ + public static function intToAlphadecimal($i = 0) { + $num = base_convert((int) $i, 10, 36); + $length = strlen($num); + + return chr($length + ord('0') - 1) . $num; + } + + /** + * Decodes a sorting code back to an integer. + * + * @param string $string + * The alpha decimal value to convert + * + * @return int + * The integer value. + * + * @see \Drupal\Component\Utility\Number::intToAlphadecimal + */ + public static function alphadecimalToInt($string = '00') { + return (int) base_convert(substr($string, 1), 36, 10); + } + } diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 3e971117e34a948066427706a666c785cc36d08c..0fce7ec10bce49e19e4ce8292724a37b9de37d03 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -10,6 +10,7 @@ * book page, user etc. */ +use Drupal\Component\Utility\Number; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityChangedInterface; use Drupal\comment\CommentInterface; @@ -1590,21 +1591,20 @@ function _comment_per_page() { * 110, 111, ... , 1zy, 1zz, * 2100, 2101, ..., 2zzy, 2zzz, * 31000, 31001, ... + * + * @deprecated Use \Drupal\Component\Utility\Number::intToAlphadecimal() instead. */ function comment_int_to_alphadecimal($i = 0) { - $num = base_convert((int) $i, 10, 36); - $length = strlen($num); - - return chr($length + ord('0') - 1) . $num; + return Number::alphadecimalToInt($i); } /** * Decodes a sorting code back to an integer. * - * @see comment_int_to_alphadecimal() + * @deprecated Use \Drupal\Component\Utility\Number::alphadecimalToInt() instead. */ function comment_alphadecimal_to_int($c = '00') { - return base_convert(substr($c, 1), 36, 10); + return Number::alphadecimalToInt($c); } /** diff --git a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php index 62c9e56c9a630b5061a099084854e72ecd73f7ad..3c8b696cc6e9425655e7eceb9c02cc450716bc4b 100644 --- a/core/modules/comment/lib/Drupal/comment/Entity/Comment.php +++ b/core/modules/comment/lib/Drupal/comment/Entity/Comment.php @@ -7,6 +7,7 @@ namespace Drupal\comment\Entity; +use Drupal\Component\Utility\Number; use Drupal\Core\Entity\ContentEntityBase; use Drupal\comment\CommentInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; @@ -245,7 +246,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { $max = rtrim($max, '/'); // We need to get the value at the correct depth. $parts = explode('.', $max); - $n = comment_alphadecimal_to_int($parts[0]); + $n = Number::alphadecimalToInt($parts[0]); $prefix = ''; } else { @@ -272,14 +273,14 @@ public function preSave(EntityStorageControllerInterface $storage_controller) { // Get the value at the correct depth. $parts = explode('.', $max); $parent_depth = count(explode('.', $parent->thread->value)); - $n = comment_alphadecimal_to_int($parts[$parent_depth]); + $n = Number::alphadecimalToInt($parts[$parent_depth]); } } // Finally, build the thread field for this new comment. To avoid // race conditions, get a lock on the thread. If another process already // has the lock, just move to the next integer. do { - $thread = $prefix . comment_int_to_alphadecimal(++$n) . '/'; + $thread = $prefix . Number::intToAlphadecimal(++$n) . '/'; $lock_name = "comment:{$this->entity_id->value}:$thread"; } while (!\Drupal::lock()->acquire($lock_name)); $this->threadLock = $lock_name; diff --git a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php index 307a9b54bd702c7cd07c8477c94388ecc2b1c71e..48cc862cc854793041ebd36e9f1d8c216574da94 100644 --- a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php +++ b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php @@ -37,7 +37,7 @@ public function testLocks() { $container->register('request', 'Symfony\Component\HttpFoundation\Request'); $lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface'); $cid = 2; - $lock_name = "comment:$cid:./"; + $lock_name = "comment:$cid:01.00/"; $lock->expects($this->at(0)) ->method('acquire') ->with($lock_name, 30) @@ -65,9 +65,10 @@ public function testLocks() { $comment->status->value = 1; $comment->entity_id->value = $cid; $comment->uid->target_id = 3; + // Parent comment is the first in thread. $comment->pid->target_id = 42; $comment->pid->entity = new \stdClass(); - $comment->pid->entity->thread = (object) array('value' => ''); + $comment->pid->entity->thread = (object) array('value' => '01/'); $storage_controller = $this->getMock('Drupal\comment\CommentStorageControllerInterface'); $comment->preSave($storage_controller); $comment->postSave($storage_controller); @@ -85,9 +86,6 @@ protected function tearDown() { } } namespace { -if (!function_exists('comment_int_to_alphadecimal')) { - function comment_int_to_alphadecimal() {} -} if (!function_exists('module_invoke_all')) { function module_invoke_all() {} } diff --git a/core/tests/Drupal/Tests/Component/Utility/NumberAlphadecimalTest.php b/core/tests/Drupal/Tests/Component/Utility/NumberAlphadecimalTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1c9d8362d96e40e37b97e4e2053e47d8e5b0438 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Utility/NumberAlphadecimalTest.php @@ -0,0 +1,70 @@ +<?php + +/** + * @file + * Contains \Drupal\Tests\Component\Utility\NumberAlphadecimalTest. + */ + +namespace Drupal\Tests\Component\Utility; + +use Drupal\Component\Utility\Number; +use Drupal\Tests\UnitTestCase; + +/** + * Unit tests for the number alphadecimal functions. + * + * @see \Drupal\Component\Utility\Number + * + * @group Drupal + */ +class NumberAlphadecimalTest extends UnitTestCase { + + /** + * {@inheritdoc} + */ + public static function getInfo() { + return array( + 'name' => 'Number alphadecimal functions', + 'description' => 'Tests alphadecimal number conversion functions.', + 'group' => 'Common', + ); + } + + /** + * Tests the alphadecimal conversion functions. + * + * @param int $value + * The integer value. + * @param string $expected + * The expected alphadecimal value. + * + * @dataProvider providerTestConversions + */ + public function testConversions($value, $expected) { + $this->assertSame(Number::intToAlphadecimal($value), $expected); + $this->assertSame($value, Number::alphadecimalToInt($expected)); + } + + /** + * Data provider for testConversions(). + * + * @see testConversions() + * + * @return array + * An array containing: + * - The integer value. + * - The alphadecimal value. + */ + public function providerTestConversions() { + return array( + array(0, '00'), + array(1, '01'), + array(10, '0a'), + array(20, '0k'), + array(35, '0z'), + array(36, '110'), + array(100, '12s'), + ); + } + +}