Completed
Push — master ( 8112ea...73e535 )
by Aimeos
13:12
created

Base::checkConfig()   C

Complexity

Conditions 32
Paths 24

Size

Total Lines 77
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 32
eloc 53
nc 24
nop 2
dl 0
loc 77
rs 5.0735
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2017
7
 * @package MShop
8
 * @subpackage Service
9
 */
10
11
12
namespace Aimeos\MShop\Service\Provider;
13
14
15
/**
16
 * Abstract class for all service provider implementations with some default methods.
17
 *
18
 * @package MShop
19
 * @subpackage Service
20
 */
21
abstract class Base
22
{
23
	private $context;
24
	private $serviceItem;
25
	private $communication;
26
	private $beGlobalConfig;
27
28
29
	/**
30
	 * Initializes the service provider object.
31
	 *
32
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object with required objects
33
	 * @param \Aimeos\MShop\Service\Item\Iface $serviceItem Service item with configuration for the provider
34
	 */
35
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, \Aimeos\MShop\Service\Item\Iface $serviceItem )
36
	{
37
		$this->context = $context;
38
		$this->serviceItem = $serviceItem;
39
	}
40
41
42
	/**
43
	 * Catch unknown methods
44
	 *
45
	 * @param string $name Name of the method
46
	 * @param array $param List of method parameter
47
	 * @throws \Aimeos\MShop\Common\Manager\Exception If method call failed
48
	 */
49
	public function __call( $name, array $param )
50
	{
51
		throw new \Aimeos\MShop\Service\Exception( sprintf( 'Unable to call method "%1$s"', $name ) );
52
	}
53
54
55
	/**
56
	 * Returns the price when using the provider.
57
	 * Usually, this is the lowest price that is available in the service item but can also be a calculated based on
58
	 * the basket content, e.g. 2% of the value as transaction cost.
59
	 *
60
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
61
	 * @return \Aimeos\MShop\Price\Item\Iface Price item containing the price, shipping, rebate
62
	 */
63
	public function calcPrice( \Aimeos\MShop\Order\Item\Base\Iface $basket )
64
	{
65
		$priceManager = \Aimeos\MShop\Factory::createManager( $this->context, 'price' );
66
		$prices = $this->serviceItem->getRefItems( 'price', 'default', 'default' );
67
68
		if( count( $prices ) > 0 ) {
69
			return $priceManager->getLowestPrice( $prices, 1 );
70
		}
71
72
		return $priceManager->createItem();
73
	}
74
75
76
	/**
77
	 * Checks the backend configuration attributes for validity.
78
	 *
79
	 * @param array $attributes Attributes added by the shop owner in the administraton interface
80
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
81
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
82
	 */
83
	public function checkConfigBE( array $attributes )
84
	{
85
		return [];
86
	}
87
88
89
	/**
90
	 * Checks the frontend configuration attributes for validity.
91
	 *
92
	 * @param array $attributes Attributes entered by the customer during the checkout process
93
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
94
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
95
	 */
96
	public function checkConfigFE( array $attributes )
97
	{
98
		return [];
99
	}
100
101
102
	/**
103
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
104
	 * rules for the value of each field in the administration interface.
105
	 *
106
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
107
	 */
108
	public function getConfigBE()
109
	{
110
		return [];
111
	}
112
113
114
	/**
115
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
116
	 * rules for the value of each field in the frontend.
117
	 *
118
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
119
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
120
	 */
121
	public function getConfigFE( \Aimeos\MShop\Order\Item\Base\Iface $basket )
122
	{
123
		return [];
124
	}
125
126
127
	/**
128
	 * Returns the service item which also includes the configuration for the service provider.
129
	 *
130
	 * @return \Aimeos\MShop\Service\Item\Iface Service item
131
	 */
132
	public function getServiceItem()
133
	{
134
		return $this->serviceItem;
135
	}
136
137
138
	/**
139
	 * Injects additional global configuration for the backend.
140
	 *
141
	 * It's used for adding additional backend configuration from the application
142
	 * like the URLs to redirect to.
143
	 *
144
	 * Supported redirect URLs are:
145
	 * - payment.url-success
146
	 * - payment.url-failure
147
	 * - payment.url-cancel
148
	 * - payment.url-update
149
	 *
150
	 * @param array $config Associative list of config keys and their value
151
	 */
152
	public function injectGlobalConfigBE( array $config )
153
	{
154
		$this->beGlobalConfig = $config;
155
	}
156
157
158
	/**
159
	 * Checks if payment provider can be used based on the basket content.
160
	 * Checks for country, currency, address, RMS, etc. -> in separate decorators
161
	 *
162
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket object
163
	 * @return boolean True if payment provider can be used, false if not
164
	 */
165
	public function isAvailable( \Aimeos\MShop\Order\Item\Base\Iface $basket )
166
	{
167
		return true;
168
	}
169
170
171
	/**
172
	 * Checks what features the payment provider implements.
173
	 *
174
	 * @param integer $what Constant from abstract class
175
	 * @return boolean True if feature is available in the payment provider, false if not
176
	 */
177
	public function isImplemented( $what )
178
	{
179
		return false;
180
	}
181
182
183
	/**
184
	 * Queries for status updates for the given order if supported.
185
	 *
186
	 * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object
187
	 */
188
	public function query( \Aimeos\MShop\Order\Item\Iface $order )
189
	{
190
		throw new \Aimeos\MShop\Service\Exception( sprintf( 'Method "%1$s" for provider not available', 'query' ) );
191
	}
192
193
194
	/**
195
	 * Looks for new update files and updates the orders for which status updates were received.
196
	 * If batch processing of files isn't supported, this method can be empty.
197
	 *
198
	 * @return boolean True if the update was successful, false if async updates are not supported
199
	 * @throws \Aimeos\MShop\Service\Exception If updating one of the orders failed
200
	 */
201
	public function updateAsync()
202
	{
203
		return false;
204
	}
205
206
207
	/**
208
	 * Updates the orders for which status updates were received via direct requests (like HTTP).
209
	 *
210
	 * @param array $params Associative list of request parameters
211
	 * @param string|null $body Information sent within the body of the request
212
	 * @param string|null &$response Response body for notification requests
213
	 * @param array &$header Response headers for notification requests
214
	 * @return \Aimeos\MShop\Order\Item\Iface|null Order item if update was successful, null if the given parameters are not valid for this provider
215
	 * @throws \Aimeos\MShop\Service\Exception If updating one of the orders failed
216
	 */
217
	public function updateSync( array $params = [], $body = null, &$response = null, array &$header = [] )
218
	{
219
		if( isset( $params['orderid'] ) ) {
220
			return $this->getOrder( $params['orderid'] );
221
		}
222
223
		return null;
224
	}
225
226
227
	/**
228
	 * Sets the communication object for a service provider.
229
	 *
230
	 * @param \Aimeos\MW\Communication\Iface $communication Object of communication
231
	 */
232
	public function setCommunication( \Aimeos\MW\Communication\Iface $communication )
233
	{
234
		$this->communication = $communication;
235
	}
236
237
238
	/**
239
	 * Returns the communication object for the service provider.
240
	 *
241
	 * @return \Aimeos\MW\Communication\Iface Object for communication
242
	 */
243
	protected function getCommunication()
244
	{
245
		if( !isset( $this->communication ) ) {
246
			$this->communication = new \Aimeos\MW\Communication\Curl();
247
		}
248
249
		return $this->communication;
250
	}
251
252
253
	/**
254
	 * Calculates the last date behind the given timestamp depending on the other paramters.
255
	 *
256
	 * This method is used to calculate the date for comparing the order date to
257
	 * if e.g. credit card payments should be captured or direct debit should be
258
	 * checked after the given amount of days from external payment providers.
259
	 * This method can calculate with business/working days only if requested
260
	 * and use the given list of public holidays to take them into account.
261
	 *
262
	 * @param integer $timestamp Timestamp to use as starting point for the backward calculation
263
	 * @param integer $skipdays Number of days to calculate backwards
264
	 * @param boolean $businessOnly True if only business days should be used for calculation, false if not
265
	 * @param string $publicHolidays Comma separated list of public holidays in YYYY-MM-DD format
266
	 * @return string Date in YYY-MM-DD format to be compared to the order date
267
	 * @throws \Aimeos\MShop\Service\Exception If the given holiday string is in the wrong format and can't be processed
268
	 */
