Sam & Max » php http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Qu’est-ce que MVC et à quoi ça sert ? 23 http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/ http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/#comments Tue, 10 Dec 2013 08:39:53 +0000 http://sametmax.com/?p=7440 et PHP, car c'est une question qui hante les codeurs de ce langage. En effet on leur rabâche qu'il faut utiliser MVC, que tel framework est MVC, que leur code à eux ne l'est pas, etc. Sans que nul part, évidement, on ne donne une explication correcte de la notion.]]> MVC, pour “Modèle, Vue, Contrôleur”, est le nom donné à une manière d’organiser son code. C’est une façon d’appliquer le principe de séparation des responsabilités, en l’occurrence celles du traitement de l’information et de sa mise en forme.

Une fois n’est pas coutume je vais donner un exemple en Python et PHP, car c’est une question qui hante les codeurs de ce langage. En effet on leur rabâche qu’il faut utiliser MVC, que tel framework est MVC, que leur code à eux ne l’est pas, etc. Sans que nulle part, évidement, on ne donne une explication correcte de la notion.

Long article, petite musique.

(piqué à What the cut :-))

Principe de base

Il n’y a pas de Tables De La Loi qui disent ce qu’est le MVC, il y a donc autant de manières de le faire que de programmes. En fait, c’est un simple principe d’organisation de code, et il y en a d’autres. Mais généralement, c’est basé sur la répartition suivante :

  • Une part du code gère l’affichage. C’est la partie “Vue”.
  • Une part du code gère la manipulation des données. C’est la partie “Modèle”.
  • Tout le reste. L’espèce de merdier qu’on va mettre en place pour faire marcher le programme, c’est le contrôleur. Souvent, c’est le code qui réagit à l’action de l’utilisateur, mais pas seulement.

MVC est typiquement quelque chose d’abstrait qu’on ne peut pas comprendre avec une explication seule. Passons donc rapidement à un exemple.

Imaginons que l’on ait des tas de fichiers CSV ainsi faits :

"Jeu";"Nombre de joueurs Max";"Support"
"Secret of Mana";"3";"Super Nintendo"
"Bomberman";"8";"Super Nintendo"
"Mario Kart";"4";"Nintendo 64"
"Age of Empire 2";"8";"PC"

Et que nous voulions un programme qui fasse un rapport sur le CSV, affichant :

Nombre de jeux analysés : 10

Détails
--------

Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8

Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4

etc

Il y a de nombreuses manières de coder ce programme. Si on le fait en suivant le principe du modèle MVC, on va faire 3 fichiers : un pour le modèle, un pour la vue, et un pour le contrôleur. On peut avoir plus ou moins de 3 fichiers, j’ai choisi 3 fichiers pour bien illustrer le principe de séparation des responsabilités.

Le modèle

Le modèle manipule la donnée. Dans un site Web, le modèle est souvent le code qui permet de faire de requêtes à la base de données. Dans notre cas, c’est le code qui va manipuler le CSV. Encore une fois, il n’y a pas de définition divine de ce qu’est un modèle, ceci n’est qu’un exemple de ce que cela PEUT être. C’est le choix du dev.

modele.py

 
from __future__ import unicode_literals, absolute_import
 
from csv import DictReader
from collections import OrderedDict
 
class Modele(object):
 
    def __init__(self, csv):
        self.total_jeux = 0
        self.supports = OrderedDict()
        with open(csv) as f:
            # on parse le csv
            for data in DictReader(f, delimiter=b';', quotechar=b'"'):
                # on calcule les stats pour que ligne du csv
                support = self.supports.setdefault(data['Support'], {})
                support['nombre_de_jeux'] = support.get('nombre_de_jeux', 0) + 1
                self.total_jeux += 1
                if support.get('joueurs_max', 0) < data['Nombre de joueurs Max']:
                    support['joueurs_max'] = data['Nombre de joueurs Max']
 
    def __iter__(self):
        # goodies pour pouvoir itérer sur le modèle
        return self.supports.iteritems()

Ca s’utilise comme ça :

>>> modele = Modele("Bureau/jeux.csv")
>>> modele.total_jeux
4
>>> for support, data in modele:
    print support
    print data
...
Super Nintendo
{u'nombre_de_jeux': 2, u'joueurs_max': '8'}
Nintendo 64
{u'nombre_de_jeux': 1, u'joueurs_max': '4'}
PC
{u'nombre_de_jeux': 1, u'joueurs_max': '8'}

On voit ici le principe : le modèle ne fait que manipuler la donnée, et rien d’autre. Il extrait, regroupe, calcule, raffine, et donne une belle interface propre pour que le reste du programme puisse utiliser le résultat sans avoir à connaitre les détails du traitement.

La vue

La vue, c’est de la présentation. C’est comment on veut que la donnée soit présentée à l’utilisateur. Ça peut être le code qui pond du HTML ou produit un CSV, ou fait configurer de jolis boutons dans une UI.

Dans notre cas, c’est le code qui va formater le texte pour la console.

On veut un truc comme ça :

Nombre de jeux analysés : 10

Détails
--------

Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8

Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4

Normalement, on voudrait un template. Mais on a pas de langage de template qui accepte les boucles dans la lib standard, alors on va faire comme la norme WSGI et retourner un générateur de strings.

