mediawiki-skins-Vector/includes/FeatureManagement/Requirements/DynamicConfigRequirement.php
Sam Smith 84226b41b6 featureManager: Add DynamicConfigRequirement requirement
We expect the vast majority of requirements and features to be defined
in services as possible. However, there are some "complex" requirements
that require additional application/HTTP request state. Unfortunately,
service wiring is done before some of that state is available.

I65702426 attempted to work around this by requiring clients of the
Feature Manager to pass that additional state on every interaction with
the system. Those complex requirements would then select the parts of
the state that they required when it was required. However
implementations of \IContextSource are God objects and their use should
be limited.

Whilst reviewing I65702426, Stephen Niedzielski mentioned that the
application state being available is a requirement. This remarkably
simple solution:

- Keeps the Requirement interface and FeatureManager API free of God
  objects;

- Is true to the nature of the Feature Manager - it makes clear and
  centralizes the various checks for application state being available
  across the codebase; and

- Inject a Requirement implementations' dependencies at construction
  time

It just so happens that the $wgFullyInitialised variable flags whether
the application state is available...

Changes:

- Add the the FeatureManager\Requirements\DynamicConfigRequirement class
  and tests. The DynamicConfigRequirement lazily evaluates a single
  configuration value whenever ::isMet is invoked

- Register an DynamicConfigRequirement instance, configured to evaluate
  $wgFullyInitialised while constructing the Vector.FeatureManager
  service

Bug: T244481
Change-Id: I7a2cdc2dfdf20d78e4548f07cf53994563b234b3
2020-03-18 10:10:42 +00:00

98 lines
2.6 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @since 1.35
*/
namespace Vector\FeatureManagement\Requirements;
use Vector\FeatureManagement\Requirement;
/**
* Some application state changes throughout the lifetime of the application, e.g.
* `wgFullyInitialised`, which signals whether the application boot process has finished and
* critical resources like database connections are available.
*
* The `DynamicStateRequirement` allows us to define requirements that lazily evaluate the
* application state statically, e.g.
*
* ```lang=php
* $featureManager->registerComplexRequirement(
* new DynamicConfigRequirement(
* $config,
* 'FullyInitialised',
* 'Foo'
* )
* );
* ```
*
* registers a requirement that will evaluate to true only when `mediawiki/includes/Setup.php` has
* finished executing (after all service wiring has executed).
*
* NOTE: This API hasn't settled. It may change at any time without warning. Please don't bind to
* it unless you absolutely need to
*
* @unstable
*
* @package FeatureManagement
* @internal
*/
final class DynamicConfigRequirement implements Requirement {
/**
* @var \Config
*/
private $config;
/**
* @var string
*/
private $configName;
/**
* @var string
*/
private $requirementName;
/**
* @param \Config $config
* @param string $configName
* @param string $requirementName The name of the requirement presented to the Feature Manager
*/
public function __construct( \Config $config, string $configName, string $requirementName ) {
$this->config = $config;
$this->configName = $configName;
$this->requirementName = $requirementName;
}
/**
* @inheritDoc
*/
public function getName() : string {
return $this->requirementName;
}
/**
* @inheritDoc
*/
public function isMet() : bool {
return (bool)$this->config->get( $this->configName );
}
}