@anonympins/fingerprint 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/composer.json +8 -1
  4. package/package.json +1 -1
  5. package/public/fingerprint-wordpress.zip +0 -0
  6. package/src/js/build-client.js +8 -10
  7. package/src/js/fingerprint.builder.js +37 -38
  8. package/src/js/fingerprint.client.js +1531 -1463
  9. package/src/js/fingerprint.client.obfuscated.js +1 -0
  10. package/src/js/fingerprint.js +190 -16
  11. package/src/js/fingerprint.utils.js +213 -213
  12. package/src/js/library.js +340 -376
  13. package/src/js/mongodb-store.js +1 -1
  14. package/src/js/optimization.worker.js +27 -27
  15. package/src/js/pow.solver.inline.js +154 -56
  16. package/src/js/pow.solver.js +180 -92
  17. package/src/js/pow.worker.js +48 -48
  18. package/src/js/redis-store.js +1 -1
  19. package/src/js/tests/fingerprint.client.init.test.js +146 -143
  20. package/src/js/tests/fingerprint.test.js +124 -10
  21. package/src/js/tests/ip-reputation.test.js +1 -0
  22. package/src/js/upow-model-task.js +14 -15
  23. package/src/php/AutoTuner.php +34 -34
  24. package/src/php/Challenge/ChallengeUtils.php +57 -11
  25. package/src/php/Config/SecurityProfiles.php +20 -0
  26. package/src/php/DirectFingerprint.php +190 -145
  27. package/src/php/FingerprintBuilder.php +29 -30
  28. package/src/php/FingerprintClient.php +147 -148
  29. package/src/php/FingerprintEngine.php +44 -17
  30. package/src/php/Ja3AnomalyDetector.php +33 -32
  31. package/src/php/Optimization/FunctionRegistry.php +7 -7
  32. package/src/php/Optimization/Optimization.php +24 -24
  33. package/src/php/Optimization/OptimizationOperators.php +26 -25
  34. package/src/php/Optimization/ProblemInitializers.php +52 -52
  35. package/src/php/ProblemManager.php +31 -34
  36. package/src/php/RequestContext.php +8 -1
  37. package/src/php/Store/IStore.php +7 -8
  38. package/src/php/Store/InMemoryStore.php +66 -66
  39. package/src/php/Store/MongoDbStore.php +5 -5
  40. package/src/php/Store/RedisStore.php +5 -5
  41. package/src/php/Store/StoreManager.php +35 -35
  42. package/src/php/Tests/ChallengeUtilsTest.php +453 -453
  43. package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
  44. package/src/php/Tests/FingerprintEngineTest.php +38 -0
  45. package/src/php/Tests/IpReputationTest.php +10 -0
  46. package/src/php/Tests/MaliciousPatternsTest.php +103 -103
  47. package/src/php/Tests/MetricsTest.php +97 -97
  48. package/src/php/Tests/ProblemManagerTest.php +376 -376
  49. package/src/php/Tests/QuicFingerprintTest.php +53 -53
  50. package/src/php/Tests/RequestUtilsTest.php +471 -471
  51. package/src/php/Tests/TLSClientHelloParserTest.php +177 -177
  52. package/src/php/Tests/config/ed25519_key.json +3 -3
  53. package/src/php/Utils/BlockList.php +99 -99
  54. package/src/php/Utils/Env.php +49 -49
  55. package/src/php/Utils/MaliciousPatterns.php +74 -74
  56. package/src/php/Utils/MetricsManager.php +209 -209
  57. package/src/php/Utils/RequestUtils.php +129 -7
  58. package/src/php/Utils/TLSClientHelloParser.php +369 -369
  59. package/src/php/WordPress/WpDbStore.php +155 -0
  60. package/src/php/WordPress/fingerprint-wordpress.php +554 -0
  61. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  62. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +262 -0
  63. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  64. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +268 -0
  65. package/src/php/WordPress/package.php +218 -0
  66. package/src/php/bin/auto-tune.php +118 -118
@@ -19,7 +19,7 @@ class ProblemManager
19
19
  private bool $initialized = false;
20
20
 
21
21
  /**
22
- * Le constructeur est privé pour forcer l'utilisation du singleton.
22
+ * Private constructor enforces singleton usage.
23
23
  */
24
24
  private function __construct(string $configPath, IStore $store)
25
25
  {
@@ -29,8 +29,8 @@ class ProblemManager
29
29
  }
30
30
 
