diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/ipl/orm/src/Behavior.php | 12 | ||||
-rw-r--r-- | vendor/ipl/orm/src/Behavior/Binary.php | 101 | ||||
-rw-r--r-- | vendor/ipl/orm/src/Behavior/BoolCast.php | 147 | ||||
-rw-r--r-- | vendor/ipl/orm/src/Behavior/MillisecondTimestamp.php | 41 | ||||
-rw-r--r-- | vendor/ipl/orm/src/Behaviors.php | 238 |
5 files changed, 539 insertions, 0 deletions
diff --git a/vendor/ipl/orm/src/Behavior.php b/vendor/ipl/orm/src/Behavior.php new file mode 100644 index 0000000..45b5e87 --- /dev/null +++ b/vendor/ipl/orm/src/Behavior.php @@ -0,0 +1,12 @@ +<?php + +namespace ipl\Orm; + +/** + * Interface Behavior + * + * @internal Used for type hinting only. Concrete behaviors are supposed to implement contracts from ipl\Orm\Contract + */ +interface Behavior +{ +} diff --git a/vendor/ipl/orm/src/Behavior/Binary.php b/vendor/ipl/orm/src/Behavior/Binary.php new file mode 100644 index 0000000..c43082a --- /dev/null +++ b/vendor/ipl/orm/src/Behavior/Binary.php @@ -0,0 +1,101 @@ +<?php + +namespace ipl\Orm\Behavior; + +use ipl\Orm\Contract\PropertyBehavior; +use ipl\Orm\Contract\QueryAwareBehavior; +use ipl\Orm\Contract\RewriteFilterBehavior; +use ipl\Orm\Exception\ValueConversionException; +use ipl\Orm\Query; +use ipl\Sql\Adapter\Pgsql; +use ipl\Stdlib\Filter\Condition; +use UnexpectedValueException; + +use function ipl\Stdlib\get_php_type; + +/** + * Support hex filters for binary columns and PHP resource (in) / bytea hex format (out) transformation for PostgreSQL + */ +class Binary extends PropertyBehavior implements QueryAwareBehavior, RewriteFilterBehavior +{ + /** @var bool Whether the query is using a pgsql adapter */ + protected $isPostgres = true; + + public function fromDb($value, $key, $_) + { + if (! $this->isPostgres) { + return $value; + } + + if ($value !== null) { + if (is_resource($value)) { + return stream_get_contents($value); + } + + return $value; + } + + return null; + } + + /** + * @throws ValueConversionException If value is a resource + */ + public function toDb($value, $key, $_) + { + if (! $this->isPostgres) { + return $value; + } + + if (is_resource($value)) { + throw new ValueConversionException(sprintf('Unexpected resource for %s', $key)); + } + + if ($value === '*') { + /** + * Support IS (NOT) NULL filter transformation. + * {@see \ipl\Sql\Compat\FilterProcessor::assemblePredicate()} + */ + return $value; + } + + return sprintf('\\x%s', bin2hex($value)); + } + + public function setQuery(Query $query) + { + $this->isPostgres = $query->getDb()->getAdapter() instanceof Pgsql; + + return $this; + } + + public function rewriteCondition(Condition $condition, $relation = null) + { + /** + * TODO(lippserd): Duplicate code because {@see RewriteFilterBehavior}s come after {@see PropertyBehavior}s. + * {@see \ipl\Orm\Compat\FilterProcessor::requireAndResolveFilterColumns()} + */ + $column = $condition->metaData()->get('columnName'); + if (isset($this->properties[$column])) { + $value = $condition->metaData()->get('originalValue'); + + if ($this->isPostgres && is_resource($value)) { + throw new UnexpectedValueException(sprintf('Unexpected resource for %s', $column)); + } + + // ctype_xdigit expects strings. + $value = (string) $value; + /** + * Although this code path is also affected by the duplicate behavior evaluation stated in {@see toDb()}, + * no further adjustments are needed as ctype_xdigit returns false for binary and bytea hex strings. + */ + if (ctype_xdigit($value)) { + if (! $this->isPostgres) { + $condition->setValue(hex2bin($value)); + } elseif (substr($value, 0, 2) !== '\\x') { + $condition->setValue(sprintf('\\x%s', $value)); + } + } + } + } +} diff --git a/vendor/ipl/orm/src/Behavior/BoolCast.php b/vendor/ipl/orm/src/Behavior/BoolCast.php new file mode 100644 index 0000000..ad1748a --- /dev/null +++ b/vendor/ipl/orm/src/Behavior/BoolCast.php @@ -0,0 +1,147 @@ +<?php + +namespace ipl\Orm\Behavior; + +use InvalidArgumentException; +use ipl\Orm\Contract\PropertyBehavior; +use ipl\Orm\Exception\ValueConversionException; + +use function ipl\Stdlib\get_php_type; + +/** + * Convert specific database values from and to boolean + * + * To unify the support of boolean values in different database systems, + * specific database values are converted to and from boolean values, + * e.g. by default `n` is converted to `false` and `y` to `true` and vice versa respectively, + * which could be stored as `ENUM('n', 'y')`. + */ +class BoolCast extends PropertyBehavior +{ + /** @var mixed Database value for boolean `false` */ + protected $falseValue = 'n'; + + /** @var mixed Database value for boolean `true` */ + protected $trueValue = 'y'; + + /** @var bool Whether to throw an exception if the value is not equal to the value for false or true */ + protected $strict = true; + + /** + * Get the database value representing boolean `false` + * + * @return mixed + */ + public function getFalseValue() + { + return $this->falseValue; + } + + /** + * Set the database value representing boolean `false` + * + * @param mixed $falseValue + * + * @return $this + */ + public function setFalseValue($falseValue): self + { + $this->falseValue = $falseValue; + + return $this; + } + + /** + * Get the database value representing boolean `true` + * + * @return mixed + */ + public function getTrueValue() + { + return $this->trueValue; + } + + /** + * Get the database value representing boolean `true` + * + * @param mixed $trueValue + * + * @return $this + */ + public function setTrueValue($trueValue): self + { + $this->trueValue = $trueValue; + + return $this; + } + + /** + * Get whether to throw an exception if the value is not equal to the value for false or true + * + * @return bool + */ + public function isStrict(): bool + { + return $this->strict; + } + + /** + * Set whether to throw an exception if the value is not equal to the value for false or true + * + * @param bool $strict + * + * @return $this + */ + public function setStrict(bool $strict): self + { + $this->strict = $strict; + + return $this; + } + + public function fromDb($value, $key, $_) + { + switch (true) { + case $this->trueValue === $value: + return true; + case $this->falseValue === $value: + return false; + default: + if ($this->isStrict() && $value !== null) { + throw new InvalidArgumentException(sprintf( + 'Expected %s or %s, got %s instead', + $this->trueValue, + $this->falseValue, + $value + )); + } + + return $value; + } + } + + public function toDb($value, $key, $_) + { + if ($value === null) { + return null; + } + + if (! is_bool($value)) { + if ( + $this->isStrict() + && $value !== '*' + && $value !== $this->getFalseValue() + && $value !== $this->getTrueValue() + ) { + throw new ValueConversionException(sprintf( + 'Expected bool, got %s instead', + get_php_type($value) + )); + } + + return $value; + } + + return $value ? $this->trueValue : $this->falseValue; + } +} diff --git a/vendor/ipl/orm/src/Behavior/MillisecondTimestamp.php b/vendor/ipl/orm/src/Behavior/MillisecondTimestamp.php new file mode 100644 index 0000000..65d8033 --- /dev/null +++ b/vendor/ipl/orm/src/Behavior/MillisecondTimestamp.php @@ -0,0 +1,41 @@ +<?php + +namespace ipl\Orm\Behavior; + +use DateTime; +use DateTimeZone; +use Exception; +use ipl\Orm\Contract\PropertyBehavior; +use ipl\Orm\Exception\ValueConversionException; + +class MillisecondTimestamp extends PropertyBehavior +{ + public function fromDb($value, $key, $context) + { + if ($value === null) { + return null; + } + + $datetime = DateTime::createFromFormat('U.u', sprintf('%F', $value / 1000.0)); + $datetime->setTimezone(new DateTimeZone(date_default_timezone_get())); + + return $datetime; + } + + public function toDb($value, $key, $context) + { + if (is_numeric($value)) { + return (int) ($value * 1000.0); + } + + if (! $value instanceof DateTime) { + try { + $value = new DateTime($value); + } catch (Exception $err) { + throw new ValueConversionException(sprintf('Invalid date time format provided: %s', $value)); + } + } + + return (int) ($value->format('U.u') * 1000.0); + } +} diff --git a/vendor/ipl/orm/src/Behaviors.php b/vendor/ipl/orm/src/Behaviors.php new file mode 100644 index 0000000..5c54350 --- /dev/null +++ b/vendor/ipl/orm/src/Behaviors.php @@ -0,0 +1,238 @@ +<?php + +namespace ipl\Orm; + +use ArrayIterator; +use ipl\Orm\Contract\PersistBehavior; +use ipl\Orm\Contract\PropertyBehavior; +use ipl\Orm\Contract\RetrieveBehavior; +use ipl\Orm\Contract\RewriteColumnBehavior; +use ipl\Orm\Contract\RewriteFilterBehavior; +use ipl\Orm\Contract\RewritePathBehavior; +use ipl\Stdlib\Filter; +use IteratorAggregate; +use Traversable; + +class Behaviors implements IteratorAggregate +{ + /** @var array Registered behaviors */ + protected $behaviors = []; + + /** @var RetrieveBehavior[] Registered retrieve behaviors */ + protected $retrieveBehaviors = []; + + /** @var PersistBehavior[] Registered persist behaviors */ + protected $persistBehaviors = []; + + /** @var PropertyBehavior[] Registered property behaviors */ + protected $propertyBehaviors = []; + + /** @var RewriteFilterBehavior[] Registered rewrite filter behaviors */ + protected $rewriteFilterBehaviors = []; + + /** @var RewriteColumnBehavior[] Registered rewrite column behaviors */ + protected $rewriteColumnBehaviors = []; + + /** @var RewritePathBehavior[] Registered rewrite path behaviors */ + protected $rewritePathBehaviors = []; + + /** + * Add a behavior + * + * @param PersistBehavior|PropertyBehavior|RetrieveBehavior|RewriteFilterBehavior $behavior + */ + public function add(Behavior $behavior) + { + $this->behaviors[] = $behavior; + + if ($behavior instanceof PropertyBehavior) { + $this->retrieveBehaviors[] = $behavior; + $this->persistBehaviors[] = $behavior; + $this->propertyBehaviors[] = $behavior; + } else { + if ($behavior instanceof RetrieveBehavior) { + $this->retrieveBehaviors[] = $behavior; + } + + if ($behavior instanceof PersistBehavior) { + $this->persistBehaviors[] = $behavior; + } + } + + if ($behavior instanceof RewriteFilterBehavior) { + $this->rewriteFilterBehaviors[] = $behavior; + } + + if ($behavior instanceof RewriteColumnBehavior) { + $this->rewriteColumnBehaviors[] = $behavior; + } + + if ($behavior instanceof RewritePathBehavior) { + $this->rewritePathBehaviors[] = $behavior; + } + } + + /** + * Iterate registered behaviors + * + * @return ArrayIterator + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->behaviors); + } + + /** + * Apply all retrieve behaviors on the given model + * + * @param Model $model + */ + public function retrieve(Model $model) + { + foreach ($this->retrieveBehaviors as $behavior) { + $behavior->retrieve($model); + } + } + + /** + * Apply all persist behaviors on the given model + * + * @param Model $model + */ + public function persist(Model $model) + { + foreach ($this->persistBehaviors as $behavior) { + $behavior->persist($model); + } + } + + /** + * Transform the retrieved key's value by use of all property behaviors + * + * @param mixed $value + * @param string $key + * + * @return mixed + */ + public function retrieveProperty($value, $key) + { + foreach ($this->propertyBehaviors as $behavior) { + $value = $behavior->retrieveProperty($value, $key); + } + + return $value; + } + + /** + * Transform the to be persisted key's value by use of all property behaviors + * + * @param mixed $value + * @param string $key + * + * @return mixed + */ + public function persistProperty($value, $key) + { + foreach ($this->propertyBehaviors as $behavior) { + $value = $behavior->persistProperty($value, $key); + } + + return $value; + } + + /** + * Rewrite the given filter condition + * + * @param Filter\Condition $condition + * @param string $relation Absolute path (with a trailing dot) of the model + * + * @return Filter\Rule|null + */ + public function rewriteCondition(Filter\Condition $condition, $relation = null) + { + $filter = null; + foreach ($this->rewriteFilterBehaviors as $behavior) { + $replacement = $behavior->rewriteCondition($filter ?: $condition, $relation); + if ($replacement !== null) { + $filter = $replacement; + } + } + + return $filter; + } + + /** + * Rewrite the given relation path + * + * @param string $path + * @param string $relation Absolute path of the model + * + * @return string|null + */ + public function rewritePath($path, $relation = null) + { + $newPath = null; + foreach ($this->rewritePathBehaviors as $behavior) { + $replacement = $behavior->rewritePath($newPath ?: $path, $relation); + if ($replacement !== null) { + $newPath = $replacement; + } + } + + return $newPath; + } + + /** + * Rewrite the given column + * + * @param string $column + * @param string $relation Absolute path of the model + * + * @return mixed + */ + public function rewriteColumn($column, $relation = null) + { + $newColumn = null; + foreach ($this->rewriteColumnBehaviors as $behavior) { + $replacement = $behavior->rewriteColumn($newColumn ?: $column, $relation); + if ($replacement !== null) { + $newColumn = $replacement; + } + } + + return $newColumn; + } + + /** + * Rewrite the given column definition + * + * @param ColumnDefinition $def + * @param string $relation Absolute path of the model + * + * @return void + */ + public function rewriteColumnDefinition(ColumnDefinition $def, string $relation): void + { + foreach ($this->rewriteColumnBehaviors as $behavior) { + $behavior->rewriteColumnDefinition($def, $relation); + } + } + + /** + * Get whether the given column is selectable + * + * @param string $column + * + * @return bool + */ + public function isSelectableColumn(string $column): bool + { + foreach ($this->rewriteColumnBehaviors as $behavior) { + if ($behavior->isSelectableColumn($column)) { + return true; + } + } + + return false; + } +} |