1
0

first commit

This commit is contained in:
2020-08-06 14:58:51 +08:00
commit 17096657dc
780 changed files with 92857 additions and 0 deletions

7
vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitfea605573c13ab63a5388432dc557bc5::getLoader();

7
vendor/bacon/bacon-qr-code/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
nbproject
.idea
.buildpath
.project
.DS_Store
.*.sw*
.*.un~

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 7.0
- hhvm
script: phpunit --bootstrap tests/bootstrap.php --configuration tests/phpunit.xml tests

22
vendor/bacon/bacon-qr-code/LICENSE vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2013, Ben 'DASPRiD' Scholzen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

37
vendor/bacon/bacon-qr-code/Module.php vendored Normal file
View File

@@ -0,0 +1,37 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
/**
* Module for generating QR codes.
*/
class Module implements AutoloaderProviderInterface
{
/**
* Get autoloader config.
*
* @return array
*/
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
}

24
vendor/bacon/bacon-qr-code/README.md vendored Normal file
View File

@@ -0,0 +1,24 @@
QR Code generator
=================
Master: [![Build Status](https://api.travis-ci.org/Bacon/BaconQrCode.png?branch=master)](http://travis-ci.org/Bacon/BaconQrCode)
Introduction
------------
BaconQrCode is a port of QR code portion of the ZXing library. It currently
only features the encoder part, but could later receive the decoder part as
well.
As the Reed Solomon codec implementation of the ZXing library performs quite
slow in PHP, it was exchanged with the implementation by Phil Karn.
Example usage
-------------
```php
$renderer = new \BaconQrCode\Renderer\Image\Png();
$renderer->setHeight(256);
$renderer->setWidth(256);
$writer = new \BaconQrCode\Writer($renderer);
$writer->writeFile('Hello World!', 'qrcode.png');
```

View File

@@ -0,0 +1,43 @@
<?php
// Generated by ZF2's ./bin/classmap_generator.php
return array(
'BaconQrCode\Common\AbstractEnum' => __DIR__ . '/src/BaconQrCode/Common/AbstractEnum.php',
'BaconQrCode\Common\BitArray' => __DIR__ . '/src/BaconQrCode/Common/BitArray.php',
'BaconQrCode\Common\BitMatrix' => __DIR__ . '/src/BaconQrCode/Common/BitMatrix.php',
'BaconQrCode\Common\BitUtils' => __DIR__ . '/src/BaconQrCode/Common/BitUtils.php',
'BaconQrCode\Common\CharacterSetEci' => __DIR__ . '/src/BaconQrCode/Common/CharacterSetEci.php',
'BaconQrCode\Common\EcBlock' => __DIR__ . '/src/BaconQrCode/Common/EcBlock.php',
'BaconQrCode\Common\EcBlocks' => __DIR__ . '/src/BaconQrCode/Common/EcBlocks.php',
'BaconQrCode\Common\ErrorCorrectionLevel' => __DIR__ . '/src/BaconQrCode/Common/ErrorCorrectionLevel.php',
'BaconQrCode\Common\FormatInformation' => __DIR__ . '/src/BaconQrCode/Common/FormatInformation.php',
'BaconQrCode\Common\Mode' => __DIR__ . '/src/BaconQrCode/Common/Mode.php',
'BaconQrCode\Common\ReedSolomonCodec' => __DIR__ . '/src/BaconQrCode/Common/ReedSolomonCodec.php',
'BaconQrCode\Common\Version' => __DIR__ . '/src/BaconQrCode/Common/Version.php',
'BaconQrCode\Encoder\BlockPair' => __DIR__ . '/src/BaconQrCode/Encoder/BlockPair.php',
'BaconQrCode\Encoder\ByteMatrix' => __DIR__ . '/src/BaconQrCode/Encoder/ByteMatrix.php',
'BaconQrCode\Encoder\Encoder' => __DIR__ . '/src/BaconQrCode/Encoder/Encoder.php',
'BaconQrCode\Encoder\MaskUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MaskUtil.php',
'BaconQrCode\Encoder\MatrixUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MatrixUtil.php',
'BaconQrCode\Encoder\QrCode' => __DIR__ . '/src/BaconQrCode/Encoder/QrCode.php',
'BaconQrCode\Exception\ExceptionInterface' => __DIR__ . '/src/BaconQrCode/Exception/ExceptionInterface.php',
'BaconQrCode\Exception\InvalidArgumentException' => __DIR__ . '/src/BaconQrCode/Exception/InvalidArgumentException.php',
'BaconQrCode\Exception\OutOfBoundsException' => __DIR__ . '/src/BaconQrCode/Exception/OutOfBoundsException.php',
'BaconQrCode\Exception\RuntimeException' => __DIR__ . '/src/BaconQrCode/Exception/RuntimeException.php',
'BaconQrCode\Exception\UnexpectedValueException' => __DIR__ . '/src/BaconQrCode/Exception/UnexpectedValueException.php',
'BaconQrCode\Exception\WriterException' => __DIR__ . '/src/BaconQrCode/Exception/WriterException.php',
'BaconQrCode\Renderer\Color\Cmyk' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Cmyk.php',
'BaconQrCode\Renderer\Color\ColorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Color/ColorInterface.php',
'BaconQrCode\Renderer\Color\Gray' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Gray.php',
'BaconQrCode\Renderer\Color\Rgb' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Rgb.php',
'BaconQrCode\Renderer\Image\AbstractRenderer' => __DIR__ . '/src/BaconQrCode/Renderer/Image/AbstractRenderer.php',
'BaconQrCode\Renderer\Image\Decorator\DecoratorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php',
'BaconQrCode\Renderer\Image\Decorator\FinderPattern' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/FinderPattern.php',
'BaconQrCode\Renderer\Image\Eps' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Eps.php',
'BaconQrCode\Renderer\Image\Png' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Png.php',
'BaconQrCode\Renderer\Image\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/RendererInterface.php',
'BaconQrCode\Renderer\Image\Svg' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Svg.php',
'BaconQrCode\Renderer\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/RendererInterface.php',
'BaconQrCode\Renderer\Text\Plain' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Plain.php',
'BaconQrCode\Renderer\Text\Html' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Html.php',
'BaconQrCode\Writer' => __DIR__ . '/src/BaconQrCode/Writer.php',
);

View File

@@ -0,0 +1,12 @@
<?php
return function ($class) {
static $map;
if (!$map) {
$map = include __DIR__ . '/autoload_classmap.php';
}
if (!isset($map[$class])) {
return false;
}
return include $map[$class];
};

View File

@@ -0,0 +1,2 @@
<?php
spl_autoload_register(include __DIR__ . '/autoload_function.php');

View File

@@ -0,0 +1,25 @@
{
"name": "bacon/bacon-qr-code",
"description": "BaconQrCode is a QR code generator for PHP.",
"license" : "BSD-2-Clause",
"homepage": "https://github.com/Bacon/BaconQrCode",
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-gd": "to generate QR code images"
},
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "http://www.dasprids.de",
"role": "Developer"
}
],
"autoload": {
"psr-0": {
"BaconQrCode": "src/"
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use BaconQrCode\Exception;
use ReflectionClass;
/**
* A general enum implementation until we got SplEnum.
*/
abstract class AbstractEnum
{
/**
* Default value.
*/
const __default = null;
/**
* Current value.
*
* @var mixed
*/
protected $value;
/**
* Cache of constants.
*
* @var array
*/
protected $constants;
/**
* Whether to handle values strict or not.
*
* @var boolean
*/
protected $strict;
/**
* Creates a new enum.
*
* @param mixed $initialValue
* @param boolean $strict
*/
public function __construct($initialValue = null, $strict = false)
{
$this->strict = $strict;
$this->change($initialValue);
}
/**
* Changes the value of the enum.
*
* @param mixed $value
* @return void
*/
public function change($value)
{
if (!in_array($value, $this->getConstList(), $this->strict)) {
throw new Exception\UnexpectedValueException('Value not a const in enum ' . get_class($this));
}
$this->value = $value;
}
/**
* Gets current value.
*
* @return mixed
*/
public function get()
{
return $this->value;
}
/**
* Gets all constants (possible values) as an array.
*
* @param boolean $includeDefault
* @return array
*/
public function getConstList($includeDefault = true)
{
if ($this->constants === null) {
$reflection = new ReflectionClass($this);
$this->constants = $reflection->getConstants();
}
if ($includeDefault) {
return $this->constants;
}
$constants = $this->constants;
unset($constants['__default']);
return $constants;
}
/**
* Gets the name of the enum.
*
* @return string
*/
public function __toString()
{
return array_search($this->value, $this->getConstList());
}
}

View File

@@ -0,0 +1,435 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use SplFixedArray;
/**
* A simple, fast array of bits.
*/
class BitArray
{
/**
* Bits represented as an array of integers.
*
* @var SplFixedArray
*/
protected $bits;
/**
* Size of the bit array in bits.
*
* @var integer
*/
protected $size;
/**
* Creates a new bit array with a given size.
*
* @param integer $size
*/
public function __construct($size = 0)
{
$this->size = $size;
$this->bits = new SplFixedArray(($this->size + 31) >> 3);
}
/**
* Gets the size in bits.
*
* @return integer
*/
public function getSize()
{
return $this->size;
}
/**
* Gets the size in bytes.
*
* @return integer
*/
public function getSizeInBytes()
{
return ($this->size + 7) >> 3;
}
/**
* Ensures that the array has a minimum capacity.
*
* @param integer $size
* @return void
*/
public function ensureCapacity($size)
{
if ($size > count($this->bits) << 5) {
$this->bits->setSize(($size + 31) >> 5);
}
}
/**
* Gets a specific bit.
*
* @param integer $i
* @return boolean
*/
public function get($i)
{
return ($this->bits[$i >> 5] & (1 << ($i & 0x1f))) !== 0;
}
/**
* Sets a specific bit.
*
* @param integer $i
* @return void
*/
public function set($i)
{
$this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f);
}
/**
* Flips a specific bit.
*
* @param integer $i
* @return void
*/
public function flip($i)
{
$this->bits[$i >> 5] ^= 1 << ($i & 0x1f);
}
/**
* Gets the next set bit position from a given position.
*
* @param integer $from
* @return integer
*/
public function getNextSet($from)
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = $from >> 5;
$currentBits = $this->bits[$bitsOffset];
$bitsLength = count($this->bits);
$currentBits &= ~((1 << ($from & 0x1f)) - 1);
while ($currentBits === 0) {
if (++$bitsOffset === $bitsLength) {
return $this->size;
}
$currentBits = $this->bits[$bitsOffset];
}
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
return $result > $this->size ? $this->size : $result;
}
/**
* Gets the next unset bit position from a given position.
*
* @param integer $from
* @return integer
*/
public function getNextUnset($from)
{
if ($from >= $this->size) {
return $this->size;
}
$bitsOffset = $from >> 5;
$currentBits = ~$this->bits[$bitsOffset];
$bitsLength = count($this->bits);
$currentBits &= ~((1 << ($from & 0x1f)) - 1);
while ($currentBits === 0) {
if (++$bitsOffset === $bitsLength) {
return $this->size;
}
$currentBits = ~$this->bits[$bitsOffset];
}
$result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits);
return $result > $this->size ? $this->size : $result;
}
/**
* Sets a bulk of bits.
*
* @param integer $i
* @param integer $newBits
* @return void
*/
public function setBulk($i, $newBits)
{
$this->bits[$i >> 5] = $newBits;
}
/**
* Sets a range of bits.
*
* @param integer $start
* @param integer $end
* @return void
* @throws Exception\InvalidArgumentException
*/
public function setRange($start, $end)
{
if ($end < $start) {
throw new Exception\InvalidArgumentException('End must be greater or equal to start');
}
if ($end === $start) {
return;
}
$end--;
$firstInt = $start >> 5;
$lastInt = $end >> 5;
for ($i = $firstInt; $i <= $lastInt; $i++) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1f;
$lastBit = $i < $lastInt ? 31 : $end & 0x1f;
if ($firstBit === 0 && $lastBit === 31) {
$mask = 0x7fffffff;
} else {
$mask = 0;
for ($j = $firstBit; $j < $lastBit; $j++) {
$mask |= 1 << $j;
}
}
$this->bits[$i] = $this->bits[$i] | $mask;
}
}
/**
* Clears the bit array, unsetting every bit.
*
* @return void
*/
public function clear()
{
$bitsLength = count($this->bits);
for ($i = 0; $i < $bitsLength; $i++) {
$this->bits[$i] = 0;
}
}
/**
* Checks if a range of bits is set or not set.
*
* @param integer $start
* @param integer $end
* @param integer $value
* @return boolean
* @throws Exception\InvalidArgumentException
*/
public function isRange($start, $end, $value)
{
if ($end < $start) {
throw new Exception\InvalidArgumentException('End must be greater or equal to start');
}
if ($end === $start) {
return;
}
$end--;
$firstInt = $start >> 5;
$lastInt = $end >> 5;
for ($i = $firstInt; $i <= $lastInt; $i++) {
$firstBit = $i > $firstInt ? 0 : $start & 0x1f;
$lastBit = $i < $lastInt ? 31 : $end & 0x1f;
if ($firstBit === 0 && $lastBit === 31) {
$mask = 0x7fffffff;
} else {
$mask = 0;
for ($j = $firstBit; $j <= $lastBit; $j++) {
$mask |= 1 << $j;
}
}
if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) {
return false;
}
}
return true;
}
/**
* Appends a bit to the array.
*
* @param boolean $bit
* @return void
*/
public function appendBit($bit)
{
$this->ensureCapacity($this->size + 1);
if ($bit) {
$this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f));
}
$this->size++;
}
/**
* Appends a number of bits (up to 32) to the array.
*
* @param integer $value
* @param integer $numBits
* @return void
* @throws Exception\InvalidArgumentException
*/
public function appendBits($value, $numBits)
{
if ($numBits < 0 || $numBits > 32) {
throw new Exception\InvalidArgumentException('Num bits must be between 0 and 32');
}
$this->ensureCapacity($this->size + $numBits);
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1);
}
}
/**
* Appends another bit array to this array.
*
* @param BitArray $other
* @return void
*/
public function appendBitArray(self $other)
{
$otherSize = $other->getSize();
$this->ensureCapacity($this->size + $other->getSize());
for ($i = 0; $i < $otherSize; $i++) {
$this->appendBit($other->get($i));
}
}
/**
* Makes an exclusive-or comparision on the current bit array.
*
* @param BitArray $other
* @return void
* @throws Exception\InvalidArgumentException
*/
public function xorBits(self $other)
{
$bitsLength = count($this->bits);
$otherBits = $other->getBitArray();
if ($bitsLength !== count($otherBits)) {
throw new Exception\InvalidArgumentException('Sizes don\'t match');
}
for ($i = 0; $i < $bitsLength; $i++) {
$this->bits[$i] = $this->bits[$i] ^ $otherBits[$i];
}
}
/**
* Converts the bit array to a byte array.
*
* @param integer $bitOffset
* @param integer $numBytes
* @return SplFixedArray
*/
public function toBytes($bitOffset, $numBytes)
{
$bytes = new SplFixedArray($numBytes);
for ($i = 0; $i < $numBytes; $i++) {
$byte = 0;
for ($j = 0; $j < 8; $j++) {
if ($this->get($bitOffset)) {
$byte |= 1 << (7 - $j);
}
$bitOffset++;
}
$bytes[$i] = $byte;
}
return $bytes;
}
/**
* Gets the internal bit array.
*
* @return SplFixedArray
*/
public function getBitArray()
{
return $this->bits;
}
/**
* Reverses the array.
*
* @return void
*/
public function reverse()
{
$newBits = new SplFixedArray(count($this->bits));
for ($i = 0; $i < $this->size; $i++) {
if ($this->get($this->size - $i - 1)) {
$newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f));
}
}
$this->bits = newBits;
}
/**
* Returns a string representation of the bit array.
*
* @return string
*/
public function __toString()
{
$result = '';
for ($i = 0; $i < $this->size; $i++) {
if (($i & 0x07) === 0) {
$result .= ' ';
}
$result .= $this->get($i) ? 'X' : '.';
}
return $result;
}
}

View File

@@ -0,0 +1,350 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use SplFixedArray;
/**
* Bit matrix.
*
* Represents a 2D matrix of bits. In function arguments below, and throughout
* the common module, x is the column position, and y is the row position. The
* ordering is always x, y. The origin is at the top-left.
*/
class BitMatrix
{
/**
* Width of the bit matrix.
*
* @var integer
*/
protected $width;
/**
* Height of the bit matrix.
*
* @var integer
*/
protected $height;
/**
* Size in bits of each individual row.
*
* @var integer
*/
protected $rowSize;
/**
* Bits representation.
*
* @var SplFixedArray
*/
protected $bits;
/**
* Creates a new bit matrix with given dimensions.
*
* @param integer $width
* @param integer|null $height
* @throws Exception\InvalidArgumentException
*/
public function __construct($width, $height = null)
{
if ($height === null) {
$height = $width;
}
if ($width < 1 || $height < 1) {
throw new Exception\InvalidArgumentException('Both dimensions must be greater than zero');
}
$this->width = $width;
$this->height = $height;
$this->rowSize = ($width + 31) >> 5;
$this->bits = new SplFixedArray($this->rowSize * $height);
}
/**
* Gets the requested bit, where true means black.
*
* @param integer $x
* @param integer $y
* @return boolean
*/
public function get($x, $y)
{
$offset = $y * $this->rowSize + ($x >> 5);
return (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1) !== 0;
}
/**
* Sets the given bit to true.
*
* @param integer $x
* @param integer $y
* @return void
*/
public function set($x, $y)
{
$offset = $y * $this->rowSize + ($x >> 5);
$this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f));
}
/**
* Flips the given bit.
*
* @param integer $x
* @param integer $y
* @return void
*/
public function flip($x, $y)
{
$offset = $y * $this->rowSize + ($x >> 5);
$this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f));
}
/**
* Clears all bits (set to false).
*
* @return void
*/
public function clear()
{
$max = count($this->bits);
for ($i = 0; $i < $max; $i++) {
$this->bits[$i] = 0;
}
}
/**
* Sets a square region of the bit matrix to true.
*
* @param integer $left
* @param integer $top
* @param integer $width
* @param integer $height
* @return void
*/
public function setRegion($left, $top, $width, $height)
{
if ($top < 0 || $left < 0) {
throw new Exception\InvalidArgumentException('Left and top must be non-negative');
}
if ($height < 1 || $width < 1) {
throw new Exception\InvalidArgumentException('Width and height must be at least 1');
}
$right = $left + $width;
$bottom = $top + $height;
if ($bottom > $this->height || $right > $this->width) {
throw new Exception\InvalidArgumentException('The region must fit inside the matrix');
}
for ($y = $top; $y < $bottom; $y++) {
$offset = $y * $this->rowSize;
for ($x = $left; $x < $right; $x++) {
$index = $offset + ($x >> 5);
$this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f));
}
}
}
/**
* A fast method to retrieve one row of data from the matrix as a BitArray.
*
* @param integer $y
* @param BitArray $row
* @return BitArray
*/
public function getRow($y, BitArray $row = null)
{
if ($row === null || $row->getSize() < $this->width) {
$row = new BitArray($this->width);
}
$offset = $y * $this->rowSize;
for ($x = 0; $x < $this->rowSize; $x++) {
$row->setBulk($x << 5, $this->bits[$offset + $x]);
}
return $row;
}
/**
* Sets a row of data from a BitArray.
*
* @param integer $y
* @param BitArray $row
* @return void
*/
public function setRow($y, BitArray $row)
{
$bits = $row->getBitArray();
for ($i = 0; $i < $this->rowSize; $i++) {
$this->bits[$y * $this->rowSize + $i] = $bits[$i];
}
}
/**
* This is useful in detecting the enclosing rectangle of a 'pure' barcode.
*
* @return SplFixedArray
*/
public function getEnclosingRectangle()
{
$left = $this->width;
$top = $this->height;
$right = -1;
$bottom = -1;
for ($y = 0; $y < $this->height; $y++) {
for ($x32 = 0; $x32 < $this->rowSize; $x32++) {
$bits = $this->bits[$y * $this->rowSize + $x32];
if ($bits !== 0) {
if ($y < $top) {
$top = $y;
}
if ($y > $bottom) {
$bottom = $y;
}
if ($x32 * 32 < $left) {
$bit = 0;
while (($bits << (31 - $bit)) === 0) {
$bit++;
}
if (($x32 * 32 + $bit) < $left) {
$left = $x32 * 32 + $bit;
}
}
}
if ($x32 * 32 + 31 > $right) {
$bit = 31;
while (BitUtils::unsignedRightShift($bits, $bit) === 0) {
$bit--;
}
if (($x32 * 32 + $bit) > $right) {
$right = $x32 * 32 + $bit;
}
}
}
}
$width = $right - $left;
$height = $bottom - $top;
if ($width < 0 || $height < 0) {
return null;
}
return SplFixedArray::fromArray(array($left, $top, $width, $height), false);
}
/**
* Gets the most top left set bit.
*
* This is useful in detecting a corner of a 'pure' barcode.
*
* @return SplFixedArray
*/
public function getTopLeftOnBit()
{
$bitsOffset = 0;
while ($bitsOffset < count($this->bits) && $this->bits[$bitsOffset] === 0) {
$bitsOffset++;
}
if ($bitsOffset === count($this->bits)) {
return null;
}
$x = intval($bitsOffset / $this->rowSize);
$y = ($bitsOffset % $this->rowSize) << 5;
$bits = $this->bits[$bitsOffset];
$bit = 0;
while (($bits << (31 - $bit)) === 0) {
$bit++;
}
$x += $bit;
return SplFixedArray::fromArray(array($x, $y), false);
}
/**
* Gets the most bottom right set bit.
*
* This is useful in detecting a corner of a 'pure' barcode.
*
* @return SplFixedArray
*/
public function getBottomRightOnBit()
{
$bitsOffset = count($this->bits) - 1;
while ($bitsOffset >= 0 && $this->bits[$bitsOffset] === 0) {
$bitsOffset--;
}
if ($bitsOffset < 0) {
return null;
}
$x = intval($bitsOffset / $this->rowSize);
$y = ($bitsOffset % $this->rowSize) << 5;
$bits = $this->bits[$bitsOffset];
$bit = 0;
while (BitUtils::unsignedRightShift($bits, $bit) === 0) {
$bit--;
}
$x += $bit;
return SplFixedArray::fromArray(array($x, $y), false);
}
/**
* Gets the width of the matrix,
*
* @return integer
*/
public function getWidth()
{
return $this->width;
}
/**
* Gets the height of the matrix.
*
* @return integer
*/
public function getHeight()
{
return $this->height;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* General bit utilities.
*
* All utility methods are based on 32-bit integers and also work on 64-bit
* systems.
*/
class BitUtils
{
/**
* Performs an unsigned right shift.
*
* This is the same as the unsigned right shift operator ">>>" in other
* languages.
*
* @param integer $a
* @param integer $b
* @return integer
*/
public static function unsignedRightShift($a, $b)
{
return (
$a >= 0
? $a >> $b
: (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1))
);
}
/**
* Gets the number of trailing zeros.
*
* @param integer $i
* @return integer
*/
public static function numberOfTrailingZeros($i)
{
$lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1');
return $lastPos === false ? 32 : 31 - $lastPos;
}
}

View File

@@ -0,0 +1,134 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Encapsulates a Character Set ECI, according to "Extended Channel
* Interpretations" 5.3.1.1 of ISO 18004.
*/
class CharacterSetEci extends AbstractEnum
{
/**#@+
* Character set constants.
*/
const CP437 = 0;
const ISO8859_1 = 1;
const ISO8859_2 = 4;
const ISO8859_3 = 5;
const ISO8859_4 = 6;
const ISO8859_5 = 7;
const ISO8859_6 = 8;
const ISO8859_7 = 9;
const ISO8859_8 = 10;
const ISO8859_9 = 11;
const ISO8859_10 = 12;
const ISO8859_11 = 13;
const ISO8859_12 = 14;
const ISO8859_13 = 15;
const ISO8859_14 = 16;
const ISO8859_15 = 17;
const ISO8859_16 = 18;
const SJIS = 20;
const CP1250 = 21;
const CP1251 = 22;
const CP1252 = 23;
const CP1256 = 24;
const UNICODE_BIG_UNMARKED = 25;
const UTF8 = 26;
const ASCII = 27;
const BIG5 = 28;
const GB18030 = 29;
const EUC_KR = 30;
/**#@-*/
/**
* Map between character names and their ECI values.
*
* @var array
*/
protected static $nameToEci = array(
'ISO-8859-1' => self::ISO8859_1,
'ISO-8859-2' => self::ISO8859_2,
'ISO-8859-3' => self::ISO8859_3,
'ISO-8859-4' => self::ISO8859_4,
'ISO-8859-5' => self::ISO8859_5,
'ISO-8859-6' => self::ISO8859_6,
'ISO-8859-7' => self::ISO8859_7,
'ISO-8859-8' => self::ISO8859_8,
'ISO-8859-9' => self::ISO8859_9,
'ISO-8859-10' => self::ISO8859_10,
'ISO-8859-11' => self::ISO8859_11,
'ISO-8859-12' => self::ISO8859_12,
'ISO-8859-13' => self::ISO8859_13,
'ISO-8859-14' => self::ISO8859_14,
'ISO-8859-15' => self::ISO8859_15,
'ISO-8859-16' => self::ISO8859_16,
'SHIFT-JIS' => self::SJIS,
'WINDOWS-1250' => self::CP1250,
'WINDOWS-1251' => self::CP1251,
'WINDOWS-1252' => self::CP1252,
'WINDOWS-1256' => self::CP1256,
'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
'UTF-8' => self::UTF8,
'ASCII' => self::ASCII,
'GBK' => self::GB18030,
'EUC-KR' => self::EUC_KR,
);
/**
* Additional possible values for character sets.
*
* @var array
*/
protected $additionalValues = array(
self::CP437 => 2,
self::ASCII => 170,
);
/**
* Gets character set ECI by value.
*
* @param string $name
* @return CharacterSetEci|null
*/
public static function getCharacterSetECIByValue($value)
{
if ($value < 0 || $value >= 900) {
throw new Exception\InvalidArgumentException('Value must be between 0 and 900');
}
if (false !== ($key = array_search($value, self::$additionalValues))) {
$value = $key;
}
try {
return new self($value);
} catch (Exception\UnexpectedValueException $e) {
return null;
}
}
/**
* Gets character set ECI by name.
*
* @param string $name
* @return CharacterSetEci|null
*/
public static function getCharacterSetECIByName($name)
{
$name = strtoupper($name);
if (isset(self::$nameToEci[$name])) {
return new self(self::$nameToEci[$name]);
}
return null;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Encapsualtes the parameters for one error-correction block in one symbol
* version. This includes the number of data codewords, and the number of times
* a block with these parameters is used consecutively in the QR code version's
* format.
*/
class EcBlock
{
/**
* How many times the block is used.
*
* @var integer
*/
protected $count;
/**
* Number of data codewords.
*
* @var integer
*/
protected $dataCodewords;
/**
* Creates a new EC block.
*
* @param integer $count
* @param integer $dataCodewords
*/
public function __construct($count, $dataCodewords)
{
$this->count = $count;
$this->dataCodewords = $dataCodewords;
}
/**
* Returns how many times the block is used.
*
* @return integer
*/
public function getCount()
{
return $this->count;
}
/**
* Returns the number of data codewords.
*
* @return integer
*/
public function getDataCodewords()
{
return $this->dataCodewords;
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use SplFixedArray;
/**
* Encapsulates a set of error-correction blocks in one symbol version. Most
* versions will use blocks of differing sizes within one version, so, this
* encapsulates the parameters for each set of blocks. It also holds the number
* of error-correction codewords per block since it will be the same across all
* blocks within one version.
*/
class EcBlocks
{
/**
* Number of EC codewords per block.
*
* @var integer
*/
protected $ecCodewordsPerBlock;
/**
* List of EC blocks.
*
* @var SplFixedArray
*/
protected $ecBlocks;
/**
* Creates a new EC blocks instance.
*
* @param integer $ecCodewordsPerBlock
* @param EcBlock $ecb1
* @param EcBlock|null $ecb2
*/
public function __construct($ecCodewordsPerBlock, EcBlock $ecb1, EcBlock $ecb2 = null)
{
$this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
$this->ecBlocks = new SplFixedArray($ecb2 === null ? 1 : 2);
$this->ecBlocks[0] = $ecb1;
if ($ecb2 !== null) {
$this->ecBlocks[1] = $ecb2;
}
}
/**
* Gets the number of EC codewords per block.
*
* @return integer
*/
public function getEcCodewordsPerBlock()
{
return $this->ecCodewordsPerBlock;
}
/**
* Gets the total number of EC block appearances.
*
* @return integer
*/
public function getNumBlocks()
{
$total = 0;
foreach ($this->ecBlocks as $ecBlock) {
$total += $ecBlock->getCount();
}
return $total;
}
/**
* Gets the total count of EC codewords.
*
* @return integer
*/
public function getTotalEcCodewords()
{
return $this->ecCodewordsPerBlock * $this->getNumBlocks();
}
/**
* Gets the EC blocks included in this collection.
*
* @return SplFixedArray
*/
public function getEcBlocks()
{
return $this->ecBlocks;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Enum representing the four error correction levels.
*/
class ErrorCorrectionLevel extends AbstractEnum
{
/**
* Level L, ~7% correction.
*/
const L = 0x1;
/**
* Level M, ~15% correction.
*/
const M = 0x0;
/**
* Level Q, ~25% correction.
*/
const Q = 0x3;
/**
* Level H, ~30% correction.
*/
const H = 0x2;
/**
* Gets the ordinal of this enumeration constant.
*
* @return integer
*/
public function getOrdinal()
{
switch ($this->value) {
case self::L:
return 0;
break;
case self::M:
return 1;
break;
case self::Q:
return 2;
break;
case self::H:
return 3;
break;
}
}
}

View File

@@ -0,0 +1,236 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Encapsulates a QR Code's format information, including the data mask used and
* error correction level.
*/
class FormatInformation
{
/**
* Mask for format information.
*/
const FORMAT_INFO_MASK_QR = 0x5412;
/**
* Lookup table for decoding format information.
*
* See ISO 18004:2006, Annex C, Table C.1
*
* @var array
*/
protected static $formatInfoDecodeLookup = array(
array(0x5412, 0x00),
array(0x5125, 0x01),
array(0x5e7c, 0x02),
array(0x5b4b, 0x03),
array(0x45f9, 0x04),
array(0x40ce, 0x05),
array(0x4f97, 0x06),
array(0x4aa0, 0x07),
array(0x77c4, 0x08),
array(0x72f3, 0x09),
array(0x7daa, 0x0a),
array(0x789d, 0x0b),
array(0x662f, 0x0c),
array(0x6318, 0x0d),
array(0x6c41, 0x0e),
array(0x6976, 0x0f),
array(0x1689, 0x10),
array(0x13be, 0x11),
array(0x1ce7, 0x12),
array(0x19d0, 0x13),
array(0x0762, 0x14),
array(0x0255, 0x15),
array(0x0d0c, 0x16),
array(0x083b, 0x17),
array(0x355f, 0x18),
array(0x3068, 0x19),
array(0x3f31, 0x1a),
array(0x3a06, 0x1b),
array(0x24b4, 0x1c),
array(0x2183, 0x1d),
array(0x2eda, 0x1e),
array(0x2bed, 0x1f),
);
/**
* Offset i holds the number of 1 bits in the binary representation of i.
*
* @var array
*/
protected static $bitsSetInHalfByte = array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
/**
* Error correction level.
*
* @var ErrorCorrectionLevel
*/
protected $ecLevel;
/**
* Data mask.
*
* @var integer
*/
protected $dataMask;
/**
* Creates a new format information instance.
*
* @param integer $formatInfo
*/
protected function __construct($formatInfo)
{
$this->ecLevel = new ErrorCorrectionLevel(($formatInfo >> 3) & 0x3);
$this->dataMask = $formatInfo & 0x7;
}
/**
* Checks how many bits are different between two integers.
*
* @param integer $a
* @param integer $b
* @return integer
*/
public static function numBitsDiffering($a, $b)
{
$a ^= $b;
return (
self::$bitsSetInHalfByte[$a & 0xf]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 4) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 8) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 12) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 16) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 20) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 24) & 0xf)]
+ self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 28) & 0xf)]
);
}
/**
* Decodes format information.
*
* @param integer $maskedFormatInfo1
* @param integer $maskedFormatInfo2
* @return FormatInformation|null
*/
public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
{
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
if ($formatInfo !== null) {
return $formatInfo;
}
// Should return null, but, some QR codes apparently do not mask this
// info. Try again by actually masking the pattern first.
return self::doDecodeFormatInformation(
$maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR,
$maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR
);
}
/**
* Internal method for decoding format information.
*
* @param integer $maskedFormatInfo1
* @param integer $maskedFormatInfo2
* @return FormatInformation|null
*/
protected static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2)
{
$bestDifference = PHP_INT_MAX;
$bestFormatInfo = 0;
foreach (self::$formatInfoDecodeLookup as $decodeInfo) {
$targetInfo = $decodeInfo[0];
if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) {
// Found an exact match
return new self($decodeInfo[1]);
}
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference;
}
if ($maskedFormatInfo1 !== $maskedFormatInfo2) {
// Also try the other option
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
if ($bitsDifference < $bestDifference) {
$bestFormatInfo = $decodeInfo[1];
$bestDifference = $bitsDifference;
}
}
}
// Hamming distance of the 32 masked codes is 7, by construction, so
// <= 3 bits differing means we found a match.
if ($bestDifference <= 3) {
return new self($bestFormatInfo);
}
return null;
}
/**
* Gets the error correction level.
*
* @return ErrorCorrectionLevel
*/
public function getErrorCorrectionLevel()
{
return $this->ecLevel;
}
/**
* Gets the data mask.
*
* @return integer
*/
public function getDataMask()
{
return $this->dataMask;
}
/**
* Hashes the code of the EC level.
*
* @return integer
*/
public function hashCode()
{
return ($this->ecLevel->get() << 3) | $this->dataMask;
}
/**
* Verifies if this instance equals another one.
*
* @param mixed $other
* @return boolean
*/
public function equals($other) {
if (!$other instanceof self) {
return false;
}
return (
$this->ecLevel->get() === $other->getErrorCorrectionLevel()->get()
&& $this->dataMask === $other->getDataMask()
);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
/**
* Enum representing various modes in which data can be encoded to bits.
*/
class Mode extends AbstractEnum
{
/**#@+
* Mode constants.
*/
const TERMINATOR = 0x0;
const NUMERIC = 0x1;
const ALPHANUMERIC = 0x2;
const STRUCTURED_APPEND = 0x3;
const BYTE = 0x4;
const ECI = 0x7;
const KANJI = 0x8;
const FNC1_FIRST_POSITION = 0x5;
const FNC1_SECOND_POSITION = 0x9;
const HANZI = 0xd;
/**#@-*/
/**
* Character count bits for each version.
*
* @var array
*/
protected static $characterCountBitsForVersions = array(
self::TERMINATOR => array(0, 0, 0),
self::NUMERIC => array(10, 12, 14),
self::ALPHANUMERIC => array(9, 11, 13),
self::STRUCTURED_APPEND => array(0, 0, 0),
self::BYTE => array(8, 16, 16),
self::ECI => array(0, 0, 0),
self::KANJI => array(8, 10, 12),
self::FNC1_FIRST_POSITION => array(0, 0, 0),
self::FNC1_SECOND_POSITION => array(0, 0, 0),
self::HANZI => array(8, 10, 12),
);
/**
* Gets the number of bits used in a specific QR code version.
*
* @param Version $version
* @return integer
*/
public function getCharacterCountBits(Version $version)
{
$number = $version->getVersionNumber();
if ($number <= 9) {
$offset = 0;
} elseif ($number <= 26) {
$offset = 1;
} else {
$offset = 2;
}
return self::$characterCountBitsForVersions[$this->value][$offset];
}
}

View File

@@ -0,0 +1,476 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use BaconQrCode\Exception;
use SplFixedArray;
/**
* Reed-Solomon codec for 8-bit characters.
*
* Based on libfec by Phil Karn, KA9Q.
*/
class ReedSolomonCodec
{
/**
* Symbol size in bits.
*
* @var integer
*/
protected $symbolSize;
/**
* Block size in symbols.
*
* @var integer
*/
protected $blockSize;
/**
* First root of RS code generator polynomial, index form.
*
* @var integer
*/
protected $firstRoot;
/**
* Primitive element to generate polynomial roots, index form.
*
* @var integer
*/
protected $primitive;
/**
* Prim-th root of 1, index form.
*
* @var integer
*/
protected $iPrimitive;
/**
* RS code generator polynomial degree (number of roots).
*
* @var integer
*/
protected $numRoots;
/**
* Padding bytes at front of shortened block.
*
* @var integer
*/
protected $padding;
/**
* Log lookup table.
*
* @var SplFixedArray
*/
protected $alphaTo;
/**
* Anti-Log lookup table.
*
* @var SplFixedArray
*/
protected $indexOf;
/**
* Generator polynomial.
*
* @var SplFixedArray
*/
protected $generatorPoly;
/**
* Creates a new reed solomon instance.
*
* @param integer $symbolSize
* @param integer $gfPoly
* @param integer $firstRoot
* @param integer $primitive
* @param integer $numRoots
* @param integer $padding
* @throws Exception\InvalidArgumentException
* @throws Exception\RuntimeException
*/
public function __construct($symbolSize, $gfPoly, $firstRoot, $primitive, $numRoots, $padding)
{
if ($symbolSize < 0 || $symbolSize > 8) {
throw new Exception\InvalidArgumentException('Symbol size must be between 0 and 8');
}
if ($firstRoot < 0 || $firstRoot >= (1 << $symbolSize)) {
throw new Exception\InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize));
}
if ($numRoots < 0 || $numRoots >= (1 << $symbolSize)) {
throw new Exception\InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize));
}
if ($padding < 0 || $padding >= ((1 << $symbolSize) - 1 - $numRoots)) {
throw new Exception\InvalidArgumentException('Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots));
}
$this->symbolSize = $symbolSize;
$this->blockSize = (1 << $symbolSize) - 1;
$this->padding = $padding;
$this->alphaTo = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false);
$this->indexOf = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0), false);
// Generate galous field lookup table
$this->indexOf[0] = $this->blockSize;
$this->alphaTo[$this->blockSize] = 0;
$sr = 1;
for ($i = 0; $i < $this->blockSize; $i++) {
$this->indexOf[$sr] = $i;
$this->alphaTo[$i] = $sr;
$sr <<= 1;
if ($sr & (1 << $symbolSize)) {
$sr ^= $gfPoly;
}
$sr &= $this->blockSize;
}
if ($sr !== 1) {
throw new Exception\RuntimeException('Field generator polynomial is not primitive');
}
// Form RS code generator polynomial from its roots
$this->generatorPoly = SplFixedArray::fromArray(array_fill(0, $numRoots + 1, 0), false);
$this->firstRoot = $firstRoot;
$this->primitive = $primitive;
$this->numRoots = $numRoots;
// Find prim-th root of 1, used in decoding
for ($iPrimitive = 1; ($iPrimitive % $primitive) !== 0; $iPrimitive += $this->blockSize);
$this->iPrimitive = intval($iPrimitive / $primitive);
$this->generatorPoly[0] = 1;
for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; $i++, $root += $primitive) {
$this->generatorPoly[$i + 1] = 1;
for ($j = $i; $j > 0; $j--) {
if ($this->generatorPoly[$j] !== 0) {
$this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root)];
} else {
$this->generatorPoly[$j] = $this->generatorPoly[$j - 1];
}
}
$this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)];
}
// Convert generator poly to index form for quicker encoding
for ($i = 0; $i <= $numRoots; $i++) {
$this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]];
}
}
/**
* Encodes data and writes result back into parity array.
*
* @param SplFixedArray $data
* @param SplFixedArray $parity
* @return void
*/
public function encode(SplFixedArray $data, SplFixedArray $parity)
{
for ($i = 0; $i < $this->numRoots; $i++) {
$parity[$i] = 0;
}
$iterations = $this->blockSize - $this->numRoots - $this->padding;
for ($i = 0; $i < $iterations; $i++) {
$feedback = $this->indexOf[$data[$i] ^ $parity[0]];
if ($feedback !== $this->blockSize) {
// Feedback term is non-zero
$feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback);
for ($j = 1; $j < $this->numRoots; $j++) {
$parity[$j] = $parity[$j] ^ $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j])];
}
}
for ($j = 0; $j < $this->numRoots - 1; $j++) {
$parity[$j] = $parity[$j + 1];
}
if ($feedback !== $this->blockSize) {
$parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])];
} else {
$parity[$this->numRoots - 1] = 0;
}
}
}
/**
* Decodes received data.
*
* @param SplFixedArray $data
* @param SplFixedArray|null $erasures
* @return null|integer
*/
public function decode(SplFixedArray $data, SplFixedArray $erasures = null)
{
// This speeds up the initialization a bit.
$numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0), false);
$numRoots = SplFixedArray::fromArray(array_fill(0, $this->numRoots, 0), false);
$lambda = clone $numRootsPlusOne;
$b = clone $numRootsPlusOne;
$t = clone $numRootsPlusOne;
$omega = clone $numRootsPlusOne;
$root = clone $numRoots;
$loc = clone $numRoots;
$numErasures = ($erasures !== null ? count($erasures) : 0);
// Form the Syndromes; i.e., evaluate data(x) at roots of g(x)
$syndromes = SplFixedArray::fromArray(array_fill(0, $this->numRoots, $data[0]), false);
for ($i = 1; $i < $this->blockSize - $this->padding; $i++) {
for ($j = 0; $j < $this->numRoots; $j++) {
if ($syndromes[$j] === 0) {
$syndromes[$j] = $data[$i];
} else {
$syndromes[$j] = $data[$i] ^ $this->alphaTo[
$this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive)
];
}
}
}
// Convert syndromes to index form, checking for nonzero conditions
$syndromeError = 0;
for ($i = 0; $i < $this->numRoots; $i++) {
$syndromeError |= $syndromes[$i];
$syndromes[$i] = $this->indexOf[$syndromes[$i]];
}
if (!$syndromeError) {
// If syndrome is zero, data[] is a codeword and there are no errors
// to correct, so return data[] unmodified.
return 0;
}
$lambda[0] = 1;
if ($numErasures > 0) {
// Init lambda to be the erasure locator polynomial
$lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))];
for ($i = 1; $i < $numErasures; $i++) {
$u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i]));
for ($j = $i + 1; $j > 0; $j--) {
$tmp = $this->indexOf[$lambda[$j - 1]];
if ($tmp !== $this->blockSize) {
$lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)];
}
}
}
}
for ($i = 0; $i <= $this->numRoots; $i++) {
$b[$i] = $this->indexOf[$lambda[$i]];
}
// Begin Berlekamp-Massey algorithm to determine error+erasure locator
// polynomial
$r = $numErasures;
$el = $numErasures;
while (++$r <= $this->numRoots) {
// Compute discrepancy at the r-th step in poly form
$discrepancyR = 0;
for ($i = 0; $i < $r; $i++) {
if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) {
$discrepancyR ^= $this->alphaTo[$this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1])];
}
}
$discrepancyR = $this->indexOf[$discrepancyR];
if ($discrepancyR === $this->blockSize) {
$tmp = $b->toArray();
array_unshift($tmp, $this->blockSize);
array_pop($tmp);
$b = SplFixedArray::fromArray($tmp, false);
} else {
$t[0] = $lambda[0];
for ($i = 0; $i < $this->numRoots; $i++) {
if ($b[$i] !== $this->blockSize) {
$t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])];
} else {
$t[$i + 1] = $lambda[$i + 1];
}
}
if (2 * $el <= $r + $numErasures - 1) {
$el = $r + $numErasures - $el;
for ($i = 0; $i <= $this->numRoots; $i++) {
$b[$i] = (
$lambda[$i] === 0
? $this->blockSize
: $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize)
);
}
} else {
$tmp = $b->toArray();
array_unshift($tmp, $this->blockSize);
array_pop($tmp);
$b = SplFixedArray::fromArray($tmp, false);
}
$lambda = clone $t;
}
}
// Convert lambda to index form and compute deg(lambda(x))
$degLambda = 0;
for ($i = 0; $i <= $this->numRoots; $i++) {
$lambda[$i] = $this->indexOf[$lambda[$i]];
if ($lambda[$i] !== $this->blockSize) {
$degLambda = $i;
}
}
// Find roots of the error+erasure locator polynomial by Chien search.
$reg = clone $lambda;
$reg[0] = 0;
$count = 0;
for ($i = 1, $k = $this->iPrimitive - 1; $i <= $this->blockSize; $i++, $k = $this->modNn($k + $this->iPrimitive)) {
$q = 1;
for ($j = $degLambda; $j > 0; $j--) {
if ($reg[$j] !== $this->blockSize) {
$reg[$j] = $this->modNn($reg[$j] + $j);
$q ^= $this->alphaTo[$reg[$j]];
}
}
if ($q !== 0) {
// Not a root
continue;
}
// Store root (index-form) and error location number
$root[$count] = $i;
$loc[$count] = $k;
if (++$count === $degLambda) {
break;
}
}
if ($degLambda !== $count) {
// deg(lambda) unequal to number of roots: uncorreactable error
// detected
return null;
}
// Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo
// x**numRoots). In index form. Also find deg(omega).
$degOmega = $degLambda - 1;
for ($i = 0; $i <= $degOmega; $i++) {
$tmp = 0;
for ($j = $i; $j >= 0; $j--) {
if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) {
$tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])];
}
}
$omega[$i] = $this->indexOf[$tmp];
}
// Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
// inv(X(l))**(firstRoot-1) and den = lambda_pr(inv(X(l))) all in poly
// form.
for ($j = $count - 1; $j >= 0; $j--) {
$num1 = 0;
for ($i = $degOmega; $i >= 0; $i--) {
if ($omega[$i] !== $this->blockSize) {
$num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])];
}
}
$num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)];
$den = 0;
// lambda[i+1] for i even is the formal derivativelambda_pr of
// lambda[i]
for ($i = min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) {
if ($lambda[$i + 1] !== $this->blockSize) {
$den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])];
}
}
// Apply error to data
if ($num1 !== 0 && $loc[$j] >= $this->padding) {
$data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ (
$this->alphaTo[
$this->modNn(
$this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den]
)
]
);
}
}
if ($erasures !== null) {
if (count($erasures) < $count) {
$erasures->setSize($count);
}
for ($i = 0; $i < $count; $i++) {
$erasures[$i] = $loc[$i];
}
}
return $count;
}
/**
* Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow
* divide.
*
* @param itneger $x
* @return integer
*/
protected function modNn($x)
{
while ($x >= $this->blockSize) {
$x -= $this->blockSize;
$x = ($x >> $this->symbolSize) + ($x & $this->blockSize);
}
return $x;
}
}

View File

@@ -0,0 +1,687 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use SplFixedArray;
/**
* Version representation.
*/
class Version
{
/**
* Version decode information.
*
* @var array
*/
protected static $versionDecodeInfo = array(
0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
0x27541, 0x28c69
);
/**
* Cached version instances.
*
* @var array
*/
protected static $versions = array();
/**
* Version number of this version.
*
* @var integer
*/
protected $versionNumber;
/**
* Alignment pattern centers.
*
* @var SplFixedArray
*/
protected $alignmentPatternCenters;
/**
* Error correction blocks.
*
* @var SplFixedArray
*/
protected $errorCorrectionBlocks;
/**
* Total number of codewords.
*
* @var integer
*/
protected $totalCodewords;
/**
* Creates a new version.
*
* @param integer $versionNumber
* @param SplFixedArray $alignmentPatternCenters
* @param SplFixedArray $ecBlocks
*/
protected function __construct(
$versionNumber,
SplFixedArray $alignmentPatternCenters,
SplFixedArray $ecBlocks
) {
$this->versionNumber = $versionNumber;
$this->alignmentPatternCenters = $alignmentPatternCenters;
$this->errorCorrectionBlocks = $ecBlocks;
$totalCodewords = 0;
$ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock();
foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) {
$totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
}
$this->totalCodewords = $totalCodewords;
}
/**
* Gets the version number.
*
* @return integer
*/
public function getVersionNumber()
{
return $this->versionNumber;
}
/**
* Gets the alignment pattern centers.
*
* @return SplFixedArray
*/
public function getAlignmentPatternCenters()
{
return $this->alignmentPatternCenters;
}
/**
* Gets the total number of codewords.
*
* @return integer
*/
public function getTotalCodewords()
{
return $this->totalCodewords;
}
/**
* Gets the dimension for the current version.
*
* @return integer
*/
public function getDimensionForVersion()
{
return 17 + 4 * $this->versionNumber;
}
/**
* Gets the number of EC blocks for a specific EC level.
*
* @param ErrorCorrectionLevel $ecLevel
* @return integer
*/
public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel)
{
return $this->errorCorrectionBlocks[$ecLevel->getOrdinal()];
}
/**
* Gets a provisional version number for a specific dimension.
*
* @param integer $dimension
* @return Version
* @throws Exception\InvalidArgumentException
*/
public static function getProvisionalVersionForDimension($dimension)
{
if ($dimension % 4 !== 1) {
throw new Exception\InvalidArgumentException('Dimension is not 1 mod 4');
}
return self::getVersionForNumber(($dimension - 17) >> 2);
}
/**
* Gets a version instance for a specific version number.
*
* @param integer $versionNumber
* @return Version
* @throws Exception\InvalidArgumentException
*/
public static function getVersionForNumber($versionNumber)
{
if ($versionNumber < 1 || $versionNumber > 40) {
throw new Exception\InvalidArgumentException('Version number must be between 1 and 40');
}
if (!isset(self::$versions[$versionNumber])) {
self::buildVersion($versionNumber);
}
return self::$versions[$versionNumber - 1];
}
/**
* Decodes version information from an integer and returns the version.
*
* @param integer $versionBits
* @return Version|null
*/
public static function decodeVersionInformation($versionBits)
{
$bestDifference = PHP_INT_MAX;
$bestVersion = 0;
foreach (self::$versionDecodeInfo as $i => $targetVersion) {
if ($targetVersion === $versionBits) {
return self::getVersionForNumber($i + 7);
}
$bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
if ($bitsDifference < $bestDifference) {
$bestVersion = $i + 7;
$bestDifference = $bitsDifference;
}
}
if ($bestDifference <= 3) {
return self::getVersionForNumber($bestVersion);
}
return null;
}
/**
* Builds the function pattern for the current version.
*
* @return BitMatrix
*/
public function buildFunctionPattern()
{
$dimension = $this->getDimensionForVersion();
$bitMatrix = new BitMatrix($dimension);
// Top left finder pattern + separator + format
$bitMatrix->setRegion(0, 0, 9, 9);
// Top right finder pattern + separator + format
$bitMatrix->setRegion($dimension - 8, 0, 8, 9);
// Bottom left finder pattern + separator + format
$bitMatrix->setRegion(0, $dimension - 8, 9, 8);
// Alignment patterns
$max = count($this->alignmentPatternCenters);
for ($x = 0; $x < $max; $x++) {
$i = $this->alignmentPatternCenters[$x] - 2;
for ($y = 0; $y < $max; $y++) {
if (($x === 0 && ($y === 0 || $y === $max - 1)) || ($x === $max - 1 && $y === 0)) {
// No alignment patterns near the three finder paterns
continue;
}
$bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
}
}
// Vertical timing pattern
$bitMatrix->setRegion(6, 9, 1, $dimension - 17);
// Horizontal timing pattern
$bitMatrix->setRegion(9, 6, $dimension - 17, 1);
if ($this->versionNumber > 6) {
// Version info, top right
$bitMatrix->setRegion($dimension - 11, 0, 3, 6);
// Version info, bottom left
$bitMatrix->setRegion(0, $dimension - 11, 6, 3);
}
return $bitMatrix;
}
/**
* Returns a string representation for the version.
*
* @return string
*/
public function __toString()
{
return (string) $this->versionNumber;
}
/**
* Build and cache a specific version.
*
* See ISO 18004:2006 6.5.1 Table 9.
*
* @param integer $versionNumber
* @return void
*/
protected static function buildVersion($versionNumber)
{
switch ($versionNumber) {
case 1:
$patterns = array();
$ecBlocks = array(
new EcBlocks(7, new EcBlock(1, 19)),
new EcBlocks(10, new EcBlock(1, 16)),
new EcBlocks(13, new EcBlock(1, 13)),
new EcBlocks(17, new EcBlock(1, 9)),
);
break;
case 2:
$patterns = array(6, 18);
$ecBlocks = array(
new EcBlocks(10, new EcBlock(1, 34)),
new EcBlocks(16, new EcBlock(1, 28)),
new EcBlocks(22, new EcBlock(1, 22)),
new EcBlocks(28, new EcBlock(1, 16)),
);
break;
case 3:
$patterns = array(6, 22);
$ecBlocks = array(
new EcBlocks(15, new EcBlock(1, 55)),
new EcBlocks(26, new EcBlock(1, 44)),
new EcBlocks(18, new EcBlock(2, 17)),
new EcBlocks(22, new EcBlock(2, 13)),
);
break;
case 4:
$patterns = array(6, 26);
$ecBlocks = array(
new EcBlocks(20, new EcBlock(1, 80)),
new EcBlocks(18, new EcBlock(2, 32)),
new EcBlocks(26, new EcBlock(3, 24)),
new EcBlocks(16, new EcBlock(4, 9)),
);
break;
case 5:
$patterns = array(6, 30);
$ecBlocks = array(
new EcBlocks(26, new EcBlock(1, 108)),
new EcBlocks(24, new EcBlock(2, 43)),
new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)),
new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12)),
);
break;
case 6:
$patterns = array(6, 34);
$ecBlocks = array(
new EcBlocks(18, new EcBlock(2, 68)),
new EcBlocks(16, new EcBlock(4, 27)),
new EcBlocks(24, new EcBlock(4, 19)),
new EcBlocks(28, new EcBlock(4, 15)),
);
break;
case 7:
$patterns = array(6, 22, 38);
$ecBlocks = array(
new EcBlocks(20, new EcBlock(2, 78)),
new EcBlocks(18, new EcBlock(4, 31)),
new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)),
new EcBlocks(26, new EcBlock(4, 13)),
);
break;
case 8:
$patterns = array(6, 24, 42);
$ecBlocks = array(
new EcBlocks(24, new EcBlock(2, 97)),
new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)),
new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)),
new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15)),
);
break;
case 9:
$patterns = array(6, 26, 46);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(2, 116)),
new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)),
new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)),
new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13)),
);
break;
case 10:
$patterns = array(6, 28, 50);
$ecBlocks = array(
new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)),
new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)),
new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)),
new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16)),
);
break;
case 11:
$patterns = array(6, 30, 54);
$ecBlocks = array(
new EcBlocks(20, new EcBlock(4, 81)),
new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)),
new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)),
new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13)),
);
break;
case 12:
$patterns = array(6, 32, 58);
$ecBlocks = array(
new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)),
new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)),
new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)),
new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15)),
);
break;
case 13:
$patterns = array(6, 34, 62);
$ecBlocks = array(
new EcBlocks(26, new EcBlock(4, 107)),
new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)),
new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)),
new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12)),
);
break;
case 14:
$patterns = array(6, 26, 46, 66);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)),
new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)),
new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)),
new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13)),
);
break;
case 15:
$patterns = array(6, 26, 48, 70);
$ecBlocks = array(
new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)),
new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)),
new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)),
new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13)),
);
break;
case 16:
$patterns = array(6, 26, 50, 74);
$ecBlocks = array(
new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)),
new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)),
new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)),
new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16)),
);
break;
case 17:
$patterns = array(6, 30, 54, 78);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)),
new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)),
new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)),
new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15)),
);
break;
case 18:
$patterns = array(6, 30, 56, 82);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)),
new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)),
new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)),
new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15)),
);
break;
case 19:
$patterns = array(6, 30, 58, 86);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)),
new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)),
new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)),
new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14)),
);
break;
case 20:
$patterns = array(6, 34, 62, 90);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)),
new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)),
new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)),
new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16)),
);
break;
case 21:
$patterns = array(6, 28, 50, 72, 94);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)),
new EcBlocks(26, new EcBlock(17, 42)),
new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)),
new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17)),
);
break;
case 22:
$patterns = array(6, 26, 50, 74, 98);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)),
new EcBlocks(28, new EcBlock(17, 46)),
new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)),
new EcBlocks(24, new EcBlock(34, 13)),
);
break;
case 23:
$patterns = array(6, 30, 54, 78, 102);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)),
new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)),
new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)),
new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16)),
);
break;
case 24:
$patterns = array(6, 28, 54, 80, 106);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)),
new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)),
new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)),
new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17)),
);
break;
case 25:
$patterns = array(6, 32, 58, 84, 110);
$ecBlocks = array(
new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)),
new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)),
new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)),
new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16)),
);
break;
case 26:
$patterns = array(6, 30, 58, 86, 114);
$ecBlocks = array(
new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)),
new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)),
new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)),
new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17)),
);
break;
case 27:
$patterns = array(6, 34, 62, 90, 118);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)),
new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)),
new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)),
new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16)),
);
break;
case 28:
$patterns = array(6, 26, 50, 74, 98, 122);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)),
new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)),
new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)),
new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16)),
);
break;
case 29:
$patterns = array(6, 30, 54, 78, 102, 126);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)),
new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)),
new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)),
new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16)),
);
break;
case 30:
$patterns = array(6, 26, 52, 78, 104, 130);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)),
new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)),
new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)),
new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16)),
);
break;
case 31:
$patterns = array(6, 30, 56, 82, 108, 134);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)),
new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)),
new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)),
new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16)),
);
break;
case 32:
$patterns = array(6, 34, 60, 86, 112, 138);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(17, 115)),
new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)),
new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)),
new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16)),
);
break;
case 33:
$patterns = array(6, 30, 58, 86, 114, 142);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)),
new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)),
new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)),
new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16)),
);
break;
case 34:
$patterns = array(6, 34, 62, 90, 118, 146);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)),
new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)),
new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)),
new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17)),
);
break;
case 35:
$patterns = array(6, 30, 54, 78, 102, 126, 150);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)),
new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)),
new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)),
new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16)),
);
break;
case 36:
$patterns = array(6, 24, 50, 76, 102, 128, 154);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)),
new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)),
new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)),
new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16)),
);
break;
case 37:
$patterns = array(6, 28, 54, 80, 106, 132, 158);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)),
new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)),
new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)),
new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16)),
);
break;
case 38:
$patterns = array(6, 32, 58, 84, 110, 136, 162);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)),
new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)),
new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)),
new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16)),
);
break;
case 39:
$patterns = array(6, 26, 54, 82, 110, 138, 166);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)),
new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)),
new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)),
new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16)),
);
break;
case 40:
$patterns = array(6, 30, 58, 86, 114, 142, 170);
$ecBlocks = array(
new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)),
new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)),
new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)),
new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)),
);
break;
}
self::$versions[$versionNumber - 1] = new self(
$versionNumber,
SplFixedArray::fromArray($patterns, false),
SplFixedArray::fromArray($ecBlocks, false)
);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use SplFixedArray;
/**
* Block pair.
*/
class BlockPair
{
/**
* Data bytes in the block.
*
* @var SplFixedArray
*/
protected $dataBytes;
/**
* Error correction bytes in the block.
*
* @var SplFixedArray
*/
protected $errorCorrectionBytes;
/**
* Creates a new block pair.
*
* @param SplFixedArray $data
* @param SplFixedArray $errorCorrection
*/
public function __construct(SplFixedArray $data, SplFixedArray $errorCorrection)
{
$this->dataBytes = $data;
$this->errorCorrectionBytes = $errorCorrection;
}
/**
* Gets the data bytes.
*
* @return SplFixedArray
*/
public function getDataBytes()
{
return $this->dataBytes;
}
/**
* Gets the error correction bytes.
*
* @return SplFixedArray
*/
public function getErrorCorrectionBytes()
{
return $this->errorCorrectionBytes;
}
}

View File

@@ -0,0 +1,158 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use SplFixedArray;
/**
* Byte matrix.
*/
class ByteMatrix
{
/**
* Bytes in the matrix, represented as array.
*
* @var SplFixedArray
*/
protected $bytes;
/**
* Width of the matrix.
*
* @var integer
*/
protected $width;
/**
* Height of the matrix.
*
* @var integer
*/
protected $height;
/**
* Creates a new byte matrix.
*
* @param integer $width
* @param integer $height
*/
public function __construct($width, $height)
{
$this->height = $height;
$this->width = $width;
$this->bytes = new SplFixedArray($height);
for ($y = 0; $y < $height; $y++) {
$this->bytes[$y] = new SplFixedArray($width);
}
}
/**
* Gets the width of the matrix.
*
* @return integer
*/
public function getWidth()
{
return $this->width;
}
/**
* Gets the height of the matrix.
*
* @return integer
*/
public function getHeight()
{
return $this->height;
}
/**
* Gets the internal representation of the matrix.
*
* @return SplFixedArray
*/
public function getArray()
{
return $this->bytes;
}
/**
* Gets the byte for a specific position.
*
* @param integer $x
* @param integer $y
* @return integer
*/
public function get($x, $y)
{
return $this->bytes[$y][$x];
}
/**
* Sets the byte for a specific position.
*
* @param integer $x
* @param integer $y
* @param integer $value
* @return void
*/
public function set($x, $y, $value)
{
$this->bytes[$y][$x] = (int) $value;
}
/**
* Clears the matrix with a specific value.
*
* @param integer $value
* @return void
*/
public function clear($value)
{
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
$this->bytes[$y][$x] = $value;
}
}
}
/**
* Returns a string representation of the matrix.
*
* @return string
*/
public function __toString()
{
$result = '';
for ($y = 0; $y < $this->height; $y++) {
for ($x = 0; $x < $this->width; $x++) {
switch ($this->bytes[$y][$x]) {
case 0:
$result .= ' 0';
break;
case 1:
$result .= ' 1';
break;
default:
$result .= ' ';
break;
}
}
$result .= "\n";
}
return $result;
}
}

View File

