OxPHP\Shared\* Naming Conventions

The OxPHP\Shared\* namespace is the application-level concurrency API: Atomic, Counter, Flag, Map, Channel, Mutex, Once, Pool. Method names follow a single set of rules so users can predict the API without consulting the docs for every type.

This document is the canonical reference. New primitives, and changes to existing ones, MUST follow it.

Rules

1. Read a value — get()

PHP convention. Used by Map::get(), Counter::get(), Once::get().

Atomic::load(Ordering $order = Ordering::SeqCst) is the deliberate exception: its existence carries the ordering argument, signalling that the read is part of a memory-model contract distinct from a plain getter.

2. Write a value — set(), store() for atomics

Map::set(), Mutex value update (by reference inside withLock), Once::getOrInit(). Atomic::store(int $value, Ordering $order = Ordering::SeqCst) mirrors load for the same reason.

3. Number of elements — count(): int

Every container that exposes its current size does so under the name count(): int. Channel additionally implements \Countable, so count($ch) works as a native idiom for queued items. Map exposes count(): int as a method but does not implement \Countable, so call it directly. Pool has no count(); read its live slots from stats()->size():

php
$ch = new OxPHP\Shared\Channel(1024); $map = new OxPHP\Shared\Map(); $pool = new OxPHP\Shared\Pool($factory); count($ch); // queued items (Channel implements \Countable) $map->count(); // entries $pool->stats()->size(); // total live slots (in-use + idle)

size(), len(), and pending() are forbidden on the public surface, regardless of which language the implementer's muscle memory comes from.

4. Boolean-getter — is*() prefix

Channel::isClosed().

No bare verbs (test, check) and no domain-specific names (closed). The is prefix marks a pure read of a boolean property.

A type whose state is richer than a single boolean exposes it as a status() method returning an enum instead of an is*() getter. Channel's RecvResult::status() and Once::status(): Once\Status (Uninitialized/Pending/Ready/Poisoned) follow this. Reach for status() when the answer has more than two cases.

Mutex does not expose isCorrupted(). Corruption is sticky, non-recoverable, and surfaced via CorruptedMutexException on the next acquire. There's nothing useful to do with the probe other than re-acquire and catch.

5. Wait-policy trichotomy — try* / bare / *Timeout

Blocking primitives (Channel, Mutex) express the wait policy through the method name, not through an overloaded ?float $timeout argument:

Suffix Behaviour Examples
try* Non-blocking; reports the failure variant immediately. Channel::trySend, Channel::tryRecv, Mutex::tryWithLock
(bare name) Block forever (or until the request fiber is cancelled). Channel::send, Channel::recv, Mutex::withLock
*Timeout Bounded wait. Takes a mandatory int $ms > 0. Channel::sendTimeout, Channel::recvTimeout, Mutex::withLockTimeout

The trichotomy moves three ambiguous policies (null = forever, 0 = try, positive = bounded) out of one parameter and into three methods with self-documenting names.

Note

The $ms argument on *Timeout methods is strictly positive. Zero, negative, non-int, and absent values raise OxPHP\Shared\TypeException at the bridge.

Conditional-success ops live on Map under the setIfAbsent spelling rather than try*: Map::setIfAbsent commits only when the key was absent (parallel to HashMap::try_insert) and returns the existing value, or null when the insert happened. The setIfAbsent name is reserved for that single semantics; do not reuse it elsewhere.

How a try* method reports failure depends on the type: Channel returns a value-typed Result, Mutex throws a ContentionException, Pool::tryAcquire() returns null when the pool is saturated, and Once::trySet() returns false when the cell is already Ready or Pending.

6. Compare-and-swap — compareAndSet()

