| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- # `QROutputAbstract`
- The abstract class `QROutputAbstract` contains several commonly used methods and properties and can be used as a basis for a custom output class.
- ## Properties
- ### `$options` and `$matrix`
- The `QROptions` and `QRMatrix` instances that were passed to the constructor of the output class.
- Both objects can be modified during runtime, for example to override settings or add matrix modifications.
- ### `$moduleCount`, `$scale` and `$length`
- These are convenience variables mostly to avoid multiple method calls to `QRMatrix::getSize()` and `QROptions::__get('scale')` inside loops,
- the `$length` is calculated from the aforementioned values (`$moduleCount * $scale`).
- The method `setMatrixDimensions()` can be called to update these 3 values after the matrix has been modified, e.g. by adding a quiet zone during output.
- ### `$moduleValues`
- The finalized map of `$M_TYPE` to value for the current output. This map is generated during invocation of the output class via `setModuleValues()`.
- ### Copies of `QROptions` values
- Some values from the `QROptions` instance are copied to properties to avoid calling the magic getters in long loops for a significant performance increase, e.g. in the module collector.
- Currently, the following values are copied via `copyVars()` during invocation: `$connectPaths`, `$excludeFromConnect`, `$eol`,
- `$drawLightModules`, `$drawCircularModules`, `$keepAsSquare`, `$circleRadius` (and additionally `$circleDiameter`).
- ## Methods
- ### `setModuleValues()`
- This method calls the abstract/interface methods `moduleValueIsValid()`, `prepareModuleValue()` and `getDefaultModuleValue()` to prepare the module values map.
- ### `moduleValueIsValid()`
- This method is declared in the `QROutputInterface` and needs to be implemented by the output class; it is `static` so that it can be called before invocation.
- The purpose is to determine whether the given `mixed` input is a valid module value for the current output class and returns `bool`.
- It's also useful to check values from `QROptions` such as `$bgColor` or `$transparencyColor`.
- Below is a pseudo implementation, check the code of the several output classes for actual implementations
- (e.g. [`QRImagick::moduleValueIsValid()`](https://github.com/chillerlan/php-qrcode/blob/4bd4b59fdec72397f5b1f70da9cadcb76764191b/src/Output/QRImagick.php#L68-L96))
- ```php
- class MyOutput extends QROutputAbstract{
- public static function moduleValueIsValid(mixed $value):bool{
- // check the type of the input value first
- if(!is_expected_type($value)){
- return false;
- }
- // do some more checks to determine the value
- if(!is_somehow_valid($value)){
- return false;
- }
- // looks like we got a match
- return true;
- }
- }
- ```
- ### `prepareModuleValue()`
- This method prepares the final replacement value from the given input.
- It might still be necessary to validate the given value despite it being checked earlier by `moduleValueIsValid()` -
- if nothing helps, this is a good place to throw an exception.
- Below a pseudo implementation example (see [`QRGdImage::prepareModuleValue()`](https://github.com/chillerlan/php-qrcode/blob/4bd4b59fdec72397f5b1f70da9cadcb76764191b/src/Output/QRGdImage.php#L138-L158)):
- ```php
- class MyOutput extends QROutputAbstract{
- protected function prepareModuleValue(mixed $value):mixed{
- // extended validation to make sure the values are valid for output
- // e.g. examine array values, clamp etc.
- if(!is_valid($value)){
- throw new QRCodeOutputException('invalid module value');
- }
- return $this->modifyValue($value);
- }
- }
- ```
- ### `getDefaultModuleValue()`
- Finally, setting a default value is required, in case a value for an `$M_TYPE` is not set or it's invalid.
- ```php
- class MyOutput extends QROutputAbstract{
- protected function getDefaultModuleValue(bool $isDark):mixed{
- $defaultValue = ($isDark === true)
- ? 'default value for dark'
- : 'default value for light';
- return $this->prepareModuleValue($defaultValue);
- }
- }
- ```
- ### `getModuleValue()` and `getModuleValueAt()`
- Both methods return a module value, the main difference is that `getModuleValueAt()` is a convenience method
- that makes an extra call to retrieve the `$M_TYPE` from the given matrix coordinate to return the value via `getModuleValue()`.
- A `foreach` loop over the matrix gives you the key (coordinate) *and* value of an array element:
- ```php
- class MyOutput extends QROutputAbstract{
- public function dump(string $file = null):string{
- $lines = [];
- foreach($this->matrix->getMatrix() as $y => $row){
- $lines[$y] = '';
- foreach($row as $x => $M_TYPE){
- $lines[$y] .= $this->getModuleValue($M_TYPE);
- }
- }
- return implode($this->options->eol, $lines);
- }
- }
- ```
- However, sometimes you might happen to use a `for` loop instead. The `for` loop leaves you only with the matrix coordinates, so you need to call `getModuleValueAt()`:
- ```php
- class MyOutput extends QROutputAbstract{
- public function dump(string $file = null):string{
- $lines = [];
- for($y = 0; $y < $this->moduleCount; $y++){
- $lines[$y] = '';
- for($x = 0; $x < $this->moduleCount; $x++){
- $lines[$y] .= $this->getModuleValueAt($x, $y);
- }
- }
- return implode($this->options->eol, $lines);
- }
- }
- ```
- ### `setMatrixDimensions()`
- As mentioned before, this method is supposed to set the values for the properties `$moduleCount`, `$scale` and `$length`.
- It is called in the constructor during invocation, but it might be necessary to call it again if the size of the matrix was changed in the output class
- (see [the round quiet zone example](https://github.com/chillerlan/php-qrcode/blob/99b1f9cf454ab1316cb643950a71caed3a6c0f5a/examples/svgRoundQuietzone.php#L38-L44) for a use case).
- ### `getOutputDimensions()`
- This method provides a simple way for consistent width/height values for the output (if applicable) which then can be changed by simply overriding this method.
- It returns a 2-element array that contains the values in a format that can be used by the output class, which is `QROutputAbstract::$length` (`$moduleCount * $scale`):
- ```php
- [$width, $height] = $this->getOutputDimensions();
- ```
- The output width and height can be changed in all places by simply overriding the method:
- ```php
- class MyOutput extends QROutputAbstract{
- protected function getOutputDimensions():array{
- // adjust the height in order to add something under the QR Code
- return [$this->length, ($this->length + 69)];
- }
- }
- ```
- ### `collectModules()`
- The module collector is particularly useful for plain text based file formats, for example the various markup languages like SVG and HTML or other structured file formats such as EPS.
- This method calls a method `moduleTransform()` internally with 4 parameters: the module coordinates `$x` and `$y`, the `$M_TYPE` and `$M_TYPE_LAYER`.
- The transform method should return a value that is valid for a single module of the QR matrix, or `null` if no transform was performed for the current module.
- The `$M_TYPE_LAYER` is a copy of the `$M_TYPE` that represents the array key of the returned array and that may have been reassigned in the collector to another path layer, e.g. through `QROptions::$connectPaths`.
- ```php
- class MyOutput extends QROutputAbstract{
- public function dump(string $file = null):string{
- // collect the modules for the path elements
- $paths = $this->collectModules();
- // loop over the paths
- foreach($paths as $M_TYPE_LAYER => &$path){
- if($path === []){
- continue;
- }
- $path = implode($this->options->eol, $path);
- }
- return implode($this->options->eol, $paths);
- }
- // this method must be implemented/overridden if your output class uses the module collector
- protected function moduleTransform(int $x, int $y, int $M_TYPE, int $M_TYPE_LAYER):string{
- return sprintf('%d %d %012b', $x, $y, $M_TYPE);
- }
- }
- ```
- Sometimes it can be necessary to override `collectModules()` in order to apply special effects such as random colors - you can find some implementations in [the SVG examples](https://github.com/chillerlan/php-qrcode/tree/main/examples).
- ### `saveToFile()` and `toBase64DataURI()`
- The void method `saveToFile()` takes a data blob and the `$file` given in `QROutputInterface::dump()` and save to the path if it is not `null` - the file path itself is not checked except for writability.
- The final output can be transformed to a [base64 data URI](https://en.wikipedia.org/wiki/Data_URI_scheme) with `toBase64DataURI()`, where the data blob and a valid mime type as parameters - the mime type is not checked.
- ```php
- class MyOutput extends QROutputAbstract{
- public function dump(string $file = null):string{
- $output = 'qrcode data string';
- // save the plain data to file
- $this->saveToFile($output, $file);
- // base64 encoding may be called optionally
- if($this->options->outputBase64){
- $output = $this->toBase64DataURI($output, 'text/plain');
- }
- return $output;
- }
- }
- ```
|