@@ -0,0 +1,687 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\CharacterSetEci;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\ReedSolomonCodec;
use BaconQrCode\Common\Version;
use BaconQrCode\Exception;
use SplFixedArray;
/**
* Encoder.
*/
class Encoder
{
/**
* Default byte encoding.
*/
const DEFAULT_BYTE_MODE_ECODING = 'ISO-8859-1';
/**
* The original table is defined in the table 5 of JISX0510:2004 (p.19).
*
* @var array
*/
protected static $alphanumericTable = array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
);
/**
* Codec cache.
*
* @var array
*/
protected static $codecs = array();
/**
* Encodes "content" with the error correction level "ecLevel".
*
* @param string $content
* @param ErrorCorrectionLevel $ecLevel
* @param ? $hints
* @return QrCode
*/
public static function encode($content, ErrorCorrectionLevel $ecLevel, $encoding = self::DEFAULT_BYTE_MODE_ECODING)
{
// Pick an encoding mode appropriate for the content. Note that this
// will not attempt to use multiple modes / segments even if that were
// more efficient. Would be nice.
$mode = self::chooseMode($content, $encoding);
// This will store the header information, like mode and length, as well
// as "header" segments like an ECI segment.
$headerBits = new BitArray();
// Append ECI segment if applicable
if ($mode->get() === Mode::BYTE && $encoding !== self::DEFAULT_BYTE_MODE_ECODING) {
$eci = CharacterSetEci::getCharacterSetEciByName($encoding);
if ($eci !== null) {
self::appendEci($eci, $headerBits);
}
}
// (With ECI in place,) Write the mode marker
self::appendModeInfo($mode, $headerBits);
// Collect data within the main segment, separately, to count its size
// if needed. Don't add it to main payload yet.
$dataBits = new BitArray();
self::appendBytes($content, $mode, $dataBits, $encoding);
// Hard part: need to know version to know how many bits length takes.
// But need to know how many bits it takes to know version. First we
// take a guess at version by assuming version will be the minimum, 1:
$provisionalBitsNeeded = $headerBits->getSize()
+ $mode->getCharacterCountBits(Version::getVersionForNumber(1))
+ $dataBits->getSize();
$provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel);
// Use that guess to calculate the right version. I am still not sure
// this works in 100% of cases.
$bitsNeeded = $headerBits->getSize()
+ $mode->getCharacterCountBits($provisionalVersion)
+ $dataBits->getSize();
$version = self::chooseVersion($bitsNeeded, $ecLevel);
$headerAndDataBits = new BitArray();
$headerAndDataBits->appendBitArray($headerBits);
// Find "length" of main segment and write it.
$numLetters = ($mode->get() === Mode::BYTE ? $dataBits->getSizeInBytes() : strlen($content));
self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits);
// Put data together into the overall payload.
$headerAndDataBits->appendBitArray($dataBits);
$ecBlocks = $version->getEcBlocksForLevel($ecLevel);
$numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords();
// Terminate the bits properly.
self::terminateBits($numDataBytes, $headerAndDataBits);
// Interleave data bits with error correction code.
$finalBits = self::interleaveWithEcBytes(
$headerAndDataBits,
$version->getTotalCodewords(),
$numDataBytes,
$ecBlocks->getNumBlocks()
);
$qrCode = new QrCode();
$qrCode->setErrorCorrectionLevel($ecLevel);
$qrCode->setMode($mode);
$qrCode->setVersion($version);
// Choose the mask pattern and set to "qrCode".
$dimension = $version->getDimensionForVersion();
$matrix = new ByteMatrix($dimension, $dimension);
$maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix);
$qrCode->setMaskPattern($maskPattern);
// Build the matrix and set it to "qrCode".
MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix);
$qrCode->setMatrix($matrix);
return $qrCode;
}
/**
* Gets the alphanumeric code for a byte.
*
* @param string|integer $code
* @return integer
*/
protected static function getAlphanumericCode($code)
{
$code = (is_string($code) ? ord($code) : $code);
if (isset(self::$alphanumericTable[$code])) {
return self::$alphanumericTable[$code];
}
return -1;
}
/**
* Chooses the best mode for a given content.
*
* @param string $content
* @param string $encoding
* @return Mode
*/
protected static function chooseMode($content, $encoding = null)
{
if (strcasecmp($encoding, 'SHIFT-JIS') === 0) {
return self::isOnlyDoubleByteKanji($content) ? new Mode(Mode::KANJI) : new Mode(Mode::BYTE);
}
$hasNumeric = false;
$hasAlphanumeric = false;
$contentLength = strlen($content);
for ($i = 0; $i < $contentLength; $i++) {
$char = $content[$i];
if (ctype_digit($char)) {
$hasNumeric = true;
} elseif (self::getAlphanumericCode($char) !== -1) {
$hasAlphanumeric = true;
} else {
return new Mode(Mode::BYTE);
}
}
if ($hasAlphanumeric) {
return new Mode(Mode::ALPHANUMERIC);
} elseif ($hasNumeric) {
return new Mode(Mode::NUMERIC);
}
return new Mode(Mode::BYTE);
}
/**
* Calculates the mask penalty for a matrix.
*
* @param ByteMatrix $matrix
* @return integer
*/
protected static function calculateMaskPenalty(ByteMatrix $matrix)
{
return (
MaskUtil::applyMaskPenaltyRule1($matrix)
+ MaskUtil::applyMaskPenaltyRule2($matrix)
+ MaskUtil::applyMaskPenaltyRule3($matrix)
+ MaskUtil::applyMaskPenaltyRule4($matrix)
);
}
/**
* Chooses the best mask pattern for a matrix.
*
* @param BitArray $bits
* @param ErrorCorrectionLevel $ecLevel
* @param Version $version
* @param ByteMatrix $matrix
* @return integer
*/
protected static function chooseMaskPattern(
BitArray $bits,
ErrorCorrectionLevel $ecLevel,
Version $version,
ByteMatrix $matrix
) {
$minPenality = PHP_INT_MAX;
$bestMaskPattern = -1;
for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; $maskPattern++) {
MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix);
$penalty = self::calculateMaskPenalty($matrix);
if ($penalty < $minPenality) {
$minPenality = $penalty;
$bestMaskPattern = $maskPattern;
}
}
return $bestMaskPattern;
}
/**
* Chooses the best version for the input.
*
* @param integer $numInputBits
* @param ErrorCorrectionLevel $ecLevel
* @return Version
* @throws Exception\WriterException
*/
protected static function chooseVersion($numInputBits, ErrorCorrectionLevel $ecLevel)
{
for ($versionNum = 1; $versionNum <= 40; $versionNum++) {
$version = Version::getVersionForNumber($versionNum);
$numBytes = $version->getTotalCodewords();
$ecBlocks = $version->getEcBlocksForLevel($ecLevel);
$numEcBytes = $ecBlocks->getTotalEcCodewords();
$numDataBytes = $numBytes - $numEcBytes;
$totalInputBytes = intval(($numInputBits + 8) / 8);
if ($numDataBytes >= $totalInputBytes) {
return $version;
}
}
throw new Exception\WriterException('Data too big');
}
/**
* Terminates the bits in a bit array.
*
* @param integer $numDataBytes
* @param BitArray $bits
* @throws Exception\WriterException
*/
protected static function terminateBits($numDataBytes, BitArray $bits)
{
$capacity = $numDataBytes << 3;
if ($bits->getSize() > $capacity) {
throw new Exception\WriterException('Data bits cannot fit in the QR code');
}
for ($i = 0; $i < 4 && $bits->getSize() < $capacity; $i++) {
$bits->appendBit(false);
}
$numBitsInLastByte = $bits->getSize() & 0x7;
if ($numBitsInLastByte > 0) {
for ($i = $numBitsInLastByte; $i < 8; $i++) {
$bits->appendBit(false);
}
}
$numPaddingBytes = $numDataBytes - $bits->getSizeInBytes();
for ($i = 0; $i < $numPaddingBytes; $i++) {
$bits->appendBits(($i & 0x1) === 0 ? 0xec : 0x11, 8);
}
if ($bits->getSize() !== $capacity) {
throw new Exception\WriterException('Bits size does not equal capacity');
}
}
/**
* Gets number of data- and EC bytes for a block ID.
*
* @param integer $numTotalBytes
* @param integer $numDataBytes
* @param integer $numRsBlocks
* @param integer $blockId
* @return array
* @throws Exception\WriterException
*/
protected static function getNumDataBytesAndNumEcBytesForBlockId(
$numTotalBytes,
$numDataBytes,
$numRsBlocks,
$blockId
) {
if ($blockId >= $numRsBlocks) {
throw new Exception\WriterException('Block ID too large');
}
$numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks;
$numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2;
$numTotalBytesInGroup1 = intval($numTotalBytes / $numRsBlocks);
$numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1;
$numDataBytesInGroup1 = intval($numDataBytes / $numRsBlocks);
$numDataBytesInGroup2 = $numDataBytesInGroup1 + 1;
$numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1;
$numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2;
if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) {
throw new Exception\WriterException('EC bytes mismatch');
}
if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) {
throw new Exception\WriterException('RS blocks mismatch');
}
if ($numTotalBytes !==
(($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1)
+ (($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2)
) {
throw new Exception\WriterException('Total bytes mismatch');
}
if ($blockId < $numRsBlocksInGroup1) {
return array($numDataBytesInGroup1, $numEcBytesInGroup1);
} else {
return array($numDataBytesInGroup2, $numEcBytesInGroup2);
}
}
/**
* Interleaves data with EC bytes.
*
* @param BitArray $bits
* @param integer $numTotalBytes
* @param integer $numDataBytes
* @param integer $numRsBlocks
* @return BitArray
* @throws Exception\WriterException
*/
protected static function interleaveWithEcBytes(BitArray $bits, $numTotalBytes, $numDataBytes, $numRsBlocks)
{
if ($bits->getSizeInBytes() !== $numDataBytes) {
throw new Exception\WriterException('Number of bits and data bytes does not match');
}
$dataBytesOffset = 0;
$maxNumDataBytes = 0;
$maxNumEcBytes = 0;
$blocks = new SplFixedArray($numRsBlocks);
for ($i = 0; $i < $numRsBlocks; $i++) {
list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId(
$numTotalBytes,
$numDataBytes,
$numRsBlocks,
$i
);
$size = $numDataBytesInBlock;
$dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size);
$ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock);
$blocks[$i] = new BlockPair($dataBytes, $ecBytes);
$maxNumDataBytes = max($maxNumDataBytes, $size);
$maxNumEcBytes = max($maxNumEcBytes, count($ecBytes));
$dataBytesOffset += $numDataBytesInBlock;
}
if ($numDataBytes !== $dataBytesOffset) {
throw new Exception\WriterException('Data bytes does not match offset');
}
$result = new BitArray();
for ($i = 0; $i < $maxNumDataBytes; $i++) {
foreach ($blocks as $block) {
$dataBytes = $block->getDataBytes();
if ($i < count($dataBytes)) {
$result->appendBits($dataBytes[$i], 8);
}
}
}
for ($i = 0; $i < $maxNumEcBytes; $i++) {
foreach ($blocks as $block) {
$ecBytes = $block->getErrorCorrectionBytes();
if ($i < count($ecBytes)) {
$result->appendBits($ecBytes[$i], 8);
}
}
}
if ($numTotalBytes !== $result->getSizeInBytes()) {
throw new Exception\WriterException('Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ');
}
return $result;
}
/**
* Generates EC bytes for given data.
*
* @param SplFixedArray $dataBytes
* @param integer $numEcBytesInBlock
* @return SplFixedArray
*/
protected static function generateEcBytes(SplFixedArray $dataBytes, $numEcBytesInBlock)
{
$numDataBytes = count($dataBytes);
$toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock);
for ($i = 0; $i < $numDataBytes; $i++) {
$toEncode[$i] = $dataBytes[$i] & 0xff;
}
$ecBytes = new SplFixedArray($numEcBytesInBlock);
$codec = self::getCodec($numDataBytes, $numEcBytesInBlock);
$codec->encode($toEncode, $ecBytes);
return $ecBytes;
}
/**
* Gets an RS codec and caches it.
*
* @param integer $numDataBytes
* @param integer $numEcBytesInBlock
* @return ReedSolomonCodec
*/
protected static function getCodec($numDataBytes, $numEcBytesInBlock)
{
$cacheId = $numDataBytes . '-' . $numEcBytesInBlock;
if (!isset(self::$codecs[$cacheId])) {
self::$codecs[$cacheId] = new ReedSolomonCodec(
8,
0x11d,
0,
1,
$numEcBytesInBlock,
255 - $numDataBytes - $numEcBytesInBlock
);
}
return self::$codecs[$cacheId];
}
/**
* Appends mode information to a bit array.
*
* @param Mode $mode
* @param BitArray $bits
* @return void
*/
protected static function appendModeInfo(Mode $mode, BitArray $bits)
{
$bits->appendBits($mode->get(), 4);
}
/**
* Appends length information to a bit array.
*
* @param integer $numLetters
* @param Version $version
* @param Mode $mode
* @param BitArray $bits
* @return void
* @throws Exception\WriterException
*/
protected static function appendLengthInfo($numLetters, Version $version, Mode $mode, BitArray $bits)
{
$numBits = $mode->getCharacterCountBits($version);
if ($numLetters >= (1 << $numBits)) {
throw new Exception\WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1));
}
$bits->appendBits($numLetters, $numBits);
}
/**
* Appends bytes to a bit array in a specific mode.
*
* @param stirng $content
* @param Mode $mode
* @param BitArray $bits
* @param string $encoding
* @return void
* @throws Exception\WriterException
*/
protected static function appendBytes($content, Mode $mode, BitArray $bits, $encoding)
{
switch ($mode->get()) {
case Mode::NUMERIC:
self::appendNumericBytes($content, $bits);
break;
case Mode::ALPHANUMERIC:
self::appendAlphanumericBytes($content, $bits);
break;
case Mode::BYTE:
self::append8BitBytes($content, $bits, $encoding);
break;
case Mode::KANJI:
self::appendKanjiBytes($content, $bits);
break;
default:
throw new Exception\WriterException('Invalid mode: ' . $mode->get());
}
}
/**
* Appends numeric bytes to a bit array.
*
* @param string $content
* @param BitArray $bits
* @return void
*/
protected static function appendNumericBytes($content, BitArray $bits)
{
$length = strlen($content);
$i = 0;
while ($i < $length) {
$num1 = (int) $content[$i];
if ($i + 2 < $length) {
// Encode three numeric letters in ten bits.
$num2 = (int) $content[$i + 1];
$num3 = (int) $content[$i + 2];
$bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10);
$i += 3;
} elseif ($i + 1 < $length) {
// Encode two numeric letters in seven bits.
$num2 = (int) $content[$i + 1];
$bits->appendBits($num1 * 10 + $num2, 7);
$i += 2;
} else {
// Encode one numeric letter in four bits.
$bits->appendBits($num1, 4);
$i++;
}
}
}
/**
* Appends alpha-numeric bytes to a bit array.
*
* @param string $content
* @param BitArray $bits
* @return void
*/
protected static function appendAlphanumericBytes($content, BitArray $bits)
{
$length = strlen($content);
$i = 0;
while ($i < $length) {
if (-1 === ($code1 = self::getAlphanumericCode($content[$i]))) {
throw new Exception\WriterException('Invalid alphanumeric code');
}
if ($i + 1 < $length) {
if (-1 === ($code2 = self::getAlphanumericCode($content[$i + 1]))) {
throw new Exception\WriterException('Invalid alphanumeric code');
}
// Encode two alphanumeric letters in 11 bits.
$bits->appendBits($code1 * 45 + $code2, 11);
$i += 2;
} else {
// Encode one alphanumeric letter in six bits.
$bits->appendBits($code1, 6);
$i++;
}
}
}
/**
* Appends regular 8-bit bytes to a bit array.
*
* @param string $content
* @param BitArray $bits
* @return void
*/
protected static function append8BitBytes($content, BitArray $bits, $encoding)
{
if (false === ($bytes = @iconv('utf-8', $encoding, $content))) {
throw new Exception\WriterException('Could not encode content to ' . $encoding);
}
$length = strlen($bytes);
for ($i = 0; $i < $length; $i++) {
$bits->appendBits(ord($bytes[$i]), 8);
}
}
/**
* Appends KANJI bytes to a bit array.
*
* @param string $content
* @param BitArray $bits
* @return void
*/
protected static function appendKanjiBytes($content, BitArray $bits)
{
if (strlen($content) % 2 > 0) {
// We just do a simple length check here. The for loop will check
// individual characters.
throw new Exception\WriterException('Content does not seem to be encoded in SHIFT-JIS');
}
$length = strlen($content);
for ($i = 0; $i < $length; $i += 2) {
$byte1 = ord($content[$i]) & 0xff;
$byte2 = ord($content[$i + 1]) & 0xff;
$code = ($byte1 << 8) | $byte2;
if ($code >= 0x8140 && $code <= 0x9ffc) {
$subtracted = $code - 0x8140;
} elseif ($code >= 0xe040 && $code <= 0xebbf) {
$subtracted = $code - 0xc140;
} else {
throw new Exception\WriterException('Invalid byte sequence');
}
$encoded = (($subtracted >> 8) * 0xc0) + ($subtracted & 0xff);
$bits->appendBits($encoded, 13);
}
}
/**
* Appends ECI information to a bit array.
*
* @param CharacterSetEci $eci
* @param BitArray $bits
* @return void
*/
protected static function appendEci(CharacterSetEci $eci, BitArray $bits)
{
$mode = new Mode(Mode::ECI);
$bits->appendBits($mode->get(), 4);
$bits->appendBits($eci->get(), 8);
}
}

View File

@@ -0,0 +1,291 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitUtils;
/**
* Mask utility.
*/
class MaskUtil
{
/**#@+
* Penalty weights from section 6.8.2.1
*/
const N1 = 3;
const N2 = 3;
const N3 = 40;
const N4 = 10;
/**#@-*/
/**
* Applies mask penalty rule 1 and returns the penalty.
*
* Finds repetitive cells with the same color and gives penalty to them.
* Example: 00000 or 11111.
*
* @param ByteMatrix $matrix
* @return integer
*/
public static function applyMaskPenaltyRule1(ByteMatrix $matrix)
{
return (
self::applyMaskPenaltyRule1Internal($matrix, true)
+ self::applyMaskPenaltyRule1Internal($matrix, false)
);
}
/**
* Applies mask penalty rule 2 and returns the penalty.
*
* Finds 2x2 blocks with the same color and gives penalty to them. This is
* actually equivalent to the spec's rule, which is to find MxN blocks and
* give a penalty proportional to (M-1)x(N-1), because this is the number of
* 2x2 blocks inside such a block.
*
* @param ByteMatrix $matrix
* @return integer
*/
public static function applyMaskPenaltyRule2(ByteMatrix $matrix)
{
$penalty = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height - 1; $y++) {
for ($x = 0; $x < $width - 1; $x++) {
$value = $array[$y][$x];
if ($value === $array[$y][$x + 1] && $value === $array[$y + 1][$x] && $value === $array[$y + 1][$x + 1]) {
$penalty++;
}
}
}
return self::N2 * $penalty;
}
/**
* Applies mask penalty rule 3 and returns the penalty.
*
* Finds consecutive cells of 00001011101 or 10111010000, and gives penalty
* to them. If we find patterns like 000010111010000, we give penalties
* twice (i.e. 40 * 2).
*
* @param ByteMatrix $matrix
* @return integer
*/
public static function applyMaskPenaltyRule3(ByteMatrix $matrix)
{
$penalty = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height; $y++) {
for ($x = 0; $x < $width; $x++) {
if (
$x + 6 < $width
&& $array[$y][$x] === 1
&& $array[$y][$x + 1] === 0
&& $array[$y][$x + 2] === 1
&& $array[$y][$x + 3] === 1
&& $array[$y][$x + 4] === 1
&& $array[$y][$x + 5] === 0
&& $array[$y][$x + 6] === 1
&& (
(
$x + 10 < $width
&& $array[$y][$x + 7] === 0
&& $array[$y][$x + 8] === 0
&& $array[$y][$x + 9] === 0
&& $array[$y][$x + 10] === 0
)
|| (
$x - 4 >= 0
&& $array[$y][$x - 1] === 0
&& $array[$y][$x - 2] === 0
&& $array[$y][$x - 3] === 0
&& $array[$y][$x - 4] === 0
)
)
) {
$penalty += self::N3;
}
if (
$y + 6 < $height
&& $array[$y][$x] === 1
&& $array[$y + 1][$x] === 0
&& $array[$y + 2][$x] === 1
&& $array[$y + 3][$x] === 1
&& $array[$y + 4][$x] === 1
&& $array[$y + 5][$x] === 0
&& $array[$y + 6][$x] === 1
&& (
(
$y + 10 < $height
&& $array[$y + 7][$x] === 0
&& $array[$y + 8][$x] === 0
&& $array[$y + 9][$x] === 0
&& $array[$y + 10][$x] === 0
)
|| (
$y - 4 >= 0
&& $array[$y - 1][$x] === 0
&& $array[$y - 2][$x] === 0
&& $array[$y - 3][$x] === 0
&& $array[$y - 4][$x] === 0
)
)
) {
$penalty += self::N3;
}
}
}
return $penalty;
}
/**
* Applies mask penalty rule 4 and returns the penalty.
*
* Calculates the ratio of dark cells and gives penalty if the ratio is far
* from 50%. It gives 10 penalty for 5% distance.
*
* @param ByteMatrix $matrix
* @return integer
*/
public static function applyMaskPenaltyRule4(ByteMatrix $matrix)
{
$numDarkCells = 0;
$array = $matrix->getArray();
$width = $matrix->getWidth();
$height = $matrix->getHeight();
for ($y = 0; $y < $height; $y++) {
$arrayY = $array[$y];
for ($x = 0; $x < $width; $x++) {
if ($arrayY[$x] === 1) {
$numDarkCells++;
}
}
}
$numTotalCells = $height * $width;
$darkRatio = $numDarkCells / $numTotalCells;
$fixedPercentVariances = (int) (abs($darkRatio - 0.5) * 20);
return $fixedPercentVariances * self::N4;
}
/**
* Returns the mask bit for "getMaskPattern" at "x" and "y".
*
* See 8.8 of JISX0510:2004 for mask pattern conditions.
*
* @param integer $maskPattern
* @param integer $x
* @param integer $y
* @return integer
* @throws Exception\InvalidArgumentException
*/
public static function getDataMaskBit($maskPattern, $x, $y)
{
switch ($maskPattern) {
case 0:
$intermediate = ($y + $x) & 0x1;
break;
case 1:
$intermediate = $y & 0x1;
break;
case 2:
$intermediate = $x % 3;
break;
case 3:
$intermediate = ($y + $x) % 3;
break;
case 4:
$intermediate = (BitUtils::unsignedRightShift($y, 1) + ($x / 3)) & 0x1;
break;
case 5:
$temp = $y * $x;
$intermediate = ($temp & 0x1) + ($temp % 3);
break;
case 6:
$temp = $y * $x;
$intermediate = (($temp & 0x1) + ($temp % 3)) & 0x1;
break;
case 7:
$temp = $y * $x;
$intermediate = (($temp % 3) + (($y + $x) & 0x1)) & 0x1;
break;
default:
throw new Exception\InvalidArgumentException('Invalid mask pattern: ' . $maskPattern);
}
return $intermediate === 0;
}
/**
* Helper function for applyMaskPenaltyRule1.
*
* We need this for doing this calculation in both vertical and horizontal
* orders respectively.
*
* @param ByteMatrix $matrix
* @param boolean $isHorizontal
* @return integer
*/
protected static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, $isHorizontal)
{
$penalty = 0;
$iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth();
$jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight();
$array = $matrix->getArray();
for ($i = 0; $i < $iLimit; $i++) {
$numSameBitCells = 0;
$prevBit = -1;
for ($j = 0; $j < $jLimit; $j++) {
$bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i];
if ($bit === $prevBit) {
$numSameBitCells++;
} else {
if ($numSameBitCells >= 5) {
$penalty += self::N1 + ($numSameBitCells - 5);
}
$numSameBitCells = 1;
$prevBit = $bit;
}
}
if ($numSameBitCells >= 5) {
$penalty += self::N1 + ($numSameBitCells - 5);
}
}
return $penalty;
}
}

View File

@@ -0,0 +1,580 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use BaconQrCode\Exception;
/**
* Matrix utility.
*/
class MatrixUtil
{
/**
* Position detection pattern.
*
* @var array
*/
protected static $positionDetectionPattern = array(
array(1, 1, 1, 1, 1, 1, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 0, 1, 1, 1, 0, 1),
array(1, 0, 1, 1, 1, 0, 1),
array(1, 0, 1, 1, 1, 0, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 1, 1, 1, 1, 1, 1),
);
/**
* Position adjustment pattern.
*
* @var array
*/
protected static $positionAdjustmentPattern = array(
array(1, 1, 1, 1, 1),
array(1, 0, 0, 0, 1),
array(1, 0, 1, 0, 1),
array(1, 0, 0, 0, 1),
array(1, 1, 1, 1, 1),
);
/**
* Coordinates for position adjustment patterns for each version.
*
* @var array
*/
protected static $positionAdjustmentPatternCoordinateTable = array(
array(null, null, null, null, null, null, null), // Version 1
array( 6, 18, null, null, null, null, null), // Version 2
array( 6, 22, null, null, null, null, null), // Version 3
array( 6, 26, null, null, null, null, null), // Version 4
array( 6, 30, null, null, null, null, null), // Version 5
array( 6, 34, null, null, null, null, null), // Version 6
array( 6, 22, 38, null, null, null, null), // Version 7
array( 6, 24, 42, null, null, null, null), // Version 8
array( 6, 26, 46, null, null, null, null), // Version 9
array( 6, 28, 50, null, null, null, null), // Version 10
array( 6, 30, 54, null, null, null, null), // Version 11
array( 6, 32, 58, null, null, null, null), // Version 12
array( 6, 34, 62, null, null, null, null), // Version 13
array( 6, 26, 46, 66, null, null, null), // Version 14
array( 6, 26, 48, 70, null, null, null), // Version 15
array( 6, 26, 50, 74, null, null, null), // Version 16
array( 6, 30, 54, 78, null, null, null), // Version 17
array( 6, 30, 56, 82, null, null, null), // Version 18
array( 6, 30, 58, 86, null, null, null), // Version 19
array( 6, 34, 62, 90, null, null, null), // Version 20
array( 6, 28, 50, 72, 94, null, null), // Version 21
array( 6, 26, 50, 74, 98, null, null), // Version 22
array( 6, 30, 54, 78, 102, null, null), // Version 23
array( 6, 28, 54, 80, 106, null, null), // Version 24
array( 6, 32, 58, 84, 110, null, null), // Version 25
array( 6, 30, 58, 86, 114, null, null), // Version 26
array( 6, 34, 62, 90, 118, null, null), // Version 27
array( 6, 26, 50, 74, 98, 122, null), // Version 28
array( 6, 30, 54, 78, 102, 126, null), // Version 29
array( 6, 26, 52, 78, 104, 130, null), // Version 30
array( 6, 30, 56, 82, 108, 134, null), // Version 31
array( 6, 34, 60, 86, 112, 138, null), // Version 32
array( 6, 30, 58, 86, 114, 142, null), // Version 33
array( 6, 34, 62, 90, 118, 146, null), // Version 34
array( 6, 30, 54, 78, 102, 126, 150), // Version 35
array( 6, 24, 50, 76, 102, 128, 154), // Version 36
array( 6, 28, 54, 80, 106, 132, 158), // Version 37
array( 6, 32, 58, 84, 110, 136, 162), // Version 38
array( 6, 26, 54, 82, 110, 138, 166), // Version 39
array( 6, 30, 58, 86, 114, 142, 170), // Version 40
);
/**
* Type information coordinates.
*
* @var array
*/
protected static $typeInfoCoordinates = array(
array(8, 0),
array(8, 1),
array(8, 2),
array(8, 3),
array(8, 4),
array(8, 5),
array(8, 7),
array(8, 8),
array(7, 8),
array(5, 8),
array(4, 8),
array(3, 8),
array(2, 8),
array(1, 8),
array(0, 8),
);
/**
* Version information polynomial.
*
* @var integer
*/
protected static $versionInfoPoly = 0x1f25;
/**
* Type information polynomial.
*
* @var integer
*/
protected static $typeInfoPoly = 0x537;
/**
* Type information mask pattern.
*
* @var integer
*/
protected static $typeInfoMaskPattern = 0x5412;
/**
* Clears a given matrix.
*
* @param ByteMatrix $matrix
* @return void
*/
public static function clearMatrix(ByteMatrix $matrix)
{
$matrix->clear(-1);
}
/**
* Builds a complete matrix.
*
* @param BitArray $dataBits
* @param ErrorCorrectionLevel $level
* @param Version $version
* @param integer $maskPattern
* @param ByteMatrix $matrix
* @return void
*/
public static function buildMatrix(
BitArray $dataBits,
ErrorCorrectionLevel $level,
Version $version,
$maskPattern,
ByteMatrix $matrix
) {
self::clearMatrix($matrix);
self::embedBasicPatterns($version, $matrix);
self::embedTypeInfo($level, $maskPattern, $matrix);
self::maybeEmbedVersionInfo($version, $matrix);
self::embedDataBits($dataBits, $maskPattern, $matrix);
}
/**
* Embeds type information into a matrix.
*
* @param ErrorCorrectionLevel $level
* @param integer $maskPattern
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedTypeInfo(ErrorCorrectionLevel $level, $maskPattern, ByteMatrix $matrix)
{
$typeInfoBits = new BitArray();
self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits);
$typeInfoBitsSize = $typeInfoBits->getSize();
for ($i = 0; $i < $typeInfoBitsSize; $i++) {
$bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i);
$x1 = self::$typeInfoCoordinates[$i][0];
$y1 = self::$typeInfoCoordinates[$i][1];
$matrix->set($x1, $y1, $bit);
if ($i < 8) {
$x2 = $matrix->getWidth() - $i - 1;
$y2 = 8;
} else {
$x2 = 8;
$y2 = $matrix->getHeight() - 7 + ($i - 8);
}
$matrix->set($x2, $y2, $bit);
}
}
/**
* Generates type information bits and appends them to a bit array.
*
* @param ErrorCorrectionLevel $level
* @param integer $maskPattern
* @param BitArray $bits
* @return void
* @throws Exception\RuntimeException
*/
protected static function makeTypeInfoBits(ErrorCorrectionLevel $level, $maskPattern, BitArray $bits)
{
$typeInfo = ($level->get() << 3) | $maskPattern;
$bits->appendBits($typeInfo, 5);
$bchCode = self::calculateBchCode($typeInfo, self::$typeInfoPoly);
$bits->appendBits($bchCode, 10);
$maskBits = new BitArray();
$maskBits->appendBits(self::$typeInfoMaskPattern, 15);
$bits->xorBits($maskBits);
if ($bits->getSize() !== 15) {
throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
}
}
/**
* Embeds version information if required.
*
* @param Version $version
* @param ByteMatrix $matrix
* @return void
*/
protected static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix)
{
if ($version->getVersionNumber() < 7) {
return;
}
$versionInfoBits = new BitArray();
self::makeVersionInfoBits($version, $versionInfoBits);
$bitIndex = 6 * 3 - 1;
for ($i = 0; $i < 6; $i++) {
for ($j = 0; $j < 3; $j++) {
$bit = $versionInfoBits->get($bitIndex);
$bitIndex--;
$matrix->set($i, $matrix->getHeight() - 11 + $j, $bit);
$matrix->set($matrix->getHeight() - 11 + $j, $i, $bit);
}
}
}
/**
* Generates version information bits and appends them to a bit array.
*
* @param Version $version
* @param BitArray $bits
* @return void
* @throws Exception\RuntimeException
*/
protected static function makeVersionInfoBits(Version $version, BitArray $bits)
{
$bits->appendBits($version->getVersionNumber(), 6);
$bchCode = self::calculateBchCode($version->getVersionNumber(), self::$versionInfoPoly);
$bits->appendBits($bchCode, 12);
if ($bits->getSize() !== 18) {
throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize());
}
}
/**
* Calculates the BHC code for a value and a polynomial.
*
* @param integer $value
* @param integer $poly
* @return integer
*/
protected static function calculateBchCode($value, $poly)
{
$msbSetInPoly = self::findMsbSet($poly);
$value <<= $msbSetInPoly - 1;
while (self::findMsbSet($value) >= $msbSetInPoly) {
$value ^= $poly << (self::findMsbSet($value) - $msbSetInPoly);
}
return $value;
}
/**
* Finds and MSB set.
*
* @param integer $value
* @return integer
*/
protected static function findMsbSet($value)
{
$numDigits = 0;
while ($value !== 0) {
$value >>= 1;
$numDigits++;
}
return $numDigits;
}
/**
* Embeds basic patterns into a matrix.
*
* @param Version $version
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedBasicPatterns(Version $version, ByteMatrix $matrix)
{
self::embedPositionDetectionPatternsAndSeparators($matrix);
self::embedDarkDotAtLeftBottomCorner($matrix);
self::maybeEmbedPositionAdjustmentPatterns($version, $matrix);
self::embedTimingPatterns($matrix);
}
/**
* Embeds position detection patterns and separators into a byte matrix.
*
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix)
{
$pdpWidth = count(self::$positionDetectionPattern[0]);
self::embedPositionDetectionPattern(0, 0, $matrix);
self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix);
self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix);
$hspWidth = 8;
self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix);
self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix);
self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix);
$vspSize = 7;
self::embedVerticalSeparationPattern($vspSize, 0, $matrix);
self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix);
self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix);
}
/**
* Embeds a single position detection pattern into a byte matrix.
*
* @param integer $xStart
* @param integer $yStart
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedPositionDetectionPattern($xStart, $yStart, ByteMatrix $matrix)
{
for ($y = 0; $y < 7; $y++) {
for ($x = 0; $x < 7; $x++) {
$matrix->set($xStart + $x, $yStart + $y, self::$positionDetectionPattern[$y][$x]);
}
}
}
/**
* Embeds a single horizontal separation pattern.
*
* @param integer $xStart
* @param integer $yStart
* @param ByteMatrix $matrix
* @return void
* @throws Exception\RuntimeException
*/
protected static function embedHorizontalSeparationPattern($xStart, $yStart, ByteMatrix $matrix)
{
for ($x = 0; $x < 8; $x++) {
if ($matrix->get($xStart + $x, $yStart) !== -1) {
throw new Exception\RuntimeException('Byte already set');
}
$matrix->set($xStart + $x, $yStart, 0);
}
}
/**
* Embeds a single vertical separation pattern.
*
* @param integer $xStart
* @param integer $yStart
* @param ByteMatrix $matrix
* @return void
* @throws Exception\RuntimeException
*/
protected static function embedVerticalSeparationPattern($xStart, $yStart, ByteMatrix $matrix)
{
for ($y = 0; $y < 7; $y++) {
if ($matrix->get($xStart, $yStart + $y) !== -1) {
throw new Exception\RuntimeException('Byte already set');
}
$matrix->set($xStart, $yStart + $y, 0);
}
}
/**
* Embeds a dot at the left bottom corner.
*
* @param ByteMatrix $matrix
* @return void
* @throws Exception\RuntimeException
*/
protected static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix)
{
if ($matrix->get(8, $matrix->getHeight() - 8) === 0) {
throw new Exception\RuntimeException('Byte already set to 0');
}
$matrix->set(8, $matrix->getHeight() - 8, 1);
}
/**
* Embeds position adjustment patterns if required.
*
* @param Version $version
* @param ByteMatrix $matrix
* @return void
*/
protected static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix)
{
if ($version->getVersionNumber() < 2) {
return;
}
$index = $version->getVersionNumber() - 1;
$coordinates = self::$positionAdjustmentPatternCoordinateTable[$index];
$numCoordinates = count($coordinates);
for ($i = 0; $i < $numCoordinates; $i++) {
for ($j = 0; $j < $numCoordinates; $j++) {
$y = $coordinates[$i];
$x = $coordinates[$j];
if ($x === null || $y === null) {
continue;
}
if ($matrix->get($x, $y) === -1) {
self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix);
}
}
}
}
/**
* Embeds a single position adjustment pattern.
*
* @param integer $xStart
* @param intger $yStart
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedPositionAdjustmentPattern($xStart, $yStart, ByteMatrix $matrix)
{
for ($y = 0; $y < 5; $y++) {
for ($x = 0; $x < 5; $x++) {
$matrix->set($xStart + $x, $yStart + $y, self::$positionAdjustmentPattern[$y][$x]);
}
}
}
/**
* Embeds timing patterns into a matrix.
*
* @param ByteMatrix $matrix
* @return void
*/
protected static function embedTimingPatterns(ByteMatrix $matrix)
{
$matrixWidth = $matrix->getWidth();
for ($i = 8; $i < $matrixWidth - 8; $i++) {
$bit = ($i + 1) % 2;
if ($matrix->get($i, 6) === -1) {
$matrix->set($i, 6, $bit);
}
if ($matrix->get(6, $i) === -1) {
$matrix->set(6, $i, $bit);
}
}
}
/**
* Embeds "dataBits" using "getMaskPattern".
*
* For debugging purposes, it skips masking process if "getMaskPattern" is
* -1. See 8.7 of JISX0510:2004 (p.38) for how to embed data bits.
*
* @param BitArray $dataBits
* @param integer $maskPattern
* @param ByteMatrix $matrix
* @return void
* @throws Exception\WriterException
*/
protected static function embedDataBits(BitArray $dataBits, $maskPattern, ByteMatrix $matrix)
{
$bitIndex = 0;
$direction = -1;
// Start from the right bottom cell.
$x = $matrix->getWidth() - 1;
$y = $matrix->getHeight() - 1;
while ($x > 0) {
// Skip vertical timing pattern.
if ($x === 6) {
$x--;
}
while ($y >= 0 && $y < $matrix->getHeight()) {
for ($i = 0; $i < 2; $i++) {
$xx = $x - $i;
// Skip the cell if it's not empty.
if ($matrix->get($xx, $y) !== -1) {
continue;
}
if ($bitIndex < $dataBits->getSize()) {
$bit = $dataBits->get($bitIndex);
$bitIndex++;
} else {
// Padding bit. If there is no bit left, we'll fill the
// left cells with 0, as described in 8.4.9 of
// JISX0510:2004 (p. 24).
$bit = false;
}
// Skip masking if maskPattern is -1.
if ($maskPattern !== -1 && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) {
$bit = !$bit;
}
$matrix->set($xx, $y, $bit);
}
$y += $direction;
}
$direction = -$direction;
$y += $direction;
$x -= 2;
}
// All bits should be consumed
if ($bitIndex !== $dataBits->getSize()) {
throw new Exception\WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() .')');
}
}
}

View File

@@ -0,0 +1,201 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\Version;
/**
* QR code.
*/
class QrCode
{
/**
* Number of possible mask patterns.
*/
const NUM_MASK_PATTERNS = 8;
/**
* Mode of the QR code.
*
* @var Mode
*/
protected $mode;
/**
* EC level of the QR code.
*
* @var ErrorCorrectionLevel
*/
protected $errorCorrectionLevel;
/**
* Version of the QR code.
*
* @var Version
*/
protected $version;
/**
* Mask pattern of the QR code.
*
* @var integer
*/
protected $maskPattern = -1;
/**
* Matrix of the QR code.
*
* @var ByteMatrix
*/
protected $matrix;
/**
* Gets the mode.
*
* @return Mode
*/
public function getMode()
{
return $this->mode;
}
/**
* Sets the mode.
*
* @param Mode $mode
* @return void
*/
public function setMode(Mode $mode)
{
$this->mode = $mode;
}
/**
* Gets the EC level.
*
* @return ErrorCorrectionLevel
*/
public function getErrorCorrectionLevel()
{
return $this->errorCorrectionLevel;
}
/**
* Sets the EC level.
*
* @param ErrorCorrectionLevel $errorCorrectionLevel
* @return void
*/
public function setErrorCorrectionLevel(ErrorCorrectionLevel $errorCorrectionLevel)
{
$this->errorCorrectionLevel = $errorCorrectionLevel;
}
/**
* Gets the version.
*
* @return Version
*/
public function getVersion()
{
return $this->version;
}
/**
* Sets the version.
*
* @param Version $version
* @return void
*/
public function setVersion(Version $version)
{
$this->version = $version;
}
/**
* Gets the mask pattern.
*
* @return integer
*/
public function getMaskPattern()
{
return $this->maskPattern;
}
/**
* Sets the mask pattern.
*
* @param integer $maskPattern
* @return void
*/
public function setMaskPattern($maskPattern)
{
$this->maskPattern = $maskPattern;
}
/**
* Gets the matrix.
*
* @return ByteMatrix
*/
public function getMatrix()
{
return $this->matrix;
}
/**
* Sets the matrix.
*
* @param ByteMatrix $matrix
* @return void
*/
public function setMatrix(ByteMatrix $matrix)
{
$this->matrix = $matrix;
}
/**
* Validates whether a mask pattern is valid.
*
* @param integer $maskPattern
* @return boolean
*/
public static function isValidMaskPattern($maskPattern)
{
return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS;
}
/**
* Returns a string representation of the QR code.
*
* @return string
*/
public function __toString()
{
$result = "<<\n"
. " mode: " . $this->mode . "\n"
. " ecLevel: " . $this->errorCorrectionLevel . "\n"
. " version: " . $this->version . "\n"
. " maskPattern: " . $this->maskPattern . "\n";
if ($this->matrix === null) {
$result .= " matrix: null\n";
} else {
$result .= " matrix:\n";
$result .= $this->matrix;
}
$result .= ">>\n";
return $result;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Exception;
class WriterException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,160 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Color;
use BaconQrCode\Exception;
/**
* CMYK color.
*/
class Cmyk implements ColorInterface
{
/**
* Cyan value.
*
* @var integer
*/
protected $cyan;
/**
* Magenta value.
*
* @var integer
*/
protected $magenta;
/**
* Yellow value.
*
* @var integer
*/
protected $yellow;
/**
* Black value.
*
* @var integer
*/
protected $black;
/**
* Creates a new CMYK color.
*
* @param integer $cyan
* @param integer $magenta
* @param integer $yellow
* @param integer $black
*/
public function __construct($cyan, $magenta, $yellow, $black)
{
if ($cyan < 0 || $cyan > 100) {
throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100');
}
if ($magenta < 0 || $magenta > 100) {
throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100');
}
if ($yellow < 0 || $yellow > 100) {
throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100');
}
if ($black < 0 || $black > 100) {
throw new Exception\InvalidArgumentException('Black must be between 0 and 100');
}
$this->cyan = (int) $cyan;
$this->magenta = (int) $magenta;
$this->yellow = (int) $yellow;
$this->black = (int) $black;
}
/**
* Returns the cyan value.
*
* @return integer
*/
public function getCyan()
{
return $this->cyan;
}
/**
* Returns the magenta value.
*
* @return integer
*/
public function getMagenta()
{
return $this->magenta;
}
/**
* Returns the yellow value.
*
* @return integer
*/
public function getYellow()
{
return $this->yellow;
}
/**
* Returns the black value.
*
* @return integer
*/
public function getBlack()
{
return $this->black;
}
/**
* toRgb(): defined by ColorInterface.
*
* @see ColorInterface::toRgb()
* @return Rgb
*/
public function toRgb()
{
$k = $this->black / 100;
$c = (-$k * $this->cyan + $k * 100 + $this->cyan) / 100;
$m = (-$k * $this->magenta + $k * 100 + $this->magenta) / 100;
$y = (-$k * $this->yellow + $k * 100 + $this->yellow) / 100;
return new Rgb(
-$c * 255 + 255,
-$m * 255 + 255,
-$y * 255 + 255
);
}
/**
* toCmyk(): defined by ColorInterface.
*
* @see ColorInterface::toCmyk()
* @return Cmyk
*/
public function toCmyk()
{
return $this;
}
/**
* toGray(): defined by ColorInterface.
*
* @see ColorInterface::toGray()
* @return Gray
*/
public function toGray()
{
return $this->toRgb()->toGray();
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Color;
/**
* Color interface.
*/
interface ColorInterface
{
/**
* Converts the color to RGB.
*
* @return Rgb
*/
public function toRgb();
/**
* Converts the color to CMYK.
*
* @return Cmyk
*/
public function toCmyk();
/**
* Converts the color to gray.
*
* @return Gray
*/
public function toGray();
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Color;
use BaconQrCode\Exception;
/**
* Gray color.
*/
class Gray implements ColorInterface
{
/**
* Gray value.
*
* @var integer
*/
protected $gray;
/**
* Creates a new gray color.
*
* A low gray value means black, while a high value means white.
*
* @param integer $gray
*/
public function __construct($gray)
{
if ($gray < 0 || $gray > 100) {
throw new Exception\InvalidArgumentException('Gray must be between 0 and 100');
}
$this->gray = (int) $gray;
}
/**
* Returns the gray value.
*
* @return integer
*/
public function getGray()
{
return $this->gray;
}
/**
* toRgb(): defined by ColorInterface.
*
* @see ColorInterface::toRgb()
* @return Rgb
*/
public function toRgb()
{
return new Rgb($this->gray * 2.55, $this->gray * 2.55, $this->gray * 2.55);
}
/**
* toCmyk(): defined by ColorInterface.
*
* @see ColorInterface::toCmyk()
* @return Cmyk
*/
public function toCmyk()
{
return new Cmyk(0, 0, 0, 100 - $this->gray);
}
/**
* toGray(): defined by ColorInterface.
*
* @see ColorInterface::toGray()
* @return Gray
*/
public function toGray()
{
return $this;
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Color;
use BaconQrCode\Exception;
/**
* RGB color.
*/
class Rgb implements ColorInterface
{
/**
* Red value.
*
* @var integer
*/
protected $red;
/**
* Green value.
*
* @var integer
*/
protected $green;
/**
* Blue value.
*
* @var integer
*/
protected $blue;
/**
* Creates a new RGB color.
*
* @param integer $red
* @param integer $green
* @param integer $blue
*/
public function __construct($red, $green, $blue)
{
if ($red < 0 || $red > 255) {
throw new Exception\InvalidArgumentException('Red must be between 0 and 255');
}
if ($green < 0 || $green > 255) {
throw new Exception\InvalidArgumentException('Green must be between 0 and 255');
}
if ($blue < 0 || $blue > 255) {
throw new Exception\InvalidArgumentException('Blue must be between 0 and 255');
}
$this->red = (int) $red;
$this->green = (int) $green;
$this->blue = (int) $blue;
}
/**
* Returns the red value.
*
* @return integer
*/
public function getRed()
{
return $this->red;
}
/**
* Returns the green value.
*
* @return integer
*/
public function getGreen()
{
return $this->green;
}
/**
* Returns the blue value.
*
* @return integer
*/
public function getBlue()
{
return $this->blue;
}
/**
* Returns a hexadecimal string representation of the RGB value.
*
* @return string
*/
public function __toString()
{
return sprintf('%02x%02x%02x', $this->red, $this->green, $this->blue);
}
/**
* toRgb(): defined by ColorInterface.
*
* @see ColorInterface::toRgb()
* @return Rgb
*/
public function toRgb()
{
return $this;
}
/**
* toCmyk(): defined by ColorInterface.
*
* @see ColorInterface::toCmyk()
* @return Cmyk
*/
public function toCmyk()
{
$c = 1 - ($this->red / 255);
$m = 1 - ($this->green / 255);
$y = 1 - ($this->blue / 255);
$k = min($c, $m, $y);
return new Cmyk(
100 * ($c - $k) / (1 - $k),
100 * ($m - $k) / (1 - $k),
100 * ($y - $k) / (1 - $k),
100 * $k
);
}
/**
* toGray(): defined by ColorInterface.
*
* @see ColorInterface::toGray()
* @return Gray
*/
public function toGray()
{
return new Gray(($this->red * 0.21 + $this->green * 0.71 + $this->blue * 0.07) / 2.55);
}
}

View File

@@ -0,0 +1,338 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Renderer\Color;
use BaconQrCode\Renderer\Image\Decorator\DecoratorInterface;
use BaconQrCode\Exception;
/**
* Image renderer, supporting multiple backends.
*/
abstract class AbstractRenderer implements RendererInterface
{
/**
* Margin around the QR code, also known as quiet zone.
*
* @var integer
*/
protected $margin = 4;
/**
* Requested width of the rendered image.
*
* @var integer
*/
protected $width = 0;
/**
* Requested height of the rendered image.
*
* @var integer
*/
protected $height = 0;
/**
* Whether dimensions should be rounded down.
*
* @var boolean
*/
protected $roundDimensions = true;
/**
* Final width of the image.
*
* @var integer
*/
protected $finalWidth;
/**
* Final height of the image.
*
* @var integer
*/
protected $finalHeight;
/**
* Size of each individual block.
*
* @var integer
*/
protected $blockSize;
/**
* Background color.
*
* @var Color\ColorInterface
*/
protected $backgroundColor;
/**
* Whether dimensions should be rounded down
*
* @var boolean
*/
protected $floorToClosestDimension;
/**
* Foreground color.
*
* @var Color\ColorInterface
*/
protected $foregroundColor;
/**
* Decorators used on QR codes.
*
* @var array
*/
protected $decorators = array();
/**
* Sets the margin around the QR code.
*
* @param integer $margin
* @return AbstractRenderer
* @throws Exception\InvalidArgumentException
*/
public function setMargin($margin)
{
if ($margin < 0) {
throw new Exception\InvalidArgumentException('Margin must be equal to greater than 0');
}
$this->margin = (int) $margin;
return $this;
}
/**
* Gets the margin around the QR code.
*
* @return integer
*/
public function getMargin()
{
return $this->margin;
}
/**
* Sets the height around the renderd image.
*
* If the width is smaller than the matrix width plus padding, the renderer
* will automatically use that as the width instead of the specified one.
*
* @param integer $width
* @return AbstractRenderer
*/
public function setWidth($width)
{
$this->width = (int) $width;
return $this;
}
/**
* Gets the width of the rendered image.
*
* @return integer
*/
public function getWidth()
{
return $this->width;
}
/**
* Sets the height around the renderd image.
*
* If the height is smaller than the matrix height plus padding, the
* renderer will automatically use that as the height instead of the
* specified one.
*
* @param integer $height
* @return AbstractRenderer
*/
public function setHeight($height)
{
$this->height = (int) $height;
return $this;
}
/**
* Gets the height around the rendered image.
*
* @return integer
*/
public function getHeight()
{
return $this->height;
}
/**
* Sets whether dimensions should be rounded down.
*
* @param boolean $flag
* @return AbstractRenderer
*/
public function setRoundDimensions($flag)
{
$this->floorToClosestDimension = $flag;
return $this;
}
/**
* Gets whether dimensions should be rounded down.
*
* @return boolean
*/
public function shouldRoundDimensions()
{
return $this->floorToClosestDimension;
}
/**
* Sets background color.
*
* @param Color\ColorInterface $color
* @return AbstractRenderer
*/
public function setBackgroundColor(Color\ColorInterface $color)
{
$this->backgroundColor = $color;
return $this;
}
/**
* Gets background color.
*
* @return Color\ColorInterface
*/
public function getBackgroundColor()
{
if ($this->backgroundColor === null) {
$this->backgroundColor = new Color\Gray(100);
}
return $this->backgroundColor;
}
/**
* Sets foreground color.
*
* @param Color\ColorInterface $color
* @return AbstractRenderer
*/
public function setForegroundColor(Color\ColorInterface $color)
{
$this->foregroundColor = $color;
return $this;
}
/**
* Gets foreground color.
*
* @return Color\ColorInterface
*/
public function getForegroundColor()
{
if ($this->foregroundColor === null) {
$this->foregroundColor = new Color\Gray(0);
}
return $this->foregroundColor;
}
/**
* Adds a decorator to the renderer.
*
* @param DecoratorInterface $decorator
* @return AbstractRenderer
*/
public function addDecorator(DecoratorInterface $decorator)
{
$this->decorators[] = $decorator;
return $this;
}
/**
* render(): defined by RendererInterface.
*
* @see RendererInterface::render()
* @param QrCode $qrCode
* @return string
*/
public function render(QrCode $qrCode)
{
$input = $qrCode->getMatrix();
$inputWidth = $input->getWidth();
$inputHeight = $input->getHeight();
$qrWidth = $inputWidth + ($this->getMargin() << 1);
$qrHeight = $inputHeight + ($this->getMargin() << 1);
$outputWidth = max($this->getWidth(), $qrWidth);
$outputHeight = max($this->getHeight(), $qrHeight);
$multiple = (int) min($outputWidth / $qrWidth, $outputHeight / $qrHeight);
if ($this->shouldRoundDimensions()) {
$outputWidth -= $outputWidth % $multiple;
$outputHeight -= $outputHeight % $multiple;
}
// Padding includes both the quiet zone and the extra white pixels to
// accommodate the requested dimensions. For example, if input is 25x25
// the QR will be 33x33 including the quiet zone. If the requested size
// is 200x160, the multiple will be 4, for a QR of 132x132. These will
// handle all the padding from 100x100 (the actual QR) up to 200x160.
$leftPadding = (int) (($outputWidth - ($inputWidth * $multiple)) / 2);
$topPadding = (int) (($outputHeight - ($inputHeight * $multiple)) / 2);
// Store calculated parameters
$this->finalWidth = $outputWidth;
$this->finalHeight = $outputHeight;
$this->blockSize = $multiple;
$this->init();
$this->addColor('background', $this->getBackgroundColor());
$this->addColor('foreground', $this->getForegroundColor());
$this->drawBackground('background');
foreach ($this->decorators as $decorator) {
$decorator->preProcess(
$qrCode,
$this,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
}
for ($inputY = 0, $outputY = $topPadding; $inputY < $inputHeight; $inputY++, $outputY += $multiple) {
for ($inputX = 0, $outputX = $leftPadding; $inputX < $inputWidth; $inputX++, $outputX += $multiple) {
if ($input->get($inputX, $inputY) === 1) {
$this->drawBlock($outputX, $outputY, 'foreground');
}
}
}
foreach ($this->decorators as $decorator) {
$decorator->postProcess(
$qrCode,
$this,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
}
return $this->getByteStream();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image\Decorator;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Renderer\Image\RendererInterface;
/**
* Decorator interface.
*/
interface DecoratorInterface
{
/**
* Pre-process a QR code.
*
* @param QrCode $qrCode
* @param RendererInterface $renderer
* @param integer $outputWidth
* @param integer $outputHeight
* @param integer $leftPadding
* @param integer $topPadding
* @param integer $multiple
* @return void
*/
public function preProcess(
QrCode $qrCode,
RendererInterface $renderer,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
/**
* Post-process a QR code.
*
* @param QrCode $qrCode
* @param RendererInterface $renderer
* @param integer $outputWidth
* @param integer $outputHeight
* @param integer $leftPadding
* @param integer $topPadding
* @param integer $multiple
* @return void
*/
public function postProcess(
QrCode $qrCode,
RendererInterface $renderer,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
}

View File

@@ -0,0 +1,210 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image\Decorator;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Renderer\Image\RendererInterface;
use BaconQrCode\Renderer\Color;
/**
* Finder pattern decorator.
*/
class FinderPattern implements DecoratorInterface
{
/**
* @var Color\ColorInterface
*/
protected $innerColor;
/**
* @varColor\ColorInterface
*/
protected $outerColor;
/**
* Outer position detection pattern.
*
* @var array
*/
protected static $outerPositionDetectionPattern = array(
array(1, 1, 1, 1, 1, 1, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 0, 0, 0, 0, 0, 1),
array(1, 1, 1, 1, 1, 1, 1),
);
/**
* Inner position detection pattern.
*
* @var array
*/
protected static $innerPositionDetectionPattern = array(
array(0, 0, 0, 0, 0, 0, 0),
array(0, 0, 0, 0, 0, 0, 0),
array(0, 0, 1, 1, 1, 0, 0),
array(0, 0, 1, 1, 1, 0, 0),
array(0, 0, 1, 1, 1, 0, 0),
array(0, 0, 0, 0, 0, 0, 0),
array(0, 0, 0, 0, 0, 0, 0),
);
/**
* Sets outer color.
*
* @param Color\ColorInterface $color
* @return FinderPattern
*/
public function setOuterColor(Color\ColorInterface $color)
{
$this->outerColor = $color;
return $this;
}
/**
* Gets outer color.
*
* @return Color\ColorInterface
*/
public function getOuterColor()
{
if ($this->outerColor === null) {
$this->outerColor = new Color\Gray(100);
}
return $this->outerColor;
}
/**
* Sets inner color.
*
* @param Color\ColorInterface $color
* @return FinderPattern
*/
public function setInnerColor(Color\ColorInterface $color)
{
$this->innerColor = $color;
return $this;
}
/**
* Gets inner color.
*
* @return Color\ColorInterface
*/
public function getInnerColor()
{
if ($this->innerColor === null) {
$this->innerColor = new Color\Gray(0);
}
return $this->innerColor;
}
/**
* preProcess(): defined by DecoratorInterface.
*
* @see DecoratorInterface::preProcess()
* @param QrCode $qrCode
* @param RendererInterface $renderer
* @param integer $outputWidth
* @param integer $outputHeight
* @param integer $leftPadding
* @param integer $topPadding
* @param integer $multiple
* @return void
*/
public function preProcess(
QrCode $qrCode,
RendererInterface $renderer,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
) {
$matrix = $qrCode->getMatrix();
$positions = array(
array(0, 0),
array($matrix->getWidth() - 7, 0),
array(0, $matrix->getHeight() - 7),
);
foreach (self::$outerPositionDetectionPattern as $y => $row) {
foreach ($row as $x => $isSet) {
foreach ($positions as $position) {
$matrix->set($x + $position[0], $y + $position[1], 0);
}
}
}
}
/**
* postProcess(): defined by DecoratorInterface.
*
* @see DecoratorInterface::postProcess()
*
* @param QrCode $qrCode
* @param RendererInterface $renderer
* @param integer $outputWidth
* @param integer $outputHeight
* @param integer $leftPadding
* @param integer $topPadding
* @param integer $multiple
* @return void
*/
public function postProcess(
QrCode $qrCode,
RendererInterface $renderer,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
) {
$matrix = $qrCode->getMatrix();
$positions = array(
array(0, 0),
array($matrix->getWidth() - 7, 0),
array(0, $matrix->getHeight() - 7),
);
$renderer->addColor('finder-outer', $this->getOuterColor());
$renderer->addColor('finder-inner', $this->getInnerColor());
foreach (self::$outerPositionDetectionPattern as $y => $row) {
foreach ($row as $x => $isOuterSet) {
$isInnerSet = self::$innerPositionDetectionPattern[$y][$x];
if ($isOuterSet) {
foreach ($positions as $position) {
$renderer->drawBlock(
$leftPadding + $x * $multiple + $position[0] * $multiple,
$topPadding + $y * $multiple + $position[1] * $multiple,
'finder-outer'
);
}
}
if ($isInnerSet) {
foreach ($positions as $position) {
$renderer->drawBlock(
$leftPadding + $x * $multiple + $position[0] * $multiple,
$topPadding + $y * $multiple + $position[1] * $multiple,
'finder-inner'
);
}
}
}
}
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\Color\Rgb;
use BaconQrCode\Renderer\Color\Cmyk;
use BaconQrCode\Renderer\Color\Gray;
/**
* EPS backend.
*/
class Eps extends AbstractRenderer
{
/**
* EPS string.
*
* @var string
*/
protected $eps;
/**
* Colors used for drawing.
*
* @var array
*/
protected $colors = array();
/**
* Current color.
*
* @var string
*/
protected $currentColor;
/**
* init(): defined by RendererInterface.
*
* @see ImageRendererInterface::init()
* @return void
*/
public function init()
{
$this->eps = "%!PS-Adobe-3.0 EPSF-3.0\n"
. "%%BoundingBox: 0 0 " . $this->finalWidth . " " . $this->finalHeight . "\n"
. "/F { rectfill } def\n";
}
/**
* addColor(): defined by RendererInterface.
*
* @see ImageRendererInterface::addColor()
* @param string $id
* @param ColorInterface $color
* @return void
*/
public function addColor($id, ColorInterface $color)
{
if (
!$color instanceof Rgb
&& !$color instanceof Cmyk
&& !$color instanceof Gray
) {
$color = $color->toCmyk();
}
$this->colors[$id] = $color;
}
/**
* drawBackground(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBackground()
* @param string $colorId
* @return void
*/
public function drawBackground($colorId)
{
$this->setColor($colorId);
$this->eps .= "0 0 " . $this->finalWidth . " " . $this->finalHeight . " F\n";
}
/**
* drawBlock(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBlock()
* @param integer $x
* @param integer $y
* @param string $colorId
* @return void
*/
public function drawBlock($x, $y, $colorId)
{
$this->setColor($colorId);
$this->eps .= $x . " " . ($this->finalHeight - $y) . " " . $this->blockSize . " " . $this->blockSize . " F\n";
}
/**
* getByteStream(): defined by RendererInterface.
*
* @see ImageRendererInterface::getByteStream()
* @return string
*/
public function getByteStream()
{
return $this->eps;
}
/**
* Sets color to use.
*
* @param string $colorId
* @return void
*/
protected function setColor($colorId)
{
if ($colorId !== $this->currentColor) {
$color = $this->colors[$colorId];
if ($color instanceof Rgb) {
$this->eps .= sprintf(
"%F %F %F setrgbcolor\n",
$color->getRed() / 100,
$color->getGreen() / 100,
$color->getBlue() / 100
);
} elseif ($color instanceof Cmyk) {
$this->eps .= sprintf(
"%F %F %F %F setcmykcolor\n",
$color->getCyan() / 100,
$color->getMagenta() / 100,
$color->getYellow() / 100,
$color->getBlack() / 100
);
} elseif ($color instanceof Gray) {
$this->eps .= sprintf(
"%F setgray\n",
$color->getGray() / 100
);
}
$this->currentColor = $colorId;
}
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Exception;
use BaconQrCode\Renderer\Color\ColorInterface;
/**
* PNG backend.
*/
class Png extends AbstractRenderer
{
/**
* Image resource used when drawing.
*
* @var resource
*/
protected $image;
/**
* Colors used for drawing.
*
* @var array
*/
protected $colors = array();
/**
* init(): defined by RendererInterface.
*
* @see ImageRendererInterface::init()
* @return void
*/
public function init()
{
$this->image = imagecreatetruecolor($this->finalWidth, $this->finalHeight);
}
/**
* addColor(): defined by RendererInterface.
*
* @see ImageRendererInterface::addColor()
* @param string $id
* @param ColorInterface $color
* @return void
* @throws Exception\RuntimeException
*/
public function addColor($id, ColorInterface $color)
{
if ($this->image === null) {
throw new Exception\RuntimeException('Colors can only be added after init');
}
$color = $color->toRgb();
$this->colors[$id] = imagecolorallocate(
$this->image,
$color->getRed(),
$color->getGreen(),
$color->getBlue()
);
}
/**
* drawBackground(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBackground()
* @param string $colorId
* @return void
*/
public function drawBackground($colorId)
{
imagefill($this->image, 0, 0, $this->colors[$colorId]);
}
/**
* drawBlock(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBlock()
* @param integer $x
* @param integer $y
* @param string $colorId
* @return void
*/
public function drawBlock($x, $y, $colorId)
{
imagefilledrectangle(
$this->image,
$x,
$y,
$x + $this->blockSize - 1,
$y + $this->blockSize - 1,
$this->colors[$colorId]
);
}
/**
* getByteStream(): defined by RendererInterface.
*
* @see ImageRendererInterface::getByteStream()
* @return string
*/
public function getByteStream()
{
ob_start();
imagepng($this->image);
return ob_get_clean();
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Renderer\Color\ColorInterface;
use BaconQrCode\Renderer\RendererInterface as GeneralRendererInterface;
/**
* Renderer interface.
*/
interface RendererInterface extends GeneralRendererInterface
{
/**
* Initiates the drawing area.
*
* @return void
*/
public function init();
/**
* Adds a color to the drawing area.
*
* @param string $id
* @param ColorInterface $color
* @return void
*/
public function addColor($id, ColorInterface $color);
/**
* Draws the background.
*
* @param string $colorId
* @return void
*/
public function drawBackground($colorId);
/**
* Draws a block at a specified position.
*
* @param integer $x
* @param integer $y
* @param string $colorId
* @return void
*/
public function drawBlock($x, $y, $colorId);
/**
* Returns the byte stream representing the QR code.
*
* @return string
*/
public function getByteStream();
}

View File

@@ -0,0 +1,146 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Exception;
use BaconQrCode\Renderer\Color\ColorInterface;
use SimpleXMLElement;
/**
* SVG backend.
*/
class Svg extends AbstractRenderer
{
/**
* SVG resource.
*
* @var SimpleXMLElement
*/
protected $svg;
/**
* Colors used for drawing.
*
* @var array
*/
protected $colors = array();
/**
* Prototype IDs.
*
* @var array
*/
protected $prototypeIds = array();
/**
* init(): defined by RendererInterface.
*
* @see ImageRendererInterface::init()
* @return void
*/
public function init()
{
$this->svg = new SimpleXMLElement(
'<?xml version="1.0" encoding="UTF-8"?>'
. '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"/>'
);
$this->svg->addAttribute('version', '1.1');
$this->svg->addAttribute('width', $this->finalWidth . 'px');
$this->svg->addAttribute('height', $this->finalHeight . 'px');
$this->svg->addAttribute('viewBox', '0 0 ' . $this->finalWidth . ' ' . $this->finalHeight);
$this->svg->addChild('defs');
}
/**
* addColor(): defined by RendererInterface.
*
* @see ImageRendererInterface::addColor()
* @param string $id
* @param ColorInterface $color
* @return void
* @throws Exception\InvalidArgumentException
*/
public function addColor($id, ColorInterface $color)
{
$this->colors[$id] = (string) $color->toRgb();
}
/**
* drawBackground(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBackground()
* @param string $colorId
* @return void
*/
public function drawBackground($colorId)
{
$rect = $this->svg->addChild('rect');
$rect->addAttribute('x', 0);
$rect->addAttribute('y', 0);
$rect->addAttribute('width', $this->finalWidth);
$rect->addAttribute('height', $this->finalHeight);
$rect->addAttribute('fill', '#' . $this->colors[$colorId]);
}
/**
* drawBlock(): defined by RendererInterface.
*
* @see ImageRendererInterface::drawBlock()
* @param integer $x
* @param integer $y
* @param string $colorId
* @return void
*/
public function drawBlock($x, $y, $colorId)
{
$use = $this->svg->addChild('use');
$use->addAttribute('x', $x);
$use->addAttribute('y', $y);
$use->addAttribute(
'xlink:href',
$this->getRectPrototypeId($colorId),
'http://www.w3.org/1999/xlink'
);
}
/**
* getByteStream(): defined by RendererInterface.
*
* @see ImageRendererInterface::getByteStream()
* @return string
*/
public function getByteStream()
{
return $this->svg->asXML();
}
/**
* Get the prototype ID for a color.
*
* @param integer $colorId
* @return string
*/
protected function getRectPrototypeId($colorId)
{
if (!isset($this->prototypeIds[$colorId])) {
$id = 'r' . dechex(count($this->prototypeIds));
$rect = $this->svg->defs->addChild('rect');
$rect->addAttribute('id', $id);
$rect->addAttribute('width', $this->blockSize);
$rect->addAttribute('height', $this->blockSize);
$rect->addAttribute('fill', '#' . $this->colors[$colorId]);
$this->prototypeIds[$colorId] = '#' . $id;
}
return $this->prototypeIds[$colorId];
}
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer;
use BaconQrCode\Encoder\QrCode;
/**
* Renderer interface.
*/
interface RendererInterface
{
/**
* Renders a QR code.
*
* @param QrCode $qrCode
* @return string
*/
public function render(QrCode $qrCode);
}

View File

@@ -0,0 +1,91 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Text;
use BaconQrCode\Encoder\QrCode;
/**
* Html renderer.
*/
class Html extends Plain
{
/**
* HTML CSS class attribute value.
*
* @var string
*/
protected $class = '';
/**
* HTML CSS style definition for the code element.
*
* @var string
*/
protected $style = 'font-family: monospace; line-height: 0.65em; letter-spacing: -1px';
/**
* Set CSS class name.
*
* @param string $class
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* Get CSS class name.
*
* @return string
*/
public function getClass()
{
return $this->class;
}
/**
* Set CSS style value.
*
* @param string $style
*/
public function setStyle($style)
{
$this->style = $style;
}
/**
* Get CSS style value.
*
* @return string
*/
public function getStyle()
{
return $this->style;
}
/**
* render(): defined by RendererInterface.
*
* @see RendererInterface::render()
* @param QrCode $qrCode
* @return string
*/
public function render(QrCode $qrCode)
{
$textCode = parent::render($qrCode);
$result = '<pre'
. ' style="' . htmlspecialchars($this->style, ENT_QUOTES, 'utf-8') . '"'
. ' class="' . htmlspecialchars($this->class, ENT_QUOTES, 'utf-8') . '"'
. '>' . $textCode . '</pre>';
return $result;
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Text;
use BaconQrCode\Exception;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Renderer\RendererInterface;
/**
* Plaintext renderer.
*/
class Plain implements RendererInterface
{
/**
* Margin around the QR code, also known as quiet zone.
*
* @var integer
*/
protected $margin = 1;
/**
* Char used for full block.
*
* UTF-8 FULL BLOCK (U+2588)
*
* @var string
* @link http://www.fileformat.info/info/unicode/char/2588/index.htm
*/
protected $fullBlock = "\xE2\x96\x88";
/**
* Char used for empty space
*
* @var string
*/
protected $emptyBlock = ' ';
/**
* Set char used as full block (occupied space, "black").
*
* @param string $fullBlock
*/
public function setFullBlock($fullBlock)
{
$this->fullBlock = $fullBlock;
}
/**
* Get char used as full block (occupied space, "black").
*
* @return string
*/
public function getFullBlock()
{
return $this->fullBlock;
}
/**
* Set char used as empty block (empty space, "white").
*
* @param string $emptyBlock
*/
public function setEmptyBlock($emptyBlock)
{
$this->emptyBlock = $emptyBlock;
}
/**
* Get char used as empty block (empty space, "white").
*
* @return string
*/
public function getEmptyBlock()
{
return $this->emptyBlock;
}
/**
* Sets the margin around the QR code.
*
* @param integer $margin
* @return AbstractRenderer
* @throws Exception\InvalidArgumentException
*/
public function setMargin($margin)
{
if ($margin < 0) {
throw new Exception\InvalidArgumentException('Margin must be equal to greater than 0');
}
$this->margin = (int) $margin;
return $this;
}
/**
* Gets the margin around the QR code.
*
* @return integer
*/
public function getMargin()
{
return $this->margin;
}
/**
* render(): defined by RendererInterface.
*
* @see RendererInterface::render()
* @param QrCode $qrCode
* @return string
*/
public function render(QrCode $qrCode)
{
$result = '';
$matrix = $qrCode->getMatrix();
$width = $matrix->getWidth();
// Top margin
for ($x = 0; $x < $this->margin; $x++) {
$result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n";
}
// Body
$array = $matrix->getArray();
foreach ($array as $row) {
$result .= str_repeat($this->emptyBlock, $this->margin); // left margin
foreach ($row as $byte) {
$result .= $byte ? $this->fullBlock : $this->emptyBlock;
}
$result .= str_repeat($this->emptyBlock, $this->margin); // right margin
$result .= "\n";
}
// Bottom margin
for ($x = 0; $x < $this->margin; $x++) {
$result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n";
}
return $result;
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Encoder\Encoder;
use BaconQrCode\Exception;
use BaconQrCode\Renderer\RendererInterface;
/**
* QR code writer.
*/
class Writer
{
/**
* Renderer instance.
*
* @var RendererInterface
*/
protected $renderer;
/**
* Creates a new writer with a specific renderer.
*
* @param RendererInterface $renderer
*/
public function __construct(RendererInterface $renderer)
{
$this->renderer = $renderer;
}
/**
* Sets the renderer used to create a byte stream.
*
* @param RendererInterface $renderer
* @return Writer
*/
public function setRenderer(RendererInterface $renderer)
{
$this->renderer = $renderer;
return $this;
}
/**
* Gets the renderer used to create a byte stream.
*
* @return RendererInterface
*/
public function getRenderer()
{
return $this->renderer;
}
/**
* Writes QR code and returns it as string.
*
* Content is a string which *should* be encoded in UTF-8, in case there are
* non ASCII-characters present.
*
* @param string $content
* @param string $encoding
* @param integer $ecLevel
* @return string
* @throws Exception\InvalidArgumentException
*/
public function writeString(
$content,
$encoding = Encoder::DEFAULT_BYTE_MODE_ECODING,
$ecLevel = ErrorCorrectionLevel::L
) {
if (strlen($content) === 0) {
throw new Exception\InvalidArgumentException('Found empty contents');
}
$qrCode = Encoder::encode($content, new ErrorCorrectionLevel($ecLevel), $encoding);
return $this->getRenderer()->render($qrCode);
}
/**
* Writes QR code to a file.
*
* @see Writer::writeString()
* @param string $content
* @param string $filename
* @param string $encoding
* @param integer $ecLevel
* @return void
*/
public function writeFile(
$content,
$filename,
$encoding = Encoder::DEFAULT_BYTE_MODE_ECODING,
$ecLevel = ErrorCorrectionLevel::L
) {
file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel));
}
}

View File

@@ -0,0 +1,197 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class BitArrayTest extends TestCase
{
public function testGetSet()
{
$array = new BitArray(33);
for ($i = 0; $i < 33; $i++) {
$this->assertFalse($array->get($i));
$array->set($i);
$this->assertTrue($array->get($i));
}
}
public function testGetNextSet1()
{
$array = new BitArray(32);
for ($i = 0; $i < $array->getSize(); $i++) {
$this->assertEquals($i, 32, '', $array->getNextSet($i));
}
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); $i++) {
$this->assertEquals($i, 33, '', $array->getNextSet($i));
}
}
public function testGetNextSet2()
{
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); $i++) {
$this->assertEquals($i, $i <= 31 ? 31 : 33, '', $array->getNextSet($i));
}
$array = new BitArray(33);
for ($i = 0; $i < $array->getSize(); $i++) {
$this->assertEquals($i, 32, '', $array->getNextSet($i));
}
}
public function testGetNextSet3()
{
$array = new BitArray(63);
$array->set(31);
$array->set(32);
for ($i = 0; $i < $array->getSize(); $i++) {
if ($i <= 31) {
$expected = 31;
} elseif ($i <= 32) {
$expected = 32;
} else {
$expected = 63;
}
$this->assertEquals($i, $expected, '', $array->getNextSet($i));
}
}
public function testGetNextSet4()
{
$array = new BitArray(63);
$array->set(33);
$array->set(40);
for ($i = 0; $i < $array->getSize(); $i++) {
if ($i <= 33) {
$expected = 33;
} elseif ($i <= 40) {
$expected = 40;
} else {
$expected = 63;
}
$this->assertEquals($i, $expected, '', $array->getNextSet($i));
}
}
public function testGetNextSet5()
{
mt_srand(hexdec('deadbeef'));
for ($i = 0; $i < 10; $i++) {
$array = new BitArray(mt_rand(1, 100));
$numSet = mt_rand(0, 19);
for ($j = 0; $j < $numSet; $j++) {
$array->set(mt_rand(0, $array->getSize() - 1));
}
$numQueries = mt_rand(0, 19);
for ($j = 0; $j < $numQueries; $j++) {
$query = mt_rand(0, $array->getSize() - 1);
$expected = $query;
while ($expected < $array->getSize() && !$array->get($expected)) {
$expected++;
}
$actual = $array->getNextSet($query);
if ($actual !== $expected) {
$array->getNextSet($query);
}
$this->assertEquals($expected, $actual);
}
}
}
public function testSetBulk()
{
$array = new BitArray(64);
$array->setBulk(32, 0xFFFF0000);
for ($i = 0; $i < 48; $i++) {
$this->assertFalse($array->get($i));
}
for ($i = 48; $i < 64; $i++) {
$this->assertTrue($array->get($i));
}
}
public function testClear()
{
$array = new BitArray(32);
for ($i = 0; $i < 32; $i++) {
$array->set($i);
}
$array->clear();
for ($i = 0; $i < 32; $i++) {
$this->assertFalse($array->get($i));
}
}
public function testGetArray()
{
$array = new BitArray(64);
$array->set(0);
$array->set(63);
$ints = $array->getBitArray();
$this->assertEquals(1, $ints[0]);
$this->assertEquals(0x80000000, $ints[1]);
}
public function testIsRange()
{
$array = new BitArray(64);
$this->assertTrue($array->isRange(0, 64, false));
$this->assertFalse($array->isRange(0, 64, true));
$array->set(32);
$this->assertTrue($array->isRange(32, 33, true));
$array->set(31);
$this->assertTrue($array->isRange(31, 33, true));
$array->set(34);
$this->assertFalse($array->isRange(31, 35, true));
for ($i = 0; $i < 31; $i++) {
$array->set($i);
}
$this->assertTrue($array->isRange(0, 33, true));
for ($i = 33; $i < 64; $i++) {
$array->set($i);
}
$this->assertTrue($array->isRange(0, 64, true));
$this->assertFalse($array->isRange(0, 64, false));
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class BitMatrixTest extends TestCase
{
public function testGetSet()
{
$matrix = new BitMatrix(33);
$this->assertEquals(33, $matrix->getHeight());
for ($y = 0; $y < 33; $y++) {
for ($x = 0; $x < 33; $x++) {
if ($y * $x % 3 === 0) {
$matrix->set($x, $y);
}
}
}
for ($y = 0; $y < 33; $y++) {
for ($x = 0; $x < 33; $x++) {
$this->assertEquals($x * $y % 3 === 0, $matrix->get($x, $y));
}
}
}
public function testSetRegion()
{
$matrix = new BitMatrix(5);
$matrix->setRegion(1, 1, 3, 3);
for ($y = 0; $y < 5; $y++) {
for ($x = 0; $x < 5; $x++) {
$this->assertEquals($y >= 1 && $y <= 3 && $x >= 1 && $x <= 3, $matrix->get($x, $y));
}
}
}
public function testRectangularMatrix()
{
$matrix = new BitMatrix(75, 20);
$this->assertEquals(75, $matrix->getWidth());
$this->assertEquals(20, $matrix->getHeight());
$matrix->set(10, 0);
$matrix->set(11, 1);
$matrix->set(50, 2);
$matrix->set(51, 3);
$matrix->flip(74, 4);
$matrix->flip(0, 5);
$this->assertTrue($matrix->get(10, 0));
$this->assertTrue($matrix->get(11, 1));
$this->assertTrue($matrix->get(50, 2));
$this->assertTrue($matrix->get(51, 3));
$this->assertTrue($matrix->get(74, 4));
$this->assertTrue($matrix->get(0, 5));
$matrix->flip(50, 2);
$matrix->flip(51, 3);
$this->assertFalse($matrix->get(50, 2));
$this->assertFalse($matrix->get(51, 3));
}
public function testRectangularSetRegion()
{
$matrix = new BitMatrix(320, 240);
$this->assertEquals(320, $matrix->getWidth());
$this->assertEquals(240, $matrix->getHeight());
$matrix->setRegion(105, 22, 80, 12);
for ($y = 0; $y < 240; $y++) {
for ($x = 0; $x < 320; $x++) {
$this->assertEquals($y >= 22 && $y < 34 && $x >= 105 && $x < 185, $matrix->get($x, $y));
}
}
}
public function testGetRow()
{
$matrix = new BitMatrix(102, 5);
for ($x = 0; $x < 102; $x++) {
if ($x & 3 === 0) {
$matrix->set($x, 2);
}
}
$array1 = $matrix->getRow(2, null);
$this->assertEquals(102, $array1->getSize());
$array2 = new BitArray(60);
$array2 = $matrix->getRow(2, $array2);
$this->assertEquals(102, $array2->getSize());
$array3 = new BitArray(200);
$array3 = $matrix->getRow(2, $array3);
$this->assertEquals(200, $array3->getSize());
for ($x = 0; $x < 102; $x++) {
$on = ($x & 3 === 0);
$this->assertEquals($on, $array1->get($x));
$this->assertEquals($on, $array2->get($x));
$this->assertEquals($on, $array3->get($x));
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class BitUtilsTest extends TestCase
{
public function testUnsignedRightShift()
{
$this->assertEquals(1, BitUtils::unsignedRightShift(1, 0));
$this->assertEquals(1, BitUtils::unsignedRightShift(10, 3));
$this->assertEquals(536870910, BitUtils::unsignedRightShift(-10, 3));
}
public function testNumberOfTrailingZeros()
{
$this->assertEquals(32, BitUtils::numberOfTrailingZeros(0));
$this->assertEquals(1, BitUtils::numberOfTrailingZeros(10));
$this->assertEquals(0, BitUtils::numberOfTrailingZeros(15));
$this->assertEquals(2, BitUtils::numberOfTrailingZeros(20));
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class ErrorCorrectionLevelTest extends TestCase
{
public function testCreationThrowsNoException()
{
new ErrorCorrectionLevel(ErrorCorrectionLevel::M);
new ErrorCorrectionLevel(ErrorCorrectionLevel::L);
new ErrorCorrectionLevel(ErrorCorrectionLevel::H);
new ErrorCorrectionLevel(ErrorCorrectionLevel::Q);
}
public function testBitsMatchConstants()
{
$this->assertEquals(0x0, ErrorCorrectionLevel::M);
$this->assertEquals(0x1, ErrorCorrectionLevel::L);
$this->assertEquals(0x2, ErrorCorrectionLevel::H);
$this->assertEquals(0x3, ErrorCorrectionLevel::Q);
}
public function testInvalidErrorCorrectionLevelThrowsException()
{
$this->setExpectedException(
'BaconQrCode\Exception\UnexpectedValueException',
'Value not a const in enum BaconQrCode\Common\ErrorCorrectionLevel'
);
new ErrorCorrectionLevel(4);
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class FormatInformationTest extends TestCase
{
protected $maskedTestFormatInfo = 0x2bed;
protected $unmaskedTestFormatInfo;
public function setUp()
{
$this->unmaskedTestFormatInfo = $this->maskedTestFormatInfo ^ 0x5412;
}
public function testBitsDiffering()
{
$this->assertEquals(0, FormatInformation::numBitsDiffering(1, 1));
$this->assertEquals(1, FormatInformation::numBitsDiffering(0, 2));
$this->assertEquals(2, FormatInformation::numBitsDiffering(1, 2));
$this->assertEquals(32, FormatInformation::numBitsDiffering(-1, 0));
}
public function testDecode()
{
$expected = FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo,
$this->maskedTestFormatInfo
);
$this->assertNotNull($expected);
$this->assertEquals(7, $expected->getDataMask());
$this->assertEquals(ErrorCorrectionLevel::Q, $expected->getErrorCorrectionLevel()->get());
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
$this->unmaskedTestFormatInfo,
$this->maskedTestFormatInfo
)
);
}
public function testDecodeWithBitDifference()
{
$expected = FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo,
$this->maskedTestFormatInfo
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo ^ 0x1,
$this->maskedTestFormatInfo ^ 0x1
)
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo ^ 0x3,
$this->maskedTestFormatInfo ^ 0x3
)
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo ^ 0x7,
$this->maskedTestFormatInfo ^ 0x7
)
);
$this->assertNull(
FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo ^ 0xf,
$this->maskedTestFormatInfo ^ 0xf
)
);
}
public function testDecodeWithMisRead()
{
$expected = FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo,
$this->maskedTestFormatInfo
);
$this->assertEquals(
$expected,
FormatInformation::decodeFormatInformation(
$this->maskedTestFormatInfo ^ 0x3,
$this->maskedTestFormatInfo ^ 0xf
)
);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class ModeTest extends TestCase
{
public function testCreationThrowsNoException()
{
new Mode(Mode::TERMINATOR);
new Mode(Mode::NUMERIC);
new Mode(Mode::ALPHANUMERIC);
new Mode(Mode::BYTE);
new Mode(Mode::KANJI);
}
public function testBitsMatchConstants()
{
$this->assertEquals(0x0, Mode::TERMINATOR);
$this->assertEquals(0x1, Mode::NUMERIC);
$this->assertEquals(0x2, Mode::ALPHANUMERIC);
$this->assertEquals(0x4, Mode::BYTE);
$this->assertEquals(0x8, Mode::KANJI);
}
public function testInvalidModeThrowsException()
{
$this->setExpectedException(
'BaconQrCode\Exception\UnexpectedValueException',
'Value not a const in enum BaconQrCode\Common\Mode'
);
new Mode(10);
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
use SplFixedArray;
class ReedSolomonTest extends TestCase
{
public static function tabProvider()
{
return array(
array(2, 0x7, 1, 1, 1),
array(3, 0xb, 1, 1, 2),
array(4, 0x13, 1, 1, 4),
array(5, 0x25, 1, 1, 6),
array(6, 0x43, 1, 1, 8),
array(7, 0x89, 1, 1, 10),
array(8, 0x11d, 1, 1, 32),
);
}
/**
* @dataProvider tabProvider
* @param integer $symbolSize
* @param integer $generatorPoly
* @param integer $firstRoot
* @param integer $primitive
* @param integer $numRoots
* @return void
*/
public function testCodec($symbolSize, $generatorPoly, $firstRoot, $primitive, $numRoots)
{
mt_srand(0xdeadbeef);
$blockSize = (1 << $symbolSize) - 1;
$dataSize = $blockSize - $numRoots;
$codec = new ReedSolomonCodec($symbolSize, $generatorPoly, $firstRoot, $primitive, $numRoots, 0);
for ($errors = 0; $errors <= $numRoots / 2; $errors++) {
// Load block with random data and encode
$block = SplFixedArray::fromArray(array_fill(0, $blockSize, 0), false);
for ($i = 0; $i < $dataSize; $i++) {
$block[$i] = mt_rand(0, $blockSize);
}
// Make temporary copy
$tBlock = clone $block;
$parity = SplFixedArray::fromArray(array_fill(0, $numRoots, 0), false);
$errorLocations = SplFixedArray::fromArray(array_fill(0, $blockSize, 0), false);
$erasures = array();
// Create parity
$codec->encode($block, $parity);
// Copy parity into test blocks
for ($i = 0; $i < $numRoots; $i++) {
$block[$i + $dataSize] = $parity[$i];
$tBlock[$i + $dataSize] = $parity[$i];
}
// Seed with errors
for ($i = 0; $i < $errors; $i++) {
$errorValue = mt_rand(1, $blockSize);
do {
$errorLocation = mt_rand(0, $blockSize);
} while ($errorLocations[$errorLocation] !== 0);
$errorLocations[$errorLocation] = 1;
if (mt_rand(0, 1)) {
$erasures[] = $errorLocation;
}
$tBlock[$errorLocation] ^= $errorValue;
}
$erasures = SplFixedArray::fromArray($erasures, false);
// Decode the errored block
$foundErrors = $codec->decode($tBlock, $erasures);
if ($errors > 0 && $foundErrors === null) {
$this->assertEquals($block, $tBlock, 'Decoder failed to correct errors');
}
$this->assertEquals($errors, $foundErrors, 'Found errors do not equal expected errors');
for ($i = 0; $i < $foundErrors; $i++) {
if ($errorLocations[$erasures[$i]] === 0) {
$this->fail(sprintf('Decoder indicates error in location %d without error', $erasures[$i]));
}
}
$this->assertEquals($block, $tBlock, 'Decoder did not correct errors');
}
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Common;
use PHPUnit_Framework_TestCase as TestCase;
class VersionTest extends TestCase
{
public static function versionProvider()
{
$array = array();
for ($i = 1; $i <= 40; $i++) {
$array[] = array($i, 4 * $i + 17);
}
return $array;
}
public static function decodeInformationProvider()
{
return array(
array(7, 0x07c94),
array(12, 0x0c762),
array(17, 0x1145d),
array(22, 0x168c9),
array(27, 0x1b08e),
array(32, 0x209d5),
);
}
/**
* @dataProvider versionProvider
* @param integer $versionNumber
* @param integer $dimension
*/
public function testVersionForNumber($versionNumber, $dimension)
{
$version = Version::getVersionForNumber($versionNumber);
$this->assertNotNull($version);
$this->assertEquals($versionNumber, $version->getVersionNumber());
$this->assertNotNull($version->getAlignmentPatternCenters());
if ($versionNumber > 1) {
$this->assertTrue(count($version->getAlignmentPatternCenters()) > 0);
}
$this->assertEquals($dimension, $version->getDimensionForVersion());
$this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::H)));
$this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::L)));
$this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::M)));
$this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::Q)));
$this->assertNotNull($version->buildFunctionPattern());
}
/**
* @dataProvider versionProvider
* @param integer $versionNumber
* @param integer $dimension
*/
public function testGetProvisionalVersionForDimension($versionNumber, $dimension)
{
$this->assertEquals(
$versionNumber,
Version::getProvisionalVersionForDimension($dimension)->getVersionNumber()
);
}
/**
* @dataProvider decodeInformationProvider
* @param integer $expectedVersion
* @param integer $mask
*/
public function testDecodeVersionInformation($expectedVersion, $mask)
{
$version = Version::decodeVersionInformation($mask);
$this->assertNotNull($version);
$this->assertEquals($expectedVersion, $version->getVersionNumber());
}
}

View File

@@ -0,0 +1,468 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Mode;
use BaconQrCode\Common\Version;
use PHPUnit_Framework_TestCase as TestCase;
use ReflectionClass;
use ReflectionMethod;
use SplFixedArray;
class EncoderTest extends TestCase
{
protected $methods = array();
public function setUp()
{
// Hack to be able to test protected methods
$reflection = new ReflectionClass('BaconQrCode\Encoder\Encoder');
foreach ($reflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
$method->setAccessible(true);
$this->methods[$method->getName()] = $method;
}
}
public function testGetAlphanumericCode()
{
// The first ten code points are numbers.
for ($i = 0; $i < 10; $i++) {
$this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('0') + $i));
}
// The next 26 code points are capital alphabet letters.
for ($i = 10; $i < 36; $i++) {
// The first ten code points are numbers
$this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('A') + $i - 10));
}
// Others are symbol letters.
$this->assertEquals(36, $this->methods['getAlphanumericCode']->invoke(null, ' '));
$this->assertEquals(37, $this->methods['getAlphanumericCode']->invoke(null, '$'));
$this->assertEquals(38, $this->methods['getAlphanumericCode']->invoke(null, '%'));
$this->assertEquals(39, $this->methods['getAlphanumericCode']->invoke(null, '*'));
$this->assertEquals(40, $this->methods['getAlphanumericCode']->invoke(null, '+'));
$this->assertEquals(41, $this->methods['getAlphanumericCode']->invoke(null, '-'));
$this->assertEquals(42, $this->methods['getAlphanumericCode']->invoke(null, '.'));
$this->assertEquals(43, $this->methods['getAlphanumericCode']->invoke(null, '/'));
$this->assertEquals(44, $this->methods['getAlphanumericCode']->invoke(null, ':'));
// Should return -1 for other letters.
$this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, 'a'));
$this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, '#'));
$this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, "\0"));
}
public function testChooseMode()
{
// Numeric mode
$this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0')->get());
$this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789')->get());
// Alphanumeric mode
$this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, 'A')->get());
$this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:')->get());
// 8-bit byte mode
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, 'a')->get());
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '#')->get());
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '')->get());
// AIUE in Hiragana in SHIFT-JIS
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x8\xa\x8\xa\x8\xa\x8\xa6")->get());
// Nihon in Kanji in SHIFT-JIS
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x9\xf\x9\x7b")->get());
// Sou-Utso-Byou in Kanji in SHIFT-JIS
$this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\xe\x4\x9\x5\x9\x61")->get());
}
public function testEncode()
{
$qrCode = Encoder::encode('ABCDEF', new ErrorCorrectionLevel(ErrorCorrectionLevel::H));
$expected = "<<\n"
. " mode: ALPHANUMERIC\n"
. " ecLevel: H\n"
. " version: 1\n"
. " maskPattern: 0\n"
. " matrix:\n"
. " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n"
. " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n"
. " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n"
. " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n"
. " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n"
. " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n"
. " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n"
. " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n"
. ">>\n";
$this->assertEquals($expected, $qrCode->__toString());
}
public function testSimpleUtf8Eci()
{
$qrCode = Encoder::encode('hello', new ErrorCorrectionLevel(ErrorCorrectionLevel::H), 'utf-8');
$expected = "<<\n"
. " mode: BYTE\n"
. " ecLevel: H\n"
. " version: 1\n"
. " maskPattern: 3\n"
. " matrix:\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n"
. " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n"
. " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n"
. " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n"
. " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n"
. " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n"
. " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n"
. " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n"
. ">>\n";
$this->assertEquals($expected, $qrCode->__toString());
}
public function testAppendModeInfo()
{
$bits = new BitArray();
$this->methods['appendModeInfo']->invoke(null, new Mode(Mode::NUMERIC), $bits);
$this->assertEquals(' ...X', $bits->__toString());
}
public function testAppendLengthInfo()
{
// 1 letter (1/1), 10 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
1,
Version::getVersionForNumber(1),
new Mode(Mode::NUMERIC),
$bits
);
$this->assertEquals(' ........ .X', $bits->__toString());
// 2 letters (2/1), 11 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
2,
Version::getVersionForNumber(10),
new Mode(Mode::ALPHANUMERIC),
$bits
);
$this->assertEquals(' ........ .X.', $bits->__toString());
// 255 letters (255/1), 16 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
255,
Version::getVersionForNumber(27),
new Mode(Mode::BYTE),
$bits
);
$this->assertEquals(' ........ XXXXXXXX', $bits->__toString());
// 512 letters (1024/2), 12 bits.
$bits = new BitArray();
$this->methods['appendLengthInfo']->invoke(
null,
512,
Version::getVersionForNumber(40),
new Mode(Mode::KANJI),
$bits
);
$this->assertEquals(' ..X..... ....', $bits->__toString());
}
public function testAppendBytes()
{
// Should use appendNumericBytes.
// 1 = 01 = 0001 in 4 bits.
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'1',
new Mode(Mode::NUMERIC),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals(' ...X', $bits->__toString());
// Should use appendAlphaNumericBytes.
// A = 10 = 0xa = 001010 in 6 bits.
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'A',
new Mode(Mode::ALPHANUMERIC),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals(' ..X.X.', $bits->__toString());
// Should use append8BitBytes.
// 0x61, 0x62, 0x63
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
'abc',
new Mode(Mode::BYTE),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString());
// Should use appendKanjiBytes.
// 0x93, 0x5f
$bits = new BitArray();
$this->methods['appendBytes']->invoke(
null,
"\x93\x5f",
new Mode(Mode::KANJI),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString());
// Lower letters such as 'a' cannot be encoded in alphanumeric mode.
$this->setExpectedException(
'BaconQrCode\Exception\WriterException',
'Invalid alphanumeric code'
);
$this->methods['appendBytes']->invoke(
null,
"a",
new Mode(Mode::ALPHANUMERIC),
$bits,
Encoder::DEFAULT_BYTE_MODE_ECODING
);
}
public function testTerminateBits()
{
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 0, $bits);
$this->assertEquals('', $bits->__toString());
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertEquals(' ........', $bits->__toString());
$bits = new BitArray();
$bits->appendBits(0, 3);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertEquals(' ........', $bits->__toString());
$bits = new BitArray();
$bits->appendBits(0, 5);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertEquals(' ........', $bits->__toString());
$bits = new BitArray();
$bits->appendBits(0, 8);
$this->methods['terminateBits']->invoke(null, 1, $bits);
$this->assertEquals(' ........', $bits->__toString());
$bits = new BitArray();
$this->methods['terminateBits']->invoke(null, 2, $bits);
$this->assertEquals(' ........ XXX.XX..', $bits->__toString());
$bits = new BitArray();
$bits->appendBits(0, 1);
$this->methods['terminateBits']->invoke(null, 3, $bits);
$this->assertEquals(' ........ XXX.XX.. ...X...X', $bits->__toString());
}
public function testGetNumDataBytesAndNumEcBytesForBlockId()
{
// Version 1-H.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 26, 9, 1, 0);
$this->assertEquals(9, $numDataBytes);
$this->assertEquals(17, $numEcBytes);
// Version 3-H. 2 blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 0);
$this->assertEquals(13, $numDataBytes);
$this->assertEquals(22, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 1);
$this->assertEquals(13, $numDataBytes);
$this->assertEquals(22, $numEcBytes);
// Version 7-H. (4 + 1) blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 0);
$this->assertEquals(13, $numDataBytes);
$this->assertEquals(26, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 4);
$this->assertEquals(14, $numDataBytes);
$this->assertEquals(26, $numEcBytes);
// Version 40-H. (20 + 61) blocks.
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 0);
$this->assertEquals(15, $numDataBytes);
$this->assertEquals(30, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 20);
$this->assertEquals(16, $numDataBytes);
$this->assertEquals(30, $numEcBytes);
list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 80);
$this->assertEquals(16, $numDataBytes);
$this->assertEquals(30, $numEcBytes);
}
public function testInterleaveWithEcBytes()
{
$dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236), false);
$in = new BitArray();
foreach ($dataBytes as $dataByte) {
$in->appendBits($dataByte, 8);
}
$outBits = $this->methods['interleaveWithEcBytes']->invoke(null, $in, 26, 9, 1);
$expected = SplFixedArray::fromArray(array(
// Data bytes.
32, 65, 205, 69, 41, 220, 46, 128, 236,
// Error correction bytes.
42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61,
), false);
$out = $outBits->toBytes(0, count($expected));
$this->assertEquals($expected, $out);
}
public function testAppendNumericBytes()
{
// 1 = 01 = 0001 in 4 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '1', $bits);
$this->assertEquals(' ...X', $bits->__toString());
// 12 = 0xc = 0001100 in 7 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '12', $bits);
$this->assertEquals(' ...XX..', $bits->__toString());
// 123 = 0x7b = 0001111011 in 10 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '123', $bits);
$this->assertEquals(' ...XXXX. XX', $bits->__toString());
// 1234 = "123" + "4" = 0001111011 + 0100 in 14 bits.
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '1234', $bits);
$this->assertEquals(' ...XXXX. XX.X..', $bits->__toString());
// Empty
$bits = new BitArray();
$this->methods['appendNumericBytes']->invoke(null, '', $bits);
$this->assertEquals('', $bits->__toString());
}
public function testAppendAlphanumericBytes()
{
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'A', $bits);
$this->assertEquals(' ..X.X.', $bits->__toString());
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'AB', $bits);
$this->assertEquals(' ..XXX..X X.X', $bits->__toString());
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'ABC', $bits);
$this->assertEquals(' ..XXX..X X.X..XX. .', $bits->__toString());
// Empty
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, '', $bits);
$this->assertEquals('', $bits->__toString());
// Invalid data
$this->setExpectedException('BaconQrCode\Exception\WriterException', 'Invalid alphanumeric code');
$bits = new BitArray();
$this->methods['appendAlphanumericBytes']->invoke(null, 'abc', $bits);
}
public function testAppend8BitBytes()
{
// 0x61, 0x62, 0x63
$bits = new BitArray();
$this->methods['append8BitBytes']->invoke(null, 'abc', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING);
$this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString());
// Empty
$bits = new BitArray();
$this->methods['append8BitBytes']->invoke(null, '', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING);
$this->assertEquals('', $bits->__toString());
}
public function testAppendKanjiBytes()
{
// Numbers are from page 21 of JISX0510:2004
$bits = new BitArray();
$this->methods['appendKanjiBytes']->invoke(null, "\x93\x5f", $bits);
$this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString());
$this->methods['appendKanjiBytes']->invoke(null, "\xe4\xaa", $bits);
$this->assertEquals(' .XX.XX.. XXXXXXX. X.X.X.X. X.', $bits->__toString());
}
public function testGenerateEcBytes()
{
// Numbers are from http://www.swetake.com/qr/qr3.html and
// http://www.swetake.com/qr/qr9.html
$dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236), false);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17);
$expected = SplFixedArray::fromArray(array(42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61), false);
$this->assertEquals($expected, $ecBytes);
$dataBytes = SplFixedArray::fromArray(array(67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214), false);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 18);
$expected = SplFixedArray::fromArray(array(175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187), false);
$this->assertEquals($expected, $ecBytes);
// High-order zero coefficient case.
$dataBytes = SplFixedArray::fromArray(array(32, 49, 205, 69, 42, 20, 0, 236, 17), false);
$ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17);
$expected = SplFixedArray::fromArray(array(0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213), false);
$this->assertEquals($expected, $ecBytes);
}
}

