Page 1 sur 1

Programation objet : hériter des propriétés statiques avant

Posté : 11 juil. 2008, 21:52
par elvex
Bonjour à tous,

Je n'arrive pas à résoudre un problème d'héritage des propriétés statiques, en PHP 5.2. En PHP 5.3, mon code serait le suivant :
abstract class SayIt_Abstract {
	
	protected static $_format ; // Le format est définit dans les héritages
	protected $_name ;
	
	final public function __construct($name) {
		$this->_name = $name ;
	}
	
	final public function sayIt() {
		echo sprintf(static::$_format, $this->_name) . "\n" ;
	}
	
}

class SayIt_Hello extends SayIt_Abstract {
	protected static $_format = 'Hello %s' ;
}

$w = new SayIt_Hello('World') ;
echo $w->sayIt() ; // Affiche 'Hello World'
$j = new SayIt_Hello('Joe') ;
echo $j->sayIt() ; // Affiche 'Hello Joe'
Dans ce code, l'accesseur static fourni par PHP 5.3 permet de résoudre le problème, car l'accesseur self ne peut faire référence qu'à la méthode abstraite.


EDIT

Voilà la solution que j'envisage : utiliser des objets de type singleton pour contenir les propriétés et méthodes statiques.
Les accès se déroulent comme suit :
- Dans SayIt_Abstract, une méthode statique manipulant des propriétés statiques est implémentée dans le singleton SayIt_Abstract_Static
- Dans SayIt_Abstract, une méthode instanciée manipulant des propriétés statiques accède à la bonne instance *_Static du registre grâce au $this
- Ailleurs, la manipulation des propriétés et méthodes statiques se fait en utilisant une surcharge dans la classe concrète qui rappelle le nom de la classe à SayIt_Abstract

L'idée de ne pas créer de fichier supplémentaire pour les classes SayIt_*_Static rappelle que ces classes ne doivent pas être manipulées en dehors du contexte des classes SayIt_*

Cette méthode semble suffisamment propre, souple et réutilisable, même si elle implique une structure assez lourde pour peu de choses. Toutes les critiques sont les bienvenues.

Fichier test.php
header('Content-type: text/plain') ;

// Manipulation externe
SayIt_Foo::getStatic()->format = 'GOODBYE %s' ; // Fixe la propriété pseudo-statique à l'extérieur de la classe
echo SayIt_Foo::getStatic()->format . "\n" ; // Affiche 'GOODBYE %s'
SayIt_Foo::getStatic()->doSomeTrick() . "\n" ; // Appelle une méthode statique qui manipule les données statiques de la classe
echo SayIt_Foo::getStatic()->format . "\n" ; // Affiche 'Goodbye %s'
echo "\n" ;

// Instanciation
$hw = new SayIt_Hello('World') ;
$hj = new SayIt_Hello('Joe') ;
$gw = new SayIt_Foo('World') ;
$gj = new SayIt_Foo('Joe') ;

// Manipulation interne
echo $hw->sayIt() ; // Affiche 'Hello World'
echo $hj->sayIt() ; // Affiche 'Hello Joe'
echo $gw->sayIt() ; // Affiche 'Goodbye World'
echo $gj->sayIt() ; // Affiche 'Goodbye Joe'
echo "\n" ;
var_dump(Singleton::getRegistry()) ; // Il n'y a bien que 2 instances de SayIt_*_Static : #2 et #5
Fichier Library/SayIt/Abstract.php

- L'objet *_Static simule les composantes statiques.
- L'objet que l'on souhaite manipuler implémente les composantes instanciables, mais peut malgré tout accéder aux propriétés et méthodes statiques.
- L'existence des objets supplémentaires est masquée par l'emploi d'un seul fichier : l'autoload est rendu impossible.
abstract class SayIt_Abstract_Static extends Singleton
{
	public $format ;
	
	public function doSomeTrick()
	{
		$this->format = ucwords(strtolower($this->format)) ;
	}
	
}

abstract class SayIt_Abstract
{
	protected $_name ;
	
	public static function getStatic($className)
	{
		return Singleton::getInstance($className . '_Static') ;
	}
	
	public function toStatic()
	{
		return self::getStatic(get_class($this)) ;
	}
	
	public function __construct($name)
	{
		$this->_name = $name ;
	}
	
	final public function sayIt() // Utiliser un appel statique dans une méthode d'instance
	{
		echo sprintf($this->toStatic()->format, $this->_name) . "\n" ;
	}
	
}
Fichier Library/SayIt/Hello.php

Implémentation concrète
class SayIt_Hello_Static extends SayIt_Abstract_Static
{
	public $format = 'Hello %s' ;
}

class SayIt_Hello extends SayIt_Abstract
{
	public static function getStatic() { return parent::getStatic(__CLASS__) ; }
}
Fichier Library/SayIt/Foo.php

Implémentation concrète
class SayIt_Foo_Static extends SayIt_Abstract_Static
{
	public $format = 'Foo' ;
}

class SayIt_Foo extends SayIt_Abstract
{
	public static function getStatic() { return parent::getStatic(__CLASS__) ; }
}
Fichier Library/Singleton.php

- Pattern de singleton abstrait (registre)
- Voir http://uk3.php.net/manual/fr/language.oop5.patterns.php
abstract class Singleton
{
	const CLASS_DOES_NOT_EXISTS  = 1 ;
	const CLASS_IS_NOT_SINGLETON = 2 ;
	
	private static $registry ;
	
	final public function getInstance($className, array $params = array())
	{
		if (!isset(self::$registry[$className]))
		{
			if (!class_exists($className))
				throw new Exception("La classe " . __CLASS__ . " n'existe pas", CLASS_DOES_NOT_EXISTS) ;
			if (!is_subclass_of($className, __CLASS__))
				throw new Exception("La classe " . __CLASS__ . " n'est pas héritée de Singleton", CLASS_IS_NOT_SINGLETON) ;
			self::$registry[$className] = new $className($params) ;
		}
		return self::$registry[$className] ;
	}
	
	final public function getRegistry()
	{
		return self::$registry ;
	}
	
	protected function __construct() {}
	
	protected function __clone() {}
	
}

Merci d'avance,
elvex

Posté : 14 juil. 2008, 11:10
par Hywan
Hey :),

J'ai bien conscience du problème mais j'ai une question très bête : pourquoi vouloir absolument avoir la propriété $format en statique ?
Je n'en vois pas du tout l'utilité, et spécialement dans un cas comme le tien.

Posté : 14 juil. 2008, 12:29
par savageman
Sinon, j'ai une technique... Tu fais une autre classe SayIt_Static, avec une propriété statique $format...