Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.05% covered (warning)
78.05%
32 / 41
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
PathResolverFactory
78.05% covered (warning)
78.05%
32 / 41
0.00% covered (danger)
0.00%
0 / 1
11.06
0.00% covered (danger)
0.00%
0 / 1
 __invoke
78.05% covered (warning)
78.05%
32 / 41
0.00% covered (danger)
0.00%
0 / 1
11.06
1<?php
2
3/**
4 * Factory for PathResolver.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) The National Library of Finland 2022.
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  Config
25 * @author   Ere Maijala <ere.maijala@helsinki.fi>
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\Exception\ServiceNotCreatedException;
34use Laminas\ServiceManager\Exception\ServiceNotFoundException;
35use Laminas\ServiceManager\Factory\FactoryInterface;
36use Psr\Container\ContainerExceptionInterface as ContainerException;
37use Psr\Container\ContainerInterface;
38use VuFind\Config\Feature\IniReaderTrait;
39
40use function defined;
41use function in_array;
42use function strlen;
43
44/**
45 * Factory for PathResolver.
46 *
47 * @category VuFind
48 * @package  Config
49 * @author   Ere Maijala <ere.maijala@helsinki.fi>
50 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
51 * @link     https://vufind.org/wiki/development Wiki
52 */
53class PathResolverFactory implements FactoryInterface
54{
55    use IniReaderTrait;
56
57    /**
58     * Default base config file subdirectory under the base directory
59     *
60     * @var string
61     */
62    protected $defaultBaseConfigSubdir = PathResolver::DEFAULT_CONFIG_SUBDIR;
63
64    /**
65     * Default config file subdirectory under a local override directory
66     *
67     * @var string
68     */
69    protected $defaultLocalConfigSubdir = PathResolver::DEFAULT_CONFIG_SUBDIR;
70
71    /**
72     * Create an object
73     *
74     * @param ContainerInterface $container     Service manager
75     * @param string             $requestedName Service being created
76     * @param null|array         $options       Extra options (optional)
77     *
78     * @return object
79     *
80     * @throws ServiceNotFoundException if unable to resolve the service.
81     * @throws ServiceNotCreatedException if an exception is raised when
82     * creating a service.
83     * @throws ContainerException&\Throwable if any other error occurs
84     */
85    public function __invoke(
86        ContainerInterface $container,
87        $requestedName,
88        array $options = null
89    ) {
90        if (!empty($options)) {
91            throw new \Exception('Unexpected options sent to factory.');
92        }
93        $localDirs = [];
94        $currentDir = defined('LOCAL_OVERRIDE_DIR')
95            && strlen(trim(LOCAL_OVERRIDE_DIR)) > 0
96            ? LOCAL_OVERRIDE_DIR : '';
97        while (!empty($currentDir)) {
98            // check if the directory exists
99            if (!($canonicalizedCurrentDir = realpath($currentDir))) {
100                trigger_error('Configured local directory does not exist: ' . $currentDir, E_USER_WARNING);
101                break;
102            }
103            $currentDir = $canonicalizedCurrentDir;
104
105            // check if the current directory was already included in the stack to avoid infinite loops
106            if (in_array($currentDir, array_column($localDirs, 'directory'))) {
107                trigger_error('Current directory was already included in the stack: ' . $currentDir, E_USER_WARNING);
108                break;
109            }
110
111            // loading DirLocations.ini of currentDir
112            $systemConfigFile = $currentDir . '/DirLocations.ini';
113            $systemConfig = new Config(
114                file_exists($systemConfigFile)
115                    ? $this->getIniReader()->fromFile($systemConfigFile)
116                    : []
117            );
118
119            // adding directory to the stack
120            array_unshift(
121                $localDirs,
122                [
123                    'directory' => $currentDir,
124                    'defaultConfigSubdir' =>
125                        $systemConfig['Local_Dir']['config_subdir']
126                        ?? $this->defaultLocalConfigSubdir,
127                ]
128            );
129
130            // If there's a parent, set it as the current directory for the next loop iteration:
131            if (!empty($systemConfig['Parent_Dir']['path'])) {
132                $isRelative = $systemConfig['Parent_Dir']['is_relative_path'] ?? false;
133                $parentDir = $systemConfig['Parent_Dir']['path'];
134                $currentDir = $isRelative ? $currentDir . '/' . $parentDir : $parentDir;
135            } else {
136                $currentDir = '';
137            }
138        }
139        return new $requestedName(
140            [
141                'directory' => APPLICATION_PATH,
142                'defaultConfigSubdir' => $this->defaultBaseConfigSubdir,
143            ],
144            $localDirs
145        );
146    }
147}