vue.py

from __future__ import unicode_literals, absolute_import
 
def rapport(modele):
    # affichage de l'en-tête
    yield ("Nombre de jeux analysés : {total_jeux}\n\n"
           "Détails\n--------\n").format(total_jeux=modele.total_jeux)
 
    # affichage des stats pour chaque console
    for support, data in modele:
        yield ("Support: {support}\n"
               "Nombre de jeux : {nombre_de_jeux}\n"
               "Nombre de joueurs max : {joueurs_max}\n").format(
               support=support, **data)

Et ça s’utilise comme ça :

>>> m = Modele("Bureau/jeux.csv")
>>> list(rapport(m))
[u'Nombre de jeux analys\xe9s : 4\n\nD\xe9tails\n--------\n', u'Support: Super Nintendo\nNombre de jeux : 2\nNombre de joueurs max : 8\n', u'Support: Nintendo 64\nNombre de jeux : 1\nNombre de joueurs max : 4\n', u'Support: PC\nNombre de jeux : 1\nNombre de joueurs max : 8\n']

Encore une fois, ceci n’est pas LA manière de faire une vue. Ceci est UNE manière de faire une vue. Le but de la vue est de contenir le code qui se charge de formater la donnée pour l’utilisateur.

Il est plus courant d’utiliser un template pour cela, c’est à dire une sorte lib de texte à trou à remplir plus tard avec le modèle. C’est plus facile et flexible qu’une fonction. Il y a des tas de libs de templates en Python. Je ferai sans doute un article dessus un jour. Si vous voulez un truc simple et rapide, utilisez templite : rien besoin d’installer, ça tient dans un fichier. Si vous voulez le truc le plus standard possible, utiliser jinja2, c’est plus ou moins la lib la plus connue actuellement.

Le contrôleur

Le contrôleur, c’est tout le reste. Essayer de définir le contrôleur est généralement voué à l’échec, tant sa nature change d’une application à l’autre. Certains disent que c’est le code glue qui permet de lier le modèle et la vue. D’autres qu’il contient la logique de flux du programme. Personnellement, je vous invite à vous fier à la définition “c’est tout le reste”. Avec l’expérience, vous en viendrez à faire des modèles et des vues de plus en plus adaptées, et la partie contrôleur découlera d’elle-même.

De toute façon, aucun MVC n’est parfait, et un peu de vue dégouline parfois sur le contrôleur, un peu de contrôleur coule dans le modèle, ou inversement. Il ne sert à rien d’être un nazi du MVC, c’est une bonne pratique, pas un dogme religieux.

Dans notre cas le programme a besoin d’un code qui :

  • Importe notre vue et notre modèle.
  • Prend en paramètre le fichier CSV via la ligne de commande.
  • Mélange tout ça pour afficher le résultat dans la console.

Le contrôleur est par ailleurs le point d’entrée d’un programme. Et ce sera essentiellement ça, le contrôleur de notre programme : un point d’entrée.

controlleur.py

from __future__ import unicode_literals, absolute_import
 
import os
import sys
 
from vue import rapport
from modele import Modele
 
# on prend le csv en paramètre du script
try:
    f = sys.argv[1]
except IndexError:
    sys.exit("Veuillez passer le chemin d'un fichier CSV en paramètre.")
 
# on vérifie que le csv existe
if not os.path.isfile(f):
    sys.exit("Le fichier '%s' n'existe pas" % f)
 
# on analyse le CSV et on affiche le rapport
for texte in rapport(Modele(f)):
    print texte

Résultat final

$ python controlleur.py jeux.csv
Nombre de jeux analysés : 4
 
Détails
--------
 
Support: Super Nintendo
Nombre de jeux : 2
Nombre de joueurs max : 8
 
Support: Nintendo 64
Nombre de jeux : 1
Nombre de joueurs max : 4
 
Support: PC
Nombre de jeux : 1
Nombre de joueurs max : 8

Vous pouvez télécharger le code Python de cet article.

Exemple en PHP

Le PHP a eu beaucoup de succès du fait de la facilité avec laquelle on pouvait coder un site Web, en mélangeant code et HTML. Malheureusement cela a donné lieu à des codes très sales, où on trouvait les requêtes SQL à côté de l’affichage d’un tableau, l’analyse des paramètres $_GET à deux pas de la vérification du mot de passe.

MVC a été une réponse à cela.

Un modèle MVC propre sera généralement très riche et complexe, mais il est possible de bricoler un site en MVC basique à la main sans trop de problème. Je ne vous recommande pas d’utiliser ce code en prod, mais c’est un bon début pour comprendre comment ça marche. Une fois que vous serez à l’aise avec l’idée, n’hésitez pas à coder le votre sur un petit projet, puis à tester un framework. Symfony, par exemple, est une valeur sûre en PHP.

Admettons que notre site ait deux pages : accueil et liste des utilisateurs.

L’accueil dit juste bonjour, la liste affiche tous les utilisateurs du site Web. Passionnant.

Le modèle

L’idée est de mettre toutes les requêtes SQL au même endroit.

