Conventions de nommage OxPHP\Shared\*

L'espace de noms OxPHP\Shared\* est l'API de concurrence au niveau applicatif : Atomic, Counter, Flag, Map, Channel, Mutex, Once, Pool. Les noms de méthodes suivent un ensemble unique de règles, ce qui permet aux utilisateurs de prédire l'API sans consulter la documentation de chaque type.

Ce document est la référence canonique. Les nouvelles primitives, ainsi que les modifications apportées aux primitives existantes, DOIVENT le respecter.

Règles

1. Lire une valeur — get()

Convention PHP. Utilisée par Map::get(), Counter::get(), Once::get().

Atomic::load(?Ordering $order = null) constitue l'exception délibérée : son existence porte l'argument d'ordonnancement, signalant que la lecture fait partie d'un contrat de modèle mémoire distinct d'un simple getter.

2. Écrire une valeur — set(), store() pour les atomiques

Map::set(), réinitialisation de la valeur d'un Mutex (via with), Once::getOrInit(). Atomic::store($value, ?Ordering) reflète load pour la même raison.

3. Nombre d'éléments — count(): int

Tout conteneur qui expose sa taille actuelle le fait sous le nom count(): int. Channel implémente en plus \Countable, si bien que count($ch) fonctionne comme un idiome natif pour les éléments en file d'attente. Map et Pool exposent count(): int en tant que méthode mais n'implémentent pas \Countable — appelez-la directement :

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->count(); // total live slots (in-use + idle)

Pas de size(), len() ni pending() — ils sont interdits sur la surface publique, quelle que soit la langue dont proviennent les automatismes de l'implémenteur.

4. Getter booléen — préfixe is*()

Channel::isClosed().

Pas de verbes nus (test, check) ni de noms spécifiques au domaine (closed). Le préfixe is marque une lecture pure d'une propriété booléenne.

Un type dont l'état est plus riche qu'un simple booléen l'expose via une méthode status() renvoyant une énumération plutôt qu'un getter is*() — le RecvResult::status() de Channel et Once::status(): Once\Status (Uninitialized/Pending/Ready/Poisoned) suivent ce principe. Optez pour status() lorsque la réponse comporte plus de deux cas.

Mutex n'expose pas isCorrupted() — la corruption est persistante, irrécupérable, et remontée via CorruptedMutexException lors de la prochaine acquisition. Il n'y a rien d'utile à faire avec la sonde, si ce n'est réacquérir et intercepter.

5. Trichotomie de la politique d'attente — try* / nu / *Timeout

Les primitives bloquantes (Channel, Mutex) expriment la politique d'attente à travers le nom de la méthode, et non via un argument ?float $timeout surchargé :

Suffixe Comportement Exemples
try* Non bloquant ; signale immédiatement la variante d'échec. Channel::trySend, Channel::tryRecv, Mutex::tryWithLock
(nom nu) Bloque indéfiniment (ou jusqu'à l'annulation de la Fiber de la requête). Channel::send, Channel::recv, Mutex::withLock
*Timeout Attente bornée. Prend un int $ms > 0 obligatoire. Channel::sendTimeout, Channel::recvTimeout, Mutex::withLockTimeout

La trichotomie fait sortir trois politiques ambiguës (null = indéfiniment, 0 = try, positif = borné) d'un seul paramètre pour les répartir dans trois méthodes aux noms auto-documentés.

Note

L'argument $ms des méthodes *Timeout est strictement positif. Les valeurs nulles, négatives, non entières ou absentes lèvent OxPHP\Shared\TypeException au niveau du pont.

Les opérations à succès conditionnel résident sur Map sous l'appellation setIfAbsent plutôt que try* : Map::setIfAbsent valide uniquement lorsque la clé était absente et renvoie bool (en parallèle de HashMap::try_insert). Le nom setIfAbsent est réservé à cette unique sémantique ; ne le réutilisez pas ailleurs.

L'invariant unificateur de try* : soit il renvoie un Result typé par valeur (Channel), soit il lève une ContentionException (Mutex). Il ne renvoie jamais null pour encoder « n'a pas réussi ». C'était l'ancienne API, qui produisait l'ambiguïté de coalescence null que la trichotomie élimine.

6. Compare-and-swap — compareAndSet()

