3 namespace Org\Mxchange\CoreFramework\Criteria\Search;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Criteria\BaseCriteria;
8 use Org\Mxchange\CoreFramework\Criteria\Local\LocalSearchCriteria;
9 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
12 use \InvalidArgumentException;
13 use \UnexpectedValueException;
16 * Search criteria for e.g. searching in databases. Do not use this class if
17 * you are looking for a ship or company, or what ever. Instead use this class
18 * for looking in storages like the database.
20 * @author Roland Haeder <webmaster@shipsimu.org>
22 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
23 * @license GNU GPL 3.0 or any newer version
24 * @link http://www.shipsimu.org
26 * This program is free software: you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation, either version 3 of the License, or
29 * (at your option) any later version.
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public License
37 * along with this program. If not, see <http://www.gnu.org/licenses/>.
39 class SearchCriteria extends BaseCriteria implements LocalSearchCriteria {
43 private $criteria = [];
46 * Limitation for the search
51 * Skip these entries before using them
56 * Protected constructor
60 private function __construct () {
61 // Call parent constructor
62 parent::__construct(__CLASS__);
66 * Create an instance of this class
68 * @return $criteriaInstance An instance of this criteria
70 public static final function createSearchCriteria () {
72 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('SEARCH-CRITERIA: CALLED!');
73 $criteriaInstance = new SearchCriteria();
75 // Return this instance
76 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: criteriaInstance=%s - EXIT!', $criteriaInstance->__toString()));
77 return $criteriaInstance;
83 * @param $limit Search limit
85 * @todo Find a nice casting here. (int) allows until and including 32766.
87 public final function setLimit (int $limit) {
88 $this->limit = $limit;
92 * "Setter" for limit from a configuration entry
94 * @param $configKey The configuration entry which hold a number as limit
96 * @throws InvalidArgumentException If a paramter has an invalid value
98 public final function setConfiguredLimit (string $configKey) {
100 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: configKey=%s - CALLED!', $configKey));
101 if (empty($configKey)) {
103 throw new InvalidArgumentException('Parameter "configKey" is empty');
106 // Get the limit from config entry
107 $limit = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($configKey);
110 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: limit=%d', $limit));
111 $this->setLimit($limit);
114 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('SEARCH-CRITERIA: EXIT!');
120 * @return $limit Search limit
122 public final function getLimit () {
129 * @param $skip Search skip
131 * @todo Find a nice casting here. (int) allows until and including 32766.
133 public final function setSkip (int $skip) {
140 * @return $skip Search skip
142 public final function getSkip () {
147 * Checks whether the given key/value pair is matching with 'default' and one of 'choice' and
148 * never with in 'exclude'.
150 * @param $key Key element to check
151 * @param $value Value to check
152 * @param $separator Separator for "exploding" $value (default: ',')
153 * @return $isMatching Whether the key/value is matching or excluded
154 * @throws InvalidArgumentException If a parameter is invalid
155 * @throws UnexpectedValueException If $searchChoice is not an array
157 public function isCriteriaMatching (string $key, $value, string $separator = ',') {
158 // $key/$value cannot be array/NULL/bool, value can be NULL but then NULL must be loocked for
159 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: key=%s,value[]=%s,separator=%s - CALLED!', $key, gettype($value), $separator));
162 throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
163 } elseif (is_array($value) || is_bool($value) || is_object($value) || is_resource($value)) {
165 throw new InvalidArgumentException(sprintf('value[]=%s is not supported/valid', gettype($value)));
166 } elseif (empty($separator)) {
168 throw new InvalidArgumentException('Parameter "separator" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
172 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: Invoking explode(%s,value[]=%s) ...', $seperator, gettype($value)));
173 $valueArray = explode($separator, $value);
174 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: valueArray()=%d', count($valueArray)));
176 // Get 'default' search value
177 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: Invoking this->getCriteriaElemnent(%s) ...', $key));
178 $searchDefault = $this->getCriteriaElemnent($key);
181 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchDefault[%s]=%s', gettype($searchDefault), $searchDefault));
185 ($searchDefault !== false) &&
186 ($searchDefault == $value)
188 is_null($searchDefault) &&
192 $searchDefault === false
196 // Get 'choice' search value (can be NULL or $separator-separated string)
197 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d', intval($isMatching)));
198 $searchChoice = $this->getCriteriaChoiceElemnent($key);
200 // Is an array returned?
201 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchChoice[]=%s', gettype($searchChoice)));
202 if (!is_array($searchChoice)) {
204 throw new UnexpectedValueException(sprintf('searchChoice[]=%s is unexpected', gettype($searchChoice)), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
208 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchChoice(%d)=%s', count($searchChoice), print_r($searchChoice, true)));
209 if ((is_array($searchChoice)) && (count($valueArray) == 1)) {
210 // $value is a single-search value, so use in_[]
211 $isMatching = ((($isMatching === true) || (($searchDefault === false) && (!is_null($value)))) && (in_array($value, $searchChoice)));
214 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - SINGLE-MATCH', intval($isMatching)));
215 } elseif ((is_array($searchChoice)) && (count($valueArray) > 1)) {
216 // $value is choice-search value, so check all entries
217 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchChoice()=%d,valueArray()=%d - MULTI-MATCH', count($searchChoice), count($valueArray)));
218 $isMatching = (($isMatching === true) || (($searchDefault === false) && (!is_null($value))));
220 // Loop through all values
221 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - BEFORE!', intval($isMatching)));
222 foreach ($valueArray as $idx => $match) {
223 // Is it found? (one is okay)
224 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: idx=%d,match=%s', $idx, $match));
225 $isMatching = (($isMatching === true) && (in_array($match, $searchChoice)));
227 // No longer matching?
228 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - LOOP!', intval($isMatching)));
230 // Skip further iterations
231 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('SEARCH-CRITERIA: BREAK!');
237 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - AFTER!', intval($isMatching)));
239 // Choice-match is false
240 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: key=%s[],searchChoice()=%d,value[%s]=%s,isMatching=%d - FALSE-MATCH', $key, count($searchChoice), gettype($value), $value, intval($isMatching)));
243 // Get 'exclude' search value
244 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: Invoking this->getCriteriaExcludeElemnent(%s) ...', $key));
245 $searchExclude = $this->getCriteriaExcludeElemnent($key);
248 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchExclude[%s]=%s', gettype($searchExclude), $searchExclude));
254 $searchExclude === false
261 $searchExclude !== false
263 $searchExclude !== $value
270 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - EXIT!', intval($isMatching)));