31
31
  /**
32
- * Obtient l'instance singleton du ProblemManager.
33
- * Doit être initialisé une fois avec `init`.
32
+ * Retrieves the ProblemManager singleton instance.
33
+ * Must be initialized with configPath and store.
34
34
  */
35
35
  public static function getInstance(?string $configPath = null, ?IStore $store = null): self
36
36
  {
@@ -39,7 +39,7 @@ class ProblemManager
39
39
  $defaultPath = dirname(__DIR__, 2) . '/config/problems.config.json';
40
40
  $configPath = file_exists($defaultPath) ? $defaultPath : null;
41
41
  }
42
- // Si on essaie d'obtenir l'instance sans l'initialiser d'abord, c'est une erreur.
42
+ // Error if accessed before initialization
43
43
  if ($configPath === null || $store === null) {
44
44
  throw new \RuntimeException("ProblemManager must be initialized with configPath and store.");
45
45
  }
@@ -53,7 +53,7 @@ class ProblemManager
53
53
  return self::$instance !== null && self::$instance->initialized;
54
54
  }
55
55
  /**
56
- * Charge et parse les problèmes depuis le fichier de configuration.
56
+ * Loads and parses problem definitions from configuration file.
57
57
  */
58
58
 
59
59
  private function loadProblems(): void
@@ -83,7 +83,7 @@ class ProblemManager
83
83
  }
84
84
  $problem['state'] = $storedState;
85
85
 
86
- // Résolution dynamique des fonctions et des données
86
+ // Dynamic function and data resolution
87
87
  if (isset($problem['workUnit']['scoreFunction'])) {
88
88
  $problem['workUnit']['scoreFunction'] = FunctionRegistry::get($problem['workUnit']['scoreFunction']);
89
89
  if ($problem['workUnit']['scoreFunction'] === null) {
@@ -105,7 +105,7 @@ class ProblemManager
105
105
  }
106
106
  $this->problems[] = $problem;
107
107
  }
108
- $this->initialized = true; // Marquer comme initialisé seulement après un chargement réussi
108
+ $this->initialized = true; // Mark initialized only after successful load
109
109
  }
110
110
 
111
111
  public function dispatchWork(float $suspicionFactor): ?array
@@ -175,10 +175,10 @@ class ProblemManager
175
175
  }
176
176
 
177
177
  /**
178
- * Intègre la solution d'un client dans l'état du problème.
178
+ * Integrates client solution into problem state.
179
179
  *
180
- * @param string $problemId L'ID du problème.
181
- * @param array $solutionData La solution renvoyée par le client.
180
+ * @param string $problemId Problem identifier.
181
+ * @param array $solutionData Client-submitted solution payload.
182
182
  */
183
183
  public function integrateSolution(string $problemId, array $solutionData): void
