vendredi 1 décembre 2017

PHP 7.2 est là !

Ca y est, PHP 7.2 est disponible !

Voilà une liste des principales nouveautés :
  • Convert numeric keys in object/array casts : correction d'un gros bug dans le cast des clefs d'un tableau, quand on convertit un tableau en objet, et un objet en tableau.
  • Counting of non-countable objects : ajout d'une E_WARNING quand on appelle count() sur un objet qui n'implément pas \Countable. Continue de retourner 1 dans ce cas (qui ne correspond à rien), pour garder la compatibilité.
  • Object typehint : ajout du type hint object, qu'on peut donc utiliser dans la signature d'une méthode par exemple, pour indiquer qu'une variable est un object dont ne connait pas la classe. Il ne manquait quasiment que ça ! Plus que le typage des propriétés, et peut-être des variables, et on sera enfin débarassé de toutes ces PHPDoc ;).
  • Migration Hash Context from Resource to Object : enfin une meilleure gestion des resources, qui sont des objets particuliers.
  • Argon2 Password Hash : ajout de Argon2 comme encryptage disponible.
  • Improved SSL / TLS constants : évolution dans la gestion de TLS, qui privilégie la sécurité à la compatibilité entre les versions de PHP.
  • Deprecate (then Remove) Mcrypt : mcrypt, librairie d'encryptage qui était intégrée au noyau de PHP, n'a plus été mise à jour depuis 2007. Elle est donc supprimée du noyau de PHP 7.2, mais reste disponible via PEAR.
  • Make Libsodium a Core Extension : intégration de la librairie de cryptage Sodium dans le noyau de PHP 7.2.

PHP 7.2 devrait être installable facilement sous Linux via le package ondrej, qui nous fournit depuis quelques années une installation facile de plusieurs versions de PHP en parallèle, toujours à jours.

Changelog PHP 7.2

Symfony 4 est là !

Ca y est, Symfony 4 est disponible !

Parmi les grosses nouveautés :
  • Symfony Flex : installation plus rapide des dépendances. Pour l'instant je suis pas vraiment convaincu, l'installation n'étant déjà pas compliquée, et ne se fait pas tous les jours. Pas sur que ça change réellement quelque chose, une fois la hype de la 1ère installation passée.
  • Auto-registered & autowired services : définition automatique des services, en fonction des paramètres attendus dans le constructeur par exemple. Pratique pour ne plus avoir de fichiers de config complexes, le revers de la médaille c'est qu'on a encore une couche de magie supplémentaire. A voir !
  • Installation plus light : Symfony 4 est 70% plus léger que Symfony 3.4 à l'installation. Beaucoup de dépendances pas forcément utiles à tous les projets ne sont pas installées par défaut. Ca, c'est top !
  • Performances : benchmark #1, benchmark #2, benchmark #3. C'est en bonne voie, mais concrètement, les benchmarks n'indiquent pas de réelles améliorations en prod. A voir quand phpbenchmarks.com aura fait ses benchmarks pour comparer.
  • Directory structure : pas mal de modifications dans l'architecture des répertoires du projet (pas de Symfony ou des bundles). Ca ressemble plus à du conventionnel, orientation bundle-less pour notre projet (depuis le temps qu'ils veulent se débarasser de ça !), etc. A voir ce que ça va nous apporter, en tout cas l'idée d'être plus conventionnel, oui !
Annonce de la sortie de Symfony 4
Changelog Symfony 4
Symfony 3.4 (Symfony 4 avec les deprecated)

mercredi 4 octobre 2017

DoctrineIssue #6751 : EntityRepository et query hints par défaut

Si vous définissez des query hints par défaut, pour toutes vos requêtes, sachez que les méthodes de EntityRepository ne les gèrent pas forcément !

En l'occurence, findAll(), findBy() et findOneBy() n'ajoutent pas ces hints par défaut.

Issue #6751

