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