diff options
Diffstat (limited to 'vendor/ipl/stdlib')
41 files changed, 2439 insertions, 0 deletions
diff --git a/vendor/ipl/stdlib/LICENSE b/vendor/ipl/stdlib/LICENSE new file mode 100644 index 0000000..58005ec --- /dev/null +++ b/vendor/ipl/stdlib/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2018 Icinga GmbH https://www.icinga.com + +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. diff --git a/vendor/ipl/stdlib/composer.json b/vendor/ipl/stdlib/composer.json new file mode 100644 index 0000000..cc93968 --- /dev/null +++ b/vendor/ipl/stdlib/composer.json @@ -0,0 +1,22 @@ +{ + "name": "ipl/stdlib", + "description": "ipl Standard Library", + "type": "library", + "license": "MIT", + "autoload": { + "files": ["src/functions_include.php"], + "psr-4": { + "ipl\\Stdlib\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "ipl\\Tests\\Stdlib\\": "tests" + } + }, + "require": { + "php": ">=7.2", + "ext-openssl": "*", + "evenement/evenement": "^3.0.1" + } +} diff --git a/vendor/ipl/stdlib/src/BaseFilter.php b/vendor/ipl/stdlib/src/BaseFilter.php new file mode 100644 index 0000000..267decb --- /dev/null +++ b/vendor/ipl/stdlib/src/BaseFilter.php @@ -0,0 +1,45 @@ +<?php + +namespace ipl\Stdlib; + +use ipl\Stdlib\Filter\Rule; + +trait BaseFilter +{ + /** @var Rule Base filter */ + private $baseFilter; + + /** + * Get whether a base filter has been set + * + * @return bool + */ + public function hasBaseFilter(): bool + { + return $this->baseFilter !== null; + } + + /** + * Get the base filter + * + * @return ?Rule + */ + public function getBaseFilter() + { + return $this->baseFilter; + } + + /** + * Set the base filter + * + * @param Rule $baseFilter + * + * @return $this + */ + public function setBaseFilter(Rule $baseFilter = null): self + { + $this->baseFilter = $baseFilter; + + return $this; + } +} diff --git a/vendor/ipl/stdlib/src/Contract/Filterable.php b/vendor/ipl/stdlib/src/Contract/Filterable.php new file mode 100644 index 0000000..2a6316a --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/Filterable.php @@ -0,0 +1,63 @@ +<?php + +namespace ipl\Stdlib\Contract; + +use ipl\Stdlib\Filter; + +interface Filterable +{ + /** + * Get the filter of the query + * + * @return Filter\Chain + */ + public function getFilter(); + + /** + * Add a filter to the query + * + * Note that this method does not override an already set filter. Instead, multiple calls to this function add + * the specified filter using a {@see Filter\All} chain. + * + * @param Filter\Rule $filter + * + * @return $this + */ + public function filter(Filter\Rule $filter); + + /** + * Add a filter to the query + * + * Note that this method does not override an already set filter. Instead, multiple calls to this function add + * the specified filter using a {@see Filter\Any} chain. + * + * @param Filter\Rule $filter + * + * @return $this + */ + public function orFilter(Filter\Rule $filter); + + /** + * Add a filter to the query + * + * Note that this method does not override an already set filter. Instead, multiple calls to this function add + * the specified filter wrapped by a {@see Filter\None} chain and using a {@see Filter\All} chain. + * + * @param Filter\Rule $filter + * + * @return $this + */ + public function notFilter(Filter\Rule $filter); + + /** + * Add a filter to the query + * + * Note that this method does not override an already set filter. Instead, multiple calls to this function add + * the specified filter wrapped by a {@see Filter\None} chain and using a {@see Filter\Any} chain. + * + * @param Filter\Rule $filter + * + * @return $this + */ + public function orNotFilter(Filter\Rule $filter); +} diff --git a/vendor/ipl/stdlib/src/Contract/Paginatable.php b/vendor/ipl/stdlib/src/Contract/Paginatable.php new file mode 100644 index 0000000..3e2a4ee --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/Paginatable.php @@ -0,0 +1,56 @@ +<?php + +namespace ipl\Stdlib\Contract; + +use Countable; + +interface Paginatable extends Countable +{ + /** + * Get whether a limit is set + * + * @return bool + */ + public function hasLimit(); + + /** + * Get the limit + * + * @return int|null + */ + public function getLimit(); + + /** + * Set the limit + * + * @param int|null $limit Maximum number of items to return. If you want to disable the limit, + * it is best practice to use null or a negative value + * + * @return $this + */ + public function limit($limit); + + /** + * Get whether an offset is set + * + * @return bool + */ + public function hasOffset(); + + /** + * Get the offset + * + * @return int|null + */ + public function getOffset(); + + /** + * Set the offset + * + * @param int|null $offset Start result set after this many rows. If you want to disable the offset, + * it is best practice to use null or a negative value + * + * @return $this + */ + public function offset($offset); +} diff --git a/vendor/ipl/stdlib/src/Contract/PaginationInterface.php b/vendor/ipl/stdlib/src/Contract/PaginationInterface.php new file mode 100644 index 0000000..b00fa66 --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/PaginationInterface.php @@ -0,0 +1,8 @@ +<?php + +namespace ipl\Stdlib\Contract; + +/** @deprecated Use {@link Paginatable} instead */ +interface PaginationInterface extends Paginatable +{ +} diff --git a/vendor/ipl/stdlib/src/Contract/PluginLoader.php b/vendor/ipl/stdlib/src/Contract/PluginLoader.php new file mode 100644 index 0000000..1be779c --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/PluginLoader.php @@ -0,0 +1,21 @@ +<?php + +namespace ipl\Stdlib\Contract; + +/** + * Representation of plugin loaders + * + * Plugin loaders must implement the {@link load()} method in order to provide the fully qualified class name of a + * plugin to load. + */ +interface PluginLoader +{ + /** + * Load the class file for a given plugin name + * + * @param string $name Name of the plugin + * + * @return string|false FQN of the plugin's class if found, false otherwise + */ + public function load($name); +} diff --git a/vendor/ipl/stdlib/src/Contract/Translator.php b/vendor/ipl/stdlib/src/Contract/Translator.php new file mode 100644 index 0000000..85ab515 --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/Translator.php @@ -0,0 +1,65 @@ +<?php + +namespace ipl\Stdlib\Contract; + +/** + * Representation of translators + */ +interface Translator +{ + /** + * Translate a message + * + * @param string $message + * @param string $context Message context + * + * @return string Translated message or original message if no translation is found + */ + public function translate($message, $context = null); + + /** + * Translate a message in the given domain + * + * If no translation is found in the specified domain, the translation is also searched for in the default domain. + * + * @param string $domain + * @param string $message + * @param string $context Message context + * + * @return string Translated message or original message if no translation is found + */ + public function translateInDomain($domain, $message, $context = null); + + /** + * Translate a plural message + * + * The returned message is based on the given number to decide between the singular and plural forms. + * That is also the case if no translation is found. + * + * @param string $singular Singular message + * @param string $plural Plural message + * @param int $number Number to decide between the returned singular and plural forms + * @param string $context Message context + * + * @return string Translated message or original message if no translation is found + */ + public function translatePlural($singular, $plural, $number, $context = null); + + /** + * Translate a plural message in the given domain + * + * If no translation is found in the specified domain, the translation is also searched for in the default domain. + * + * The returned message is based on the given number to decide between the singular and plural forms. + * That is also the case if no translation is found. + * + * @param string $domain + * @param string $singular Singular message + * @param string $plural Plural message + * @param int $number Number to decide between the returned singular and plural forms + * @param string $context Message context + * + * @return string Translated message or original message if no translation is found + */ + public function translatePluralInDomain($domain, $singular, $plural, $number, $context = null); +} diff --git a/vendor/ipl/stdlib/src/Contract/Validator.php b/vendor/ipl/stdlib/src/Contract/Validator.php new file mode 100644 index 0000000..e43821d --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/Validator.php @@ -0,0 +1,22 @@ +<?php + +namespace ipl\Stdlib\Contract; + +interface Validator +{ + /** + * Get whether the given value is valid + * + * @param mixed $value + * + * @return bool + */ + public function isValid($value); + + /** + * Get the validation error messages + * + * @return array<string> + */ + public function getMessages(); +} diff --git a/vendor/ipl/stdlib/src/Contract/ValidatorInterface.php b/vendor/ipl/stdlib/src/Contract/ValidatorInterface.php new file mode 100644 index 0000000..36cf55e --- /dev/null +++ b/vendor/ipl/stdlib/src/Contract/ValidatorInterface.php @@ -0,0 +1,8 @@ +<?php + +namespace ipl\Stdlib\Contract; + +/** @deprecated Use {@link Validator} instead */ +interface ValidatorInterface extends Validator +{ +} diff --git a/vendor/ipl/stdlib/src/Data.php b/vendor/ipl/stdlib/src/Data.php new file mode 100644 index 0000000..b12306c --- /dev/null +++ b/vendor/ipl/stdlib/src/Data.php @@ -0,0 +1,89 @@ +<?php + +namespace ipl\Stdlib; + +class Data +{ + /** @var array<string, mixed> */ + protected $data = []; + + /** + * Check whether there's any data + * + * @return bool + */ + public function isEmpty() + { + return empty($this->data); + } + + /** + * Check whether the given data exists + * + * @param string $name The name of the data + * + * @return bool + */ + public function has($name) + { + return array_key_exists($name, $this->data); + } + + /** + * Get the value of the given data + * + * @param string $name The name of the data + * @param mixed $default The value to return if there's no such data + * + * @return mixed + */ + public function get($name, $default = null) + { + if ($this->has($name)) { + return $this->data[$name]; + } + + return $default; + } + + /** + * Set the value of the given data + * + * @param string $name The name of the data + * @param mixed $value + * + * @return $this + */ + public function set($name, $value) + { + $this->data[$name] = $value; + + return $this; + } + + /** + * Merge the given data + * + * @param Data $with + * + * @return $this + */ + public function merge(self $with) + { + $this->data = array_merge($this->data, $with->data); + + return $this; + } + + /** + * Clear all data + * + * @return $this + */ + public function clear() + { + $this->data = []; + + return $this; + } +} diff --git a/vendor/ipl/stdlib/src/EventEmitter.php b/vendor/ipl/stdlib/src/EventEmitter.php new file mode 100644 index 0000000..6d189ee --- /dev/null +++ b/vendor/ipl/stdlib/src/EventEmitter.php @@ -0,0 +1,9 @@ +<?php + +namespace ipl\Stdlib; + +/** @deprecated Use {@link Events} instead */ +trait EventEmitter +{ + use Events; +} diff --git a/vendor/ipl/stdlib/src/Events.php b/vendor/ipl/stdlib/src/Events.php new file mode 100644 index 0000000..3405086 --- /dev/null +++ b/vendor/ipl/stdlib/src/Events.php @@ -0,0 +1,57 @@ +<?php + +namespace ipl\Stdlib; + +use Evenement\EventEmitterTrait; +use InvalidArgumentException; + +trait Events +{ + use EventEmitterTrait { + EventEmitterTrait::on as private evenementUnvalidatedOn; + } + + /** @var array */ + protected $eventsEmittedOnce = []; + + /** + * @param string $event + * @param array $arguments + */ + protected function emitOnce($event, array $arguments = []) + { + if (! isset($this->eventsEmittedOnce[$event])) { + $this->eventsEmittedOnce[$event] = true; + $this->emit($event, $arguments); + } + } + + /** + * @param string $event + * @param callable $listener + * @return $this + */ + public function on($event, callable $listener) + { + $this->assertValidEvent($event); + $this->evenementUnvalidatedOn($event, $listener); + + return $this; + } + + protected function assertValidEvent($event) + { + if (! $this->isValidEvent($event)) { + throw new InvalidArgumentException("$event is not a valid event"); + } + } + + /** + * @param string $event + * @return bool + */ + public function isValidEvent($event) + { + return true; + } +} diff --git a/vendor/ipl/stdlib/src/Filter.php b/vendor/ipl/stdlib/src/Filter.php new file mode 100644 index 0000000..9523f1f --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter.php @@ -0,0 +1,584 @@ +<?php + +namespace ipl\Stdlib; + +use Exception; +use InvalidArgumentException; +use ipl\Stdlib\Filter\All; +use ipl\Stdlib\Filter\Any; +use ipl\Stdlib\Filter\Chain; +use ipl\Stdlib\Filter\Condition; +use ipl\Stdlib\Filter\Equal; +use ipl\Stdlib\Filter\GreaterThan; +use ipl\Stdlib\Filter\GreaterThanOrEqual; +use ipl\Stdlib\Filter\LessThan; +use ipl\Stdlib\Filter\LessThanOrEqual; +use ipl\Stdlib\Filter\Like; +use ipl\Stdlib\Filter\None; +use ipl\Stdlib\Filter\Rule; +use ipl\Stdlib\Filter\Unequal; +use ipl\Stdlib\Filter\Unlike; + +class Filter +{ + /** + * protected - This is only a factory class + */ + protected function __construct() + { + } + + /** + * Return whether the given rule matches the given item + * + * @param Rule $rule + * @param array<mixed>|object $row + * + * @return bool + */ + public static function match(Rule $rule, $row) + { + if (! is_object($row)) { + if (is_array($row)) { + $row = (object) $row; + } else { + throw new InvalidArgumentException(sprintf( + 'Object or array expected, got %s instead', + get_php_type($row) + )); + } + } + + return (new self())->performMatch($rule, $row); + } + + /** + * Create a rule that matches if **all** of the given rules do + * + * @param Rule ...$rules + * + * @return Chain + */ + public static function all(Rule ...$rules) + { + return new All(...$rules); + } + + /** + * Return whether the given rules all match the given item + * + * @param All $rules + * @param object $row + * + * @return bool + */ + protected function matchAll(All $rules, $row) + { + foreach ($rules as $rule) { + if (! $this->performMatch($rule, $row)) { + return false; + } + } + + return true; + } + + /** + * Create a rule that matches if **any** of the given rules do + * + * @param Rule ...$rules + * + * @return Chain + */ + public static function any(Rule ...$rules) + { + return new Any(...$rules); + } + + /** + * Return whether any of the given rules match the given item + * + * @param Any $rules + * @param object $row + * + * @return bool + */ + protected function matchAny(Any $rules, $row) + { + foreach ($rules as $rule) { + if ($this->performMatch($rule, $row)) { + return true; + } + } + + return false; + } + + /** + * Create a rule that matches if **none** of the given rules do + * + * @param Rule ...$rules + * + * @return Chain + */ + public static function none(Rule ...$rules) + { + return new None(...$rules); + } + + /** + * Return whether none of the given rules match the given item + * + * @param None $rules + * @param object $row + * + * @return bool + */ + protected function matchNone(None $rules, $row) + { + foreach ($rules as $rule) { + if ($this->performMatch($rule, $row)) { + return false; + } + } + + return true; + } + + /** + * Create a rule that matches rows with a column that **equals** the given value + * + * @param string $column + * @param array<mixed>|bool|float|int|string $value + * + * @return Condition + */ + public static function equal($column, $value) + { + return new Equal($column, $value); + } + + /** + * Return whether the given rule's value equals the given item's value + * + * @param Equal|Unequal $rule + * @param object $row + * + * @return bool + */ + protected function matchEqual($rule, $row) + { + if (! $rule instanceof Equal && ! $rule instanceof Unequal) { + throw new InvalidArgumentException(sprintf( + 'Rule must be of type %s or %s, got %s instead', + Equal::class, + Unequal::class, + get_php_type($rule) + )); + } + + $rowValue = $this->extractValue($rule->getColumn(), $row); + $value = $rule->getValue(); + $this->normalizeTypes($rowValue, $value); + + if (! is_array($rowValue)) { + $rowValue = [$rowValue]; + } + + foreach ($rowValue as $rowVal) { + if ($this->performEqualityMatch($value, $rowVal, $rule->ignoresCase())) { + return true; + } + } + + return false; + } + + /** + * Create a rule that matches rows with a column that is **similar** to the given value + * + * Performs a wildcard search if the value contains asterisks. + * + * @param string $column + * @param string|string[] $value + * + * @return Condition + */ + public static function like($column, $value) + { + return new Like($column, $value); + } + + /** + * Return whether the given rule's value is similar to the given item's value + * + * @param Like|Unlike $rule + * @param object $row + * + * @return bool + */ + protected function matchSimilar($rule, $row) + { + if (! $rule instanceof Like && ! $rule instanceof Unlike) { + throw new InvalidArgumentException(sprintf( + 'Rule must be of type %s or %s, got %s instead', + Like::class, + Unlike::class, + get_php_type($rule) + )); + } + + $rowValue = $this->extractValue($rule->getColumn(), $row); + $value = $rule->getValue(); + $this->normalizeTypes($rowValue, $value); + + if (! is_array($rowValue)) { + $rowValue = [$rowValue]; + } + + foreach ($rowValue as $rowVal) { + if ($this->performSimilarityMatch($value, $rowVal, $rule->ignoresCase())) { + return true; + } + } + + return false; + } + + /** + * Apply equality matching rules on the given row value + * + * @param mixed $value + * @param mixed $rowValue + * @param bool $ignoreCase + * + * @return bool + */ + protected function performEqualityMatch($value, $rowValue, $ignoreCase = false) + { + if ($ignoreCase && is_string($rowValue)) { + $rowValue = strtolower($rowValue); + $value = is_array($value) + ? array_map(function ($val) { + return strtolower((string) $val); + }, $value) + : strtolower((string) $value); + } + + if (is_array($value)) { + return in_array($rowValue, $value, true); + } elseif (! is_string($value)) { + if (is_string($rowValue)) { + $value = (string) $value; + } + } + + return $rowValue === $value; + } + + /** + * Apply similarity matching rules on the given row value + * + * @param string|string[] $value + * @param string $rowValue + * @param bool $ignoreCase + * + * @return bool + */ + protected function performSimilarityMatch($value, $rowValue, $ignoreCase = false) + { + if ($ignoreCase) { + $rowValue = strtolower($rowValue); + $value = is_array($value) + ? array_map('strtolower', $value) + : strtolower($value); + } + + if (is_array($value)) { + return in_array($rowValue, $value, true); + } + + $wildcardSubSegments = preg_split('~\*~', $value); + if (! $wildcardSubSegments) { + $wildcardSubSegments = []; + } + + if (count($wildcardSubSegments) === 1) { + return $rowValue === $value; + } + + $parts = []; + foreach ($wildcardSubSegments as $part) { + $parts[] = preg_quote($part, '~'); + } + + $pattern = '~^' . join('.*', $parts) . '$~'; + + return (bool) preg_match($pattern, $rowValue); + } + + /** + * Create a rule that matches rows with a column that is **unequal** with the given value + * + * @param string $column + * @param array<mixed>|bool|float|int|string $value + * + * @return Condition + */ + public static function unequal($column, $value) + { + return new Unequal($column, $value); + } + + /** + * Return whether the given rule's value does not equal the given item's value + * + * @param Unequal $rule + * @param object $row + * + * @return bool + */ + protected function matchUnequal(Unequal $rule, $row) + { + return ! $this->matchEqual($rule, $row); + } + + /** + * Create a rule that matches rows with a column that is **unlike** with the given value + * + * Performs a wildcard search if the value contains asterisks. + * + * @param string $column + * @param string|string[] $value + * + * @return Condition + */ + public static function unlike($column, $value) + { + return new Unlike($column, $value); + } + + /** + * Return whether the given rule's value is unlike the given item's value + * + * @param Unlike $rule + * @param object $row + * + * @return bool + */ + protected function matchUnlike(Unlike $rule, $row) + { + return ! $this->matchSimilar($rule, $row); + } + + /** + * Create a rule that matches rows with a column that is **greater** than the given value + * + * @param string $column + * @param float|int|string $value + * + * @return Condition + */ + public static function greaterThan($column, $value) + { + return new GreaterThan($column, $value); + } + + /** + * Return whether the given rule's value is greater than the given item's value + * + * @param GreaterThan $rule + * @param object $row + * + * @return bool + */ + protected function matchGreaterThan(GreaterThan $rule, $row) + { + return $this->extractValue($rule->getColumn(), $row) > $rule->getValue(); + } + + /** + * Create a rule that matches rows with a column that is **less** than the given value + * + * @param string $column + * @param float|int|string $value + * + * @return Condition + */ + public static function lessThan($column, $value) + { + return new LessThan($column, $value); + } + + /** + * Return whether the given rule's value is less than the given item's value + * + * @param LessThan $rule + * @param object $row + * + * @return bool + */ + protected function matchLessThan(LessThan $rule, $row) + { + $rowValue = $this->extractValue($rule->getColumn(), $row); + if ($rowValue === null) { + return false; + } + + return $rowValue < $rule->getValue(); + } + + /** + * Create a rule that matches rows with a column that is **greater** than or **equal** to the given value + * + * @param string $column + * @param float|int|string $value + * + * @return Condition + */ + public static function greaterThanOrEqual($column, $value) + { + return new GreaterThanOrEqual($column, $value); + } + + /** + * Return whether the given rule's value is greater than or equals the given item's value + * + * @param GreaterThanOrEqual $rule + * @param object $row + * + * @return bool + */ + protected function matchGreaterThanOrEqual(GreaterThanOrEqual $rule, $row) + { + return $this->extractValue($rule->getColumn(), $row) >= $rule->getValue(); + } + + /** + * Create a rule that matches rows with a column that is **less** than or **equal** to the given value + * + * @param string $column + * @param float|int|string $value + * + * @return Condition + */ + public static function lessThanOrEqual($column, $value) + { + return new LessThanOrEqual($column, $value); + } + + /** + * Return whether the given rule's value is less than or equals the given item's value + * + * @param LessThanOrEqual $rule + * @param object $row + * + * @return bool + */ + protected function matchLessThanOrEqual(LessThanOrEqual $rule, $row) + { + $rowValue = $this->extractValue($rule->getColumn(), $row); + if ($rowValue === null) { + return false; + } + + return $rowValue <= $rule->getValue(); + } + + /** + * Perform the appropriate match for the given rule on the given item + * + * @param Rule $rule + * @param object $row + * + * @return bool + */ + protected function performMatch(Rule $rule, $row) + { + switch (true) { + case $rule instanceof All: + return $this->matchAll($rule, $row); + case $rule instanceof Any: + return $this->matchAny($rule, $row); + case $rule instanceof Like: + return $this->matchSimilar($rule, $row); + case $rule instanceof Equal: + return $this->matchEqual($rule, $row); + case $rule instanceof GreaterThan: + return $this->matchGreaterThan($rule, $row); + case $rule instanceof GreaterThanOrEqual: + return $this->matchGreaterThanOrEqual($rule, $row); + case $rule instanceof LessThan: + return $this->matchLessThan($rule, $row); + case $rule instanceof LessThanOrEqual: + return $this->matchLessThanOrEqual($rule, $row); + case $rule instanceof None: + return $this->matchNone($rule, $row); + case $rule instanceof Unequal: + return $this->matchUnequal($rule, $row); + case $rule instanceof Unlike: + return $this->matchUnlike($rule, $row); + default: + throw new InvalidArgumentException(sprintf( + 'Unable to match filter. Rule type %s is unknown', + get_class($rule) + )); + } + } + + /** + * Return a value from the given row suitable to work with + * + * @param string $column + * @param object $row + * + * @return mixed + */ + protected function extractValue($column, $row) + { + try { + return $row->{$column}; + } catch (Exception $_) { + return null; + } + } + + /** + * Normalize type of $value to the one of $rowValue + * + * For details on how this works please see the corresponding test + * {@see \ipl\Tests\Stdlib\FilterTest::testConditionsAreValueTypeAgnostic} + * + * @param mixed $rowValue + * @param mixed $value + * + * @return void + */ + protected function normalizeTypes($rowValue, &$value) + { + if ($rowValue === null || $value === null) { + return; + } + + if (is_array($rowValue)) { + if (empty($rowValue)) { + return; + } + + $rowValue = array_shift($rowValue); + } + + if (is_array($value)) { + if (is_bool($rowValue) && ! empty($value) && is_string(array_values($value)[0])) { + return; + } + + $rowValueType = gettype($rowValue); + foreach ($value as &$val) { + settype($val, $rowValueType); + } + } elseif (! is_bool($rowValue) || ! is_string($value)) { + settype($value, gettype($rowValue)); + } + } +} diff --git a/vendor/ipl/stdlib/src/Filter/All.php b/vendor/ipl/stdlib/src/Filter/All.php new file mode 100644 index 0000000..67b47b6 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/All.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class All extends Chain +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/Any.php b/vendor/ipl/stdlib/src/Filter/Any.php new file mode 100644 index 0000000..5d47ebe --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Any.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class Any extends Chain +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/Chain.php b/vendor/ipl/stdlib/src/Filter/Chain.php new file mode 100644 index 0000000..9422d3a --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Chain.php @@ -0,0 +1,179 @@ +<?php + +namespace ipl\Stdlib\Filter; + +use ArrayIterator; +use Countable; +use IteratorAggregate; +use OutOfBoundsException; +use Traversable; + +abstract class Chain implements Rule, MetaDataProvider, IteratorAggregate, Countable +{ + use MetaData; + + /** @var array<int, Rule> */ + protected $rules = []; + + /** + * Create a new Chain + * + * @param Rule ...$rules + */ + public function __construct(Rule ...$rules) + { + foreach ($rules as $rule) { + $this->add($rule); + } + } + + /** + * Clone this chain's meta data and rules + */ + public function __clone() + { + if ($this->metaData !== null) { + $this->metaData = clone $this->metaData; + } + + foreach ($this->rules as $i => $rule) { + $this->rules[$i] = clone $rule; + } + } + + /** + * Get an iterator this chain's rules + * + * @return ArrayIterator<int, Rule> + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->rules); + } + + /** + * Add a rule to this chain + * + * @param Rule $rule + * + * @return $this + */ + public function add(Rule $rule) + { + $this->rules[] = $rule; + + return $this; + } + + /** + * Prepend a rule to an existing rule in this chain + * + * @param Rule $rule + * @param Rule $before + * + * @throws OutOfBoundsException In case no existing rule is found + * @return $this + */ + public function insertBefore(Rule $rule, Rule $before) + { + $ruleAt = array_search($before, $this->rules, true); + if ($ruleAt === false) { + throw new OutOfBoundsException('Reference rule not found'); + } + + array_splice($this->rules, $ruleAt, 0, [$rule]); + + return $this; + } + + /** + * Append a rule to an existing rule in this chain + * + * @param Rule $rule + * @param Rule $after + * + * @throws OutOfBoundsException In case no existing rule is found + * @return $this + */ + public function insertAfter(Rule $rule, Rule $after) + { + $ruleAt = array_search($after, $this->rules, true); + if ($ruleAt === false) { + throw new OutOfBoundsException('Reference rule not found'); + } + + array_splice($this->rules, $ruleAt + 1, 0, [$rule]); + + return $this; + } + + /** + * Get whether this chain contains the given rule + * + * @param Rule $rule + * + * @return bool + */ + public function has(Rule $rule) + { + return array_search($rule, $this->rules, true) !== false; + } + + /** + * Replace a rule with another one in this chain + * + * @param Rule $rule + * @param Rule $replacement + * + * @throws OutOfBoundsException In case no existing rule is found + * @return $this + */ + public function replace(Rule $rule, Rule $replacement) + { + $ruleAt = array_search($rule, $this->rules, true); + if ($ruleAt === false) { + throw new OutOfBoundsException('Rule to replace not found'); + } + + array_splice($this->rules, $ruleAt, 1, [$replacement]); + + return $this; + } + + /** + * Remove a rule from this chain + * + * @param Rule $rule + * + * @return $this + */ + public function remove(Rule $rule) + { + $ruleAt = array_search($rule, $this->rules, true); + if ($ruleAt !== false) { + array_splice($this->rules, $ruleAt, 1, []); + } + + return $this; + } + + /** + * Get whether this chain has any rules + * + * @return bool + */ + public function isEmpty() + { + return empty($this->rules); + } + + /** + * Count this chain's rules + * + * @return int + */ + public function count(): int + { + return count($this->rules); + } +} diff --git a/vendor/ipl/stdlib/src/Filter/Condition.php b/vendor/ipl/stdlib/src/Filter/Condition.php new file mode 100644 index 0000000..cc35610 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Condition.php @@ -0,0 +1,84 @@ +<?php + +namespace ipl\Stdlib\Filter; + +abstract class Condition implements Rule, MetaDataProvider +{ + use MetaData; + + /** @var string */ + protected $column; + + /** @var mixed */ + protected $value; + + /** + * Create a new Condition + * + * @param string $column + * @param mixed $value + */ + public function __construct($column, $value) + { + $this->setColumn($column) + ->setValue($value); + } + + /** + * Clone this condition's meta data + */ + public function __clone() + { + if ($this->metaData !== null) { + $this->metaData = clone $this->metaData; + } + } + + /** + * Set this condition's column + * + * @param string $column + * + * @return $this + */ + public function setColumn($column) + { + $this->column = $column; + + return $this; + } + + /** + * Get this condition's column + * + * @return string + */ + public function getColumn() + { + return $this->column; + } + + /** + * Set this condition's value + * + * @param mixed $value + * + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get this condition's value + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/ipl/stdlib/src/Filter/Equal.php b/vendor/ipl/stdlib/src/Filter/Equal.php new file mode 100644 index 0000000..71da490 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Equal.php @@ -0,0 +1,31 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class Equal extends Condition +{ + /** @var bool */ + protected $ignoreCase = false; + + /** + * Ignore case on both sides of the equation + * + * @return $this + */ + public function ignoreCase() + { + $this->ignoreCase = true; + + return $this; + } + + /** + * Return whether this rule ignores case + * + * @return bool + */ + public function ignoresCase() + { + return $this->ignoreCase; + } +} diff --git a/vendor/ipl/stdlib/src/Filter/GreaterThan.php b/vendor/ipl/stdlib/src/Filter/GreaterThan.php new file mode 100644 index 0000000..fd8190c --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/GreaterThan.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class GreaterThan extends Condition +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/GreaterThanOrEqual.php b/vendor/ipl/stdlib/src/Filter/GreaterThanOrEqual.php new file mode 100644 index 0000000..4cd4a73 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/GreaterThanOrEqual.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class GreaterThanOrEqual extends Condition +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/LessThan.php b/vendor/ipl/stdlib/src/Filter/LessThan.php new file mode 100644 index 0000000..297493f --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/LessThan.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class LessThan extends Condition +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/LessThanOrEqual.php b/vendor/ipl/stdlib/src/Filter/LessThanOrEqual.php new file mode 100644 index 0000000..ef35974 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/LessThanOrEqual.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class LessThanOrEqual extends Condition +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/Like.php b/vendor/ipl/stdlib/src/Filter/Like.php new file mode 100644 index 0000000..7a06279 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Like.php @@ -0,0 +1,31 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class Like extends Condition +{ + /** @var bool */ + protected $ignoreCase = false; + + /** + * Ignore case on both sides of the equation + * + * @return $this + */ + public function ignoreCase() + { + $this->ignoreCase = true; + + return $this; + } + + /** + * Return whether this rule ignores case + * + * @return bool + */ + public function ignoresCase() + { + return $this->ignoreCase; + } +} diff --git a/vendor/ipl/stdlib/src/Filter/MetaData.php b/vendor/ipl/stdlib/src/Filter/MetaData.php new file mode 100644 index 0000000..6fe2523 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/MetaData.php @@ -0,0 +1,20 @@ +<?php + +namespace ipl\Stdlib\Filter; + +use ipl\Stdlib\Data; + +trait MetaData +{ + /** @var Data */ + protected $metaData; + + public function metaData() + { + if ($this->metaData === null) { + $this->metaData = new Data(); + } + + return $this->metaData; + } +} diff --git a/vendor/ipl/stdlib/src/Filter/MetaDataProvider.php b/vendor/ipl/stdlib/src/Filter/MetaDataProvider.php new file mode 100644 index 0000000..ef9557e --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/MetaDataProvider.php @@ -0,0 +1,15 @@ +<?php + +namespace ipl\Stdlib\Filter; + +use ipl\Stdlib\Data; + +interface MetaDataProvider +{ + /** + * Get this rule's meta data + * + * @return Data + */ + public function metaData(); +} diff --git a/vendor/ipl/stdlib/src/Filter/None.php b/vendor/ipl/stdlib/src/Filter/None.php new file mode 100644 index 0000000..a1b14f7 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/None.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class None extends Chain +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/Rule.php b/vendor/ipl/stdlib/src/Filter/Rule.php new file mode 100644 index 0000000..dc83c80 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Rule.php @@ -0,0 +1,7 @@ +<?php + +namespace ipl\Stdlib\Filter; + +interface Rule +{ +} diff --git a/vendor/ipl/stdlib/src/Filter/Unequal.php b/vendor/ipl/stdlib/src/Filter/Unequal.php new file mode 100644 index 0000000..5e37cbd --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Unequal.php @@ -0,0 +1,31 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class Unequal extends Condition +{ + /** @var bool */ + protected $ignoreCase = false; + + /** + * Ignore case on both sides of the equation + * + * @return $this + */ + public function ignoreCase() + { + $this->ignoreCase = true; + + return $this; + } + + /** + * Return whether this rule ignores case + * + * @return bool + */ + public function ignoresCase() + { + return $this->ignoreCase; + } +} diff --git a/vendor/ipl/stdlib/src/Filter/Unlike.php b/vendor/ipl/stdlib/src/Filter/Unlike.php new file mode 100644 index 0000000..16b9fb3 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filter/Unlike.php @@ -0,0 +1,31 @@ +<?php + +namespace ipl\Stdlib\Filter; + +class Unlike extends Condition +{ + /** @var bool */ + protected $ignoreCase = false; + + /** + * Ignore case on both sides of the equation + * + * @return $this + */ + public function ignoreCase() + { + $this->ignoreCase = true; + + return $this; + } + + /** + * Return whether this rule ignores case + * + * @return bool + */ + public function ignoresCase() + { + return $this->ignoreCase; + } +} diff --git a/vendor/ipl/stdlib/src/Filters.php b/vendor/ipl/stdlib/src/Filters.php new file mode 100644 index 0000000..defff43 --- /dev/null +++ b/vendor/ipl/stdlib/src/Filters.php @@ -0,0 +1,58 @@ +<?php + +namespace ipl\Stdlib; + +trait Filters +{ + /** @var Filter\Chain */ + protected $filter; + + public function getFilter() + { + return $this->filter ?: Filter::all(); + } + + public function filter(Filter\Rule $filter) + { + $currentFilter = $this->getFilter(); + if ($currentFilter instanceof Filter\All) { + $this->filter = $currentFilter->add($filter); + } else { + $this->filter = Filter::all($filter); + if (! $currentFilter->isEmpty()) { + $this->filter->insertBefore($currentFilter, $filter); + } + } + + return $this; + } + + public function orFilter(Filter\Rule $filter) + { + $currentFilter = $this->getFilter(); + if ($currentFilter instanceof Filter\Any) { + $this->filter = $currentFilter->add($filter); + } else { + $this->filter = Filter::any($filter); + if (! $currentFilter->isEmpty()) { + $this->filter->insertBefore($currentFilter, $filter); + } + } + + return $this; + } + + public function notFilter(Filter\Rule $filter) + { + $this->filter(Filter::none($filter)); + + return $this; + } + + public function orNotFilter(Filter\Rule $filter) + { + $this->orFilter(Filter::none($filter)); + + return $this; + } +} diff --git a/vendor/ipl/stdlib/src/Loader/AutoloadingPluginLoader.php b/vendor/ipl/stdlib/src/Loader/AutoloadingPluginLoader.php new file mode 100644 index 0000000..ba195c6 --- /dev/null +++ b/vendor/ipl/stdlib/src/Loader/AutoloadingPluginLoader.php @@ -0,0 +1,52 @@ +<?php + +namespace ipl\Stdlib\Loader; + +use ipl\Stdlib\Contract\PluginLoader; + +/** + * Plugin loader that makes use of registered PHP autoloaders + */ +class AutoloadingPluginLoader implements PluginLoader +{ + /** @var string Namespace of the plugins */ + protected $namespace; + + /** @var string Class name postfix */ + protected $postfix; + + /** + * Create a new autoloading plugin loader + * + * @param string $namespace Namespace of the plugins + * @param string $postfix Class name postfix + */ + public function __construct($namespace, $postfix = '') + { + $this->namespace = $namespace; + $this->postfix = $postfix; + } + + /** + * Get the FQN of a plugin + * + * @param string $name Name of the plugin + * + * @return string + */ + protected function getFqn($name) + { + return $this->namespace . '\\' . ucfirst($name) . $this->postfix; + } + + public function load($name) + { + $class = $this->getFqn($name); + + if (! class_exists($class)) { + return false; + } + + return $class; + } +} diff --git a/vendor/ipl/stdlib/src/MessageContainer.php b/vendor/ipl/stdlib/src/MessageContainer.php new file mode 100644 index 0000000..3b383b1 --- /dev/null +++ b/vendor/ipl/stdlib/src/MessageContainer.php @@ -0,0 +1,9 @@ +<?php + +namespace ipl\Stdlib; + +/** @deprecated Use {@link Messages} instead */ +trait MessageContainer +{ + use Messages; +} diff --git a/vendor/ipl/stdlib/src/Messages.php b/vendor/ipl/stdlib/src/Messages.php new file mode 100644 index 0000000..b601c1d --- /dev/null +++ b/vendor/ipl/stdlib/src/Messages.php @@ -0,0 +1,92 @@ +<?php + +namespace ipl\Stdlib; + +trait Messages +{ + /** @var array */ + protected $messages = []; + + /** + * Get whether there are any messages + * + * @return bool + */ + public function hasMessages() + { + return ! empty($this->messages); + } + + /** + * Get all messages + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Set the given messages overriding existing ones + * + * @param string[] $messages + * + * @return $this + */ + public function setMessages(array $messages) + { + $this->clearMessages(); + + foreach ($messages as $message) { + $this->addMessage($message); + } + + return $this; + } + + /** + * Add a single message + * + * @param string $message + * @param mixed ...$args Optional args for sprintf-style messages + * + * @return $this + */ + public function addMessage($message, ...$args) + { + if (empty($args)) { + $this->messages[] = $message; + } else { + $this->messages[] = vsprintf($message, $args); + } + + return $this; + } + + /** + * Add the given messages + * + * @param array $messages + * + * @return $this + */ + public function addMessages(array $messages) + { + $this->messages = array_merge($this->messages, $messages); + + return $this; + } + + /** + * Drop any existing message + * + * @return $this + */ + public function clearMessages() + { + $this->messages = []; + + return $this; + } +} diff --git a/vendor/ipl/stdlib/src/Plugins.php b/vendor/ipl/stdlib/src/Plugins.php new file mode 100644 index 0000000..a5dbb77 --- /dev/null +++ b/vendor/ipl/stdlib/src/Plugins.php @@ -0,0 +1,95 @@ +<?php + +namespace ipl\Stdlib; + +use ipl\Stdlib\Contract\PluginLoader; +use ipl\Stdlib\Loader\AutoloadingPluginLoader; + +trait Plugins +{ + /** @var array Registered plugin loaders by type */ + protected $pluginLoaders = []; + + /** + * Factory for plugin loaders + * + * @param PluginLoader|string $loaderOrNamespace + * @param string $postfix + * + * @return PluginLoader + */ + public static function wantPluginLoader($loaderOrNamespace, $postfix = '') + { + if ($loaderOrNamespace instanceof PluginLoader) { + $loader = $loaderOrNamespace; + } else { + $loader = new AutoloadingPluginLoader($loaderOrNamespace, $postfix); + } + + return $loader; + } + + /** + * Get whether a plugin loader for the given type exists + * + * @param string $type + * + * @return bool + */ + public function hasPluginLoader($type) + { + return isset($this->pluginLoaders[$type]); + } + + /** + * Add a plugin loader for the given type + * + * @param string $type + * @param PluginLoader|string $loaderOrNamespace + * @param string $postfix + * + * @return $this + */ + public function addPluginLoader($type, $loaderOrNamespace, $postfix = '') + { + $loader = static::wantPluginLoader($loaderOrNamespace, $postfix); + + if (! isset($this->pluginLoaders[$type])) { + $this->pluginLoaders[$type] = []; + } + + array_unshift($this->pluginLoaders[$type], $loader); + + return $this; + } + + /** + * Load the class file of the given plugin + * + * @param string $type + * @param string $name + * + * @return string|false + */ + public function loadPlugin($type, $name) + { + if ($this->hasPluginLoader($type)) { + /** @var PluginLoader $loader */ + foreach ($this->pluginLoaders[$type] as $loader) { + $class = $loader->load($name); + if ($class) { + return $class; + } + } + } + + return false; + } + + protected function addDefaultPluginLoader($type, $loaderOrNamespace, $postfix) + { + $this->pluginLoaders[$type][] = static::wantPluginLoader($loaderOrNamespace, $postfix); + + return $this; + } +} diff --git a/vendor/ipl/stdlib/src/PriorityQueue.php b/vendor/ipl/stdlib/src/PriorityQueue.php new file mode 100644 index 0000000..9047af4 --- /dev/null +++ b/vendor/ipl/stdlib/src/PriorityQueue.php @@ -0,0 +1,42 @@ +<?php + +namespace ipl\Stdlib; + +use Generator; +use SplPriorityQueue; + +/** + * Stable priority queue that also maintains insertion order for items with the same priority + */ +class PriorityQueue extends SplPriorityQueue +{ + /** @var int */ + protected $serial = PHP_INT_MAX; + + /** + * @inheritDoc + * + * Maintains insertion order for items with the same priority. + */ + public function insert($value, $priority): bool + { + return parent::insert($value, [$priority, $this->serial--]); + } + + /** + * Yield all items as priority-value pairs + * + * @return Generator + */ + public function yieldAll() + { + // Clone queue because the SplPriorityQueue acts as a heap and thus items are removed upon iteration + $queue = clone $this; + + $queue->setExtractFlags(static::EXTR_BOTH); + + foreach ($queue as $item) { + yield $item['priority'][0] => $item['data']; + } + } +} diff --git a/vendor/ipl/stdlib/src/Properties.php b/vendor/ipl/stdlib/src/Properties.php new file mode 100644 index 0000000..5726af3 --- /dev/null +++ b/vendor/ipl/stdlib/src/Properties.php @@ -0,0 +1,205 @@ +<?php + +namespace ipl\Stdlib; + +use OutOfBoundsException; +use Traversable; + +/** + * Trait for property access, mutation and array access. + */ +trait Properties +{ + /** @var array */ + private $properties = []; + + /** + * Get whether this class has any properties + * + * @return bool + */ + public function hasProperties() + { + return ! empty($this->properties); + } + + /** + * Get whether a property with the given key exists + * + * @param string $key + * + * @return bool + */ + public function hasProperty($key) + { + return array_key_exists($key, $this->properties); + } + + /** + * Set the given properties + * + * @param array $properties + * + * @return $this + */ + public function setProperties(array $properties) + { + foreach ($properties as $key => $value) { + $this->setProperty($key, $value); + } + + return $this; + } + + /** + * Get the property by the given key + * + * @param string $key + * + * @return mixed + * + * @throws OutOfBoundsException If the property by the given key does not exist + */ + protected function getProperty($key) + { + if (array_key_exists($key, $this->properties)) { + return $this->properties[$key]; + } + + throw new OutOfBoundsException("Can't access property '$key'. Property does not exist"); + } + + /** + * Set a property with the given key and value + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + protected function setProperty($key, $value) + { + $this->properties[$key] = $value; + + return $this; + } + + /** + * Iterate over all existing properties + * + * @return Traversable + */ + public function getIterator(): Traversable + { + foreach ($this->properties as $key => $value) { + yield $key => $value; + } + } + + /** + * Check whether an offset exists + * + * @param mixed $offset + * + * @return bool + */ + public function offsetExists($offset): bool + { + return isset($this->properties[$offset]); + } + + /** + * Get the value for an offset + * + * @param mixed $offset + * + * @return mixed + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->getProperty($offset); + } + + /** + * Set the value for an offset + * + * @param mixed $offset + * @param mixed $value + */ + public function offsetSet($offset, $value): void + { + $this->setProperty($offset, $value); + } + + /** + * Unset the value for an offset + * + * @param mixed $offset + */ + public function offsetUnset($offset): void + { + unset($this->properties[$offset]); + } + + /** + * Get the value of a non-public property + * + * This is a PHP magic method which is implicitly called upon access to non-public properties, + * e.g. `$value = $object->property;`. + * Do not call this method directly. + * + * @param mixed $key + * + * @return mixed + */ + public function __get($key) + { + return $this->getProperty($key); + } + + /** + * Set the value of a non-public property + * + * This is a PHP magic method which is implicitly called upon access to non-public properties, + * e.g. `$object->property = $value;`. + * Do not call this method directly. + * + * @param string $key + * @param mixed $value + */ + public function __set($key, $value) + { + $this->setProperty($key, $value); + } + + /** + * Check whether a non-public property is defined and not null + * + * This is a PHP magic method which is implicitly called upon access to non-public properties, + * e.g. `isset($object->property);`. + * Do not call this method directly. + * + * @param string $key + * + * @return bool + */ + public function __isset($key) + { + return $this->offsetExists($key); + } + + /** + * Unset the value of a non-public property + * + * This is a PHP magic method which is implicitly called upon access to non-public properties, + * e.g. `unset($object->property);`. This method does nothing if the property does not exist. + * Do not call this method directly. + * + * @param string $key + */ + public function __unset($key) + { + $this->offsetUnset($key); + } +} diff --git a/vendor/ipl/stdlib/src/Seq.php b/vendor/ipl/stdlib/src/Seq.php new file mode 100644 index 0000000..02a3bd0 --- /dev/null +++ b/vendor/ipl/stdlib/src/Seq.php @@ -0,0 +1,111 @@ +<?php + +namespace ipl\Stdlib; + +use Closure; + +/** + * Collection of utilities for traversables + */ +class Seq +{ + /** + * Check if the traversable contains the given needle + * + * @param array<mixed>|iterable<mixed> $traversable + * @param mixed $needle Might also be a closure + * @param bool $caseSensitive Whether strings should be compared case-sensitive + * + * @return bool + */ + public static function contains($traversable, $needle, $caseSensitive = true) + { + return self::find($traversable, $needle, $caseSensitive)[0] !== null; + } + + /** + * Search in the traversable for the given needle and return its key and value + * + * @param array<mixed>|iterable<mixed> $traversable + * @param mixed $needle Might also be a closure + * @param bool $caseSensitive Whether strings should be compared case-sensitive + * + * @return array<mixed> An array with two entries, the first is the key, then the value. + * Both are null if nothing is found. + */ + public static function find($traversable, $needle, $caseSensitive = true) + { + $usesCallback = $needle instanceof Closure; + if (! $usesCallback && $caseSensitive && is_array($traversable)) { + return [array_search($needle, $traversable, true), $needle]; + } + + if (! $caseSensitive && is_string($needle) && ! $usesCallback) { + $needle = strtolower($needle); + } + + foreach ($traversable as $key => $item) { + $originalItem = $item; + if (! $caseSensitive && is_string($item)) { + $item = strtolower($item); + } + + if ($usesCallback && $needle($item)) { + return [$key, $originalItem]; + } elseif ($item === $needle) { + return [$key, $originalItem]; + } + } + + return [null, null]; + } + + /** + * Search in the traversable for the given needle and return its key + * + * @param array<mixed>|iterable<mixed> $traversable + * @param mixed $needle Might also be a closure + * @param bool $caseSensitive Whether strings should be compared case-sensitive + * + * @return mixed|null Null if nothing is found + */ + public static function findKey($traversable, $needle, $caseSensitive = true) + { + return self::find($traversable, $needle, $caseSensitive)[0]; + } + + /** + * Search in the traversable for the given needle and return its value + * + * @param array<mixed>|iterable<mixed> $traversable + * @param mixed $needle Might also be a closure + * @param bool $caseSensitive Whether strings should be compared case-sensitive + * + * @return mixed|null Null if nothing is found + */ + public static function findValue($traversable, $needle, $caseSensitive = true) + { + $usesCallback = $needle instanceof Closure; + if (! $usesCallback && $caseSensitive && is_array($traversable)) { + return isset($traversable[$needle]) ? $traversable[$needle] : null; + } + + if (! $caseSensitive && is_string($needle) && ! $usesCallback) { + $needle = strtolower($needle); + } + + foreach ($traversable as $key => $item) { + if (! $caseSensitive && is_string($key)) { + $key = strtolower($key); + } + + if ($usesCallback && $needle($key)) { + return $item; + } elseif ($key === $needle) { + return $item; + } + } + + return null; + } +} diff --git a/vendor/ipl/stdlib/src/Str.php b/vendor/ipl/stdlib/src/Str.php new file mode 100644 index 0000000..9cf1cae --- /dev/null +++ b/vendor/ipl/stdlib/src/Str.php @@ -0,0 +1,93 @@ +<?php + +namespace ipl\Stdlib; + +/** + * Collection of string manipulation functions + */ +class Str +{ + /** + * Convert the given string to camel case + * + * The given string may be delimited by the following characters: '_' (underscore), '-' (dash), ' ' (space). + * + * @param ?string $subject + * + * @return string + */ + public static function camel(?string $subject) + { + if ($subject === null) { + return ''; + } + + $normalized = str_replace(['-', '_'], ' ', $subject); + + return lcfirst(str_replace(' ', '', ucwords(strtolower($normalized)))); + } + + /** + * Check if the given string starts with the specified substring + * + * @param ?string $subject + * @param string $start + * @param bool $caseSensitive + * + * @return bool + */ + public static function startsWith(?string $subject, string $start, bool $caseSensitive = true) + { + $subject = $subject ?? ''; + if (! $caseSensitive) { + return strncasecmp($subject, $start, strlen($start)) === 0; + } + + return substr($subject, 0, strlen($start)) === $start; + } + + /** + * Split string into an array padded to the size specified by limit + * + * This method is a perfect fit if you need default values for symmetric array destructuring. + * + * @param ?string $subject + * @param string $delimiter + * @param int $limit + * @param mixed $default + * + * @return array<int, mixed> + */ + public static function symmetricSplit(?string $subject, string $delimiter, int $limit, $default = null) + { + if ($subject === null) { + return array_pad([], $limit, $default); + } + + return array_pad(explode($delimiter, $subject, $limit), $limit, $default); + } + + /** + * Split string into an array and trim spaces + * + * @param ?string $subject + * @param string $delimiter + * @param ?int $limit + * + * @return array<string> + */ + public static function trimSplit(?string $subject, string $delimiter = ',', int $limit = null) + { + if ($subject === null) { + return []; + } + + if ($limit !== null) { + $exploded = explode($delimiter, $subject, $limit); + } else { + $exploded = explode($delimiter, $subject); + } + + return array_map('trim', $exploded); + } +} diff --git a/vendor/ipl/stdlib/src/functions.php b/vendor/ipl/stdlib/src/functions.php new file mode 100644 index 0000000..e7f9be0 --- /dev/null +++ b/vendor/ipl/stdlib/src/functions.php @@ -0,0 +1,128 @@ +<?php + +namespace ipl\Stdlib; + +use Generator; +use InvalidArgumentException; +use IteratorIterator; +use Traversable; +use stdClass; + +/** + * Detect and return the PHP type of the given subject + * + * If subject is an object, the name of the object's class is returned, otherwise the subject's type. + * + * @param mixed $subject + * + * @return string + */ +function get_php_type($subject) +{ + if (is_object($subject)) { + return get_class($subject); + } else { + return gettype($subject); + } +} + +/** + * Get the array value of the given subject + * + * @param array<mixed>|object|Traversable $subject + * + * @return array<mixed> + * + * @throws InvalidArgumentException If subject type is invalid + */ +function arrayval($subject) +{ + if (is_array($subject)) { + return $subject; + } + + if ($subject instanceof stdClass) { + return (array) $subject; + } + + if ($subject instanceof Traversable) { + // Works for generators too + return iterator_to_array($subject); + } + + throw new InvalidArgumentException(sprintf( + 'arrayval expects arrays, objects or instances of Traversable. Got %s instead.', + get_php_type($subject) + )); +} + +/** + * Get the first key of an iterable + * + * @param iterable<mixed> $iterable + * + * @return mixed The first key of the iterable if it is not empty, null otherwise + */ +function iterable_key_first($iterable) +{ + foreach ($iterable as $key => $_) { + return $key; + } + + return null; +} + +/** + * Get the first value of an iterable + * + * @param iterable<mixed> $iterable + * + * @return ?mixed + */ +function iterable_value_first($iterable) +{ + foreach ($iterable as $_ => $value) { + return $value; + } + + return null; +} + +/** + * Yield sets of items from a sorted traversable grouped by a specific criterion gathered from a callback + * + * The traversable must be sorted by the criterion. The callback must return at least the criterion, + * but can also return value and key in addition. + * + * @param Traversable<mixed, mixed> $traversable + * @param callable(mixed $value, mixed $key): array{0: mixed, 1?: mixed, 2?: mixed} $groupBy + * + * @return Generator + */ +function yield_groups(Traversable $traversable, callable $groupBy): Generator +{ + $iterator = new IteratorIterator($traversable); + $iterator->rewind(); + + if (! $iterator->valid()) { + return; + } + + list($criterion, $v, $k) = array_pad((array) $groupBy($iterator->current(), $iterator->key()), 3, null); + $group = [$k ?? $iterator->key() => $v ?? $iterator->current()]; + + $iterator->next(); + for (; $iterator->valid(); $iterator->next()) { + list($c, $v, $k) = array_pad((array) $groupBy($iterator->current(), $iterator->key()), 3, null); + if ($c !== $criterion) { + yield $criterion => $group; + + $group = []; + $criterion = $c; + } + + $group[$k ?? $iterator->key()] = $v ?? $iterator->current(); + } + + yield $criterion => $group; +} diff --git a/vendor/ipl/stdlib/src/functions_include.php b/vendor/ipl/stdlib/src/functions_include.php new file mode 100644 index 0000000..9a2dc6f --- /dev/null +++ b/vendor/ipl/stdlib/src/functions_include.php @@ -0,0 +1,6 @@ +<?php + +// Don't redefine the functions if included multiple times +if (! function_exists('ipl\Stdlib\get_php_type')) { + require __DIR__ . '/functions.php'; +} |