View File

@@ -0,0 +1,281 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use PHPUnit_Framework_TestCase as TestCase;
class MaskUtilTest extends TestCase
{
public static function dataMaskBitProvider()
{
return array(
array(
0,
array(
array(1, 0, 1, 0, 1, 0),
array(0, 1, 0, 1, 0, 1),
array(1, 0, 1, 0, 1, 0),
array(0, 1, 0, 1, 0, 1),
array(1, 0, 1, 0, 1, 0),
array(0, 1, 0, 1, 0, 1),
)
),
array(
1,
array(
array(1, 1, 1, 1, 1, 1),
array(0, 0, 0, 0, 0, 0),
array(1, 1, 1, 1, 1, 1),
array(0, 0, 0, 0, 0, 0),
array(1, 1, 1, 1, 1, 1),
array(0, 0, 0, 0, 0, 0),
)
),
array(
2,
array(
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 1, 0, 0),
)
),
array(
3,
array(
array(1, 0, 0, 1, 0, 0),
array(0, 0, 1, 0, 0, 1),
array(0, 1, 0, 0, 1, 0),
array(1, 0, 0, 1, 0, 0),
array(0, 0, 1, 0, 0, 1),
array(0, 1, 0, 0, 1, 0),
)
),
array(
4,
array(
array(1, 1, 1, 0, 0, 0),
array(1, 1, 1, 0, 0, 0),
array(0, 0, 0, 1, 1, 1),
array(0, 0, 0, 1, 1, 1),
array(1, 1, 1, 0, 0, 0),
array(1, 1, 1, 0, 0, 0),
)
),
array(
5,
array(
array(1, 1, 1, 1, 1, 1),
array(1, 0, 0, 0, 0, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 1, 0, 1, 0),
array(1, 0, 0, 1, 0, 0),
array(1, 0, 0, 0, 0, 0),
)
),
array(
6,
array(
array(1, 1, 1, 1, 1, 1),
array(1, 1, 1, 0, 0, 0),
array(1, 1, 0, 1, 1, 0),
array(1, 0, 1, 0, 1, 0),
array(1, 0, 1, 1, 0, 1),
array(1, 0, 0, 0, 1, 1),
)
),
array(
7,
array(
array(1, 0, 1, 0, 1, 0),
array(0, 0, 0, 1, 1, 1),
array(1, 0, 0, 0, 1, 1),
array(0, 1, 0, 1, 0, 1),
array(1, 1, 1, 0, 0, 0),
array(0, 1, 1, 1, 0, 0),
)
),
);
}
/**
* @dataProvider dataMaskBitProvider
* @param integer $maskPattern
* @param array $expected
* @return void
*/
public function testGetDatMaskBit($maskPattern, array $expected)
{
for ($x = 0; $x < 6; $x++) {
for ($y = 0; $y < 6; $y++) {
if (($expected[$y][$x] === 1) !== MaskUtil::getDataMaskBit($maskPattern, $x, $y)) {
$this->fail('Data mask bit did not match');
}
}
}
}
public function testApplyMaskPenaltyRule1()
{
$matrix = new ByteMatrix(4, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$this->assertEquals(0, MaskUtil::applyMaskPenaltyRule1($matrix));
// Horizontal
$matrix = new ByteMatrix(6, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$matrix->set(4, 0, 0);
$matrix->set(5, 0, 1);
$this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix));
$matrix->set(5, 0, 0);
$this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix));
// Vertical
$matrix = new ByteMatrix(1, 6);
$matrix->set(0, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(0, 3, 0);
$matrix->set(0, 4, 0);
$matrix->set(0, 5, 1);
$this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix));
$matrix->set(0, 5, 0);
$this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix));
}
public function testApplyMaskPenaltyRule2()
{
$matrix = new ByteMatrix(1, 1);
$matrix->set(0, 0, 0);
$this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(2, 2);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 1);
$this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(2, 2);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 0);
$this->assertEquals(3, MaskUtil::applyMaskPenaltyRule2($matrix));
$matrix = new ByteMatrix(3, 3);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(1, 1, 0);
$matrix->set(2, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(1, 2, 0);
$matrix->set(2, 2, 0);
$this->assertEquals(3 * 4, MaskUtil::applyMaskPenaltyRule2($matrix));
}
public function testApplyMaskPenalty3()
{
// Horizontal 00001011101
$matrix = new ByteMatrix(11, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 0);
$matrix->set(3, 0, 0);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$matrix->set(6, 0, 1);
$matrix->set(7, 0, 1);
$matrix->set(8, 0, 1);
$matrix->set(9, 0, 0);
$matrix->set(10, 0, 1);
$this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Horizontal 10111010000
$matrix = new ByteMatrix(11, 1);
$matrix->set(0, 0, 1);
$matrix->set(1, 0, 0);
$matrix->set(2, 0, 1);
$matrix->set(3, 0, 1);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$matrix->set(6, 0, 1);
$matrix->set(7, 0, 0);
$matrix->set(8, 0, 0);
$matrix->set(9, 0, 0);
$matrix->set(10, 0, 0);
$this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Vertical 00001011101
$matrix = new ByteMatrix(1, 11);
$matrix->set(0, 0, 0);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 0);
$matrix->set(0, 3, 0);
$matrix->set(0, 4, 1);
$matrix->set(0, 5, 0);
$matrix->set(0, 6, 1);
$matrix->set(0, 7, 1);
$matrix->set(0, 8, 1);
$matrix->set(0, 9, 0);
$matrix->set(0, 10, 1);
$this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix));
// Vertical 10111010000
$matrix = new ByteMatrix(1, 11);
$matrix->set(0, 0, 1);
$matrix->set(0, 1, 0);
$matrix->set(0, 2, 1);
$matrix->set(0, 3, 1);
$matrix->set(0, 4, 1);
$matrix->set(0, 5, 0);
$matrix->set(0, 6, 1);
$matrix->set(0, 7, 0);
$matrix->set(0, 8, 0);
$matrix->set(0, 9, 0);
$matrix->set(0, 10, 0);
$this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix));
}
public function testApplyMaskPenaltyRule4()
{
// Dark cell ratio = 0%
$matrix = new ByteMatrix(1, 1);
$matrix->set(0, 0, 0);
$this->assertEquals(100, MaskUtil::applyMaskPenaltyRule4($matrix));
// Dark cell ratio = 5%
$matrix = new ByteMatrix(2, 1);
$matrix->set(0, 0, 0);
$matrix->set(0, 0, 1);
$this->assertEquals(0, MaskUtil::applyMaskPenaltyRule4($matrix));
// Dark cell ratio = 66.67%
$matrix = new ByteMatrix(6, 1);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 1);
$matrix->set(2, 0, 1);
$matrix->set(3, 0, 1);
$matrix->set(4, 0, 1);
$matrix->set(5, 0, 0);
$this->assertEquals(30, MaskUtil::applyMaskPenaltyRule4($matrix));
}
}

