Number.php 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <?php
  2. /**
  3. * Class Number
  4. *
  5. * @filesource Number.php
  6. * @created 26.11.2015
  7. * @package chillerlan\QRCode\Data
  8. * @author Smiley <smiley@chillerlan.net>
  9. * @copyright 2015 Smiley
  10. * @license MIT
  11. */
  12. namespace chillerlan\QRCode\Data;
  13. use chillerlan\QRCode\Helpers\BitBuffer;
  14. use chillerlan\QRCode\Common\Mode;
  15. use function ceil, ord, sprintf, str_split, substr;
  16. /**
  17. * Numeric mode: decimal digits 0 to 9
  18. *
  19. * ISO/IEC 18004:2000 Section 8.3.2
  20. * ISO/IEC 18004:2000 Section 8.4.2
  21. */
  22. final class Number extends QRDataModeAbstract{
  23. /**
  24. * @var int[]
  25. */
  26. protected const CHAR_MAP_NUMBER = [
  27. '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
  28. ];
  29. protected int $datamode = Mode::DATA_NUMBER;
  30. /**
  31. * @inheritdoc
  32. */
  33. public function getLengthInBits():int{
  34. return (int)ceil($this->getCharCount() * (10 / 3));
  35. }
  36. /**
  37. * @inheritdoc
  38. */
  39. public static function validateString(string $string):bool{
  40. foreach(str_split($string) as $chr){
  41. if(!isset(self::CHAR_MAP_NUMBER[$chr])){
  42. return false;
  43. }
  44. }
  45. return true;
  46. }
  47. /**
  48. * @inheritdoc
  49. */
  50. public function write(BitBuffer $bitBuffer, int $version):void{
  51. $len = $this->getCharCount();
  52. $bitBuffer
  53. ->put($this->datamode, 4)
  54. ->put($len, Mode::getLengthBitsForVersion($this->datamode, $version))
  55. ;
  56. $i = 0;
  57. // encode numeric triplets in 10 bits
  58. while($i + 2 < $len){
  59. $bitBuffer->put($this->parseInt(substr($this->data, $i, 3)), 10);
  60. $i += 3;
  61. }
  62. if($i < $len){
  63. // encode 2 remaining numbers in 7 bits
  64. if($len - $i === 2){
  65. $bitBuffer->put($this->parseInt(substr($this->data, $i, 2)), 7);
  66. }
  67. // encode one remaining number in 4 bits
  68. elseif($len - $i === 1){
  69. $bitBuffer->put($this->parseInt(substr($this->data, $i, 1)), 4);
  70. }
  71. }
  72. }
  73. /**
  74. * get the code for the given numeric string
  75. *
  76. * @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
  77. */
  78. protected function parseInt(string $string):int{
  79. $num = 0;
  80. foreach(str_split($string) as $chr){
  81. $c = ord($chr);
  82. if(!isset(self::CHAR_MAP_NUMBER[$chr])){
  83. throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
  84. }
  85. $c = $c - 48; // ord('0')
  86. $num = $num * 10 + $c;
  87. }
  88. return $num;
  89. }
  90. }