Giter VIP home page Giter VIP logo

laminas-config-aggregator's Introduction

laminas-config-aggregator

Build Status

🇷🇺 Русским гражданам

Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.

У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.

Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"

🇺🇸 To Citizens of Russia

We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.

One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.

You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"

Aggregates and merges configuration, from a variety of formats. Supports caching for fast bootstrap in production environments.

Usage

The standalone ConfigAggregator can be used to merge PHP-based configuration files:

use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\ConfigAggregator\PhpFileProvider;

$aggregator = new ConfigAggregator([
    new PhpFileProvider('*.global.php'),
]);

var_dump($aggregator->getMergedConfig());

Using this provider, each file should return a PHP array:

// db.global.php
return [
    'db' => [
        'dsn' => 'mysql:...',
    ],    
];

// cache.global.php
return [
    'cache_storage' => 'redis',
    'redis' => [ ... ],
];

Result:

array(3) {
  'db' =>
  array(1) {
    'dsn' =>
    string(9) "mysql:..."
  }
  'cache_storage' =>
  string(5) "redis"
  'redis' =>
  array(0) {
     ...
  }
}

Configuration is merged in the same order as it is passed, with later entries having precedence.

Together with laminas-config, laminas-config-aggregator can be also used to load configuration in different formats, including YAML, JSON, XML, or INI:

use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\ConfigAggregator\LaminasConfigProvider;

$aggregator = new ConfigAggregator([
    new LaminasConfigProvider('config/*.{json,yaml,php}'),
]);

For more details, please refer to the documentation.


laminas-config-aggregator's People

Contributors

acelaya avatar arueckauer avatar bakura10 avatar bcremer avatar boesing avatar geerteltink avatar ghostwriter avatar gsteel avatar kokspflanze avatar kynx avatar laminas-bot avatar malios avatar michalbundyra avatar mtymek avatar ocramius avatar renovate[bot] avatar samsonasik avatar weierophinney avatar xerkus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laminas-config-aggregator's Issues

Upgrade 1.1.1 to 1.2.0 break caching feature

BC Break Report

After running the application on Windows.

PHP Warning:  rename(C:\Users\****\AppData\Local\Temp\2\test.php.tmp,test.php): Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird. (code: 32) in D:\Apache_idpm\htdocs\test-config-cache.php on line 18

Warning: rename(C:\Users\****\AppData\Local\Temp\2\test.php.tmp,test.php): Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird. (code: 32) in D:\Apache_idpm\htdocs\test-config-cache.php on line 18
Q A
laminas-config-aggregator 1.2.0
PHP CGI/Fast-CGI 7.2.23
Apache 2.4
Windows Windows Server 2016

Summary

When executing any scripts or running the application the error above appears.
It seems that the function ConfigAggregator::cacheConfig generate a file in the sys_temp_dir and then try to rename the source file to the desired filename.

I tried to check if a process where using the source file with the service Process explorer but no results found.

Previous behavior

The file config-cache.php is generated in the data folder.

Current behavior

No file config-cache.php is generated and the error above appears.

How to reproduce

The sys_temp_dir is default to C:\Users\****\AppData\Local\Temp
This is a simple php script to reproduce the behaviour on Windows.

$cachedConfigFile='test.php';
$tempFile=sys_get_temp_dir() . DIRECTORY_SEPARATOR . basename($cachedConfigFile) . '.tmp';
$fh = fopen($tempFile, 'c');
//chmod($tempFile, '0777');
ftruncate($fh, 0);
fputs($fh,'test');
rename($tempFile, $cachedConfigFile);
fclose($fh);

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Repository problems

These problems occurred while renovating this repository. View logs.

  • WARN: Use matchDepNames instead of matchPackageNames

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • Lock file maintenance

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

composer
composer.json
  • php ~8.1.0 || ~8.2.0 || ~8.3.0
  • brick/varexporter ^0.5.0 || ^0.4.0
  • laminas/laminas-stdlib ^3.18.0
  • webimpress/safe-writer ^2.2.0
  • laminas/laminas-coding-standard ~2.5.0
  • laminas/laminas-config ^3.9.0
  • phpunit/phpunit ^10.5.11
  • psalm/plugin-phpunit ^0.19.0
  • vimeo/psalm ^5.22.2
github-actions
.github/workflows/continuous-integration.yml
.github/workflows/docs-build.yml
.github/workflows/release-on-milestone-closed.yml

  • Check this box to trigger a request for Renovate to run again on this repository

Validation for `class-string` config providers to be unique

Feature Request

Q A
New Feature yes
RFC yes
BC Break no

Summary

