\Drupal\strata\Capture\Classifier Heuristics

Guesses what a key is for, from what it is called.

Drupal's ephemeral keyspace is named by convention rather than by contract, and the conventions are strong enough to classify most of it: anything under cache is a cache, anything under lock is a lock, queue holds work, flood holds a security counter. So a first pass over a discovered keyspace can place the great majority of it without asking anyone.

Every rule here is a GUESS and is recorded as one. A rule that matches produces a classification with Heuristics::SOURCE attached, and a human decision always outranks it - see ClassificationRegistry, which never overwrites a human's answer with a rule's. That distinction is what makes it safe to add rules: the worst a wrong rule can do is be corrected, not silently override a decision someone already made.

The rules are ordered and the first match wins, so a specific pattern must sit above the general one it would otherwise be swallowed by.

Summary

Methods
Properties
Constants
classify
patternFor
namespaceOf
matches
No public properties found
SOURCE
HUMAN
RULES
No protected methods found
No protected properties found
No protected constants found
normalise
No private properties found
No private constants found

Constant

SOURCE

SOURCE = 'heuristic'

Recorded against any classification a rule produced.

HUMAN

HUMAN = 'human'

Recorded against a classification a person chose.

RULES

RULES = [
    // expirable stores come first: their names contain the names of the persistent ones, so a
    // broader rule below would otherwise swallow them and mark a cache authoritative
    'key_value_expire*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'keyvalue.expirable*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    '*expirable*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    '*_expire*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    '*.expire' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'tempstore*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    // work nobody else has a record of: losing a queue loses the work in it
    'queue*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    '*.queue' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    'reliable_queue*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    // a security counter; rebuilding it as empty hands an attacker their attempts back
    'flood*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    // someone is logged in with this
    'session*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    'php_session*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    // a semaphore mid-operation; restoring a stale one deadlocks whatever waits on it
    'semaphore*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    // key-value collections are persistent by name, and are captured in their own realm
    'key_value*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    'keyvalue*' => \Drupal\strata\Capture\Classifier\Classification::AUTHORITATIVE,
    // a lock is only meaningful while its holder is running, and its holder is not
    'lock*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    // every cache bin rebuilds itself on demand, by definition
    'cache*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    '*_cache' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'cachetags*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'render*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'page*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'dynamic_page_cache*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'discovery*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'bootstrap*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'config*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'container*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'menu*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'entity*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'data*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'default*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'jsonapi*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'library*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
    'toolbar*' => \Drupal\strata\Capture\Classifier\Classification::DERIVABLE,
] : array&lt;string, <a href="classes/Drupal-strata-Capture-Classifier-Classification.html"><abbr title="\Drupal\strata\Capture\Classifier\Classification">Classification</abbr></a>&gt;

Glob patterns keyed to what they mean, most specific first.

Methods

classify()

classify(string  $key) : \Drupal\strata\Capture\Classifier\Classification

Classifies one key.

Parameters

string $key

The key or namespace, such as "cache_render" or "queue:aggregator_feeds".

Returns

\Drupal\strata\Capture\Classifier\Classification —

What the rules make of it, or Classification::UNCLASSIFIED when nothing matched.

patternFor()

patternFor(string  $key) : string

The pattern that classified a key, for the audit trail.

A registry stores rules by pattern rather than by key, so a thousand cache keys under one bin become one row rather than a thousand. This is how the row is named.

Parameters

string $key

The key or namespace.

Returns

string —

The matching pattern, or the key's own namespace with a wildcard when nothing matched.

namespaceOf()

namespaceOf(string  $key) : string

The leading namespace of a key.

Keys arrive as bin:key, bin.key or bin_key depending on who wrote them, so all three separators are treated as the boundary. A key with no separator is its own namespace.

Parameters

string $key

The key.

Returns

string —

The namespace, without a trailing separator.

matches()

matches(string  $pattern, string  $key) : bool

Whether a glob pattern matches a key.

fnmatch() is not used: it is unavailable on some Windows builds of PHP and its behaviour around separators differs by platform, which would make a classification depend on the host.

Parameters

string $pattern

A pattern using * as the only wildcard.

string $key

The key to test.

Returns

bool —

TRUE when the pattern matches.

normalise()

normalise(string  $key) : string

Lowercases and trims a key so matching does not depend on how it was written.

Parameters

string $key

The key.

Returns

string —

The normalised form.