Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.71% covered (warning)
85.71%
36 / 42
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ThemeCompiler
85.71% covered (warning)
85.71%
36 / 42
66.67% covered (warning)
66.67%
2 / 3
18.94
0.00% covered (danger)
0.00%
0 / 1
 compile
76.00% covered (warning)
76.00%
19 / 25
0.00% covered (danger)
0.00%
0 / 1
10.12
 removeTheme
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 mergeConfig
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3/**
4 * Class to compile a theme hierarchy into a single flat theme.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2017.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Theme
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org Main Site
28 */
29
30namespace VuFindTheme;
31
32use function is_array;
33
34/**
35 * Class to compile a theme hierarchy into a single flat theme.
36 *
37 * @category VuFind
38 * @package  Theme
39 * @author   Demian Katz <demian.katz@villanova.edu>
40 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
41 * @link     https://vufind.org Main Site
42 */
43class ThemeCompiler extends AbstractThemeUtility
44{
45    /**
46     * Compile from $source theme into $target theme.
47     *
48     * @param string $source         Name of source theme
49     * @param string $target         Name of target theme
50     * @param bool   $forceOverwrite Should we overwrite the target if it exists?
51     *
52     * @return bool
53     */
54    public function compile($source, $target, $forceOverwrite = false)
55    {
56        // Validate input:
57        try {
58            $this->info->setTheme($source);
59        } catch (\Exception $ex) {
60            return $this->setLastError($ex->getMessage());
61        }
62        // Validate output:
63        $baseDir = $this->info->getBaseDir();
64        $targetDir = "$baseDir/$target";
65        if (file_exists($targetDir)) {
66            if (!$forceOverwrite) {
67                return $this->setLastError(
68                    'Cannot overwrite ' . $targetDir . ' without --force switch!'
69                );
70            }
71            if (!$this->deleteDir($targetDir)) {
72                return false;
73            }
74        }
75        if (!mkdir($targetDir)) {
76            return $this->setLastError("Cannot create $targetDir");
77        }
78
79        // Copy all the files, relying on the fact that the output of getThemeInfo
80        // includes the entire theme inheritance chain in the appropriate order:
81        $info = $this->info->getThemeInfo();
82        $config = [];
83        foreach ($info as $source => $currentConfig) {
84            $config = $this->mergeConfig($currentConfig, $config);
85            if (!$this->copyDir("$baseDir/$source", $targetDir)) {
86                return false;
87            }
88        }
89        $configFile = "$targetDir/theme.config.php";
90        $configContents = '<?php return ' . var_export($config, true) . ';';
91        if (!file_put_contents($configFile, $configContents)) {
92            return $this->setLastError("Problem exporting $configFile.");
93        }
94        return true;
95    }
96
97    /**
98     * Remove a theme directory (used for cleanup in testing).
99     *
100     * @param string $theme Name of theme to remove.
101     *
102     * @return bool
103     */
104    public function removeTheme($theme)
105    {
106        return $this->deleteDir($this->info->getBaseDir() . '/' . $theme);
107    }
108
109    /**
110     * Merge configurations from $src into $dest; return the result.
111     *
112     * @param array $src  Source configuration
113     * @param array $dest Destination configuration
114     *
115     * @return array
116     */
117    protected function mergeConfig($src, $dest)
118    {
119        foreach ($src as $key => $value) {
120            switch ($key) {
121                case 'extends':
122                    // always set "extends" to false; we're flattening, after all!
123                    $dest[$key] = false;
124                    break;
125                case 'helpers':
126                    // Call this function recursively to deal with the helpers
127                    // sub-array:
128                    $dest[$key] = $this
129                        ->mergeConfig($value, $dest[$key] ?? []);
130                    break;
131                case 'mixins':
132                    // Omit mixin settings entirely
133                    break;
134                default:
135                    // Default behavior: merge arrays, let existing flat settings
136                    // trump new incoming ones:
137                    if (!isset($dest[$key])) {
138                        $dest[$key] = $value;
139                    } elseif (is_array($dest[$key])) {
140                        $dest[$key] = array_merge($value, $dest[$key]);
141                    }
142                    break;
143            }
144        }
145        return $dest;
146    }
147}