Completed
Push — master ( f548dd...46b15a )
by Hamish
23s
created

view/TemplateLoader.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\View;
4
5
/**
6
 * Handles finding templates from a stack of template manifest objects.
7
 *
8
 * @package framework
9
 * @subpackage view
10
 */
11
class TemplateLoader {
12
13
	/**
14
	 * @var TemplateLoader
15
	 */
16
	private static $instance;
17
18
	protected $base;
19
20
	/**
21
	 * @var ThemeManifest[]
22
	 */
23
	protected $sets = [];
24
25
	public static function instance() {
26
		return self::$instance ? self::$instance : self::$instance = new self();
27
	}
28
29
	public static function set_instance(TemplateLoader $instance) {
30
		self::$instance = $instance;
31
	}
32
33
	public function __construct($base = null) {
34
		$this->base = $base ? $base : BASE_PATH;
35
	}
36
37
	/**
38
	 * Add a new theme manifest for a given identifier. E.g. '$default'
39
	 *
40
	 * @param string $set
41
	 * @param ThemeManifest $manifest
42
	 */
43
	public function addSet($set, $manifest) {
44
		$this->sets[$set] = $manifest;
45
	}
46
47
	/**
48
	 * Given a theme identifier, determine the path from the root directory
49
	 *
50
	 * The mapping from $identifier to path follows these rules:
51
	 * - A simple theme name ('mytheme') which maps to the standard themes dir (/themes/mytheme)
52
	 * - A theme path with a leading slash ('/mymodule/themes/mytheme') which maps directly to that path.
53
	 * - or a vendored theme path. (vendor/mymodule:mytheme) which maps to the nested 'theme' within
54
	 *   that module. ('/mymodule/themes/mytheme').
55
	 * - A vendored module with no nested theme (vendor/mymodule) which maps to the root directory
56
	 *   of that module. ('/mymodule').
57
	 *
58
	 * @param string $identifier Theme identifier.
59
	 * @return string Path from root, not including leading forward slash. E.g. themes/mytheme
60
	 */
61
	public function getPath($identifier) {
62
		$slashPos = strpos($identifier, '/');
63
64
		// If identifier starts with "/", it's a path from root
65
		if ($slashPos === 0) {
66
			return substr($identifier, 1);
67
		}
68
		// Otherwise if there is a "/", identifier is a vendor'ed module
69
		elseif ($slashPos !== false) {
70
			// Extract from <vendor>/<module>:<theme> format.
71
			// <vendor> is optional, and if <theme> is omitted it defaults to the module root dir.
72
			// If <theme> is included, this is the name of the directory under moduleroot/themes/
73
			// which contains the theme.
74
			// <module> is always the name of the install directory, not necessarily the composer name.
75
			$parts = explode(':', $identifier, 2);
76
77
			list($vendor, $module) = explode('/', $parts[0], 2);
0 ignored issues
show
The assignment to $vendor is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
78
			$theme = count($parts) > 1 ? $parts[1] : '';
79
80
			$path = $module . ($theme ? '/themes/'.$theme : '');
81
82
			// Right now we require $module to be a silverstripe module (in root) or theme (in themes dir)
83
			// If both exist, we prefer theme
84
			if (is_dir(THEMES_PATH . '/' .$path)) {
85
				return THEMES_DIR . '/' . $path;
86
			}
87
			else {
88
				return $path;
89
			}
90
		}
91
		// Otherwise it's a (deprecated) old-style "theme" identifier
92
		else {
93
			return THEMES_DIR.'/'.$identifier;
94
		}
95
	}
96
97
	/**
98
	 * Attempts to find possible candidate templates from a set of template
99
	 * names from modules, current theme directory and finally the application
100
	 * folder.
101
	 *
102
	 * The template names can be passed in as plain strings, or be in the
103
	 * format "type/name", where type is the type of template to search for
104
	 * (e.g. Includes, Layout).
105
	 *
106
	 * @param string|array $template Template name, or template spec in array format with the keys
107
	 * 'type' (type string) and 'templates' (template hierarchy in order of precedence).
108
	 * If 'templates' is ommitted then any other item in the array will be treated as the template
109
	 * list.
110
	 * Templates with an .ss extension will be treated as file paths, and will bypass
111
	 * theme-coupled resolution.
112
	 * @param array $themes List of themes to use to resolve themes. In most cases
113
	 * you should pass in {@see SSViewer::get_themes()}
114
	 * @return string Path to resolved template file, or null if not resolved.
115
	 */
116
	public function findTemplate($template, $themes) {
117
		$type = '';
118
		if(is_array($template)) {
119
			// Check if templates has type specified
120
			if (array_key_exists('type', $template)) {
121
				$type = $template['type'];
122
				unset($template['type']);
123
			}
124
			// Templates are either nested in 'templates' or just the rest of the list
125
			$templateList = array_key_exists('templates', $template) ? $template['templates'] : $template;
126
		} else {
127
			$templateList = array($template);
128
		}
129
130
		// If we have an .ss extension, this is a path, not a template name. We should
131
		// pass in templates without extensions in order for template manifest to find
132
		// files dynamically.
133
		if(count($templateList) == 1 && substr($templateList[0], -3) == '.ss') {
134
			return $templateList[0];
135
		}
136
137
		foreach($templateList as $i => $template) {
138
			$template = str_replace('\\', '/', $template);
139
			$parts = explode('/', $template);
140
141
			$tail = array_pop($parts);
142
			$head = implode('/', $parts);
143
144
			foreach($themes as $themename) {
145
				$subthemes = isset($this->sets[$themename]) ? $this->sets[$themename]->getThemes() : [$themename];
146
147
				foreach($subthemes as $theme) {
148
					$themePath = $this->base . '/' . $this->getPath($theme);
149
150
					$path = $themePath . '/templates/' . implode('/', array_filter([$head, $type, $tail])) . '.ss';
151
					if (file_exists($path)) return $path;
152
				}
153
			}
154
		}
155
156
		// No template found
157
		return null;
158
	}
159
160
}
161