| Server IP : 172.64.80.1 / Your IP : 172.70.80.151 Web Server : Apache System : Linux mail.federalpolyede.edu.ng 5.10.0-32-amd64 #1 SMP Debian 5.10.223-1 (2024-08-10) x86_64 User : federalpolyede.edu.ng_idh35skikv ( 10000) PHP Version : 7.4.33 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/federalpolyede.edu.ng/httpdocs/php-qrcode/src/Data/ |
Upload File : |
<?php
/**
* Class QRData
*
* @created 25.11.2015
* @author Smiley <[email protected]>
* @copyright 2015 Smiley
* @license MIT
*/
namespace chillerlan\QRCode\Data;
use chillerlan\QRCode\QROptions;
use chillerlan\QRCode\Common\{BitBuffer, EccLevel, Mode, Version};
use chillerlan\Settings\SettingsContainerInterface;
use function sprintf;
/**
* Processes the binary data and maps it on a QRMatrix which is then being returned
*/
final class QRData{
/**
* the options instance
*/
private SettingsContainerInterface|QROptions $options;
/**
* a BitBuffer instance
*/
private BitBuffer $bitBuffer;
/**
* an EccLevel instance
*/
private EccLevel $eccLevel;
/**
* current QR Code version
*/
private Version $version;
/**
* @var \chillerlan\QRCode\Data\QRDataModeInterface[]
*/
private array $dataSegments = [];
/**
* Max bits for the current ECC mode
*
* @var int[]
*/
private array $maxBitsForEcc;
/**
* QRData constructor.
*/
public function __construct(SettingsContainerInterface|QROptions $options, array $dataSegments = []){
$this->options = $options;
$this->bitBuffer = new BitBuffer;
$this->eccLevel = new EccLevel($this->options->eccLevel);
$this->maxBitsForEcc = $this->eccLevel->getMaxBits();
$this->setData($dataSegments);
}
/**
* Sets the data string (internally called by the constructor)
*
* Subsequent calls will overwrite the current state - use the QRCode::add*Segement() method instead
*
* @param \chillerlan\QRCode\Data\QRDataModeInterface[] $dataSegments
*/
public function setData(array $dataSegments):self{
$this->dataSegments = $dataSegments;
$this->version = $this->getMinimumVersion();
$this->bitBuffer->clear();
$this->writeBitBuffer();
return $this;
}
/**
* Returns the current BitBuffer instance
*
* @codeCoverageIgnore
*/
public function getBitBuffer():BitBuffer{
return $this->bitBuffer;
}
/**
* Sets a BitBuffer object
*
* This can be used instead of setData(), however, the version auto-detection is not available in this case.
* The version needs to match the length bits range for the data mode the data has been encoded with,
* additionally the bit array needs to contain enough pad bits.
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function setBitBuffer(BitBuffer $bitBuffer):self{
if($this->options->version === Version::AUTO){
throw new QRCodeDataException('version auto detection is not available');
}
if($bitBuffer->getLength() === 0){
throw new QRCodeDataException('the given BitBuffer is empty');
}
$this->dataSegments = [];
$this->bitBuffer = $bitBuffer;
$this->version = new Version($this->options->version);
return $this;
}
/**
* returns a fresh matrix object with the data written and masked with the given $maskPattern
*/
public function writeMatrix():QRMatrix{
return (new QRMatrix($this->version, $this->eccLevel))
->initFunctionalPatterns()
->writeCodewords($this->bitBuffer)
;
}
/**
* estimates the total length of the several mode segments in order to guess the minimum version
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function estimateTotalBitLength():int{
$length = 0;
foreach($this->dataSegments as $segment){
// data length of the current segment
$length += $segment->getLengthInBits();
// +4 bits for the mode descriptor
$length += 4;
// Hanzi mode sets an additional 4 bit long subset identifier
if($segment instanceof Hanzi){
$length += 4;
}
}
$provisionalVersion = null;
foreach($this->maxBitsForEcc as $version => $maxBits){
if($length <= $maxBits){
$provisionalVersion = $version;
}
}
if($provisionalVersion !== null){
// add character count indicator bits for the provisional version
foreach($this->dataSegments as $segment){
$length += Mode::getLengthBitsForVersion($segment::DATAMODE, $provisionalVersion);
}
// it seems that in some cases the estimated total length is not 100% accurate,
// so we substract 4 bits from the total when not in mixed mode
if(count($this->dataSegments) <= 1){
$length -= 4;
}
// we've got a match!
// or let's see if there's a higher version number available
if($length <= $this->maxBitsForEcc[$provisionalVersion] || isset($this->maxBitsForEcc[($provisionalVersion + 1)])){
return $length;
}
}
throw new QRCodeDataException(sprintf('estimated data exceeds %d bits', $length));
}
/**
* returns the minimum version number for the given string
*
* @throws \chillerlan\QRCode\Data\QRCodeDataException
*/
public function getMinimumVersion():Version{
if($this->options->version !== Version::AUTO){
return new Version($this->options->version);
}
$total = $this->estimateTotalBitLength();
// guess the version number within the given range
for($version = $this->options->versionMin; $version <= $this->options->versionMax; $version++){
if($total <= ($this->maxBitsForEcc[$version] - 4)){
return new Version($version);
}
}
// it's almost impossible to run into this one as $this::estimateTotalBitLength() would throw first
throw new QRCodeDataException('failed to guess minimum version'); // @codeCoverageIgnore
}
/**
* creates a BitBuffer and writes the string data to it
*
* @throws \chillerlan\QRCode\QRCodeException on data overflow
*/
private function writeBitBuffer():void{
$MAX_BITS = $this->eccLevel->getMaxBitsForVersion($this->version);
foreach($this->dataSegments as $segment){
$segment->write($this->bitBuffer, $this->version->getVersionNumber());
}
// overflow, likely caused due to invalid version setting
if($this->bitBuffer->getLength() > $MAX_BITS){
throw new QRCodeDataException(
sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS)
);
}
// add terminator (ISO/IEC 18004:2000 Table 2)
if(($this->bitBuffer->getLength() + 4) <= $MAX_BITS){
$this->bitBuffer->put(Mode::TERMINATOR, 4);
}
// Padding: ISO/IEC 18004:2000 8.4.9 Bit stream to codeword conversion
// if the final codeword is not exactly 8 bits in length, it shall be made 8 bits long
// by the addition of padding bits with binary value 0
while(($this->bitBuffer->getLength() % 8) !== 0){
if($this->bitBuffer->getLength() === $MAX_BITS){
break;
}
$this->bitBuffer->putBit(false);
}
// The message bit stream shall then be extended to fill the data capacity of the symbol
// corresponding to the Version and Error Correction Level, by the addition of the Pad
// Codewords 11101100 and 00010001 alternately.
$alternate = false;
while(($this->bitBuffer->getLength() + 8) <= $MAX_BITS){
$this->bitBuffer->put(($alternate) ? 0b00010001 : 0b11101100, 8);
$alternate = !$alternate;
}
// In certain versions of symbol, it may be necessary to add 3, 4 or 7 Remainder Bits (all zeros)
// to the end of the message in order exactly to fill the symbol capacity
while($this->bitBuffer->getLength() <= $MAX_BITS){
$this->bitBuffer->putBit(false);
}
}
}