PHPackages                             jrdev/drange - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. jrdev/drange

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

jrdev/drange
============

Efficiently manage non-contiguous integer ranges with automatic merging and set operations

v1.1.0(1w ago)314[2 issues](https://github.com/jrobinsonc/drange/issues)MITPHPPHP &gt;=8.2CI passing

Since Jul 2Pushed 1w ago1 watchersCompare

[ Source](https://github.com/jrobinsonc/drange)[ Packagist](https://packagist.org/packages/jrdev/drange)[ Docs](https://github.com/jrobinsonc/drange/blob/master/README.md)[ RSS](/packages/jrdev-drange/feed)WikiDiscussions master Synced 3d ago

READMEChangelog (3)Dependencies (11)Versions (4)Used By (0)

DRange
======

[](#drange)

[![Tests](https://github.com/jrobinsonc/drange/actions/workflows/tests.yml/badge.svg)](https://github.com/jrobinsonc/drange/actions/workflows/tests.yml)[![Latest Version](https://camo.githubusercontent.com/766ac0d12eb834456d703b28a55ca7c32a4abd172185fcab6395b7871042793c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a726465762f6472616e6765)](https://packagist.org/packages/jrdev/drange)[![PHP](https://camo.githubusercontent.com/7c7d3ed22a97b95bdeb7be391b08d45af570627dee8db50258d7b9af6e3905c6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e322d383839326266)](https://www.php.net/)[![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](https://github.com/jrobinsonc/drange/blob/master/LICENSE)

DRange is a PHP library for managing **discontinuous (non-contiguous) ranges of integers**. Rather than storing every number individually, it holds a compact, sorted list of contiguous sub-ranges — automatically merging adjacent ranges when you add numbers and splitting them when you subtract. The result is an expressive, memory-efficient structure for any problem that involves gaps in sequences.

Features
--------

[](#features)

- Add, subtract, or intersect individual integers, numeric ranges, `SubRange` instances, or entire `DRange` objects
- Automatic merging of adjacent and overlapping ranges on `add()`
- Automatic splitting of ranges on `subtract()`
- Random-access by logical index (`->index($n)`) across all sub-ranges
- Native PHP `count()` support via `Countable`
- Human-readable string representation (e.g. `[ 1-5, 8-10 ]`)
- Chainable `add()` and `subtract()` calls
- Full clone support — deep copy, mutations on the clone do not affect the original
- Zero runtime dependencies

Requirements
------------

[](#requirements)

- PHP &gt;= 8.2
- Composer

Installation
------------

[](#installation)

```
composer require jrdev/drange
```

```
use jrdev\DRange;
use jrdev\DRange\SubRange; // only needed if you use SubRange directly
```

Quick Start
-----------

[](#quick-start)

```
$drange = new DRange(1, 5);   // [ 1-5 ]
$drange->add(8);               // [ 1-5, 8 ]   gap at 6-7
$drange->add(6, 7);            // [ 1-8 ]       auto-merged
$drange->subtract(1, 3);       // [ 4-8 ]

echo $drange;           // "[ 4-8 ]"
echo count($drange);    // 5
```

API Reference
-------------

[](#api-reference)

### `DRange` (`jrdev\DRange`)

[](#drange-jrdevdrange)

Method / UsageReturnsDescription`new DRange()``DRange`Empty range`new DRange($n)``DRange`Range containing the single integer `$n``new DRange($low, $high)``DRange`Contiguous range `[$low..$high]``->add($n)``$this`Add a single integer`->add($low, $high)``$this`Add a contiguous range`->add($drange)``$this`Add all sub-ranges from another `DRange``->add($subrange)``$this`Add a `SubRange` instance`->subtract($n)``$this`Subtract a single integer`->subtract($low, $high)``$this`Subtract a contiguous range`->subtract($drange)``$this`Subtract all sub-ranges of another `DRange``->subtract($subrange)``$this`Subtract a `SubRange` instance`->intersect($n)``DRange`New range containing only integers present in both this range and `$n``->intersect($low, $high)``DRange`Intersection with a contiguous range`->intersect($drange)``DRange`Intersection with another `DRange``->intersect($subrange)``DRange`Intersection with a `SubRange` instance`->index($i)``int|null`Value at logical position `$i` (0-based); `null` if out of bounds`count($drange)``int`Total number of integers across all sub-ranges`(string) $drange``string`e.g. `"[ 1-5, 8-10 ]"``clone $drange``DRange`Deep copy; mutations do not affect the originalAdjacent ranges (touching but not overlapping) are merged automatically on every `add()` call.

### `SubRange` (`jrdev\DRange\SubRange`)

[](#subrange-jrdevdrangesubrange)

MemberTypeDescription`new SubRange($low, $high)`—Throws `\UnexpectedValueException` if `$low > $high``->low``int`Lower bound (inclusive)`->high``int`Upper bound (inclusive)`->length``int``$high - $low + 1``->overlaps($range)``bool``true` if the two ranges share at least one integer`->touches($range)``bool``true` if the ranges overlap or are adjacent (no gap between them)`->add($range)``SubRange`Merged range — only meaningful when `touches()` is `true``->subtract($range)``SubRange[]`Returns 0, 1, or 2 sub-ranges after removing the overlap`(string) $subrange``string``"low-high"` or just `"n"` for a single-number range`clone $subrange``SubRange`Deep copyUsage Examples
--------------

[](#usage-examples)

### Method chaining

[](#method-chaining)

```
$drange = new DRange(1, 10)
    ->subtract(5)
    ->subtract(7);
// [ 1-4, 6, 8-10 ]
```

### Operating on two DRange objects

[](#operating-on-two-drange-objects)

```
$allowed = new DRange(1, 1023);

$blocked = new DRange(22);
$blocked->add(23)->add(3306)->add(5432);

$available = clone $allowed;
$available->subtract($blocked);
// [ 1-21, 24-3305, 3307-5431, 5433-1023 ]
```

### Random access with `index()`

[](#random-access-with-index)

```
$drange = new DRange(0, 9);   // 10 elements: 0–9
$drange->add(20, 29);          // 10 more:      20–29
$drange->add(40, 49);          // 10 more:      40–49

$drange->index(0);   // 0   (1st element)
$drange->index(15);  // 25  (16th element, 6th in second sub-range)
$drange->index(25);  // 45  (26th element, 6th in third sub-range)
$drange->index(55);  // null (out of bounds)
```

### Counting with native `count()`

[](#counting-with-native-count)

```
$drange = new DRange(1, 5);   // 5 elements
$drange->add(10, 15);          // 6 more

count($drange); // 11
```

### Computing intersections with `intersect()`

[](#computing-intersections-with-intersect)

```
$available = new DRange(1, 10);
$available->add(20, 30);
// [ 1-10, 20-30 ]

$requested = new DRange(5, 25);

$overlap = $available->intersect($requested);
// [ 5-10, 20-25 ]

count($overlap); // 12
```

### Using `SubRange` directly

[](#using-subrange-directly)

```
use jrdev\DRange\SubRange;

$drange = new DRange(1, 10);
$drange->subtract(new SubRange(4, 6));
// [ 1-3, 7-10 ]
```

Real-World Use Cases
--------------------

[](#real-world-use-cases)

- **Network port management** — Represent firewall allowlists or blocklists as unions of port ranges; subtract blocked ranges from the full `0–65535` range to derive what is open.
- **HTTP range requests** — Track which byte offsets of a large file have been downloaded; call `subtract()` as each chunk arrives and `count()` to know how many bytes remain.
- **Appointment and booking systems** — Represent availability as numeric time ranges (minutes since midnight, Unix timestamps); subtract booked slots to see what is still open.
- **Batch job progress tracking** — Record which database record ID ranges have been processed; use `index()` to resume from an arbitrary position without scanning the full set.
- **Pagination and lazy loading** — Track which page or item index ranges have already been fetched from an API; avoid re-requesting pages that fall inside an already-loaded range.
- **Seat allocation** — Model auditorium or transit rows as numeric ranges; subtract reserved seats and inspect the remaining range to find the next available contiguous block.
- **IP address management** — Convert IPv4 addresses to 32-bit integers and use `DRange` to track allocated and free address blocks without needing a dedicated CIDR library.

License
-------

[](#license)

Licensed under the [MIT licence](https://github.com/jrobinsonc/drange/blob/master/LICENSE).

###  Health Score

48

↓

FairBetter than 94% of packages

Maintenance85

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community4

Small or concentrated contributor base

Maturity78

Established project with proven stability

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1615 days

Total

3

Last Release

9d ago

PHP version history (2 changes)v1.0.0PHP &gt;=5.4

v1.1.0PHP &gt;=8.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/15801?v=4)[Jose Robinson](/maintainers/jrobinsonc)[@jrobinsonc](https://github.com/jrobinsonc)

---

Tags

integermergerangedata structureset-operationsintersectionrangesgapsallocationdiscontinuousnon-contiguous

###  Code Quality

TestsCodeception

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/jrdev-drange/health.svg)

```
[![Health](https://phpackages.com/badges/jrdev-drange/health.svg)](https://phpackages.com/packages/jrdev-drange)
```

###  Alternatives

[brick/math

Arbitrary-precision arithmetic library

2.1k504.0M277](/packages/brick-math)[league/period

Time range API for PHP

7335.4M21](/packages/league-period)[gehrisandro/tailwind-merge-laravel

TailwindMerge for Laravel merges multiple Tailwind CSS classes by automatically resolving conflicts between them

341682.2k18](/packages/gehrisandro-tailwind-merge-laravel)[kartik-v/yii2-date-range

An advanced Yii 2 date range picker input for based on bootstrap-daterangepicker plugin.

894.4M42](/packages/kartik-v-yii2-date-range)[gehrisandro/tailwind-merge-php

TailwindMerge for PHP merges multiple Tailwind CSS classes by automatically resolving conflicts between them

1391.5M9](/packages/gehrisandro-tailwind-merge-php)[loophp/phptree

An implementation of tree data structure

981.8M2](/packages/loophp-phptree)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
