Présentation de pdoc (rc1)

16 octobre 2009

J’en avais un peu marre de ces générateurs de documentation qui obligent à utiliser des syntaxes bizarres, et qui obligent à utiliser une certaine forme de commentaire, au lieu de dire « tout commentaire qui précède un espace de nom, une classe, une méthode, un attribut ou une fonction est sa documentation ». Après tout, s’il y a un commentaire, c’est que c’est utile.

De l’autre côté il y a rdoc pour ruby : simple, efficace et on rédige sa doc de manière naturelle, en écrivant un texte avec des paragraphes, des listes et des blocs de code.

J’ai (un peu) cherché un équivalent pour PHP, mais je n’ai rien trouvé. La plupart sont des clones de doxygen — et vous aurez compris que je n’aime pas doxygen. La configuration est une horreur, et la documentation est chiante à rédiger.

Comme le moto de tout bon linuxien qui se respecte c’est « si ça existe pas code-le » et qu’on n’est jamais mieux servis que par soi-même, voici pdoc (télécharger). Il est plutôt basique et le code est un poil crade, mais il marche et génère de la doc comme je l’aime. Vous pouvez voir par exemple la documentation de Misago (un autre de mes projets PHP).

Et pour finir un exemple :

# This is a comment for the following class.
#
# = This is a heading
#
# This is a
# paragraph.
#
#
# == This is sub-heading
# 
# - this is a list (first item)
# - (second item)
#
class MyClass
{
  # Comments `$var`.
  public $var;

  # This comment is discarded.

  # This is the long comment for +MyClass::my_method()+.
  # You may fill it with a lot of things.
  # 
  # `$toto` can be:
  # 
  # - something
  # - something else
  # 
  # == Example:
  # 
  #   $c = new MyClass();
  #   $c->my_method('my_var');
  # 
  # See http://some.url/ for more details.
  function my_method($toto)
  {
    
  }
  
  # @private
  function undocumented_public_method()
  {
    
  }
}

Injecter du SVG directement dans du HTML 5

15 octobre 2009

J’ai commencé à bidouiller une petite lib JavaScript capable de générer des charts en SVG (graphiques statistiques). Générer les graphiques en SVG n’est pas très dur, il suffit de quelques calculs mathématiques. En revanche ce qui m’a posé problème, c’est injecter le SVG dans la page HTML depuis le JavaScript. Je me suis alors penché sur l’intégration de SVG dans du HTML 5.

Images SVG

Afficher des images SVG externes est plutôt facile, il suffit d’utiliser la balise <object> ou la balise <iframe> , comme ceci :

<object type="image/svg+xml" data="chart.svg" width="120" height="40">
  <p>Votre navigateur ne supporte pas le SVG.</p>
</object>

SVG dans du XHTML

En revanche injecter du SVG directement dans le HTML est un peu plus sport. Historiquement il faut passer en XHTML et servir ses pages en utilisant le mimetype associé : application/xhtml+xml. Passant sur un format XML il est possible d’insérer du XML dans du XML. Dans notre cas : du SVG dans du XHTML (ça marche aussi pour du MathML).

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:svg="http://www.w3.org/2000/svg">
<body>
  <svg xmlns="http://www.w3.org/2000/svg" width="120" height="40">
    <rect x="10" y="10" width="100" height="20" style="fill:#000"/>
  </svg>
</body>
</html>

La contrepartie de cette technique c’est qu’il faut que les pages soient bien formées. Le moindre bug XML et l’affichage se réduit à une erreur de parsing. Outch.

SVG dans du HTML 5

Heureusement j’ai découvert il y a quelques jours une technique (presque) valide en HTML 5 et qui marche sur tous les navigateurs supportant le SVG. L’astuce consiste à déclarer la doctype HTML 5 tout en spécifiant les espaces de noms XHTML et SVG sur les balises html et svg, au besoin :

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
  <svg xmlns="http://www.w3.org/2000/svg" width="120" height="40">
    <rect x="10" y="10" width="100" height="20" style="fill:#000"/>
  </svg>
</body>
</html>

Le SVG s’affiche alors correctement, et l’affichage de la page ne plante plus à la moindre erreur de parsing. Glop.

À noter cependant que pour être valide et conforme HTML 5, il faut alors servir ses pages avec le prologue XML <?xml version="1.0"?>, ainsi qu’avec le mimetype application/xhtml+xml. Le problème c’est que nos pages vont à nouveau être du XML et plus du HTML. C’est donc à vous de choisir : être valide et risquer des erreurs de parsing ; ou ne pas être valide.

Générer du SVG depuis JavaScript

Cette dernière technique est la seule qui soit aisée à utiliser lorsqu’on souhaite scripter la création du SVG (en JavaScript par ex) :

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
  <script type="text/javascript">
  svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svg.setAttribute('width', 120);
  svg.setAttribute('height', 40);
  document.body.appendChild(svg);
  </script>
</body>
</html>

Pour générer ensuite des éléments à ajouter à notre objet svg, il faut utiliser document.createElementNS. Ensuite c’est de la DOM bien classique (et c’est ça qui est génial avec SVG). Par exemple :

var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', 10);
rect.setAttribute('y', 10);
rect.setAttribute('width', 200);
rect.setAttribute('height', 20);
svg.appendChild(rect);

Autre solution :

Au départ j’étais parti sur l’utilisation de la balide object, qui demande d’utiliser un fichier externe (contenant uniquement la déclaration SVG) ou une data-URI pour afficher le SVG initial. D’ailleurs le data-URI n’est pas possible, car Webkit refuse tout mimetype navigable dans les data-URI, ce qui oblige donc à utiliser un fichier externe. Il faut aussi ajouter un évènement load sur notre object pour savoir quand on peut commencer à ajouter des éléments, etc.

Cette solution marche aussi, mais fort compliquée. Je pensais alors que SVG était particulièrement pénible à injecter dans les pages HTML, comparé à la balise CANVAS par exemple. Bah j’avais tord, en fait c’est super simple avec HTML 5 :)

Manipulation de pixels

23 juin 2009

Voilà quelques temps que je m’amuse avec la balise CANVAS. Il s’agit d’une zone de dessin en 2D dont on peut manipuler chaque pixel. Plus tard on devrait avoir accès à une zone en 3D (via OpenGL), mais pour le moment on se limite à la 2D, ce qui est déjà pas mal !

J’ai commencé par un essai d’oekaki qui est réalisé sans aucune image et qui permet de dessiner sur son écran.

oekaki

oekaki

Puis j’ai eu envie de tester de bon vieux effets de la scène démo, comme on en faisait sur amiga et autres pentiums. Des metaballs par exemple, ou des lucioles. À noter que pour que l’animation des metaballs soit fluide, il faut utiliser un navigateur toute dernière génération (par ex. Firefox 3.5).

Lucioles

Lucioles

Metaballs

Metaballs

NB : bien entendu tout cela ne marche pas sur Internet Explorer (même le 8), seul navigateur actuel qui ne supporte toujours pas la balise CANVAS.

HTML et cases à cocher (checkboxes)

17 mars 2009

L’un des problèmes du HTML, c’est ses éléments de formulaires qui n’ont pas évolué depuis des années. J’ai même l’impression qu’il n’ont jamais évolué après leur invention. Pourtant il manque des tas d’éléments (où sont les combobox?), tandis que d’autres sont limités, pour ne pas dire buggués. C’est notamment le cas des checkbox, ou cases à cocher en français.

Lire la suite »

Des attributs en lecture seule en PHP

6 mars 2009

S’il y a bien une chose que je trouve limitante dans le modèle objet de PHP (et certainement d’autres langages) c’est bien le fait de ne pas pouvoir déclarer un attribut en lecture seule. On peut définir l’accès à un attribut, via public, protected et private, mais un accès public va autoriser un accès en lecture qu’en écriture. Soit on autorise les deux, soit on n’autorise rien du tout.

Comment dès lors autoriser l’accès en lecture tout en interdisant l’écriture ? PHP n’offre pas de mécanisme nous permettant de le faire simplement. Il va donc falloir ruser, mais c’est possible.

Lire la suite »

La visibilité d’un objet ne se limite pas à son instance

19 février 2009

J’ai découvert aujourd’hui un truc génial en PHP : la visibilité restreinte (private et protected) d’un attribut ou d’une méthode dans un objet dépasse le cadre stricte de son instance, et est visible et accessible au niveau de la classe. Soit depuis toutes les autres instances !

Ce matin je suis tombé sur ce bout de code, extrait de misago. Comment pouvait-il fonctionner ? Mes tests me prouvaient qu’il n’y avait aucun bug et que tout fonctionnait correctement, contre toute attente.

class ActiveRecord
{
  function create()
  {
    $class = get_class($this);
    $record = new $class($attributes);
    if ($record->_create()) {
      return $record;
    }
  }
  
  private function _create()
  {
    // ...
  }
}

Si vous n’avez pas remarqué ce qui cloche : la méthode ActiveRecord::create() crée un nouvel objet (instance de ActiveRecord) et appelle une méthode sur ce nouvel objet. Problème : ActiveRecord::_create() est une méthode privée… comment puis-je l’appeler depuis l’extérieur ?!

Prenons les choses dans l’ordre. Dans le cas d’une définition publique, attributs et méthodes sont accessibles à partir de tout endroit. Dans le cas d’une définition restreinte, soit privée ou protégée, la visibilité des attributs et méthodes s’en retrouve elle aussi restreinte. C’est cette restriction qui m’intéresse dans le cas présent.

Une méthode privée n’est accessible qu’à partir de la classe qui l’a définie. Une méthode protégée n’est accessible qu’à partir de la classe qui l’a définie, mais est aussi accessible aux classes parentes et héritées. On s’attend ainsi à ne pouvoir appeler une méthode privée de la classe A que depuis des méthodes de A. Si B hérite de A, alors les méthodes de B n’auront pas accès aux méthodes privées de A. Cependant il aura accès aux méthodes publiques et protégées de A. L’inverse est tout aussi vrai : A a accès aux méthodes publiques et protégées de B, mais pas à ses méthodes privées. Par contre une classe C, qui n’hérite ni de A ni de B, n’aura accès qu’aux méthodes publiques de A et de B.

Prenons maintenant une instance de B ($b1). Depuis cette instance nous pensons avoir accès aux méthodes privées et protégées de B ainsi qu’aux méthodes protégées de A. C’est logique on est à l’intérieur de notre objet, c’est visible et accessible. Mais quid d’une autre instance de B ($b2) ? Étant une autre instance on pense ne pas pouvoir y accéder, car on se trouve en dehors de l’objet et que l’on n’a donc accès qu’à ses méthodes publiques.

Cela est à la fois vrai… et faux, cela dépend du contexte d’appel !

En effet quand on parle de méthodes privées et protégées on parle de visibilité dans la classe ! Oui, dans la classe et pas dans l’instance. La visibilité d’une méthode privée ne se limite à une instance de la classe, mais s’applique à la classe, et donc à toutes ses instances.

Appeller directement une méthode protégée de $b1 (définie dans A ou dans B) déclenchera une erreur fatale, mais si on l’appele depuis $b2… ça marche et c’est le comportement attendu. Idem pour une méthode privée, tant qu’on reste dans une logique de classe. Ainsi $b2 peut appeler une méthode privée de $b1 définie dans B, mais pas une méthode privée définie dans A.

Qu’est-ce que cela change ? Peu de choses ou beaucoup de choses. Pour moi cela veut dire un code encore plus propre, quoiqu’un peu étrange, le temps que je m’y habitue. En attendant le code présenté en exemple fonctionne, et je trouve cela génial. C’est propre, c’est beau, c’est fonctionnel, ça a tout pour plaire.

Encore un autre Framework PHP

13 février 2009

J’analyse l’API de Ruby on Rails en ce moment. Je cherche à comprendre comment il fonctionne, et pourquoi tel ou tel choix a été fait par les développeurs. J’essaye aussi de déterminer où il est particulièrement lié à Ruby, et ce qu’il n’est pas possible de transposer directement à un autre langage.

Je commence à mieux saisir Rails et son API. Le concept de module m’a dérouté un moment, surtout dans la lecture de l’API Rails. Venant d’un monde où cette notion n’existe pas et où les classes ne se mélangent que par héritage, pouvoir inclure des fonctions dans plein de classes, sans notion d’héritage ou de devoir instancier des objets dans l’objet… c’est déroutant. Pourtant c’est génial : cela distingue encore mieux les logiques et permet de partager beaucoup plus facilement du code.

Pendant le même temps je travaille sur un framework taillé pour PHP 5.2 (en attendant la version 5.3).

Mais pourquoi réinventer la roue ? Franchement, je me suis posé la question depuis Noël dernier ; et puis j’ai lu un article sur le blog de Jeff Atwood : Don’t Reinvent The Wheel, Unless You Plan on Learning More About Wheels. Cela m’a rappelé les raisons premières à l’écriture de mon propre framework : la facilité d’utilisation de CakePHP, mon envie de plonger un peu dans le code, pour comprendre certains aspects… et ma réticence face à sa complexité interne : je ne pigeais pas pourquoi tout y était si complexe et pourquoi continuer à supporter PHP 4.

