Completed
Push — master ( 649aac...9efee0 )
by Aimeos
01:52
created

JsonApi::createNew()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.0808
c 0
b 0
f 0
cc 5
nc 7
nop 3
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2018
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 extends \Aimeos\Client\JsonApi\Common\Factory\Base
21
{
22
	static private $cache = true;
23
	static private $clients = [];
24
25
26
	/**
27
	 * Enables or disables caching of class instances
28
	 *
29
	 * @param boolean $value True to enable caching, false to disable it.
30
	 * @return boolean Previous cache setting
31
	 */
32
	static public function cache( $value )
33
	{
34
		$old = self::$cache;
35
		self::$cache = (boolean) $value;
36
37
		return $old;
38
	}
39
40
41
	/**
42
	 * Removes the client objects from the cache
43
	 *
44
	 * If neither a context ID nor a path is given, the complete cache will be pruned.
45
	 *
46
	 * @param integer $id Context ID the objects have been created with (string of \Aimeos\MShop\Context\Item\Iface)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
47
	 * @param string $path Path describing the client to clear, e.g. "product/lists/type"
0 ignored issues
show
Documentation introduced by
Should the type for parameter $path not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
48
	 */
49
	static public function clear( $id = null, $path = null )
50
	{
51
		if( $id !== null )
52
		{
53
			if( $path !== null ) {
54
				self::$clients[$id][$path] = null;
55
			} else {
56
				self::$clients[$id] = [];
57
			}
58
59
			return;
60
		}
61
62
		self::$clients = [];
63
	}
64
65
66
	/**
67
	 * Creates the required client specified by the given path of client names
68
	 *
69
	 * Clients are created by providing only the domain name, e.g. "product"
70
	 *  for the \Aimeos\Client\JsonApi\Product\Standard or a path of names to
71
	 * retrieve a specific sub-client, e.g. "product/type" for the
72
	 * \Aimeos\Client\JsonApi\Product\Type\Standard client.
73
	 *
74
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object required by clients
75
	 * @param string $path Name of the client separated by slashes, e.g "order/base"
76
	 * @param string|null $name Name of the client implementation ("Standard" if null)
77
	 * @return \Aimeos\Client\JsonApi\Iface JSON client instance
78
	 * @throws \Aimeos\Client\JsonApi\Exception If the given path is invalid
79
	 */
80
	static public function create( \Aimeos\MShop\Context\Item\Iface $context, $path, $name = null )
81
	{
82
		$path = strtolower( trim( $path, "/ \n\t\r\0\x0B" ) );
83
84
		if( empty( $path ) ) {
85
			return self::createRoot( $context, $path, $name );
86
		}
87
88
		$id = (string) $context;
89
90
		if( self::$cache === false || !isset( self::$clients[$id][$path] ) ) {
91
			self::$clients[$id][$path] = self::createNew( $context, $path, $name );
92
		}
93
94
		return self::$clients[$id][$path];
95
	}
96
97
98
	/**
99
	 * Creates a new client specified by the given path of client names.
100
	 *
101
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object required by clients
102
	 * @param string $path Name of the client separated by slashes, e.g "product/stock"
103
	 * @param string|null $name Name of the client implementation ("Standard" if null)
104
	 * @return \Aimeos\Client\JsonApi\Iface JSON client instance
105
	 * @throws \Aimeos\Client\JsonApi\Exception If the given path is invalid
106
	 */
107
	protected static function createNew( \Aimeos\MShop\Context\Item\Iface $context, $path, $name )
108
	{
109
		$parts = explode( '/', $path );
110
111
		foreach( $parts as $key => $part )
112
		{
113
			if( ctype_alnum( $part ) === false )
114
			{
115
				$msg = sprintf( 'Invalid client "%1$s" in "%2$s"', $part, $path );
116
				throw new \Aimeos\Client\JsonApi\Exception( $msg, 400 );
117
			}
118
119
			$parts[$key] = ucwords( $part );
120
		}
121
122
123
		$factory = '\\Aimeos\\Client\\JsonApi\\' . join( '\\', $parts ) . '\\Factory';
124
125
		if( class_exists( $factory ) === true )
126
		{
127
			$args = array( $context, $path, $name );
128
129
			if( ( $client = @call_user_func_array( array( $factory, 'createClient' ), $args ) ) === false ) {
130
				throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Invalid factory "%1$s"', $factory ), 400 );
131
			}
132
		}
133
		else
134
		{
135
			$client = self::createRoot( $context, $path, $name );
136
		}
137
138
		return $client;
139
	}
140
141
142
	/**
143
	 * Creates the top level client
144
	 *
145
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object required by clients
146
	 * @param string $path Name of the client separated by slashes, e.g "order/base"
147
	 * @param string|null $name Name of the JsonApi client (default: "Standard")
148
	 * @return \Aimeos\Client\JsonApi\Iface JSON client instance
149
	 * @throws \Aimeos\Client\JsonApi\Exception If the client couldn't be created
150
	 */
151
	protected static function createRoot( \Aimeos\MShop\Context\Item\Iface $context, $path, $name = null )
152
	{
153
		/** client/jsonapi/name
154
		 * Class name of the used JSON API client implementation
155
		 *
156
		 * Each default JSON API client can be replace by an alternative imlementation.
157
		 * To use this implementation, you have to set the last part of the class
158
		 * name as configuration value so the client factory knows which class it
159
		 * has to instantiate.
160
		 *
161
		 * For example, if the name of the default class is
162
		 *
163
		 *  \Aimeos\Client\JsonApi\Standard
164
		 *
165
		 * and you want to replace it with your own version named
166
		 *
167
		 *  \Aimeos\Client\JsonApi\Mycntl
168
		 *
169
		 * then you have to set the this configuration option:
170
		 *
171
		 *  client/jsonapi/name = Mycntl
172
		 *
173
		 * The value is the last part of your own class name and it's case sensitive,
174
		 * so take care that the configuration value is exactly named like the last
175
		 * part of the class name.
176
		 *
177
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
178
		 * characters are possible! You should always start the last part of the class
179
		 * name with an upper case character and continue only with lower case characters
180
		 * or numbers. Avoid chamel case names like "MyCntl"!
181
		 *
182
		 * @param string Last part of the class name
183
		 * @since 2015.12
184
		 * @category Developer
185
		 */
186
		if( $name === null ) {
187
			$name = $context->getConfig()->get( 'client/jsonapi/name', 'Standard' );
188
		}
189
190
		if( ctype_alnum( $name ) === false )
191
		{
192
			$classname = is_string( $name ) ? '\\Aimeos\\Client\\JsonApi\\' . $name : '<not a string>';
193
			throw new \Aimeos\Client\JsonApi\Exception( sprintf( 'Invalid class name "%1$s"', $classname ) );
194
		}
195
196
		$iface = '\\Aimeos\\Client\\JsonApi\\Iface';
197
		$classname = '\\Aimeos\\Client\\JsonApi\\' . $name;
198
199
		$client = self::createClientBase( $classname, $iface, $context, $path );
200
201
		/** client/jsonapi/decorators/excludes
202
		 * Excludes decorators added by the "common" option from the JSON API clients
203
		 *
204
		 * Decorators extend the functionality of a class by adding new aspects
205
		 * (e.g. log what is currently done), executing the methods of the underlying
206
		 * class only in certain conditions (e.g. only for logged in users) or
207
		 * modify what is returned to the caller.
208
		 *
209
		 * This option allows you to remove a decorator added via
210
		 * "client/jsonapi/common/decorators/default" before they are wrapped
211
		 * around the Jsonadm client.
212
		 *
213
		 *  client/jsonapi/decorators/excludes = array( 'decorator1' )
214
		 *
215
		 * This would remove the decorator named "decorator1" from the list of
216
		 * common decorators ("\Aimeos\Client\JsonApi\Common\Decorator\*") added via
217
		 * "client/jsonapi/common/decorators/default" for the JSON API client.
218
		 *
219
		 * @param array List of decorator names
220
		 * @since 2016.01
221
		 * @category Developer
222
		 * @see client/jsonapi/common/decorators/default
223
		 * @see client/jsonapi/decorators/global
224
		 * @see client/jsonapi/decorators/local
225
		 */
226
227
		/** client/jsonapi/decorators/global
228
		 * Adds a list of globally available decorators only to the Jsonadm client
229
		 *
230
		 * Decorators extend the functionality of a class by adding new aspects
231
		 * (e.g. log what is currently done), executing the methods of the underlying
232
		 * class only in certain conditions (e.g. only for logged in users) or
233
		 * modify what is returned to the caller.
234
		 *
235
		 * This option allows you to wrap global decorators
236
		 * ("\Aimeos\Client\Jsonadm\Common\Decorator\*") around the Jsonadm
237
		 * client.
238
		 *
239
		 *  client/jsonapi/product/decorators/global = array( 'decorator1' )
240
		 *
241
		 * This would add the decorator named "decorator1" defined by
242
		 * "\Aimeos\Client\Jsonadm\Common\Decorator\Decorator1" only to the
243
		 * "product" Jsonadm client.
244
		 *
245
		 * @param array List of decorator names
246
		 * @since 2016.01
247
		 * @category Developer
248
		 * @see client/jsonapi/common/decorators/default
249
		 * @see client/jsonapi/decorators/excludes
250
		 * @see client/jsonapi/decorators/local
251
		 */
252
253
		/** client/jsonapi/decorators/local
254
		 * Adds a list of local decorators only to the Jsonadm client
255
		 *
256
		 * Decorators extend the functionality of a class by adding new aspects
257
		 * (e.g. log what is currently done), executing the methods of the underlying
258
		 * class only in certain conditions (e.g. only for logged in users) or
259
		 * modify what is returned to the caller.
260
		 *
261
		 * This option allows you to wrap local decorators
262
		 * ("\Aimeos\Client\Jsonadm\Product\Decorator\*") around the Jsonadm
263
		 * client.
264
		 *
265
		 *  client/jsonapi/product/decorators/local = array( 'decorator2' )
266
		 *
267
		 * This would add the decorator named "decorator2" defined by
268
		 * "\Aimeos\Client\Jsonadm\Product\Decorator\Decorator2" only to the
269
		 * "product" Jsonadm client.
270
		 *
271
		 * @param array List of decorator names
272
		 * @since 2016.01
273
		 * @category Developer
274
		 * @see client/jsonapi/common/decorators/default
275
		 * @see client/jsonapi/decorators/excludes
276
		 * @see client/jsonapi/decorators/global
277
		 */
278
279
		$client = self::addClientDecorators( $client, $context, $path );
280
281
		return $client->setView( $context->getView() );
282
	}
283
}
284