269
	protected function calcDateLimit( $timestamp, $skipdays = 0, $businessOnly = false, $publicHolidays = '' )
270
	{
271
		$holidays = $this->getPublicHolidays( $publicHolidays );
272
273
		if( !empty( $holidays ) )
274
		{
275
			for( $i = 0; $i <= $skipdays; $i++ )
276
			{
277
				$date = date( 'Y-m-d', $timestamp - $i * 86400 );
278
279
				if( isset( $holidays[$date] ) ) {
280
					$skipdays++;
281
				}
282
			}
283
		}
284
285
		if( $businessOnly === true )
286
		{
287
			// adds days for weekends
288
			for( $i = 0; $i <= $skipdays; $i++ )
289
			{
290
				$ts = $timestamp - $i * 86400;
291
292
				if( date( 'N', $ts ) > 5 && !isset( $holidays[date( 'Y-m-d', $ts )] ) ) {
293
					$skipdays++;
294
				}
295
			}
296
		}
297
298
		return date( 'Y-m-d', $timestamp - $skipdays * 86400 );
299
	}
300
301
302
	/**
303
	 * Checks required fields and the types of the config array.
304
	 *
305
	 * @param array $config Config parameters
306
	 * @param array $attributes Attributes for the config array
307
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
308
	 * 	known by the provider but aren't valid resp. null for attributes whose values are OK
309
	 */
310
	protected function checkConfig( array $config, array $attributes )
311
	{
312
		$errors = [];
313
314
		foreach( $config as $key => $def )
315
		{
316
			if( $def['required'] === true && ( !isset( $attributes[$key] ) || $attributes[$key] === '' ) )
317
			{
318
				$errors[$key] = sprintf( 'Configuration for "%1$s" is missing', $key );
319
				continue;
320
			}
321
322
			if( isset( $attributes[$key] ) )
323
			{
324
				switch( $def['type'] )
325
				{
326
					case 'boolean':
327
						if( !is_string( $attributes[$key] ) || $attributes[$key] !== '0' && $attributes[$key] !== '1' ) {
328
							$errors[$key] = sprintf( 'Not a true/false value' ); continue 2;
329
						}
330
						break;
331
					case 'string':
332
						if( is_string( $attributes[$key] ) === false ) {
333
							$errors[$key] = sprintf( 'Not a string' ); continue 2;
334
						}
335
						break;
336
					case 'integer':
337
						if( ctype_digit( $attributes[$key] ) === false ) {
338
							$errors[$key] = sprintf( 'Not an integer number' ); continue 2;
339
						}
340
						break;
341
					case 'number':
342
						if( is_numeric( $attributes[$key] ) === false ) {
343
							$errors[$key] = sprintf( 'Not a number' ); continue 2;
344
						}
345
						break;
346
					case 'date':
347
						$pattern = '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/';
348
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
349
							$errors[$key] = sprintf( 'Not a date' ); continue 2;
350
						}
351
						break;
352
					case 'datetime':
353
						$pattern = '/^[0-9]{4}-[0-1][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9](:[0-5][0-9])?$/';
354
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
355
							$errors[$key] = sprintf( 'Not a date and time' ); continue 2;
356
						}
357
						break;
358
					case 'time':
359
						$pattern = '/^([0-2])?[0-9]:[0-5][0-9](:[0-5][0-9])?$/';
360
						if( !is_string( $attributes[$key] ) || preg_match( $pattern, $attributes[$key] ) !== 1 ) {
361
							$errors[$key] = sprintf( 'Not a time' ); continue 2;
362
						}
363
						break;
364
					case 'list':
365
					case 'select':
366
						if( !is_array( $def['default'] ) || !isset( $def['default'][$attributes[$key]] )
367
							&& !in_array( $attributes[$key], $def['default'] )
368
						) {
369
							$errors[$key] = sprintf( 'Not a listed value' ); continue 2;
370
						}
371
						break;
372
					case 'map':
373
						if( !is_array( $attributes[$key] ) ) {
374
							$errors[$key] = sprintf( 'Not a key/value map' ); continue 2;
375
						}
376
						break;
377
					default:
378
						throw new \Aimeos\MShop\Service\Exception( sprintf( 'Invalid type "%1$s"', $def['type'] ) );
379
				}
380
			}