Les vieux routards du PHP m’excuseront, mais je n’ai plus codé dans ce langage depuis des années, donc mon style va dater un peu :-) Et honnêtement tous ces points-virgules, ces dollars et ces brackets dans tous les sens, sans compter la flèche comme caractère de look up, ça me perturbe grandement.

modele.php

<?php
 
$con = mysqli_connect("127.0.0.1", 'root', 'admin123', 'ma_db');
 
class User {
 
    public $name;
    public $age;
 
    function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
 
    static function liste() {
 
        $users = array();
 
        $query =  mysqli_query($con, 'SELECT * FROM `user`');
 
        while ($row = mysql_fetch_assoc($query))
        {
            $users[] = User($row[0], $row[1]);
        }
 
        return users;
    }
 
}

Et ça s’utilise comme ça :

$users = User->liste();
foreach ($users as $user) {
    echo $user.name . '(' . $users.age. 'ans)';
}

Ce qui affiche tous les noms et les ages des utilisateurs.

Bien, on a isolé l’accès aux données, maintenant on va isoler la mise en forme.

La vue

Ou plutôt, les vues, puisqu’on a deux pages, et donc deux vues.

Vous ne le savez peut être pas, mais PHP vient avec une syntaxe alternative spécialement conçue pour être utilisée dans le HTML. Elle est similaire à la syntaxe originale, mais les blocs sont ouverts avec : au lieux de { et fermés par endinstruction. Les variables sont affichées avec <?=$nom_de_variable?>.

Par exemple:

<?php if $truc: ?>
    <p>
        <?=$machin?>
    </p>
<?php endif; ?>

Cette syntaxe permet de bien séparer le texte du code PHP, et donc sera utilisée pour la vue.

accueil.php

<html><body><h1>Bonjour</h1></body></html>

liste_utilisateurs.php

<html>
    <body>
        <h1>Utilisateurs</h1>
 
        <ul>http://www.php.net/manual/fr/control-structures.alternative-syntax.php
            <?php foreach $users as $user: ?>
                <li><?=$user->name?> (<?=$user->age?> ans)</li>
            <?php endforeach; ?>
        </ul>
 
    </body>
</html>

Et voilà, on a deux pages, et la deuxième affiche notre liste d’utilisateur. Vous remarquerez qu’il n’y a pas de requête ou de logique de choix de page, pas d’accès à mysql_* ou à $_GET dans ce code. Que de l’affichage.

Le contrôleur

Puisque le contrôleur, c’est le reste, ce sera à la fois notre point d’entrée, notre code glue et notre routing.

 
<?php
 
if (isset($_GET['page']) && $_GET['page'] == 'liste') {
    require 'modele.php'
    require 'liste_utilisateurs.php'
} else {
    require 'accueil.php'
}

Et c’est tout.

Si l’utilisateur va sur l’adresse monsite.com/, il va arriver sur l’accueil, si il va sur monsite.com/?page=liste, il va atterrir sur la liste des utilisateurs.

Si on veut changer le look de la page, on modifie la vue. Si on veut changer de base de source de données (et lire par exemple depuis un fichier), on change le modèle. Si on veut rajouter des pages, on change le contrôleur. L’avantage de la séparation des responsabilités, c’est la facilité de lecture et donc de maintenance et d’évolution.

J’insiste sur le fait que c’est un exemple pédagogique, et pas quelque chose à utiliser en prod (par exemple à cause des URLs très moches). Mais il va vous permettre de coder votre premier site en MVC, et plus tard, aller vers des versions plus sérieuses.

L’important, c’est la séparation donnée / formatage / reste du code.

MVC dans la vraie vie vivante

Créer un modèle MVC à la main propre et efficace, c’est énormément de taff. C’est pour cela qu’on utilise des outils tout fait comme des frameworks Web ou des libs graphiques (Qt, wxWidget et Gtk ont toutes des outils MVC, ex : Qt possède QML, un dialecte type CSS pour manipuler des vues).

Un modèle MVC simple, mais propre, est celui du micro-framework Web Python bottle, dont Max vous avait parlé ici. Lisez l’article, et revenez à ce paragraphe, et vous comprendrez que :

  • La vue, c’est le template.
  • Le modèle n’est inclus dans bottle, il faut le faire à la main ou utiliser quelque chose comme un ORM (peewee est très bien adapté).
  • Le contrôleur, ce sont les fonctions qu’on trouve sous les décorateurs @url.

Comme je l’ai dit précédemment, il y a de nombreuses manières de séparer le contenu de sa présentation.

Django par exemple n’utilise pas un modèle MVC au sens traditionnel, mais plutôt du MVT (Modèle – Vue – Template). Ce qu’il appelle les vues sont en fait ce qu’on appelle le contrôleur dans bottle : les fonctions qui mélangent les données avec le template. Django propose par contre un ORM, qui est bel est bien un système de modèle très élaboré.

C’est une question de sémantique, et au final, qu’importe le flacon, pourvu qu’on ait l’ivresse.

]]>
http://sametmax.com/quest-de-que-mvc-et-a-quoi-ca-sert/feed/ 23
Le choix d’un langage influence le fun de votre carrière 32 http://sametmax.com/le-choix-dun-langage-influence-le-fun-de-votre-carriere/ http://sametmax.com/le-choix-dun-langage-influence-le-fun-de-votre-carriere/#comments Tue, 14 May 2013 09:47:04 +0000 http://sametmax.com/?p=6095 Les langages de programmation sont censés être des technologies neutres, mais comme toute chose utilisée dans le monde réel pour des usages concrets et nombreux, l’humain finit par leur donner une orientation, une préférence.

De fait, chaque langage finit par être plus utilisé dans certains types de métiers ou d’activités, pour certains types de projets, dans certains environnements. Plus important encore, un langage appelle d’autres outils, et un certain type de collègue, et même si, comme d’habitude, la généralisation est un piège à con, il y a bien des stéréotypes visibles qui se dégagent.

Ainsi, Java et PHP sont des langages pour lesquels il est très facile de trouver du travail. Il y a une telle base de code là dehors qu’il y a des annonces partout, et tout le temps. En revanche, les environnements Java sont généralement très très lourds à manipuler. Pas en terme de performance (Java est aujourd’hui très rapide), mais en terme de charge de travail : énormément de configuration, beaucoup d’abstractions et de design pattern imbriqués, des APIs et des formats très verbeux…

Travailler dans un monde Java, c’est généralement travailler à un rythme lent, plus souvent dans des grosses boîtes, pour des systèmes assez larges avec un grosse inertie. Ne vous attendez pas à des Java-party avec vos collègues après le boulot.

PHP, c’est la même chose, avec des projets et composants plus simples (dans des entreprises de toutes tailles), et souvent bien plus dégueulasses. Non pas qu’on ne puisse pas écrire du PHP propre, mais 15 ans de PHP codé à l’arrache ne s’effacent pas avec quelques années de Symfony, et autre cakePHP. Du coup on hérite souvent d’un projet moche comme ta mère.

Bref, Java et PHP vous garantissent un job, mais les projets sont rarement marrants, et l’ambiance au taff sera pas pumped up. Par contre il y a de la doc et des tutos, même si généralement ceux pour PHP sont de meilleure qualité que pour Java (qui peuvent être assez indigestes).

Il y a une alternative intermédiaire : le C#. Beau langage, beaux outils, c’est propre sans être trop lourd, et la base de code est pas à vomir. Mais on reste dans le corporate, et la plupart du temps, sur du 98% Microsoft ou affiliés.

Après vous avez des langages spécialisés comme Erlang, Scala, Lisp. Là, trouver du taff sera généralement un challenge. Ce n’est pas le genre de truc qu’on voit partout. Et le niveau sera difficile : un débutant peut arriver avec la bite et le couteau dans un projet PHP, mais si vous vous pointez avec 3 mois d’expérience sur une gateway jabber haute tolérance, le bidouillage ça va pas le faire.

Ce sont des langages qu’il faut choisir si on aime le taff de haut niveau, la débrouillardise et les solutions intelligentes. En général les missions sont super intéressantes, mais la reconversion est dure. Par contre, vous rencontrerez des collègues qui valent le détour.

Puis il y a les langages de bas niveau, type C/C++. Aujourd’hui, c’est massivement des missions scientifiques ou de l’embarqué : analyse d’image, cartes électroniques, petites machines, etc. Il y a encore des trucs pas cool genre Windev (j’ai un pote coincé là dedans, c’est triste, fuyez ces annonces), mais ce n’est plus la majorité. Ces langages, c’est une question de tempérament. Est-ce que vous aimez la minutie ? L’efficacité des algos ? Le travail sous contrainte technique ? Si oui, alors ce genre de taff peut être très sympa. Sinon, choisissez un langage plus dynamique.

Ensuite il y a les langages de niches : Cobol, Power Builder, etc. Là c’est généralement des projets de merde très bien payés. Plus personne ne veut coder avec ces trucs, mais ça coûte trop cher à changer. Les jeunes sont acceptés, les formations offertes, le salaire de début de carrière est bon, mais par contre, le code est un truc de mémé. C’est pas mal pour commencer une carrière et se faire un peu de thune, mais faut savoir en sortir, et ça ne donne pas de bonnes habitudes en prog.

Et pour finir il y des langages dit “modernes” (ce qui est un abus… de langage, car ils ne datent pas d’hier) comme Python, Ruby ou Javascript. Le côté “moderne” vient surtout du fait qu’ils sont maintenant très adaptés à des projets modernes, où la souplesse de développement a priorité sur le reste des caractéristiques. Choisissez ces langages si vous visez des missions, principalement Web, sur des produits récents. Ce sont les communautés les plus sympas et dynamiques, ça brasse pas mal dans le milieu et c’est assez jeune. Mais un grand nombre de start up, donc il faut pas chercher la sécurité de l’emploi.

Une petite parenthèse pour Python tout de même : il n’est pas autant limité au Web et on le voit aussi beaucoup dans le secteur scientifique et bancaire. C’est sa versatilité et la facilité de reconversion qu’il offre qui me fait dire que c’est un très bon choix comme premier langage. Mais. Car il y a un “mais”. Trouver une offre Python sera plus difficile qu’une offre Ruby, et BEAUCOUP plus difficile qu’une offre PHP ou Java. Après perso je n’ai jamais connu le chômage.

