Passed
Push — master ( 9d8f04...f72a92 )
by Atanas
04:02
created

PhpViewEngine::resolveRelativeFilepath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 14
ccs 9
cts 9
cp 1
crap 3
rs 10
c 0
b 0
f 0
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 1
				return $this->makeView( $view, $filepath );
82
			}
83 1
		}
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 1
		}
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 2
		) );
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 1
		}
149
150 1
		if ( ! $file ) {
151 1
			$file = $this->resolveFromAbsoluteFilepath( $view );
152 1
		}
153
154 1
		if ( $file ) {
155 1
			$file = realpath( $file );
156 1
		}
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
		$paths = [ STYLESHEETPATH, TEMPLATEPATH ];
170
171 1
		foreach ( $paths as $path ) {
172 1
			$path = MixedType::addTrailingSlash( $path );
173
174 1
			if ( substr( $normalized_view, 0, strlen( $path ) ) === $path ) {
175 1
				return substr( $normalized_view, strlen( $path ) );
176
			}
177 1
		}
178
179
		// Bail if we've failed to convert the view to a relative path.
180 1
		return $view;
181
	}
182
183
	/**
184
	 * Resolve a view if it exists in the custom views directory.
185
	 *
186
	 * @param  string $view
187
	 * @return string
188
	 */
189 1
	protected function resolveFromCustomFilepath( $view ) {
190 1
		$directory = $this->getDirectory();
191
192 1
		if ( $directory === '' ) {
193 1
			return '';
194
		}
195
196
		// Normalize to ensure there are no doubled separators.
197 1
		$file = MixedType::normalizePath( $directory . DIRECTORY_SEPARATOR . $view );
198
199 1
		if ( ! file_exists( $file ) ) {
200
			// Try adding a .php extension.
201 1
			$file .= '.php';
202 1
		};
203
204 1
		return file_exists( $file ) ? $file : '';
205
	}
206
207
	/**
208
	 * Resolve a view if it exists in the current theme.
209
	 *
210
	 * @param  string $view
211
	 * @return string
212
	 */
213 1
	protected function resolveFromThemeFilepath( $view ) {
214 1
		$file = locate_template( $view, false );
215
216 1
		if ( ! $file ) {
217
			// Try adding a .php extension.
218 1
			$file = locate_template( $view . '.php', false );
219 1
		}
220
221 1
		return $file;
222
	}
223
224
	/**
225
	 * Resolve a view if it exists on the filesystem.
226
	 *
227
	 * @param  string $view
228
	 * @return string
229
	 */
230 1
	protected function resolveFromAbsoluteFilepath( $view ) {
231 1
		return file_exists( $view ) ? $view : '';
232
	}
233
}
234