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