Skip to content

Commit

Permalink
Ensure that framework configuration is loaded with application (#600)
Browse files Browse the repository at this point in the history
* Ensure that configuration is properly loaded

* CHANGELOG

* Remove cache from about

* Fix the bootloader in config cache
  • Loading branch information
srtfisher authored Nov 1, 2024
1 parent 1d4a671 commit a07ad63
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure that the `delete()` method of the HTTP Client doesn't set a body by default.
- Ensure that `with_terms()` can support an array of term slugs when passed with a
taxonomy index.
- Ensure that framework configuration respects the application configuration.

## v1.2.0 - 2024-09-23

Expand Down
13 changes: 11 additions & 2 deletions src/mantle/database/console/class-seed-command.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,31 @@ class Seed_Command extends Command {
/**
* Run Database Seeding
*/
public function handle(): void {
public function handle(): int {
if ( ! $this->confirm_to_proceed() ) {
return;
return static::FAILURE;
}

// Disable cache purging.
if ( class_exists( 'WPCOM_VIP_Cache_Manager' ) ) {
remove_action_validated( 'shutdown', [ \WPCOM_VIP_Cache_Manager::instance(), 'execute_purges' ] );
}

$class = $this->container->get_namespace() . '\\Database\\Seeds\\Database_Seeder';

if ( ! class_exists( $class ) ) {
$this->error( "Database Seeder class not found: {$class}" );
return static::FAILURE;
}

$this->container
->make( $this->option( 'class', \App\Database\Seeds\Database_Seeder::class ) )
->set_container( $this->container )
->set_command( $this )
->__invoke();

$this->success( __( 'Database seeding completed.', 'mantle' ) );

return static::SUCCESS;
}
}
105 changes: 65 additions & 40 deletions src/mantle/framework/bootstrap/class-load-configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ public static function merge( array $config ): void {
public function bootstrap( Application $app ): void {
$config = $app->make( 'config' );

// Load the configuration files if not loaded from cache.
// Load the configuration files if not already loaded from cache.
if ( ! $config->get( 'config.loaded_from_cache' ) ) {
$this->load_configuration_files( $app, $config );
}

if ( ! empty( static::$merge ) ) {
$this->load_merge_configuration( $config );
$this->load_late_configuration( $config );
}
}

Expand Down Expand Up @@ -81,13 +81,7 @@ protected function load_configuration_to_repository( array $files, Repository_Co
continue;
}

$existing_config = $repository->get( $key );

foreach ( $this->get_mergeable_options( $key ) as $option ) {
$config[ $option ] = array_merge( $existing_config[ $option ] ?? [], $config[ $option ] ?? [] );
}

$repository->set( $key, $config );
$repository->set( $key, $this->merge_configuration( $key, $repository->get( $key ), $config ) );
}
}
}
Expand All @@ -106,14 +100,13 @@ protected function load_configuration_files( Application $app, Repository_Contra
return;
}

// Load the root-level config.
$this->load_configuration_to_repository( $files['global'], $repository );

$env = $app->environment();

// Load the environment-specific configurations if one exists.
if ( ! empty( $files['env'][ $env ] ) ) {
$this->load_configuration_to_repository( $files['env'][ $env ], $repository );
if ( ! empty( $files['environment'][ $env ] ) ) {
$this->load_configuration_to_repository( $files['environment'][ $env ], $repository );
}
}

