Skip to content

Releases: spiral/framework

v3.10.1

12 Dec 12:03
Compare
Choose a tag to compare

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

24 Nov 08:33
Compare
Choose a tag to compare

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.

by @msmakouz in #1015

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.

by @msmakouz in #1016

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...
        ];
    }
}

by @msmakouz in #1017

Other changes

  • [spiral/reactor] Up min version of nette/php-generator by @msmakouz in #1014

Bugfixes

  • [spiral/queue] Add RetryPolicyInterface instead of RetryPolicy by @msmakouz in #1012

Full Changelog: 3.9.1...3.10

v3.9.0

24 Oct 14:35
Compare
Choose a tag to compare

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?

  1. Simplified Storage: No more juggling with complex storage solutions. Save snapshots directly to S3 with ease.
  2. Tailored for Stateless Apps: Designed specifically for stateless applications, making your deployments smoother and hassle-free.
  3. Reliability: With S3's proven track record, know your snapshots are stored safely and can be accessed whenever you need.

How to use:

  1. Switch to the new bootloader: Swap out Spiral\Bootloader\SnapshotsBootloader with Spiral\Bootloader\StorageSnapshotsBootloader.
  2. Set up your bucket for snapshot storage and specify the desired bucket using the SNAPSHOTS_BUCKET environment variable.
  3. Modify 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: #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 old prototype:list command to prototype:usage to better align with its purpose.

Pull request: #1003 by @msmakouz

Other changes

  1. [spiral/scaffolder] Changed Queue job handler payload type from array to mixed by @msmakouz in #992
  2. [spiral/monolog-bridge] Set bubble as true by default in logRotate method by @msmakouz in #997
  3. [spiral/prototype] Initialize PrototypeRegistry only when registry requires from container by @msmakouz in #1005

Bug fixes

  1. [spiral/router] Fixed issue with Registering Routes Containing Host using RoutesBootloader by @msmakouz in #990
  2. [spiral/reactor] Fix Psalm issues and tests in Reactor by @msmakouz in #1002

Full Changelog: ...

Read more

v3.8.4

08 Sep 11:27
Compare
Choose a tag to compare

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 using unset 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

29 Aug 15:51
Compare
Choose a tag to compare

What's Changed

  • Checking singletons in the hasInstance method by @msmakouz in #975

Full Changelog: 3.8.2...3.8.3

v3.8.2

18 Aug 10:16
Compare
Choose a tag to compare

What's Changed

  • Adding force parameter to the bindSingleton method by @msmakouz in #973

Full Changelog: 3.8.1...3.8.2

v3.8.1

16 Aug 17:53
Compare
Choose a tag to compare

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

14 Aug 17:56
Compare
Choose a tag to compare

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.

by @msmakouz in #925

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());
    }
}

by @msmakouz in #961

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);
    }
}

by @msmakouz in #931

5. Container improvements

In this update, our primary focus has been on elevating the overall performance of the container

  1. We have successfully migrated a significant portion of the internal operations from runtime to configuration time.
  2. 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 of Spiral\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...

Read more

v3.7.1

22 Apr 07:56
Compare
Choose a tag to compare

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

14 Apr 07:58
Compare
Choose a tag to compare

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

by @msmakouz in #887

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

by @msmakouz in #893

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

by @msmakouz in #918

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

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