From 6481ffe9c8a7a158f0d19f5ecda2724a4ff4f4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bouc=CC=8Cek?= Date: Wed, 5 May 2021 11:28:00 +0200 Subject: [PATCH 1/7] Add MIT license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bab7c54 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Redbit s.r.o. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 87e20d4834800b2e8538fee0da5fc4fbb45e0d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bouc=CC=8Cek?= Date: Wed, 5 May 2021 11:33:24 +0200 Subject: [PATCH 2/7] Composer: Add JB as co-author --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index b2befe6..374c209 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,10 @@ { "name": "Martin Dostál", "email": "martin.dostal@redbit.cz" + }, + { + "name": "Jakub Bouček", + "email": "jakub.boucek@redbit.cz" } ], "require": { From 353540c4bbf352c17e7995dc1e6779f617176399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bouc=CC=8Cek?= Date: Wed, 5 May 2021 13:43:09 +0200 Subject: [PATCH 3/7] Add Readme --- README.md | 83 +++++++++++++++++++++++++++++++++ examples/pcntl/async-signal.php | 1 - 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..60cdb13 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# Redbit Utils + +Lightweight utilities for logging, IO, and Unix-like process signal + +## Installation + +Install via Composer: + +```shell +composer install redbitcz/utils +``` + +## Requirements +Package requires PHP version 7.3 and above. + +For handling Unix-like process signals requires the `pcntl` and `posix` PHP extensions. Without that support +related method call will be siletly ignored. + +## Usage + +### `Logger` + +The `\Redbitcz\Utils\Log\Logger` class is implementation of PSR-3 logger interface and it decorates each +logger record with time and log severity name. + +Example: +``` +[2021-05-05 11:49:36] Logged message 1 +[2021-05-05 11:49:38] Another logged message +``` + +Logger requires Writer `\Redbitcz\Utils\IO\IOutStream` instance. Package contains few several types +of Writer implementations which are different by the log target (console, general output, standard output, HTML output, +or file). + +See [`Logger` example](examples/log/output-logger.php). + +### `Progress` +The `\Redbitcz\Utils\Log\Progress` class is simple generator of progress status to reporting progress of operations. +In additive is added the time spent is each step and whole operation. + +Example: +```shell +[2021-05-05 10:40:06] DEBUG: [ 0.000s/ 0.000] step 1/9: Logged step 1 +[2021-05-05 10:40:06] DEBUG: [ 0.000s/ 0.000] step 2/9: Another logged message +[2021-05-05 10:40:06] DEBUG: [ 0.371s/ 0.371] step 3/9: Foo +[2021-05-05 10:40:10] DEBUG: [ 3.900s/ 4.271] step 4/9: Bar +[2021-05-05 10:40:10] DEBUG: [ 0.000s/ 4.271] step 5/9: Foo Bar +[2021-05-05 10:40:10] DEBUG: [ 0.000s/ 4.271] step 6/9: Foo Baz +[2021-05-05 10:40:10] DEBUG: [ 0.000s/ 4.271] step 7/9: Foo comes to the Bar +[2021-05-05 10:40:11] DEBUG: [ 0.212s/ 4.483] step 8/9: Hey Donald, get off that chandelier! +[2021-05-05 10:40:11] DEBUG: [ 0.001s/ 4.484] step 9/9: All Done +``` +See [`Progress` example](examples/log/progress.php). + +## `ProcessTerminationLock` + +The `\Redbitcz\Utils\Process\ProcessTerminationLock` class is simple mechanism how to prevent (rspt. delay) unexpected +exit of PHP process during operation processing. It's recommended to workers to prevent break during processing a job +and similar usage in processes managed by a Process Control system (`systemd`, `supervisor`, etc.). + +Example: + +```php +while(true) { + $job = $worker->waitToJob(); + + ProcessTerminationLock::lock(); // Lock termination to prevent break job processing + + //... long job processing + + ProcessTerminationLock::unlock(); // Unlock +} +``` +See [`ProcessTerminationLock` example](examples/process/termination-lock.php). + +## License +The MIT License (MIT). Please see [License File](LICENSE) for more information. + +## Contact +Redbit s.r.o. - @redbitcz - info@redbit.cz + + diff --git a/examples/pcntl/async-signal.php b/examples/pcntl/async-signal.php index 8b019dc..9e34199 100644 --- a/examples/pcntl/async-signal.php +++ b/examples/pcntl/async-signal.php @@ -1,5 +1,4 @@ Date: Wed, 5 May 2021 14:40:42 +0200 Subject: [PATCH 4/7] Add BitwiseVariator --- README.md | 50 +++++++++++++++- examples/bitwise/bitwise-variator.php | 28 +++++++++ src/Bitwise/BitwiseFilter.php | 40 +++++++++++++ src/Bitwise/BitwiseVariator.php | 61 ++++++++++++++++++++ src/Bitwise/BitwiseVariatorFilter.php | 29 ++++++++++ src/Bitwise/BitwiseVariatorFilterLogicOr.php | 34 +++++++++++ 6 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 examples/bitwise/bitwise-variator.php create mode 100644 src/Bitwise/BitwiseFilter.php create mode 100644 src/Bitwise/BitwiseVariator.php create mode 100644 src/Bitwise/BitwiseVariatorFilter.php create mode 100644 src/Bitwise/BitwiseVariatorFilterLogicOr.php diff --git a/README.md b/README.md index 60cdb13..36ffd4f 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Example: ``` See [`Progress` example](examples/log/progress.php). -## `ProcessTerminationLock` +### `ProcessTerminationLock` The `\Redbitcz\Utils\Process\ProcessTerminationLock` class is simple mechanism how to prevent (rspt. delay) unexpected exit of PHP process during operation processing. It's recommended to workers to prevent break during processing a job @@ -74,6 +74,54 @@ while(true) { ``` See [`ProcessTerminationLock` example](examples/process/termination-lock.php). +### `BitwiseVariator` +Classes in `\Redbitcz\Utils\Bitwise` namespace provides filtered bit variations generator over +[Bitwise values](https://en.wikipedia.org/wiki/Bitwise_operation). + +That mean, when you have bits `1011`, variator generates all bits variations. + +```php +$variations = BitwiseVariator::create(0b1011)->variate(); +``` + +| Variation for bits `1011` | +|--------------------------:| +| `0000` | +| `0001` | +| `0010` | +| `0011` | +| `1000` | +| `1001` | +| `1010` | +| `1011` | + +#### Filters + +`BitwiseVariator` class provide filter to select variations with(out) some bits only. + +```php +$variations = BitwiseVariator::create(0b1011)->must(0b0010)->variate(); +``` + +| Variation for bits `1011` with bite `0010` | +|-------------------------------------------:| +| `0010` | +| `0011` | +| `1010` | +| `1011` | + + +```php +$variations = BitwiseVariator::create(0b1011)->mustNot(0b0010)->variate(); +``` + +| Variation for bits `1011` without bite `0010` | +|----------------------------------------------:| +| `0000` | +| `0001` | +| `1000` | +| `1001` | + ## License The MIT License (MIT). Please see [License File](LICENSE) for more information. diff --git a/examples/bitwise/bitwise-variator.php b/examples/bitwise/bitwise-variator.php new file mode 100644 index 0000000..2f297fe --- /dev/null +++ b/examples/bitwise/bitwise-variator.php @@ -0,0 +1,28 @@ +variate(); +foreach ($variations as $i => $variation) { + echo sprintf("% 2s. variation: %04s (decimal: % 3s)\n", $i+1, decbin($variation), $variation); +} + +echo "\nVariations of 1011 with bit 0010 =====================\n"; +$variations = BitwiseVariator::create(0b1011)->must(0b0010)->variate(); +foreach ($variations as $i => $variation) { + echo sprintf("% 2s. variation: %04s (decimal: % 3s)\n", $i+1, decbin($variation), $variation); +} + +echo "\nVariations of 1011 without bit 0010 ==================\n"; +$variations = BitwiseVariator::create(0b1011)->mustNot(0b0010)->variate(); +foreach ($variations as $i => $variation) { + echo sprintf("% 2s. variation: %04s (decimal: % 3s)\n", $i+1, decbin($variation), $variation); +} + + + diff --git a/src/Bitwise/BitwiseFilter.php b/src/Bitwise/BitwiseFilter.php new file mode 100644 index 0000000..4b55b33 --- /dev/null +++ b/src/Bitwise/BitwiseFilter.php @@ -0,0 +1,40 @@ +filter |= $bite; + $this->values |= $bite; + return $this; + } + + public function mustNot(int $bite): self + { + $this->filter |= $bite; + $this->values &= ~$bite; + return $this; + } + + public function getFilter(): int + { + return $this->filter; + } + + /** + * @return int[] + */ + public function getValues(): array + { + return [$this->values]; + } +} diff --git a/src/Bitwise/BitwiseVariator.php b/src/Bitwise/BitwiseVariator.php new file mode 100644 index 0000000..ee02f84 --- /dev/null +++ b/src/Bitwise/BitwiseVariator.php @@ -0,0 +1,61 @@ +filter(); + } + + public function __construct(int $bits) + { + $this->bits = $bits; + } + + public function filter(): BitwiseVariatorFilter + { + return new BitwiseVariatorFilter($this); + } + + /** + * @param int $filter + * @param int[] $values + * @return int[] + */ + public function variate(int $filter, array $values): array + { + $filter &= $this->bits; + $cleaner = static function ($value) use ($filter) { + return $value & $filter; + }; + $values = array_map($cleaner, $values); + $qbits = $this->bits & ~$filter; + + $variations = $values; + for ($weight = 1; $weight <= $qbits; $weight <<= 1) { + $bit = $qbits & $weight; + if ($bit) { + foreach ($variations as $variation) { + $variations[] = $variation | $bit; + } + } + } + return $variations; + } + + /** + * @return int + * @internal + */ + public function getBits(): int + { + return $this->bits; + } +} diff --git a/src/Bitwise/BitwiseVariatorFilter.php b/src/Bitwise/BitwiseVariatorFilter.php new file mode 100644 index 0000000..6baa016 --- /dev/null +++ b/src/Bitwise/BitwiseVariatorFilter.php @@ -0,0 +1,29 @@ +variator = $variator; + } + + public function or(): BitwiseVariatorFilterLogicOr + { + return new BitwiseVariatorFilterLogicOr($this->variator, $this); + } + + /** + * @return int[] + */ + public function variate(): array + { + return $this->variator->variate($this->getFilter(), $this->getValues()); + } +} diff --git a/src/Bitwise/BitwiseVariatorFilterLogicOr.php b/src/Bitwise/BitwiseVariatorFilterLogicOr.php new file mode 100644 index 0000000..7cccb3a --- /dev/null +++ b/src/Bitwise/BitwiseVariatorFilterLogicOr.php @@ -0,0 +1,34 @@ +previous = $previous; + } + + public function getFilter(): int + { + return parent::getFilter() | $this->previous->getFilter(); + } + + /** + * @return int[] + */ + public function getValues(): array + { + $variator = new BitwiseVariator($this->getFilter() & $this->variator->getBits()); + + $meVariations = $variator->variate(parent::getFilter(), parent::getValues()); + $prevVariations = $variator->variate($this->previous->getFilter(), $this->previous->getValues()); + return array_unique(array_merge($meVariations, $prevVariations)); + } +} From 6d814b02b9cf643e3a8073eb32477e81bdb6d779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bouc=CC=8Cek?= Date: Mon, 24 May 2021 10:29:00 +0200 Subject: [PATCH 5/7] Bitwise: Disable PhpStan on Bitwise Tests because cyclics iterators --- phpstan.neon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/phpstan.neon b/phpstan.neon index 928362d..bf41108 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,4 +1,5 @@ parameters: excludes_analyse: - 'tests/Pcntl/*' # because crashes PhpStan - - 'tests/Process/*' # because crashes PhpStan \ No newline at end of file + - 'tests/Process/*' # because crashes PhpStan + - 'tests/Bitwise/*' # because hacking tests From 4e4943462f7b376b0d0ea5cbc1c319a5d7c7fdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bouc=CC=8Cek?= Date: Tue, 25 May 2021 15:44:15 +0200 Subject: [PATCH 6/7] Bitwise: Add tests, make Filter abstract, small fixes --- phpstan.neon | 2 +- src/Bitwise/BitwiseFilter.php | 11 +- tests/Bitwise/BitwiseVariatorTest.php | 168 ++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 2 deletions(-) create mode 100644 tests/Bitwise/BitwiseVariatorTest.php diff --git a/phpstan.neon b/phpstan.neon index bf41108..f9f5b33 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,4 +2,4 @@ parameters: excludes_analyse: - 'tests/Pcntl/*' # because crashes PhpStan - 'tests/Process/*' # because crashes PhpStan - - 'tests/Bitwise/*' # because hacking tests + - 'tests/Bitwise/*' # because test with too deep recurtion over data structures diff --git a/src/Bitwise/BitwiseFilter.php b/src/Bitwise/BitwiseFilter.php index 4b55b33..c26e61e 100644 --- a/src/Bitwise/BitwiseFilter.php +++ b/src/Bitwise/BitwiseFilter.php @@ -4,13 +4,18 @@ namespace Redbitcz\Utils\Bitwise; -class BitwiseFilter +abstract class BitwiseFilter { /** @var int */ private $filter = 0; + /** @var int */ private $values = 0; + /** + * @param int $bite + * @return static + */ public function must(int $bite): self { $this->filter |= $bite; @@ -18,6 +23,10 @@ public function must(int $bite): self return $this; } + /** + * @param int $bite + * @return static + */ public function mustNot(int $bite): self { $this->filter |= $bite; diff --git a/tests/Bitwise/BitwiseVariatorTest.php b/tests/Bitwise/BitwiseVariatorTest.php new file mode 100644 index 0000000..9a8833d --- /dev/null +++ b/tests/Bitwise/BitwiseVariatorTest.php @@ -0,0 +1,168 @@ +variate()); + } + + public function getVariationsFilterMustData(): array + { + return [ + [0b0000, 0b0000, [0b0000]], + [0b0011, 0b0000, [0b0000, 0b0001, 0b0010, 0b0011]], + [0b0011, 0b0010, [0b0010, 0b0011]], + [0b0011, 0b1010, [0b0010, 0b0011]], // Filter out of bits + ]; + } + + /** + * @dataProvider getVariationsFilterMustData + * @param int $bits + * @param int $must + * @param int[] $variations + */ + public function testVariationsFilterMust(int $bits, int $must, array $variations): void + { + Assert::equal($variations, BitwiseVariator::create($bits)->must($must)->variate()); + } + + public function getVariationsFilterCombinedMustData(): array + { + return [ + [0b11011, 0b00001, 0b00010, 0b01000, [0b01011, 0b11011]], + [0b00011, 0b00000, 0b00000, 0b00000, [0b00000, 0b00001, 0b00010, 0b00011]], + [0b00011, 0b00010, 0b00010, 0b00010, [0b00010, 0b00011]], + [0b00011, 0b00010, 0b00010, 0b00100, [0b00010, 0b00011]], // Filter out of bits + ]; + } + + /** + * @dataProvider getVariationsFilterCombinedMustData + * @param int $bits + * @param int $must1 + * @param int $must2 + * @param int $must3 + * @param int[] $variations + */ + public function testVariationsFilterCombinedMust( + int $bits, + int $must1, + int $must2, + int $must3, + array $variations + ): void { + Assert::equal($variations, BitwiseVariator::create($bits)->must($must1)->must($must2)->must($must3)->variate()); + } + + public function getVariationsFilterMustNotData(): array + { + return [ + [0b0000, 0b0000, [0b0000]], + [0b0011, 0b0000, [0b0000, 0b0001, 0b0010, 0b0011]], + [0b0011, 0b0010, [0b0000, 0b0001]], + [0b0011, 0b1010, [0b0000, 0b0001]], // Filter out of bits + ]; + } + + /** + * @dataProvider getVariationsFilterMustNotData + * @param int $bits + * @param int $mustNot + * @param int[] $variations + */ + public function testVariationsFilterMustNot(int $bits, int $mustNot, array $variations): void + { + Assert::equal($variations, BitwiseVariator::create($bits)->mustNot($mustNot)->variate()); + } + + public function getVariationsFilterCombinedMustNotData(): array + { + return [ + [0b11011, 0b00001, 0b00010, 0b01000, [0b00000, 0b10000]], + [0b00011, 0b00000, 0b00000, 0b00000, [0b00000, 0b00001, 0b00010, 0b00011]], + [0b00011, 0b00010, 0b00010, 0b00010, [0b00000, 0b00001]], + [0b00011, 0b00010, 0b00010, 0b00100, [0b00000, 0b00001]], // Filter out of bits + ]; + } + + /** + * @dataProvider getVariationsFilterCombinedMustNotData + * @param int $bits + * @param int $mustNot1 + * @param int $mustNot2 + * @param int $mustNot3 + * @param int[] $variations + */ + public function testVariationsFilterCombinedMustNot( + int $bits, + int $mustNot1, + int $mustNot2, + int $mustNot3, + array $variations + ): void { + Assert::equal( + $variations, + BitwiseVariator::create($bits)->mustNot($mustNot1)->mustNot($mustNot2)->mustNot($mustNot3)->variate() + ); + } + + /** + * @return int[][] + */ + public function getVariationGeneratorData(): array + { + return [ + [1, 0b0000], + [2, 0b0001], + [4, 0b0011], + [8, 0b1011], + ]; + } + + /** + * @dataProvider getVariationGeneratorData + * @param int $bits + * @param int $variationsCount + */ + public function testVariationGenerator(int $variationsCount, int $bits): void + { + $count = 0; + foreach (BitwiseVariator::create($bits)->variate() as $variate) { + $count++; + } + + Assert::same($variationsCount, $count); + } + +} + +(new BitwiseVariatorTest)->run(); From 55e696dfdc27d86557b2d8c1bc7d99f8e4df9e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Dost=C3=A1l?= Date: Wed, 26 May 2021 13:41:56 +0200 Subject: [PATCH 7/7] Null writer --- src/IO/NullWriter.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/IO/NullWriter.php diff --git a/src/IO/NullWriter.php b/src/IO/NullWriter.php new file mode 100644 index 0000000..a9ec090 --- /dev/null +++ b/src/IO/NullWriter.php @@ -0,0 +1,16 @@ +