smiley 10 years ago
parent
commit
8671a6936a

+ 2 - 4
examples/custom.php

@@ -10,13 +10,11 @@ $starttime = microtime(true);
 // google authenticator
 // https://chart.googleapis.com/chart?chs=200x200&chld=M%7C0&cht=qr&chl=otpauth%3A%2F%2Ftotp%2Ftest%3Fsecret%3DB3JX4VCVJDVNXNZ5%26issuer%3Dchillerlan.net
 
-$qrcode = (new QRCode(5, QRConst::ERROR_CORRECT_LEVEL_M))
-	->addData('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net')
-	->make();
+$qrcode = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M, 5);
 
 for($row = 0; $row < $qrcode->moduleCount; $row++){
 	for($col = 0; $col < $qrcode->moduleCount; $col++){
-		echo $qrcode->isDark($row, $col) ? 'X' : ' ';
+		echo (bool)$qrcode->modules[$row][$col] ? '#' : ' ';
 	}
 	echo PHP_EOL;
 }

+ 1 - 1
examples/html.php

@@ -31,5 +31,5 @@ echo '<style>
 </style>';
 
 // google authenticator
-$qr = (new QRCode)->getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M);
+$qr = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_M);
 echo $qr->printHTML();

+ 1 - 1
examples/image.php

@@ -6,7 +6,7 @@ use codemasher\QRCode\QRCode;
 use codemasher\QRCode\QRConst;
 
 // google authenticator
-$qr = (new QRCode)->getMinimumQRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_L);
+$qr = new QRCode('otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net', QRConst::ERROR_CORRECT_LEVEL_L);
 
 header('Content-type: image/png');
 

+ 11 - 2
src/BitBuffer.php

