xref: /webtrees/app/View.php (revision 67994fb087e1b24564a780e4ae8aeff801733e35)
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