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