Html::addDecorators()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 6
c 1
b 0
f 0
nc 3
nop 4
dl 0
loc 15
rs 10
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2025
6
 * @package Client
7
 * @subpackage Html
8
 */
9
10
11
namespace Aimeos\Client;
12
13
14
/**
15
 * Common factory for HTML clients
16
 *
17
 * @package Client
18
 * @subpackage Html
19
 */
20
class Html
21
{
22
	private static $objects = [];
23
24
25
	/**
26
	 * Creates a new client object
27
	 *
28
	 * @param \Aimeos\MShop\ContextIface $context Shop context instance with necessary objects
29
	 * @param string $path Type of the client, e.g 'account/favorite' for \Aimeos\Client\Html\Account\Favorite\Standard
30
	 * @param string|null $name Client name (default: "Standard")
31
	 * @return \Aimeos\Client\Html\Iface HTML client implementing \Aimeos\Client\Html\Iface
32
	 * @throws \Aimeos\Client\Html\Exception If requested client implementation couldn't be found or initialisation fails
33
	 */
34
	public static function create( \Aimeos\MShop\ContextIface $context,
35
		string $path, ?string $name = null ) : \Aimeos\Client\Html\Iface
36
	{
37
		if( empty( $path ) ) {
38
			throw new \Aimeos\Client\Html\Exception( 'Component path is empty', 400 );
39
		}
40
41
		if( empty( $name ) ) {
42
			$name = $context->config()->get( 'client/html/' . $path . '/name', 'Standard' );
43
		}
44
45
		$interface = '\\Aimeos\\Client\Html\\Iface';
46
		$classname = '\\Aimeos\\Client\\Html\\' . str_replace( '/', '\\', ucwords( $path, '/' ) ) . '\\' . $name;
47
48
		if( class_exists( $classname ) === false ) {
49
			throw new \Aimeos\Client\Html\Exception( sprintf( 'Class "%1$s" not found', $classname, 404 ) );
50
		}
51
52
		$client = self::createComponent( $context, $classname, $interface, $path );
53
54
		return $client->setObject( $client );
55
	}
56
57
58
	/**
59
	 * Injects a client object.
60
	 * The object is returned via create() if an instance of the class
61
	 * with the name name is requested.
62
	 *
63
	 * @param string $classname Full name of the class for which the object should be returned
64
	 * @param \Aimeos\Client\Html\Iface|null $client ExtJS client object
65
	 */
66
	public static function inject( string $classname, ?\Aimeos\Client\Html\Iface $client = null )
67
	{
68
		self::$objects['\\' . ltrim( $classname, '\\' )] = $client;
69
	}
70
71
72
	/**
73
	 * Adds the decorators to the client object.
74
	 *
75
	 * @param \Aimeos\MShop\ContextIface $context Context instance with necessary objects
76
	 * @param \Aimeos\Client\Html\Iface $client Client object
77
	 * @param string $path Path of the client in lower case, e.g. "catalog/detail"
78
	 * @return \Aimeos\Client\Html\Iface Client object
79
	 */
80
	protected static function addComponentDecorators( \Aimeos\MShop\ContextIface $context,
81
		\Aimeos\Client\Html\Iface $client, string $path ) : \Aimeos\Client\Html\Iface
82
	{
83
		$config = $context->config();
84
		$localClass = str_replace( '/', '\\', ucwords( $path, '/' ) );
85
86
		$classprefix = '\\Aimeos\\Client\\Html\\' . $localClass . '\\Decorator\\';
87
		$decorators = array_reverse( $config->get( 'client/html/' . $path . '/decorators/local', [] ) );
88
		$client = self::addDecorators( $context, $client, $decorators, $classprefix );
89
90
		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
91
		$decorators = array_reverse( $config->get( 'client/html/' . $path . '/decorators/global', [] ) );
92
		$client = self::addDecorators( $context, $client, $decorators, $classprefix );
93
94
		/** client/html/common/decorators/default
95
		 * Configures the list of decorators applied to all html clients
96
		 *
97
		 * Decorators extend the functionality of a class by adding new aspects
98
		 * (e.g. log what is currently done), executing the methods of the underlying
99
		 * class only in certain conditions (e.g. only for logged in users) or
100
		 * modify what is returned to the caller.
101
		 *
102
		 * This option allows you to configure a list of decorator names that should
103
		 * be wrapped around the original instance of all created clients:
104
		 *
105
		 *  client/html/common/decorators/default = array( 'decorator1', 'decorator2' )
106
		 *
107
		 * This would wrap the decorators named "decorator1" and "decorator2" around
108
		 * all client instances in that order. The decorator classes would be
109
		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" and
110
		 * "\Aimeos\Client\Html\Common\Decorator\Decorator2".
111
		 *
112
		 * @param array List of decorator names
113
		 * @since 2014.03
114
		 */
115
		$decorators = array_reverse( $config->get( 'client/html/common/decorators/default', [] ) );
116
		$excludes = $config->get( 'client/html/' . $path . '/decorators/excludes', [] );
117
118
		foreach( $decorators as $key => $name )
119
		{
120
			if( in_array( $name, $excludes ) ) {
121
				unset( $decorators[$key] );
122
			}
123
		}
124
125
		$classprefix = '\\Aimeos\\Client\\Html\\Common\\Decorator\\';
126
		$client = self::addDecorators( $context, $client, $decorators, $classprefix );
127
128
		return $client;
129
	}
130
131
132
	/**
133
	 * Adds the decorators to the client object.
134
	 *
135
	 * @param \Aimeos\MShop\ContextIface $context Context instance with necessary objects
136
	 * @param \Aimeos\Client\Html\Iface $client Client object
137
	 * @param array $decorators List of decorator name that should be wrapped around the client
138
	 * @param string $classprefix Decorator class prefix, e.g. "\Aimeos\Client\Html\Catalog\Decorator\"
139
	 * @return \Aimeos\Client\Html\Iface Client object
140
	 * @throws \LogicException If class can't be instantiated
141
	 */
142
	protected static function addDecorators( \Aimeos\MShop\ContextIface $context,
143
		\Aimeos\Client\Html\Iface $client, array $decorators, string $classprefix ) : \Aimeos\Client\Html\Iface
144
	{
145
		$interface = \Aimeos\Client\Html\Common\Decorator\Iface::class;
146
147
		foreach( $decorators as $name )
148
		{
149
			if( ctype_alnum( $name ) === false ) {
150
				throw new \LogicException( sprintf( 'Invalid class name "%1$s"', $name ), 400 );
151
			}
152
153
			$client = \Aimeos\Utils::create( $classprefix . $name, [$client, $context], $interface );
154
		}
155
156
		return $client;
157
	}
158
159
160
	/**
161
	 * Creates a client object.
162
	 *
163
	 * @param \Aimeos\MShop\ContextIface $context Context instance with necessary objects
164
	 * @param string $classname Name of the client class
165
	 * @param string $interface Name of the client interface
166
	 * @param string $path Type of the client, e.g 'account/favorite' for \Aimeos\Client\Html\Account\Favorite\Standard
167
	 * @return \Aimeos\Client\Html\Iface Client object
168
	 * @throws \Aimeos\Client\Html\Exception If client couldn't be found or doesn't implement the interface
169
	 */
170
	protected static function createComponent( \Aimeos\MShop\ContextIface $context,
171
		string $classname, string $interface, string $path ) : \Aimeos\Client\Html\Iface
172
	{
173
		if( isset( self::$objects[$classname] ) ) {
174
			return self::$objects[$classname];
175
		}
176
177
		$client = \Aimeos\Utils::create( $classname, [$context], $interface );
178
179
		return self::addComponentDecorators( $context, $client, $path );
180
	}
181
}
182