From 6cb9887f036e405a3e9d0ff2a2a665a16e540bca Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 25 Feb 2022 15:22:57 +0100 Subject: [PATCH] new: [metaFields] Support of meta-fields types --- .../default/meta_fields_types/IPv4Type.php | 178 ++++++++++++++++++ .../default/meta_fields_types/TextType.php | 36 ++++ src/Model/Behavior/MetaFieldsBehavior.php | 40 +++- 3 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 src/Lib/default/meta_fields_types/IPv4Type.php create mode 100644 src/Lib/default/meta_fields_types/TextType.php diff --git a/src/Lib/default/meta_fields_types/IPv4Type.php b/src/Lib/default/meta_fields_types/IPv4Type.php new file mode 100644 index 0000000..c2b28c8 --- /dev/null +++ b/src/Lib/default/meta_fields_types/IPv4Type.php @@ -0,0 +1,178 @@ +setQueryExpression($exp, $searchValue, $metaTemplateField); + } + $allMetaValues = $this->fetchAllValuesForThisType([], $metaTemplateField); + $isNegation = false; + if (substr($searchValue, 0, 1) == '!') { + $searchValue = substr($searchValue, 1); + $isNegation = true; + } + + foreach ($allMetaValues as $fieldID => $ip) { + if (!$this->IPInCidrBlock($searchValue, $ip)) { + if (!$isNegation) { + unset($allMetaValues[$fieldID]); + } + } else if ($isNegation) { + unset($allMetaValues[$fieldID]); + } + } + $matchingIDs = array_keys($allMetaValues); + if (!empty($matchingIDs)) { + $exp->in('MetaFields.id', $matchingIDs); + } else { + $exp->eq('MetaFields.id', -1); // No matching meta-fields, generate an impossible condition to return nothing + } + return $exp; + } + + private function fetchAllMetatemplateFieldsIdForThisType(\App\Model\Entity\MetaTemplateField $metaTemplateField = null): Query + { + $conditions =[]; + if (!is_null($metaTemplateField)) { + $conditions['id'] = $metaTemplateField->id; + } else { + $conditions['type'] = IPv4Type::TYPE; + } + $query = $this->MetaTemplateFields->find()->select(['id']) + ->distinct() + ->where($conditions); + return $query; + } + + private function fetchAllValuesForThisType(array $conditions=[], \App\Model\Entity\MetaTemplateField $metaTemplateField=null): array + { + $metaTemplateFieldsIDs = $this->fetchAllMetatemplateFieldsIdForThisType($metaTemplateField); + if (empty($metaTemplateFieldsIDs)) { + return []; + } + $conditions = array_merge($conditions, ['meta_template_field_id IN' => $metaTemplateFieldsIDs]); + $allMetaValues = $this->MetaFields->find('list', [ + 'keyField' => 'id', + 'valueField' => 'value' + ])->where($conditions)->toArray(); + return $allMetaValues; + } + + /** + * Convert a CIDR block to an array containing the minimum and maximum IP address for this block + * + * @param string $cidr an CIDR block with the form x.x.x.x/yy + * @return array + */ + protected function cidrToRange($cidr): array + { + $range = array(); + $cidr = explode('/', $cidr); + $range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1])))); + $range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1); + return $range; + } + + /** + * Check if the provided IP in contained by the CIDR block + * + * @param string $ip + * @param string $cidr an CIDR block with the form x.x.x.x/yy + * @return boolean + */ + private function _IPInCidrBlock(string $ip, string $cidr): bool + { + $range = $this->cidrToRange($cidr); + return ip2long($range[0]) <= ip2long($ip) && ip2long($ip) <= ip2long($range[1]); + } + + /** + * Check if the provided cidr block in contained by the CIDR block + * + * @param string $cidr1 an CIDR block with the form x.x.x.x/yy + * @param string $cidr2 an CIDR block with the form x.x.x.x/yy + * @return boolean + */ + private function _cidrInCidrBlock(string $cidr1, string $cidr2): bool + { + $range = $this->cidrToRange($cidr1); + return $this->_IPInCidrBlock($range[0], $cidr2) && $this->_IPInCidrBlock($range[1], $cidr2); + } + + private function _isValidIP(string $value): bool + { + return filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + } + + private function _isValidCidrBlock(string $value): bool + { + $explodedValue = explode('/', $value); + return $this->_isValidIP($explodedValue[0]); + } + + private function _isValidIPOrCidrBlock(string $value): bool + { + $explodedValue = explode('/', $value); + if (count($explodedValue) == 1) { + return $this->_isValidIP($value); + } else if (count($explodedValue) == 2) { + return $this->_isValidCidrBlock($value); + } + return false; + } + + protected function IPInCidrBlock(string $ip, string $cidr, bool $throw=false): bool + { + if (!$this->_isValidCidrBlock($cidr)) { + if ($throw) { + throw new TypeError("Invalid CDIR block."); + } + return false; + } + if (!$this->_isValidIPOrCidrBlock($ip)) { + if ($throw) { + throw new TypeError("Invalid IP."); + } + return false; + } + + $explodedIp = explode('/', $ip); + if (count($explodedIp) == 1) { + return $this->_IPInCidrBlock($ip, $cidr); + } else { + return $this->_cidrInCidrBlock($ip, $cidr); + } + } +} diff --git a/src/Lib/default/meta_fields_types/TextType.php b/src/Lib/default/meta_fields_types/TextType.php new file mode 100644 index 0000000..900a4a5 --- /dev/null +++ b/src/Lib/default/meta_fields_types/TextType.php @@ -0,0 +1,36 @@ +MetaFields = TableRegistry::getTableLocator()->get('MetaFields'); + $this->MetaTemplateFields = TableRegistry::getTableLocator()->get('MetaTemplateFields'); + } + + public function validate(string $value): bool + { + return is_string($value); + } + + public function setQueryExpression(QueryExpression $exp, string $searchValue, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression + { + $field = 'MetaFields.value'; + if (substr($searchValue, 0, 1) == '!') { + $searchValue = substr($searchValue, 1); + $exp->notEq($field, $searchValue); + } else if (strpos($searchValue, '%') !== false) { + $exp->like($field, $searchValue); + } else { + $exp->eq($field, $searchValue); + } + return $exp; + } +} diff --git a/src/Model/Behavior/MetaFieldsBehavior.php b/src/Model/Behavior/MetaFieldsBehavior.php index 9d520a6..7fb62aa 100644 --- a/src/Model/Behavior/MetaFieldsBehavior.php +++ b/src/Model/Behavior/MetaFieldsBehavior.php @@ -6,10 +6,14 @@ use Cake\ORM\Behavior; use Cake\ORM\Entity; use Cake\ORM\Query; use Cake\ORM\Table; +use Cake\ORM\TableRegistry; use Cake\Utility\Inflector; use Cake\Database\Expression\QueryExpression; -use function PHPSTORM_META\type; +use MetaFieldsTypes\TextType; +use MetaFieldsTypes\IPv4Type; +require_once(APP . 'Lib' . DS . 'default' . DS . 'meta_fields_types' . DS . 'TextType.php'); +require_once(APP . 'Lib' . DS . 'default' . DS . 'meta_fields_types' . DS . 'IPv4Type.php'); class MetaFieldsBehavior extends Behavior { @@ -44,12 +48,25 @@ class MetaFieldsBehavior extends Behavior ]; private $aliasScope = null; + private $typeHandlers = []; public function initialize(array $config): void { $this->bindAssociations(); $this->_metaTemplateFieldTable = $this->_table; $this->_metaTemplateTable = $this->_table; + $this->loadTypeHandlers(); + } + + private function loadTypeHandlers() + { + $typeHandlers = [ + new TextType(), + new IPv4Type(), + ]; + foreach ($typeHandlers as $handler) { + $this->typeHandlers[$handler::TYPE] = $handler; + } } public function getScope() @@ -198,7 +215,7 @@ class MetaFieldsBehavior extends Behavior } - protected function getQueryExpressionForField(QueryExpression $exp, string $field, string $value): QueryExpression + protected function setQueryExpressionForTextField(QueryExpression $exp, string $field, string $value): QueryExpression { if (substr($value, 0, 1) == '!') { $value = substr($value, 1); @@ -213,10 +230,16 @@ class MetaFieldsBehavior extends Behavior protected function buildQuerySnippet(array $filter): Query { - $whereClosure = function (QueryExpression $exp) use ($filter) { + $this->MetaTemplateFields = TableRegistry::getTableLocator()->get('MetaTemplateFields'); + $metaTemplateField = $this->MetaTemplateFields->get($filter['meta_template_field_id']); + $whereClosure = function (QueryExpression $exp) use ($filter, $metaTemplateField) { foreach ($filter as $column => $value) { $keyedColumn = 'MetaFields.' . $column; - $this->getQueryExpressionForField($exp, $keyedColumn, $value); + if ($column == 'value') { + $this->setQueryExpressionForField($exp, $keyedColumn, $value, $metaTemplateField); + } else { + $this->setQueryExpressionForTextField($exp, $keyedColumn, $value); + } } return $exp; }; @@ -228,4 +251,13 @@ class MetaFieldsBehavior extends Behavior return $query; } + protected function setQueryExpressionForField(QueryExpression $exp, string $field, string $value, \App\Model\Entity\MetaTemplateField $metaTemplateField): QueryExpression + { + if (isset($this->typeHandlers[$metaTemplateField->type])) { + $exp = $this->typeHandlers[$metaTemplateField->type]->setQueryExpression($exp, $value, $metaTemplateField); + } else { + $exp = $this->setQueryExpressionForTextField($exp, $field, $value); + } + return $exp; + } }