184
184
  {
@@ -192,13 +192,13 @@ class ProblemManager
192
192
 
193
193
  $storeKey = "problem-state:{$problem['id']}";
194
194
 
195
- // La logique d'intégration dépend du type de problème.
195
+ // Integration logic depends on problem type
196
196
  switch ($problem['workUnit']['type']) {
197
197
  case 'simulated_annealing_iterations':
198
198
  if (isset($solutionData['solution']) && isset($solutionData['energy'])) {
199
199
  $scoreFunction = $problem['workUnit']['scoreFunction'] ?? null;
200
200
  if (!$scoreFunction) {
201
- error_log("[ProblemManager] Aucune fonction de score définie pour {$problemId}.");
201
+ error_log("[ProblemManager] No score function defined for {$problemId}.");
202
202
  return;
203
203
  }
204
204
  // DoS mitigation: validate solution size and structure
@@ -211,15 +211,15 @@ class ProblemManager
211
211
  error_log("[ProblemManager] Solution payload size exceeds safe limit for {$problemId}.");
212
212
  return;
213
213
  }
214
- // 1. Ne JAMAIS faire confiance au score du client. Recalculer systématiquement.
214
+ // 1. Never trust client-reported score. Recalculate server-side.
215
215
  $recalculatedEnergy = $scoreFunction($solutionData['solution'], $problem['payload'] ?? []);
216
216
 
217
217
  $currentBest = (float)($problem['state']['bestEnergy'] ?? INF);
218
218
 
219
- // 2. Comparer le score recalculé, pas celui du client.
219
+ // 2. Compare verified score
220
220
  if ($recalculatedEnergy < $currentBest) { // @phpstan-ignore-line
221
221
  $problem['state']['bestSolution'] = $solutionData['solution'];
222
- $problem['state']['bestEnergy'] = $recalculatedEnergy; // 3. Stocker le score vérifié.
222
+ $problem['state']['bestEnergy'] = $recalculatedEnergy; // 3. Store verified score
223
223
  $problem['state']['lastUpdate'] = (new \DateTime())->format(\DateTime::ATOM);
224
224
  $stateChanged = true;
225
225
  error_log("[ProblemManager] New best solution for {$problemId}: {$recalculatedEnergy}"); // @phpstan-ignore-line
@@ -239,7 +239,7 @@ class ProblemManager
239
239
  }
240
240
  }
241
241
 
242
- // VÉRIFICATION PAR ÉCHANTILLONNAGE (Parité avec JS)
242
+ // Sample-based verification (parity with JS)
243
243
  $fitnessFunction = FunctionRegistry::get('portfolio.calculateMetrics');
244
244
  if ($fitnessFunction) {
245
245
  $sampleSize = min(5, count($solutionData['population']));
@@ -251,8 +251,8 @@ class ProblemManager
251
251
  $recalculated = $fitnessFunction($individual['chromosome'], $problem['payload'] ?? []);
252
252
  if (isset($individual['fitness']) && $individual['fitness'] !== -1) {
253
253
  if (abs($individual['fitness'] - $recalculated) > 1e-4) {
254
- error_log("[ProblemManager] Triche détectée pour {$problemId}! Fitness déclaré: {$individual['fitness']}, recalculé: {$recalculated}");
255
- return; // Rejeter en cas d'incohérence
254
+ error_log("[ProblemManager] Cheating detected for {$problemId}! Reported: {$individual['fitness']}, recalculated: {$recalculated}");
255
+ return; // Reject inconsistent population
256
256
  }
257
257
  }
258
258
  $solutionData['population'][$key]['fitness'] = $recalculated;
@@ -266,7 +266,7 @@ class ProblemManager
266
266
  }
267
267
  break;
268
268
  case 'multi_objective_genetic_algorithm':
269
- // Pour les algorithmes génétiques, on intègre le nouveau front de Pareto.
269
+ // Merge candidate Pareto front for multi-objective problems
270
270
  if (isset($solutionData['paretoFront']) && is_array($solutionData['paretoFront'])) {
271
271
  $stateChanged = $this->_integrateParetoFront($problem, $solutionData['paretoFront']);
272
272
  }
@@ -275,24 +275,21 @@ class ProblemManager
275
275
  error_log("[ProblemManager] Integration not implemented for useful work type: {$problem['workUnit']['type']}");
276
276
  break;
277
277
  }
278
- // Sauvegarder l'état mis à jour dans le store.
278
+ // Persist updated state to datastore
279
279
  if ($stateChanged) {
280
280
  $this->store->set($storeKey, $problem['state']);
281
281
  }
282
282
  }
283
283
 
284
284
  /**
285
- * Intègre un nouveau front de Pareto dans l'état du problème.
285
+ * Integrates a new Pareto front into the problem state.
286
286
  *
287
- * @param array &$problem Le problème à mettre à jour (passé par référence).
288
- * @param array $newFront Le nouveau front de Pareto soumis par le client.
287
+ * @param array &$problem Target problem passed by reference.
288
+ * @param array $newFront Candidate Pareto front submitted by client.
289
289
  */
290
290
  private function _integrateParetoFront(array &$problem, array $newFront): bool