381
382
			$errors[$key] = null;
383
		}
384
385
		return $errors;
386
	}
387
388
389
	/**
390
	 * Returns the criteria attribute items for the backend configuration
391
	 *
392
	 * @return \Aimeos\MW\Criteria\Attribute\Iface[] List of criteria attribute items
393
	 */
394
	protected function getConfigItems( array $configList )
395
	{
396
		$list = [];
397
398
		foreach( $configList as $key => $config ) {
399
			$list[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $config );
400
		}
401
402
		return $list;
403
	}
404
405
406
	/**
407
	 * Returns the configuration value that matches one of the given keys.
408
	 *
409
	 * The config of the service item and (optionally) the global config
410
	 * is tested in the order of the keys. The first one that matches will
411
	 * be returned.
412
	 *
413
	 * @param array|string $keys Key name or list of key names that should be tested for in the order to test
414
	 * @param mixed $default Returned value if the key wasn't was found
415
	 * @return mixed Value of the first key that matches or null if none was found
416
	 */
417
	protected function getConfigValue( $keys, $default = null )
418
	{
419
		$srvconfig = $this->getServiceItem()->getConfig();
420
421
		foreach( (array) $keys as $key )
422
		{
423
			if( isset( $srvconfig[$key] ) ) {
424
				return $srvconfig[$key];
425
			}
426
427
			if( isset( $this->beGlobalConfig[$key] ) ) {
428
				return $this->beGlobalConfig[$key];
429
			}
430
		}
431
432
		return $default;
433
	}
434
435
436
	/**
437
	 * Returns the context item.
438
	 *
439
	 * @return \Aimeos\MShop\Context\Item\Iface Context item
440
	 */
441
	protected function getContext()
442
	{
443
		return $this->context;
444
	}
445
446
447
	/**
448
	 * Returns the calculated amount of the price item
449
	 *
450
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price item
451
	 * @param boolean $costs Include costs per item
452
	 * @param boolean $tax Include tax
453
	 * @return string Formatted money amount
454
	 */
455
	protected function getAmount( \Aimeos\MShop\Price\Item\Iface $price, $costs = true, $tax = true )
456
	{
457
		$amount = $price->getValue();
458
459
		if( $costs === true ) {
460
			$amount += $price->getCosts();
461
		}
462
463
		if( $tax === true && $price->getTaxFlag() === false )
464
		{
465
			$tmp = clone $price;
466
467
			if( $costs === false )
468
			{
469
				$tmp->clear();
470
				$tmp->setValue( $price->getValue() );
471
				$tmp->setTaxRate( $price->getTaxRate() );
472
				$tmp->setQuantity( $price->getQuantity() );
473
			}
474
475
			$amount += $tmp->getTaxValue();
476
		}
477
478
		return number_format( $amount, 2, '.', '' );
479
	}
480
481
482
	/**
483
	 * Returns the order item for the given ID.
484
	 *
485
	 * @param string $id Unique order ID
486
	 * @return \Aimeos\MShop\Order\Item\Iface $item Order object
487
	 */
488
	protected function getOrder( $id )
489
	{
490
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'order' );
491
492
		$search = $manager->createSearch( true );
493
		$expr = [
494
			$search->getConditions(),
495
			$search->compare( '==', 'order.id', $id ),
496
			$search->compare( '==', 'order.base.service.code', $this->serviceItem->getCode() ),
497
		];