Sur un coup de tête je me suis lancé dans un brouillon. En une nuit j’avais un truc qui tournait correctement. J’ai beaucoup apprécié cette expérience : j’avais quelque chose de plus simple, léger et rapide (à mes yeux) et j’y ai beaucoup appris ; alors j’ai continué. Voilà pourquoi je réinvente encore la roue : pour apprendre, pour fournir encore un autre framework à la communauté, et pour moi pouvoir utiliser un framework que je maîtrise sur mes projets.

Aujourd’hui je m’y relance : un framework full objet en PHP. La version de développement (alpha) est d’ailleurs disponible. Le code y est relativement stable, le développement étant test-driven, et l’API reprend grosso-modo l’API publique de Rails, minus les appels statiques (eg: Product::find()), qui devront attendre PHP 5.3 et son Late Static Binding pour être faisable.

Si cela vous intéresse, il s’appelle misago, et il est disponible sur github : http://github.com/ysbaddaden/misago/.

Analyser un objet en PHP

27 janvier 2009

Ruby est un langage qui s’auto documente. On peut analyser en permanence n’importe quel objet. Le motto étant de ne pas forcer un type, mais de vérifier si un objet répond à ce qu’on demande.

En Java ou PHP on force généralement le type lors de l’appel d’une fonction. Par exemple :

function do_something(Integer $num) {
  $num->do_another_thing();
}

Alors qu’en ruby on va plutôt faire ceci :

def do_something(num)
  if num.respond_to? :do_another_thing  
    num.do_another_thing
  else
    raise Exception.new "num can't do another thing!"
  end
end

Qu’importe la classe de num, tant qu’elle répond à la méthode do_another_thing.

Est-ce possible en PHP5 ? Lire la suite »

Apprendre Rails par la pratique

22 janvier 2009

Il n’y a pas de meilleur apprentissage que la pratique. C’est là qu’on apprend réellement. Lire des livres de programmation est intéressant, sur un plan intellectuel. On découvre certaines choses et aspects. Mais c’est en programmant qu’on retient ce qu’on apprend. On tombe tout le temps sur des problèmes, et il faut chercher la solution, ce qui permet une bonne mémorisation.

Trève de blabla, que coder ?

Cela fait quelques mois années que je me dis qu’une petite application simple me permettant de suivre mon compte bancaire serait vraiment bien. Tous les logiciels existants sont trop compliqués et cherchent à faire beaucoup trop de choses. Je n’ai que faire de la compta d’une PME, je veux juste voir l’évolution de mes comptes et analyser mes postes de dépenses.

Voilà une application intéressante et que j’utiliserai en plus.

Commençons par une analyser mes besoins en les écrivants. C’est une application bancaire, on devrait donc y trouver des opérations bancaires et des comptes bancaires.

Opérations :

  • créer / modifier / effacer des opérations
  • importer des opérations depuis un fichier QIF (fournit par ma banque)
    • identifier les doublons
  • lister les opérations sur une période donnée
    • résumer les entrées et sorties
    • afficher un graphique des opérations (pour voir l’évolution)

Comptes :

  • créer / modifier / effacer des comptes
  • déplacer des opérations d’un compte à un autre
  • gérer des virements d’un compte à un autre
    • débit sur un compte
    • crédit sur un autre compte

Tags :

  • coller / décoller des tags sur les opérations
  • automatiser le collage de tags
  • identifier les postes de dépenses (soit la somme des dépenses par tag)

Voilà les actions que je réalise chaque fois que je fais mes comptes. Les tags c’est quelque chose que j’aimerai bien faire, mais qui est lourd à réaliser sur le logiciel que j’utilise, et je ne le fais donc jamais.

jsUnit

21 janvier 2009

Kent Beck dit que lorsqu’il découvre un nouveau langage de programmation, il commence par écrire une suite de tests unitaires. Ma foi, pourquoi pas se lancer dans les tests unitaires en écrivant une telle suite qui s’auto-testerait ?

J’ai choisi le JavaScript, car je commence à comprendre les intérêts de ce langage, basé sur les prototypes et non pas sur un système d’héritage et de dérivation comme les langages objets qu’on connait généralement. J’ai aussi choisi le JavaScript car il n’a besoin que d’un navigateur, et de rien d’autre.

Ce fut plutôt sympa à faire, car je souhaitais que le script JS soit suffisant à lui-même et qu’il ne requière pas de fichier CSS externe. J’ai donc dû chercher comment injecter correctement du CSS dans un document HTML via JS, et qui soit compatible avec Gecko, IE et Webkit.

La solution est dans le code, et le résultat est disponible sur github : http://www.github.com/ysbaddaden/jsunit/


Suivre

Recevez les nouvelles publications par mail.