Warning ! Il faut faire gaffe à se lancer dans le dev Web. C’est très tentant, mais contrairement aux autres carrières, ça veut dire une courbe d’apprentissage beaucoup plus longue. Vous ne pouvez pas juste apprendre votre langage, ses libs et son env et vous vendre comme “expert”.

J’ai laissé pas mal de truc de côté, comme l’objective C, Haskell, Smalltalk, le Bash, Perl et quelques milliers d’autres langages. On ne peut pas tout faire, et je voudrais surtout peindre un tableau global du marché. En me relisant, je me dis que c’est bourré d’idées reçues, qu’il y a plein d’exceptions, etc. Mais je pense que les infos données peuvent permettre à des gens qui se posent la question de l’orientation de prendre une décision moins mauvaise que “je lance un D20 et on verra bien”.

]]>
http://sametmax.com/le-choix-dun-langage-influence-le-fun-de-votre-carriere/feed/ 32
Python, Ruby et PHP sont lents 30 http://sametmax.com/python-ruby-et-php-sont-lents/ http://sametmax.com/python-ruby-et-php-sont-lents/#comments Tue, 30 Oct 2012 16:44:54 +0000 http://sametmax.com/?p=2787 Ce matin je lançais ma petit recherche Python/Django/Git habituelle sur Twitter pour voir ce qui s’y tramait, quand je suis tombé sur plusieurs tweets qui m’ont fait tiquer.

Il y en a un qui résume très bien l’idée:

Cay de la merde je préfère les langages plus bas niveau, Python c’est lent.

C’est un argument que je lis souvent, et qui est aussi utilisé contre Ruby ou PHP.

Si j’ai la personne en face de moi, généralement la question qui suit est:

Ouai je comprends. C’est quoi ta contrainte ? Tu dois exécuter quel calcul sous quelle limite de temps ?

Il n’y a jamais aucune réponse autre qu’un bafouillement, car une personne qui a une réelle contrainte de temps d’exécution ne sort pas ce genre d’ânerie: il utilise le bon outil pour le bon travail selon des metrics précises, pas selon le nom du langage.

Ceux qui trollent, généralement des étudiants en informatique qui n’ont encore jamais codé d’utile de leur vie (je l’ai fait, donc je +1, d’ailleurs parfois je le fais toujours :-p), ont entendu / lu que Python, Ruby et PHP étaient lents, et donc les rejette car ils ne correspondent pas à l’idée qu’il se fait de la programmation, encore naissante dans sa tête.

La vérité est que les personnes qui ont des contraintes de temps d’éxécution auquel langage X, n’importe quel langage, ne peut pas répondre, font partie des 0.00000001 de la population des programmeurs: le plus souvent dans l’embarqué (voiture, satellite, microchips, etc) et les systèmes temps réels (navigation, chaîne de productions, bourse d’échange…). Pour les autres, nous, quasiment tous, les problèmes de performances se joueront sur l’algo, les libs, les serveurs Web et BDD, le caching, etc. On peut programmer en Basic ou en GOTO++, ça ne change rien.

Languages, libraries and frameworks don’t scale. Architectures do.

Cal Henderson

Dans certains cas, par exemple le calcul scientifique, les interfaces graphiques ou les jeux videos, la rapidité est importante, et on le sait sans même mesurer. Mais ces problèmes-là sont résolus depuis longtemps: il existe des bindings en C extrêmement rapides qui permettent de coder dans son langage interprété favori tout en bénéficiant d’algos Speedy Gonzalez sous le capot.

Par ailleurs, ça a déjà été dit 1000 fois, mais ça ne fait pas de mal de le répéter:

Le salaire du développeur et l’impact commercial de la lenteur d’un développement coûtent immensément plus cher qu’un ordinateur plus puissant. Qui d’ailleurs ne coûtera plus rien dans 6 mois.

Alors OK, ce n’est pas une invitation à coder avec ses pieds sous prétexte qu’on la puissance à disposition (ce qui se fait malheureusement de plus en plus).

Mais choisissez une techno parce que vous êtes productif avec, pas pour ses performances. Sauf si vous avez des mesures chiffrées qui s’imposent à vous. Auquel cas vous n’avez de toute façon pas besoin de lire cet article, vous êtes plus compétent que moi.

]]>
http://sametmax.com/python-ruby-et-php-sont-lents/feed/ 30
Déterrer le cadavre d’un troll : Non, PHP n’est pas simple 28 http://sametmax.com/deterer-le-cadavre-dun-troll-non-php-nest-pas-simple/ http://sametmax.com/deterer-le-cadavre-dun-troll-non-php-nest-pas-simple/#comments Sat, 28 Jul 2012 14:18:19 +0000 http://sametmax.com/?p=1132 Ce n'est pas grâce au langage, mais en dépit de lui.]]> Sujet traité 100 fois mais c’est à vocation thérapeuthique.

Quand on demande pourquoi je préfère Python à PHP, je réponds souvent que Python est beaucoup plus simple à utiliser. C’est généralement le moment où tout le monde me répond que PHP est justement un langage qui a eu du succès parcequ’il était simple.

Faux.

PHP a eu du succès parcequ’il a une excellente doc, des tutoriaux partout sur le net, et une méthode de déploiement facile proposée pour la plupart des hébergeurs.

