Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

A la caza de los últimos depredadores

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

¡Muy bien equipo! Vamos a arreglar estas últimas depreciaciones. Una de las cosas más complicadas de estas es que, a veces, provienen de paquetes de terceros. No tengo ningún ejemplo aquí, pero a veces recibirás una desaprobación y... si lo investigas, te darás cuenta de que no es culpa tuya. Viene de una biblioteca o de un paquete que estás utilizando. Cuando esto ocurre, tienes que actualizar ese paquete y esperar que haya una nueva versión sin depreciaciones. De hecho, ya tuvimos algunos ejemplos de esto al principio del tutorial. Pero... ya hemos ejecutadocomposer update unas cuantas veces y, aparentemente, hemos actualizado todas nuestras dependencias a versiones sin depreciaciones. Sí, ¡eficiencia!

ROLE_PREVIOUS_ADMIN -> IS_IMPERSONATOR

Bien, echemos un vistazo a esta lista. Dice que, en Symfony 5.1,ROLE_PREVIOUS_ADMIN está obsoleto y que deberíamos usar IS_IMPERSONATOR en su lugar. Puedes mostrar el contexto o rastrear para intentar obtener más información, como de dónde viene esto. No siempre es obvio... y esa es una de las cosas más complicadas de las depreciaciones. Pero ésta viene de base.html.twig.

¡Genial! Abre templates/base.html.twig y busca "previous_admin". En un tutorial anterior, lo utilizamos para comprobar si estamos suplantando a un usuario con la función switch_user de Symfony. Si lo estamos, cambiamos el fondo a rojo para que sea realmente obvio.

Para arreglar la desaprobación, muy sencillo, cambia esto por IS_IMPERSONATOR. Cópialo... porque hay otro punto en esta página donde tenemos que hacer lo mismo:IS_IMPERSONATOR. ¡Ya está! ¡Una depreciación menos!

... lines 1 - 21
<nav
... line 23
{{ is_granted('IS_IMPERSONATOR') ? 'style="background-color: red !important"' }}
>
... lines 26 - 60
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="user-dropdown">
{% if is_granted('IS_IMPERSONATOR') %}
... lines 63 - 67
{% endif %}
... lines 69 - 71
</ul>
... lines 73 - 79
</nav>
... lines 81 - 95

IS_AUTHENTICATED_ANONYMOUSLY -> PUBLIC_ACCESS

Mientras hablamos de seguridad, abre config/packages/security.yaml y dirígete a access_control. Tengo unas cuantas entradas - /logout, /admin/login - que quiero asegurarme absolutamente de que son accesibles para todo el mundo, incluso para los usuarios que no han iniciado sesión. Para ello, añadimos estas reglas en la parte superior y, previamente, utilizamos IS_AUTHENTICATED_ANONYMOUSLY. De modo que si voy a /logout, sólo se empareja este access_control... y como el role esIS_AUTHENTICATED_ANONYMOUSLY el acceso está siempre concedido.

En Symfony 6, IS_AUTHENTICATED_ANONYMOUSLY ha cambiado a PUBLIC_ACCESS. Así que úsalo en ambos sitios.

security:
... lines 2 - 38
access_control:
... lines 40 - 41
- { path: ^/logout, role: PUBLIC_ACCESS }
- { path: ^/admin/login, roles: PUBLIC_ACCESS }
... lines 44 - 59

Si te preguntas por qué no tenemos una deprecación para esto... bueno... es un caso raro en el que Symfony no es capaz de captar esa ruta depreciada y mostrárnosla. Esto no ocurre muy a menudo, pero es una situación en la que una herramienta como SymfonyInsight puede ayudar a detectar esta.... incluso cuando el propio Symfony no puede hacerlo.

El servicio de sesión desaprobado

Bien, la última deprecación de la lista dice

SessionInterface los alias están obsoletos, utiliza $requestStack->getSession() en su lugar. Está siendo referenciado por el servicio LoginFormAuthenticator.

¡Vamos a comprobarlo! Abre src/Security/LoginFormAuthenticator.php. Ahh. Estoy autoconectando el servicio SessionInterface. En Symfony 6, ese servicio ya no existe. Hay algunas razones técnicas para ello... pero resumiendo, la sesión nunca fue, realmente, un verdadero servicio. Lo que se supone que tienes que hacer ahora es obtenerla de Request.

