Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
ClassBasedTemplateRendererTrait
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
5 / 5
11
100.00% covered (success)
100.00%
1 / 1
 resolveClassTemplate
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 renderClassTemplate
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 getCachedClassTemplate
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 getBriefClass
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTemplateWithClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Trait for view helpers that render a template based on a class name.
5 *
6 * Note: This trait is for view helpers only. It expects $this->getView() method to
7 * be available.
8 *
9 * PHP version 8
10 *
11 * Copyright (C) Villanova University 2018.
12 * Copyright (C) The National Library of Finland 2020.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2,
16 * as published by the Free Software Foundation.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26 *
27 * @category VuFind
28 * @package  View_Helpers
29 * @author   Demian Katz <demian.katz@villanova.edu>
30 * @author   Ere Maijala <ere.maijala@helsinki.fi>
31 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
32 * @link     https://vufind.org/wiki/development Wiki
33 */
34
35namespace VuFind\View\Helper\Root;
36
37use Laminas\View\Exception\RuntimeException;
38use Laminas\View\Resolver\ResolverInterface;
39
40/**
41 * Trait for view helpers that render a template based on a class name.
42 *
43 * @category VuFind
44 * @package  View_Helpers
45 * @author   Demian Katz <demian.katz@villanova.edu>
46 * @author   Ere Maijala <ere.maijala@helsinki.fi>
47 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
48 * @link     https://vufind.org/wiki/development Wiki
49 */
50trait ClassBasedTemplateRendererTrait
51{
52    /**
53     * Cache for found templates
54     *
55     * @var array
56     */
57    protected $templateCache = [];
58
59    /**
60     * Recursively locate a template that matches the provided class name
61     * (or one of its parent classes); throw an exception if no match is found.
62     *
63     * @param string            $template     Template path (with %s as class name
64     * placeholder)
65     * @param string            $className    Name of class to apply to template.
66     * @param ResolverInterface $resolver     Resolver to use
67     * @param string            $topClassName Top-level parent class of $className
68     * (or null if $className is already the top level; used for recursion only).
69     *
70     * @return string
71     */
72    protected function resolveClassTemplate(
73        $template,
74        $className,
75        ResolverInterface $resolver,
76        $topClassName = null
77    ) {
78        // If the template resolves, return it:
79        $templateWithClass = $this->getTemplateWithClass($template, $className);
80        if ($resolver->resolve($templateWithClass)) {
81            return $templateWithClass;
82        }
83
84        // If the template doesn't resolve, let's see if we can inherit a
85        // template from a parent class:
86        $parentClass = get_parent_class($className);
87        if (empty($parentClass)) {
88            return '';
89        }
90
91        // Recurse until we find a template or run out of parents...
92        return $this->resolveClassTemplate(
93            $template,
94            $parentClass,
95            $resolver,
96            $topClassName ?? $className
97        );
98    }
99
100    /**
101     * Render a template associated with the provided class name, applying to
102     * specified context variables.
103     *
104     * @param string $template  Template path (with %s as class name placeholder)
105     * @param string $className Name of class to apply to template.
106     * @param array  $context   Context for rendering template
107     * @param bool   $throw     If true (default), an exception is thrown if the
108     * template is not found. Otherwise an empty string is returned.
109     *
110     * @return string
111     * @throws RuntimeException
112     */
113    protected function renderClassTemplate(
114        $template,
115        $className,
116        $context = [],
117        $throw = true
118    ) {
119        // Set up the needed context in the view:
120        $view = $this->getView();
121        $contextHelper = $view->plugin('context');
122        $oldContext = $contextHelper($view)->apply($context);
123
124        // Find and render the template:
125        $classTemplate = $this->getCachedClassTemplate($template, $className);
126        if (!$classTemplate && $throw) {
127            throw new RuntimeException(
128                'Cannot find '
129                . $this->getTemplateWithClass($template, '[brief class name]')
130                . " for class $className or any of its parent classes"
131            );
132        }
133
134        $html = $classTemplate ? $view->render($classTemplate) : '';
135
136        // Restore the original context before returning the result:
137        $contextHelper($view)->restore($oldContext);
138        return $html;
139    }
140
141    /**
142     * Resolve the class template file unless already cached and return the file
143     * name.
144     *
145     * @param string $template  Template path (with %s as class name placeholder)
146     * @param string $className Name of class to apply to template.
147     *
148     * @return string
149     */
150    protected function getCachedClassTemplate($template, $className)
151    {
152        if (!isset($this->templateCache[$className][$template])) {
153            $this->templateCache[$className][$template]
154                = $this->resolveClassTemplate(
155                    $template,
156                    $className,
157                    $this->getView()->resolver()
158                );
159        }
160        return $this->templateCache[$className][$template];
161    }
162
163    /**
164     * Helper to grab the end of the class name
165     *
166     * @param string $className Class name to abbreviate
167     *
168     * @return string
169     */
170    protected function getBriefClass($className)
171    {
172        $classParts = explode('\\', $className);
173        return array_pop($classParts);
174    }
175
176    /**
177     * Helper to put the template path and class name together
178     *
179     * @param string $template  Template path (with %s as class name placeholder)
180     * @param string $className Class name to abbreviate
181     *
182     * @return string
183     */
184    protected function getTemplateWithClass(
185        string $template,
186        string $className
187    ): string {
188        return sprintf($template, $this->getBriefClass($className));
189    }
190}