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 :
$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.
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/storeetCounter::setci-dessus). - Si le type contient une collection de valeurs, il implémente
\Countableet exposecount(): int. - Les méthodes de lecture sont
getouload(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*Timeoutprendint $ms > 0et rejette les entrées nulles, négatives ou non entières avecTypeException. Les méthodes de politique d'attentetry*renvoient soit un Result typé par valeur, soit lèvent une exception de domaine — jamais unnull-encodé. Les opérations à succès conditionnel suivent le nommage dédiésetIfAbsentplutôt quetry*. - 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.
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.