Así que no hay problema. Elimina el argumento del constructor SessionInterface... y tampoco necesitamos ya esta declaración use

... lines 1 - 21
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
... lines 24 - 27
public function __construct(private EntityManagerInterface $entityManager, private UrlGeneratorInterface $urlGenerator)
{
}
... lines 31 - 84
}

Ahora busca "sesión". La usamos en onAuthenticationSuccess(). Afortunadamente, ¡esto ya nos pasa el objeto$request! Así que podemos decir simplemente $request->getSession().

... lines 1 - 60
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
... line 64
}
... lines 66 - 67
}
... lines 69 - 86

Buscando las últimas desapariciones

¡Ya está! Entonces... ¿lo hemos conseguido? ¿Hemos conseguido cero depreciaciones y la iluminación espiritual? Vuelve a la página de inicio, actualiza y... ¡lo hemos conseguido! Bueno, al menos la primera parte... ¡sin depreciaciones! Y si navegamos un poco por nuestro sitio... ¡no veo ninguna deprecación en ninguna de estas páginas!

¿Significa esto que hemos terminado? Bueno, hemos comprobado manualmente todas las páginas en las que podemos hacer clic. Pero ¿qué pasa con las peticiones de POST... como el envío de los formularios de inicio de sesión o de registro? ¿Y qué pasa con las rutas de la API? Tenemos uno llamado/api/me... que no funciona porque no estoy conectado. Vuelve a entrar como "abraca_admin@example.com" con la contraseña "tada" y entonces... sí, /api/me funciona.

No podemos ver la barra de herramientas de depuración de la web para esta petición, pero seguro que ya conoces el truco. Ve a /_profiler para ver las diez últimas peticiones. Aquí está la petición de POSTa /login. Baja a Registros. ¡Genial! Eso no tenía depreciaciones. Vuelve y comprueba también la ruta de la API. Si volvemos a mirar los registros, tampoco había desaprobaciones. ¡Estamos de enhorabuena!

Otra opción, en lugar de comprobar el perfilador todo el tiempo, es ir a tu terminal y seguir el archivo de registro:

tail -f var/log/dev.log

De este modo, se transmitirá constantemente cualquier registro nuevo. En realidad, pulsa "ctrl" + "C" y ejecuta eso de nuevo, pero grep para deprecation:

tail -f var/log/dev.log | grep deprecation

Perfecto. Ahora, si llega algún registro que contenga la palabra "deprecación", lo veremos. Y como las rutas de código desaprobadas activan un registro en el entorno dev, ésta es una poderosa herramienta.

Método $this->getDoctrine() obsoleto

Por ejemplo, vamos a registrarnos como nuevo usuario. Voy a cerrar la sesión, y luego a "Registrarme". Me pide mi nombre, mi correo electrónico y una contraseña. Haz clic en "Aceptar" unas condiciones inventadas y envíalo. Oh, mi contraseña es demasiado corta: ¡mis propias reglas de validación vuelven a perseguirme! Arregla eso, pulsa "Registrarse" de nuevo y... ¡funciona!

Pero si volvemos a nuestro terminal... ¡rut roo!

Desde symfony/framework-bundle 5.4, el método AbstractController::getDoctrine() está obsoleto. Inyecta una instancia de ManagerRegistry en tu controlador en su lugar.

No es fácil ver de dónde viene esto en nuestro código, pero acabamos de registrarnos... así que abramos RegistrationController. Ah, aquí se queja de esto: el método getDoctrine() está obsoleto.

En lugar de utilizarlo, podemos inyectar el método $entityManager. Al final de la lista de argumentos, autowire EntityManagerInterface $entityManager. Y... luego aquí abajo, borra esta línea porque ahora se inyecta $entityManager. ¡Otra deprecación desaparecida!

... lines 1 - 7
use Doctrine\ORM\EntityManagerInterface;
... lines 9 - 16
class RegistrationController extends AbstractController
{
... line 19
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, VerifyEmailHelperInterface $verifyEmailHelper, EntityManagerInterface $entityManager): Response
{
... lines 22 - 56
}
... lines 58 - 88
}

Registrar las depreciaciones en producción

¿Ya hemos terminado? Probablemente. Nuestro proyecto es bastante pequeño, así que comprobar todas las páginas manualmente no es un gran problema. Pero para proyectos más grandes, podría ser... ¡un gran problema comprobarlo todo manualmente! Y realmente quieres estar seguro de que no te has dejado nada antes de actualizar.

Una gran opción para asegurarte de que no se te ha escapado nada es registrar tus depreciaciones en producción. Abre config/packages/monolog.yaml y baja a when@prod. Esto tiene una serie de manejadores que registrarán todos los errores en php://stderr. También hay una sección deprecation. Con esta configuración, Symfony registrará cualquier mensaje de depreciación (eso es lo que significa este channels: [deprecation] ) en php://stderr.

... lines 1 - 40
when@prod:
monolog:
handlers:
... lines 44 - 58
deprecation:
type: stream
channels: [deprecation]
path: php://stderr

Esto significa que puedes desplegar, esperar una hora, un día o una semana, y luego... ¡consultar el registro! Si en cambio quieres registrar en un archivo, cambia la ruta a algo como%kernel.logs_dir%/deprecations.log.

Así que eso es lo que más me gusta hacer: desplegarlo, y luego ver -en el mundo real- si alguien sigue o no dando con las rutas de código obsoletas.

En este momento, no veo más desaprobaciones en nuestra barra de herramientas de depuración web, así que creo que hemos terminado ¡Y eso significa que estamos listos para Symfony 6! ¡Hagamos la actualización a continuación!

Leave a comment!

8
Login or Register to join the conversation
Dan_M Avatar

Hey Guys!

I worked through the code to this point, and I have 14 deprecations coming from two sources. KNP Markdown Bundle has four methods missing return definitions. Given that KNP Markdown Bundle has been abandoned, are these deprecations the trigger to replace KNP Markdown Bundle with twig/markdown-extra (or can I upgrade to Symfony 6 without removing those deprecations)?

The second source is BabDev\PagerfantaBundle. That module is at ^2.0 in composer.json. When I upgraded to version 3, Symfony blew up with a failed to open stream error. Is there any direction on upgrading Pagerfanta?

Thanks!

Reply
Dan_M Avatar

Ok, I just moved on to Chapter 16, and my questions were answered. Life is good.

Reply
Dan_M Avatar

Hmmmm...I just restarted my development system, and the KNP Markdown Bundle deprecations are no longer showing up in the debug toolbar. I don't know what that means.

Reply

Hey Dan_M!

Haha, yup I'm glad you found the answer! About the deprecations no longer showing up, that can happen sometimes - I think it deals with whether or not some of the cache needed to be built during that request or not. In short, you will see some variations between the deprecations count from time to time, even on the same page (that's why, once you think all the deprecations are gone, deploying to production with deprecations logging enabled is a great way to make sure you haven't missed anything).

Cheers!

Reply
MattWelander Avatar
MattWelander Avatar MattWelander | posted hace 3 meses | edited

Hi, I also wanted to ask - you mentioned in another video that one can create a base class to extend all other controllers from, rather than extending the AbstractController every time.

Would it be possible to set up my own "template" sort of, with all the use statements that are common to all of my controllers, and also initialize the variables in the construct, so I don't need to clutter my every controller with that?

So I'm thinking something like this

<?php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Security;

class WebtoolsController extends AbstractController
{

    private $requestStack;
    private $security;

    public function __construct(RequestStack $requestStack, Security $security)
    {
        $this->requestStack = $requestStack;
        $this->security = $security;
    }

}

And then omit all of this in my every controller, like this:

<?php
namespace App\Controller\ExtraOpening;
use App\Controller\WebtoolsController;

/**
 * Price controller.
 *
 */
class priceController extends WebtoolsController
{
    public function __construct()
    {
        parent::__construct();
    }

I get an error message when I do the parent::__construct();, apparently I'm missing arguments. But if I need to repeat the arguments on every controller it isn't much point in trying to abstract it in this way anyways...

Just thought maybe there is a smart way? =)

Reply

Hey Matt,

