Passed
Pull Request — master (#79)
by Glynn
05:50
created

PHP_Engine::component()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Basic PHP engine for using the Renderable interface.
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package PinkCrab\Perique\View
23
 */
24
25
namespace PinkCrab\Perique\Services\View;
26
27
use Exception;
28
use PinkCrab\Perique\Interfaces\Renderable;
29
use PinkCrab\Perique\Services\View\View_Model;
30
use PinkCrab\Perique\Services\View\Component\Component;
31
use PinkCrab\Perique\Services\View\Component\Component_Compiler;
32
33
class PHP_Engine implements Renderable {
34
35
	/**
36
	 * The path to base of templates.
37
	 *
38
	 * @var string
39
	 */
40
	protected $base_view_path;
41
42
	/**
43
	 * Access to the component compiler.
44
	 *
45
	 * @var Component_Compiler
46
	 */
47
	protected $component_compiler;
48
49
	/**
50
	 * Creates an instance of the PHP_Engine
51
	 *
52
	 * @param string $base_view_path
53
	 */
54
	public function __construct( string $base_view_path ) {
55
		$this->base_view_path = $this->verify_view_path( $base_view_path );
56
	}
57
58
	/**
59
	 * Sets the component compiler.
60
	 *
61
	 * @param Component_Compiler $compiler
62
	 * @return void
63
	 */
64
	public function set_component_compiler( Component_Compiler $compiler ): void {
65
		$this->component_compiler = $compiler;
66
	}
67
68
	/**
69
	 * Renders a template with data.
70
	 *
71
	 * @param string $view
72
	 * @param iterable<string, mixed> $data
73
	 * @param bool $print
74
	 * @return string|void
75
	 */
76
	public function render( string $view, iterable $data, bool $print = true ) {
77
78
		if ( $print ) {
79
			print( $this->render_buffer( $view, $data ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
80
		} else {
81
			return $this->render_buffer( $view, $data );
82
		}
83
	}
84
85
	/**
86
	 * Renders a component.
87
	 *
88
	 * @param Component $component
89
	 * @return string|void
90
	 */
91
	public function component( Component $component, bool $print = true ) {
92
93
		// Throw exception of no compiler passed.
94
		if ( ! is_a( $this->component_compiler, Component_Compiler::class ) ) {
95
			throw new Exception( 'No component compiler passed to PHP_Engine' );
96
		}
97
98
		// Compile the component.
99
		$compiled = $this->component_compiler->compile( $component );
100
		return $this->render( $compiled->template(), $compiled->data(), $print );
101
	}
102
103
104
	/**
105
	 * Renders a view Model
106
	 *
107
	 * @param View_Model $view_model
108
	 * @return string|void
109
	 */
110
	public function view_model( View_Model $view_model, bool $print = true ) {
111
		return $this->render( $view_model->template(), $view_model->data(), $print );
112
	}
113
114
	/**
115
	 * Include a partial sub template
116
	 *
117
	 * @param string $view
118
	 * @param iterable<string, mixed> $data
119
	 * @param bool $print
120
	 * @return string|void
121
	 */
122
	public function partial( string $view, iterable $data = array(), bool $print = true ) {
123
		if ( $print ) {
124
			$this->render( $view, $data, $print );
125
		} else {
126
			return $this->render( $view, $data, $print );
127
		}
128
	}
129
130
	/**
131
	 * Builds the view.
132
	 *
133
	 * @param string $view
134
	 * @param iterable<string, mixed> $__data
135
	 * @return string
136
	 * @throws Exception
137
	 */
138
	protected function render_buffer( string $view, iterable $__data ): string {
139
140
		if ( ! file_exists( $this->resolve_file_path( $view ) ) ) {
141
			throw new Exception( "{$view} doesn't exist" );
142
		}
143
144
		$output = '';
145
		ob_start();
146
147
		// Set all the data values a parameters.
148
		foreach ( $__data as $__key => $__value ) {
149
			if ( is_string( $__key ) ) {
150
				${\wp_strip_all_tags( $__key )} = $__value;
151
			}
152
153
			// Unset the key and value.
154
			unset( $__key, $__value, $__data );
155
		}
156
157
		include $this->resolve_file_path( $view );
158
		$output = ob_get_contents();
159
		ob_end_clean();
160
		return $output ?: '';
161
	}
162
163
	/**
164
	 * Trims any leading slash and removes .php
165
	 *
166
	 * @param string $file
167
	 * @return string
168
	 */
169
	protected function clean_filename( string $file ): string {
170
			$file = ltrim( $file, '/' );
171
			return substr( $file, -4 ) === '.php'
172
			? substr( $file, 0, -4 )
173
			: $file;
174
175
	}
176
177
	/**
178
	 * Resolves the filepath from a filenane.
179
	 *
180
	 * @param string $filename
181
	 * @return string
182
	 */
183
	protected function resolve_file_path( string $filename ): string {
184
		return sprintf(
185
			'%s%s.php',
186
			$this->base_view_path,
187
			$this->clean_filename( $filename )
188
		);
189
	}
190
191
192
	/**
193
	 * Verifies the view path exists and it has the trailing slash.
194
	 *
195
	 * @param string $path
196
	 * @return string
197
	 * @throws Exception
198
	 */
199
	protected function verify_view_path( string $path ): string {
200
201
		$path = rtrim( $path, '/' ) . '/';
202
203
		if ( ! \is_dir( $path ) ) {
204
			throw new Exception( "{$path} doesn't exist and cant be used as the base view path." );
205
		}
206
207
		return $path;
208
	}
209
}
210