Atomic::compareAndSet(), Flag::compareAndSet(). Renvoie toujours bool (l'échange a eu lieu, ou non).

7. Remplacer et renvoyer la valeur précédente — swap()

Atomic::swap() pour les entiers, Flag::swap() pour les booléens. Renvoie la valeur précédente.

8. RMW atomique renvoyant la valeur précédente — préfixe fetch*()

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

Le préfixe fetch encode le contrat de retour : la valeur avant l'opération. Cela contraste avec Counter::add(), qui renvoie la nouvelle valeur (compteur agrégé de style LongAdder).

Lors de l'ajout de nouvelles méthodes RMW, choisissez d'abord le contrat, puis le nom :

  • retour de la valeur précédente → fetchVerb(args)
  • retour de la nouvelle valeur → verb(args) nu

Ne les mélangez pas.

9. Réinitialiser à la valeur par défaut — clear()

Map::clear() — vide le conteneur ; renvoie void.

Counter n'a pas de clear()set(0) constitue sa remise à zéro par fenêtre. Counter::set() est l'exception documentée qui renvoie la valeur précédente (et non void) : il s'agit de l'échange atomique, et set(0) lisant le total antérieur correspond à l'idiome sumThenReset de LongAdder. (Atomic nomme la même opération swap() ; Counter conserve set parce que set($n) se lit naturellement pour l'amorçage et le fenêtrage.)

10. Identité dans le registre — id(): int

Chaque instance Shared\* expose id(): int pour les logs et l'endpoint d'observabilité /__ox_shared/entry?id=<id>.

Aide-mémoire

Concept Nom canonique Exemples
Lire une valeur get() Map::get, Counter::get
Lire un atomique load($order) Atomic::load
Écrire une valeur set() Map::set
Écrire un atomique store($v, $order) Atomic::store
Nombre d'éléments count(): int Map::count, Channel::count, Pool::count
Propriété booléenne is*(): bool Channel::isClosed
Insertion conditionnelle setIfAbsent($k, $v) Map::setIfAbsent
Attente non bloquante try*() Channel::trySend, Mutex::tryWithLock
Attente indéfinie verbe nu Channel::send, Channel::recv, Mutex::withLock
Attente bornée *Timeout(int $ms) Channel::sendTimeout, Mutex::withLockTimeout
Compare-and-swap compareAndSet() Atomic::compareAndSet
Échange, renvoie la préc. swap() Atomic::swap, Flag::swap
RMW atomique, renvoie la préc. fetch*() Atomic::fetchAdd
RMW atomique, renvoie la nouvelle verbe nu Counter::add
Réinitialiser à la valeur par défaut clear() Map::clear
ID de registre id(): int chaque type Shared\*

Ajouter un nouveau type Shared\*

Lorsque vous proposez une nouvelle primitive, remplissez cette liste de contrôle avant de fusionner :

  • Chaque méthode correspond à une ligne de l'aide-mémoire, ou dispose d'un ADR expliquant l'exception (voir Atomic::load/store et Counter::set ci-dessus).
  • Si le type contient une collection de valeurs, il implémente \Countable et expose count(): int.
  • Les méthodes de lecture sont get ou load (atomiques uniquement).
  • Les getters booléens utilisent le préfixe is*.
  • Les variantes de politique d'attente suivent la trichotomie try* / nu / *Timeout(int $ms). La variante *Timeout prend int $ms > 0 et rejette les entrées nulles, négatives ou non entières avec TypeException. Les méthodes de politique d'attente try* renvoient soit un Result typé par valeur, soit lèvent une exception de domaine — jamais un null-encodé. Les opérations à succès conditionnel suivent le nommage dédié setIfAbsent plutôt que try*.
  • Pas de len, size, pending, test, ni d'autres noms ad hoc.
  • Les verbes spécifiques au domaine (evict, drain, flush, etc.) n'apparaissent que lorsqu'aucune entrée canonique de l'aide-mémoire ne couvre le concept.

Les noms d'observabilité sont en retard sur l'API PHP

La surface destinée aux opérateurs — les noms de métriques Prometheus et le JSON à /__ox_shared/entry?id=<id> — constitue un contrat distinct de l'API PHP. Le renommer casse les tableaux de bord et les règles d'alerte. Pour éviter toute incohérence silencieuse, les noms concernés sont émis deux fois pendant un cycle de publication :

Surface Obsolète (toujours émis) Canonique
Prometheus oxphp_shared_channel_pending oxphp_shared_channel_count
Prometheus oxphp_shared_pool_size oxphp_shared_pool_count
Entrée JSON Channel.pending Channel.count
Entrée JSON Pool.size Pool.count

Les lignes # HELP des métriques obsolètes portent un préfixe (deprecated, removed in a future release; use *_count), et le plugin ox_shared émet un WARN au démarrage dès que l'introspection ou les métriques sont activées.

Migration

Migrez les tableaux de bord et les règles d'alerte vers les noms _count avant la fin du cycle de dépréciation. Après suppression, seuls les noms canoniques seront émis, et les panneaux Prometheus/Grafana référençant les anciens commenceront à renvoyer des séries vides.

Stabilité

Ces règles font partie du contrat OxPHP\Shared\* 1.0. Après la sortie de la 1.0, les renommages sont des changements cassants et nécessitent un cycle de dépréciation. Avant la 1.0, les règles restent contraignantes — les nouvelles méthodes qui les enfreignent seront rejetées en revue.