xref: /webtrees/app/View.php (revision 4a86d7149e8ef5bb77f9d427c9b91b695138e67b)
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 */
16namespace Fisharebest\Webtrees;
17
18use Exception;
19
20/**
21 * Simple view/template class.
22 */
23class View
24{
25    /**
26     * @var string The (file) name of the view.
27     */
28    private $name;
29
30    /**
31     * @var mixed[] Data to be inserted into the view.
32     */
33    private $data;
34
35    /**
36     * @var mixed[] Data to be inserted into all views.
37     */
38    private static $shared_data = [];
39
40    /**
41     * @var string Implementation of Blade "stacks".
42     */
43    private static $stack;
44
45    /**
46     * @var array[] Implementation of Blade "stacks".
47     */
48    private static $stacks = [];
49
50    /**
51     * Createa view from a template name and optional data.
52     *
53     * @param string $name
54     * @param array  $data
55     */
56    public function __construct(string $name, $data = [])
57    {
58        $this->name = $name;
59        $this->data = $data;
60    }
61
62    /**
63     * Shared data that is available to all views.
64     *
65     * @param string $key
66     * @param mixed  $value
67     *
68     * @return void
69     */
70    public static function share(string $key, $value)
71    {
72        self::$shared_data[$key] = $value;
73    }
74
75    /**
76     * Implementation of Blade "stacks".
77     *
78     * @see https://laravel.com/docs/5.5/blade#stacks
79     *
80     * @param string $stack
81     *
82     * @return void
83     */
84    public static function push(string $stack)
85    {
86        self::$stack = $stack;
87        ob_start();
88    }
89
90    /**
91     * Implementation of Blade "stacks".
92     *
93     * @return void
94     */
95    public static function endpush()
96    {
97        self::$stacks[self::$stack][] = ob_get_clean();
98    }
99
100    /**
101     * Implementation of Blade "stacks".
102     *
103     * @param string $stack
104     *
105     * @return string
106     */
107    public static function stack(string $stack): string
108    {
109        $content = implode('', self::$stacks[$stack] ?? []);
110
111        self::$stacks[$stack] = [];
112
113        return $content;
114    }
115
116    /**
117     * Render a view.
118     *
119     * @return string
120     */
121    public function render(): string
122    {
123        $variables_for_view = $this->data + self::$shared_data;
124        extract($variables_for_view);
125
126        ob_start();
127        // Do not use require, so we can catch errors for missing files
128        include $this->getFilenameForView($this->name);
129
130        return ob_get_clean();
131    }
132
133    /**
134     * Allow a theme to override the default views.
135     *
136     * @param string $view_name
137     *
138     * @return string
139     * @throws Exception
140     */
141    public function getFilenameForView($view_name): string
142    {
143        foreach ($this->paths() as $path) {
144            $view_file = $path . '/' . $view_name . '.php';
145
146            if (is_file($view_file)) {
147                return $view_file;
148            }
149        }
150
151        throw new Exception('View not found: ' . e($view_name));
152    }
153
154    /**
155     * Cerate and render a view in a single operation.
156     *
157     * @param string  $name
158     * @param mixed[] $data
159     *
160     * @return string
161     */
162    public static function make($name, $data = []): string
163    {
164        $view = new static($name, $data);
165
166        DebugBar::addView($name, $data);
167
168        return $view->render();
169    }
170
171    /**
172     * @return string[]
173     */
174    private function paths(): array
175    {
176        static $paths = [];
177
178        if (empty($paths)) {
179            // Module views
180            // @TODO - this includes disabled modules.
181            $paths = glob(WT_ROOT . WT_MODULES_DIR . '*/resources/views');
182            // Theme views
183            $paths[] = WT_ROOT . WT_THEMES_DIR . Theme::theme()->themeId() . '/resources/views';
184            // Core views
185            $paths[] = WT_ROOT . 'resources/views';
186
187            $paths = array_filter($paths, function (string $path): bool {
188                return is_dir($path);
189            });
190        }
191
192        return $paths;
193    }
194}
195