Skip to content

Commit d6f529c

Browse files
committed
feat: add NoSessionInPaymentHandlerAndStoreApiRule to PHPStan rules
1 parent 082b142 commit d6f529c

5 files changed

+171
-0
lines changed

rules.neon

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ rules:
88
- Shopware\PhpStan\Rule\InternalMethodCallRule
99
- Shopware\PhpStan\Rule\MethodBecomesAbstractRule
1010
- Shopware\PhpStan\Rule\NoDALFilterByID
11+
- Shopware\PhpStan\Rule\NoSessionInPaymentHandlerAndStoreApiRule
1112
- Shopware\PhpStan\Rule\NoSymfonySessionInConstructor
1213
- Shopware\PhpStan\Rule\NoUserEntityGetStoreTokenRule
1314
- Shopware\PhpStan\Rule\ScheduledTaskTooLowIntervalRule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopware\PhpStan\Rule;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\MethodCall;
9+
use PHPStan\Analyser\Scope;
10+
use PHPStan\Rules\Rule;
11+
use PHPStan\Rules\RuleErrorBuilder;
12+
use PHPStan\Type\ObjectType;
13+
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHandler;
14+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
15+
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
16+
use Symfony\Component\Routing\Attribute\Route as RouteAttribute;
17+
18+
/**
19+
* @implements Rule<MethodCall>
20+
*/
21+
class NoSessionInPaymentHandlerAndStoreApiRule implements Rule
22+
{
23+
public function getNodeType(): string
24+
{
25+
return MethodCall::class;
26+
}
27+
28+
public function processNode(Node $node, Scope $scope): array
29+
{
30+
$type = $scope->getType($node->var);
31+
32+
// @phpstan-ignore-next-line
33+
if (!$type instanceof ObjectType) {
34+
return [];
35+
}
36+
37+
if (!$type->isInstanceOf(SessionInterface::class)->yes()) {
38+
return [];
39+
}
40+
41+
$classReflection = $scope->getClassReflection();
42+
43+
if ($classReflection === null) {
44+
return [];
45+
}
46+
47+
// Check if class extends AbstractPaymentHandler
48+
if ($classReflection->isSubclassOf(AbstractPaymentHandler::class)) {
49+
return [
50+
RuleErrorBuilder::message('Session usage is not allowed in payment handlers.')
51+
->identifier('shopware.sessionUsageInPaymentHandler')
52+
->build(),
53+
];
54+
}
55+
56+
// Check for Store-API route attribute
57+
$nativeReflection = $classReflection->getNativeReflection();
58+
$attributes = array_merge(
59+
$nativeReflection->getAttributes(RouteAnnotation::class),
60+
$nativeReflection->getAttributes(RouteAttribute::class),
61+
);
62+
63+
foreach ($attributes as $attribute) {
64+
/** @var array{defaults?: array{_routeScope?: array<string>}} $args */
65+
$args = $attribute->getArguments();
66+
if (isset($args['defaults']['_routeScope']) && in_array('store-api', (array) $args['defaults']['_routeScope'], true)) {
67+
return [
68+
RuleErrorBuilder::message('Session usage is not allowed in Store-API controllers.')
69+
->identifier('shopware.sessionUsageInStoreApi')
70+
->build(),
71+
];
72+
}
73+
}
74+
75+
return [];
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopware\PhpStan\Tests\Rule;
6+
7+
use PHPStan\Rules\Rule;
8+
use PHPStan\Testing\RuleTestCase;
9+
use Shopware\PhpStan\Rule\NoSessionInPaymentHandlerAndStoreApiRule;
10+
11+
/**
12+
* @extends RuleTestCase<NoSessionInPaymentHandlerAndStoreApiRule>
13+
*/
14+
class NoSessionInPaymentHandlerAndStoreApiRuleTest extends RuleTestCase
15+
{
16+
protected function getRule(): Rule
17+
{
18+
return new NoSessionInPaymentHandlerAndStoreApiRule();
19+
}
20+
21+
public function testRule(): void
22+
{
23+
$this->analyse([__DIR__ . '/fixtures/NoSessionInPaymentHandlerAndStoreApi/PaymentHandlerWithSession.php'], [
24+
[
25+
'Session usage is not allowed in payment handlers.',
26+
25,
27+
],
28+
]);
29+
30+
$this->analyse([__DIR__ . '/fixtures/NoSessionInPaymentHandlerAndStoreApi/StoreApiControllerWithSession.php'], [
31+
[
32+
'Session usage is not allowed in Store-API controllers.',
33+
20,
34+
],
35+
]);
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopware\PhpStan\Tests\Rule\Fixtures\NoSessionInPaymentHandlerAndStoreApi;
6+
7+
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHandler;
8+
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerInterface;
9+
use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerType;
10+
use Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct;
11+
use Shopware\Core\Framework\Context;
12+
use Shopware\Core\Framework\Struct\Struct;
13+
use Symfony\Component\HttpFoundation\RedirectResponse;
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
16+
17+
class PaymentHandlerWithSession extends AbstractPaymentHandler implements PaymentHandlerInterface
18+
{
19+
public function __construct(
20+
private readonly SessionInterface $session,
21+
) {}
22+
23+
public function pay(Request $request, PaymentTransactionStruct $transaction, Context $context, ?Struct $validateStruct = null): ?RedirectResponse
24+
{
25+
$this->session->get('foo');
26+
27+
return null;
28+
}
29+
30+
public function supports(PaymentHandlerType $type, string $paymentMethodId, Context $context): bool
31+
{
32+
return true;
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopware\PhpStan\Tests\Rule\Fixtures\NoSessionInPaymentHandlerAndStoreApi;
6+
7+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
8+
use Symfony\Component\Routing\Annotation\Route;
9+
10+
#[Route(defaults: ['_routeScope' => ['store-api']])]
11+
class StoreApiControllerWithSession
12+
{
13+
public function __construct(
14+
private readonly SessionInterface $session,
15+
) {}
16+
17+
#[Route(path: '/store-api/test', name: 'store-api.test', methods: ['GET'])]
18+
public function testAction(): void
19+
{
20+
$this->session->get('foo');
21+
}
22+
}

0 commit comments

Comments
 (0)