URL: https://linuxfr.org/users/pere_noel/journaux/taptempo-en-php Title: TapTempo en PHP Authors: Christie Poutrelle Date: 2018年03月12日T19:06:06+01:00 License: CC By-SA Tags: taptempo et php Score: 2 TapTempo, c'est plus ce que c'était, on a vu a de tout, mais pas encore de PHP, redonnons lui sa gloire. J'ai écrit plusieurs versions différentes de ce code, mais je vais seulement en donner une, que je trouve intéressante pour plusieurs raisons. Déjà, le PHP en lui même donne un code plutôt lisible, et sans surprise, ensuite: * PHP n'a pas de types de liste avancés (ou peu) dans sa bibliothèque standard, j'ai opté ici pour une implémentation très primitive d'un ring buffer pour compter les entrées. Aussi bizarre que ça puisse paraître, je pense pas qu'on puisse faire plus performant (cf. la classe `TapRing` dans le code), * la magie de la fonction `overwriteLine()` que vous pouvez admirer, hideuse mais fonctionnelle (merci au composant console de Symfony qui m'a donné cette solution), * le trick bien hideux qui va avec la ligne `\system("stty -icanon");` trouvé au hasard sur Stack Overflow (une méthode plus native existe, je n'ai juste pas pris le temps de ~~la copier-coller~~ l'implémenter). Maintenant passons aux points positive, avec quelques évolution récentes du language : * l'utilisation de `declare(strict_types=1);` qui rend le typage strict (et oui, PHP a ça maintenant, malheureusement, ce n'est pas statique mais reste dynamique, à savoir que c'est évalué à l’exécution et non à la compilation), * il en va de même pour le typage utilisé dans l'ensemble du code, * bien entendu, le code vit dans son propre namespace, d'où les `\` précédent les appels de fonctions de la bibliothèque standard qui vivent dans le namespace global, * et puis c'est tout, mais c'est pas si mal. Roulements de tambours, voici le code: ```php #!/usr/bin/env php thresold = $validityThreshold; } public function tap(): void { $this->buffer[$this->index] = \microtime(true); $this->index = ($this->index + 1) % self::MAX_SIZE; } public function getTempo(): int { $now = microtime(true); $first = $last = 0; $count = 0; foreach ($this->buffer as $value) { if (!$value) { continue; // Unused buffer position } if ($value < $now - $this->thresold) { continue; // This value is too old to be used } if (!$first || $value < $first) { $first = $value; } if (!$last || $value> $last) { $last = $value; } ++$count; } if ($count> 2) { return (int)\round($count / ($last - $first) * 60, 0); } return 0; } public function reset(): void { $this->buffer = []; $this->index = 0; } } function overwriteLine(string $message): void { print "\x0D"; // Move the cursor to the beginning of the line print "\x1B[2K"; // Erase the line print str_repeat("\x1B[1A\x1B[2K", 1); // Erase previous line print $message; } /** * Main loop */ function loop(): void { $buffer = new TapRing(); do { // fread() would to the trick too $char = \stream_get_contents(STDIN, 1); if ('q' === $char) { print "\n"; break; } else if (10 === \ord($char)) { // 10 is Enter key ASCII code $buffer->tap(); } overwriteLine(\str_pad((string)$buffer->getTempo(), 10, " ", STR_PAD_LEFT)); } while (true); } print "Appuyez sur la touche entrée en cadence (\"q\" pour quitter)\n"; // Only hack in this file, please read: // https://stackoverflow.com/a/3684565 // @todo write it using https://stackoverflow.com/a/21628935 instead \system("stty -icanon"); loop(); ``` Et pour les barbus, l'OPCode généré pour la fonction `TapTempo::tap()`: ``` function name: tap L33-37 TapTempo\TapRing::tap() /home/pounard/taptempo/vanilla.php - 0x7f084c088000 + 14 ops L35 #0 FETCH_OBJ_R THIS "index" @1 L35 #1 INIT_FCALL<1> 96 "microtime" L35 #2 SEND_VAL true 1 L35 #3 DO_ICALL @3 L35 #4 FETCH_OBJ_W THIS "buffer" @0 L35 #5 ASSIGN_DIM @0 @1 L35 #6 OP_DATA @3 L36 #7 FETCH_OBJ_R THIS "index" @5 L36 #8 ADD @5 1 ~6 L36 #9 FETCH_CLASS_CONSTANT "MAX_SIZE" ~7 L36 #10 MOD ~6 ~7 ~8 L36 #11 ASSIGN_OBJ THIS "index" L36 #12 OP_DATA ~8 L37 #13 RETURN<-1> null ```