@@ -29,7 +29,7 @@ class BitBuffer{
 	/**
 	 * @return string
 	 */
-	public function __toString(){
+/*	public function __toString(){
 		$buffer = '';
 
 		for($i = 0; $i < $this->length; $i++){
@@ -38,19 +38,26 @@ class BitBuffer{
 
 		return $buffer;
 	}
-
+*/
 	/**
 	 * @param $num
 	 * @param $length
+	 *
+	 * @return $this
 	 */
 	public function put($num, $length){
+
 		for($i = 0; $i < $length; $i++){
 			$this->putBit((($num >> ($length - $i - 1))&1) === 1);
 		}
+
+		return $this;
 	}
 
 	/**
 	 * @param $bit
+	 *
+	 * @return $this
 	 */
 	public function putBit($bit){
 		$bufIndex = floor($this->length / 8);
@@ -64,6 +71,8 @@ class BitBuffer{
 		}
 
 		$this->length++;
+
+		return $this;
 	}
 
 }

+ 18 - 23
src/Data/AlphaNum.php

@@ -4,7 +4,7 @@
  *
  * @filesource   AlphaNum.php
  * @created      25.11.2015
- * @package      codemasher\QRCode
+ * @package      codemasher\QRCode\Data
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT
@@ -13,8 +13,6 @@
 namespace codemasher\QRCode\Data;
 
 use codemasher\QRCode\BitBuffer;
-use codemasher\QRCode\Data\QRDataBase;
-use codemasher\QRCode\Data\QRDataInterface;
 use codemasher\QRCode\QRCodeException;
 use codemasher\QRCode\QRConst;
 
@@ -58,26 +56,23 @@ class AlphaNum extends QRDataBase implements QRDataInterface{
 	 */
 	protected function getCode($c){
 
-		if(ord('0') <= $c && $c <= ord('9')){
-			return $c - ord('0');
-		}
-		else if(ord('A') <= $c && $c <= ord('Z')){
-			return $c - ord('A') + 10;
-		}
-		else{
-			switch($c){
-				case ord(' '): return 36;
-				case ord('$'): return 37;
-				case ord('%'): return 38;
-				case ord('*'): return 39;
-				case ord('+'): return 40;
-				case ord('-'): return 41;
-				case ord('.'): return 42;
-				case ord('/'): return 43;
-				case ord(':'): return 44;
-				default :
-					throw new QRCodeException('illegal char: '.$c);
-			}
+		switch(true){
+			case ord('0') <= $c && $c <= ord('9'): return $c - ord('0');
+			case ord('A') <= $c && $c <= ord('Z'): return $c - ord('A') + 10;
+			default:
+				switch($c){
+					case ord(' '): return 36;
+					case ord('$'): return 37;
+					case ord('%'): return 38;
+					case ord('*'): return 39;
+					case ord('+'): return 40;
+					case ord('-'): return 41;
+					case ord('.'): return 42;
+					case ord('/'): return 43;
+					case ord(':'): return 44;
+					default:
+						throw new QRCodeException('illegal char: '.$c);
+				}
 		}
 
 	}

+ 1 - 4
src/Data/Byte.php

@@ -4,7 +4,7 @@
  *
  * @filesource   Byte.php
  * @created      25.11.2015
- * @package      codemasher\QRCode
+ * @package      codemasher\QRCode\Data
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT
@@ -14,9 +14,6 @@ namespace codemasher\QRCode\Data;
 
 use codemasher\QRCode\BitBuffer;
 use codemasher\QRCode\QRConst;
-use codemasher\QRCode\Data\QRDataBase;
-use codemasher\QRCode\Data\QRDataInterface;
-
 
 /**
  *

+ 1 - 3
src/Data/Kanji.php

@@ -4,7 +4,7 @@
  *
  * @filesource   Kanji.php
  * @created      25.11.2015
- * @package      codemasher\QRCode
+ * @package      codemasher\QRCode\Data
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT
@@ -13,8 +13,6 @@
 namespace codemasher\QRCode\Data;
 
 use codemasher\QRCode\BitBuffer;
-use codemasher\QRCode\Data\QRDataBase;
-use codemasher\QRCode\Data\QRDataInterface;
 use codemasher\QRCode\QRCodeException;
 use codemasher\QRCode\QRConst;
 

+ 1 - 3
src/Data/Number.php

@@ -4,7 +4,7 @@
  *
  * @filesource   Number.php
  * @created      26.11.2015
- * @package      codemasher\QRCode
+ * @package      QRCode
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT
@@ -13,8 +13,6 @@
 namespace codemasher\QRCode\Data;
 
 use codemasher\QRCode\BitBuffer;
-use codemasher\QRCode\Data\QRDataBase;
-use codemasher\QRCode\Data\QRDataInterface;
 use codemasher\QRCode\QRCodeException;
 use codemasher\QRCode\QRConst;
 

+ 17 - 14
src/Data/QRDataBase.php

@@ -4,7 +4,7 @@
  *
  * @filesource   QRDataBase.php
  * @created      25.11.2015
- * @package      codemasher\QRCode
+ * @package      codemasher\QRCode\Data
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT
@@ -13,14 +13,12 @@
 namespace codemasher\QRCode\Data;
 
 use codemasher\QRCode\BitBuffer;
-use codemasher\QRCode\QRConst;
 use codemasher\QRCode\QRCodeException;
-use codemasher\QRCode\Util;
 
 /**
  *
  */
-class QRDataBase{
+class QRDataBase implements QRDataInterface{
 
 	/**
 	 * @var
@@ -42,11 +40,6 @@ class QRDataBase{
 	 */
 	protected $lengthBits = [0, 0, 0];
 
-	/**
-	 * @var \codemasher\QRCode\Util
-	 */
-	protected $util;
-
 	/**
 	 * QRDataBase constructor.
 	 *
@@ -55,7 +48,6 @@ class QRDataBase{
 	public function __construct($data){
 		$this->data = $data;
 		$this->dataLength = strlen($data);
-		$this->util = new Util;
 	}
 
 	/**
@@ -67,13 +59,24 @@ class QRDataBase{
 	public function getLengthInBits($type){
 
 		switch(true){
-			case 1 <= $type && $type < 10: return $this->lengthBits[0]; break; // 1 - 9
-			case $type < 27: return  $this->lengthBits[1]; break; // 10 - 26
-			case $type < 41: return  $this->lengthBits[2]; break; // 27 - 40
+			case $type >= 1 && $type <= 9: return $this->lengthBits[0]; // 1 - 9
+			case $type <= 26             : return $this->lengthBits[1]; // 10 - 26
+			case $type <= 40             : return $this->lengthBits[2]; // 27 - 40
 			default:
-				throw new QRCodeException('mode: '.$this->mode);
+				throw new QRCodeException('$type: '.$type);
 		}
 
 	}
 
+	/**
+	 * @todo  Implement write() method.
+	 *
+	 * @param \codemasher\QRCode\BitBuffer $buffer
+	 *
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function write(BitBuffer &$buffer){
+		throw new QRCodeException('Method should be implemented in child classes');
+	}
+
 }

+ 1 - 0
src/Data/QRDataInterface.php

@@ -4,6 +4,7 @@
  *
  * @filesource   QRDataInterface.php
  * @created      01.12.2015
+ * @package      codemasher\QRCode\Data
  * @author       Smiley <smiley@chillerlan.net>
  * @copyright    2015 Smiley
  * @license      MIT

+ 14 - 39
src/Polynomial.php

@@ -38,8 +38,7 @@ class Polynomial{
 	 * @param int   $shift
 	 */
 	public function __construct(array $num = [1], $shift = 0){
-		$this->setNum($num, $shift)
-		     ->setTables();
+		$this->setNum($num, $shift)->setTables();
 	}
 
 	/**
@@ -56,10 +55,7 @@ class Polynomial{
 			$offset++;
 		}
 
-		$this->num = [];
-		for($i = 0; $i < $numCount - $offset + $shift; $i++){
-			$this->num[] = 0;
-		}
+		$this->num = array_fill(0, $numCount - $offset + $shift, 0);
 
 		for($i = 0; $i < $numCount - $offset; $i++){
 			$this->num[$i] = $num[$i + $offset];
@@ -72,12 +68,8 @@ class Polynomial{
 	 * @return $this
 	 */
 	protected function setTables(){
-		$numArr = [];
-		for($i = 0; $i < 256; $i++){
-			$numArr[] = 0;
-		}
 
-		$this->EXP_TABLE = $this->LOG_TABLE = $numArr;
+		$this->EXP_TABLE = $this->LOG_TABLE = array_fill(0, 256, 0);
 
 		for($i = 0; $i < 8; $i++){
 			$this->EXP_TABLE[$i] = 1 << $i;
@@ -97,7 +89,7 @@ class Polynomial{
 	/**
 	 * @return string
 	 */
-	public function __toString(){
+/*	public function __toString(){
 		$buffer = '';
 
 		foreach($this->num as $i => $value){
@@ -109,11 +101,11 @@ class Polynomial{
 
 		return $buffer;
 	}
-
+*/
 	/**
 	 * @return string
 	 */
-	public function toLogString(){
+/*	public function toLogString(){
 		$buffer = '';
 
 		foreach($this->num as $i => $value){
@@ -125,7 +117,7 @@ class Polynomial{
 
 		return $buffer;
 	}
-
+*/
 	/**
 	 * @param array $e
 	 *
@@ -133,12 +125,7 @@ class Polynomial{
 	 */
 	public function multiply(array $e){
 
-		$num = [];
-		$len = count($this->num) + count($e) - 1;
-		for($i = 0; $i < $len; $i++){
-			$num[] = 0;
-		}
-
+		$num = array_fill(0, count($this->num) + count($e) - 1, 0);
 		foreach($this->num as $i => $vi){
 			foreach($e as $j => $vj){
 				$num[$i + $j] ^= $this->gexp($this->glog($vi) + $this->glog($vj));
@@ -156,28 +143,19 @@ class Polynomial{
 	 * @return $this
 	 */
 	public function mod($e){
-#		$starttime = microtime(true);
 
 		if(count($this->num) - count($e) < 0){
 			return $this;
 		}
 
 		$ratio = $this->glog($this->num[0]) - $this->glog($e[0]);
-
-		$num = [];
-		foreach($this->num as $i => $value){
-			$num[$i] = $value;
-		}
-
 		foreach($e as $i => $value){
-			$num[$i] ^= $this->gexp($this->glog($e[$i]) + $ratio);
+			$this->num[$i] ^= $this->gexp($this->glog($e[$i]) + $ratio);
 		}
 
-		$this->setNum($num)->mod($e);
+		$this->setNum($this->num)->mod($e);
 
-#		echo 'Polynomial::mod '.round((microtime(true)-$starttime), 5).PHP_EOL;
-
-		return $num;
+		return $this->num;
 	}
 
 	/**
@@ -202,12 +180,9 @@ class Polynomial{
 	 */
 	public function gexp($n){
 
-		while($n < 0){
-			$n += 255;
-		}
-
-		while($n >= 256){
-			$n -= 255;
+		switch(true){
+			case $n < 0   : $n += 255; break;
+			case $n >= 256: $n -= 255; break;
 		}
 
 		return $this->EXP_TABLE[$n];

+ 268 - 269
src/QRCode.php

@@ -12,13 +12,7 @@
 
 namespace codemasher\QRCode;
 
-use codemasher\QRCode\Polynomial;
-use codemasher\QRCode\QRConst;
-use codemasher\QRCode\Data\AlphaNum;
-use codemasher\QRCode\Data\Byte;
-use codemasher\QRCode\Data\Kanji;
-use codemasher\QRCode\Data\Number;
-use codemasher\QRCode\Data\QRDataInterface;
+use codemasher\QRCode\Data\QRDataBase;
 
 /**
  * @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
@@ -31,25 +25,25 @@ class QRCode{
 	/**
 	 * @var int
 	 */
-	public $typeNumber;
+	public $moduleCount;
 
 	/**
 	 * @var array
 	 */
-	protected $modules;
+	public $modules;
 
 	/**
 	 * @var int
 	 */
-	public $moduleCount;
+	protected $typeNumber;
 
 	/**
 	 * @var int
 	 */
-	public $errorCorrectLevel;
+	protected $errorCorrectLevel;
 
 	/**
-	 * @var array
+	 * @var array -> \codemasher\QRCode\Data\QRDataInterface
 	 */
 	protected $qrDataList = [];
 
@@ -63,6 +57,31 @@ class QRCode{
 	 */
 	protected $data;
 
+	/**
+	 * @var int
+	 */
+	protected $mode;
+
+	/**
+	 * @var array
+	 */
+	protected $ERROR_CORRECT_LEVEL = [
+		QRConst::ERROR_CORRECT_LEVEL_L,
+		QRConst::ERROR_CORRECT_LEVEL_M,
+		QRConst::ERROR_CORRECT_LEVEL_Q,
+		QRConst::ERROR_CORRECT_LEVEL_H,
+	];
+
+	/**
+	 * @var array
+	 */
+	protected $qrDataInterface = [
+		QRConst::MODE_ALPHANUM => '\\codemasher\\QRCode\\Data\\AlphaNum',
+		QRConst::MODE_BYTE     => '\\codemasher\\QRCode\\Data\\Byte',
+		QRConst::MODE_KANJI    => '\\codemasher\\QRCode\\Data\\Kanji',
+		QRConst::MODE_NUMBER   => '\\codemasher\\QRCode\\Data\\Number',
+	];
+
 	/**
 	 * @var \codemasher\QRCode\Util
 	 */
@@ -79,23 +98,63 @@ class QRCode{
 	protected $bitBuffer;
 
 	/**
+	 * @todo WIP
+	 *
 	 * QRCode constructor.
 	 *
-	 * @param int $typeNumber
-	 * @param int $errorCorrectLevel
+	 * @param string $data
+	 * @param int    $errorCorrectLevel
+	 * @param int    $typeNumber
+	 *
+	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	public function __construct($typeNumber = 1, $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H){
+	public function __construct($data = '', $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_M, $typeNumber = null){
 		$this->util = new Util;
 		$this->rsBlock = new RSBlock;
 		$this->bitBuffer = new BitBuffer;
 
-		$this->typeNumber = $typeNumber;
-		$this->errorCorrectLevel = $errorCorrectLevel;
+		$this->setErrorCorrectLevel($errorCorrectLevel)->setTypeNumber($typeNumber);
+
+		if(!empty($data)){
+			$this->getMinimumQRCode($data);
+		}
+
+	}
+
+	/**
+	 * @todo WIP
+	 *
+	 * @param $data
+	 *
+	 * @return \codemasher\QRCode\QRCode
+	 * @throws \codemasher\QRCode\QRCodeException
+	 */
+	public function getMinimumQRCode($data){
+		$mode = $this->util->getMode($data);
+		$this->addData($data, $mode);
+
+		/** @var \codemasher\QRCode\Data\QRDataBase $qrData */
+		$qrData = $this->qrDataList[0];
+		$length = $qrData->mode === QRConst::MODE_KANJI ? floor($qrData->dataLength / 2) : $qrData->dataLength;
+
+		for($typeNumber = 1; $typeNumber <= 10; $typeNumber++){
+			if($length <= $this->util->getMaxLength($typeNumber, $mode, $this->errorCorrectLevel)){
+				$this->typeNumber = $typeNumber;
+				break;
+			}
+		}
+
+		$this->make();
+
+		return $this;
 	}
 
 	/**
+	 * @todo WIP
+	 *
 	 * @param string $data
-	 * @param int    $mode
+	 *
+	 * @param null   $mode
 	 *
 	 * @return $this
 	 * @throws \codemasher\QRCode\QRCodeException
@@ -105,77 +164,174 @@ class QRCode{
 			$mode = $this->util->getMode($data);
 		}
 
-		switch($mode){
-			case QRConst::MODE_NUMBER  : $this->qrDataList[] = new Number($data); break;
-			case QRConst::MODE_ALPHANUM: $this->qrDataList[] = new AlphaNum($data); break;
-			case QRConst::MODE_BYTE    : $this->qrDataList[] = new Byte($data); break;
-			case QRConst::MODE_KANJI   : $this->qrDataList[] = new Kanji($data); break;
-			default:
-				throw new QRCodeException('mode: '.$mode);
+		if(!isset($this->qrDataInterface[$mode])){
+			throw new QRCodeException('mode: '.$mode);
 		}
 
+		$this->qrDataList[] = new $this->qrDataInterface[$mode]($data);
+
 		return $this;
 	}
 
+
 	/**
-	 * @param int $row
-	 * @param int $col
+	 * @param $errorCorrectLevel
 	 *
-	 * @return bool
+	 * @return $this
+	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	public function isDark($row, $col){
-		if($this->modules[$row][$col] !== null){
-			return (bool)$this->modules[$row][$col];
-		}
-		else{
-			return false;
+	public function setErrorCorrectLevel($errorCorrectLevel){
+
+		if(!in_array($errorCorrectLevel, $this->ERROR_CORRECT_LEVEL)){
+			throw new QRCodeException('Invalid error correct level: '.$errorCorrectLevel);
 		}
+
+		$this->errorCorrectLevel = $errorCorrectLevel;
+
+		return $this;
 	}
 
 	/**
+	 * @param int $typeNumber
 	 *
+	 * @return $this
+	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	public function make(){
-		$this->makeImpl(false, $this->getBestMaskPattern());
+	public function setTypeNumber($typeNumber){
+		$typeNumber = intval($typeNumber);
+
+		if($typeNumber < 1 || $typeNumber > 10){
+			throw new QRCodeException('Invalid type number: '.$typeNumber);
+		}
+
+		$this->typeNumber = $typeNumber;
 
 		return $this;
 	}
 
 	/**
-	 * @param $data
-	 * @param $errorCorrectLevel
 	 *
-	 * @return \codemasher\QRCode\QRCode
-	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	public function getMinimumQRCode($data, $errorCorrectLevel = QRConst::ERROR_CORRECT_LEVEL_H){
-		$mode = $this->util->getMode($data);
-		$this->addData($data, $mode);
-		$this->errorCorrectLevel = $errorCorrectLevel;
+	public function make(){
+		$minLostPoint = 0;
+		$pattern = 0;
 
-		/** @var \codemasher\QRCode\Data\QRDataBase $qrData */
-		$qrData = $this->qrDataList[0];
-		$length = $qrData->mode === QRConst::MODE_KANJI ? floor($qrData->dataLength / 2) : $qrData->dataLength;
+		for($i = 0; $i < 8; $i++){
+			$this->makeImpl(true, $i);
+			$lostPoint = 0;
 
-		for($typeNumber = 1; $typeNumber <= 10; $typeNumber++){
-			if($length <= $this->util->getMaxLength($typeNumber, $mode, $this->errorCorrectLevel)){
-				$this->typeNumber = $typeNumber;
-				break;
+			// LEVEL1
+			for($row = 0; $row < $this->moduleCount; $row++){
+				for($col = 0; $col < $this->moduleCount; $col++){
+					$sameCount = 0;
+					$dark = $this->modules[$row][$col];
+
+					for($r = -1; $r <= 1; $r++){
+						if($row + $r < 0 || $this->moduleCount <= $row + $r){
+							continue;
+						}
+
+						for($c = -1; $c <= 1; $c++){
+
+							if(($r === 0 && $c === 0) || ($col + $c < 0 || $this->moduleCount <= $col + $c)){
+								continue;
+							}
+
+							if($this->modules[$row + $r][$col + $c] === $dark){
+								$sameCount++;
+							}
+
+						}
+					}
+
+					if($sameCount > 5){
+						$lostPoint += (3 + $sameCount - 5);
+					}
+				}
+			}
+
+			// LEVEL2
+			for($row = 0; $row < $this->moduleCount - 1; $row++){
+				for($col = 0; $col < $this->moduleCount - 1; $col++){
+					$count = 0;
+
+					if($this->modules[$row][$col] || $this->modules[$row][$col + 1]
+						|| $this->modules[$row + 1][$col] || $this->modules[$row + 1][$col + 1]
+					){
+						$count++;
+					}
+
+					if($count === 0 || $count === 4){
+						$lostPoint += 3;
+					}
+
+				}
 			}
+
+			// LEVEL3
+			for($row = 0; $row < $this->moduleCount; $row++){
+				for($col = 0; $col < $this->moduleCount - 6; $col++){
+					if($this->modules[$row][$col]
+						&& !$this->modules[$row][$col + 1]
+						&&  $this->modules[$row][$col + 2]
+						&&  $this->modules[$row][$col + 3]
+						&&  $this->modules[$row][$col + 4]
+						&& !$this->modules[$row][$col + 5]
+						&&  $this->modules[$row][$col + 6]
+					){
+						$lostPoint += 40;
+					}
+				}
+			}
+
+			for($col = 0; $col < $this->moduleCount; $col++){
+				for($row = 0; $row < $this->moduleCount - 6; $row++){
+					if($this->modules[$row][$col]
+						&& !$this->modules[$row + 1][$col]
+						&&  $this->modules[$row + 2][$col]
+						&&  $this->modules[$row + 3][$col]
+						&&  $this->modules[$row + 4][$col]
+						&& !$this->modules[$row + 5][$col]
+						&&  $this->modules[$row + 6][$col]
+					){
+						$lostPoint += 40;
+					}
+				}
+			}
+
+			// LEVEL4
+			$darkCount = 0;
+			for($col = 0; $col < $this->moduleCount; $col++){
+				for($row = 0; $row < $this->moduleCount; $row++){
+					if($this->modules[$row][$col]){
+						$darkCount++;
+					}
+				}
+			}
+
+			$ratio = abs(100 * $darkCount / $this->moduleCount / $this->moduleCount - 50) / 5;
+			$lostPoint += $ratio * 10;
+
+			if($i === 0 || $minLostPoint > $lostPoint){
+				$minLostPoint = $lostPoint;
+				$pattern = $i;
+			}
+
 		}
 
-		$this->makeImpl(false, $this->getBestMaskPattern());
+		$this->makeImpl(false, $pattern);
 
 		return $this;
 	}
 
 	/**
 	 * @param bool $test
-	 * @param int  $maskPattern
+	 * @param int  $pattern
 	 *
 	 * @return $this
+	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	protected function makeImpl($test, $maskPattern){
+	protected function makeImpl($test, $pattern){
 		$this->moduleCount = $this->typeNumber * 4 + 17;
 		$this->modules = [];
 
@@ -186,15 +342,41 @@ class QRCode{
 
 		$this->setupPositionProbePattern(0, 0)
 		     ->setupPositionProbePattern($this->moduleCount - 7, 0)
-		     ->setupPositionProbePattern(0, $this->moduleCount - 7)
-		     ->setupPositionAdjustPattern()
-		     ->setupTimingPattern();
+		     ->setupPositionProbePattern(0, $this->moduleCount - 7);
+
+		// setupPositionAdjustPattern
+		$pos = $this->util->PATTERN_POSITION[$this->typeNumber - 1];
+		foreach($pos as $i => $posI){
+			foreach($pos as $j => $posJ){
+				if($this->modules[$posI][$posJ] !== null){
+					continue;
+				}
 
-		$data = ($this->errorCorrectLevel << 3) | $maskPattern;
+				for($row = -2; $row <= 2; $row++){
+					for($col = -2; $col <= 2; $col++){
+						$this->modules[$posI + $row][$posJ + $col] =
+							(bool)$row === -2 || $row === 2 || $col === -2 || $col === 2 || ($row === 0 && $col === 0);
+					}
+				}
+
+			}
+		}
+
+		// setupTimingPattern
+		for($i = 8; $i < $this->moduleCount - 8; $i++){
+			if($this->modules[$i][6] !== null){
+				continue;
+			}
+
+			$this->modules[$i][6] = $this->modules[6][$i] = (bool)$i % 2 === 0;
+		}
+
+
+		$data = ($this->errorCorrectLevel << 3) | $pattern;
 		$bits = $this->util->getBCHTypeInfo($data);
 
 		for($i = 0; $i < 15; $i++){
-			$mod = !$test && (($bits >> $i) & 1) === 1;
+			$mod = (bool)!$test && (($bits >> $i) & 1) === 1;
 
 			switch(true){
 				case $i < 6: $this->modules[$i][8] = $mod; break;
@@ -221,23 +403,12 @@ class QRCode{
 				$a = (int)floor($i / 3);
 				$b = $i % 3 + $this->moduleCount - 8 - 3;
 
-				$this->modules[$a][$b] = $this->modules[$b][$a] = !$test && (($bits >> $i) & 1) === 1;
+				$this->modules[$a][$b] = $this->modules[$b][$a] = (bool)!$test && (($bits >> $i) & 1) === 1;
 			}
 		}
 
 		$this->data = $this->createData($this->typeNumber, $this->errorCorrectLevel);
-		$this->mapData($maskPattern);
 
-		return $this;
-	}
-
-	/**
-	 * @param $maskPattern
-	 *
-	 * @return $this
-	 * @throws \codemasher\QRCode\QRCodeException
-	 */
-	protected function mapData($maskPattern){
 		$inc = -1;
 		$row = $this->moduleCount - 1;
 		$bitIndex = 7;
@@ -261,24 +432,27 @@ class QRCode{
 						}
 
 						$_col = $col - $c;
-						switch($maskPattern){
-							case QRConst::MASK_PATTERN000: $mask = ($row + $_col) % 2 === 0; break;
-							case QRConst::MASK_PATTERN001: $mask = $row % 2 === 0; break;
-							case QRConst::MASK_PATTERN010: $mask = $_col % 3 === 0; break;
-							case QRConst::MASK_PATTERN011: $mask = ($row + $_col) % 3 === 0; break;
-							case QRConst::MASK_PATTERN100: $mask = (floor($row / 2) + floor($_col / 3)) % 2 === 0; break;
-							case QRConst::MASK_PATTERN101: $mask = ($row * $_col) % 2 + ($row * $_col) % 3 === 0; break;
-							case QRConst::MASK_PATTERN110: $mask = (($row * $_col) % 2 + ($row * $_col) % 3) % 2 === 0; break;
-							case QRConst::MASK_PATTERN111: $mask = (($row * $_col) % 3 + ($row + $_col) % 2) % 2 === 0; break;
-							default :
-								throw new QRCodeException('mask: '.$maskPattern);
+
+						$MASK_PATTERN = [
+							QRConst::MASK_PATTERN000 => ($row + $_col) % 2 === 0,
+							QRConst::MASK_PATTERN001 => $row % 2 === 0,
+							QRConst::MASK_PATTERN010 => $_col % 3 === 0,
+							QRConst::MASK_PATTERN011 => ($row + $_col) % 3 === 0,
+							QRConst::MASK_PATTERN100 => (floor($row / 2) + floor($_col / 3)) % 2 === 0,
+							QRConst::MASK_PATTERN101 => ($row * $_col) % 2 + ($row * $_col) % 3 === 0,
+							QRConst::MASK_PATTERN110 => (($row * $_col) % 2 + ($row * $_col) % 3) % 2 === 0,
+							QRConst::MASK_PATTERN111 => (($row * $_col) % 3 + ($row + $_col) % 2) % 2 === 0,
+						];
+
+						if(!isset($MASK_PATTERN[$pattern])){
+							throw new QRCodeException('mask: '.$pattern);
 						}
 
-						if($mask){
+						if($MASK_PATTERN[$pattern]){
 							$dark = !$dark;
 						}
 
-						$this->modules[$row][$col - $c] = $dark;
+						$this->modules[$row][$col - $c] = (bool)$dark;
 						$bitIndex--;
 
 						if($bitIndex === -1){
@@ -328,198 +502,29 @@ class QRCode{
 		return $this;
 	}
 
-	/**
-	 * @return $this
-	 */
-	protected function setupPositionAdjustPattern(){
-		$pos = $this->util->PATTERN_POSITION[$this->typeNumber - 1];
-
-		foreach($pos as $i => $posI){
-			foreach($pos as $j => $posJ){
-				if($this->modules[$posI][$posJ] !== null){
-					continue;
-				}
-
-				for($row = -2; $row <= 2; $row++){
-					for($col = -2; $col <= 2; $col++){
-						$this->modules[$posI + $row][$posJ + $col] =
-							(bool)$row === -2 || $row === 2 || $col === -2 || $col === 2 || ($row === 0 && $col === 0);
-					}
-				}
-
-			}
-		}
-
-		return $this;
-	}
-
-	/**
-	 * @return $this
-	 */
-	protected function setupTimingPattern(){
-
-		for($i = 8; $i < $this->moduleCount - 8; $i++){
-			if($this->modules[$i][6] !== null){
-				continue;
-			}
-
-			$this->modules[$i][6] = $this->modules[6][$i] = $i % 2 === 0;
-		}
-
-		return $this;
-	}
-
-	/**
-	 * @return int
-	 */
-	protected function getBestMaskPattern(){
-		$minLostPoint = 0;
-		$pattern = 0;
-
-		for($i = 0; $i < 8; $i++){
-			$this->makeImpl(true, $i);
-			$lostPoint = 0;
-
-			// LEVEL1
-
-			for($row = 0; $row < $this->moduleCount; $row++){
-				for($col = 0; $col < $this->moduleCount; $col++){
-					$sameCount = 0;
-					$dark = $this->isDark($row, $col);
-
-					for($r = -1; $r <= 1; $r++){
-						if($row + $r < 0 || $this->moduleCount <= $row + $r){
-							continue;
-						}
-
-						for($c = -1; $c <= 1; $c++){
-
-							if($col + $c < 0 || $this->moduleCount <= $col + $c){
-								continue;
-							}
-
-							if($r == 0 && $c == 0){
-								continue;
-							}
-
-							if($dark === $this->isDark($row + $r, $col + $c)){
-								$sameCount++;
-							}
-						}
-					}
-
-					if($sameCount > 5){
-						$lostPoint += (3 + $sameCount - 5);
-					}
-				}
-			}
-
-			// LEVEL2
-
-			for($row = 0; $row < $this->moduleCount - 1; $row++){
-				for($col = 0; $col < $this->moduleCount - 1; $col++){
-					$count = 0;
-
-					if($this->isDark($row, $col)){
-						$count++;
-					}
-
-					if($this->isDark($row + 1, $col)){
-						$count++;
-					}
-
-					if($this->isDark($row, $col + 1)){
-						$count++;
-					}
-
-					if($this->isDark($row + 1, $col + 1)){
-						$count++;
-					}
-
-					if($count === 0 || $count === 4){
-						$lostPoint += 3;
-					}
-				}
-			}
-
-			// LEVEL3
-
-			for($row = 0; $row < $this->moduleCount; $row++){
-				for($col = 0; $col < $this->moduleCount - 6; $col++){
-					if($this->isDark($row, $col)
-						&& !$this->isDark($row, $col + 1)
-						&& $this->isDark($row, $col + 2)
-						&& $this->isDark($row, $col + 3)
-						&& $this->isDark($row, $col + 4)
-						&& !$this->isDark($row, $col + 5)
-						&& $this->isDark($row, $col + 6)
-					){
-						$lostPoint += 40;
-					}
-				}
-			}
-
-			for($col = 0; $col < $this->moduleCount; $col++){
-				for($row = 0; $row < $this->moduleCount - 6; $row++){
-					if($this->isDark($row, $col)
-						&& !$this->isDark($row + 1, $col)
-						&& $this->isDark($row + 2, $col)
-						&& $this->isDark($row + 3, $col)
-						&& $this->isDark($row + 4, $col)
-						&& !$this->isDark($row + 5, $col)
-						&& $this->isDark($row + 6, $col)
-					){
-						$lostPoint += 40;
-					}
-				}
-			}
-
-			// LEVEL4
-
-			$darkCount = 0;
-			for($col = 0; $col < $this->moduleCount; $col++){
-				for($row = 0; $row < $this->moduleCount; $row++){
-					if($this->isDark($row, $col)){
-						$darkCount++;
-					}
-				}
-			}
-
-			$ratio = abs(100 * $darkCount / $this->moduleCount / $this->moduleCount - 50) / 5;
-			$lostPoint += $ratio * 10;
-
-			if($i === 0 || $minLostPoint > $lostPoint){
-				$minLostPoint = $lostPoint;
-				$pattern = $i;
-			}
-		}
-
-		return $pattern;
-	}
-
 	/**
 	 * @param $typeNumber
 	 * @param $errorCorrectLevel
 	 *
 	 * @return array
 	 * @throws \codemasher\QRCode\QRCodeException
-	 * @todo: slooooow in PHP5
-	 *
 	 */
 	protected function createData($typeNumber, $errorCorrectLevel){
 		$this->bitBuffer->buffer = [];
 		$this->bitBuffer->length = 0;
-
 		$this->rsBlockList = $this->rsBlock->getRSBlocks($typeNumber, $errorCorrectLevel);
+		$rsBlockCount = count($this->rsBlockList);
+
+		$totalDataCount = $totalCodeCount = $offset = $maxDcCount = $maxEcCount = $index = 0;
+		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
 
-		$totalDataCount = $totalCodeCount = $offset = $maxDcCount = $maxEcCount = 0;
 
-		/** @var \codemasher\QRCode\Data\QRDataInterface $data */
+		/** @var \codemasher\QRCode\Data\QRDataBase $data */
 		foreach($this->qrDataList as &$data){
-			$len = $data->mode === QRConst::MODE_KANJI ? floor($data->dataLength / 2) : $data->dataLength;
+			$this->bitBuffer
+				->put($data->mode, 4)
+				->put($data->mode === QRConst::MODE_KANJI ? floor($data->dataLength / 2) : $data->dataLength, $data->getLengthInBits($typeNumber));
 
-			$this->bitBuffer->put($data->mode, 4);
-			$this->bitBuffer->put($len, $data->getLengthInBits($typeNumber));
 			$data->write($this->bitBuffer);
 		}
 
@@ -560,9 +565,6 @@ class QRCode{
 			$this->bitBuffer->put(self::QR_PAD1, 8);
 		}
 
-		$rsBlockCount = count($this->rsBlockList);
-		$dcdata = $ecdata = array_fill(0, $rsBlockCount, null);
-
 		foreach($this->rsBlockList as $r => &$_rsData){
 			$this->rsBlock->totalCount = $_rsData[0];
 			$this->rsBlock->dataCount = $_rsData[1];
@@ -582,13 +584,11 @@ class QRCode{
 
 			$offset += $this->rsBlock->dataCount;
 
-			// PHP5: 0.09s
 			for($i = 0; $i < $this->rsBlock->totalCount - $this->rsBlock->dataCount; $i++){
 				$modPoly->setNum([1, $modPoly->gexp($i)]);
 				$rsPoly->multiply($modPoly->num);
 			}
 
-			// PHP5: 0.11s
 			$rsPolyCount = count($rsPoly->num);
 			$modPoly->setNum($dcdata[$r], $rsPolyCount - 1)->mod($rsPoly->num);
 			$ecdata[$r] = array_fill(0, $rsPolyCount - 1, null);
@@ -603,7 +603,6 @@ class QRCode{
 		}
 
 		$data = array_fill(0, $totalCodeCount, null);
-		$index = 0;
 
 		for($i = 0; $i < $maxDcCount; $i++){
 			for($r = 0; $r < $rsBlockCount; $r++){
@@ -681,7 +680,7 @@ class QRCode{
 
 		for($r = 0; $r < $this->moduleCount; $r++){
 			for($c = 0; $c < $this->moduleCount; $c++){
-				if($this->isDark($r, $c)){
+				if($this->modules[$r][$c]){
 
 					// update $black to $fgc
 					imagefilledrectangle($image,
@@ -707,7 +706,7 @@ class QRCode{
 			$html .= '<tr>';
 
 			for($col = 0; $col < $this->moduleCount; $col++){
-				$html .= '<td class="'.($this->isDark($row, $col) ? 'dark' : 'light').'"></td>';
+				$html .= '<td class="'.($this->modules[$row][$col] ? 'dark' : 'light').'"></td>';
 			}
 
 			$html .= '</tr>';

+ 13 - 9
src/RSBlock.php

@@ -101,6 +101,16 @@ class RSBlock{
 
 	];
 
+	/**
+	 * @var array
+	 */
+	protected $RSBLOCK = [
+		QRConst::ERROR_CORRECT_LEVEL_L => 0,
+		QRConst::ERROR_CORRECT_LEVEL_M => 1,
+		QRConst::ERROR_CORRECT_LEVEL_Q => 2,
+		QRConst::ERROR_CORRECT_LEVEL_H => 3,
+	];
+
 	/**
 	 * @param $typeNumber
 	 * @param $errorCorrectLevel
@@ -110,16 +120,11 @@ class RSBlock{
 	 */
 	public function getRSBlocks($typeNumber, $errorCorrectLevel){
 
-		switch($errorCorrectLevel){
-			case QRConst::ERROR_CORRECT_LEVEL_L: $rsBlock = 0; break;
-			case QRConst::ERROR_CORRECT_LEVEL_M: $rsBlock = 1; break;
-			case QRConst::ERROR_CORRECT_LEVEL_Q: $rsBlock = 2; break;
-			case QRConst::ERROR_CORRECT_LEVEL_H: $rsBlock = 3; break;
-			default:
-				throw new QRCodeException('$typeNumber:'.$typeNumber.'/$errorCorrectLevel:'.$errorCorrectLevel);
+		if(!isset($this->RSBLOCK[$errorCorrectLevel])){
+			throw new QRCodeException('$typeNumber: '.$typeNumber.' / $errorCorrectLevel: '.$errorCorrectLevel.PHP_EOL.print_r($this->RSBLOCK, true));
 		}
 
-		$rsBlock = $this->BLOCK_TABLE[($typeNumber - 1) * 4 + $rsBlock];
+		$rsBlock = $this->BLOCK_TABLE[($typeNumber - 1) * 4 + $this->RSBLOCK[$errorCorrectLevel]];
 
 		$list = [];
 		$length = count($rsBlock) / 3;
@@ -136,5 +141,4 @@ class RSBlock{
 		return $list;
 	}
 
-
 }

+ 36 - 39
src/Util.php

@@ -11,11 +11,6 @@
 
 namespace codemasher\QRCode;
 
-use codemasher\QRCode\Polynomial;
-use codemasher\QRCode\QRCode;
-use codemasher\QRCode\QRConst;
-use codemasher\QRCode\QRCodeException;
-
 /**
  * Class Util
  */
@@ -87,39 +82,47 @@ class Util{
 		[6, 30, 58, 86, 114, 142, 170],
 	];
 
+	/**
+	 * @var array
+	 */
+	protected $ERROR_CORRECT_LEVEL = [
+		QRConst::ERROR_CORRECT_LEVEL_L => 0,
+		QRConst::ERROR_CORRECT_LEVEL_M => 1,
+		QRConst::ERROR_CORRECT_LEVEL_Q => 2,
+		QRConst::ERROR_CORRECT_LEVEL_H => 3,
+	];
+
+	/**
+	 * @var array
+	 */
+	protected $MODE = [
+		QRConst::MODE_NUMBER => 0,
+		QRConst::MODE_ALPHANUM => 1,
+		QRConst::MODE_BYTE => 2,
+		QRConst::MODE_KANJI => 3,
+	];
+
 	/**
 	 * @param int $typeNumber
 	 * @param int $mode
-	 * @param int $errorCorrectLevel
+	 * @param int $ecLevel
 	 *
 	 * @return mixed
 	 * @throws \codemasher\QRCode\QRCodeException
 	 */
-	public function getMaxLength($typeNumber, $mode, $errorCorrectLevel){
-		$_type = $typeNumber - 1;
-
-		switch($errorCorrectLevel){
-			case QRConst::ERROR_CORRECT_LEVEL_L: $_err = 0; break;
-			case QRConst::ERROR_CORRECT_LEVEL_M: $_err = 1; break;
-			case QRConst::ERROR_CORRECT_LEVEL_Q: $_err = 2; break;
-			case QRConst::ERROR_CORRECT_LEVEL_H: $_err = 3; break;
-			default:
-				throw new QRCodeException('$_err: '.$errorCorrectLevel);
+	public function getMaxLength($typeNumber, $mode, $ecLevel){
+
+		if(!isset($this->ERROR_CORRECT_LEVEL[$ecLevel])){
+			throw new QRCodeException('$_err: '.$ecLevel);
 		}
 
-		switch($mode){
-			case QRConst::MODE_NUMBER   : $_mode = 0; break;
-			case QRConst::MODE_ALPHANUM: $_mode = 1; break;
-			case QRConst::MODE_BYTE: $_mode = 2; break;
-			case QRConst::MODE_KANJI    : $_mode = 3; break;
-			default :
-				throw new QRCodeException('$_mode: '.$mode);
+		if(!isset($this->MODE[$mode])){
+			throw new QRCodeException('$_mode: '.$mode);
 		}
 
-		return $this->MAX_LENGTH[$_type][$_err][$_mode];
+		return $this->MAX_LENGTH[$typeNumber - 1][$this->ERROR_CORRECT_LEVEL[$ecLevel]][$this->MODE[$mode]];
 	}
 
-
 	/**
 	 * @param $s
 	 *
@@ -127,17 +130,11 @@ class Util{
 	 */
 	public function getMode($s){
 
-		if($this->isAlphaNum($s)){
-			if($this->isNumber($s)){
-				return QRConst::MODE_NUMBER;
-			}
-			return QRConst::MODE_ALPHANUM;
-		}
-		else if($this->isKanji($s)){
-			return QRConst::MODE_KANJI;
-		}
-		else{
-			return QRConst::MODE_BYTE;
+		switch(true){
+			case $this->isAlphaNum($s): return $this->isNumber($s) ? QRConst::MODE_NUMBER : QRConst::MODE_ALPHANUM;
+			case $this->isKanji($s)   : return QRConst::MODE_KANJI;
+			default:
+				return QRConst::MODE_BYTE;
 		}
 
 	}
@@ -262,9 +259,9 @@ class Util{
 	 */
 	public function hex2rgb($hex = 0x0){
 		return [
-				'r' => floor($hex / 65536),
-				'g' => floor($hex / 256) % 256,
-				'b' => $hex % 256,
+			'r' => floor($hex / 65536),
+			'g' => floor($hex / 256) % 256,
+			'b' => $hex % 256,
 		];
 	}