Bot releases are hidden (Show)
spiral/core
Advanced Context Handling in Injector Implementations by @roxblnfk in https://github.com/spiral/framework/pull/1041
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.
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'),
};
}
}
Add non-reportable exceptions by @msmakouz in https://github.com/spiral/framework/pull/1044
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:
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
{
// ...
}
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);
}
}
nonReportableExceptions
You can override the nonReportableExceptions property with predefined exceptions.
The context is also extended on other container methods get()
(see https://github.com/spiral/framework/pull/1041)
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.
The experimental ContainerScopeInterface
has been removed. The method getBinder(?string $scope = null): BinderInterface
has been moved to BinderInterface
at the annotation level.
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'));
},
);
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:
// 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();
}
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 [];
}
}
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(ScopeName::Http)
->bindSingleton(PaginationProviderInterface::class, PaginationFactory::class);
$this->binder->bind(
PaginationProviderInterface::class,
new DeprecationProxy(PaginationProviderInterface::class, true, ScopeName::Http, '4.0') // <----------
);
return [];
}
}
Added the ability to specify the scope name for the dispatcher using the Spiral\Attribute\DispatcherScope
attribute.
use Spiral\Attribute\DispatcherScope;
use Spiral\Boot\DispatcherInterface;
#[DispatcherScope(scope: 'console')]
final class ConsoleDispatcher implements DispatcherInterface
{
// ...
}
The registration of dispatchers has been changed. The accepted type in the addDispatcher
method of the Spiral\Boot\AbstractKernel
class has been extended from DispatcherInterface
to string|DispatcherInterface
. Before these changes, the method accepted a created DispatcherInterface
object, now it can accept a class name string or an object. In version 4.0, the DispatcherInterface
type will be removed. When passing an object, only its class name will be saved. And when using the dispatcher, its object will be created anew.
Example with ConsoleDispatcher
:
public function init(AbstractKernel $kernel): void
{
$kernel->bootstrapped(static function (AbstractKernel $kernel): void {
$kernel->addDispatcher(ConsoleDispatcher::class);
});
}
The dispatchers are now created in their own scope and receive dependencies that are specified in this scope. But due to the need to check whether the dispatcher can handle the request or not before creating the dispatcher object, the canServe method in dispatchers must be static:
public static function canServe(EnvironmentInterface $env): bool
{
return (PHP_SAPI === 'cli' && $env->get('RR_MODE') === null);
}
This method has been removed from the Spiral\Boot\DispatcherInterface
, for backward compatibility it can be non-static, as it was before (then an object will be created for its call) or static and accept Spiral\Boot\EnvironmentInterface
.
scaffolder:info
console commandAdds
scaffolder:info
console command by @butschster in https://github.com/spiral/framework/pull/1068
Now you can list available commands.
monolog/monolog
v3.x by @msmakouz in https://github.com/spiral/framework/pull/1049
cocur/slugify
4.x by @msmakouz in https://github.com/spiral/framework/pull/1048
league/flysystem
v3.x by @msmakouz in https://github.com/spiral/framework/pull/1050
doctrine/annotations
by @msmakouz in https://github.com/spiral/framework/pull/1059
spiral/translator
Added a check for the existence of a message by @msmakouz in https://github.com/spiral/framework/pull/1062
spiral/translator
Fixed getLocaleDirectory
method by @msmakouz in https://github.com/spiral/framework/pull/1075
spiral/core
Fixed check for the existence of a binding in parent scopes using Conainer::has
by @msmakouz in https://github.com/spiral/framework/pull/1065
spiral/router
Updated route:list
command for customization route class by @iAvenger01 in https://github.com/spiral/framework/pull/1070
spiral/core
Added the ability to configure container via options by @msmakouz in https://github.com/spiral/framework/pull/1082Full Changelog: https://github.com/spiral/framework/compare/3.11.1...3.12.0
Published by butschster 10 months ago
tokenizer:Info
console command by @msmakouz in https://github.com/spiral/framework/pull/1042
Full Changelog: https://github.com/spiral/framework/compare/3.11.0...3.11.1
Published by butschster 10 months ago
Spiral\Debug\Config\DebugConfig
by @msmakouz in https://github.com/spiral/framework/pull/1026
booting
event by @msmakouz in https://github.com/spiral/framework/pull/1033
runScope()
by @roxblnfk in https://github.com/spiral/framework/pull/1038
Full Changelog: https://github.com/spiral/framework/compare/3.10.1...3.11.0
Published by butschster 10 months ago
namedArguments
parameter to the TargetAttribute by @msmakouz in https://github.com/spiral/framework/pull/1018
Full Changelog: https://github.com/spiral/framework/compare/3.10.0...3.10.1
Published by butschster 11 months ago
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.
by @msmakouz in https://github.com/spiral/framework/pull/1015
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.
by @msmakouz in https://github.com/spiral/framework/pull/1016
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...
];
}
}
by @msmakouz in https://github.com/spiral/framework/pull/1017
Full Changelog: https://github.com/spiral/framework/compare/3.9.1...3.10
Published by butschster 12 months ago
RetryPolicyInterceptor
for Queue componentAdded 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: https://github.com/spiral/framework/pull/980 by @msmakouz
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: https://github.com/spiral/framework/pull/990 by @msmakouz
Now you can configure the Monolog messages format via environment variable MONOLOG_FORMAT
.
MONOLOG_FORMAT="[%datetime%] %level_name%: %message% %context%\n"
Pull request: https://github.com/spiral/framework/pull/994 by @msmakouz
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.
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\I18nBootloader;
final class AppBootloader extends Bootloader
{
public function init(I18nBootloader $i18n): void
{
$i18n->addDirectory('some/directory');
}
}
return [
// ...
'directories' => [
'some/directory'
],
// ...
];
Pull request: https://github.com/spiral/framework/pull/996 by @msmakouz
Storage
componentHave 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.
Spiral\Bootloader\SnapshotsBootloader
with Spiral\Bootloader\StorageSnapshotsBootloader
.SNAPSHOTS_BUCKET
environment variable.app/src/Application/Bootloader/ExceptionHandlerBootloader.php
to replace the exception reporter Spiral\Exceptions\Reporter\FileReporter
with Spiral\Exceptions\Reporter\StorageReporter
in the boot
method (an example for a default installation of spiral/app).Pull request: https://github.com/spiral/framework/pull/986 by @msmakouz
prototype:list
console command for listing prototype dependenciesThe 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.
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 |
+------------------+-------------------------------------------------------+
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: https://github.com/spiral/framework/pull/1003 by @msmakouz
array
to mixed
by @msmakouz in https://github.com/spiral/framework/pull/992
bubble
as true
by default in logRotate method by @msmakouz in https://github.com/spiral/framework/pull/997
Full Changelog: https://github.com/spiral/framework/compare/3.8.4...3.9.0
Published by butschster about 1 year ago
visibility
in the Storage configuration by @msmakouz in https://github.com/spiral/framework/pull/977
Tokenizer Info
console command by @msmakouz in https://github.com/spiral/framework/pull/979
null
instead of using unset
in the reset method by @msmakouz in https://github.com/spiral/framework/pull/985
hasInstance
in the parent scope by @msmakouz in https://github.com/spiral/framework/pull/981
Full Changelog: https://github.com/spiral/framework/compare/3.8.3...3.8.4
Published by butschster about 1 year ago
singletons
in the hasInstance
method by @msmakouz in https://github.com/spiral/framework/pull/975
Full Changelog: https://github.com/spiral/framework/compare/3.8.2...3.8.3
Published by butschster about 1 year ago
force
parameter to the bindSingleton
method by @msmakouz in https://github.com/spiral/framework/pull/973
Full Changelog: https://github.com/spiral/framework/compare/3.8.1...3.8.2
Published by butschster about 1 year ago
Full Changelog: https://github.com/spiral/framework/compare/3.8.0...3.8.1
Published by butschster about 1 year ago
TokenStorageScope
by @msmakouz in https://github.com/spiral/framework/pull/931
psr/http-message
v2 by @roxblnfk in https://github.com/spiral/framework/pull/944
countPages
correctly in constructor by @butschster in https://github.com/spiral/framework/pull/928
AuthTransportMiddleware
by @msmakouz in https://github.com/spiral/framework/pull/954
Full Changelog: https://github.com/spiral/framework/compare/3.7.1...3.8.0
Published by butschster over 1 year ago
Full Changelog: https://github.com/spiral/framework/compare/3.7.0...3.7.1
Published by butschster over 1 year ago
Previously, only arrays could be pushed to the queue, but with this feature, developers can now push any other types, such as objects, strings, etc. Now you can use various serializers like Symfony Serializer, Valinor, and Protobuf to serialize and deserialize objects. For example, using a more efficient and compact serialization format like Protobuf can help reduce the size of the data being pushed to the queue, resulting in faster processing times and lower storage costs.
Note
Read more about jobs payload serialization on official documentation
by @msmakouz in https://github.com/spiral/framework/pull/887
This makes it easier for developers to define command options by allowing them to simply define the property type and the console command will automatically guess the appropriate mode for the option.
namespace App\Endpoint\Console;
use Spiral\Console\Attribute\AsCommand;
use Spiral\Console\Attribute\Option;
use Spiral\Console\Command;
#[AsCommand(name: 'create:user', description: 'Create a new user')]
class CreateUserCommand extends Command
{
#[Option]
private bool $active; // InputOption::VALUE_NEGATABLE
#[Option]
private int $age; // InputOption::VALUE_REQUIRED
#[Option]
private int $friends = 0; // InputOption::VALUE_OPTIONAL (property have a default value)
#[Option]
private ?string $address = null; // InputOption::VALUE_OPTIONAL (nullable property)
#[Option]
private array $groups; // InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY
#[Option]
private array $phones = []; // InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY
#[Option]
private ?array $emails; // InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY
public function __invoke(): int
{
// ...
}
}
Note
Read more about console commands on official documentation
by @msmakouz in https://github.com/spiral/framework/pull/893
Previously, if a developer wanted to customize the scaffolder configuration, they had to override the entire default configuration in the scaffolder.php
config file.
With this feature, developers can configure only specific options for each declaration type.
return [
'namespace' => 'App',
'declarations' => [
Declaration\BootloaderDeclaration::TYPE => [
'namespace' => 'Application\Bootloader',
],
Declaration\ConfigDeclaration::TYPE => [
'namespace' => 'Application\Config',
],
Declaration\ControllerDeclaration::TYPE => [
'namespace' => 'Endpoint\Web',
],
Declaration\MiddlewareDeclaration::TYPE => [
'class' => Declaration\MiddlewareDeclaration::class,
'namespace' => 'Application\Http\Middleware',
],
Declaration\CommandDeclaration::TYPE => [
'namespace' => 'Endpoint\Console',
],
Declaration\JobHandlerDeclaration::TYPE => [
'namespace' => 'Endpoint\Job',
'postfix' => 'Job',
],
],
];
It allows developers to customize only the specific declaration types they need, without having to override the entire default declaration configuration. This can make the configuration process simpler and reduce the risk of errors.
Note
Read more about scaffolding on official documentation
by @msmakouz in https://github.com/spiral/framework/pull/918
create:bootloader
command now has an ability to create a domain bootloader with interceptors using the -d option.create:command
- command has been improved with several changes, such as adding the final keyword for class and using PHP attributes for command definition and console command declaration. The command now also has the ability to add arguments and options to the generated console command.// Generate command with arguments and options
php app.php create:command UserRegister -a username -a password -o isAdmin -d "Register a new user"
Will generate
<?php
declare(strict_types=1);
namespace App\Api\Cli\Command;
use Spiral\Console\Attribute\Argument;
use Spiral\Console\Attribute\AsCommand;
use Spiral\Console\Attribute\Option;
use Spiral\Console\Attribute\Question;
use Spiral\Console\Command;
#[AsCommand(name: 'user:register', description: 'Register a new user')]
final class UserRegisterCommand extends Command
{
#[Argument(description: 'Argument description')]
#[Question(question: 'What would you like to name the username argument?')]
private string $username;
#[Argument(description: 'Argument description')]
#[Question(question: 'What would you like to name the password argument?')]
private string $password;
#[Option(description: 'Argument description')]
private bool $isAdmin;
public function __invoke(): int
{
// Put your command logic here
$this->info('Command logic is not implemented yet');
return self::SUCCESS;
}
}
create:filter
command has also been added for filters declaration, which allows developers to generate filters with validation rules.php app.php create:filter CreateUser -p username:post -p tags:post:array -p ip:ip -p token:header -p status:query:int
Will generate
<?php
declare(strict_types=1);
namespace App\Api\Web\Filter;
use Spiral\Filters\Attribute\Input\Header;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Attribute\Input\Query;
use Spiral\Filters\Attribute\Input\RemoteAddress;
use Spiral\Filters\Model\Filter;
final class CreateUserFilter extends Filter
{
#[Post(key: 'username')]
public string $username;
#[Post(key: 'tags')]
public array $tags;
#[RemoteAddress(key: 'ip')]
public string $ip;
#[Header(key: 'token')]
public string $token;
#[Query(key: 'status')]
public int $status;
}
There is also an ability to use validator to validate filter
php app.php create:filter CreateUser -p ... -s
Will generate
<?php
declare(strict_types=1);
namespace App\Api\Web\Filter;
use Spiral\Filters\Attribute\Input\Header;
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Attribute\Input\Query;
use Spiral\Filters\Attribute\Input\RemoteAddress;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;
final class CreateUserFilter extends Filter implements HasFilterDefinition
{
#[Post(key: 'username')]
public string $username;
#[Post(key: 'tags')]
public array $tags;
#[RemoteAddress(key: 'ip')]
public string $ip;
#[Header(key: 'token')]
public string $token;
#[Query(key: 'status')]
public int $status;
public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition(validationRules: [
// Put your validation rules here
]);
}
}
All these improvements can help to streamline the process of generating classes using the scaffolder component, making it easier and more efficient for developers to generate the necessary classes for their application.
Note
Read more about scaffolding on official documentation
by @butschster in https://github.com/spiral/framework/pull/902
doctrine/annotations:2.x
package by @msmakouz in https://github.com/spiral/framework/pull/897
DESCRIPTION
constant by @msmakouz in https://github.com/spiral/framework/pull/889
Published by butschster over 1 year ago
namespace
and comment
options by @butschster in https://github.com/spiral/framework/pull/884
Full Changelog: https://github.com/spiral/framework/compare/3.6.0...3.6.1
Published by butschster over 1 year ago
Spiral Framework 3.6 has been released, introducing a range of new features, performance improvements, and bug fixes.
One notable change is the introduction of a new and improved container, which offers isolated memory scopes for more fine-grained control over how dependencies are resolved. This is useful in situations where you want to ensure that certain dependencies are only used within a specific context and do not bleed over to other parts of the application.
PR: https://github.com/spiral/framework/pull/870
The Container::scope()
method is used to run a true isolated scope, and it takes a callable as its first argument. The callable is executed within the scope, and any bindings passed as the second argument will only be used within that scope.
Named scopes can also be used. This is useful for cases where you have multiple scopes with similar dependencies, but with some variations. Parallel named scopes are also allowed.
When a scope is finished callback execution, all the dependencies that were created in that scope will be destroyed, including singletons. This means that any instances of objects that were created within the scope will no longer be available.
Additionally, the container that was used to run the scope will also be destroyed, which means that any attempts to use the container after the scope has finished execution will result in an exception being thrown.
If you create a named scope using Container::scope()
method, you can access and modify the default bindings for that scope by calling Container::getBinder(string $scope)
method and using the returned Binder instance to make new bindings.
$container = new Container();
// Configure `root` scope bindings (the current instance)
$container->bindSingleton(Interface::class, Implementation::class);
// Configure `request` scope default bindings
// Prefer way to make many bindings
$binder = $container->getBinder('request');
$binder->bindSingleton(Interface::class, Implementation::class);
$binder->bind(Interface::class, factory(...));
Here's an overview of the new attributes in Spiral's new container:
#[Singleton]
: This attribute is used to mark a class as a singleton. This attribute can be used in addition to the traditional bindSingleton()
method.
#[Scope(string $name)]
: This attribute is used to set a scope in which a dependency can be resolved.
#[Finalize(string $method)]
: This an experimental attribute is used to define a finalize method for a class. The finalize method will be called before the scope is being destroyed. This can be used to perform any necessary cleanup operations.
And also it's now fully compatible with PHP 8.2 Fibers. The static class \Spiral\Core\ContainerScope::getContainer()
will return correct container instance for the current fiber and scope.
Additionally, We improved application performance when utilizing tokenizer components. Specifically, the introduction of a TOKENIZER_CACHE_TARGETS
environment variable now allows for tokenizer listener caching. Upon the initial application bootstrapping, all found classes for each tokenizer listener will be cached. Subsequent bootstrapping processes will then retrieve the cached data, leading to faster and more efficient performance.
PR: https://github.com/spiral/framework/pull/878
Developers can now create console commands using attributes.
PR: https://github.com/spiral/framework/pull/872
Furthermore, if required arguments are not provided when calling a console command, the framework will prompt users to input the missing arguments rather than displaying an error. This is particularly useful for scaffolder commands like php app.php create:controller
, where developers previously needed to remember which arguments were required. With this new feature, all required arguments will be automatically prompted for, streamlining the development process.
When creating a console command, developers can use the Spiral\Console\Attribute\Question
attribute to specify the question text that should be displayed when the command is run and a required argument is missing.
final class CreateUser extends Command
{
#[Argument]
#[Question(question: 'Provide the User Email')]
private string $email;
}
Another update is that the Setter attribute for the spiral/filters component is now repeatable. This means that it can be used multiple times on the same property, enabling more flexible and powerful filtering options.
class StoreUserFilter extends Filter
{
#[Post]
#[Setter(filter: 'strval')]
#[Setter('ltrim', '-')]
#[Setter('rtrim', ' ')]
#[Setter('htmlspecialchars')]
public string $name;
}
PR: https://github.com/spiral/framework/pull/874
Spiral also includes a new feature for cache storage aliases. Developers can now configure a key prefix for each alias, providing more granular control over caching options.
PR: https://github.com/spiral/framework/pull/869
return [
'aliases' => [
'user-data' => [
'storage' => 'in-memory',
'prefix' => 'user_'
],
'blog-data' => [
'storage' => 'in-memory',
'prefix' => 'blog_'
],
],
'storages' => [
'in-memory' => [
'type' => 'roadrunner-local'
],
],
];
This allows developers to use the same cache storage for multiple purposes while still maintaining separate cache keys for each purpose. The prefix option can be used to differentiate cache keys and ensure that they do not overlap, improving the reliability and accuracy of caching in the application.
// Cache Manager $cache
$cache->storage('user-data')->set('data', 'foo');
$cache->storage('blog-data')->set('data', 'bar');
// Result
array:2 [
"user_data" => array:2 [
"value" => "foo"
"timestamp" => 1677694480
]
"blog_data" => array:2 [
"value" => "bar"
"timestamp" => 1677694480
]
]
Another new feature is the ability to register a custom mail transport factory via Bootloader. This can be useful in cases where developers want to use a non-standard mail transport with the SendIt component.
PR: https://github.com/spiral/framework/pull/873
To register a new mail transport factory, developers can create a new Bootloader class
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\SendIt\TransportRegistryInterface;
use Symfony\Component\Mailer\Transport\c;
class AppBootloader extends Bootloader
{
public function boot(TransportRegistryInterface $registry): void
{
$registry->registerTransport(new SendmailTransportFactory(...));
}
}
Spiral Framework 3.6 also includes several bug fixes to improve the reliability and stability of the framework.
$service = new Service();
$container->bindSingleton(Service::class, $service);
$service === $container->make(Service::class); // true
$service === $container->make(Service::class, ['foo' => 'bar']); // false
PR: https://github.com/spiral/framework/pull/862
Updating PrototypeTrait DOCComment... DOCComment is missing
no longer appears.PR: https://github.com/spiral/framework/pull/865
PR: https://github.com/spiral/framework/pull/868
Full Changelog: https://github.com/spiral/framework/compare/3.5.0...3.6.0
Published by butschster almost 2 years ago
Spiral\Router\Registry\RoutePatternRegistryInterface
to allow for easier management of route patterns. by @kastahov in https://github.com/spiral/framework/pull/847
VERBOSITY_LEVEL
. by @kastahov in https://github.com/spiral/framework/pull/846
defineSystemBootloaders
to allow for more flexibility in defining system bootloaders. by @kastahov in https://github.com/spiral/framework/pull/845
Full Changelog: https://github.com/spiral/framework/compare/3.4.0...3.5.0
Published by butschster almost 2 years ago
Spiral\Boot\BootloadManager\BootloadManager
is deprecated. Will be removed in version v4.0. https://github.com/spiral/framework/pull/840
[[ ... ]]
when don't use Translator component by @butschster in https://github.com/spiral/framework/pull/843
PATCH
method into route:list
command by @gam6itko in https://github.com/spiral/framework/pull/839
Spiral\Boot\BootloadManager\InitializerInterface
. This will allow changing the implementationSpiral\Boot\BootloadManager\StrategyBasedBootloadManager
. It allows the implementation of afinal
from the Spiral\Boot\BootloadManager\Initializer
class by @msmakouz https://github.com/spiral/framework/pull/840
Spiral\Telemetry\Bootloader\TelemetryBootloader
dependency to QueueBootloader by @butschster in https://github.com/spiral/framework/pull/837
Spiral\Core\Container
by @msmakouz in https://github.com/spiral/framework/pull/844
Full Changelog: https://github.com/spiral/framework/compare/3.3.0...3.4.0
Published by butschster almost 2 years ago
Spiral\Auth\Middleware\Firewall\RedirectFirewall
by @msmakouz in https://github.com/spiral/framework/pull/827
TokenStorageProvider
to able manage token storages by @kastahov in https://github.com/spiral/framework/pull/826
Spiral\Http\Middleware\ErrorHandlerMiddleware
by @msmakouz in https://github.com/spiral/framework/pull/833
Full Changelog: https://github.com/spiral/framework/compare/3.2.0...3.3.0
Published by butschster almost 2 years ago
Full Changelog: https://github.com/spiral/framework/compare/3.1.0...3.2.0
Published by butschster about 2 years ago
ValidationHandlerMiddleware
for handling filter validation exception. by @butschster in https://github.com/spiral/framework/pull/803
Spiral\Stempler\Transform\Import\Bundle
by @msmakouz in https://github.com/spiral/framework/pull/800
AuthorizationStatus::$topics
property by @butschster in https://github.com/spiral/framework/pull/804
symfony/console
version by @msmakouz in https://github.com/spiral/framework/pull/795
ValidationProvider
by @msmakouz in https://github.com/spiral/framework/pull/794
Full Changelog: https://github.com/spiral/framework/compare/3.0.0...3.1.0