I recently detected some duplicate entries within the config provider array which is passed to the ConfigAggregator#__construct. Since it requires the config to be merged multiple times while it makes no sense, I'd say we could add some validation to the ConfigAggregator#__construct to at least report duplicated class-string and/or function-string values.

WDYT?

Feature Request / RFC: Allow closures in cached config for lean object construction

Feature Request

Q A
New Feature yes
RFC yes
BC Break no

Summary

Right now it is not possible to use closures in a ConfigProvider or PHP Config file if config caching is enabled (ConfigAggregator::ENABLE_CACHE => true).

Example:

// config/autoload/dependencies.global.php
return [
    'dependencies' => [
        'factories'  => [
            SomeService::class => fn() => new SomeService(),
        ],
    ],
];

When calling the application and thus calling \Laminas\ConfigAggregator\ConfigAggregator::__construct() the first request will be fine and working like expected, also the cached config is generated and sucessfully written to config_cache_path.

On the second call the config is read from the cache file and will fail with the following exception message:

$ php bin/console.php 
PHP Fatal error:  Uncaught Error: Call to undefined method Closure::__set_state() in /example/data/cache/config-cache.php:186
Stack trace:
#0 /example/vendor/laminas/laminas-config-aggregator/src/ConfigAggregator.php(257): require()
#1 /example/vendor/laminas/laminas-config-aggregator/src/ConfigAggregator.php(76): Laminas\ConfigAggregator\ConfigAggregator->loadConfigFromCache()

This is due to the fact that PHP natives var_export is used to generate the cache file contents, see: https://github.com/laminas/laminas-config-aggregator/blob/1.2.2/src/ConfigAggregator.php#L281

Proposed solution

Instead of the PHP native var_export function the ConfigAggregator should optionally use a library that is able to export closures into a native PHP array.

I successfully implemented a POC using brick/varexporter by @BenMorel that provides a drop in replacement for var_export and is able to export closures.

Rational

Problem

A typical service in a laminas/mezzio application uses a separate factory class to resolve dependencies from the ContainerInterface and to instantiate the service.

<?php

namespace App\Api\Search;

use Doctrine\DBAL\Connection;
use Psr\Container\ContainerInterface;
use Psr\Logger\LoggerInterface;

final class SearchRepositoryFactory
{
    public function __invoke(ContainerInterface $container): SearchRepository
    {
        return new SearchRepository(
            $container->get(Connection::class),
            $container->get(LoggerInterface::class),
        );
    }
}

Additionally the factory must be registered in the config file or a ConfigProvider like so:

return [
    'dependencies' => [
        'factories'  => [
            SearchRepositoryFactory::class => SearchRepositoryFactory::class,
        ],
    ],
];

This is a lot of boilerplate code to introduce a new service into an application.

Proposed solution

With the introduction of Arrow functions in PHP 7.4 the factory code can be written in a very lean way in the config provider or config file like this:

<?php

use Doctrine\DBAL\Connection;
use Psr\Container\ContainerInterface;
use Psr\Logger\LoggerInterface;

return [
    'dependencies' => [
        'factories'  => [
            SearchRepository::class => fn (ContainerInterface $container) => new SearchRepository(
                $container->get(Connection::class),
                $container->get(LoggerInterface::class),
            ),

            AnotherService::class => fn (ContainerInterface $container) => new AnotherService(
                $container->get(LoggerInterface::class),
                $container->get(SystemClock::class)
            ),
        ],
    ],
];

In my opinion the this provides a good middle ground between the convenience of the ReflectionBasedAbstractFactory and the control and performance of manually written factory classes.

I'm not proposing to get rid of all factory classes but only factory classes that contain no logic but resolving and instantiating the given service class.
I'm still using dedicated factory class for any object construction logic that is more complicated.

Performance Considerations

The exported closures look like the following in the generated cache files:

        'factories' => [
            'App\\Api\\Search\\SearchRepository' => function (\Psr\Container\ContainerInterface $container) {
                return new \App\Api\Search\SearchRepository($container->get(\Doctrine\DBAL\Connection::class));
            },
        ]

Performance related questions:

  • Can the generated cache file still be picked up into the opcache
  • Is calling the closures slower than calling instantiating and calling factory classes?

Alternative approaches

Questions

  • How do you feel generally about using Arrow functions instead of factory classes?
  • Can a third party library like brick/varexporter be a viable solution to export closures in the cached config file?
  • How should this be implemented
    • brick/varexporter could be used a soft dependecy and used instead of the native function when available
  • Are you open for an PR implementing the proposed solution either as a separate ConfigAggregator or as the soft dependency or maybe a postprocessor? I'm open for implementation ideas.