291
291
  {
292
- // Logique de fusion et de tri non-dominé pour mettre à jour le front de Pareto.
293
- // Pour cet exemple, nous remplaçons simplement le front, mais une vraie implémentation
294
- // fusionnerait les deux fronts et recalculerait le meilleur.
295
- // On vérifie si le nouveau front est différent de l'actuel pour éviter des écritures inutiles.
292
+ // Verify content differences before updating to prevent redundant writes
296
293
  $currentFront = $problem['state']['paretoFront'] ?? [];
297
294
  if (!empty($newFront) && json_encode($newFront) !== json_encode($currentFront)) {
298
295
  $problem['state']['paretoFront'] = $newFront;
@@ -304,9 +301,9 @@ class ProblemManager
304
301
  }
305
302
 
306
303
  /**
307
- * S'assure qu'un problème a une solution initiale. Si non, en génère une.
304
+ * Ensures that the problem has an initial solution. Generates one if missing.
308
305
  *
309
- * @param array &$problem Le problème à vérifier (passé par référence).
306
+ * @param array &$problem Problem passed by reference.
310
307
  */
311
308
  private function ensureInitialSolution(array &$problem): void
312
309
  {
@@ -328,7 +325,7 @@ class ProblemManager
328
325
  }
329
326
 
330
327
  /**
331
- * Formate l'état d'un problème pour l'export externe.
328
+ * Formats problem state for external consumers.
332
329
  */
333
330
  private function formatSolution(array $problem): ?array
334
331
  {
@@ -354,10 +351,10 @@ class ProblemManager
354
351
  }
355
352
 
356
353
  /**
357
- * Récupère la meilleure solution actuellement connue pour un ou plusieurs problèmes.
354
+ * Retrieves the best known solution(s) for loaded problems.
358
355
  *
359
- * @param string|null $problemId L'ID optionnel du problème à consulter.
360
- * @return array|null Un tableau associatif ou une liste de tableaux associatifs.
356
+ * @param string|null $problemId Optional problem ID filter.
357
+ * @return array|null Associative array or list of solutions.
361
358
  */
362
359
  public function getBestSolutions(?string $problemId = null): ?array
363
360
  {
@@ -387,7 +384,7 @@ class ProblemManager
387
384
  }
388
385
 
389
386
  /**
390
- * Réinitialise l'instance singleton.
387
+ * Resets singleton instance.
391
388
  * @internal Uniquement pour les tests.
392
389
  */
393
390
  public static function resetInstanceForTests(): void
@@ -25,6 +25,7 @@ class RequestContext
25
25
  public ?string $httpVersion = null;
26
26
  public int $requestTimestamp = 0;
27
27
  public ?string $tlsSessionId = null;
28
+ public bool $isHttps = false;
28
29
 
29
30
  /** @var ?array{type: string, name: string} */
30
31
  public ?array $graphqlOperation = null;
@@ -67,7 +68,8 @@ class RequestContext
67
68
  $body,
68
69
  array $cookies,
69
70
  ?string $httpVersion,
70
- ?int $requestTimestamp = null
71
+ ?int $requestTimestamp = null,
72
+ ?bool $isHttps = null
71
73
  ) {
72
74
  $this->clientIp = $clientIp;
73
75
  $this->path = $path;
@@ -78,6 +80,11 @@ class RequestContext
78
80
  $this->cookies = $cookies;
79
81
  $this->httpVersion = $httpVersion;
80
82
  $this->requestTimestamp = $requestTimestamp ?? (int)(microtime(true) * 1000);
83
+ $this->isHttps = $isHttps ?? (
84
+ (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
85
+ (isset($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443) ||
86
+ ($this->getHeader('x-forwarded-proto') === 'https')
87
+ );
81
88
 
82
89
  // Extraire les empreintes TLS/HTTP2/TCP si elles sont fournies par les en-têtes
83
90
  $this->ja3 = $this->headers['x-ja3-hash'] ?? null;
@@ -5,43 +5,42 @@ declare(strict_types=1);
5
5
  namespace Anonympins\Fingerprint\Store;
6
6
 
7
7
  /**
8
- * Interface pour un système de stockage persistant.
9
- * Utilisé pour stocker les données des appareils, les secrets des challenges, etc.
8
+ * Interface for a persistent storage adapter (device data, challenge nonces, tickets).
10
9
  */
11
10
  interface IStore
12
11
  {
13
12
  /**
14
- * Récupère une valeur associée à une clé.
13
+ * Retrieves a value associated with a key.
15
14
  * @param string $key
16
15
  * @return mixed|null
17
16
  */
18
17
  public function get(string $key);
19
18
 
20
19
  /**
21
- * Stocke une valeur associée à une clé, avec une durée de vie optionnelle.
20
+ * Stores a value associated with a key, with an optional TTL.
22
21
  * @param string $key
23
22
  * @param mixed $value
24
- * @param int|null $ttl Durée de vie en secondes.
23
+ * @param int|null $ttl Time to live in seconds.
25
24
  * @return void
26
25
  */
27
26
  public function set(string $key, $value, ?int $ttl = null): void;
28
27
 
29
28
  /**
30
- * Vérifie si une clé existe dans le stockage.
29
+ * Checks whether a key exists in storage.
31
30
  * @param string $key
32
31
  * @return bool
33
32
  */
34
33
  public function has(string $key): bool;
35
34
 
36
35
  /**
37
- * Supprime une clé du stockage.
36
+ * Deletes a key from storage.
38
37
  * @param string $key
39
38
  * @return void
40
39
  */
41
40
  public function delete(string $key): void;
42
41
 
43
42
  /**
44
- * Supprime toutes les clés du stockage.
43
+ * Clears all keys from storage.
45
44
  * @return void
46
45
  */
47
46
  public function clear(): void;
@@ -1,67 +1,67 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Store;
6
-
7
- /**
8
- * Implémentation en mémoire de l'interface IStore.
9
- * Utile pour le développement ou les applications à instance unique sans persistance externe.
10
- */
11
- class InMemoryStore implements IStore
12
- {
13
- /**
14
- * @var array<string, array{value: mixed, expiresAt: int|null}>
15
- */
16
- private array $data = [];
17
-
18
- public function get(string $key)
19
- {
20
- if (!isset($this->data[$key])) {
21
- return null;
22
- }
23
-
24
- $item = $this->data[$key];
25
- if ($item['expiresAt'] !== null && $item['expiresAt'] < time()) {
26
- $this->delete($key); // Supprime l'élément expiré
27
- return null;
28
- }
29
-
30
- return $item['value'];
31
- }
32
-
33
- public function set(string $key, $value, ?int $ttl = null): void
34
- {
35
- $expiresAt = $ttl !== null ? time() + $ttl : null;
36
-
37
- $this->data[$key] = ['value' => $value, 'expiresAt' => $expiresAt];
38
- }
39
-
40
- public function has(string $key): bool
41
- {
42
- if (!isset($this->data[$key])) {
43
- return false;
44
- }
45
-
46
- $item = $this->data[$key];
47
- if ($item['expiresAt'] !== null && $item['expiresAt'] < time()) {
48
- $this->delete($key);
49
- return false;
50
- }
51
-
52
- return true;
53
- }
54
-
55
- public function delete(string $key): void
56
- {
57
- unset($this->data[$key]);
58
- }
59
-
60
- /**
61
- * Efface toutes les données du store. Utile pour les tests.
62
- */
63
- public function clear(): void
64
- {
65
- $this->data = [];
66
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Store;
6
+
7
+ /**
8
+ * In-memory implementation of the IStore interface.
9
+ * Suitable for local development, tests, or single-instance deployments.
10
+ */
11
+ class InMemoryStore implements IStore
12
+ {
13
+ /**
14
+ * @var array<string, array{value: mixed, expiresAt: int|null}>
15
+ */
16
+ private array $data = [];
17
+
18
+ public function get(string $key)
19
+ {
20
+ if (!isset($this->data[$key])) {
21
+ return null;
22
+ }
23
+
24
+ $item = $this->data[$key];
25
+ if ($item['expiresAt'] !== null && $item['expiresAt'] < time()) {
26
+ $this->delete($key); // Delete expired item
27
+ return null;
28
+ }
29
+
30
+ return $item['value'];
31
+ }
32
+
33
+ public function set(string $key, $value, ?int $ttl = null): void
34
+ {
35
+ $expiresAt = $ttl !== null ? time() + $ttl : null;
36
+
37
+ $this->data[$key] = ['value' => $value, 'expiresAt' => $expiresAt];
38
+ }
39
+
40
+ public function has(string $key): bool
41
+ {
42
+ if (!isset($this->data[$key])) {
43
+ return false;
44
+ }
45
+
46
+ $item = $this->data[$key];
47
+ if ($item['expiresAt'] !== null && $item['expiresAt'] < time()) {
48
+ $this->delete($key);
49
+ return false;
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ public function delete(string $key): void
56
+ {
57
+ unset($this->data[$key]);
58
+ }
59
+
60
+ /**
61
+ * Clears all data from the store. Useful for testing.
62
+ */
63
+ public function clear(): void
64
+ {
65
+ $this->data = [];
66
+ }
67
67
  }
@@ -5,8 +5,8 @@ declare(strict_types=1);
5
5
  namespace Anonympins\Fingerprint\Store;
6
6
 
7
7
  /**
8
- * Adaptateur de stockage MongoDB pour le moteur Fingerprint.
9
- * Utilise la bibliothèque officielle mongodb/mongodb.
8
+ * MongoDB storage adapter for the Fingerprint engine.
9
+ * Utilizes the official mongodb/mongodb driver.
10
10
  */
11
11
  class MongoDbStore implements IStore
12
12
  {
@@ -31,7 +31,7 @@ class MongoDbStore implements IStore
31
31
  private float $lastReconnectAttempt = 0.0;
32
32
 
33
33
  /**
34
- * @param \MongoDB\Collection $collection Collection dédiée au stockage des empreintes
34
+ * @param \MongoDB\Collection $collection Target collection for fingerprint data storage.
35
35
  */
36
36
  public function __construct($collection)
37
37
  {
@@ -69,7 +69,7 @@ class MongoDbStore implements IStore
69
69
  return null;
70
70
  }
71
71
 
72
- // Vérification d'expiration active pour bypasser le délai de 60s du démon de MongoDB
72
+ // Active TTL check to bypass MongoDB daemon 60-second cleanup latency
73
73
  if (isset($doc['expiresAt'])) {
74
74
  $expiresAt = $doc['expiresAt'];
75
75
  if ($expiresAt instanceof \MongoDB\BSON\UTCDateTime) {
@@ -168,7 +168,7 @@ class MongoDbStore implements IStore
168
168
  }
169
169
 
170
170
  /**
171
- * Automatise la configuration de l'index TTL nécessaire dans MongoDB.
171
+ * Automatically initializes required TTL index in MongoDB collection.
172
172
  */
173
173
  public function init(): void
174
174
  {
@@ -5,13 +5,13 @@ declare(strict_types=1);
5
5
  namespace Anonympins\Fingerprint\Store;
6
6
 
7
7
  /**
8
- * Adaptateur de stockage Redis pour le moteur Fingerprint.
9
- * Compatible avec phpredis et predis.
8
+ * Redis storage adapter for the Fingerprint engine.
9
+ * Compatible with phpredis and predis clients.
10
10
  */
11
11
  class RedisStore implements IStore
12
12
  {
13
13
  /**
14
- * @var mixed Une instance de \Redis ou de \Predis\Client
14
+ * @var mixed An instance of \Redis or \Predis\Client
15
15
  */
16
16
  private $redis;
17
17
 
@@ -31,7 +31,7 @@ class RedisStore implements IStore
31
31
  private float $lastReconnectAttempt = 0.0;
32
32
 
33
33
  /**
34
- * @param mixed $redis Client Redis déjà configuré et connecté
34
+ * @param mixed $redis Pre-configured and connected Redis client instance.
35
35
  */
36
36
  public function __construct($redis)
37
37
  {
@@ -46,7 +46,7 @@ class RedisStore implements IStore
46
46
  }
47
47
 
48
48
  $now = microtime(true);
49
- // Tentative de reconnexion toutes les 5 secondes
49
+ // Retry connection every 5 seconds
50
50
  if ($now - $this->lastReconnectAttempt > 5.0) {
51
51
  $this->lastReconnectAttempt = $now;
52
52
  try {
@@ -1,36 +1,36 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Store;
6
-
7
- /**
8
- * Gère l'instance du store global.
9
- */
10
- class StoreManager
11
- {
12
- private static ?IStore $store = null;
13
-
14
- public static function getStore(): IStore
15
- {
16
- if (self::$store === null) {
17
- self::$store = new InMemoryStore();
18
- }
19
- return self::$store;
20
- }
21
-
22
- public static function configureStore(IStore $externalStore): void
23
- {
24
- self::$store = $externalStore;
25
- }
26
-
27
- /**
28
- * Définit l'instance active du store (utile pour l'injection de dépendances et les tests).
29
- *
30
- * @param mixed $store
31
- */
32
- public static function setStore($store): void
33
- {
34
- self::$store = $store;
35
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Store;
6
+
7
+ /**
8
+ * Manages the global store singleton instance.
9
+ */
10
+ class StoreManager
11
+ {
12
+ private static ?IStore $store = null;
13
+
14
+ public static function getStore(): IStore
15
+ {
16
+ if (self::$store === null) {
17
+ self::$store = new InMemoryStore();
18
+ }
19
+ return self::$store;
20
+ }
21
+
22
+ public static function configureStore(IStore $externalStore): void
23
+ {
24
+ self::$store = $externalStore;
25
+ }
26
+
27
+ /**
28
+ * Sets the active store instance (useful for dependency injection and tests).
29
+ *
30
+ * @param mixed $store
31
+ */
32
+ public static function setStore($store): void
33
+ {
34
+ self::$store = $store;
35
+ }
36
36
  }