498
		$search->setConditions( $search->combine( '&&', $expr ) );
499
500
		$result = $manager->searchItems( $search );
501
502
		if( ( $item = reset( $result ) ) === false ) {
503
			throw new \Aimeos\MShop\Service\Exception( sprintf( 'No order for ID "%1$s" found', $id ) );
504
		}
505
506
		return $item;
507
	}
508
509
510
	/**
511
	 * Returns the base order which is equivalent to the basket.
512
	 *
513
	 * @param string $baseId Order base ID stored in the order item
514
	 * @param integer $parts Bitmap of the basket parts that should be loaded
515
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Basket, optional with addresses, products, services and coupons
516
	 */
517
	protected function getOrderBase( $baseId, $parts = \Aimeos\MShop\Order\Manager\Base\Base::PARTS_SERVICE )
518
	{
519
		return \Aimeos\MShop\Factory::createManager( $this->context, 'order/base' )->load( $baseId, $parts );
520
	}
521
522
523
	/**
524
	 * Saves the order item.
525
	 *
526
	 * @param \Aimeos\MShop\Order\Item\Iface $item Order object
527
	 * @return \Aimeos\MShop\Order\Item\Iface Order object including the generated ID
0 ignored issues
show
Documentation introduced by
Should the return type not be \Aimeos\MShop\Order\Item\Iface|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
528
	 */
529
	protected function saveOrder( \Aimeos\MShop\Order\Item\Iface $item )
530
	{
531
		return \Aimeos\MShop\Factory::createManager( $this->context, 'order' )->saveItem( $item );
532
	}
533
534
535
	/**
536
	 * Saves the base order which is equivalent to the basket and its dependent objects.
537
	 *
538
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $base Order base object with associated items
539
	 * @param integer $parts Bitmap of the basket parts that should be stored
540
	 */
541
	protected function saveOrderBase( \Aimeos\MShop\Order\Item\Base\Iface $base, $parts = \Aimeos\MShop\Order\Manager\Base\Base::PARTS_SERVICE )
542
	{
543
		\Aimeos\MShop\Factory::createManager( $this->context, 'order/base' )->store( $base, $parts );
544
	}
545
546
547
	/**
548
	 * Sets the attributes in the given service item.
549
	 *
550
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $orderServiceItem Order service item that will be added to the basket
551
	 * @param array $attributes Attribute key/value pairs entered by the customer during the checkout process
552
	 * @param string $type Type of the configuration values (delivery or payment)
553
	 */
554
	protected function setAttributes( \Aimeos\MShop\Order\Item\Base\Service\Iface $orderServiceItem, array $attributes, $type )
555
	{
556
		$manager = \Aimeos\MShop\Factory::createManager( $this->context, 'order/base/service/attribute' );
557
558
		foreach( $attributes as $key => $value )
559
		{
560
			$item = $manager->createItem();
561
			$item->setCode( $key );
562
			$item->setValue( $value );
563
			$item->setType( $type );
564
565
			$orderServiceItem->setAttributeItem( $item );
566
		}
567
	}
568
569
570
	/**
571
	 * Returns the public holidays in ISO format
572
	 *
573
	 * @param string $list Comma separated list of public holidays in YYYY-MM-DD format
574
	 * @return array List of dates in YYYY-MM-DD format
575
	 * @throws \Aimeos\MShop\Service\Exception If the given holiday string is in the wrong format and can't be processed
576
	 */
577
	private function getPublicHolidays( $list )
578
	{
579
		$holidays = [];
580
581
		if( is_string( $list ) && $list !== '' )
582
		{
583
			$holidays = explode( ',', str_replace( ' ', '', $list ) );
584
585
			if( sort( $holidays ) === false ) {
586
				throw new \Aimeos\MShop\Service\Exception( sprintf( 'Unable to sort public holidays: "%1$s"', $list ) );
587
			}
588
589
			$holidays = array_flip( $holidays );
590
		}
591
592
		return $holidays;
593
	}
594
}
595