Completed
Push — master ( 05180c...82db24 )
by Aimeos
08:11
created

Standard::render()   B

Complexity

Conditions 4
Paths 9

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
c 0
b 0
f 0
cc 4
eloc 12
nc 9
nop 1
rs 8.5806
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2012
6
 * @copyright Aimeos (aimeos.org), 2015-2016
7
 * @package MW
8
 * @subpackage View
9
 */
10
11
12
namespace Aimeos\MW\View;
13
14
15
/**
16
 * Default view implementation.
17
 *
18
 * @method mixed config(string $name = null, string|array $default = null) Returns the config value for the given key
19
 * @method \Aimeos\MW\View\Helper\Iface csrf() Returns the CSRF helper object
20
 * @method string date(string $date) Returns the formatted date
21
 * @method \Aimeos\MW\View\Helper\Iface encoder() Returns the encoder helper object
22
 * @method string formparam(string|array $names) Returns the name for the HTML form parameter
23
 * @method \Aimeos\MW\Mail\Message\Iface mail() Returns the e-mail message object
24
 * @method string number(integer|float|decimal $number, integer $decimals = 2) Returns the formatted number
25
 * @method string|array param(string|null $name, string|array $default) Returns the parameter value
26
 * @method string partial(string $filepath, array $params = array() ) Renders the partial template
27
 * @method \Aimeos\MW\View\Helper\Iface request() Returns the request helper object
28
 * @method string translate(string $domain, string $singular, string $plural = '', integer $number = 1) Returns the translated string or the original one if no translation is available
29
 * @method string url(string|null $target, string|null $controller = null, string|null $action = null, array $params = array(), array $trailing = array(), array $config = array()) Returns the URL assembled from the given arguments
30
 *
31
 * @package MW
32
 * @subpackage View
33
 */
