1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2018 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees; 19 20use Exception; 21 22/** 23 * Simple view/template class. 24 */ 25class View 26{ 27 /** 28 * @var string The (file) name of the view. 29 */ 30 private $name; 31 32 /** 33 * @var mixed[] Data to be inserted into the view. 34 */ 35 private $data; 36 37 /** 38 * @var mixed[] Data to be inserted into all views. 39 */ 40 private static $shared_data = []; 41 42 /** 43 * @var string Implementation of Blade "stacks". 44 */ 45 private static $stack; 46 47 /** 48 * @var array[] Implementation of Blade "stacks". 49 */ 50 private static $stacks = []; 51 52 /** 53 * Createa view from a template name and optional data. 54 * 55 * @param string $name 56 * @param array $data 57 */ 58 public function __construct(string $name, $data = []) 59 { 60 $this->name = $name; 61 $this->data = $data; 62 } 63 64 /** 65 * Shared data that is available to all views. 66 * 67 * @param string $key 68 * @param mixed $value 69 * 70 * @return void 71 */ 72 public static function share(string $key, $value) 73 { 74 self::$shared_data[$key] = $value; 75 } 76 77 /** 78 * Implementation of Blade "stacks". 79 * 80 * @see https://laravel.com/docs/5.5/blade#stacks 81 * 82 * @param string $stack 83 * 84 * @return void 85 */ 86 public static function push(string $stack) 87 { 88 self::$stack = $stack; 89 ob_start(); 90 } 91 92 /** 93 * Implementation of Blade "stacks". 94 * 95 * @return void 96 */ 97 public static function endpush() 98 { 99 self::$stacks[self::$stack][] = ob_get_clean(); 100 } 101 102 /** 103 * Implementation of Blade "stacks". 104 * 105 * @param string $stack 106 * 107 * @return string 108 */ 109 public static function stack(string $stack): string 110 { 111 $content = implode('', self::$stacks[$stack] ?? []); 112 113 self::$stacks[$stack] = []; 114 115 return $content; 116 } 117 118 /** 119 * Render a view. 120 * 121 * @return string 122 */ 123 public function render(): string 124 { 125 $variables_for_view = $this->data + self::$shared_data; 126 extract($variables_for_view); 127 128 ob_start(); 129 // Do not use require, so we can catch errors for missing files 130 include $this->getFilenameForView($this->name); 131 132 return ob_get_clean(); 133 } 134 135 /** 136 * Allow a theme to override the default views. 137 * 138 * @param string $view_name 139 * 140 * @return string 141 * @throws Exception 142 */ 143 public function getFilenameForView($view_name): string 144 { 145 foreach ($this->paths() as $path) { 146 $view_file = $path . '/' . $view_name . '.php'; 147 148 if (is_file($view_file)) { 149 return $view_file; 150 } 151 } 152 153 throw new Exception('View not found: ' . e($view_name)); 154 } 155 156 /** 157 * Cerate and render a view in a single operation. 158 * 159 * @param string $name 160 * @param mixed[] $data 161 * 162 * @return string 163 */ 164 public static function make($name, $data = []): string 165 { 166 $view = new static($name, $data); 167 168 DebugBar::addView($name, $data); 169 170 return $view->render(); 171 } 172 173 /** 174 * @return string[] 175 */ 176 private function paths(): array 177 { 178 static $paths = []; 179 180 if (empty($paths)) { 181 // Module views 182 // @TODO - this includes disabled modules. 183 $paths = glob(WT_ROOT . WT_MODULES_DIR . '*/resources/views'); 184 // Theme views 185 $paths[] = WT_ROOT . WT_THEMES_DIR . Theme::theme()->themeId() . '/resources/views'; 186 // Core views 187 $paths[] = WT_ROOT . 'resources/views'; 188 189 $paths = array_filter($paths, function (string $path): bool { 190 return is_dir($path); 191 }); 192 } 193 194 return $paths; 195 } 196} 197