mercredi 20 septembre 2017

Configurations par défaut des bundles

Quand on configure un bundle, par exemple dans app/config/config.yml, ces 2 configurations devraient avoir le même comportement :
framework: # pas de clef form
framework: form: ~
Mais le comportement est différent.

Pour le premier cas, sans la clef form, le composant Form de Symfony n'est pas chargé, parce que la configuration par défaut de framework.form.enabled est à false.
Dans le 2ème cas, le composant Form est chargé, alors qu'on s'attend à ce que ~ soit équivalent à "prend les valeurs par défaut".

Peut-être que c'est une sorte de raccourci, pour activer le composant Form, sans avoir à définir framework.form.enabled à true.
Si c'est le cas, la documentation ne le mentionne pas, et il faudrait qu'on ait une liste des configurations qui diffèrent entre ne rien mettre, et mettre ~.
Issue #24269

mercredi 5 juillet 2017

Issue #23406 : Doctrine bigint casté en string depuis la 3.2.10

Une PR, validée pour Symfony 3.2.10 mais pas les autres versions (2.7, 2.8 et 3.3, à voir si la PR n'était pas déjà présente sur ces branches) casse tous les typages PHP pour le type Doctrine bigint.

Ils ont décidé de caster les bigint Doctrine en string PHP, au lieu de int PHP (comme avant).
Tous les typages PHP7 sont cassés, et toutes les comparaisons typés avec === également.

En espérant qu'ils corrigent ça vite, le type bigint est sûrement utilisé par beaucoup de gens pour les identifiants de table !

Issue #23406

mardi 27 juin 2017

Doctrine issue #6509 : PersistentCollection et orphanRemoval

Remontée de bug pour Doctrine 2.5.x (et probalement les versions précédentes) : si on appelle PersistentCollection::clear() ou PersistentCollection::removeElement(), et que la liaison a orphanRemoval, alors les éléments supprimés seront enregistrés dans l'UnitOfWork comme étant à supprimer en base de données.

Jusque là, tout va bien. Mais si on veut de nouveau ajouter un élément dans la PersistentCollection, et que cet élément a été indiqué comme étant à supprimer avant, alors PersistentCollection n'annule pas la demande de suppression.

Résultat : l'élément supprimé, puis re-ajouté, est supprimé en base, alors qu'on voulait le conserver.

Issue #6509

mercredi 21 juin 2017

Symfony Issue #23248 : DataCollectorTranslator::getFallbackLocales() retourne un tableau vide

En environnement de développement, si on appelle $container->get('translator')->getFallbackLocales(), on a un tableau vide.

La surcharge DataCollectorTranslator, qu'on n'a qu'en environnement de développement (pour le profiler), gère correctement les fallbacks si le service translator est une instance de Translator.
Pour les autres implémentations, fournies par Symfony (LogginTranslator par exemple), DataCollectorTranslator retourne un tableau vide.

DataCollectorTranslator.php#100
Issue #23248

vendredi 2 juin 2017

[symfony/validator] Issue #23032 Valider un tableau valide les valeurs récursivement

Si on valide un tableau, qui contient un objet parmi ses valeurs, alors cet objet sera validé, avec le groupe de validation utilisé pour le tableau.

C'est différent pour les objets, qui ont besoin du validateur Valid pour avoir le même comportement.

#23032

mardi 23 mai 2017

Connexion d'un User dans Symfony via un Listener

On peut avoir besoin de connecter un utilisateur via du code, par exemple pour des tests d'url via steevanb/php-url-test.
La documentation de Symfony est une base pour comprendre le mécanisme, il manque cependant quelques détails.

Vous trouverez ci-dessous un exemple complet, qui créé LoginListener, pour connecter votre utilisateur avant que le firewall n'essaye de le récupérer.
Notez que pour des raisons de sécurité, je met ce code dans app/AppTestKernel.php. Ce fichier sera supprimé de l'environnement de prod.

Création du listener dans AppTestKernel.php :
protected function getContainerBuilder(): ContainerBuilder { $container = parent::getContainerBuilder(); $container->setDefinition( 'foo.login_event_listener', new Definition( LoginEventListener::class, [ new Reference('security.token_storage'), new Reference('foo.user_provider'), new Reference('event_dispatcher'), new Reference('session'), new Reference('request_stack') ] ) ); return $container; }
Ajout du listener sur kernel.request, dans AppTestKernel.php :
public function boot(): void { parent::boot(); $this ->getContainer() ->get('event_dispatcher') // priorité 9 parceque le firewall qui redirige sur la page de login a une priorité de 8 ->addListenerService('kernel.request', ['foo.login_event_listener', 'login'], 9); }
Listener qui log le user admin :
<?php declare(strict_types=1); namespace Foo; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpFoundation\{ RequestStack, Session\SessionInterface }; use Symfony\Component\Security\{ Core\Authentication\Token\Storage\TokenStorageInterface, Core\Authentication\Token\UsernamePasswordToken, Core\User\UserProviderInterface, Http\Event\InteractiveLoginEvent }; class LoginEventListener { /** @var TokenStorageInterface */ protected $tokenStorage; /** @var UserProviderInterface */ protected $userProvider; /** @var EventDispatcher */ protected $eventDispatcher; /** @var SessionInterface */ protected $session; /** @var RequestStack */ protected $requestStack; public function __construct( TokenStorageInterface $tokenStorage, UserProviderInterface $userProvider, EventDispatcher $eventDispatcher, SessionInterface $session, RequestStack $requestStack ) { $this->tokenStorage = $tokenStorage; $this->userProvider = $userProvider; $this->eventDispatcher = $eventDispatcher; $this->session = $session; $this->requestStack = $requestStack; } public function login(GetResponseEvent $event): void { // récupération de l'utilisateur et connexion $user = $this->userProvider->loadUserByUsername('admin'); $token = new UsernamePasswordToken( $user, $user->getPassword(), 'main', $user->getRoles() ); $this->tokenStorage->setToken($token); // l'événement security.interactive_login n'est pas lancé automatiquement $event = new InteractiveLoginEvent($event->getRequest(), $token); $this->eventDispatcher->dispatch('security.interactive_login', $event); // http://symfony.com/doc/current/testing/http_authentication.html // sauvegarde du token dans la session, pour que le firewall le retrouve $this->session->set('_security_main', serialize($token)); $this->session->save(); // création du cookie // si on ne le fait pas, le firewall redirige sur la page de connexion $this->requestStack->getCurrentRequest()->cookies->set( $this->session->getName(), $this->session->getId() ); } }
Documentation Symfony

vendredi 5 mai 2017

Feature request : [symfony/console] pouvoir supprimer une commande

Le composant symfony/console permet d'exécuter des commandes PHP facilement.
Intégré dans Symfony, il permet à des bundles externes d'ajouter leurs commandes, par exemple doctrine:schema:update.
Cependant, certaines commandes ne devraient pas être accessibles en fonction de l'environnement, ou du projet.

Pour reprendre doctrine:schema:update, elle est très utile en dev, mais ne devrait pas pouvoir être lancée en prod.
Autre exemple : l'application sur laquelle je travaille est découpée en plusieurs projets, dont un qui contient toutes les fixtures, migrations, création de triggers, vues, etc.
Toute la gestion de la base de données est centralisée dans ce projet.
Donc, les commandes doctrine:* n'ont pas lieu d'être dans les autres projets, et surtout, ne profitent pas d'une éventuelle surcharge effectuée par le projet database.

Feature request #22645

mardi 14 mars 2017

Git log coloré et plus facile à lire

Merci à Sebastien Huot pour cet alias à git log, avec des couleurs, et plus lisible :
alias gitlog="git log --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --reverse --max-count=50"