1: <?php
2: /**
3: * Wei Framework
4: *
5: * @copyright Copyright (c) 2008-2013 Twin Huang
6: * @license http://opensource.org/licenses/mit-license.php MIT License
7: */
8:
9: namespace Wei;
10:
11: /**
12: * A service that use to render PHP template
13: *
14: * @author Twin Huang <twinhuang@qq.com>
15: */
16: class View extends Base implements \ArrayAccess
17: {
18: /**
19: * The template variables
20: *
21: * @var array
22: */
23: protected $data = array();
24:
25: /**
26: * The directories to find template
27: *
28: * @var array
29: */
30: protected $dirs = array('.');
31:
32: /**
33: * Default template file extension
34: *
35: * @var string
36: */
37: protected $extension = '.php';
38:
39: /**
40: * The layout configuration
41: *
42: * @var array
43: */
44: protected $layout = array();
45:
46: /**
47: * The default layout path for layout method
48: *
49: * @var string
50: */
51: protected $defaultLayout;
52:
53: /**
54: * The current render view name
55: *
56: * @var string
57: */
58: private $currentName;
59:
60: /**
61: * Constructor
62: *
63: * @param array $options
64: */
65: public function __construct(array $options = array())
66: {
67: parent::__construct($options);
68:
69: // Adds service container and view service to template variables
70: $this->assign(array(
71: 'wei' => $this->wei,
72: 'view' => $this,
73: ));
74: }
75:
76: /**
77: * Render a PHP template
78: *
79: * @param string $name The name of template
80: * @param array $data The variables pass to template
81: *
82: * @return string
83: */
84: public function __invoke($name = null, $data = array())
85: {
86: return $this->render($name, $data);
87: }
88:
89: /**
90: * Render a template
91: *
92: * @param string $name The name of template
93: * @param array $data The variables pass to template
94: * @return string|null
95: */
96: public function render($name, $data = array())
97: {
98: // Set extra view variables
99: $data = $data ? $data + $this->data : $this->data;
100:
101: // Assign $name to $this->currentName to avoid conflict with view parameter
102: $this->currentName = $name;
103:
104: // Render view
105: extract($data, EXTR_OVERWRITE);
106: ob_start();
107: require $this->getFile($this->currentName);
108: $content = ob_get_clean();
109:
110: // Render layout
111: if ($this->layout) {
112: $layout = $this->layout;
113: $this->layout = array();
114: $content = $this->render($layout['name'], array(
115: $layout['variable'] => $content
116: ) + $data);
117: }
118:
119: return $content;
120: }
121:
122: /**
123: * Output a rendered template
124: *
125: * @param string $name The name of template
126: * @param array $data The variables pass to template
127: * @return void
128: */
129: public function display($name, $data = array())
130: {
131: echo $this->render($name, $data);
132: }
133:
134: /**
135: * Assign variables to template
136: *
137: * @param string $name The name of the variable
138: * @param mixed $value The value of the variable
139: * @return $this
140: */
141: public function assign($name, $value = null)
142: {
143: if (is_array($name)) {
144: $this->data = $name + $this->data;
145: } else {
146: $this->data[$name] = $value;
147: }
148: return $this;
149: }
150:
151: /**
152: * Returns the variable value or null if not defined
153: *
154: * @param string $name The name of variable
155: * @return mixed
156: */
157: public function get($name)
158: {
159: return isset($this->data[$name]) ? $this->data[$name] : null;
160: }
161:
162: /**
163: * Get the template file by name
164: *
165: * @param string $name The name of template
166: * @return string The template file path
167: * @throws \RuntimeException When template file not found
168: */
169: public function getFile($name)
170: {
171: foreach ($this->dirs as $dir) {
172: if (is_file($file = $dir . '/' . $name)) {
173: return $file;
174: }
175: }
176: throw new \RuntimeException(sprintf('Template "%s" not found in directories "%s"', $name, implode('", "', $this->dirs)), 404);
177: }
178:
179: /**
180: * Set layout file for current view
181: *
182: * @param string $name The name of layout template
183: * @param string $variable The variable name of layout content
184: * @return $this
185: */
186: public function layout($name = null, $variable = 'content')
187: {
188: $this->layout = array(
189: 'name' => $name ?: $this->defaultLayout,
190: 'variable' => $variable
191: );
192: return $this;
193: }
194:
195: /**
196: * Set the default layout parameter
197: *
198: * @param string $defaultLayout
199: * @return $this
200: */
201: public function setDefaultLayout($defaultLayout)
202: {
203: $this->defaultLayout = $defaultLayout;
204: return $this;
205: }
206:
207: /**
208: * Set base directory for views
209: *
210: * @param string|array $dirs
211: * @return $this
212: */
213: public function setDirs($dirs)
214: {
215: $this->dirs = (array)$dirs;
216: return $this;
217: }
218:
219: /**
220: * Get default template file extension, such as php, tpl, this is useful for
221: * automatic render template
222: *
223: * @return string
224: */
225: public function getExtension()
226: {
227: return $this->extension;
228: }
229:
230: /**
231: * Check if the offset exists
232: *
233: * @param string $offset
234: * @return bool
235: */
236: public function offsetExists($offset)
237: {
238: return array_key_exists($offset, $this->data);
239: }
240:
241: /**
242: * Get the offset value
243: *
244: * @param string $offset
245: * @return mixed
246: */
247: public function &offsetGet($offset)
248: {
249: if (isset($this->data[$offset])) {
250: return $this->data[$offset];
251: } else {
252: $ret = null;
253: return $ret;
254: }
255: }
256:
257: /**
258: * Set the offset value
259: *
260: * @param string $offset
261: * @param mixed $value
262: */
263: public function offsetSet($offset, $value)
264: {
265: $this->data[$offset] = $value;
266: }
267:
268: /**
269: * Unset the offset
270: *
271: * @param string $offset
272: */
273: public function offsetUnset($offset)
274: {
275: unset($this->data[$offset]);
276: }
277: }
278: