Archives pour la catégorie Développement web

Sur-Javascript

J’avais regardé CoffeeScript il y a longtemps, mais sans être convaincu. Si j’ai besoin de faire du Javascript, je fais du Javascript. Coffee apporte bien des améliorations sur la syntaxe, mais le langage n’est lui-même pas parfait et je doute que le rapport bénéfice/coût soit très élevé.

J’en ai à peu près autant au service de Dart même si, sans réellement percer pourquoi, j’ai l’impression qu’ils ont réussi à mieux se détacher de Javascript, et donc avoir un vrai langage distinct qui « compile » du Javascript (c’est bien l’esprit de Coffee aussi, mais je n’ai pas eu ce ressenti).

Je suis probablement plus ouvert à TypeScript ou Traceur, qui sont plus proches du langage d’origine et dont les objectifs et syntaxes sont presque « le prochain Javascript ». On a plutôt une couche de compatibilité arrière, et c’est un bon système.

L’impression que ça donne est tout de même qu’ils ont fait leurs propres extensions qui n’appartiennent qu’à eux.

Est-ce qu’on a quelque part un projet similaire, qui implémente un maximum de nouveautés des futurs EcmaScript mais qui évite d’ajouter trop de syntaxes divergentes au cœur du langage ?

Quelles sont vos expériences avec l’un ou l’autre de ces systèmes ?

Documentation PHP

Quelques (nombreux) écrans de présentation de Willian Durand à propos de PHP

Je ne sais pas à qui est destiné cette documentation, mais c’est un boulot énorme et très bien fait de collecte, analyse et présentation des bonnes pratiques. Vous devriez passer dessus et prendre du temps à lire même si vous travaillez déjà avec PHP au jour le jour.

Pour m’être frotté à ce genre d’exercice, j’ai rarement vu un résultat aussi bon.

Il y a une version pour la suite qui parle plus particulièrement de Symfony, mais moins essentielle à mon avis.

Lien vers du Javascript

Problématique du jour : Intercepter l’appel à des liens via Javascript.

Mon cas d’usage : J’ai des contenus (images, vidéos, audio, polices de caractères) stockés côté client (indexedDB, webSQL ou DOMStorage) que je souhaite insérer dans mes pages.

(billet mis à jour au fur et à mesure des réponses)

Quelques solutions :

Data:URI

Je récupère ma donnée, je la transforme en base64, et je remplace le lien standard par un lien en data:uri.

Deux défauts : Je stocke N fois la donnée dans le DOM où N est le nombre d’apparition de l’image ou de la ressource dans mes pages HTML/CSS. Pour ne rien gâcher, on stocke en base64 donc avec 30% de poids en plus. De plus, même si je n’ai pas de test à montrer, on s’est déjà pris les pieds dans le tapis à cause de très mauvaises performances de pages avec beaucoup de data:uri, spécialement sur Firefox (probablement sur les polices de caractères)

Blob + createObjectURL

Je récupère ma donnée, je créé un Blob à partir de cette donnée, je passe par URL.createObjectURL pour créer une URL dédiée et j’utilise cette dernière quand je référence ma ressource.

On résout les problèmes du data:uri mais on se coupe de IE 9, IE mobile et iOS 5. Pas gravissime mais j’aurai préféré éviter.

Par contre la solution ne fonctionnera de toutes façons pas pour les images ou polices de caractères référencées depuis les CSS (sauf à construire les CSS via Javascript mais là on entre dans des usines à gaz).

Cas spécifique des vidéo et audio

Les deux solutions me posent de toutes façons un sérieux problème pour les vidéo et les audio, qui peuvent être de gros volume. Je me vois mal sortir d’indexedDB des dizaines de mégaoctets (au mieux) pour construire un blob juste et avoir une URL dans ma balise HTML sans même savoir si l’utilisateur tentera effectivement de lire la vidéo ou le fichier audio.

Pour les vidéos et les audio (mais uniquement ces deux types de contenu) je peux réfléchir à mettre un lien vers une vidéo de taille quasi nulle et le changer dès que la vidéo est activée. J’ai toutefois un peu peur des effets de bords. Il va falloir aussi bosser en amont pour que la première image s’affiche bien dans le lecteur vidéo malgré l’absence de la vidéo complète.

Bidouille

Pour l’instant ma solution serait :

  • Pour les images et polices de caractères dans les CSS : data:uri. En espérant que la CSS ne contient pas trop de ressources inutiles ou trop de liens vers la même ressource.
    • Au pire : Générer la CSS en Javascript avec des liens obtenus par createObjectUrl, l’insérer dans le DOM manuellement
  • Pour les images dans le code HTML : createObjectURL si possible.
    • Vérifier tout de même si le data:uri n’est pas plus simple. La différence entre les deux sera assez faible si les images ne sont pas répétées plusieurs fois.
  • Pour les audio et vidéo : Désactiver le preload, renseigner le lien via createObjectURL qu’au lancement de la vidéo. Pour les vidéo, penser à créer une image d’attente avec l’attribut poster.

Ça reste franchement du bricolage je trouve, et ça va nécessiter plein de javascript pour générer tout ça.

Dans mon monde idéal

Dans l’idéal j’aurai bien aimé avoir une sorte de faux serveur web en javascript depuis le navigateur. Genre toute url en « local-js://xxxxx » fait appel à un objet Javascript qui répond ensuite ce qu’il veut.

À défaut, un URL.createObjectURL( 'text/html', function() { return bindata; } ) serait bien pratique : Le navigateur n’appelant la fonction pour récupérer le contenu que quand il cherche à accéder au dit contenu, au lieu de lui donner tout le contenu par avance au cas où il en aurait besoin.

Quelqu’un a des pistes pour moi ?

Please stop pretending PHP is a good language

The first step to fixing a problem is admitting that there is one.

Bon, des critiques de PHP ce n’est pas ce qui manque mais pour une raison inconnue je m’étais dit que ça partait bien quand j’ai lu la première ligne. Sauf qu’au final

  • It’s not ok that you can’t reliably get the first element of an array using less than 4 lines of code without causing side effects.*[1]
  • It’s not ok that the output of echo 5/3 might depend on the country you live in if you don’t know the fine details of configuring PHP.
  • It’s not ok that you won’t be able can’t call array_map” or just “$iterator->reduce” on an iterator in 2014.
  • It’s not ok to ignore the simple fact that most of the PHP world currently relies on parsing function and class comments for it’s code to function because people can’t get their shit together on mailing lists.
  • It’s not ok to run around shouting “type hinting for literals would mean that passing an int to float hint would fatal PHP” and calling that an reasonable argument while people just write $x = (float)$x; in the cases where it actually does matter anyways.
  • It’s not ok to be not able to talk to 2 back end data sources in parallel, using “promises” or whatever, in a language that has “pull stuff out of database and put it into the internet” as a proclaimed core competency.
  • It’s not ok that echo 0.000001; produces 1.0E-6 and that casting it to string doesn’t help but putting quotes around it does.
  • It’s not ok that you have to clear the error buffer by generating a suppressed undefined variable error just to be able to sanely use token_get_all().

Au final la moitié des items ressemblent juste à « ça ne fait pas ce que j’espère ». Alors pour ceux qui m’ont fait suivre le lien :

Pour le premier item il existe plusieurs solutions, dont un simple array_values($tab)[0]. Bref, rien d’exceptionnel pour aller itérer sur un dictionnaire.

Pour le second, si on demande explicitement au niveau du système à afficher les résultats suivant les conventions d’un pays spécifique, PHP s’y conforme. C’est le cas de la plupart des langages, y compris la ligne de commande de base. Difficile d’avancer que c’est un problème, d’autant qu’il est bien évidemment possible d’ignorer la configuration du système pour forcer une locale au niveau du langage.

Quant à savoir comment afficher 0.000001 ou 1E-6, comme le langage n’a aucun moyen de savoir comment a été tapé la valeur initiale dans le code source (rien de spécifique à PHP, à ma connaissance aucun ne le fait), il faut bien qu’il choisisse une forme arbitrairement à la sortie. Si l’auteur veut forcer autre chose, il a tous les outils pour ça.

Pour le dernier item j’ai la flemme de vérifier les cas limites mais à priori c’est juste que l’auteur n’a pas eu le courage d’aller créer un gestionnaire d’erreur pour gérer ses erreurs.

Bref, tout ça c’est bien joli mais à première vue une bonne partie n’est qu’un problème de développeur frustré, pas un problème de langage.

Ce qui me frustre moi c’est que des problèmes de langages il y en a plein, et que pousser des faux problèmes décrédibilise ceux qui essayent de corriger les problèmes réels.

Bomberman massivement multijoueur

Un bomberman-like massivement multijoueur ? Je ne suis pas convaincu par l’intérêt ludique. Ne pas jouer avec ses amis, ne pas tisser de liens, avoir une interaction limitée à quelques minutes et faite au hasard, j’ai peur que ça ne remplisse pas de promesses sur le long terme.

Techniquement par contre c’est intéressant, pas forcément si complexe que ça, mais sacrément intéressant. Le jeu dans le navigateur n’est qu’à ses débuts.

Je veux changer ça, et ça, et ça

Je pense que je ne suis pas le seul à imaginer régulièrement comment créer un nouveau langage ou modifier les existants à ma convenance. Sans aller jusque là, en croisant ce qui se fait dans les différents langages, on trouve toujours des point intéressants qu’on aimerait voir copiés.

Voilà donc quelques unes de mes frustrations, parce que les exprimer permet de s’en débarrasser un peu et de se concentrer sur l’important : ce qu’on fait avec le langage.

Persistance du code en PHP

Chaque requête web recharge entièrement l’applicatif PHP. APC apporte une béquille indispensable mais ça reste au niveau de la béquille. Toute l’initialisation est à refaire à chaque fois. Ça fonctionne, mais j’aimerai vraiment un mode de PHP ou un framework web PHP qui permette de commencer directement au traitement des requêtes sans avoir à tout refaire de zéro.

Accesseurs en PHP

Toujours côté PHP j’attends depuis bien longtemps l’apparition d’accesseurs transparents pour les attributs des objets. Ruby, Python et Javascript ont chacun leur façon de faire. Là il ne s’agit pas de repiquer une syntaxe au langage voisin mais bien de combler un manque.
Sérieusement, je n’en peux plus de ces getX() et setX(). C’est encore plus pénible à l’utilisation qu’à la création.

Espaces de noms

Fut un temps je râlais beaucoup contre PHP mais ce dernier une bonne partie du retard qu’il avait. Mieux : Arrivé en dernier il s’est permis de parfois faire les choses plus intelligemment.

Dites, pourquoi n’ai-je pas d’espaces de nom en Javascript ?

Les « use » de PHP me manquent aussi en Ruby. Ils présentent une solution élégante et pour donner des noms courts en local à des classes qui viennent d’autres espaces de noms, mais ils permettent aussi les alias pour changer facilement une implémentation par une autre sans impacter le code.

Pendant qu’on y est, pourquoi pas d’auto-chargement en Ruby ? Si je charge X::Y::Z, j’aimerai bien que le langage se charge tout seul d’aller chercher le fichier X/Y/Z.rb. Ça fonctionne dans quasiment tous les autres langages mais Ruby continue de faire du spaghetti d’inclusion manuelle de fichiers.

Blocs et fermetures lexicales

Les blocs sont *la* fonctionnalité qui me fait utiliser Ruby. On peut certes faire beaucoup de choses similaires avec des fonctions anonymes en Javascript ou en PHP mais c’est juste moins élégant (et ça compte beaucoup pour se sentir à l’aise).

Par contre, sérieusement, la réception des paramètres dans les blocs est vraiment peu lisible. Le choix de la barre verticale comme délimiteur est juste illisible.