View File

@@ -0,0 +1,336 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\BitArray;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Common\Version;
use PHPUnit_Framework_TestCase as TestCase;
use ReflectionClass;
use ReflectionMethod;
class MatrixUtilTest extends TestCase
{
protected $methods = array();
public function setUp()
{
// Hack to be able to test protected methods
$reflection = new ReflectionClass('BaconQrCode\Encoder\MatrixUtil');
foreach ($reflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
$method->setAccessible(true);
$this->methods[$method->getName()] = $method;
}
}
public function testToString()
{
$matrix= new ByteMatrix(3, 3);
$matrix->set(0, 0, 0);
$matrix->set(1, 0, 1);
$matrix->set(2, 0, 0);
$matrix->set(0, 1, 1);
$matrix->set(1, 1, 0);
$matrix->set(2, 1, 1);
$matrix->set(0, 2, -1);
$matrix->set(1, 2, -1);
$matrix->set(2, 2, -1);
$expected = " 0 1 0\n 1 0 1\n \n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testClearMatrix()
{
$matrix = new ByteMatrix(2, 2);
MatrixUtil::clearMatrix($matrix);
$this->assertEquals(-1, $matrix->get(0, 0));
$this->assertEquals(-1, $matrix->get(1, 0));
$this->assertEquals(-1, $matrix->get(0, 1));
$this->assertEquals(-1, $matrix->get(1, 1));
}
public function testEmbedBasicPatterns1()
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(1),
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 0 0 0 0 0 0 0 1 \n"
. " 1 1 1 1 1 1 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 1 1 1 1 1 1 0 \n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testEmbedBasicPatterns2()
{
$matrix = new ByteMatrix(25, 25);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(2),
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 \n"
. " 0 \n"
. " 1 1 1 1 1 1 \n"
. " 0 0 0 0 0 0 0 0 1 1 0 0 0 1 \n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 \n"
. " 1 0 0 0 0 0 1 0 1 0 0 0 1 \n"
. " 1 0 1 1 1 0 1 0 1 1 1 1 1 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 1 1 1 0 1 0 \n"
. " 1 0 0 0 0 0 1 0 \n"
. " 1 1 1 1 1 1 1 0 \n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testEmbedTypeInfo()
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedTypeInfo']->invoke(
null,
new ErrorCorrectionLevel(ErrorCorrectionLevel::M),
5,
$matrix
);
$expected = " 0 \n"
. " 1 \n"
. " 1 \n"
. " 1 \n"
. " 0 \n"
. " 0 \n"
. " \n"
. " 1 \n"
. " 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0\n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 0 \n"
. " 1 \n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testEmbedVersionInfo()
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['maybeEmbedVersionInfo']->invoke(
null,
Version::getVersionForNumber(7),
$matrix
);
$expected = " 0 0 1 \n"
. " 0 1 0 \n"
. " 0 1 0 \n"
. " 0 1 1 \n"
. " 1 1 1 \n"
. " 0 0 0 \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " 0 0 0 0 1 0 \n"
. " 0 1 1 1 1 0 \n"
. " 1 0 0 1 1 0 \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n"
. " \n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testEmbedDataBits()
{
$matrix = new ByteMatrix(21, 21);
MatrixUtil::clearMatrix($matrix);
$this->methods['embedBasicPatterns']->invoke(
null,
Version::getVersionForNumber(1),
$matrix
);
$bits = new BitArray();
$this->methods['embedDataBits']->invoke(
null,
$bits,
-1,
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testBuildMatrix()
{
$bytes = array(
32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169,
239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61
);
$bits = new BitArray();
foreach ($bytes as $byte) {
$bits->appendBits($byte, 8);
}
$matrix = new ByteMatrix(21, 21);
MatrixUtil::buildMatrix(
$bits,
new ErrorCorrectionLevel(ErrorCorrectionLevel::H),
Version::getVersionForNumber(1),
3,
$matrix
);
$expected = " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n"
. " 1 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1\n"
. " 1 0 1 1 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 1\n"
. " 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1\n"
. " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0\n"
. " 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0\n"
. " 1 0 1 0 1 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0\n"
. " 1 1 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0\n"
. " 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0\n"
. " 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 1 1 1 1 1\n"
. " 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 1 1\n"
. " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0\n"
. " 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1\n"
. " 1 0 1 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 1 0\n"
. " 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0\n"
. " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0\n"
. " 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 0 1 0 0 1 0\n";
$this->assertEquals($expected, $matrix->__toString());
}
public function testFindMsbSet()
{
$this->assertEquals(0, $this->methods['findMsbSet']->invoke(null, 0));
$this->assertEquals(1, $this->methods['findMsbSet']->invoke(null, 1));
$this->assertEquals(8, $this->methods['findMsbSet']->invoke(null, 0x80));
$this->assertEquals(32, $this->methods['findMsbSet']->invoke(null, 0x80000000));
}
public function testCalculateBchCode()
{
// Encoding of type information.
// From Appendix C in JISX0510:2004 (p 65)
$this->assertEquals(0xdc, $this->methods['calculateBchCode']->invoke(null, 5, 0x537));
// From http://www.swetake.com/qr/qr6.html
$this->assertEquals(0x1c2, $this->methods['calculateBchCode']->invoke(null, 0x13, 0x537));
// From http://www.swetake.com/qr/qr11.html
$this->assertEquals(0x214, $this->methods['calculateBchCode']->invoke(null, 0x1b, 0x537));
// Encoding of version information.
// From Appendix D in JISX0510:2004 (p 68)
$this->assertEquals(0xc94, $this->methods['calculateBchCode']->invoke(null, 7, 0x1f25));
$this->assertEquals(0x5bc, $this->methods['calculateBchCode']->invoke(null, 8, 0x1f25));
$this->assertEquals(0xa99, $this->methods['calculateBchCode']->invoke(null, 9, 0x1f25));
$this->assertEquals(0x4d3, $this->methods['calculateBchCode']->invoke(null, 10, 0x1f25));
$this->assertEquals(0x9a6, $this->methods['calculateBchCode']->invoke(null, 20, 0x1f25));
$this->assertEquals(0xd75, $this->methods['calculateBchCode']->invoke(null, 30, 0x1f25));
$this->assertEquals(0xc69, $this->methods['calculateBchCode']->invoke(null, 40, 0x1f25));
}
public function testMakeVersionInfoBits()
{
// From Appendix D in JISX0510:2004 (p 68)
$bits = new BitArray();
$this->methods['makeVersionInfoBits']->invoke(null, Version::getVersionForNumber(7), $bits);
$this->assertEquals(' ...XXXXX ..X..X.X ..', $bits->__toString());
}
public function testMakeTypeInfoBits()
{
// From Appendix D in JISX0510:2004 (p 68)
$bits = new BitArray();
$this->methods['makeTypeInfoBits']->invoke(null, new ErrorCorrectionLevel(ErrorCorrectionLevel::M), 5, $bits);
$this->assertEquals(' X......X X..XXX.', $bits->__toString());
}
}

View File

@@ -0,0 +1,99 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Renderer\Text\Html;
use BaconQrCode\Writer;
use PHPUnit_Framework_TestCase as TestCase;
class HtmlTest extends TestCase
{
/**
* @var Html
*/
protected $renderer;
/**
* @var Writer
*/
protected $writer;
public function setUp()
{
$this->renderer = new Html();
$this->writer = new Writer($this->renderer);
}
public function testBasicRender()
{
$content = 'foobar';
$expected =
'<pre style="font-family: monospace; line-height: 0.65em; letter-spacing: -1px" class="">' .
" \n" .
" ███████ █████ ███████ \n" .
" █ █ █ █ █ █ \n" .
" █ ███ █ ██ █ ███ █ \n" .
" █ ███ █ ███ █ ███ █ \n" .
" █ ███ █ █ █ █ ███ █ \n" .
" █ █ ██ █ █ \n" .
" ███████ █ █ █ ███████ \n" .
" █████ \n" .
" ██ ██ █ ██ █ █ █ \n" .
" ██ ██ █ █ ██ \n" .
" ████████ █ ██ █ ██ \n" .
" ██ █ █ \n" .
" ██ ███ █ █ █ █ \n" .
" █ ███ █ █ \n" .
" ███████ ██ ██████ \n" .
" █ █ ████ ██ \n" .
" █ ███ █ ██ ██ ██ █ ██ \n" .
" █ ███ █ ██ ██ █ ██ \n" .
" █ ███ █ █ █ ██ ██ \n" .
" █ █ ███ ███ ████ \n" .
" ███████ ████ ██ \n" .
" \n" .
'</pre>'
;
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals($expected, $this->renderer->render($qrCode));
}
public function testSetStyle()
{
$content = 'foobar';
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->renderer->setStyle('bar');
$this->assertEquals('bar', $this->renderer->getStyle());
$this->assertStringMatchesFormat('%astyle="bar"%a', $this->renderer->render($qrCode));
}
public function testSetClass()
{
$content = 'foobar';
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->renderer->setClass('bar');
$this->assertEquals('bar', $this->renderer->getClass());
$this->assertStringMatchesFormat('%aclass="bar"%a', $this->renderer->render($qrCode));
}
}

View File

@@ -0,0 +1,149 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Encoder;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Renderer\Text\Plain;
use BaconQrCode\Writer;
use PHPUnit_Framework_TestCase as TestCase;
class PlainTest extends TestCase
{
/**
* @var Plain
*/
protected $renderer;
/**
* @var Writer
*/
protected $writer;
public function setUp()
{
$this->renderer = new Plain();
$this->writer = new Writer($this->renderer);
}
public function testBasicRender()
{
$content = 'foobar';
$expected =
" \n" .
" ███████ █████ ███████ \n" .
" █ █ █ █ █ █ \n" .
" █ ███ █ ██ █ ███ █ \n" .
" █ ███ █ ███ █ ███ █ \n" .
" █ ███ █ █ █ █ ███ █ \n" .
" █ █ ██ █ █ \n" .
" ███████ █ █ █ ███████ \n" .
" █████ \n" .
" ██ ██ █ ██ █ █ █ \n" .
" ██ ██ █ █ ██ \n" .
" ████████ █ ██ █ ██ \n" .
" ██ █ █ \n" .
" ██ ███ █ █ █ █ \n" .
" █ ███ █ █ \n" .
" ███████ ██ ██████ \n" .
" █ █ ████ ██ \n" .
" █ ███ █ ██ ██ ██ █ ██ \n" .
" █ ███ █ ██ ██ █ ██ \n" .
" █ ███ █ █ █ ██ ██ \n" .
" █ █ ███ ███ ████ \n" .
" ███████ ████ ██ \n" .
" \n"
;
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->assertEquals($expected, $this->renderer->render($qrCode));
}
public function testBasicRenderNoMargins()
{
$content = 'foobar';
$expected =
"███████ █████ ███████\n" .
"█ █ █ █ █ █\n" .
"█ ███ █ ██ █ ███ █\n" .
"█ ███ █ ███ █ ███ █\n" .
"█ ███ █ █ █ █ ███ █\n" .
"█ █ ██ █ █\n" .
"███████ █ █ █ ███████\n" .
" █████ \n" .
"██ ██ █ ██ █ █ █\n" .
" ██ ██ █ █ ██ \n" .
" ████████ █ ██ █ ██\n" .
" ██ █ █\n" .
" ██ ███ █ █ █ █\n" .
" █ ███ █ █ \n" .
"███████ ██ ██████ \n" .
"█ █ ████ ██ \n" .
"█ ███ █ ██ ██ ██ █ ██\n" .
"█ ███ █ ██ ██ █ ██ \n" .
"█ ███ █ █ █ ██ ██\n" .
"█ █ ███ ███ ████\n" .
"███████ ████ ██ \n"
;
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->renderer->setMargin(0);
$this->assertEquals(0, $this->renderer->getMargin());
$this->assertEquals($expected, $this->renderer->render($qrCode));
}
public function testBasicRenderCustomChar()
{
$content = 'foobar';
$expected =
"-----------------------\n" .
"-#######-#####-#######-\n" .
"-#-----#--#-#--#-----#-\n" .
"-#-###-#--##---#-###-#-\n" .
"-#-###-#--###--#-###-#-\n" .
"-#-###-#---#-#-#-###-#-\n" .
"-#-----#----##-#-----#-\n" .
"-#######-#-#-#-#######-\n" .
"---------#####---------\n" .
"-##-##-#--##-#-#-----#-\n" .
"----##----##-#-#-##----\n" .
"--########-#--##-#--##-\n" .
"-----------##------#-#-\n" .
"--##--###--#---#--#--#-\n" .
"---------#-###----#-#--\n" .
"-#######--##-######----\n" .
"-#-----#---####---##---\n" .
"-#-###-#-##-##-##-#-##-\n" .
"-#-###-#-##-##--#-##---\n" .
"-#-###-#---#---#-##-##-\n" .
"-#-----#-###--###-####-\n" .
"-#######-####---##-----\n" .
"-----------------------\n"
;
$qrCode = Encoder::encode(
$content,
new ErrorCorrectionLevel(ErrorCorrectionLevel::L),
Encoder::DEFAULT_BYTE_MODE_ECODING
);
$this->renderer->setFullBlock('#');
$this->renderer->setEmptyBlock('-');
$this->assertEquals('#', $this->renderer->getFullBlock());
$this->assertEquals('-', $this->renderer->getEmptyBlock());
$this->assertEquals($expected, $this->renderer->render($qrCode));
}
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
require_once __DIR__ . '/../autoload_register.php';

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./bootstrap.php" colors="true">
<testsuite name="BaconQrCode">
<directory>.</directory>
</testsuite>
<filter>
<whitelist>
<directory suffix=".php">../src/</directory>
</whitelist>
</filter>
</phpunit>

24
vendor/cjango/wechat/composer.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "cjango/wechat",
"description": "wechat sdk",
"keywords": ["cjango", "wechat", "sdk", "code"],
"homepage": "https://github.com/cjango/wechat",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jason.Chen",
"email": "chenjxlg@163.com",
"homepage": "http://www.cjango.com/"
}
],
"minimum-stability": "dev",
"require": {
"php": ">=5.5.0"
},
"autoload": {
"psr-4": {
"cjango\\": "src/"
}
}
}

111
vendor/cjango/wechat/src/Wechat.php vendored Normal file
View File

@@ -0,0 +1,111 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango;
/**
* 微信公众平台开发SDK,
* 架构问题又凌乱了.
* @author 小陈叔叔 <20511924@qq.com>
*/
class Wechat
{
/**
* 保存错误信息
* @var string
*/
protected static $error = '';
/**
* 默认的配置参数
* @var array
*/
protected static $config = [
'token' => '',
'appid' => '',
'secret' => '',
'access_token' => '',
'encode' => false,
'AESKey' => '',
'mch_id' => '',
'paykey' => '',
'pem' => '',
];
/**
* 传入初始化参数
* 接受消息,如果是加密的需要传入一些参数,否则用不到
*/
public function __construct($config = [])
{
if (!empty($config) && is_array($config)) {
self::$config = array_merge(self::$config, $config);
}
}
/**
* 初始化
* @param array $config
* @param boolean $force 强制初始化
* @return [type]
*/
public static function instance($config = [], $force = false)
{
static $wechat;
if (is_null($wechat) || $force == true) {
$wechat = new Wechat($config);
}
return $wechat;
}
/**
* 验证URL有效性,校验请求签名
* @return string|boolean
*/
public static function valid()
{
$echoStr = isset($_GET["echostr"]) ? $_GET["echostr"] : '';
if ($echoStr) {
self::checkSignature() && exit($echoStr);
} else {
!self::checkSignature() && exit('Access Denied!');
}
}
/**
* 检查请求URL签名
* @return boolean
*/
private static function checkSignature()
{
$signature = isset($_GET['signature']) ? $_GET['signature'] : '';
$timestamp = isset($_GET['timestamp']) ? $_GET['timestamp'] : '';
$nonce = isset($_GET['nonce']) ? $_GET['nonce'] : '';
if (empty($signature) || empty($timestamp) || empty($nonce)) {
return false;
}
$token = self::$config['token'];
$tmpArr = [$token, $timestamp, $nonce];
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
return sha1($tmpStr) == $signature;
}
/**
* 返回错误信息
* @return string
*/
public static function error($msg = null)
{
if (!is_null($msg)) {
self::$error = $msg;
} else {
return self::$error;
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 微信卡券相关部分,太难了 等我喝醉了再说吧!
*/
class Card extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'card_create' => 'https://api.weixin.qq.com/card/create',
'card_active' => 'https://api.weixin.qq.com/card/membercard/activate',
];
public static function create()
{
$params = [
'card' => [
'card_type' => 'MEMBER_CARD',
'member_card' => [
'base_info' => [
'logo_url' => '',
'brand_name' => '',
'code_type' => 'CODE_TYPE_TEXT',
'title' => 'Color010',
],
],
],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['card_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
public static function active()
{
#Todo..
}
}

View File

@@ -0,0 +1,41 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 数据统计接口
*/
class Datacube extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'user_summary' => 'https://api.weixin.qq.com/datacube/getusersummary',
'user_cumulate' => 'https://api.weixin.qq.com/datacube/getusercumulate',
'' => 'https://api.weixin.qq.com/datacube/getarticlesummary',
'' => 'https://api.weixin.qq.com/datacube/getarticletotal',
'' => 'https://api.weixin.qq.com/datacube/getuserread',
'' => 'https://api.weixin.qq.com/datacube/getuserreadhour',
'' => 'https://api.weixin.qq.com/datacube/getusershare',
'' => 'https://api.weixin.qq.com/datacube/getusersharehour',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsg',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsghour',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsgweek',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsgmonth',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsgdist',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsgdistweek',
'' => 'https://api.weixin.qq.com/datacube/getupstreammsgdistmonth',
'' => 'https://api.weixin.qq.com/datacube/getinterfacesummary',
'' => 'https://api.weixin.qq.com/datacube/getinterfacesummaryhour',
];
}

View File

@@ -0,0 +1,100 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 分组管理
*/
class Group extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'group_get' => 'https://api.weixin.qq.com/cgi-bin/groups/get',
'group_create' => 'https://api.weixin.qq.com/cgi-bin/groups/create',
'group_update' => 'https://api.weixin.qq.com/cgi-bin/groups/update',
'group_delete' => 'https://api.weixin.qq.com/cgi-bin/groups/delete',
];
/**
* 获取全部分组
*/
public static function get()
{
$params = [
'access_token' => parent::$config['access_token'],
];
$result = Utils::api(self::$url['group_get'], $params);
if ($result) {
return $result['groups'];
} else {
return false;
}
}
/**
* 新增分组
* @param string $name
* @return array ['id' => ID, 'name' => NAME]
*/
public static function create($name)
{
$params = [
'group' => [
'name' => $name,
],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
$result = Utils::api(self::$url['group_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return $result['group'];
} else {
return false;
}
}
/**
* 修改分组名
* @param [type] $id [description]
* @param [type] $name [description]
* @return [type] [description]
*/
public static function update($id, $name)
{
$params = [
'group' => [
'id' => $id,
'name' => $name,
],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['group_update'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 删除分组
* @param [type] $id [description]
* @return [type] [description]
*/
public static function delete($id)
{
$params = [
'group' => [
'id' => $id,
],
];
$params = json_encode($params);
return Utils::api(self::$url['group_delete'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
}

View File

@@ -0,0 +1,51 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 素材管理
*/
class Material extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'media_upload' => 'https://api.weixin.qq.com/cgi-bin/media/upload', // 新增临时素材
'media_get' => 'https://api.weixin.qq.com/cgi-bin/media/get', // 获取临时素材
'material_news' => 'https://api.weixin.qq.com/cgi-bin/material/add_news', // 新增永久图文素材
'material_material' => 'https://api.weixin.qq.com/cgi-bin/material/add_material', // 新增永久素材
'material_get' => 'https://api.weixin.qq.com/cgi-bin/material/get_material', // 获取永久素材
'material_del' => 'https://api.weixin.qq.com/cgi-bin/material/del_material', // 删除永久素材
'material_update' => 'https://api.weixin.qq.com/cgi-bin/material/update_news', // 修改永久图文素材
'material_count' => 'https://api.weixin.qq.com/cgi-bin/material/get_materialcount', // 获取永久素材数量
'material_lists' => 'https://api.weixin.qq.com/cgi-bin/material/batchget_material', // 获取永久素材列表
];
/**
* 新增临时素材
* @return [type] [description]
*/
public static function upload()
{
#Todo..
}
/**
* 获取临时素材
* @return [type] [description]
*/
public static function get()
{
#Todo..
}
}

View File

@@ -0,0 +1,70 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 自定义菜单
*/
class Menu extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'menu_get' => 'https://api.weixin.qq.com/cgi-bin/menu/get', // 获取菜单
'menu_create' => 'https://api.weixin.qq.com/cgi-bin/menu/create', // 创建菜单
'menu_delete' => 'https://api.weixin.qq.com/cgi-bin/menu/delete', // 删除菜单
];
/**
* 获取自定义菜单
* @return [type] [description]
*/
public static function get()
{
$params = [
'access_token' => parent::$config['access_token'],
];
$result = Utils::api(self::$url['menu_get'], $params);
if ($result) {
return $result['menu']['button'];
} else {
return false;
}
}
/**
* 创建菜单
* @param [type] $menu [description]
* @return [type] [description]
*/
public static function create($menu)
{
$params = [
'button' => $menu,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['menu_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 删除自定义菜单
* @return [type] [description]
*/
public static function delete()
{
$params = [
'access_token' => parent::$config['access_token'],
];
return Utils::api(self::$url['menu_delete'], $params);
}
}

View File

@@ -0,0 +1,83 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* ACCESS TOKEN获取
*/
class Oauth extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'oauth_authorize' => 'https://open.weixin.qq.com/connect/oauth2/authorize',
'oauth_user_token' => 'https://api.weixin.qq.com/sns/oauth2/access_token',
'oauth_get_userinfo' => 'https://api.weixin.qq.com/sns/userinfo',
];
/**
* OAuth 用户同意授权获取code
* @param [type] $callback 回调URI填写完整地址带http://
* @param string $state 重定向后会带上state参数开发者可以填写a-zA-Z0-9的参数值
* @param string $scope snsapi_userinfo 获取用户授权信息snsapi_base直接返回openid
* @return string
*/
public static function url($callback, $state = 'STATE', $scope = 'snsapi_base')
{
$params = [
'appid' => self::$config['appid'],
'redirect_uri' => $callback,
'response_type' => 'code',
'scope' => $scope,
'state' => $state,
];
return self::$url['oauth_authorize'] . '?' . http_build_query($params) . '#wechat_redirect';
}
/**
* 通过code获取Access Token
* @return array|boolean
*/
public static function token()
{
$code = isset($_GET['code']) ? $_GET['code'] : '';
if (!$code) {
parent::$error = '未获取到CODE信息';
return false;
}
$params = [
'appid' => self::$config['appid'],
'secret' => self::$config['secret'],
'code' => $code,
'grant_type' => 'authorization_code',
];
return Utils::api(self::$url['oauth_user_token'], $params);
}
/**
* 网页获取用户信息
* @param string $access_token 通过getOauthAccessToken方法获取到的token
* @param string $openid 用户的OPENID
* @return array
*/
public static function info($token, $openid)
{
$params = [
'access_token' => $token,
'openid' => $openid,
'lang' => 'zh_CN',
];
return Utils::api(self::$url['oauth_get_userinfo'], $params);
}
}

280
vendor/cjango/wechat/src/Wechat/Pay.php vendored Normal file
View File

@@ -0,0 +1,280 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 微信支付相关部分
*/
class Pay extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'unified_order' => 'https://api.mch.weixin.qq.com/pay/unifiedorder', // 统一下单地址
'order_query' => 'https://api.mch.weixin.qq.com/pay/orderquery', // 订单状态查询
'close_order' => 'https://api.mch.weixin.qq.com/pay/closeorder', // 关闭订单
'refund_order' => 'https://api.mch.weixin.qq.com/secapi/pay/refund', // 退款地址
'refund_query' => 'https://api.mch.weixin.qq.com/pay/refundquery', // 退款查询
'pay_transfers' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', // 企业付款
'get_pay_transfers' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo', // 企业付款查询
'download_bill' => 'https://api.mch.weixin.qq.com/pay/downloadbill', // 下载对账单
'send_red_pack' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack', // 发放红包高级接口
'send_group_red_pack' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack', // 发送裂变红包接口(拼手气)
'get_red_pack_info' => 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo', // 红包查询接口
];
/**
* 统一下单支付
* @param string $orderId 系统订单号
* @param string $body 产品描述
* @param string $money 支付金额
* @param string $trade_type (JSAPI NATIVE APP)
* @param string $notify_url 回调URL
* @param string $openid 支付用户
* @param string $attach 附加信息
* @param array $extends 订单扩展数据
* @return boolean|JSON 直接用于H5调用支付的API参数
*/
public static function unified($orderId, $body, $money, $tradeType = 'JSAPI', $notifyUrl = '', $openid = '', $attach = '', $extends = [])
{
$params = [
'appid' => parent::$config['appid'],
'mch_id' => parent::$config['mch_id'],
'nonce_str' => uniqid(),
'body' => $body,
'out_trade_no' => $orderId,
'total_fee' => $money * 100, // 转换成分
'spbill_create_ip' => self::_getClientIp(),
'notify_url' => $notifyUrl,
'trade_type' => $tradeType,
];
if (!empty($openid) && $tradeType == 'JSAPI') {
$params['openid'] = $openid;
}
if (!empty($attach)) {
$params['attach'] = $attach;
}
$params['sign'] = self::_getOrderSign($params);
$data = Utils::array2xml($params);
$data = Utils::http(self::$url['unified_order'], $data, 'POST');
$result = self::parsePayResult(Utils::xml2array($data));
if ($result) {
return self::createPayParams($result['prepay_id']);
} else {
return false;
}
}
/**
* 创建支付参数
* @param [type] $prepay_id [description]
* @return JSON
*/
private static function createPayParams($prepay_id)
{
$params['appId'] = parent::$config['appid'];
$params['timeStamp'] = (string) time();
$params['nonceStr'] = uniqid();
$params['package'] = 'prepay_id=' . $prepay_id;
$params['signType'] = 'MD5';
$params['paySign'] = self::_getOrderSign($params);
return json_encode($params);
}
/**
* 查询订单
* @param [type] $out_trade_no [description]
* @return [type] [description]
*/
public static function query($orderId = '', $type = 'out_trade_no')
{
$params = [
'appid' => parent::$config['appid'],
'mch_id' => parent::$config['mch_id'],
'nonce_str' => uniqid(),
];
if ($type == 'out_trade_no') {
$params['out_trade_no'] = $orderId;
} else {
$params['transaction_id'] = $orderId;
}
$params['sign'] = self::_getOrderSign($params);
$data = Utils::array2xml($params);
$data = Utils::http(self::$url['order_query'], $data, 'POST');
return Utils::xml2array($data);
}
/**
* 关闭订单
* @param string $orderId 商户订单号
* @return boolean
*/
public static function close($orderId)
{
$params = [
'appid' => parent::$config['appid'],
'mch_id' => parent::$config['mch_id'],
'out_trade_no' => $orderId,
'nonce_str' => uniqid(),
];
$params['sign'] = self::_getOrderSign($params);
$data = Utils::array2xml($params);
$data = Utils::http(self::$url['close_order'], $data, 'POST');
$result = self::parsePayResult(Utils::xml2array($data));
if ($result) {
return true;
} else {
return false;
}
}
/**
* 退款申请
* @param [type] $transaction_id [description]
* @param [type] $orderId [description]
* @param [type] $out_refund_no [description]
* @param [type] $total_fee [description]
* @param [type] $refund_fee [description]
* @param [type] $op_user_id [description]
* @return [type] [description]
*/
public static function refund($orderId, $refundId, $total_fee, $refund_fee)
{
$params = [
'appid' => parent::$config['appid'],
'mch_id' => parent::$config['mch_id'],
'out_trade_no' => $orderId,
'out_refund_no' => $refundId,
'total_fee' => $total_fee,
'refund_fee' => $refund_fee,
'op_user_id' => parent::$config['mch_id'],
'nonce_str' => uniqid(),
];
$params['sign'] = self::_getOrderSign($params);
$data = Utils::array2xml($params);
$data = Utils::http(self::$url['close_order'], $data, 'POST');
$result = self::parsePayResult(Utils::xml2array($data));
if ($result) {
return $result;
} else {
return false;
}
}
/**
* 解析支付接口的返回结果
* @param xmlstring $data 接口返回的数据
* @param boolean $checkSign 是否需要签名校验
* @return boolean|array
*/
public static function parsePayRequest($checkSign = true)
{
$post = file_get_contents("php://input");
$data = Utils::xml2array($post);
if (empty($data)) {
Wechat::error('回调结果解析失败');
return false;
}
if ($checkSign) {
$sign = $data['sign'];
unset($data['sign']);
if (self::_getOrderSign($data) != $sign) {
Wechat::error('签名校验失败');
return false;
}
}
return self::parsePayResult($data);
}
/**
* 解析支付借口的返回数据
* @param [type] $data [description]
*/
private static function parsePayResult($data)
{
if ($data['return_code'] == 'SUCCESS') {
if ($data['result_code'] == 'SUCCESS') {
return $data;
} else {
Wechat::error($data['err_code']);
return false;
}
} else {
Wechat::error($data['return_msg']);
return false;
}
}
/**
* 支付结果通知
* @param [type] $code 支付结果
* @param [type] $msg 返回信息
* @return xml
*/
public static function returnNotify($msg = true)
{
if ($msg === true) {
$params = [
'return_code' => 'SUCCESS',
'return_msg' => '',
];
} else {
$params = [
'return_code' => 'FAIL',
'return_msg' => $msg,
];
}
exit(Utils::array2xml($params));
}
/**
* 获取客户端IP地址
* @return string
*/
private static function _getClientIp()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr);
if (false !== $pos) {
unset($arr[$pos]);
}
$ip = trim(current($arr));
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
$ip = $_SERVER['REMOTE_ADDR'];
}
return $ip;
}
/**
* 本地MD5签名
* @param array $params 需要签名的数据
* @return string 大写字母与数字签名串32位
*/
private static function _getOrderSign($params)
{
ksort($params);
$params['key'] = parent::$config['paykey'];
return strtoupper(md5(urldecode(http_build_query($params))));
}
}

View File

@@ -0,0 +1,98 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 自定义菜单
*/
class QRcode extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'qrcode_create' => 'https://api.weixin.qq.com/cgi-bin/qrcode/create',
'qrcode_show' => 'https://mp.weixin.qq.com/cgi-bin/showqrcode',
'short_url' => 'https://api.mch.weixin.qq.com/tools/shorturl', // 转换短链接
];
/**
* 临时二维码
* @param [type] $scene_id [description]
* @param integer $expire [description]
* @return [type] [description]
*/
public static function temp($scene_id, $expire = 604800)
{
$params = [
'expire_seconds' => $expire,
'action_name' => 'QR_SCENE',
'action_info' => [
'scene' => [
'scene_id' => $scene_id,
],
],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
$result = Utils::api(self::$url['qrcode_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return self::$url['qrcode_show'] . '?ticket=' . $result['ticket'];
} else {
return false;
}
}
/**
* 永久二维码
* @return [type] [description]
*/
public static function limit($scene_str)
{
$params = [
'action_name' => 'QR_LIMIT_SCENE',
'action_info' => [
'scene' => [
'scene_str' => $scene_str,
],
],
'access_token' => parent::$config['access_token'],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
$result = Utils::api(self::$url['qrcode_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return self::$url['qrcode_show'] . '?ticket=' . $result['ticket'];
} else {
return false;
}
}
/**
* 转换短链接
* @param [type] $longUrl
* @return [type]
*/
public static function short($longUrl)
{
$params = [
'action' => 'long2short',
'long_url' => $longUrl,
];
$params = json_encode($params);
$result = Utils::api(self::$url['short_url'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return $result;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,170 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 被动消息回复
*/
class Reply extends Wechat
{
/**
* 接收到的消息内容
* @var array
*/
private static $request = [];
private static $response = [];
/**
* 接受消息,通用,接受到的消息
* 用户自己处理消息类型就可以
* 暂时不处理加密问题
* @return array|boolean
*/
public static function request()
{
$postStr = file_get_contents("php://input");
if (!empty($postStr)) {
$data = Utils::xml2array($postStr);
return self::$request = $data;
} else {
return false;
}
}
/**
* 回复消息
* @param array|string $content 回复的消息内容
* @param string $type 回复的消息类型
* @return xml
*/
public static function response($content, $type = 'text')
{
self::$response = [
'ToUserName' => self::$request['fromusername'],
'FromUserName' => self::$request['tousername'],
'CreateTime' => time(),
'MsgType' => $type,
];
self::$type($content);
$response = Utils::array2xml(self::$response);
exit($response);
}
/**
* 回复文本类型消息
* @param string $content
*/
private static function text($content)
{
self::$response['Content'] = $content;
}
/**
* 回复图片类型消息
* @param string $mediaId
*/
private static function image($mediaId)
{
self::$response['Image']['MediaId'] = $mediaId;
}
/**
* 回复语音类型消息
* @param string $mediaId
*/
private static function voice($mediaId)
{
self::$response['Voice']['MediaId'] = $mediaId;
}
/**
* 回复视频类型消息
* @param array $media
*/
private static function video($video)
{
list(
$video['MediaId'],
$video['Title'],
$video['Description']
) = $video;
self::$response['Video'] = $video;
}
/**
* 回复音乐信息
* @param string $content 要回复的音乐
*/
private static function music($music)
{
list(
$music['Title'],
$music['Description'],
$music['MusicUrl'],
$music['HQMusicUrl'],
$music['ThumbMediaId']
) = $music;
self::$response['Music'] = $music;
}
/**
* 回复图文列表消息
* @param [type] $content [description]
*/
private static function news($news)
{
$articles = [];
foreach ($news as $key => $value) {
list(
$articles[$key]['Title'],
$articles[$key]['Description'],
$articles[$key]['PicUrl'],
$articles[$key]['Url']
) = $value;
if ($key >= 9) {
break;
}
}
self::$response['ArticleCount'] = count($articles);
self::$response['Articles'] = $articles;
}
/**
* 将消息转发至客服
* @param string $account 指定的客服账号
* @return xml
*/
public static function transfer($account = '')
{
self::$response = [
'ToUserName' => self::$request['fromusername'],
'FromUserName' => self::$request['tousername'],
'CreateTime' => time(),
'MsgType' => 'transfer_customer_service',
];
if (!empty($account)) {
self::$response['TransInfo']['KfAccount'] = $account;
}
$response = Utils::array2xml(self::$response);
exit($response);
}
}

View File

@@ -0,0 +1,30 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 语义理解
*/
class Semantic extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'semproxy' => 'https://api.weixin.qq.com/semantic/semproxy/search',
];
public static function search()
{
#Todo..
}
}

View File

@@ -0,0 +1,255 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 客服相关接口
*/
class Service extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'service_list' => 'https://api.weixin.qq.com/cgi-bin/customservice/getkflist',
'online_list' => 'https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist',
'service_add' => 'https://api.weixin.qq.com/customservice/kfaccount/add',
'service_update' => 'https://api.weixin.qq.com/customservice/kfaccount/update',
'service_delete' => 'https://api.weixin.qq.com/customservice/kfaccount/del',
'invite_worker' => 'https://api.weixin.qq.com/customservice/kfaccount/inviteworker',
'upload_avatr' => 'http://api.weixin.qq.com/customservice/kfaccount/uploadheadimg',
'msg_record' => 'https://api.weixin.qq.com/customservice/msgrecord/getrecord',
'custom_send' => 'https://api.weixin.qq.com/cgi-bin/message/custom/send',
'session_create' => 'https://api.weixin.qq.com/customservice/kfsession/create',
'session_close' => 'https://api.weixin.qq.com/customservice/kfsession/close',
'session_get' => 'https://api.weixin.qq.com/customservice/kfsession/getsession',
'session_list' => 'https://api.weixin.qq.com/customservice/kfsession/getsessionlist',
'session_wait' => 'https://api.weixin.qq.com/customservice/kfsession/getwaitcase',
];
/**
* 获取客服列表
* @return array|boolean
*/
public static function get()
{
$result = Utils::api(self::$url['service_list'] . '?access_token=' . parent::$config['access_token']);
if ($result) {
return $result['kf_list'];
} else {
return false;
}
}
/**
* 获取在线客服
* @return array|boolean
*/
public static function online()
{
$result = Utils::api(self::$url['online_list'] . '?access_token=' . parent::$config['access_token']);
if ($result) {
return $result['kf_online_list'];
} else {
return false;
}
}
/**
* 添加客服账号
* @param string $account
* @param string $nickname
*/
public static function add($account, $nickname)
{
$params = [
'kf_account' => $account . '@' . parent::$config['appid'],
'nickname' => $nickname,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['service_add'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 修改客服账号
* @param string $account
* @param string $nickname
* @param string $password
* @return boolean
*/
public static function update($account, $nickname)
{
$params = [
'kf_account' => $account,
'nickname' => $nickname,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['service_update'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 删除客服账号
* @param string $account
* @return boolean
*/
public static function delete($account)
{
$params = [
'kf_account' => $account,
'access_token' => parent::$config['access_token'],
];
return Utils::api(self::$url['service_delete'], $params);
}
/**
* 邀请绑定客服
* @param string $account
* @param string $weixin
* @return boolean
*/
public static function invite($account, $weixin)
{
$params = [
'kf_account' => $account,
'invite_wx' => $weixin,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['invite_worker'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 获取客服聊天记录
* @param integer $starttime
* @param integer $endtime
* @param integer $pageindex
* @param integer $pagesize
* @return array|boolean
*/
public static function record($starttime, $endtime, $pageindex = 1, $pagesize = 50)
{
$params = [
'starttime' => $starttime,
'endtime' => $endtime,
'pageindex' => $pageindex,
'pagesize' => $pagesize,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
$result = Utils::api(self::$url['msg_record'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return $result['recordlist'];
} else {
return false;
}
}
/**
* 发送客服消息
* @param string $openid
* @param string $type
* @param string $content
* @return boolean
*/
public static function send($openid, $type, $content)
{
$params = [
'touser' => $openid,
'msgtype' => $type,
'text' => [
'content' => $content,
],
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['custom_send'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 创建会话
* @param string $account 完整客服账号
* @param string $openid 客户openid
* @return boolean
*/
public static function session_create($account, $openid)
{
$params = [
'kf_account' => $account,
'openid' => $openid,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['session_create'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 关闭会话
* @param string $account 完整客服账号
* @param string $openid 客户openid
* @return boolean
*/
public static function session_close($account, $openid)
{
$params = [
'kf_account' => $account,
'openid' => $openid,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['session_close'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 获取客户的会话状态
* @param string $openid 客户openid
* @return array
*/
public static function session_get($openid)
{
$params = [
'openid' => $openid,
'access_token' => parent::$config['access_token'],
];
return Utils::api(self::$url['session_get'], $params);
}
/**
* 获取客服的会话列表
* @param string $account
* @return array
*/
public static function session_list($account)
{
$params = [
'kf_account' => $account,
'access_token' => parent::$config['access_token'],
];
$result = Utils::api(self::$url['session_list'], $params);
if ($result) {
return $result['sessionlist'];
} else {
return false;
}
}
/**
* 获取未接入会话列表
* @return array
*/
public static function session_wait()
{
$params = [
'access_token' => parent::$config['access_token'],
];
$result = Utils::api(self::$url['session_wait'], $params);
if ($result) {
return $result['waitcaselist'];
} else {
return false;
}
}
}

View File

@@ -0,0 +1,111 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 模板消息
*/
class Template extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'set_industry' => 'https://api.weixin.qq.com/cgi-bin/template/api_set_industry', // 设置所属行业
'get_industry' => 'https://api.weixin.qq.com/cgi-bin/template/get_industry', // 获取设置的行业信息
'add_template' => 'https://api.weixin.qq.com/cgi-bin/template/api_add_template', // 获得模板ID
'get_template' => 'https://api.weixin.qq.com/cgi-bin/template/get_all_private_template', // 获取模板列表
'del_template' => 'https://api.weixin.qq.com/cgi-bin/template/del_private_template', // 删除模板
'send_template' => 'https://api.weixin.qq.com/cgi-bin/message/template/send', // 发送模板消息
];
/**
* 设置所属行业
* @param [type] $industry1 [description]
* @param [type] $industry2 [description]
*/
public static function setIndustry($primary, $secondary)
{
$params = [
'industry_id1' => $primary,
'industry_id2' => $secondary,
];
$params = json_encode($params);
return Utils::api(self::$url['set_industry'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 获取设置的行业信息
* @return [type] [description]
*/
public static function getIndustry()
{
return Utils::api(self::$url['get_industry'] . '?access_token=' . parent::$config['access_token'], '', 'GET');
}
/**
* 获得模板ID
* @return [type] [description]
*/
public static function add($shortTemplateId)
{
$params = [
'template_id_short' => $shortTemplateId,
];
$params = json_encode($params);
return Utils::api(self::$url['add_template'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 获取模板列表
* @return [type] [description]
*/
public static function get()
{
return Utils::api(self::$url['get_template'] . '?access_token=' . parent::$config['access_token'], '', 'GET');
}
/**
* 删除模板
* @param [type] $templateId 长模板ID 例Dyvp3-Ff0cnail_CDSzk1fIc6-9lOkxsQE7exTJbwUE
* @return boolean
*/
public static function delete($templateId)
{
$params = [
'template_id' => $templateId,
];
$params = json_encode($params);
return Utils::api(self::$url['del_template'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 发送模板消息
* @param [type] $openid 接收用户
* @param [type] $templateId 模板ID
* @param array $data 消息体
* @param string $url 连接URL
* @return boolean
*/
public static function send($openid, $templateId, $data = [], $url = '')
{
$params = [
'touser' => $openid,
'template_id' => $templateId,
'url' => $url,
'data' => $data,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['send_template'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
}

View File

@@ -0,0 +1,65 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* ACCESS TOKEN获取
*/
class Token extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'access_token' => 'https://api.weixin.qq.com/cgi-bin/token', // 获取ACCESS_TOKEN
'jsapi_ticket' => 'https://api.weixin.qq.com/cgi-bin/ticket/getticket', // JSAPI_TICKET获取地址
];
/**
* 获取ACCESS_TOKEN
* @return [type] [description]
*/
public static function get()
{
$params = [
'appid' => parent::$config['appid'],
'secret' => parent::$config['secret'],
'grant_type' => 'client_credential',
];
$result = Utils::api(self::$url['access_token'], $params);
if ($result) {
return $result['access_token'];
} else {
return false;
}
}
/**
* 获取JSAPI_TICKET
* @return [type] [description]
*/
public static function ticket()
{
$params = [
'access_token' => parent::$config['access_token'],
'type' => 'jsapi',
];
$result = Utils::http(self::$url['jsapi_ticket'], $params);
if ($result) {
$result = json_decode($result, true);
return $result['ticket'];
} else {
return false;
}
}
}

135
vendor/cjango/wechat/src/Wechat/User.php vendored Normal file
View File

@@ -0,0 +1,135 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
* 用户管理
*/
class User extends Wechat
{
/**
* 接口名称与URL映射
* @var array
*/
protected static $url = [
'user_get' => 'https://api.weixin.qq.com/cgi-bin/user/get',
'user_info' => 'https://api.weixin.qq.com/cgi-bin/user/info',
'user_info_batch' => 'https://api.weixin.qq.com/cgi-bin/user/info/batchget',
'user_remark' => 'https://api.weixin.qq.com/cgi-bin/user/info/updateremark',
'user_in_group' => 'https://api.weixin.qq.com/cgi-bin/groups/getid',
'user_to_group' => 'https://api.weixin.qq.com/cgi-bin/groups/members/update',
'batch_to_group' => 'https://api.weixin.qq.com/cgi-bin/groups/members/batchupdate',
];
/**
* 获取全部关注用户
* @param [type] $nextOpenid [description]
*/
public static function get($nextOpenid = '')
{
$params = [
'next_openid' => $nextOpenid,
'access_token' => parent::$config['access_token'],
];
$result = Utils::api(self::$url['user_get'], $params);
if ($result) {
return $result['data']['openid'];
} else {
return false;
}
}
/**
* 获取用户信息
* @param [type] $openid [description]
* @param [type] $lang [description]
* @return [type] [description]
*/
public static function info($openid, $lang = 'zh_CN')
{
$params = [
'openid' => $openid,
'access_token' => parent::$config['access_token'],
'lang' => $lang,
];
$result = Utils::api(self::$url['user_info'], $params);
if ($result) {
return $result;
} else {
return false;
}
}
/**
* 设置用户备注名
* @param [type] $openid
* @param [type] $remark
* @return [type]
*/
public static function remark($openid, $remark)
{
$params = [
'openid' => $openid,
'remark' => $remark,
];
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
return Utils::api(self::$url['user_remark'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 查询用户所在分组
* @param [type] $openid [description]
* @return [type] [description]
*/
public static function getgroup($openid)
{
$params = [
'openid' => $openid,
];
$params = json_encode($params);
$result = Utils::api(self::$url['user_in_group'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
if ($result) {
return $result['groupid'];
} else {
return false;
}
}
/**
* 移动用户分组
*/
public static function togroup($openid, $groupid)
{
$params = [
'openid' => $openid,
'to_groupid' => $groupid,
];
$params = json_encode($params);
return Utils::api(self::$url['user_to_group'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
/**
* 批量移动用户分组
* @param array $openid_list [description]
* @param [type] $groupid [description]
* @return [type] [description]
*/
public static function batchgroup($openid_list = [], $groupid)
{
$params = [
'openid_list' => $openid_list,
'to_groupid' => $groupid,
];
$params = json_encode($params);
return Utils::api(self::$url['batch_to_group'] . '?access_token=' . parent::$config['access_token'], $params, 'POST');
}
}

View File

@@ -0,0 +1,325 @@
<?php
// +------------------------------------------------+
// |http://www.cjango.com |
// +------------------------------------------------+
// | 修复BUG不是一朝一夕的事情等我喝醉了再说吧 |
// +------------------------------------------------+
// | Author: 小陈叔叔 <Jason.Chen> |
// +------------------------------------------------+
namespace cjango\Wechat;
use cjango\Wechat;
/**
*
*/
class Utils
{
/**
* 错误信息
* @var array
*/
private static $errMsg = [
-1 => '系统繁忙,此时请开发者稍候再试',
0 => '请求成功',
40001 => '获取access_token时AppSecret错误或者access_token无效。请开发者认真比对AppSecret的正确性或查看是否正在为恰当的公众号调用接口',
40002 => '不合法的凭证类型',
40003 => '不合法的OpenID请开发者确认OpenID该用户是否已关注公众号或是否是其他公众号的OpenID',
40004 => '不合法的媒体文件类型',
40005 => '不合法的文件类型',
40006 => '不合法的文件大小',
40007 => '不合法的媒体文件id',
40008 => '不合法的消息类型',
40009 => '不合法的图片文件大小',
40010 => '不合法的语音文件大小',
40011 => '不合法的视频文件大小',
40012 => '不合法的缩略图文件大小',
40013 => '不合法的AppID请开发者检查AppID的正确性避免异常字符注意大小写',
40014 => '不合法的access_token请开发者认真比对access_token的有效性如是否过期或查看是否正在为恰当的公众号调用接口',
40015 => '不合法的菜单类型',
40016 => '不合法的按钮个数',
40017 => '不合法的按钮个数',
40018 => '不合法的按钮名字长度',
40019 => '不合法的按钮KEY长度',
40020 => '不合法的按钮URL长度',
40021 => '不合法的菜单版本号',
40022 => '不合法的子菜单级数',
40023 => '不合法的子菜单按钮个数',
40024 => '不合法的子菜单按钮类型',
40025 => '不合法的子菜单按钮名字长度',
40026 => '不合法的子菜单按钮KEY长度',
40027 => '不合法的子菜单按钮URL长度',
40028 => '不合法的自定义菜单使用用户',
40029 => '不合法的oauth_code',
40030 => '不合法的refresh_token',
40031 => '不合法的openid列表',
40032 => '不合法的openid列表长度',
40033 => '不合法的请求字符,不能包含\uxxxx格式的字符',
40035 => '不合法的参数',
40038 => '不合法的请求格式',
40039 => '不合法的URL长度',
40050 => '不合法的分组id',
40051 => '分组名字不合法',
40117 => '分组名字不合法',
40118 => 'media_id大小不合法',
40119 => 'button类型错误',
40120 => 'button类型错误',
40121 => '不合法的media_id类型',
40132 => '微信号不合法',
40137 => '不支持的图片格式',
41001 => '缺少access_token参数',
41002 => '缺少appid参数',
41003 => '缺少refresh_token参数',
41004 => '缺少secret参数',
41005 => '缺少多媒体文件数据',
41006 => '缺少media_id参数',
41007 => '缺少子菜单数据',
41008 => '缺少oauth code',
41009 => '缺少openid',
42001 => 'access_token超时请检查access_token的有效期',
42002 => 'refresh_token超时',
42003 => 'oauth_code超时',
42007 => '用户修改微信密码accesstoken和refreshtoken失效需要重新授权',
43001 => '需要GET请求',
43002 => '需要POST请求',
43003 => '需要HTTPS请求',
43004 => '需要接收者关注',
43005 => '需要好友关系',
44001 => '多媒体文件为空',
44002 => 'POST的数据包为空',
44003 => '图文消息内容为空',
44004 => '文本消息内容为空',
45001 => '多媒体文件大小超过限制',
45002 => '消息内容超过限制',
45003 => '标题字段超过限制',
45004 => '描述字段超过限制',
45005 => '链接字段超过限制',
45006 => '图片链接字段超过限制',
45007 => '语音播放时间超过限制',
45008 => '图文消息超过限制',
45009 => '接口调用超过限制',
45010 => '创建菜单个数超过限制',
45015 => '回复时间超过限制',
45016 => '系统分组,不允许修改',
45017 => '分组名字过长',
45018 => '分组数量超过上限',
45047 => '客服接口下行条数超过上限',
46001 => '不存在媒体数据',
46002 => '不存在的菜单版本',
46003 => '不存在的菜单数据',
46004 => '不存在的用户',
47001 => '解析JSON/XML内容错误',
48001 => 'api功能未授权请确认公众号已获得该接口可以在公众平台官网-开发者中心页中查看接口权限',
48004 => 'api接口被封禁请登录mp.weixin.qq.com查看详情',
48005 => 'api禁止删除被自动回复和自定义菜单引用的素材',
48006 => 'api禁止清零调用次数因为清零次数达到上限',
50001 => '用户未授权该api',
50002 => '用户受限,可能是违规后接口被封禁',
61450 => '客服系统错误',
61451 => '参数错误',
61452 => '无效客服账号',
61453 => '客服帐号已存在',
61454 => '客服帐号名长度超过限制(仅允许10个英文字符不包括@及@后的公众号的微信号)',
61455 => '客服帐号名包含非法字符(仅允许英文+数字)',
61456 => '客服帐号个数超过限制(10个客服账号)',
61457 => '无效头像文件类型',
61458 => '客户正在被其他客服接待',
61459 => '客服不在线',
61500 => '日期格式错误',
65301 => '不存在此menuid对应的个性化菜单',
65302 => '没有相应的用户',
65303 => '没有默认菜单,不能创建个性化菜单',
65304 => 'MatchRule信息为空',
65305 => '个性化菜单数量受限',
65306 => '不支持个性化菜单的帐号',
65307 => '个性化菜单信息为空',
65308 => '包含没有响应类型的button',
65309 => '个性化菜单开关处于关闭状态',
65310 => '填写了省份或城市信息,国家信息不能为空',
65311 => '填写了城市信息,省份信息不能为空',
65312 => '不合法的国家信息',
65313 => '不合法的省份信息',
65314 => '不合法的城市信息',
65316 => '该公众号的菜单设置了过多的域名外跳最多跳转到3个域名的链接',
65317 => '不合法的URL',
65400 => 'API不可用即没有开通/升级到新版客服',
65401 => '无效客服帐号',
65402 => '客服帐号尚未绑定微信号,不能投入使用',
65403 => '客服昵称不合法',
65404 => '客服帐号不合法',
65405 => '帐号数目已达到上限,不能继续添加',
65406 => '已经存在的客服帐号',
65407 => '邀请对象已经是本公众号客服',
65408 => '本公众号已发送邀请给该微信号',
65409 => '无效的微信号',
65410 => '邀请对象绑定公众号客服数量达到上限',
65411 => '该帐号已经有一个等待确认的邀请,不能重复邀请',
65412 => '该帐号已经绑定微信号,不能进行邀请',
65413 => '不存在对应用户的会话信息',
65414 => '粉丝正在被其他客服接待',
65415 => '指定的客服不在线',
9001001 => 'POST数据参数不合法',
9001002 => '远端服务不可用',
9001003 => 'Ticket不合法',
9001004 => '获取摇周边用户信息失败',
9001005 => '获取商户信息失败',
9001006 => '获取OpenID失败',
9001007 => '上传文件缺失',
9001008 => '上传素材的文件类型不合法',
9001009 => '上传素材的文件尺寸不合法',
9001010 => '上传失败',
9001020 => '帐号不合法',
9001021 => '已有设备激活率低于50%,不能新增设备',
9001022 => '设备申请数不合法必须为大于0的数字',
9001023 => '已存在审核中的设备ID申请',
9001024 => '一次查询设备ID数量不能超过50',
9001025 => '设备ID不合法',
9001026 => '页面ID不合法',
9001027 => '页面参数不合法',
9001028 => '一次删除页面ID数量不能超过10',
9001029 => '页面已应用在设备中,请先解除应用关系再删除',
9001030 => '一次查询页面ID数量不能超过50',
9001031 => '时间区间不合法',
9001032 => '保存设备与页面的绑定关系参数错误',
9001033 => '门店ID不合法',
9001034 => '设备备注信息过长',
9001035 => '设备申请参数不合法',
9001036 => '查询起始值begin不合法',
];
/**
* 返回数据结果集
* @var mixed
*/
public static $result;
public static function api($url, $params = [], $method = 'GET')
{
$result = self::http($url, $params, $method);
return self::parseRequestJson($result);
}
/**
* 发送HTTP请求方法目前只支持CURL发送请求
* @param string $url 请求URL
* @param array $params 请求参数
* @param string $method 请求方法GET/POST
* @param boolean $ssl 是否进行SSL双向认证
* @return array $data 响应数据
*/
public static function http($url, $params = [], $method = 'GET', $ssl = false)
{
$opts = [
CURLOPT_TIMEOUT => 30,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
];
/* 根据请求类型设置特定参数 */
switch (strtoupper($method)) {
case 'GET':
$getQuerys = !empty($params) ? '?' . urldecode(http_build_query($params)) : '';
$opts[CURLOPT_URL] = $url . $getQuerys;
break;
case 'POST':
$opts[CURLOPT_URL] = $url;
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $params;
break;
}
/* 初始化并执行curl请求 */
$ch = curl_init();
curl_setopt_array($ch, $opts);
$data = curl_exec($ch);
$err = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
if ($err > 0) {
\cjango\Wechat::error('CURL:' . $errmsg);
return false;
} else {
return $data;
}
}
/**
* XML文档解析成数组并将键值转成小写
* @param xml $xml
* @return array
*/
public static function xml2array($xml)
{
$data = (array) simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
return array_change_key_case($data, CASE_LOWER);
}
/**
* 将数组转换成XML
* @param array $array
* @return xml
*/
public static function array2xml($array = [])
{
$xml = new \SimpleXMLElement('<xml></xml>');
self::_data2xml($xml, $array);
return $xml->asXML();
}
/**
* 数据XML编码
* @param xml $xml XML对象
* @param mixed $data 数据
* @param string $item 数字索引时的节点名称
* @return string xml
*/
private static function _data2xml($xml, $data, $item = 'item')
{
foreach ($data as $key => $value) {
is_numeric($key) && $key = $item;
if (is_array($value) || is_object($value)) {
$child = $xml->addChild($key);
self::_data2xml($child, $value, $item);
} else {
if (is_numeric($value)) {
$child = $xml->addChild($key, $value);
} else {
$child = $xml->addChild($key);
$node = dom_import_simplexml($child);
$node->appendChild($node->ownerDocument->createCDATASection($value));
}
}
}
}
/**
* 解析返回的json数据
* @param [type] $json
* @return
*/
private static function parseRequestJson($json)
{
$result = json_decode($json, true);
if (isset($result['errcode'])) {
if ($result['errcode'] == 0) {
self::$result = $result;
return true;
} else {
Wechat::error(self::parseError($result));
return false;
}
} else {
return $result;
}
}
/**
* 解析错误信息
* @param array $result
* @return string
*/
public static function parseError($result)
{
$code = $result['errcode'];
return '[' . $code . '] ' . (isset(self::$errMsg[$code]) ? self::$errMsg[$code] : $result['errmsg']);
}
}

441
vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,441 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

74
vendor/composer/autoload_classmap.php vendored Normal file
View File

@@ -0,0 +1,74 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
'QrReader' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/QrReader.php',
'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
'Zxing\\Binarizer' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/Binarizer.php',
'Zxing\\BinaryBitmap' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/BinaryBitmap.php',
'Zxing\\ChecksumException' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/ChecksumException.php',
'Zxing\\Common\\BitArray' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/BitArray.php',
'Zxing\\Common\\BitMatrix' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/BitMatrix.php',
'Zxing\\Common\\BitSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/BitSource.php',
'Zxing\\Common\\CharacterSetECI' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/CharacterSetEci.php',
'Zxing\\Common\\CharacterSetEci\\AbstractEnum\\AbstractEnum' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/AbstractEnum.php',
'Zxing\\Common\\DecoderResult' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/DecoderResult.php',
'Zxing\\Common\\DefaultGridSampler' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/DefaultGridSampler.php',
'Zxing\\Common\\DetectorResult' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/DetectorResult.php',
'Zxing\\Common\\Detector\\MathUtils' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/detector/MathUtils.php',
'Zxing\\Common\\Detector\\MonochromeRectangleDetector' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/detector/MonochromeRectangleDetector.php',
'Zxing\\Common\\GlobalHistogramBinarizer' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/GlobalHistogramBinarizer.php',
'Zxing\\Common\\GridSampler' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/GridSampler.php',
'Zxing\\Common\\HybridBinarizer' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/HybridBinarizer.php',
'Zxing\\Common\\PerspectiveTransform' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/PerspectiveTransform.php',
'Zxing\\Common\\Reedsolomon\\GenericGF' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGF.php',
'Zxing\\Common\\Reedsolomon\\GenericGFPoly' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGFPoly.php',
'Zxing\\Common\\Reedsolomon\\ReedSolomonDecoder' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonDecoder.php',
'Zxing\\Common\\Reedsolomon\\ReedSolomonException' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonException.php',
'Zxing\\FormatException' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/FormatException.php',
'Zxing\\GDLuminanceSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php',
'Zxing\\IMagickLuminanceSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/IMagickLuminanceSource.php',
'Zxing\\LuminanceSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/LuminanceSource.php',
'Zxing\\NotFoundException' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/NotFoundException.php',
'Zxing\\PlanarYUVLuminanceSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/PlanarYUVLuminanceSource.php',
'Zxing\\Qrcode\\Decoder\\BitMatrixParser' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/BitMatrixParser.php',
'Zxing\\Qrcode\\Decoder\\DataBlock' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataBlock.php',
'Zxing\\Qrcode\\Decoder\\DataMask' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask000' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask001' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask010' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask011' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask100' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask101' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask110' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask111' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DecodedBitStreamParser' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DecodedBitStreamParser.php',
'Zxing\\Qrcode\\Decoder\\Decoder' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Decoder.php',
'Zxing\\Qrcode\\Decoder\\ECB' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Decoder\\ECBlocks' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Decoder\\ErrorCorrectionLevel' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/ErrorCorrectionLevel.php',
'Zxing\\Qrcode\\Decoder\\FormatInformation' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/FormatInformation.php',
'Zxing\\Qrcode\\Decoder\\Mode' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Mode.php',
'Zxing\\Qrcode\\Decoder\\Version' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Detector\\AlignmentPattern' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPattern.php',
'Zxing\\Qrcode\\Detector\\AlignmentPatternFinder' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPatternFinder.php',
'Zxing\\Qrcode\\Detector\\Detector' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/Detector.php',
'Zxing\\Qrcode\\Detector\\FinderPattern' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPattern.php',
'Zxing\\Qrcode\\Detector\\FinderPatternFinder' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternFinder.php',
'Zxing\\Qrcode\\Detector\\FinderPatternInfo' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternInfo.php',
'Zxing\\Qrcode\\QRCodeReader' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/QRCodeReader.php',
'Zxing\\RGBLuminanceSource' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/RGBLuminanceSource.php',
'Zxing\\Reader' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/Reader.php',
'Zxing\\ReaderException' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/ReaderException.php',
'Zxing\\Result' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/Result.php',
'Zxing\\ResultPoint' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/ResultPoint.php',
);

13
vendor/composer/autoload_files.php vendored Normal file
View File

@@ -0,0 +1,13 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
'626dcc41390ebdaa619faa02d99943b0' => $vendorDir . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => $vendorDir . '/topthink/think-captcha/src/helper.php',
);

10
vendor/composer/autoload_namespaces.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'BaconQrCode' => array($vendorDir . '/bacon/bacon-qr-code/src'),
);

19
vendor/composer/autoload_psr4.php vendored Normal file
View File

@@ -0,0 +1,19 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'think\\composer\\' => array($vendorDir . '/topthink/think-installer/src'),
'think\\captcha\\' => array($vendorDir . '/topthink/think-captcha/src'),
'think\\' => array($baseDir . '/thinkphp/library/think'),
'cjango\\' => array($vendorDir . '/cjango/wechat/src'),
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
'Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'),
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
'Symfony\\Component\\Inflector\\' => array($vendorDir . '/symfony/inflector'),
'MyCLabs\\Enum\\' => array($vendorDir . '/myclabs/php-enum/src'),
'Endroid\\QrCode\\' => array($vendorDir . '/endroid/qrcode/src'),
);

70
vendor/composer/autoload_real.php vendored Normal file
View File

@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitfea605573c13ab63a5388432dc557bc5
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitfea605573c13ab63a5388432dc557bc5', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitfea605573c13ab63a5388432dc557bc5', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitfea605573c13ab63a5388432dc557bc5::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitfea605573c13ab63a5388432dc557bc5::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirefea605573c13ab63a5388432dc557bc5($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequirefea605573c13ab63a5388432dc557bc5($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

175
vendor/composer/autoload_static.php vendored Normal file
View File

@@ -0,0 +1,175 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitfea605573c13ab63a5388432dc557bc5
{
public static $files = array (
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
'626dcc41390ebdaa619faa02d99943b0' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php',
'1cfd2761b63b0a29ed23657ea394cb2d' => __DIR__ . '/..' . '/topthink/think-captcha/src/helper.php',
);
public static $prefixLengthsPsr4 = array (
't' =>
array (
'think\\composer\\' => 15,
'think\\captcha\\' => 14,
'think\\' => 6,
),
'c' =>
array (
'cjango\\' => 7,
),
'S' =>
array (
'Symfony\\Polyfill\\Php70\\' => 23,
'Symfony\\Component\\PropertyAccess\\' => 33,
'Symfony\\Component\\OptionsResolver\\' => 34,
'Symfony\\Component\\Inflector\\' => 28,
),
'M' =>
array (
'MyCLabs\\Enum\\' => 13,
),
'E' =>
array (
'Endroid\\QrCode\\' => 15,
),
);
public static $prefixDirsPsr4 = array (
'think\\composer\\' =>
array (
0 => __DIR__ . '/..' . '/topthink/think-installer/src',
),
'think\\captcha\\' =>
array (
0 => __DIR__ . '/..' . '/topthink/think-captcha/src',
),
'think\\' =>
array (
0 => __DIR__ . '/../..' . '/thinkphp/library/think',
),
'cjango\\' =>
array (
0 => __DIR__ . '/..' . '/cjango/wechat/src',
),
'Symfony\\Polyfill\\Php70\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
),
'Symfony\\Component\\PropertyAccess\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/property-access',
),
'Symfony\\Component\\OptionsResolver\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/options-resolver',
),
'Symfony\\Component\\Inflector\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/inflector',
),
'MyCLabs\\Enum\\' =>
array (
0 => __DIR__ . '/..' . '/myclabs/php-enum/src',
),
'Endroid\\QrCode\\' =>
array (
0 => __DIR__ . '/..' . '/endroid/qrcode/src',
),
);
public static $prefixesPsr0 = array (
'B' =>
array (
'BaconQrCode' =>
array (
0 => __DIR__ . '/..' . '/bacon/bacon-qr-code/src',
),
),
);
public static $classMap = array (
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
'QrReader' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/QrReader.php',
'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
'Zxing\\Binarizer' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/Binarizer.php',
'Zxing\\BinaryBitmap' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/BinaryBitmap.php',
'Zxing\\ChecksumException' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/ChecksumException.php',
'Zxing\\Common\\BitArray' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/BitArray.php',
'Zxing\\Common\\BitMatrix' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/BitMatrix.php',
'Zxing\\Common\\BitSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/BitSource.php',
'Zxing\\Common\\CharacterSetECI' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/CharacterSetEci.php',
'Zxing\\Common\\CharacterSetEci\\AbstractEnum\\AbstractEnum' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/AbstractEnum.php',
'Zxing\\Common\\DecoderResult' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/DecoderResult.php',
'Zxing\\Common\\DefaultGridSampler' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/DefaultGridSampler.php',
'Zxing\\Common\\DetectorResult' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/DetectorResult.php',
'Zxing\\Common\\Detector\\MathUtils' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/detector/MathUtils.php',
'Zxing\\Common\\Detector\\MonochromeRectangleDetector' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/detector/MonochromeRectangleDetector.php',
'Zxing\\Common\\GlobalHistogramBinarizer' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/GlobalHistogramBinarizer.php',
'Zxing\\Common\\GridSampler' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/GridSampler.php',
'Zxing\\Common\\HybridBinarizer' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/HybridBinarizer.php',
'Zxing\\Common\\PerspectiveTransform' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/PerspectiveTransform.php',
'Zxing\\Common\\Reedsolomon\\GenericGF' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGF.php',
'Zxing\\Common\\Reedsolomon\\GenericGFPoly' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGFPoly.php',
'Zxing\\Common\\Reedsolomon\\ReedSolomonDecoder' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonDecoder.php',
'Zxing\\Common\\Reedsolomon\\ReedSolomonException' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonException.php',
'Zxing\\FormatException' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/FormatException.php',
'Zxing\\GDLuminanceSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php',
'Zxing\\IMagickLuminanceSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/IMagickLuminanceSource.php',
'Zxing\\LuminanceSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/LuminanceSource.php',
'Zxing\\NotFoundException' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/NotFoundException.php',
'Zxing\\PlanarYUVLuminanceSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/PlanarYUVLuminanceSource.php',
'Zxing\\Qrcode\\Decoder\\BitMatrixParser' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/BitMatrixParser.php',
'Zxing\\Qrcode\\Decoder\\DataBlock' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataBlock.php',
'Zxing\\Qrcode\\Decoder\\DataMask' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask000' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask001' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask010' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask011' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask100' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask101' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask110' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DataMask111' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php',
'Zxing\\Qrcode\\Decoder\\DecodedBitStreamParser' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DecodedBitStreamParser.php',
'Zxing\\Qrcode\\Decoder\\Decoder' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Decoder.php',
'Zxing\\Qrcode\\Decoder\\ECB' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Decoder\\ECBlocks' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Decoder\\ErrorCorrectionLevel' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/ErrorCorrectionLevel.php',
'Zxing\\Qrcode\\Decoder\\FormatInformation' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/FormatInformation.php',
'Zxing\\Qrcode\\Decoder\\Mode' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Mode.php',
'Zxing\\Qrcode\\Decoder\\Version' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php',
'Zxing\\Qrcode\\Detector\\AlignmentPattern' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPattern.php',
'Zxing\\Qrcode\\Detector\\AlignmentPatternFinder' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPatternFinder.php',
'Zxing\\Qrcode\\Detector\\Detector' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/Detector.php',
'Zxing\\Qrcode\\Detector\\FinderPattern' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPattern.php',
'Zxing\\Qrcode\\Detector\\FinderPatternFinder' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternFinder.php',
'Zxing\\Qrcode\\Detector\\FinderPatternInfo' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternInfo.php',
'Zxing\\Qrcode\\QRCodeReader' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/qrcode/QRCodeReader.php',
'Zxing\\RGBLuminanceSource' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/RGBLuminanceSource.php',
'Zxing\\Reader' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/Reader.php',
'Zxing\\ReaderException' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/ReaderException.php',
'Zxing\\Result' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/Result.php',
'Zxing\\ResultPoint' => __DIR__ . '/..' . '/khanamiryan/qrcode-detector-decoder/lib/ResultPoint.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitfea605573c13ab63a5388432dc557bc5::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitfea605573c13ab63a5388432dc557bc5::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInitfea605573c13ab63a5388432dc557bc5::$prefixesPsr0;
$loader->classMap = ComposerStaticInitfea605573c13ab63a5388432dc557bc5::$classMap;
}, null, ClassLoader::class);
}
}

691
vendor/composer/installed.json vendored Normal file
View File

@@ -0,0 +1,691 @@
[
{
"name": "topthink/think-installer",
"version": "v1.0.12",
"version_normalized": "1.0.12.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-installer.git",
"reference": "1be326e68f63de4e95977ed50f46ae75f017556d"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/top-think/think-installer/1be326e68f63de4e95977ed50f46ae75f017556d.zip",
"reference": "1be326e68f63de4e95977ed50f46ae75f017556d",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0"
},
"require-dev": {
"composer/composer": "1.0.*@dev"
},
"time": "2017-05-27T06:58:09+00:00",
"type": "composer-plugin",
"extra": {
"class": "think\\composer\\Plugin"
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"think\\composer\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
]
},
{
"name": "topthink/think-captcha",
"version": "v1.0.7",
"version_normalized": "1.0.7.0",
"source": {
"type": "git",
"url": "https://github.com/top-think/think-captcha.git",
"reference": "0c55455df26a1626a60d0dc35d2d89002b741d44"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/top-think/think-captcha/0c55455df26a1626a60d0dc35d2d89002b741d44.zip",
"reference": "0c55455df26a1626a60d0dc35d2d89002b741d44",
"shasum": ""
},
"time": "2016-07-06T01:47:11+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"think\\captcha\\": "src/"
},
"files": [
"src/helper.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"description": "captcha package for thinkphp5"
},
{
"name": "cjango/wechat",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/cjango/wechat.git",
"reference": "085ece24569802ebc08488fdfad252c0ce12e5bc"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/cjango/wechat/085ece24569802ebc08488fdfad252c0ce12e5bc.zip",
"reference": "085ece24569802ebc08488fdfad252c0ce12e5bc",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
},
"time": "2017-06-08T03:09:34+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"cjango\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jason.Chen",
"email": "chenjxlg@163.com",
"homepage": "http://www.cjango.com/"
}
],
"description": "wechat sdk",
"homepage": "https://github.com/cjango/wechat",
"keywords": [
"cjango",
"code",
"sdk",
"wechat"
]
},
{
"name": "topthink/framework",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/top-think/framework.git",
"reference": "d1c2044745a7465f827c733affbcfcb6e0f1bb49"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/top-think/framework/d1c2044745a7465f827c733affbcfcb6e0f1bb49.zip",
"reference": "d1c2044745a7465f827c733affbcfcb6e0f1bb49",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"topthink/think-installer": "~1.0"
},
"require-dev": {
"johnkary/phpunit-speedtrap": "^1.0",
"mikey179/vfsstream": "~1.6",
"phpdocumentor/reflection-docblock": "^2.0",
"phploc/phploc": "2.*",
"phpunit/phpunit": "4.8.*",
"sebastian/phpcpd": "2.*"
},
"time": "2017-07-27T10:03:26+00:00",
"type": "think-framework",
"installation-source": "dist",
"autoload": {
"psr-4": {
"think\\": "library/think"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
}
],
"description": "the new thinkphp framework",
"homepage": "http://thinkphp.cn/",
"keywords": [
"framework",
"orm",
"thinkphp"
]
},
{
"name": "khanamiryan/qrcode-detector-decoder",
"version": "1",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git",
"reference": "96d5f80680b04803c4f1b69d6e01735e876b80c7"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/khanamiryan/php-qrcode-detector-decoder/96d5f80680b04803c4f1b69d6e01735e876b80c7.zip",
"reference": "96d5f80680b04803c4f1b69d6e01735e876b80c7",
"shasum": ""
},
"require": {
"php": "^5.6|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
},
"time": "2017-01-13T09:11:46+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"lib/"
],
"files": [
"lib/common/customFunctions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ashot Khanamiryan",
"email": "a.khanamiryan@gmail.com",
"homepage": "https://github.com/khanamiryan",
"role": "Developer"
}
],
"description": "QR code decoder / reader",
"homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder",
"keywords": [
"barcode",
"qr",
"zxing"
]
},
{
"name": "symfony/inflector",
"version": "v3.3.5",
"version_normalized": "3.3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/inflector.git",
"reference": "aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/symfony/inflector/aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb.zip",
"reference": "aed5a0874a3bcfd8d0393a2d91b4cf828f29c7fb",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"time": "2017-04-12T14:14:56+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Inflector\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Inflector Component",
"homepage": "https://symfony.com",
"keywords": [
"inflection",
"pluralize",
"singularize",
"string",
"symfony",
"words"
]
},
{
"name": "paragonie/random_compat",
"version": "v2.0.10",
"version_normalized": "2.0.10.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/paragonie/random_compat/634bae8e911eefa89c1abfbf1b66da679ac8f54d.zip",
"reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"time": "2017-03-13T16:27:32+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"lib/random.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"pseudorandom",
"random"
]
},
{
"name": "symfony/polyfill-php70",
"version": "v1.4.0",
"version_normalized": "1.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php70.git",
"reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/symfony/polyfill-php70/032fd647d5c11a9ceab8ee8747e13b5448e93874.zip",
"reference": "032fd647d5c11a9ceab8ee8747e13b5448e93874",
"shasum": ""
},
"require": {
"paragonie/random_compat": "~1.0|~2.0",
"php": ">=5.3.3"
},
"time": "2017-06-09T14:24:12+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php70\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
]
},
{
"name": "symfony/property-access",
"version": "v3.3.5",
"version_normalized": "3.3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
"reference": "4cd2bc4afdfd914ad18cec97bb4159fc403384ea"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/symfony/property-access/4cd2bc4afdfd914ad18cec97bb4159fc403384ea.zip",
"reference": "4cd2bc4afdfd914ad18cec97bb4159fc403384ea",
"shasum": ""
},
"require": {
"php": ">=5.5.9",
"symfony/inflector": "~3.1",
"symfony/polyfill-php70": "~1.0"
},
"require-dev": {
"symfony/cache": "~3.1"
},
"suggest": {
"psr/cache-implementation": "To cache access methods."
},
"time": "2017-07-03T08:12:02+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\PropertyAccess\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony PropertyAccess Component",
"homepage": "https://symfony.com",
"keywords": [
"access",
"array",
"extraction",
"index",
"injection",
"object",
"property",
"property path",
"reflection"
]
},
{
"name": "symfony/options-resolver",
"version": "v3.3.5",
"version_normalized": "3.3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/symfony/options-resolver/ff48982d295bcac1fd861f934f041ebc73ae40f0.zip",
"reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"time": "2017-04-12T14:14:56+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony OptionsResolver Component",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
]
},
{
"name": "myclabs/php-enum",
"version": "1.5.2",
"version_normalized": "1.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/php-enum.git",
"reference": "3ed7088cfd0a0e06534b7f8b0eee82acea574fac"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/myclabs/php-enum/3ed7088cfd0a0e06534b7f8b0eee82acea574fac.zip",
"reference": "3ed7088cfd0a0e06534b7f8b0eee82acea574fac",
"shasum": ""
},
"require": {
"php": ">=5.3"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35|^5.7|^6.0",
"squizlabs/php_codesniffer": "1.*"
},
"time": "2017-06-28T16:24:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"MyCLabs\\Enum\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP Enum contributors",
"homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
}
],
"description": "PHP Enum implementation",
"homepage": "http://github.com/myclabs/php-enum",
"keywords": [
"enum"
]
},
{
"name": "bacon/bacon-qr-code",
"version": "1.0.1",
"version_normalized": "1.0.1.0",
"source": {
"type": "git",
"url": "https://github.com/Bacon/BaconQrCode.git",
"reference": "031a2ce68c5794064b49d11775b2daf45c96e21c"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/Bacon/BaconQrCode/031a2ce68c5794064b49d11775b2daf45c96e21c.zip",
"reference": "031a2ce68c5794064b49d11775b2daf45c96e21c",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-gd": "to generate QR code images"
},
"time": "2016-01-09T22:55:35+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"BaconQrCode": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Ben Scholzen 'DASPRiD'",
"email": "mail@dasprids.de",
"homepage": "http://www.dasprids.de",
"role": "Developer"
}
],
"description": "BaconQrCode is a QR code generator for PHP.",
"homepage": "https://github.com/Bacon/BaconQrCode"
},
{
"name": "endroid/qrcode",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/endroid/QrCode.git",
"reference": "2974f1c1fd020eb323c3c97179a40dd1d7795627"
},
"dist": {
"type": "zip",
"url": "https://files.phpcomposer.com/files/endroid/QrCode/2974f1c1fd020eb323c3c97179a40dd1d7795627.zip",
"reference": "2974f1c1fd020eb323c3c97179a40dd1d7795627",
"shasum": ""
},
"require": {
"bacon/bacon-qr-code": "^1.0",
"ext-gd": "*",
"khanamiryan/qrcode-detector-decoder": "^1.0",
"myclabs/php-enum": "^1.5",
"php": ">=5.6",
"symfony/options-resolver": "^2.7|^3.0",
"symfony/property-access": "^2.7|^3.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7|^6.0",
"sensio/framework-extra-bundle": "^3.0",
"symfony/asset": "^2.7|^3.0",
"symfony/browser-kit": "^2.7|^3.0",
"symfony/finder": "^2.7|^3.0",
"symfony/framework-bundle": "^2.7|^3.0",
"symfony/http-kernel": "^2.7|^3.0",
"symfony/templating": "^2.7|^3.0",
"symfony/twig-bundle": "^2.7|^3.0",
"symfony/yaml": "^2.7|^3.0"
},
"time": "2017-07-23T14:07:09+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Endroid\\QrCode\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jeroen van den Enden",
"email": "info@endroid.nl",
"homepage": "http://endroid.nl/"
}
],
"description": "Endroid QR Code",
"homepage": "https://github.com/endroid/QrCode",
"keywords": [
"bundle",
"code",
"endroid",
"qr",
"qrcode",
"symfony"
]
}
]

5
vendor/endroid/qrcode/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
/bin
/composer.lock
/composer.phar
/phpunit.xml
/vendor

18
vendor/endroid/qrcode/.travis.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
language: php
php:
- 5.6
- 7.0
- 7.1
matrix:
fast_finish: true
before_install:
- phpenv config-rm xdebug.ini
- composer self-update && composer install --no-interaction
script: bin/phpunit
notifications:
email: info@endroid.nl

19
vendor/endroid/qrcode/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) Jeroen van den Enden
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

147
vendor/endroid/qrcode/README.md vendored Normal file
View File

@@ -0,0 +1,147 @@
QR Code
=======
*By [endroid](http://endroid.nl/)*
[![Latest Stable Version](http://img.shields.io/packagist/v/endroid/qrcode.svg)](https://packagist.org/packages/endroid/qrcode)
[![Build Status](http://img.shields.io/travis/endroid/QrCode.svg)](http://travis-ci.org/endroid/QrCode)
[![Total Downloads](http://img.shields.io/packagist/dt/endroid/qrcode.svg)](https://packagist.org/packages/endroid/qrcode)
[![Monthly Downloads](http://img.shields.io/packagist/dm/endroid/qrcode.svg)](https://packagist.org/packages/endroid/qrcode)
[![License](http://img.shields.io/packagist/l/endroid/qrcode.svg)](https://packagist.org/packages/endroid/qrcode)
This library helps you generate QR codes in an easy way and provides a Symfony
bundle for rapid integration in your project.
## Installation
Use [Composer](https://getcomposer.org/) to install the library.
``` bash
$ composer require endroid/qrcode
```
## Usage
```php
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\LabelAlignment;
use Endroid\QrCode\QrCode;
use Symfony\Component\HttpFoundation\Response;
// Create a basic QR code
$qrCode = new QrCode('Life is too short to be generating QR codes');
$qrCode->setSize(300);
// Set advanced options
$qrCode
->setWriterByName('png')
->setMargin(10)
->setEncoding('UTF-8')
->setErrorCorrectionLevel(ErrorCorrectionLevel::HIGH)
->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0])
->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255])
->setLabel('Scan the code', 16, __DIR__.'/../assets/noto_sans.otf', LabelAlignment::CENTER)
->setLogoPath(__DIR__.'/../assets/symfony.png')
->setLogoWidth(150)
->setValidateResult(false)
;
// Directly output the QR code
header('Content-Type: '.$qrCode->getContentType());
echo $qrCode->writeString();
// Save it to a file
$qrCode->writeFile(__DIR__.'/qrcode.png');
// Create a response object
$response = new Response($qrCode->writeString(), Response::HTTP_OK, ['Content-Type' => $qrCode->getContentType()]);
```
![QR Code](http://endroid.nl/qrcode/Dit%20is%20een%20test.png)
## Symfony integration
Register the Symfony bundle in the kernel.
```php
// app/AppKernel.php
public function registerBundles()
{
$bundles = [
// ...
new Endroid\QrCode\Bundle\EndroidQrCodeBundle(),
];
}
```
The bundle makes use of a factory to create QR codes. The default parameters
applied by the factory can optionally be overridden via the configuration.
```yaml
endroid_qr_code:
writer: 'png'
size: 300
margin: 10
foreground_color: { r: 0, g: 0, b: 0 }
background_color: { r: 255, g: 255, b: 255 }
error_correction_level: low # low, medium, quartile or high
encoding: UTF-8
label: Scan the code
label_font_size: 20
label_alignment: left # left, center or right
label_margin: { b: 20 }
logo_path: '%kernel.root_dir%/../vendor/endroid/qrcode/assets/symfony.png'
logo_width: 150
validate_result: false # checks if the result is readable
```
The readability of a QR code is primarily determined by the size, the input
length, the error correction level and any possible logo over the image. The
`validate_result` option uses a built-in reader to validate the resulting
image. This does not guarantee that the code will be readable by all readers
but this helps you provide a minimum level of quality. Take note that the
validator can consume quite an amount of resources and is disabled by default.
Now you can retrieve the factory from the service container and create a QR
code. For instance in your controller this would look like this.
```php
$qrCode = $this->get('endroid.qrcode.factory')->create('QR Code', ['size' => 200]);
```
Add the following section to your routing to be able to handle QR code URLs.
This step can be skipped if you only use data URIs to display your images.
``` yml
EndroidQrCodeBundle:
resource: "@EndroidQrCodeBundle/Controller/"
type: annotation
prefix: /qrcode
```
After installation and configuration, QR codes can be generated by appending
the QR code text to the url followed by any of the supported extensions.
## Twig extension
The bundle provides a Twig extension for generating a QR code URL, path or data
URI. You can use the second argument of any of these functions to override any
defaults defined by the bundle or set via your configuration.
``` twig
<img src="{{ qrcode_path(message) }}" />
<img src="{{ qrcode_url(message, { writer: 'eps' }) }}" />
<img src="{{ qrcode_data_uri(message, { writer: 'svg', size: 150 }) }}" />
```
## Versioning
Version numbers follow the MAJOR.MINOR.PATCH scheme. Backwards compatibility
breaking changes will be kept to a minimum but be aware that these can occur.
Lock your dependencies for production and test your code when upgrading.
## License
This bundle is under the MIT license. For the full copyright and license
information please view the LICENSE file that was distributed with this source code.

Binary file not shown.

Binary file not shown.

BIN
vendor/endroid/qrcode/assets/symfony.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

54
vendor/endroid/qrcode/composer.json vendored Normal file
View File

@@ -0,0 +1,54 @@
{
"name": "endroid/qrcode",
"description": "Endroid QR Code",
"keywords": ["endroid", "qrcode", "qr", "code", "bundle", "symfony"],
"homepage": "https://github.com/endroid/QrCode",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Jeroen van den Enden",
"email": "info@endroid.nl",
"homepage": "http://endroid.nl/"
}
],
"require": {
"php": ">=5.6",
"ext-gd": "*",
"symfony/options-resolver": "^2.7|^3.0",
"bacon/bacon-qr-code": "^1.0",
"khanamiryan/qrcode-detector-decoder": "^1.0",
"symfony/property-access": "^2.7|^3.0",
"myclabs/php-enum": "^1.5"
},
"require-dev": {
"symfony/asset": "^2.7|^3.0",
"symfony/browser-kit": "^2.7|^3.0",
"symfony/finder": "^2.7|^3.0",
"symfony/framework-bundle": "^2.7|^3.0",
"symfony/http-kernel": "^2.7|^3.0",
"symfony/templating": "^2.7|^3.0",
"symfony/twig-bundle": "^2.7|^3.0",
"symfony/yaml": "^2.7|^3.0",
"sensio/framework-extra-bundle": "^3.0",
"phpunit/phpunit": "^5.7|^6.0"
},
"autoload": {
"psr-4": {
"Endroid\\QrCode\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Endroid\\QrCode\\Tests\\": "tests/"
}
},
"config": {
"bin-dir": "bin"
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
}
}

11
vendor/endroid/qrcode/phpunit.xml.dist vendored Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/Bundle/app/bootstrap.php" colors="true">
<testsuites>
<testsuite>
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>
<php>
<server name="KERNEL_DIR" value="tests/Bundle/app" />
</php>
</phpunit>

View File

@@ -0,0 +1,62 @@
<?php
/*
* (c) Jeroen van den Enden <info@endroid.nl>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Endroid\QrCode\Bundle\Controller;
use Endroid\QrCode\Factory\QrCodeFactory;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
/**
* QR code controller.
*/
class QrCodeController extends Controller
{
/**
* @Route("/{text}.{extension}", name="endroid_qrcode_generate", requirements={"text"="[\w\W]+"})
*
* @param Request $request
* @param string $text
* @param string $extension
* @return Response
*/
public function generateAction(Request $request, $text, $extension)
{
$options = $request->query->all();
$qrCode = $this->getQrCodeFactory()->create($text, $options);
$qrCode->setWriterByExtension($extension);
return new Response($qrCode->writeString(), Response::HTTP_OK, ['Content-Type' => $qrCode->getContentType()]);
}
/**
* @Route("/twig", name="endroid_qrcode_twig_functions")
* @Template()
*
* @return array
*/
public function twigFunctionsAction()
{
return [
'message' => 'QR Code'
];
}
/**
* @return QrCodeFactory
*/
protected function getQrCodeFactory()
{
return $this->get('endroid.qrcode.factory');
}
}

Some files were not shown because too many files have changed in this diff Show More