Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.88% covered (success)
93.88%
46 / 49
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
PluginFactory
93.88% covered (success)
93.88%
46 / 49
66.67% covered (warning)
66.67%
2 / 3
16.06
0.00% covered (danger)
0.00%
0 / 1
 loadConfigFile
93.18% covered (success)
93.18%
41 / 44
0.00% covered (danger)
0.00%
0 / 1
14.06
 canCreate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * VuFind Config Plugin Factory
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
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  ServiceManager
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/wiki/development Wiki
28 */
29
30namespace VuFind\Config;
31
32use Laminas\Config\Config;
33use Laminas\ServiceManager\Factory\AbstractFactoryInterface;
34use Psr\Container\ContainerInterface;
35use VuFind\Config\Feature\IniReaderTrait;
36
37use function count;
38use function in_array;
39use function is_object;
40
41/**
42 * VuFind Config Plugin Factory
43 *
44 * @category VuFind
45 * @package  ServiceManager
46 * @author   Demian Katz <demian.katz@villanova.edu>
47 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
48 * @link     https://vufind.org/wiki/development Wiki
49 */
50class PluginFactory implements AbstractFactoryInterface
51{
52    use IniReaderTrait;
53
54    /**
55     * Load the specified configuration file.
56     *
57     * @param string $filename Config file name
58     *
59     * @return Config
60     */
61    protected function loadConfigFile(string $filename): Config
62    {
63        $configs = [];
64
65        // Return empty configuration if file does not exist:
66        if (!file_exists($filename)) {
67            return new Config([]);
68        }
69
70        // Retrieve and parse at least one configuration file, and possibly a whole
71        // chain of them if the Parent_Config setting is used:
72        do {
73            $configs[]
74                = new Config($this->getIniReader()->fromFile($filename), true);
75
76            $i = count($configs) - 1;
77            if (isset($configs[$i]->Parent_Config->path)) {
78                $filename = $configs[$i]->Parent_Config->path;
79            } elseif (isset($configs[$i]->Parent_Config->relative_path)) {
80                $filename = pathinfo($filename, PATHINFO_DIRNAME)
81                    . DIRECTORY_SEPARATOR
82                    . $configs[$i]->Parent_Config->relative_path;
83            } else {
84                $filename = false;
85            }
86        } while ($filename);
87
88        // The last element in the array will be the top of the inheritance tree.
89        // Let's establish a baseline:
90        $config = array_pop($configs);
91
92        // Now we'll pull all the children down one at a time and override settings
93        // as appropriate:
94        while (null !== ($child = array_pop($configs))) {
95            $overrideSections = isset($child->Parent_Config->override_full_sections)
96                ? explode(
97                    ',',
98                    str_replace(
99                        ' ',
100                        '',
101                        $child->Parent_Config->override_full_sections
102                    )
103                )
104                : [];
105            foreach ($child as $section => $contents) {
106                // Check if arrays in the current config file should be merged with
107                // preceding arrays from config files defined as Parent_Config.
108                $mergeArraySettings
109                    = !empty($child->Parent_Config->merge_array_settings);
110
111                // Omit Parent_Config from the returned configuration; it is only
112                // needed during loading, and its presence will cause problems in
113                // config files that iterate through all of the sections (e.g.
114                // combined.ini, permissions.ini).
115                if ($section === 'Parent_Config') {
116                    continue;
117                }
118                if (
119                    in_array($section, $overrideSections)
120                    || !isset($config->$section)
121                ) {
122                    $config->$section = $child->$section;
123                } else {
124                    foreach (array_keys($contents->toArray()) as $key) {
125                        // If a key is defined as key[] in the config file the key
126                        // remains a Laminas\Config\Config object. If the current
127                        // section is not configured as an override section we try to
128                        // merge the key[] values instead of overwriting them.
129                        if (
130                            is_object($config->$section->$key)
131                            && is_object($child->$section->$key)
132                            && $mergeArraySettings
133                        ) {
134                            $config->$section->$key = array_merge(
135                                $config->$section->$key->toArray(),
136                                $child->$section->$key->toArray()
137                            );
138                        } else {
139                            $config->$section->$key = $child->$section->$key;
140                        }
141                    }
142                }
143            }
144        }
145
146        $config->setReadOnly();
147        return $config;
148    }
149
150    /**
151     * Can we create a service for the specified name?
152     *
153     * @param ContainerInterface $container     Service container
154     * @param string             $requestedName Name of service
155     *
156     * @return bool
157     *
158     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
159     */
160    public function canCreate(ContainerInterface $container, $requestedName)
161    {
162        // Assume that configurations exist:
163        return true;
164    }
165
166    /**
167     * Create a service for the specified name.
168     *
169     * @param ContainerInterface $container     Service container
170     * @param string             $requestedName Name of service
171     * @param array              $options       Options (unused)
172     *
173     * @return object
174     *
175     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
176     */
177    public function __invoke(
178        ContainerInterface $container,
179        $requestedName,
180        array $options = null
181    ) {
182        $pathResolver = $container->get(PathResolver::class);
183        return $this->loadConfigFile(
184            $pathResolver->getConfigPath($requestedName . '.ini')
185        );
186    }
187}