Passed
Push — master ( 5f90b3...823da0 )
by Aimeos
04:37
created

Base::getEditor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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-2021
7
 * @package MShop
8
 * @subpackage Common
9
 */
10
11
12
namespace Aimeos\MShop\Common\Item;
13
14
15
/**
16
 * Common methods for all item objects.
17
 *
18
 * @package MShop
19
 * @subpackage Common
20
 */
21
abstract class Base
22
	extends \Aimeos\MW\Common\Item\Base
23
	implements \Aimeos\MShop\Common\Item\Iface, \Aimeos\Macro\Iface, \ArrayAccess
24
{
25
	use \Aimeos\Macro\Macroable;
26
27
	private $available = true;
28
	private $modified = false;
29
	private $prefix;
30
31
	// protected due to PHP serialization
32
	protected $bdata;
33
34
35
	/**
36
	 * Initializes the class properties.
37
	 *
38
	 * @param string $prefix Prefix for the keys returned by toArray()
39
	 * @param array $values Associative list of key/value pairs of the item properties
40
	 */
41
	public function __construct( string $prefix, array $values )
42
	{
43
		$this->prefix = (string) $prefix;
44
		$this->bdata = $values;
45
	}
46
47
48
	/**
49
	 * Creates a deep clone of all objects
50
	 */
51
	public function __clone()
52
	{
53
	}
54
55
56
	/**
57
	 * Returns the item property for the given name
58
	 *
59
	 * @param string $name Name of the property
60
	 * @return mixed|null Property value or null if property is unknown
61
	 */
62
	public function __get( string $name )
63
	{
64
		return $this->get( $name );
65
	}
66
67
68
	/**
69
	 * Tests if the item property for the given name is available
70
	 *
71
	 * @param string $name Name of the property
72
	 * @return bool True if the property exists, false if not
73
	 */
74
	public function __isset( string $name ) : bool
75
	{
76
		return array_key_exists( $name, $this->bdata );
77
	}
78
79
80
	/**
81
	 * Sets the new item property for the given name
82
	 *
83
	 * @param string $name Name of the property
84
	 * @param mixed $value New property value
85
	 */
86
	public function __set( string $name, $value )
87
	{
88
		$this->set( $name, $value );
89
	}
90
91
92
	/**
93
	 * Tests if the item property for the given name is available
94
	 *
95
	 * @param string $name Name of the property
96
	 * @return bool True if the property exists, false if not
97
	 */
98
	public function offsetExists( $name )
99
	{
100
		return array_key_exists( $name, $this->bdata );
101
	}
102
103
104
	/**
105
	 * Returns the item property for the given name
106
	 *
107
	 * @param string $name Name of the property
108
	 * @return mixed|null Property value or null if property is unknown
109
	 */
110
	public function offsetGet( $name )
111
	{
112
		return $this->get( $name );
113
	}
114
115
116
	/**
117
	 * Sets the new item property for the given name
118
	 *
119
	 * @param string $name Name of the property
120
	 * @param mixed $value New property value
121
	 */
122
	public function offsetSet( $name, $value )
123
	{
124
		$this->set( $name, $value );
125
	}
126
127
128
	/**
129
	 * Removes an item property
130
	 * This is not supported by items
131
	 *
132
	 * @param string $name Name of the property
133
	 * @throws \LogicException Always thrown because this method isn't supported
134
	 */
135
	public function offsetUnset( $name )
136
	{
137
		throw new \LogicException( 'Not implemented' );
138
	}
139
140
141
	/**
142
	 * Returns the ID of the items
143
	 *
144
	 * @return string ID of the item or null
145
	 */
146
	public function __toString() : string
147
	{
148
		return (string) $this->getId();
149
	}
150
151
152
	/**
153
	 * Assigns multiple key/value pairs to the item
154
	 *
155
	 * @param iterable $pairs Associative list of key/value pairs
156
	 * @return \Aimeos\MShop\Common\Item\Iface Item for method chaining
157
	 */
158
	public function assign( iterable $pairs ) : \Aimeos\MShop\Common\Item\Iface
159
	{
160
		foreach( $pairs as $key => $value ) {
161
			$this->set( $key, $value );
162
		}
163
164
		return $this;
165
	}
166
167
168
	/**
169
	 * Returns the item property for the given name
170
	 *
171
	 * @param string $name Name of the property
172
	 * @param mixed $default Default value if property is unknown
173
	 * @return mixed|null Property value or default value if property is unknown
174
	 */
175
	public function get( string $name, $default = null )
176
	{
177
		if( array_key_exists( $name, $this->bdata ) ) {
178
			return $this->bdata[$name];
179
		}
180
181
		return $default;
182
	}
183
184
185
	/**
186
	 * Sets the new item property for the given name
187
	 *
188
	 * @param string $name Name of the property
189
	 * @param mixed $value New property value
190
	 * @return \Aimeos\MShop\Common\Item\Iface Item for method chaining
191
	 */
192
	public function set( string $name, $value ) : \Aimeos\MShop\Common\Item\Iface
193
	{
194
		// workaround for NULL values instead of empty strings and stringified integers from database
195
		if( !array_key_exists( $name, $this->bdata ) || $this->bdata[$name] != $value
196
			|| $value === null && $this->bdata[$name] !== null
197
			|| $value !== null && $this->bdata[$name] === null
198
		) {
199
			$this->bdata[$name] = $value;
200
			$this->setModified();
201
		}
202
203
		return $this;
204
	}
205
206
207
	/**
208
	 * Returns the ID of the item if available.
209
	 *
210
	 * @return string|null ID of the item
211
	 */
212
	public function getId() : ?string
213
	{
214
		$key = $this->prefix . 'id';
215
216
		if( isset( $this->bdata[$key] ) && $this->bdata[$key] != '' ) {
217
			return (string) $this->bdata[$key];
218
		}
219
220
		return null;
221
	}
222
223
224
	/**
225
	 * Sets the new ID of the item.
226
	 *
227
	 * @param string|null $id ID of the item
228
	 * @return \Aimeos\MShop\Common\Item\Iface Item for chaining method calls
229
	 */
230
	public function setId( ?string $id ) : \Aimeos\MShop\Common\Item\Iface
231
	{
232
		$key = $this->prefix . 'id';
233
234
		if( ( $this->bdata[$key] = $this->checkId( $this->getId(), $id ) ) === null ) {
235
			$this->modified = true;
236
		} else {
237
			$this->modified = false;
238
		}
239
240
		return $this;
241
	}
242
243
244
	/**
245
	 * Returns the site ID of the item.
246
	 *
247
	 * @return string Site ID or null if no site id is available
248
	 */
249
	public function getSiteId() : string
250
	{
251
		return $this->get( $this->prefix . 'siteid', $this->get( 'siteid', '' ) );
252
	}
253
254
255
	/**
256
	 * Returns the list site IDs up to the root site item.
257
	 *
258
	 * @return array List of site IDs
259
	 */
260
	public function getSitePath() : array
261
	{
262
		$pos = 0;
263
		$list = [];
264
		$siteId = $this->getSiteId();
265
266
		while( ( $pos = strpos( $siteId, '.', $pos ) ) !== false ) {
267
			$list[] = substr( $siteId, 0, ++$pos );
268
		}
269
270
		return $list;
271
	}
272
273
274
	/**
275
	 * Returns modify date/time of the order coupon.
276
	 *
277
	 * @return string|null Modification time (YYYY-MM-DD HH:mm:ss)
278
	 */
279
	public function getTimeModified() : ?string
280
	{
281
		return $this->get( $this->prefix . 'mtime', $this->get( 'mtime' ) );
282
	}
283
284
285
	/**
286
	 * Returns the create date of the item.
287
	 *
288
	 * @return string|null ISO date in YYYY-MM-DD hh:mm:ss format
289
	 */
290
	public function getTimeCreated() : ?string
291
	{
292
		return $this->get( $this->prefix . 'ctime', $this->get( 'ctime' ) );
293
	}
294
295
296
	/**
297
	 * Returns the name of editor who created/modified the item at last.
298
	 *
299
	 * @return string Name of editor who created/modified the item at last
300
	 */
301
	public function editor() : string
302
	{
303
		return $this->get( $this->prefix . 'editor', $this->get( 'editor', '' ) );
304
	}
305
306
307
	/**
308
	 * Tests if the item is available based on status, time, language and currency
309
	 *
310
	 * @return bool True if available, false if not
311
	 */
312
	public function isAvailable() : bool
313
	{
314
		return $this->available;
315
	}
316
317
318
	/**
319
	 * Sets the general availability of the item
320
	 *
321
	 * @return bool $value True if available, false if not
322
	 * @return \Aimeos\MShop\Common\Item\Iface Item for chaining method calls
323
	 */
324
	public function setAvailable( bool $value ) : \Aimeos\MShop\Common\Item\Iface
325
	{
326
		$this->available = $value;
327
		return $this;
328
	}
329
330
331
	/**
332
	 * Tests if this Item object was modified.
333
	 *
334
	 * @return bool True if modified, false if not
335
	 */
336
	public function isModified() : bool
337
	{
338
		return $this->modified;
339
	}
340
341
342
	/**
343
	 * Sets the modified flag of the object.
344
	 *
345
	 * @return \Aimeos\MShop\Common\Item\Iface Item for chaining method calls
346
	 */
347
	public function setModified() : \Aimeos\MShop\Common\Item\Iface
348
	{
349
		$this->modified = true;
350
		return $this;
351
	}
352
353
354
	/**
355
	 * Sets the item values from the given array and removes that entries from the list
356
	 *
357
	 * @param array $list Associative list of item keys and their values
358
	 * @param bool True to set private properties too, false for public only
359
	 * @return \Aimeos\MShop\Common\Item\Iface Item for chaining method calls
360
	 */
361
	public function fromArray( array &$list, bool $private = false ) : \Aimeos\MShop\Common\Item\Iface
362
	{
363
		if( $private && array_key_exists( $this->prefix . 'id', $list ) )
364
		{
365
			$this->setId( $list[$this->prefix . 'id'] );
366
			unset( $list[$this->prefix . 'id'] );
367
		}
368
369
		// Add custom columns
370
		foreach( $list as $key => $value )
371
		{
372
			if( ( $value === null || is_scalar( $value ) ) && strpos( $key, '.' ) === false ) {
373
				$this->set( $key, $value );
374
			}
375
		}
376
377
		return $this;
378
	}
379
380
381
	/**
382
	 * Returns the item values as array.
383
	 *
384
	 * @param bool True to return private properties, false for public only
385
	 * @return array Associative list of item properties and their values
386
	 */
387
	public function toArray( bool $private = false ) : array
388
	{
389
		$list = [$this->prefix . 'id' => $this->getId()];
390
391
		if( $private === true )
392
		{
393
			$list[$this->prefix . 'siteid'] = $this->getSiteId();
394
			$list[$this->prefix . 'ctime'] = $this->getTimeCreated();
395
			$list[$this->prefix . 'mtime'] = $this->getTimeModified();
396
			$list[$this->prefix . 'editor'] = $this->editor();
397
		}
398
399
		foreach( $this->bdata as $key => $value )
400
		{
401
			if( strpos( $key, '.' ) === false ) {
402
				$list[$key] = $value;
403
			}
404
		}
405
406
		return $list;
407
	}
408
409
410
	/**
411
	 * Checks if the new ID is valid for the item.
412
	 *
413
	 * @param string|null $old Current ID of the item
414
	 * @param string|null $new New ID which should be set in the item
415
	 * @return string|null Value of the new ID
416
	 */
417
	public static function checkId( ?string $old, ?string $new ) : ?string
418
	{
419
		return ( $new !== null ? (string) $new : $new );
420
	}
421
422
423
	/**
424
	 * Tests if the date parameter represents an ISO format.
425
	 *
426
	 * @param string|null $date ISO date in yyyy-mm-dd HH:ii:ss format or null
427
	 * @return string|null Clean date or null for no date
428
	 * @throws \Aimeos\MShop\Exception If the date is invalid
429
	 */
430
	protected function checkDateFormat( ?string $date ) : ?string
431
	{
432
		$regex = '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9](( |T)[0-2][0-9]:[0-5][0-9](:[0-5][0-9])?)?$/';
433
434
		if( $date != null )
435
		{
436
			if( preg_match( $regex, (string) $date ) !== 1 ) {
437
				throw new \Aimeos\MShop\Exception( sprintf( 'Invalid characters in date, ISO format "YYYY-MM-DD hh:mm:ss" expected' ) );
438
			}
439
440
			if( strlen( $date ) === 16 ) {
441
				$date .= ':00';
442
			}
443
444
			return str_replace( 'T', ' ', (string) $date );
445
		}
446
447
		return null;
448
	}
449
450
451
	/**
452
	 * Tests if the date param represents an ISO format.
453
	 *
454
	 * @param string|null $date ISO date in YYYY-MM-DD format or null for no date
455
	 */
456
	protected function checkDateOnlyFormat( ?string $date ) : ?string
457
	{
458
		if( $date !== null && $date !== '' )
459
		{
460
			if( preg_match( '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/', (string) $date ) !== 1 ) {
461
				throw new \Aimeos\MShop\Exception( sprintf( 'Invalid characters in date, ISO format "YYYY-MM-DD" expected' ) );
462
			}
463
464
			return (string) $date;
465
		}
466
467
		return null;
468
	}
469
470
471
	/**
472
	 * Tests if the code is valid.
473
	 *
474
	 * @param string $code New code for an item
475
	 * @param int $length Number of allowed characters
476
	 * @return string Item code
477
	 * @throws \Aimeos\MShop\Exception If the code is invalid
478
	 */
479
	protected function checkCode( string $code, int $length = 64 ) : string
480
	{
481
		if( strlen( $code ) > $length ) {
482
			throw new \Aimeos\MShop\Exception( sprintf( 'Code is too long' ) );
483
		}
484
485
		return (string) $code;
486
	}
487
488
489
	/**
490
	 * Tests if the country ID parameter represents an ISO country format.
491
	 *
492
	 * @param string|null $countryid Two letter ISO country format, e.g. DE
493
	 * @param bool $null True if null is allowed, false if not
494
	 * @return string|null Two letter ISO country ID or null for no country
495
	 * @throws \Aimeos\MShop\Exception If the country ID is invalid
496
	 */
497
	protected function checkCountryId( ?string $countryid, bool $null = true ) : ?string
498
	{
499
		if( $null === false && $countryid == null ) {
500
			throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO country code' ) );
501
		}
502
503
		if( $countryid != null )
504
		{
505
			if( preg_match( '/^[A-Za-z]{2}$/', $countryid ) !== 1 ) {
506
				throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO country code' ) );
507
			}
508
509
			return strtoupper( $countryid );
510
		}
511
512
		return null;
513
	}
514
515
516
	/**
517
	 * Tests if the currency ID parameter represents an ISO currency format.
518
	 *
519
	 * @param string|null $currencyid Three letter ISO currency format, e.g. EUR
520
	 * @param bool $null True if null is allowed, false if not
521
	 * @return string|null Three letter ISO currency ID or null for no currency
522
	 * @throws \Aimeos\MShop\Exception If the currency ID is invalid
523
	 */
524
	protected function checkCurrencyId( ?string $currencyid, bool $null = true ) : ?string
525
	{
526
		if( $null === false && $currencyid == null ) {
527
			throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO currency code' ) );
528
		}
529
530
		if( $currencyid != null )
531
		{
532
			if( preg_match( '/^[A-Z]{3}$/', $currencyid ) !== 1 ) {
533
				throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO currency code' ) );
534
			}
535
536
			return strtoupper( $currencyid );
537
		}
538
539
		return null;
540
	}
541
542
543
	/**
544
	 * Tests if the language ID parameter represents an ISO language format.
545
	 *
546
	 * @param string|null $langid ISO language format, e.g. de or de_DE
547
	 * @param bool $null True if null is allowed, false if not
548
	 * @return string|null ISO language ID or null for no language
549
	 * @throws \Aimeos\MShop\Exception If the language ID is invalid
550
	 */
551
	protected function checkLanguageId( ?string $langid, bool $null = true ) : ?string
552
	{
553
		if( $null === false && $langid == null ) {
554
			throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO language code' ) );
555
		}
556
557
		if( $langid != null )
558
		{
559
			if( preg_match( '/^[a-zA-Z]{2}(_[a-zA-Z]{2})?$/', $langid ) !== 1 ) {
560
				throw new \Aimeos\MShop\Exception( sprintf( 'Invalid ISO language code' ) );
561
			}
562
563
			$parts = explode( '_', $langid );
564
			$parts[0] = strtolower( $parts[0] );
565
566
			if( isset( $parts[1] ) ) {
567
				$parts[1] = strtoupper( $parts[1] );
568
			}
569
570
			return implode( '_', $parts );
571
		}
572
573
		return null;
574
	}
575
}
576