# PHP Judy

> PHP extension (PHP 8.1+) wrapping the Judy C library: memory-efficient,
> ordered, sparse dynamic arrays. 2-4x less memory than native PHP arrays for
> large integer-keyed datasets, ordered-key navigation, set operations, and
> atomic increments. Install: `pie install orieg/judy` or
> `install-php-extensions judy` (Docker).

Key facts: class `Judy` implements ArrayAccess, Countable, Iterator,
JsonSerializable. Ten type constants across integer-keyed, sorted string-keyed
(trie), and unsorted string-keyed (hash) families. The ordered search method is
`searchNext()` — `next()` is the Iterator method (old v1.x docs on php.net
show a removed API; trust the docs below instead).

## Docs

- [AGENTS.md](https://github.com/orieg/php-judy/blob/main/AGENTS.md): fast
  reference for coding agents — type decision table, core usage, pitfalls
- [API.md](https://github.com/orieg/php-judy/blob/main/API.md): complete API
  reference, generated from the canonical stub
- [BENCHMARK.md](https://github.com/orieg/php-judy/blob/main/BENCHMARK.md):
  measured performance, memory numbers, when (not) to use Judy, and php-judy
  versus APCu / SplFixedArray / sorted arrays
- [BACKEND_EVALUATION.md](https://github.com/orieg/php-judy/blob/main/BACKEND_EVALUATION.md):
  maintainer-facing — whether the extension should keep libJudy as its C
  backend, measured against the Adaptive Radix Tree (ART) with Masstree, HOT
  and Wormhole considered. Verdict: keep Judy (ART ties on lookup and costs
  27% more memory)
- [README.md](https://github.com/orieg/php-judy/blob/main/README.md):
  install paths for Linux/macOS/Windows/Docker, usage examples per type

## Examples

Runnable, self-contained scripts — one per pattern Judy is chosen for.

- [examples/README.md](https://github.com/orieg/php-judy/blob/main/examples/README.md):
  index of all demos, with the Judy type each one uses
- [quickstart.php](https://github.com/orieg/php-judy/blob/main/examples/quickstart.php):
  the essentials — array access, iteration, navigation, bulk ops
- [dedup-large-stream.php](https://github.com/orieg/php-judy/blob/main/examples/dedup-large-stream.php):
  "have I seen this ID?" membership over millions of keys (crawler frontiers,
  queue dedup) with a peak-RSS comparison of array vs Judy vs `BITSET`
- [ip-range-lookup.php](https://github.com/orieg/php-judy/blob/main/examples/ip-range-lookup.php):
  floor lookup via `last()` — IP-to-CIDR matching, tariff bands, ID shards
- [sliding-window-rate-limit.php](https://github.com/orieg/php-judy/blob/main/examples/sliding-window-rate-limit.php):
  sliding-window rate limiting and rolling metrics; `deleteRange()` expires
  aged-out time buckets without scanning the retained set
- [prefix-invalidation.php](https://github.com/orieg/php-judy/blob/main/examples/prefix-invalidation.php):
  namespace invalidation (`user:123:*`) walking only the matching key slice,
  with a keys-visited comparison against a hash table
- [symbol-table-prefix.php](https://github.com/orieg/php-judy/blob/main/examples/symbol-table-prefix.php):
  symbol table keyed by fully-qualified class name, queried by namespace
  (`App\Domain\*`) for LSP completion, namespace-scoped analysis rules and
  PHPUnit `--filter`: deriving inclusive key bounds from a prefix
  binary-safely, then reading the slice with one `keys`/`values`/`toArray($lo,
  $hi)` call, with keys-visited and crossing counts against a hash scan
- [autocomplete-trie.php](https://github.com/orieg/php-judy/blob/main/examples/autocomplete-trie.php):
  prefix search / typeahead via `first()` + `searchNext()`, and when that walk
  still beats a bounded bulk read
- [worker-counters.php](https://github.com/orieg/php-judy/blob/main/examples/worker-counters.php):
  metrics accumulators in long-running workers with atomic `increment()`

## Contributing

- [CONTRIBUTING.md](https://github.com/orieg/php-judy/blob/main/CONTRIBUTING.md):
  C-extension build/test workflow
- [SECURITY.md](https://github.com/orieg/php-judy/blob/main/SECURITY.md):
  private vulnerability reporting
