Passed
Push — master ( 491d5e...9d8f04 )
by Atanas
01:41
created

PhpViewEngine   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Test Coverage

Coverage 96.92%

Importance

Changes 0
Metric Value
eloc 60
dl 0
loc 216
ccs 63
cts 65
cp 0.9692
rs 10
c 0
b 0
f 0
wmc 28

13 Methods

Rating   Name   Duplication   Size   Complexity  
A canonical() 0 4 1
A exists() 0 3 1
A __construct() 0 2 1
A setDirectory() 0 2 1
A getDirectory() 0 2 1
A makeView() 0 12 2
A getViewLayout() 0 17 3
A make() 0 9 3
A resolveFromAbsoluteFilepath() 0 2 2
A resolveFilepath() 0 17 4
A resolveFromCustomFilepath() 0 16 4
A resolveRelativeFilepath() 0 15 3
A resolveFromThemeFilepath() 0 9 2
1
<?php
2
/**
3
 * @package   WPEmerge
4
 * @author    Atanas Angelov <[email protected]>
5
 * @copyright 2018 Atanas Angelov
6
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0
7
 * @link      https://wpemerge.com/
8
 */
9
10
namespace WPEmerge\View;
11
12
use WPEmerge\Helpers\MixedType;
13
14
/**
15
 * Render view files with php.
16
 */
