xref: /webtrees/app/View.php (revision a45f98897789fc9ff88705eb09ae5f037bf49c10)
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()
122    {
123        extract($this->data + self::$shared_data);
124
125        ob_start();
126        // Do not use require, so we can catch errors for missing files
127        include $this->getFilenameForView($this->name);
128
129        return ob_get_clean();
130    }
131
132    /**
133     * Allow a theme to override the default views.
134     *
135     * @param string $view_name
136     *
137     * @return string
138     * @throws Exception
139     */
140    public function getFilenameForView($view_name)
141    {
142        foreach ($this->paths() as $path) {
143            $view_file = $path . '/' . $view_name . '.php';
144
145            if (is_file($view_file)) {
146                return $view_file;
147            }
148        }
149
150        throw new Exception('View not found: ' . e($view_name));
151    }
152
153    /**
154     * Cerate and render a view in a single operation.
155     *
156     * @param string  $name
157     * @param mixed[] $data
158     *
159     * @return string
160     */
161    public static function make($name, $data = [])
162    {
163        $view = new static($name, $data);
164
165        DebugBar::addView($name, $data);
166
167        return $view->render();
168    }
169
170    /**
171     * @return string[]
172     */
173    private function paths(): array
174    {
175        static $paths = [];
176
177        if (empty($paths)) {
178            // Module views
179            // @TODO - this includes disabled modules.
180            $paths = glob(WT_ROOT . WT_MODULES_DIR . '*/resources/views');
181            // Theme views
182            $paths[] = WT_ROOT . WT_THEMES_DIR . Theme::theme()->themeId() . '/resources/views';
183            // Core views
184            $paths[] = WT_ROOT . 'resources/views';
185
186            $paths = array_filter($paths, function (string $path): bool {
187                return is_dir($path);
188            });
189        }
190
191        return $paths;
192    }
193}
194