Le code lui-même, n’est pas simple du tout. Prenez par exemple la fonction de base pour ouvrir un fichier, fopen().

@fopen('http://example.com/not-existing-file', 'r');

Savez vous ce que ce code va faire ?

Si PHP a été compilé avec --disable-url-fopen-wrapper, ça ne marchera pas. “Ne marchera pas” veut-il dire retourner null ou lever une exception ? Aucune idée, la doc ne le dit pas. De toute façon cette option est retirée avec PHP 5.2.5, et nous sommes en PHP 5.3.10 sur la plupart des distribs. Sauf sur la plupart des hébergeurs…

Si allow_url_fopen est désactivé dans php.ini, ça ne marchera pas non plus. Aucune idée de pourquoi.

Grâve à ce merveilleux “@”, le warning en cas de fichier n’existant pas ne sera pas affiché pas vrai ?

Sauf bien sur si scream.enabled est activé dans le php.ini ou manuellement avec ini_set.

A condition que le niveau error_reporting soit ajusté en conséquence.

Mais alors sa sortie dépend du paramètre display_errors, lui même activable dans le php.ini ou par ini_set.

Et si par un hasard incroyable vous savez tout cela, savez-vous comment est configuré votre hébergeur ?

This is going to be legend…

Après il est vrai que l’écosystème de Python, lui, possède une richesse qui le rend complexe. et le déploiement n’est pas aussi simple qu’avec PHP. D’ailleurs, je ne prétend pas qu’on ne puisse pas coder d’excellents produits avec PHP. Mais ce n’est pas grâce à la prétendue simplicité du langage (Ruby et même Javascript sont plus simples).

Le Web utilise par exemple massivement le JSON. Mais saviez-vous que json_decode retourne null en cas d’erreur de décodage ? Ce qui est aussi une valeur de retour de décoage de JSON valide, vous forçant donc à a vérifier json_last_error à chaque appel.

Je vous passe l’habituelle critique de la surabondance de fonctions PHP pour faire des choses similaires, mais avec des noms complètement à l’arrache (chercher du texte: ereg, eregi, mb_ereg, mb_eregi, preg_match, strstr, strchr, stristr, strrchr, strpos, stripos, strrpos, strripos, mb_strpos, mb_strrpos) ainsi que de l’inutilité totale du signe == ("foo" == true, et "foo" == 0 mais true != 0).

Par contre, quelqu’un pourrait m’expliquer pourquoi les noms des variables sont sensibles à la casse, mais PAS ceux des fonctions et classes ?

Et vous voulez vérifier qu’une variable est vide ou n’existe pas ? empty() est là pour ça ! Sauf que empty($var) marche, mais empty($var1 || $var2) lève une exception.

…wait for it…

En parlant d’exception, amusons nous un peu avec la gestions des erreurs:

  • la généricité c’est pas pour demain: curl_error, json_last_error, openssl_error_string, imap_errors, mysql_error, xml_get_error_code, bzerror, date_get_last_errors
  • on autorise à rendre les erreurs silencieuses avec ‘@’ (wtf ?)
  • pas de stack trace. Un bug ? Démerde toi. Le langage est en typage faible, tu vas t’amuser !
  • :: est nommé T_PAAMAYIM_NEKUDOTAYIM et <<, T_SL, et la première fois qu'on le voit dans un message d'erreur, ça fait tout drôle. Il vaut mieux ne pas être déconnecté.
  • E_ALL inclue toutes les catégories d'erreur. Sauf E_STRICT. Quelqu'un sait à quoi sert E_STRICT ?
  • on a pas le droit de lever une exception dans __toString ! Si on le fait, PHP balance une erreur fatale, et ...
  • ... les erreurs fatales ne peuvent pas être gérées dans un try/catch. Et de toute façon il n'y a pas de clause finally.

Et quand PHP évolue, au lieu de corriger ce bordel, on rajoute une couche !

Par exemple, PHP est faiblement typé. Sauf qu'on peut maintenant forcer le type des paramètres d'une fonction en donnant ce qu'on appelle des type hints. Sauf qu'on qu'on ne peut pas choisir "string" ou "int" ni aucun type de base pour cela. Sauf que les fontions de les bibliothèques standards elles, le font sans problème. Il faut donc utiliser une classe que l'on fait soit même. Sauf que les "type hints" acceptent une classe non déclarée comme type (faute de frappe mon ami...). Sauf que dans ce cas on ne peut passer aucun argument à la fonction. Absolument aucun.

Par contre, mettre des arguments nommés pour les fonctions, ça, on ne le rajoute pas. Ca rendrait le code plus bordélique, et il faut garder le langage simple. C'est d'ailleurs, j'en suis certain, au nom de cette simplicité que mktime a pour ordre d'arguments: heure, minute, seconde, mois, jour, année.

... dary !

Heureusement il y a des fonctions cachées qui font que PHP est vraiment un langage pour faciliter la vie du débutant:

preg_replace avec le flag /e prend la chaine, effectue le remplacement, et ensuite fait un eval() dessus !

scandir retourne une liste de fichiers dans un dossier, mais pas dans l'ordre du dossier. Non, ce serait pas fun. Il les retourne dans l'ordre alphabetique, ou l'inverse si on lui passe un argument. Ce qui est logique, puisque PHP est bien connu pour manque de fontions de tris: array_multisort, arsort, asort, ksort, krsort, natsort, natcasesort, sort, rsort, uasort, uksort, usort.

