Completed
Push — master ( af891e...5c98d3 )
by Sam
11:04
created

ThemeManifest::init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 9.4285
1
<?php
2
3
namespace SilverStripe\View;
4
5
use \ManifestFileFinder;
6
7
/**
8
 * A class which builds a manifest of all themes (which is really just a directory called "templates")
9
 *
10
 * @package framework
11
 * @subpackage manifest
12
 */
13
class ThemeManifest {
14
15
	const TEMPLATES_DIR = 'templates';
16
17
	protected $base;
18
	protected $tests;
19
	protected $project;
20
21
	protected $cache;
22
	protected $cacheKey;
23
24
	protected $themes = null;
25
26
	/**
27
	 * Constructs a new template manifest. The manifest is not actually built
28
	 * or loaded from cache until needed.
29
	 *
30
	 * @param string $base The base path.
31
	 * @param string $project Path to application code
32
	 *
33
	 * @param bool $includeTests Include tests in the manifest.
34
	 * @param bool $forceRegen Force the manifest to be regenerated.
35
	 */
36
	public function __construct($base, $project, $includeTests = false, $forceRegen = false) {
37
		$this->base  = $base;
38
		$this->tests = $includeTests;
39
40
		$this->project = $project;
41
42
		$cacheClass = defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE : 'ManifestCache_File';
43
44
		$this->cache = new $cacheClass('thememanifest'.($includeTests ? '_tests' : ''));
45
		$this->cacheKey = $this->getCacheKey();
46
47
		if ($forceRegen) {
48
			$this->regenerate();
49
		}
50
	}
51
52
	/**
53
	 * @return string
54
	 */
55
	public function getBase() {
56
		return $this->base;
57
	}
58
59
	/**
60
	 * Generate a unique cache key to avoid manifest cache collisions.
61
	 * We compartmentalise based on the base path, the given project, and whether
62
	 * or not we intend to include tests.
63
	 * @return string
64
	 */
65
	public function getCacheKey() {
66
		return sha1(sprintf(
67
			"manifest-%s-%s-%u",
68
			$this->base,
69
			$this->project,
70
			$this->tests
71
		));
72
	}
73
74
	/**
75
	 * Returns a map of all themes information. The map is in the following format:
76
	 *
77
	 * <code>
78
	 *   [
79
	 *     'mysite',
80
	 *     'framework',
81
	 *     'framework/admin'
82
	 *   ]
83
	 * </code>
84
	 *
85
	 * @return array
86
	 */
87
	public function getThemes() {
88
		if ($this->themes === null) $this->init();
89
		return $this->themes;
90
	}
91
92
	/**
93
	 * Regenerates the manifest by scanning the base path.
94
	 *
95
	 * @param bool $cache
96
	 */
97
	public function regenerate($cache = true) {
98
		$finder = new ManifestFileFinder();
99
		$finder->setOptions(array(
100
			'include_themes' => false,
101
			'ignore_tests'  => !$this->tests,
102
			'dir_callback'  => array($this, 'handleDirectory')
103
		));
104
105
		$this->themes = [];
106
		$finder->find($this->base);
107
108
		if ($cache) {
109
			$this->cache->save($this->themes, $this->cacheKey);
110
		}
111
	}
112
113
	public function handleDirectory($basename, $pathname, $depth)
114
	{
115
		if ($basename == self::TEMPLATES_DIR) {
116
			// We only want part of the full path, so split into directories
117
			$parts = explode('/', $pathname);
118
			// Take the end (the part relative to base), except the very last directory
119
			$themeParts = array_slice($parts, -$depth, $depth-1);
120
			// Then join again
121
			$path = '/'.implode('/', $themeParts);
122
123
			// If this is in the project, add to beginning of list. Else add to end.
124
			if ($themeParts[0] == $this->project) {
125
				array_unshift($this->themes, $path);
126
			}
127
			else {
128
				array_push($this->themes, $path);
129
			}
130
		}
131
	}
132
133
	protected function init() {
134
		if ($data = $this->cache->load($this->cacheKey)) {
135
			$this->themes = $data;
136
		} else {
137
			$this->regenerate();
138
		}
139
	}
140
}
141