]> git.mxchange.org Git - core.git/blob - framework/main/classes/criteria/search/class_SearchCriteria.php
Continued:
[core.git] / framework / main / classes / criteria / search / class_SearchCriteria.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Criteria\Search;
4
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;
10
11 // Import SPL stuff
12 use \InvalidArgumentException;
13 use \UnexpectedValueException;
14
15 /**
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.
19  *
20  * @author              Roland Haeder <webmaster@shipsimu.org>
21  * @version             0.0.0
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
25  *
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.
30  *
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.
35  *
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/>.
38  */
39 class SearchCriteria extends BaseCriteria implements LocalSearchCriteria {
40         /**
41          * Criteria to handle
42          */
43         private $criteria = [];
44
45         /**
46          * Limitation for the search
47          */
48         private $limit = 0;
49
50         /**
51          * Skip these entries before using them
52          */
53         private $skip = 0;
54
55         /**
56          * Protected constructor
57          *
58          * @return      void
59          */
60         private function __construct () {
61                 // Call parent constructor
62                 parent::__construct(__CLASS__);
63         }
64
65         /**
66          * Create an instance of this class
67          *
68          * @return      $criteriaInstance       An instance of this criteria
69          */
70         public static final function createSearchCriteria () {
71                 // Get a new instance
72                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('SEARCH-CRITERIA: CALLED!');
73                 $criteriaInstance = new SearchCriteria();
74
75                 // Return this instance
76                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: criteriaInstance=%s - EXIT!', $criteriaInstance->__toString()));
77                 return $criteriaInstance;
78         }
79
80         /**
81          * Setter for limit
82          *
83          * @param       $limit  Search limit
84          * @return      void
85          * @todo        Find a nice casting here. (int) allows until and including 32766.
86          */
87         public final function setLimit (int $limit) {
88                 $this->limit = $limit;
89         }
90
91         /**
92          * "Setter" for limit from a configuration entry
93          *
94          * @param       $configKey      The configuration entry which hold a number as limit
95          * @return      void
96          * @throws      InvalidArgumentException        If a paramter has an invalid value
97          */
98         public final function setConfiguredLimit (string $configKey) {
99                 // Check parameter
100                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: configKey=%s - CALLED!', $configKey));
101                 if (empty($configKey)) {
102                         // Throw IAE
103                         throw new InvalidArgumentException('Parameter "configKey" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
104                 }
105
106                 // Get the limit from config entry
107                 $limit = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry($configKey);
108
109                 // And set it
110                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: limit=%d', $limit));
111                 $this->setLimit($limit);
112
113                 // Trace message
114                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('SEARCH-CRITERIA: EXIT!');
115         }
116
117         /**
118          * Getter for limit
119          *
120          * @return      $limit  Search limit
121          */
122         public final function getLimit () {
123                 return $this->limit;
124         }
125
126         /**
127          * Setter for skip
128          *
129          * @param       $skip   Search skip
130          * @return      void
131          * @todo        Find a nice casting here. (int) allows until and including 32766.
132          */
133         public final function setSkip (int $skip) {
134                 $this->skip = $skip;
135         }
136
137         /**
138          * Getter for skip
139          *
140          * @return      $skip   Search skip
141          */
142         public final function getSkip () {
143                 return $this->skip;
144         }
145
146         /**
147          * Checks whether the given key/value pair is matching with 'default' and one of 'choice' and
148          * never with in 'exclude'.
149          *
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
156          */
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));
160                 if (empty($key)) {
161                         // Throw IAE
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)) {
164                         // Throw it again
165                         throw new InvalidArgumentException(sprintf('value[]=%s is not supported/valid', gettype($value)));
166                 } elseif (empty($separator)) {
167                         // Throw IAE
168                         throw new InvalidArgumentException('Parameter "separator" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
169                 }
170
171                 // "Explode" value
172                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: Invoking explode("%s",value[]=%s) ...', $separator, gettype($value)));
173                 $valueArray = explode($separator, $value);
174                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: valueArray()=%d', count($valueArray)));
175
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);
179
180                 // 'default' check
181                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchDefault[%s]=%s', gettype($searchDefault), $searchDefault));
182                 $isMatching = (
183                         (
184                                 (
185                                         ($searchDefault !== false) &&
186                                         ($searchDefault == $value)
187                                 ) || (
188                                         is_null($searchDefault) &&
189                                         is_null($value)
190                                 )
191                         ) || (
192                                 $searchDefault === false
193                         )
194                 );
195
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);
199
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) && !is_bool($searchChoice)) {
203                         // Should not happen
204                         throw new UnexpectedValueException(sprintf('searchChoice[]=%s is unexpected', gettype($searchChoice)), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
205                 }
206
207                 // 'choice' check
208                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchChoice[]=%s', gettype($searchChoice)));
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)));
212
213                         // Debug message
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))));
219
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)));
226
227                                 // No longer matching?
228                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - LOOP!', intval($isMatching)));
229                                 if (!$isMatching) {
230                                         // Skip further iterations
231                                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('SEARCH-CRITERIA: BREAK!');
232                                         break;
233                                 }
234                         }
235
236                         // Debug message
237                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - AFTER!', intval($isMatching)));
238                 } else {
239                         // Choice-match is false
240                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: key=%s[],searchChoice[]=%s,value[%s]=%s,isMatching=%d - FALSE-MATCH', $key, gettype($searchChoice), gettype($value), $value, intval($isMatching)));
241                 }
242
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);
246
247                 // 'exclude' check
248                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('SEARCH-CRITERIA: searchExclude[%s]=%s', gettype($searchExclude), $searchExclude));
249                 $isMatching = (
250                         (
251                                 (
252                                         $isMatching === true
253                                 ) && (
254                                         $searchExclude === false
255                                 )
256                         ) || (
257                                 (
258                                         (
259                                                 $isMatching === true
260                                         ) && (
261                                                 $searchExclude !== false
262                                         ) && (
263                                                 $searchExclude !== $value
264                                         )
265                                 )
266                         )
267                 );
268
269                 // Return result
270                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('SEARCH-CRITERIA: isMatching=%d - EXIT!', intval($isMatching)));
271                 return $isMatching;
272         }
273
274 }