Releases: spiral/framework
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
v3.8.2
What's Changed
Full Changelog: 3.8.1...3.8.2
v3.8.1
What's Changed
- Adding the ability to configure the Attributes cache or disable the cache by @msmakouz in #968
- Fixes Event Dispatcher rebinding by @butschster in #970
- Fixes incorrect Concatenation of Route Pattern with Prefix in Route Group by @butschster in #967
- Fixes loading ENV variables from dotenv in Kernel System section by @butschster in #972
Full Changelog: 3.8.0...3.8.1
v3.8.0
Improvements
1. Added instructions feature for scaffold generator
We are excited to announce a new feature, that enhances the scaffold generation process by providing clear and concise instructions on the next steps to be taken after generating various classes for your application.
With the Instructions feature, you can generate classes for the following components:
- Bootloader
- Command
- Config
- Controller
- Request Filter
- Job Handler
- Middleware
Each generated class comes with a set of instructions on what to do next, ensuring a smooth and hassle-free development experience.
Let's take a look at an example:
php app.php create:command ScanSite
Will display
Declaration of 'ScanSiteCommand' has been successfully written into 'app/src/Endpoint/Console/ScanSiteCommand.php'.
Next steps:
1. Use the following command to run your command: 'php app.php scan:site'
2. Read more about user Commands in the documentation: https://spiral.dev/docs/console-commands
To experience the Instructions feature, simply use the scaffold generator commands for the respective components and follow the provided instructions for each generated class. The documentation links accompanying the instructions serve as a valuable resource for in-depth explanations and best practices.
By @butschster in #945
2. Adds the ability to specify a custom directory for specific declaration types in the [spiral/scaffolder] component
TheΒ spiral/scaffolder
Β component provides the ability to generate various classes, such as bootloaders, HTTP controllers, console commands, request filters, and Cycle ORM entities. These generated classes are stored in a directory specified in the configuration file. However, there was no way to specify a custom directory for specific declaration types.
This PR added a new configuration option to the declarations array that allows for the specification of a custom directory for a specific declaration type where to store a generated file.
UsingΒ directory
Β in theΒ ScaffolderBootloader::addDeclaration
Β method:
class ScaffolderBootloader extends Bootloader
{
//...
public function boot(BaseScaffolderBootloader $scaffolder, DatabaseSeederConfig $config): void
{
$scaffolder->addDeclaration(Declaration\FactoryDeclaration::TYPE, [
'namespace' => $config->getFactoriesNamespace(),
'postfix' => 'Factory',
'class' => Declaration\FactoryDeclaration::class,
'baseNamespace' => 'Database',
'directory' => $config->getFactoriesDirectory() // <=============
]);
$scaffolder->addDeclaration(Declaration\SeederDeclaration::TYPE, [
'namespace' => $config->getSeedersNamespace(),
'postfix' => 'Seeder',
'class' => Declaration\SeederDeclaration::class,
'baseNamespace' => 'Database',
'directory' => $config->getSeedersDirectory() // <=============
]);
}
}
Via configuration fileΒ app/config/scaffolder.php
return [
'directory' => directory('app') . 'src/',
'declarations' => [
Declaration\BootloaderDeclaration::TYPE => [
'namespace' => 'Application\Bootloader',
'postfix' => 'Bootloader',
'class' => Declaration\BootloaderDeclaration::class,
'directory' => directpry('app') . '/Infrastructure/Bootloader' // <=============
],
],
];
This allows users to customize the directory structure of their generated classes more easily, and improves the overall flexibility of the scaffolder component.
3. Filters improvements
Value casters for Filter properties
Introducing Spiral\Filters\Model\Mapper\Mapper
that sets values for filter properties. It utilizes a collection of casters, each designed to handle a specific type of value.
Default casters include:
Spiral\Filters\Model\Mapper\EnumCaster
Allows the use ofΒ PHP enumsΒ in a filter properties. This caster verifies the property type as an enum and creates the necessary enumeration from the value passed to the filter.
<?php
declare(strict_types=1);
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use App\User\Type;
final class RegisterUser extends Filter
{
#[Post]
public string $name;
#[Post]
public Type $status = Type::Admin;
// ...
}
Spiral\Filters\Model\Mapper\UuidCaster
Allows the use ofΒ ramsey/uuidΒ in a filter properties. This caster confirms the property type as UUID and constructs an UUID object from the string provided to the Filter.
<?php
declare(strict_types=1);
use Spiral\Filters\Attribute\Input\Post;
use Spiral\Filters\Model\Filter;
use Ramsey\Uuid\UuidInterface;
final class UpdateUser extends Filter
{
#[Post]
public UuidInterface $uuid;
// ...
}
Spiral\Filters\Model\Mapper\DefaultCaster
: Used when other casters are unable to assign a value. It sets the property value without any transformations.
Custom casters
Casters are extensible and can be created and added by users within the application. To achieve this, it's necessary to create a custom caster object, implement theΒ Spiral\Filters\Model\Mapper\CasterInterface
Β interface.
Let's take a look at an example:
<?php
declare(strict_types=1);
namespace App\Application\Filter\Caster;
use Spiral\Filters\Model\Mapper\CasterInterface;
final class BooleanCaster implements CasterInterface
{
public function supports(\ReflectionNamedType $type): bool
{
return $type->isBuiltin() && $type->getName() === 'bool';
}
public function setValue(FilterInterface $filter, \ReflectionProperty $property, mixed $value): void
{
$property->setValue($filter, (bool) $value);
}
}
And register it in theΒ Spiral\Filters\Model\Mapper\CasterRegistryInterface
Β viaΒ registerΒ method.
<?php
declare(strict_types=1);
namespace App\Application\Bootloader;
use Spiral\Filters\Model\Mapper\CasterRegistryInterface;
use Spiral\Boot\Bootloader\Bootloader;
use App\Application\Filter\Caster\BooleanCaster;
final class FilerBootloader extends Bootloader
{
public function init(CasterRegistryInterface $registry)
{
$registr->register(new BooleanCaster());
}
}
4. Added TokenStorageScope
This class can be used to access the specific implementation of the token storage that is used in the current container scope.
use Spiral\Auth\TokenStorageScope;
final class SomeController
{
public function index(TokenStorageScope $tokenStorage): string
{
$tokenStorage->load('some-id');
$tokenStorage->create(['id' => 'some-id']);
$tokenStorage->delete($token);
}
}
5. Container improvements
In this update, our primary focus has been on elevating the overall performance of the container
- We have successfully migrated a significant portion of the internal operations from runtime to configuration time.
- Furthermore, we've replaced the previous array-based structure that was utilized to store information about bindings within the container. The new approach involves the utilization of Data Transfer Objects (DTOs), which not only provides a more structured and organized manner of storing binding information but also offers developers a seamless way to configure container bindings using these objects.
These changes together make the container work faster and more efficiently while also using less memory. As a result, your applications should perform better overall.
Additionally,
To demonstrate the enhanced container functionality, here's an example showcasing the new binding configuration:
use Spiral\Core\Config\Factory;
$container->bind(LoggerInterface::class, new Factory(
callable: static function() {
return new Logger(....);
},
singleton: true
))
Read more about new binding here
Scopes
We also added a new container scope interface Spiral\Core\ContainerScopeInterface
that can be used to run code withing isolated IoC scope.
Note
Use it instead ofSpiral\Core\ScopeInterface
, which is deprecated now.
A new interface provides also an ability to bind aliases in a specific scope
use Spiral\Core\ScopeInterface;
final class AppBootloader extends Bootloader
{
public function init(ContainerScopeInterface $container)
{
// Bind into a specific scope
$container->getBinder('http')->bind(...);
// Bind into the current scope
$container->getBinder()->bind(...);
}
}
by @roxblnfk in #941 #935 #936
Singletons
In this release, we've introduced a robust safeguard to prevent accidental overwriting of existing singletons within the container. This new feature ensures a more controlled environment for managing your application's dependencies and provides greater clarity when redefining singletons. In previous versions of the container, users could override existing singleton definitions by redefining them with new configurations. While this flexibility allowed for dynamic changes, it also led to potential confusion and unexpected behavior. To ad...
v3.7.1
What's Changed
- Fixing PromotedParameter in the Reactor by @msmakouz in #922
- Fixed InputScope to allow retrieval of non-bag input sources by @butschster in #926
Full Changelog: 3.7.0...3.7.1
v3.7.0
New Features
1. Added the ability to push objects to a queue
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
2. Added the ability to guess option mode in by property type console commands
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
3. Added the ability to override Scaffolder config partially
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
4. Refactored scaffolder commands
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 #902
Improvements
- Updated Psalm up to v5 by @msmakouz in #906
- Added support for
doctrine/annotations:2.x
package by @msmakouz in #897
Bug Fixes
- Fixed problem with displaying console commands description when it was defined via
DESCRIPTION
constant by @msmakouz in #889 - Fixed applying setters during validation filter objects by @msmakouz in #891
- Fixed the problem with using named parameters in class located by a tokenizer by @butschster in #895
- Fixed elapsed time calculation for LogTracer in spiral/telemetry component by @gam6itko in #919