Update php-qrcode and php-settings-container for PHP 8.1
By running the following command after updating composer.json
```
composer update chillerlan/php-qrcode chillerlan/php-settings-container
```
This change fixes a deprecation warning from Preferences ->
Personal data / Authentication -> Authenticator (OTP).
```
Return type of chillerlan\Settings\SettingsContainerAbstract::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice
1. vendor/chillerlan/php-settings-container/src/SettingsContainerAbstract.php(19): ttrss_error_handler(Return type of chillerlan\Settings\SettingsContainerAbstract::jsonSerialize() should either be compatible with JsonSerializable::jsonSerialize(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice, vendor/chillerlan/php-settings-container/src/SettingsContainerAbstract.php)
2. vendor/composer/ClassLoader.php(571): include(/usr/share/webapps/tt-rss/vendor/chillerlan/php-settings-container/src/SettingsContainerAbstract.php)
3. vendor/composer/ClassLoader.php(428): Composer\Autoload\includeFile(/usr/share/webapps/tt-rss/vendor/composer/../chillerlan/php-settings-container/src/SettingsContainerAbstract.php)
4. vendor/chillerlan/php-qrcode/src/QROptions.php(59): loadClass(chillerlan\Settings\SettingsContainerAbstract)
5. vendor/composer/ClassLoader.php(571): include(/usr/share/webapps/tt-rss/vendor/chillerlan/php-qrcode/src/QROptions.php)
6. vendor/composer/ClassLoader.php(428): Composer\Autoload\includeFile(/usr/share/webapps/tt-rss/vendor/composer/../chillerlan/php-qrcode/src/QROptions.php)
7. vendor/chillerlan/php-qrcode/src/QRCode.php(113): loadClass(chillerlan\QRCode\QROptions)
8. classes/pref/prefs.php(958): __construct()
9. classes/pref/prefs.php(469): _get_otp_qrcode_img()
10. classes/pref/prefs.php(541): index_auth_2fa()
11. backend.php(136): index_auth()
```
The issue is fixed in php-settings-container 2.1.1 [1] Here I use the
latest php-qrcode version for another PHP 8.1 fix [2].
[1] 68bc5019c8 (diff-359c7f7a6d32d9935951e1b0742eb116fb654f4a932c8d40328bb5dcab2fa111L162)
[2] https://github.com/chillerlan/php-qrcode/issues/97
This commit is contained in:
parent
d9861038bc
commit
4b61618920
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"spomky-labs/otphp": "^10.0",
|
"spomky-labs/otphp": "^10.0",
|
||||||
"chillerlan/php-qrcode": "^3.3",
|
"chillerlan/php-qrcode": "^4.3.3",
|
||||||
"mervick/material-design-icons": "^2.2",
|
"mervick/material-design-icons": "^2.2",
|
||||||
"j4mie/idiorm": "^1.5"
|
"j4mie/idiorm": "^1.5"
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "0c52fd6b8f33561f7c03f12df6f5480f",
|
"content-hash": "8bb918e9a6d0b833ebcb0b525885d36d",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "beberlei/assert",
|
"name": "beberlei/assert",
|
||||||
|
@ -75,26 +75,26 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-qrcode",
|
"name": "chillerlan/php-qrcode",
|
||||||
"version": "3.4.1",
|
"version": "4.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/chillerlan/php-qrcode.git",
|
"url": "https://github.com/chillerlan/php-qrcode.git",
|
||||||
"reference": "468603b687a5fe75c1ff33857a45f1726c7b95a9"
|
"reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/468603b687a5fe75c1ff33857a45f1726c7b95a9",
|
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3",
|
||||||
"reference": "468603b687a5fe75c1ff33857a45f1726c7b95a9",
|
"reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"chillerlan/php-settings-container": "^1.2.2",
|
"chillerlan/php-settings-container": "^2.1",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^7.4 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phan/phan": "^3.2.2",
|
"phan/phan": "^5.3",
|
||||||
"phpunit/phpunit": "^8.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"setasign/fpdf": "^1.8.2"
|
"setasign/fpdf": "^1.8.2"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
|
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A QR code generator. PHP 7.2+",
|
"description": "A QR code generator. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-qrcode",
|
"homepage": "https://github.com/chillerlan/php-qrcode",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"phpqrcode",
|
"phpqrcode",
|
||||||
|
@ -135,6 +135,10 @@
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"qrcode-generator"
|
"qrcode-generator"
|
||||||
],
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/chillerlan/php-qrcode/issues",
|
||||||
|
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3"
|
||||||
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
||||||
|
@ -145,28 +149,29 @@
|
||||||
"type": "ko_fi"
|
"type": "ko_fi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-09-03T17:54:45+00:00"
|
"time": "2021-11-25T22:38:09+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-settings-container",
|
"name": "chillerlan/php-settings-container",
|
||||||
"version": "1.2.2",
|
"version": "2.1.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/chillerlan/php-settings-container.git",
|
"url": "https://github.com/chillerlan/php-settings-container.git",
|
||||||
"reference": "d1b5284d6eb3a767459738bb0b20073f0cb3eeaf"
|
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/d1b5284d6eb3a767459738bb0b20073f0cb3eeaf",
|
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/125dd573b45ffc7cabecf385986a356ba2c6f602",
|
||||||
"reference": "d1b5284d6eb3a767459738bb0b20073f0cb3eeaf",
|
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^7.4 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8.4"
|
"phan/phan": "^5.3",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -185,14 +190,19 @@
|
||||||
"homepage": "https://github.com/codemasher"
|
"homepage": "https://github.com/codemasher"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.2+",
|
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-settings-container",
|
"homepage": "https://github.com/chillerlan/php-settings-container",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"PHP7",
|
"PHP7",
|
||||||
"Settings",
|
"Settings",
|
||||||
|
"configuration",
|
||||||
"container",
|
"container",
|
||||||
"helper"
|
"helper"
|
||||||
],
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/chillerlan/php-settings-container/issues",
|
||||||
|
"source": "https://github.com/chillerlan/php-settings-container"
|
||||||
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
||||||
|
@ -203,7 +213,7 @@
|
||||||
"type": "ko_fi"
|
"type": "ko_fi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-09-03T17:33:25+00:00"
|
"time": "2022-03-09T13:18:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "j4mie/idiorm",
|
"name": "j4mie/idiorm",
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
ko_fi: codemasher
|
ko_fi: codemasher
|
||||||
|
custom: "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4"
|
||||||
|
|
|
@ -2,8 +2,12 @@
|
||||||
# https://github.com/sebastianbergmann/phpunit/blob/master/.github/workflows/ci.yml
|
# https://github.com/sebastianbergmann/phpunit/blob/master/.github/workflows/ci.yml
|
||||||
|
|
||||||
on:
|
on:
|
||||||
- pull_request
|
push:
|
||||||
- push
|
branches:
|
||||||
|
- v4.3.x
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- v4.3.x
|
||||||
|
|
||||||
name: "Continuous Integration"
|
name: "Continuous Integration"
|
||||||
|
|
||||||
|
@ -46,14 +50,17 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
# - windows-latest
|
- windows-latest
|
||||||
php-version:
|
php-version:
|
||||||
- "7.2"
|
|
||||||
- "7.3"
|
|
||||||
- "7.4"
|
- "7.4"
|
||||||
- "8.0"
|
- "8.0"
|
||||||
|
- "8.1"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
# - name: "Configure git to avoid issues with line endings"
|
||||||
|
# if: matrix.os == 'windows-latest'
|
||||||
|
# run: git config --global core.autocrlf false
|
||||||
|
|
||||||
- name: "Checkout"
|
- name: "Checkout"
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
@ -69,7 +76,7 @@ jobs:
|
||||||
run: composer update --no-ansi --no-interaction --no-progress --no-suggest
|
run: composer update --no-ansi --no-interaction --no-progress --no-suggest
|
||||||
|
|
||||||
- name: "Run tests with phpunit"
|
- name: "Run tests with phpunit"
|
||||||
run: php vendor/phpunit/phpunit/phpunit --configuration=phpunit.xml
|
run: php vendor/bin/phpunit --configuration=phpunit.xml
|
||||||
|
|
||||||
- name: "Send code coverage report to Codecov.io"
|
- name: "Send code coverage report to Codecov.io"
|
||||||
uses: codecov/codecov-action@v1
|
uses: codecov/codecov-action@v1
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.build/*
|
.build/*
|
||||||
.idea/*
|
.idea/*
|
||||||
|
docs/*
|
||||||
vendor/*
|
vendor/*
|
||||||
composer.lock
|
composer.lock
|
||||||
*.phpunit.result.cache
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This configuration will be read and overlaid on top of the
|
||||||
|
* default configuration. Command-line arguments will be applied
|
||||||
|
* after this file is read.
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
// Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`,
|
||||||
|
// `'7.4'`, `null`.
|
||||||
|
// If this is set to `null`,
|
||||||
|
// then Phan assumes the PHP version which is closest to the minor version
|
||||||
|
// of the php executable used to execute Phan.
|
||||||
|
//
|
||||||
|
// Note that the **only** effect of choosing `'5.6'` is to infer
|
||||||
|
// that functions removed in php 7.0 exist.
|
||||||
|
// (See `backward_compatibility_checks` for additional options)
|
||||||
|
'target_php_version' => '7.4',
|
||||||
|
|
||||||
|
// A list of directories that should be parsed for class and
|
||||||
|
// method information. After excluding the directories
|
||||||
|
// defined in exclude_analysis_directory_list, the remaining
|
||||||
|
// files will be statically analyzed for errors.
|
||||||
|
//
|
||||||
|
// Thus, both first-party and third-party code being used by
|
||||||
|
// your application should be included in this list.
|
||||||
|
'directory_list' => [
|
||||||
|
'examples',
|
||||||
|
'src',
|
||||||
|
'tests',
|
||||||
|
'vendor',
|
||||||
|
'.phan/stubs'
|
||||||
|
],
|
||||||
|
|
||||||
|
// A regex used to match every file name that you want to
|
||||||
|
// exclude from parsing. Actual value will exclude every
|
||||||
|
// "test", "tests", "Test" and "Tests" folders found in
|
||||||
|
// "vendor/" directory.
|
||||||
|
'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
|
||||||
|
|
||||||
|
// A directory list that defines files that will be excluded
|
||||||
|
// from static analysis, but whose class and method
|
||||||
|
// information should be included.
|
||||||
|
//
|
||||||
|
// Generally, you'll want to include the directories for
|
||||||
|
// third-party code (such as "vendor/") in this list.
|
||||||
|
//
|
||||||
|
// n.b.: If you'd like to parse but not analyze 3rd
|
||||||
|
// party code, directories containing that code
|
||||||
|
// should be added to both the `directory_list`
|
||||||
|
// and `exclude_analysis_directory_list` arrays.
|
||||||
|
'exclude_analysis_directory_list' => [
|
||||||
|
'vendor/',
|
||||||
|
'.phan/stubs'
|
||||||
|
],
|
||||||
|
];
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,16 @@
|
||||||
|
build:
|
||||||
|
nodes:
|
||||||
|
analysis:
|
||||||
|
tests:
|
||||||
|
override:
|
||||||
|
- php-scrutinizer-run
|
||||||
|
environment:
|
||||||
|
php: 8.0.0
|
||||||
|
|
||||||
filter:
|
filter:
|
||||||
excluded_paths:
|
excluded_paths:
|
||||||
- examples/*
|
- examples/*
|
||||||
- tests/*
|
- tests/*
|
||||||
- vendor/*
|
- vendor/*
|
||||||
|
- .github/*
|
||||||
|
- .phan/*
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- main
|
|
||||||
- v3.2.x
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- imagemagick
|
|
||||||
|
|
||||||
language: php
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- php: 7.2
|
|
||||||
- php: 7.3
|
|
||||||
- php: 7.4
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- pecl channel-update pecl.php.net
|
|
||||||
- printf "\n" | pecl install imagick
|
|
||||||
install: travis_retry composer install --no-interaction --prefer-source
|
|
||||||
script: vendor/bin/phpunit --configuration phpunit.xml --coverage-clover clover.xml
|
|
||||||
after_script: bash <(curl -s https://codecov.io/bash)
|
|
|
@ -1,39 +1,38 @@
|
||||||
# chillerlan/php-qrcode
|
# chillerlan/php-qrcode
|
||||||
|
|
||||||
A PHP7.2+ QR Code library based on the [implementation](https://github.com/kazuhikoarase/qrcode-generator) by [Kazuhiko Arase](https://github.com/kazuhikoarase),
|
A PHP 7.4+ QR Code library based on the [implementation](https://github.com/kazuhikoarase/qrcode-generator) by [Kazuhiko Arase](https://github.com/kazuhikoarase),
|
||||||
namespaced, cleaned up, improved and other stuff.
|
namespaced, cleaned up, improved and other stuff.
|
||||||
|
|
||||||
|
[![PHP Version Support][php-badge]][php]
|
||||||
[![Packagist version][packagist-badge]][packagist]
|
[![Packagist version][packagist-badge]][packagist]
|
||||||
[![License][license-badge]][license]
|
[![License][license-badge]][license]
|
||||||
[![Travis CI][travis-badge]][travis]
|
|
||||||
[![CodeCov][coverage-badge]][coverage]
|
[![CodeCov][coverage-badge]][coverage]
|
||||||
[![Scrunitizer CI][scrutinizer-badge]][scrutinizer]
|
[![Scrunitizer CI][scrutinizer-badge]][scrutinizer]
|
||||||
[![Packagist downloads][downloads-badge]][downloads]
|
[![Packagist downloads][downloads-badge]][downloads]<br/>
|
||||||
[![PayPal donate][donate-badge]][donate]
|
|
||||||
|
|
||||||
[![Continuous Integration][gh-action-badge]][gh-action]
|
[![Continuous Integration][gh-action-badge]][gh-action]
|
||||||
|
|
||||||
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg?style=flat-square
|
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-qrcode?logo=php&color=8892BF
|
||||||
|
[php]: https://www.php.net/supported-versions.php
|
||||||
|
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-qrcode.svg
|
||||||
[packagist]: https://packagist.org/packages/chillerlan/php-qrcode
|
[packagist]: https://packagist.org/packages/chillerlan/php-qrcode
|
||||||
[license-badge]: https://img.shields.io/github/license/chillerlan/php-qrcode.svg?style=flat-square
|
[license-badge]: https://img.shields.io/github/license/chillerlan/php-qrcode.svg
|
||||||
[license]: https://github.com/chillerlan/php-qrcode/blob/main/LICENSE
|
[license]: https://github.com/chillerlan/php-qrcode/blob/main/LICENSE
|
||||||
[travis-badge]: https://img.shields.io/travis/chillerlan/php-qrcode.svg?style=flat-square
|
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode.svg?logo=codecov
|
||||||
[travis]: https://travis-ci.org/chillerlan/php-qrcode
|
|
||||||
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-qrcode.svg?style=flat-square
|
|
||||||
[coverage]: https://codecov.io/github/chillerlan/php-qrcode
|
[coverage]: https://codecov.io/github/chillerlan/php-qrcode
|
||||||
[scrutinizer-badge]: https://img.shields.io/scrutinizer/g/chillerlan/php-qrcode.svg?style=flat-square
|
[scrutinizer-badge]: https://img.shields.io/scrutinizer/g/chillerlan/php-qrcode.svg?logo=scrutinizer
|
||||||
[scrutinizer]: https://scrutinizer-ci.com/g/chillerlan/php-qrcode
|
[scrutinizer]: https://scrutinizer-ci.com/g/chillerlan/php-qrcode
|
||||||
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode.svg?style=flat-square
|
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-qrcode.svg
|
||||||
[downloads]: https://packagist.org/packages/chillerlan/php-qrcode/stats
|
[downloads]: https://packagist.org/packages/chillerlan/php-qrcode/stats
|
||||||
[donate-badge]: https://img.shields.io/badge/donate-paypal-ff33aa.svg?style=flat-square
|
|
||||||
[donate]: https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4
|
|
||||||
[gh-action-badge]: https://github.com/chillerlan/php-qrcode/workflows/Continuous%20Integration/badge.svg
|
[gh-action-badge]: https://github.com/chillerlan/php-qrcode/workflows/Continuous%20Integration/badge.svg
|
||||||
[gh-action]: https://github.com/chillerlan/php-qrcode/actions
|
[gh-action]: https://github.com/chillerlan/php-qrcode/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Av4.3.x
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
See [the wiki](https://github.com/chillerlan/php-qrcode/wiki) for advanced documentation.
|
||||||
|
An API documentation created with [phpDocumentor](https://www.phpdoc.org/) can be found at https://chillerlan.github.io/php-qrcode/ (WIP).
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
- PHP 7.2+
|
- PHP 7.4+
|
||||||
- `ext-mbstring`
|
- `ext-mbstring`
|
||||||
- optional:
|
- optional:
|
||||||
- `ext-json`, `ext-gd`
|
- `ext-json`, `ext-gd`
|
||||||
|
@ -45,22 +44,29 @@ namespaced, cleaned up, improved and other stuff.
|
||||||
|
|
||||||
via terminal: `composer require chillerlan/php-qrcode`
|
via terminal: `composer require chillerlan/php-qrcode`
|
||||||
|
|
||||||
*composer.json* (note: replace `dev-master` with a [version boundary](https://getcomposer.org/doc/articles/versions.md), e.g. `^3.2`)
|
*composer.json*
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2",
|
"php": "^7.4",
|
||||||
"chillerlan/php-qrcode": "^3.4"
|
"chillerlan/php-qrcode": "dev-main"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Usage
|
Note: replace `dev-main` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^3.2` - see [releases](https://github.com/chillerlan/php-qrcode/releases) for valid versions.
|
||||||
|
For PHP version ...
|
||||||
|
- 7.4+ use `^4.3`
|
||||||
|
- 7.2+ use `^3.4.1` (PHP 7.2 is EOL and 7.3 soon! v3.4.1 also supports PHP8 - however, it's no longer supported)
|
||||||
|
- 7.0+ use `^2.0` (PHP 7.0 and 7.1 are EOL!)
|
||||||
|
- 5.6+ use `^1.0` (please let PHP 5 die!)
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
We want to encode this URI for a mobile authenticator into a QRcode image:
|
We want to encode this URI for a mobile authenticator into a QRcode image:
|
||||||
```php
|
```php
|
||||||
$data = 'otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net';
|
$data = 'otpauth://totp/test?secret=B3JX4VCVJDVNXNZ5&issuer=chillerlan.net';
|
||||||
|
|
||||||
//quick and simple:
|
// quick and simple:
|
||||||
echo '<img src="'.(new QRCode)->render($data).'" alt="QR Code" />';
|
echo '<img src="'.(new QRCode)->render($data).'" alt="QR Code" />';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -69,310 +75,7 @@ echo '<img src="'.(new QRCode)->render($data).'" alt="QR Code" />';
|
||||||
<img alt="QR codes are awesome!" src="https://raw.githubusercontent.com/chillerlan/php-qrcode/main/examples/example_svg.png">
|
<img alt="QR codes are awesome!" src="https://raw.githubusercontent.com/chillerlan/php-qrcode/main/examples/example_svg.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Wait, what was that? Please again, slower!
|
Wait, what was that? Please again, slower! See [Advanced usage](https://github.com/chillerlan/php-qrcode/wiki/Advanced-usage) on the wiki.
|
||||||
|
|
||||||
### Advanced usage
|
|
||||||
|
|
||||||
Ok, step by step. First you'll need a `QRCode` instance, which can be optionally invoked with a `QROptions` (or a [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/master/src/SettingsContainerInterface.php), respectively) object as the only parameter.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$options = new QROptions([
|
|
||||||
'version' => 5,
|
|
||||||
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
|
|
||||||
'eccLevel' => QRCode::ECC_L,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// invoke a fresh QRCode instance
|
|
||||||
$qrcode = new QRCode($options);
|
|
||||||
|
|
||||||
// and dump the output
|
|
||||||
$qrcode->render($data);
|
|
||||||
|
|
||||||
// ...with additional cache file
|
|
||||||
$qrcode->render($data, '/path/to/file.svg');
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you just want the raw QR code matrix, call `QRCode::getMatrix()` - this method is also called internally from `QRCode::render()`. See also [Custom output modules](#custom-qroutputinterface).
|
|
||||||
|
|
||||||
```php
|
|
||||||
$matrix = $qrcode->getMatrix($data);
|
|
||||||
|
|
||||||
foreach($matrix->matrix() as $y => $row){
|
|
||||||
foreach($row as $x => $module){
|
|
||||||
|
|
||||||
// get a module's value
|
|
||||||
$value = $module;
|
|
||||||
$value = $matrix->get($x, $y);
|
|
||||||
|
|
||||||
// boolean check a module
|
|
||||||
if($matrix->check($x, $y)){ // if($module >> 8 > 0)
|
|
||||||
// do stuff, the module is dark
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
// do other stuff, the module is light
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Have a look [in this folder](https://github.com/chillerlan/php-qrcode/tree/master/examples) for some more usage examples.
|
|
||||||
|
|
||||||
#### Custom module values
|
|
||||||
Previous versions of `QRCode` held only boolean matrix values that only allowed to determine whether a module was dark or not. Now you can distinguish between different parts of the matrix, namely the several required patterns from the QR Code specification, and use them in different ways.
|
|
||||||
|
|
||||||
The dark value is the module (light) value shifted by 8 bits to the left: `$value = $M_TYPE << ($bool ? 8 : 0);`, where `$M_TYPE` is one of the `QRMatrix::M_*` constants.
|
|
||||||
You can check the value for a type explicitly like...
|
|
||||||
```php
|
|
||||||
// for true (dark)
|
|
||||||
$value >> 8 === $M_TYPE;
|
|
||||||
|
|
||||||
//for false (light)
|
|
||||||
$value === $M_TYPE;
|
|
||||||
```
|
|
||||||
...or you can perform a loose check, ignoring the module value
|
|
||||||
```php
|
|
||||||
// for true
|
|
||||||
$value >> 8 > 0;
|
|
||||||
|
|
||||||
// for false
|
|
||||||
$value >> 8 === 0
|
|
||||||
```
|
|
||||||
|
|
||||||
See also `QRMatrix::set()`, `QRMatrix::check()` and [`QRMatrix` constants](#qrmatrix-constants).
|
|
||||||
|
|
||||||
To map the values and properly render the modules for the given `QROutputInterface`, it's necessary to overwrite the default values:
|
|
||||||
```php
|
|
||||||
$options = new QROptions;
|
|
||||||
|
|
||||||
// for HTML, SVG and ImageMagick
|
|
||||||
$options->moduleValues = [
|
|
||||||
// finder
|
|
||||||
1536 => '#A71111', // dark (true)
|
|
||||||
6 => '#FFBFBF', // light (false)
|
|
||||||
// alignment
|
|
||||||
2560 => '#A70364',
|
|
||||||
10 => '#FFC9C9',
|
|
||||||
// timing
|
|
||||||
3072 => '#98005D',
|
|
||||||
12 => '#FFB8E9',
|
|
||||||
// format
|
|
||||||
3584 => '#003804',
|
|
||||||
14 => '#00FB12',
|
|
||||||
// version
|
|
||||||
4096 => '#650098',
|
|
||||||
16 => '#E0B8FF',
|
|
||||||
// data
|
|
||||||
1024 => '#4A6000',
|
|
||||||
4 => '#ECF9BE',
|
|
||||||
// darkmodule
|
|
||||||
512 => '#080063',
|
|
||||||
// separator
|
|
||||||
8 => '#AFBFBF',
|
|
||||||
// quietzone
|
|
||||||
18 => '#FFFFFF',
|
|
||||||
];
|
|
||||||
|
|
||||||
// for the image output types
|
|
||||||
$options->moduleValues = [
|
|
||||||
512 => [0, 0, 0],
|
|
||||||
// ...
|
|
||||||
];
|
|
||||||
|
|
||||||
// for string/text output
|
|
||||||
$options->moduleValues = [
|
|
||||||
512 => '#',
|
|
||||||
// ...
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom `QROutputInterface`
|
|
||||||
Instead of bloating your code you can simply create your own output interface by extending `QROutputAbstract`. Have a look at the [built-in output modules](https://github.com/chillerlan/php-qrcode/tree/master/src/Output).
|
|
||||||
|
|
||||||
```php
|
|
||||||
class MyCustomOutput extends QROutputAbstract{
|
|
||||||
|
|
||||||
// inherited from QROutputAbstract
|
|
||||||
protected $matrix; // QRMatrix
|
|
||||||
protected $moduleCount; // modules QRMatrix::size()
|
|
||||||
protected $options; // MyCustomOptions or QROptions
|
|
||||||
protected $scale; // scale factor from options
|
|
||||||
protected $length; // length of the matrix ($moduleCount * $scale)
|
|
||||||
|
|
||||||
// ...check/set default module values (abstract method, called by the constructor)
|
|
||||||
protected function setModuleValues():void{
|
|
||||||
// $this->moduleValues = ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// QROutputInterface::dump()
|
|
||||||
public function dump(string $file = null):string{
|
|
||||||
$output = '';
|
|
||||||
|
|
||||||
for($row = 0; $row < $this->moduleCount; $row++){
|
|
||||||
for($col = 0; $col < $this->moduleCount; $col++){
|
|
||||||
$output .= (int)$this->matrix->check($col, $row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you need additional settings for your output module, just extend `QROptions`...
|
|
||||||
```
|
|
||||||
class MyCustomOptions extends QROptions{
|
|
||||||
protected $myParam = 'defaultValue';
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
...or use the [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/master/src/SettingsContainerInterface.php), which is the more flexible approach.
|
|
||||||
|
|
||||||
```php
|
|
||||||
trait MyCustomOptionsTrait{
|
|
||||||
protected $myParam = 'defaultValue';
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
set the options:
|
|
||||||
```php
|
|
||||||
$myOptions = [
|
|
||||||
'version' => 5,
|
|
||||||
'eccLevel' => QRCode::ECC_L,
|
|
||||||
'outputType' => QRCode::OUTPUT_CUSTOM,
|
|
||||||
'outputInterface' => MyCustomOutput::class,
|
|
||||||
// your custom settings
|
|
||||||
'myParam' => 'whatever value',
|
|
||||||
];
|
|
||||||
|
|
||||||
// extends QROptions
|
|
||||||
$myCustomOptions = new MyCustomOptions($myOptions);
|
|
||||||
|
|
||||||
// using the SettingsContainerInterface
|
|
||||||
$myCustomOptions = new class($myOptions) extends SettingsContainerAbstract{
|
|
||||||
use QROptionsTrait, MyCustomOptionsTrait;
|
|
||||||
};
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
You can then call `QRCode` with the custom modules...
|
|
||||||
```php
|
|
||||||
(new QRCode($myCustomOptions))->render($data);
|
|
||||||
```
|
|
||||||
...or invoke the `QROutputInterface` manually.
|
|
||||||
```php
|
|
||||||
$qrOutputInterface = new MyCustomOutput($myCustomOptions, (new QRCode($myCustomOptions))->getMatrix($data));
|
|
||||||
|
|
||||||
//dump the output, which is equivalent to QRCode::render()
|
|
||||||
$qrOutputInterface->dump();
|
|
||||||
```
|
|
||||||
|
|
||||||
### API
|
|
||||||
|
|
||||||
#### `QRCode` methods
|
|
||||||
method | return | description
|
|
||||||
------ | ------ | -----------
|
|
||||||
`__construct(QROptions $options = null)` | - | see [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/master/src/SettingsContainerInterface.php)
|
|
||||||
`render(string $data, string $file = null)` | mixed, `QROutputInterface::dump()` | renders a QR Code for the given `$data` and `QROptions`, saves `$file` optional
|
|
||||||
`getMatrix(string $data)` | `QRMatrix` | returns a `QRMatrix` object for the given `$data` and current `QROptions`
|
|
||||||
`initDataInterface(string $data)` | `QRDataInterface` | returns a fresh `QRDataInterface` for the given `$data`
|
|
||||||
`isNumber(string $string)` | bool | checks if a string qualifies for `Number`
|
|
||||||
`isAlphaNum(string $string)` | bool | checks if a string qualifies for `AlphaNum`
|
|
||||||
`isKanji(string $string)` | bool | checks if a string qualifies for `Kanji`
|
|
||||||
|
|
||||||
#### `QRCode` constants
|
|
||||||
name | description
|
|
||||||
---- | -----------
|
|
||||||
`VERSION_AUTO` | `QROptions::$version`
|
|
||||||
`MASK_PATTERN_AUTO` | `QROptions::$maskPattern`
|
|
||||||
`OUTPUT_MARKUP_SVG`, `OUTPUT_MARKUP_HTML` | `QROptions::$outputType` markup
|
|
||||||
`OUTPUT_IMAGE_PNG`, `OUTPUT_IMAGE_JPG`, `OUTPUT_IMAGE_GIF` | `QROptions::$outputType` image
|
|
||||||
`OUTPUT_STRING_JSON`, `OUTPUT_STRING_TEXT` | `QROptions::$outputType` string
|
|
||||||
`OUTPUT_IMAGICK` | `QROptions::$outputType` ImageMagick
|
|
||||||
`OUTPUT_FPDF` | `QROptions::$outputType` PDF, using [FPDF](https://github.com/setasign/fpdf)
|
|
||||||
`OUTPUT_CUSTOM` | `QROptions::$outputType`, requires `QROptions::$outputInterface`
|
|
||||||
`ECC_L`, `ECC_M`, `ECC_Q`, `ECC_H`, | ECC-Level: 7%, 15%, 25%, 30% in `QROptions::$eccLevel`
|
|
||||||
`DATA_NUMBER`, `DATA_ALPHANUM`, `DATA_BYTE`, `DATA_KANJI` | `QRDataInterface::$datamode`
|
|
||||||
|
|
||||||
#### `QROptions` properties
|
|
||||||
property | type | default | allowed | description
|
|
||||||
-------- | ---- | ------- | ------- | -----------
|
|
||||||
`$version` | int | `QRCode::VERSION_AUTO` | 1...40 | the [QR Code version number](http://www.qrcode.com/en/about/version.html)
|
|
||||||
`$versionMin` | int | 1 | 1...40 | Minimum QR version (if `$version = QRCode::VERSION_AUTO`)
|
|
||||||
`$versionMax` | int | 40 | 1...40 | Maximum QR version (if `$version = QRCode::VERSION_AUTO`)
|
|
||||||
`$eccLevel` | int | `QRCode::ECC_L` | `QRCode::ECC_X` | Error correct level, where X = L (7%), M (15%), Q (25%), H (30%)
|
|
||||||
`$maskPattern` | int | `QRCode::MASK_PATTERN_AUTO` | 0...7 | Mask Pattern to use
|
|
||||||
`$addQuietzone` | bool | `true` | - | Add a "quiet zone" (margin) according to the QR code spec
|
|
||||||
`$quietzoneSize` | int | 4 | clamped to 0 ... `$matrixSize / 2` | Size of the quiet zone
|
|
||||||
`$dataMode` | string | `null` | `Number`, `AlphaNum`, `Kanji`, `Byte` | allows overriding the data type detection
|
|
||||||
`$outputType` | string | `QRCode::OUTPUT_IMAGE_PNG` | `QRCode::OUTPUT_*` | built-in output type
|
|
||||||
`$outputInterface` | string | `null` | * | FQCN of the custom `QROutputInterface` if `QROptions::$outputType` is set to `QRCode::OUTPUT_CUSTOM`
|
|
||||||
`$cachefile` | string | `null` | * | optional cache file path
|
|
||||||
`$eol` | string | `PHP_EOL` | * | newline string (HTML, SVG, TEXT)
|
|
||||||
`$scale` | int | 5 | * | size of a QR code pixel (SVG, IMAGE_*), HTML -> via CSS
|
|
||||||
`$cssClass` | string | `null` | * | a common css class
|
|
||||||
`$svgOpacity` | float | 1.0 | 0...1 |
|
|
||||||
`$svgDefs` | string | * | * | anything between [`<defs>`](https://developer.mozilla.org/docs/Web/SVG/Element/defs)
|
|
||||||
`$svgViewBoxSize` | int | `null` | * | a positive integer which defines width/height of the [viewBox attribute](https://css-tricks.com/scale-svg/#article-header-id-3)
|
|
||||||
`$textDark` | string | '🔴' | * | string substitute for dark
|
|
||||||
`$textLight` | string | '⭕' | * | string substitute for light
|
|
||||||
`$markupDark` | string | '#000' | * | markup substitute for dark (CSS value)
|
|
||||||
`$markupLight` | string | '#fff' | * | markup substitute for light (CSS value)
|
|
||||||
`$imageBase64` | bool | `true` | - | whether to return the image data as base64 or raw like from `file_get_contents()`
|
|
||||||
`$imageTransparent` | bool | `true` | - | toggle transparency (no jpeg support)
|
|
||||||
`$imageTransparencyBG` | array | `[255, 255, 255]` | `[R, G, B]` | the RGB values for the transparent color, see [`imagecolortransparent()`](http://php.net/manual/function.imagecolortransparent.php)
|
|
||||||
`$pngCompression` | int | -1 | -1 ... 9 | `imagepng()` compression level, -1 = auto
|
|
||||||
`$jpegQuality` | int | 85 | 0 - 100 | `imagejpeg()` quality
|
|
||||||
`$imagickFormat` | string | 'png' | * | ImageMagick output type, see `Imagick::setType()`
|
|
||||||
`$imagickBG` | string | `null` | * | ImageMagick background color, see `ImagickPixel::__construct()`
|
|
||||||
`$moduleValues` | array | `null` | * | Module values map, see [Custom output modules](#custom-qroutputinterface) and `QROutputInterface::DEFAULT_MODULE_VALUES`
|
|
||||||
|
|
||||||
#### `QRMatrix` methods
|
|
||||||
method | return | description
|
|
||||||
------ | ------ | -----------
|
|
||||||
`__construct(int $version, int $eclevel)` | - | -
|
|
||||||
`matrix()` | array | the internal matrix representation as a 2 dimensional array
|
|
||||||
`version()` | int | the current QR Code version
|
|
||||||
`eccLevel()` | int | current ECC level
|
|
||||||
`maskPattern()` | int | the used mask pattern
|
|
||||||
`size()` | int | the absoulute size of the matrix, including quiet zone (if set). `$version * 4 + 17 + 2 * $quietzone`
|
|
||||||
`get(int $x, int $y)` | int | returns the value of the module
|
|
||||||
`set(int $x, int $y, bool $value, int $M_TYPE)` | `QRMatrix` | sets the `$M_TYPE` value for the module
|
|
||||||
`check(int $x, int $y)` | bool | checks whether a module is true (dark) or false (light)
|
|
||||||
`setLogoSpace(int $width, int $height, int $startX = null, int $startY = null)` | `QRMatrix` | creates a logo space in the matrix
|
|
||||||
|
|
||||||
#### `QRMatrix` constants
|
|
||||||
name | light (false) | dark (true) | description
|
|
||||||
---- | ------------- | ----------- | -----------
|
|
||||||
`M_NULL` | 0 | - | module not set (should never appear. if so, there's an error)
|
|
||||||
`M_DARKMODULE` | - | 512 | once per matrix at `$xy = [8, 4 * $version + 9]`
|
|
||||||
`M_DATA` | 4 | 1024 | the actual encoded data
|
|
||||||
`M_FINDER` | 6 | 1536 | the 7x7 finder patterns
|
|
||||||
`M_SEPARATOR` | 8 | - | separator lines around the finder patterns
|
|
||||||
`M_ALIGNMENT` | 10 | 2560 | the 5x5 alignment patterns
|
|
||||||
`M_TIMING` | 12 | 3072 | the timing pattern lines
|
|
||||||
`M_FORMAT` | 14 | 3584 | format information pattern
|
|
||||||
`M_VERSION` | 16 | 4096 | version information pattern
|
|
||||||
`M_QUIETZONE` | 18 | - | margin around the QR Code
|
|
||||||
`M_LOGO` | 20 | - | space for a logo image (not used yet)
|
|
||||||
`M_TEST` | 255 | 65280 | test value
|
|
||||||
|
|
||||||
|
|
||||||
### Notes
|
|
||||||
The QR encoder, especially the subroutines for mask pattern testing, can cause high CPU load on increased matrix size.
|
|
||||||
You can avoid a part of this load by choosing a fast output module, like `OUTPUT_IMAGE_*` and setting the mask pattern manually (which may result in unreadable QR Codes).
|
|
||||||
Oh hey and don't forget to sanitize any user input!
|
|
||||||
|
|
||||||
### Disclaimer!
|
|
||||||
I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
|
|
||||||
|
|
||||||
#### Trademark Notice
|
|
||||||
|
|
||||||
The word "QR Code" is registered trademark of *DENSO WAVE INCORPORATED*<br>
|
|
||||||
http://www.denso-wave.com/qrcode/faqpatent-e.html
|
|
||||||
|
|
||||||
### Framework Integration
|
### Framework Integration
|
||||||
- Drupal [Google Authenticator Login `ga_login`](https://www.drupal.org/project/ga_login)
|
- Drupal [Google Authenticator Login `ga_login`](https://www.drupal.org/project/ga_login)
|
||||||
|
@ -381,12 +84,20 @@ http://www.denso-wave.com/qrcode/faqpatent-e.html
|
||||||
- WoltLab Suite [two-step-verification](http://pluginstore.woltlab.com/file/3007-two-step-verification/)
|
- WoltLab Suite [two-step-verification](http://pluginstore.woltlab.com/file/3007-two-step-verification/)
|
||||||
- [Cachet](https://github.com/CachetHQ/Cachet)
|
- [Cachet](https://github.com/CachetHQ/Cachet)
|
||||||
- [Appwrite](https://github.com/appwrite/appwrite)
|
- [Appwrite](https://github.com/appwrite/appwrite)
|
||||||
|
- [twill](https://github.com/area17/twill)
|
||||||
- other uses: [dependents](https://github.com/chillerlan/php-qrcode/network/dependents) / [packages](https://github.com/chillerlan/php-qrcode/network/dependents?dependent_type=PACKAGE)
|
- other uses: [dependents](https://github.com/chillerlan/php-qrcode/network/dependents) / [packages](https://github.com/chillerlan/php-qrcode/network/dependents?dependent_type=PACKAGE)
|
||||||
|
|
||||||
|
### Shameless advertising
|
||||||
Hi, please check out my other projects that are way cooler than qrcodes!
|
Hi, please check out my other projects that are way cooler than qrcodes!
|
||||||
|
|
||||||
- [php-oauth-core](https://github.com/chillerlan/php-oauth-core) - an OAuth 1/2 client library along with a bunch of [providers](https://github.com/chillerlan/php-oauth-providers)
|
- [php-oauth-core](https://github.com/chillerlan/php-oauth-core) - an OAuth 1/2 client library along with a bunch of [providers](https://github.com/chillerlan/php-oauth-providers)
|
||||||
- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
|
- [php-httpinterface](https://github.com/chillerlan/php-httpinterface) - a PSR-7/15/17/18 implemetation
|
||||||
- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
|
- [php-database](https://github.com/chillerlan/php-database) - a database client & querybuilder for MySQL, Postgres, SQLite, MSSQL, Firebird
|
||||||
|
|
||||||
|
### Disclaimer!
|
||||||
|
I don't take responsibility for molten CPUs, misled applications, failed log-ins etc.. Use at your own risk!
|
||||||
|
|
||||||
|
#### Trademark Notice
|
||||||
|
|
||||||
|
The word "QR Code" is registered trademark of *DENSO WAVE INCORPORATED*<br>
|
||||||
|
http://www.denso-wave.com/qrcode/faqpatent-e.html
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-qrcode",
|
"name": "chillerlan/php-qrcode",
|
||||||
"description": "A QR code generator. PHP 7.2+",
|
"description": "A QR code generator. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-qrcode",
|
"homepage": "https://github.com/chillerlan/php-qrcode",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
|
@ -24,13 +24,13 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.4 || ^8.0",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"chillerlan/php-settings-container": "^1.2.2"
|
"chillerlan/php-settings-container": "^2.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"phan/phan": "^3.2.2",
|
"phan/phan": "^5.3",
|
||||||
"setasign/fpdf": "^1.8.2"
|
"setasign/fpdf": "^1.8.2"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
|
@ -48,5 +48,14 @@
|
||||||
"chillerlan\\QRCodeTest\\": "tests/",
|
"chillerlan\\QRCodeTest\\": "tests/",
|
||||||
"chillerlan\\QRCodeExamples\\": "examples/"
|
"chillerlan\\QRCodeExamples\\": "examples/"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"phpunit": "@php vendor/bin/phpunit",
|
||||||
|
"phan": "@php vendor/bin/phan"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"lock": false,
|
||||||
|
"sort-packages": true,
|
||||||
|
"platform-check": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ class MyCustomOutput extends QROutputAbstract{
|
||||||
for($col = 0; $col < $this->moduleCount; $col++){
|
for($col = 0; $col < $this->moduleCount; $col++){
|
||||||
$output .= (int)$this->matrix->check($col, $row);
|
$output .= (int)$this->matrix->check($col, $row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$output .= \PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
namespace chillerlan\QRCodeExamples;
|
namespace chillerlan\QRCodeExamples;
|
||||||
|
|
||||||
use chillerlan\QRCode\Output\QRImage;
|
use chillerlan\QRCode\Output\QRImage;
|
||||||
|
|
||||||
use function base64_encode, imagechar, imagecolorallocate, imagecolortransparent, imagecopymerge, imagecreatetruecolor,
|
use function base64_encode, imagechar, imagecolorallocate, imagecolortransparent, imagecopymerge, imagecreatetruecolor,
|
||||||
imagedestroy, imagefilledrectangle, imagefontwidth, in_array, round, str_split, strlen;
|
imagedestroy, imagefilledrectangle, imagefontwidth, in_array, round, str_split, strlen;
|
||||||
|
|
||||||
|
@ -31,29 +32,24 @@ class QRImageWithText extends QRImage{
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null, string $text = null):string{
|
public function dump(string $file = null, string $text = null):string{
|
||||||
$this->image = imagecreatetruecolor($this->length, $this->length);
|
// set returnResource to true to skip further processing for now
|
||||||
$background = imagecolorallocate($this->image, ...$this->options->imageTransparencyBG);
|
$this->options->returnResource = true;
|
||||||
|
|
||||||
if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
|
// there's no need to save the result of dump() into $this->image here
|
||||||
imagecolortransparent($this->image, $background);
|
parent::dump($file);
|
||||||
}
|
|
||||||
|
|
||||||
imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $background);
|
|
||||||
|
|
||||||
foreach($this->matrix->matrix() as $y => $row){
|
|
||||||
foreach($row as $x => $M_TYPE){
|
|
||||||
$this->setPixel($x, $y, $this->moduleValues[$M_TYPE]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// render text output if a string is given
|
// render text output if a string is given
|
||||||
if($text !== null){
|
if($text !== null){
|
||||||
$this->addText($text);
|
$this->addText($text);
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageData = $this->dumpImage($file);
|
$imageData = $this->dumpImage();
|
||||||
|
|
||||||
if((bool)$this->options->imageBase64){
|
if($file !== null){
|
||||||
|
$this->saveToFile($imageData, $file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->options->imageBase64){
|
||||||
$imageData = 'data:image/'.$this->options->outputType.';base64,'.base64_encode($imageData);
|
$imageData = 'data:image/'.$this->options->outputType.';base64,'.base64_encode($imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +76,7 @@ class QRImageWithText extends QRImage{
|
||||||
$background = imagecolorallocate($this->image, ...$textBG);
|
$background = imagecolorallocate($this->image, ...$textBG);
|
||||||
|
|
||||||
// allow transparency
|
// allow transparency
|
||||||
if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
|
if($this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
|
||||||
imagecolortransparent($this->image, $background);
|
imagecolortransparent($this->image, $background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +93,7 @@ class QRImageWithText extends QRImage{
|
||||||
|
|
||||||
// loop through the string and draw the letters
|
// loop through the string and draw the letters
|
||||||
foreach(str_split($text) as $i => $chr){
|
foreach(str_split($text) as $i => $chr){
|
||||||
imagechar($this->image, $textSize, $i * $w + $x, $this->length, $chr, $fontColor);
|
imagechar($this->image, $textSize, (int)($i * $w + $x), $this->length, $chr, $fontColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,16 @@ require_once __DIR__.'/../vendor/autoload.php';
|
||||||
$data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
|
$data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
|
||||||
|
|
||||||
$options = new QROptions([
|
$options = new QROptions([
|
||||||
'version' => 7,
|
'version' => 10,
|
||||||
'outputType' => QRCode::OUTPUT_IMAGE_PNG,
|
'outputType' => QRCode::OUTPUT_IMAGE_PNG,
|
||||||
'eccLevel' => QRCode::ECC_L,
|
'eccLevel' => QRCode::ECC_H,
|
||||||
'scale' => 5,
|
'scale' => 5,
|
||||||
'imageBase64' => false,
|
'imageBase64' => false,
|
||||||
'moduleValues' => [
|
'moduleValues' => [
|
||||||
// finder
|
// finder
|
||||||
1536 => [0, 63, 255], // dark (true)
|
1536 => [0, 63, 255], // dark (true)
|
||||||
6 => [255, 255, 255], // light (false), white is the transparency color and is enabled by default
|
6 => [255, 255, 255], // light (false), white is the transparency color and is enabled by default
|
||||||
|
5632 => [241, 28, 163], // finder dot, dark (true)
|
||||||
// alignment
|
// alignment
|
||||||
2560 => [255, 0, 255],
|
2560 => [255, 0, 255],
|
||||||
10 => [255, 255, 255],
|
10 => [255, 255, 255],
|
||||||
|
@ -33,7 +34,7 @@ $options = new QROptions([
|
||||||
3072 => [255, 0, 0],
|
3072 => [255, 0, 0],
|
||||||
12 => [255, 255, 255],
|
12 => [255, 255, 255],
|
||||||
// format
|
// format
|
||||||
3584 => [67, 191, 84],
|
3584 => [67, 99, 84],
|
||||||
14 => [255, 255, 255],
|
14 => [255, 255, 255],
|
||||||
// version
|
// version
|
||||||
4096 => [62, 174, 190],
|
4096 => [62, 174, 190],
|
||||||
|
@ -47,6 +48,8 @@ $options = new QROptions([
|
||||||
8 => [255, 255, 255],
|
8 => [255, 255, 255],
|
||||||
// quietzone
|
// quietzone
|
||||||
18 => [255, 255, 255],
|
18 => [255, 255, 255],
|
||||||
|
// logo (requires a call to QRMatrix::setLogoSpace())
|
||||||
|
20 => [255, 255, 255],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ $data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
|
||||||
*/
|
*/
|
||||||
class LogoOptions extends QROptions{
|
class LogoOptions extends QROptions{
|
||||||
// size in QR modules, multiply with QROptions::$scale for pixel size
|
// size in QR modules, multiply with QROptions::$scale for pixel size
|
||||||
protected $logoSpaceWidth;
|
protected int $logoSpaceWidth;
|
||||||
protected $logoSpaceHeight;
|
protected int $logoSpaceHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
$options = new LogoOptions;
|
$options = new LogoOptions;
|
||||||
|
|
|
@ -20,6 +20,7 @@ $gzip = true;
|
||||||
$options = new QROptions([
|
$options = new QROptions([
|
||||||
'version' => 7,
|
'version' => 7,
|
||||||
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
|
'outputType' => QRCode::OUTPUT_MARKUP_SVG,
|
||||||
|
'imageBase64' => false,
|
||||||
'eccLevel' => QRCode::ECC_L,
|
'eccLevel' => QRCode::ECC_L,
|
||||||
'svgViewBoxSize' => 530,
|
'svgViewBoxSize' => 530,
|
||||||
'addQuietzone' => true,
|
'addQuietzone' => true,
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<phpdoc>
|
<phpdoc>
|
||||||
<parser>
|
<parser>
|
||||||
<target>public/docs</target>
|
<target>docs</target>
|
||||||
|
<encoding>utf8</encoding>
|
||||||
|
<markers>
|
||||||
|
<item>TODO</item>
|
||||||
|
</markers>
|
||||||
</parser>
|
</parser>
|
||||||
<transformer>
|
<transformer>
|
||||||
<target>public/docs</target>
|
<target>docs</target>
|
||||||
</transformer>
|
</transformer>
|
||||||
<files>
|
<files>
|
||||||
<directory>src</directory>
|
<directory>src</directory>
|
||||||
|
<directory>tests</directory>
|
||||||
</files>
|
</files>
|
||||||
<transformations>
|
<transformations>
|
||||||
<template name="responsive-twig"/>
|
<template name="responsive-twig"/>
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<ruleset name="codemasher/php-qrcode PMD ruleset"
|
|
||||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
|
||||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
|
||||||
<description>codemasher/php-qrcode PMD ruleset</description>
|
|
||||||
<exclude-pattern>*/examples/*</exclude-pattern>
|
|
||||||
<exclude-pattern>*/tests/*</exclude-pattern>
|
|
||||||
<rule ref="rulesets/cleancode.xml">
|
|
||||||
<exclude name="BooleanArgumentFlag"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
|
|
||||||
<priority>1</priority>
|
|
||||||
<properties>
|
|
||||||
<property name="maximum" value="200" />
|
|
||||||
</properties>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/controversial.xml">
|
|
||||||
<exclude name="CamelCaseMethodName"/>
|
|
||||||
<exclude name="CamelCasePropertyName"/>
|
|
||||||
<exclude name="CamelCaseParameterName"/>
|
|
||||||
<exclude name="CamelCaseVariableName"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/design.xml">
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/naming.xml">
|
|
||||||
<exclude name="LongVariable"/>
|
|
||||||
<exclude name="ShortVariable"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/unusedcode.xml">
|
|
||||||
<exclude name="UnusedFormalParameter"/>
|
|
||||||
</rule>
|
|
||||||
</ruleset>
|
|
|
@ -1,23 +1,26 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
|
||||||
bootstrap="vendor/autoload.php"
|
bootstrap="vendor/autoload.php"
|
||||||
cacheResultFile=".build/phpunit.result.cache"
|
cacheResultFile=".build/phpunit.result.cache"
|
||||||
colors="true"
|
colors="true"
|
||||||
verbose="true"
|
verbose="true"
|
||||||
>
|
>
|
||||||
<filter>
|
<coverage processUncoveredFiles="true">
|
||||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
<include>
|
||||||
<directory suffix=".php">./src</directory>
|
<directory suffix=".php">./src</directory>
|
||||||
</whitelist>
|
</include>
|
||||||
</filter>
|
<report>
|
||||||
|
<clover outputFile=".build/coverage/clover.xml"/>
|
||||||
|
<xml outputDirectory=".build/coverage/coverage-xml"/>
|
||||||
|
</report>
|
||||||
|
</coverage>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="php-qrcode test suite">
|
<testsuite name="php-qrcode test suite">
|
||||||
<directory suffix=".php">./tests/</directory>
|
<directory suffix=".php">./tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<logging>
|
<logging>
|
||||||
<log type="coverage-clover" target=".build/coverage/clover.xml"/>
|
<junit outputFile=".build/logs/junit.xml"/>
|
||||||
<log type="coverage-xml" target=".build/coverage/coverage-xml"/>
|
|
||||||
<log type="junit" target=".build/logs/junit.xml"/>
|
|
||||||
</logging>
|
</logging>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -14,22 +14,19 @@ namespace chillerlan\QRCode\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\QRCode;
|
use chillerlan\QRCode\QRCode;
|
||||||
|
|
||||||
use function array_search, ord, sprintf;
|
use function ord, sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
|
* Alphanumeric mode: 0 to 9, A to Z, space, $ % * + - . / :
|
||||||
|
*
|
||||||
|
* ISO/IEC 18004:2000 Section 8.3.3
|
||||||
|
* ISO/IEC 18004:2000 Section 8.4.3
|
||||||
*/
|
*/
|
||||||
class AlphaNum extends QRDataAbstract{
|
final class AlphaNum extends QRDataAbstract{
|
||||||
|
|
||||||
/**
|
protected int $datamode = QRCode::DATA_ALPHANUM;
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $datamode = QRCode::DATA_ALPHANUM;
|
|
||||||
|
|
||||||
/**
|
protected array $lengthBits = [9, 11, 13];
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $lengthBits = [9, 11, 13];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -47,19 +44,17 @@ class AlphaNum extends QRDataAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $chr
|
* get the code for the given character
|
||||||
*
|
*
|
||||||
* @return int
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
|
||||||
*/
|
*/
|
||||||
protected function getCharCode(string $chr):int{
|
protected function getCharCode(string $chr):int{
|
||||||
$i = array_search($chr, $this::ALPHANUM_CHAR_MAP);
|
|
||||||
|
|
||||||
if($i !== false){
|
|
||||||
return $i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if(!isset($this::CHAR_MAP_ALPHANUM[$chr])){
|
||||||
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr)));
|
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, ord($chr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this::CHAR_MAP_ALPHANUM[$chr];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,15 @@ use function ord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Byte mode, ISO-8859-1 or UTF-8
|
* Byte mode, ISO-8859-1 or UTF-8
|
||||||
|
*
|
||||||
|
* ISO/IEC 18004:2000 Section 8.3.4
|
||||||
|
* ISO/IEC 18004:2000 Section 8.4.4
|
||||||
*/
|
*/
|
||||||
class Byte extends QRDataAbstract{
|
final class Byte extends QRDataAbstract{
|
||||||
|
|
||||||
/**
|
protected int $datamode = QRCode::DATA_BYTE;
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $datamode = QRCode::DATA_BYTE;
|
|
||||||
|
|
||||||
/**
|
protected array $lengthBits = [8, 16, 16];
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $lengthBits = [8, 16, 16];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
|
|
@ -18,18 +18,15 @@ use function mb_strlen, ord, sprintf, strlen;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kanji mode: double-byte characters from the Shift JIS character set
|
* Kanji mode: double-byte characters from the Shift JIS character set
|
||||||
|
*
|
||||||
|
* ISO/IEC 18004:2000 Section 8.3.5
|
||||||
|
* ISO/IEC 18004:2000 Section 8.4.5
|
||||||
*/
|
*/
|
||||||
class Kanji extends QRDataAbstract{
|
final class Kanji extends QRDataAbstract{
|
||||||
|
|
||||||
/**
|
protected int $datamode = QRCode::DATA_KANJI;
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $datamode = QRCode::DATA_KANJI;
|
|
||||||
|
|
||||||
/**
|
protected array $lengthBits = [8, 10, 12];
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $lengthBits = [8, 10, 12];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -40,6 +37,8 @@ class Kanji extends QRDataAbstract{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
*
|
||||||
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
|
||||||
*/
|
*/
|
||||||
protected function write(string $data):void{
|
protected function write(string $data):void{
|
||||||
$len = strlen($data);
|
$len = strlen($data);
|
||||||
|
@ -47,17 +46,17 @@ class Kanji extends QRDataAbstract{
|
||||||
for($i = 0; $i + 1 < $len; $i += 2){
|
for($i = 0; $i + 1 < $len; $i += 2){
|
||||||
$c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
|
$c = ((0xff & ord($data[$i])) << 8) | (0xff & ord($data[$i + 1]));
|
||||||
|
|
||||||
if(0x8140 <= $c && $c <= 0x9FFC){
|
if($c >= 0x8140 && $c <= 0x9FFC){
|
||||||
$c -= 0x8140;
|
$c -= 0x8140;
|
||||||
}
|
}
|
||||||
elseif(0xE040 <= $c && $c <= 0xEBBF){
|
elseif($c >= 0xE040 && $c <= 0xEBBF){
|
||||||
$c -= 0xC140;
|
$c -= 0xC140;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
|
throw new QRCodeDataException(sprintf('illegal char at %d [%d]', $i + 1, $c));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->bitBuffer->put((($c >> 8) & 0xff) * 0xC0 + ($c & 0xff), 13);
|
$this->bitBuffer->put(((($c >> 8) & 0xff) * 0xC0) + ($c & 0xff), 13);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,41 +8,51 @@
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2017 Smiley
|
* @copyright 2017 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
*
|
||||||
|
* @noinspection PhpUnused
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace chillerlan\QRCode\Data;
|
namespace chillerlan\QRCode\Data;
|
||||||
|
|
||||||
use function abs, call_user_func_array;
|
use function abs, array_search, call_user_func_array, min;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sole purpose of this class is to receive a QRMatrix object and run the pattern tests on it.
|
* Receives a QRDataInterface object and runs the mask pattern tests on it.
|
||||||
*
|
*
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/data-masking
|
* ISO/IEC 18004:2000 Section 8.8.2 - Evaluation of masking results
|
||||||
|
*
|
||||||
|
* @see http://www.thonky.com/qr-code-tutorial/data-masking
|
||||||
*/
|
*/
|
||||||
class MaskPatternTester{
|
final class MaskPatternTester{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\Data\QRMatrix
|
* The data interface that contains the data matrix to test
|
||||||
*/
|
*/
|
||||||
protected $matrix;
|
protected QRDataInterface $dataInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* Receives the QRDataInterface
|
||||||
*/
|
|
||||||
protected $moduleCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receives the matrix an sets the module count
|
|
||||||
*
|
*
|
||||||
* @see \chillerlan\QRCode\QROptions::$maskPattern
|
* @see \chillerlan\QRCode\QROptions::$maskPattern
|
||||||
* @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
|
* @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
|
||||||
* @see \chillerlan\QRCode\QRCode::getBestMaskPattern()
|
|
||||||
*
|
|
||||||
* @param \chillerlan\QRCode\Data\QRMatrix $matrix
|
|
||||||
*/
|
*/
|
||||||
public function __construct(QRMatrix $matrix){
|
public function __construct(QRDataInterface $dataInterface){
|
||||||
$this->matrix = $matrix;
|
$this->dataInterface = $dataInterface;
|
||||||
$this->moduleCount = $this->matrix->size();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shoves a QRMatrix through the MaskPatternTester to find the lowest penalty mask pattern
|
||||||
|
*
|
||||||
|
* @see \chillerlan\QRCode\Data\MaskPatternTester
|
||||||
|
*/
|
||||||
|
public function getBestMaskPattern():int{
|
||||||
|
$penalties = [];
|
||||||
|
|
||||||
|
for($pattern = 0; $pattern < 8; $pattern++){
|
||||||
|
$penalties[$pattern] = $this->testPattern($pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_search(min($penalties), $penalties, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,15 +60,13 @@ class MaskPatternTester{
|
||||||
*
|
*
|
||||||
* @see \chillerlan\QRCode\QROptions::$maskPattern
|
* @see \chillerlan\QRCode\QROptions::$maskPattern
|
||||||
* @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
|
* @see \chillerlan\QRCode\Data\QRMatrix::$maskPattern
|
||||||
* @see \chillerlan\QRCode\QRCode::getBestMaskPattern()
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function testPattern():int{
|
public function testPattern(int $pattern):int{
|
||||||
|
$matrix = $this->dataInterface->initMatrix($pattern, true);
|
||||||
$penalty = 0;
|
$penalty = 0;
|
||||||
|
|
||||||
for($level = 1; $level <= 4; $level++){
|
for($level = 1; $level <= 4; $level++){
|
||||||
$penalty += call_user_func_array([$this, 'testLevel'.$level], [$this->matrix->matrix(true)]);
|
$penalty += call_user_func_array([$this, 'testLevel'.$level], [$matrix->matrix(true), $matrix->size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)$penalty;
|
return (int)$penalty;
|
||||||
|
@ -66,10 +74,8 @@ class MaskPatternTester{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for each group of five or more same-colored modules in a row (or column)
|
* Checks for each group of five or more same-colored modules in a row (or column)
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function testLevel1(array $m):int{
|
protected function testLevel1(array $m, int $size):int{
|
||||||
$penalty = 0;
|
$penalty = 0;
|
||||||
|
|
||||||
foreach($m as $y => $row){
|
foreach($m as $y => $row){
|
||||||
|
@ -78,13 +84,13 @@ class MaskPatternTester{
|
||||||
|
|
||||||
for($ry = -1; $ry <= 1; $ry++){
|
for($ry = -1; $ry <= 1; $ry++){
|
||||||
|
|
||||||
if($y + $ry < 0 || $this->moduleCount <= $y + $ry){
|
if($y + $ry < 0 || $size <= $y + $ry){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for($rx = -1; $rx <= 1; $rx++){
|
for($rx = -1; $rx <= 1; $rx++){
|
||||||
|
|
||||||
if(($ry === 0 && $rx === 0) || (($x + $rx) < 0 || $this->moduleCount <= ($x + $rx))){
|
if(($ry === 0 && $rx === 0) || (($x + $rx) < 0 || $size <= ($x + $rx))){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,21 +113,19 @@ class MaskPatternTester{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for each 2x2 area of same-colored modules in the matrix
|
* Checks for each 2x2 area of same-colored modules in the matrix
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function testLevel2(array $m):int{
|
protected function testLevel2(array $m, int $size):int{
|
||||||
$penalty = 0;
|
$penalty = 0;
|
||||||
|
|
||||||
foreach($m as $y => $row){
|
foreach($m as $y => $row){
|
||||||
|
|
||||||
if($y > ($this->moduleCount - 2)){
|
if($y > $size - 2){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($row as $x => $val){
|
foreach($row as $x => $val){
|
||||||
|
|
||||||
if($x > ($this->moduleCount - 2)){
|
if($x > $size - 2){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,17 +144,15 @@ class MaskPatternTester{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if there are patterns that look similar to the finder patterns (1:1:3:1:1 ratio)
|
* Checks if there are patterns that look similar to the finder patterns (1:1:3:1:1 ratio)
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function testLevel3(array $m):int{
|
protected function testLevel3(array $m, int $size):int{
|
||||||
$penalties = 0;
|
$penalties = 0;
|
||||||
|
|
||||||
foreach($m as $y => $row){
|
foreach($m as $y => $row){
|
||||||
foreach($row as $x => $val){
|
foreach($row as $x => $val){
|
||||||
|
|
||||||
if(
|
if(
|
||||||
($x + 6) < $this->moduleCount
|
$x + 6 < $size
|
||||||
&& $val
|
&& $val
|
||||||
&& !$m[$y][$x + 1]
|
&& !$m[$y][$x + 1]
|
||||||
&& $m[$y][$x + 2]
|
&& $m[$y][$x + 2]
|
||||||
|
@ -163,7 +165,7 @@ class MaskPatternTester{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(
|
if(
|
||||||
($y + 6) < $this->moduleCount
|
$y + 6 < $size
|
||||||
&& $val
|
&& $val
|
||||||
&& !$m[$y + 1][$x]
|
&& !$m[$y + 1][$x]
|
||||||
&& $m[$y + 2][$x]
|
&& $m[$y + 2][$x]
|
||||||
|
@ -183,10 +185,8 @@ class MaskPatternTester{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference
|
* Checks if more than half of the modules are dark or light, with a larger penalty for a larger difference
|
||||||
*
|
|
||||||
* @return float
|
|
||||||
*/
|
*/
|
||||||
protected function testLevel4(array $m):float{
|
protected function testLevel4(array $m, int $size):float{
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
foreach($m as $y => $row){
|
foreach($m as $y => $row){
|
||||||
|
@ -197,7 +197,7 @@ class MaskPatternTester{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (abs(100 * $count / $this->moduleCount / $this->moduleCount - 50) / 5) * 10;
|
return (abs(100 * $count / $size / $size - 50) / 5) * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* @filesource Number.php
|
* @filesource Number.php
|
||||||
* @created 26.11.2015
|
* @created 26.11.2015
|
||||||
* @package QRCode
|
* @package chillerlan\QRCode\Data
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2015 Smiley
|
* @copyright 2015 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -14,22 +14,19 @@ namespace chillerlan\QRCode\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\QRCode;
|
use chillerlan\QRCode\QRCode;
|
||||||
|
|
||||||
use function ord, sprintf, substr;
|
use function ord, sprintf, str_split, substr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Numeric mode: decimal digits 0 through 9
|
* Numeric mode: decimal digits 0 to 9
|
||||||
|
*
|
||||||
|
* ISO/IEC 18004:2000 Section 8.3.2
|
||||||
|
* ISO/IEC 18004:2000 Section 8.4.2
|
||||||
*/
|
*/
|
||||||
class Number extends QRDataAbstract{
|
final class Number extends QRDataAbstract{
|
||||||
|
|
||||||
/**
|
protected int $datamode = QRCode::DATA_NUMBER;
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $datamode = QRCode::DATA_NUMBER;
|
|
||||||
|
|
||||||
/**
|
protected array $lengthBits = [10, 12, 14];
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
protected $lengthBits = [10, 12, 14];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -56,20 +53,18 @@ class Number extends QRDataAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $string
|
* get the code for the given numeric string
|
||||||
*
|
*
|
||||||
* @return int
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException on an illegal character occurence
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
|
||||||
*/
|
*/
|
||||||
protected function parseInt(string $string):int{
|
protected function parseInt(string $string):int{
|
||||||
$num = 0;
|
$num = 0;
|
||||||
|
|
||||||
$len = strlen($string);
|
foreach(str_split($string) as $chr){
|
||||||
for($i = 0; $i < $len; $i++){
|
$c = ord($chr);
|
||||||
$c = ord($string[$i]);
|
|
||||||
|
|
||||||
if(!in_array($string[$i], $this::NUMBER_CHAR_MAP, true)){
|
if(!isset($this::CHAR_MAP_NUMBER[$chr])){
|
||||||
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $string[$i], $c));
|
throw new QRCodeDataException(sprintf('illegal char: "%s" [%d]', $chr, $c));
|
||||||
}
|
}
|
||||||
|
|
||||||
$c = $c - 48; // ord('0')
|
$c = $c - 48; // ord('0')
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCode\Data;
|
namespace chillerlan\QRCode\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QRCode, QRCodeException};
|
use chillerlan\QRCode\QRCode;
|
||||||
use chillerlan\QRCode\Helpers\{BitBuffer, Polynomial};
|
use chillerlan\QRCode\Helpers\{BitBuffer, Polynomial};
|
||||||
use chillerlan\Settings\SettingsContainerInterface;
|
use chillerlan\Settings\SettingsContainerInterface;
|
||||||
|
|
||||||
|
@ -25,68 +25,50 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the string byte count
|
* the string byte count
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $strlen;
|
protected ?int $strlen = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the current data mode: Num, Alphanum, Kanji, Byte
|
* the current data mode: Num, Alphanum, Kanji, Byte
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $datamode;
|
protected int $datamode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mode length bits for the version breakpoints 1-9, 10-26 and 27-40
|
* mode length bits for the version breakpoints 1-9, 10-26 and 27-40
|
||||||
*
|
*
|
||||||
* @var array
|
* ISO/IEC 18004:2000 Table 3 - Number of bits in Character Count Indicator
|
||||||
*/
|
*/
|
||||||
protected $lengthBits = [0, 0, 0];
|
protected array $lengthBits = [0, 0, 0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* current QR Code version
|
* current QR Code version
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $version;
|
protected int $version;
|
||||||
|
|
||||||
/**
|
|
||||||
* the raw data that's being passed to QRMatrix::mapData()
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $matrixdata;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECC temp data
|
* ECC temp data
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
protected $ecdata;
|
protected array $ecdata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECC temp data
|
* ECC temp data
|
||||||
|
*/
|
||||||
|
protected array $dcdata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the options instance
|
||||||
*
|
*
|
||||||
* @var array
|
* @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
|
||||||
*/
|
*/
|
||||||
protected $dcdata;
|
protected SettingsContainerInterface $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\QROptions
|
* a BitBuffer instance
|
||||||
*/
|
*/
|
||||||
protected $options;
|
protected BitBuffer $bitBuffer;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \chillerlan\QRCode\Helpers\BitBuffer
|
|
||||||
*/
|
|
||||||
protected $bitBuffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QRDataInterface constructor.
|
* QRDataInterface constructor.
|
||||||
*
|
|
||||||
* @param \chillerlan\Settings\SettingsContainerInterface $options
|
|
||||||
* @param string|null $data
|
|
||||||
*/
|
*/
|
||||||
public function __construct(SettingsContainerInterface $options, string $data = null){
|
public function __construct(SettingsContainerInterface $options, string $data = null){
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
@ -110,10 +92,7 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
? $this->getMinimumVersion()
|
? $this->getMinimumVersion()
|
||||||
: $this->options->version;
|
: $this->options->version;
|
||||||
|
|
||||||
$this->matrixdata = $this
|
$this->writeBitBuffer($data);
|
||||||
->writeBitBuffer($data)
|
|
||||||
->maskECC()
|
|
||||||
;
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -123,21 +102,14 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
*/
|
*/
|
||||||
public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{
|
public function initMatrix(int $maskPattern, bool $test = null):QRMatrix{
|
||||||
return (new QRMatrix($this->version, $this->options->eccLevel))
|
return (new QRMatrix($this->version, $this->options->eccLevel))
|
||||||
->setFinderPattern()
|
->init($maskPattern, $test)
|
||||||
->setSeparators()
|
->mapData($this->maskECC(), $maskPattern)
|
||||||
->setAlignmentPattern()
|
|
||||||
->setTimingPattern()
|
|
||||||
->setVersionNumber($test)
|
|
||||||
->setFormatInfo($maskPattern, $test)
|
|
||||||
->setDarkModule()
|
|
||||||
->mapData($this->matrixdata, $maskPattern)
|
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the length bits for the version breakpoints 1-9, 10-26 and 27-40
|
* returns the length bits for the version breakpoints 1-9, 10-26 and 27-40
|
||||||
*
|
*
|
||||||
* @return int
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*/
|
*/
|
||||||
|
@ -154,10 +126,6 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the byte count of the $data string
|
* returns the byte count of the $data string
|
||||||
*
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
protected function getLength(string $data):int{
|
protected function getLength(string $data):int{
|
||||||
return strlen($data);
|
return strlen($data);
|
||||||
|
@ -166,15 +134,17 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
/**
|
/**
|
||||||
* returns the minimum version number for the given string
|
* returns the minimum version number for the given string
|
||||||
*
|
*
|
||||||
* @return int
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
protected function getMinimumVersion():int{
|
protected function getMinimumVersion():int{
|
||||||
$maxlength = 0;
|
$maxlength = 0;
|
||||||
|
|
||||||
// guess the version number within the given range
|
// guess the version number within the given range
|
||||||
|
$dataMode = QRCode::DATA_MODES[$this->datamode];
|
||||||
|
$eccMode = QRCode::ECC_MODES[$this->options->eccLevel];
|
||||||
|
|
||||||
foreach(range($this->options->versionMin, $this->options->versionMax) as $version){
|
foreach(range($this->options->versionMin, $this->options->versionMax) as $version){
|
||||||
$maxlength = $this::MAX_LENGTH[$version][QRCode::DATA_MODES[$this->datamode]][QRCode::ECC_MODES[$this->options->eccLevel]];
|
$maxlength = $this::MAX_LENGTH[$version][$dataMode][$eccMode];
|
||||||
|
|
||||||
if($this->strlen <= $maxlength){
|
if($this->strlen <= $maxlength){
|
||||||
return $version;
|
return $version;
|
||||||
|
@ -188,81 +158,72 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
* writes the actual data string to the BitBuffer
|
* writes the actual data string to the BitBuffer
|
||||||
*
|
*
|
||||||
* @see \chillerlan\QRCode\Data\QRDataAbstract::writeBitBuffer()
|
* @see \chillerlan\QRCode\Data\QRDataAbstract::writeBitBuffer()
|
||||||
*
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
abstract protected function write(string $data):void;
|
abstract protected function write(string $data):void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a BitBuffer and writes the string data to it
|
* creates a BitBuffer and writes the string data to it
|
||||||
*
|
*
|
||||||
* @param string $data
|
* @throws \chillerlan\QRCode\QRCodeException on data overflow
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRDataAbstract
|
|
||||||
* @throws \chillerlan\QRCode\QRCodeException
|
|
||||||
*/
|
*/
|
||||||
protected function writeBitBuffer(string $data):QRDataInterface{
|
protected function writeBitBuffer(string $data):void{
|
||||||
$this->bitBuffer = new BitBuffer;
|
$this->bitBuffer = new BitBuffer;
|
||||||
|
|
||||||
$MAX_BITS = $this::MAX_BITS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
|
$MAX_BITS = $this::MAX_BITS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
|
||||||
|
|
||||||
$this->bitBuffer
|
$this->bitBuffer
|
||||||
->clear()
|
|
||||||
->put($this->datamode, 4)
|
->put($this->datamode, 4)
|
||||||
->put($this->strlen, $this->getLengthBits())
|
->put($this->strlen, $this->getLengthBits())
|
||||||
;
|
;
|
||||||
|
|
||||||
$this->write($data);
|
$this->write($data);
|
||||||
|
|
||||||
// there was an error writing the BitBuffer data, which is... unlikely.
|
// overflow, likely caused due to invalid version setting
|
||||||
if($this->bitBuffer->length > $MAX_BITS){
|
if($this->bitBuffer->getLength() > $MAX_BITS){
|
||||||
throw new QRCodeException(sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->length, $MAX_BITS)); // @codeCoverageIgnore
|
throw new QRCodeDataException(sprintf('code length overflow. (%d > %d bit)', $this->bitBuffer->getLength(), $MAX_BITS));
|
||||||
}
|
}
|
||||||
|
|
||||||
// end code.
|
// add terminator (ISO/IEC 18004:2000 Table 2)
|
||||||
if($this->bitBuffer->length + 4 <= $MAX_BITS){
|
if($this->bitBuffer->getLength() + 4 <= $MAX_BITS){
|
||||||
$this->bitBuffer->put(0, 4);
|
$this->bitBuffer->put(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// padding
|
// padding
|
||||||
while($this->bitBuffer->length % 8 !== 0){
|
while($this->bitBuffer->getLength() % 8 !== 0){
|
||||||
$this->bitBuffer->putBit(false);
|
$this->bitBuffer->putBit(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// padding
|
// padding
|
||||||
while(true){
|
while(true){
|
||||||
|
|
||||||
if($this->bitBuffer->length >= $MAX_BITS){
|
if($this->bitBuffer->getLength() >= $MAX_BITS){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->bitBuffer->put(0xEC, 8);
|
$this->bitBuffer->put(0xEC, 8);
|
||||||
|
|
||||||
if($this->bitBuffer->length >= $MAX_BITS){
|
if($this->bitBuffer->getLength() >= $MAX_BITS){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->bitBuffer->put(0x11, 8);
|
$this->bitBuffer->put(0x11, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ECC masking
|
* ECC masking
|
||||||
*
|
*
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/error-correction-coding
|
* ISO/IEC 18004:2000 Section 8.5 ff
|
||||||
*
|
*
|
||||||
* @return array
|
* @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
|
||||||
*/
|
*/
|
||||||
protected function maskECC():array{
|
protected function maskECC():array{
|
||||||
[$l1, $l2, $b1, $b2] = $this::RSBLOCKS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
|
[$l1, $l2, $b1, $b2] = $this::RSBLOCKS[$this->version][QRCode::ECC_MODES[$this->options->eccLevel]];
|
||||||
|
|
||||||
$rsBlocks = array_fill(0, $l1, [$b1, $b2]);
|
$rsBlocks = array_fill(0, $l1, [$b1, $b2]);
|
||||||
$rsCount = $l1 + $l2;
|
$rsCount = $l1 + $l2;
|
||||||
$this->ecdata = array_fill(0, $rsCount, null);
|
$this->ecdata = array_fill(0, $rsCount, []);
|
||||||
$this->dcdata = $this->ecdata;
|
$this->dcdata = $this->ecdata;
|
||||||
|
|
||||||
if($l2 > 0){
|
if($l2 > 0){
|
||||||
|
@ -274,6 +235,8 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
$maxEcCount = 0;
|
$maxEcCount = 0;
|
||||||
$offset = 0;
|
$offset = 0;
|
||||||
|
|
||||||
|
$bitBuffer = $this->bitBuffer->getBuffer();
|
||||||
|
|
||||||
foreach($rsBlocks as $key => $block){
|
foreach($rsBlocks as $key => $block){
|
||||||
[$rsBlockTotal, $dcCount] = $block;
|
[$rsBlockTotal, $dcCount] = $block;
|
||||||
|
|
||||||
|
@ -283,12 +246,12 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
$this->dcdata[$key] = array_fill(0, $dcCount, null);
|
$this->dcdata[$key] = array_fill(0, $dcCount, null);
|
||||||
|
|
||||||
foreach($this->dcdata[$key] as $a => $_z){
|
foreach($this->dcdata[$key] as $a => $_z){
|
||||||
$this->dcdata[$key][$a] = 0xff & $this->bitBuffer->buffer[$a + $offset];
|
$this->dcdata[$key][$a] = 0xff & $bitBuffer[$a + $offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
[$num, $add] = $this->poly($key, $ecCount);
|
[$num, $add] = $this->poly($key, $ecCount);
|
||||||
|
|
||||||
foreach($this->ecdata[$key] as $c => $_z){
|
foreach($this->ecdata[$key] as $c => $_){
|
||||||
$modIndex = $c + $add;
|
$modIndex = $c + $add;
|
||||||
$this->ecdata[$key][$c] = $modIndex >= 0 ? $num[$modIndex] : 0;
|
$this->ecdata[$key][$c] = $modIndex >= 0 ? $num[$modIndex] : 0;
|
||||||
}
|
}
|
||||||
|
@ -300,7 +263,7 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
$data = array_fill(0, $totalCodeCount, null);
|
$data = array_fill(0, $totalCodeCount, null);
|
||||||
$index = 0;
|
$index = 0;
|
||||||
|
|
||||||
$mask = function($arr, $count) use (&$data, &$index, $rsCount){
|
$mask = function(array $arr, int $count) use (&$data, &$index, $rsCount):void{
|
||||||
for($x = 0; $x < $count; $x++){
|
for($x = 0; $x < $count; $x++){
|
||||||
for($y = 0; $y < $rsCount; $y++){
|
for($y = 0; $y < $rsCount; $y++){
|
||||||
if($x < count($arr[$y])){
|
if($x < count($arr[$y])){
|
||||||
|
@ -318,10 +281,7 @@ abstract class QRDataAbstract implements QRDataInterface{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $key
|
* helper method for the polynomial operations
|
||||||
* @param int $count
|
|
||||||
*
|
|
||||||
* @return int[]
|
|
||||||
*/
|
*/
|
||||||
protected function poly(int $key, int $count):array{
|
protected function poly(int $key, int $count):array{
|
||||||
$rsPoly = new Polynomial;
|
$rsPoly = new Polynomial;
|
||||||
|
|
|
@ -13,23 +13,38 @@
|
||||||
namespace chillerlan\QRCode\Data;
|
namespace chillerlan\QRCode\Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Specifies the methods reqired for the data modules (Number, Alphanum, Byte and Kanji)
|
||||||
|
* and holds version information in several constants
|
||||||
*/
|
*/
|
||||||
interface QRDataInterface{
|
interface QRDataInterface{
|
||||||
|
|
||||||
const NUMBER_CHAR_MAP = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
/**
|
||||||
|
* @var int[]
|
||||||
const ALPHANUM_CHAR_MAP = [
|
*/
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
const CHAR_MAP_NUMBER = [
|
||||||
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9,
|
||||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
|
||||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
|
||||||
'W', 'X', 'Y', 'Z', ' ', '$', '%', '*',
|
|
||||||
'+', '-', '.', '/', ':',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.qrcode.com/en/about/version.html
|
* ISO/IEC 18004:2000 Table 5
|
||||||
|
*
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
const CHAR_MAP_ALPHANUM = [
|
||||||
|
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7,
|
||||||
|
'8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15,
|
||||||
|
'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23,
|
||||||
|
'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31,
|
||||||
|
'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39,
|
||||||
|
'+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
|
||||||
|
*
|
||||||
|
* @see http://www.qrcode.com/en/about/version.html
|
||||||
|
*
|
||||||
|
* @var int [][][]
|
||||||
*/
|
*/
|
||||||
const MAX_LENGTH =[
|
const MAX_LENGTH =[
|
||||||
// v => [NUMERIC => [L, M, Q, H ], ALPHANUM => [L, M, Q, H], BINARY => [L, M, Q, H ], KANJI => [L, M, Q, H ]] // modules
|
// v => [NUMERIC => [L, M, Q, H ], ALPHANUM => [L, M, Q, H], BINARY => [L, M, Q, H ], KANJI => [L, M, Q, H ]] // modules
|
||||||
|
@ -75,6 +90,11 @@ interface QRDataInterface{
|
||||||
40 => [[7089, 5596, 3993, 3057], [4296, 3391, 2420, 1852], [2953, 2331, 1663, 1273], [1817, 1435, 1024, 784]], // 177
|
40 => [[7089, 5596, 3993, 3057], [4296, 3391, 2420, 1852], [2953, 2331, 1663, 1273], [1817, 1435, 1024, 784]], // 177
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO/IEC 18004:2000 Tables 7-11 - Number of symbol characters and input data capacity for versions 1 to 40
|
||||||
|
*
|
||||||
|
* @var int [][]
|
||||||
|
*/
|
||||||
const MAX_BITS = [
|
const MAX_BITS = [
|
||||||
// version => [L, M, Q, H ]
|
// version => [L, M, Q, H ]
|
||||||
1 => [ 152, 128, 104, 72],
|
1 => [ 152, 128, 104, 72],
|
||||||
|
@ -120,7 +140,9 @@ interface QRDataInterface{
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/error-correction-table
|
* @see http://www.thonky.com/qr-code-tutorial/error-correction-table
|
||||||
|
*
|
||||||
|
* @var int [][][]
|
||||||
*/
|
*/
|
||||||
const RSBLOCKS = [
|
const RSBLOCKS = [
|
||||||
1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]],
|
1 => [[ 1, 0, 26, 19], [ 1, 0, 26, 16], [ 1, 0, 26, 13], [ 1, 0, 26, 9]],
|
||||||
|
@ -167,20 +189,11 @@ interface QRDataInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the data string (internally called by the constructor)
|
* Sets the data string (internally called by the constructor)
|
||||||
*
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRDataInterface
|
|
||||||
*/
|
*/
|
||||||
public function setData(string $data):QRDataInterface;
|
public function setData(string $data):QRDataInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a fresh matrix object with the data written for the given $maskPattern
|
* returns a fresh matrix object with the data written for the given $maskPattern
|
||||||
*
|
|
||||||
* @param int $maskPattern
|
|
||||||
* @param bool|null $test
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
*/
|
||||||
public function initMatrix(int $maskPattern, bool $test = null):QRMatrix;
|
public function initMatrix(int $maskPattern, bool $test = null):QRMatrix;
|
||||||
|
|
||||||
|
|
|
@ -18,29 +18,46 @@ use Closure;
|
||||||
use function array_fill, array_key_exists, array_push, array_unshift, count, floor, in_array, max, min, range;
|
use function array_fill, array_key_exists, array_push, array_unshift, count, floor, in_array, max, min, range;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/format-version-information
|
* Holds a numerical representation of the final QR Code;
|
||||||
|
* maps the ECC coded binary data and applies the mask pattern
|
||||||
|
*
|
||||||
|
* @see http://www.thonky.com/qr-code-tutorial/format-version-information
|
||||||
*/
|
*/
|
||||||
class QRMatrix{
|
final class QRMatrix{
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
public const M_NULL = 0x00;
|
public const M_NULL = 0x00;
|
||||||
|
/** @var int */
|
||||||
public const M_DARKMODULE = 0x02;
|
public const M_DARKMODULE = 0x02;
|
||||||
|
/** @var int */
|
||||||
public const M_DATA = 0x04;
|
public const M_DATA = 0x04;
|
||||||
|
/** @var int */
|
||||||
public const M_FINDER = 0x06;
|
public const M_FINDER = 0x06;
|
||||||
|
/** @var int */
|
||||||
public const M_SEPARATOR = 0x08;
|
public const M_SEPARATOR = 0x08;
|
||||||
|
/** @var int */
|
||||||
public const M_ALIGNMENT = 0x0a;
|
public const M_ALIGNMENT = 0x0a;
|
||||||
|
/** @var int */
|
||||||
public const M_TIMING = 0x0c;
|
public const M_TIMING = 0x0c;
|
||||||
|
/** @var int */
|
||||||
public const M_FORMAT = 0x0e;
|
public const M_FORMAT = 0x0e;
|
||||||
|
/** @var int */
|
||||||
public const M_VERSION = 0x10;
|
public const M_VERSION = 0x10;
|
||||||
|
/** @var int */
|
||||||
public const M_QUIETZONE = 0x12;
|
public const M_QUIETZONE = 0x12;
|
||||||
|
/** @var int */
|
||||||
public const M_LOGO = 0x14;
|
public const M_LOGO = 0x14;
|
||||||
|
/** @var int */
|
||||||
public const M_FINDER_DOT = 0x16;
|
public const M_FINDER_DOT = 0x16;
|
||||||
|
/** @var int */
|
||||||
public const M_TEST = 0xff;
|
public const M_TEST = 0xff;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/alignment-pattern-locations
|
* ISO/IEC 18004:2000 Annex E, Table E.1 - Row/column coordinates of center module of Alignment Patterns
|
||||||
*
|
*
|
||||||
* version -> pattern
|
* version -> pattern
|
||||||
|
*
|
||||||
|
* @var int[][]
|
||||||
*/
|
*/
|
||||||
protected const alignmentPattern = [
|
protected const alignmentPattern = [
|
||||||
1 => [],
|
1 => [],
|
||||||
|
@ -86,9 +103,11 @@ class QRMatrix{
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/format-version-tables
|
* ISO/IEC 18004:2000 Annex D, Table D.1 - Version information bit stream for each version
|
||||||
*
|
*
|
||||||
* no version pattern for QR Codes < 7
|
* no version pattern for QR Codes < 7
|
||||||
|
*
|
||||||
|
* @var int[]
|
||||||
*/
|
*/
|
||||||
protected const versionPattern = [
|
protected const versionPattern = [
|
||||||
7 => 0b000111110010010100,
|
7 => 0b000111110010010100,
|
||||||
|
@ -127,7 +146,13 @@ class QRMatrix{
|
||||||
40 => 0b101000110001101001,
|
40 => 0b101000110001101001,
|
||||||
];
|
];
|
||||||
|
|
||||||
// ECC level -> mask pattern
|
/**
|
||||||
|
* ISO/IEC 18004:2000 Section 8.9 - Format Information
|
||||||
|
*
|
||||||
|
* ECC level -> mask pattern
|
||||||
|
*
|
||||||
|
* @var int[][]
|
||||||
|
*/
|
||||||
protected const formatPattern = [
|
protected const formatPattern = [
|
||||||
[ // L
|
[ // L
|
||||||
0b111011111000100,
|
0b111011111000100,
|
||||||
|
@ -172,36 +197,35 @@ class QRMatrix{
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the current QR Code version number
|
||||||
*/
|
*/
|
||||||
protected $version;
|
protected int $version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the current ECC level
|
||||||
*/
|
*/
|
||||||
protected $eclevel;
|
protected int $eclevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the used mask pattern, set via QRMatrix::mapData()
|
||||||
*/
|
*/
|
||||||
protected $maskPattern = QRCode::MASK_PATTERN_AUTO;
|
protected int $maskPattern = QRCode::MASK_PATTERN_AUTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the size (side length) of the matrix
|
||||||
*/
|
*/
|
||||||
protected $moduleCount;
|
protected int $moduleCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[]
|
* the actual matrix data array
|
||||||
|
*
|
||||||
|
* @var int[][]
|
||||||
*/
|
*/
|
||||||
protected $matrix;
|
protected array $matrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QRMatrix constructor.
|
* QRMatrix constructor.
|
||||||
*
|
*
|
||||||
* @param int $version
|
|
||||||
* @param int $eclevel
|
|
||||||
*
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
public function __construct(int $version, int $eclevel){
|
public function __construct(int $version, int $eclevel){
|
||||||
|
@ -220,6 +244,21 @@ class QRMatrix{
|
||||||
$this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL));
|
$this->matrix = array_fill(0, $this->moduleCount, array_fill(0, $this->moduleCount, $this::M_NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shortcut to initialize the matrix
|
||||||
|
*/
|
||||||
|
public function init(int $maskPattern, bool $test = null):QRMatrix{
|
||||||
|
return $this
|
||||||
|
->setFinderPattern()
|
||||||
|
->setSeparators()
|
||||||
|
->setAlignmentPattern()
|
||||||
|
->setTimingPattern()
|
||||||
|
->setVersionNumber($test)
|
||||||
|
->setFormatInfo($maskPattern, $test)
|
||||||
|
->setDarkModule()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data matrix, returns a pure boolean representation if $boolean is set to true
|
* Returns the data matrix, returns a pure boolean representation if $boolean is set to true
|
||||||
*
|
*
|
||||||
|
@ -245,21 +284,21 @@ class QRMatrix{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* Returns the current version number
|
||||||
*/
|
*/
|
||||||
public function version():int{
|
public function version():int{
|
||||||
return $this->version;
|
return $this->version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* Returns the current ECC level
|
||||||
*/
|
*/
|
||||||
public function eccLevel():int{
|
public function eccLevel():int{
|
||||||
return $this->eclevel;
|
return $this->eclevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int
|
* Returns the current mask pattern
|
||||||
*/
|
*/
|
||||||
public function maskPattern():int{
|
public function maskPattern():int{
|
||||||
return $this->maskPattern;
|
return $this->maskPattern;
|
||||||
|
@ -269,8 +308,6 @@ class QRMatrix{
|
||||||
* Returns the absoulute size of the matrix, including quiet zone (after setting it).
|
* Returns the absoulute size of the matrix, including quiet zone (after setting it).
|
||||||
*
|
*
|
||||||
* size = version * 4 + 17 [ + 2 * quietzone size]
|
* size = version * 4 + 17 [ + 2 * quietzone size]
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function size():int{
|
public function size():int{
|
||||||
return $this->moduleCount;
|
return $this->moduleCount;
|
||||||
|
@ -278,11 +315,6 @@ class QRMatrix{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the module at position [$x, $y]
|
* Returns the value of the module at position [$x, $y]
|
||||||
*
|
|
||||||
* @param int $x
|
|
||||||
* @param int $y
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function get(int $x, int $y):int{
|
public function get(int $x, int $y):int{
|
||||||
return $this->matrix[$y][$x];
|
return $this->matrix[$y][$x];
|
||||||
|
@ -293,13 +325,6 @@ class QRMatrix{
|
||||||
*
|
*
|
||||||
* true => $M_TYPE << 8
|
* true => $M_TYPE << 8
|
||||||
* false => $M_TYPE
|
* false => $M_TYPE
|
||||||
*
|
|
||||||
* @param int $x
|
|
||||||
* @param int $y
|
|
||||||
* @param int $M_TYPE
|
|
||||||
* @param bool $value
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
*/
|
||||||
public function set(int $x, int $y, bool $value, int $M_TYPE):QRMatrix{
|
public function set(int $x, int $y, bool $value, int $M_TYPE):QRMatrix{
|
||||||
$this->matrix[$y][$x] = $M_TYPE << ($value ? 8 : 0);
|
$this->matrix[$y][$x] = $M_TYPE << ($value ? 8 : 0);
|
||||||
|
@ -315,21 +340,14 @@ class QRMatrix{
|
||||||
*
|
*
|
||||||
* false => $value === $M_TYPE
|
* false => $value === $M_TYPE
|
||||||
* $value >> 8 === 0
|
* $value >> 8 === 0
|
||||||
*
|
|
||||||
* @param int $x
|
|
||||||
* @param int $y
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function check(int $x, int $y):bool{
|
public function check(int $x, int $y):bool{
|
||||||
return $this->matrix[$y][$x] >> 8 > 0;
|
return ($this->matrix[$y][$x] >> 8) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
|
* Sets the "dark module", that is always on the same position 1x1px away from the bottom left finder
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
*/
|
||||||
public function setDarkModule():QRMatrix{
|
public function setDarkModule():QRMatrix{
|
||||||
$this->set(8, 4 * $this->version + 9, true, $this::M_DARKMODULE);
|
$this->set(8, 4 * $this->version + 9, true, $this::M_DARKMODULE);
|
||||||
|
@ -340,7 +358,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the 7x7 finder patterns in the corners top left/right and bottom left
|
* Draws the 7x7 finder patterns in the corners top left/right and bottom left
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
* ISO/IEC 18004:2000 Section 7.3.2
|
||||||
*/
|
*/
|
||||||
public function setFinderPattern():QRMatrix{
|
public function setFinderPattern():QRMatrix{
|
||||||
|
|
||||||
|
@ -375,7 +393,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the separator lines around the finder patterns
|
* Draws the separator lines around the finder patterns
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
* ISO/IEC 18004:2000 Section 7.3.3
|
||||||
*/
|
*/
|
||||||
public function setSeparators():QRMatrix{
|
public function setSeparators():QRMatrix{
|
||||||
|
|
||||||
|
@ -405,7 +423,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the 5x5 alignment patterns
|
* Draws the 5x5 alignment patterns
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
* ISO/IEC 18004:2000 Section 7.3.5
|
||||||
*/
|
*/
|
||||||
public function setAlignmentPattern():QRMatrix{
|
public function setAlignmentPattern():QRMatrix{
|
||||||
|
|
||||||
|
@ -435,7 +453,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the timing pattern (h/v checkered line between the finder patterns)
|
* Draws the timing pattern (h/v checkered line between the finder patterns)
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
* ISO/IEC 18004:2000 Section 7.3.4
|
||||||
*/
|
*/
|
||||||
public function setTimingPattern():QRMatrix{
|
public function setTimingPattern():QRMatrix{
|
||||||
|
|
||||||
|
@ -457,9 +475,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the version information, 2x 3x6 pixel
|
* Draws the version information, 2x 3x6 pixel
|
||||||
*
|
*
|
||||||
* @param bool|null $test
|
* ISO/IEC 18004:2000 Section 8.10
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
*/
|
||||||
public function setVersionNumber(bool $test = null):QRMatrix{
|
public function setVersionNumber(bool $test = null):QRMatrix{
|
||||||
$bits = $this::versionPattern[$this->version] ?? false;
|
$bits = $this::versionPattern[$this->version] ?? false;
|
||||||
|
@ -483,10 +499,7 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the format info along the finder patterns
|
* Draws the format info along the finder patterns
|
||||||
*
|
*
|
||||||
* @param int $maskPattern
|
* ISO/IEC 18004:2000 Section 8.9
|
||||||
* @param bool|null $test
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
*/
|
||||||
public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{
|
public function setFormatInfo(int $maskPattern, bool $test = null):QRMatrix{
|
||||||
$bits = $this::formatPattern[QRCode::ECC_MODES[$this->eclevel]][$maskPattern] ?? 0;
|
$bits = $this::formatPattern[QRCode::ECC_MODES[$this->eclevel]][$maskPattern] ?? 0;
|
||||||
|
@ -524,9 +537,8 @@ class QRMatrix{
|
||||||
/**
|
/**
|
||||||
* Draws the "quiet zone" of $size around the matrix
|
* Draws the "quiet zone" of $size around the matrix
|
||||||
*
|
*
|
||||||
* @param int|null $size
|
* ISO/IEC 18004:2000 Section 7.3.7
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
public function setQuietZone(int $size = null):QRMatrix{
|
public function setQuietZone(int $size = null):QRMatrix{
|
||||||
|
@ -574,18 +586,12 @@ class QRMatrix{
|
||||||
*
|
*
|
||||||
* @link https://github.com/chillerlan/php-qrcode/issues/52
|
* @link https://github.com/chillerlan/php-qrcode/issues/52
|
||||||
*
|
*
|
||||||
* @param int $width
|
|
||||||
* @param int $height
|
|
||||||
* @param int|null $startX
|
|
||||||
* @param int|null $startY
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
public function setLogoSpace(int $width, int $height, int $startX = null, int $startY = null):QRMatrix{
|
public function setLogoSpace(int $width, int $height, int $startX = null, int $startY = null):QRMatrix{
|
||||||
|
|
||||||
// for logos we operate in ECC H (30%) only
|
// for logos we operate in ECC H (30%) only
|
||||||
if($this->eclevel !== 0b10){
|
if($this->eclevel !== QRCode::ECC_H){
|
||||||
throw new QRCodeDataException('ECC level "H" required to add logo space');
|
throw new QRCodeDataException('ECC level "H" required to add logo space');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +641,8 @@ class QRMatrix{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps the binary $data array from QRDataInterface::maskECC() on the matrix, using $maskPattern
|
* Maps the binary $data array from QRDataInterface::maskECC() on the matrix,
|
||||||
|
* masking the data using $maskPattern (ISO/IEC 18004:2000 Section 8.8)
|
||||||
*
|
*
|
||||||
* @see \chillerlan\QRCode\Data\QRDataAbstract::maskECC()
|
* @see \chillerlan\QRCode\Data\QRDataAbstract::maskECC()
|
||||||
*
|
*
|
||||||
|
@ -647,10 +654,13 @@ class QRMatrix{
|
||||||
public function mapData(array $data, int $maskPattern):QRMatrix{
|
public function mapData(array $data, int $maskPattern):QRMatrix{
|
||||||
$this->maskPattern = $maskPattern;
|
$this->maskPattern = $maskPattern;
|
||||||
$byteCount = count($data);
|
$byteCount = count($data);
|
||||||
$size = $this->moduleCount - 1;
|
$y = $this->moduleCount - 1;
|
||||||
|
$inc = -1;
|
||||||
|
$byteIndex = 0;
|
||||||
|
$bitIndex = 7;
|
||||||
$mask = $this->getMask($this->maskPattern);
|
$mask = $this->getMask($this->maskPattern);
|
||||||
|
|
||||||
for($i = $size, $y = $size, $inc = -1, $byteIndex = 0, $bitIndex = 7; $i > 0; $i -= 2){
|
for($i = $y; $i > 0; $i -= 2){
|
||||||
|
|
||||||
if($i === 6){
|
if($i === 6){
|
||||||
$i--;
|
$i--;
|
||||||
|
@ -707,9 +717,6 @@ class QRMatrix{
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
* @param int $maskPattern
|
|
||||||
*
|
|
||||||
* @return \Closure
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
protected function getMask(int $maskPattern):Closure{
|
protected function getMask(int $maskPattern):Closure{
|
||||||
|
@ -719,14 +726,14 @@ class QRMatrix{
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
0b000 => function($x, $y):int{ return ($x + $y) % 2; },
|
0b000 => fn($x, $y):int => ($x + $y) % 2,
|
||||||
0b001 => function($x, $y):int{ return $y % 2; },
|
0b001 => fn($x, $y):int => $y % 2,
|
||||||
0b010 => function($x, $y):int{ return $x % 3; },
|
0b010 => fn($x, $y):int => $x % 3,
|
||||||
0b011 => function($x, $y):int{ return ($x + $y) % 3; },
|
0b011 => fn($x, $y):int => ($x + $y) % 3,
|
||||||
0b100 => function($x, $y):int{ return ((int)($y / 2) + (int)($x / 3)) % 2; },
|
0b100 => fn($x, $y):int => ((int)($y / 2) + (int)($x / 3)) % 2,
|
||||||
0b101 => function($x, $y):int{ return (($x * $y) % 2) + (($x * $y) % 3); },
|
0b101 => fn($x, $y):int => (($x * $y) % 2) + (($x * $y) % 3),
|
||||||
0b110 => function($x, $y):int{ return ((($x * $y) % 2) + (($x * $y) % 3)) % 2; },
|
0b110 => fn($x, $y):int => ((($x * $y) % 2) + (($x * $y) % 3)) % 2,
|
||||||
0b111 => function($x, $y):int{ return ((($x * $y) % 3) + (($x + $y) % 2)) % 2; },
|
0b111 => fn($x, $y):int => ((($x * $y) % 3) + (($x + $y) % 2)) % 2,
|
||||||
][$maskPattern];
|
][$maskPattern];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,20 +14,25 @@ namespace chillerlan\QRCode\Helpers;
|
||||||
|
|
||||||
use function count, floor;
|
use function count, floor;
|
||||||
|
|
||||||
class BitBuffer{
|
/**
|
||||||
|
* Holds the raw binary data
|
||||||
|
*/
|
||||||
|
final class BitBuffer{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The buffer content
|
||||||
|
*
|
||||||
* @var int[]
|
* @var int[]
|
||||||
*/
|
*/
|
||||||
public $buffer = [];
|
protected array $buffer = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* Length of the content (bits)
|
||||||
*/
|
*/
|
||||||
public $length = 0;
|
protected int $length = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \chillerlan\QRCode\Helpers\BitBuffer
|
* clears the buffer
|
||||||
*/
|
*/
|
||||||
public function clear():BitBuffer{
|
public function clear():BitBuffer{
|
||||||
$this->buffer = [];
|
$this->buffer = [];
|
||||||
|
@ -37,10 +42,7 @@ class BitBuffer{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $num
|
* appends a sequence of bits
|
||||||
* @param int $length
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Helpers\BitBuffer
|
|
||||||
*/
|
*/
|
||||||
public function put(int $num, int $length):BitBuffer{
|
public function put(int $num, int $length):BitBuffer{
|
||||||
|
|
||||||
|
@ -52,9 +54,7 @@ class BitBuffer{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param bool $bit
|
* appends a single bit
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Helpers\BitBuffer
|
|
||||||
*/
|
*/
|
||||||
public function putBit(bool $bit):BitBuffer{
|
public function putBit(bool $bit):BitBuffer{
|
||||||
$bufIndex = floor($this->length / 8);
|
$bufIndex = floor($this->length / 8);
|
||||||
|
@ -72,4 +72,18 @@ class BitBuffer{
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the current buffer length
|
||||||
|
*/
|
||||||
|
public function getLength():int{
|
||||||
|
return $this->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the buffer content
|
||||||
|
*/
|
||||||
|
public function getBuffer():array{
|
||||||
|
return $this->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,14 @@ use chillerlan\QRCode\QRCodeException;
|
||||||
use function array_fill, count, sprintf;
|
use function array_fill, count, sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/error-correction-coding
|
* Polynomial long division helpers
|
||||||
|
*
|
||||||
|
* @see http://www.thonky.com/qr-code-tutorial/error-correction-coding
|
||||||
*/
|
*/
|
||||||
class Polynomial{
|
final class Polynomial{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/log-antilog-table
|
* @see http://www.thonky.com/qr-code-tutorial/log-antilog-table
|
||||||
*/
|
*/
|
||||||
protected const table = [
|
protected const table = [
|
||||||
[ 1, 0], [ 2, 0], [ 4, 1], [ 8, 25], [ 16, 2], [ 32, 50], [ 64, 26], [128, 198],
|
[ 1, 0], [ 2, 0], [ 4, 1], [ 8, 25], [ 16, 2], [ 32, 50], [ 64, 26], [128, 198],
|
||||||
|
@ -60,29 +62,26 @@ class Polynomial{
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var int[]
|
||||||
*/
|
*/
|
||||||
protected $num = [];
|
protected array $num = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Polynomial constructor.
|
* Polynomial constructor.
|
||||||
*
|
|
||||||
* @param array|null $num
|
|
||||||
* @param int|null $shift
|
|
||||||
*/
|
*/
|
||||||
public function __construct(array $num = null, int $shift = null){
|
public function __construct(array $num = null, int $shift = null){
|
||||||
$this->setNum($num ?? [1], $shift);
|
$this->setNum($num ?? [1], $shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
*
|
||||||
*/
|
*/
|
||||||
public function getNum():array{
|
public function getNum():array{
|
||||||
return $this->num;
|
return $this->num;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $num
|
* @param int[] $num
|
||||||
* @param int|null $shift
|
* @param int|null $shift
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Helpers\Polynomial
|
* @return \chillerlan\QRCode\Helpers\Polynomial
|
||||||
|
@ -105,7 +104,7 @@ class Polynomial{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $e
|
* @param int[] $e
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Helpers\Polynomial
|
* @return \chillerlan\QRCode\Helpers\Polynomial
|
||||||
*/
|
*/
|
||||||
|
@ -127,7 +126,7 @@ class Polynomial{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $e
|
* @param int[] $e
|
||||||
*
|
*
|
||||||
* @return \chillerlan\QRCode\Helpers\Polynomial
|
* @return \chillerlan\QRCode\Helpers\Polynomial
|
||||||
*/
|
*/
|
||||||
|
@ -150,9 +149,6 @@ class Polynomial{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $n
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
* @throws \chillerlan\QRCode\QRCodeException
|
* @throws \chillerlan\QRCode\QRCodeException
|
||||||
*/
|
*/
|
||||||
public function glog(int $n):int{
|
public function glog(int $n):int{
|
||||||
|
@ -165,9 +161,7 @@ class Polynomial{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $n
|
|
||||||
*
|
*
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function gexp(int $n):int{
|
public function gexp(int $n):int{
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ class QRFpdf extends QROutputAbstract{
|
||||||
* @return string|\FPDF
|
* @return string|\FPDF
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null){
|
public function dump(string $file = null){
|
||||||
$file = $file ?? $this->options->cachefile;
|
$file ??= $this->options->cachefile;
|
||||||
|
|
||||||
$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]);
|
$fpdf = new FPDF('P', $this->options->fpdfMeasureUnit, [$this->length, $this->length]);
|
||||||
$fpdf->AddPage();
|
$fpdf->AddPage();
|
||||||
|
@ -83,6 +83,7 @@ class QRFpdf extends QROutputAbstract{
|
||||||
$color = $this->moduleValues[$M_TYPE];
|
$color = $this->moduleValues[$M_TYPE];
|
||||||
|
|
||||||
if($prevColor === null || $prevColor !== $color){
|
if($prevColor === null || $prevColor !== $color){
|
||||||
|
/** @phan-suppress-next-line PhanParamTooFewUnpack */
|
||||||
$fpdf->SetFillColor(...$color);
|
$fpdf->SetFillColor(...$color);
|
||||||
$prevColor = $color;
|
$prevColor = $color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,30 +19,36 @@ use chillerlan\QRCode\{QRCode, QRCodeException};
|
||||||
use chillerlan\Settings\SettingsContainerInterface;
|
use chillerlan\Settings\SettingsContainerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
use function array_values, base64_encode, call_user_func, count, imagecolorallocate, imagecolortransparent,
|
use function array_values, base64_encode, call_user_func, count, extension_loaded, imagecolorallocate, imagecolortransparent,
|
||||||
imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array,
|
imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array,
|
||||||
is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf;
|
is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the matrix into GD images, raw or base64 output
|
* Converts the matrix into GD images, raw or base64 output (requires ext-gd)
|
||||||
* requires ext-gd
|
*
|
||||||
* @link http://php.net/manual/book.image.php
|
* @see http://php.net/manual/book.image.php
|
||||||
*/
|
*/
|
||||||
class QRImage extends QROutputAbstract{
|
class QRImage extends QROutputAbstract{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GD image types that support transparency
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
protected const TRANSPARENCY_TYPES = [
|
protected const TRANSPARENCY_TYPES = [
|
||||||
QRCode::OUTPUT_IMAGE_PNG,
|
QRCode::OUTPUT_IMAGE_PNG,
|
||||||
QRCode::OUTPUT_IMAGE_GIF,
|
QRCode::OUTPUT_IMAGE_GIF,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
protected string $defaultMode = QRCode::OUTPUT_IMAGE_PNG;
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $defaultMode = QRCode::OUTPUT_IMAGE_PNG;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The GD image resource
|
||||||
|
*
|
||||||
* @see imagecreatetruecolor()
|
* @see imagecreatetruecolor()
|
||||||
* @var resource
|
* @var resource|\GdImage
|
||||||
|
*
|
||||||
|
* @phan-suppress PhanUndeclaredTypeProperty
|
||||||
*/
|
*/
|
||||||
protected $image;
|
protected $image;
|
||||||
|
|
||||||
|
@ -84,14 +90,19 @@ class QRImage extends QROutputAbstract{
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*
|
*
|
||||||
* @return string|resource
|
* @return string|resource|\GdImage
|
||||||
|
*
|
||||||
|
* @phan-suppress PhanUndeclaredTypeReturnType, PhanTypeMismatchReturn
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null){
|
public function dump(string $file = null){
|
||||||
|
$file ??= $this->options->cachefile;
|
||||||
|
|
||||||
$this->image = imagecreatetruecolor($this->length, $this->length);
|
$this->image = imagecreatetruecolor($this->length, $this->length);
|
||||||
|
|
||||||
// avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect
|
// avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect
|
||||||
// https://stackoverflow.com/a/10455217
|
// https://stackoverflow.com/a/10455217
|
||||||
$tbg = $this->options->imageTransparencyBG;
|
$tbg = $this->options->imageTransparencyBG;
|
||||||
|
/** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
|
||||||
$background = imagecolorallocate($this->image, ...$tbg);
|
$background = imagecolorallocate($this->image, ...$tbg);
|
||||||
|
|
||||||
if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
|
if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
|
||||||
|
@ -110,7 +121,11 @@ class QRImage extends QROutputAbstract{
|
||||||
return $this->image;
|
return $this->image;
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageData = $this->dumpImage($file);
|
$imageData = $this->dumpImage();
|
||||||
|
|
||||||
|
if($file !== null){
|
||||||
|
$this->saveToFile($imageData, $file);
|
||||||
|
}
|
||||||
|
|
||||||
if($this->options->imageBase64){
|
if($this->options->imageBase64){
|
||||||
$imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData));
|
$imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData));
|
||||||
|
@ -120,11 +135,7 @@ class QRImage extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $x
|
* Creates a single QR pixel with the given settings
|
||||||
* @param int $y
|
|
||||||
* @param array $rgb
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function setPixel(int $x, int $y, array $rgb):void{
|
protected function setPixel(int $x, int $y, array $rgb):void{
|
||||||
imagefilledrectangle(
|
imagefilledrectangle(
|
||||||
|
@ -133,20 +144,17 @@ class QRImage extends QROutputAbstract{
|
||||||
$y * $this->scale,
|
$y * $this->scale,
|
||||||
($x + 1) * $this->scale,
|
($x + 1) * $this->scale,
|
||||||
($y + 1) * $this->scale,
|
($y + 1) * $this->scale,
|
||||||
|
/** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
|
||||||
imagecolorallocate($this->image, ...$rgb)
|
imagecolorallocate($this->image, ...$rgb)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $file
|
* Creates the final image by calling the desired GD output function
|
||||||
*
|
*
|
||||||
* @return string
|
|
||||||
|
|
||||||
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
||||||
*/
|
*/
|
||||||
protected function dumpImage(string $file = null):string{
|
protected function dumpImage():string{
|
||||||
$file = $file ?? $this->options->cachefile;
|
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
@ -164,14 +172,12 @@ class QRImage extends QROutputAbstract{
|
||||||
|
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
|
|
||||||
if($file !== null){
|
|
||||||
$this->saveToFile($imageData, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $imageData;
|
return $imageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* PNG output
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function png():void{
|
protected function png():void{
|
||||||
|
@ -186,6 +192,7 @@ class QRImage extends QROutputAbstract{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jiff - like... JitHub!
|
* Jiff - like... JitHub!
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function gif():void{
|
protected function gif():void{
|
||||||
|
@ -193,6 +200,8 @@ class QRImage extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* JPG output
|
||||||
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function jpg():void{
|
protected function jpg():void{
|
||||||
|
|
|
@ -19,24 +19,20 @@ use chillerlan\QRCode\QRCodeException;
|
||||||
use chillerlan\Settings\SettingsContainerInterface;
|
use chillerlan\Settings\SettingsContainerInterface;
|
||||||
use Imagick, ImagickDraw, ImagickPixel;
|
use Imagick, ImagickDraw, ImagickPixel;
|
||||||
|
|
||||||
use function is_string;
|
use function extension_loaded, is_string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ImageMagick output module
|
* ImageMagick output module (requires ext-imagick)
|
||||||
* requires ext-imagick
|
*
|
||||||
* @link http://php.net/manual/book.imagick.php
|
* @see http://php.net/manual/book.imagick.php
|
||||||
* @link http://phpimagick.com
|
* @see http://phpimagick.com
|
||||||
*/
|
*/
|
||||||
class QRImagick extends QROutputAbstract{
|
class QRImagick extends QROutputAbstract{
|
||||||
|
|
||||||
/**
|
protected Imagick $imagick;
|
||||||
* @var \Imagick
|
|
||||||
*/
|
|
||||||
protected $imagick;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
* @throws \chillerlan\QRCode\QRCodeException
|
|
||||||
*/
|
*/
|
||||||
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
|
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
|
||||||
|
|
||||||
|
@ -72,7 +68,7 @@ class QRImagick extends QROutputAbstract{
|
||||||
* @return string|\Imagick
|
* @return string|\Imagick
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null){
|
public function dump(string $file = null){
|
||||||
$file = $file ?? $this->options->cachefile;
|
$file ??= $this->options->cachefile;
|
||||||
$this->imagick = new Imagick;
|
$this->imagick = new Imagick;
|
||||||
|
|
||||||
$this->imagick->newImage(
|
$this->imagick->newImage(
|
||||||
|
@ -98,7 +94,7 @@ class QRImagick extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* Creates the QR image via ImagickDraw
|
||||||
*/
|
*/
|
||||||
protected function drawImage():void{
|
protected function drawImage():void{
|
||||||
$draw = new ImagickDraw;
|
$draw = new ImagickDraw;
|
||||||
|
|
|
@ -21,17 +21,13 @@ use function is_string, sprintf, strip_tags, trim;
|
||||||
*/
|
*/
|
||||||
class QRMarkup extends QROutputAbstract{
|
class QRMarkup extends QROutputAbstract{
|
||||||
|
|
||||||
/**
|
protected string $defaultMode = QRCode::OUTPUT_MARKUP_SVG;
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $defaultMode = QRCode::OUTPUT_MARKUP_SVG;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \sprintf()
|
* @see \sprintf()
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="qr-svg %1$s" style="width: 100%%; height: auto;" viewBox="0 0 %2$d %2$d">';
|
protected string $svgHeader = '<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" '.
|
||||||
|
'style="width: 100%%; height: auto;" viewBox="0 0 %2$d %2$d">';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
|
@ -55,10 +51,15 @@ class QRMarkup extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* HTML output
|
||||||
*/
|
*/
|
||||||
protected function html():string{
|
protected function html(string $file = null):string{
|
||||||
$html = '<div class="'.$this->options->cssClass.'">'.$this->options->eol;
|
|
||||||
|
$html = empty($this->options->cssClass)
|
||||||
|
? '<div>'
|
||||||
|
: '<div class="'.$this->options->cssClass.'">';
|
||||||
|
|
||||||
|
$html .= $this->options->eol;
|
||||||
|
|
||||||
foreach($this->matrix->matrix() as $row){
|
foreach($this->matrix->matrix() as $row){
|
||||||
$html .= '<div>';
|
$html .= '<div>';
|
||||||
|
@ -72,19 +73,21 @@ class QRMarkup extends QROutputAbstract{
|
||||||
|
|
||||||
$html .= '</div>'.$this->options->eol;
|
$html .= '</div>'.$this->options->eol;
|
||||||
|
|
||||||
if($this->options->cachefile){
|
if($file !== null){
|
||||||
return '<!DOCTYPE html><head><meta charset="UTF-8"></head><body>'.$this->options->eol.$html.'</body>';
|
return '<!DOCTYPE html>'.
|
||||||
|
'<head><meta charset="UTF-8"><title>QR Code</title></head>'.
|
||||||
|
'<body>'.$this->options->eol.$html.'</body>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link https://github.com/codemasher/php-qrcode/pull/5
|
* SVG output
|
||||||
*
|
*
|
||||||
* @return string
|
* @see https://github.com/codemasher/php-qrcode/pull/5
|
||||||
*/
|
*/
|
||||||
protected function svg():string{
|
protected function svg(string $file = null):string{
|
||||||
$matrix = $this->matrix->matrix();
|
$matrix = $this->matrix->matrix();
|
||||||
|
|
||||||
$svg = sprintf($this->svgHeader, $this->options->cssClass, $this->options->svgViewBoxSize ?? $this->moduleCount)
|
$svg = sprintf($this->svgHeader, $this->options->cssClass, $this->options->svgViewBoxSize ?? $this->moduleCount)
|
||||||
|
@ -116,6 +119,8 @@ class QRMarkup extends QROutputAbstract{
|
||||||
|
|
||||||
if($count > 0){
|
if($count > 0){
|
||||||
$len = $count;
|
$len = $count;
|
||||||
|
$start ??= 0; // avoid type coercion in sprintf() - phan happy
|
||||||
|
|
||||||
$path .= sprintf('M%s %s h%s v1 h-%sZ ', $start, $y, $len, $len);
|
$path .= sprintf('M%s %s h%s v1 h-%sZ ', $start, $y, $len, $len);
|
||||||
|
|
||||||
// reset count
|
// reset count
|
||||||
|
@ -128,7 +133,10 @@ class QRMarkup extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!empty($path)){
|
if(!empty($path)){
|
||||||
$svg .= sprintf('<path class="qr-%s %s" stroke="transparent" fill="%s" fill-opacity="%s" d="%s" />', $M_TYPE, $this->options->cssClass, $value, $this->options->svgOpacity, $path);
|
$svg .= sprintf(
|
||||||
|
'<path class="qr-%s %s" stroke="transparent" fill="%s" fill-opacity="%s" d="%s" />',
|
||||||
|
$M_TYPE, $this->options->cssClass, $value, $this->options->svgOpacity, $path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -137,8 +145,9 @@ class QRMarkup extends QROutputAbstract{
|
||||||
$svg .= '</svg>'.$this->options->eol;
|
$svg .= '</svg>'.$this->options->eol;
|
||||||
|
|
||||||
// if saving to file, append the correct headers
|
// if saving to file, append the correct headers
|
||||||
if($this->options->cachefile){
|
if($file !== null){
|
||||||
return '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'.$this->options->eol.$svg;
|
return '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'.
|
||||||
|
$this->options->eol.$svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->options->imageBase64){
|
if($this->options->imageBase64){
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace chillerlan\QRCode\Output;
|
||||||
use chillerlan\QRCode\{Data\QRMatrix, QRCode};
|
use chillerlan\QRCode\{Data\QRMatrix, QRCode};
|
||||||
use chillerlan\Settings\SettingsContainerInterface;
|
use chillerlan\Settings\SettingsContainerInterface;
|
||||||
|
|
||||||
use function call_user_func, dirname, file_put_contents, get_called_class, in_array, is_writable, sprintf;
|
use function call_user_func_array, dirname, file_put_contents, get_called_class, in_array, is_writable, sprintf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* common output abstract
|
* common output abstract
|
||||||
|
@ -23,50 +23,53 @@ use function call_user_func, dirname, file_put_contents, get_called_class, in_ar
|
||||||
abstract class QROutputAbstract implements QROutputInterface{
|
abstract class QROutputAbstract implements QROutputInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the current size of the QR matrix
|
||||||
|
*
|
||||||
|
* @see \chillerlan\QRCode\Data\QRMatrix::size()
|
||||||
*/
|
*/
|
||||||
protected $moduleCount;
|
protected int $moduleCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \chillerlan\QRCode\Data\QRMatrix $matrix
|
* the current output mode
|
||||||
|
*
|
||||||
|
* @see \chillerlan\QRCode\QROptions::$outputType
|
||||||
*/
|
*/
|
||||||
protected $matrix;
|
protected string $outputMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\QROptions
|
* the default output mode of the current output module
|
||||||
*/
|
*/
|
||||||
protected $options;
|
protected string $defaultMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* the current scaling for a QR pixel
|
||||||
|
*
|
||||||
|
* @see \chillerlan\QRCode\QROptions::$scale
|
||||||
*/
|
*/
|
||||||
protected $outputMode;
|
protected int $scale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string;
|
* the side length of the QR image (modules * scale)
|
||||||
*/
|
*/
|
||||||
protected $defaultMode;
|
protected int $length;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* an (optional) array of color values for the several QR matrix parts
|
||||||
*/
|
*/
|
||||||
protected $scale;
|
protected array $moduleValues;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* the (filled) data matrix object
|
||||||
*/
|
*/
|
||||||
protected $length;
|
protected QRMatrix $matrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var \chillerlan\Settings\SettingsContainerInterface|\chillerlan\QRCode\QROptions
|
||||||
*/
|
*/
|
||||||
protected $moduleValues;
|
protected SettingsContainerInterface $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QROutputAbstract constructor.
|
* QROutputAbstract constructor.
|
||||||
*
|
|
||||||
* @param \chillerlan\Settings\SettingsContainerInterface $options
|
|
||||||
* @param \chillerlan\QRCode\Data\QRMatrix $matrix
|
|
||||||
*/
|
*/
|
||||||
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
|
public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
|
||||||
$this->options = $options;
|
$this->options = $options;
|
||||||
|
@ -86,8 +89,6 @@ abstract class QROutputAbstract implements QROutputInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the initial module values (clean-up & defaults)
|
* Sets the initial module values (clean-up & defaults)
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
abstract protected function setModuleValues():void;
|
abstract protected function setModuleValues():void;
|
||||||
|
|
||||||
|
@ -97,10 +98,6 @@ abstract class QROutputAbstract implements QROutputInterface{
|
||||||
* @see file_put_contents()
|
* @see file_put_contents()
|
||||||
* @see \chillerlan\QRCode\QROptions::cachefile
|
* @see \chillerlan\QRCode\QROptions::cachefile
|
||||||
*
|
*
|
||||||
* @param string $data
|
|
||||||
* @param string $file
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
||||||
*/
|
*/
|
||||||
protected function saveToFile(string $data, string $file):bool{
|
protected function saveToFile(string $data, string $file):bool{
|
||||||
|
@ -116,9 +113,11 @@ abstract class QROutputAbstract implements QROutputInterface{
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null){
|
public function dump(string $file = null){
|
||||||
// call the built-in output method
|
$file ??= $this->options->cachefile;
|
||||||
$data = call_user_func([$this, $this->outputMode ?? $this->defaultMode]);
|
|
||||||
$file = $file ?? $this->options->cachefile;
|
// call the built-in output method with the optional file path as parameter
|
||||||
|
// to make the called method aware if a cache file was given
|
||||||
|
$data = call_user_func_array([$this, $this->outputMode ?? $this->defaultMode], [$file]);
|
||||||
|
|
||||||
if($file !== null){
|
if($file !== null){
|
||||||
$this->saveToFile($data, $file);
|
$this->saveToFile($data, $file);
|
||||||
|
|
|
@ -21,6 +21,7 @@ interface QROutputInterface{
|
||||||
|
|
||||||
const DEFAULT_MODULE_VALUES = [
|
const DEFAULT_MODULE_VALUES = [
|
||||||
// light
|
// light
|
||||||
|
QRMatrix::M_NULL => false, // 0
|
||||||
QRMatrix::M_DATA => false, // 4
|
QRMatrix::M_DATA => false, // 4
|
||||||
QRMatrix::M_FINDER => false, // 6
|
QRMatrix::M_FINDER => false, // 6
|
||||||
QRMatrix::M_SEPARATOR => false, // 8
|
QRMatrix::M_SEPARATOR => false, // 8
|
||||||
|
@ -46,8 +47,6 @@ interface QROutputInterface{
|
||||||
/**
|
/**
|
||||||
* generates the output, optionally dumps it to a file, and returns it
|
* generates the output, optionally dumps it to a file, and returns it
|
||||||
*
|
*
|
||||||
* @param string|null $file
|
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function dump(string $file = null);
|
public function dump(string $file = null);
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2015 Smiley
|
* @copyright 2015 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
*
|
||||||
|
* @noinspection PhpUnusedParameterInspection
|
||||||
|
* @noinspection PhpComposerExtensionStubsInspection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace chillerlan\QRCode\Output;
|
namespace chillerlan\QRCode\Output;
|
||||||
|
@ -21,10 +24,7 @@ use function implode, is_string, json_encode;
|
||||||
*/
|
*/
|
||||||
class QRString extends QROutputAbstract{
|
class QRString extends QROutputAbstract{
|
||||||
|
|
||||||
/**
|
protected string $defaultMode = QRCode::OUTPUT_STRING_TEXT;
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $defaultMode = QRCode::OUTPUT_STRING_TEXT;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
|
@ -48,9 +48,9 @@ class QRString extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* string output
|
||||||
*/
|
*/
|
||||||
protected function text():string{
|
protected function text(string $file = null):string{
|
||||||
$str = [];
|
$str = [];
|
||||||
|
|
||||||
foreach($this->matrix->matrix() as $row){
|
foreach($this->matrix->matrix() as $row){
|
||||||
|
@ -67,9 +67,9 @@ class QRString extends QROutputAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* JSON output
|
||||||
*/
|
*/
|
||||||
protected function json():string{
|
protected function json(string $file = null):string{
|
||||||
return json_encode($this->matrix->matrix());
|
return json_encode($this->matrix->matrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,58 +13,49 @@
|
||||||
namespace chillerlan\QRCode;
|
namespace chillerlan\QRCode;
|
||||||
|
|
||||||
use chillerlan\QRCode\Data\{
|
use chillerlan\QRCode\Data\{
|
||||||
MaskPatternTester, QRCodeDataException, QRDataInterface, QRMatrix
|
AlphaNum, Byte, Kanji, MaskPatternTester, Number, QRCodeDataException, QRDataInterface, QRMatrix
|
||||||
};
|
};
|
||||||
use chillerlan\QRCode\Output\{
|
use chillerlan\QRCode\Output\{
|
||||||
QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
|
QRCodeOutputException, QRFpdf, QRImage, QRImagick, QRMarkup, QROutputInterface, QRString
|
||||||
};
|
};
|
||||||
use chillerlan\Settings\SettingsContainerInterface;
|
use chillerlan\Settings\SettingsContainerInterface;
|
||||||
|
|
||||||
use function array_search, call_user_func_array, class_exists, in_array, min, ord, strlen;
|
use function call_user_func_array, class_exists, in_array, ord, strlen, strtolower, str_split;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a text string into a Model 2 QR Code
|
* Turns a text string into a Model 2 QR Code
|
||||||
*
|
*
|
||||||
* @link https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
|
* @see https://github.com/kazuhikoarase/qrcode-generator/tree/master/php
|
||||||
* @link http://www.qrcode.com/en/codes/model12.html
|
* @see http://www.qrcode.com/en/codes/model12.html
|
||||||
* @link http://www.thonky.com/qr-code-tutorial/
|
* @see https://www.swisseduc.ch/informatik/theoretische_informatik/qr_codes/docs/qr_standard.pdf
|
||||||
|
* @see https://en.wikipedia.org/wiki/QR_code
|
||||||
|
* @see http://www.thonky.com/qr-code-tutorial/
|
||||||
*/
|
*/
|
||||||
class QRCode{
|
class QRCode{
|
||||||
|
|
||||||
/**
|
/** @var int */
|
||||||
* API constants
|
|
||||||
*/
|
|
||||||
public const OUTPUT_MARKUP_HTML = 'html';
|
|
||||||
public const OUTPUT_MARKUP_SVG = 'svg';
|
|
||||||
public const OUTPUT_IMAGE_PNG = 'png';
|
|
||||||
public const OUTPUT_IMAGE_JPG = 'jpg';
|
|
||||||
public const OUTPUT_IMAGE_GIF = 'gif';
|
|
||||||
public const OUTPUT_STRING_JSON = 'json';
|
|
||||||
public const OUTPUT_STRING_TEXT = 'text';
|
|
||||||
public const OUTPUT_IMAGICK = 'imagick';
|
|
||||||
public const OUTPUT_FPDF = 'fpdf';
|
|
||||||
public const OUTPUT_CUSTOM = 'custom';
|
|
||||||
|
|
||||||
public const VERSION_AUTO = -1;
|
public const VERSION_AUTO = -1;
|
||||||
|
/** @var int */
|
||||||
public const MASK_PATTERN_AUTO = -1;
|
public const MASK_PATTERN_AUTO = -1;
|
||||||
|
|
||||||
public const ECC_L = 0b01; // 7%.
|
// ISO/IEC 18004:2000 Table 2
|
||||||
public const ECC_M = 0b00; // 15%.
|
|
||||||
public const ECC_Q = 0b11; // 25%.
|
|
||||||
public const ECC_H = 0b10; // 30%.
|
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
public const DATA_NUMBER = 0b0001;
|
public const DATA_NUMBER = 0b0001;
|
||||||
|
/** @var int */
|
||||||
public const DATA_ALPHANUM = 0b0010;
|
public const DATA_ALPHANUM = 0b0010;
|
||||||
|
/** @var int */
|
||||||
public const DATA_BYTE = 0b0100;
|
public const DATA_BYTE = 0b0100;
|
||||||
|
/** @var int */
|
||||||
public const DATA_KANJI = 0b1000;
|
public const DATA_KANJI = 0b1000;
|
||||||
|
|
||||||
public const ECC_MODES = [
|
/**
|
||||||
self::ECC_L => 0,
|
* References to the keys of the following tables:
|
||||||
self::ECC_M => 1,
|
*
|
||||||
self::ECC_Q => 2,
|
* @see \chillerlan\QRCode\Data\QRDataInterface::MAX_LENGTH
|
||||||
self::ECC_H => 3,
|
*
|
||||||
];
|
* @var int[]
|
||||||
|
*/
|
||||||
public const DATA_MODES = [
|
public const DATA_MODES = [
|
||||||
self::DATA_NUMBER => 0,
|
self::DATA_NUMBER => 0,
|
||||||
self::DATA_ALPHANUM => 1,
|
self::DATA_ALPHANUM => 1,
|
||||||
|
@ -72,6 +63,59 @@ class QRCode{
|
||||||
self::DATA_KANJI => 3,
|
self::DATA_KANJI => 3,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// ISO/IEC 18004:2000 Tables 12, 25
|
||||||
|
|
||||||
|
/** @var int */
|
||||||
|
public const ECC_L = 0b01; // 7%.
|
||||||
|
/** @var int */
|
||||||
|
public const ECC_M = 0b00; // 15%.
|
||||||
|
/** @var int */
|
||||||
|
public const ECC_Q = 0b11; // 25%.
|
||||||
|
/** @var int */
|
||||||
|
public const ECC_H = 0b10; // 30%.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* References to the keys of the following tables:
|
||||||
|
*
|
||||||
|
* @see \chillerlan\QRCode\Data\QRDataInterface::MAX_BITS
|
||||||
|
* @see \chillerlan\QRCode\Data\QRDataInterface::RSBLOCKS
|
||||||
|
* @see \chillerlan\QRCode\Data\QRMatrix::formatPattern
|
||||||
|
*
|
||||||
|
* @var int[]
|
||||||
|
*/
|
||||||
|
public const ECC_MODES = [
|
||||||
|
self::ECC_L => 0,
|
||||||
|
self::ECC_M => 1,
|
||||||
|
self::ECC_Q => 2,
|
||||||
|
self::ECC_H => 3,
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_MARKUP_HTML = 'html';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_MARKUP_SVG = 'svg';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_IMAGE_PNG = 'png';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_IMAGE_JPG = 'jpg';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_IMAGE_GIF = 'gif';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_STRING_JSON = 'json';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_STRING_TEXT = 'text';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_IMAGICK = 'imagick';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_FPDF = 'fpdf';
|
||||||
|
/** @var string */
|
||||||
|
public const OUTPUT_CUSTOM = 'custom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of built-in output modules => capabilities
|
||||||
|
*
|
||||||
|
* @var string[][]
|
||||||
|
*/
|
||||||
public const OUTPUT_MODES = [
|
public const OUTPUT_MODES = [
|
||||||
QRMarkup::class => [
|
QRMarkup::class => [
|
||||||
self::OUTPUT_MARKUP_SVG,
|
self::OUTPUT_MARKUP_SVG,
|
||||||
|
@ -95,19 +139,33 @@ class QRCode{
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
|
* Map of data mode => interface
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
*/
|
*/
|
||||||
protected $options;
|
protected const DATA_INTERFACES = [
|
||||||
|
'number' => Number::class,
|
||||||
|
'alphanum' => AlphaNum::class,
|
||||||
|
'kanji' => Kanji::class,
|
||||||
|
'byte' => Byte::class,
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\Data\QRDataInterface
|
* The settings container
|
||||||
|
*
|
||||||
|
* @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface
|
||||||
*/
|
*/
|
||||||
protected $dataInterface;
|
protected SettingsContainerInterface $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selected data interface (Number, AlphaNum, Kanji, Byte)
|
||||||
|
*/
|
||||||
|
protected QRDataInterface $dataInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QRCode constructor.
|
* QRCode constructor.
|
||||||
*
|
*
|
||||||
* @param \chillerlan\Settings\SettingsContainerInterface|null $options
|
* Sets the options instance, determines the current mb-encoding and sets it to UTF-8
|
||||||
*/
|
*/
|
||||||
public function __construct(SettingsContainerInterface $options = null){
|
public function __construct(SettingsContainerInterface $options = null){
|
||||||
$this->options = $options ?? new QROptions;
|
$this->options = $options ?? new QROptions;
|
||||||
|
@ -116,9 +174,6 @@ class QRCode{
|
||||||
/**
|
/**
|
||||||
* Renders a QR Code for the given $data and QROptions
|
* Renders a QR Code for the given $data and QROptions
|
||||||
*
|
*
|
||||||
* @param string $data
|
|
||||||
* @param string|null $file
|
|
||||||
*
|
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function render(string $data, string $file = null){
|
public function render(string $data, string $file = null){
|
||||||
|
@ -128,9 +183,6 @@ class QRCode{
|
||||||
/**
|
/**
|
||||||
* Returns a QRMatrix object for the given $data and current QROptions
|
* Returns a QRMatrix object for the given $data and current QROptions
|
||||||
*
|
*
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
public function getMatrix(string $data):QRMatrix{
|
public function getMatrix(string $data):QRMatrix{
|
||||||
|
@ -142,7 +194,7 @@ class QRCode{
|
||||||
$this->dataInterface = $this->initDataInterface($data);
|
$this->dataInterface = $this->initDataInterface($data);
|
||||||
|
|
||||||
$maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO
|
$maskPattern = $this->options->maskPattern === $this::MASK_PATTERN_AUTO
|
||||||
? $this->getBestMaskPattern()
|
? (new MaskPatternTester($this->dataInterface))->getBestMaskPattern()
|
||||||
: $this->options->maskPattern;
|
: $this->options->maskPattern;
|
||||||
|
|
||||||
$matrix = $this->dataInterface->initMatrix($maskPattern);
|
$matrix = $this->dataInterface->initMatrix($maskPattern);
|
||||||
|
@ -154,49 +206,24 @@ class QRCode{
|
||||||
return $matrix;
|
return $matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* shoves a QRMatrix through the MaskPatternTester to find the lowest penalty mask pattern
|
|
||||||
*
|
|
||||||
* @see \chillerlan\QRCode\Data\MaskPatternTester
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function getBestMaskPattern():int{
|
|
||||||
$penalties = [];
|
|
||||||
|
|
||||||
for($pattern = 0; $pattern < 8; $pattern++){
|
|
||||||
$tester = new MaskPatternTester($this->dataInterface->initMatrix($pattern, true));
|
|
||||||
|
|
||||||
$penalties[$pattern] = $tester->testPattern();
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_search(min($penalties), $penalties, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a fresh QRDataInterface for the given $data
|
* returns a fresh QRDataInterface for the given $data
|
||||||
*
|
*
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Data\QRDataInterface
|
|
||||||
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
* @throws \chillerlan\QRCode\Data\QRCodeDataException
|
||||||
*/
|
*/
|
||||||
public function initDataInterface(string $data):QRDataInterface{
|
public function initDataInterface(string $data):QRDataInterface{
|
||||||
$dataModes = ['Number', 'AlphaNum', 'Kanji', 'Byte'];
|
|
||||||
$dataNamespace = __NAMESPACE__.'\\Data\\';
|
|
||||||
|
|
||||||
// allow forcing the data mode
|
// allow forcing the data mode
|
||||||
// see https://github.com/chillerlan/php-qrcode/issues/39
|
// see https://github.com/chillerlan/php-qrcode/issues/39
|
||||||
if(in_array($this->options->dataMode, $dataModes, true)){
|
$interface = $this::DATA_INTERFACES[strtolower($this->options->dataModeOverride)] ?? null;
|
||||||
$dataInterface = $dataNamespace.$this->options->dataMode;
|
|
||||||
|
|
||||||
return new $dataInterface($this->options, $data);
|
if($interface !== null){
|
||||||
|
return new $interface($this->options, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($dataModes as $mode){
|
foreach($this::DATA_INTERFACES as $mode => $dataInterface){
|
||||||
$dataInterface = $dataNamespace.$mode;
|
|
||||||
|
|
||||||
if(call_user_func_array([$this, 'is'.$mode], [$data]) && class_exists($dataInterface)){
|
if(call_user_func_array([$this, 'is'.$mode], [$data])){
|
||||||
return new $dataInterface($this->options, $data);
|
return new $dataInterface($this->options, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,14 +235,12 @@ class QRCode{
|
||||||
/**
|
/**
|
||||||
* returns a fresh (built-in) QROutputInterface
|
* returns a fresh (built-in) QROutputInterface
|
||||||
*
|
*
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return \chillerlan\QRCode\Output\QROutputInterface
|
|
||||||
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
* @throws \chillerlan\QRCode\Output\QRCodeOutputException
|
||||||
*/
|
*/
|
||||||
protected function initOutputInterface(string $data):QROutputInterface{
|
protected function initOutputInterface(string $data):QROutputInterface{
|
||||||
|
|
||||||
if($this->options->outputType === $this::OUTPUT_CUSTOM && class_exists($this->options->outputInterface)){
|
if($this->options->outputType === $this::OUTPUT_CUSTOM && class_exists($this->options->outputInterface)){
|
||||||
|
/** @phan-suppress-next-line PhanTypeExpectedObjectOrClassName */
|
||||||
return new $this->options->outputInterface($this->options, $this->getMatrix($data));
|
return new $this->options->outputInterface($this->options, $this->getMatrix($data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,39 +257,25 @@ class QRCode{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks if a string qualifies as numeric
|
* checks if a string qualifies as numeric
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function isNumber(string $string):bool{
|
public function isNumber(string $string):bool{
|
||||||
return $this->checkString($string, QRDataInterface::NUMBER_CHAR_MAP);
|
return $this->checkString($string, QRDataInterface::CHAR_MAP_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks if a string qualifies as alphanumeric
|
* checks if a string qualifies as alphanumeric
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function isAlphaNum(string $string):bool{
|
public function isAlphaNum(string $string):bool{
|
||||||
return $this->checkString($string, QRDataInterface::ALPHANUM_CHAR_MAP);
|
return $this->checkString($string, QRDataInterface::CHAR_MAP_ALPHANUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks is a given $string matches the characters of a given $charmap, returns false on the first invalid occurence.
|
* checks is a given $string matches the characters of a given $charmap, returns false on the first invalid occurence.
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
* @param array $charmap
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function checkString(string $string, array $charmap):bool{
|
protected function checkString(string $string, array $charmap):bool{
|
||||||
$len = strlen($string);
|
|
||||||
|
|
||||||
for($i = 0; $i < $len; $i++){
|
foreach(str_split($string) as $chr){
|
||||||
if(!in_array($string[$i], $charmap, true)){
|
if(!isset($charmap[$chr])){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,10 +285,6 @@ class QRCode{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* checks if a string qualifies as Kanji
|
* checks if a string qualifies as Kanji
|
||||||
*
|
|
||||||
* @param string $string
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function isKanji(string $string):bool{
|
public function isKanji(string $string):bool{
|
||||||
$i = 0;
|
$i = 0;
|
||||||
|
@ -298,12 +305,8 @@ class QRCode{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a dummy
|
* a dummy
|
||||||
*
|
|
||||||
* @param $data
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function isByte(string $data):bool{
|
public function isByte(string $data):bool{
|
||||||
return !empty($data);
|
return !empty($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,9 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCode;
|
namespace chillerlan\QRCode;
|
||||||
|
|
||||||
class QRCodeException extends \Exception{}
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An exception container
|
||||||
|
*/
|
||||||
|
class QRCodeException extends Exception{}
|
||||||
|
|
|
@ -15,46 +15,39 @@ namespace chillerlan\QRCode;
|
||||||
use chillerlan\Settings\SettingsContainerAbstract;
|
use chillerlan\Settings\SettingsContainerAbstract;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The QRCode settings container
|
||||||
|
*
|
||||||
* @property int $version
|
* @property int $version
|
||||||
* @property int $versionMin
|
* @property int $versionMin
|
||||||
* @property int $versionMax
|
* @property int $versionMax
|
||||||
* @property int $eccLevel
|
* @property int $eccLevel
|
||||||
* @property int $maskPattern
|
* @property int $maskPattern
|
||||||
* @property bool $addQuietzone
|
* @property bool $addQuietzone
|
||||||
* @property bool $quietzoneSize
|
* @property int $quietzoneSize
|
||||||
*
|
* @property string|null $dataModeOverride
|
||||||
* @property string $dataMode
|
|
||||||
* @property string $outputType
|
* @property string $outputType
|
||||||
* @property string $outputInterface
|
* @property string|null $outputInterface
|
||||||
* @property string $cachefile
|
* @property string|null $cachefile
|
||||||
*
|
|
||||||
* @property string $eol
|
* @property string $eol
|
||||||
* @property int $scale
|
* @property int $scale
|
||||||
*
|
|
||||||
* @property string $cssClass
|
* @property string $cssClass
|
||||||
* @property string $svgOpacity
|
* @property float $svgOpacity
|
||||||
* @property string $svgDefs
|
* @property string $svgDefs
|
||||||
* @property int $svgViewBoxSize
|
* @property int $svgViewBoxSize
|
||||||
*
|
|
||||||
* @property string $textDark
|
* @property string $textDark
|
||||||
* @property string $textLight
|
* @property string $textLight
|
||||||
*
|
|
||||||
* @property string $markupDark
|
* @property string $markupDark
|
||||||
* @property string $markupLight
|
* @property string $markupLight
|
||||||
*
|
|
||||||
* @property bool $returnResource
|
* @property bool $returnResource
|
||||||
* @property bool $imageBase64
|
* @property bool $imageBase64
|
||||||
* @property bool $imageTransparent
|
* @property bool $imageTransparent
|
||||||
* @property array $imageTransparencyBG
|
* @property array $imageTransparencyBG
|
||||||
* @property int $pngCompression
|
* @property int $pngCompression
|
||||||
* @property int $jpegQuality
|
* @property int $jpegQuality
|
||||||
*
|
|
||||||
* @property string $imagickFormat
|
* @property string $imagickFormat
|
||||||
* @property string $imagickBG
|
* @property string|null $imagickBG
|
||||||
*
|
|
||||||
* @property string $fpdfMeasureUnit
|
* @property string $fpdfMeasureUnit
|
||||||
*
|
* @property array|null $moduleValues
|
||||||
* @property array $moduleValues
|
|
||||||
*/
|
*/
|
||||||
class QROptions extends SettingsContainerAbstract{
|
class QROptions extends SettingsContainerAbstract{
|
||||||
use QROptionsTrait;
|
use QROptionsTrait;
|
||||||
|
|
|
@ -8,146 +8,125 @@
|
||||||
* @author smiley <smiley@chillerlan.net>
|
* @author smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
*
|
||||||
|
* @noinspection PhpUnused
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace chillerlan\QRCode;
|
namespace chillerlan\QRCode;
|
||||||
|
|
||||||
use function array_values, count, in_array, is_array, is_numeric, max, min, sprintf, strtolower;
|
use function array_values, count, in_array, is_numeric, max, min, sprintf, strtolower;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QRCode plug-in settings & setter functionality
|
||||||
|
*/
|
||||||
trait QROptionsTrait{
|
trait QROptionsTrait{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QR Code version number
|
* QR Code version number
|
||||||
*
|
*
|
||||||
* [1 ... 40] or QRCode::VERSION_AUTO
|
* [1 ... 40] or QRCode::VERSION_AUTO
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $version = QRCode::VERSION_AUTO;
|
protected int $version = QRCode::VERSION_AUTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimum QR version (if $version = QRCode::VERSION_AUTO)
|
* Minimum QR version
|
||||||
*
|
*
|
||||||
* @var int
|
* if $version = QRCode::VERSION_AUTO
|
||||||
*/
|
*/
|
||||||
protected $versionMin = 1;
|
protected int $versionMin = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum QR version
|
* Maximum QR version
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $versionMax = 40;
|
protected int $versionMax = 40;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error correct level
|
* Error correct level
|
||||||
*
|
*
|
||||||
* QRCode::ECC_X where X is
|
* QRCode::ECC_X where X is:
|
||||||
* L => 7%
|
|
||||||
* M => 15%
|
|
||||||
* Q => 25%
|
|
||||||
* H => 30%
|
|
||||||
*
|
*
|
||||||
* @var int
|
* - L => 7%
|
||||||
|
* - M => 15%
|
||||||
|
* - Q => 25%
|
||||||
|
* - H => 30%
|
||||||
*/
|
*/
|
||||||
protected $eccLevel = QRCode::ECC_L;
|
protected int $eccLevel = QRCode::ECC_L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mask Pattern to use
|
* Mask Pattern to use
|
||||||
*
|
*
|
||||||
* [0...7] or QRCode::MASK_PATTERN_AUTO
|
* [0...7] or QRCode::MASK_PATTERN_AUTO
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $maskPattern = QRCode::MASK_PATTERN_AUTO;
|
protected int $maskPattern = QRCode::MASK_PATTERN_AUTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a "quiet zone" (margin) according to the QR code spec
|
* Add a "quiet zone" (margin) according to the QR code spec
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
*/
|
||||||
protected $addQuietzone = true;
|
protected bool $addQuietzone = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Size of the quiet zone
|
* Size of the quiet zone
|
||||||
*
|
*
|
||||||
* internally clamped to [0 ... $moduleCount / 2], defaults to 4 modules
|
* internally clamped to [0 ... $moduleCount / 2], defaults to 4 modules
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $quietzoneSize = 4;
|
protected int $quietzoneSize = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use this to circumvent the data mode detection and force the usage of the given mode.
|
* Use this to circumvent the data mode detection and force the usage of the given mode.
|
||||||
* valid modes are: Number, AlphaNum, Kanji, Byte
|
*
|
||||||
|
* valid modes are: Number, AlphaNum, Kanji, Byte (case insensitive)
|
||||||
*
|
*
|
||||||
* @see https://github.com/chillerlan/php-qrcode/issues/39
|
* @see https://github.com/chillerlan/php-qrcode/issues/39
|
||||||
*
|
* @see https://github.com/chillerlan/php-qrcode/issues/97 (changed default value to '')
|
||||||
* @var string|null
|
|
||||||
*/
|
*/
|
||||||
protected $dataMode = null;
|
protected string $dataModeOverride = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QRCode::OUTPUT_MARKUP_XXXX where XXXX = HTML, SVG
|
* The output type
|
||||||
* QRCode::OUTPUT_IMAGE_XXX where XXX = PNG, GIF, JPG
|
|
||||||
* QRCode::OUTPUT_STRING_XXXX where XXXX = TEXT, JSON
|
|
||||||
* QRCode::OUTPUT_CUSTOM
|
|
||||||
*
|
*
|
||||||
* @var string
|
* - QRCode::OUTPUT_MARKUP_XXXX where XXXX = HTML, SVG
|
||||||
|
* - QRCode::OUTPUT_IMAGE_XXX where XXX = PNG, GIF, JPG
|
||||||
|
* - QRCode::OUTPUT_STRING_XXXX where XXXX = TEXT, JSON
|
||||||
|
* - QRCode::OUTPUT_CUSTOM
|
||||||
*/
|
*/
|
||||||
protected $outputType = QRCode::OUTPUT_IMAGE_PNG;
|
protected string $outputType = QRCode::OUTPUT_IMAGE_PNG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the FQCN of the custom QROutputInterface if $outputType is set to QRCode::OUTPUT_CUSTOM
|
* the FQCN of the custom QROutputInterface if $outputType is set to QRCode::OUTPUT_CUSTOM
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
*/
|
||||||
protected $outputInterface = null;
|
protected ?string $outputInterface = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /path/to/cache.file
|
* /path/to/cache.file
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
*/
|
||||||
protected $cachefile = null;
|
protected ?string $cachefile = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* newline string [HTML, SVG, TEXT]
|
* newline string [HTML, SVG, TEXT]
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $eol = PHP_EOL;
|
protected string $eol = PHP_EOL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* size of a QR code pixel [SVG, IMAGE_*]
|
* size of a QR code pixel [SVG, IMAGE_*], HTML via CSS
|
||||||
* HTML -> via CSS
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $scale = 5;
|
protected int $scale = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a common css class
|
* a common css class
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $cssClass = '';
|
protected string $cssClass = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SVG opacity
|
* SVG opacity
|
||||||
*
|
|
||||||
* @var float
|
|
||||||
*/
|
*/
|
||||||
protected $svgOpacity = 1.0;
|
protected float $svgOpacity = 1.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* anything between <defs>
|
* anything between <defs>
|
||||||
*
|
*
|
||||||
* @see https://developer.mozilla.org/docs/Web/SVG/Element/defs
|
* @see https://developer.mozilla.org/docs/Web/SVG/Element/defs
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $svgDefs = '<style>rect{shape-rendering:crispEdges}</style>';
|
protected string $svgDefs = '<style>rect{shape-rendering:crispEdges}</style>';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SVG viewBox size. a single integer number which defines width/height of the viewBox attribute.
|
* SVG viewBox size. a single integer number which defines width/height of the viewBox attribute.
|
||||||
|
@ -155,38 +134,28 @@ trait QROptionsTrait{
|
||||||
* viewBox="0 0 x x"
|
* viewBox="0 0 x x"
|
||||||
*
|
*
|
||||||
* @see https://css-tricks.com/scale-svg/#article-header-id-3
|
* @see https://css-tricks.com/scale-svg/#article-header-id-3
|
||||||
*
|
|
||||||
* @var int|null
|
|
||||||
*/
|
*/
|
||||||
protected $svgViewBoxSize = null;
|
protected ?int $svgViewBoxSize = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* string substitute for dark
|
* string substitute for dark
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $textDark = '🔴';
|
protected string $textDark = '🔴';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* string substitute for light
|
* string substitute for light
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $textLight = '⭕';
|
protected string $textLight = '⭕';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* markup substitute for dark (CSS value)
|
* markup substitute for dark (CSS value)
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $markupDark = '#000';
|
protected string $markupDark = '#000';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* markup substitute for light (CSS value)
|
* markup substitute for light (CSS value)
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $markupLight = '#fff';
|
protected string $markupLight = '#fff';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the image resource instead of a render if applicable.
|
* Return the image resource instead of a render if applicable.
|
||||||
|
@ -194,7 +163,7 @@ trait QROptionsTrait{
|
||||||
*
|
*
|
||||||
* Supported by the following modules:
|
* Supported by the following modules:
|
||||||
*
|
*
|
||||||
* - QRImage: resource
|
* - QRImage: resource (PHP < 8), GdImage
|
||||||
* - QRImagick: Imagick
|
* - QRImagick: Imagick
|
||||||
* - QRFpdf: FPDF
|
* - QRFpdf: FPDF
|
||||||
*
|
*
|
||||||
|
@ -202,85 +171,66 @@ trait QROptionsTrait{
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
protected $returnResource = false;
|
protected bool $returnResource = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggle base64 or raw image data
|
* toggle base64 or raw image data
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
*/
|
||||||
protected $imageBase64 = true;
|
protected bool $imageBase64 = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toggle transparency, not supported by jpg
|
* toggle transparency, not supported by jpg
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
*/
|
||||||
protected $imageTransparent = true;
|
protected bool $imageTransparent = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see imagecolortransparent()
|
* @see imagecolortransparent()
|
||||||
*
|
*
|
||||||
* @var array [R, G, B]
|
* [R, G, B]
|
||||||
*/
|
*/
|
||||||
protected $imageTransparencyBG = [255, 255, 255];
|
protected array $imageTransparencyBG = [255, 255, 255];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see imagepng()
|
* @see imagepng()
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $pngCompression = -1;
|
protected int $pngCompression = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see imagejpeg()
|
* @see imagejpeg()
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
*/
|
||||||
protected $jpegQuality = 85;
|
protected int $jpegQuality = 85;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imagick output format
|
* Imagick output format
|
||||||
*
|
*
|
||||||
* @see Imagick::setType()
|
* @see \Imagick::setType()
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
*/
|
||||||
protected $imagickFormat = 'png';
|
protected string $imagickFormat = 'png';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imagick background color (defaults to "transparent")
|
* Imagick background color (defaults to "transparent")
|
||||||
*
|
*
|
||||||
* @see \ImagickPixel::__construct()
|
* @see \ImagickPixel::__construct()
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
*/
|
||||||
protected $imagickBG = null;
|
protected ?string $imagickBG = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measurement unit for FPDF output: pt, mm, cm, in (defaults to "pt")
|
* Measurement unit for FPDF output: pt, mm, cm, in (defaults to "pt")
|
||||||
*
|
*
|
||||||
* @see \FPDF::__construct()
|
* @see \FPDF::__construct()
|
||||||
*/
|
*/
|
||||||
protected $fpdfMeasureUnit = 'pt';
|
protected string $fpdfMeasureUnit = 'pt';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module values map
|
* Module values map
|
||||||
*
|
*
|
||||||
* HTML, IMAGICK: #ABCDEF, cssname, rgb(), rgba()...
|
* - HTML, IMAGICK: #ABCDEF, cssname, rgb(), rgba()...
|
||||||
* IMAGE: [63, 127, 255] // R, G, B
|
* - IMAGE: [63, 127, 255] // R, G, B
|
||||||
*
|
|
||||||
* @var array|null
|
|
||||||
*/
|
*/
|
||||||
protected $moduleValues = null;
|
protected ?array $moduleValues = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clamp min/max version number
|
* clamp min/max version number
|
||||||
*
|
|
||||||
* @param int $versionMin
|
|
||||||
* @param int $versionMax
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function setMinMaxVersion(int $versionMin, int $versionMax):void{
|
protected function setMinMaxVersion(int $versionMin, int $versionMax):void{
|
||||||
$min = max(1, min(40, $versionMin));
|
$min = max(1, min(40, $versionMin));
|
||||||
|
@ -292,10 +242,6 @@ trait QROptionsTrait{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the minimum version number
|
* sets the minimum version number
|
||||||
*
|
|
||||||
* @param int $version
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function set_versionMin(int $version):void{
|
protected function set_versionMin(int $version):void{
|
||||||
$this->setMinMaxVersion($version, $this->versionMax);
|
$this->setMinMaxVersion($version, $this->versionMax);
|
||||||
|
@ -303,10 +249,6 @@ trait QROptionsTrait{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the maximum version number
|
* sets the maximum version number
|
||||||
*
|
|
||||||
* @param int $version
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function set_versionMax(int $version):void{
|
protected function set_versionMax(int $version):void{
|
||||||
$this->setMinMaxVersion($this->versionMin, $version);
|
$this->setMinMaxVersion($this->versionMin, $version);
|
||||||
|
@ -315,9 +257,6 @@ trait QROptionsTrait{
|
||||||
/**
|
/**
|
||||||
* sets the error correction level
|
* sets the error correction level
|
||||||
*
|
*
|
||||||
* @param int $eccLevel
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws \chillerlan\QRCode\QRCodeException
|
* @throws \chillerlan\QRCode\QRCodeException
|
||||||
*/
|
*/
|
||||||
protected function set_eccLevel(int $eccLevel):void{
|
protected function set_eccLevel(int $eccLevel):void{
|
||||||
|
@ -331,10 +270,6 @@ trait QROptionsTrait{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets/clamps the mask pattern
|
* sets/clamps the mask pattern
|
||||||
*
|
|
||||||
* @param int $maskPattern
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function set_maskPattern(int $maskPattern):void{
|
protected function set_maskPattern(int $maskPattern):void{
|
||||||
|
|
||||||
|
@ -347,15 +282,12 @@ trait QROptionsTrait{
|
||||||
/**
|
/**
|
||||||
* sets the transparency background color
|
* sets the transparency background color
|
||||||
*
|
*
|
||||||
* @param mixed $imageTransparencyBG
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
* @throws \chillerlan\QRCode\QRCodeException
|
* @throws \chillerlan\QRCode\QRCodeException
|
||||||
*/
|
*/
|
||||||
protected function set_imageTransparencyBG($imageTransparencyBG):void{
|
protected function set_imageTransparencyBG(array $imageTransparencyBG):void{
|
||||||
|
|
||||||
// invalid value - set to white as default
|
// invalid value - set to white as default
|
||||||
if(!is_array($imageTransparencyBG) || count($imageTransparencyBG) < 3){
|
if(count($imageTransparencyBG) < 3){
|
||||||
$this->imageTransparencyBG = [255, 255, 255];
|
$this->imageTransparencyBG = [255, 255, 255];
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -363,6 +295,11 @@ trait QROptionsTrait{
|
||||||
|
|
||||||
foreach($imageTransparencyBG as $k => $v){
|
foreach($imageTransparencyBG as $k => $v){
|
||||||
|
|
||||||
|
// cut off exceeding items
|
||||||
|
if($k > 2){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(!is_numeric($v)){
|
if(!is_numeric($v)){
|
||||||
throw new QRCodeException('Invalid RGB value.');
|
throw new QRCodeException('Invalid RGB value.');
|
||||||
}
|
}
|
||||||
|
@ -377,10 +314,6 @@ trait QROptionsTrait{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets/clamps the version number
|
* sets/clamps the version number
|
||||||
*
|
|
||||||
* @param int $version
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function set_version(int $version):void{
|
protected function set_version(int $version):void{
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,19 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\Data\{AlphaNum, QRCodeDataException};
|
use chillerlan\QRCode\Data\{AlphaNum, QRCodeDataException, QRDataInterface};
|
||||||
|
use chillerlan\QRCode\QROptions;
|
||||||
|
|
||||||
class AlphaNumTest extends DatainterfaceTestAbstract{
|
/**
|
||||||
|
* Tests the AlphaNum class
|
||||||
|
*/
|
||||||
|
final class AlphaNumTest extends DatainterfaceTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = AlphaNum::class;
|
/** @internal */
|
||||||
protected $testdata = '0 $%*+-./:';
|
protected string $testdata = '0 $%*+-./:';
|
||||||
protected $expected = [
|
|
||||||
|
/** @internal */
|
||||||
|
protected array $expected = [
|
||||||
32, 80, 36, 212, 252, 15, 175, 251,
|
32, 80, 36, 212, 252, 15, 175, 251,
|
||||||
176, 236, 17, 236, 17, 236, 17, 236,
|
176, 236, 17, 236, 17, 236, 17, 236,
|
||||||
17, 236, 17, 236, 17, 236, 17, 236,
|
17, 236, 17, 236, 17, 236, 17, 236,
|
||||||
|
@ -34,7 +40,18 @@ class AlphaNumTest extends DatainterfaceTestAbstract{
|
||||||
92, 112, 20, 198, 27
|
92, 112, 20, 198, 27
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testGetCharCodeException(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getDataInterfaceInstance(QROptions $options):QRDataInterface{
|
||||||
|
return new AlphaNum($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid character is encountered
|
||||||
|
*/
|
||||||
|
public function testGetCharCodeException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('illegal char: "#" [35]');
|
$this->expectExceptionMessage('illegal char: "#" [35]');
|
||||||
|
|
||||||
|
|
|
@ -13,12 +13,19 @@
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\Data\Byte;
|
use chillerlan\QRCode\Data\Byte;
|
||||||
|
use chillerlan\QRCode\Data\QRDataInterface;
|
||||||
|
use chillerlan\QRCode\QROptions;
|
||||||
|
|
||||||
class ByteTest extends DatainterfaceTestAbstract{
|
/**
|
||||||
|
* Tests the Byte class
|
||||||
|
*/
|
||||||
|
final class ByteTest extends DatainterfaceTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = Byte::class;
|
/** @internal */
|
||||||
protected $testdata = '[¯\_(ツ)_/¯]';
|
protected string $testdata = '[¯\_(ツ)_/¯]';
|
||||||
protected $expected = [
|
|
||||||
|
/** @internal */
|
||||||
|
protected array $expected = [
|
||||||
64, 245, 188, 42, 245, 197, 242, 142,
|
64, 245, 188, 42, 245, 197, 242, 142,
|
||||||
56, 56, 66, 149, 242, 252, 42, 245,
|
56, 56, 66, 149, 242, 252, 42, 245,
|
||||||
208, 236, 17, 236, 17, 236, 17, 236,
|
208, 236, 17, 236, 17, 236, 17, 236,
|
||||||
|
@ -34,5 +41,12 @@ class ByteTest extends DatainterfaceTestAbstract{
|
||||||
21, 47, 250, 101
|
21, 47, 250, 101
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getDataInterfaceInstance(QROptions $options):QRDataInterface{
|
||||||
|
return new Byte($options);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,54 +12,116 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
|
use chillerlan\QRCode\QRCode;
|
||||||
use chillerlan\QRCode\QROptions;
|
use chillerlan\QRCode\QROptions;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
use chillerlan\QRCode\Data\{QRCodeDataException, QRDataInterface, QRMatrix};
|
use chillerlan\QRCode\Data\{QRCodeDataException, QRDataInterface, QRMatrix};
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use ReflectionClass;
|
||||||
|
|
||||||
abstract class DatainterfaceTestAbstract extends QRTestAbstract{
|
use function str_repeat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data interface test abstract
|
||||||
|
*/
|
||||||
|
abstract class DatainterfaceTestAbstract extends TestCase{
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
protected ReflectionClass $reflection;
|
||||||
|
/** @internal */
|
||||||
|
protected QRDataInterface $dataInterface;
|
||||||
|
/** @internal */
|
||||||
|
protected string $testdata;
|
||||||
|
/** @internal */
|
||||||
|
protected array $expected;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\Data\QRDataAbstract
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected $dataInterface;
|
|
||||||
|
|
||||||
protected $testdata;
|
|
||||||
protected $expected;
|
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
parent::setUp();
|
$this->dataInterface = $this->getDataInterfaceInstance(new QROptions(['version' => 4]));
|
||||||
|
$this->reflection = new ReflectionClass($this->dataInterface);
|
||||||
$this->dataInterface = $this->reflection->newInstanceArgs([new QROptions(['version' => 4])]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInstance(){
|
/**
|
||||||
$this->dataInterface = $this->reflection->newInstanceArgs([new QROptions, $this->testdata]);
|
* Returns a data interface instance
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract protected function getDataInterfaceInstance(QROptions $options):QRDataInterface;
|
||||||
|
|
||||||
$this->assertInstanceOf(QRDataInterface::class, $this->dataInterface);
|
/**
|
||||||
|
* Verifies the data interface instance
|
||||||
|
*/
|
||||||
|
public function testInstance():void{
|
||||||
|
$this::assertInstanceOf(QRDataInterface::class, $this->dataInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetData(){
|
/**
|
||||||
|
* Tests ecc masking and verifies against a sample
|
||||||
|
*/
|
||||||
|
public function testMaskEcc():void{
|
||||||
$this->dataInterface->setData($this->testdata);
|
$this->dataInterface->setData($this->testdata);
|
||||||
|
|
||||||
$this->assertSame($this->expected, $this->getProperty('matrixdata')->getValue($this->dataInterface));
|
$maskECC = $this->reflection->getMethod('maskECC');
|
||||||
|
$maskECC->setAccessible(true);
|
||||||
|
|
||||||
|
$this::assertSame($this->expected, $maskECC->invoke($this->dataInterface));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInitMatrix(){
|
/**
|
||||||
$m = $this->dataInterface->setData($this->testdata)->initMatrix(0);
|
* @see testInitMatrix()
|
||||||
|
* @internal
|
||||||
$this->assertInstanceOf(QRMatrix::class, $m);
|
* @return int[][]
|
||||||
|
*/
|
||||||
|
public function MaskPatternProvider():array{
|
||||||
|
return [[0], [1], [2], [3], [4], [5], [6], [7]];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMinimumVersion(){
|
/**
|
||||||
$this->assertSame(1, $this->getMethod('getMinimumVersion')->invoke($this->dataInterface));
|
* Tests initializing the data matrix
|
||||||
|
*
|
||||||
|
* @dataProvider MaskPatternProvider
|
||||||
|
*/
|
||||||
|
public function testInitMatrix(int $maskPattern):void{
|
||||||
|
$this->dataInterface->setData($this->testdata);
|
||||||
|
|
||||||
|
$matrix = $this->dataInterface->initMatrix($maskPattern);
|
||||||
|
|
||||||
|
$this::assertInstanceOf(QRMatrix::class, $matrix);
|
||||||
|
$this::assertSame($maskPattern, $matrix->maskPattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMinimumVersionException(){
|
/**
|
||||||
|
* Tests getting the minimum QR version for the given data
|
||||||
|
*/
|
||||||
|
public function testGetMinimumVersion():void{
|
||||||
|
$this->dataInterface->setData($this->testdata);
|
||||||
|
|
||||||
|
$getMinimumVersion = $this->reflection->getMethod('getMinimumVersion');
|
||||||
|
$getMinimumVersion->setAccessible(true);
|
||||||
|
|
||||||
|
$this::assertSame(1, $getMinimumVersion->invoke($this->dataInterface));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when the data exceeds the maximum version while auto detecting
|
||||||
|
*/
|
||||||
|
public function testGetMinimumVersionException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('data exceeds');
|
$this->expectExceptionMessage('data exceeds');
|
||||||
|
|
||||||
$this->getProperty('strlen')->setValue($this->dataInterface, 13370);
|
$this->dataInterface = $this->getDataInterfaceInstance(new QROptions(['version' => QRCode::VERSION_AUTO]));
|
||||||
$this->getMethod('getMinimumVersion')->invoke($this->dataInterface);
|
$this->dataInterface->setData(str_repeat($this->testdata, 1337));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown on data overflow
|
||||||
|
*/
|
||||||
|
public function testCodeLengthOverflowException():void{
|
||||||
|
$this->expectException(QRCodeDataException::class);
|
||||||
|
$this->expectExceptionMessage('code length overflow');
|
||||||
|
|
||||||
|
$this->dataInterface->setData(str_repeat($this->testdata, 1337));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,19 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\Data\{Kanji, QRCodeDataException};
|
use chillerlan\QRCode\QROptions;
|
||||||
|
use chillerlan\QRCode\Data\{Kanji, QRCodeDataException, QRDataInterface};
|
||||||
|
|
||||||
class KanjiTest extends DatainterfaceTestAbstract{
|
/**
|
||||||
|
* Tests the Kanji class
|
||||||
|
*/
|
||||||
|
final class KanjiTest extends DatainterfaceTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = Kanji::class;
|
/** @internal */
|
||||||
protected $testdata = '茗荷茗荷茗荷茗荷茗荷';
|
protected string $testdata = '茗荷茗荷茗荷茗荷茗荷';
|
||||||
protected $expected = [
|
|
||||||
|
/** @internal */
|
||||||
|
protected array $expected = [
|
||||||
128, 173, 85, 26, 95, 85, 70, 151,
|
128, 173, 85, 26, 95, 85, 70, 151,
|
||||||
213, 81, 165, 245, 84, 105, 125, 85,
|
213, 81, 165, 245, 84, 105, 125, 85,
|
||||||
26, 92, 0, 236, 17, 236, 17, 236,
|
26, 92, 0, 236, 17, 236, 17, 236,
|
||||||
|
@ -34,17 +40,32 @@ class KanjiTest extends DatainterfaceTestAbstract{
|
||||||
96, 113, 54, 191
|
96, 113, 54, 191
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testIllegalCharException1(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getDataInterfaceInstance(QROptions $options):QRDataInterface{
|
||||||
|
return new Kanji($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid character is encountered
|
||||||
|
*/
|
||||||
|
public function testIllegalCharException1():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('illegal char at 1 [16191]');
|
$this->expectExceptionMessage('illegal char at 1 [16191]');
|
||||||
|
|
||||||
$this->dataInterface->setData('ÃÃ');
|
$this->dataInterface->setData('ÃÃ');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIllegalCharException2(){
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid character is encountered
|
||||||
|
*/
|
||||||
|
public function testIllegalCharException2():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('illegal char at 1');
|
$this->expectExceptionMessage('illegal char at 1');
|
||||||
|
|
||||||
$this->dataInterface->setData('Ã');
|
$this->dataInterface->setData('Ã');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,18 +12,31 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QROptions, Data\Byte, Data\MaskPatternTester};
|
use chillerlan\QRCode\QROptions;
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use chillerlan\QRCode\Data\{Byte, MaskPatternTester};
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class MaskPatternTesterTest extends QRTestAbstract{
|
/**
|
||||||
|
* MaskPatternTester coverage test
|
||||||
|
*/
|
||||||
|
final class MaskPatternTesterTest extends TestCase{
|
||||||
|
|
||||||
protected $FQCN = MaskPatternTester::class;
|
/**
|
||||||
|
* Tests getting the best mask pattern
|
||||||
|
*/
|
||||||
|
public function testMaskpattern():void{
|
||||||
|
$dataInterface = new Byte(new QROptions(['version' => 10]), 'test');
|
||||||
|
|
||||||
// coverage
|
$this::assertSame(3, (new MaskPatternTester($dataInterface))->getBestMaskPattern());
|
||||||
public function testMaskpattern(){
|
}
|
||||||
$matrix = (new Byte(new QROptions(['version' => 10]), 'test'))->initMatrix(3, true);
|
|
||||||
|
|
||||||
$this->assertSame(4243, (new MaskPatternTester($matrix))->testPattern());
|
/**
|
||||||
|
* Tests getting the penalty value for a given mask pattern
|
||||||
|
*/
|
||||||
|
public function testMaskpatternID():void{
|
||||||
|
$dataInterface = new Byte(new QROptions(['version' => 10]), 'test');
|
||||||
|
|
||||||
|
$this::assertSame(4243, (new MaskPatternTester($dataInterface))->testPattern(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,19 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Data;
|
namespace chillerlan\QRCodeTest\Data;
|
||||||
|
|
||||||
use chillerlan\QRCode\Data\{Number, QRCodeDataException};
|
use chillerlan\QRCode\QROptions;
|
||||||
|
use chillerlan\QRCode\Data\{Number, QRCodeDataException, QRDataInterface};
|
||||||
|
|
||||||
class NumberTest extends DatainterfaceTestAbstract{
|
/**
|
||||||
|
* Tests the Number class
|
||||||
|
*/
|
||||||
|
final class NumberTest extends DatainterfaceTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = Number::class;
|
/** @internal */
|
||||||
protected $testdata = '0123456789';
|
protected string $testdata = '0123456789';
|
||||||
protected $expected = [
|
|
||||||
|
/** @internal */
|
||||||
|
protected array $expected = [
|
||||||
16, 40, 12, 86, 106, 105, 0, 236,
|
16, 40, 12, 86, 106, 105, 0, 236,
|
||||||
17, 236, 17, 236, 17, 236, 17, 236,
|
17, 236, 17, 236, 17, 236, 17, 236,
|
||||||
17, 236, 17, 236, 17, 236, 17, 236,
|
17, 236, 17, 236, 17, 236, 17, 236,
|
||||||
|
@ -34,7 +40,18 @@ class NumberTest extends DatainterfaceTestAbstract{
|
||||||
89, 63, 168, 151
|
89, 63, 168, 151
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testGetCharCodeException(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getDataInterfaceInstance(QROptions $options):QRDataInterface{
|
||||||
|
return new Number($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid character is encountered
|
||||||
|
*/
|
||||||
|
public function testGetCharCodeException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('illegal char: "#" [35]');
|
$this->expectExceptionMessage('illegal char: "#" [35]');
|
||||||
|
|
||||||
|
|
|
@ -15,177 +15,294 @@ namespace chillerlan\QRCodeTest\Data;
|
||||||
use chillerlan\QRCode\QRCode;
|
use chillerlan\QRCode\QRCode;
|
||||||
use chillerlan\QRCode\QROptions;
|
use chillerlan\QRCode\QROptions;
|
||||||
use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
|
use chillerlan\QRCode\Data\{QRCodeDataException, QRMatrix};
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use PHPUnit\Framework\TestCase;
|
||||||
use ReflectionClass;
|
use ReflectionClass;
|
||||||
|
|
||||||
class QRMatrixTest extends QRTestAbstract{
|
/**
|
||||||
|
* Tests the QRMatix class
|
||||||
|
*/
|
||||||
|
final class QRMatrixTest extends TestCase{
|
||||||
|
|
||||||
protected $FQCN = QRMatrix::class;
|
/** @internal */
|
||||||
|
protected const version = 40;
|
||||||
protected $version = 7;
|
/** @internal */
|
||||||
|
protected QRMatrix $matrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\Data\QRMatrix
|
* invokes a QRMatrix object
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected $matrix;
|
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
parent::setUp();
|
$this->matrix = $this->getMatrix($this::version);
|
||||||
|
|
||||||
$this->matrix = $this->reflection->newInstanceArgs([$this->version, QRCode::ECC_L]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidVersionException(){
|
/**
|
||||||
|
* shortcut
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getMatrix(int $version):QRMatrix{
|
||||||
|
return new QRMatrix($version, QRCode::ECC_L);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the QRMatrix instance
|
||||||
|
*/
|
||||||
|
public function testInstance():void{
|
||||||
|
$this::assertInstanceOf(QRMatrix::class, $this->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid QR version was given
|
||||||
|
*/
|
||||||
|
public function testInvalidVersionException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('invalid QR Code version');
|
$this->expectExceptionMessage('invalid QR Code version');
|
||||||
|
|
||||||
$this->reflection->newInstanceArgs([42, 0]);
|
$this->matrix = new QRMatrix(42, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidEccException(){
|
/**
|
||||||
|
* Tests if an exception is thrown when an invalid ECC level was given
|
||||||
|
*/
|
||||||
|
public function testInvalidEccException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('invalid ecc level');
|
$this->expectExceptionMessage('invalid ecc level');
|
||||||
|
|
||||||
$this->reflection->newInstanceArgs([1, 42]);
|
$this->matrix = new QRMatrix(1, 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInstance(){
|
/**
|
||||||
$this->assertInstanceOf($this->FQCN, $this->matrix);
|
* Tests if size() returns the actual matrix size/count
|
||||||
|
*/
|
||||||
|
public function testSize():void{
|
||||||
|
$this::assertCount($this->matrix->size(), $this->matrix->matrix());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSize(){
|
/**
|
||||||
$this->assertCount($this->matrix->size(), $this->matrix->matrix());
|
* Tests if version() returns the current (given) version
|
||||||
|
*/
|
||||||
|
public function testVersion():void{
|
||||||
|
$this::assertSame($this::version, $this->matrix->version());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testVersion(){
|
/**
|
||||||
$this->assertSame($this->version, $this->matrix->version());
|
* Tests if eccLevel() returns the current (given) ECC level
|
||||||
|
*/
|
||||||
|
public function testECC():void{
|
||||||
|
$this::assertSame(QRCode::ECC_L, $this->matrix->eccLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testECC(){
|
/**
|
||||||
$this->assertSame(QRCode::ECC_L, $this->matrix->eccLevel());
|
* Tests if maskPattern() returns the current (or default) mask pattern
|
||||||
|
*/
|
||||||
|
public function testMaskPattern():void{
|
||||||
|
$this::assertSame(-1, $this->matrix->maskPattern()); // default
|
||||||
|
|
||||||
|
// @todo: actual mask pattern after mapData()
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMaskPattern(){
|
/**
|
||||||
$this->assertSame(-1, $this->matrix->maskPattern());
|
* Tests the set(), get() and check() methods
|
||||||
}
|
*/
|
||||||
|
public function testGetSetCheck():void{
|
||||||
public function testGetSetCheck(){
|
|
||||||
$this->matrix->set(10, 10, true, QRMatrix::M_TEST);
|
$this->matrix->set(10, 10, true, QRMatrix::M_TEST);
|
||||||
$this->assertSame(65280, $this->matrix->get(10, 10));
|
$this::assertSame(65280, $this->matrix->get(10, 10));
|
||||||
$this->assertTrue($this->matrix->check(10, 10));
|
$this::assertTrue($this->matrix->check(10, 10));
|
||||||
|
|
||||||
$this->matrix->set(20, 20, false, QRMatrix::M_TEST);
|
$this->matrix->set(20, 20, false, QRMatrix::M_TEST);
|
||||||
$this->assertSame(255, $this->matrix->get(20, 20));
|
$this::assertSame(255, $this->matrix->get(20, 20));
|
||||||
$this->assertFalse($this->matrix->check(20, 20));
|
$this::assertFalse($this->matrix->check(20, 20));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetDarkModule(){
|
/**
|
||||||
$this->matrix->setDarkModule();
|
* Version data provider for several pattern tests
|
||||||
|
*
|
||||||
|
* @return int[][]
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function versionProvider():array{
|
||||||
|
$versions = [];
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_DARKMODULE << 8, $this->matrix->get(8, $this->matrix->size() - 8));
|
for($i = 1; $i <= 40; $i++){
|
||||||
|
$versions[] = [$i];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetFinderPattern(){
|
return $versions;
|
||||||
$this->matrix->setFinderPattern();
|
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_FINDER << 8, $this->matrix->get(0, 0));
|
|
||||||
$this->assertSame(QRMatrix::M_FINDER << 8, $this->matrix->get(0, $this->matrix->size() - 1));
|
|
||||||
$this->assertSame(QRMatrix::M_FINDER << 8, $this->matrix->get($this->matrix->size() - 1, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetSeparators(){
|
/**
|
||||||
$this->matrix->setSeparators();
|
* Tests setting the dark module and verifies its position
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetDarkModule(int $version):void{
|
||||||
|
$matrix = $this->getMatrix($version)->setDarkModule();
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_SEPARATOR, $this->matrix->get(7, 0));
|
$this::assertSame(QRMatrix::M_DARKMODULE << 8, $matrix->get(8, $matrix->size() - 8));
|
||||||
$this->assertSame(QRMatrix::M_SEPARATOR, $this->matrix->get(0, 7));
|
|
||||||
$this->assertSame(QRMatrix::M_SEPARATOR, $this->matrix->get(0, $this->matrix->size() - 8));
|
|
||||||
$this->assertSame(QRMatrix::M_SEPARATOR, $this->matrix->get($this->matrix->size() - 8, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetAlignmentPattern(){
|
/**
|
||||||
$this->matrix
|
* Tests setting the finder patterns and verifies their positions
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetFinderPattern(int $version):void{
|
||||||
|
$matrix = $this->getMatrix($version)->setFinderPattern();
|
||||||
|
|
||||||
|
$this::assertSame(QRMatrix::M_FINDER << 8, $matrix->get(0, 0));
|
||||||
|
$this::assertSame(QRMatrix::M_FINDER << 8, $matrix->get(0, $matrix->size() - 1));
|
||||||
|
$this::assertSame(QRMatrix::M_FINDER << 8, $matrix->get($matrix->size() - 1, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the separator patterns and verifies their positions
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetSeparators(int $version):void{
|
||||||
|
$matrix = $this->getMatrix($version)->setSeparators();
|
||||||
|
|
||||||
|
$this::assertSame(QRMatrix::M_SEPARATOR, $matrix->get(7, 0));
|
||||||
|
$this::assertSame(QRMatrix::M_SEPARATOR, $matrix->get(0, 7));
|
||||||
|
$this::assertSame(QRMatrix::M_SEPARATOR, $matrix->get(0, $matrix->size() - 8));
|
||||||
|
$this::assertSame(QRMatrix::M_SEPARATOR, $matrix->get($matrix->size() - 8, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the alignment patterns and verifies their positions - version 1 (no pattern) skipped
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetAlignmentPattern(int $version):void{
|
||||||
|
|
||||||
|
if($version === 1){
|
||||||
|
$this->markTestSkipped('N/A');
|
||||||
|
|
||||||
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$matrix = $this
|
||||||
|
->getMatrix($version)
|
||||||
->setFinderPattern()
|
->setFinderPattern()
|
||||||
->setAlignmentPattern()
|
->setAlignmentPattern()
|
||||||
;
|
;
|
||||||
|
|
||||||
$alignmentPattern = (new ReflectionClass(QRMatrix::class))->getConstant('alignmentPattern')[$this->version];
|
$alignmentPattern = (new ReflectionClass(QRMatrix::class))->getConstant('alignmentPattern')[$version];
|
||||||
|
|
||||||
foreach($alignmentPattern as $py){
|
foreach($alignmentPattern as $py){
|
||||||
foreach($alignmentPattern as $px){
|
foreach($alignmentPattern as $px){
|
||||||
|
|
||||||
if($this->matrix->get($px, $py) === QRMatrix::M_FINDER << 8){
|
if($matrix->get($px, $py) === QRMatrix::M_FINDER << 8){
|
||||||
$this->assertSame(QRMatrix::M_FINDER << 8, $this->matrix->get($px, $py), 'skipped finder pattern');
|
$this::assertSame(QRMatrix::M_FINDER << 8, $matrix->get($px, $py), 'skipped finder pattern');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_ALIGNMENT << 8, $this->matrix->get($px, $py));
|
$this::assertSame(QRMatrix::M_ALIGNMENT << 8, $matrix->get($px, $py));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetTimingPattern(){
|
/**
|
||||||
$this->matrix
|
* Tests the timing patterns and verifies their positions
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetTimingPattern(int $version):void{
|
||||||
|
|
||||||
|
$matrix = $this
|
||||||
|
->getMatrix($version)
|
||||||
->setAlignmentPattern()
|
->setAlignmentPattern()
|
||||||
->setTimingPattern()
|
->setTimingPattern()
|
||||||
;
|
;
|
||||||
|
|
||||||
$size = $this->matrix->size();
|
$size = $matrix->size();
|
||||||
|
|
||||||
for($i = 7; $i < $size - 7; $i++){
|
for($i = 7; $i < $size - 7; $i++){
|
||||||
if($i % 2 === 0){
|
if($i % 2 === 0){
|
||||||
$p1 = $this->matrix->get(6, $i);
|
$p1 = $matrix->get(6, $i);
|
||||||
|
|
||||||
if($p1 === QRMatrix::M_ALIGNMENT << 8){
|
if($p1 === QRMatrix::M_ALIGNMENT << 8){
|
||||||
$this->assertSame(QRMatrix::M_ALIGNMENT << 8, $p1, 'skipped alignment pattern');
|
$this::assertSame(QRMatrix::M_ALIGNMENT << 8, $p1, 'skipped alignment pattern');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_TIMING << 8, $p1);
|
$this::assertSame(QRMatrix::M_TIMING << 8, $p1);
|
||||||
$this->assertSame(QRMatrix::M_TIMING << 8, $this->matrix->get($i, 6));
|
$this::assertSame(QRMatrix::M_TIMING << 8, $matrix->get($i, 6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetVersionNumber(){
|
/**
|
||||||
$this->matrix->setVersionNumber(true);
|
* Tests the version patterns and verifies their positions - version < 7 skipped
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetVersionNumber(int $version):void{
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_VERSION, $this->matrix->get($this->matrix->size() - 9, 0));
|
if($version < 7){
|
||||||
$this->assertSame(QRMatrix::M_VERSION, $this->matrix->get($this->matrix->size() - 11, 5));
|
$this->markTestSkipped('N/A');
|
||||||
$this->assertSame(QRMatrix::M_VERSION, $this->matrix->get(0, $this->matrix->size() - 9));
|
|
||||||
$this->assertSame(QRMatrix::M_VERSION, $this->matrix->get(5, $this->matrix->size() - 11));
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetFormatInfo(){
|
$matrix = $this->getMatrix($version)->setVersionNumber(true);
|
||||||
$this->matrix->setFormatInfo(0, true);
|
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_FORMAT, $this->matrix->get(8, 0));
|
$this::assertSame(QRMatrix::M_VERSION, $matrix->get($matrix->size() - 9, 0));
|
||||||
$this->assertSame(QRMatrix::M_FORMAT, $this->matrix->get(0, 8));
|
$this::assertSame(QRMatrix::M_VERSION, $matrix->get($matrix->size() - 11, 5));
|
||||||
$this->assertSame(QRMatrix::M_FORMAT, $this->matrix->get($this->matrix->size() - 1, 8));
|
$this::assertSame(QRMatrix::M_VERSION, $matrix->get(0, $matrix->size() - 9));
|
||||||
$this->assertSame(QRMatrix::M_FORMAT, $this->matrix->get($this->matrix->size() - 8, 8));
|
$this::assertSame(QRMatrix::M_VERSION, $matrix->get(5, $matrix->size() - 11));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetQuietZone(){
|
/**
|
||||||
$size = $this->matrix->size();
|
* Tests the format patterns and verifies their positions
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetFormatInfo(int $version):void{
|
||||||
|
$matrix = $this->getMatrix($version)->setFormatInfo(0, true);
|
||||||
|
|
||||||
|
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get(8, 0));
|
||||||
|
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get(0, 8));
|
||||||
|
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get($matrix->size() - 1, 8));
|
||||||
|
$this::assertSame(QRMatrix::M_FORMAT, $matrix->get($matrix->size() - 8, 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the quiet zone pattern and verifies its position
|
||||||
|
*
|
||||||
|
* @dataProvider versionProvider
|
||||||
|
*/
|
||||||
|
public function testSetQuietZone(int $version):void{
|
||||||
|
$matrix = $this->getMatrix($version);
|
||||||
|
|
||||||
|
$size = $matrix->size();
|
||||||
$q = 5;
|
$q = 5;
|
||||||
|
|
||||||
$this->matrix->set(0, 0, true, QRMatrix::M_TEST);
|
$matrix->set(0, 0, true, QRMatrix::M_TEST);
|
||||||
$this->matrix->set($size - 1, $size - 1, true, QRMatrix::M_TEST);
|
$matrix->set($size - 1, $size - 1, true, QRMatrix::M_TEST);
|
||||||
|
|
||||||
$this->matrix->setQuietZone($q);
|
$matrix->setQuietZone($q);
|
||||||
|
|
||||||
$this->assertCount($size + 2 * $q, $this->matrix->matrix());
|
$this::assertCount($size + 2 * $q, $matrix->matrix());
|
||||||
$this->assertCount($size + 2 * $q, $this->matrix->matrix()[$size - 1]);
|
$this::assertCount($size + 2 * $q, $matrix->matrix()[$size - 1]);
|
||||||
|
|
||||||
$size = $this->matrix->size();
|
$size = $matrix->size();
|
||||||
$this->assertSame(QRMatrix::M_QUIETZONE, $this->matrix->get(0, 0));
|
$this::assertSame(QRMatrix::M_QUIETZONE, $matrix->get(0, 0));
|
||||||
$this->assertSame(QRMatrix::M_QUIETZONE, $this->matrix->get($size - 1, $size - 1));
|
$this::assertSame(QRMatrix::M_QUIETZONE, $matrix->get($size - 1, $size - 1));
|
||||||
|
|
||||||
$this->assertSame(QRMatrix::M_TEST << 8, $this->matrix->get($q, $q));
|
$this::assertSame(QRMatrix::M_TEST << 8, $matrix->get($q, $q));
|
||||||
$this->assertSame(QRMatrix::M_TEST << 8, $this->matrix->get($size - 1 - $q, $size - 1 - $q));
|
$this::assertSame(QRMatrix::M_TEST << 8, $matrix->get($size - 1 - $q, $size - 1 - $q));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetQuietZoneException(){
|
/**
|
||||||
|
* Tests if an exception is thrown in an attempt to create it before data was written
|
||||||
|
*/
|
||||||
|
public function testSetQuietZoneException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('use only after writing data');
|
$this->expectExceptionMessage('use only after writing data');
|
||||||
|
|
||||||
|
|
|
@ -12,21 +12,22 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Helpers;
|
namespace chillerlan\QRCodeTest\Helpers;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QRCode, Helpers\BitBuffer};
|
use chillerlan\QRCode\QRCode;
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use chillerlan\QRCode\Helpers\BitBuffer;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class BitBufferTest extends QRTestAbstract{
|
/**
|
||||||
|
* BitBuffer coverage test
|
||||||
/**
|
|
||||||
* @var \chillerlan\QRCode\Helpers\BitBuffer
|
|
||||||
*/
|
*/
|
||||||
protected $bitBuffer;
|
final class BitBufferTest extends TestCase{
|
||||||
|
|
||||||
|
protected BitBuffer $bitBuffer;
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
$this->bitBuffer = new BitBuffer;
|
$this->bitBuffer = new BitBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function bitProvider(){
|
public function bitProvider():array{
|
||||||
return [
|
return [
|
||||||
'number' => [QRCode::DATA_NUMBER, 16],
|
'number' => [QRCode::DATA_NUMBER, 16],
|
||||||
'alphanum' => [QRCode::DATA_ALPHANUM, 32],
|
'alphanum' => [QRCode::DATA_ALPHANUM, 32],
|
||||||
|
@ -38,16 +39,16 @@ class BitBufferTest extends QRTestAbstract{
|
||||||
/**
|
/**
|
||||||
* @dataProvider bitProvider
|
* @dataProvider bitProvider
|
||||||
*/
|
*/
|
||||||
public function testPut($data, $value){
|
public function testPut(int $data, int $value):void{
|
||||||
$this->bitBuffer->put($data, 4);
|
$this->bitBuffer->put($data, 4);
|
||||||
$this->assertSame($value, $this->bitBuffer->buffer[0]);
|
$this::assertSame($value, $this->bitBuffer->getBuffer()[0]);
|
||||||
$this->assertSame(4, $this->bitBuffer->length);
|
$this::assertSame(4, $this->bitBuffer->getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClear(){
|
public function testClear():void{
|
||||||
$this->bitBuffer->clear();
|
$this->bitBuffer->clear();
|
||||||
$this->assertSame([], $this->bitBuffer->buffer);
|
$this::assertSame([], $this->bitBuffer->getBuffer());
|
||||||
$this->assertSame(0, $this->bitBuffer->length);
|
$this::assertSame(0, $this->bitBuffer->getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,26 +14,26 @@ namespace chillerlan\QRCodeTest\Helpers;
|
||||||
|
|
||||||
use chillerlan\QRCode\Helpers\Polynomial;
|
use chillerlan\QRCode\Helpers\Polynomial;
|
||||||
use chillerlan\QRCode\QRCodeException;
|
use chillerlan\QRCode\QRCodeException;
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class PolynomialTest extends QRTestAbstract{
|
/**
|
||||||
|
* Polynomial coverage test
|
||||||
/**
|
|
||||||
* @var \chillerlan\QRCode\Helpers\Polynomial
|
|
||||||
*/
|
*/
|
||||||
protected $polynomial;
|
final class PolynomialTest extends TestCase{
|
||||||
|
|
||||||
|
protected Polynomial $polynomial;
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
$this->polynomial = new Polynomial;
|
$this->polynomial = new Polynomial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGexp(){
|
public function testGexp():void{
|
||||||
$this->assertSame(142, $this->polynomial->gexp(-1));
|
$this::assertSame(142, $this->polynomial->gexp(-1));
|
||||||
$this->assertSame(133, $this->polynomial->gexp(128));
|
$this::assertSame(133, $this->polynomial->gexp(128));
|
||||||
$this->assertSame(2, $this->polynomial->gexp(256));
|
$this::assertSame(2, $this->polynomial->gexp(256));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGlogException(){
|
public function testGlogException():void{
|
||||||
$this->expectException(QRCodeException::class);
|
$this->expectException(QRCodeException::class);
|
||||||
$this->expectExceptionMessage('log(0)');
|
$this->expectExceptionMessage('log(0)');
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,6 @@ use function class_exists, substr;
|
||||||
*/
|
*/
|
||||||
class QRFpdfTest extends QROutputTestAbstract{
|
class QRFpdfTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = QRFpdf::class;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
* @internal
|
* @internal
|
||||||
|
@ -33,12 +31,32 @@ class QRFpdfTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
if(!class_exists(FPDF::class)){
|
if(!class_exists(FPDF::class)){
|
||||||
$this->markTestSkipped('FPDF not available');
|
$this->markTestSkipped('FPDF not available');
|
||||||
|
|
||||||
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getOutputInterface(QROptions $options):QROutputInterface{
|
||||||
|
return new QRFpdf($options, $this->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function types():array{
|
||||||
|
return [
|
||||||
|
'fpdf' => [QRCode::OUTPUT_FPDF],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +68,7 @@ class QRFpdfTest extends QROutputTestAbstract{
|
||||||
4 => [255, 255, 255],
|
4 => [255, 255, 255],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$this->outputInterface->dump();
|
$this->outputInterface->dump();
|
||||||
|
|
||||||
$this::assertTrue(true); // tricking the code coverage
|
$this::assertTrue(true); // tricking the code coverage
|
||||||
|
@ -57,25 +76,22 @@ class QRFpdfTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
|
* @dataProvider types
|
||||||
*/
|
*/
|
||||||
public function testRenderImage():void{
|
public function testRenderImage(string $type):void{
|
||||||
$type = QRCode::OUTPUT_FPDF;
|
|
||||||
|
|
||||||
$this->options->outputType = $type;
|
$this->options->outputType = $type;
|
||||||
$this->options->imageBase64 = false;
|
$this->options->imageBase64 = false;
|
||||||
$this->outputInterface->dump($this::cachefile.$type);
|
|
||||||
|
|
||||||
// substr() to avoid CreationDate
|
// substr() to avoid CreationDate
|
||||||
$expected = substr(file_get_contents($this::cachefile.$type), 0, 2000);
|
$expected = substr(file_get_contents(__DIR__.'/samples/'.$type), 0, 2500);
|
||||||
$actual = substr($this->outputInterface->dump(), 0, 2000);
|
$actual = substr((new QRCode($this->options))->render('test'), 0, 2500);
|
||||||
|
|
||||||
$this::assertSame($expected, $actual);
|
$this::assertSame($expected, $actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOutputGetResource():void{
|
public function testOutputGetResource():void{
|
||||||
$this->options->returnResource = true;
|
$this->options->returnResource = true;
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$this->setOutputInterface();
|
|
||||||
|
|
||||||
$this::assertInstanceOf(FPDF::class, $this->outputInterface->dump());
|
$this::assertInstanceOf(FPDF::class, $this->outputInterface->dump());
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,41 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Output;
|
namespace chillerlan\QRCodeTest\Output;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QRCode, Output\QRImage};
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
use const PHP_MAJOR_VERSION;
|
use chillerlan\QRCode\Output\{QROutputInterface, QRImage};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the QRImage output module
|
||||||
|
*/
|
||||||
class QRImageTest extends QROutputTestAbstract{
|
class QRImageTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = QRImage::class;
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setUp():void{
|
||||||
|
|
||||||
public function types(){
|
if(!extension_loaded('gd')){
|
||||||
|
$this->markTestSkipped('ext-gd not loaded');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getOutputInterface(QROptions $options):QROutputInterface{
|
||||||
|
return new QRImage($options, $this->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function types():array{
|
||||||
return [
|
return [
|
||||||
'png' => [QRCode::OUTPUT_IMAGE_PNG],
|
'png' => [QRCode::OUTPUT_IMAGE_PNG],
|
||||||
'gif' => [QRCode::OUTPUT_IMAGE_GIF],
|
'gif' => [QRCode::OUTPUT_IMAGE_GIF],
|
||||||
|
@ -28,25 +55,9 @@ class QRImageTest extends QROutputTestAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider types
|
* @inheritDoc
|
||||||
* @param $type
|
|
||||||
*/
|
*/
|
||||||
public function testImageOutput($type){
|
public function testSetModuleValues():void{
|
||||||
$this->options->outputType = $type;
|
|
||||||
$this->options->imageBase64 = false;
|
|
||||||
|
|
||||||
$this->setOutputInterface();
|
|
||||||
$this->outputInterface->dump($this::cachefile.$type);
|
|
||||||
$img = $this->outputInterface->dump();
|
|
||||||
|
|
||||||
if($type === QRCode::OUTPUT_IMAGE_JPG){ // jpeg encoding may cause different results
|
|
||||||
$this->markAsRisky();
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertSame($img, file_get_contents($this::cachefile.$type));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetModuleValues(){
|
|
||||||
|
|
||||||
$this->options->moduleValues = [
|
$this->options->moduleValues = [
|
||||||
// data
|
// data
|
||||||
|
@ -54,24 +65,25 @@ class QRImageTest extends QROutputTestAbstract{
|
||||||
4 => [255, 255, 255],
|
4 => [255, 255, 255],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->setOutputInterface()->dump();
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
|
$this->outputInterface->dump();
|
||||||
|
|
||||||
$this->assertTrue(true); // tricking the code coverage
|
$this::assertTrue(true); // tricking the code coverage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phan-suppress PhanUndeclaredClassReference
|
||||||
|
*/
|
||||||
public function testOutputGetResource():void{
|
public function testOutputGetResource():void{
|
||||||
$this->options->returnResource = true;
|
$this->options->returnResource = true;
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
|
|
||||||
$this->setOutputInterface();
|
$actual = $this->outputInterface->dump();
|
||||||
|
|
||||||
$data = $this->outputInterface->dump();
|
/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */
|
||||||
|
\PHP_MAJOR_VERSION >= 8
|
||||||
if(PHP_MAJOR_VERSION >= 8){
|
? $this::assertInstanceOf(\GdImage::class, $actual)
|
||||||
$this::assertInstanceOf('\\GdImage', $data);
|
: $this::assertIsResource($actual);
|
||||||
}
|
|
||||||
else{
|
|
||||||
$this::assertIsResource($data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,40 +9,59 @@
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*
|
*
|
||||||
|
* @noinspection PhpUndefinedClassInspection
|
||||||
* @noinspection PhpComposerExtensionStubsInspection
|
* @noinspection PhpComposerExtensionStubsInspection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Output;
|
namespace chillerlan\QRCodeTest\Output;
|
||||||
|
|
||||||
use Imagick;
|
use Imagick;
|
||||||
use chillerlan\QRCode\{QRCode, Output\QRImagick};
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
|
use chillerlan\QRCode\Output\{QROutputInterface, QRImagick};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the QRImagick output module
|
||||||
|
*/
|
||||||
class QRImagickTest extends QROutputTestAbstract{
|
class QRImagickTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = QRImagick::class;
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
public function setUp():void{
|
public function setUp():void{
|
||||||
|
|
||||||
if(!extension_loaded('imagick')){
|
if(!extension_loaded('imagick')){
|
||||||
$this->markTestSkipped('ext-imagick not loaded');
|
$this->markTestSkipped('ext-imagick not loaded');
|
||||||
|
|
||||||
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImageOutput(){
|
/**
|
||||||
$type = QRCode::OUTPUT_IMAGICK;
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
$this->options->outputType = $type;
|
*/
|
||||||
$this->setOutputInterface();
|
protected function getOutputInterface(QROptions $options):QROutputInterface{
|
||||||
$this->outputInterface->dump($this::cachefile.$type);
|
return new QRImagick($options, $this->matrix);
|
||||||
$img = $this->outputInterface->dump();
|
|
||||||
|
|
||||||
$this->assertSame($img, file_get_contents($this::cachefile.$type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetModuleValues(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function types():array{
|
||||||
|
return [
|
||||||
|
'imagick' => [QRCode::OUTPUT_IMAGICK],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function testSetModuleValues():void{
|
||||||
|
|
||||||
$this->options->moduleValues = [
|
$this->options->moduleValues = [
|
||||||
// data
|
// data
|
||||||
|
@ -50,17 +69,18 @@ class QRImagickTest extends QROutputTestAbstract{
|
||||||
4 => '#ECF9BE',
|
4 => '#ECF9BE',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->setOutputInterface()->dump();
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
|
$this->outputInterface->dump();
|
||||||
|
|
||||||
$this->assertTrue(true); // tricking the code coverage
|
$this::assertTrue(true); // tricking the code coverage
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOutputGetResource():void{
|
public function testOutputGetResource():void{
|
||||||
$this->options->returnResource = true;
|
$this->options->returnResource = true;
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$this->setOutputInterface();
|
|
||||||
|
|
||||||
$this::assertInstanceOf(Imagick::class, $this->outputInterface->dump());
|
$this::assertInstanceOf(Imagick::class, $this->outputInterface->dump());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,27 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Output;
|
namespace chillerlan\QRCodeTest\Output;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QRCode, Output\QRMarkup};
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
|
use chillerlan\QRCode\Output\{QROutputInterface, QRMarkup};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the QRMarkup output module
|
||||||
|
*/
|
||||||
class QRMarkupTest extends QROutputTestAbstract{
|
class QRMarkupTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = QRMarkup::class;
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getOutputInterface(QROptions $options):QROutputInterface{
|
||||||
|
return new QRMarkup($options, $this->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
public function types(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function types():array{
|
||||||
return [
|
return [
|
||||||
'html' => [QRCode::OUTPUT_MARKUP_HTML],
|
'html' => [QRCode::OUTPUT_MARKUP_HTML],
|
||||||
'svg' => [QRCode::OUTPUT_MARKUP_SVG],
|
'svg' => [QRCode::OUTPUT_MARKUP_SVG],
|
||||||
|
@ -26,43 +40,9 @@ class QRMarkupTest extends QROutputTestAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider types
|
* @inheritDoc
|
||||||
* @param $type
|
|
||||||
*/
|
*/
|
||||||
public function testMarkupOutputFile($type){
|
public function testSetModuleValues():void{
|
||||||
$this->options->outputType = $type;
|
|
||||||
$this->options->cachefile = $this::cachefile.$type;
|
|
||||||
$this->setOutputInterface();
|
|
||||||
$data = $this->outputInterface->dump();
|
|
||||||
|
|
||||||
$this->assertSame($data, file_get_contents($this->options->cachefile));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider types
|
|
||||||
* @param $type
|
|
||||||
*/
|
|
||||||
public function testMarkupOutput($type){
|
|
||||||
$this->options->imageBase64 = false;
|
|
||||||
$this->options->outputType = $type;
|
|
||||||
$this->setOutputInterface();
|
|
||||||
|
|
||||||
$expected = explode($this->options->eol, file_get_contents($this::cachefile.$type));
|
|
||||||
// cut off the doctype & head
|
|
||||||
array_shift($expected);
|
|
||||||
|
|
||||||
if($type === QRCode::OUTPUT_MARKUP_HTML){
|
|
||||||
// cut off the </body> tag
|
|
||||||
array_pop($expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
$expected = implode($this->options->eol, $expected);
|
|
||||||
|
|
||||||
$this->assertSame(trim($expected), trim($this->outputInterface->dump()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetModuleValues(){
|
|
||||||
|
|
||||||
$this->options->imageBase64 = false;
|
$this->options->imageBase64 = false;
|
||||||
$this->options->moduleValues = [
|
$this->options->moduleValues = [
|
||||||
// data
|
// data
|
||||||
|
@ -70,10 +50,10 @@ class QRMarkupTest extends QROutputTestAbstract{
|
||||||
4 => '#ECF9BE',
|
4 => '#ECF9BE',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->setOutputInterface();
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$data = $this->outputInterface->dump();
|
$data = $this->outputInterface->dump();
|
||||||
$this->assertStringContainsString('#4A6000', $data);
|
$this::assertStringContainsString('#4A6000', $data);
|
||||||
$this->assertStringContainsString('#ECF9BE', $data);
|
$this::assertStringContainsString('#ECF9BE', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,60 +12,129 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Output;
|
namespace chillerlan\QRCodeTest\Output;
|
||||||
|
|
||||||
use chillerlan\QRCode\QROptions;
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
use chillerlan\QRCode\Data\Byte;
|
use chillerlan\QRCode\Data\{Byte, QRMatrix};
|
||||||
use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
|
use chillerlan\QRCode\Output\{QRCodeOutputException, QROutputInterface};
|
||||||
use chillerlan\QRCodeTest\QRTestAbstract;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
use function dirname, file_exists, mkdir;
|
use function file_exists, in_array, mkdir;
|
||||||
|
|
||||||
abstract class QROutputTestAbstract extends QRTestAbstract{
|
use const PHP_OS_FAMILY, PHP_VERSION_ID;
|
||||||
|
|
||||||
const cachefile = __DIR__.'/../../.build/output_test/test.';
|
/**
|
||||||
|
* Test abstract for the several (built-in) output modules,
|
||||||
|
* should also be used to test custom output modules
|
||||||
|
*/
|
||||||
|
abstract class QROutputTestAbstract extends TestCase{
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
protected string $builddir = __DIR__.'/../../.build/output_test';
|
||||||
|
/** @internal */
|
||||||
|
protected QROutputInterface $outputInterface;
|
||||||
|
/** @internal */
|
||||||
|
protected QROptions $options;
|
||||||
|
/** @internal */
|
||||||
|
protected QRMatrix $matrix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\Output\QROutputInterface
|
* Attempts to create a directory under /.build and instances several required objects
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected $outputInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \chillerlan\QRCode\QROptions
|
|
||||||
*/
|
|
||||||
protected $options;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \chillerlan\QRCode\Data\QRMatrix
|
|
||||||
*/
|
|
||||||
protected $matrix;
|
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$buildDir = dirname($this::cachefile);
|
if(!file_exists($this->builddir)){
|
||||||
if(!file_exists($buildDir)){
|
mkdir($this->builddir, 0777, true);
|
||||||
mkdir($buildDir, 0777, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->options = new QROptions;
|
$this->options = new QROptions;
|
||||||
$this->setOutputInterface();
|
$this->matrix = (new Byte($this->options, 'testdata'))->initMatrix(0);
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setOutputInterface(){
|
/**
|
||||||
$this->outputInterface = $this->reflection->newInstanceArgs([$this->options, (new Byte($this->options, 'testdata'))->initMatrix(0)]);
|
* Returns a QROutputInterface instance with the given options and using $this->matrix
|
||||||
return $this->outputInterface;
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract protected function getOutputInterface(QROptions $options):QROutputInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the instance of the interface
|
||||||
|
*/
|
||||||
|
public function testInstance():void{
|
||||||
|
$this::assertInstanceOf(QROutputInterface::class, $this->outputInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInstance(){
|
/**
|
||||||
$this->assertInstanceOf(QROutputInterface::class, $this->outputInterface);
|
* Tests if an exception is thrown when trying to write a cache file to an invalid destination
|
||||||
}
|
*/
|
||||||
|
public function testSaveException():void{
|
||||||
public function testSaveException(){
|
|
||||||
$this->expectException(QRCodeOutputException::class);
|
$this->expectException(QRCodeOutputException::class);
|
||||||
$this->expectExceptionMessage('Could not write data to cache file: /foo');
|
$this->expectExceptionMessage('Could not write data to cache file: /foo/bar.test');
|
||||||
|
|
||||||
$this->options->cachefile = '/foo';
|
$this->options->cachefile = '/foo/bar.test';
|
||||||
$this->setOutputInterface();
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$this->outputInterface->dump();
|
$this->outputInterface->dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* covers the module values settings
|
||||||
|
*/
|
||||||
|
abstract public function testSetModuleValues():void;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* additional, non-essential, potentially inaccurate coverage tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see testStringOutput()
|
||||||
|
* @return string[][]
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract public function types():array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* coverage of the built-in output modules
|
||||||
|
*
|
||||||
|
* @dataProvider types
|
||||||
|
*/
|
||||||
|
public function testStringOutput(string $type):void{
|
||||||
|
$this->options->outputType = $type;
|
||||||
|
$this->options->cachefile = $this->builddir.'/test.'.$type;
|
||||||
|
$this->options->imageBase64 = false;
|
||||||
|
|
||||||
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
|
$data = $this->outputInterface->dump(); // creates the cache file
|
||||||
|
|
||||||
|
$this::assertSame($data, file_get_contents($this->options->cachefile));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* covers the built-in output modules, tests against pre-rendered data
|
||||||
|
*
|
||||||
|
* @dataProvider types
|
||||||
|
*/
|
||||||
|
public function testRenderImage(string $type):void{
|
||||||
|
|
||||||
|
// may fail on CI, different PHP (platform) versions produce different output
|
||||||
|
// the samples were generated on php-7.4.3-Win32-vc15-x64
|
||||||
|
if(
|
||||||
|
(PHP_OS_FAMILY !== 'Windows' || PHP_VERSION_ID >= 80100)
|
||||||
|
&& in_array($type, [QRCode::OUTPUT_IMAGE_JPG, QRCode::OUTPUT_IMAGICK, QRCode::OUTPUT_MARKUP_SVG])
|
||||||
|
){
|
||||||
|
$this::markTestSkipped('may fail on CI');
|
||||||
|
|
||||||
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->options->outputType = $type;
|
||||||
|
|
||||||
|
$this::assertSame(
|
||||||
|
trim(file_get_contents(__DIR__.'/samples/'.$type)),
|
||||||
|
trim((new QRCode($this->options))->render('test'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,28 @@
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest\Output;
|
namespace chillerlan\QRCodeTest\Output;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QRCode, Output\QRString};
|
use chillerlan\QRCodeExamples\MyCustomOutput;
|
||||||
|
use chillerlan\QRCode\{QRCode, QROptions};
|
||||||
|
use chillerlan\QRCode\Output\{QROutputInterface, QRString};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the QRString output module
|
||||||
|
*/
|
||||||
class QRStringTest extends QROutputTestAbstract{
|
class QRStringTest extends QROutputTestAbstract{
|
||||||
|
|
||||||
protected $FQCN = QRString::class;
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected function getOutputInterface(QROptions $options):QROutputInterface{
|
||||||
|
return new QRString($options, $this->matrix);
|
||||||
|
}
|
||||||
|
|
||||||
public function types(){
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function types():array{
|
||||||
return [
|
return [
|
||||||
'json' => [QRCode::OUTPUT_STRING_JSON],
|
'json' => [QRCode::OUTPUT_STRING_JSON],
|
||||||
'text' => [QRCode::OUTPUT_STRING_TEXT],
|
'text' => [QRCode::OUTPUT_STRING_TEXT],
|
||||||
|
@ -26,19 +41,9 @@ class QRStringTest extends QROutputTestAbstract{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider types
|
* @inheritDoc
|
||||||
* @param $type
|
|
||||||
*/
|
*/
|
||||||
public function testStringOutput($type){
|
public function testSetModuleValues():void{
|
||||||
$this->options->outputType = $type;
|
|
||||||
$this->options->cachefile = $this::cachefile.$type;
|
|
||||||
$this->setOutputInterface();
|
|
||||||
$data = $this->outputInterface->dump();
|
|
||||||
|
|
||||||
$this->assertSame($data, file_get_contents($this->options->cachefile));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetModuleValues(){
|
|
||||||
|
|
||||||
$this->options->moduleValues = [
|
$this->options->moduleValues = [
|
||||||
// data
|
// data
|
||||||
|
@ -46,11 +51,26 @@ class QRStringTest extends QROutputTestAbstract{
|
||||||
4 => 'B',
|
4 => 'B',
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->setOutputInterface();
|
$this->outputInterface = $this->getOutputInterface($this->options);
|
||||||
$data = $this->outputInterface->dump();
|
$data = $this->outputInterface->dump();
|
||||||
|
|
||||||
$this->assertStringContainsString('A', $data);
|
$this::assertStringContainsString('A', $data);
|
||||||
$this->assertStringContainsString('B', $data);
|
$this::assertStringContainsString('B', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* covers the custom output functionality via an example
|
||||||
|
*/
|
||||||
|
public function testCustomOutput():void{
|
||||||
|
$this->options->version = 5;
|
||||||
|
$this->options->eccLevel = QRCode::ECC_L;
|
||||||
|
$this->options->outputType = QRCode::OUTPUT_CUSTOM;
|
||||||
|
$this->options->outputInterface = MyCustomOutput::class;
|
||||||
|
|
||||||
|
$this::assertSame(
|
||||||
|
file_get_contents(__DIR__.'/samples/custom'),
|
||||||
|
(new QRCode($this->options))->render('test')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000011111110111010000101111010000011111110000
|
||||||
|
000010000010111000001101011000001010000010000
|
||||||
|
000010111010101101011000001101011010111010000
|
||||||
|
000010111010110100111110010100111010111010000
|
||||||
|
000010111010000001101011000001101010111010000
|
||||||
|
000010000010100111110010100111110010000010000
|
||||||
|
000011111110101010101010101010101011111110000
|
||||||
|
000000000000010010100111110010100000000000000
|
||||||
|
000011001110000101111010000101111001011110000
|
||||||
|
000000000000111010000101111010000111100010000
|
||||||
|
000001011010100111110010100111110011001010000
|
||||||
|
000010000101111101011000001101011110011110000
|
||||||
|
000000011010100011000001101011000101110100000
|
||||||
|
000011001100001001101011000001101010011010000
|
||||||
|
000010110111110000001101011000001100110100000
|
||||||
|
000010000100100010100111110010100001100100000
|
||||||
|
000011111110111101111010000101111010100110000
|
||||||
|
000011010000111010000101111010000111100100000
|
||||||
|
000010101111111111110010100111110011001000000
|
||||||
|
000010110001110101011000001101011110011010000
|
||||||
|
000001001111100011000001101011000101110010000
|
||||||
|
000011000100110001101011000001101010011100000
|
||||||
|
000001000011001000001101011000001100110000000
|
||||||
|
000011101001011010100111110010100001100000000
|
||||||
|
000010111010001101111010000101111010100110000
|
||||||
|
000011100000001010000101111010000111100000000
|
||||||
|
000000001110110111110010100111110011001000000
|
||||||
|
000000011001011101011000001101011110011100000
|
||||||
|
000011111110101011000001101011001111110110000
|
||||||
|
000000000000110001101011000001101000111100000
|
||||||
|
000011111110001000001101011000011010110000000
|
||||||
|
000010000010101010100111110010101000100100000
|
||||||
|
000010111010111101111010000101111111100110000
|
||||||
|
000010111010011010000101111010001101100010000
|
||||||
|
000010111010000111110010100111100101101100000
|
||||||
|
000010000010101101011000001101001100111100000
|
||||||
|
000011111110101011000001101011000110010110000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
||||||
|
000000000000000000000000000000000000000000000
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
data:image/gif;base64,R0lGODlhkQCRAIAAAAQCBP///yH5BAEAAAEALAAAAACRAJEAAAL+jI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpfMpvOpBEin1Kr1ek1gpY6qdgsOU1niMviL7Y4R5raV7I4D0NmGly13w/Nm+tu+dsDXtjcY5nfHkGhgWFY4x7GoIOlXGRmYQnmhKYg56clZEToyOjF6CuqJURrCGoE6pRaLN7vh+nH7AMsFWMuouglcIklIy9vpG0CcmlyMfJzJLGb5DFlN/dvcZ2wdre3Ija0s3T3+PR2+sgx+nd7+ng1tjg6Psk4fX36vT464JX5i3xl3+WTJE+esoDpyA+vtKodwW72ADP8RnAfxYkT+dgpVCLToUFjHBR/TaDRRsk7IZAAvJsT4yJ/Jlfh0VZw50t5NlTlfGuTHEebCc4dcSoSQ8s9ElDuV9jxqk2jDnBSlgnwa9KdMnkI9iqSQa9VXU2NblX11VkPYXgeZsgz2FsRaRWk9zCVZV2xcUXkv7QXb18LdT39FNKq5b+NhnDoXM05s1DHXqpIh0wRaObDazJaxyuM8WC9ozKQVg4bC1ilqHVNX52jt+gbs2DVm087nM/Lnrw9bh4YbR6vujEur+dacgY/wy8vxBv0tOE/zrlRPGr9KvXHw1Kanb1UNne/flw9bXu6snThu7OWtewaPXLR6jMfH9+PODT3l+eT+ed+naxVRMaVWX1vM4XedgEPtFuBkCK4nx0/hAaZggg4CyKAhEsYHXGn5YTfdYhsWZlaFEF7oXIaDjGggCejlhiFpyhHIYXQNzvedabnot6CMdRXoI4kHvpAUWY81ZaSKMRQpAZA5NvmfC0yideSNSXpIg5Pu0ceef1W22IKWxZ0In31f4tjji14itmaZYD6ipplsyukmml6daR6ZPFqI5W17vtfdbYA+mN2fUBgap5KCFtpmoGMeGiVhig4p5WhvbgmkbZVaeiWLQaIYpqV2VsfndxN2IOqo2T3J56l+jdYpjTe6akuNpL4HI5k2TNheqyB+KKQ3l1IJZlKachn+bHqoNgpsTazOwCuzvjL2rAzGRmqqtEhBtV+2JkpaVIxXHmtYU4hyu6W3w+JiLrbNqpYnuOSWqKKYKYZ777i/DtPut/M+am+os0b6763ITrppvV1aCSWdhnb7bqLwElotreIpjOe7scb4sFsYcyXxud8u2S/CjGYsbro9qtvnySBruyoM1/qLJKbuEllyy1Ma3PGd60b1sR7D9SqsqiEepiPMENc68HM1p+yz0YSKOLTSHksNtZ5Q7QwunJlVvK/BO6QKNrUqvyZq2aBanB5nak/86Nhpa0x31ovejXfeeu/Nd99+/w144IIPTnjhhh+OeOKKL854444/DnnkkhcCXgAAOw==
|
|
@ -0,0 +1,31 @@
|
||||||
|
<div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #000;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
<div><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span><span style="background: #fff;"></span></div>
|
||||||
|
</div>
|
Binary file not shown.
After Width: | Height: | Size: 348 B |
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
[[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,1536,1536,1536,1536,1536,1536,1536,8,3584,1024,4,4,1024,8,1536,1536,1536,1536,1536,1536,1536,18,18,18,18],[18,18,18,18,1536,6,6,6,6,6,1536,8,14,1024,4,4,1024,8,1536,6,6,6,6,6,1536,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,3584,4,1024,4,1024,8,1536,6,5632,5632,5632,6,1536,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,3584,4,4,1024,4,8,1536,6,5632,5632,5632,6,1536,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,3584,1024,1024,4,4,8,1536,6,5632,5632,5632,6,1536,18,18,18,18],[18,18,18,18,1536,6,6,6,6,6,1536,8,14,4,4,4,4,8,1536,6,6,6,6,6,1536,18,18,18,18],[18,18,18,18,1536,1536,1536,1536,1536,1536,1536,8,3072,12,3072,12,3072,8,1536,1536,1536,1536,1536,1536,1536,18,18,18,18],[18,18,18,18,8,8,8,8,8,8,8,8,14,1024,1024,4,4,8,8,8,8,8,8,8,8,18,18,18,18],[18,18,18,18,3584,3584,3584,3584,14,14,3072,14,3584,4,1024,4,4,3584,14,14,3584,3584,3584,14,3584,18,18,18,18],[18,18,18,18,1024,4,4,1024,1024,1024,12,1024,4,4,1024,4,1024,4,4,1024,4,1024,1024,4,1024,18,18,18,18],[18,18,18,18,1024,4,1024,4,1024,1024,3072,4,4,4,1024,4,1024,1024,1024,1024,1024,4,4,1024,1024,18,18,18,18],[18,18,18,18,1024,4,1024,1024,4,4,12,1024,1024,4,1024,4,1024,1024,4,4,4,1024,4,1024,4,18,18,18,18],[18,18,18,18,4,1024,1024,4,1024,4,3072,4,4,1024,1024,1024,4,1024,4,4,1024,1024,4,1024,4,18,18,18,18],[18,18,18,18,8,8,8,8,8,8,8,8,512,4,1024,4,4,1024,4,1024,4,1024,4,1024,4,18,18,18,18],[18,18,18,18,1536,1536,1536,1536,1536,1536,1536,8,14,4,1024,4,4,1024,1024,4,1024,1024,1024,4,4,18,18,18,18],[18,18,18,18,1536,6,6,6,6,6,1536,8,14,1024,4,1024,1024,1024,1024,4,4,1024,1024,4,4,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,14,1024,1024,1024,4,4,1024,4,4,4,1024,1024,1024,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,3584,1024,4,4,4,1024,4,4,1024,1024,4,1024,4,18,18,18,18],[18,18,18,18,1536,6,5632,5632,5632,6,1536,8,3584,4,1024,1024,4,1024,4,4,1024,4,1024,4,4,18,18,18,18],[18,18,18,18,1536,6,6,6,6,6,1536,8,3584,1024,1024,1024,1024,4,1024,4,1024,1024,4,4,1024,18,18,18,18],[18,18,18,18,1536,1536,1536,1536,1536,1536,1536,8,3584,1024,4,1024,1024,4,4,1024,4,4,4,4,4,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18]]
|
|
@ -0,0 +1 @@
|
||||||
|
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJEAAACRCAIAAABMus10AAAABnRSTlMA/wD/AP83WBt9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACd0lEQVR4nO3dQW7jIABA0fFo7n/ldFnJC0sMBvu3762bJtUXMnUwHJ/P5w8pf5/+AAzTrEezHs16NOvRrEezHs16NOvRrEezHs16NOvRrEezHs16NOvRrEeznn8zLz6O467Pce20aOX6fa9XuJxeO/SbbzSzDMc469GsR7MezXqm5iAnNy5vHZplrJs4bPuLhhhnPZr1aNajWc+dc5CToavu0NV+6F7GU/OIdQ8cGWc9mvVo1qNZz8I5yFO2TUmeYpz1aNajWY9mPT9hDjKzPKTIOOvRrEezHs16Fs5B1l38n5pWvGQ6Y5z1aNajWY9mPXfOQbY9VDKzHmToq5ltf9EQ46xHsx7NejTrOV7yv/0M38Xwdpr1aNajWc9j+4PcuIvHzKO6626LrFsaa5z1aNajWY9mPfv2BzldhGd2AJm5gM9MOp7a0OzEOOvRrEezHs169u0PMnQB3/ZA7Utumgwxzno069GsR7Oefc/mzkwrtq34uHGrtJnffM0469GsR7MezXqm5iAzx7hse3pl3eKRpxhnPZr1aNajWc9j58XM3DXYtle79SDcQ7MezXo060mem7ttecg717AYZz2a9WjWo1lP8tzc6x9ed7fCfRD+k2Y9mvVo1pM8N3fmfbftALJuHa1x1qNZj2Y9mvUkz6y78fJ+4+LXbef1Gmc9mvVo1qNZT3IOcjLzeM6697UehG+a9WjWo1lP8tzck3WLR2Yez5n54WvGWY9mPZr1aNaTPDf32szK0XW3NnwX86tp1qNZj2Y9P+Hc3N/GOOvRrEezHs16NOvRrEezHs16NOvRrEezHs16NOvRrEezHs16NOvRrEezni+wifc/oOyB/wAAAABJRU5ErkJggg==
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,29 @@
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴🔴🔴🔴🔴🔴🔴⭕🔴🔴⭕⭕🔴⭕🔴🔴🔴🔴🔴🔴🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕🔴⭕⭕🔴⭕⭕🔴⭕🔴⭕⭕⭕⭕⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕🔴⭕🔴⭕🔴⭕🔴⭕🔴🔴🔴⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕🔴⭕⭕🔴⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕🔴🔴🔴⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴🔴🔴🔴🔴🔴🔴⭕🔴⭕🔴⭕🔴⭕🔴🔴🔴🔴🔴🔴🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕🔴🔴⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴🔴🔴🔴⭕⭕🔴⭕🔴⭕🔴⭕⭕🔴⭕⭕🔴🔴🔴⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕⭕🔴🔴🔴⭕🔴⭕⭕🔴⭕🔴⭕⭕🔴⭕🔴🔴⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴⭕🔴🔴🔴⭕⭕⭕🔴⭕🔴🔴🔴🔴🔴⭕⭕🔴🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴⭕⭕⭕🔴🔴⭕🔴⭕🔴🔴⭕⭕⭕🔴⭕🔴⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕🔴🔴⭕🔴⭕🔴⭕⭕🔴🔴🔴⭕🔴⭕⭕🔴🔴⭕🔴⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕🔴⭕🔴⭕⭕🔴⭕🔴⭕🔴⭕🔴⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴🔴🔴🔴🔴🔴🔴⭕⭕⭕🔴⭕⭕🔴🔴⭕🔴🔴🔴⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕🔴⭕⭕🔴⭕🔴🔴🔴🔴⭕⭕🔴🔴⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕⭕🔴🔴🔴⭕⭕🔴⭕⭕⭕🔴🔴🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕🔴🔴⭕⭕⭕🔴⭕⭕🔴🔴⭕🔴⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕🔴🔴🔴⭕🔴⭕🔴⭕🔴🔴⭕🔴⭕⭕🔴⭕🔴⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴⭕⭕⭕⭕⭕🔴⭕🔴🔴🔴🔴🔴⭕🔴⭕🔴🔴⭕⭕🔴⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕🔴🔴🔴🔴🔴🔴🔴⭕🔴🔴⭕🔴🔴⭕⭕🔴⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
||||||
|
⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕
|
|
@ -13,128 +13,139 @@
|
||||||
namespace chillerlan\QRCodeTest;
|
namespace chillerlan\QRCodeTest;
|
||||||
|
|
||||||
use chillerlan\QRCode\{QROptions, QRCode};
|
use chillerlan\QRCode\{QROptions, QRCode};
|
||||||
use chillerlan\QRCode\Data\{AlphaNum, Byte, Number, QRCodeDataException};
|
use chillerlan\QRCode\Data\{AlphaNum, Byte, Kanji, Number, QRCodeDataException};
|
||||||
use chillerlan\QRCode\Output\QRCodeOutputException;
|
use chillerlan\QRCode\Output\QRCodeOutputException;
|
||||||
use chillerlan\QRCodeExamples\MyCustomOutput;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
use function random_bytes;
|
use function random_bytes;
|
||||||
|
|
||||||
class QRCodeTest extends QRTestAbstract{
|
/**
|
||||||
|
* Tests basic functions of the QRCode class
|
||||||
|
*/
|
||||||
|
class QRCodeTest extends TestCase{
|
||||||
|
|
||||||
protected $FQCN = QRCode::class;
|
/** @internal */
|
||||||
|
protected QRCode $qrcode;
|
||||||
|
/** @internal */
|
||||||
|
protected QROptions $options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\QRCode
|
* invoke test instances
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected $qrcode;
|
|
||||||
|
|
||||||
protected function setUp():void{
|
protected function setUp():void{
|
||||||
parent::setUp();
|
$this->qrcode = new QRCode;
|
||||||
|
$this->options = new QROptions;
|
||||||
$this->qrcode = $this->reflection->newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testIsNumber(){
|
|
||||||
$this->assertTrue($this->qrcode->isNumber('0123456789'));
|
|
||||||
$this->assertFalse($this->qrcode->isNumber('ABC'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testIsAlphaNum(){
|
|
||||||
$this->assertTrue($this->qrcode->isAlphaNum('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:'));
|
|
||||||
$this->assertFalse($this->qrcode->isAlphaNum('abc'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testIsKanji(){
|
|
||||||
$this->assertTrue($this->qrcode->isKanji('茗荷'));
|
|
||||||
$this->assertFalse($this->qrcode->isKanji('Ã'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// coverage
|
|
||||||
|
|
||||||
public function typeDataProvider(){
|
|
||||||
return [
|
|
||||||
'png' => [QRCode::OUTPUT_IMAGE_PNG, 'data:image/png;base64,'],
|
|
||||||
'gif' => [QRCode::OUTPUT_IMAGE_GIF, 'data:image/gif;base64,'],
|
|
||||||
'jpg' => [QRCode::OUTPUT_IMAGE_JPG, 'data:image/jpg;base64,'],
|
|
||||||
'svg' => [QRCode::OUTPUT_MARKUP_SVG, 'data:image/svg+xml;base64,'],
|
|
||||||
'html' => [QRCode::OUTPUT_MARKUP_HTML, '<div><span style="background:'],
|
|
||||||
'text' => [QRCode::OUTPUT_STRING_TEXT, '⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕⭕'.PHP_EOL],
|
|
||||||
'json' => [QRCode::OUTPUT_STRING_JSON, '[[18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18],'],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider typeDataProvider
|
* isNumber() should pass on any number and fail on anything else
|
||||||
* @param $type
|
|
||||||
*/
|
*/
|
||||||
public function testRenderImage($type, $expected){
|
public function testIsNumber():void{
|
||||||
$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['outputType' => $type])]);
|
$this::assertTrue($this->qrcode->isNumber('0123456789'));
|
||||||
|
|
||||||
$this->assertStringContainsString($expected, $this->qrcode->render('test'));
|
$this::assertFalse($this->qrcode->isNumber('ABC123'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInitDataInterfaceException(){
|
/**
|
||||||
|
* isAlphaNum() should pass on the 45 defined characters and fail on anything else (e.g. lowercase)
|
||||||
|
*/
|
||||||
|
public function testIsAlphaNum():void{
|
||||||
|
$this::assertTrue($this->qrcode->isAlphaNum('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 $%*+-./:'));
|
||||||
|
|
||||||
|
$this::assertFalse($this->qrcode->isAlphaNum('abc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isKanji() should pass on Kanji/SJIS characters and fail on everything else
|
||||||
|
*/
|
||||||
|
public function testIsKanji():void{
|
||||||
|
$this::assertTrue($this->qrcode->isKanji('茗荷'));
|
||||||
|
|
||||||
|
$this::assertFalse($this->qrcode->isKanji('Ã'));
|
||||||
|
$this::assertFalse($this->qrcode->isKanji('ABC'));
|
||||||
|
$this::assertFalse($this->qrcode->isKanji('123'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isByte() passses any binary string and only fails on empty strings
|
||||||
|
*/
|
||||||
|
public function testIsByte():void{
|
||||||
|
$this::assertTrue($this->qrcode->isByte("\x01\x02\x03"));
|
||||||
|
$this::assertTrue($this->qrcode->isByte(' ')); // not empty!
|
||||||
|
|
||||||
|
$this::assertFalse($this->qrcode->isByte(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tests if an exception is thrown when an invalid (built-in) output type is specified
|
||||||
|
*/
|
||||||
|
public function testInitDataInterfaceException():void{
|
||||||
$this->expectException(QRCodeOutputException::class);
|
$this->expectException(QRCodeOutputException::class);
|
||||||
$this->expectExceptionMessage('invalid output type');
|
$this->expectExceptionMessage('invalid output type');
|
||||||
|
|
||||||
(new QRCode(new QROptions(['outputType' => 'foo'])))->render('test');
|
$this->options->outputType = 'foo';
|
||||||
|
|
||||||
|
(new QRCode($this->options))->render('test');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMatrixException(){
|
/**
|
||||||
|
* tests if an exception is thrown when trying to call getMatrix() without data (empty string, no data set)
|
||||||
|
*/
|
||||||
|
public function testGetMatrixException():void{
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('QRCode::getMatrix() No data given.');
|
$this->expectExceptionMessage('QRCode::getMatrix() No data given.');
|
||||||
|
|
||||||
$this->qrcode->getMatrix('');
|
$this->qrcode->getMatrix('');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTrim() {
|
/**
|
||||||
$m1 = $this->qrcode->getMatrix('hello');
|
* test whether stings are trimmed (they are not) - i'm still torn on that (see isByte)
|
||||||
$m2 = $this->qrcode->getMatrix('hello '); // added space
|
*/
|
||||||
|
public function testAvoidTrimming():void{
|
||||||
|
$m1 = $this->qrcode->getMatrix('hello')->matrix();
|
||||||
|
$m2 = $this->qrcode->getMatrix('hello ')->matrix(); // added space
|
||||||
|
|
||||||
$this->assertNotEquals($m1, $m2);
|
$this::assertNotSame($m1, $m2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImageTransparencyBGDefault(){
|
/**
|
||||||
$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['imageTransparencyBG' => 'foo'])]);
|
* tests if the data mode is overriden if QROptions::$dataModeOverride is set to a valid value
|
||||||
|
*
|
||||||
|
* @see https://github.com/chillerlan/php-qrcode/issues/39
|
||||||
|
*/
|
||||||
|
public function testDataModeOverride():void{
|
||||||
|
|
||||||
$this->assertSame([255,255,255], $this->getProperty('options')->getValue($this->qrcode)->imageTransparencyBG);
|
// no (or invalid) value set - auto detection
|
||||||
|
$this->options->dataModeOverride = 'foo';
|
||||||
|
$this->qrcode = new QRCode;
|
||||||
|
|
||||||
|
$this::assertInstanceOf(Number::class, $this->qrcode->initDataInterface('123'));
|
||||||
|
$this::assertInstanceOf(AlphaNum::class, $this->qrcode->initDataInterface('ABC123'));
|
||||||
|
$this::assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
|
||||||
|
$this::assertInstanceOf(Kanji::class, $this->qrcode->initDataInterface('茗荷'));
|
||||||
|
|
||||||
|
// data mode set: force the given data mode
|
||||||
|
$this->options->dataModeOverride = 'Byte';
|
||||||
|
$this->qrcode = new QRCode($this->options);
|
||||||
|
|
||||||
|
$this::assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('123'));
|
||||||
|
$this::assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('ABC123'));
|
||||||
|
$this::assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
|
||||||
|
$this::assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('茗荷'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCustomOutput(){
|
/**
|
||||||
|
* tests if an exception is thrown when an invalid character occurs when forcing a data mode other than Byte
|
||||||
$options = new QROptions([
|
*/
|
||||||
'version' => 5,
|
public function testDataModeOverrideError():void{
|
||||||
'eccLevel' => QRCode::ECC_L,
|
|
||||||
'outputType' => QRCode::OUTPUT_CUSTOM,
|
|
||||||
'outputInterface' => MyCustomOutput::class,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$expected = '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110111010000101111010000011111110000000010000010111000001101011000001010000010000000010111010101101011000001101011010111010000000010111010110100111110010100111010111010000000010111010000001101011000001101010111010000000010000010100111110010100111110010000010000000011111110101010101010101010101011111110000000000000000010010100111110010100000000000000000011001110000101111010000101111001011110000000000000000111010000101111010000111100010000000001011010100111110010100111110011001010000000010000101111101011000001101011110011110000000000011010100011000001101011000101110100000000011001100001001101011000001101010011010000000010110111110000001101011000001100110100000000010000100100010100111110010100001100100000000011111110111101111010000101111010100110000000011010000111010000101111010000111100100000000010101111111111110010100111110011001000000000010110001110101011000001101011110011010000000001001111100011000001101011000101110010000000011000100110001101011000001101010011100000000001000011001000001101011000001100110000000000011101001011010100111110010100001100000000000010111010001101111010000101111010100110000000011100000001010000101111010000111100000000000000001110110111110010100111110011001000000000000011001011101011000001101011110011100000000011111110101011000001101011001111110110000000000000000110001101011000001101000111100000000011111110001000001101011000011010110000000000010000010101010100111110010101000100100000000010111010111101111010000101111111100110000000010111010011010000101111010001101100010000000010111010000111110010100111100101101100000000010000010101101011000001101001100111100000000011111110101011000001101011000110010110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
|
|
||||||
|
|
||||||
$this->assertSame($expected, $this->reflection->newInstanceArgs([$options])->render('test'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDataModeOverride(){
|
|
||||||
$this->qrcode = $this->reflection->newInstance();
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Number::class, $this->qrcode->initDataInterface('123'));
|
|
||||||
$this->assertInstanceOf(AlphaNum::class, $this->qrcode->initDataInterface('ABC123'));
|
|
||||||
$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
|
|
||||||
|
|
||||||
$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['dataMode' => 'Byte'])]);
|
|
||||||
|
|
||||||
$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('123'));
|
|
||||||
$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface('ABC123'));
|
|
||||||
$this->assertInstanceOf(Byte::class, $this->qrcode->initDataInterface(random_bytes(32)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDataModeOverrideError(){
|
|
||||||
$this->expectException(QRCodeDataException::class);
|
$this->expectException(QRCodeDataException::class);
|
||||||
$this->expectExceptionMessage('illegal char:');
|
$this->expectExceptionMessage('illegal char:');
|
||||||
|
|
||||||
$this->qrcode = $this->reflection->newInstanceArgs([new QROptions(['dataMode' => 'AlphaNum'])]);
|
$this->options->dataModeOverride = 'AlphaNum';
|
||||||
|
|
||||||
$this->qrcode->initDataInterface(random_bytes(32));
|
(new QRCode($this->options))->initDataInterface(random_bytes(32));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
* @author smiley <smiley@chillerlan.net>
|
* @author smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
*
|
||||||
|
* @noinspection PhpUnusedLocalVariableInspection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest;
|
namespace chillerlan\QRCodeTest;
|
||||||
|
@ -15,66 +17,128 @@ namespace chillerlan\QRCodeTest;
|
||||||
use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
|
use chillerlan\QRCode\{QRCode, QRCodeException, QROptions};
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QROptions test
|
||||||
|
*/
|
||||||
class QROptionsTest extends TestCase{
|
class QROptionsTest extends TestCase{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \chillerlan\QRCode\QROptions
|
* @see testVersionClamp()
|
||||||
|
* @return int[][]
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected $options;
|
public function VersionProvider():array{
|
||||||
|
return [
|
||||||
public function testVersionClamp(){
|
'values > 40 should be clamped to 40' => [42, 40],
|
||||||
$this->assertSame(40, (new QROptions(['version' => 42]))->version);
|
'values < 1 should be clamped to 1' => [-42, 1],
|
||||||
$this->assertSame(1, (new QROptions(['version' => -42]))->version);
|
'values in between shold not be touched' => [21, 21],
|
||||||
$this->assertSame(21, (new QROptions(['version' => 21]))->version);
|
'value -1 should be treated as is (default)' => [QRCode::VERSION_AUTO, -1],
|
||||||
$this->assertSame(QRCode::VERSION_AUTO, (new QROptions)->version); // QRCode::VERSION_AUTO = -1, default
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testVersionMinMaxClamp(){
|
/**
|
||||||
// normal clamp
|
* Tests the $version clamping
|
||||||
$o = new QROptions(['versionMin' => 5, 'versionMax' => 10]);
|
*
|
||||||
$this->assertSame(5, $o->versionMin);
|
* @dataProvider VersionProvider
|
||||||
$this->assertSame(10, $o->versionMax);
|
*/
|
||||||
|
public function testVersionClamp(int $version, int $expected):void{
|
||||||
|
$o = new QROptions(['version' => $version]);
|
||||||
|
|
||||||
// exceeding values
|
$this::assertSame($expected, $o->version);
|
||||||
$o = new QROptions(['versionMin' => -42, 'versionMax' => 42]);
|
|
||||||
$this->assertSame(1, $o->versionMin);
|
|
||||||
$this->assertSame(40, $o->versionMax);
|
|
||||||
|
|
||||||
// min > max
|
|
||||||
$o = new QROptions(['versionMin' => 10, 'versionMax' => 5]);
|
|
||||||
$this->assertSame(5, $o->versionMin);
|
|
||||||
$this->assertSame(10, $o->versionMax);
|
|
||||||
|
|
||||||
$o = new QROptions(['versionMin' => 42, 'versionMax' => -42]);
|
|
||||||
$this->assertSame(1, $o->versionMin);
|
|
||||||
$this->assertSame(40, $o->versionMax);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMaskPatternClamp(){
|
/**
|
||||||
$this->assertSame(7, (new QROptions(['maskPattern' => 42]))->maskPattern);
|
* @see testVersionMinMaxClamp()
|
||||||
$this->assertSame(0, (new QROptions(['maskPattern' => -42]))->maskPattern);
|
* @return int[][]
|
||||||
$this->assertSame(QRCode::MASK_PATTERN_AUTO, (new QROptions)->maskPattern); // QRCode::MASK_PATTERN_AUTO = -1, default
|
* @internal
|
||||||
|
*/
|
||||||
|
public function VersionMinMaxProvider():array{
|
||||||
|
return [
|
||||||
|
'normal clamp' => [5, 10, 5, 10],
|
||||||
|
'exceeding values' => [-42, 42, 1, 40],
|
||||||
|
'min > max' => [10, 5, 5, 10],
|
||||||
|
'min > max, exceeding' => [42, -42, 1, 40],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidEccLevelException(){
|
/**
|
||||||
|
* Tests the $versionMin/$versionMax clamping
|
||||||
|
*
|
||||||
|
* @dataProvider VersionMinMaxProvider
|
||||||
|
*/
|
||||||
|
public function testVersionMinMaxClamp(int $versionMin, int $versionMax, int $expectedMin, int $expectedMax):void{
|
||||||
|
$o = new QROptions(['versionMin' => $versionMin, 'versionMax' => $versionMax]);
|
||||||
|
|
||||||
|
$this::assertSame($expectedMin, $o->versionMin);
|
||||||
|
$this::assertSame($expectedMax, $o->versionMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see testMaskPatternClamp()
|
||||||
|
* @return int[][]
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function MaskPatternProvider():array{
|
||||||
|
return [
|
||||||
|
'exceed max' => [42, 7,],
|
||||||
|
'exceed min' => [-42, 0],
|
||||||
|
'default (-1)' => [QRCode::MASK_PATTERN_AUTO, -1],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the $maskPattern clamping
|
||||||
|
*
|
||||||
|
* @dataProvider MaskPatternProvider
|
||||||
|
*/
|
||||||
|
public function testMaskPatternClamp(int $maskPattern, int $expected):void{
|
||||||
|
$o = new QROptions(['maskPattern' => $maskPattern]);
|
||||||
|
|
||||||
|
$this::assertSame($expected, $o->maskPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown on an incorrect ECC level
|
||||||
|
*/
|
||||||
|
public function testInvalidEccLevelException():void{
|
||||||
$this->expectException(QRCodeException::class);
|
$this->expectException(QRCodeException::class);
|
||||||
$this->expectExceptionMessage('Invalid error correct level: 42');
|
$this->expectExceptionMessage('Invalid error correct level: 42');
|
||||||
|
|
||||||
new QROptions(['eccLevel' => 42]);
|
$o = new QROptions(['eccLevel' => 42]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClampRGBValues(){
|
/**
|
||||||
$o = new QROptions(['imageTransparencyBG' => [-1, 0, 999]]);
|
* @see testClampRGBValues()
|
||||||
|
* @return int[][][]
|
||||||
$this->assertSame(0, $o->imageTransparencyBG[0]);
|
* @internal
|
||||||
$this->assertSame(0, $o->imageTransparencyBG[1]);
|
*/
|
||||||
$this->assertSame(255, $o->imageTransparencyBG[2]);
|
public function RGBProvider():array{
|
||||||
|
return [
|
||||||
|
'exceeding values' => [[-1, 0, 999], [0, 0 ,255]],
|
||||||
|
'too few values' => [[1, 2], [255, 255, 255]],
|
||||||
|
'too many values' => [[1, 2, 3, 4, 5], [1, 2, 3]],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInvalidRGBValueException(){
|
/**
|
||||||
|
* Tests clamping of the RGB values for $imageTransparencyBG
|
||||||
|
*
|
||||||
|
* @dataProvider RGBProvider
|
||||||
|
*/
|
||||||
|
public function testClampRGBValues(array $rgb, array $expected):void{
|
||||||
|
$o = new QROptions(['imageTransparencyBG' => $rgb]);
|
||||||
|
|
||||||
|
$this::assertSame($expected, $o->imageTransparencyBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if an exception is thrown when a non-numeric RGB value was encoutered
|
||||||
|
*/
|
||||||
|
public function testInvalidRGBValueException():void{
|
||||||
$this->expectException(QRCodeException::class);
|
$this->expectException(QRCodeException::class);
|
||||||
$this->expectExceptionMessage('Invalid RGB value.');
|
$this->expectExceptionMessage('Invalid RGB value.');
|
||||||
|
|
||||||
new QROptions(['imageTransparencyBG' => ['r', 'g', 'b']]);
|
$o = new QROptions(['imageTransparencyBG' => ['r', 'g', 'b']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Class QRTestAbstract
|
|
||||||
*
|
|
||||||
* @filesource QRTestAbstract.php
|
|
||||||
* @created 17.11.2017
|
|
||||||
* @package chillerlan\QRCodeTest
|
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
|
||||||
* @copyright 2017 Smiley
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace chillerlan\QRCodeTest;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use ReflectionClass, ReflectionMethod, ReflectionProperty;
|
|
||||||
|
|
||||||
abstract class QRTestAbstract extends TestCase{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \ReflectionClass
|
|
||||||
*/
|
|
||||||
protected $reflection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $FQCN;
|
|
||||||
|
|
||||||
protected function setUp():void{
|
|
||||||
$this->reflection = new ReflectionClass($this->FQCN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $method
|
|
||||||
*
|
|
||||||
* @return \ReflectionMethod
|
|
||||||
*/
|
|
||||||
protected function getMethod(string $method):ReflectionMethod {
|
|
||||||
$method = $this->reflection->getMethod($method);
|
|
||||||
$method->setAccessible(true);
|
|
||||||
|
|
||||||
return $method;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $property
|
|
||||||
*
|
|
||||||
* @return \ReflectionProperty
|
|
||||||
*/
|
|
||||||
protected function getProperty(string $property):ReflectionProperty{
|
|
||||||
$property = $this->reflection->getProperty($property);
|
|
||||||
$property->setAccessible(true);
|
|
||||||
|
|
||||||
return $property;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $object
|
|
||||||
* @param string $property
|
|
||||||
* @param $value
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function setProperty($object, string $property, $value){
|
|
||||||
$property = $this->getProperty($property);
|
|
||||||
$property->setAccessible(true);
|
|
||||||
$property->setValue($object, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ko_fi: codemasher
|
||||||
|
custom: "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4"
|
|
@ -0,0 +1,107 @@
|
||||||
|
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
|
||||||
|
# https://github.com/sebastianbergmann/phpunit/blob/master/.github/workflows/ci.yml
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
|
||||||
|
name: "CI"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
static-code-analysis:
|
||||||
|
name: "Static Code Analysis"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
PHAN_ALLOW_XDEBUG: 0
|
||||||
|
PHAN_DISABLE_XDEBUG_WARN: 1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "7.4"
|
||||||
|
tools: pecl
|
||||||
|
coverage: none
|
||||||
|
extensions: ast, json
|
||||||
|
|
||||||
|
- name: "Update dependencies with composer"
|
||||||
|
run: composer update --no-interaction --no-ansi --no-progress --no-suggest
|
||||||
|
|
||||||
|
- name: "Run phan"
|
||||||
|
run: php vendor/bin/phan
|
||||||
|
|
||||||
|
build-docs:
|
||||||
|
name: "Build and publish Docs"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout sources"
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: "7.4"
|
||||||
|
coverage: none
|
||||||
|
tools: phpDocumentor
|
||||||
|
extensions: json
|
||||||
|
|
||||||
|
- name: "Build Docs"
|
||||||
|
run: phpdoc --config=phpdoc.xml
|
||||||
|
|
||||||
|
- name: "Publish Docs to gh-pages"
|
||||||
|
uses: JamesIves/github-pages-deploy-action@v4.2.5
|
||||||
|
with:
|
||||||
|
branch: gh-pages
|
||||||
|
folder: docs
|
||||||
|
clean: true
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: "Unit Tests"
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- windows-latest
|
||||||
|
php-version:
|
||||||
|
- "7.4"
|
||||||
|
- "8.0"
|
||||||
|
- "8.1"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: "Install PHP with extensions"
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php-version }}
|
||||||
|
coverage: pcov
|
||||||
|
extensions: json
|
||||||
|
|
||||||
|
- name: "Install dependencies with composer"
|
||||||
|
run: composer update --no-ansi --no-interaction --no-progress --no-suggest
|
||||||
|
|
||||||
|
- name: "Run tests with phpunit"
|
||||||
|
run: php vendor/phpunit/phpunit/phpunit --configuration=phpunit.xml
|
||||||
|
|
||||||
|
- name: "Send code coverage report to Codecov.io"
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@ -1,4 +1,4 @@
|
||||||
.idea
|
/.build
|
||||||
.vendor
|
/.idea
|
||||||
|
/vendor
|
||||||
composer.lock
|
composer.lock
|
||||||
*.phpunit.result.cache
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This configuration will be read and overlaid on top of the
|
||||||
|
* default configuration. Command-line arguments will be applied
|
||||||
|
* after this file is read.
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
// Supported values: `'5.6'`, `'7.0'`, `'7.1'`, `'7.2'`, `'7.3'`,
|
||||||
|
// `'7.4'`, `null`.
|
||||||
|
// If this is set to `null`,
|
||||||
|
// then Phan assumes the PHP version which is closest to the minor version
|
||||||
|
// of the php executable used to execute Phan.
|
||||||
|
//
|
||||||
|
// Note that the **only** effect of choosing `'5.6'` is to infer
|
||||||
|
// that functions removed in php 7.0 exist.
|
||||||
|
// (See `backward_compatibility_checks` for additional options)
|
||||||
|
'target_php_version' => '7.4',
|
||||||
|
|
||||||
|
// A list of directories that should be parsed for class and
|
||||||
|
// method information. After excluding the directories
|
||||||
|
// defined in exclude_analysis_directory_list, the remaining
|
||||||
|
// files will be statically analyzed for errors.
|
||||||
|
//
|
||||||
|
// Thus, both first-party and third-party code being used by
|
||||||
|
// your application should be included in this list.
|
||||||
|
'directory_list' => [
|
||||||
|
'examples',
|
||||||
|
'src',
|
||||||
|
'tests',
|
||||||
|
'vendor',
|
||||||
|
],
|
||||||
|
|
||||||
|
// A regex used to match every file name that you want to
|
||||||
|
// exclude from parsing. Actual value will exclude every
|
||||||
|
// "test", "tests", "Test" and "Tests" folders found in
|
||||||
|
// "vendor/" directory.
|
||||||
|
'exclude_file_regex' => '@^vendor/.*/(tests?|Tests?)/@',
|
||||||
|
|
||||||
|
// A directory list that defines files that will be excluded
|
||||||
|
// from static analysis, but whose class and method
|
||||||
|
// information should be included.
|
||||||
|
//
|
||||||
|
// Generally, you'll want to include the directories for
|
||||||
|
// third-party code (such as "vendor/") in this list.
|
||||||
|
//
|
||||||
|
// n.b.: If you'd like to parse but not analyze 3rd
|
||||||
|
// party code, directories containing that code
|
||||||
|
// should be added to both the `directory_list`
|
||||||
|
// and `exclude_analysis_directory_list` arrays.
|
||||||
|
'exclude_analysis_directory_list' => [
|
||||||
|
'tests',
|
||||||
|
'vendor',
|
||||||
|
],
|
||||||
|
];
|
|
@ -1,3 +1,12 @@
|
||||||
|
build:
|
||||||
|
nodes:
|
||||||
|
analysis:
|
||||||
|
tests:
|
||||||
|
override:
|
||||||
|
- php-scrutinizer-run
|
||||||
|
environment:
|
||||||
|
php: 8.0.0
|
||||||
|
|
||||||
filter:
|
filter:
|
||||||
excluded_paths:
|
excluded_paths:
|
||||||
- examples/*
|
- examples/*
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
language: php
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- php: 7.2
|
|
||||||
- php: 7.3
|
|
||||||
- php: 7.4
|
|
||||||
- php: 8.0
|
|
||||||
- php: nightly
|
|
||||||
allow_failures:
|
|
||||||
- php: nightly
|
|
||||||
|
|
||||||
|
|
||||||
before_script: travis_retry composer install --no-interaction --prefer-source
|
|
||||||
|
|
||||||
script: vendor/bin/phpunit --configuration phpunit.xml --coverage-clover clover.xml
|
|
||||||
|
|
||||||
after_script: bash <(curl -s https://codecov.io/bash)
|
|
|
@ -1,52 +1,46 @@
|
||||||
# chillerlan/php-settings-container
|
# chillerlan/php-settings-container
|
||||||
|
|
||||||
A container class for immutable settings objects. Not a DI container. PHP 7.2+
|
A container class for immutable settings objects. Not a DI container. PHP 7.4+
|
||||||
- [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/master/src/SettingsContainerInterface.php) provides immutable properties with magic getter & setter and some fancy
|
- [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/main/src/SettingsContainerInterface.php) provides immutable properties with magic getter & setter and some fancy - decouple configuration logic from your application!
|
||||||
|
|
||||||
|
[![PHP Version Support][php-badge]][php]
|
||||||
[![version][packagist-badge]][packagist]
|
[![version][packagist-badge]][packagist]
|
||||||
[![license][license-badge]][license]
|
[![license][license-badge]][license]
|
||||||
[![Travis][travis-badge]][travis]
|
|
||||||
[![Coverage][coverage-badge]][coverage]
|
[![Coverage][coverage-badge]][coverage]
|
||||||
[![Scrunitizer][scrutinizer-badge]][scrutinizer]
|
[![Scrunitizer][scrutinizer-badge]][scrutinizer]
|
||||||
[![Packagist downloads][downloads-badge]][downloads]
|
[![Packagist downloads][downloads-badge]][downloads]
|
||||||
[![PayPal donate][donate-badge]][donate]
|
[![Continuous Integration][gh-action-badge]][gh-action]
|
||||||
|
|
||||||
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-settings-container.svg?style=flat-square
|
[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-settings-container?logo=php&color=8892BF
|
||||||
|
[php]: https://www.php.net/supported-versions.php
|
||||||
|
[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-settings-container.svg?logo=packagist
|
||||||
[packagist]: https://packagist.org/packages/chillerlan/php-settings-container
|
[packagist]: https://packagist.org/packages/chillerlan/php-settings-container
|
||||||
[license-badge]: https://img.shields.io/github/license/chillerlan/php-settings-container.svg?style=flat-square
|
[license-badge]: https://img.shields.io/github/license/chillerlan/php-settings-container.svg
|
||||||
[license]: https://github.com/chillerlan/php-settings-container/blob/master/LICENSE
|
[license]: https://github.com/chillerlan/php-settings-container/blob/main/LICENSE
|
||||||
[travis-badge]: https://img.shields.io/travis/chillerlan/php-settings-container.svg?style=flat-square
|
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-settings-container.svg?logo=codecov
|
||||||
[travis]: https://travis-ci.org/chillerlan/php-settings-container
|
|
||||||
[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-settings-container.svg?style=flat-square
|
|
||||||
[coverage]: https://codecov.io/github/chillerlan/php-settings-container
|
[coverage]: https://codecov.io/github/chillerlan/php-settings-container
|
||||||
[scrutinizer-badge]: https://img.shields.io/scrutinizer/g/chillerlan/php-settings-container.svg?style=flat-square
|
[scrutinizer-badge]: https://img.shields.io/scrutinizer/g/chillerlan/php-settings-container.svg?logo=scrutinizer
|
||||||
[scrutinizer]: https://scrutinizer-ci.com/g/chillerlan/php-settings-container
|
[scrutinizer]: https://scrutinizer-ci.com/g/chillerlan/php-settings-container
|
||||||
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-settings-container.svg?style=flat-square
|
[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-settings-container.svg?logo=packagist
|
||||||
[downloads]: https://packagist.org/packages/chillerlan/php-settings-container/stats
|
[downloads]: https://packagist.org/packages/chillerlan/php-settings-container/stats
|
||||||
[donate-badge]: https://img.shields.io/badge/donate-paypal-ff33aa.svg?style=flat-square
|
[gh-action-badge]: https://github.com/chillerlan/php-settings-container/workflows/CI/badge.svg
|
||||||
[donate]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLYUNAT9ZTJZ4
|
[gh-action]: https://github.com/chillerlan/php-settings-container/actions?query=workflow%3A%22CI%22
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
**requires [composer](https://getcomposer.org)**
|
**requires [composer](https://getcomposer.org)**
|
||||||
|
|
||||||
*composer.json* (note: replace `dev-master` with a version boundary)
|
*composer.json* (note: replace `dev-main` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^2.1` - see [releases](https://github.com/chillerlan/php-settings-container/releases) for valid versions)
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2",
|
"php": "^7.4",
|
||||||
"chillerlan/php-settings-container": "^1.0"
|
"chillerlan/php-settings-container": "dev-main"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manual installation
|
|
||||||
Download the desired version of the package from [master](https://github.com/chillerlan/php-settings-container/archive/master.zip) or
|
|
||||||
[release](https://github.com/chillerlan/php-settings-container/releases) and extract the contents to your project folder. After that:
|
|
||||||
- run `composer install` to install the required dependencies and generate `/vendor/autoload.php`.
|
|
||||||
- if you use a custom autoloader, point the namespace `chillerlan\Settings` to the folder `src` of the package
|
|
||||||
|
|
||||||
Profit!
|
Profit!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
@ -140,7 +134,7 @@ var_dump($container->what); // -> md5 hash of "some value"
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
#### [`SettingsContainerAbstract`](https://github.com/chillerlan/php-settings-container/blob/master/src/SettingsContainerAbstract.php)
|
#### [`SettingsContainerAbstract`](https://github.com/chillerlan/php-settings-container/blob/main/src/SettingsContainerAbstract.php)
|
||||||
|
|
||||||
method | return | info
|
method | return | info
|
||||||
-------- | ---- | -----------
|
-------- | ---- | -----------
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-settings-container",
|
"name": "chillerlan/php-settings-container",
|
||||||
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.2+",
|
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-settings-container",
|
"homepage": "https://github.com/chillerlan/php-settings-container",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"php7", "helper", "container", "settings"
|
"php7", "helper", "container", "settings", "configuration"
|
||||||
],
|
],
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,12 @@
|
||||||
"source": "https://github.com/chillerlan/php-settings-container"
|
"source": "https://github.com/chillerlan/php-settings-container"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"php": "^7.4 || ^8.0",
|
||||||
"ext-json": "*"
|
"ext-json": "*"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8.4"
|
"phan/phan": "^5.3",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
@ -36,5 +37,14 @@
|
||||||
"chillerlan\\SettingsTest\\": "tests/",
|
"chillerlan\\SettingsTest\\": "tests/",
|
||||||
"chillerlan\\SettingsExamples\\": "examples/"
|
"chillerlan\\SettingsExamples\\": "examples/"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"phpunit": "@php vendor/bin/phpunit",
|
||||||
|
"phan": "@php vendor/bin/phan"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"lock": false,
|
||||||
|
"sort-packages": true,
|
||||||
|
"platform-check": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Auto generated API documentation
|
||||||
|
|
||||||
|
The API documentation can be auto generated with [phpDocumentor](https://www.phpdoc.org/).
|
||||||
|
There is an [online version available](https://chillerlan.github.io/php-settings-container/) via the [gh-pages branch](https://github.com/chillerlan/php-settings-container/tree/gh-pages) that is [automatically deployed](https://github.com/chillerlan/php-settings-container/deployments) on each push to main.
|
||||||
|
|
||||||
|
Locally created docs will appear in this directory. If you'd like to create local docs, please follow these steps:
|
||||||
|
|
||||||
|
- [download phpDocumentor](https://github.com/phpDocumentor/phpDocumentor/releases) v3+ as .phar archive
|
||||||
|
- run it in the repository root directory:
|
||||||
|
- on Windows `c:\path\to\php.exe c:\path\to\phpDocumentor.phar --config=phpdoc.xml`
|
||||||
|
- on Linux just `php /path/to/phpDocumentor.phar --config=phpdoc.xml`
|
||||||
|
- open [index.html](./index.html) in a browser
|
||||||
|
- profit!
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* @filesource advanced.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @author smiley <smiley@chillerlan.net>
|
* @author smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
|
@ -15,7 +14,7 @@ require_once __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
// from library #1
|
// from library #1
|
||||||
trait SomeOptions{
|
trait SomeOptions{
|
||||||
protected $foo;
|
protected string $foo = '';
|
||||||
|
|
||||||
// this method will be called in SettingsContainerAbstract::__construct() after the properties have been set
|
// this method will be called in SettingsContainerAbstract::__construct() after the properties have been set
|
||||||
protected function SomeOptions(){
|
protected function SomeOptions(){
|
||||||
|
@ -26,7 +25,7 @@ trait SomeOptions{
|
||||||
|
|
||||||
// from library #2
|
// from library #2
|
||||||
trait MoreOptions{
|
trait MoreOptions{
|
||||||
protected $bar = 'whatever'; // provide default values
|
protected string $bar = 'whatever'; // provide default values
|
||||||
}
|
}
|
||||||
|
|
||||||
$commonOptions = [
|
$commonOptions = [
|
||||||
|
@ -37,10 +36,16 @@ $commonOptions = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// now plug the several library options together to a single object
|
// now plug the several library options together to a single object
|
||||||
/** @var \chillerlan\Settings\SettingsContainerInterface $container */
|
|
||||||
$container = new class ($commonOptions) extends SettingsContainerAbstract{
|
/**
|
||||||
|
* @property string $foo
|
||||||
|
* @property string $bar
|
||||||
|
*/
|
||||||
|
class MySettings extends SettingsContainerAbstract{
|
||||||
use SomeOptions, MoreOptions; // ...
|
use SomeOptions, MoreOptions; // ...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$container = new MySettings($commonOptions);
|
||||||
|
|
||||||
var_dump($container->foo); // -> WHATEVER (constructor ran strtoupper on the value)
|
var_dump($container->foo); // -> WHATEVER (constructor ran strtoupper on the value)
|
||||||
var_dump($container->bar); // -> nothing
|
var_dump($container->bar); // -> nothing
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* @filesource simple.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @author smiley <smiley@chillerlan.net>
|
* @author smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<phpdoc>
|
||||||
|
<parser>
|
||||||
|
<target>docs</target>
|
||||||
|
<encoding>utf8</encoding>
|
||||||
|
<markers>
|
||||||
|
<item>TODO</item>
|
||||||
|
</markers>
|
||||||
|
</parser>
|
||||||
|
<transformer>
|
||||||
|
<target>docs</target>
|
||||||
|
</transformer>
|
||||||
|
<files>
|
||||||
|
<directory>src</directory>
|
||||||
|
<directory>tests</directory>
|
||||||
|
</files>
|
||||||
|
<transformations>
|
||||||
|
<template name="responsive-twig"/>
|
||||||
|
</transformations>
|
||||||
|
</phpdoc>
|
|
@ -1,35 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<ruleset name="codemasher/php-settings-container PMD ruleset"
|
|
||||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
|
||||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
|
|
||||||
<description>codemasher/php-settings-container PMD ruleset</description>
|
|
||||||
<exclude-pattern>*/examples/*</exclude-pattern>
|
|
||||||
<exclude-pattern>*/tests/*</exclude-pattern>
|
|
||||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
|
||||||
<rule ref="rulesets/cleancode.xml">
|
|
||||||
<exclude name="BooleanArgumentFlag"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/codesize.xml/CyclomaticComplexity">
|
|
||||||
<priority>1</priority>
|
|
||||||
<properties>
|
|
||||||
<property name="maximum" value="150" />
|
|
||||||
</properties>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/controversial.xml">
|
|
||||||
<exclude name="CamelCaseMethodName"/>
|
|
||||||
<exclude name="CamelCasePropertyName"/>
|
|
||||||
<exclude name="CamelCaseParameterName"/>
|
|
||||||
<exclude name="CamelCaseVariableName"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/design.xml">
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/naming.xml">
|
|
||||||
<exclude name="LongVariable"/>
|
|
||||||
<exclude name="ShortVariable"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/unusedcode.xml">
|
|
||||||
<exclude name="UnusedFormalParameter"/>
|
|
||||||
</rule>
|
|
||||||
</ruleset>
|
|
|
@ -1,22 +1,26 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0"?>
|
||||||
<phpunit backupGlobals="false"
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
backupStaticAttributes="false"
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
|
||||||
bootstrap="vendor/autoload.php"
|
bootstrap="vendor/autoload.php"
|
||||||
|
cacheResultFile=".build/phpunit.result.cache"
|
||||||
colors="true"
|
colors="true"
|
||||||
convertErrorsToExceptions="true"
|
verbose="true"
|
||||||
convertNoticesToExceptions="true"
|
|
||||||
convertWarningsToExceptions="true"
|
|
||||||
processIsolation="false"
|
|
||||||
stopOnFailure="false"
|
|
||||||
>
|
>
|
||||||
<filter>
|
<coverage processUncoveredFiles="true">
|
||||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
<include>
|
||||||
<directory suffix=".php">./src</directory>
|
<directory suffix=".php">./src</directory>
|
||||||
</whitelist>
|
</include>
|
||||||
</filter>
|
<report>
|
||||||
|
<clover outputFile=".build/coverage/clover.xml"/>
|
||||||
|
<xml outputDirectory=".build/coverage/coverage-xml"/>
|
||||||
|
</report>
|
||||||
|
</coverage>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="php-settings-container test suite">
|
<testsuite name="php-settings-container test suite">
|
||||||
<directory suffix=".php">./tests/</directory>
|
<directory suffix=".php">./tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
<logging>
|
||||||
|
<junit outputFile=".build/logs/junit.xml"/>
|
||||||
|
</logging>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Class SettingsContainerAbstract
|
* Class SettingsContainerAbstract
|
||||||
*
|
*
|
||||||
* @filesource SettingsContainerAbstract.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @package chillerlan\Settings
|
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 Smiley
|
* @copyright 2018 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -12,16 +10,15 @@
|
||||||
|
|
||||||
namespace chillerlan\Settings;
|
namespace chillerlan\Settings;
|
||||||
|
|
||||||
use Exception, ReflectionClass, ReflectionProperty;
|
use ReflectionClass, ReflectionProperty;
|
||||||
|
|
||||||
use function call_user_func, call_user_func_array, get_object_vars, json_decode, json_encode, method_exists, property_exists;
|
use function get_object_vars, json_decode, json_encode, method_exists, property_exists;
|
||||||
|
use const JSON_THROW_ON_ERROR;
|
||||||
|
|
||||||
abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SettingsContainerAbstract constructor.
|
* SettingsContainerAbstract constructor.
|
||||||
*
|
|
||||||
* @param iterable|null $properties
|
|
||||||
*/
|
*/
|
||||||
public function __construct(iterable $properties = null){
|
public function __construct(iterable $properties = null){
|
||||||
|
|
||||||
|
@ -35,8 +32,6 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
/**
|
/**
|
||||||
* calls a method with trait name as replacement constructor for each used trait
|
* calls a method with trait name as replacement constructor for each used trait
|
||||||
* (remember pre-php5 classname constructors? yeah, basically this.)
|
* (remember pre-php5 classname constructors? yeah, basically this.)
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
protected function construct():void{
|
protected function construct():void{
|
||||||
$traits = (new ReflectionClass($this))->getTraits();
|
$traits = (new ReflectionClass($this))->getTraits();
|
||||||
|
@ -45,7 +40,7 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
$method = $trait->getShortName();
|
$method = $trait->getShortName();
|
||||||
|
|
||||||
if(method_exists($this, $method)){
|
if(method_exists($this, $method)){
|
||||||
call_user_func([$this, $method]);
|
$this->{$method}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,18 +51,19 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
*/
|
*/
|
||||||
public function __get(string $property){
|
public function __get(string $property){
|
||||||
|
|
||||||
if(property_exists($this, $property) && !$this->isPrivate($property)){
|
if(!property_exists($this, $property) || $this->isPrivate($property)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if(method_exists($this, 'get_'.$property)){
|
$method = 'get_'.$property;
|
||||||
return call_user_func([$this, 'get_'.$property]);
|
|
||||||
|
if(method_exists($this, $method)){
|
||||||
|
return $this->{$method}();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->{$property};
|
return $this->{$property};
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
@ -77,8 +73,10 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(method_exists($this, 'set_'.$property)){
|
$method = 'set_'.$property;
|
||||||
call_user_func_array([$this, 'set_'.$property], [$value]);
|
|
||||||
|
if(method_exists($this, $method)){
|
||||||
|
$this->{$method}($value);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -95,10 +93,6 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal Checks if a property is private
|
* @internal Checks if a property is private
|
||||||
*
|
|
||||||
* @param string $property
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function isPrivate(string $property):bool{
|
protected function isPrivate(string $property):bool{
|
||||||
return (new ReflectionProperty($this, $property))->isPrivate();
|
return (new ReflectionProperty($this, $property))->isPrivate();
|
||||||
|
@ -152,12 +146,7 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
public function fromJSON(string $json):SettingsContainerInterface{
|
public function fromJSON(string $json):SettingsContainerInterface{
|
||||||
|
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
||||||
$data = json_decode($json, true); // as of PHP 7.3: JSON_THROW_ON_ERROR
|
|
||||||
|
|
||||||
if($data === false || $data === null){
|
|
||||||
throw new Exception('error while decoding JSON');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->fromIterable($data);
|
return $this->fromIterable($data);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +154,7 @@ abstract class SettingsContainerAbstract implements SettingsContainerInterface{
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
public function jsonSerialize(){
|
public function jsonSerialize():array{
|
||||||
return $this->toArray();
|
return $this->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Interface SettingsContainerInterface
|
* Interface SettingsContainerInterface
|
||||||
*
|
*
|
||||||
* @filesource SettingsContainerInterface.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @package chillerlan\Settings
|
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 Smiley
|
* @copyright 2018 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -22,9 +20,7 @@ interface SettingsContainerInterface extends JsonSerializable{
|
||||||
/**
|
/**
|
||||||
* Retrieve the value of $property
|
* Retrieve the value of $property
|
||||||
*
|
*
|
||||||
* @param string $property
|
* @return mixed|null
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function __get(string $property);
|
public function __get(string $property);
|
||||||
|
|
||||||
|
@ -33,69 +29,43 @@ interface SettingsContainerInterface extends JsonSerializable{
|
||||||
*
|
*
|
||||||
* @param string $property
|
* @param string $property
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function __set(string $property, $value):void;
|
public function __set(string $property, $value):void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if $property is set (aka. not null), excluding private properties
|
* Checks if $property is set (aka. not null), excluding private properties
|
||||||
*
|
|
||||||
* @param string $property
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function __isset(string $property):bool;
|
public function __isset(string $property):bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsets $property while avoiding private and non-existing properties
|
* Unsets $property while avoiding private and non-existing properties
|
||||||
*
|
|
||||||
* @param string $property
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function __unset(string $property):void;
|
public function __unset(string $property):void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see SettingsContainerInterface::toJSON()
|
* @see SettingsContainerInterface::toJSON()
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function __toString():string;
|
public function __toString():string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array representation of the settings object
|
* Returns an array representation of the settings object
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function toArray():array;
|
public function toArray():array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets properties from a given iterable
|
* Sets properties from a given iterable
|
||||||
*
|
|
||||||
* @param iterable $properties
|
|
||||||
*
|
|
||||||
* @return \chillerlan\Settings\SettingsContainerInterface
|
|
||||||
*/
|
*/
|
||||||
public function fromIterable(iterable $properties):SettingsContainerInterface;
|
public function fromIterable(iterable $properties):SettingsContainerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a JSON representation of the settings object
|
* Returns a JSON representation of the settings object
|
||||||
* @see \json_encode()
|
* @see \json_encode()
|
||||||
*
|
|
||||||
* @param int|null $jsonOptions
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function toJSON(int $jsonOptions = null):string;
|
public function toJSON(int $jsonOptions = null):string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets properties from a given JSON string
|
* Sets properties from a given JSON string
|
||||||
*
|
*
|
||||||
* @param string $json
|
|
||||||
*
|
|
||||||
* @return \chillerlan\Settings\SettingsContainerInterface
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
* @throws \JsonException
|
* @throws \JsonException
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Class ContainerTraitTest
|
* Class ContainerTraitTest
|
||||||
*
|
*
|
||||||
* @filesource ContainerTraitTest.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @package chillerlan\SettingsTest
|
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 Smiley
|
* @copyright 2018 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -13,74 +11,86 @@
|
||||||
namespace chillerlan\SettingsTest;
|
namespace chillerlan\SettingsTest;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Exception, TypeError;
|
use JsonException, TypeError;
|
||||||
|
use function sha1;
|
||||||
|
|
||||||
class ContainerTraitTest extends TestCase{
|
class ContainerTest extends TestCase{
|
||||||
|
|
||||||
public function testConstruct(){
|
public function testConstruct(){
|
||||||
$container = new TestContainer([
|
$container = new TestContainer([
|
||||||
'test1' => 'test1',
|
'test1' => 'test1',
|
||||||
'test2' => 'test2',
|
'test2' => true,
|
||||||
'test3' => 'test3',
|
'test3' => 'test3',
|
||||||
'test4' => 'test4',
|
'test4' => 'test4',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertSame('test1', $container->test1);
|
$this::assertSame('test1', $container->test1);
|
||||||
$this->assertSame('test2', $container->test2);
|
$this::assertSame(true, $container->test2);
|
||||||
$this->assertNull($container->test3);
|
$this::assertNull($container->test3);
|
||||||
$this->assertSame('test4', $container->test4);
|
$this::assertSame('test4', $container->test4);
|
||||||
|
|
||||||
$this->assertSame('success', $container->testConstruct);
|
$this::assertSame('success', $container->testConstruct);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGet(){
|
public function testGet(){
|
||||||
$container = new TestContainer;
|
$container = new TestContainer;
|
||||||
|
|
||||||
$this->assertSame('foo', $container->test1);
|
$this::assertSame('foo', $container->test1);
|
||||||
$this->assertNull($container->test2);
|
$this::assertNull($container->test2);
|
||||||
$this->assertNull($container->test3);
|
$this::assertNull($container->test3);
|
||||||
$this->assertNull($container->test4);
|
$this::assertNull($container->test4);
|
||||||
$this->assertNull($container->foo);
|
$this::assertNull($container->foo);
|
||||||
|
|
||||||
// isset test
|
// isset test
|
||||||
$this->assertTrue(isset($container->test1));
|
$this::assertTrue(isset($container->test1));
|
||||||
$this->assertFalse(isset($container->test2));
|
$this::assertFalse(isset($container->test2));
|
||||||
$this->assertFalse(isset($container->test3));
|
$this::assertFalse(isset($container->test3));
|
||||||
$this->assertFalse(isset($container->test4));
|
$this::assertFalse(isset($container->test4));
|
||||||
$this->assertFalse(isset($container->foo));
|
$this::assertFalse(isset($container->foo));
|
||||||
|
|
||||||
// custom getter
|
// custom getter
|
||||||
$container->test6 = 'foo';
|
$container->test6 = 'foo';
|
||||||
$this->assertSame(sha1('foo'), $container->test6);
|
$this::assertSame(sha1('foo'), $container->test6);
|
||||||
// nullable/isset test
|
// nullable/isset test
|
||||||
$container->test6 = null;
|
$container->test6 = null;
|
||||||
$this->assertFalse(isset($container->test6));
|
$this::assertFalse(isset($container->test6));
|
||||||
$this->assertSame('null', $container->test6);
|
$this::assertSame('null', $container->test6);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSet(){
|
public function testSet(){
|
||||||
$container = new TestContainer;
|
$container = new TestContainer;
|
||||||
$container->test1 = 'bar';
|
$container->test1 = 'bar';
|
||||||
$container->test2 = 'what';
|
$container->test2 = false;
|
||||||
$container->test3 = 'nope';
|
$container->test3 = 'nope';
|
||||||
|
|
||||||
$this->assertSame('bar', $container->test1);
|
$this::assertSame('bar', $container->test1);
|
||||||
$this->assertSame('what', $container->test2);
|
$this::assertSame(false, $container->test2);
|
||||||
$this->assertNull($container->test3);
|
$this::assertNull($container->test3);
|
||||||
|
|
||||||
// unset
|
// unset
|
||||||
unset($container->test1);
|
unset($container->test1);
|
||||||
$this->assertFalse(isset($container->test1));
|
$this::assertFalse(isset($container->test1));
|
||||||
|
|
||||||
// custom setter
|
// custom setter
|
||||||
$container->test5 = 'bar';
|
$container->test5 = 'bar';
|
||||||
$this->assertSame('bar_test5', $container->test5);
|
$this::assertSame('bar_test5', $container->test5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToArray(){
|
public function testToArray(){
|
||||||
$container = new TestContainer(['test1' => 'no', 'test2' => true, 'testConstruct' => 'success']);
|
$container = new TestContainer([
|
||||||
|
'test1' => 'no',
|
||||||
|
'test2' => true,
|
||||||
|
'testConstruct' => 'success',
|
||||||
|
]);
|
||||||
|
|
||||||
$this->assertSame(['test1' => 'no', 'test2' => true, 'testConstruct' => 'success', 'test4' => null, 'test5' => null, 'test6' => null], $container->toArray());
|
$this::assertSame([
|
||||||
|
'test1' => 'no',
|
||||||
|
'test2' => true,
|
||||||
|
'testConstruct' => 'success',
|
||||||
|
'test4' => null,
|
||||||
|
'test5' => null,
|
||||||
|
'test6' => null
|
||||||
|
], $container->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToJSON(){
|
public function testToJSON(){
|
||||||
|
@ -88,12 +98,12 @@ class ContainerTraitTest extends TestCase{
|
||||||
|
|
||||||
$expected = '{"test1":"no","test2":true,"testConstruct":"success","test4":null,"test5":null,"test6":null}';
|
$expected = '{"test1":"no","test2":true,"testConstruct":"success","test4":null,"test5":null,"test6":null}';
|
||||||
|
|
||||||
$this->assertSame($expected, $container->toJSON());
|
$this::assertSame($expected, $container->toJSON());
|
||||||
$this->assertSame($expected, (string)$container);
|
$this::assertSame($expected, (string)$container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFromJsonException(){
|
public function testFromJsonException(){
|
||||||
$this->expectException(Exception::class);
|
$this->expectException(JsonException::class);
|
||||||
(new TestContainer)->fromJSON('-');
|
(new TestContainer)->fromJSON('-');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Class TestContainer
|
* Class TestContainer
|
||||||
*
|
*
|
||||||
* @filesource TestContainer.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @package chillerlan\SettingsTest
|
|
||||||
* @author Smiley <smiley@chillerlan.net>
|
* @author Smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 Smiley
|
* @copyright 2018 Smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -25,5 +23,5 @@ use chillerlan\Settings\SettingsContainerAbstract;
|
||||||
class TestContainer extends SettingsContainerAbstract{
|
class TestContainer extends SettingsContainerAbstract{
|
||||||
use TestOptionsTrait;
|
use TestOptionsTrait;
|
||||||
|
|
||||||
private $test3 = 'what';
|
private string $test3 = 'what';
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Trait TestOptionsTrait
|
* Trait TestOptionsTrait
|
||||||
*
|
*
|
||||||
* @filesource TestOptionsTrait.php
|
|
||||||
* @created 28.08.2018
|
* @created 28.08.2018
|
||||||
* @package chillerlan\SettingsTest
|
|
||||||
* @author smiley <smiley@chillerlan.net>
|
* @author smiley <smiley@chillerlan.net>
|
||||||
* @copyright 2018 smiley
|
* @copyright 2018 smiley
|
||||||
* @license MIT
|
* @license MIT
|
||||||
|
@ -12,29 +10,31 @@
|
||||||
|
|
||||||
namespace chillerlan\SettingsTest;
|
namespace chillerlan\SettingsTest;
|
||||||
|
|
||||||
|
use function sha1;
|
||||||
|
|
||||||
trait TestOptionsTrait{
|
trait TestOptionsTrait{
|
||||||
|
|
||||||
protected $test1 = 'foo';
|
protected string $test1 = 'foo';
|
||||||
|
|
||||||
protected $test2;
|
protected ?bool $test2 = null;
|
||||||
|
|
||||||
protected $testConstruct;
|
protected string $testConstruct;
|
||||||
|
|
||||||
protected $test4;
|
protected ?string $test4 = null;
|
||||||
|
|
||||||
protected $test5;
|
protected ?string $test5 = null;
|
||||||
|
|
||||||
protected $test6;
|
protected ?string $test6 = null;
|
||||||
|
|
||||||
protected function TestOptionsTrait(){
|
protected function TestOptionsTrait():void{
|
||||||
$this->testConstruct = 'success';
|
$this->testConstruct = 'success';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function set_test5($value){
|
protected function set_test5($value):void{
|
||||||
$this->test5 = $value.'_test5';
|
$this->test5 = $value.'_test5';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function get_test6(){
|
protected function get_test6():string{
|
||||||
return $this->test6 === null
|
return $this->test6 === null
|
||||||
? 'null'
|
? 'null'
|
||||||
: sha1($this->test6);
|
: sha1($this->test6);
|
||||||
|
|
|
@ -72,34 +72,34 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-qrcode",
|
"name": "chillerlan/php-qrcode",
|
||||||
"version": "3.4.1",
|
"version": "4.3.3",
|
||||||
"version_normalized": "3.4.1.0",
|
"version_normalized": "4.3.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/chillerlan/php-qrcode.git",
|
"url": "https://github.com/chillerlan/php-qrcode.git",
|
||||||
"reference": "468603b687a5fe75c1ff33857a45f1726c7b95a9"
|
"reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/468603b687a5fe75c1ff33857a45f1726c7b95a9",
|
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/6356b246948ac1025882b3f55e7c68ebd4515ae3",
|
||||||
"reference": "468603b687a5fe75c1ff33857a45f1726c7b95a9",
|
"reference": "6356b246948ac1025882b3f55e7c68ebd4515ae3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"chillerlan/php-settings-container": "^1.2.2",
|
"chillerlan/php-settings-container": "^2.1",
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^7.4 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phan/phan": "^3.2.2",
|
"phan/phan": "^5.3",
|
||||||
"phpunit/phpunit": "^8.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"setasign/fpdf": "^1.8.2"
|
"setasign/fpdf": "^1.8.2"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
|
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
|
||||||
"setasign/fpdf": "Required to use the QR FPDF output."
|
"setasign/fpdf": "Required to use the QR FPDF output."
|
||||||
},
|
},
|
||||||
"time": "2021-09-03T17:54:45+00:00",
|
"time": "2021-11-25T22:38:09+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
|
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A QR code generator. PHP 7.2+",
|
"description": "A QR code generator. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-qrcode",
|
"homepage": "https://github.com/chillerlan/php-qrcode",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"phpqrcode",
|
"phpqrcode",
|
||||||
|
@ -135,6 +135,10 @@
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"qrcode-generator"
|
"qrcode-generator"
|
||||||
],
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/chillerlan/php-qrcode/issues",
|
||||||
|
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.3"
|
||||||
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
||||||
|
@ -149,27 +153,28 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "chillerlan/php-settings-container",
|
"name": "chillerlan/php-settings-container",
|
||||||
"version": "1.2.2",
|
"version": "2.1.3",
|
||||||
"version_normalized": "1.2.2.0",
|
"version_normalized": "2.1.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/chillerlan/php-settings-container.git",
|
"url": "https://github.com/chillerlan/php-settings-container.git",
|
||||||
"reference": "d1b5284d6eb3a767459738bb0b20073f0cb3eeaf"
|
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/d1b5284d6eb3a767459738bb0b20073f0cb3eeaf",
|
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/125dd573b45ffc7cabecf385986a356ba2c6f602",
|
||||||
"reference": "d1b5284d6eb3a767459738bb0b20073f0cb3eeaf",
|
"reference": "125dd573b45ffc7cabecf385986a356ba2c6f602",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^7.4 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8.4"
|
"phan/phan": "^5.3",
|
||||||
|
"phpunit/phpunit": "^9.5"
|
||||||
},
|
},
|
||||||
"time": "2021-09-03T17:33:25+00:00",
|
"time": "2022-03-09T13:18:58+00:00",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"installation-source": "dist",
|
"installation-source": "dist",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
@ -188,14 +193,19 @@
|
||||||
"homepage": "https://github.com/codemasher"
|
"homepage": "https://github.com/codemasher"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.2+",
|
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
|
||||||
"homepage": "https://github.com/chillerlan/php-settings-container",
|
"homepage": "https://github.com/chillerlan/php-settings-container",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"PHP7",
|
"PHP7",
|
||||||
"Settings",
|
"Settings",
|
||||||
|
"configuration",
|
||||||
"container",
|
"container",
|
||||||
"helper"
|
"helper"
|
||||||
],
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/chillerlan/php-settings-container/issues",
|
||||||
|
"source": "https://github.com/chillerlan/php-settings-container"
|
||||||
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
'name' => '__root__',
|
'name' => '__root__',
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => 'b148d2f5153f9711120b3a3ac50ee84509c9cdfb',
|
'reference' => '1e668c7572346f6adc47cc0359c2fd616481e730',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
'__root__' => array(
|
'__root__' => array(
|
||||||
'pretty_version' => 'dev-master',
|
'pretty_version' => 'dev-master',
|
||||||
'version' => 'dev-master',
|
'version' => 'dev-master',
|
||||||
'reference' => 'b148d2f5153f9711120b3a3ac50ee84509c9cdfb',
|
'reference' => '1e668c7572346f6adc47cc0359c2fd616481e730',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
@ -29,18 +29,18 @@
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'chillerlan/php-qrcode' => array(
|
'chillerlan/php-qrcode' => array(
|
||||||
'pretty_version' => '3.4.1',
|
'pretty_version' => '4.3.3',
|
||||||
'version' => '3.4.1.0',
|
'version' => '4.3.3.0',
|
||||||
'reference' => '468603b687a5fe75c1ff33857a45f1726c7b95a9',
|
'reference' => '6356b246948ac1025882b3f55e7c68ebd4515ae3',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../chillerlan/php-qrcode',
|
'install_path' => __DIR__ . '/../chillerlan/php-qrcode',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'chillerlan/php-settings-container' => array(
|
'chillerlan/php-settings-container' => array(
|
||||||
'pretty_version' => '1.2.2',
|
'pretty_version' => '2.1.3',
|
||||||
'version' => '1.2.2.0',
|
'version' => '2.1.3.0',
|
||||||
'reference' => 'd1b5284d6eb3a767459738bb0b20073f0cb3eeaf',
|
'reference' => '125dd573b45ffc7cabecf385986a356ba2c6f602',
|
||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../chillerlan/php-settings-container',
|
'install_path' => __DIR__ . '/../chillerlan/php-settings-container',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
$issues = array();
|
$issues = array();
|
||||||
|
|
||||||
if (!(PHP_VERSION_ID >= 70200)) {
|
if (!(PHP_VERSION_ID >= 70400)) {
|
||||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
|
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.4.0". You are running ' . PHP_VERSION . '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($issues) {
|
if ($issues) {
|
||||||
|
|
Loading…
Reference in New Issue