parse_str ne parse pas une chaîne, mais une "query string". Qu'il dump ensuite dans l'espace de nom local. A moins qu'on lui passe un array à populer. Mais dans tous les cas la fonction ne retourne rien.

gzgetss parse les lignes d'un fichier *.gz et en retire les ligne HTML. Indispensable dans la vie de tous les jours, ça valait le coup d'en faire une fonction built-in. Hier encore je me demandais comme j'aillais rajouter cette opération à toutes mes vues Django.

php_uname retourne l'OS courrant, mais en cas d'échec, il retourne silencieusement l'OS sur lequel il a été compilé.

Et les arrays... Cette structure de donnée magique qu'on aime tant quand on commence à programmer. Ca fait tout, tout je vous dis !

Les arrays sont une combinaisons de lists, ordered hashes, ordered sets et sparse lists. Là ou c'est amusant, c'est quand on tente de les comparer:

array_diff(array("marc" => 12, "alice" => 23),
           array("marc" => 23, "alice" => 12));

Quel comportement vérifier, celui d'une liste, d'un set ou d'un mapping ?

Ici la réponse est un set, ce qui fait que les deux arrays sont considérés comme égaux.

Par contre:

array("foo", "bar") != array("bar", "foo")

Et:

array("foo" => 1, "bar" => 2) == array("bar" => 2, "foo" => 1)

Tient, tant qu'on est dans la prédictibilité de comportement. Puisque PHP est un langage simple, quelqu'un peut me prédire ce que ça fait ?

php > $a = null;
php > $a++;
php > echo $a;

Non ?

Ben ça a affiche 1. Si, si.

Mais comme on aime bien la congruence chez Zend:

php > $a = null;
php > $a--;
php > echo $a;

Ça n'affiche rien.

Et pour finir sur une petite note de sécurité spécial noob: include accepte une url en paramètre.

J'ai passé des années à coder en PHP. J'ai vu du code très propre faire des choses très belles. Ce n'est pas grâce au langage, mais en dépit de lui.

Je suis heureux que le langage ai eu un tel succès. Il a permis au Web de devenir ce qu'il est. Il est temps maintenant de passer à autre chose.

]]>
http://sametmax.com/deterer-le-cadavre-dun-troll-non-php-nest-pas-simple/feed/ 28
WordPress, je te hais 4 http://sametmax.com/wordpress-je-te-hais/ http://sametmax.com/wordpress-je-te-hais/#comments Wed, 18 Jul 2012 16:29:31 +0000 http://sametmax.com/?p=1203 Cherchant à rendre le formulaire commentaires de Sam et Max plus ergonomique, j’ai jeté un coup d’oeil sur le template lié.

L’intégralité du code qui génère le formulaire pourri que vous voyez sous l’article est ici:

<?php if ('open' == $post->comment_status) : ?>
        <?php comment_form( array('label_submit' => esc_attr__( 'Submit Comment', 'Minimal' ), 'title_reply' => '<span>' . esc_attr__( 'Leave a Reply', 'Minimal' ) . '</span>', 'title_reply_to' => esc_attr__( 'Leave a Reply to %s' )) ); ?>
<?php else: ?>
 
<?php endif; // if you delete this the sky will fall on your head ?>
</div> <!-- end comment-section -->

Je vous passe le commentaire amical qui stipule “si vous supprimez ceci le ciel vous tombera sur la tête” et toute remarque désobligeante sur le formatage absolument bordélique de l’intégralité du fichier.

Non, je reste optimise, je vois un comment_form qui encapsule la génération du formulaire. Peut être une API propre ? J’ai bon espoir (mais le fait que ce soit une fonction et non une classe aurait du me mettre la puce à l’oreille).

Je trouve un très bon article qui explique où on trouve ce merveilleux comment_form (ben oui parceque les includes et autoloads en PHP c’est pas vraiment le truc le plus top pour retrouver la chaîne de dépendances). Et là, je n’ai rien supprimé, mais le ciel m’est quand même tombé sur la gueule. Et toute la couche d’ozone avec.

La valeur par défaut d’UN des paramètres de la fonction qui génère le formulaire, c’est ça :