I think in your case it would be better to use the ServiceSubscriber. You can define a list of services that you always want to have access to. You can read more about it here https://symfony.com/doc/current/service_container/service_subscribers_locators.html

By the way, if a child's constructor does nothing, you don't need a constructor at all. PHP will call the parent's constructor automatically

Cheers!

Reply
MattWelander Avatar
MattWelander Avatar MattWelander | posted hace 3 meses | edited

Hi, I still get this deprecation:

The "Symfony\Bridge\Doctrine\Logger\DbalLogger" class implements "Doctrine\DBAL\Logging\SQLLogger" that is deprecated Use {@see \Doctrine\DBAL\Logging\Middleware} or implement {@see \Doctrine\DBAL\Driver\Middleware} instead.

From this issue (https://github.com/doctrine/DoctrineBundle/issues/1429) it seems it was fixed in Doctrine 2.6 but I'm running 2.7 and it is still there.

Guess I shouldn't worry too much about it, by the time I'm ready to move up to 6.x the matching doctrine version is probably fixed, but it'd be a good feeling to have a completely deprecation free board =)

Reply

Yea, it would feel nicer to get rid of all deprecations, but in this case, I think it would be better to just wait until you upgrade Doctrine

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^8.0.2",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "babdev/pagerfanta-bundle": "^3.6", // v3.6.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.5
        "doctrine/annotations": "^1.13", // 1.13.2
        "doctrine/dbal": "^3.3", // 3.3.5
        "doctrine/doctrine-bundle": "^2.0", // 2.6.2
        "doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
        "doctrine/orm": "^2.0", // 2.11.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.10.0
        "knplabs/knp-time-bundle": "^1.18", // v1.18.0
        "pagerfanta/doctrine-orm-adapter": "^3.6", // v3.6.1
        "pagerfanta/twig": "^3.6", // v3.6.1
        "sensio/framework-extra-bundle": "^6.0", // v6.2.6
        "sentry/sentry-symfony": "^4.0", // 4.2.8
        "stof/doctrine-extensions-bundle": "^1.5", // v1.7.0
        "symfony/asset": "6.0.*", // v6.0.7
        "symfony/console": "6.0.*", // v6.0.7
        "symfony/dotenv": "6.0.*", // v6.0.5
        "symfony/flex": "^2.1", // v2.1.7
        "symfony/form": "6.0.*", // v6.0.7
        "symfony/framework-bundle": "6.0.*", // v6.0.7
        "symfony/mailer": "6.0.*", // v6.0.5
        "symfony/monolog-bundle": "^3.0", // v3.7.1
        "symfony/property-access": "6.0.*", // v6.0.7
        "symfony/property-info": "6.0.*", // v6.0.7
        "symfony/proxy-manager-bridge": "6.0.*", // v6.0.6
        "symfony/routing": "6.0.*", // v6.0.5
        "symfony/runtime": "6.0.*", // v6.0.7
        "symfony/security-bundle": "6.0.*", // v6.0.5
        "symfony/serializer": "6.0.*", // v6.0.7
        "symfony/stopwatch": "6.0.*", // v6.0.5
        "symfony/twig-bundle": "6.0.*", // v6.0.3
        "symfony/ux-chartjs": "^2.0", // v2.1.0
        "symfony/validator": "6.0.*", // v6.0.7
        "symfony/webpack-encore-bundle": "^1.7", // v1.14.0
        "symfony/yaml": "6.0.*", // v6.0.3
        "symfonycasts/verify-email-bundle": "^1.7", // v1.10.0
        "twig/extra-bundle": "^2.12|^3.0", // v3.3.8
        "twig/string-extra": "^3.3", // v3.3.5
        "twig/twig": "^2.12|^3.0" // v3.3.10
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.1
        "phpunit/phpunit": "^9.5", // 9.5.20
        "rector/rector": "^0.12.17", // 0.12.20
        "symfony/debug-bundle": "6.0.*", // v6.0.3
        "symfony/maker-bundle": "^1.15", // v1.38.0
        "symfony/var-dumper": "6.0.*", // v6.0.6
        "symfony/web-profiler-bundle": "6.0.*", // v6.0.6
        "zenstruck/foundry": "^1.16" // v1.18.0
    }
}