Skip to content

Commit

Permalink
[1.1] Overhauling the bootloader to be more flexible (#541)
Browse files Browse the repository at this point in the history
* WIP

* Improve the bootloader to all better extension

* Contd work

* Gut the legacy route service provider:

* WIP

* Restore the service provider to not break BC

* WIP

* Remove base path from bootloader

* Ensure that duplicate service providers arent registered

* CHANGELOG

* Fix default return

* Adding bootloader create method

* Wiring up the simplified configuration to the two Create Application traits we have

* PHPStan fix

* Fixing arguments

* Allow classic route registration

* Apply suggestions from code review

Co-authored-by: Damian <4309872+attackant@users.noreply.github.com>

---------

Co-authored-by: Damian <4309872+attackant@users.noreply.github.com>
  • Loading branch information
srtfisher and attackant committed Jun 12, 2024
1 parent d47209e commit 5a5e10c
Show file tree
Hide file tree
Showing 31 changed files with 857 additions and 607 deletions.
14 changes: 11 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
## v1.1.0

- Added a `classname`/`the_classname` helper to generate complex class names.
- Added support for installing the Redis `object-cache.php` drop-in during
testing with `with_object_cache()`.
- Added support for PHPUnit 11.

### Changed

- Overhauled the bootloader to be more flexible and allow for more
customization. Supports passing configuration, custom kernels, exception
handlers, etc. via the bootloader when configuring the application.
- Ensure that framework configuration is properly merge into application
configuration when booting the application. This allows for slimmer
configuration files in the application. Service providers will always
load without needing to be declared in the application configuration.

## Fixed

- Fixed issue with command jobs not working properly.
Expand Down
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
"mantle-framework/testkit": "self.version",
"mantle-framework/view": "self.version"
},
"conflict": {
"phpunit/phpunit": ">11.0.0"
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
Expand Down
1 change: 0 additions & 1 deletion config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
Mantle\Application\App_Service_Provider::class,
Mantle\Assets\Asset_Service_Provider::class,
Mantle\Events\Event_Service_Provider::class,
Mantle\Framework\Providers\Route_Service_Provider::class,
],

