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