Le pire arrive avec les fermetures lexicales. Ruby laisse bien trop facilement utiliser par erreur une variable venant de la portée parente. La syntaxe pour forcer une variable comme locale ajoute encore plus au côté non lisible. |x, y=3; z| ? sérieusement ?

À côté de ça PHP et Python proposent des variables en lecture seule, ce qui limite la casse. PHP impose même de déclarer explicitement les variables à importer de la portée parente au lieu de déclarer les variables locales. Difficile à imaginer en monde Ruby mais assez confortable au final.

Et vous ? qu’est-ce que vous changeriez en premier ?

La lame de fond nodejs

En ce moment côté startup et innovateurs, les développeurs javascript ont le vent en poupe. Pour autant, je ne crois pas que Javascript côté serveur soit le rouleau compresseur qu’on veut nous faire croire.

La syntaxe du langage est honnête, mais a largement autant de points négatifs que de points positifs par rapport à l’existant ailleurs.

Si je résume, on me dit que Javascript a

  • Une grosse base de développeurs à cause de son utilisation dans les pages web
  • Un runtime existant sur quasiment toutes les machines
  • La possibilité d’avoir un seul langage côté client et côté serveur
  • Un système de prototype
  • Un système d’event loop, api asynchrones et callbacks sur nodejs

La base d’utilisateur est un facteur très important, mais sur ceux ci une frange très mineure peut se réclamer d’avoir une bonne connaissance de Javascript. Le niveau moyen est même presque pire que sur PHP. Si on se contente de ceux qui font plus de quelques lignes et qui pourraient passer côté serveur, la base utilisateur n’est plus si fantastique que cela. PHP ou Java en ont probablement autant si ce n’est plus.

On trouve de quoi exécuter Javascript même sous Windows, mais au final on va installer une machine virtuelle dédiée. Encore une fois, si on parle de côté serveur, Linux et autres BSD sont une bien meilleure cible et là Python ou Ruby sont par défaut, PHP est déployable facilement, Java est presque partout. Je n’ai jamais entendu dire que l’un des trois premiers souffrait d’un frein à cause de la nécessité d’installation sur telle ou telle machine.

La possibilité d’avoir un seul langage n’est pas à négliger, mais au final coder du Javascript pour une page web dans le navigateur n’est pas comme coder du Javascript pour nodejs : Les API, les performances, les besoins, tout ça est différent. Même sans ça, les partages de code entre client et serveurs resteront anecdotique, Java en a fait l’expérience il y a longtemps. Reste la syntaxe qui est la même, et évite un nouvel apprentissage, mais c’est assez faible. L’expertise dans un langage est principalement liée à l’API et à ses implications, la syntaxe de base s’apprend rapidement.

Le système de prototype est effectivement une des spécificités de Javascript par rapport aux langages courants. Ceci dit c’est un point incompris par quasiment tous les développeurs au point que tous essayent de recréer artificiellement un système de classe au dessus du système de prototype. Le résultat est d’ailleurs un peu bancal. En théorie le système de prototype ça peut être génial. Dans la réalité, sauf pour une petite minorité, c’est un gros point noir. Difficile de considérer ça comme un avantage.

Il reste un point, particulier : Tout l’environnement Javascript côté serveur s’est construit autour d’un système asynchrone bourré de callback. S’il est facile d’y faire de la programmation spaghetti, c’est aussi une grande force et la plus grande spécificité du langage.

Avoir un système d’évent loop avec des accès asynchrones c’est réalisable sur les autres langages, mais ça prend du temps. Il faut refaire tout un jeu de bibliothèques. Les quelques essais actuels sont limités, complexes à mettre en oeuvre, et surtout n’ont pas eu le coup de projecteur qui a lancé nodejs.

Et c’est un peu ça l’idée : Rien n’empêche Ruby, Python ou Java de créer une bibliothèque équivalente à nodejs. S’il y a une vraie valeur ajoutée, alors ça se fera. À ce moment là, à part le coup de projecteur, Javascript n’aura plus de quoi prétendre être en avance. Ça restera un bon langage, avec une excellente machine virtuelle, qui méritera probablement d’être côté serveur, mais pas plus que les autres. Je ne vois pas ce qui justifiera la lame de fond que certains imaginent.

