1: <?php
2: /**
3: *
4: *
5: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6: * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7: *
8: * Licensed under The MIT License
9: * For full copyright and license information, please see the LICENSE.txt
10: * Redistributions of files must retain the above copyright notice.
11: *
12: * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13: * @link http://cakephp.org CakePHP(tm) Project
14: * @since CakePHP(tm) v2.1
15: * @license http://www.opensource.org/licenses/mit-license.php MIT License
16: */
17:
18: /**
19: * ViewBlock implements the concept of Blocks or Slots in the View layer.
20: * Slots or blocks are combined with extending views and layouts to afford slots
21: * of content that are present in a layout or parent view, but are defined by the child
22: * view or elements used in the view.
23: *
24: * @package Cake.View
25: */
26: class ViewBlock {
27:
28: /**
29: * Append content
30: *
31: * @var string
32: */
33: const APPEND = 'append';
34:
35: /**
36: * Prepend content
37: *
38: * @var string
39: */
40: const PREPEND = 'prepend';
41:
42: /**
43: * Block content. An array of blocks indexed by name.
44: *
45: * @var array
46: */
47: protected $_blocks = array();
48:
49: /**
50: * The active blocks being captured.
51: *
52: * @var array
53: */
54: protected $_active = array();
55:
56: /**
57: * Should the currently captured content be discarded on ViewBlock::end()
58: *
59: * @var boolean
60: * @see ViewBlock::end()
61: * @see ViewBlock::startIfEmpty()
62: */
63: protected $_discardActiveBufferOnEnd = false;
64:
65: /**
66: * Start capturing output for a 'block'
67: *
68: * Blocks allow you to create slots or blocks of dynamic content in the layout.
69: * view files can implement some or all of a layout's slots.
70: *
71: * You can end capturing blocks using View::end(). Blocks can be output
72: * using View::get();
73: *
74: * @param string $name The name of the block to capture for.
75: * @throws CakeException When starting a block twice
76: * @return void
77: */
78: public function start($name) {
79: if (in_array($name, $this->_active)) {
80: throw new CakeException(__("A view block with the name '%s' is already/still open.", $name));
81: }
82: $this->_active[] = $name;
83: ob_start();
84: }
85:
86: /**
87: * Start capturing output for a 'block' if it is empty
88: *
89: * Blocks allow you to create slots or blocks of dynamic content in the layout.
90: * view files can implement some or all of a layout's slots.
91: *
92: * You can end capturing blocks using View::end(). Blocks can be output
93: * using View::get();
94: *
95: * @param string $name The name of the block to capture for.
96: * @return void
97: */
98: public function startIfEmpty($name) {
99: if (empty($this->_blocks[$name])) {
100: return $this->start($name);
101: }
102: $this->_discardActiveBufferOnEnd = true;
103: ob_start();
104: }
105:
106: /**
107: * End a capturing block. The compliment to ViewBlock::start()
108: *
109: * @return void
110: * @see ViewBlock::start()
111: */
112: public function end() {
113: if ($this->_discardActiveBufferOnEnd) {
114: $this->_discardActiveBufferOnEnd = false;
115: ob_end_clean();
116: return;
117: }
118: if (!empty($this->_active)) {
119: $active = end($this->_active);
120: $content = ob_get_clean();
121: if (!isset($this->_blocks[$active])) {
122: $this->_blocks[$active] = '';
123: }
124: $this->_blocks[$active] .= $content;
125: array_pop($this->_active);
126: }
127: }
128:
129: /**
130: * Concat content to an existing or new block.
131: * Concating to a new block will create the block.
132: *
133: * Calling concat() without a value will create a new capturing
134: * block that needs to be finished with View::end(). The content
135: * of the new capturing context will be added to the existing block context.
136: *
137: * @param string $name Name of the block
138: * @param mixed $value The content for the block
139: * @param string $mode If ViewBlock::APPEND content will be appended to existing content.
140: * If ViewBlock::PREPEND it will be prepended.
141: * @return void
142: */
143: public function concat($name, $value = null, $mode = ViewBlock::APPEND) {
144: if (isset($value)) {
145: if (!isset($this->_blocks[$name])) {
146: $this->_blocks[$name] = '';
147: }
148: if ($mode === ViewBlock::PREPEND) {
149: $this->_blocks[$name] = $value . $this->_blocks[$name];
150: } else {
151: $this->_blocks[$name] .= $value;
152: }
153: } else {
154: $this->start($name);
155: }
156: }
157:
158: /**
159: * Append to an existing or new block. Appending to a new
160: * block will create the block.
161: *
162: * Calling append() without a value will create a new capturing
163: * block that needs to be finished with View::end(). The content
164: * of the new capturing context will be added to the existing block context.
165: *
166: * @param string $name Name of the block
167: * @param string $value The content for the block.
168: * @return void
169: * @deprecated As of 2.3 use ViewBlock::concat() instead.
170: */
171: public function append($name, $value = null) {
172: $this->concat($name, $value);
173: }
174:
175: /**
176: * Set the content for a block. This will overwrite any
177: * existing content.
178: *
179: * @param string $name Name of the block
180: * @param mixed $value The content for the block.
181: * @return void
182: */
183: public function set($name, $value) {
184: $this->_blocks[$name] = (string)$value;
185: }
186:
187: /**
188: * Get the content for a block.
189: *
190: * @param string $name Name of the block
191: * @param string $default Default string
192: * @return string The block content or $default if the block does not exist.
193: */
194: public function get($name, $default = '') {
195: if (!isset($this->_blocks[$name])) {
196: return $default;
197: }
198: return $this->_blocks[$name];
199: }
200:
201: /**
202: * Get the names of all the existing blocks.
203: *
204: * @return array An array containing the blocks.
205: */
206: public function keys() {
207: return array_keys($this->_blocks);
208: }
209:
210: /**
211: * Get the name of the currently open block.
212: *
213: * @return mixed Either null or the name of the last open block.
214: */
215: public function active() {
216: return end($this->_active);
217: }
218:
219: /**
220: * Get the names of the unclosed/active blocks.
221: *
222: * @return array An array of unclosed blocks.
223: */
224: public function unclosed() {
225: return $this->_active;
226: }
227:
228: }
229: