Releases: spiral/framework
3.14.8
3.14.7
3.14.6
3.14.5
3.14.4
What's Changed
- Container scope related fixes by @roxblnfk in #1149:
- [spiral/router] Router now uses proxied container to create middlewares in a right scope.
- [spiral/router] Better binding for the interceptor handler.
DebugBootloader
now uses a Factory Proxy to resolve collectors.
Unresolved collectors don't break state populating flow.
Full Changelog: 3.14.3...3.14.4
3.14.3
3.14.2
What's Changed
Fixes:
- Fix concurrent writing and reading on workers boot by @wapmorgan in #1137
- Fix Container Proxy recursion in ContainerScope by @roxblnfk in #1145
Code quality:
- Apply php7.4 in Rector preset by @samsonasik in #1134
- Register tests directory to Rector config path by @samsonasik in #1135
- Add runtime cache listeners to .gitignore by @samsonasik in #1138
- Apply Rector on src/*/tests for tests inside each components by @samsonasik in #1136
- Apply php8.0 in Rector preset by @samsonasik in #1139
- Bump Rector to 1.2.5 by @samsonasik in #1143
- Apply php8.1 in Rector preset by @samsonasik in #1141
New Contributors
- @wapmorgan made their first contribution in #1137
Full Changelog: 3.14.1...3.14.2
3.14.1
What's Changed
Fixes
- Router: fix fallback handler in
AbstractTarget
by @roxblnfk in #1132 - Added compatibility constraints with
roadrunner-bridge
<3.7
andsapi-bridge
<1.1
Code quality
- Bump to Rector 1.2.4 by @samsonasik in #1129
- Enable Rector rules
RemoveUselessParamTagRector
andRemoveUselessReturnTagRector
by @samsonasik in #1130 - Apply php7.3 in Rector preset by @samsonasik in #1133
Full Changelog: 3.14.0...3.14.1
3.14.0
Warning
If you are using spiral/roadrunner-bridge
, you need to update it to version ^3.7
or ^4.0
.
New Interceptors
The HMVC
package has been deprecated. It has been replaced by the new spiral/interceptors
package, where we have reworked the interceptors. The basic principle remains the same, but the interface is now more understandable and convenient.
InterceptorInterface
In the old CoreInterceptorInterface
, the $controller
and $action
parameters caused confusion by creating a false association with HTTP controllers. However, interceptors are not tied to HTTP and are used universally. Now, instead of $controller
and $action
, we use the Target definition, which can point to more than just class methods.
The $parameters
parameter, which is a list of arguments, has now been moved to the CallContextInterface
invocation context.
/** @deprecated Use InterceptorInterface instead */
interface CoreInterceptorInterface
{
public function process(string $controller, string $action, array $parameters, CoreInterface $core): mixed;
}
interface InterceptorInterface
{
public function intercept(CallContextInterface $context, HandlerInterface $handler): mixed;
}
CallContextInterface
The CallContextInterface
invocation context contains all the information about the call:
- Target β the definition of the call target.
- Arguments β the list of arguments for the call.
- Attributes β additional context that can be used to pass data between interceptors.
Note
CallContextInterface
is immutable.
TargetInterface
TargetInterface
defines the target whose call we want to intercept.
If you need to replace the Target in the interceptor chain, use the static methods of the \Spiral\Interceptors\Context\Target
class to create a new Target.
The basic set includes several types of Target:
Target::fromReflectionMethod(ReflectionFunctionAbstract $reflection, class-string|object $classOrObject)
Creates a Target from a method reflection. The second argument is mandatory because the method reflection may refer to a parent class.Target::fromReflectionFunction(\ReflectionFunction $reflection, array $path = [])
Creates a Target from a function reflection.Target::fromClosure(\Closure $closure, array $path = [])
Creates a Target from a closure. Use PHP 8 syntax for better clarity:$target = Target::fromClosure($this->someAction(...));
Target::fromPathString(string $path, string $delimiter = '.')
and
Target::fromPathArray(array $path, string $delimiter = '.')
Creates a Target from a path string or array. In the first case, the path is split by the delimiter; in the second, the delimiter is used when converting the Target to a string. This type of Target without an explicit handler is used for RPC endpoints or message queue dispatching.Target::fromPair(string|object $controller, string $action)
An alternative way to create a Target from a controller and method, as in the old interface. The method will automatically determine how the Target should be created.
Compatibility
Spiral 3.x will work as expected with both old and new interceptors. However, new interceptors should be created based on the new interface.
In Spiral 4.x, support for old interceptors will be disabled. You will likely be able to restore it by including the spiral/hmvc
package.
Building an Interceptor Chain
If you need to manually build an interceptor chain, use \Spiral\Interceptors\PipelineBuilderInterface
.
In Spiral v3, two implementations are provided:
\Spiral\Interceptors\PipelineBuilder
β an implementation for new interceptors only.\Spiral\Core\CompatiblePipelineBuilder
β an implementation from thespiral/hmvc
package that supports both old and new interceptors simultaneously.
Note
In Spiral 3.14, the implementation for PipelineBuilderInterface
is not defined in the container by default.
CompatiblePipelineBuilder
is used in Spiral v3 services as a fallback implementation.
If you define your own implementation, it will be used instead of the fallback implementation in all framework pipelines.
At the end of the interceptor chain, there should always be a \Spiral\Interceptors\HandlerInterface
, which will be called if the interceptor chain does not terminate with a result or exception.
The spiral/interceptors
package provides several basic handlers:
\Spiral\Interceptors\Handler\CallableHandler
β simply calls the callable from the Target "as is".\Spiral\Interceptors\Handler\AutowireHandler
β calls a method or function, resolving missing arguments using the container.
use Spiral\Core\CompatiblePipelineBuilder;
use Spiral\Interceptors\Context\CallContext;
use Spiral\Interceptors\Context\Target;
use Spiral\Interceptors\Handler\CallableHandler;
$interceptors = [
new MyInterceptor(),
new MySecondInterceptor(),
new MyThirdInterceptor(),
];
$pipeline = (new CompatiblePipelineBuilder())
->withInterceptors(...$interceptors)
->build(handler: new CallableHandler());
$pipeline->handle(new CallContext(
target: Target::fromPair($controller, $action),
arguments: $arguments,
attributes: $attributes,
));
Pull Requests
- New interceptors by @roxblnfk in #1095
- Refactor interceptors target by @roxblnfk in #1106
- Add PipelineBuilder by @roxblnfk in #1111
- Update interceptors by @roxblnfk in #1117
- Separate new Interceptors by @roxblnfk in #1122
- Polish interceptors by @roxblnfk in #1128
Container Scopes
Container Scopes are integrated even deeper.
In Spiral, each type of worker is handled by a separate dispatcher.
Each dispatcher has its own scope, which can be used to limit the set of services available to the worker.
During request processing, such as HTTP, the context (ServerRequestInterface
) passes through a middleware pipeline.
At the very end, when the middleware has finished processing and the controller has not yet been called, there is a moment when the request context is finally prepared.
At this moment, the contextual container scope (in our case, http-request
) is opened, and the ServerRequestInterface
is placed in the container.
In this scope, interceptors come into play, after which the controller is executed.
As before, you can additionally open scopes in middleware, interceptors, or the business layer, for example, to limit the authorization context in a multi-tenant application.
You can view the names of dispatcher scopes and their contexts in the enum \Spiral\Framework\Spiral
.
Pull Requests
- [spiral/router] Opening of
http.request
scope added by @msmakouz in #1069 - Change the name of the enum from
ScopeName
toSpiral
by @msmakouz in #1078 - Added
PaginationProviderInterface
binding in scope by @msmakouz in #1079 - Console scopes by @butschster in #1085
- [spiral/queue] Add
Proxy
to the InvokerInterface in JobHandler by @msmakouz in #1091 - Usage of scopes in sessions and auth, adding CurrentRequest by @msmakouz in #1080
- Merge changes from the master branch by @msmakouz in #1103
- Integrate Container Scopes by @roxblnfk in #1104
- Rename context scopes by @roxblnfk in #1127
Fixes
- Fix psalm issue: remove internal annotation from LoggerTrait::$logger by @gam6itko in #1118
- Fix psalm issues related to
TracerFactoryInterface
by @gam6itko in #1119 - Fix error when a file from the stacktrace doesn't exist by @roxblnfk in #1114
- Fix an exception message by @msmakouz in #1115
- Fix OTEL: cast request URI to string for http.url trace attribute by @devnev in #1126
- Fix psalm issues about
Spiral\Queue\HandlerInterface::handle()
by @gam6itko in #1120 - Fix funding links by @roxblnfk in #1123
New Contributors
Full Changelog: 3.13.0...3.14.0
v3.13.0
New features
1. Introduced LoggerChannel
attribute
We are excited to introduce a new feature that enhances the flexibility of the logging component. With the new LoggerChannel
attribute, developers can now specify the logger channel directly in the code.
Example Usage:
class SomeService
{
public function __construct(
// Logger with channel `roadrunner` will be injected
#[LoggerChannel('roadrunner')] public LoggerInterface $logger
){}
}
This feature allows for better organization and clarity in logging, helping you maintain and debug your application more efficiently.
2. Added an ability additionally to scan parent classes.
With this update, you can now scan for attributes in parent classes, making your class discovery process more comprehensive and efficient.
Why This Matters
Previously, the tokenizer could only listen to classes where attributes were found. This limitation did not allow for the automatic (convenient) detection of classes by parent attributes and the effective use of the tokenizer cache. With this update, it will also listen to interfaces that the class with the attribute implements and the classes that it extends. This new feature leverages the full power of the tokenizer without the need to scan all classes and handle them manually, ensuring a more efficient and thorough attribute detection process.
Here is a practical example of how to use this feature:
use Spiral\Tokenizer\Attribute\TargetAttribute;
#[TargetAttribute(attribute: MyAttribute::class, scanParents: true)]
class MyListener implements TokenizationListenerInterface
{
public function listen(\ReflectionClass $class): void
{
// Your logic here
}
public function finalize(): void
{
// Your logic here
}
}
Other
- [spiral/queue] Added an ability to pass headers to the job handler using
SyncDriver
by @msmakouz in #1107 - [spiral/router] Added
HEAD
andOPTIONS
HTTP methods toroute:list
command by @tairau in #1109 - [spiral/stempler] Fixed bug in a
directive
parser by @butschster in #1098 - [spiral/core] Container Proxy classes are generated now with the
mixed
type in the methods parameters by @msmakouz in #1092 - [spiral/core] Optimized the
StateBinder::hasInjector()
method by @anoxia in #1105 - [spiral/tokenizer] Fixed registration tokenizer scopes via
TokenizerBootloader
by @butschster in #1093
Full Changelog: 3.12.0...3.13.0