Releases: spiral/framework
3.14.1
What's Changed
Fixes
- Router: fix fallback handler in
AbstractTarget
by @roxblnfk in #1132 - Added compatibility constraints with
roadrunner-bridge
<3.7
andsapi-bridge
<1.1
Code quality
- Bump to Rector 1.2.4 by @samsonasik in #1129
- Enable Rector rules
RemoveUselessParamTagRector
andRemoveUselessReturnTagRector
by @samsonasik in #1130 - Apply php7.3 in Rector preset by @samsonasik in #1133
3.14.0
Warning
If you are using spiral/roadrunner-bridge
, you need to update it to version ^3.7
or ^4.0
.
New Interceptors
The HMVC
package has been deprecated. It has been replaced by the new spiral/interceptors
package, where we have reworked the interceptors. The basic principle remains the same, but the interface is now more understandable and convenient.
InterceptorInterface
In the old CoreInterceptorInterface
, the $controller
and $action
parameters caused confusion by creating a false association with HTTP controllers. However, interceptors are not tied to HTTP and are used universally. Now, instead of $controller
and $action
, we use the Target definition, which can point to more than just class methods.
The $parameters
parameter, which is a list of arguments, has now been moved to the CallContextInterface
invocation context.
/** @deprecated Use InterceptorInterface instead */
interface CoreInterceptorInterface
{
public function process(string $controller, string $action, array $parameters, CoreInterface $core): mixed;
}
interface InterceptorInterface
{
public function intercept(CallContextInterface $context, HandlerInterface $handler): mixed;
}
CallContextInterface
The CallContextInterface
invocation context contains all the information about the call:
- Target — the definition of the call target.
- Arguments — the list of arguments for the call.
- Attributes — additional context that can be used to pass data between interceptors.
Note
CallContextInterface
is immutable.
TargetInterface
TargetInterface
defines the target whose call we want to intercept.
If you need to replace the Target in the interceptor chain, use the static methods of the \Spiral\Interceptors\Context\Target
class to create a new Target.
The basic set includes several types of Target:
Target::fromReflectionMethod(ReflectionFunctionAbstract $reflection, class-string|object $classOrObject)
Creates a Target from a method reflection. The second argument is mandatory because the method reflection may refer to a parent class.Target::fromReflectionFunction(\ReflectionFunction $reflection, array $path = [])
Creates a Target from a function reflection.Target::fromClosure(\Closure $closure, array $path = [])
Creates a Target from a closure. Use PHP 8 syntax for better clarity:$target = Target::fromClosure($this->someAction(...));
Target::fromPathString(string $path, string $delimiter = '.')
and
Target::fromPathArray(array $path, string $delimiter = '.')
Creates a Target from a path string or array. In the first case, the path is split by the delimiter; in the second, the delimiter is used when converting the Target to a string. This type of Target without an explicit handler is used for RPC endpoints or message queue dispatching.Target::fromPair(string|object $controller, string $action)
An alternative way to create a Target from a controller and method, as in the old interface. The method will automatically determine how the Target should be created.
Compatibility
Spiral 3.x will work as expected with both old and new interceptors. However, new interceptors should be created based on the new interface.
In Spiral 4.x, support for old interceptors will be disabled. You will likely be able to restore it by including the spiral/hmvc
package.
Building an Interceptor Chain
If you need to manually build an interceptor chain, use \Spiral\Interceptors\PipelineBuilderInterface
.
In Spiral v3, two implementations are provided:
\Spiral\Interceptors\PipelineBuilder
— an implementation for new interceptors only.\Spiral\Core\CompatiblePipelineBuilder
— an implementation from thespiral/hmvc
package that supports both old and new interceptors simultaneously.
Note
In Spiral 3.14, the implementation for PipelineBuilderInterface
is not defined in the container by default.
CompatiblePipelineBuilder
is used in Spiral v3 services as a fallback implementation.
If you define your own implementation, it will be used instead of the fallback implementation in all framework pipelines.
At the end of the interceptor chain, there should always be a \Spiral\Interceptors\HandlerInterface
, which will be called if the interceptor chain does not terminate with a result or exception.
The spiral/interceptors
package provides several basic handlers:
\Spiral\Interceptors\Handler\CallableHandler
— simply calls the callable from the Target "as is".\Spiral\Interceptors\Handler\AutowireHandler
— calls a method or function, resolving missing arguments using the container.
use Spiral\Core\CompatiblePipelineBuilder;
use Spiral\Interceptors\Context\CallContext;
use Spiral\Interceptors\Context\Target;
use Spiral\Interceptors\Handler\CallableHandler;
$interceptors = [
new MyInterceptor(),
new MySecondInterceptor(),
new MyThirdInterceptor(),
];
$pipeline = (new CompatiblePipelineBuilder())
->withInterceptors(...$interceptors)
->build(handler: new CallableHandler());
$pipeline->handle(new CallContext(
target: Target::fromPair($controller, $action),
arguments: $arguments,
attributes: $attributes,
));
Pull Requests
- New interceptors by @roxblnfk in #1095
- Refactor interceptors target by @roxblnfk in #1106
- Add PipelineBuilder by @roxblnfk in #1111
- Update interceptors by @roxblnfk in #1117
- Separate new Interceptors by @roxblnfk in #1122
- Polish interceptors by @roxblnfk in #1128
Container Scopes
Container Scopes are integrated even deeper.
In Spiral, each type of worker is handled by a separate dispatcher.
Each dispatcher has its own scope, which can be used to limit the set of services available to the worker.
During request processing, such as HTTP, the context (ServerRequestInterface
) passes through a middleware pipeline.
At the very end, when the middleware has finished processing and the controller has not yet been called, there is a moment when the request context is finally prepared.
At this moment, the contextual container scope (in our case, http-request
) is opened, and the ServerRequestInterface
is placed in the container.
In this scope, interceptors come into play, after which the controller is executed.
As before, you can additionally open scopes in middleware, interceptors, or the business layer, for example, to limit the authorization context in a multi-tenant application.
You can view the names of dispatcher scopes and their contexts in the enum \Spiral\Framework\Spiral
.
Pull Requests
- [spiral/router] Opening of
http.request
scope added by @msmakouz in #1069 - Change the name of the enum from
ScopeName
toSpiral
by @msmakouz in #1078 - Added
PaginationProviderInterface
binding in scope by @msmakouz in #1079 - Console scopes by @butschster in #1085
- [spiral/queue] Add
Proxy
to the InvokerInterface in JobHandler by @msmakouz in #1091 - Usage of scopes in sessions and auth, adding CurrentRequest by @msmakouz in #1080
- Merge changes from the master branch by @msmakouz in #1103
- Integrate Container Scopes by @roxblnfk in #1104
- Rename context scopes by @roxblnfk in #1127
Fixes
- Fix psalm issue: remove internal annotation from LoggerTrait::$logger by @gam6itko in #1118
- Fix psalm issues related to
TracerFactoryInterface
by @gam6itko in #1119 - Fix error when a file from the stacktrace doesn't exist by @roxblnfk in #1114
- Fix an exception message by @msmakouz in #1115
- Fix OTEL: cast request URI to string for http.url trace attribute by @devnev in #1126
- Fix psalm issues about
Spiral\Queue\HandlerInterface::handle()
by @gam6itko in #1120 - Fix funding links by @roxblnfk in #1123
New Contributors
Full Changelog: 3.13.0...3.14.1
v3.13.0
New features
1. Introduced LoggerChannel
attribute
We are excited to introduce a new feature that enhances the flexibility of the logging component. With the new LoggerChannel
attribute, developers can now specify the logger channel directly in the code.
Example Usage:
class SomeService
{
public function __construct(
// Logger with channel `roadrunner` will be injected
#[LoggerChannel('roadrunner')] public LoggerInterface $logger
){}
}
This feature allows for better organization and clarity in logging, helping you maintain and debug your application more efficiently.
2. Added an ability additionally to scan parent classes.
With this update, you can now scan for attributes in parent classes, making your class discovery process more comprehensive and efficient.
Why This Matters
Previously, the tokenizer could only listen to classes where attributes were found. This limitation did not allow for the automatic (convenient) detection of classes by parent attributes and the effective use of the tokenizer cache. With this update, it will also listen to interfaces that the class with the attribute implements and the classes that it extends. This new feature leverages the full power of the tokenizer without the need to scan all classes and handle them manually, ensuring a more efficient and thorough attribute detection process.
Here is a practical example of how to use this feature:
use Spiral\Tokenizer\Attribute\TargetAttribute;
#[TargetAttribute(attribute: MyAttribute::class, scanParents: true)]
class MyListener implements TokenizationListenerInterface
{
public function listen(\ReflectionClass $class): void
{
// Your logic here
}
public function finalize(): void
{
// Your logic here
}
}
Other
- [spiral/queue] Added an ability to pass headers to the job handler using
SyncDriver
by @msmakouz in #1107 - [spiral/router] Added
HEAD
andOPTIONS
HTTP methods toroute:list
command by @tairau in #1109 - [spiral/stempler] Fixed bug in a
directive
parser by @butschster in #1098 - [spiral/core] Container Proxy classes are generated now with the
mixed
type in the methods parameters by @msmakouz in #1092 - [spiral/core] Optimized the
StateBinder::hasInjector()
method by @anoxia in #1105 - [spiral/tokenizer] Fixed registration tokenizer scopes via
TokenizerBootloader
by @butschster in #1093
Full Changelog: 3.12.0...3.13.0
v3.12.0
What's Changed
New features
1. Improved container injectors
spiral/core
Advanced Context Handling in Injector Implementations by @roxblnfk in #1041
This pull request presents a significant update to the injector system, focusing on the createInjection
method of the Spiral\Core\Container\InjectorInterface
. The key enhancement lies in the augmented ability of the injector to handle context more effectively.
Previously, the createInjection
method accepted two parameters: the ReflectionClass
object of the requested class and a context, which was limited to being either a string
or null
. This approach, while functional, offered limited flexibility in dynamically resolving dependencies based on the calling context.
The updated createInjection
method can now accept an extended range of context types including Stringable|string|null
, mixed
, or ReflectionParameter|string|null
. This broadening allows the injector to receive more detailed contextual information, enhancing its capability to make more informed decisions about which implementation to provide.
Now you can do something like this:
<?php
declare(strict_types=1);
namespace App\Application;
final class SomeService
{
public function __construct(
#[DatabaseDriver(name: 'mysql')]
public DatabaseInterface $database,
#[DatabaseDriver(name: 'sqlite')]
public DatabaseInterface $database1,
) {
}
}
And example of injector
<?php
declare(strict_types=1);
namespace App\Application;
use Spiral\Core\Container\InjectorInterface;
final class DatabaseInjector implements InjectorInterface
{
public function createInjection(\ReflectionClass $class, \ReflectionParameter|null|string $context = null): object
{
$driver = $context?->getAttributes(DatabaseDriver::class)[0]?->newInstance()?->name ?? 'mysql';
return match ($driver) {
'sqlite' => new Sqlite(),
'mysql' => new Mysql(),
default => throw new \InvalidArgumentException('Invalid database driver'),
};
}
}
2. Added ability to suppress non-reportable exceptions
The ability to exclude reporting of certain exceptions has been added. By default, Spiral\Http\Exception\ClientException
, Spiral\Filters\Exception\ValidationException
, and Spiral\Filters\Exception\AuthorizationException
are ignored.
Exceptions can be excluded from the report in several different ways:
Attribute NonReportable
To exclude an exception from the report, you need to add the Spiral\Exceptions\Attribute\NonReportable
attribute to the exception class.
use Spiral\Exceptions\Attribute\NonReportable;
#[NonReportable]
class AccessDeniedException extends \Exception
{
// ...
}
Method dontReport
Invoke the dontReport
method in the Spiral\Exceptions\ExceptionHandler
class. This can be done using the bootloader.
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Exceptions\ExceptionHandler;
final class AppBootloader extends Bootloader
{
public function init(ExceptionHandler $handler): void
{
$handler->dontReport(EntityNotFoundException::class);
}
}
Overriding the property nonReportableExceptions
You can override the nonReportableExceptions property with predefined exceptions.
3. Better container scopes
This release marks a foundational shift in how we approach dependency management within our framework, setting the stage for the upcoming version 4.0. With these changes, we're not just tweaking the system; we're laying down the groundwork for more robust, efficient, and intuitive handling of dependencies in the long run. To ensure everyone can make the most out of these updates, we will be rolling out a series of tutorials aimed at helping you navigate through the new features and enhancements.
Context
The context is also extended on other container methods get()
(see #1041)
Scopes
Default scope fix
If the container scope is not open, it is assumed by default that dependencies are resolved in the scope named root
. Now when calling invoke()
, make()
, get()
, the container will globally register itself with the root
scope if no other scope was opened. Before this, the container resolved dependencies as if outside the scope.
Scoped Interface
The experimental ContainerScopeInterface
has been removed. The method getBinder(?string $scope = null): BinderInterface
has been moved to BinderInterface
at the annotation level.
runScope method
The Container::runScoped()
method (in the implementation) was additionally marked as @deprecated
and will be removed when its use in tests is reduced to zero. Instead of the Container::runScoped()
, you should now call the old Container::runScope()
, but with passing the DTO Spiral\Core\Scope
instead of the list of bindings.
$container->runScope(
new Scope(name: 'auth', bindings: ['actor' => new Actor()]),
function(ContainerInterface $container) {
dump($container->get('actor'));
},
);
Scope Proxy
Instead of the now removed ContainerScopeInterface::getCurrentContainer()
method, the user is offered another way to get dependencies from the container of the current scope - a proxy.
The user can mark the dependency with a new attribute Spiral\Core\Attribute\Proxy
.
Warning: The dependency must be defined by an interface.
When resolving dependencies, the container will create a proxy object that implements the specified interface. When calling the interface method, the proxy object will get the container of the current scope, request the dependency from it using its interface, and start the necessary method.
final class Service
{
public function __construct(
#[Proxy] public LoggerInterface $logger,
) {
}
public function doAction() {
// Equals to
// $container->getCurrentContainer()->get(LoggerInterface::class)->log('foo')
$this->logger->log('foo');
}
}
Important nuances:
- The proxy refers to the active scope of the container, regardless of the scope in which the proxy object was created.
- Each call to the proxy method pulls the container. If there are many calls within the method, you should consider making a proxy for the container
// class function __construct( #[Proxy] private Dependency $dep, #[Proxy] private ContainerInterface $container, ) {} function handle() { // There are four calls to the container under the hood. $this->dep->foo(); $this->dep->bar(); $this->dep->baz(); $this->dep->red(); // Only two calls to the container and caching the value in a variable // The first call - getting the container through the proxy // The second - explicit retrieval of the dependency from the container $dep = $this->container->get(Dependency::class); $dep->foo(); $dep->bar(); $dep->baz(); $dep->red(); }
- The proxied interface should not contain a constructor signature (although this sometimes happens).
- Calls to methods outside the interface will not be proxied. This option is possible in principle, but it is disabled. If it is absolutely necessary, we will consider whether to enable it.
- The destructor method call will not be proxied.
Proxy
Added the ability to bind an interface as a proxy using the Spiral\Core\Config\Proxy
configuration. This is useful in cases where a service needs to be used within a specific scope but must be accessible within the container for other services in root or other scopes (so that a service requiring the dependency can be successfully created and used when needed in the correct scope).
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Core\BinderInterface;
use Spiral\Core\Config\Proxy;
use Spiral\Framework\ScopeName;
use Spiral\Http\PaginationFactory;
use Spiral\Pagination\PaginationProviderInterface;
final class PaginationBootloader extends Bootloader
{
public function __construct(
private readonly BinderInterface $binder,
) {
}
public function defineSingletons(): array
{
$this->binder
->getBinder(ScopeName::Http)
->bindSingleton(PaginationProviderInterface::class, PaginationFactory::class);
$this->binder->bind(
PaginationProviderInterface::class,
new Proxy(PaginationProviderInterface::class, true) // <-------
);
return [];
}
}
DeprecationProxy
Similar to Proxy, but also allows outputting a deprecation message when attempting to retrieve a dependency from the container. In the example below, we use two bindings, one in scope and one out of scope with Spiral\Core\Config\DeprecationProxy
. When requesting the interface in scope, we will receive the service, and when requesting it out of scope, we will receive the service and a deprecation message.
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Core\BinderInterface;
use Spiral\Core\Config\DeprecationProxy;
use Spiral\Framework\ScopeName;
use Spiral\Http\PaginationFactory;
use Spiral\Pagination\PaginationProviderInterface;
final class PaginationBootloader extends Bootloader
{
public function __construct(
private readonly BinderInterface $binder,
) {
}
public function defineSingletons(): array
{
$this->binder
->getBinder(Sco...
v3.11.1
What's Changed
- Adding Listeners to the
tokenizer:Info
console command by @msmakouz in #1042 - Fixed finalize for listeners by @andrey-mokhov in #1043
- Adding a console command for validating Tokenizer listeners by @msmakouz in #1040
- Fixes proto serializer unit tests by @butschster in #1046
- Freeze phpunit on v10.5.3 by @roxblnfk in #1047
Full Changelog: 3.11.0...3.11.1
v3.11.0
What's Changed
- [spiral/serializer] Adding Google protobuf serializer by @msmakouz in #1031
- [spiral/console] Add the ability to use enum as console option by @msmakouz in #1036
- [spiral/debug] Adding
Spiral\Debug\Config\DebugConfig
by @msmakouz in #1026 - [spiral/router] Implement Custom Path Segment Encoding in Router by @butschster in #1039
Other changes
- [spiral/tokenizer] Write logs only if debug is enabled by @msmakouz in #1027
- [spiral/tokenizer] Additional invocation of listeners in the
booting
event by @msmakouz in #1033 - Add PHP 8.3 to GitHub actions by @msmakouz in #1028
Bugfixes
- [spiral/storage] Fixed configuration for Async AWS S3 adapter by @msmakouz in #1029
- [spiral/files] Fixing UNC path normalization by @msmakouz in #1030
- [spiral/console] Check that the stream is not null before calling the posix_isatty function by @msmakouz in #1034
- [spiral/core] Disable scoped container leak check; dedeprecate method
runScope()
by @roxblnfk in #1038
Full Changelog: 3.10.1...3.11.0
v3.10.1
What's Changed
- [spiral/tokenizer] Add
namedArguments
parameter to the TargetAttribute by @msmakouz in #1018 - [spiral/filters] Fix method supports in UuidCaster by @msmakouz in #1019
- Allow Symfony 7.0 components by @msmakouz in #1021
- Fix Psalm issues by @msmakouz in #1022
- [spiral/queue] Return the job type as a class name by @msmakouz in #1023
- [spiral/queue] Add Job Handler searching if a string is used as the job name by @msmakouz in #1024
Full Changelog: 3.10.0...3.10.1
v3.10.0
Improvements
1. Improved the bootloader registration process
We've introduced a new interface, Spiral\Boot\Bootloader\BootloaderRegistryInterface
, and its implementation, Spiral\Boot\Bootloader\BootloaderRegistry
. This update makes the process of registering bootloaders in Spiral much simpler and more flexible.
Now, you can easily manage your bootloaders using our spiral-packages/discoverer
package. This package helps you automatically find and register bootloaders specified in your composer.json
like in example below:
{
// ...
"extra": {
"spiral": {
"bootloaders": [
"Spiral\\Monolog\\Bootloader\\DotenvBootloader",
"Spiral\\DotEnv\\Bootloader\\MonologBootloader"
],
"dont-discover": [
"spiral-packages/event-bus"
]
}
}
}
This feature also allows for bootloader discovery from various sources, such as configuration files or other custom methods.
2. Enhanced Error Handling for Incorrect Data Types in Filters.
The spiral/filters
package in Spiral's ecosystem is designed for filtering and, optionally, validating input data. It enables you to set specific rules for each input field, ensuring that the data received matches the expected format and other defined criteria.
For example, consider this filter:
namespace App\Endpoint\Web\Filter;
use Spiral\Filters\Attribute\Input\Query;
use Spiral\Filters\Model\Filter;
final class UserFilter extends Filter
{
#[Query(key: 'username')]
public string $username;
}
In this scenario, the username
is expected to be a string. However, there might be instances where the input data is of the wrong type, such as an array
or an integer
. Previously, such mismatches would result in an exception being thrown by the application.
With the new update, we've added the capability to specify custom error messages for these mismatches. This enhancement allows for more graceful handling of incorrect data types. Here's how you can implement it:
namespace App\Endpoint\Web\Filter;
use Spiral\Filters\Attribute\Input\Query;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Attribute\CastingErrorMessage;
final class UserFilter extends Filter
{
#[Query(key: 'username')]
#[CastingErrorMessage('Invalid type')]
public string $username;
}
This update ensures that your application can provide clearer feedback when encountering data of an unexpected type.
3. Added the ability to configure bootloaders via BootloadConfig
There is a new DTO class Spiral\Boot\Attribute\BootloadConfig
which enables the inclusion or exclusion of bootloaders, passing parameters that will be forwarded to the init and boot methods of the bootloader, and dynamically adjusting the bootloader loading based on environment variables.
Here is a simple example:
namespace App\Application;
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Prototype\Bootloader\PrototypeBootloader;
class Kernel extends \Spiral\Framework\Kernel
{
// ...
public function defineBootloaders(): array
{
return [
// ...
PrototypeBootloader::class => new BootloadConfig(allowEnv: ['APP_ENV' => ['local', 'dev']]),
// ...
];
}
// ...
}
In this example, we specified that the PrototypeBootloader
should be loaded only if the environment variable APP_ENV
is defined and has a value of local
or dev
.
You can also define a function that returns a BootloadConfig
object. This function can take arguments, which might be obtained from the container.
PrototypeBootloader::class => static fn (AppEnvironment $env) => new BootloadConfig(enabled: $env->isLocal()),
You can also use BootloadConfig
class as an attribute to control how a bootloader behaves.
use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\Bootloader;
#[BootloadConfig(allowEnv: ['APP_ENV' => 'local'])]
final class SomeBootloader extends Bootloader
{
}
Attributes are a great choice when you want to keep the configuration close to the bootloader's code. It's a more intuitive way to set up bootloaders, especially in cases where the configuration is straightforward and doesn't require complex logic.
By extending BootloadConfig
, you can create custom classes that encapsulate specific conditions under which bootloaders should operate.
Here's an example
class TargetRRWorker extends BootloadConfig {
public function __construct(array $modes)
{
parent::__construct(
env: ['RR_MODE' => $modes],
);
}
}
// ...
class Kernel extends Kernel
{
public function defineBootloaders(): array
{
return [
HttpBootloader::class => new TargetRRWorker(['http']),
RoutesBootloader::class => new TargetRRWorker(['http']),
// Other bootloaders...
];
}
}
Other changes
Bugfixes
Full Changelog: 3.9.1...3.10
v3.9.0
Improvements
1. Added RetryPolicyInterceptor
for Queue component
Added Spiral\Queue\Interceptor\Consume\RetryPolicyInterceptor
to enable automatic job retries with a configurable retry policy. To use it, need to add the Spiral\Queue\Attribute\RetryPolicy
attribute to the job class:
use Spiral\Queue\Attribute\RetryPolicy;
use Spiral\Queue\JobHandler;
#[RetryPolicy(maxAttempts: 3, delay: 5, multiplier: 2)]
final class Ping extends JobHandler
{
public function invoke(array $payload): void
{
// ...
}
}
Create an exception that implements interface Spiral\Queue\Exception\RetryableExceptionInterface
:
use Spiral\Queue\Exception\RetryableExceptionInterface;
use Spiral\Queue\RetryPolicyInterface;
class RetryException extends \DomainException implements RetryableExceptionInterface
{
public function isRetryable(): bool
{
return true;
}
public function getRetryPolicy(): ?RetryPolicyInterface
{
return null;
}
}
The exception must implement the two methods isRetryable and getRetryPolicy. These methods can override the retry behavior and cancel the re-queue- or change the retry policy.
If a RetryException
is thrown while a job runs, the job will be re-queued according to the retry policy.
Pull request: #980 by @msmakouz
2. Added the ability to configure serializer and job type for Queue component via attributes
Added ability to configure serializer and job type using attributes.
use App\Domain\User\Entity\User;
use Spiral\Queue\Attribute\Serializer;
use Spiral\Queue\Attribute\JobHandler as Handler;
use Spiral\Queue\JobHandler;
#[Handler('ping')]
#[Serializer('marshaller-json')]
final class Ping extends JobHandler
{
public function invoke(User $payload): void
{
// ...
}
}
Pull request: #990 by @msmakouz
3. Added the ability to configure the Monolog messages format
Now you can configure the Monolog messages format via environment variable MONOLOG_FORMAT
.
MONOLOG_FORMAT="[%datetime%] %level_name%: %message% %context%\n"
Pull request: #994 by @msmakouz
4. Added the ability to register additional translation directories
Now you can register additional directories with translation files for the Translator component. This can be useful when developing additional packages for the Spiral Framework, where the package may provide translation files (for example, validators). Translation files in an application can override translations from additional directories.
A directory with translations can be registered via the Spiral\Bootloader\I18nBootloader
bootloader or translator.php
configuration file.
Via I18nBootloader bootloader
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\I18nBootloader;
final class AppBootloader extends Bootloader
{
public function init(I18nBootloader $i18n): void
{
$i18n->addDirectory('some/directory');
}
}
Via configuration file
return [
// ...
'directories' => [
'some/directory'
],
// ...
];
Pull request: #996 by @msmakouz
5. Added the ability to store snapshots using Storage
component
Have you ever faced challenges in storing your app's exception snapshots when working with stateless applications? We've got some good news. With our latest update, we've made it super easy for you.
By integrating with the spiral/storage
component, we're giving your stateless apps the power to save exception snapshots straight into S3.
Why is this awesome for you?
- Simplified Storage: No more juggling with complex storage solutions. Save snapshots directly to S3 with ease.
- Tailored for Stateless Apps: Designed specifically for stateless applications, making your deployments smoother and hassle-free.
- Reliability: With S3's proven track record, know your snapshots are stored safely and can be accessed whenever you need.
How to use:
- Switch to the new bootloader: Swap out
Spiral\Bootloader\SnapshotsBootloader
withSpiral\Bootloader\StorageSnapshotsBootloader
. - Set up your bucket for snapshot storage and specify the desired bucket using the
SNAPSHOTS_BUCKET
environment variable. - Modify
app/src/Application/Bootloader/ExceptionHandlerBootloader.php
to replace the exception reporterSpiral\Exceptions\Reporter\FileReporter
withSpiral\Exceptions\Reporter\StorageReporter
in theboot
method (an example for a default installation of spiral/app).
Pull request: #986 by @msmakouz
6. Introduced new prototype:list
console command for listing prototype dependencies
The prototype:list
command is a super cool addition to our Spiral Framework. It helps developers by providing an easy way to list all the classes registered in the Spiral\Prototype\PrototypeRegistry
. These registered classes are essential for project prototyping.
How to Use It
Using the command is simple. Just run the following line in your terminal:
php app.php prototype:list
Once you do that, you'll get a neat table that displays all the registered prototypes, including their names and target classes. This makes it incredibly easy to see what's available for your project prototyping needs.
+------------------+-------------------------------------------------------+
| Name: | Target: |
+------------------+-------------------------------------------------------+
| app | App\Application\Kernel |
| classLocator | Spiral\Tokenizer\ClassesInterface |
| console | Spiral\Console\Console |
| broadcast | Spiral\Broadcasting\BroadcastInterface |
| container | Psr\Container\ContainerInterface |
| encrypter | Spiral\Encrypter\EncrypterInterface |
| env | Spiral\Boot\EnvironmentInterface |
| files | Spiral\Files\FilesInterface |
| guard | Spiral\Security\GuardInterface |
| http | Spiral\Http\Http |
| i18n | Spiral\Translator\TranslatorInterface |
| input | Spiral\Http\Request\InputManager |
| session | Spiral\Session\SessionScope |
| cookies | Spiral\Cookies\CookieManager |
| logger | Psr\Log\LoggerInterface |
| logs | Spiral\Logger\LogsInterface |
| memory | Spiral\Boot\MemoryInterface |
| paginators | Spiral\Pagination\PaginationProviderInterface |
| queue | Spiral\Queue\QueueInterface |
| queueManager | Spiral\Queue\QueueConnectionProviderInterface |
| request | Spiral\Http\Request\InputManager |
| response | Spiral\Http\ResponseWrapper |
| router | Spiral\Router\RouterInterface |
| snapshots | Spiral\Snapshots\SnapshotterInterface |
| storage | Spiral\Storage\BucketInterface |
| serializer | Spiral\Serializer\SerializerManager |
| validator | Spiral\Validation\ValidationInterface |
| views | Spiral\Views\ViewsInterface |
| auth | Spiral\Auth\AuthScope |
| authTokens | Spiral\Auth\TokenStorageInterface |
| cache | Psr\SimpleCache\CacheInterface |
| cacheManager | Spiral\Cache\CacheStorageProviderInterface |
| exceptionHandler | Spiral\Exceptions\ExceptionHandlerInterface |
| users | App\Infrastructure\Persistence\CycleORMUserRepository |
+------------------+-------------------------------------------------------+
Why It Matters
This new feature enhances developer productivity and ensures that we're making the most of the Spiral Framework's capabilities. It provides clarity on available prototypes, which can be crucial when building and extending our projects.
Note
You might notice that we've also renamed the oldprototype:list
command toprototype:usage
to better align with its purpose.
Pull request: #1003 by @msmakouz
Other changes
- [spiral/scaffolder] Changed Queue job handler payload type from
array
tomixed
by @msmakouz in #992 - [spiral/monolog-bridge] Set
bubble
astrue
by default in logRotate method by @msmakouz in #997 - [spiral/prototype] Initialize PrototypeRegistry only when registry requires from container by @msmakouz in #1005
Bug fixes
- [spiral/router] Fixed issue with Registering Routes Containing Host using RoutesBootloader by @msmakouz in #990
- [spiral/reactor] Fix Psalm issues and tests in Reactor by @msmakouz in #1002
Full Changelog: ...
v3.8.4
What's Changed
- Fixing
visibility
in the Storage configuration by @msmakouz in #977 - [spiral/tokenizer] Improving
Tokenizer Info
console command by @msmakouz in #979 - Assigning
null
instead of usingunset
in the reset method by @msmakouz in #985 - [spiral/core] Added checking
hasInstance
in the parent scope by @msmakouz in #981 - Remove TG link from README.md by @roxblnfk in #987
Full Changelog: 3.8.3...3.8.4
v3.8.3
What's Changed
Full Changelog: 3.8.2...3.8.3