Expand Down Expand Up @@ -154,18 +147,18 @@ protected function get_configuration_directories( Application $app ): array {
* Find the configuration files to load.
*
* @param Application $app Application instance.
* @return array{global: array<string, string[]>, env: array<string, array<string, string[]>>}
* @return array{global: array<string, string[]>, environment: array<string, array<string, string[]>>}
*/
protected function get_configuration_files( Application $app ): array {
$files = [
'global' => [],
'env' => [],
'global' => [],
'environment' => [],
];

$finder = Finder::create()
->files()
->name( '*.php' )
->depth( '< 2' ) // Only descend two levels.
->depth( '< 2' )
->in( $this->get_configuration_directories( $app ) );

foreach ( $finder as $file ) {
Expand All @@ -179,50 +172,52 @@ protected function get_configuration_files( Application $app ): array {
};

if ( $environment ) {
$files['env'][ $environment ][ $name ][] = $file->getRealPath();
$files['environment'][ $environment ][ $name ][] = $file->getRealPath();
} else {
$files['global'][ $name ][] = $file->getRealPath();
}
}

// Sort them to ensure a similar experience across all hosting environments.
foreach ( $files as $type => $config_files ) {
ksort( $files[ $type ], SORT_NATURAL );
return $files;
}

/**
* Retrieve the base configuration.
*
* @return array<string, array<mixed>>
*/
protected function get_base_configuration(): array {
$config = [];

foreach ( Finder::create()->files()->name( '*.php' )->depth( '< 2' )->in( dirname( __DIR__, 4 ) . '/config' ) as $file ) {
$name = basename( $file->getRealPath(), '.php' );

$config[ $name ] = $this->merge_configuration(
$name, $config[ $name ] ?? [],
require $file->getRealPath() // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
);
}

return $files;
return $config;
}

/**
* Load the additional configuration from the bootloader to the repository.
*
* @throws InvalidArgumentException If the configuration value is not an array.
* @throws InvalidArgumentException If the mergeable option is not an array.
*
* @param Repository_Contract $repository Configuration Repository.
*/
protected function load_merge_configuration( Repository_Contract $repository ): void {
protected function load_late_configuration( Repository_Contract $repository ): void {
foreach ( static::$merge as $config_key => $values ) {
if ( ! is_array( $values ) ) {
throw new InvalidArgumentException( "The bootstrap-loaded configuration value for key '{$config_key}' must be an array." );
}

$config = $repository->get( $config_key, [] );
$mergeable_options = $this->get_mergeable_options( $config_key );

foreach ( $values as $key => $value ) {
if ( in_array( $key, $mergeable_options, true ) ) {
if ( ! is_array( $value ) ) {
throw new InvalidArgumentException( "The mergeable option '{$key}' for key '{$config_key}' must be an array." );
}

$config[ $key ] = array_merge( $config[ $key ] ?? [], $value );
} else {
$config[ $key ] = $value;
}
throw new InvalidArgumentException( "The bootloader configuration value for key '{$config_key}' must be an array." );
}

$repository->set( $config_key, $config );
$repository->set(
$config_key,
$this->merge_configuration( $config_key, $repository->get( $config_key, [] ), $values ),
);

static::$merge = [];
}
Expand All @@ -241,4 +236,34 @@ protected function get_mergeable_options( string $name ): array {
'app' => [ 'providers' ],
][ $name ] ?? [];
}

/**
* Merge two configurations together.
*
* Will merge the two configurations together and merge any mergeable options.
*
* @throws InvalidArgumentException If the mergeable option is not an array.
* @see Load_Configuration::get_mergeable_options()
*
* @param string $config_name Configuration name.
* @param array $config_a First configuration.
* @param array $config_b Second configuration.
*/
protected function merge_configuration( string $config_name, array $config_a, array $config_b ): array {
$new_config = array_merge( $config_a, $config_b );

foreach ( $this->get_mergeable_options( $config_name ) as $option ) {
if ( ! isset( $config_a[ $option ] ) || ! isset( $config_b[ $option ] ) ) {
continue;
}

if ( ! is_array( $config_a[ $option ] ) || ! is_array( $config_b[ $option ] ) ) {
throw new InvalidArgumentException( "The mergeable option '{$option}' for key '{$config_name}' must be an array." );
}

$new_config[ $option ] = array_merge( $config_a[ $option ] ?? [], $config_b[ $option ] ?? [] );
}

return $new_config;
}
}
1 change: 0 additions & 1 deletion src/mantle/framework/console/class-about-command.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ protected function gather_information(): void {

static::add(
fn () => [
'Cache' => config( 'cache.default' ),
'Filesystem' => config( 'filesystem.default' ),
'Logging' => config( 'logging.default' ),
'Queue' => config( 'queue.default' ),
Expand Down
5 changes: 4 additions & 1 deletion src/mantle/framework/console/class-config-cache-command.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ public function handle( Filesystem $filesystem ): void {
* Boot a fresh copy of the application configuration.
*/
protected function get_fresh_configuration(): array {
$app = require $this->container->get_bootstrap_path( '/app.php' ); // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable
$bootloader = require $this->container->get_bootstrap_path( '/app.php' ); // phpcs:ignore WordPressVIPMinimum.Files.IncludingFile.UsingVariable

$app = $bootloader->get_application();

$app->set_base_path( $this->container->get_base_path() );
$app->make( Kernel::class )->bootstrap();

Expand Down

0 comments on commit a07ad63

Please sign in to comment.