diff --git a/.github/workflows/static_tests.yml b/.github/workflows/static_tests.yml
new file mode 100644
index 0000000..9e06766
--- /dev/null
+++ b/.github/workflows/static_tests.yml
@@ -0,0 +1,19 @@
+name: Static Tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ static_tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.1
+ coverage: none # disable xdebug, pcov
+ - run: composer install --no-progress --no-interaction --no-suggest
+ - run: composer static-tests
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..39e4f7e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,45 @@
+name: Tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - master
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php:
+ - "8.1"
+ dependency-version:
+ # - prefer-lowest
+ - prefer-stable
+
+ name: PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} - tests
+ steps:
+ # basically git clone
+ - uses: actions/checkout@v2
+
+ - name: Setup Git
+ run: |
+ git --version
+ git config --global user.email "test@github.com"
+ git config --global user.name "GitHub Action"
+ git --version
+
+ - name: Setup PHP
+ # use PHP of specific version
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none # disable xdebug, pcov
+ tools: composer
+
+ - name: Install Composer Dependencies
+ run: |
+ composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
+
+ - name: Run PHPUnit Tests
+ run: composer tests
diff --git a/README.md b/README.md
index 31b5d9e..75c7735 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ $text = $qrcode->text(); //return decoded text from QR Code
```
## Requirements
-* PHP >= 5.6
+* PHP >= 8.1
* GD Library
diff --git a/composer.json b/composer.json
index da466df..26a03c7 100644
--- a/composer.json
+++ b/composer.json
@@ -21,12 +21,13 @@
}
],
"require": {
- "php": ">=5.6"
+ "php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0",
+ "phpunit/phpunit": "^7.5 | ^8.0 | ^9.0",
"rector/rector": "^0.13.6",
- "symplify/easy-coding-standard": "^11.0"
+ "symplify/easy-coding-standard": "^11.0",
+ "vimeo/psalm": "^4.24"
},
"autoload": {
"psr-4": {
@@ -39,7 +40,8 @@
"scripts": {
"check-cs": "./vendor/bin/ecs check",
"fix-cs": "./vendor/bin/ecs check --fix",
- "tests": "./vendor/bin/phpunit"
+ "tests": "./vendor/bin/phpunit",
+ "static-tests": "./vendor/bin/psalm --php-version=8.1"
},
"autoload-dev": {
"psr-4": {
diff --git a/lib/Binarizer.php b/lib/Binarizer.php
index ef7a129..33c7322 100644
--- a/lib/Binarizer.php
+++ b/lib/Binarizer.php
@@ -31,8 +31,8 @@
abstract class Binarizer
{
protected function __construct(private $source)
- {
- }
+ {
+ }
/**
* @return LuminanceSource
@@ -50,14 +50,14 @@ final public function getLuminanceSource()
* and passed in with each call for performance. However it is legal to keep more than one row
* at a time if needed.
*
- * @param $y The row to fetch, which must be in [0, bitmap height)
- * @param An $row optional preallocated array. If null or too small, it will be ignored.
+ * @param int $y The row to fetch, which must be in [0, bitmap height)
+ * @param BitArray|null $row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
*
- * @return array The array of bits for this row (true means black).
+ * @return BitArray The array of bits for this row (true means black).
* @throws NotFoundException if row can't be binarized
*/
- abstract public function getBlackRow($y, $row);
+ abstract public function getBlackRow(int $y, ?BitArray $row = null): BitArray;
/**
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
diff --git a/lib/BinaryBitmap.php b/lib/BinaryBitmap.php
index 352b842..12a368d 100644
--- a/lib/BinaryBitmap.php
+++ b/lib/BinaryBitmap.php
@@ -60,13 +60,14 @@ public function getHeight()
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
*
* @param $y The row to fetch, which must be in [0, bitmap height)
- * @param An $row optional preallocated array. If null or too small, it will be ignored.
+ * @param array|null $row An optional preallocated array. If null or too small, it will be ignored.
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
*
- * @return array The array of bits for this row (true means black).
+ * @return Common\BitArray The array of bits for this row (true means black).
+ *
* @throws NotFoundException if row can't be binarized
*/
- public function getBlackRow($y, $row)
+ public function getBlackRow($y, $row): Common\BitArray
{
return $this->binarizer->getBlackRow($y, $row);
}
@@ -98,7 +99,7 @@ public function crop($left, $top, $width, $height): \Zxing\BinaryBitmap
}
/**
- * @return Whether this bitmap supports counter-clockwise rotation.
+ * @return bool this Whether bitmap supports counter-clockwise rotation.
*/
public function isRotateSupported()
{
@@ -131,7 +132,7 @@ public function rotateCounterClockwise45(): \Zxing\BinaryBitmap
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
}
- public function toString()
+ public function toString(): string
{
try {
return $this->getBlackMatrix()->toString();
diff --git a/lib/ChecksumException.php b/lib/ChecksumException.php
index db88c58..b65cb84 100644
--- a/lib/ChecksumException.php
+++ b/lib/ChecksumException.php
@@ -27,7 +27,7 @@ final class ChecksumException extends ReaderException
{
private static ?\Zxing\ChecksumException $instance = null;
- public static function getChecksumInstance($cause = null)
+ public static function getChecksumInstance($cause = null): self
{
if (self::$isStackTrace) {
return new ChecksumException($cause);
diff --git a/lib/Common/AbstractEnum.php b/lib/Common/AbstractEnum.php
index 04efe97..72f33e2 100644
--- a/lib/Common/AbstractEnum.php
+++ b/lib/Common/AbstractEnum.php
@@ -10,8 +10,9 @@
final class AbstractEnum implements \Stringable
{
/**
- * Default value.
- */
+ * Default value.
+ * @var null
+ */
public const __default = null;
/**
* Current value.
@@ -24,7 +25,7 @@ final class AbstractEnum implements \Stringable
*
* @var array|null
*/
- private ?array $constants = null;
+ private ?array $constants = null;
/**
* Creates a new enum.
@@ -53,14 +54,14 @@ public function change($value)
}
/**
- * Gets all constants (possible values) as an array.
- *
- * @param boolean $includeDefault
- *
- * @return array
- */
- public function getConstList($includeDefault = true)
+ * Gets all constants (possible values) as an array.
+ *
+ *
+ * @return array
+ */
+ public function getConstList(bool $includeDefault = true)
{
+ $constants = [];
if ($this->constants === null) {
$reflection = new ReflectionClass($this);
$this->constants = $reflection->getConstants();
diff --git a/lib/Common/BitArray.php b/lib/Common/BitArray.php
index 248400f..f526a4c 100644
--- a/lib/Common/BitArray.php
+++ b/lib/Common/BitArray.php
@@ -33,13 +33,13 @@
final class BitArray
{
/**
- * @var mixed[]|mixed|int[]|null
+ * @var mixed[]|int[]|null
*/
- private $bits;
+ private $bits;
/**
* @var mixed|null
*/
- private $size;
+ private $size;
public function __construct($bits = [], $size = 0)
@@ -56,7 +56,10 @@ public function __construct($bits = [], $size = 0)
}
}
- private static function makeArray($size)
+ /**
+ * @psalm-return array
+ */
+ private static function makeArray($size): array
{
return [];
}
@@ -74,7 +77,7 @@ public function getSizeInBytes()
/**
* Sets bit i.
*
- * @param bit $i to set
+ * @param int $i bit to set
*/
public function set($i): void
{
@@ -85,7 +88,7 @@ public function set($i): void
/**
* Flips bit i.
*
- * @param bit $i to set
+ * @param int $i bit to set
*/
public function flip($i): void
{
@@ -94,9 +97,9 @@ public function flip($i): void
}
/**
- * @param first $from bit to check
+ * @param int $from first bit to check
*
- * @return index of first bit that is set, starting from the given index, or size if none are set
+ * @return int index of first bit that is set, starting from the given index, or size if none are set
* at or beyond this given index
* @see #getNextUnset(int)
*/
@@ -121,9 +124,9 @@ public function getNextSet($from)
}
/**
- * @param index $from to start looking for unset bit
+ * @param int $from index to start looking for unset bit
*
- * @return index of next unset bit, or {@code size} if none are unset until the end
+ * @return int index of next unset bit, or {@code size} if none are unset until the end
* @see #getNextSet(int)
*/
public function getNextUnset($from)
@@ -149,8 +152,8 @@ public function getNextUnset($from)
/**
* Sets a block of 32 bits, starting at bit i.
*
- * @param first $i bit to set
- * @param the $newBits new value of the next 32 bits. Note again that the least-significant bit
+ * @param int $i first bit to set
+ * @param int $newBits the new value of the next 32 bits. Note again that the least-significant bit
* corresponds to bit i, the next-least-significant to i+1, and so on.
*/
public function setBulk($i, $newBits): void
@@ -161,8 +164,10 @@ public function setBulk($i, $newBits): void
/**
* Sets a range of bits.
*
- * @param start $start of range, inclusive.
- * @param end $end of range, exclusive
+ * @param int $start start of range, inclusive.
+ * @param int $end end of range, exclusive
+ *
+ * @return void
*/
public function setRange($start, $end)
{
@@ -205,14 +210,15 @@ public function clear(): void
/**
* Efficient method to check if a range of bits is set, or not set.
*
- * @param start $start of range, inclusive.
- * @param end $end of range, exclusive
- * @param if $value true, checks that bits in range are set, otherwise checks that they are not set
+ * @param int $start start of range, inclusive.
+ * @param int $end end of range, exclusive
+ * @param bool $value if true, checks that bits in range are set, otherwise checks that they are not set
*
- * @return true iff all bits are set or not set in range, according to value argument
- * @throws InvalidArgumentException if end is less than or equal to start
+ * @return bool iff all bits are set or not set in range, according to value argument
+ *
+ * @throws \InvalidArgumentException if end is less than or equal to start
*/
- public function isRange($start, $end, $value)
+ public function isRange($start, $end, $value): bool
{
if ($end < $start) {
throw new \InvalidArgumentException();
@@ -251,10 +257,10 @@ public function isRange($start, $end, $value)
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*
- * @param $value {@code int} containing bits to append
- * @param bits $numBits from value to append
+ * @param int $value {@code int} containing bits to append
+ * @param int $numBits bits from value to append
*/
- public function appendBits($value, $numBits)
+ public function appendBits($value, $numBits): void
{
if ($numBits < 0 || $numBits > 32) {
throw new \InvalidArgumentException("Num bits must be between 0 and 32");
@@ -274,7 +280,7 @@ private function ensureCapacity($size): void
}
}
- public function appendBit($bit): void
+ public function appendBit(bool $bit): void
{
$this->ensureCapacity($this->size + 1);
if ($bit) {
@@ -292,7 +298,7 @@ public function appendBitArray($other): void
}
}
- public function _xor($other)
+ public function _xor($other): void
{
if ((is_countable($this->bits) ? count($this->bits) : 0) !== (is_countable($other->bits) ? count($other->bits) : 0)) {
throw new \InvalidArgumentException("Sizes don't match");
@@ -307,13 +313,13 @@ public function _xor($other)
/**
*
- * @param first $bitOffset bit to start writing
+ * @param int $bitOffset first bit to start writing
* @param array $array to write into. Bytes are written most-significant byte first. This is the opposite
* of the internal representation, which is exposed by {@link #getBitArray()}
- * @param position $offset in array to start writing
- * @param how $numBytes many bytes to write
+ * @param int $offset position in array to start writing
+ * @param int $numBytes how many bytes to write
*/
- public function toBytes($bitOffset, &$array, $offset, $numBytes): void
+ public function toBytes($bitOffset, array &$array, $offset, $numBytes): void
{
for ($i = 0; $i < $numBytes; $i++) {
$theByte = 0;
@@ -329,10 +335,11 @@ public function toBytes($bitOffset, &$array, $offset, $numBytes): void
/**
* @param $i ; bit to get
+ * @param float|int first $i
*
- * @return true iff bit i is set
+ * @return bool iff bit i is set
*/
- public function get($i)
+ public function get(int|float $i): bool
{
$key = (int)($i / 32);
@@ -340,10 +347,11 @@ public function get($i)
}
/**
- * @return array underlying array of ints. The first element holds the first 32 bits, and the least
- * significant bit is bit 0.
+ * @return (int|mixed)[]|null underlying array of ints. The first element holds the first 32 bits, and the least significant bit is bit 0.
+ *
+ * @psalm-return array|null
*/
- public function getBitArray()
+ public function getBitArray(): array|null
{
return $this->bits;
}
@@ -390,7 +398,7 @@ public function reverse(): void
// $bits = $newBits;
}
- public function equals($o)
+ public function equals($o): bool
{
if (!($o instanceof BitArray)) {
return false;
@@ -405,7 +413,7 @@ public function hashCode()
return 31 * $this->size + hashCode($this->bits);
}
- public function toString()
+ public function toString(): string
{
$result = '';
for ($i = 0; $i < $this->size; $i++) {
diff --git a/lib/Common/BitMatrix.php b/lib/Common/BitMatrix.php
index e9691fc..442cd0c 100644
--- a/lib/Common/BitMatrix.php
+++ b/lib/Common/BitMatrix.php
@@ -10,7 +10,7 @@ final class BitMatrix
/**
* @var mixed|int[]
*/
- private $bits;
+ private $bits;
public function __construct($width, $height = false, $rowSize = false, $bits = false)
{
@@ -30,7 +30,7 @@ public function __construct($width, $height = false, $rowSize = false, $bits = f
$this->bits = $bits;
}
- public static function parse($stringRepresentation, $setString, $unsetString)
+ public static function parse($stringRepresentation, $setString, $unsetString): self
{
if (!$stringRepresentation) {
throw new \InvalidArgumentException();
@@ -94,8 +94,10 @@ public static function parse($stringRepresentation, $setString, $unsetString)
*
* @param $x ; The horizontal component (i.e. which column)
* @param $y ; The vertical component (i.e. which row)
+ * @param float|int $x
+ * @param float|int $y
*/
- public function set($x, $y): void
+ public function set(int|float $x, int|float $y): void
{
$offset = (int)($y * $this->rowSize + ($x / 32));
if (!isset($this->bits[$offset])) {
@@ -126,7 +128,11 @@ public function _unset($x, $y): void
* @param $x ; The horizontal component (i.e. which column)
* @param $y ; The vertical component (i.e. which row)
*/
- public function flip($x, $y): void
+ /**
+ * @psalm-param 0|positive-int $x
+ * @psalm-param 0|positive-int $y
+ */
+ public function flip(int $x, int $y): void
{
$offset = $y * $this->rowSize + (int)($x / 32);
@@ -139,7 +145,7 @@ public function flip($x, $y): void
*
* @param $mask ; XOR mask
*/
- public function _xor($mask)
+ public function _xor($mask): void
{//было xor, php не позволяет использовать xor
if ($this->width != $mask->getWidth() || $this->height != $mask->getHeight()
|| $this->rowSize != $mask->getRowSize()) {
@@ -173,8 +179,11 @@ public function clear(): void
* @param $top ; The vertical position to begin at (inclusive)
* @param $width ; The width of the region
* @param $height ; The height of the region
+ *
+ * @psalm-param 0|6|9 $left
+ * @psalm-param 0|6|9 $top
*/
- public function setRegion($left, $top, $width, $height)
+ public function setRegion(int $left, int $top, int $width, int $height): void
{
if ($top < 0 || $left < 0) {
throw new \InvalidArgumentException("Left and top must be nonnegative");
@@ -227,11 +236,11 @@ public function getWidth()
*
* @param $y ; The row to retrieve
* @param $row ; An optional caller-allocated BitArray, will be allocated if null or too small
+ * @param float|int $y
*
- * @return BitArray The resulting BitArray - this reference should always be used even when passing
- * your own row
+ * @psalm-param 0|float|positive-int $y
*/
- public function getRow($y, $row)
+ public function getRow(int|float $y, BitArray $row): BitArray
{
if ($row == null || $row->getSize() < $this->width) {
$row = new BitArray($this->width);
@@ -249,8 +258,11 @@ public function getRow($y, $row)
/**
* @param $y ; row to set
* @param $row ; {@link BitArray} to copy from
+ * @param float|int $y
+ *
+ * @psalm-param 0|float|positive-int $y
*/
- public function setRow($y, $row): void
+ public function setRow(int|float $y, BitArray $row): void
{
$this->bits = arraycopy($row->getBitArray(), 0, $this->bits, $y * $this->rowSize, $this->rowSize);
}
@@ -258,9 +270,11 @@ public function setRow($y, $row): void
/**
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
*
- * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
+ * @return (int|mixed)[]|null
+ *
+ * @psalm-return array{0: int|mixed, 1: 0|mixed|positive-int, 2: mixed, 3: mixed}|null
*/
- public function getEnclosingRectangle()
+ public function getEnclosingRectangle(): array|null
{
$left = $this->width;
$top = $this->height;
@@ -312,9 +326,9 @@ public function getEnclosingRectangle()
/**
* This is useful in detecting a corner of a 'pure' barcode.
*
- * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white
+ * @psalm-return array{0: mixed, 1: mixed}|null
*/
- public function getTopLeftOnBit()
+ public function getTopLeftOnBit(): array|null
{
$bitsOffset = 0;
while ($bitsOffset < (is_countable($this->bits) ? count($this->bits) : 0) && $this->bits[$bitsOffset] == 0) {
@@ -336,7 +350,10 @@ public function getTopLeftOnBit()
return [$x, $y];
}
- public function getBottomRightOnBit()
+ /**
+ * @psalm-return array{0: mixed, 1: mixed}|null
+ */
+ public function getBottomRightOnBit(): array|null
{
$bitsOffset = (is_countable($this->bits) ? count($this->bits) : 0) - 1;
while ($bitsOffset >= 0 && $this->bits[$bitsOffset] == 0) {
@@ -375,7 +392,7 @@ public function getRowSize()
return $this->rowSize;
}
- public function equals($o)
+ public function equals($o): bool
{
if (!($o instanceof BitMatrix)) {
return false;
@@ -388,7 +405,7 @@ public function equals($o)
&& $this->bits === $other->bits;
}
- //@Override
+
public function hashCode()
{
@@ -401,9 +418,9 @@ public function hashCode()
return $hash;
}
- //@Override
+
- public function toString($setString = '', $unsetString = '', $lineSeparator = '')
+ public function toString($setString = '', $unsetString = '', $lineSeparator = ''): string
{
if (!$setString || !$unsetString) {
return (string)'X ' . ' ';
@@ -415,7 +432,7 @@ public function toString($setString = '', $unsetString = '', $lineSeparator = ''
return (string)($setString . $unsetString . "\n");
}
- public function toString_($setString, $unsetString, $lineSeparator)
+ public function toString_($setString, $unsetString, $lineSeparator): string
{
//$result = new StringBuilder(height * (width + 1));
$result = '';
@@ -439,9 +456,9 @@ public function toString_($setString, $unsetString, $lineSeparator)
* @param $x ; The horizontal component (i.e. which column)
* @param $y ; The vertical component (i.e. which row)
*
- * @return value of given bit in matrix
+ * @return bool of given bit in matrix
*/
- public function get($x, $y)
+ public function get(int $x, int $y): bool
{
$offset = (int)($y * $this->rowSize + ($x / 32));
if (!isset($this->bits[$offset])) {
diff --git a/lib/Common/BitSource.php b/lib/Common/BitSource.php
index dede538..86dcdc0 100644
--- a/lib/Common/BitSource.php
+++ b/lib/Common/BitSource.php
@@ -32,35 +32,35 @@ final class BitSource
private int $bitOffset = 0;
/**
- * @param bytes $bytes from which this will read bits. Bits will be read from the first byte first.
+ * @param array $bytes bytes from which this will read bits. Bits will be read from the first byte first.
* Bits are read within a byte from most-significant to least-significant bit.
*/
- public function __construct(private $bytes)
- {
- }
+ public function __construct(private array $bytes)
+ {
+ }
/**
- * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
+ * @return int of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
*/
- public function getBitOffset()
+ public function getBitOffset(): int
{
return $this->bitOffset;
}
/**
- * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
+ * @return int of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
*/
- public function getByteOffset()
+ public function getByteOffset(): int
{
return $this->byteOffset;
}
/**
- * @param number $numBits of bits to read
+ * @param int $numBits number of bits to read
*
* @return int representing the bits read. The bits will appear as the least-significant
* bits of the int
- * @throws InvalidArgumentException if numBits isn't in [1,32] or more than is available
+ * @throws \InvalidArgumentException if numBits isn't in [1,32] or more than is available
*/
public function readBits($numBits)
{
@@ -106,9 +106,9 @@ public function readBits($numBits)
}
/**
- * @return number of bits that can be read successfully
+ * @return int of bits that can be read successfully
*/
- public function available()
+ public function available(): int
{
return 8 * ((is_countable($this->bytes) ? count($this->bytes) : 0) - $this->byteOffset) - $this->bitOffset;
}
diff --git a/lib/Common/CharacterSetECI.php b/lib/Common/CharacterSetECI.php
index bbabc19..19a7709 100644
--- a/lib/Common/CharacterSetECI.php
+++ b/lib/Common/CharacterSetECI.php
@@ -9,40 +9,124 @@
final class CharacterSetECI
{
/**#@+
- * Character set constants.
+ * Character set constants.
+ */
+ /**
+ * @var int
*/
public const CP437 = 0;
+ /**
+ * @var int
+ */
public const ISO8859_1 = 1;
+ /**
+ * @var int
+ */
public const ISO8859_2 = 4;
+ /**
+ * @var int
+ */
public const ISO8859_3 = 5;
+ /**
+ * @var int
+ */
public const ISO8859_4 = 6;
+ /**
+ * @var int
+ */
public const ISO8859_5 = 7;
+ /**
+ * @var int
+ */
public const ISO8859_6 = 8;
+ /**
+ * @var int
+ */
public const ISO8859_7 = 9;
+ /**
+ * @var int
+ */
public const ISO8859_8 = 10;
+ /**
+ * @var int
+ */
public const ISO8859_9 = 11;
+ /**
+ * @var int
+ */
public const ISO8859_10 = 12;
+ /**
+ * @var int
+ */
public const ISO8859_11 = 13;
+ /**
+ * @var int
+ */
public const ISO8859_12 = 14;
+ /**
+ * @var int
+ */
public const ISO8859_13 = 15;
+ /**
+ * @var int
+ */
public const ISO8859_14 = 16;
+ /**
+ * @var int
+ */
public const ISO8859_15 = 17;
+ /**
+ * @var int
+ */
public const ISO8859_16 = 18;
+ /**
+ * @var int
+ */
public const SJIS = 20;
+ /**
+ * @var int
+ */
public const CP1250 = 21;
+ /**
+ * @var int
+ */
public const CP1251 = 22;
+ /**
+ * @var int
+ */
public const CP1252 = 23;
+ /**
+ * @var int
+ */
public const CP1256 = 24;
+ /**
+ * @var int
+ */
public const UNICODE_BIG_UNMARKED = 25;
+ /**
+ * @var int
+ */
public const UTF8 = 26;
+ /**
+ * @var int
+ */
public const ASCII = 27;
+ /**
+ * @var int
+ */
public const BIG5 = 28;
+ /**
+ * @var int
+ */
public const GB18030 = 29;
+ /**
+ * @var int
+ */
public const EUC_KR = 30;
/**
* Map between character names and their ECI values.
*/
- private static array $nameToEci = [
+ private static array $nameToEci = [
'ISO-8859-1' => self::ISO8859_1,
'ISO-8859-2' => self::ISO8859_2,
'ISO-8859-3' => self::ISO8859_3,
@@ -71,23 +155,22 @@ final class CharacterSetECI
'EUC-KR' => self::EUC_KR,
];
/**#@-*/
- /**
- * Additional possible values for character sets.
- */
- private static array $additionalValues = [
+ /**
+ * Additional possible values for character sets.
+ */
+ private static array $additionalValues = [
self::CP437 => 2,
self::ASCII => 170,
];
private static int|string|null $name = null;
/**
- * Gets character set ECI by value.
- *
- * @param string $value
- *
- * @return CharacterSetEci|null
- */
- public static function getCharacterSetECIByValue($value)
+ * Gets character set ECI by value.
+ *
+ *
+ * @return CharacterSetEci|null
+ */
+ public static function getCharacterSetECIByValue(string $value)
{
if ($value < 0 || $value >= 900) {
throw new \InvalidArgumentException('Value must be between 0 and 900');
@@ -105,6 +188,13 @@ public static function getCharacterSetECIByValue($value)
}
}
+ /**
+ * @param (int|string) $value
+ *
+ * @psalm-param array-key $value
+ *
+ * @return null|true
+ */
private static function setName($value)
{
foreach (self::$nameToEci as $name => $key) {
@@ -128,21 +218,20 @@ private static function setName($value)
/**
* Gets character set ECI name.
*
- * @return character set ECI name|null
+ * @return int|null|string set ECI name|null
*/
- public static function name()
+ public static function name(): string|int|null
{
return self::$name;
}
/**
- * Gets character set ECI by name.
- *
- * @param string $name
- *
- * @return CharacterSetEci|null
- */
- public static function getCharacterSetECIByName($name)
+ * Gets character set ECI by name.
+ *
+ *
+ * @return CharacterSetEci|null
+ */
+ public static function getCharacterSetECIByName(string $name)
{
$name = strtoupper($name);
if (isset(self::$nameToEci[$name])) {
diff --git a/lib/Common/DecoderResult.php b/lib/Common/DecoderResult.php
index b468dd8..8b9ea83 100644
--- a/lib/Common/DecoderResult.php
+++ b/lib/Common/DecoderResult.php
@@ -29,20 +29,20 @@ final class DecoderResult
/**
* @var mixed|null
*/
- private $errorsCorrected;
+ private $errorsCorrected;
/**
* @var mixed|null
*/
- private $erasures;
+ private $erasures;
/**
* @var mixed|null
*/
- private $other;
+ private $other;
public function __construct(private $rawBytes, private $text, private $byteSegments, private $ecLevel, private $structuredAppendSequenceNumber = -1, private $structuredAppendParity = -1)
- {
- }
+ {
+ }
public function getRawBytes()
{
@@ -89,12 +89,12 @@ public function getOther()
return $this->other;
}
- public function setOther($other): void
+ public function setOther(\Zxing\Qrcode\Decoder\QRCodeDecoderMetaData $other): void
{
$this->other = $other;
}
- public function hasStructuredAppend()
+ public function hasStructuredAppend(): bool
{
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
}
diff --git a/lib/Common/DefaultGridSampler.php b/lib/Common/DefaultGridSampler.php
index 4e63195..c8a455c 100644
--- a/lib/Common/DefaultGridSampler.php
+++ b/lib/Common/DefaultGridSampler.php
@@ -24,7 +24,9 @@
*/
final class DefaultGridSampler extends GridSampler
{
- //@Override
+ /**
+ * @return BitMatrix
+ */
public function sampleGrid(
$image,
$dimensionX,
@@ -68,15 +70,18 @@ public function sampleGrid(
return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
}
- //@Override
+
+ /**
+ * @return BitMatrix
+ */
public function sampleGrid_(
- $image,
- $dimensionX,
- $dimensionY,
- $transform
- ) {
+ BitMatrix $image,
+ int $dimensionX,
+ int $dimensionY,
+ PerspectiveTransform $transform
+ ): BitMatrix {
if ($dimensionX <= 0 || $dimensionY <= 0) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("X or Y dimensions smaller than zero");
}
$bits = new BitMatrix($dimensionX, $dimensionY);
$points = fill_array(0, 2 * $dimensionX, 0.0);
@@ -98,7 +103,7 @@ public function sampleGrid_(
$bits->set($x / 2, $y);
}
}
- } catch (\Exception) {//ArrayIndexOutOfBoundsException
+ } catch (\Exception) { //ArrayIndexOutOfBoundsException
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
@@ -106,7 +111,7 @@ public function sampleGrid_(
// This results in an ugly runtime exception despite our clever checks above -- can't have
// that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("ArrayIndexOutOfBoundsException");
}
}
diff --git a/lib/Common/Detector/MathUtils.php b/lib/Common/Detector/MathUtils.php
index 061318e..54bdbbe 100644
--- a/lib/Common/Detector/MathUtils.php
+++ b/lib/Common/Detector/MathUtils.php
@@ -33,12 +33,12 @@ private function __construct()
*
* @return int {@code int}
*/
- public static function round($d)
+ public static function round(float $d)
{
return (int)($d + ($d < 0.0 ? -0.5 : 0.5));
}
- public static function distance($aX, $aY, $bX, $bY)
+ public static function distance(float|int $aX, float|int $aY, float $bX, float $bY): float
{
$xDiff = $aX - $bX;
$yDiff = $aY - $bY;
diff --git a/lib/Common/Detector/MonochromeRectangleDetector.php b/lib/Common/Detector/MonochromeRectangleDetector.php
index 90cbda2..d7f5be2 100644
--- a/lib/Common/Detector/MonochromeRectangleDetector.php
+++ b/lib/Common/Detector/MonochromeRectangleDetector.php
@@ -38,8 +38,8 @@ class MonochromeRectangleDetector
private static int $MAX_MODULES = 32;
public function __construct(private readonly BinaryBitmap $image)
- {
- }
+ {
+ }
/**
* Detects a rectangular region of black and white -- mostly black -- with a region of mostly
@@ -138,31 +138,31 @@ public function detect(): \Zxing\ResultPoint
*
* @param float $centerX center's x component (horizontal)
* @param float $deltaX same as deltaY but change in x per step instead
- * @param float $left minimum value of x
- * @param float $right maximum value of x
+ * @param int $left
+ * @param int $right
* @param float $centerY center's y component (vertical)
* @param float $deltaY change in y per step. If scanning up this is negative; down, positive;
* left or right, 0
- * @param float $top minimum value of y to search through (meaningless when di == 0)
- * @param float $bottom maximum value of y
+ * @param int $top
+ * @param int $bottom
* @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
* the barcode
*
* @return ResultPoint {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
+ *
* @throws NotFoundException if such a point cannot be found
*/
private function findCornerFromCenter(
- $centerX,
- $deltaX,
- $left,
- $right,
- $centerY,
- $deltaY,
- $top,
- $bottom,
- $maxWhiteRun
- ): \Zxing\ResultPoint
- {
+ int|float $centerX,
+ int|float $deltaX,
+ int $left,
+ int $right,
+ int|float $centerY,
+ float|int $deltaY,
+ int $top,
+ int $bottom,
+ int|float $maxWhiteRun
+ ): \Zxing\ResultPoint {
$lastRange = null;
for ($y = $centerY, $x = $centerX;
$y < $bottom && $y >= $top && $x < $right && $x >= $left;
@@ -177,7 +177,7 @@ private function findCornerFromCenter(
}
if ($range == null) {
if ($lastRange == null) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("No corner from center found");
}
// lastRange was found
if ($deltaX == 0) {
@@ -207,7 +207,7 @@ private function findCornerFromCenter(
}
$lastRange = $range;
}
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("No corner from center found");
}
@@ -215,19 +215,17 @@ private function findCornerFromCenter(
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
* be part of a Data Matrix barcode.
*
- * @param if $fixedDimension scanning horizontally, this is the row (the fixed vertical location)
- * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
- * @param largest $maxWhiteRun run of white pixels that can still be considered part of the
- * barcode region
- * @param minimum $minDim pixel location, horizontally or vertically, to consider
- * @param maximum $maxDim pixel location, horizontally or vertically, to consider
- * @param if $horizontal true, we're scanning left-right, instead of up-down
+ * @param float|int $fixedDimension
+ * @param float|int $maxWhiteRun
+ * @param int $minDim
+ * @param int $maxDim
+ * @param bool $horizontal
+ *
+ * @return (float|int)[]|null with start and end of found range, or null if no such range is found (e.g. only white was found)
*
- * @return int[] with start and end of found range, or null if no such range is found
- * (e.g. only white was found)
+ * @psalm-return array{0: float|int, 1: float|int}|null
*/
-
- private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal)
+ private function blackWhiteRange(float|int $fixedDimension, int|float $maxWhiteRun, int $minDim, int $maxDim, bool $horizontal): array|null
{
$center = ($minDim + $maxDim) / 2;
diff --git a/lib/Common/DetectorResult.php b/lib/Common/DetectorResult.php
index a6bf38c..b8a3cee 100644
--- a/lib/Common/DetectorResult.php
+++ b/lib/Common/DetectorResult.php
@@ -27,8 +27,8 @@
class DetectorResult
{
public function __construct(private $bits, private $points)
- {
- }
+ {
+ }
final public function getBits()
{
diff --git a/lib/Common/GlobalHistogramBinarizer.php b/lib/Common/GlobalHistogramBinarizer.php
index 7567c09..4ba6e4a 100644
--- a/lib/Common/GlobalHistogramBinarizer.php
+++ b/lib/Common/GlobalHistogramBinarizer.php
@@ -44,7 +44,7 @@ class GlobalHistogramBinarizer extends Binarizer
/**
* @var mixed|\Zxing\LuminanceSource
*/
- private $source = [];
+ private $source = [];
public function __construct($source)
{
@@ -59,7 +59,7 @@ public function __construct($source)
}
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
- public function getBlackRow($y, $row = null)
+ public function getBlackRow(int $y, ?BitArray $row = null): BitArray
{
$this->source = $this->getLuminanceSource();
$width = $this->source->getWidth();
@@ -95,7 +95,7 @@ public function getBlackRow($y, $row = null)
}
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
- private function initArrays($luminanceSize): void
+ private function initArrays(float $luminanceSize): void
{
if (count($this->luminances) < $luminanceSize) {
$this->luminances = [];
@@ -105,7 +105,7 @@ private function initArrays($luminanceSize): void
}
}
- private static function estimateBlackPoint($buckets)
+ private static function estimateBlackPoint(array $buckets): int
{
// Find the tallest peak in the histogram.
$numBuckets = is_countable($buckets) ? count($buckets) : 0;
@@ -145,7 +145,7 @@ private static function estimateBlackPoint($buckets)
// If there is too little contrast in the image to pick a meaningful black point, throw rather
// than waste time trying to decode the image, and risk false positives.
if ($secondPeak - $firstPeak <= $numBuckets / 16) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("too little contrast in the image to pick a meaningful black point");
}
// Find a valley between them that is low and closer to the white peak.
diff --git a/lib/Common/GridSampler.php b/lib/Common/GridSampler.php
index 7871859..1b49962 100644
--- a/lib/Common/GridSampler.php
+++ b/lib/Common/GridSampler.php
@@ -35,9 +35,9 @@
abstract class GridSampler
{
/**
- * @var mixed|\Zxing\Common\DefaultGridSampler|null
- */
- private static $gridSampler;
+ * @var GridSampler|null
+ */
+ private static $gridSampler;
/**
* Sets the implementation of GridSampler used by the library. One global
@@ -46,7 +46,7 @@ abstract class GridSampler
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
* an implementation that takes advantage of native platform libraries.
*
- * @param $newGridSampler The platform-specific object to install.
+ * @param GridSampler $newGridSampler The platform-specific object to install.
*/
public static function setGridSampler($newGridSampler): void
{
@@ -76,15 +76,18 @@ public static function getInstance()
*
For efficiency, the method will check points from either end of the line until one is found
* to be within the image. Because the set of points are assumed to be linear, this is valid.
*
- * @param image $image into which the points should map
- * @param actual $points points in x1,y1,...,xn,yn form
+ * @param BitMatrix $image image into which the points should map
+ * @param array $points actual points in x1,y1,...,xn,yn form
+ * @param float[] $points
*
* @throws NotFoundException if an endpoint is lies outside the image boundaries
+ *
+ * @psalm-param array $points
*/
protected static function checkAndNudgePoints(
- $image,
- $points
- ) {
+ BitMatrix $image,
+ array $points
+ ): void {
$width = $image->getWidth();
$height = $image->getHeight();
// Check and nudge points from start until we see some that are OK:
@@ -93,7 +96,7 @@ protected static function checkAndNudgePoints(
$x = (int)$points[$offset];
$y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Endpoint ($x, $y) lies outside the image boundaries ($width, $height)");
}
$nudged = false;
if ($x == -1) {
@@ -117,7 +120,7 @@ protected static function checkAndNudgePoints(
$x = (int)$points[$offset];
$y = (int)$points[$offset + 1];
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Endpoint ($x, $y) lies outside the image boundaries ($width, $height)");
}
$nudged = false;
if ($x == -1) {
@@ -142,25 +145,25 @@ protected static function checkAndNudgePoints(
* transformation is determined by the coordinates of 4 points, in the original and transformed
* image space.
*
- * @param image $image to sample
- * @param width $dimensionX of {@link BitMatrix} to sample from image
- * @param height $dimensionY of {@link BitMatrix} to sample from image
- * @param point $p1ToX 1 preimage X
- * @param point $p1ToY 1 preimage Y
- * @param point $p2ToX 2 preimage X
- * @param point $p2ToY 2 preimage Y
- * @param point $p3ToX 3 preimage X
- * @param point $p3ToY 3 preimage Y
- * @param point $p4ToX 4 preimage X
- * @param point $p4ToY 4 preimage Y
- * @param point $p1FromX 1 image X
- * @param point $p1FromY 1 image Y
- * @param point $p2FromX 2 image X
- * @param point $p2FromY 2 image Y
- * @param point $p3FromX 3 image X
- * @param point $p3FromY 3 image Y
- * @param point $p4FromX 4 image X
- * @param point $p4FromY 4 image Y
+ * @param BitMatrix $image image to sample
+ * @param int $dimensionX width of {@link BitMatrix} to sample from image
+ * @param int $dimensionY height of {@link BitMatrix} to sample from image
+ * @param float $p1ToX point 1 preimage X
+ * @param float $p1ToY point 1 preimage Y
+ * @param float $p2ToX point 2 preimage X
+ * @param float $p2ToY point 2 preimage Y
+ * @param float $p3ToX point 3 preimage X
+ * @param float $p3ToY point 3 preimage Y
+ * @param float $p4ToX point 4 preimage X
+ * @param float $p4ToY point 4 preimage Y
+ * @param float $p1FromX point 1 image X
+ * @param float $p1FromY point 1 image Y
+ * @param float $p2FromX point 2 image X
+ * @param float $p2FromY point 2 image Y
+ * @param float $p3FromX point 3 image X
+ * @param float $p3FromY point 3 image Y
+ * @param float $p4FromX point 4 image X
+ * @param float $p4FromY point 4 image Y
*
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
* defined by the "from" parameters
@@ -190,9 +193,9 @@ abstract public function sampleGrid(
);
abstract public function sampleGrid_(
- $image,
- $dimensionX,
- $dimensionY,
- $transform
- );
+ BitMatrix $image,
+ int $dimensionX,
+ int $dimensionY,
+ PerspectiveTransform $transform
+ ): BitMatrix;
}
diff --git a/lib/Common/HybridBinarizer.php b/lib/Common/HybridBinarizer.php
index 620e12f..638c53c 100644
--- a/lib/Common/HybridBinarizer.php
+++ b/lib/Common/HybridBinarizer.php
@@ -98,14 +98,18 @@ public function getBlackMatrix()
* Calculates a single black point for each block of pixels and saves it away.
* See the following thread for a discussion of this algorithm:
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
+ *
+ * @return ((int|mixed)[]|mixed)[]
+ *
+ * @psalm-return array|mixed>
*/
private static function calculateBlackPoints(
- $luminances,
- $subWidth,
- $subHeight,
- $width,
- $height
- ) {
+ array|BitMatrix $luminances,
+ float $subWidth,
+ float $subHeight,
+ float $width,
+ float $height
+ ): array {
$blackPoints = fill_array(0, $subHeight, 0);
foreach ($blackPoints as $key => $point) {
$blackPoints[$key] = fill_array(0, $subWidth, 0);
@@ -185,15 +189,19 @@ private static function calculateBlackPoints(
* For each block in the image, calculate the average black point using a 5x5 grid
* of the blocks around it. Also handles the corner cases (fractional blocks are computed based
* on the last pixels in the row/column which are also used in the previous block).
+ *
+ * @param ((int|mixed)[]|mixed)[] $blackPoints
+ *
+ * @psalm-param array|mixed> $blackPoints
*/
private static function calculateThresholdForBlock(
- $luminances,
- $subWidth,
- $subHeight,
- $width,
- $height,
- $blackPoints,
- $matrix
+ array|BitMatrix $luminances,
+ float $subWidth,
+ float $subHeight,
+ float $width,
+ float $height,
+ array $blackPoints,
+ BitMatrix $matrix
): void {
for ($y = 0; $y < $subHeight; $y++) {
$yoffset = ($y << self::$BLOCK_SIZE_POWER);
@@ -221,7 +229,15 @@ private static function calculateThresholdForBlock(
}
}
- private static function cap($value, $min, $max)
+ /**
+ * @psalm-param 0|positive-int $value
+ * @psalm-param 2 $min
+ *
+ * @return float|int
+ *
+ * @psalm-return float|int<2, max>
+ */
+ private static function cap(int $value, int $min, float $max): int|float
{
if ($value < $min) {
return $min;
@@ -234,14 +250,18 @@ private static function cap($value, $min, $max)
/**
* Applies a single threshold to a block of pixels.
+ *
+ * @param BitMatrix|array $luminances
+ * @param float|int $xoffset
+ * @param float|int $yoffset
*/
private static function thresholdBlock(
- $luminances,
- $xoffset,
- $yoffset,
- $threshold,
- $stride,
- $matrix
+ array|BitMatrix $luminances,
+ int|float $xoffset,
+ int|float $yoffset,
+ int $threshold,
+ float $stride,
+ BitMatrix $matrix
): void {
for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
diff --git a/lib/Common/PerspectiveTransform.php b/lib/Common/PerspectiveTransform.php
index ca6bfff..e02fbbc 100644
--- a/lib/Common/PerspectiveTransform.php
+++ b/lib/Common/PerspectiveTransform.php
@@ -27,27 +27,27 @@
final class PerspectiveTransform
{
private function __construct(private $a11, private $a21, private $a31, private $a12, private $a22, private $a32, private $a13, private $a23, private $a33)
- {
- }
+ {
+ }
public static function quadrilateralToQuadrilateral(
- $x0,
- $y0,
- $x1,
- $y1,
- $x2,
- $y2,
- $x3,
- $y3,
- $x0p,
- $y0p,
- $x1p,
- $y1p,
- $x2p,
- $y2p,
- $x3p,
- $y3p
- ) {
+ float $x0,
+ float $y0,
+ float $x1,
+ float $y1,
+ float $x2,
+ float $y2,
+ float $x3,
+ float $y3,
+ float $x0p,
+ float $y0p,
+ float $x1p,
+ float $y1p,
+ float $x2p,
+ float $y2p,
+ float $x3p,
+ float $y3p
+ ): self {
$qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
@@ -55,15 +55,15 @@ public static function quadrilateralToQuadrilateral(
}
public static function quadrilateralToSquare(
- $x0,
- $y0,
- $x1,
- $y1,
- $x2,
- $y2,
- $x3,
- $y3
- ) {
+ float $x0,
+ float $y0,
+ float $x1,
+ float $y1,
+ float $x2,
+ float $y2,
+ float $x3,
+ float $y3
+ ): self {
// Here, the adjoint serves as the inverse:
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
}
@@ -85,14 +85,14 @@ public function buildAdjoint(): \Zxing\Common\PerspectiveTransform
}
public static function squareToQuadrilateral(
- $x0,
- $y0,
- $x1,
- $y1,
- $x2,
- $y2,
- $x3,
- $y3
+ float $x0,
+ float $y0,
+ float $x1,
+ float $y1,
+ float $x2,
+ float $y2,
+ float $x3,
+ float $y3
): \Zxing\Common\PerspectiveTransform {
$dx3 = $x0 - $x1 + $x2 - $x3;
$dy3 = $y0 - $y1 + $y2 - $y3;
@@ -132,7 +132,7 @@ public static function squareToQuadrilateral(
}
}
- public function times($other): \Zxing\Common\PerspectiveTransform
+ public function times(self $other): \Zxing\Common\PerspectiveTransform
{
return new PerspectiveTransform(
$this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
@@ -147,7 +147,12 @@ public function times($other): \Zxing\Common\PerspectiveTransform
);
}
- public function transformPoints(&$points, &$yValues = 0): void
+ /**
+ * @param (float|mixed)[] $points
+ *
+ * @psalm-param array $points
+ */
+ public function transformPoints(array &$points, &$yValues = 0): void
{
if ($yValues) {
$this->transformPoints_($points, $yValues);
@@ -173,7 +178,12 @@ public function transformPoints(&$points, &$yValues = 0): void
}
}
- public function transformPoints_(&$xValues, &$yValues): void
+ /**
+ * @param (float|mixed)[] $xValues
+ *
+ * @psalm-param array $xValues
+ */
+ public function transformPoints_(array &$xValues, &$yValues): void
{
$n = is_countable($xValues) ? count($xValues) : 0;
for ($i = 0; $i < $n; $i++) {
diff --git a/lib/Common/Reedsolomon/GenericGF.php b/lib/Common/Reedsolomon/GenericGF.php
index bae7835..38e75a8 100644
--- a/lib/Common/Reedsolomon/GenericGF.php
+++ b/lib/Common/Reedsolomon/GenericGF.php
@@ -47,15 +47,15 @@ final class GenericGF
/**
* Create a representation of GF(size) using the given primitive polynomial.
*
- * @param irreducible $primitive polynomial whose coefficients are represented by
+ * @param int $primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
- * @param the $size size of the field
- * @param the $generatorBase factor b in the generator polynomial can be 0- or 1-based
- (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
- In most cases it should be 1, but for QR code it is 0.
+ * @param int $size the size of the field
+ * @param int $generatorBase the factor b in the generator polynomial can be 0- or 1-based
+ (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
+ In most cases it should be 1, but for QR code it is 0.
*/
- public function __construct(private $primitive, private $size, private $generatorBase)
+ public function __construct(private $primitive, private $size, private $generatorBase)
{
$x = 1;
for ($i = 0; $i < $size; $i++) {
@@ -89,19 +89,21 @@ public static function Init(): void
/**
* Implements both addition and subtraction -- they are the same in GF(size).
*
- * @return sum/difference of a and b
+ * @return float|int sum/difference of a and b
+ *
+ * @param float|int|null $b
*/
- public static function addOrSubtract($a, $b)
+ public static function addOrSubtract(int $a, int|float|null $b)
{
return $a ^ $b;
}
- public function getZero()
+ public function getZero(): GenericGFPoly
{
return $this->zero;
}
- public function getOne()
+ public function getOne(): GenericGFPoly
{
return $this->one;
}
@@ -109,7 +111,7 @@ public function getOne()
/**
* @return GenericGFPoly the monomial representing coefficient * x^degree
*/
- public function buildMonomial($degree, $coefficient)
+ public function buildMonomial($degree, int $coefficient)
{
if ($degree < 0) {
throw new \InvalidArgumentException();
@@ -132,9 +134,9 @@ public function exp($a)
}
/**
- * @return base 2 log of a in GF(size)
+ * @return float base 2 log of a in GF(size)
*/
- public function log($a)
+ public function log(float|int|null $a)
{
if ($a == 0) {
throw new \InvalidArgumentException();
@@ -144,7 +146,7 @@ public function log($a)
}
/**
- * @return multiplicative inverse of a
+ * @return float multiplicative inverse of a
*/
public function inverse($a)
{
@@ -157,8 +159,11 @@ public function inverse($a)
/**
* @return int product of a and b in GF(size)
+ *
+ * @param float|int|null $b
+ * @param float|int|null $a
*/
- public function multiply($a, $b)
+ public function multiply(int|float|null $a, int|float|null $b)
{
if ($a == 0 || $b == 0) {
return 0;
@@ -178,7 +183,7 @@ public function getGeneratorBase()
}
// @Override
- public function toString()
+ public function toString(): string
{
return "GF(0x" . dechex((int)($this->primitive)) . ',' . $this->size . ')';
}
diff --git a/lib/Common/Reedsolomon/GenericGFPoly.php b/lib/Common/Reedsolomon/GenericGFPoly.php
index 8a71a9b..fcb3ffc 100644
--- a/lib/Common/Reedsolomon/GenericGFPoly.php
+++ b/lib/Common/Reedsolomon/GenericGFPoly.php
@@ -29,17 +29,17 @@
final class GenericGFPoly
{
/**
- * @var int[]|mixed|null
+ * @var int[]|float[]|null
*/
- private $coefficients;
+ private $coefficients;
/**
- * @param the $field {@link GenericGF} instance representing the field to use
+ * @param GenericGF $field {@link GenericGF} the instance representing the field to use
* to perform computations
* @param array $coefficients coefficients as ints representing elements of GF(size), arranged
* from most significant (highest-power term) coefficient to least significant
*
- * @throws InvalidArgumentException if argument is null or empty,
+ * @throws \InvalidArgumentException if argument is null or empty,
* or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0")
*/
@@ -72,15 +72,20 @@ public function __construct(private $field, $coefficients)
}
}
- public function getCoefficients()
+ /**
+ * @return (float|int)[]|null
+ *
+ * @psalm-return array|null
+ */
+ public function getCoefficients(): array|null
{
return $this->coefficients;
}
/**
- * @return evaluation of this polynomial at a given point
+ * @return float|int|null evaluation of this polynomial at a given point
*/
- public function evaluateAt($a)
+ public function evaluateAt($a): int|float|null
{
if ($a == 0) {
// Just return the x^0 coefficient
@@ -105,21 +110,23 @@ public function evaluateAt($a)
}
/**
- * @return coefficient of x^degree term in this polynomial
+ * @return float|int|null coefficient of x^degree term in this polynomial
+ *
+ * @param float|int $degree
*/
- public function getCoefficient($degree)
+ public function getCoefficient(int|float $degree): int|float|null
{
return $this->coefficients[(is_countable($this->coefficients) ? count($this->coefficients) : 0) - 1 - $degree];
}
- public function multiply($other)
+ public function multiply($other): self
{
$aCoefficients = [];
- $bCoefficients = [];
- $aLength = null;
- $bLength = null;
- $product = [];
- if (is_int($other)) {
+ $bCoefficients = [];
+ $aLength = null;
+ $bLength = null;
+ $product = [];
+ if (is_int($other)) {
return $this->multiply_($other);
}
if ($this->field !== $other->field) {
@@ -146,7 +153,7 @@ public function multiply($other)
return new GenericGFPoly($this->field, $product);
}
- public function multiply_($scalar)
+ public function multiply_(int $scalar): self
{
if ($scalar == 0) {
return $this->field->getZero();
@@ -164,14 +171,14 @@ public function multiply_($scalar)
}
/**
- * @return true iff this polynomial is the monomial "0"
+ * @return bool iff this polynomial is the monomial "0"
*/
- public function isZero()
+ public function isZero(): bool
{
return $this->coefficients[0] == 0;
}
- public function multiplyByMonomial($degree, $coefficient)
+ public function multiplyByMonomial($degree, $coefficient): self
{
if ($degree < 0) {
throw new \InvalidArgumentException();
@@ -188,7 +195,10 @@ public function multiplyByMonomial($degree, $coefficient)
return new GenericGFPoly($this->field, $product);
}
- public function divide($other)
+ /**
+ * @psalm-return array{0: mixed, 1: mixed}
+ */
+ public function divide($other): array
{
if ($this->field !== $other->field) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
@@ -216,21 +226,21 @@ public function divide($other)
}
/**
- * @return degree of this polynomial
+ * @return int of this polynomial
*/
- public function getDegree()
+ public function getDegree(): int
{
return (is_countable($this->coefficients) ? count($this->coefficients) : 0) - 1;
}
- public function addOrSubtract($other)
+ public function addOrSubtract(self $other): self
{
$smallerCoefficients = [];
- $largerCoefficients = [];
- $sumDiff = [];
- $lengthDiff = null;
- $countLargerCoefficients = null;
- if ($this->field !== $other->field) {
+ $largerCoefficients = [];
+ $sumDiff = [];
+ $lengthDiff = null;
+ $countLargerCoefficients = null;
+ if ($this->field !== $other->field) {
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
}
if ($this->isZero()) {
@@ -260,9 +270,9 @@ public function addOrSubtract($other)
return new GenericGFPoly($this->field, $sumDiff);
}
- //@Override
+
- public function toString()
+ public function toString(): string
{
$result = '';
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
diff --git a/lib/Common/Reedsolomon/ReedSolomonDecoder.php b/lib/Common/Reedsolomon/ReedSolomonDecoder.php
index e95243d..8b8d313 100644
--- a/lib/Common/Reedsolomon/ReedSolomonDecoder.php
+++ b/lib/Common/Reedsolomon/ReedSolomonDecoder.php
@@ -42,18 +42,20 @@
final class ReedSolomonDecoder
{
public function __construct(private $field)
- {
- }
+ {
+ }
/**
* Decodes given set of received codewords, which include both data and error-correction
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
* in the input.
*
- * @param data $received and error-correction codewords
- * @param number $twoS of error-correction codewords available
+ * @param array $received data and error-correction codewords
+ * @param int|float $twoS number of error-correction codewords available
*
* @throws ReedSolomonException if decoding fails for any reason
+ *
+ * @return void
*/
public function decode(&$received, $twoS)
{
@@ -87,7 +89,10 @@ public function decode(&$received, $twoS)
}
}
- private function runEuclideanAlgorithm($a, $b, $R)
+ /**
+ * @psalm-return array{0: mixed, 1: mixed}
+ */
+ private function runEuclideanAlgorithm($a, \Zxing\Common\Reedsolomon\GenericGFPoly $b, int|float $R): array
{
// Assume a's degree is >= b's
if ($a->getDegree() < $b->getDegree()) {
@@ -143,7 +148,10 @@ private function runEuclideanAlgorithm($a, $b, $R)
return [$sigma, $omega];
}
- private function findErrorLocations($errorLocator)
+ /**
+ * @psalm-return array
+ */
+ private function findErrorLocations($errorLocator): array
{
// This is a direct application of Chien's search
$numErrors = $errorLocator->getDegree();
@@ -165,7 +173,11 @@ private function findErrorLocations($errorLocator)
return $result;
}
- private function findErrorMagnitudes($errorEvaluator, $errorLocations)
+ /**
+ * @psalm-return array
+ * @psalm-param array $errorLocations
+ */
+ private function findErrorMagnitudes($errorEvaluator, array $errorLocations): array
{
// This is directly applying Forney's Formula
$s = is_countable($errorLocations) ? count($errorLocations) : 0;
diff --git a/lib/Common/customFunctions.php b/lib/Common/customFunctions.php
index 7836cc7..089c908 100644
--- a/lib/Common/customFunctions.php
+++ b/lib/Common/customFunctions.php
@@ -1,7 +1,7 @@
>> 32 bit
*/
if (!function_exists('sdvig3')) {
- function sdvig3($a, $b)
+ function sdvig3($a, $b): float|int
{
if ($a >= 0) {
return bindec(decbin($a >> $b)); //simply right shift for positive number
@@ -92,7 +95,10 @@ function floatToIntBits($float_val)
if (!function_exists('fill_array')) {
- function fill_array($index, $count, $value)
+ /**
+ * @psalm-return array
+ */
+ function fill_array($index, $count, $value): array
{
if ($count <= 0) {
return [0];
diff --git a/lib/FormatException.php b/lib/FormatException.php
index d003d6c..e24f002 100644
--- a/lib/FormatException.php
+++ b/lib/FormatException.php
@@ -35,7 +35,7 @@ public function __construct($cause = null)
}
}
- public static function getFormatInstance($cause = null)
+ public static function getFormatInstance($cause = null): self
{
if (!self::$instance) {
self::$instance = new FormatException();
diff --git a/lib/GDLuminanceSource.php b/lib/GDLuminanceSource.php
index 1725a2c..a2ead89 100644
--- a/lib/GDLuminanceSource.php
+++ b/lib/GDLuminanceSource.php
@@ -18,15 +18,15 @@ final class GDLuminanceSource extends LuminanceSource
/**
* @var mixed|int
*/
- private $left;
+ private $left;
/**
* @var mixed|int
*/
- private $top;
+ private $top;
/**
* @var mixed|null
*/
- private $gdImage;
+ private $gdImage;
public function __construct(
$gdImage,
@@ -115,7 +115,7 @@ public function GDLuminanceSource($gdImage, $width, $height): void
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
}
- //@Override
+
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
@@ -131,7 +131,7 @@ public function getRow($y, $row = null)
return $row;
}
- //@Override
+
public function getMatrix()
{
$width = $this->getWidth();
@@ -165,13 +165,13 @@ public function getMatrix()
return $matrix;
}
- //@Override
- public function isCropSupported()
+
+ public function isCropSupported(): bool
{
return true;
}
- //@Override
+
public function crop($left, $top, $width, $height): \Zxing\GDLuminanceSource
{
return new GDLuminanceSource(
@@ -184,4 +184,14 @@ public function crop($left, $top, $width, $height): \Zxing\GDLuminanceSource
$height
);
}
+
+ public function rotateCounterClockwise(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise");
+ }
+
+ public function rotateCounterClockwise45(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise45");
+ }
}
diff --git a/lib/IMagickLuminanceSource.php b/lib/IMagickLuminanceSource.php
index a39f640..90fe4cd 100644
--- a/lib/IMagickLuminanceSource.php
+++ b/lib/IMagickLuminanceSource.php
@@ -12,13 +12,13 @@ final class IMagickLuminanceSource extends LuminanceSource
private $dataWidth;
private $dataHeight;
/**
- * @var mixed|int
- */
- private $left;
+ * @var mixed|int
+ */
+ private $left;
/**
- * @var mixed|int
- */
- private $top;
+ * @var mixed|int
+ */
+ private $top;
private ?\Imagick $image = null;
public function __construct(
@@ -46,6 +46,42 @@ public function __construct(
$this->top = $top;
}
+ public function rotateCounterClockwise(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise");
+ }
+
+ public function rotateCounterClockwise45(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise45");
+ }
+
+ /**
+ * TODO: move to some utility class or something
+ * Converts shorthand memory notation value to bytes
+ * From http://php.net/manual/en/function.ini-get.php
+ *
+ * @param int $val Memory size shorthand notation string
+ */
+ protected static function kmgStringToBytes(string $val)
+ {
+ $val = trim($val);
+ $last = strtolower($val[strlen($val) - 1]);
+ $val = substr($val, 0, -1);
+ switch ($last) {
+ // The 'G' modifier is available since PHP 5.1.0
+ case 'g':
+ $val *= 1024;
+ // no break
+ case 'm':
+ $val *= 1024;
+ // no break
+ case 'k':
+ $val *= 1024;
+ }
+ return $val;
+ }
+
public function _IMagickLuminanceSource(\Imagick $image, $width, $height): void
{
parent::__construct($width, $height);
@@ -56,13 +92,15 @@ public function _IMagickLuminanceSource(\Imagick $image, $width, $height): void
$this->top = 0;
$this->image = $image;
-
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
$this->luminances = [];
$image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
- // $image->newPseudoImage(0, 0, "magick:rose");
+ // Check that we actually have enough space to do it
+ if ($width * $height * 16 * 3 > $this->kmgStringToBytes(ini_get('memory_limit'))) {
+ throw new \RuntimeException("PHP Memory Limit does not allow pixel export.");
+ }
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
$array = [];
@@ -75,16 +113,15 @@ public function _IMagickLuminanceSource(\Imagick $image, $width, $height): void
$b = $pixels[$i + 2] & 0xff;
if ($r == $g && $g == $b) {
// Image is already greyscale, so pick any channel.
-
- $this->luminances[] = $r;//(($r + 128) % 256) - 128;
+ $this->luminances[] = $r; //(($r + 128) % 256) - 128;
} else {
// Calculate luminance cheaply, favoring green.
- $this->luminances[] = ($r + 2 * $g + $b) / 4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
+ $this->luminances[] = ($r + 2 * $g + $b) / 4; //(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
}
}
}
- //@Override
+
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
@@ -100,7 +137,7 @@ public function getRow($y, $row = null)
return $row;
}
- //@Override
+
public function getMatrix()
{
$width = $this->getWidth();
@@ -134,14 +171,14 @@ public function getMatrix()
return $matrix;
}
- //@Override
- public function isCropSupported(): bool
+
+ public function isCropSupported(): bool
{
return true;
}
- //@Override
- public function crop($left, $top, $width, $height)
+
+ public function crop($left, $top, $width, $height): LuminanceSource
{
return $this->luminances->cropImage($width, $height, $left, $top);
diff --git a/lib/LuminanceSource.php b/lib/LuminanceSource.php
index 7d39c67..432e8a2 100644
--- a/lib/LuminanceSource.php
+++ b/lib/LuminanceSource.php
@@ -17,6 +17,8 @@
namespace Zxing;
+use Zxing\Common\BitMatrix;
+
/**
* The purpose of this class hierarchy is to abstract different bitmap implementations across
* platforms into a standard interface for requesting greyscale luminance values. The interface
@@ -28,138 +30,129 @@
*/
abstract class LuminanceSource
{
- public function __construct(private $width, private $height)
- {
- }
+ public function __construct(private $width, private $height)
+ {
+ }
- /**
- * Fetches luminance data for the underlying bitmap. Values should be fetched using:
- * {@code int luminance = array[y * width + x] & 0xff}
- *
- * @return A row-major 2D array of luminance values. Do not use result.length as it may be
- * larger than width * height bytes on some platforms. Do not modify the contents
- * of the result.
- */
- abstract public function getMatrix();
+ /**
+ * Fetches luminance data for the underlying bitmap. Values should be fetched using:
+ * {@code int luminance = array[y * width + x] & 0xff}
+ *
+ * @return BitMatrix A row-major 2D array of luminance values. Do not use result.length as it may be
+ * larger than width * height bytes on some platforms. Do not modify the contents
+ * of the result.
+ */
+ abstract public function getMatrix();
- /**
- * @return float The width of the bitmap.
- */
- final public function getWidth(): float
- {
- return $this->width;
- }
+ /**
+ * @return float The width of the bitmap.
+ */
+ final public function getWidth(): float
+ {
+ return $this->width;
+ }
- /**
- * @return float The height of the bitmap.
- */
- final public function getHeight(): float
- {
- return $this->height;
- }
+ /**
+ * @return float The height of the bitmap.
+ */
+ final public function getHeight(): float
+ {
+ return $this->height;
+ }
- /**
- * @return bool Whether this subclass supports cropping.
- */
- public function isCropSupported(): bool
- {
- return false;
- }
+ /**
+ * @return bool Whether this subclass supports cropping.
+ */
+ public function isCropSupported(): bool
+ {
+ return false;
+ }
- /**
- * Returns a new object with cropped image data. Implementations may keep a reference to the
- * original data rather than a copy. Only callable if isCropSupported() is true.
- *
- * @param $left The left coordinate, which must be in [0,getWidth())
- * @param $top The top coordinate, which must be in [0,getHeight())
- * @param $width The width of the rectangle to crop.
- * @param $height The height of the rectangle to crop.
- *
- * @return mixed A cropped version of this object.
- */
- public function crop($left, $top, $width, $height)
- {
- throw new \Exception("This luminance source does not support cropping.");
- }
+ /**
+ * Returns a new object with cropped image data. Implementations may keep a reference to the
+ * original data rather than a copy. Only callable if isCropSupported() is true.
+ *
+ * @param $left The left coordinate, which must be in [0,getWidth())
+ * @param $top The top coordinate, which must be in [0,getHeight())
+ * @param $width The width of the rectangle to crop.
+ * @param $height The height of the rectangle to crop.
+ *
+ * @return mixed A cropped version of this object.
+ */
+ abstract public function crop($left, $top, $width, $height): LuminanceSource;
- /**
- * @return bool Whether this subclass supports counter-clockwise rotation.
- */
- public function isRotateSupported(): bool
- {
- return false;
- }
+ /**
+ * @return bool Whether this subclass supports counter-clockwise rotation.
+ */
+ public function isRotateSupported(): bool
+ {
+ return false;
+ }
- /**
- * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
- * white and vice versa, and each value becomes (255-value).
- */
- // public function invert()
- // {
- // return new InvertedLuminanceSource($this);
- // }
+ /**
+ * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
+ * white and vice versa, and each value becomes (255-value).
+ */
+ // public function invert()
+ // {
+ // return new InvertedLuminanceSource($this);
+ // }
- /**
- * Returns a new object with rotated image data by 90 degrees counterclockwise.
- * Only callable if {@link #isRotateSupported()} is true.
- *
- * @return mixed A rotated version of this object.
- */
- public function rotateCounterClockwise()
- {
- throw new \Exception("This luminance source does not support rotation by 90 degrees.");
- }
+ /**
+ * Returns a new object with rotated image data by 90 degrees counterclockwise.
+ * Only callable if {@link #isRotateSupported()} is true.
+ *
+ * @return mixed A rotated version of this object.
+ */
+ abstract public function rotateCounterClockwise(): void;
- /**
- * Returns a new object with rotated image data by 45 degrees counterclockwise.
- * Only callable if {@link #isRotateSupported()} is true.
- *
- * @return mixed A rotated version of this object.
- */
- public function rotateCounterClockwise45()
- {
- throw new \Exception("This luminance source does not support rotation by 45 degrees.");
- }
+ /**
+ * Returns a new object with rotated image data by 45 degrees counterclockwise.
+ * Only callable if {@link #isRotateSupported()} is true.
+ *
+ * @return mixed A rotated version of this object.
+ */
+ abstract public function rotateCounterClockwise45(): void;
- final public function toString()
- {
- $row = [];
- $result = '';
- for ($y = 0; $y < $this->height; $y++) {
- $row = $this->getRow($y, $row);
- for ($x = 0; $x < $this->width; $x++) {
- $luminance = $row[$x] & 0xFF;
- $c = '';
- if ($luminance < 0x40) {
- $c = '#';
- } elseif ($luminance < 0x80) {
- $c = '+';
- } elseif ($luminance < 0xC0) {
- $c = '.';
- } else {
- $c = ' ';
- }
- $result .= ($c);
- }
- $result .= ('\n');
- }
+ final public function toString(): string
+ {
+ $row = [];
+ $result = '';
+ for ($y = 0; $y < $this->height; $y++) {
+ $row = $this->getRow($y, $row);
+ for ($x = 0; $x < $this->width; $x++) {
+ $luminance = $row[$x] & 0xFF;
+ $c = '';
+ if ($luminance < 0x40) {
+ $c = '#';
+ } elseif ($luminance < 0x80) {
+ $c = '+';
+ } elseif ($luminance < 0xC0) {
+ $c = '.';
+ } else {
+ $c = ' ';
+ }
+ $result .= ($c);
+ }
+ $result .= ('\n');
+ }
- return $result;
- }
+ return $result;
+ }
- /**
- * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
- * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
- * to bitwise and with 0xff for each value. It is preferable for implementations of this method
- * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
- * getMatrix() may never be called.
- *
- * @param $y ; The row to fetch, which must be in [0,getHeight())
- * @param $row ; An optional preallocated array. If null or too small, it will be ignored.
- * Always use the returned object, and ignore the .length of the array.
- *
- * @return array
- * An array containing the luminance data.
- */
- abstract public function getRow($y, $row);
+ /**
+ * Fetches one row of luminance data from the underlying platform's bitmap. Values range from
+ * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
+ * to bitwise and with 0xff for each value. It is preferable for implementations of this method
+ * to only fetch this row rather than the whole image, since no 2D Readers may be installed and
+ * getMatrix() may never be called.
+ *
+ * @param $y ; The row to fetch, which must be in [0,getHeight())
+ * @param $row ; An optional preallocated array. If null or too small, it will be ignored.
+ * Always use the returned object, and ignore the .length of the array.
+ *
+ * @return array
+ * An array containing the luminance data.
+ */
+ abstract public function getRow(int $y, array $row);
}
diff --git a/lib/NotFoundException.php b/lib/NotFoundException.php
index 5dab398..0b5261b 100644
--- a/lib/NotFoundException.php
+++ b/lib/NotFoundException.php
@@ -27,10 +27,10 @@ final class NotFoundException extends ReaderException
{
private static ?\Zxing\NotFoundException $instance = null;
- public static function getNotFoundInstance()
+ public static function getNotFoundInstance(string $message = ""): self
{
if (!self::$instance) {
- self::$instance = new NotFoundException();
+ self::$instance = new NotFoundException($message);
}
return self::$instance;
diff --git a/lib/PlanarYUVLuminanceSource.php b/lib/PlanarYUVLuminanceSource.php
index fa08fd1..b3475ba 100644
--- a/lib/PlanarYUVLuminanceSource.php
+++ b/lib/PlanarYUVLuminanceSource.php
@@ -44,8 +44,7 @@ public function __construct(
$width,
$height,
$reverseHorizontal
- )
- {
+ ) {
parent::__construct($width, $height);
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
@@ -60,15 +59,24 @@ public function __construct(
}
}
- //@Override
+ public function rotateCounterClockwise(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise");
+ }
+
+ public function rotateCounterClockwise45(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise45");
+ }
+
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
- throw new \InvalidArgumentException("Requested row is outside the image: " + \Y);
+ throw new \InvalidArgumentException("Requested row is outside the image: " + $y);
}
$width = $this->getWidth();
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
- $row = [];//new byte[width];
+ $row = []; //new byte[width];
}
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
$row = arraycopy($this->yuvData, $offset, $row, 0, $width);
@@ -76,7 +84,7 @@ public function getRow($y, $row = null)
return $row;
}
- //@Override
+
public function getMatrix()
{
$width = $this->getWidth();
@@ -89,7 +97,7 @@ public function getMatrix()
}
$area = $width * $height;
- $matrix = [];//new byte[area];
+ $matrix = []; //new byte[area];
$inputOffset = $this->top * $this->dataWidth + $this->left;
// If the width matches the full width of the underlying data, perform a single copy.
@@ -111,7 +119,7 @@ public function getMatrix()
}
// @Override
- public function isCropSupported()
+ public function isCropSupported(): bool
{
return true;
}
@@ -131,11 +139,14 @@ public function crop($left, $top, $width, $height): \Zxing\PlanarYUVLuminanceSou
);
}
- public function renderThumbnail()
+ /**
+ * @return int[]
+ */
+ public function renderThumbnail(): array
{
$width = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
$height = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
- $pixels = [];//new int[width * height];
+ $pixels = []; //new int[width * height];
$yuv = $this->yuvData;
$inputOffset = $this->top * $this->dataWidth + $this->left;
@@ -162,21 +173,27 @@ public function renderThumbnail()
/**
* @return height of image from {@link #renderThumbnail()}
*/
- /*
- public int getThumbnailHeight() {
- return getHeight() / THUMBNAIL_SCALE_FACTOR;
- }
-
- private void reverseHorizontal(int width, int height) {
- byte[] yuvData = this.yuvData;
- for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
- int middle = rowStart + width / 2;
- for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
- byte temp = yuvData[x1];
- yuvData[x1] = yuvData[x2];
- yuvData[x2] = temp;
- }
+ /**public function getThumbnailHeight(): int
+ {
+ return getHeight() / THUMBNAIL_SCALE_FACTOR;
+ }*/
+
+ /**
+ *
+ * @param int $width
+ * @param int $height
+ * @return void
+ */
+ private function reverseHorizontal(int $width, int $height): void
+ {
+ $yuvData = $this->yuvData;
+ for ($y = 0, $rowStart = $this->top * $this->dataWidth + $this->left; $y < $height; $y++, $rowStart += $this->dataWidth) {
+ $middle = (int)round($rowStart + $width / 2);
+ for ($x1 = $rowStart, $x2 = $rowStart + $width - 1; $x1 < $middle; $x1++, $x2--) {
+ $temp = $yuvData[$x1];
+ $yuvData[$x1] = $yuvData[$x2];
+ $yuvData[$x2] = $temp;
+ }
+ }
}
- }
-*/
}
diff --git a/lib/QrReader.php b/lib/QrReader.php
index 1ee649c..f5a9660 100644
--- a/lib/QrReader.php
+++ b/lib/QrReader.php
@@ -2,18 +2,29 @@
namespace Zxing;
+use Exception;
use Zxing\Common\HybridBinarizer;
use Zxing\Qrcode\QRCodeReader;
final class QrReader
{
+ /**
+ * @var string
+ */
public const SOURCE_TYPE_FILE = 'file';
+ /**
+ * @var string
+ */
public const SOURCE_TYPE_BLOB = 'blob';
+ /**
+ * @var string
+ */
public const SOURCE_TYPE_RESOURCE = 'resource';
private readonly \Zxing\BinaryBitmap $bitmap;
private readonly \Zxing\Qrcode\QRCodeReader $reader;
private \Zxing\Result|bool|null $result = null;
+ private ?Exception $error = null;
public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true)
{
@@ -78,8 +89,9 @@ public function decode($hints = null): void
{
try {
$this->result = $this->reader->decode($this->bitmap, $hints);
- } catch (NotFoundException|FormatException|ChecksumException) {
+ } catch (NotFoundException | FormatException | ChecksumException $e) {
$this->result = false;
+ $this->error = $e;
}
}
@@ -94,8 +106,13 @@ public function text($hints = null)
return $this->result;
}
- public function getResult()
+ public function getResult(): bool|Result|null
{
return $this->result;
}
+
+ public function getError(): Exception|null
+ {
+ return $this->error;
+ }
}
diff --git a/lib/Qrcode/Decoder/BitMatrixParser.php b/lib/Qrcode/Decoder/BitMatrixParser.php
index 0732f86..27afb73 100644
--- a/lib/Qrcode/Decoder/BitMatrixParser.php
+++ b/lib/Qrcode/Decoder/BitMatrixParser.php
@@ -29,7 +29,7 @@ final class BitMatrixParser
/**
* @var mixed|null
*/
- private $parsedVersion;
+ private $parsedVersion;
private $parsedFormatInfo;
private $mirror;
@@ -52,7 +52,7 @@ public function __construct($bitMatrix)
* correct order in order to reconstruct the codewords bytes contained within the
* QR Code.
*
- * @return bytes encoded within the QR Code
+ * @return array bytes encoded within the QR Code
* @throws FormatException if the exact number of bytes expected is not read
*/
public function readCodewords()
@@ -159,7 +159,12 @@ public function readFormatInformation()
throw FormatException::getFormatInstance();
}
- private function copyBit($i, $j, $versionBits)
+ /**
+ * @psalm-param 0 $versionBits
+ *
+ * @psalm-return 0|1
+ */
+ private function copyBit(int|float $i, int|float $j, int $versionBits): int
{
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
@@ -216,7 +221,7 @@ public function readVersion()
return $theParsedVersion;
}
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("both version information locations cannot be parsed as the valid encoding of version information");
}
/**
@@ -238,7 +243,7 @@ public function remask(): void
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
* {@link #mirror()} method should be called.
*
- * @param Whether $mirror to read version and format information mirrored.
+ * @param bool $mirror Whether to read version and format information mirrored.
*/
public function setMirror($mirror): void
{
diff --git a/lib/Qrcode/Decoder/DataBlock.php b/lib/Qrcode/Decoder/DataBlock.php
index 1fb8565..2677537 100644
--- a/lib/Qrcode/Decoder/DataBlock.php
+++ b/lib/Qrcode/Decoder/DataBlock.php
@@ -29,27 +29,26 @@ final class DataBlock
//byte[]
private function __construct(private $numDataCodewords, private $codewords)
- {
- }
+ {
+ }
/**
* When QR Codes use multiple data blocks, they are actually interleaved.
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
* method will separate the data into original blocks.
*
- * @param bytes $rawCodewords as read directly from the QR Code
- * @param version $version of the QR Code
- * @param error $ecLevel-correction level of the QR Code
+ * @param array $rawCodewords as read directly from the QR Code
+ * @param Version $version of the QR Code
+ * @param ErrorCorrectionLevel $ecLevel error-correction level of the QR Code
*
- * @return array DataBlocks containing original bytes, "de-interleaved" from representation in the
- * QR Code
+ * @return \Zxing\Qrcode\Decoder\DataBlock[] DataBlocks containing original bytes, "de-interleaved" from representation in the
+ QR Code
*/
public static function getDataBlocks(
$rawCodewords,
- $version,
- $ecLevel
- )
- {
+ Version $version,
+ ErrorCorrectionLevel $ecLevel
+ ): array {
if ((is_countable($rawCodewords) ? count($rawCodewords) : 0) != $version->getTotalCodewords()) {
throw new \InvalidArgumentException();
}
@@ -66,7 +65,7 @@ public static function getDataBlocks(
}
// Now establish DataBlocks of the appropriate size and number of data codewords
- $result = [];//new DataBlock[$totalBlocks];
+ $result = []; //new DataBlock[$totalBlocks];
$numResultBlocks = 0;
foreach ($ecBlockArray as $ecBlock) {
$ecBlockCount = $ecBlock->getCount();
diff --git a/lib/Qrcode/Decoder/DataMask.php b/lib/Qrcode/Decoder/DataMask.php
index 4d5d2f2..6e4d5ce 100644
--- a/lib/Qrcode/Decoder/DataMask.php
+++ b/lib/Qrcode/Decoder/DataMask.php
@@ -56,7 +56,7 @@ public static function Init(): void
}
/**
- * @param a $reference value between 0 and 7 indicating one of the eight possible
+ * @param int $reference a value between 0 and 7 indicating one of the eight possible
* data mask patterns a QR Code may use
*
* @return DataMask encapsulating the data mask pattern
@@ -74,8 +74,8 @@ public static function forReference($reference)
* Implementations of this method reverse the data masking process applied to a QR Code and
* make its bits ready to read.
*
- * @param representation $bits of QR Code bits
- * @param dimension $dimension of QR Code, represented by bits, being unmasked
+ * @param BitMatrix $bits representation of QR Code bits
+ * @param int $dimension dimension of QR Code, represented by bits, being unmasked
*/
final public function unmaskBitMatrix($bits, $dimension): void
{
@@ -88,7 +88,11 @@ final public function unmaskBitMatrix($bits, $dimension): void
}
}
- abstract public function isMasked($i, $j);
+ /**
+ * @psalm-param 0|positive-int $i
+ * @psalm-param 0|positive-int $j
+ */
+ abstract public function isMasked(int $i, int $j);
}
DataMask::Init();
@@ -99,7 +103,7 @@ abstract public function isMasked($i, $j);
final class DataMask000 extends DataMask
{
// @Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return (($i + $j) & 0x01) == 0;
}
@@ -110,8 +114,7 @@ public function isMasked($i, $j)
*/
final class DataMask001 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return ($i & 0x01) == 0;
}
@@ -122,8 +125,7 @@ public function isMasked($i, $j)
*/
final class DataMask010 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return $j % 3 == 0;
}
@@ -134,8 +136,7 @@ public function isMasked($i, $j)
*/
final class DataMask011 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return ($i + $j) % 3 == 0;
}
@@ -146,8 +147,7 @@ public function isMasked($i, $j)
*/
final class DataMask100 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
}
@@ -158,8 +158,7 @@ public function isMasked($i, $j)
*/
final class DataMask101 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
$temp = $i * $j;
@@ -172,8 +171,7 @@ public function isMasked($i, $j)
*/
final class DataMask110 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
$temp = $i * $j;
@@ -186,8 +184,7 @@ public function isMasked($i, $j)
*/
final class DataMask111 extends DataMask
{
- //@Override
- public function isMasked($i, $j)
+ public function isMasked($i, $j): bool
{
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
}
diff --git a/lib/Qrcode/Decoder/DecodedBitStreamParser.php b/lib/Qrcode/Decoder/DecodedBitStreamParser.php
index d4bdc7f..e7a958d 100644
--- a/lib/Qrcode/Decoder/DecodedBitStreamParser.php
+++ b/lib/Qrcode/Decoder/DecodedBitStreamParser.php
@@ -17,6 +17,7 @@
namespace Zxing\Qrcode\Decoder;
+use ValueError;
use Zxing\Common\BitSource;
use Zxing\Common\CharacterSetECI;
use Zxing\Common\DecoderResult;
@@ -43,15 +44,17 @@ final class DecodedBitStreamParser
];
private static int $GB2312_SUBSET = 1;
+ /**
+ * @psalm-param array $bytes
+ */
public static function decode(
- $bytes,
- $version,
- $ecLevel,
- $hints
- ): \Zxing\Common\DecoderResult
- {
+ array $bytes,
+ Version $version,
+ ErrorCorrectionLevel $ecLevel,
+ array|null $hints
+ ): \Zxing\Common\DecoderResult {
$bits = new BitSource($bytes);
- $result = '';//new StringBuilder(50);
+ $result = ''; //new StringBuilder(50);
$byteSegments = [];
$symbolSequence = -1;
$parityData = -1;
@@ -74,7 +77,7 @@ public static function decode(
$fc1InEffect = true;
} elseif ($mode == Mode::$STRUCTURED_APPEND) {
if ($bits->available() < 16) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Bits available < 16");
}
// sequence number and parity is added later to the result metadata
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
@@ -85,7 +88,7 @@ public static function decode(
$value = self::parseECIValue($bits);
$currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
if ($currentCharacterSetECI == null) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Current character set ECI is null");
}
} else {
// First handle Hanzi mode which does not start with character count
@@ -109,28 +112,28 @@ public static function decode(
} elseif ($mode == Mode::$KANJI) {
self::decodeKanjiSegment($bits, $result, $count);
} else {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Unknown mode $mode to decode");
}
}
}
}
} while ($mode != Mode::$TERMINATOR);
- } catch (\InvalidArgumentException) {
+ } catch (\InvalidArgumentException $e) {
// from readBits() calls
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Invalid argument exception when formatting: " . $e->getMessage());
}
return new DecoderResult(
$bytes,
$result,
empty($byteSegments) ? null : $byteSegments,
- $ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
+ $ecLevel == null ? null : 'L', //ErrorCorrectionLevel::toString($ecLevel),
$symbolSequence,
$parityData
);
}
- private static function parseECIValue($bits)
+ private static function parseECIValue(BitSource $bits): int
{
$firstByte = $bits->readBits(8);
if (($firstByte & 0x80) == 0) {
@@ -149,21 +152,22 @@ private static function parseECIValue($bits)
return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
}
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("ECI Value parsing failed.");
}
/**
* See specification GBT 18284-2000
+ *
+ * @psalm-param '' $result
*/
private static function decodeHanziSegment(
- $bits,
- &$result,
- $count
- )
- {
+ BitSource $bits,
+ string &$result,
+ int $count
+ ): void {
// Don't crash trying to read more bits than we have available.
if ($count * 13 > $bits->available()) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Trying to read more bits than we have available");
}
// Each character will require 2 bytes. Read the characters as 2-byte pairs
@@ -181,8 +185,8 @@ private static function decodeHanziSegment(
// In the 0xB0A1 to 0xFAFE range
$assembledTwoBytes += 0x0A6A1;
}
- $buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
- $buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
+ $buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF); //(byte)
+ $buffer[$offset + 1] = ($assembledTwoBytes & 0xFF); //(byte)
$offset += 2;
$count--;
}
@@ -190,20 +194,19 @@ private static function decodeHanziSegment(
}
private static function decodeNumericSegment(
- $bits,
- &$result,
- $count
- )
- {
+ BitSource $bits,
+ string &$result,
+ int $count
+ ): void {
// Read three digits at a time
while ($count >= 3) {
// Each 10 bits encodes three digits
if ($bits->available() < 10) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Not enough bits available");
}
$threeDigitsBits = $bits->readBits(10);
if ($threeDigitsBits >= 1000) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Too many three digit bits");
}
$result .= (self::toAlphaNumericChar($threeDigitsBits / 100));
$result .= (self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
@@ -213,48 +216,50 @@ private static function decodeNumericSegment(
if ($count == 2) {
// Two digits left over to read, encoded in 7 bits
if ($bits->available() < 7) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Two digits left over to read, encoded in 7 bits, but only " . $bits->available() . ' bits available');
}
$twoDigitsBits = $bits->readBits(7);
if ($twoDigitsBits >= 100) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Too many bits: $twoDigitsBits expected < 100");
}
$result .= (self::toAlphaNumericChar($twoDigitsBits / 10));
$result .= (self::toAlphaNumericChar($twoDigitsBits % 10));
} elseif ($count == 1) {
// One digit left over to read
if ($bits->available() < 4) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("One digit left to read, but < 4 bits available");
}
$digitBits = $bits->readBits(4);
if ($digitBits >= 10) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Too many bits: $digitBits expected < 10");
}
$result .= (self::toAlphaNumericChar($digitBits));
}
}
- private static function toAlphaNumericChar($value)
+ /**
+ * @param float|int $value
+ */
+ private static function toAlphaNumericChar(int|float $value)
{
if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("$value has too many alphanumeric chars");
}
return self::$ALPHANUMERIC_CHARS[$value];
}
private static function decodeAlphanumericSegment(
- $bits,
- &$result,
- $count,
- $fc1InEffect
- )
- {
+ BitSource $bits,
+ string &$result,
+ int $count,
+ bool $fc1InEffect
+ ): void {
// Read two characters at a time
$start = strlen((string) $result);
while ($count > 1) {
if ($bits->available() < 11) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Not enough bits available to read two expected characters");
}
$nextTwoCharsBits = $bits->readBits(11);
$result .= (self::toAlphaNumericChar($nextTwoCharsBits / 45));
@@ -264,7 +269,7 @@ private static function decodeAlphanumericSegment(
if ($count == 1) {
// special case: one character left
if ($bits->available() < 6) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Not enough bits available to read one expected character");
}
$result .= self::toAlphaNumericChar($bits->readBits(6));
}
@@ -275,10 +280,10 @@ private static function decodeAlphanumericSegment(
if ($result[$i] == '%') {
if ($i < strlen((string) $result) - 1 && $result[$i + 1] == '%') {
// %% is rendered as %
- $result = substr_replace($result, '', $i + 1, 1);//deleteCharAt(i + 1);
+ $result = substr_replace($result, '', $i + 1, 1); //deleteCharAt(i + 1);
} else {
// In alpha mode, % should be converted to FNC1 separator 0x1D
- $result . setCharAt($i, chr(0x1D));
+ $result[$i] = chr(0x1D);
}
}
}
@@ -286,22 +291,21 @@ private static function decodeAlphanumericSegment(
}
private static function decodeByteSegment(
- $bits,
- &$result,
- $count,
- $currentCharacterSetECI,
- &$byteSegments,
- $hints
- )
- {
+ BitSource $bits,
+ string &$result,
+ int $count,
+ CharacterSetECI|null $currentCharacterSetECI,
+ array &$byteSegments,
+ array|null $hints
+ ): void {
// Don't crash trying to read more bits than we have available.
if (8 * $count > $bits->available()) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Trying to read more bits than we have available");
}
$readBytes = fill_array(0, $count, 0);
for ($i = 0; $i < $count; $i++) {
- $readBytes[$i] = $bits->readBits(8);//(byte)
+ $readBytes[$i] = $bits->readBits(8); //(byte)
}
$text = implode(array_map('chr', $readBytes));
$encoding = '';
@@ -312,25 +316,28 @@ private static function decodeByteSegment(
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
- $encoding = mb_detect_encoding($text, $hints);
+ try {
+ $encoding = mb_detect_encoding($text, $hints);
+ } catch (ValueError $e) {
+ $encoding = mb_detect_encoding($text, mb_detect_order(), false);
+ }
} else {
$encoding = $currentCharacterSetECI->name();
}
- // $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
- $result .= $text;//(new String(readBytes, encoding));
+ $result .= mb_convert_encoding($text, $encoding); //(new String(readBytes, encoding));
+ // $result .= $text; //(new String(readBytes, encoding));
$byteSegments = array_merge($byteSegments, $readBytes);
}
private static function decodeKanjiSegment(
- $bits,
- &$result,
- $count
- )
- {
+ BitSource $bits,
+ string &$result,
+ int $count
+ ): void {
// Don't crash trying to read more bits than we have available.
if ($count * 13 > $bits->available()) {
- throw FormatException::getFormatInstance();
+ throw FormatException::getFormatInstance("Trying to read more bits than we have available");
}
// Each character will require 2 bytes. Read the characters as 2-byte pairs
@@ -348,7 +355,7 @@ private static function decodeKanjiSegment(
// In the 0xE040 to 0xEBBF range
$assembledTwoBytes += 0x0C140;
}
- $buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
+ $buffer[$offset] = ($assembledTwoBytes >> 8); //(byte)
$buffer[$offset + 1] = $assembledTwoBytes; //(byte)
$offset += 2;
$count--;
diff --git a/lib/Qrcode/Decoder/Decoder.php b/lib/Qrcode/Decoder/Decoder.php
index 4a40e89..c88279e 100644
--- a/lib/Qrcode/Decoder/Decoder.php
+++ b/lib/Qrcode/Decoder/Decoder.php
@@ -19,6 +19,7 @@
use Zxing\ChecksumException;
use Zxing\Common\BitMatrix;
+use Zxing\Common\DecoderResult;
use Zxing\Common\Reedsolomon\GenericGF;
use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
use Zxing\Common\Reedsolomon\ReedSolomonException;
@@ -39,7 +40,7 @@ public function __construct()
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
}
- public function decode($variable, $hints = null)
+ public function decode(BitMatrix|BitMatrixParser $variable, array|null $hints = null): string|DecoderResult
{
if (is_array($variable)) {
return $this->decodeImage($variable, $hints);
@@ -56,15 +57,16 @@ public function decode($variable, $hints = null)
* "true" is taken to mean a black module.
*
* @param array $image booleans representing white/black QR Code modules
- * @param decoding $hints hints that should be used to influence decoding
+ * @param array|null $hints decoding hints that should be used to influence decoding
+ *
+ * @return DecoderResult|string text and bytes encoded within the QR Code
*
- * @return text and bytes encoded within the QR Code
* @throws FormatException if the QR Code cannot be decoded
* @throws ChecksumException if error correction fails
*/
- public function decodeImage($image, $hints = null)
+ public function decodeImage(array $image, $hints = null): string|DecoderResult
{
- $dimension = count($image);
+ $dimension = is_countable($image) ? count($image) : 0;
$bits = new BitMatrix($dimension);
for ($i = 0; $i < $dimension; $i++) {
for ($j = 0; $j < $dimension; $j++) {
@@ -82,16 +84,17 @@ public function decodeImage($image, $hints = null)
* Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.
*
* @param BitMatrix $bits booleans representing white/black QR Code modules
- * @param decoding $hints hints that should be used to influence decoding
+ * @param array|null $hints decoding hints that should be used to influence decoding
+ *
+ * @return DecoderResult|string string text and bytes encoded within the QR Code
*
- * @return text and bytes encoded within the QR Code
* @throws FormatException if the QR Code cannot be decoded
* @throws ChecksumException if error correction fails
*/
- public function decodeBits($bits, $hints = null)
+ public function decodeBits(\Zxing\Common\BitMatrix $bits, $hints = null): string|DecoderResult
{
-// Construct a parser and read version, error-correction level
+ // Construct a parser and read version, error-correction level
$parser = new BitMatrixParser($bits);
$fe = null;
$ce = null;
@@ -132,7 +135,7 @@ public function decodeBits($bits, $hints = null)
$result->setOther(new QRCodeDecoderMetaData(true));
return $result;
- } catch (FormatException $e) {// catch (FormatException | ChecksumException e) {
+ } catch (FormatException $e) { // catch (FormatException | ChecksumException e) {
// Throw the exception from the original reading
if ($fe != null) {
throw $fe;
@@ -144,7 +147,7 @@ public function decodeBits($bits, $hints = null)
}
}
- private function decodeParser($parser, $hints = null)
+ private function decodeParser(\Zxing\Qrcode\Decoder\BitMatrixParser $parser, array $hints = null): DecoderResult
{
$version = $parser->readVersion();
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
@@ -180,12 +183,12 @@ private function decodeParser($parser, $hints = null)
* Given data and error-correction codewords received, possibly corrupted by errors, attempts to
* correct the errors in-place using Reed-Solomon error correction.
*
- * @param data $codewordBytes and error correction codewords
- * @param number $numDataCodewords of codewords that are data bytes
+ * @param array $codewordBytes and error correction codewords
+ * @param int $numDataCodewords of codewords that are data bytes
*
* @throws ChecksumException if error correction fails
*/
- private function correctErrors(&$codewordBytes, $numDataCodewords)
+ private function correctErrors(&$codewordBytes, int $numDataCodewords): void
{
$numCodewords = is_countable($codewordBytes) ? count($codewordBytes) : 0;
// First read into an array of ints
diff --git a/lib/Qrcode/Decoder/ErrorCorrectionLevel.php b/lib/Qrcode/Decoder/ErrorCorrectionLevel.php
index f2ccfca..3dabca6 100644
--- a/lib/Qrcode/Decoder/ErrorCorrectionLevel.php
+++ b/lib/Qrcode/Decoder/ErrorCorrectionLevel.php
@@ -28,11 +28,11 @@ class ErrorCorrectionLevel
/**
* @var \Zxing\Qrcode\Decoder\ErrorCorrectionLevel[]|null
*/
- private static ?array $FOR_BITS = null;
+ private static ?array $FOR_BITS = null;
public function __construct(private $bits, private $ordinal = 0)
- {
- }
+ {
+ }
public static function Init(): void
{
@@ -57,9 +57,9 @@ public static function Init(): void
/**
* @param int $bits containing the two bits encoding a QR Code's error correction level
*
- * @return ErrorCorrectionLevel representing the encoded error correction level
+ * @return null|self representing the encoded error correction level
*/
- public static function forBits($bits)
+ public static function forBits(int $bits): self|null
{
if ($bits < 0 || $bits >= (is_countable(self::$FOR_BITS) ? count(self::$FOR_BITS) : 0)) {
throw new \InvalidArgumentException();
diff --git a/lib/Qrcode/Decoder/FormatInformation.php b/lib/Qrcode/Decoder/FormatInformation.php
index fcee43c..a358904 100644
--- a/lib/Qrcode/Decoder/FormatInformation.php
+++ b/lib/Qrcode/Decoder/FormatInformation.php
@@ -37,7 +37,7 @@ final class FormatInformation
* Offset i holds the number of 1 bits in the binary representation of i
* @var int[]|null
*/
- private static ?array $BITS_SET_IN_HALF_BYTE = null;
+ private static ?array $BITS_SET_IN_HALF_BYTE = null;
private readonly \Zxing\Qrcode\Decoder\ErrorCorrectionLevel $errorCorrectionLevel;
private readonly int $dataMask;
@@ -95,10 +95,13 @@ public static function Init(): void
* @param $maskedFormatInfo2 ; second copy of same info; both are checked at the same time
* to establish best match
*
- * @return information about the format it specifies, or {@code null}
+ * @return FormatInformation|null information about the format it specifies, or {@code null}
* if doesn't seem to match any known pattern
+ *
+ * @psalm-param 0|1 $maskedFormatInfo1
+ * @psalm-param 0|1 $maskedFormatInfo2
*/
- public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
+ public static function decodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2)
{
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
if ($formatInfo != null) {
@@ -113,7 +116,11 @@ public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormat
);
}
- private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
+ /**
+ * @psalm-param 0|1 $maskedFormatInfo1
+ * @psalm-param 0|1 $maskedFormatInfo2
+ */
+ private static function doDecodeFormatInformation(int $maskedFormatInfo1, int $maskedFormatInfo2): self|null
{
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
$bestDifference = PHP_INT_MAX;
@@ -147,7 +154,10 @@ private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFor
return null;
}
- public static function numBitsDiffering($a, $b)
+ /**
+ * @psalm-param 0|1 $a
+ */
+ public static function numBitsDiffering(int $a, $b): int
{
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
// Count bits set quickly with a series of lookups:
@@ -161,24 +171,24 @@ public static function numBitsDiffering($a, $b)
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 28) & 0x0F)];
}
- public function getErrorCorrectionLevel()
+ public function getErrorCorrectionLevel(): ErrorCorrectionLevel
{
return $this->errorCorrectionLevel;
}
- public function getDataMask()
+ public function getDataMask(): int
{
return $this->dataMask;
}
- //@Override
+
public function hashCode()
{
return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($this->dataMask);
}
- //@Override
- public function equals($o)
+
+ public function equals($o): bool
{
if (!($o instanceof FormatInformation)) {
return false;
diff --git a/lib/Qrcode/Decoder/Mode.php b/lib/Qrcode/Decoder/Mode.php
index 165a5c0..a7473c5 100644
--- a/lib/Qrcode/Decoder/Mode.php
+++ b/lib/Qrcode/Decoder/Mode.php
@@ -37,8 +37,8 @@ class Mode
public static $HANZI;
public function __construct(private $characterCountBitsForVersions, private $bits)
- {
- }
+ {
+ }
public static function Init(): void
{
@@ -56,35 +56,35 @@ public static function Init(): void
}
/**
- * @param four $bits bits encoding a QR Code data mode
+ * @param int $bits four bits encoding a QR Code data mode
*
* @return Mode encoded by these bits
- * @throws InvalidArgumentException if bits do not correspond to a known mode
+ * @throws \InvalidArgumentException if bits do not correspond to a known mode
*/
public static function forBits($bits)
{
return match ($bits) {
- 0x0 => self::$TERMINATOR,
- 0x1 => self::$NUMERIC,
- 0x2 => self::$ALPHANUMERIC,
- 0x3 => self::$STRUCTURED_APPEND,
- 0x4 => self::$BYTE,
- 0x5 => self::$FNC1_FIRST_POSITION,
- 0x7 => self::$ECI,
- 0x8 => self::$KANJI,
- 0x9 => self::$FNC1_SECOND_POSITION,
- 0xD => self::$HANZI,
- default => throw new \InvalidArgumentException(),
- };
+ 0x0 => self::$TERMINATOR,
+ 0x1 => self::$NUMERIC,
+ 0x2 => self::$ALPHANUMERIC,
+ 0x3 => self::$STRUCTURED_APPEND,
+ 0x4 => self::$BYTE,
+ 0x5 => self::$FNC1_FIRST_POSITION,
+ 0x7 => self::$ECI,
+ 0x8 => self::$KANJI,
+ 0x9 => self::$FNC1_SECOND_POSITION,
+ 0xD => self::$HANZI,
+ default => throw new \InvalidArgumentException(),
+ };
}
/**
* @param version $version in question
*
- * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
+ * @return int number of bits used, in this QR Code symbol {@link Version}, to encode the
* count of characters that will follow encoded in this Mode
*/
- public function getCharacterCountBits($version)
+ public function getCharacterCountBits(\Zxing\Qrcode\Decoder\version $version)
{
$number = $version->getVersionNumber();
$offset = 0;
diff --git a/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php b/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php
index ecc6421..cd498c5 100644
--- a/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php
+++ b/lib/Qrcode/Decoder/QRCodeDecoderMetaData.php
@@ -9,10 +9,10 @@ class QRCodeDecoderMetaData
* @param bool $mirrored
*/
public function __construct(private $mirrored)
- {
- }
+ {
+ }
- public function isMirrored()
+ public function isMirrored(): bool
{
return $this->mirrored;
}
diff --git a/lib/Qrcode/Decoder/Version.php b/lib/Qrcode/Decoder/Version.php
index be5f8ce..5a6e0ca 100644
--- a/lib/Qrcode/Decoder/Version.php
+++ b/lib/Qrcode/Decoder/Version.php
@@ -44,15 +44,15 @@ class Version
/**
* @var mixed|null
*/
- private static $VERSIONS;
+ private static $VERSIONS;
private readonly float|int $totalCodewords;
public function __construct(
private $versionNumber,
private $alignmentPatternCenters,
private $ecBlocks
- )
- {$total = 0;
+ ) {
+ $total = 0;
if (is_array($ecBlocks)) {
$ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock();
$ecbArray = $ecBlocks[0]->getECBlocks();
@@ -75,7 +75,7 @@ public function getAlignmentPatternCenters()
return $this->alignmentPatternCenters;
}
- public function getTotalCodewords()
+ public function getTotalCodewords(): int|float
{
return $this->totalCodewords;
}
@@ -85,7 +85,7 @@ public function getDimensionForVersion()
return 17 + 4 * $this->versionNumber;
}
- public function getECBlocksForLevel($ecLevel)
+ public function getECBlocksForLevel(ErrorCorrectionLevel $ecLevel)
{
return $this->ecBlocks[$ecLevel->getOrdinal()];
}
@@ -93,7 +93,7 @@ public function getECBlocksForLevel($ecLevel)
/**
* Deduces version information purely from QR Code dimensions.
*
- * @param dimension $dimension in modules
+ * @param int $dimension dimension in modules
* @return Version for a QR Code of that dimension
* @throws FormatException if dimension is not 1 mod 4
*/
@@ -109,7 +109,10 @@ public static function getProvisionalVersionForDimension($dimension)
}
}
- public static function getVersionForNumber($versionNumber)
+ /**
+ * @param float|int $versionNumber
+ */
+ public static function getVersionForNumber(int|float $versionNumber)
{
if ($versionNumber < 1 || $versionNumber > 40) {
throw new \InvalidArgumentException();
@@ -120,7 +123,10 @@ public static function getVersionForNumber($versionNumber)
return self::$VERSIONS[$versionNumber - 1];
}
- public static function decodeVersionInformation($versionBits)
+ /**
+ * @psalm-param 0|1 $versionBits
+ */
+ public static function decodeVersionInformation(int $versionBits)
{
$bestDifference = PHP_INT_MAX;
$bestVersion = 0;
@@ -150,7 +156,7 @@ public static function decodeVersionInformation($versionBits)
/**
* See ISO 18004:2006 Annex E
*/
- public function buildFunctionPattern()
+ public function buildFunctionPattern(): BitMatrix
{
$dimension = self::getDimensionForVersion();
$bitMatrix = new BitMatrix($dimension);
@@ -191,8 +197,12 @@ public function buildFunctionPattern()
}
/**
* See ISO 18004:2006 6.5.1 Table 9
+ *
+ * @return self[]
+ *
+ * @psalm-return array{0: self, 1: self, 2: self, 3: self, 4: self, 5: self, 6: self, 7: self, 8: self, 9: self, 10: self, 11: self, 12: self, 13: self, 14: self, 15: self, 16: self, 17: self, 18: self, 19: self, 20: self, 21: self, 22: self, 23: self, 24: self, 25: self, 26: self, 27: self, 28: self, 29: self, 30: self, 31: self, 32: self, 33: self, 34: self, 35: self, 36: self, 37: self, 38: self, 39: self}
*/
- private static function buildVersions()
+ private static function buildVersions(): array
{
return [
new Version(
@@ -656,8 +666,8 @@ private static function buildVersions()
final class ECBlocks
{
public function __construct(private $ecCodewordsPerBlock, private $ecBlocks)
- {
- }
+ {
+ }
public function getECCodewordsPerBlock()
{
@@ -692,8 +702,8 @@ public function getECBlocks()
final class ECB
{
public function __construct(private $count, private $dataCodewords)
- {
- }
+ {
+ }
public function getCount()
{
@@ -705,9 +715,7 @@ public function getDataCodewords()
return $this->dataCodewords;
}
-
- //@Override
- public function toString(): never
+ public function toString(): string
{
die('Version ECB toString()');
// return parent::$versionNumber;
diff --git a/lib/Qrcode/Detector/AlignmentPattern.php b/lib/Qrcode/Detector/AlignmentPattern.php
index 5f0e7c4..2967e86 100644
--- a/lib/Qrcode/Detector/AlignmentPattern.php
+++ b/lib/Qrcode/Detector/AlignmentPattern.php
@@ -36,7 +36,7 @@ public function __construct($posX, $posY, private $estimatedModuleSize)
* Determines if this alignment pattern "about equals" an alignment pattern at the stated
* position and size -- meaning, it is at nearly the same center with nearly the same size.
*/
- public function aboutEquals($moduleSize, $i, $j)
+ public function aboutEquals($moduleSize, $i, $j): bool
{
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
diff --git a/lib/Qrcode/Detector/AlignmentPatternFinder.php b/lib/Qrcode/Detector/AlignmentPatternFinder.php
index 614293f..07b1014 100644
--- a/lib/Qrcode/Detector/AlignmentPatternFinder.php
+++ b/lib/Qrcode/Detector/AlignmentPatternFinder.php
@@ -49,8 +49,8 @@ final class AlignmentPatternFinder
* @param float estimated $moduleSize module size so far
*/
public function __construct(private $image, private $startX, private $startY, private $width, private $height, private $moduleSize, private $resultPointCallback)
- {
- }
+ {
+ }
/**
* This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
@@ -126,16 +126,15 @@ public function find()
return $this->possibleCenters[0];
}
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Bottom right alignment pattern not found");
}
/**
- * @param count $stateCount of black/white/black pixels just read
+ * @param int $stateCount count of black/white/black pixels just read
*
- * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
- * used by alignment patterns to be considered a match
+ * @return bool iff the proportions of the counts is close enough to the 1/1/1 ratios used by alignment patterns to be considered a match
*/
- private function foundPatternCross($stateCount)
+ private function foundPatternCross($stateCount): bool
{
$moduleSize = $this->moduleSize;
$maxVariance = $moduleSize / 2.0;
@@ -154,9 +153,9 @@ private function foundPatternCross($stateCount)
* found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
* found the alignment pattern.
*
- * @param reading $stateCount state module counts from horizontal scan
- * @param row $i where alignment pattern may be found
- * @param end $j of possible alignment pattern in row
+ * @param array $stateCount state module counts from horizontal scan
+ * @param int $i where alignment pattern may be found
+ * @param int $j number of possible alignment pattern in row
*
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
*/
@@ -188,7 +187,7 @@ private function handlePossibleCenter($stateCount, $i, $j)
* Given a count of black/white/black pixels just seen and an end position,
* figures the location of the center of this black/white/black run.
*/
- private static function centerFromEnd($stateCount, $end)
+ private static function centerFromEnd(array $stateCount, int $end)
{
return (float)($end - $stateCount[2]) - $stateCount[1] / 2.0;
}
@@ -198,20 +197,19 @@ private static function centerFromEnd($stateCount, $end)
* "cross-checks" by scanning down vertically through the center of the possible
* alignment pattern to see if the same proportion is detected.
*
- * @param int row $startI where an alignment pattern was detected
- * @param float center $centerJ of the section that appears to cross an alignment pattern
- * @param int maximum $maxCount reasonable number of modules that should be
+ * @param int $startI row where an alignment pattern was detected
+ * @param float $centerJ center of the section that appears to cross an alignment pattern
+ * @param int $maxCount maximum reasonable number of modules that should be
* observed in any reading state, based on the results of the horizontal scan
*
* @return float vertical center of alignment pattern, or {@link Float#NaN} if not found
*/
private function crossCheckVertical(
- $startI,
- $centerJ,
+ int $startI,
+ int $centerJ,
$maxCount,
$originalStateCountTotal
- )
- {
+ ) {
$image = $this->image;
$maxI = $image->getHeight();
diff --git a/lib/Qrcode/Detector/Detector.php b/lib/Qrcode/Detector/Detector.php
index b3b3f04..f3dac17 100644
--- a/lib/Qrcode/Detector/Detector.php
+++ b/lib/Qrcode/Detector/Detector.php
@@ -17,6 +17,7 @@
namespace Zxing\Qrcode\Detector;
+use Zxing\Common\BitMatrix;
use Zxing\Common\Detector\MathUtils;
use Zxing\Common\DetectorResult;
use Zxing\Common\GridSampler;
@@ -38,9 +39,9 @@ class Detector
{
private $resultPointCallback;
- public function __construct(private $image)
- {
- }
+ public function __construct(private BitMatrix $image)
+ {
+ }
/**
* Detects a QR Code in an image.
@@ -51,11 +52,11 @@ public function __construct(private $image)
* @throws NotFoundException if QR Code cannot be found
* @throws FormatException if a QR Code cannot be decoded
*/
- final public function detect($hints = null)
+ final public function detect(array $hints = null): DetectorResult
{/*Map*/
- $resultPointCallback = $hints == null ? null :
- $hints->get('NEED_RESULT_POINT_CALLBACK');
+ $resultPointCallback = ($hints !== null && array_key_exists('NEED_RESULT_POINT_CALLBACK', $hints)) ?
+ $hints['NEED_RESULT_POINT_CALLBACK'] : null;
/* resultPointCallback = hints == null ? null :
(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
$finder = new FinderPatternFinder($this->image, $resultPointCallback);
@@ -64,7 +65,7 @@ final public function detect($hints = null)
return $this->processFinderPatternInfo($info);
}
- final protected function processFinderPatternInfo($info): \Zxing\Common\DetectorResult
+ final protected function processFinderPatternInfo(FinderPatternInfo $info): \Zxing\Common\DetectorResult
{
$topLeft = $info->getTopLeft();
$topRight = $info->getTopRight();
@@ -72,7 +73,7 @@ final protected function processFinderPatternInfo($info): \Zxing\Common\Detector
$moduleSize = (float)$this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
if ($moduleSize < 1.0) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Module size $moduleSize < 1.0");
}
$dimension = (int)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
$provisionalVersion = \Zxing\Qrcode\Decoder\Version::getProvisionalVersionForDimension($dimension);
@@ -82,18 +83,18 @@ final protected function processFinderPatternInfo($info): \Zxing\Common\Detector
// Anything above version 1 has an alignment pattern
if ((is_countable($provisionalVersion->getAlignmentPatternCenters()) ? count($provisionalVersion->getAlignmentPatternCenters()) : 0) > 0) {
-// Guess where a "bottom right" finder pattern would have been
+ // Guess where a "bottom right" finder pattern would have been
$bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
$bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
// Estimate that alignment pattern is closer by 3 modules
// from "bottom right" to known top left location
$correctionToTopLeft = 1.0 - 3.0 / (float)$modulesBetweenFPCenters;
- $estAlignmentX = (int)($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
- $estAlignmentY = (int)($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
+ $estAlignmentX = (int)round($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
+ $estAlignmentY = (int)round($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
// Kind of arbitrary -- expand search radius before giving up
- for ($i = 4; $i <= 16; $i <<= 1) {//??????????
+ for ($i = 4; $i <= 16; $i = $i << 1) { //??????????
try {
$alignmentPattern = $this->findAlignmentInRegion(
$moduleSize,
@@ -102,8 +103,9 @@ final protected function processFinderPatternInfo($info): \Zxing\Common\Detector
(float)$i
);
break;
- } catch (NotFoundException) {
+ } catch (NotFoundException $e) {
// try next round
+ $alignmentPattern = null;
}
}
// If we didn't find alignment pattern... well try anyway without it
@@ -136,17 +138,17 @@ final protected function processFinderPatternInfo($info): \Zxing\Common\Detector
* Computes an average estimated module size based on estimated derived from the positions
* of the three finder patterns.
*
- * @param detected $topLeft top-left finder pattern center
- * @param detected $topRight top-right finder pattern center
- * @param detected $bottomLeft bottom-left finder pattern center
+ * @param FinderPattern $topLeft detected top-left finder pattern center
+ * @param FinderPattern $topRight detected top-right finder pattern center
+ * @param FinderPattern $bottomLeft detected bottom-left finder pattern center
*
- * @return estimated module size
+ * @return float estimated module size
*/
- final protected function calculateModuleSize($topLeft, $topRight, $bottomLeft)
+ final protected function calculateModuleSize($topLeft, $topRight, $bottomLeft): float
{
// Take the average
return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
- $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
+ $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
}
/**
@@ -154,10 +156,10 @@ final protected function calculateModuleSize($topLeft, $topRight, $bottomLeft)
* {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
* width of each, measuring along the axis between their centers.
*/
- private function calculateModuleSizeOneWay($pattern, $otherPattern)
+ private function calculateModuleSizeOneWay(FinderPattern $pattern, FinderPattern $otherPattern): float
{
$moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays(
- $pattern->getX(),
+ (int)$pattern->getX(),
(int)$pattern->getY(),
(int)$otherPattern->getX(),
(int)$otherPattern->getY()
@@ -184,7 +186,7 @@ private function calculateModuleSizeOneWay($pattern, $otherPattern)
* a finder pattern by looking for a black-white-black run from the center in the direction
* of another po$(another finder pattern center), and in the opposite direction too.
*/
- private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY)
+ private function sizeOfBlackWhiteBlackRunBothWays(int $fromX, int $fromY, int $toX, int $toY): float
{
$result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
@@ -224,7 +226,7 @@ private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY)
* This is used when figuring out how wide a finder pattern is, when the finder pattern
* may be skewed or rotated.
*/
- private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY)
+ private function sizeOfBlackWhiteBlackRun(int $fromX, int $fromY, int $toX, int|float $toY): float
{
// Mild variant of Bresenham's algorithm;
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
@@ -290,59 +292,57 @@ private static function computeDimension(
$topLeft,
$topRight,
$bottomLeft,
- $moduleSize
- )
- {
+ float $moduleSize
+ ): int {
$tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
$tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
- $dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7;
+ $dimension = (int)round((($tltrCentersDimension + $tlblCentersDimension) / 2) + 7);
switch ($dimension & 0x03) { // mod 4
case 0:
$dimension++;
break;
-// 1? do nothing
+ // 1? do nothing
case 2:
$dimension--;
break;
case 3:
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Dimension ($dimension) mod 4 == 3 unusable");
}
- return $dimension;
+ return (int)round($dimension);
}
/**
* Attempts to locate an alignment pattern in a limited region of the image, which is
* guessed to contain it. This method uses {@link AlignmentPattern}.
*
- * @param estimated $overallEstModuleSize module size so far
- * @param x $estAlignmentX coordinate of center of area probably containing alignment pattern
- * @param y $estAlignmentY coordinate of above
- * @param number $allowanceFactor of pixels in all directions to search from the center
+ * @param float $overallEstModuleSize module size so far
+ * @param int $estAlignmentX coordinate of center of area probably containing alignment pattern
+ * @param int $estAlignmentY coordinate of above
+ * @param float $allowanceFactor of pixels in all directions to search from the center
*
* @return {@link AlignmentPattern} if found, or null otherwise
* @throws NotFoundException if an unexpected error occurs during detection
*/
final protected function findAlignmentInRegion(
- $overallEstModuleSize,
- $estAlignmentX,
- $estAlignmentY,
- $allowanceFactor
- )
- {
+ float $overallEstModuleSize,
+ int $estAlignmentX,
+ int $estAlignmentY,
+ float $allowanceFactor
+ ) {
// Look for an alignment pattern (3 modules in size) around where it
// should be
$allowance = (int)($allowanceFactor * $overallEstModuleSize);
$alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
$alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Alignment area right smaller than overall module size: $alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3. Allowance: $allowance, estimage of x: $estAlignmentX");
}
$alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
$alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Alignment area bottom smaller than overall module size: $alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3. Allowance: $allowance, estimage of y: $estAlignmentY");
}
$alignmentFinder =
@@ -364,9 +364,8 @@ private static function createTransform(
$topRight,
$bottomLeft,
$alignmentPattern,
- $dimension
- )
- {
+ int $dimension
+ ): PerspectiveTransform {
$dimMinusThree = (float)$dimension - 3.5;
$bottomRightX = 0.0;
$bottomRightY = 0.0;
@@ -407,10 +406,9 @@ private static function createTransform(
private static function sampleGrid(
$image,
- $transform,
- $dimension
- )
- {
+ PerspectiveTransform $transform,
+ int $dimension
+ ): \Zxing\Common\BitMatrix {
$sampler = GridSampler::getInstance();
return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
diff --git a/lib/Qrcode/Detector/FinderPattern.php b/lib/Qrcode/Detector/FinderPattern.php
index fe1f26e..7665302 100644
--- a/lib/Qrcode/Detector/FinderPattern.php
+++ b/lib/Qrcode/Detector/FinderPattern.php
@@ -53,7 +53,7 @@ public function getCount()
* Determines if this finder pattern "about equals" a finder pattern at the stated
* position and size -- meaning, it is at nearly the same center with nearly the same size.
*/
- public function aboutEquals($moduleSize, $i, $j)
+ public function aboutEquals($moduleSize, $i, $j): bool
{
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
diff --git a/lib/Qrcode/Detector/FinderPatternFinder.php b/lib/Qrcode/Detector/FinderPatternFinder.php
index e105827..c0fd99c 100644
--- a/lib/Qrcode/Detector/FinderPatternFinder.php
+++ b/lib/Qrcode/Detector/FinderPatternFinder.php
@@ -38,9 +38,9 @@ class FinderPatternFinder
private array $possibleCenters = []; //private final List possibleCenters;
private bool $hasSkipped = false;
/**
- * @var mixed|int[]
- */
- private $crossCheckStateCount;
+ * @var mixed|int[]
+ */
+ private $crossCheckStateCount;
/**
* Creates a finder that will search the image for three finder patterns.
@@ -53,10 +53,11 @@ public function __construct(private $image, private $resultPointCallback = null)
$this->crossCheckStateCount = fill_array(0, 5, 0);
}
- final public function find($hints): \Zxing\Qrcode\Detector\FinderPatternInfo
+ final public function find(array|null $hints): \Zxing\Qrcode\Detector\FinderPatternInfo
{/*final FinderPatternInfo find(Map hints) throws NotFoundException {*/
- $tryHarder = $hints != null && $hints['TRY_HARDER'];
- $pureBarcode = $hints != null && $hints['PURE_BARCODE'];
+ $tryHarder = $hints != null && array_key_exists('TRY_HARDER', $hints) && $hints['TRY_HARDER'];
+ $pureBarcode = $hints != null && array_key_exists('PURE_BARCODE', $hints) && $hints['PURE_BARCODE'];
+ $nrOfRowsSkippable = $hints != null && array_key_exists('NR_ALLOW_SKIP_ROWS', $hints) ? $hints['NR_ALLOW_SKIP_ROWS'] : ($tryHarder ? 0 : null);
$maxI = $this->image->getHeight();
$maxJ = $this->image->getWidth();
// We are looking for black/white/black/white/black modules in
@@ -100,7 +101,7 @@ final public function find($hints): \Zxing\Qrcode\Detector\FinderPatternInfo
if ($this->hasSkipped) {
$done = $this->haveMultiplyConfirmedCenters();
} else {
- $rowSkip = $this->findRowSkip();
+ $rowSkip = $nrOfRowsSkippable === null ? $this->findRowSkip() : $nrOfRowsSkippable;
if ($rowSkip > $stateCount[2]) {
// Skip rows between row of lower confirmed center
// and top of presumed third confirmed center
@@ -166,11 +167,13 @@ final public function find($hints): \Zxing\Qrcode\Detector\FinderPatternInfo
/**
* @param $stateCount ; count of black/white/black/white/black pixels just read
+ * @param int[] $stateCount
+ *
+ * @return bool iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios used by finder patterns to be considered a match
*
- * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
- * used by finder patterns to be considered a match
+ * @psalm-param array<0|positive-int, int> $stateCount
*/
- protected static function foundPatternCross($stateCount)
+ protected static function foundPatternCross(array $stateCount): bool
{
$totalModuleSize = 0;
for ($i = 0; $i < 5; $i++) {
@@ -207,14 +210,14 @@ protected static function foundPatternCross($stateCount)
* Each additional find is more evidence that the location is in fact a finder
* pattern center
*
- * @param reading $stateCount state module counts from horizontal scan
- * @param row $i where finder pattern may be found
- * @param end $j of possible finder pattern in row
- * @param true $pureBarcode if in "pure barcode" mode
+ * @param array $stateCount reading state module counts from horizontal scan
+ * @param int $i row where finder pattern may be found
+ * @param int $j end of possible finder pattern in row
+ * @param bool $pureBarcode true if in "pure barcode" mode
*
- * @return true if a finder pattern candidate was found this time
+ * @return bool if a finder pattern candidate was found this time
*/
- final protected function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
+ final protected function handlePossibleCenter($stateCount, int $i, int $j, bool $pureBarcode): bool
{
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
$stateCount[4];
@@ -223,7 +226,8 @@ final protected function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
if (!is_nan($centerI)) {
// Re-cross check
$centerJ = $this->crossCheckHorizontal((int)($centerJ), (int)($centerI), $stateCount[2], $stateCountTotal);
- if (!is_nan($centerJ) &&
+ if (
+ !is_nan($centerJ) &&
(!$pureBarcode || $this->crossCheckDiagonal((int)($centerI), (int)($centerJ), $stateCount[2], $stateCountTotal))
) {
$estimatedModuleSize = (float)$stateCountTotal / 7.0;
@@ -256,7 +260,7 @@ final protected function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
* Given a count of black/white/black/white/black pixels just seen and an end position,
* figures the location of the center of this run.
*/
- private static function centerFromEnd($stateCount, $end)
+ private static function centerFromEnd(array $stateCount, int $end)
{
return (float)($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0;
}
@@ -266,20 +270,19 @@ private static function centerFromEnd($stateCount, $end)
* "cross-checks" by scanning down vertically through the center of the possible
* finder pattern to see if the same proportion is detected.
*
- * @param $startI ; row where a finder pattern was detected
- * @param $centerJ ; center of the section that appears to cross a finder pattern
- * @param $maxCount ; maximum reasonable number of modules that should be
+ * @param int $startI ; row where a finder pattern was detected
+ * @param int $centerJ ; center of the section that appears to cross a finder pattern
+ * @param int $maxCount ; maximum reasonable number of modules that should be
* observed in any reading state, based on the results of the horizontal scan
*
* @return float vertical center of finder pattern, or {@link Float#NaN} if not found
*/
private function crossCheckVertical(
- $startI,
- $centerJ,
- $maxCount,
- $originalStateCountTotal
- )
- {
+ int $startI,
+ int $centerJ,
+ int $maxCount,
+ int|float $originalStateCountTotal
+ ) {
$image = $this->image;
$maxI = $image->getHeight();
@@ -362,12 +365,11 @@ private function getCrossCheckStateCount()
* check a vertical cross check and locate the real center of the alignment pattern.
*/
private function crossCheckHorizontal(
- $startJ,
- $centerI,
- $maxCount,
- $originalStateCountTotal
- )
- {
+ int $startJ,
+ int $centerI,
+ int $maxCount,
+ int|float $originalStateCountTotal
+ ) {
$image = $this->image;
$maxJ = $this->image->getWidth();
@@ -441,16 +443,14 @@ private function crossCheckHorizontal(
* observed in any reading state, based on the results of the horizontal scan
* @param $originalStateCountTotal ; The original state count total.
*
- * @return true if proportions are withing expected limits
+ * @return bool if proportions are withing expected limits
*/
- private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal)
+ private function crossCheckDiagonal(int $startI, int $centerJ, $maxCount, int|float $originalStateCountTotal): bool
{
$stateCount = $this->getCrossCheckStateCount();
// Start counting up, left from center finding black center mass
$i = 0;
- $startI = (int)($startI);
- $centerJ = (int)($centerJ);
while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) {
$stateCount[2]++;
$i++;
@@ -461,8 +461,10 @@ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalState
}
// Continue up, left finding white space
- while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) &&
- $stateCount[1] <= $maxCount) {
+ while (
+ $startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) &&
+ $stateCount[1] <= $maxCount
+ ) {
$stateCount[1]++;
$i++;
}
@@ -473,8 +475,10 @@ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalState
}
// Continue up, left finding black border
- while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) &&
- $stateCount[0] <= $maxCount) {
+ while (
+ $startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) &&
+ $stateCount[0] <= $maxCount
+ ) {
$stateCount[0]++;
$i++;
}
@@ -497,8 +501,10 @@ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalState
return false;
}
- while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) &&
- $stateCount[3] < $maxCount) {
+ while (
+ $startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) &&
+ $stateCount[3] < $maxCount
+ ) {
$stateCount[3]++;
$i++;
}
@@ -507,8 +513,10 @@ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalState
return false;
}
- while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) &&
- $stateCount[4] < $maxCount) {
+ while (
+ $startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) &&
+ $stateCount[4] < $maxCount
+ ) {
$stateCount[4]++;
$i++;
}
@@ -527,11 +535,9 @@ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalState
}
/**
- * @return true iff we have found at least 3 finder patterns that have been detected
- * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
- * candidates is "pretty similar"
+ * @return bool iff we have found at least 3 finder patterns that have been detected at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the candidates is "pretty similar"
*/
- private function haveMultiplyConfirmedCenters()
+ private function haveMultiplyConfirmedCenters(): bool
{
$confirmedCount = 0;
$totalModuleSize = 0.0;
@@ -584,7 +590,7 @@ private function findRowSkip()
$this->hasSkipped = true;
return (int)((abs($firstConfirmedCenter->getX() - $center->getX()) -
- abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
+ abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
}
}
}
@@ -626,7 +632,7 @@ private function selectBestPatterns()
for ($i = 0; $i < count($this->possibleCenters) && count($this->possibleCenters) > 3; $i++) {
$pattern = $this->possibleCenters[$i];
if (abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) {
- unset($this->possibleCenters[$i]);//возможно что ключи меняются в java при вызове .remove(i) ???
+ unset($this->possibleCenters[$i]); //возможно что ключи меняются в java при вызове .remove(i) ???
$this->possibleCenters = array_values($this->possibleCenters);
$i--;
}
@@ -653,8 +659,10 @@ private function selectBestPatterns()
/**
* Orders by furthest from average
+ *
+ * @psalm-return -1|0|1
*/
- public function FurthestFromAverageComparator($center1, $center2)
+ public function FurthestFromAverageComparator($center1, $center2): int
{
$dA = abs($center2->getEstimatedModuleSize() - $this->average);
$dB = abs($center1->getEstimatedModuleSize() - $this->average);
@@ -684,16 +692,14 @@ public function CenterComparator($center1, $center2)
}
}
- final protected function getImage()
+ final protected function getImage(): BitMatrix
{
return $this->image;
}
/**
* Orders by {@link FinderPattern#getCount()}, descending.
*/
-
- //@Override
- final protected function getPossibleCenters()
+ final protected function getPossibleCenters(): array
{ //List getPossibleCenters()
return $this->possibleCenters;
}
diff --git a/lib/Qrcode/QRCodeReader.php b/lib/Qrcode/QRCodeReader.php
index dc5bf77..6d65741 100644
--- a/lib/Qrcode/QRCodeReader.php
+++ b/lib/Qrcode/QRCodeReader.php
@@ -35,7 +35,7 @@
class QRCodeReader implements Reader
{
private static array $NO_POINTS = [];
- private readonly \Zxing\Qrcode\Decoder\Decoder $decoder;
+ private readonly Decoder $decoder;
public function __construct()
{
@@ -49,10 +49,10 @@ public function __construct()
* @throws \Zxing\FormatException
* @throws \Zxing\NotFoundException
*/
- public function decode(BinaryBitmap $image, $hints = null)
+ public function decode(BinaryBitmap $image, $hints = null)
{
$decoderResult = null;
- if ($hints !== null && $hints['PURE_BARCODE']) {
+ if ($hints !== null && array_key_exists('PURE_BARCODE', $hints) && $hints['PURE_BARCODE']) {
$bits = self::extractPureBits($image->getBlackMatrix());
$decoderResult = $this->decoder->decode($bits, $hints);
$points = self::$NO_POINTS;
@@ -89,27 +89,24 @@ public function decode(BinaryBitmap $image, $hints = null)
/**
* Locates and decodes a QR code in an image.
- *
- * @return a String representing the content encoded by the QR code
- * @throws NotFoundException if a QR code cannot be found
- * @throws FormatException if a QR code cannot be decoded
- * @throws ChecksumException if error correction fails
- */
-
- /**
* This method detects a code in a "pure" image -- that is, pure monochrome image
* which contains only an unrotated, unskewed, image of a code, with some white border
* around it. This is a specialized method that works exceptionally fast in this special
* case.
*
+ * @return BitMatrix a String representing the content encoded by the QR code
+ * @throws NotFoundException if a QR code cannot be found
+ * @throws FormatException if a QR code cannot be decoded
+ * @throws ChecksumException if error correction fails
+ *
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
*/
- private static function extractPureBits(BitMatrix $image)
+ private static function extractPureBits(BitMatrix $image): BitMatrix
{
$leftTopBlack = $image->getTopLeftOnBit();
$rightBottomBlack = $image->getBottomRightOnBit();
if ($leftTopBlack === null || $rightBottomBlack == null) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Top left or bottom right on bit not found");
}
$moduleSize = self::moduleSize($leftTopBlack, $image);
@@ -121,7 +118,7 @@ private static function extractPureBits(BitMatrix $image)
// Sanity check!
if ($left >= $right || $top >= $bottom) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Left vs. right ($left >= $right) or top vs. bottom ($top >= $bottom) sanity violated.");
}
if ($bottom - $top != $right - $left) {
@@ -133,11 +130,11 @@ private static function extractPureBits(BitMatrix $image)
$matrixWidth = round(($right - $left + 1) / $moduleSize);
$matrixHeight = round(($bottom - $top + 1) / $moduleSize);
if ($matrixWidth <= 0 || $matrixHeight <= 0) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Matrix dimensions <= 0 ($matrixWidth, $matrixHeight)");
}
if ($matrixHeight != $matrixWidth) {
// Only possibly decode square regions
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Matrix height $matrixHeight != matrix width $matrixWidth");
}
// Push in the "border" by half the module width so that we start
@@ -154,7 +151,7 @@ private static function extractPureBits(BitMatrix $image)
if ($nudgedTooFarRight > 0) {
if ($nudgedTooFarRight > $nudge) {
// Neither way fits; abort
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Nudge too far right ($nudgedTooFarRight > $nudge), no fit found");
}
$left -= $nudgedTooFarRight;
}
@@ -163,7 +160,7 @@ private static function extractPureBits(BitMatrix $image)
if ($nudgedTooFarDown > 0) {
if ($nudgedTooFarDown > $nudge) {
// Neither way fits; abort
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("Nudge too far down ($nudgedTooFarDown > $nudge), no fit found");
}
$top -= $nudgedTooFarDown;
}
@@ -171,9 +168,9 @@ private static function extractPureBits(BitMatrix $image)
// Now just read off the bits
$bits = new BitMatrix($matrixWidth, $matrixHeight);
for ($y = 0; $y < $matrixHeight; $y++) {
- $iOffset = $top + (int)($y * $moduleSize);
+ $iOffset = (int)($top + (int)($y * $moduleSize));
for ($x = 0; $x < $matrixWidth; $x++) {
- if ($image->get($left + (int)($x * $moduleSize), $iOffset)) {
+ if ($image->get((int)($left + (int)($x * $moduleSize)), $iOffset)) {
$bits->set($x, $y);
}
}
@@ -182,7 +179,10 @@ private static function extractPureBits(BitMatrix $image)
return $bits;
}
- private static function moduleSize($leftTopBlack, BitMatrix $image)
+ /**
+ * @psalm-param array{0: mixed, 1: mixed} $leftTopBlack
+ */
+ private static function moduleSize(array $leftTopBlack, BitMatrix $image)
{
$height = $image->getHeight();
$width = $image->getWidth();
@@ -193,7 +193,7 @@ private static function moduleSize($leftTopBlack, BitMatrix $image)
$inBlack = true;
$transitions = 0;
while ($x < $width && $y < $height) {
- if ($inBlack != $image->get($x, $y)) {
+ if ($inBlack != $image->get((int)round($x), (int)round($y))) {
if (++$transitions == 5) {
break;
}
@@ -203,7 +203,7 @@ private static function moduleSize($leftTopBlack, BitMatrix $image)
$y++;
}
if ($x == $width || $y == $height) {
- throw NotFoundException::getNotFoundInstance();
+ throw new NotFoundException("$x == $width || $y == $height");
}
return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f;
@@ -214,7 +214,7 @@ public function reset(): void
// do nothing
}
- final protected function getDecoder()
+ final protected function getDecoder(): Decoder
{
return $this->decoder;
}
diff --git a/lib/RGBLuminanceSource.php b/lib/RGBLuminanceSource.php
index d9399e0..d0d4647 100644
--- a/lib/RGBLuminanceSource.php
+++ b/lib/RGBLuminanceSource.php
@@ -32,15 +32,15 @@ final class RGBLuminanceSource extends LuminanceSource
/**
* @var mixed|int
*/
- private $left;
+ private $left;
/**
* @var mixed|int
*/
- private $top;
+ private $top;
/**
* @var mixed|null
*/
- private $pixels;
+ private $pixels;
public function __construct(
@@ -68,6 +68,16 @@ public function __construct(
$this->top = $top;
}
+ public function rotateCounterClockwise(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise");
+ }
+
+ public function rotateCounterClockwise45(): void
+ {
+ throw new \RuntimeException("This LuminanceSource does not support rotateCounterClockwise45");
+ }
+
public function RGBLuminanceSource_($width, $height, $pixels): void
{
parent::__construct($width, $height);
@@ -141,7 +151,12 @@ public function RGBLuminanceSource_($width, $height, $pixels): void
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
}
- public function grayscale()
+ /**
+ * @return (int|mixed)[]
+ *
+ * @psalm-return array
+ */
+ public function grayscale(): array
{
$width = $this->dataWidth;
$height = $this->dataHeight;
@@ -158,7 +173,7 @@ public function grayscale()
return $ret;
}
- public function getPixel($x, $y, $width, $height)
+ public function getPixel(int $x, int $y, $width, $height): int
{
$image = $this->pixels;
if ($width < $x) {
@@ -179,7 +194,12 @@ public function getPixel($x, $y, $width, $height)
return $p;
}
- public function grayScaleToBitmap($grayScale)
+ /**
+ * @return (int|mixed)[]
+ *
+ * @psalm-return array
+ */
+ public function grayScaleToBitmap($grayScale): array
{
$middle = $this->getMiddleBrightnessPerArea($grayScale);
$sqrtNumArea = is_countable($middle) ? count($middle) : 0;
@@ -200,7 +220,10 @@ public function grayScaleToBitmap($grayScale)
return $bitmap;
}
- public function getMiddleBrightnessPerArea($image)
+ /**
+ * @return float[]&mixed[][]
+ */
+ public function getMiddleBrightnessPerArea($image): array
{
$numSqrtArea = 4;
//obtain middle brightness((min + max) / 2) per area
@@ -247,11 +270,11 @@ public function getMiddleBrightnessPerArea($image)
return $middle;
}
- //@Override
+
public function getRow($y, $row = null)
{
if ($y < 0 || $y >= $this->getHeight()) {
- throw new \InvalidArgumentException("Requested row is outside the image: " + \Y);
+ throw new \InvalidArgumentException("Requested row is outside the image: " + $y);
}
$width = $this->getWidth();
if ($row == null || (is_countable($row) ? count($row) : 0) < $width) {
@@ -263,7 +286,7 @@ public function getRow($y, $row = null)
return $row;
}
- //@Override
+
public function getMatrix()
{
$width = $this->getWidth();
@@ -297,13 +320,13 @@ public function getMatrix()
return $matrix;
}
- //@Override
- public function isCropSupported()
+
+ public function isCropSupported(): bool
{
return true;
}
- //@Override
+
public function crop($left, $top, $width, $height): \Zxing\RGBLuminanceSource
{
return new RGBLuminanceSource(
diff --git a/lib/ReaderException.php b/lib/ReaderException.php
index d82bf57..ec079f4 100644
--- a/lib/ReaderException.php
+++ b/lib/ReaderException.php
@@ -26,7 +26,7 @@
*/
abstract class ReaderException extends \Exception
{
-// disable stack traces when not running inside test units
+ // disable stack traces when not running inside test units
//protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null;
protected static bool $isStackTrace = false;
@@ -41,7 +41,7 @@ public function ReaderException($cause = null): void
// Prevent stack traces from being taken
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
- //@Override
+
final public function fillInStackTrace()
{
return null;
diff --git a/lib/Result.php b/lib/Result.php
index a8bf0ab..c0c391d 100644
--- a/lib/Result.php
+++ b/lib/Result.php
@@ -27,7 +27,7 @@ final class Result
/**
* @var mixed[]|mixed
*/
- private $resultMetadata = null;
+ private $resultMetadata = null;
private $timestamp;
public function __construct(
@@ -41,7 +41,7 @@ public function __construct(
}
/**
- * @return raw text encoded by the barcode
+ * @return string raw text encoded by the barcode
*/
public function getText()
{
@@ -49,7 +49,7 @@ public function getText()
}
/**
- * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
+ * @return array|string raw bytes encoded by the barcode, if applicable, otherwise {@code null}
*/
public function getRawBytes()
{
@@ -57,7 +57,7 @@ public function getRawBytes()
}
/**
- * @return points related to the barcode in the image. These are typically points
+ * @return array points related to the barcode in the image. These are typically points
* identifying finder patterns or the corners of the barcode. The exact meaning is
* specific to the type of barcode that was decoded.
*/
@@ -84,10 +84,10 @@ public function getResultMetadata()
return $this->resultMetadata;
}
- public function putMetadata($type, $value): void
+ public function putMetadata(string $type, $value): void
{
$resultMetadata = [];
- if ($this->resultMetadata === null) {
+ if ($this->resultMetadata === null) {
$this->resultMetadata = [];
}
$resultMetadata[$type] = $value;
diff --git a/lib/ResultPoint.php b/lib/ResultPoint.php
index d53229c..986f9dc 100644
--- a/lib/ResultPoint.php
+++ b/lib/ResultPoint.php
@@ -42,7 +42,7 @@ public function __construct($x, $y)
*
* @param array $patterns of three {@code ResultPoint} to order
*/
- public static function orderBestPatterns($patterns)
+ public static function orderBestPatterns(array $patterns): array
{
// Find distances between pattern centers
@@ -86,17 +86,17 @@ public static function orderBestPatterns($patterns)
}
/**
- * @param first $pattern1 pattern
- * @param second $pattern2 pattern
+ * @param ResultPoint $pattern1 first pattern
+ * @param ResultPoint $pattern2 second pattern
*
- * @return distance between two points
+ * @return float distance between two points
*/
public static function distance($pattern1, $pattern2)
{
return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y);
}
- //@Override
+
/**
* Returns the z component of the cross product between vectors BC and BA.
@@ -105,29 +105,28 @@ private static function crossProductZ(
$pointA,
$pointB,
$pointC
- )
- {
+ ) {
$bX = $pointB->x;
$bY = $pointB->y;
return (($pointC->x - $bX) * ($pointA->y - $bY)) - (($pointC->y - $bY) * ($pointA->x - $bX));
}
- //@Override
+
- final public function getX()
+ final public function getX(): float
{
return (float)($this->x);
}
- //@Override
+
- final public function getY()
+ final public function getY(): float
{
return (float)($this->y);
}
- final public function equals($other)
+ final public function equals($other): bool
{
if ($other instanceof ResultPoint) {
$otherPoint = $other;
@@ -143,7 +142,7 @@ final public function hashCode()
return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
}
- final public function toString()
+ final public function toString(): string
{
$result = '';
$result .= ('(');
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 0000000..52a75ee
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/rector.php b/rector.php
index 8568715..6d20eb5 100644
--- a/rector.php
+++ b/rector.php
@@ -10,15 +10,23 @@
use Rector\Doctrine\Set\DoctrineSetList;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Symfony\Set\SensiolabsSetList;
+use Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector;
+use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector;
use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector;
+use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector;
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromReturnNewRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationRector;
use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictNewArrayRector;
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ArrayShapeFromConstantArrayReturnRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictBoolReturnExprRector;
+use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictNativeFuncCallRector;
return static function (RectorConfig $rectorConfig): void {
@@ -45,16 +53,21 @@
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
$rectorConfig->rule(AddReturnTypeDeclarationRector::class);
$rectorConfig->rules([
+ AddArrayParamDocTypeRector::class,
+ AddMethodCallBasedStrictParamTypeRector::class,
AddVoidReturnTypeWhereNoReturnRector::class,
ArrayShapeFromConstantArrayReturnRector::class,
ParamTypeByMethodCallTypeRector::class,
ParamTypeByParentCallTypeRector::class,
+ ParamTypeDeclarationRector::class,
PropertyTypeDeclarationRector::class,
+ RemoveUselessParamTagRector::class,
ReturnTypeFromReturnNewRector::class,
- // ReturnTypeFromStrictBoolReturnExprRector::class,
- // ReturnTypeFromStrictNativeFuncCallRector::class,
- // ReturnTypeFromStrictNewArrayRector::class,
- TypedPropertyFromAssignsRector::class
+ ReturnTypeFromStrictBoolReturnExprRector::class,
+ ReturnTypeFromStrictNativeFuncCallRector::class,
+ ReturnTypeFromStrictNewArrayRector::class,
+ TypedPropertyFromAssignsRector::class,
+ VarConstantCommentRector::class
]);
// define sets of rules
diff --git a/tests/QrReaderTest.php b/tests/QrReaderTest.php
index 831ecf1..c893ed7 100644
--- a/tests/QrReaderTest.php
+++ b/tests/QrReaderTest.php
@@ -4,9 +4,16 @@
use PHPUnit\Framework\TestCase;
use Zxing\QrReader;
+use Zxing\Result;
class QrReaderTest extends TestCase
{
+ public function setUp(): void
+ {
+ error_reporting(E_ALL);
+ ini_set('memory_limit','2G');
+ }
+
public function testText1()
{
$image = __DIR__ . "/qrcodes/hello_world.png";
@@ -21,4 +28,30 @@ public function testNoText()
$qrcode = new QrReader($image);
$this->assertSame(false, $qrcode->text());
}
+
+ public function testText2()
+ {
+ $image = __DIR__ . "/qrcodes/139225861-398ccbbd-2bfd-4736-889b-878c10573888.png";
+ $qrcode = new QrReader($image);
+ $hints = [
+ 'TRY_HARDER' => true,
+ 'NR_ALLOW_SKIP_ROWS' => 0
+ ];
+ $qrcode->decode($hints);
+ $this->assertSame(null, $qrcode->getError());
+ $this->assertInstanceOf(Result::class, $qrcode->getResult());
+ $this->assertEquals("https://www.gosuslugi.ru/covid-cert/verify/9770000014233333?lang=ru&ck=733a9d218d312fe134f1c2cc06e1a800", $qrcode->getResult()->getText());
+ $this->assertSame("https://www.gosuslugi.ru/covid-cert/verify/9770000014233333?lang=ru&ck=733a9d218d312fe134f1c2cc06e1a800", $qrcode->text($hints));
+ }
+
+ public function testText3()
+ {
+ $image = __DIR__ . "/qrcodes/test.png";
+ $qrcode = new QrReader($image);
+ $qrcode->decode([
+ 'TRY_HARDER' => true
+ ]);
+ $this->assertSame(null, $qrcode->getError());
+ $this->assertSame("https://www.gosuslugi.ru/covid-cert/verify/9770000014233333?lang=ru&ck=733a9d218d312fe134f1c2cc06e1a800", $qrcode->text());
+ }
}
diff --git a/tests/qrcodes/139225861-398ccbbd-2bfd-4736-889b-878c10573888.png b/tests/qrcodes/139225861-398ccbbd-2bfd-4736-889b-878c10573888.png
new file mode 100644
index 0000000..ea9ddef
Binary files /dev/null and b/tests/qrcodes/139225861-398ccbbd-2bfd-4736-889b-878c10573888.png differ
diff --git a/tests/qrcodes/test.png b/tests/qrcodes/test.png
new file mode 100644
index 0000000..84ff2db
Binary files /dev/null and b/tests/qrcodes/test.png differ