Passed
Push — master ( 36a836...c7a44e )
by Aimeos
16:28 queued 07:56
created

JsonApi   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 299
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 51
dl 0
loc 299
rs 10
c 0
b 0
f 0
wmc 18

5 Methods

Rating   Name   Duplication   Size   Complexity  
A addComponentDecorators() 0 50 3
A inject() 0 3 1
A create() 0 19 4
A createComponent() 0 20 4
A addDecorators() 0 28 6
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2022
6
 * @package Client
7
 * @subpackage JsonApi
8
 */
9
10
11
namespace Aimeos\Client;
12
13
14
/**
15
 * Factory which can create all JSON API clients
16
 *
17
 * @package Client
18
 * @subpackage JsonApi
19
 */
20
class JsonApi
21
{
22
	/** client/jsonapi/name
23
	 * Class name of the used JSON API client implementation
24
	 *
25
	 * Each default JSON API client can be replace by an alternative imlementation.
26
	 * To use this implementation, you have to set the last part of the class
27
	 * name as configuration value so the client factory knows which class it
28
	 * has to instantiate.
29
	 *
30
	 * For example, if the name of the default class is
31
	 *
32
	 *  \Aimeos\Client\JsonApi\Standard
33
	 *
34
	 * and you want to replace it with your own version named
35
	 *
36
	 *  \Aimeos\Client\JsonApi\Mycntl
37
	 *
38
	 * then you have to set the this configuration option:
39
	 *
40
	 *  client/jsonapi/name = Mycntl
41
	 *
42
	 * The value is the last part of your own class name and it's case sensitive,
43
	 * so take care that the configuration value is exactly named like the last
44
	 * part of the class name.
45
	 *
46
	 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
47
	 * characters are possible! You should always start the last part of the class
48
	 * name with an upper case character and continue only with lower case characters
49
	 * or numbers. Avoid chamel case names like "MyCntl"!
50
	 *
51
	 * @param string Last part of the class name
52
	 * @since 2015.12
53
	 * @category Developer
54
	 */
55
56
	/** client/jsonapi/decorators/excludes
57
	 * Excludes decorators added by the "common" option from the JSON API clients
58
	 *
59
	 * Decorators extend the functionality of a class by adding new aspects
60
	 * (e.g. log what is currently done), executing the methods of the underlying
61
	 * class only in certain conditions (e.g. only for logged in users) or
62
	 * modify what is returned to the caller.
63
	 *
64
	 * This option allows you to remove a decorator added via
65
	 * "client/jsonapi/common/decorators/default" before they are wrapped
66
	 * around the Jsonadm client.
67
	 *
68
	 *  client/jsonapi/decorators/excludes = array( 'decorator1' )
69
	 *
70
	 * This would remove the decorator named "decorator1" from the list of
71
	 * common decorators ("\Aimeos\Client\JsonApi\Common\Decorator\*") added via
72
	 * "client/jsonapi/common/decorators/default" for the JSON API client.
73
	 *
74
	 * @param array List of decorator names
75
	 * @since 2016.01
76
	 * @category Developer
77
	 * @see client/jsonapi/common/decorators/default
78
	 * @see client/jsonapi/decorators/global
79
	 * @see client/jsonapi/decorators/local
80
	 */
81
82
	/** client/jsonapi/decorators/global
83
	 * Adds a list of globally available decorators only to the Jsonadm client
84
	 *
85
	 * Decorators extend the functionality of a class by adding new aspects
86
	 * (e.g. log what is currently done), executing the methods of the underlying
87
	 * class only in certain conditions (e.g. only for logged in users) or
88
	 * modify what is returned to the caller.
89
	 *
90
	 * This option allows you to wrap global decorators
91
	 * ("\Aimeos\Client\Jsonadm\Common\Decorator\*") around the Jsonadm
92
	 * client.
93
	 *
94
	 *  client/jsonapi/product/decorators/global = array( 'decorator1' )
95
	 *
96
	 * This would add the decorator named "decorator1" defined by
97
	 * "\Aimeos\Client\Jsonadm\Common\Decorator\Decorator1" only to the
98
	 * "product" Jsonadm client.
99
	 *
100
	 * @param array List of decorator names
101
	 * @since 2016.01
102
	 * @category Developer
103
	 * @see client/jsonapi/common/decorators/default
104
	 * @see client/jsonapi/decorators/excludes
105
	 * @see client/jsonapi/decorators/local
106
	 */
107
108
	/** client/jsonapi/decorators/local
109
	 * Adds a list of local decorators only to the Jsonadm client
110
	 *
111
	 * Decorators extend the functionality of a class by adding new aspects
112
	 * (e.g. log what is currently done), executing the methods of the underlying
113
	 * class only in certain conditions (e.g. only for logged in users) or
114
	 * modify what is returned to the caller.
115
	 *
116
	 * This option allows you to wrap local decorators
117
	 * ("\Aimeos\Client\Jsonadm\Product\Decorator\*") around the Jsonadm
118
	 * client.
119
	 *
120
	 *  client/jsonapi/product/decorators/local = array( 'decorator2' )
121
	 *
122
	 * This would add the decorator named "decorator2" defined by
123
	 * "\Aimeos\Client\Jsonadm\Product\Decorator\Decorator2" only to the
124
	 * "product" Jsonadm client.
125
	 *
126
	 * @param array List of decorator names
127
	 * @since 2016.01
128
	 * @category Developer
129
	 * @see client/jsonapi/common/decorators/default
130
	 * @see client/jsonapi/decorators/excludes
131
	 * @see client/jsonapi/decorators/global
132
	 */
133
134
135
	private static $objects = [];
136
137
138
	/**
139
	 * Creates the required client specified by the given path of client names
140
	 *
141
	 * Clients are created by providing only the domain name, e.g. "product"
142
	 *  for the \Aimeos\Client\JsonApi\Product\Standard or a path of names to
143
	 * retrieve a specific sub-client, e.g. "product/type" for the
144
	 * \Aimeos\Client\JsonApi\Product\Type\Standard client.
145
	 *
146
	 * @param \Aimeos\MShop\ContextIface $context Context object required by clients
147
	 * @param string $path Name of the client separated by slashes, e.g "order/base"
148
	 * @param string|null $name Name of the client implementation ("Standard" if null)
149
	 * @return \Aimeos\Client\JsonApi\Iface JSON client instance
150
	 * @throws \Aimeos\Client\JsonApi\Exception If the given path is invalid
151
	 */
152
	public static function create( \Aimeos\MShop\ContextIface $context, string $path, string $name = null ) : \Aimeos\Client\JsonApi\Iface
153
	{
154
		empty( $path = trim( $path, '/' ) ) ?: $path .= '/';
155
156
		if( $name === null ) {
157
			$name = $context->config()->get( 'client/jsonapi/' . $path . 'name', 'Standard' );
158
		}
159
160
		$interface = 'Aimeos\\Client\\JsonApi\\Iface';
161
		$classname = 'Aimeos\\Client\\JsonApi\\' . str_replace( '/', '\\', ucwords( $path, '/' ) ) . $name;
162
163
		if( class_exists( $classname ) === false ) {
164
			throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Class "%1$s" not found', $classname, 404 ) );
165
		}
166
167
		$client = self::createComponent( $context, $classname, $interface );
168
		$client = self::addComponentDecorators( $context, $client, $path );
169
170
		return $client->setView( $context->view() );
171
	}
172
173
174
	/**
175
	 * Injects a client object
176
	 *
177
	 * The object is returned via create() if an instance of the class
178
	 * with the name name is requested.
179
	 *
180
	 * @param string $classname Full name of the class for which the object should be returned
181
	 * @param \Aimeos\Client\JsonApi\Iface|null $client JSON API client object
182
	 */
183
	public static function inject( string $classname, \Aimeos\Client\JsonApi\Iface $client = null )
184
	{
185
		self::$objects['\\' . ltrim( $classname, '\\' )] = $client;
186
	}
187
188
189
	/**
190
	 * Adds the decorators to the JSON API client object
191
	 *
192
	 * @param \Aimeos\MShop\ContextIface $context Context instance with necessary objects
193
	 * @param \Aimeos\Client\JsonApi\Common\Iface $client Client object
0 ignored issues
show
Bug introduced by
The type Aimeos\Client\JsonApi\Common\Iface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
194
	 * @param string $path Name of the client, e.g "product"
195
	 * @return \Aimeos\Client\JsonApi\Iface Client object
196
	 */
197
	protected static function addComponentDecorators( \Aimeos\MShop\ContextIface $context,
198
		\Aimeos\Client\JsonApi\Iface $client, string $path ) : \Aimeos\Client\JsonApi\Iface
199
	{
200
		$localClass = str_replace( '/', '\\', ucwords( $path, '/' ) );
0 ignored issues
show
Unused Code introduced by
The assignment to $localClass is dead and can be removed.
Loading history...
201
		$config = $context->config();
202
203
		/** client/jsonapi/common/decorators/default
204
		 * Configures the list of decorators applied to all JSON API clients
205
		 *
206
		 * Decorators extend the functionality of a class by adding new aspects
207
		 * (e.g. log what is currently done), executing the methods of the underlying
208
		 * class only in certain conditions (e.g. only for logged in users) or
209
		 * modify what is returned to the caller.
210
		 *
211
		 * This option allows you to configure a list of decorator names that should
212
		 * be wrapped around the original instance of all created clients:
213
		 *
214
		 *  client/jsonapi/common/decorators/default = array( 'decorator1', 'decorator2' )
215
		 *
216
		 * This would wrap the decorators named "decorator1" and "decorator2" around
217
		 * all client instances in that order. The decorator classes would be
218
		 * "\Aimeos\Client\JsonApi\Common\Decorator\Decorator1" and
219
		 * "\Aimeos\Client\JsonApi\Common\Decorator\Decorator2".
220
		 *
221
		 * @param array List of decorator names
222
		 * @since 2015.12
223
		 * @category Developer
224
		 */
225
		$decorators = $config->get( 'client/jsonapi/common/decorators/default', [] );
226
		$excludes = $config->get( 'client/jsonapi/' . $path . 'decorators/excludes', [] );
227
228
		foreach( $decorators as $key => $name )
229
		{
230
			if( in_array( $name, $excludes ) ) {
231
				unset( $decorators[$key] );
232
			}
233
		}
234
235
		$classprefix = '\\Aimeos\\Client\\JsonApi\\Common\\Decorator\\';
236
		return self::addDecorators( $context, $client, $path, $decorators, $classprefix );
237
238
		$classprefix = '\\Aimeos\\Client\\JsonApi\\Common\\Decorator\\';
0 ignored issues
show
Unused Code introduced by
$classprefix = '\Aimeos\...nApi\Common\Decorator\' is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
239
		$decorators = $config->get( 'client/jsonapi/' . $path . 'decorators/global', [] );
240
		$client = self::addDecorators( $context, $client, $path, $decorators, $classprefix );
241
242
		$classprefix = '\\Aimeos\\Client\\JsonApi\\' . $localClass . 'Decorator\\';
243
		$decorators = $config->get( 'client/jsonapi/' . $path . 'decorators/local', [] );
244
		$client = self::addDecorators( $context, $client, $path, $decorators, $classprefix );
245
246
		return $client;
247
	}
248
249
250
	/**
251
	 * Adds the decorators to the client object
252
	 *
253
	 * @param \Aimeos\MShop\ContextIface $context Context instance with necessary objects
254
	 * @param \Aimeos\Client\JsonApi\Iface $client Client object
255
	 * @param string $path Name of the component, e.g "product"
256
	 * @param array $decorators List of decorator names
257
	 * @param string $classprefix Decorator class prefix, e.g. "\Aimeos\Client\JsonApi\Product\Decorator\"
258
	 * @return \Aimeos\Client\JsonApi\Iface Client object
259
	 */
260
	protected static function addDecorators( \Aimeos\MShop\ContextIface $context, \Aimeos\Client\JsonApi\Iface $client,
261
		string $path, array $decorators, string $classprefix ) : \Aimeos\Client\JsonApi\Iface
262
	{
263
		foreach( $decorators as $name )
264
		{
265
			if( ctype_alnum( $name ) === false )
266
			{
267
				$classname = is_string( $name ) ? $classprefix . $name : '<not a string>';
268
				throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Invalid class name "%1$s"', $classname ), 400 );
269
			}
270
271
			$classname = $classprefix . $name;
272
273
			if( class_exists( $classname ) === false ) {
274
				throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Class "%1$s" not found', $classname ), 404 );
275
			}
276
277
			$interface = '\\Aimeos\\Client\\JsonApi\\Common\\Decorator\\Iface';
278
			$client = new $classname( $client, $context, $path );
279
280
			if( !( $client instanceof $interface ) )
281
			{
282
				$msg = sprintf( 'Class "%1$s" does not implement "%2$s"', $classname, $interface );
283
				throw new \Aimeos\Client\JsonApi\Exception( $msg, 400 );
284
			}
285
		}
286
287
		return $client;
288
	}
289
290
291
	/**
292
	 * Creates a new client object
293
	 *
294
	 * @param \Aimeos\MShop\ContextIface $context Context object
295
	 * @param string $classname Name of the client class
296
	 * @param string $interface Name of the client interface
297
	 * @return \Aimeos\Client\JsonApi\Iface Client object
298
	 */
299
	protected static function createComponent( \Aimeos\MShop\ContextIface $context,
300
		string $classname, string $interface ) : \Aimeos\Client\JsonApi\Iface
301
	{
302
		if( isset( self::$objects[$classname] ) ) {
303
			return self::$objects[$classname];
304
		}
305
306
		if( class_exists( $classname ) === false ) {
307
			throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Class "%1$s" not found', $classname ), 404 );
308
		}
309
310
		$client = new $classname( $context );
311
312
		if( !( $client instanceof $interface ) )
313
		{
314
			$msg = sprintf( 'Class "%1$s" does not implement "%2$s"', $classname, $interface );
315
			throw new \Aimeos\Client\JsonApi\Exception( $msg, 400 );
316
		}
317
318
		return $client;
319
	}
320
}
321