17
class PhpViewEngine implements ViewEngineInterface {
18
	/**
19
	 * Custom views directory to check first.
20
	 *
21
	 * @var string
22
	 */
23
	protected $directory = '';
24
25
	/**
26
	 * Constructor.
27
	 *
28
	 * @codeCoverageIgnore
29
	 * @param string $directory
30
	 */
31
	public function __construct( $directory = '' ) {
32
		$this->setDirectory( $directory );
33
	}
34
35
	/**
36
	 * Get the custom views directory.
37
	 *
38
	 * @codeCoverageIgnore
39
	 * @return string
40
	 */
41
	public function getDirectory() {
42
		return $this->directory;
43
	}
44
45
	/**
46
	 * Set the custom views directory.
47
	 *
48
	 * @codeCoverageIgnore
49
	 * @param  string $directory
50
	 * @return void
51
	 */
52
	public function setDirectory( $directory ) {
53
		$this->directory = MixedType::removeTrailingSlash( $directory );
54
	}
55
56
	/**
57
	 * {@inheritDoc}
58
	 */
59 1
	public function exists( $view ) {
60 1
		$file = $this->resolveFilepath( $view );
61 1
		return strlen( $file ) > 0;
62
	}
63
64
	/**
65
	 * {@inheritDoc}
66
	 */
67 1
	public function canonical( $view ) {
68 1
		$root = realpath( MixedType::normalizePath( get_template_directory() ) ) . DIRECTORY_SEPARATOR;
69 1
		$match_root = '/^' . preg_quote( $root, '/' ) . '/';
70 1
		return preg_replace( $match_root, '', $this->resolveFilepath( $view ) );
71
	}
72
73
	/**
74
	 * {@inheritDoc}
75
	 * @throws ViewNotFoundException
76
	 */
77 2
	public function make( $views ) {
78 2
		foreach ( $views as $view ) {
79 2
			if ( $this->exists( $view ) ) {
80 1
				$filepath = $this->resolveFilepath( $view );
81 2
				return $this->makeView( $view, $filepath );
82
			}
83
		}
84
85 1
		throw new ViewNotFoundException( 'View not found for "' . implode( ', ', $views ) . '"' );
86
	}
87
88
	/**
89
	 * Create a view instance.
90
	 *
91
	 * @param  string $name
92
	 * @param  string $filepath
93
	 * @return ViewInterface
94
	 * @throws ViewNotFoundException
95
	 */
96 2
	protected function makeView( $name, $filepath ) {
97 2
		$view = (new PhpView())
98 2
			->setName( $name )
99 2
			->setFilepath( $filepath );
100
101 2
		$layout = $this->getViewLayout( $view );
102
103 2
		if ( $layout !== null ) {
104 1
			$view->setLayout( $layout );
105
		}
106
107 2
		return $view;
108
	}
109
110
	/**
111
	 * Create a view instance for the given view's layout header, if any.
112
	 *
113
	 * @param  PhpView $view
114
	 * @return ViewInterface
115
	 * @throws ViewNotFoundException
116
	 */
117 2
	protected function getViewLayout( PhpView $view ) {
118 2
		$layout_headers = array_filter( get_file_data(
119 2
			$view->getFilepath(),
120 2
			['App Layout']
121
		) );
122
123 2
		if ( empty( $layout_headers ) ) {
124 1
			return null;
125
		}
126
127 2
		$layout_file = trim( $layout_headers[0] );
128
129 2
		if ( ! $this->exists( $layout_file ) ) {
130 1
			throw new ViewNotFoundException( 'View layout not found for "' . $layout_file . '"' );
131
		}
132
133 1
		return $this->makeView( $this->canonical( $layout_file ), $this->resolveFilepath( $layout_file ) );
134
	}
135
136
	/**
137
	 * Resolve a view name to an absolute filepath.
138
	 *
139
	 * @param  string $view
140
	 * @return string
141
	 */
142 1
	protected function resolveFilepath( $view ) {
143 1
		$view = $this->resolveRelativeFilepath( $view );
144 1
		$file = $this->resolveFromCustomFilepath( $view );
145
146 1
		if ( ! $file ) {
147 1
			$file = $this->resolveFromThemeFilepath( $view );
148
		}
149
150 1
		if ( ! $file ) {
151 1
			$file = $this->resolveFromAbsoluteFilepath( $view );
152
		}
153
154 1
		if ( $file ) {
155 1
			$file = realpath( $file );
156
		}
157
158 1
		return $file;
159
	}
160
161
	/**
162
	 * Resolve an absolute view to a relative one, if possible.
163
	 *
164
	 * @param  string $view
165
	 * @return string
166
	 */
167 1
	protected function resolveRelativeFilepath( $view ) {
168 1
		$normalized_view = MixedType::normalizePath( $view );
169 1
		$stylesheet_path = MixedType::addTrailingSlash( STYLESHEETPATH );
170 1
		$template_path = MixedType::addTrailingSlash( TEMPLATEPATH );
171
172 1
		if ( substr( $normalized_view, 0, strlen( $stylesheet_path ) ) === $stylesheet_path ) {
173 1
			return substr( $normalized_view, strlen( $stylesheet_path ) );
174
		}
175
176 1
		if ( substr( $normalized_view, 0, strlen( $template_path ) ) === $template_path ) {
177
			return substr( $normalized_view, strlen( $template_path ) );
178
		}
179
180
		// Bail if we've failed to convert the view to a relative path.
181 1
		return $view;
182
	}
183
184
	/**
185
	 * Resolve a view if it exists in the custom views directory.
186
	 *
187
	 * @param  string $view
188
	 * @return string
189
	 */
190 1
	protected function resolveFromCustomFilepath( $view ) {
191 1
		$directory = $this->getDirectory();
192
193 1
		if ( $directory === '' ) {
194
			return '';
195
		}
196
197
		// Normalize to ensure there are no doubled separators.
198 1
		$file = MixedType::normalizePath( $directory . DIRECTORY_SEPARATOR . $view );
199
200 1
		if ( ! file_exists( $file ) ) {
201
			// Try adding a .php extension.
202 1
			$file .= '.php';
203
		};
204
205 1
		return file_exists( $file ) ? $file : '';
206
	}
207
208
	/**
209
	 * Resolve a view if it exists in the current theme.
210
	 *
211
	 * @param  string $view
212
	 * @return string
213
	 */
214 1
	protected function resolveFromThemeFilepath( $view ) {
215 1
		$file = locate_template( $view, false );
216
217 1
		if ( ! $file ) {
218
			// Try adding a .php extension.
219 1
			$file = locate_template( $view . '.php', false );
220
		}
221
222 1
		return $file;
223
	}
224
225
	/**
226
	 * Resolve a view if it exists on the filesystem.
227
	 *
228
	 * @param  string $view
229
	 * @return string
230
	 */
231 1
	protected function resolveFromAbsoluteFilepath( $view ) {
232 1
		return file_exists( $view ) ? $view : '';
233
	}
234
}
235