|
|
@@ -0,0 +1,148 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * Class BenchmarkAbstract
|
|
|
+ *
|
|
|
+ * @created 23.04.2024
|
|
|
+ * @author smiley <smiley@chillerlan.net>
|
|
|
+ * @copyright 2024 smiley
|
|
|
+ * @license MIT
|
|
|
+ */
|
|
|
+declare(strict_types=1);
|
|
|
+
|
|
|
+namespace chillerlan\QRCodeBenchmark;
|
|
|
+
|
|
|
+use chillerlan\QRCode\{QRCode, QROptions};
|
|
|
+use chillerlan\QRCode\Common\{EccLevel, Mode, Version};
|
|
|
+use chillerlan\QRCode\Data\QRMatrix;
|
|
|
+use chillerlan\QRCodeTest\QRMaxLengthTrait;
|
|
|
+use PhpBench\Attributes\{Iterations, ParamProviders, Revs, Warmup};
|
|
|
+use Generator, RuntimeException;
|
|
|
+use function extension_loaded, is_dir, mb_substr, mkdir, sprintf, str_repeat, str_replace;
|
|
|
+
|
|
|
+/**
|
|
|
+ * The abstract benchmark with common methods
|
|
|
+ */
|
|
|
+#[Iterations(3)]
|
|
|
+#[Warmup(3)]
|
|
|
+#[Revs(100)]
|
|
|
+#[ParamProviders(['versionProvider', 'eccLevelProvider', 'dataModeProvider'])]
|
|
|
+abstract class BenchmarkAbstract{
|
|
|
+ use QRMaxLengthTrait;
|
|
|
+
|
|
|
+ protected const BUILDDIR = __DIR__.'/../.build/phpbench/';
|
|
|
+ protected const ECC_LEVELS = [EccLevel::L, EccLevel::M, EccLevel::Q, EccLevel::H];
|
|
|
+ protected const DATAMODES = Mode::INTERFACES;
|
|
|
+
|
|
|
+ protected array $dataModeData;
|
|
|
+ protected string $testData;
|
|
|
+ protected QROptions $options;
|
|
|
+ protected QRMatrix $matrix;
|
|
|
+
|
|
|
+ // properties from data providers
|
|
|
+ protected Version $version;
|
|
|
+ protected EccLevel $eccLevel;
|
|
|
+ protected int $mode;
|
|
|
+ protected string $modeFQCN;
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ */
|
|
|
+ public function __construct(){
|
|
|
+
|
|
|
+ foreach(['gd', 'imagick'] as $ext){
|
|
|
+ if(!extension_loaded($ext)){
|
|
|
+ throw new RuntimeException(sprintf('ext-%s not loaded', $ext));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!is_dir(self::BUILDDIR)){
|
|
|
+ mkdir(directory: self::BUILDDIR, recursive: true);
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->dataModeData = $this->generateDataModeData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Generates test data strings for each mode
|
|
|
+ */
|
|
|
+ protected function generateDataModeData():array{
|
|
|
+ return [
|
|
|
+ Mode::NUMBER => str_repeat('0123456789', 750),
|
|
|
+ Mode::ALPHANUM => str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:', 100),
|
|
|
+ Mode::KANJI => str_repeat('漂う花の香り', 350),
|
|
|
+ Mode::HANZI => str_repeat('无可奈何燃花作香', 250),
|
|
|
+ Mode::BYTE => str_repeat('https://www.youtube.com/watch?v=dQw4w9WgXcQ ', 100),
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Generates a test max-length data string for the given version, ecc level and data mode
|
|
|
+ */
|
|
|
+ protected function getData(Version $version, EccLevel $eccLevel, int $mode):string{
|
|
|
+ $maxLength = self::getMaxLengthForMode($mode, $version, $eccLevel);
|
|
|
+
|
|
|
+ if($mode === Mode::KANJI || $mode === Mode::HANZI){
|
|
|
+ return mb_substr($this->dataModeData[$mode], 0, $maxLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ return mb_substr($this->dataModeData[$mode], 0, $maxLength, '8bit');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initializes a QROptions instance and assigns it to its temp property
|
|
|
+ */
|
|
|
+ protected function initQROptions(array $options):void{
|
|
|
+ $this->options = new QROptions($options);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Initializes a QRMatrix instance and assigns it to its temp property
|
|
|
+ */
|
|
|
+ public function initMatrix():void{
|
|
|
+ $this->matrix = (new QRCode($this->options))
|
|
|
+ ->addByteSegment($this->testData)
|
|
|
+ ->getQRMatrix()
|
|
|
+ ;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Generates a test data string and assigns it to its temp property
|
|
|
+ */
|
|
|
+ public function generateTestData():void{
|
|
|
+ $this->testData = $this->getData($this->version, $this->eccLevel, $this->mode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Assigns the parameter array from the providers to properties and enforces the types
|
|
|
+ */
|
|
|
+ public function assignParams(array $params):void{
|
|
|
+ foreach($params as $k => $v){
|
|
|
+ $this->{$k} = $v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function versionProvider():Generator{
|
|
|
+ for($v = 1; $v <= 40; $v++){
|
|
|
+ $version = new Version($v);
|
|
|
+
|
|
|
+ yield (string)$version => ['version' => $version];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function eccLevelProvider():Generator{
|
|
|
+ foreach(static::ECC_LEVELS as $ecc){
|
|
|
+ $eccLevel = new EccLevel($ecc);
|
|
|
+
|
|
|
+ yield (string)$eccLevel => ['eccLevel' => $eccLevel];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function dataModeProvider():Generator{
|
|
|
+ foreach(static::DATAMODES as $mode => $modeFQCN){
|
|
|
+ $name = str_replace('chillerlan\\QRCode\\Data\\', '', $modeFQCN);
|
|
|
+
|
|
|
+ yield $name => ['mode' => $mode, 'modeFQCN' => $modeFQCN];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|