mini4work / core
Сore functionality of the Mini4Work framework.
Requires
- php: ^8.3
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
This repository contains the core functionality of the Mini4Work framework. It is designed to be a lightweight and flexible framework foundation, providing essential features such as dependency injection and a modular architecture.
Developers should use this package as a dependency in their projects. Typically, it is automatically included when
installing the mini4work/project package.
Application instances
Create an application explicitly and pass dependencies through constructors:
$app = new \M4W\Core\Framework();
$kernel = $app->make(MyKernel::class);
$app->run($kernel);
AbstractKernel receives the Framework in its constructor. Each container resolves
Framework::class, the concrete application class, Container::class and 'app' to itself. Creating a container or
loading providers never selects a global application.
For optional global helpers and facades, explicitly select a container:
\M4W\Core\Framework::setInstance($app);
app(); // $app
\M4W\Core\Facades\App::make('app'); // $app
\M4W\Core\Framework::resetInstance();
getInstance(), app() and facades throw when no global container is selected.
Facades follow the same selected container as app(); the compatibility method
Facade::setFacadeApplication() selects or clears that same global container.
Singleton services are shared only within their own container.
Kernel lifecycle
Every kernel must extend M4W\Core\Abstract\AbstractKernel.
Framework::run() accepts this base class, not arbitrary implementations of the
Kernel interface. The constructor and run() are final; constructing a kernel
only injects the application and does not load providers.
class MyKernel extends \M4W\Core\Abstract\AbstractKernel
{
public function serviceProviders(): array
{
return [MyServiceProvider::class];
}
protected function bootstrap(): void
{
// Load runtime-specific configuration before registering providers.
}
protected function execute(): void
{
// Handle a request, execute a command, or enter a game loop.
}
protected function shutdown(): void
{
// Release kernel resources, including after partial startup.
}
}
The enforced order is bootstrap() → all providers' register() → all providers'
boot() → execute(). A kernel instance can run only once, including failed runs.
Providers must extend AbstractServiceProvider; register() is required, while
boot() and shutdown() default to no-ops. Duplicate provider classes are rejected.
The default console bindings are included unless the kernel sets
protected bool $disableDefaultFacades = true.
On completion or failure, the kernel's shutdown() runs first, followed by provider
shutdown() calls in reverse registration order. Even a partially registered
provider is included. Cleanup must tolerate partial initialization; constructors
should not acquire resources. Every cleanup hook is attempted even when another
throws. The original startup/execution exception takes precedence over cleanup
exceptions; otherwise the first cleanup exception is propagated.
Kernels own their startup and execution; the framework only delegates to run().
Avoid starting different kernels on the same application unless their providers
explicitly support re-registration. Use separate containers for isolated runtimes.
Error handling
Framework registers Interfaces\ExceptionHandler as an early singleton dependency.
The default Exception\Handler uses ConsoleService directly and needs neither
facades nor a selected global application. It exposes an instance handle(Throwable)
method instead of the previous static cause() method.
Kernels may override registerExceptionHandler(), which runs before bootstrap():
protected function registerExceptionHandler(): void
{
$this->app->singleton(
\M4W\Core\Interfaces\ExceptionHandler::class,
MyExceptionHandler::class,
);
}
Custom handlers must implement ExceptionHandler::handle(Throwable): void. They can
combine logging, rendering or delegate to other handlers. The kernel reports startup,
execution and cleanup failures, while still propagating the original exception.
Handler failures never replace it. No process-global PHP exception handler is installed;
each application's handling remains local. Errors before run() can be reported
explicitly through $app->make(ExceptionHandler::class)->handle($exception).
Dependency injection
bind() creates a fresh instance on each make(); singleton() keeps the first
result. Factories run lazily and receive the container and the make() parameters.
Scalar types and arrays can be bound by type. The reserved names string, int,
float, bool and array do not allow singleton registration, including through
bind(..., shared: true). Factories for these types run on every make():
$app->bind('string', 'localhost');
$app->bind('int', 8080);
$app->bind('float', 0.5);
$app->bind('bool', false);
$app->bind('array', ['debug' => true]);
$service = $app->make(Service::class, ['port' => 9000]);
Type bindings apply globally to all constructor parameters of that type. Resolution
prefers an explicit named make() parameter, then a type binding, then a constructor
default, then autowiring a class. Required primitive parameters without bindings fail
with an exception identifying the parameter. Primitive bindings must return the exact
declared type; values are not coerced. Strings and arrays bound to primitive types
are literal values, even when callable; use a closure or invokable object for factories.
Untyped, union and intersection parameters require an explicit value or a constructor default. Circular dependencies raise an exception showing the resolution path.
Contributing
Mini4Work is designed to remain simple and lightweight. If you have any improvements or features to suggest for the core framework, feel free to submit a Pull Request.
License
Mini4Work is distributed by The MIT license.