34
class Standard implements \Aimeos\MW\View\Iface
35
{
36
	private $helper = array();
37
	private $values = array();
38
	private $engines;
39
	private $paths;
40
41
42
	/**
43
	 * Initializes the view object
44
	 *
45
	 * @param array $paths Associative list of base paths as keys and list of relative paths as value
46
	 * @param array $engines Associative list of file extensions as keys and \Aimeos\MW\View\Engine\Iface as value
47
	 */
48
	public function __construct( array $paths = array(), array $engines = array() )
49
	{
50
		$this->engines = $engines;
51
		$this->paths = $paths;
52
	}
53
54
55
	/**
56
	 * Calls the view helper with the given name and arguments and returns it's output.
57
	 *
58
	 * @param string $name Name of the view helper
59
	 * @param array $args Arguments passed to the view helper
60
	 * @return mixed Output depending on the view helper
61
	 */
62
	public function __call( $name, array $args )
63
	{
64
		if( !isset( $this->helper[$name] ) )
65
		{
66
			if( ctype_alnum( $name ) === false )
67
			{
68
				$classname = is_string( $name ) ? '\\Aimeos\\MW\\View\\Helper\\' . $name : '<not a string>';
69
				throw new \Aimeos\MW\View\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
70
			}
71
72
			$iface = '\\Aimeos\\MW\\View\\Helper\\Iface';
73
			$classname = '\\Aimeos\\MW\\View\\Helper\\' . ucfirst( $name ) . '\\Standard';
74
75
			if( class_exists( $classname ) === false ) {
76
				throw new \Aimeos\MW\View\Exception( sprintf( 'Class "%1$s" not available', $classname ) );
77
			}
78
79
			$helper = new $classname( $this );
80
81
			if( !( $helper instanceof $iface ) ) {
82
				throw new \Aimeos\MW\View\Exception( sprintf( 'Class "%1$s" does not implement interface "%2$s"', $classname, $iface ) );
83
			}
84
85
			$this->helper[$name] = $helper;
86
		}
87
88
		return call_user_func_array( array( $this->helper[$name], 'transform' ), $args );
89
	}
90
91
92
	/**
93
	 * Clones internal objects of the view.
94
	 */
95
	public function __clone()
96
	{
97
		foreach( $this->helper as $name => $helper )
98
		{
99
			$helper = clone $helper;
100
101
			// reset view so view helpers will use the current one (for translation, etc.)
102
			$helper->setView( $this );
103
104
			$this->helper[$name] = $helper;
105
		}
106
	}
107
108
109
	/**
110
	 * Returns the value associated to the given key.
111
	 *
112
	 * @param string $key Name of the value that should be returned
113
	 * @return mixed Value associated to the given key
114
	 * @throws \Aimeos\MW\View\Exception If the requested key isn't available
115
	 */
116
	public function __get( $key )
117
	{
118
		if( !isset( $this->values[$key] ) ) {
119
			throw new \Aimeos\MW\View\Exception( sprintf( 'No value for key "%1$s" found', $key ) );
120
		}
121
122
		return $this->values[$key];
123
	}
124
125
126
	/**
127
	 * Tests if a key with the given name exists.
128
	 *
129
	 * @param string $key Name of the value that should be tested
130
	 * @return boolean True if the key exists, false if not
131
	 */
132
	public function __isset( $key )
133
	{
134
		return isset( $this->values[$key] );
135
	}
136
137
138
	/**
139
	 * Removes a key from the stored values.
140
	 *
141
	 * @param string $key Name of the value that should be removed
142
	 */
143
	public function __unset( $key )
144
	{
145
		unset( $this->values[$key] );
146
	}
147
148
149
	/**
150
	 * Sets a new value for the given key.
151
	 *
152
	 * @param string $key Name of the value that should be set
153
	 * @param mixed $value Value associated to the given key
154
	 */
155
	public function __set( $key, $value )
156
	{
157
		$this->values[$key] = $value;
158
	}
159
160
161
	/**
162
	 * Adds a view helper instance to the view.
163
	 *
164
	 * @param string $name Name of the view helper as called in the template
165
	 * @param \Aimeos\MW\View\Helper\Iface $helper View helper instance
166
	 * @return \Aimeos\MW\View\Iface View object for method chaining
167
	 */
168
	public function addHelper( $name, \Aimeos\MW\View\Helper\Iface $helper )
169
	{
170
		$this->helper[$name] = $helper;
171
		return $this;
172
	}
173
174
175
	/**
176
	 * Assigns a whole set of values at once to the view.
177
	 * This method overwrites already existing key/value pairs set by the magic method.
178
	 *
179
	 * @param array $values Associative list of key/value pairs
180
	 */
181
	public function assign( array $values )
182
	{
183
		$this->values = $values;
184
	}
185
186
187
	/**
188
	 * Returns the value associated to the given key or the default value if the key is not available.
189
	 *
190
	 * @param string $key Name of the value that should be returned
191
	 * @param mixed $default Default value returned if ths key is not available
192
	 * @return mixed Value associated to the given key or the default value
193
	 */
194
	public function get( $key, $default = null )
195
	{
196
		$values = $this->values;
197
198
		foreach( explode( '/', ltrim( $key, '/' ) ) as $part )
199
		{
200
			if( is_array( $values ) && isset( $values[$part] ) ) {
201
				$values = $values[$part];
202
			} else {
203
				return $default;
204
			}
205
		}
206
207
		return $values;
208
	}
209
210
211
	/**
212
	 * Renders the output based on the given template file name and the key/value pairs.
213
	 *
214
	 * @param string|array $filename File name of list of file names for the view templates
215
	 * @return string Output generated by the template
216
	 * @throws \Aimeos\MW\View\Exception If the template isn't found
217
	 */
218
	public function render( $filename )
219
	{
220
		$filepath = $this->resolve( $filename );
221
222
		foreach( $this->engines as $fileext => $engine )
223
		{
224
			if( substr_compare( $filepath, $fileext, -strlen( $fileext ) ) ===0 ) {
225
				return $engine->render( $filepath, $this->values );
226
			}
227
		}
228
229
		try
230
		{
231
			ob_start();
232
233
			$this->includeFile( $filepath );
234
235
			return ob_get_clean();
236
		}
237
		catch( \Exception $e )
238
		{
239
			ob_end_clean();
240
			throw $e;
241
		}
242
	}
243
244
245
	/**
246
	 * Includes the template file and processes the PHP instructions.
247
	 * The filename is passed as first argument but without variable name to prevent messing the variable scope.
248
	 */
249
	protected function includeFile()
250
	{
251
		include func_get_arg( 0 );
252
	}
253
254
255
	/**
256
	 * Returns the absolute file name for the given relative one
257
	 *
258
	 * @param string|array $files File name of list of file names for the view templates
259
	 * @return string Absolute path to the template file
260
	 * @throws \Aimeos\MW\Exception If the template couldn't be found
261
	 */
262
	protected function resolve( $files )
263
	{
264
		foreach( (array) $files as $file )
265
		{
266
			if( is_file( $file ) ) {
267
				return $file;
268
			}
269
270
			$ds = DIRECTORY_SEPARATOR;
271
272
			foreach( array_reverse( $this->paths ) as $path => $relPaths )
273
			{
274
				foreach( $relPaths as $relPath )
275
				{
276
					$absPath = $path . $ds . $relPath . $ds . $file;
277
					if( $ds !== '/' ) {
278
						$absPath = str_replace( '/', $ds, $absPath );
279
					}
280
281
					if( is_file( $absPath ) ) {
282
						return $absPath;
283
					}
284
				}
285
			}
286
		}
287
288
		throw new \Aimeos\MW\View\Exception( sprintf( 'Template "%1$s" not available', $file ) );
289
	}
290
}
291