/*
Expand Down
4 changes: 4 additions & 0 deletions src/mantle/application/class-app-service-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

/**
* App Service Provider
*
* This provider is always loaded by the framework and does not need to be
* declared. It is registered before the providers are booted to allow for the
* application to extend the provider with custom functionality.
*/
class App_Service_Provider extends Service_Provider {
/**
Expand Down
160 changes: 59 additions & 101 deletions src/mantle/application/class-application.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,29 @@
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

use function Mantle\Support\Helpers\collect;
use function Mantle\Support\Helpers\data_get;
use function Mantle\Support\Helpers\str;

/**
* Mantle Application
*/
class Application extends Container implements \Mantle\Contracts\Application {
use Concerns\Loads_Base_Configuration,
use Concerns\Application_Callbacks,
Concerns\Loads_Base_Configuration,
Concerns\Loads_Environment_Variables,
Concerns\Loads_Facades,
Concerns\Manages_Service_Providers;

/**
* Base path of the application.
*
* @var string
*/
protected $base_path;
protected string $base_path;

/**
* Application path of the application.
*
* @var string
*/
protected $app_path;
protected string $app_path;

/**
* Bootstrap path of the application.
Expand All @@ -66,27 +65,6 @@ class Application extends Container implements \Mantle\Contracts\Application {
*/
protected bool $booted = false;

/**
* The array of booting callbacks.
*
* @var callable[]
*/
protected array $booting_callbacks = [];

/**
* The array of booted callbacks.
*
* @var callable[]
*/
protected array $booted_callbacks = [];

/**
* All of the registered service providers.
*
* @var \Mantle\Support\Service_Provider[]
*/
protected array $terminating_callbacks = [];

/**
* Environment file name.
*/
Expand All @@ -107,15 +85,24 @@ class Application extends Container implements \Mantle\Contracts\Application {
*/
protected ?bool $is_running_in_console = null;

/**
* Storage of the application's namespace.
*/
protected string $namespace;

/**
* Constructor.
*
* @param string $base_path Base path to set.
* @param string $root_url Root URL of the application.
* @param string|null $base_path Base path to set.
* @param string $root_url Root URL of the application.
*/
public function __construct( string $base_path = '', string $root_url = null ) {
if ( empty( $base_path ) && defined( 'MANTLE_BASE_DIR' ) ) {
$base_path = \MANTLE_BASE_DIR;
public function __construct( ?string $base_path = null, string $root_url = null ) {
if ( empty( $base_path ) ) {
$base_path = match ( true ) {
isset( $_ENV['MANTLE_BASE_PATH'] ) => $_ENV['MANTLE_BASE_PATH'],
defined( 'MANTLE_BASE_DIR' ) => MANTLE_BASE_DIR,
default => '',
};
}

if ( ! $root_url ) {
Expand Down Expand Up @@ -172,9 +159,11 @@ public function get_base_path( string $path = '' ): string {
* @param string $path Path to append, optional.
*/
public function get_app_path( string $path = '' ): string {
$app_path = $this->app_path ?: $this->get_base_path( 'app' );
if ( ! isset( $this->app_path ) ) {
$this->app_path = $this->get_base_path( 'app' );
}

return $app_path . ( $path ? DIRECTORY_SEPARATOR . $path : $path );
return $this->app_path . ( $path ? DIRECTORY_SEPARATOR . $path : $path );
}

/**
Expand Down Expand Up @@ -415,9 +404,7 @@ public function register_base_services(): void {
public function flush(): void {
parent::flush();

$this->booted_callbacks = [];
$this->booting_callbacks = [];
$this->service_providers = [];
$this->flush_callbacks();
}

/**
Expand Down Expand Up @@ -509,23 +496,51 @@ public function environment(): string {
/**
* Check if the Application's Environment matches a list.
*
* @param string|array ...$environments Environments to check.
* @param string ...$environments Environments to check.
*/
public function is_environment( ...$environments ): bool {
return in_array( $this->environment(), $environments, true );
}

/**
* Get the application namespace.
* Set the environment for the application.
*
* @throws RuntimeException If the config is not set yet.
* @param string $environment Environment to set.
* @return static
*/
public function set_environment( string $environment ) {
$this->environment = $environment;

return $this;
}

/**
* Get the application namespace.
*/
public function get_namespace(): string {
if ( ! isset( $this['config'] ) ) {
throw new RuntimeException( 'Configurations not set yet.' );
if ( ! empty( $this->namespace ) ) {
return $this->namespace;
}

if ( isset( $this['config'] ) ) {
$this->namespace = (string) $this['config']->get( 'app.namespace' );
}

// If the namespace is not set, attempt to infer it from the composer.json file.
if ( empty( $this->namespace ) && file_exists( $this->get_base_path( 'composer.json' ) ) ) {
$composer = json_decode( file_get_contents( $this->get_base_path( 'composer.json' ) ), true );
$autoload = data_get( $composer, 'extra.wordpress-autoloader.autoload', [] );

if ( ! empty( $autoload ) ) {
$this->namespace = str( collect( $autoload )->keys()->first() )->rtrim( '\\' )->value();
}
}

if ( empty( $this->namespace ) ) {
return 'App';
}

return (string) $this['config']->get( 'app.namespace', 'App' );
return $this->namespace;
}

/**
Expand Down Expand Up @@ -559,17 +574,6 @@ public function is_running_in_console_isolation(): bool {
return defined( 'MANTLE_ISOLATION_MODE' ) && MANTLE_ISOLATION_MODE;
}

/**
* Set the environment for the application.
*
* @param string $environment Environment to set.
* @return static
*/
public function set_environment( string $environment ) {
$this->environment = $environment;
return $this;
}

/**
* Throw an HttpException with the given data.
*
Expand All @@ -588,56 +592,10 @@ public function abort( int $code, string $message = '', array $headers = [] ): v
}
}

/**
* Register a new boot listener.
*
* @param callable $callback Callback for the listener.
*/
public function booting( callable $callback ): static {
$this->booting_callbacks[] = $callback;
return $this;
}

/**
* Register a new "booted" listener.
*
* @param callable $callback Callback for the listener.
*/
public function booted( callable $callback ): static {
$this->booted_callbacks[] = $callback;

if ( $this->is_booted() ) {
$this->fire_app_callbacks( [ $callback ] );
}

return $this;
}

/**
* Register a new terminating callback.
*
* @param callable $callback Callback for the listener.
*/
public function terminating( callable $callback ): static {
$this->terminating_callbacks[] = $callback;
return $this;
}

/**
* Terminate the application.
*/
public function terminate(): void {
$this->fire_app_callbacks( $this->terminating_callbacks );
}

/**
* Call the booting callbacks for the application.
*
* @param callable[] $callbacks Callbacks to fire.
*/
protected function fire_app_callbacks( array $callbacks ) {
foreach ( $callbacks as $callback ) {
$callback( $this );
}
}
}
Loading

0 comments on commit 5a5e10c

Please sign in to comment.