• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

aimeos / aimeos-core / 4f8b7e8d-5595-43b2-ba5c-df9921830178

17 May 2026 07:32AM UTC coverage: 92.581%. Remained the same
4f8b7e8d-5595-43b2-ba5c-df9921830178

push

circleci

aimeos
Fixed PHPStan issues

896 of 980 new or added lines in 165 files covered. (91.43%)

35 existing lines in 29 files now uncovered.

9734 of 10514 relevant lines covered (92.58%)

80.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.06
/src/MShop/Service/Provider/Base.php
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-2026
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
        implements Iface, \Aimeos\Macro\Iface
23
{
24
        use \Aimeos\Macro\Macroable;
25

26
        private \Aimeos\MShop\ContextIface $context;
27
        private \Aimeos\MShop\Service\Item\Iface $serviceItem;
28
        private ?\Aimeos\MShop\Service\Provider\Iface $object = null;
29
        private array $beGlobalConfig;
30

31

32
        /**
33
         * Initializes the service provider object.
34
         *
35
         * @param \Aimeos\MShop\ContextIface $context Context object with required objects
36
         * @param \Aimeos\MShop\Service\Item\Iface $serviceItem Service item with configuration for the provider
37
         */
38
        public function __construct( \Aimeos\MShop\ContextIface $context, \Aimeos\MShop\Service\Item\Iface $serviceItem )
39
        {
40
                $this->context = $context;
264✔
41
                $this->serviceItem = $serviceItem;
264✔
42
        }
43

44

45
        /**
46
         * Returns the price when using the provider.
47
         * Usually, this is the lowest price that is available in the service item but can also be a calculated based on
48
         * the basket content, e.g. 2% of the value as transaction cost.
49
         *
50
         * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object
51
         * @param array $options Selected options by customer from frontend
52
         * @return \Aimeos\MShop\Price\Item\Iface Price item containing the price, shipping, rebate
53
         */
54
        public function calcPrice( \Aimeos\MShop\Order\Item\Iface $basket, array $options = [] ) : \Aimeos\MShop\Price\Item\Iface
55
        {
56
                $manager = \Aimeos\MShop::create( $this->context, 'price' );
9✔
57
                $prices = $this->serviceItem->getRefItems( 'price', 'default', 'default' );
9✔
58

59
                // @phpstan-ignore return.type
60
                return $prices->isEmpty() ? $manager->create() : $manager->getLowestPrice( $prices, 1 );
9✔
61
        }
62

63

64
        /**
65
         * Checks the backend configuration attributes for validity.
66
         *
67
         * @param array $attributes Attributes added by the shop owner in the administraton interface
68
         * @return array An array with the attribute keys as key and an error message as values for all attributes that are
69
         *         known by the provider but aren't valid resp. null for attributes whose values are OK
70
         */
71
        public function checkConfigBE( array $attributes ) : array
72
        {
73
                return [];
4✔
74
        }
75

76

77
        /**
78
         * Checks the frontend configuration attributes for validity.
79
         *
80
         * @param array $attributes Attributes entered by the customer during the checkout process
81
         * @return array An array with the attribute keys as key and an error message as values for all attributes that are
82
         *         known by the provider but aren't valid resp. null for attributes whose values are OK
83
         */
84
        public function checkConfigFE( array $attributes ) : array
85
        {
86
                return [];
×
87
        }
88

89

90
        /**
91
         * Returns the configuration attribute definitions of the provider to generate a list of available fields and
92
         * rules for the value of each field in the administration interface.
93
         *
94
         * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface
95
         */
96
        public function getConfigBE() : array
97
        {
98
                return [];
3✔
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 frontend.
105
         *
106
         * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object
107
         * @return array List of attribute definitions implementing \Aimeos\Base\Critera\Attribute\Iface
108
         */
109
        public function getConfigFE( \Aimeos\MShop\Order\Item\Iface $basket ) : array
110
        {
111
                return [];
1✔
112
        }
113

114

115
        /**
116
         * Returns the service item which also includes the configuration for the service provider.
117
         *
118
         * @return \Aimeos\MShop\Service\Item\Iface Service item
119
         */
120
        public function getServiceItem() : \Aimeos\MShop\Service\Item\Iface
121
        {
122
                return $this->serviceItem;
122✔
123
        }
124

125

126
        /**
127
         * Injects additional global configuration for the backend.
128
         *
129
         * It's used for adding additional backend configuration from the application
130
         * like the URLs to redirect to.
131
         *
132
         * Supported redirect URLs are:
133
         * - payment.url-success
134
         * - payment.url-failure
135
         * - payment.url-cancel
136
         * - payment.url-update
137
         *
138
         * @param array $config Associative list of config keys and their value
139
         * @return \Aimeos\MShop\Service\Provider\Iface Provider object for chaining method calls
140
         */
141
        public function injectGlobalConfigBE( array $config ) : \Aimeos\MShop\Service\Provider\Iface
142
        {
143
                $this->beGlobalConfig = $config;
3✔
144
                return $this;
3✔
145
        }
146

147

148
        /**
149
         * Checks if payment provider can be used based on the basket content.
150
         * Checks for country, currency, address, RMS, etc. -> in separate decorators
151
         *
152
         * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object
153
         * @return bool True if payment provider can be used, false if not
154
         */
155
        public function isAvailable( \Aimeos\MShop\Order\Item\Iface $basket ) : bool
156
        {
157
                return true;
9✔
158
        }
159

160

161
        /**
162
         * Checks what features the payment provider implements.
163
         *
164
         * @param int $what Constant from abstract class
165
         * @return bool True if feature is available in the payment provider, false if not
166
         */
167
        public function isImplemented( int $what ) : bool
168
        {
169
                return false;
3✔
170
        }
171

172

173
        /**
174
         * Queries for status updates for the given order if supported.
175
         *
176
         * @param \Aimeos\MShop\Order\Item\Iface $order Order invoice object
177
         * @return \Aimeos\MShop\Order\Item\Iface Updated order item object
178
         */
179
        public function query( \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface
180
        {
181
                $msg = $this->context->translate( 'mshop', 'Method "%1$s" for provider not available' );
1✔
182
                throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, 'query' ) );
1✔
183
        }
184

185

186
        /**
187
         * Injects the outer object into the decorator stack
188
         *
189
         * @param \Aimeos\MShop\Service\Provider\Iface $object First object of the decorator stack
190
         * @return \Aimeos\MShop\Service\Provider\Iface Service object for chaining method calls
191
         */
192
        public function setObject( \Aimeos\MShop\Service\Provider\Iface $object ) : \Aimeos\MShop\Service\Provider\Iface
193
        {
194
                $this->object = $object;
6✔
195
                return $this;
6✔
196
        }
197

198

199
        /**
200
         * Looks for new update files and updates the orders for which status updates were received.
201
         * If batch processing of files isn't supported, this method can be empty.
202
         *
203
         * @return bool True if the update was successful, false if async updates are not supported
204
         * @throws \Aimeos\MShop\Service\Exception If updating one of the orders failed
205
         */
206
        public function updateAsync() : bool
207
        {
208
                return false;
1✔
209
        }
210

211

212
        /**
213
         * Updates the order status sent by payment gateway notifications
214
         *
215
         * @param \Psr\Http\Message\ServerRequestInterface $request Request object
216
         * @param \Psr\Http\Message\ResponseInterface $response Response object
217
         * @return \Psr\Http\Message\ResponseInterface Response object
218
         */
219
        public function updatePush( \Psr\Http\Message\ServerRequestInterface $request,
220
                \Psr\Http\Message\ResponseInterface $response ) : \Psr\Http\Message\ResponseInterface
221
        {
222
                return $response->withStatus( 501, 'Not implemented' );
1✔
223
        }
224

225

226
        /**
227
         * Updates the orders for whose status updates have been received by the confirmation page
228
         *
229
         * @param \Psr\Http\Message\ServerRequestInterface $request Request object with parameters and request body
230
         * @param \Aimeos\MShop\Order\Item\Iface $order Order item that should be updated
231
         * @return \Aimeos\MShop\Order\Item\Iface Updated order item
232
         * @throws \Aimeos\MShop\Service\Exception If updating the orders failed
233
         */
234
        public function updateSync( \Psr\Http\Message\ServerRequestInterface $request,
235
                \Aimeos\MShop\Order\Item\Iface $order ) : \Aimeos\MShop\Order\Item\Iface
236
        {
237
                return $order;
1✔
238
        }
239

240

241
        /**
242
         * Creates service attributes from the passed data
243
         *
244
         * @param array $map Attribute key/value pairs entered by the customer during the checkout process
245
         * @param string $type Type of the configuration values (delivery or payment)
246
         * @return array List of \Aimeos\MShop\Order\Item\Service\Attribute\Iface objects
247
         */
248
        protected function attributes( array $map, string $type = '' ) : array
249
        {
250
                $list = [];
9✔
251
                $manager = \Aimeos\MShop::create( $this->context, 'order/service' );
9✔
252

253
                foreach( $map as $key => $value ) {
9✔
254
                        $list[] = $manager->createAttributeItem()->setType( $type )->setCode( $key )->setValue( $value );
8✔
255
                }
256

257
                return $list;
9✔
258
        }
259

260

261
        /**
262
         * Checks required fields and the types of the given data map
263
         *
264
         * @param array $criteria Multi-dimensional associative list of criteria configuration
265
         * @param array $map Values to check agains the criteria
266
         * @return array An array with the attribute keys as key and an error message as values for all attributes that are
267
         *         known by the provider but aren't valid resp. null for attributes whose values are OK
268
         */
269
        protected function checkConfig( array $criteria, array $map ) : array
270
        {
271
                $helper = new \Aimeos\MShop\Common\Helper\Config\Standard( $this->getConfigItems( $criteria ) );
62✔
272
                return $helper->check( $map );
62✔
273
        }
274

275

276
        /**
277
         * Returns the context item.
278
         *
279
         * @return \Aimeos\MShop\ContextIface Context item
280
         */
281
        protected function context() : \Aimeos\MShop\ContextIface
282
        {
283
                return $this->context;
25✔
284
        }
285

286

287
        /**
288
         * Returns the service related data from the customer account if available
289
         *
290
         * @param string $customerId Unique customer ID the service token belongs to
291
         * @param string $key Key of the value that should be returned
292
         * @return array|string|null Service data or null if none is available
293
         */
294
        protected function data( string $customerId, string $key )
295
        {
296
                if( $customerId )
1✔
297
                {
298
                        $item = \Aimeos\MShop::create( $this->context, 'customer' )->get( $customerId, ['service'] );
1✔
299

300
                        if( $listItem = $item->getListItem( 'service', 'default', $this->getServiceItem()->getId() ) ) {
1✔
301
                                // @phpstan-ignore return.type
UNCOV
302
                                return $listItem->getConfigValue( $key );
×
303
                        }
304
                }
305

306
                return null;
1✔
307
        }
308

309

310
        /**
311
         * Returns the calculated amount of the price item
312
         *
313
         * @param \Aimeos\MShop\Price\Item\Iface $price Price item
314
         * @param bool $costs Include costs per item
315
         * @param bool $tax Include tax
316
         * @param int $precision Number for decimal digits
317
         * @return string Formatted money amount
318
         */
319
        protected function getAmount( \Aimeos\MShop\Price\Item\Iface $price, bool $costs = true, bool $tax = true,
320
                ?int $precision = null ) : string
321
        {
322
                $amount = $price->getValue();
5✔
323

324
                if( $costs === true ) {
5✔
325
                        $amount += $price->getCosts(); // @phpstan-ignore assignOp.invalid
5✔
326
                }
327

328
                if( $tax === true && $price->getTaxFlag() === false )
5✔
329
                {
330
                        $tmp = clone $price;
×
331

332
                        if( $costs === false )
×
333
                        {
334
                                $tmp->clear();
×
335
                                $tmp->setValue( $price->getValue() );
×
336
                                $tmp->setTaxRate( $price->getTaxRate() );
×
337
                                $tmp->setQuantity( $price->getQuantity() );
×
338
                        }
339

NEW
340
                        $amount += $tmp->getTaxValue(); // @phpstan-ignore assignOp.invalid
×
341
                }
342

343
                return number_format( (float) $amount, $precision !== null ? $precision : $price->getPrecision(), '.', '' );
5✔
344
        }
345

346

347
        /**
348
         * Returns the order service matching the given code from the basket
349
         *
350
         * @param \Aimeos\MShop\Order\Item\Iface $basket Basket object
351
         * @param string $type Service type constant from \Aimeos\MShop\Order\Item\Service\Base
352
         * @param string $code Code of the service item that should be returned
353
         * @return \Aimeos\MShop\Order\Item\Service\Iface Order service item
354
         * @throws \Aimeos\MShop\Order\Exception If no service for the given type and code is found
355
         */
356
        protected function getBasketService( \Aimeos\MShop\Order\Item\Iface $basket, string $type,
357
                string $code ) : \Aimeos\MShop\Order\Item\Service\Iface
358
        {
359
                $msg = $this->context->translate( 'mshop', 'Service not available' );
13✔
360

361
                // @phpstan-ignore return.type
362
                return map( $basket->getService( $type ) )->find( function( $service ) use ( $code ) {
13✔
363
                                return $service->getCode() === $code;
13✔
364
                }, new \Aimeos\MShop\Service\Exception( $msg ) );
13✔
365
        }
366

367

368
        /**
369
         * Returns the criteria attribute items for the backend configuration
370
         *
371
         * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of criteria attribute items
372
         */
373
        protected function getConfigItems( array $configList ) : array
374
        {
375
                $list = [];
86✔
376

377
                foreach( $configList as $key => $config )
86✔
378
                {
379
                        $config['code'] = $config['code'] ?? $key;
86✔
380
                        $list[$key] = new \Aimeos\Base\Criteria\Attribute\Standard( (array) $config );
86✔
381
                }
382

383
                return $list;
86✔
384
        }
385

386

387
        /**
388
         * Returns the configuration value that matches one of the given keys.
389
         *
390
         * The config of the service item and (optionally) the global config
391
         * is tested in the order of the keys. The first one that matches will
392
         * be returned.
393
         *
394
         * @param array|string $keys Key name or list of key names that should be tested for in the order to test
395
         * @param mixed $default Returned value if the key wasn't was found
396
         * @return mixed Value of the first key that matches or null if none was found
397
         */
398
        protected function getConfigValue( $keys, $default = null )
399
        {
400
                foreach( (array) $keys as $key )
111✔
401
                {
402
                        if( ( $value = $this->getServiceItem()->getConfigValue( (string) $key ) ) !== null ) {
111✔
403
                                return $value;
97✔
404
                        }
405

406
                        if( isset( $this->beGlobalConfig[$key] ) ) {
78✔
407
                                return $this->beGlobalConfig[$key];
2✔
408
                        }
409
                }
410

411
                return $default;
76✔
412
        }
413

414

415
        /**
416
         * Logs the given message with the passed log level
417
         *
418
         * @param mixed $msg Message or object
419
         * @param int $level Log level (default: ERR)
420
         * @return self Same object for fluid method calls
421
         */
422
        protected function log( $msg, int $level = \Aimeos\Base\Logger\Iface::ERR ) : self
423
        {
424
                $facility = basename( str_replace( '\\', '/', get_class( $this ) ) );
1✔
425
                $trace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
1✔
426
                $trace = array_pop( $trace ) ?: [];
1✔
427
                $name = ( $trace['class'] ?? '' ) . '::' . ( $trace['function'] ?? '' );
1✔
428

429
                if( !is_scalar( $msg ) ) {
1✔
430
                        $msg = print_r( $msg, true );
1✔
431
                }
432

433
                $this->context->logger()->log( $name . ': ' . $msg, $level, 'core/service/' . $facility );
1✔
434
                return $this;
1✔
435
        }
436

437

438
        /**
439
         * Returns the first object of the decorator stack
440
         *
441
         * @return \Aimeos\MShop\Service\Provider\Iface First object of the decorator stack
442
         */
443
        protected function object() : \Aimeos\MShop\Service\Provider\Iface
444
        {
445
                return $this->object ?? $this;
×
446
        }
447

448

449
        /**
450
         * Returns the configuration value that matches given key
451
         *
452
         * @param string $key Key name
453
         * @return mixed Value for the key
454
         * @throws \Aimeos\MShop\Service\Exception If configuration key isn't found
455
         */
456
        protected function require( string $key )
457
        {
458
                if( ( $value = $this->getConfigValue( $key ) ) === null )
1✔
459
                {
460
                        $msg = $this->context()->translate( 'mshop', 'Missing configuration "%1$s"' );
×
461
                        throw new \Aimeos\MShop\Service\Exception( sprintf( $msg, $key ) );
×
462
                }
463

464
                return $value;
1✔
465
        }
466

467

468
        /**
469
         * Saves the order item.
470
         *
471
         * @param \Aimeos\MShop\Order\Item\Iface $item Order object
472
         * @return \Aimeos\MShop\Order\Item\Iface Order object including the generated ID
473
         */
474
        protected function save( \Aimeos\MShop\Order\Item\Iface $item ) : \Aimeos\MShop\Order\Item\Iface
475
        {
NEW
476
                return \Aimeos\MShop::create( $this->context, 'order' )->save( $item ); // @phpstan-ignore return.type
×
477
        }
478

479

480
        /**
481
         * Adds the service data to the customer account if available
482
         *
483
         * @param string $customerId Unique customer ID the service token belongs to
484
         * @param string $key Key of the value that should be added
485
         * @param string|array $data Service data to store
486
         * @type \Aimeos\MShop\Service\Provider\Iface Provider object for chaining method calls
487
         */
488
        protected function setData( string $customerId, string $key, $data ) : \Aimeos\MShop\Service\Provider\Iface
489
        {
490
                if( $customerId )
1✔
491
                {
492
                        $manager = \Aimeos\MShop::create( $this->context, 'customer' );
1✔
493
                        $item = $manager->get( $customerId, ['service'] );
1✔
494
                        $serviceId = $this->getServiceItem()->getId();
1✔
495

496
                        if( ( $listItem = $item->getListItem( 'service', 'default', $serviceId, false ) ) === null )
1✔
497
                        {
498
                                $listManager = \Aimeos\MShop::create( $this->context, 'customer/lists' );
1✔
499
                                $listItem = $listManager->create()->setRefId( $serviceId );
1✔
500
                        }
501

502
                        // @phpstan-ignore argument.type
503
                        $manager->save( $item->addListItem( 'service', $listItem->setConfigValue( $key, $data ) ) );
1✔
504
                }
505

506
                return $this;
1✔
507
        }
508

509

510
        /**
511
         * Throws an exception with the given message
512
         *
513
         * @param string $msg Message
514
         * @param string|null $domain Translation domain
515
         * @param int $code Custom error code
516
         * @return never
517
         */
518
        protected function throw( string $msg, ?string $domain = null, int $code = 0 ) : never
519
        {
520
                if( $domain ) {
1✔
521
                        $msg = $this->context->translate( $domain, $msg );
1✔
522
                }
523

524
                throw new \Aimeos\MShop\Service\Exception( $msg, $code );
1✔
525
        }
526
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc