8000 [FrameworkBundle] Add `ControllerHelper`; the helpers from AbstractCo… · symfony/symfony@dd43bbf · GitHub
[go: up one dir, main page]

Skip to content

Commit dd43bbf

Browse files
[FrameworkBundle] Add ControllerHelper; the helpers from AbstractController as a standalone service
1 parent e191195 commit dd43bbf

File tree

4 files changed

+334
-0
lines changed

4 files changed

+334
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
7.4
55
---
66

7+
* Add `ControllerHelper`; the helpers from AbstractController as a standalone service
78
* Deprecate `Symfony\Bundle\FrameworkBundle\Console\Application::add()` in favor of `Symfony\Bundle\FrameworkBundle\Console\Application::addCommand()`
89
* Add `assertEmailAddressNotContains()` to the `MailerAssertionsTrait`
910

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Controller;
13+
14+
use Psr\Link\LinkInterface;
15+
use Symfony\Component\Form\FormBuilderInterface;
16+
use Symfony\Component\Form\FormInterface;
17+
use Symfony\Component\HttpFoundation\BinaryFileResponse;
18+
use Symfony\Component\HttpFoundation\JsonResponse;
19+
use Symfony\Component\HttpFoundation\RedirectResponse;
20+
use Symfony\Component\HttpFoundation\Request;
21+
use Symfony\Component\HttpFoundation\Response;
22+
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
23+
use Symfony\Component\HttpFoundation\StreamedResponse;
24+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
25+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
26+
use Symfony\Component\Security\Core\Authorization\AccessDecision;
27+
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
28+
use Symfony\Component\Security\Core\User\UserInterface;
29+
30+
/**
31+
* Provides the helpers from AbstractControler as a standalone service.
32+
*
33+
* Best used together with #[AutowireMethodOf] to remove any coupling.
34+
*/
35+
class ControllerHelper extends AbstractController
36+
{
37+
/**
38+
* Gets a container parameter by its name.
39+
*/
40+
public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
41+
{
42+
return parent::getParameter(...\func_get_args());
43+
}
44+
45+
/**
46+
* Generates a URL from the given parameters.
47+
*
48+
* @see UrlGeneratorInterface
49+
*/
50+
public function generateUrl(string $route, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
51+
{
52+
return parent::generateUrl(...\func_get_args());
53+
}
54+
55+
/**
56+
* Forwards the request to another controller.
57+
*
58+
* @param string $controller The controller name (a string like "App\Controller\PostController::index" or "App\Controller\PostController" if it is invokable)
59+
*/
60+
public function forward(string $controller, array $path = [], array $query = []): Response
61+
{
62+
return parent::forward(...\func_get_args());
63+
}
64+
65+
/**
66+
* Returns a RedirectResponse to the given URL.
67+
*
68+
* @param int $status The HTTP status code (302 "Found" by default)
69+
*/
70+
public function redirect(string $url, int $status = 302): RedirectResponse
71+
{
72+
return parent::redirect(...\func_get_args());
73+
}
74+
75+
/**
76+
* Returns a RedirectResponse to the given route with the given parameters.
77+
*
78+
* @param int $status The HTTP status code (302 "Found" by default)
79+
*/
80+
public function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse
81+
{
82+
return parent::redirectToRoute(...\func_get_args());
83+
}
84+
85+
/**
86+
* Returns a JsonResponse that uses the serializer component if enabled, or json_encode.
87+
*
88+
* @param int $status The HTTP status code (200 "OK" by default)
89+
*/
90+
public function json(mixed $data, int $status = 200, array $headers = [], array $context = []): JsonResponse
91+
{
92+
return parent::json(...\func_get_args());
93+
}
94+
95+
/**
96+
* Returns a BinaryFileResponse object with original or customized file name and disposition header.
97+
*/
98+
public function file(\SplFileInfo|string $file, ?string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse
99+
{
100+
return parent::file(...\func_get_args());
101+
}
102+
103+
/**
104+
* Adds a flash message to the current session for type.
105+
*
106+
* @throws \LogicException
107+
*/
108+
public function addFlash(string $type, mixed $message): void
109+
{
110+
parent::addFlash(...\func_get_args());
111+
}
112+
113+
/**
114+
* Checks if the attribute is granted against the current authentication token and optionally supplied subject.
115+
*
116+
* @throws \LogicException
117+
*/
118+
public function isGranted(mixed $attribute, mixed $subject = null): bool
119+
{
120+
return parent::isGranted(...\func_get_args());
121+
}
122+
123+
/**
124+
* Checks if the attribute is granted against the current authentication token and optionally supplied subject.
125+
*/
126+
public function getAccessDecision(mixed $attribute, mixed $subject = null): AccessDecision
127+
{
128+
return parent::getAccessDecision(...\func_get_args());
129+
}
130+
131+
/**
132+
* Throws an exception unless the attribute is granted against the current authentication token and optionally
133+
* supplied subject.
134+
*
135+
* @throws AccessDeniedException
136+
*/
137+
public function denyAccessUnlessGranted(mixed $attribute, mixed $subject = null, string $message = 'Access Denied.'): void
138+
{
139+
parent::denyAccessUnlessGranted(...\func_get_args());
140+
}
141+
142+
/**
143+
* Returns a rendered view.
144+
*
145+
* Forms found in parameters are auto-cast to form views.
146+
*/
147+
public function renderView(string $view, array $parameters = []): string
148+
{
149+
return parent::renderView(...\func_get_args());
150+
}
151+
152+
/**
153+
* Returns a rendered block from a view.
154+
*
155+
* Forms found in parameters are auto-cast to form views.
156+
*/
157+
public function renderBlockView(string $view, string $block, array $parameters = []): string
158+
{
159+
return parent::renderBlockView(...\func_get_args());
160+
}
161+
162+
/**
163+
* Renders a view.
164+
*
165+
* If an invalid form is found in the list of parameters, a 422 status code is returned.
166+
* Forms found in parameters are auto-cast to form views.
167+
*/
168+
public function render(string $view, array $parameters = [], ?Response $response = null): Response
169+
{
170+
return parent::render(...\func_get_args());
171+
}
172+
173+
/**
174+
* Renders a block in a view.
175+
*
176+
* If an invalid form is found in the list of parameters, a 422 status code is returned.
177+
* Forms found in parameters are auto-cast to form views.
178+
*/
179+
public function renderBlock(string $view, string $block, array $parameters = [], ?Response $response = null): Response
180+
{
181+
return parent::renderBlock(...\func_get_args());
182+
}
183+
184+
/**
185+
* Streams a view.
186+
*/
187+
public function stream(string $view, array $parameters = [], ?StreamedResponse $response = null): StreamedResponse
188+
{
189+
return parent::stream(...\func_get_args());
190+
}
191+
192+
/**
193+
* Returns a NotFoundHttpException.
194+
*
195+
* This will result in a 404 response code. Usage example:
196+
*
197+
* throw $this->createNotFoundException('Page not found!');
198+
*/
199+
public function createNotFoundException(string $message = 'Not Found', ?\Throwable $previous = null): NotFoundHttpException
200+
{
201+
return parent::createNotFoundException(...\func_get_args());
202+
}
203+
204+
/**
205+
* Returns an AccessDeniedException.
206+
*
207+
* This will result in a 403 response code. Usage example:
208+
*
209+
* throw $this->createAccessDeniedException('Unable to access this page!');
210+
*
211+
* @throws \LogicException If the Security component is not available
212+
*/
213+
public function createAccessDeniedException(string $message = 'Access Denied.', ?\Throwable $previous = null): AccessDeniedException
214+
{
215+
return parent::createAccessDeniedException(...\func_get_args());
216+
}
217+
218+
/**
219+
* Creates and returns a Form instance from the type of the form.
220+
*/
221+
public function createForm(string $type, mixed $data = null, array $options = []): FormInterface
222+
{
223+
return parent::createForm(...\func_get_args());
224+
}
225+
226+
/**
227+
* Creates and returns a form builder instance.
228+
*/
229+
public function createFormBuilder(mixed $data = null, array $options = []): FormBuilderInterface
230+
{
231+
return parent::createFormBuilder(...\func_get_args());
232+
}
233+
234+
/**
235+
* Get a user from the Security Token Storage.
236+
*
237+
* @throws \LogicException If SecurityBundle is not available
238+
*
239+
* @see TokenInterface::getUser()
240+
*/
241+
public function getUser(): ?UserInterface
242+
{
243+
return parent::getUser(...\func_get_args());
244+
}
245+
246+
/**
247+
* Checks the validity of a CSRF token.
248+
*
249+
* @param string $id The id used when generating the token
250+
* @param string|null $token The actual token sent with the request that should be validated
251+
*/
252+
public function isCsrfTokenValid(string $id, #[\SensitiveParameter] ?string $token): bool
253+
{
254+
return parent::isCsrfTokenValid(...\func_get_args());
255+
}
256+
257+
/**
258+
* Adds a Link HTTP header to the current response.
259+
*
260+
* @see https://tools.ietf.org/html/rfc5988
261+
*/
262+
public function addLink(Request $request, LinkInterface $link): void
263+
{
264+
parent::addLink(...\func_get_args());
265+
}
266+
267+
/**
268+
* @param LinkInterface[] $links
269+
*/
270+
public function sendEarlyHints(iterable $links = [], ?Response $response = null): Response
271+
{
272+
return parent::sendEarlyHints(...\func_get_args());
273+
}
274+
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

1414
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
15+
use Symfony\Bundle\FrameworkBundle\Controller\ControllerHelper;
1516
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
1617
use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
1718
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
@@ -146,5 +147,11 @@
146147
->set('controller.cache_attribute_listener', CacheAttributeListener::class)
147148
->tag('kernel.event_subscriber')
148149

150+
->set('controller.helper', ControllerHelper::class)
151+
->call('setContainer')
152+
->tag('container.service_subscriber')
153+
154+
->alias(ControllerHelper::class, 'controller.helper')
155+
149156
;
150157
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
16+
use Symfony\Bundle\FrameworkBundle\Controller\ControllerHelper;
17+
use Symfony\Component\VarExporter\ProxyHelper;
18+
19+
class ControllerHelperTest extends TestCase
20+
{
21+
public function testSync()
22+
{
23+
$src = file_get_contents(__DIR__.'/../../Controller/ControllerHelper.php');
24+
25+
$r = new \ReflectionClass(ControllerHelper::class);
26+
$src = file($r->getFileName());
27+
$src = implode('', array_slice($src, $r->getStartLine() + 1, $r->getEndLine() - $r->getStartLine() - 2));
28+
29+
$r = new \ReflectionClass(AbstractController::class);
30+
$code = [];
31+
32+
foreach ($r->getMethods(\ReflectionMethod::IS_PROTECTED) as $m) {
33+
if ($m->isStatic()) {
34+
continue;
35+
}
36+
$r = $m->getReturnType();
37+
$code[] = ' '.$m->getDocComment();
38+
$code[] = preg_replace('{\\\\[a-zA-Z0-9_\\\\]+\\\\}', '',
39+
" ".substr_replace(ProxyHelper::exportSignature($m, true, $args), 'public', 0, 9)
40+
."\n {\n "
41+
.($r instanceof \ReflectionNamedType && \in_array($r->getName(), ['void', 'never'], true) ? '' : 'return ')
42+
.'parent::'.$m->name
43+
.'('.$args.");\n }\n"
44+
);
45+
}
46+
$code = strtr(implode("\n", $code), [
47+
'\UnitEnum|array|bool|float|int|null|string' => 'array|bool|string|int|float|\UnitEnum|null',
48+
]);
49+
50+
$this->assertSame($src, $code, 'Methods from AbstractController are is not properly synced in ControllerHelper');
51+
}
52+
}

0 commit comments

Comments
 (0)
0