Accesseurs

Je déteste avoir à programmer ou utiliser des accesseurs. Voilà, c’est dit.

Sérieusement, qui a eu l’idée de faire des méthodes getX() et setX() ? Dans le meilleur des cas c’est pénible a écrire et difficile à lire.

Qu’on ne me parle pas d’encapsulation, ces méthodes artificielles sont tout sauf un besoin d’encapsulation. C’est même exactement le contraire de l’encapsulation : C’est parce que je n’ai pas à savoir si un attribut est en accès direct ou non que je n’ai pas à utiliser d’accesseurs.

Il n’est pas besoin d’impacter la manière dont on appelle les attributs d’un objet pour différencier l’interface publique et les données internes. De nombreux langages on trouvé une manière élégante de gérer les accesseurs au niveau du langage au lieu de faire faire des pirouettes à l’utilisateur. Un des gros avantages c’est qu’on ne commence à définir des méthodes d’accesseur que quand on en a vraiment l’utilité, pas « partout, au cas où pour plus tard ».

Donc, quand il s’agit de migrer un attribut autrefois en accès direct, en Javascript :

A = { 
  get b() { return this._b; },
  set b(val) { return this._b = val;} 
};
// ou 

A.__defineGetter__("b", function() { return this._b; };
A.__defineSetter__("b", function(val) { return this._b = val; };

en Ruby :

class A
  def b
    @b
  end
  def b=(val)
    @b = val
  end
end

en Python :

class X(object):
  def getb(self):
    return self._b
  def setb(self, val):
    self._b = val
  b = property(getb, setb)

# ou mieux :

class X(object):
  @property
  def b(self):
    return self._b
  @b.setter
  def b(self, val):
    self._b = val

Sérieusement, c’est moins de complexité pour démarrer du code (pas besoin de développer des accesseurs passe-plat au cas où on en aurait besoin plus tard, on peut s’en occuper uniquement quand le besoin arrive), et c’est plus de clarté pour tout ce qui utilise notre code (et là le gain est immense même si ça semble juste quelques caractères). Je n’imagine pas de m’en passer dans les langages où ça existe.

Si vous utilisez des getter et setter passes-plat, c’est soit votre code qui est mauvais, soit votre langage qui est bridé (soit que vous utilisez consciemment un langage bas niveau). Dans les deux premiers cas il y a quelque chose à repenser.

Bien entendu PHP reste à la traine, sous prétexte de simplicité (allez comprendre).

Évolution de PHP – accesseurs

Il y a matière à se réjouir : Le développement et l’évolution du langage PHP a repris. Nous avons eu les fonctions anonymes, les espaces de nom, et quelques nouveautés bienvenues, souvent attendues de trèèèèès longue date.

Bref, ça bouge, bien. Nous avons  cependant encore deux courants très opposés au niveau du langage : L’un qui souhaite le garder simple et « comme il est », avec souvent un historique quick’n dirty, et qui au final freine quasiment toutes les évolutions. L’autre qui souhaite le voir profondément changé, et reprendre les bonnes idées des autres langages, au risque de trop vouloir copier ces autres langages.

Si je devais caricaturer je dirai qu’une majorité des développeurs de PHP (surtout les historiques) sont ceux qui freinent, et qu’une majorité des utilisateurs actifs de PHP sont ceux qui poussent. Si ça bouge depuis PHP 5.3, c’est que l’excès du « on ne touche rien » a provoqué un trop grand ras-le-bol, et les développeurs de PHP ont été forcés de mettre de l’eau dans leur vin.

Malheureusement les deux courants existent toujours et rien n’a été tranché. On le voit très bien sur la RFC concernant les accesseurs : Une bonne partie des « contre » sont ceux que je qualifie de « développeurs historiques du langage », et une partie des « pour » sont ceux que je qualifie de « utilisateurs actifs dans le langage ».

Il est plus que temps de décider où l’on va, sinon nous allons continuer à avoir un Frankenstein.