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