Atomic::compareAndSet(), Flag::compareAndSet(). Always returns bool (the swap happened, or it didn't).

7. Replace and return previous — swap()

Atomic::swap() for ints, Flag::swap() for bools. Returns the previous value.

8. Atomic RMW returning previous — fetch*() prefix

Atomic::fetchAdd(), fetchSub(), fetchAnd(), fetchOr(), fetchXor().

The fetch prefix encodes the return contract: the value before the operation. This contrasts with Counter::add(), which returns the new value (LongAdder-style aggregate counter).

When adding new RMW methods, pick the contract first, then the name:

  • prev-value return → fetchVerb(args)
  • new-value return → bare verb(args)

Do not mix.

9. Reset to default — clear()

Map::clear() empties the container and returns the number of entries removed.

Counter has no clear(); set(0) is its windowed reset. Counter::set() is the documented exception that returns the previous value (not void): it is the atomic exchange, and set(0) reading the prior total is the LongAdder sumThenReset idiom. (Atomic spells the same operation swap(); Counter keeps set because set($n) reads naturally for seeding and windowing.)

10. Registry identity — id(): int

Every Shared\* instance exposes id(): int for logs and the /__ox_shared/entry?id=<id> observability endpoint.

Cheat sheet

Concept Canonical name Examples
Read a value get() Map::get, Counter::get
Read an atomic load($order) Atomic::load
Write a value set() Map::set
Write an atomic store($v, $order) Atomic::store
Number of elements count(): int Map::count, Channel::count
Boolean property is*(): bool Channel::isClosed
Conditional insert setIfAbsent($k, $v) Map::setIfAbsent
Non-blocking wait try*() Channel::trySend, Mutex::tryWithLock
Forever wait bare verb Channel::send, Channel::recv, Mutex::withLock
Bounded wait *Timeout(int $ms) Channel::sendTimeout, Mutex::withLockTimeout
Compare-and-swap compareAndSet() Atomic::compareAndSet
Swap, return prev swap() Atomic::swap, Flag::swap
Atomic RMW, return prev fetch*() Atomic::fetchAdd
Atomic RMW, return new bare verb Counter::add
Reset to default clear() Map::clear
Registry id id(): int every Shared\* type

Adding a new Shared\* type

When proposing a new primitive, fill out this checklist before merging:

  • Every method maps to a row in the cheat sheet, or has an ADR explaining the exception (see Atomic::load/store and Counter::set above).
  • If the type holds a collection of values, it implements \Countable and exposes count(): int.
  • Read methods are get or load (atomic only).
  • Boolean getters use the is* prefix.
  • Wait-policy variants follow the try* / bare / *Timeout(int $ms) trichotomy. The *Timeout variant takes int $ms > 0 and rejects zero / negative / non-int input with TypeException. Wait-policy try* methods return either a value-typed Result or throw a domain exception, and never use null to encode failure. Pool::tryAcquire(), which returns null when the pool is saturated, does not follow this. Conditional-success ops follow the dedicated setIfAbsent naming instead of try*.
  • No len, size, pending, test, or other ad-hoc names.
  • Domain-specific verbs (evict, drain, flush, etc.) appear only when no canonical entry in the cheat sheet covers the concept.

Observability names

The operator-facing surface (Prometheus metric names and the JSON at /__ox_shared/entry?id=<id>) is a separate contract from the PHP API, because renaming it breaks dashboards and alert rules. The element count of a Channel or a Pool is oxphp_shared_channel_count or oxphp_shared_pool_count on /metrics, and count in its type_specific JSON. For a Channel that is the number Channel::count() returns; for a Pool it is the live slots, the number Pool::stats()->size() returns.

Removed in 0.13.0

The earlier spellings (oxphp_shared_channel_pending, oxphp_shared_pool_size, and the JSON keys pending and size) are no longer emitted. Dashboards and alert rules still written against them find no series, so a threshold alert on them never fires.

Stability

These rules are part of the OxPHP\Shared\* 1.0 contract. After the 1.0 release, renames are breaking changes and require a deprecation cycle. Before 1.0, the rules are still binding, and new methods that violate them will be rejected in review.

Found a mistake? Report it →