PHP 8.0 support

Feature Request

Q A
New Feature yes

Summary

To be prepared for the december release of PHP 8.0, this repository has some additional TODOs to be tested against the new major version.

In order to make this repository compatible, one has to follow these steps:

  • Modify composer.json to provide support for PHP 8.0 by adding the constraint ~8.0.0
  • Modify composer.json to drop support for PHP less than 7.3
  • Modify composer.json to implement phpunit 9.3 which supports PHP 7.3+
  • Modify .travis.yml to ignore platform requirements when installing composer dependencies (simply add --ignore-platform-reqs to COMPOSER_ARGS env variable)
  • Modify .travis.yml to add PHP 8.0 to the matrix (NOTE: Do not allow failures as PHP 8.0 has a feature freeze since 2020-08-04!)
  • Modify source code in case there are incompatibilities with PHP 8.0

Psalm integration

Feature Request

Q A
QA yes

Summary

As decided during the Technical-Steering-Committee Meeting on August 3rd, 2020, Laminas wants to implement vimeo/psalm in all packages.

Implementing psalm is quite easy.

Required

  • Create a psalm.xml in the project root
  • Copy and paste the contents from this psalm.xml.dist
  • Run $ composer require --dev vimeo/psalm
  • Run $ vendor/bin/psalm --set-baseline=psalm-baseline.xml
  • Add a composer script static-analysis with the command psalm --shepherd --stats
  • Add a new line to script: in .travis.yml: - if [[ $TEST_COVERAGE == 'true' ]]; then composer static-analysis ; fi
  • Remove phpstan from the project (phpstan.neon.dist, .travis.yml entry, composer.json require-dev and scripts)
Optional
  • Fix as many psalm errors as possible.

Allow `object&callable` annotation for post- and preprocessors

Feature Request

Q A
New Feature yes
RFC no
BC Break no

Summary

Psalm is now able to properly understand something like:

object&callable(array):array

This would reduce errors with post processors being instantiated before being passed to the ConfigAggregator::__construct.

Example:

$parameterPostProcessor = new \Laminas\ConfigAggregatorParameters\LazyParameterPostProcessor(fn () => []);
$aggregator = new \Laminas\ConfigAggregator\ConfigAggregator([], null, [$parameterPostProcessor]);

This leads to an error:

InvalidArgument - config/config.php:45:50 - Argument 3 of Laminas\ConfigAggregator\ConfigAggregator::__construct expects list<callable(array<array-key, mixed>)|class-string>, but list{Laminas\ConfigAggregatorParameters\LazyParameterPostProcessor<array<string, mixed>>} provided (see https://psalm.dev/004)

To fix this, adding object&PostProcessorCallable and object&PreProcessorCallable to the annotation should fix the problem.
Something like this was also introduced in laminas-servicemanager with v4.0 and is supported by both psalm and phpstan.

[RFC]: Allow a ConfigProvider to require ConfigProviders from dependencies

RFC

Q A
Proposed Version(s) 1.10.0
BC Break? No

Goal

laminas/laminas-modulemanager has a great feature that allows modules to be in a dependency tree. I would like a similar feature in this component.

There should be no changes to the current mechanisms, but dependent providers should be obtained and required in the order that they first appear, ahead of the ConfigProvider that requires them. (Edit: A clarification on this last point, and not my original intention but after a good suggestion by @boesing, the current list of providers in config.php should be considered as the first appearance if a provider is listed there).

Background

It is always necessary at the application level to include the ConfigProvider from all components used, even if there is no application code that specifically relies on those components (as they would be required and used internally by the external component). This can often lead to not knowing about providers that also need adding and there is no check to ensure that they are, nor any sufficient error message to explain to the user that this is the case.

Considerations

To use the new proposed mechanism should be an opt-in, and not require changes for those applications or components that do not wish to use it. This should be checked only the once and not affect the performance of retrieving from cache. Dependent providers should only be included once, and in the order that they first appear in the tree.

Since the changes would need to be made to provider loading, doing this as an external package would not be possible without also providing a modified ConfigAggregator.

Proposal(s)

I propose to introduce a new interface which requires a method providing a list of direct dependent ConfigProvider classes. This component would then check for this interface and prepend those required classes to the list of providers.

Appendix

Possible interface (names are just my first suggestions):

interface HasConfigProviderDependencies
{
    /**
     * @return iterable Array or \Iterator of provider dependencies. These are string values representing
     *     classes that act as providers. They must be instantiable without constructor arguments.
     */
    public function configProviderDependencies(): iterable;
}

Similar to/overlap with:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.