From 37beaa2db5d92381afc3ca7243b316bbdb51e57c Mon Sep 17 00:00:00 2001 From: Sarjuuk Date: Tue, 14 Oct 2025 19:36:47 +0200 Subject: [PATCH] Filter/Errors * move checks to __construct so they can be run on $_POST data and don't create malformed filter urls * if we received malformed $_GET params, build new params and reload * do not store error state in cache * cleanup --- endpoints/achievements/achievements.php | 10 +- endpoints/areatriggers/areatriggers.php | 10 +- endpoints/arena-teams/arena-teams.php | 6 + endpoints/enchantments/enchantments.php | 11 +- endpoints/guilds/guilds.php | 6 + endpoints/icons/icons.php | 10 +- endpoints/items/items.php | 11 +- endpoints/itemsets/itemsets.php | 10 +- endpoints/npcs/npcs.php | 10 +- endpoints/objects/objects.php | 10 +- endpoints/profiles/profiles.php | 10 +- endpoints/quests/quests.php | 10 +- endpoints/sounds/sounds.php | 11 +- endpoints/spells/spells.php | 10 +- includes/components/filter.class.php | 330 ++++++++++-------- .../components/frontend/listview.class.php | 4 +- includes/components/pagetemplate.class.php | 9 +- includes/dbtypes/profile.class.php | 4 +- 18 files changed, 280 insertions(+), 202 deletions(-) diff --git a/endpoints/achievements/achievements.php b/endpoints/achievements/achievements.php index fc82692c..58830919 100644 --- a/endpoints/achievements/achievements.php +++ b/endpoints/achievements/achievements.php @@ -54,6 +54,12 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new AchievementListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -69,13 +75,9 @@ class AchievementsBaseResponse extends TemplateResponse implements ICache if ($this->category) $conditions[] = ['category', end($this->category)]; - $this->filter->evalCriteria(); - if ($fiCnd = $this->filter->getConditions()) $conditions[] = $fiCnd; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /*************/ /* Menu Path */ diff --git a/endpoints/areatriggers/areatriggers.php b/endpoints/areatriggers/areatriggers.php index 55ffa720..a2e03b2d 100644 --- a/endpoints/areatriggers/areatriggers.php +++ b/endpoints/areatriggers/areatriggers.php @@ -33,6 +33,12 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache parent::__construct($pageParam); $this->filter = new AreaTriggerListFilter($this->_get['filter'] ?? ''); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -40,8 +46,6 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache { $this->h1 = Util::ucFirst(Lang::game('areatriggers')); - $this->filter->evalCriteria(); - $fiForm = $this->filter->values; @@ -73,8 +77,6 @@ class AreatriggersBaseResponse extends TemplateResponse implements ICache if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - $tabData = []; $trigger = new AreaTriggerList($conditions, ['calcTotal' => true]); if (!$trigger->error) diff --git a/endpoints/arena-teams/arena-teams.php b/endpoints/arena-teams/arena-teams.php index 2b8dbf73..5160833e 100644 --- a/endpoints/arena-teams/arena-teams.php +++ b/endpoints/arena-teams/arena-teams.php @@ -53,6 +53,12 @@ class ArenateamsBaseResponse extends TemplateResponse implements IProfilerList $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new ArenaTeamListFilter($this->_get['filter'] ?? '', ['realms' => $realms]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } diff --git a/endpoints/enchantments/enchantments.php b/endpoints/enchantments/enchantments.php index 2ca90fdb..2a2e80f0 100644 --- a/endpoints/enchantments/enchantments.php +++ b/endpoints/enchantments/enchantments.php @@ -35,6 +35,12 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new EnchantmentListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -42,18 +48,13 @@ class EnchantmentsBaseResponse extends TemplateResponse implements ICache { $this->h1 = Util::ucFirst(Lang::game('enchantments')); - $this->filter->evalCriteria(); - $conditions = []; - if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /**************/ /* Page Title */ diff --git a/endpoints/guilds/guilds.php b/endpoints/guilds/guilds.php index 4142461b..99108173 100644 --- a/endpoints/guilds/guilds.php +++ b/endpoints/guilds/guilds.php @@ -53,6 +53,12 @@ class GuildsBaseResponse extends TemplateResponse implements IProfilerList $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new GuildListFilter($this->_get['filter'] ?? '', ['realms' => $realms]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } diff --git a/endpoints/icons/icons.php b/endpoints/icons/icons.php index 7b2cf323..3ceab77a 100644 --- a/endpoints/icons/icons.php +++ b/endpoints/icons/icons.php @@ -32,6 +32,12 @@ class IconsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new IconListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -43,13 +49,9 @@ class IconsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /**************/ /* Page Title */ diff --git a/endpoints/items/items.php b/endpoints/items/items.php index 442f0c08..5c9f59be 100644 --- a/endpoints/items/items.php +++ b/endpoints/items/items.php @@ -99,6 +99,12 @@ class ItemsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new ItemListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -110,14 +116,9 @@ class ItemsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - $this->filter->evalWeights(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /*******************/ /* evaluate filter */ diff --git a/endpoints/itemsets/itemsets.php b/endpoints/itemsets/itemsets.php index d1905239..eecae8a4 100644 --- a/endpoints/itemsets/itemsets.php +++ b/endpoints/itemsets/itemsets.php @@ -32,6 +32,12 @@ class ItemsetsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new ItemsetListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -43,13 +49,9 @@ class ItemsetsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /*************/ /* Menu Path */ diff --git a/endpoints/npcs/npcs.php b/endpoints/npcs/npcs.php index 5a325144..6984cb45 100644 --- a/endpoints/npcs/npcs.php +++ b/endpoints/npcs/npcs.php @@ -35,6 +35,12 @@ class NpcsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new CreatureListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -46,13 +52,9 @@ class NpcsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - if ($this->category) { $conditions[] = ['type', $this->category[0]]; diff --git a/endpoints/objects/objects.php b/endpoints/objects/objects.php index 00b3d463..0c726548 100644 --- a/endpoints/objects/objects.php +++ b/endpoints/objects/objects.php @@ -35,6 +35,12 @@ class ObjectsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new GameObjectListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -46,16 +52,12 @@ class ObjectsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; if ($this->category) $conditions[] = ['typeCat', (int)$this->category[0]]; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /*************/ /* Menu Path */ diff --git a/endpoints/profiles/profiles.php b/endpoints/profiles/profiles.php index e3d4dd78..53d3c37b 100644 --- a/endpoints/profiles/profiles.php +++ b/endpoints/profiles/profiles.php @@ -57,6 +57,12 @@ class ProfilesBaseResponse extends TemplateResponse implements IProfilerList $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new ProfileListFilter($this->_get['filter'] ?? '', ['realms' => $realms]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -64,8 +70,6 @@ class ProfilesBaseResponse extends TemplateResponse implements IProfilerList { $this->h1 = Util::ucFirst(Lang::game('profiles')); - $this->filter->evalCriteria(); - /*************/ /* Menu Path */ @@ -94,8 +98,6 @@ class ProfilesBaseResponse extends TemplateResponse implements IProfilerList if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - $fiExtraCols = $this->filter->fiExtraCols; $lvData = []; diff --git a/endpoints/quests/quests.php b/endpoints/quests/quests.php index 78fac93e..562132a2 100644 --- a/endpoints/quests/quests.php +++ b/endpoints/quests/quests.php @@ -53,6 +53,12 @@ class QuestsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new QuestListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -64,13 +70,9 @@ class QuestsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - if (isset($this->category[1])) $conditions[] = ['zoneOrSort', $this->category[1]]; else if (isset($this->category[0])) diff --git a/endpoints/sounds/sounds.php b/endpoints/sounds/sounds.php index 9132c40f..d9f3e7f5 100644 --- a/endpoints/sounds/sounds.php +++ b/endpoints/sounds/sounds.php @@ -34,6 +34,12 @@ class SoundsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new SoundListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -41,18 +47,13 @@ class SoundsBaseResponse extends TemplateResponse implements ICache { $this->h1 = Util::ucFirst(Lang::game('sounds')); - $this->filter->evalCriteria(); - $conditions = []; - if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /**************/ /* Page Title */ diff --git a/endpoints/spells/spells.php b/endpoints/spells/spells.php index fa6c4533..968e9897 100644 --- a/endpoints/spells/spells.php +++ b/endpoints/spells/spells.php @@ -99,6 +99,12 @@ class SpellsBaseResponse extends TemplateResponse implements ICache $this->subCat = $pageParam !== '' ? '='.$pageParam : ''; $this->filter = new SpellListFilter($this->_get['filter'] ?? '', ['parentCats' => $this->category]); + if ($this->filter->shouldReload) + { + $_SESSION['error']['fi'] = $this->filter::class; + $get = $this->filter->buildGETParam(); + $this->forward('?' . $this->pageName . $this->subCat . ($get ? '&filter=' . $get : '')); + } $this->filterError = $this->filter->error; } @@ -110,13 +116,9 @@ class SpellsBaseResponse extends TemplateResponse implements ICache if (!User::isInGroup(U_GROUP_EMPLOYEE)) $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]; - $this->filter->evalCriteria(); - if ($_ = $this->filter->getConditions()) $conditions[] = $_; - $this->filterError = $this->filter->error; // maybe the evalX() caused something - /*************/ /* Menu Path */ diff --git a/includes/components/filter.class.php b/includes/components/filter.class.php index c9927cd5..c58117d0 100644 --- a/includes/components/filter.class.php +++ b/includes/components/filter.class.php @@ -91,12 +91,13 @@ abstract class Filter protected const ENUM_RACE = array( null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false); protected const ENUM_PROFESSION = array( null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773); - public bool $error = false; // erroneous search fields + public bool $error = false; + public bool $shouldReload = false; // erroneous params have been corrected. Build GET string and reload // item related - public array $upgrades = []; // [itemId => slotId] - public array $extraOpts = []; // score for statWeights - public array $wtCnd = []; // DBType condition for statWeights + public array $upgrades = []; // [itemId => slotId] + public array $extraOpts = []; // score for statWeights + public array $wtCnd = []; // DBType condition for statWeights private array $cndSet = []; // db type query storage private array $rawData = []; @@ -106,7 +107,7 @@ abstract class Filter [self::CR_FLAG, , , ] # default param2: matchExact [self::CR_NUMERIC, , , ] [self::CR_STRING, , , null] - [self::CR_ENUM, , , ] # param3 ? crv is val in enum : key in enum + [self::CR_ENUM, , , ] # param3 ? crv is val in enum : key in enum [self::CR_STAFFFLAG, , null, null] [self::CR_CALLBACK, , , ] [self::CR_NYI_PH, null, , param2] # mostly 1: to ignore this criterium; 0: to fail the whole query @@ -126,8 +127,7 @@ abstract class Filter public array $fiReputationCols = []; // fn params ([[factionId, factionName], ...]) public array $fiExtraCols = []; // public string $query = ''; // as in url query params - public array $values = []; // old fiData['v'] - public array $criteria = []; // old fiData['c'] + public array $values = []; // prefiltered rawData // parse the provided request into a usable format public function __construct(string|array $data, array $opts = []) @@ -157,6 +157,8 @@ abstract class Filter } $this->initFields(); + $this->evalCriteria(); + $this->evalWeights(); } public function mergeCat(array &$cats) : void @@ -167,13 +169,13 @@ abstract class Filter private function &criteriaIterator() : \Generator { - if (!$this->criteria) + if (empty($this->values['cr'])) return; - for ($i = 0; $i < count($this->criteria['cr']); $i++) + for ($i = 0; $i < count($this->values['cr']); $i++) { // throws a notice if yielded directly "Only variable references should be yielded by reference" - $v = [&$this->criteria['cr'][$i], &$this->criteria['crs'][$i], &$this->criteria['crv'][$i]]; + $v = [&$this->values['cr'][$i], &$this->values['crs'][$i], &$this->values['crv'][$i]]; yield $i => $v; } } @@ -195,7 +197,7 @@ abstract class Filter public function buildGETParam(array $override = [], array $addCr = []) : string { $get = []; - foreach (array_merge($this->criteria, $this->values, $override) as $k => $v) + foreach (array_merge($this->values, $override) as $k => $v) { if (isset($addCr[$k])) { @@ -228,7 +230,7 @@ abstract class Filter $this->cndSet = $this->createSQLForValues(); // criteria - foreach ($this->criteriaIterator() as &$_cr) + foreach ($this->criteriaIterator() as $_cr) if ($cnd = $this->createSQLForCriterium(...$_cr)) $this->cndSet[] = $cnd; @@ -241,10 +243,10 @@ abstract class Filter public function getSetCriteria(int ...$cr) : array { - if (!$cr || !$this->fiSetCriteria) - return $this->fiSetCriteria; + if (!$cr || empty($this->values['cr'])) + return []; - return array_values(array_intersect($this->fiSetCriteria['cr'], $cr)); + return array_values(array_intersect($this->values['cr'], $cr)); } @@ -258,12 +260,17 @@ abstract class Filter return []; $data = []; + + // someone copy/pasted a WH filter + $get = preg_replace('/^(\d+(:\d+)*);(\d+(:\d+)*);(\P{C}+(:\P{C}+)*)$/', 'cr=$1;crs=$3;crv=$5', $get); + foreach (explode(';', $get) as $field) { if (!strstr($field, '=')) { trigger_error('Filter::transformGET - malformed GET string', E_USER_NOTICE); - $this->error = true; + $this->error = + $this->shouldReload = true; continue; } @@ -272,7 +279,8 @@ abstract class Filter if (!isset(static::$inputFields[$k])) { trigger_error('Filter::transformGET - GET param not in filter: '.$k, E_USER_NOTICE); - $this->error = true; + $this->error = + $this->shouldReload = true; continue; } @@ -286,13 +294,17 @@ abstract class Filter private function initFields() : void { + // quirk: in the POST step criteria will be [[''], null, null] if no criteria are selected, + // due to the first criteria selector always being visible + if (($this->rawData['cr'] ?? null) === [''] && !isset($this->rawData['crs']) && !isset($this->rawData['crv'])) + unset($this->rawData['cr']); // unset or Filter::checkInput() screams bloody error + + $cleanupCr = []; foreach (static::$inputFields as $inp => [$type, $valid, $asArray]) { - $var = in_array($inp, ['cr', 'crs', 'crv']) ? 'criteria' : 'values'; - if (!isset($this->rawData[$inp]) || $this->rawData[$inp] === '') { - $this->$var[$inp] = $asArray ? [] : null; + $this->values[$inp] = $asArray ? [] : null; continue; } @@ -300,37 +312,57 @@ abstract class Filter if ($asArray) { - // quirk: in the POST step criteria can be [[''], null, null] if not selected. $buff = []; - foreach ((array)$val as $v) // can be string|int in POST step if only one value present - if ($v !== '' && $this->checkInput($type, $valid, $v)) + foreach ((array)$val as $i => $v) // can be string|int in POST step if only one value present + { + if (in_array($inp, ['cr', 'crs', 'crv'])) + { + if (!$this->checkInput($type, $valid, $v)) + $cleanupCr[] = $i; + $buff[] = $v; // always assign, gets removed later as tuple + } + else if ($this->checkInput($type, $valid, $v)) $buff[] = $v; + } - $this->$var[$inp] = $buff; + $this->values[$inp] = $buff; } else - $this->$var[$inp] = $this->checkInput($type, $valid, $val) ? $val : null; + $this->values[$inp] = $this->checkInput($type, $valid, $val) ? $val : null; + } + + if ($cleanupCr) + { + $this->error = + $this->shouldReload = true; + + foreach (array_unique($cleanupCr) as $i) + unset($this->values['cr'][$i], $this->values['crs'][$i], $this->values['crv'][$i]); + + $this->values['cr'] = array_values($this->values['cr']); + $this->values['crs'] = array_values($this->values['crs']); + $this->values['crv'] = array_values($this->values['crv']); } } - public function evalCriteria() : void // [cr]iterium, [cr].[s]ign, [cr].[v]alue + private function evalCriteria() : void // [cr]iterium, [cr].[s]ign, [cr].[v]alue { - if (empty($this->criteria['cr']) && empty($this->criteria['crs']) && empty($this->criteria['crv'])) + if (empty($this->values['cr']) && empty($this->values['crs']) && empty($this->values['crv'])) return; - else if (empty($this->criteria['cr']) || empty($this->criteria['crs']) || empty($this->criteria['crv'])) - { - unset($this->criteria['cr']); - unset($this->criteria['crs']); - unset($this->criteria['crv']); - trigger_error('Filter::setCriteria - one of cr, crs, crv is missing', E_USER_NOTICE); - $this->error = true; + if (empty($this->values['cr']) || empty($this->values['crs']) || empty($this->values['crv'])) + { + trigger_error('Filter::evalCriteria - one of cr, crs, crv is missing', E_USER_NOTICE); + unset($this->values['cr'], $this->values['crs'], $this->values['crv']); + + $this->error = + $this->shouldReload = true; return; } - $_cr = &$this->criteria['cr']; - $_crs = &$this->criteria['crs']; - $_crv = &$this->criteria['crv']; + $_cr = &$this->values['cr']; + $_crs = &$this->values['crs']; + $_crv = &$this->values['crv']; if (count($_cr) != count($_crv) || count($_cr) != count($_crs) || count($_cr) > 5 || count($_crs) > 5 /*|| count($_crv) > 5*/) { @@ -345,70 +377,88 @@ abstract class Filter if (count($_crs) > $min) array_splice($_crs, $min); - trigger_error('Filter::setCriteria - cr, crs, crv are imbalanced', E_USER_NOTICE); - $this->error = true; + trigger_error('Filter::evalCriteria - cr, crs, crv are imbalanced', E_USER_NOTICE); + $this->error = + $this->shouldReload = true; } for ($i = 0; $i < count($_cr); $i++) { - // conduct filter specific checks & casts here - $unsetme = false; - if (isset(static::$genericFilter[$_cr[$i]])) + if (!isset(static::$genericFilter[$_cr[$i]]) || $_crs[$i] === '' || $_crv[$i] === '') { - $gf = static::$genericFilter[$_cr[$i]]; - switch ($gf[0]) - { - case self::CR_NUMERIC: - $_ = $_crs[$i]; - if (!Util::checkNumeric($_crv[$i], $gf[2]) || !$this->int2Op($_)) - $unsetme = true; - break; - case self::CR_BOOLEAN: - case self::CR_FLAG: - $_ = $_crs[$i]; - if (!$this->int2Bool($_)) - $unsetme = true; - break; - case self::CR_ENUM: - case self::CR_STAFFFLAG: - if (!Util::checkNumeric($_crs[$i], NUM_CAST_INT)) - $unsetme = true; - break; - } + if ($_crs[$i] === '' || $_crv[$i] === '') + trigger_error('Filter::evalCriteria - received malformed criterium ["'.$_cr[$i].'", "'.$_crs[$i].'", "'.$_crv[$i].'"]', E_USER_NOTICE); + else + trigger_error('Filter::evalCriteria - received unhandled criterium: '.$_cr[$i], E_USER_NOTICE); + + unset($_cr[$i], $_crs[$i], $_crv[$i]); + + $this->error = + $this->shouldReload = true; + continue; } - if (!$unsetme && intval($_cr[$i]) && $_crs[$i] !== '' && $_crv[$i] !== '') - continue; + [$crType, $colOrFn, $param1, $param2] = array_pad(static::$genericFilter[$_cr[$i]], 4, null); - unset($_cr[$i]); - unset($_crs[$i]); - unset($_crv[$i]); + // conduct filter specific checks & casts here + switch ($crType) + { + case self::CR_NUMERIC: + $_ = $_crs[$i]; + if (Util::checkNumeric($_crv[$i], $param1) && $this->int2Op($_)) + continue 2; + break; + case self::CR_BOOLEAN: + case self::CR_FLAG: + $_ = $_crs[$i]; + if ($this->int2Bool($_)) + continue 2; + break; + case self::CR_STAFFFLAG: + if (User::isInGroup(U_GROUP_EMPLOYEE) && Util::checkNumeric($_crs[$i], NUM_CAST_INT)) + continue 2; + break; + case self::CR_ENUM: + if (Util::checkNumeric($_crs[$i], NUM_CAST_INT) && ( + (!$param2 && isset(static::$enums[$_cr[$i]][$_crs[$i]])) || + ($param2 && in_array($_crs[$i], static::$enums[$_cr[$i]])) || + ($param1 && ($_crs[$i] == self::ENUM_ANY || $_crs[$i] == self::ENUM_NONE)) + )) + continue 2; + break; + case self::CR_STRING: + case self::CR_CALLBACK: + case self::CR_NYI_PH: + continue 2; + default: + trigger_error('Filter::evalCriteria - unknown criteria type: '.$crType, E_USER_WARNING); + break; + } - trigger_error('Filter::setCriteria - generic check failed ["'.$_cr[$i].'", "'.$_crs[$i].'", "'.$_crv[$i].'"]', E_USER_NOTICE); - $this->error = true; + trigger_error('Filter::evalCriteria - generic check failed ["'.$_cr[$i].'", "'.$_crs[$i].'", "'.$_crv[$i].'"]', E_USER_NOTICE); + unset($_cr[$i], $_crs[$i], $_crv[$i]); + + $this->error = + $this->shouldReload = true; } - $this->fiSetCriteria = array( - 'cr' => $_cr, - 'crs' => $_crs, - 'crv' => $_crv - ); + $this->fiSetCriteria = [$_cr, $_crs, $_crv]; } - public function evalWeights() : void + private function evalWeights() : void { // both empty: not in use; not an error - if (!$this->values['wt'] && !$this->values['wtv']) + if (empty($this->values['wt']) && empty($this->values['wtv'])) return; // one empty: erroneous manual input? if (!$this->values['wt'] || !$this->values['wtv']) { - unset($this->values['wt']); - unset($this->values['wtv']); - trigger_error('Filter::setWeights - one of wt, wtv is missing', E_USER_NOTICE); - $this->error = true; + unset($this->values['wt'], $this->values['wtv']); + + $this->error = + $this->shouldReload = true; return; } @@ -421,7 +471,8 @@ abstract class Filter if ($nwt != $nwtv) { trigger_error('Filter::setWeights - wt, wtv are imbalanced', E_USER_NOTICE); - $this->error = true; + $this->error = + $this->shouldReload = true; } if ($nwt > $nwtv) @@ -649,83 +700,72 @@ abstract class Filter return null; } - private function genericCriterion(int $cr, int $crs, string $crv) : ?array - { - [$crType, $colOrFn, $param1, $param2] = array_pad(static::$genericFilter[$cr], 4, null); - $result = null; - - switch ($crType) - { - case self::CR_NUMERIC: - $result = $this->genericNumeric($colOrFn, $crv, $crs, $param1); - break; - case self::CR_FLAG: - $result = $this->genericBooleanFlags($colOrFn, $param1, $crs, $param2); - break; - case self::CR_STAFFFLAG: - if (User::isInGroup(U_GROUP_EMPLOYEE) && $crs > 0) - $result = $this->genericBooleanFlags($colOrFn, (1 << ($crs - 1)), true); - break; - case self::CR_BOOLEAN: - $result = $this->genericBoolean($colOrFn, $crs, !empty($param1)); - break; - case self::CR_STRING: - $result = $this->genericString($colOrFn, $crv, $param1); - break; - case self::CR_ENUM: - if (!$param2 && isset(static::$enums[$cr][$crs])) - $result = $this->genericEnum($colOrFn, static::$enums[$cr][$crs]); - if ($param2 && in_array($crs, static::$enums[$cr])) - $result = $this->genericEnum($colOrFn, $crs); - else if ($param1 && ($crs == self::ENUM_ANY || $crs == self::ENUM_NONE)) - $result = $this->genericEnum($colOrFn, $crs); - break; - case self::CR_CALLBACK: - $result = $this->{$colOrFn}($cr, $crs, $crv, $param1, $param2); - break; - case self::CR_NYI_PH: // do not limit with not implemented filters - if (is_int($param1)) - return [$param1]; - - // for nonsensical values; compare against 0 - if ($this->int2Op($crs) && Util::checkNumeric($crv)) - { - if ($crs == '=') - $crs = '=='; - - return eval('return ('.$crv.' '.$crs.' 0);') ? [1] : [0]; - } - else - return [0]; - } - - if ($result && $crType == self::CR_NUMERIC && !empty($param2)) - $this->fiExtraCols[] = $cr; - - return $result; - } - /***********************************/ /* create conditions from */ /* non-generic values and criteria */ /***********************************/ - protected function createSQLForCriterium(int &$cr, int &$crs, string &$crv) : array + protected function createSQLForCriterium(int $cr, int $crs, string $crv) : array { if (!static::$genericFilter) // criteria not in use - no error return []; - if (isset(static::$genericFilter[$cr])) - if ($genCr = $this->genericCriterion($cr, $crs, $crv)) - return $genCr; + [$crType, $colOrFn, $param1, $param2] = array_pad(static::$genericFilter[$cr], 4, null); - trigger_error('Filter::createSQLForCriterium - received unhandled criterium: ["'.$cr.'", "'.$crs.'", "'.$crv.'"]', E_USER_NOTICE); - $this->error = true; + $handleEnum = function(int $cr, int $crs, string $col, bool $hasAnyNone, bool $crsAsVal) : ?array + { + if ($hasAnyNone && ($crs == self::ENUM_ANY || $crs == self::ENUM_NONE)) + return $this->genericEnum($col, $crs); + else if (!$crsAsVal && isset(static::$enums[$cr][$crs])) + return $this->genericEnum($col, static::$enums[$cr][$crs]); + else if ($crsAsVal && in_array($crs, static::$enums[$cr])) + return $this->genericEnum($col, $crs); - unset($cr, $crs, $crv); + return null; + }; - return []; + $handleNYIPH = function(int $crs, string $crv, ?int $forceResult) : ?array + { + if (is_int($forceResult)) + return [$forceResult]; + + // for nonsensical values; compare against 0 + if ($this->int2Op($crs) && Util::checkNumeric($crv)) + { + if ($crs == '=') + $crs = '=='; + + return eval('return ('.$crv.' '.$crs.' 0);') ? [1] : [0]; + } + else + return [0]; + }; + + $result = match ($crType) + { + self::CR_NUMERIC => $this->genericNumeric($colOrFn, $crv, $crs, $param1), + self::CR_FLAG => $this->genericBooleanFlags($colOrFn, $param1, $crs, $param2), + self::CR_STAFFFLAG => $this->genericBooleanFlags($colOrFn, (1 << ($crs - 1)), true), + self::CR_BOOLEAN => $this->genericBoolean($colOrFn, $crs, !empty($param1)), + self::CR_STRING => $this->genericString($colOrFn, $crv, $param1), + self::CR_CALLBACK => $this->{$colOrFn}($cr, $crs, $crv, $param1, $param2), + self::CR_ENUM => $handleEnum($cr, $crs, $colOrFn, $param1, $param2), + self::CR_NYI_PH => $handleNYIPH($crs, $crv, $param1), + default => null + }; + + if (!$result) + { + // this really should not have happened. The relevant checks are run on __construct() + trigger_error('Filter::createSQLForCriterium - failed to resolve criterium: ["'.$cr.'", "'.$crs.'", "'.$crv.'"]', E_USER_WARNING); + return []; + } + + if ($crType == self::CR_NUMERIC && !empty($param2)) + $this->fiExtraCols[] = $cr; + + return $result; } abstract protected function createSQLForValues() : array; diff --git a/includes/components/frontend/listview.class.php b/includes/components/frontend/listview.class.php index 7180d6ba..9520d306 100644 --- a/includes/components/frontend/listview.class.php +++ b/includes/components/frontend/listview.class.php @@ -146,9 +146,9 @@ class Listview implements \JsonSerializable $this->tabs = $tabVar; } - public function setError() : void + public function setError(bool $enable) : void { - $this->_errors = 1; + $this->_errors = $enable ? 1 : null; } public function jsonSerialize() : array diff --git a/includes/components/pagetemplate.class.php b/includes/components/pagetemplate.class.php index 7eb11c8c..26822561 100644 --- a/includes/components/pagetemplate.class.php +++ b/includes/components/pagetemplate.class.php @@ -336,7 +336,7 @@ class PageTemplate $result[] = "var fi_type = '".$this->filter->fiType."'"; if ($this->filter->fiSetCriteria) // arr:criteria, arr:signs, arr:values - $result[] = 'fi_setCriteria('.mb_substr(Util::toJSON(array_values($this->filter->fiSetCriteria)), 1, -1).");"; + $result[] = 'fi_setCriteria('.mb_substr(Util::toJSON($this->filter->fiSetCriteria), 1, -1).");"; /* nt: don't try to match provided weights on predefined weight sets (preselects preset from opt list and ..?) @@ -496,7 +496,7 @@ class PageTemplate foreach ($this->lvTabs->iterate() as $lv) if ($lv instanceof \Aowow\Listview) - $lv->setError(); + $lv->setError(true); } // pre-serialization: if a var is relevant it was stored in $rawData @@ -505,6 +505,11 @@ class PageTemplate $this->context = null; // unlink from TemplateResponse $this->pageData = []; // clear modified data + if ($this->lvTabs) // do not store lvErrors in cache + foreach ($this->lvTabs->iterate() as $lv) + if ($lv instanceof \Aowow\Listview) + $lv->setError(false); + $vars = []; foreach ($this as $k => $_) $vars[] = $k; diff --git a/includes/dbtypes/profile.class.php b/includes/dbtypes/profile.class.php index 8aa712b1..cf75fcb1 100644 --- a/includes/dbtypes/profile.class.php +++ b/includes/dbtypes/profile.class.php @@ -269,8 +269,8 @@ class ProfileListFilter extends Filter { parent::__construct($data, $opts); - if (!empty($this->criteria['cr'])) - if (array_intersect($this->criteria['cr'], [2, 5, 6, 7, 21])) + if (!empty($this->values['cr'])) + if (array_intersect($this->values['cr'], [2, 5, 6, 7, 21])) $this->useLocalList = true; }