Skip to content

Commit

Permalink
Inheritance of local dirs for configuration files (#2947)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThoWagen committed Jul 18, 2023
1 parent ebcd81c commit f29c765
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -21,6 +21,7 @@ lessphp_*.list
module/VuFind/tests/.phpunit.result.cache
node_modules
public/swagger-ui
local/DirLocations.ini
local/config/vufind/*.ini
local/config/vufind/*.json
local/config/vufind/*.yaml
13 changes: 13 additions & 0 deletions local/DirLocations.ini.dist
@@ -0,0 +1,13 @@
; A parent local directory can be specified. When resolving paths to configuration
; files this dir will also be searched if the file does not exist in the current dir.
[Parent_Dir]
; Path to the parent directory
;path =
; Whether the path is relative to this file (true) or absolute (false).
; Defaults to false.
;is_relative_path =

; Configurations for this local directory
[Local_Dir]
; A custom config subdir can be configured. The default is config/vufind.
;config_subdir =
68 changes: 68 additions & 0 deletions module/VuFind/src/VuFind/Config/Feature/IniReaderTrait.php
@@ -0,0 +1,68 @@
<?php

/**
* Trait for creating INI readers
*
* PHP version 8
*
* Copyright (C) Hebis Verbundzentrale 2023.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Config
* @author Thomas Wagener <wagener@hebis.uni-frankfurt.de>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org Main Site
*/

namespace VuFind\Config\Feature;

use Laminas\Config\Reader\Ini as IniReader;

/**
* Trait for creating INI readers
*
* @category VuFind
* @package Config
* @author Thomas Wagener <wagener@hebis.uni-frankfurt.de>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org Main Site
*/
trait IniReaderTrait
{
/**
* INI reader
*
* @var IniReader
*/
protected $iniReader = null;

/**
* Creates INI reader if it does not exist and returns it.
*
* @return IniReader
*/
protected function getIniReader()
{
if (null == $this->iniReader) {
// Use ASCII 0 as a nest separator; otherwise some of the unusual key names
// we have (i.e. in WorldCat.ini search options) will get parsed in
// unexpected ways.
$this->iniReader = new IniReader();
$this->iniReader->setNestSeparator(chr(0));
}
return $this->iniReader;
}
}
56 changes: 49 additions & 7 deletions module/VuFind/src/VuFind/Config/PathResolverFactory.php
Expand Up @@ -29,11 +29,13 @@

namespace VuFind\Config;

use Laminas\Config\Config;
use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Psr\Container\ContainerExceptionInterface as ContainerException;
use Psr\Container\ContainerInterface;
use VuFind\Config\Feature\IniReaderTrait;

/**
* Factory for PathResolver.
Expand All @@ -46,6 +48,8 @@
*/
class PathResolverFactory implements FactoryInterface
{
use IniReaderTrait;

/**
* Default base config file subdirectory under the base directory
*
Expand Down Expand Up @@ -82,14 +86,52 @@ public function __invoke(
if (!empty($options)) {
throw new \Exception('Unexpected options sent to factory.');
}
$localDirs = defined('LOCAL_OVERRIDE_DIR')
$localDirs = [];
$currentDir = defined('LOCAL_OVERRIDE_DIR')
&& strlen(trim(LOCAL_OVERRIDE_DIR)) > 0
? [
[
'directory' => LOCAL_OVERRIDE_DIR,
'defaultConfigSubdir' => $this->defaultLocalConfigSubdir,
],
] : [];
? LOCAL_OVERRIDE_DIR : '';
while (!empty($currentDir)) {
// check if the directory exists
if (!($canonicalizedCurrentDir = realpath($currentDir))) {
trigger_error("Configured local directory does not exist: " . $currentDir, E_USER_WARNING);
break;
}
$currentDir = $canonicalizedCurrentDir;

// check if the current directory was already included in the stack to avoid infinite loops
if (in_array($currentDir, array_column($localDirs, 'directory'))) {
trigger_error("Current directory was already included in the stack: " . $currentDir, E_USER_WARNING);
break;
}

// loading DirLocations.ini of currentDir
$systemConfigFile = $currentDir . '/DirLocations.ini';
$systemConfig = new Config(
file_exists($systemConfigFile)
? $this->getIniReader()->fromFile($systemConfigFile)
: []
);

// adding directory to the stack
array_unshift(
$localDirs,
[
'directory' => $currentDir,
'defaultConfigSubdir' =>
$systemConfig['Local_Dir']['config_subdir']
?? $this->defaultLocalConfigSubdir,
]
);

// If there's a parent, set it as the current directory for the next loop iteration:
if (!empty($systemConfig['Parent_Dir']['path'])) {
$isRelative = $systemConfig['Parent_Dir']['is_relative_path'] ?? false;
$parentDir = $systemConfig['Parent_Dir']['path'];
$currentDir = $isRelative ? $currentDir . '/' . $parentDir : $parentDir;
} else {
$currentDir = '';
}
}
return new $requestedName(
[
'directory' => APPLICATION_PATH,
Expand Down
23 changes: 3 additions & 20 deletions module/VuFind/src/VuFind/Config/PluginFactory.php
Expand Up @@ -30,9 +30,9 @@
namespace VuFind\Config;

use Laminas\Config\Config;
use Laminas\Config\Reader\Ini as IniReader;
use Laminas\ServiceManager\Factory\AbstractFactoryInterface;
use Psr\Container\ContainerInterface;
use VuFind\Config\Feature\IniReaderTrait;

/**
* VuFind Config Plugin Factory
Expand All @@ -45,24 +45,7 @@
*/
class PluginFactory implements AbstractFactoryInterface
{
/**
* INI file reader
*
* @var IniReader
*/
protected $iniReader;

/**
* Constructor
*/
public function __construct()
{
// Use ASCII 0 as a nest separator; otherwise some of the unusual key names
// we have (i.e. in WorldCat.ini search options) will get parsed in
// unexpected ways.
$this->iniReader = new IniReader();
$this->iniReader->setNestSeparator(chr(0));
}
use IniReaderTrait;

/**
* Load the specified configuration file.
Expand All @@ -84,7 +67,7 @@ protected function loadConfigFile(string $filename): Config
// chain of them if the Parent_Config setting is used:
do {
$configs[]
= new Config($this->iniReader->fromFile($filename), true);
= new Config($this->getIniReader()->fromFile($filename), true);

$i = count($configs) - 1;
if (isset($configs[$i]->Parent_Config->path)) {
Expand Down

0 comments on commit f29c765

Please sign in to comment.