mirror of
https://github.com/predis/predis.git
synced 2026-08-27 18:51:07 +00:00
Merge branch 'v2.x' into vv-added-missing-types
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
/**
|
||||
* Represents the gap between slot ranges.
|
||||
*/
|
||||
class NullSlotRange extends SlotRange
|
||||
{
|
||||
public function __construct(int $start, int $end)
|
||||
{
|
||||
parent::__construct($start, $end, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use OutOfBoundsException;
|
||||
use Predis\Connection\NodeConnectionInterface;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
*/
|
||||
class SimpleSlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
private $slots = [];
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given slot range is valid.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slot map.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slot map is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slot map as a dictionary of $slot => $node.
|
||||
*
|
||||
* The order of the slots in the dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of unique nodes in the slot map.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot range to a node.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified slot range.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified slot is assigned.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
return $this->slots[$slot] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot to a node.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($slot, $connection)
|
||||
{
|
||||
if (!static::isValid($slot)) {
|
||||
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of assigned slots.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the slot map.
|
||||
*
|
||||
* @return Traversable<int, string>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->slots);
|
||||
}
|
||||
}
|
||||
+225
-17
@@ -22,11 +22,16 @@ use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
* Compact slot map for redis-cluster.
|
||||
*/
|
||||
class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
private $slots = [];
|
||||
/**
|
||||
* Slot ranges list.
|
||||
*
|
||||
* @var SlotRange[]
|
||||
*/
|
||||
private $slotRanges = [];
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
@@ -37,7 +42,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
return $slot >= 0 && $slot <= SlotRange::MAX_SLOTS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +55,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
return SlotRange::isValidRange($first, $last);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +63,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = [];
|
||||
$this->slotRanges = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +73,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
return empty($this->slotRanges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +85,13 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
return array_reduce(
|
||||
$this->slotRanges,
|
||||
function ($carry, $slotRange) {
|
||||
return $carry + $slotRange->toArray();
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +101,22 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
return array_unique(array_map(
|
||||
function ($slotRange) {
|
||||
return $slotRange->getConnection();
|
||||
},
|
||||
$this->slotRanges
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of slot ranges.
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
public function getSlotRanges()
|
||||
{
|
||||
return $this->slotRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +134,31 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
$targetSlotRange = new SlotRange($first, $last, (string) $connection);
|
||||
|
||||
// Get gaps of slot ranges list.
|
||||
$gaps = $this->getGaps($this->slotRanges);
|
||||
|
||||
$results = $this->slotRanges;
|
||||
|
||||
foreach ($gaps as $gap) {
|
||||
if (!$gap->hasIntersectionWith($targetSlotRange)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get intersection of the gap and target slot range.
|
||||
$results[] = new SlotRange(
|
||||
max($gap->getStart(), $targetSlotRange->getStart()),
|
||||
min($gap->getEnd(), $targetSlotRange->getEnd()),
|
||||
$targetSlotRange->getConnection()
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortSlotRanges($results);
|
||||
|
||||
$results = $this->compactSlotRanges($results);
|
||||
|
||||
$this->slotRanges = $results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +167,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
@@ -125,7 +175,28 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
$placeHolder = new NullSlotRange($first, $last);
|
||||
|
||||
$intersections = [];
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if (!$placeHolder->hasIntersectionWith($slotRange)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$intersections[] = new SlotRange(
|
||||
max($placeHolder->getStart(), $slotRange->getStart()),
|
||||
min($placeHolder->getEnd(), $slotRange->getEnd()),
|
||||
$slotRange->getConnection()
|
||||
);
|
||||
}
|
||||
|
||||
return array_reduce(
|
||||
$intersections,
|
||||
function ($carry, $slotRange) {
|
||||
return $carry + $slotRange->toArray();
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +209,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
return $this->findRangeBySlot($slot) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +222,9 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
return $this->slots[$slot] ?? null;
|
||||
$found = $this->findRangeBySlot($slot);
|
||||
|
||||
return $found ? $found->getConnection() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +242,8 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
$this->offsetUnset($slot);
|
||||
$this->setSlots($slot, $slot, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +256,26 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
if (!static::isValid($slot)) {
|
||||
throw new OutOfBoundsException("Invalid slot $slot");
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if (!$slotRange->hasSlot($slot)) {
|
||||
$results[] = $slotRange;
|
||||
}
|
||||
|
||||
if (static::isValidRange($slotRange->getStart(), $slot - 1)) {
|
||||
$results[] = new SlotRange($slotRange->getStart(), $slot - 1, $slotRange->getConnection());
|
||||
}
|
||||
|
||||
if (static::isValidRange($slot + 1, $slotRange->getEnd())) {
|
||||
$results[] = new SlotRange($slot + 1, $slotRange->getEnd(), $slotRange->getConnection());
|
||||
}
|
||||
}
|
||||
|
||||
$this->slotRanges = $results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +286,12 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
return array_sum(array_map(
|
||||
function ($slotRange) {
|
||||
return $slotRange->count();
|
||||
},
|
||||
$this->slotRanges
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +302,116 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->slots);
|
||||
return new ArrayIterator($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the slot range which contains the specific slot index.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return SlotRange|false The slot range object or false if not found.
|
||||
*/
|
||||
protected function findRangeBySlot(int $slot)
|
||||
{
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if ($slotRange->hasSlot($slot)) {
|
||||
return $slotRange;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get gaps between sorted slot ranges with NullSlotRange object.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
protected function getGaps(array $slotRanges)
|
||||
{
|
||||
if (empty($slotRanges)) {
|
||||
return [
|
||||
new NullSlotRange(0, SlotRange::MAX_SLOTS),
|
||||
];
|
||||
}
|
||||
$gaps = [];
|
||||
$count = count($slotRanges);
|
||||
$i = 0;
|
||||
foreach ($slotRanges as $key => $slotRange) {
|
||||
$start = $slotRange->getStart();
|
||||
$end = $slotRange->getEnd();
|
||||
if (static::isValidRange($i, $start - 1)) {
|
||||
$gaps[] = new NullSlotRange($i, $start - 1);
|
||||
}
|
||||
|
||||
$i = $end + 1;
|
||||
|
||||
if ($key === $count - 1) {
|
||||
if (static::isValidRange($i, SlotRange::MAX_SLOTS)) {
|
||||
$gaps[] = new NullSlotRange($i, SlotRange::MAX_SLOTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $gaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort slot ranges by start index.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sortSlotRanges(array &$slotRanges)
|
||||
{
|
||||
usort(
|
||||
$slotRanges,
|
||||
function (SlotRange $a, SlotRange $b) {
|
||||
if ($a->getStart() == $b->getStart()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a->getStart() < $b->getStart() ? -1 : 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact adjacent slot ranges with the same connection.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
protected function compactSlotRanges(array $slotRanges)
|
||||
{
|
||||
if (empty($slotRanges)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$compacted = [];
|
||||
$count = count($slotRanges);
|
||||
$i = 0;
|
||||
$carry = $slotRanges[0];
|
||||
while ($i < $count) {
|
||||
$next = $slotRanges[$i + 1] ?? null;
|
||||
if (
|
||||
!is_null($next)
|
||||
&& ($carry->getEnd() + 1) === $next->getStart()
|
||||
&& $carry->getConnection() === $next->getConnection()
|
||||
) {
|
||||
$carry = new SlotRange($carry->getStart(), $next->getEnd(), $carry->getConnection());
|
||||
} else {
|
||||
$compacted[] = $carry;
|
||||
$carry = $next;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return array_values($compacted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Countable;
|
||||
use OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Represents a range of slots in a Redis cluster.
|
||||
*/
|
||||
class SlotRange implements Countable
|
||||
{
|
||||
/**
|
||||
* Maximum number of slots in a Redis cluster is 16384.
|
||||
*/
|
||||
public const MAX_SLOTS = 0x3FFF;
|
||||
|
||||
/**
|
||||
* Starting slot of the range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* Ending slot of the range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* Connection to the server hosting this slot range.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function __construct(int $start, int $end, string $connection)
|
||||
{
|
||||
if (!static::isValidRange($start, $end)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $start-$end for `$connection`");
|
||||
}
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a slot range is valid.
|
||||
*
|
||||
* @param int $first
|
||||
* @param int $last
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0 && $first <= self::MAX_SLOTS && $last >= 0x0000 && $last <= self::MAX_SLOTS && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start slot index of this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStart()
|
||||
{
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end slot index of this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEnd()
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection to the server hosting this slot range.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specific slot is contained in this range.
|
||||
*
|
||||
* @param int $slot
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSlot(int $slot)
|
||||
{
|
||||
return $this->start <= $slot && $this->end >= $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of connection strings for each slot in this range.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return array_fill($this->start, $this->end - $this->start + 1, $this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of slots in this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return $this->end - $this->start + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this range has an intersection with the given slot range.
|
||||
*
|
||||
* @param SlotRange $slotRange
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasIntersectionWith(SlotRange $slotRange): bool
|
||||
{
|
||||
return $this->start <= $slotRange->getEnd() && $this->end >= $slotRange->getStart();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\SimpleSlotMap as SlotMap;
|
||||
use PredisTestCase;
|
||||
|
||||
class SimpleSlotMapTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsTrueOnValidSlot(): void
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValid(0));
|
||||
$this->assertTrue(SlotMap::isValid(16383));
|
||||
|
||||
$this->assertTrue(SlotMap::isValid(5000));
|
||||
$this->assertTrue(SlotMap::isValid('5000'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsFalseOnInvalidSlot(): void
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValid(-1));
|
||||
$this->assertFalse(SlotMap::isValid(16384));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsTrueOnValidSlotRange(): void
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValidRange(0, 16383));
|
||||
$this->assertTrue(SlotMap::isValidRange(2000, 2999));
|
||||
$this->assertTrue(SlotMap::isValidRange(3000, 3000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsFalseOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValidRange(0, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16383));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(2999, 2000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testToArrayReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSpecifiedNodeToSlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5461, 5462, '127.0.0.1:6380'),
|
||||
array_fill(10923, 5461, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsOverwritesSlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap =
|
||||
array_fill(0, 5461, '127.0.0.1:6379') +
|
||||
array_fill(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSingleSlotWhenFirstAndLastSlotMatch(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(10, 10, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame([10 => '127.0.0.1:6379'], $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsCastsValueToString(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->willReturn('127.0.0.1:6379');
|
||||
|
||||
$slotmap->setSlots(10, 10, $connection);
|
||||
|
||||
$this->assertSame([10 => '127.0.0.1:6379'], $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsThrowsExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 0-16384 for `127.0.0.1:6379`');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 16384, '127.0.0.1:6379');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsDictionaryOfSlotsWithAssignedNodes(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap = [
|
||||
3 => '127.0.0.1:6379',
|
||||
4 => '127.0.0.1:6379',
|
||||
5 => '127.0.0.1:6379',
|
||||
10 => '127.0.0.1:6380',
|
||||
11 => '127.0.0.1:6380',
|
||||
];
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(100, 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsThrowsExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 0-16384');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->getSlots(0, 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsTrueOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsFalseOnNonEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsZeroOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertCount(0, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsAssignedSlotsInSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$this->assertCount(5461, $slotmap);
|
||||
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$this->assertCount(10923, $slotmap);
|
||||
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
$this->assertCount(16384, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testResetEmptiesSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
|
||||
$slotmap->reset();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsArrayOfNodesInSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame(['127.0.0.1:6379', '127.0.0.1:6380', '127.0.0.1:6381'], $slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsTrueOnAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[0]));
|
||||
$this->assertTrue(isset($slotmap[2000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[6000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnInvalidSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[-100]));
|
||||
$this->assertFalse(isset($slotmap[16384]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNodeOfAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6381', $slotmap[10923]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnUnassignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnInvalidSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[-100]);
|
||||
$this->assertNull($slotmap[16384]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetRemovesSlotAssignment(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[100]));
|
||||
unset($slotmap[100]);
|
||||
$this->assertFalse(isset($slotmap[100]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetDoesNotDoAnythingOnUnassignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
unset($slotmap[5461]);
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetAssignsNodeToSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100]);
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetCastsValueToString(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->willReturn('127.0.0.1:6379');
|
||||
|
||||
$this->assertSame($connection, $slotmap[0] = $connection);
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetThrowsExceptionOnInvalidSlot(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot 16384 for `127.0.0.1:6379`');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap[16384] = '127.0.0.1:6379';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetIteratorReturnsIteratorOverSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5461, 5462, '127.0.0.1:6380'),
|
||||
array_fill(10923, 5461, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, iterator_to_array($slotmap));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use PredisTestCase;
|
||||
|
||||
class SlotRangeTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testMaxSlotsEquals16383()
|
||||
{
|
||||
$this->assertEquals(SlotRange::MAX_SLOTS, 16383);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testConstructorThrowExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 600-300 for `c1`');
|
||||
|
||||
new SlotRange(600, 300, 'c1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsTrueOnValidSlotRange()
|
||||
{
|
||||
$this->assertTrue(SlotRange::isValidRange(0, SlotRange::MAX_SLOTS));
|
||||
$this->assertTrue(SlotRange::isValidRange(2000, 2999));
|
||||
$this->assertTrue(SlotRange::isValidRange(3000, 3000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsFalseOnInvalidSlotRange()
|
||||
{
|
||||
$this->assertFalse(SlotRange::isValidRange(0, 16384));
|
||||
$this->assertFalse(SlotRange::isValidRange(-1, 16383));
|
||||
$this->assertFalse(SlotRange::isValidRange(-1, 16384));
|
||||
$this->assertFalse(SlotRange::isValidRange(2999, 2000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPropertySetters()
|
||||
{
|
||||
$range = new SlotRange(2000, 5000, 'c1');
|
||||
|
||||
$this->assertEquals(2000, $range->getStart());
|
||||
$this->assertEquals(5000, $range->getEnd());
|
||||
$this->assertEquals('c1', $range->getConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHasSlot()
|
||||
{
|
||||
$range = new SlotRange(2000, 4000, 'c1');
|
||||
|
||||
$this->assertTrue($range->hasSlot(2000));
|
||||
$this->assertTrue($range->hasSlot(4000));
|
||||
$this->assertTrue($range->hasSlot(3000));
|
||||
|
||||
$this->assertFalse($range->hasSlot(1000));
|
||||
$this->assertFalse($range->hasSlot(5000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testToArray()
|
||||
{
|
||||
$range = new SlotRange(2000, 2005, 'c1');
|
||||
$array = $range->toArray();
|
||||
$this->assertSame([
|
||||
2000 => 'c1',
|
||||
2001 => 'c1',
|
||||
2002 => 'c1',
|
||||
2003 => 'c1',
|
||||
2004 => 'c1',
|
||||
2005 => 'c1',
|
||||
], $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCount()
|
||||
{
|
||||
$range = new SlotRange(2000, 3000, 'c1');
|
||||
$this->assertEquals(1001, $range->count());
|
||||
$this->assertEquals(1001, count($range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHasIntersectionWith()
|
||||
{
|
||||
$original = new SlotRange(2000, 3000, 'c1');
|
||||
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(1000, 1500, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(3001, 5000, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(1999, 1999, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(3001, 3001, 'c1')));
|
||||
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(1500, 6000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2500, 2999, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2000, 2999, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2500, 3000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(1500, 2000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(3000, 3500, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2000, 2000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(3000, 3000, 'c1')));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user