salut,
Je vais te parler de bonne pratique autant en php que sql.
=> Tu n'as pas l'air d'utiliser de variable gloable dans les fonctions (or super globales) ce qui mais tu peux faire mieux : ne pas utiliser de globale du tous.
Exemple ta fonction login prend deux paramètres que tu écrase directement avec $_POST['email'] et $_POST['mdp']
tu vire ça et utilise la fonction directement ainsi login($_POST['email'], $_POST['mdp']);
le principe est le même pour session.
Pourquoi ?
Parce que tu va pouvoir obtenir ainsi des fonctions isolable que tu pourras ainsi tester (peux être pas avec phpunit mais tu pourras utiliser la fonction dans un cas passant ou pas et voir ce que ça donne).
Dans le même genre je ne sais pas comment est faite la classe Bdd mais je suppose qu'il y a une sorte de singleton pour ne pas instancier 50 fois pdo vu qu'il s'agit d'une méthode statique.
Ceci n'est pas une bonne pratique il est préférable de passer en paramètre l'objet pdo que tu va fournir aux fonctions dont tu a besoin.
Cela te permet de gérer la chose comme tu souhaite ou créer un wrapper perso que tu pourra changer avec autre chose (il suffit d'utiliser une interface) si tu souhaite (imagine que change ton sgbd par un système de fichier

).
Pour ce qui est des requêtes SQL il y a moyen d'optimiser tes requetes (par exemple utiliser un select dans un insert ça fonctionne).
Dans une logique d'utiliser un motif de conception type MVC les fonctions "utilitaires" ne doivent pas faire d'affichage au pire retourner un message d'erreur
Pour ce qui est de requête préparée pourquoi pas (certain langage ne propose pas d'alternative). Ceci dit sachant qu'elle est mise en cache que sur la connexion que par défaut elle est émulée et que tu utilise le passage de paramètre d’exécuté il n'y pas de réel intérêt hors mis celui de ne pas gérer la "protection" de données où tu va te faire couillonner (désolé) quand tu va vouloir utiliser ce type de requête pour un paramètre dans une limite (ou similaire) et la chose va coller une chaîne de caractères à la place d'un entier (he oui vu que php n'est pas typé mais sql oui, mais s'il y a pas mal d'auto boxing tous ce qui est est chaine de caractère et traité comme tel).
pour info en sql insérer une chaine de caractère dans un entier fonction ne pour peux la chaine contienne un entier la conversion étant réalisée, par contre cela en fonctionnera pas lorsqu'un entier est demandé (dans mon exemple select * from latable where machin='bidulle' limit :start,10 sera remplacé en select * from latable where machin='bidulle' limit '0',10 par exemple et la vautrage assuré).
=> closeCursor() : très bien peu de personne y pense c'est un très bon point
Tu as du code redondant (validation d'email) tu peux créer une fonction pour cela ou utiliser la fonction php filter_var à la place;)
sinon, pour finir sur une bonne note, le code est clair et indenté (bon manque de commentaire ou moins de doc) c'est important est assez rare.
pour ce qui est des conventions de codage je t'invite à regarder ce site
http://www.php-fig.org/ (pas mal des articles sont traduit en fr) même si tout (pour moi et pour ce qui est des psr-1 et 2) n'est pas parfait c'est une bonne base.
Au final comme le Nestecha, c'est surtout pinaille & co surtout si tu ne compte pas en faire ton métier
si tu veux aller plus loin regarde du coté des motifs de conception (design pattern), la base étant le mcv (le singleton annoncé tout a l'heure en est un autre) et bien sur la poo pour une conception de plus au niveau.
Par contre n'utilise pas la poo a contre coeur ce n'est pas de la peine de le faire "pour faire de poo".
C'est incontournable pour un pro mais sinon ce n'est pas un problème
@+