Base::type()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
c 0
b 0
f 0
dl 0
loc 17
rs 8.8333
cc 7
nc 7
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2025
7
 * @package Base
8
 * @subpackage Common
9
 */
10
11
12
namespace Aimeos\Base\Criteria;
13
14
15
/**
16
 * Abstract search class
17
 *
18
 * @package Base
19
 * @subpackage Common
20
 */
21
abstract class Base implements \Aimeos\Base\Criteria\Iface
22
{
23
	/**
24
	 * Tests if all list entries implement the passed interface name
25
	 *
26
	 * @param string $interface Interface name
27
	 * @param iterable $list List of items
28
	 * @return iterable List of tested items
29
	 * @throws \Aimeos\Base\Exception If at least one items doesn't implement the interface
30
	 */
31
	public static function implements( string $interface, iterable $list ) : iterable
32
	{
33
		foreach( $list as $entry )
34
		{
35
			if( !( $entry instanceof $interface ) ) {
36
				throw new \Aimeos\Base\Exception( "Does not implement $interface: " . print_r( $entry, true ) );
0 ignored issues
show
Bug introduced by
Are you sure print_r($entry, true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

36
				throw new \Aimeos\Base\Exception( "Does not implement $interface: " . /** @scrutinizer ignore-type */ print_r( $entry, true ) );
Loading history...
37
			}
38
		}
39
40
		return $list;
41
	}
42
43
44
	/**
45
	 * Returns the database type constant for the given type value
46
	 *
47
	 * @param string $value Type value
48
	 * @return int Database type constant
49
	 */
50
	public static function type( string $value ) : int
51
	{
52
		switch( $value )
53
		{
54
			case 'null':
55
				return \Aimeos\Base\DB\Statement\Base::PARAM_NULL;
56
			case 'bool':
57
			case 'boolean':
58
				return \Aimeos\Base\DB\Statement\Base::PARAM_BOOL;
59
			case 'int':
60
			case 'integer':
61
				return \Aimeos\Base\DB\Statement\Base::PARAM_INT;
62
			case 'float':
63
				return \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT;
64
		}
65
66
		return \Aimeos\Base\DB\Statement\Base::PARAM_STR;
67
	}
68
69
70
	/**
71
	 * Returns an array representation of the expression that can be parsed again
72
	 *
73
	 * @return array Multi-dimensional expression structure
74
	 */
75
	public function __toArray() : array
76
	{
77
		$cond = $this->getConditions();
78
		return $cond ? $cond->__toArray() : [];
79
	}
80
81
82
	/**
83
	 * Adds a new expression to the existing list combined by the AND operator.
84
	 *
85
	 * You can add expression is three ways:
86
	 *
87
	 * - Name, operator and value:
88
	 *   $f->add( 'product.code', '==', 'abc' );
89
	 *
90
	 * - Name/value pairs and optional operator ("==" by default):
91
	 *   $f->add( ['product.type' => 'voucher', 'product.status' => 1], '!=' );
92
	 *   $f->add( ['product.type' => 'default', 'product.status' => 1] );
93
	 *
94
	 * - Single expression:
95
	 *   $f->add( $f->is( 'product.code', '==', 'abc' ) );
96
	 *   $f->add( $f->and( [$f->is( 'product.code', '==', 'abc' ), $f->is( 'product.status', '>', 0 )] );
97
	 *   $f->add( $f->or( [$f->is( 'product.code', '==', 'abc' ), $f->is( 'product.label', '=~', 'abc' )] );
98
	 *   $f->add( $f->not( $f->is( 'product.code', '=~', 'abc' ) );
99
	 *
100
	 * @param \Aimeos\Base\Criteria\Expression\Combine\Iface|\Aimeos\Base\Criteria\Expression\Compare\Iface|array|string|null Expression, list of name/value pairs or name
101
	 * @param string $operator Operator to compare name and value with
102
	 * @param mixed $value Value to compare the name with
103
	 * @return \Aimeos\Base\Criteria\Iface Same object for fluent interface
104
	 */
105
	public function add( $expr, string $operator = '==', $value = null ) : \Aimeos\Base\Criteria\Iface
106
	{
107
		$cond = [];
108
109
		if( is_null( $expr ) ) {
110
			return $this;
111
		}
112
113
		if( is_string( $expr ) ) {
114
			$cond[] = $this->compare( $operator, $expr, $value );
115
		}
116
117
		if( is_array( $expr ) )
118
		{
119
			$list = [];
120
121
			foreach( $expr as $name => $value ) {
122
				$list[] = $this->compare( $operator, $name, $value );
123
			}
124
125
			$cond[] = $this->and( $list );
126
		}
127
128
		if( $expr instanceof \Aimeos\Base\Criteria\Expression\Combine\Iface
129
			|| $expr instanceof \Aimeos\Base\Criteria\Expression\Compare\Iface
130
		) {
131
			$cond[] = $expr;
132
		}
133
134
		if( !empty( $cond ) )
135
		{
136
			$cond[] = $this->getConditions();
137
			return $this->setConditions( $this->and( $cond ) );
138
		}
139
140
		$msg = 'Use a column name, an array of name/value pairs or the result from and(), or(), not() or is() as first argument for add()';
141
		throw new \Aimeos\Base\Exception( $msg );
142
	}
143
144
145
	/**
146
	 * Combines the expression with an AND operator
147
	 *
148
	 * @param \Aimeos\Base\Criteria\Expression\Compare\Iface[] $list List of expression objects
149
	 * @return \Aimeos\Base\Criteria\Expression\Combine\Iface Combine expression object
150
	 */
151
	public function and( array $list ) : \Aimeos\Base\Criteria\Expression\Combine\Iface
152
	{
153
		return $this->combine( '&&', $list );
154
	}
155
156
157
	/**
158
	 * Creates a new compare expression.
159
	 *
160
	 * Available comparision operators are:
161
	 * "==": item EQUAL value
162
	 * "!=": item NOT EQUAL value
163
	 * "~=": item LIKE value
164
	 * ">=": item GREATER OR EQUAL value
165
	 * "<=": item SMALLER OR EQUAL value
166
	 * ">": item GREATER value
167
	 * "<": item SMALLER value
168
	 *
169
	 * @param string $name Name of the column or property that should be used for comparison
170
	 * @param string $operator One of the known operators
171
	 * @param mixed $value Value the column or property should be compared to
172
	 * @return \Aimeos\Base\Criteria\Expression\Compare\Iface Compare expression object
173
	 */
174
	public function is( string $name, string $operator, $value ) : \Aimeos\Base\Criteria\Expression\Compare\Iface
175
	{
176
		return $this->compare( $operator, $name, $value );
177
	}
178
179
180
	/**
181
	 * Creates a function signature for expressions used in is() and add().
182
	 *
183
	 * @param string $name Function name without parentheses
184
	 * @param array $params Single- or multi-dimensional list of parameters of type boolean, integer, float and string
185
	 * @return string Function signature
186
	 */
187
	public function make( string $name, array $params ) : string
188
	{
189
		return $name . '(' . substr( json_encode( $params ), 1, -1 ) . ')';
190
	}
191
192
193
	/**
194
	 * Negates the whole expression.
195
	 *
196
	 * @param \Aimeos\Base\Criteria\Expression\Iface $expr Expression object
197
	 * @return \Aimeos\Base\Criteria\Expression\Combine\Iface Combine expression object
198
	 */
199
	public function not( \Aimeos\Base\Criteria\Expression\Iface $expr ) : \Aimeos\Base\Criteria\Expression\Combine\Iface
200
	{
201
		return $this->combine( '!', [$expr] );
202
	}
203
204
205
	/**
206
	 * Combines the expression with an OR operator
207
	 *
208
	 * @param \Aimeos\Base\Criteria\Expression\Compare\Iface[] $list List of expression objects
209
	 * @return \Aimeos\Base\Criteria\Expression\Combine\Iface Combine expression object
210
	 */
211
	public function or( array $list ) : \Aimeos\Base\Criteria\Expression\Combine\Iface
212
	{
213
		return $this->combine( '||', $list );
214
	}
215
216
217
	/**
218
	 * Sets the keys the data should be ordered by.
219
	 *
220
	 *
221
	 * Available sorting operators are:
222
	 * "product.label": sort ascending
223
	 * "-product.label": sort descending
224
	 *
225
	 * @param array|string $keys Name of the column or property that should be used for sorting
226
	 * @return \Aimeos\Base\Criteria\Iface Object instance for fluent interface
227
	 */
228
	public function order( $names ) : \Aimeos\Base\Criteria\Iface
229
	{
230
		$sort = [];
231
232
		foreach( (array) $names as $name )
233
		{
234
			$op = '+';
235
			$name = (string) $name;
236
237
			if( strlen( $name ) && $name[0] === '-' ) {
238
				$op = '-'; $name = substr( $name, 1 );
239
			}
240
241
			$sort[] = $this->sort( $op, $name );
242
		}
243
244
		return $this->setSortations( $sort );
245
	}
246
247
248
	/**
249
	 * Creates condition expressions from a multi-dimensional associative array.
250
	 *
251
	 * The simplest form of a valid associative array is a single comparison:
252
	 * 	$array = [
253
	 * 		'==' => ['name' => 'value'],
254
	 * 	];
255
	 *
256
	 * Combining several conditions can look like:
257
	 * 	$array = [
258
	 * 		'&&' => [
259
	 * 			['==' => ['name' => 'value']],
260
	 * 			['==' => ['name2' => 'value2']],
261
	 * 		],
262
	 * 	];
263
	 *
264
	 * Nested combine operators are also possible.
265
	 *
266
	 * @param array $array Multi-dimensional associative array containing the expression arrays
267
	 * @return \Aimeos\Base\Criteria\Expression\Iface|null Condition expressions (maybe nested) or null for none
268
	 * @throws \Aimeos\Base\Exception If given array is invalid
269
	 */
270
	public function parse( array $array ) : ?\Aimeos\Base\Criteria\Expression\Iface
271
	{
272
		if( ( $value = reset( $array ) ) === false ) {
273
			return null;
274
		}
275
276
		$op = key( $array );
277
		$operators = $this->getOperators();
278
279
		if( in_array( $op, $operators['combine'], true ) ) {
280
			return $this->createCombineExpression( $op, (array) $value );
281
		}
282
		else if( in_array( $op, $operators['compare'], true ) ) {
283
			return $this->createCompareExpression( $op, (array) $value );
284
		}
285
286
		throw new \Aimeos\Base\Exception( sprintf( 'Invalid operator "%1$s"', $op ) );
287
	}
288
289
290
	/**
291
	 * Returns the list of translated colums
292
	 *
293
	 * @param array $columns List of objects implementing getName() method
294
	 * @param array $translations Associative list of item names that should be translated
295
	 * @param array $funcs Associative list of item names and functions modifying the conditions
296
	 * @return array List of translated columns
297
	 */
298
	public function translate( array $columns, array $translations = [], array $funcs = [] ) : array
299
	{
300
		$list = [];
301
302
		foreach( $columns as $item )
303
		{
304
			if( ( $value = $item->translate( $translations, $funcs ) ) !== null ) {
305
				$list[] = $value;
306
			}
307
		}
308
309
		return $list;
310
	}
311
312
313
	/**
314
	 * Creates a "combine" expression.
315
	 *
316
	 * @param string $operator One of the "combine" operators
317
	 * @param array $list List of arrays with "combine" or "compare" representations
318
	 * @return \Aimeos\Base\Criteria\Expression\Combine\Iface Combine expression object
319
	 * @throws \Aimeos\Base\Exception If operator is invalid
320
	 */
321
	protected function createCombineExpression( string $operator, array $list )
322
	{
323
		$results = [];
324
		$operators = $this->getOperators();
325
326
		foreach( $list as $entry )
327
		{
328
			$entry = (array) $entry;
329
330
			if( ( $op = key( $entry ) ) === null ) {
331
				throw new \Aimeos\Base\Exception( sprintf( 'Invalid combine condition array "%1$s"', json_encode( $entry ) ) );
332
			}
333
334
			if( in_array( $op, $operators['combine'], true ) ) {
335
				$results[] = $this->createCombineExpression( $op, (array) $entry[$op] );
336
			}
337
			else if( in_array( $op, $operators['compare'], true ) ) {
338
				$results[] = $this->createCompareExpression( $op, (array) $entry[$op] );
339
			}
340
			else {
341
				throw new \Aimeos\Base\Exception( sprintf( 'Invalid operator "%1$s"', $op ) );
342
			}
343
		}
344
345
		return $this->combine( $operator, $results );
346
	}
347
348
349
	/**
350
	 * Creates a "compare" expression.
351
	 *
352
	 * @param string $op One of the "compare" operators
353
	 * @param array $pair Associative list containing one name/value pair
354
	 * @return \Aimeos\Base\Criteria\Expression\Compare\Iface Compare expression object
355
	 * @throws \Aimeos\Base\Exception If no name/value pair is available
356
	 */
357
	protected function createCompareExpression( $op, array $pair )
358
	{
359
		if( ( $value = reset( $pair ) ) === false ) {
360
			throw new \Aimeos\Base\Exception( sprintf( 'Invalid compare condition array "%1$s"', json_encode( $pair ) ) );
361
		}
362
363
		return $this->compare( $op, key( $pair ), $value );
364
	}
365
}
366