<?php $defaults = array( 'fields' => apply_filters( 'comment_form_default_fields', array(
    'author' => '<p class="comment-form-author">' .
                '<label for="author">' . __( 'Name' ) . '</label> ' .
                ( $req ? '<span class="required">*</span>' : '' ) .
                '<input id="author" name="author" type="text" value="' .
                esc_attr( $commenter['comment_author'] ) . '" size="30" tabindex="1"' . $aria_req . ' />' .
                '</p><!-- #form-section-author .form-section -->',
    'email'  => '<p class="comment-form-email">' .
                '<label for="email">' . __( 'Email' ) . '</label> ' .
                ( $req ? '<span class="required">*</span>' : '' ) .
                '<input id="email" name="email" type="text" value="' . esc_attr(  $commenter['comment_author_email'] ) . '" size="30" tabindex="2"' . $aria_req . ' />' .
                '</p><!-- #form-section-email .form-section -->',
    'url'    => '
<p class="comment-form-url">' .</p>
 
                '<label for="url">' . __( 'Website' ) . '</label>' .
                '<input id="url" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" tabindex="3" />' .
                '
 
<!-- #<span class="hiddenSpellError" pre="">form-section-url</span> .form-section -->' ) ),
    'comment_field' => '<p class="comment-form-comment">' .
                '<label for="comment">' . __( 'Comment' ) . '</label>' .
                '<textarea id="comment" name="comment" cols="45" rows="8" tabindex="4" aria-required="true"></textarea>' .
                '</p><!-- #form-section-comment .form-section -->',
    'must_log_in' => '
<p class="must-log-in">' .  sprintf( __( 'You must be <a href="%s">logged in</a> to post a comment.' ), wp_login_url( apply_filters( 'the_permalink', get_permalink( $post_id ) ) ) ) . '</p>
 
',
    'logged_in_as' => '
<p class="logged-in-as">' . sprintf( __( 'Logged in as <a href="%s">%s</a>. <a title="Log out of this account" href="%s">Log out?</a></p>
 
' ), admin_url( 'profile.php' ), $user_identity, wp_logout_url( apply_filters( 'the_permalink', get_permalink( $post_id ) ) ) ),
    'comment_notes_before' => '<p class="comment-notes">' . __( 'Your email is <em>never</em> published nor shared.' ) . ( $req ? __( ' Required fields are marked <span class="required">*</span>' ) : '' ) . '</p>',
    'comment_notes_after' => '<dl class="form-allowed-tags"><dt>' . __( 'You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes:' ) . '</dt> <dd><code>' . allowed_tags() . '</code></dd>',
    'id_form' => 'commentform',
    'id_submit' => 'submit',
    'title_reply' => __( 'Leave a Reply' ),
    'title_reply_to' => __( 'Leave a Reply to %s' ),
    'cancel_reply_link' => __( 'Cancel reply' ),
    'label_submit' => __( 'Post Comment' ),
); ?>

Je ne sais pas ce qui me rend le plus malade.

L’absence totale de la notion de template ?

Le fait qu’on passe l’intégralité du code HTML en PARAMETRE à une fonction qui est censée générer le formulaire ?

Le fait que le code est absolument inmodifiable car c’est un mélange de concaténations, d’arrays et d’appels à gettext ?

Je voulais juste modifier le HTML du formulaire de comments bordel !

BANG !

]]>
http://sametmax.com/wordpress-je-te-hais/feed/ 4
MD5 en Bash, PHP, Python et Javascript 6 http://sametmax.com/md5-en-bash-php-python-et-javascript/ http://sametmax.com/md5-en-bash-php-python-et-javascript/#comments Mon, 18 Jun 2012 17:33:17 +0000 http://sametmax.com/?p=936 Bien que pour tout ce qui touche à la sécurité (comme les mots de passe ou les signatures) il vaille mieux préférer des fonctions plus sécurisées comme SHA-2, MD5 reste très pratique pour vérifier l’intégrité d’un fichier ou pour éviter les doublons.

Bash

echo -n "chaine à hasher" | md5sum

(N’oubliez pas -n sinon vous aurez un saut de ligne ajouté à la chaîne.)

ou

md5sum /fichier/a/hasher

Python

>>> import hashlib
>>> hashlib.md5('chaine à hasher').hexdigest()

ou

>>> import hashlib
>>> hashlib.md5(open("/fichier/a/hasher").read()).hexdigest()

La lib hashlib contient également des fonctions pour les hashs sha1, sha224, sha256, sha384 et sha512.

PHP

echo md5("chaine a hasher");

ou

echo md5(fread(fopen('/fichier/a/hasher', 'r'), TAILLE_DU_FICHIER));

A noter que l’encodage du terminal et code jouent sur le résultat. Rappelez-vous aussi que PHP et Python n’utilisent pas UTF-8 par défaut.

Javascript

Il n’existe pas de fonction cross-browser qui permette de récuperer le hash MD5 d’une chaîne, mais on peut utiliser des bibliothèques externes. La plus connue est celle de Paul Johnston que nous utilisons pour créer les hashs visuels de Vizhash.js, et donc dans 0bin.

On insère le code:

<script type="text/javascript" src="md5.js"></script>

Puis:

alert(hex_md5('chaine a hasher'));

Comme Javascript n’a pas normalement accès au système de fichiers, il n’y a pas de version qui permette de calculer la somme de contrôle d’un fichier directement.

UPDATE après commantaires:

Pour éviter les problèmes liés à l’encoding, il vaut mieux ouvrir le fichier en mode binaire: fopen('fichier', rb') et open('fichier', 'rb') en PHP et Python.

Pour PHP, peut utiliser directement md5_file pour calculer le MD5 d’un fichier.

Pour Python, dans le cas de gros fichiers, utiliser read() charde tout le contenu en mémoire. On peut éviter ça en utilisant une approche par morceaux, telle que:

hash = md5()
with open('fichier') as f:
    for chunk in f.read(500):
        hash.update(chunk)
print hash.hexdigest()
]]>
http://sametmax.com/md5-en-bash-php-python-et-javascript/feed/ 6