Présentation de pdoc (rc1)

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()
  {
    
  }
}
Publicités

HTML et cases à cocher (checkboxes)

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 « HTML et cases à cocher (checkboxes) »

Des attributs en lecture seule en PHP

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 « Des attributs en lecture seule en PHP »

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

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

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

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 « Analyser un objet en PHP »

À propos des Frameworks

Je pratique les frameworks depuis quelques années, car je trouve les principes type Domain, MVC, etc. extrèmement pratiques. Je n’arrive cependant pas à en trouver un qui me plaise. Je fais régulièrement le tour des frameworks PHP principaux, mais je trouve généralement qu’ils sont beaucoup trop compliqués, et qu’ils ne résolvent pas mes problèmes. Le principal étant de me simplifier au maximum la vie sur les tâches répétitives et d’être opérant immédiatement, sans avoir à coder 5 classes juste pour un modèle.

Je n’ai par exemple toujours pas compris comment créer un modèle avec le Zend Framework. À part qu’il faut apparemment générer 3 classes, juste pour un modèle.

J’ai donc fini par me coder un framework perso, que j’ai réécrit deux fois. Mais :

  • il n’y a aucun test unitaire ;
  • je suis le seul à bosser dessus ;
  • je n’ai donc aucun retours d’utilisateurs ou d’autres programmeurs ;
  • la plupart des idées de la dernière version viennent directement de Rails.

Pourquoi m’embêter à coder en PHP ce qui existe déjà pour Ruby ? En plus, Rails est plein d’idées que je trouve excellentes, qui me plaisent et que je copie sans vergogne. Quel intérêt alors de maintenir un tel framework ? Ça me semble idiot, surtout depuis que j’ai lu Practices of an Agile Developer.

Certes, développer un bon framework, simple, accessible et reprenant les bonnées idées de Rails mais basé sur PHP et non pas Ruby est intéressant. PHP est plus courant chez les hébergeurs par exemple, donc plus facile à déployer. Cependant coder tout seul dans son coin, et voir ses projets (Webcomics.fr par ex.) traîner du pied parce qu’il faut maintenir et faire évoluer son framework… c’est juste idiot.

Je ne rejette pas l’idée de coder un framework PHP5 tel que définit au paragraphe précédent, mais pas tout seul, et surtout : développé Test Driven, c’est-à-dire que les tests doivent conduire la programmation (et pas l’inverse). Si ça vous êtes intéresse et que vous maîtrisez le modèle objet PHP5, contactez-moi, car ça m’intéresse !

Pour le moment j’étudie Ruby, Rails, et j’apprends des trucs. Je ne sais pas ce qu’il en ressortira : un framework calqué sur Rails mais pour PHP5 ? Ou peut-être finirai-je par switcher et passer à Ruby ?

Propulsé par WordPress.com.

Retour en haut ↑