diff --git a/src/Common/MaskPattern.php b/src/Common/MaskPattern.php index e88a248ad..9dca09778 100644 --- a/src/Common/MaskPattern.php +++ b/src/Common/MaskPattern.php @@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Common; use chillerlan\QRCode\Data\QRMatrix; use chillerlan\QRCode\QRCodeException; use Closure; -use function abs, array_search, count, min; +use function abs, array_search, count, intdiv, min; /** * ISO/IEC 18004:2000 Section 8.8.1 @@ -102,10 +102,10 @@ final class MaskPattern{ self::PATTERN_001 => fn(int $x, int $y):bool => ($y % 2) === 0, self::PATTERN_010 => fn(int $x, int $y):bool => ($x % 3) === 0, self::PATTERN_011 => fn(int $x, int $y):bool => (($x + $y) % 3) === 0, - self::PATTERN_100 => fn(int $x, int $y):bool => (((int)($y / 2) + (int)($x / 3)) % 2) === 0, - self::PATTERN_101 => fn(int $x, int $y):bool => (($x * $y) % 6) === 0, // ((($x * $y) % 2) + (($x * $y) % 3)) === 0, - self::PATTERN_110 => fn(int $x, int $y):bool => (($x * $y) % 6) < 3, // (((($x * $y) % 2) + (($x * $y) % 3)) % 2) === 0, - self::PATTERN_111 => fn(int $x, int $y):bool => (($x + $y + (($x * $y) % 3)) % 2) === 0, // (((($x * $y) % 3) + (($x + $y) % 2)) % 2) === 0, + self::PATTERN_100 => fn(int $x, int $y):bool => ((intdiv($y, 2) + intdiv($x, 3)) % 2) === 0, + self::PATTERN_101 => fn(int $x, int $y):bool => (($x * $y) % 6) === 0, + self::PATTERN_110 => fn(int $x, int $y):bool => (($x * $y) % 6) < 3, + self::PATTERN_111 => fn(int $x, int $y):bool => (($x + $y + (($x * $y) % 3)) % 2) === 0, ][$this->maskPattern]; } @@ -310,7 +310,7 @@ final class MaskPattern{ } } - return ((int)(abs($darkCells * 2 - $totalCells) * 10 / $totalCells) * 10); + return (intdiv((abs($darkCells * 2 - $totalCells) * 10), $totalCells) * 10); } } diff --git a/src/Data/AlphaNum.php b/src/Data/AlphaNum.php index f074ee010..81d0ff3f3 100644 --- a/src/Data/AlphaNum.php +++ b/src/Data/AlphaNum.php @@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data; use chillerlan\QRCode\Common\{BitBuffer, Mode}; -use function array_flip, ceil, str_split; +use function array_flip, ceil, intdiv, str_split; /** * Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / : @@ -118,7 +118,7 @@ final class AlphaNum extends QRDataModeAbstract{ } $nextTwoCharsBits = $bitBuffer->read(11); - $result .= $toAlphaNumericChar((int)($nextTwoCharsBits / 45)); + $result .= $toAlphaNumericChar(intdiv($nextTwoCharsBits, 45)); $result .= $toAlphaNumericChar($nextTwoCharsBits % 45); $length -= 2; } diff --git a/src/Data/Hanzi.php b/src/Data/Hanzi.php index 564c11ec0..e11a81d9d 100644 --- a/src/Data/Hanzi.php +++ b/src/Data/Hanzi.php @@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data; use chillerlan\QRCode\Common\{BitBuffer, Mode}; use Throwable; -use function chr, implode, is_string, mb_convert_encoding, mb_detect_encoding, +use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding, mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen; /** @@ -188,7 +188,7 @@ final class Hanzi extends QRDataModeAbstract{ while($length > 0){ // Each 13 bits encodes a 2-byte character $twoBytes = $bitBuffer->read(13); - $assembledTwoBytes = ((((int)($twoBytes / 0x060)) << 8) | ($twoBytes % 0x060)); + $assembledTwoBytes = ((intdiv($twoBytes, 0x060) << 8) | ($twoBytes % 0x060)); $assembledTwoBytes += ($assembledTwoBytes < 0x00a00) // 0x003BF ? 0x0a1a1 // In the 0xA1A1 to 0xAAFE range diff --git a/src/Data/Kanji.php b/src/Data/Kanji.php index 84c33b253..d6b6de5f7 100644 --- a/src/Data/Kanji.php +++ b/src/Data/Kanji.php @@ -13,7 +13,7 @@ namespace chillerlan\QRCode\Data; use chillerlan\QRCode\Common\{BitBuffer, Mode}; use Throwable; -use function chr, implode, is_string, mb_convert_encoding, mb_detect_encoding, +use function chr, implode, intdiv, is_string, mb_convert_encoding, mb_detect_encoding, mb_detect_order, mb_internal_encoding, mb_strlen, ord, sprintf, strlen; /** @@ -174,7 +174,7 @@ final class Kanji extends QRDataModeAbstract{ while($length > 0){ // Each 13 bits encodes a 2-byte character $twoBytes = $bitBuffer->read(13); - $assembledTwoBytes = ((((int)($twoBytes / 0x0c0)) << 8) | ($twoBytes % 0x0c0)); + $assembledTwoBytes = ((intdiv($twoBytes, 0x0c0) << 8) | ($twoBytes % 0x0c0)); $assembledTwoBytes += ($assembledTwoBytes < 0x01f00) ? 0x08140 // In the 0x8140 to 0x9FFC range diff --git a/src/Data/Number.php b/src/Data/Number.php index 1f0cf8052..a2a7cee18 100644 --- a/src/Data/Number.php +++ b/src/Data/Number.php @@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Data; use chillerlan\QRCode\Common\{BitBuffer, Mode}; -use function array_flip, ceil, str_split, substr, unpack; +use function array_flip, ceil, intdiv, str_split, substr, unpack; /** * Numeric mode: decimal digits 0 to 9 @@ -140,8 +140,8 @@ final class Number extends QRDataModeAbstract{ throw new QRCodeDataException('error decoding numeric value'); } - $result .= $toNumericChar((int)($threeDigitsBits / 100)); - $result .= $toNumericChar((int)($threeDigitsBits / 10) % 10); + $result .= $toNumericChar(intdiv($threeDigitsBits, 100)); + $result .= $toNumericChar(intdiv($threeDigitsBits, 10) % 10); $result .= $toNumericChar($threeDigitsBits % 10); $length -= 3; @@ -159,7 +159,7 @@ final class Number extends QRDataModeAbstract{ throw new QRCodeDataException('error decoding numeric value'); } - $result .= $toNumericChar((int)($twoDigitsBits / 10)); + $result .= $toNumericChar(intdiv($twoDigitsBits, 10)); $result .= $toNumericChar($twoDigitsBits % 10); } elseif($length === 1){ diff --git a/src/Data/QRMatrix.php b/src/Data/QRMatrix.php index 0e3e13ef9..5e5aab474 100755 --- a/src/Data/QRMatrix.php +++ b/src/Data/QRMatrix.php @@ -11,7 +11,7 @@ namespace chillerlan\QRCode\Data; use chillerlan\QRCode\Common\{BitBuffer, EccLevel, MaskPattern, ReedSolomonEncoder, Version}; -use function array_fill, array_map, array_reverse, count, floor; +use function array_fill, array_map, array_reverse, count, floor, intdiv; /** * Holds an array representation of the final QR Code that contains numerical values for later output modifications; @@ -485,7 +485,7 @@ class QRMatrix{ if($bits !== null){ for($i = 0; $i < 18; $i++){ - $a = (int)($i / 3); + $a = intdiv($i, 3); $b = (($i % 3) + ($this->moduleCount - 8 - 3)); $v = (($bits >> $i) & 1) === 1; diff --git a/src/Decoder/Binarizer.php b/src/Decoder/Binarizer.php index 9e58167cf..fe90792a6 100644 --- a/src/Decoder/Binarizer.php +++ b/src/Decoder/Binarizer.php @@ -12,7 +12,7 @@ namespace chillerlan\QRCode\Decoder; use chillerlan\QRCode\Data\QRMatrix; -use function array_fill, count, max; +use function array_fill, count, intdiv, max; /** * This class implements a local thresholding algorithm, which while slower than the @@ -167,13 +167,14 @@ final class Binarizer{ // Quickly calculates the histogram by sampling four rows from the image. This proved to be // more robust on the blackbox tests than sampling a diagonal as we used to do. $buckets = array_fill(0, self::LUMINANCE_BUCKETS, 0); + $right = intdiv(($width * 4), 5); + $x = intdiv($width, 5); for($y = 1; $y < 5; $y++){ - $row = (int)($height * $y / 5); + $row = intdiv(($height * $y), 5); $localLuminances = $this->source->getRow($row); - $right = (int)(($width * 4) / 5); - for($x = (int)($width / 5); $x < $right; $x++){ + for(; $x < $right; $x++){ $pixel = ($localLuminances[$x] & 0xff); $buckets[($pixel >> self::LUMINANCE_SHIFT)]++; } @@ -262,7 +263,7 @@ final class Binarizer{ // // The default assumption is that the block is light/background. Since no estimate for // the level of dark pixels exists locally, use half the min for the block. - $average = (int)($min / 2); + $average = ($min / 2); if($y > 0 && $x > 0){ // Correct the "white background" assumption for blocks that have neighbors by comparing @@ -272,7 +273,7 @@ final class Binarizer{ // the boundaries is used for the interior. // The (min < bp) is arbitrary but works better than other heuristics that were tried. - $averageNeighborBlackPoint = (int)( + $averageNeighborBlackPoint = ( ($blackPoints[($y - 1)][$x] + (2 * $blackPoints[$y][($x - 1)]) + $blackPoints[($y - 1)][($x - 1)]) / 4 ); diff --git a/src/Detector/Detector.php b/src/Detector/Detector.php index 7f73bdc88..37c2e31b3 100644 --- a/src/Detector/Detector.php +++ b/src/Detector/Detector.php @@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Detector; use chillerlan\QRCode\Decoder\{Binarizer, LuminanceSourceInterface}; use chillerlan\QRCode\Common\Version; use chillerlan\QRCode\Decoder\BitMatrix; -use function abs, is_nan, max, min, round; +use function abs, intdiv, is_nan, max, min, round; use const NAN; /** @@ -42,7 +42,7 @@ final class Detector{ $moduleSize = $this->calculateModuleSize($topLeft, $topRight, $bottomLeft); $dimension = $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize); - $provisionalVersion = new Version((int)(($dimension - 17) / 4)); + $provisionalVersion = new Version(intdiv(($dimension - 17), 4)); $alignmentPattern = null; // Anything above version 1 has an alignment pattern diff --git a/src/Detector/FinderPatternFinder.php b/src/Detector/FinderPatternFinder.php index fadc0d13e..755d08c27 100644 --- a/src/Detector/FinderPatternFinder.php +++ b/src/Detector/FinderPatternFinder.php @@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Detector; use chillerlan\QRCode\Decoder\BitMatrix; -use function abs, count, usort; +use function abs, count, intdiv, usort; use const PHP_FLOAT_MAX; /** @@ -56,7 +56,7 @@ final class FinderPatternFinder{ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the // image, and then account for the center being 3 modules in size. This gives the smallest // number of pixels the center could be, so skip this often. - $iSkip = (int)((3 * $dimension) / (4 * self::MAX_MODULES)); + $iSkip = intdiv((3 * $dimension), (4 * self::MAX_MODULES)); if($iSkip < self::MIN_SKIP){ $iSkip = self::MIN_SKIP; diff --git a/src/Detector/GridSampler.php b/src/Detector/GridSampler.php index bf5b2b31c..1a79b0813 100644 --- a/src/Detector/GridSampler.php +++ b/src/Detector/GridSampler.php @@ -14,7 +14,7 @@ namespace chillerlan\QRCode\Detector; use chillerlan\QRCode\Data\QRMatrix; use chillerlan\QRCode\Decoder\BitMatrix; use Throwable; -use function array_fill, count, sprintf; +use function array_fill, count, intdiv, sprintf; /** * Implementations of this class can, given locations of finder patterns for a QR code in an @@ -31,6 +31,8 @@ use function array_fill, count, sprintf; */ final class GridSampler{ + private array $points; + /** * Checks a set of points that have been transformed to sample points on an image against * the image's dimensions to see if the point are even within the image. @@ -42,20 +44,18 @@ final class GridSampler{ * 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 \chillerlan\QRCode\Decoder\BitMatrix $matrix image into which the points should map - * @param float[] $points actual points in x1,y1,...,xn,yn form + * @param int $dimension matrix width/height * * @throws \chillerlan\QRCode\Detector\QRCodeDetectorException if an endpoint is lies outside the image boundaries */ - private function checkAndNudgePoints(BitMatrix $matrix, array $points):void{ - $dimension = $matrix->getSize(); - $nudged = true; - $max = count($points); + private function checkAndNudgePoints(int $dimension):void{ + $nudged = true; + $max = count($this->points); // Check and nudge points from start until we see some that are OK: for($offset = 0; $offset < $max && $nudged; $offset += 2){ - $x = (int)$points[$offset]; - $y = (int)$points[($offset + 1)]; + $x = (int)$this->points[$offset]; + $y = (int)$this->points[($offset + 1)]; if($x < -1 || $x > $dimension || $y < -1 || $y > $dimension){ throw new QRCodeDetectorException(sprintf('checkAndNudgePoints 1, x: %s, y: %s, d: %s', $x, $y, $dimension)); @@ -64,31 +64,31 @@ final class GridSampler{ $nudged = false; if($x === -1){ - $points[$offset] = 0.0; - $nudged = true; + $this->points[$offset] = 0.0; + $nudged = true; } elseif($x === $dimension){ - $points[$offset] = ($dimension - 1); - $nudged = true; + $this->points[$offset] = ($dimension - 1); + $nudged = true; } if($y === -1){ - $points[($offset + 1)] = 0.0; - $nudged = true; + $this->points[($offset + 1)] = 0.0; + $nudged = true; } elseif($y === $dimension){ - $points[($offset + 1)] = ($dimension - 1); - $nudged = true; + $this->points[($offset + 1)] = ($dimension - 1); + $nudged = true; } } + // Check and nudge points from end: $nudged = true; - $offset = (count($points) - 2); - for(; $offset >= 0 && $nudged; $offset -= 2){ - $x = (int)$points[$offset]; - $y = (int)$points[($offset + 1)]; + for($offset = ($max - 2); $offset >= 0 && $nudged; $offset -= 2){ + $x = (int)$this->points[$offset]; + $y = (int)$this->points[($offset + 1)]; if($x < -1 || $x > $dimension || $y < -1 || $y > $dimension){ throw new QRCodeDetectorException(sprintf('checkAndNudgePoints 2, x: %s, y: %s, d: %s', $x, $y, $dimension)); @@ -97,24 +97,25 @@ final class GridSampler{ $nudged = false; if($x === -1){ - $points[$offset] = 0.0; - $nudged = true; + $this->points[$offset] = 0.0; + $nudged = true; } elseif($x === $dimension){ - $points[$offset] = ($dimension - 1); - $nudged = true; + $this->points[$offset] = ($dimension - 1); + $nudged = true; } if($y === -1){ - $points[($offset + 1)] = 0.0; - $nudged = true; + $this->points[($offset + 1)] = 0.0; + $nudged = true; } elseif($y === $dimension){ - $points[($offset + 1)] = ($dimension - 1); - $nudged = true; + $this->points[($offset + 1)] = ($dimension - 1); + $nudged = true; } } + } /** @@ -133,31 +134,36 @@ final class GridSampler{ throw new QRCodeDetectorException('invalid matrix size'); } - $bits = new BitMatrix($dimension); - $points = array_fill(0, (2 * $dimension), 0.0); + $bits = new BitMatrix($dimension); + $this->points = array_fill(0, (2 * $dimension), 0.0); for($y = 0; $y < $dimension; $y++){ - $max = count($points); + $max = count($this->points); $iValue = ($y + 0.5); for($x = 0; $x < $max; $x += 2){ - $points[$x] = (($x / 2) + 0.5); - $points[($x + 1)] = $iValue; + $this->points[$x] = (($x / 2) + 0.5); + $this->points[($x + 1)] = $iValue; } - - $transform->transformPoints($points); + // phpcs:ignore + [$this->points, ] = $transform->transformPoints($this->points); // Quick check to see if points transformed to something inside the image; // sufficient to check the endpoints - $this->checkAndNudgePoints($matrix, $points); + $this->checkAndNudgePoints($matrix->getSize()); - try{ + // no need to try/catch as QRMatrix::set() will silently discard out of bounds values +# try{ for($x = 0; $x < $max; $x += 2){ // Black(-ish) pixel - $bits->set(($x / 2), $y, $matrix->check((int)$points[$x], (int)$points[($x + 1)]), QRMatrix::M_DATA); + $bits->set( + intdiv($x, 2), + $y, + $matrix->check((int)$this->points[$x], (int)$this->points[($x + 1)]), + QRMatrix::M_DATA + ); } - } - // @codeCoverageIgnoreStart - catch(Throwable $aioobe){//ArrayIndexOutOfBoundsException +# } +# catch(Throwable $aioobe){//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 @@ -165,9 +171,8 @@ final class GridSampler{ // 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 new QRCodeDetectorException('ArrayIndexOutOfBoundsException'); - } - // @codeCoverageIgnoreEnd +# throw new QRCodeDetectorException('ArrayIndexOutOfBoundsException'); +# } } diff --git a/src/Detector/PerspectiveTransform.php b/src/Detector/PerspectiveTransform.php index 5385d628e..5f1bff54b 100644 --- a/src/Detector/PerspectiveTransform.php +++ b/src/Detector/PerspectiveTransform.php @@ -150,12 +150,12 @@ final class PerspectiveTransform{ } /** - * + * @return array[] [$xValues, $yValues|null] */ - public function transformPoints(array &$xValues, array &$yValues = null):void{ + public function transformPoints(array $xValues, array $yValues = null):array{ $max = count($xValues); - if($yValues !== null){ + if($yValues !== null){ // unused for($i = 0; $i < $max; $i++){ $x = $xValues[$i]; @@ -165,7 +165,7 @@ final class PerspectiveTransform{ $yValues[$i] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator); } - return; + return [$xValues, $yValues]; } for($i = 0; $i < $max; $i += 2){ @@ -175,6 +175,8 @@ final class PerspectiveTransform{ $xValues[$i] = (($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator); $xValues[($i + 1)] = (($this->a12 * $x + $this->a22 * $y + $this->a32) / $denominator); } + + return [$xValues, $yValues]; } } diff --git a/src/Output/QRGdImage.php b/src/Output/QRGdImage.php index 3953d3618..14feb6fb9 100644 --- a/src/Output/QRGdImage.php +++ b/src/Output/QRGdImage.php @@ -16,7 +16,7 @@ use chillerlan\QRCode\Data\QRMatrix; use chillerlan\Settings\SettingsContainerInterface; use ErrorException, Throwable; use function array_values, count, extension_loaded, imagecolorallocate, imagecolortransparent, imagecreatetruecolor, - imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng, imagescale, intval, + imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng, imagescale, intdiv, intval, is_array, is_numeric, max, min, ob_end_clean, ob_get_contents, ob_start, restore_error_handler, set_error_handler; /** @@ -250,8 +250,8 @@ class QRGdImage extends QROutputAbstract{ $this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare) ? imagefilledellipse( $this->image, - (int)(($x * $this->scale) + ($this->scale / 2)), - (int)(($y * $this->scale) + ($this->scale / 2)), + (($x * $this->scale) + intdiv($this->scale, 2)), + (($y * $this->scale) + intdiv($this->scale, 2)), (int)(2 * $this->options->circleRadius * $this->scale), (int)(2 * $this->options->circleRadius * $this->scale), $color diff --git a/src/Output/QROutputInterface.php b/src/Output/QROutputInterface.php index 901d1c5f8..e4a68a28d 100644 --- a/src/Output/QROutputInterface.php +++ b/src/Output/QROutputInterface.php @@ -132,7 +132,7 @@ interface QROutputInterface{ public static function moduleValueIsValid($value):bool; /** - * generates the output, optionally dumps it to a file, and returns it + * Generates the output, optionally dumps it to a file, and returns it * * please note that the value of QROptions::$cachefile is already evaluated at this point. * if the output module is invoked manually, it has no effect at all.