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 3.1.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\View;
16:
17: use Cake\Core\App;
18: use Cake\Event\EventManager;
19: use Cake\Http\Response;
20: use Cake\Http\ServerRequest;
21: use Cake\View\Exception\MissingViewException;
22: use JsonSerializable;
23: use Serializable;
24:
25: /**
26: * Provides an API for iteratively building a view up.
27: *
28: * Once you have configured the view and established all the context
29: * you can create a view instance with `build()`.
30: */
31: class ViewBuilder implements JsonSerializable, Serializable
32: {
33: /**
34: * The subdirectory to the template.
35: *
36: * @var string|null
37: */
38: protected $_templatePath;
39:
40: /**
41: * The template file to render.
42: *
43: * @var string|null
44: */
45: protected $_template;
46:
47: /**
48: * The plugin name to use.
49: *
50: * @var string|false|null
51: */
52: protected $_plugin;
53:
54: /**
55: * The theme name to use.
56: *
57: * @var string|false|null
58: */
59: protected $_theme;
60:
61: /**
62: * The layout name to render.
63: *
64: * @var string|false|null
65: */
66: protected $_layout;
67:
68: /**
69: * Whether or not autoLayout should be enabled.
70: *
71: * @var bool|null
72: */
73: protected $_autoLayout;
74:
75: /**
76: * The layout path to build the view with.
77: *
78: * @var string|null
79: */
80: protected $_layoutPath;
81:
82: /**
83: * The view variables to use
84: *
85: * @var string|null
86: */
87: protected $_name;
88:
89: /**
90: * The view class name to use.
91: * Can either use plugin notation, a short name
92: * or a fully namespaced classname.
93: *
94: * @var string|null
95: */
96: protected $_className;
97:
98: /**
99: * Additional options used when constructing the view.
100: *
101: * This options array lets you provide custom constructor
102: * arguments to application/plugin view classes.
103: *
104: * @var array
105: */
106: protected $_options = [];
107:
108: /**
109: * The helpers to use
110: *
111: * @var array
112: */
113: protected $_helpers = [];
114:
115: /**
116: * View vars
117: *
118: * @var array
119: */
120: protected $_vars = [];
121:
122: /**
123: * Saves a variable for use inside a template.
124: *
125: * @param string $name A string or an array of data.
126: * @param mixed $value Value.
127: * @return $this
128: */
129: public function setVar($name, $value = null)
130: {
131: $this->_vars[$name] = $value;
132:
133: return $this;
134: }
135:
136: /**
137: * Saves view vars for use inside templates.
138: *
139: * @param array $data Array of data.
140: * @param bool $merge Whether to merge with existing vars, default true.
141: * @return $this
142: */
143: public function setVars($data, $merge = true)
144: {
145: if ($merge) {
146: $this->_vars = $data + $this->_vars;
147: } else {
148: $this->_vars = $data;
149: }
150:
151: return $this;
152: }
153:
154: /**
155: * Check if view var is set.
156: *
157: * @param string $name Var name
158: * @return bool
159: */
160: public function hasVar($name)
161: {
162: return array_key_exists($name, $this->_vars);
163: }
164:
165: /**
166: * Get view var
167: *
168: * @param string $name Var name
169: * @return mixed The var value or null if unset.
170: */
171: public function getVar($name)
172: {
173: return isset($this->_vars[$name]) ? $this->_vars[$name] : null;
174: }
175:
176: /**
177: * Get all view vars.
178: *
179: * @return array
180: */
181: public function getVars()
182: {
183: return $this->_vars;
184: }
185:
186: /**
187: * Sets path for template files.
188: *
189: * @param string|null $path Path for view files.
190: * @return $this
191: */
192: public function setTemplatePath($path)
193: {
194: $this->_templatePath = $path;
195:
196: return $this;
197: }
198:
199: /**
200: * Gets path for template files.
201: *
202: * @return string|null
203: */
204: public function getTemplatePath()
205: {
206: return $this->_templatePath;
207: }
208:
209: /**
210: * Get/set path for template files.
211: *
212: * @deprecated 3.4.0 Use setTemplatePath()/getTemplatePath() instead.
213: * @param string|null $path Path for view files. If null returns current path.
214: * @return string|$this
215: */
216: public function templatePath($path = null)
217: {
218: deprecationWarning('ViewBuilder::templatePath() is deprecated. Use ViewBuilder::setTemplatePath() or ViewBuilder::getTemplatePath() instead.');
219: if ($path !== null) {
220: return $this->setTemplatePath($path);
221: }
222:
223: return $this->getTemplatePath();
224: }
225:
226: /**
227: * Sets path for layout files.
228: *
229: * @param string|null $path Path for layout files.
230: * @return $this
231: */
232: public function setLayoutPath($path)
233: {
234: $this->_layoutPath = $path;
235:
236: return $this;
237: }
238:
239: /**
240: * Gets path for layout files.
241: *
242: * @return string|null
243: */
244: public function getLayoutPath()
245: {
246: return $this->_layoutPath;
247: }
248:
249: /**
250: * Get/set path for layout files.
251: *
252: * @deprecated 3.4.0 Use setLayoutPath()/getLayoutPath() instead.
253: * @param string|null $path Path for layout files. If null returns current path.
254: * @return string|$this
255: */
256: public function layoutPath($path = null)
257: {
258: deprecationWarning('ViewBuilder::layoutPath() is deprecated. Use ViewBuilder::setLayoutPath() or ViewBuilder::getLayoutPath() instead.');
259: if ($path !== null) {
260: return $this->setLayoutPath($path);
261: }
262:
263: return $this->getLayoutPath();
264: }
265:
266: /**
267: * Turns on or off CakePHP's conventional mode of applying layout files.
268: * On by default. Setting to off means that layouts will not be
269: * automatically applied to rendered views.
270: *
271: * @param bool $enable Boolean to turn on/off.
272: * @return $this
273: */
274: public function enableAutoLayout($enable = true)
275: {
276: $this->_autoLayout = (bool)$enable;
277:
278: return $this;
279: }
280:
281: /**
282: * Turns off CakePHP's conventional mode of applying layout files.
283: *
284: * Setting to off means that layouts will not be automatically applied to
285: * rendered views.
286: *
287: * @return $this
288: */
289: public function disableAutoLayout()
290: {
291: $this->_autoLayout = false;
292:
293: return $this;
294: }
295:
296: /**
297: * Returns if CakePHP's conventional mode of applying layout files is enabled.
298: * Disabled means that layouts will not be automatically applied to rendered views.
299: *
300: * @return bool|null
301: */
302: public function isAutoLayoutEnabled()
303: {
304: return $this->_autoLayout;
305: }
306:
307: /**
308: * Turns on or off CakePHP's conventional mode of applying layout files.
309: * On by default. Setting to off means that layouts will not be
310: * automatically applied to rendered views.
311: *
312: * @deprecated 3.4.0 Use enableAutoLayout()/isAutoLayoutEnabled() instead.
313: * @param bool|null $enable Boolean to turn on/off. If null returns current value.
314: * @return bool|$this
315: */
316: public function autoLayout($enable = null)
317: {
318: deprecationWarning('ViewBuilder::autoLayout() is deprecated. Use ViewBuilder::enableAutoLayout() or ViewBuilder::isAutoLayoutEnable() instead.');
319: if ($enable !== null) {
320: return $this->enableAutoLayout($enable);
321: }
322:
323: return $this->isAutoLayoutEnabled();
324: }
325:
326: /**
327: * Sets the plugin name to use.
328: *
329: * `False` to remove current plugin name is deprecated as of 3.4.0. Use directly `null` instead.
330: *
331: * @param string|false|null $name Plugin name.
332: * Use null or false to remove the current plugin name.
333: * @return $this
334: */
335: public function setPlugin($name)
336: {
337: $this->_plugin = $name;
338:
339: return $this;
340: }
341:
342: /**
343: * Gets the plugin name to use.
344: *
345: * @return string|false|null
346: */
347: public function getPlugin()
348: {
349: return $this->_plugin;
350: }
351:
352: /**
353: * The plugin name to use
354: *
355: * @deprecated 3.4.0 Use setPlugin()/getPlugin() instead.
356: * @param string|false|null $name Plugin name. If null returns current plugin.
357: * Use false to remove the current plugin name.
358: * @return string|false|null|$this
359: */
360: public function plugin($name = null)
361: {
362: deprecationWarning('ViewBuilder::plugin() is deprecated. Use ViewBuilder::setPlugin() or ViewBuilder::getPlugin() instead.');
363: if ($name !== null) {
364: return $this->setPlugin($name);
365: }
366:
367: return $this->getPlugin();
368: }
369:
370: /**
371: * Sets the helpers to use.
372: *
373: * @param array $helpers Helpers to use.
374: * @param bool $merge Whether or not to merge existing data with the new data.
375: * @return $this
376: */
377: public function setHelpers(array $helpers, $merge = true)
378: {
379: if ($merge) {
380: $helpers = array_merge($this->_helpers, $helpers);
381: }
382: $this->_helpers = $helpers;
383:
384: return $this;
385: }
386:
387: /**
388: * Gets the helpers to use.
389: *
390: * @return array
391: */
392: public function getHelpers()
393: {
394: return $this->_helpers;
395: }
396:
397: /**
398: * The helpers to use
399: *
400: * @deprecated 3.4.0 Use setHelpers()/getHelpers() instead.
401: * @param array|null $helpers Helpers to use.
402: * @param bool $merge Whether or not to merge existing data with the new data.
403: * @return array|$this
404: */
405: public function helpers(array $helpers = null, $merge = true)
406: {
407: deprecationWarning('ViewBuilder::helpers() is deprecated. Use ViewBuilder::setHelpers() or ViewBuilder::getHelpers() instead.');
408: if ($helpers !== null) {
409: return $this->setHelpers($helpers, $merge);
410: }
411:
412: return $this->getHelpers();
413: }
414:
415: /**
416: * Sets the view theme to use.
417: *
418: * `False` to remove current theme is deprecated as of 3.4.0. Use directly `null` instead.
419: *
420: * @param string|false|null $theme Theme name.
421: * Use null or false to remove the current theme.
422: * @return $this
423: */
424: public function setTheme($theme)
425: {
426: $this->_theme = $theme;
427:
428: return $this;
429: }
430:
431: /**
432: * Gets the view theme to use.
433: *
434: * @return string|false|null
435: */
436: public function getTheme()
437: {
438: return $this->_theme;
439: }
440:
441: /**
442: * The view theme to use.
443: *
444: * @deprecated 3.4.0 Use setTheme()/getTheme() instead.
445: * @param string|false|null $theme Theme name. If null returns current theme.
446: * Use false to remove the current theme.
447: * @return string|false|null|$this
448: */
449: public function theme($theme = null)
450: {
451: deprecationWarning('ViewBuilder::theme() is deprecated. Use ViewBuilder::setTheme() or ViewBuilder::getTheme() instead.');
452: if ($theme !== null) {
453: return $this->setTheme($theme);
454: }
455:
456: return $this->getTheme();
457: }
458:
459: /**
460: * Sets the name of the view file to render. The name specified is the
461: * filename in /src/Template/<SubFolder> without the .ctp extension.
462: *
463: * @param string|null $name View file name to set.
464: * @return $this
465: */
466: public function setTemplate($name)
467: {
468: $this->_template = $name;
469:
470: return $this;
471: }
472:
473: /**
474: * Gets the name of the view file to render. The name specified is the
475: * filename in /src/Template/<SubFolder> without the .ctp extension.
476: *
477: * @return string|null
478: */
479: public function getTemplate()
480: {
481: return $this->_template;
482: }
483:
484: /**
485: * Get/set the name of the view file to render. The name specified is the
486: * filename in /src/Template/<SubFolder> without the .ctp extension.
487: *
488: * @deprecated 3.4.0 Use setTemplate()/getTemplate()
489: * @param string|null $name View file name to set. If null returns current name.
490: * @return string|$this
491: */
492: public function template($name = null)
493: {
494: deprecationWarning('ViewBuilder::template() is deprecated. Use ViewBuilder::setTemplate() or ViewBuilder::getTemplate() instead.');
495: if ($name !== null) {
496: return $this->setTemplate($name);
497: }
498:
499: return $this->getTemplate();
500: }
501:
502: /**
503: * Sets the name of the layout file to render the view inside of.
504: * The name specified is the filename of the layout in /src/Template/Layout
505: * without the .ctp extension.
506: *
507: * @param string|false|null $name Layout file name to set.
508: * @return $this
509: */
510: public function setLayout($name)
511: {
512: $this->_layout = $name;
513:
514: return $this;
515: }
516:
517: /**
518: * Gets the name of the layout file to render the view inside of.
519: *
520: * @return string|false|null
521: */
522: public function getLayout()
523: {
524: return $this->_layout;
525: }
526:
527: /**
528: * Get/set the name of the layout file to render the view inside of.
529: * The name specified is the filename of the layout in /src/Template/Layout
530: * without the .ctp extension.
531: *
532: * @deprecated 3.4.0 Use setLayout()/getLayout() instead.
533: * @param string|null $name Layout file name to set. If null returns current name.
534: * @return string|$this
535: */
536: public function layout($name = null)
537: {
538: deprecationWarning('ViewBuilder::layout() is deprecated. Use ViewBuilder::setLayout() or ViewBuilder::getLayout() instead.');
539: if ($name !== null) {
540: return $this->setLayout($name);
541: }
542:
543: return $this->getLayout();
544: }
545:
546: /**
547: * Sets additional options for the view.
548: *
549: * This lets you provide custom constructor arguments to application/plugin view classes.
550: *
551: * @param array $options An array of options.
552: * @param bool $merge Whether or not to merge existing data with the new data.
553: * @return $this
554: */
555: public function setOptions(array $options, $merge = true)
556: {
557: if ($merge) {
558: $options = array_merge($this->_options, $options);
559: }
560: $this->_options = $options;
561:
562: return $this;
563: }
564:
565: /**
566: * Gets additional options for the view.
567: *
568: * @return array
569: */
570: public function getOptions()
571: {
572: return $this->_options;
573: }
574:
575: /**
576: * Set additional options for the view.
577: *
578: * This lets you provide custom constructor arguments to application/plugin view classes.
579: *
580: * @deprecated 3.4.0 Use setOptions()/getOptions() instead.
581: * @param array|null $options Either an array of options or null to get current options.
582: * @param bool $merge Whether or not to merge existing data with the new data.
583: * @return array|$this
584: */
585: public function options(array $options = null, $merge = true)
586: {
587: deprecationWarning('ViewBuilder::options() is deprecated. Use ViewBuilder::setOptions() or ViewBuilder::getOptions() instead.');
588: if ($options !== null) {
589: return $this->setOptions($options, $merge);
590: }
591:
592: return $this->getOptions();
593: }
594:
595: /**
596: * Sets the view name.
597: *
598: * @param string|null $name The name of the view.
599: * @return $this
600: */
601: public function setName($name)
602: {
603: $this->_name = $name;
604:
605: return $this;
606: }
607:
608: /**
609: * Gets the view name.
610: *
611: * @return string|null
612: */
613: public function getName()
614: {
615: return $this->_name;
616: }
617:
618: /**
619: * Get/set the view name
620: *
621: * @deprecated 3.4.0 Use setName()/getName() instead.
622: * @param string|null $name The name of the view
623: * @return string|$this
624: */
625: public function name($name = null)
626: {
627: deprecationWarning('ViewBuilder::name() is deprecated. Use ViewBuilder::setName() or ViewBuilder::getName() instead.');
628: if ($name !== null) {
629: return $this->setName($name);
630: }
631:
632: return $this->getName();
633: }
634:
635: /**
636: * Sets the view classname.
637: *
638: * Accepts either a short name (Ajax) a plugin name (MyPlugin.Ajax)
639: * or a fully namespaced name (App\View\AppView) or null to use the
640: * View class provided by CakePHP.
641: *
642: * @param string|null $name The class name for the view.
643: * @return $this
644: */
645: public function setClassName($name)
646: {
647: $this->_className = $name;
648:
649: return $this;
650: }
651:
652: /**
653: * Gets the view classname.
654: *
655: * @return string|null
656: */
657: public function getClassName()
658: {
659: return $this->_className;
660: }
661:
662: /**
663: * Get/set the view classname.
664: *
665: * Accepts either a short name (Ajax) a plugin name (MyPlugin.Ajax)
666: * or a fully namespaced name (App\View\AppView).
667: *
668: * @deprecated 3.4.0 Use setClassName()/getClassName() instead.
669: * @param string|null $name The class name for the view. Can
670: * be a plugin.class name reference, a short alias, or a fully
671: * namespaced name.
672: * @return string|$this
673: */
674: public function className($name = null)
675: {
676: deprecationWarning('ViewBuilder::className() is deprecated. Use ViewBuilder::setClassName() or ViewBuilder::getClassName() instead.');
677: if ($name !== null) {
678: return $this->setClassName($name);
679: }
680:
681: return $this->getClassName();
682: }
683:
684: /**
685: * Using the data in the builder, create a view instance.
686: *
687: * If className() is null, App\View\AppView will be used.
688: * If that class does not exist, then Cake\View\View will be used.
689: *
690: * @param array $vars The view variables/context to use.
691: * @param \Cake\Http\ServerRequest|null $request The request to use.
692: * @param \Cake\Http\Response|null $response The response to use.
693: * @param \Cake\Event\EventManager|null $events The event manager to use.
694: * @return \Cake\View\View
695: * @throws \Cake\View\Exception\MissingViewException
696: */
697: public function build($vars = [], ServerRequest $request = null, Response $response = null, EventManager $events = null)
698: {
699: $className = $this->_className;
700: if ($className === null) {
701: $className = App::className('App', 'View', 'View') ?: 'Cake\View\View';
702: }
703: if ($className === 'View') {
704: $className = App::className($className, 'View');
705: } else {
706: $className = App::className($className, 'View', 'View');
707: }
708: if (!$className) {
709: throw new MissingViewException(['class' => $this->_className]);
710: }
711:
712: $data = [
713: 'name' => $this->_name,
714: 'templatePath' => $this->_templatePath,
715: 'template' => $this->_template,
716: 'plugin' => $this->_plugin,
717: 'theme' => $this->_theme,
718: 'layout' => $this->_layout,
719: 'autoLayout' => $this->_autoLayout,
720: 'layoutPath' => $this->_layoutPath,
721: 'helpers' => $this->_helpers,
722: 'viewVars' => $vars + $this->_vars,
723: ];
724: $data += $this->_options;
725:
726: return new $className($request, $response, $events, $data);
727: }
728:
729: /**
730: * Serializes the view builder object to a value that can be natively
731: * serialized and re-used to clone this builder instance.
732: *
733: * @return array Serializable array of configuration properties.
734: */
735: public function jsonSerialize()
736: {
737: $properties = [
738: '_templatePath', '_template', '_plugin', '_theme', '_layout', '_autoLayout',
739: '_layoutPath', '_name', '_className', '_options', '_helpers'
740: ];
741:
742: $array = [];
743:
744: foreach ($properties as $property) {
745: $array[$property] = $this->{$property};
746: }
747:
748: return array_filter($array, function ($i) {
749: return !is_array($i) && strlen($i) || !empty($i);
750: });
751: }
752:
753: /**
754: * Configures a view builder instance from serialized config.
755: *
756: * @param array $config View builder configuration array.
757: * @return $this Configured view builder instance.
758: */
759: public function createFromArray($config)
760: {
761: foreach ($config as $property => $value) {
762: $this->{$property} = $value;
763: }
764:
765: return $this;
766: }
767:
768: /**
769: * Serializes the view builder object.
770: *
771: * @return string
772: */
773: public function serialize()
774: {
775: $array = $this->jsonSerialize();
776:
777: return serialize($array);
778: }
779:
780: /**
781: * Unserializes the view builder object.
782: *
783: * @param string $data Serialized string.
784: * @return $this Configured view builder instance.
785: */
786: public function unserialize($data)
787: {
788: return $this->createFromArray(unserialize($data));
789: }
790: }
791: