BenchmarkAbstract.php 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. /**
  3. * Class BenchmarkAbstract
  4. *
  5. * @created 23.04.2024
  6. * @author smiley <smiley@chillerlan.net>
  7. * @copyright 2024 smiley
  8. * @license MIT
  9. */
  10. declare(strict_types=1);
  11. namespace chillerlan\QRCodeBenchmark;
  12. use chillerlan\QRCode\{QRCode, QROptions};
  13. use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
  14. use chillerlan\QRCode\Data\QRMatrix;
  15. use chillerlan\QRCodeTest\Traits\QRMaxLengthTrait;
  16. use PhpBench\Attributes\{Iterations, ParamProviders, Revs, Warmup};
  17. use Generator, RuntimeException;
  18. use function extension_loaded, is_dir, mb_substr, mkdir, sprintf, str_repeat, str_replace;
  19. /**
  20. * The abstract benchmark with common methods
  21. */
  22. #[Iterations(3)]
  23. #[Warmup(3)]
  24. #[Revs(100)]
  25. #[ParamProviders(['versionProvider', 'eccLevelProvider', 'dataModeProvider'])]
  26. abstract class BenchmarkAbstract{
  27. use QRMaxLengthTrait;
  28. protected const BUILDDIR = __DIR__.'/../.build/phpbench/';
  29. protected const ECC_LEVELS = [EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H];
  30. protected const DATAMODES = Mode::INTERFACES;
  31. /** @var array<int, string> */
  32. protected array $dataModeData;
  33. protected string $testData;
  34. protected QROptions $options;
  35. protected QRMatrix $matrix;
  36. // properties from data providers
  37. protected Version $version;
  38. protected EccLevel $eccLevel;
  39. protected int $mode;
  40. protected string $modeFQCN;
  41. /**
  42. * @throws \RuntimeException
  43. */
  44. public function __construct(){
  45. foreach(['gd', 'imagick'] as $ext){
  46. if(!extension_loaded($ext)){
  47. throw new RuntimeException(sprintf('ext-%s not loaded', $ext));
  48. }
  49. }
  50. if(!is_dir(self::BUILDDIR)){
  51. mkdir(directory: self::BUILDDIR, recursive: true);
  52. }
  53. $this->dataModeData = $this->generateDataModeData();
  54. }
  55. /**
  56. * Generates test data strings for each mode
  57. *
  58. * @return array<int, string>
  59. */
  60. protected function generateDataModeData():array{
  61. return [
  62. Mode::NUMBER => str_repeat('0123456789', 750),
  63. Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
  64. Mode::KANJI => str_repeat('漂う花の香り', 350),
  65. Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
  66. Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
  67. ];
  68. }
  69. /**
  70. * Generates a test max-length data string for the given version, ecc level and data mode
  71. */
  72. protected function getData(Version $version, EccLevel $eccLevel, int $mode):string{
  73. $maxLength = self::getMaxLengthForMode($mode, $version, $eccLevel);
  74. if($mode === Mode::KANJI || $mode === Mode::HANZI){
  75. return mb_substr($this->dataModeData[$mode], 0, $maxLength);
  76. }
  77. return mb_substr($this->dataModeData[$mode], 0, $maxLength, '8bit');
  78. }
  79. /**
  80. * Initializes a QROptions instance and assigns it to its temp property
  81. *
  82. * @param array<string, mixed> $options
  83. */
  84. protected function initQROptions(array $options):void{
  85. $this->options = new QROptions($options);
  86. }
  87. /**
  88. * Initializes a QRMatrix instance and assigns it to its temp property
  89. */
  90. public function initMatrix():void{
  91. $this->matrix = (new QRCode($this->options))
  92. ->addByteSegment($this->testData)
  93. ->getQRMatrix()
  94. ;
  95. }
  96. /**
  97. * Generates a test data string and assigns it to its temp property
  98. */
  99. public function generateTestData():void{
  100. $this->testData = $this->getData($this->version, $this->eccLevel, $this->mode);
  101. }
  102. /**
  103. * Assigns the parameter array from the providers to properties and enforces the types
  104. *
  105. * @param array<string, mixed> $params
  106. */
  107. public function assignParams(array $params):void{
  108. foreach($params as $k => $v){
  109. $this->{$k} = $v;
  110. }
  111. }
  112. public function versionProvider():Generator{
  113. // run all versions between 1 and 10 as they're the most commonly used
  114. for($v = 1; $v <= 10; $v++){
  115. yield (string)$v => ['version' => new Version($v)];
  116. }
  117. // 15-40 in steps of 5
  118. for($v = 15; $v <= 40; $v += 5){
  119. yield (string)$v => ['version' => new Version($v)];
  120. }
  121. }
  122. public function eccLevelProvider():Generator{
  123. foreach(static::ECC_LEVELS as $ecc){
  124. $eccLevel = new EccLevel($ecc);
  125. yield (string)$eccLevel => ['eccLevel' => $eccLevel];
  126. }
  127. }
  128. public function dataModeProvider():Generator{
  129. foreach(static::DATAMODES as $mode => $modeFQCN){
  130. $name = str_replace('chillerlan\\QRCode\\Data\\', '', $modeFQCN);
  131. yield $name => ['mode' => $mode, 'modeFQCN' => $modeFQCN];
  132. }
  133. }
  134. }