1: <?php
2:
3: namespace Charcoal\View;
4:
5: // PHP Dependencies
6: use \InvalidArgumentException;
7:
8: // PSR-3 (logger) dependencies
9: use \Psr\Log\LoggerAwareInterface;
10: use \Psr\Log\LoggerAwareTrait;
11:
12: // Local namespace dependencies
13: use \Charcoal\View\LoaderInterface;
14:
15: /**
16: * Base template loader.
17: */
18: abstract class AbstractLoader implements
19: LoggerAwareInterface,
20: LoaderInterface
21: {
22: use LoggerAwareTrait;
23:
24: /**
25: * @var string $basePath
26: */
27: private $basePath = '';
28:
29: /**
30: * @var string[] $paths
31: */
32: private $paths = [];
33:
34: /**
35: * Default constructor, if none is provided by the concrete class implementations.
36: *
37: * ## Required dependencies
38: * - `logger` A PSR-3 logger
39: *
40: * @param array $data The class dependencies map.
41: */
42: public function __construct(array $data = null)
43: {
44: $this->setLogger($data['logger']);
45: $this->setBasePath($data['base_path']);
46: $this->setPaths($data['paths']);
47: }
48:
49: /**
50: * Load a template content
51: *
52: * @param string $ident The template ident to load and render.
53: * @throws InvalidArgumentException If the dynamic template identifier is not a string.
54: * @return string
55: */
56: public function load($ident)
57: {
58: // Handle dynamic template hack.
59: if ($ident === '$widget_template') {
60: $ident = (isset($GLOBALS['widget_template']) ? $GLOBALS['widget_template'] : null);
61: if (!is_string($ident)) {
62: throw new InvalidArgumentException(
63: 'Dynamic template ident (from "$widget_template") must be a string'
64: );
65: }
66: }
67:
68: $file = $this->findTemplateFile($ident);
69: if ($file === null || $file === '') {
70: return $ident;
71: }
72:
73: return file_get_contents($file);
74: }
75:
76: /**
77: * @return string
78: */
79: protected function basePath()
80: {
81: return $this->basePath;
82: }
83:
84: /**
85: * @see FileLoader::path()
86: * @return string[]
87: */
88: protected function paths()
89: {
90: return $this->paths;
91: }
92:
93: /**
94: * Get the template file (full path + filename) to load from an ident.
95: *
96: * This method first generates the filename for an identifier and search for it in all of the loader's paths.
97: *
98: * @param string $ident The template identifier to load.
99: * @throws InvalidArgumentException If the template ident is not a string.
100: * @return string|null The full path + filename of the found template. Null if nothing was found.
101: */
102: protected function findTemplateFile($ident)
103: {
104: if (!is_string($ident)) {
105: throw new InvalidArgumentException(
106: 'Template ident must be a string'
107: );
108: }
109:
110: $filename = $this->filenameFromIdent($ident);
111: $searchPath = $this->paths();
112: foreach ($searchPath as $path) {
113: $f = realpath($path).'/'.strtolower($filename);
114: if (file_exists($f)) {
115: return $f;
116: }
117: }
118:
119: return null;
120: }
121:
122: /**
123: * @param string $ident The template identifier to convert to a filename.
124: * @return string
125: */
126: abstract protected function filenameFromIdent($ident);
127:
128: /**
129: * @param string[] $paths The list of path to add.
130: * @return LoaderInterface Chainable
131: */
132: private function setPaths(array $paths)
133: {
134: $this->paths = [];
135:
136: foreach ($paths as $path) {
137: $this->addPath($path);
138: }
139:
140: return $this;
141: }
142:
143: /**
144: * @param string $basePath The base path to set.
145: * @throws InvalidArgumentException If the base path parameter is not a string.
146: * @return LoaderInterface Chainable
147: */
148: private function setBasePath($basePath)
149: {
150: if (!is_string($basePath)) {
151: throw new InvalidArgumentException(
152: 'Base path must be a string'
153: );
154: }
155: $basePath = realpath($basePath);
156: $this->basePath = rtrim($basePath, '/\\').DIRECTORY_SEPARATOR;
157: return $this;
158: }
159:
160: /**
161: * @param string $path The path to add to the load.
162: * @return LoaderInterface Chainable
163: */
164: private function addPath($path)
165: {
166: $this->paths[] = $this->resolvePath($path);
167:
168: return $this;
169: }
170:
171: /**
172: * @param string $path The path to resolve.
173: * @throws InvalidArgumentException If the path argument is not a string.
174: * @return string
175: */
176: private function resolvePath($path)
177: {
178: if (!is_string($path)) {
179: throw new InvalidArgumentException(
180: 'Path needs to be a string'
181: );
182: }
183:
184: $basePath = $this->basePath();
185: $path = rtrim($path, '/\\').DIRECTORY_SEPARATOR;
186: if ($basePath && strpos($path, $basePath) === false) {
187: $path = $basePath.$path;
188: }
189:
190: return $path;
191: }
192: }
193: