Ce module est EXPERIMENTAL . Cela signifie que le comportement de ces fonctions, leurs noms et, concrètement, TOUT ce qui est documenté ici peut changer dans un futur proche, SANS PREAVIS ! Soyez-en conscient, et utilisez ce module à vos risques et périls.
En programmation objet, il est courant de rencontrer la combinaison de classes simples (et de leurs instances) en une classe plus complexe. C'est une strategie habile pour mettre en place des objets complexes, et des hiérarchies d'objets. Ce système peut fonctionner comme une alternative dynamique à l'héritage multiple. Il y a deux solutions pour combiner deux classes, suivant la relation de leurs éléments constitutifs : L'Association et l'agrégation .
Une Association est une combinaison d'éléments construits indépendamment et visibles à l'extérieur. Lorsque nous associons des classes ou objets, chacun garde une référence à l'autre partie de l'association. Lorsque nous associons des classes statiquement, une classe contient une référence à une instance de l'autre classe. Par exemple :
Exemple 1780. Association de classes
<?php
class
DateTime
{
function
DateTime
() {
// constructeur vide
}
function
now
() {
return
date
(
"Y-m-d H:i:s"
);
}
}
class
Report
{
var
$_dt
= new
DateTime
();
// autres propriétés ...
function
Report
() {
// initialisation du code ...
}
function
generateReport
() {
$dateTime
=
$this
->
_dt
->
now
();
// autre code
}
// autres méthodes ...
}
$rep
= new
Report
();
?>
Exemple 1781. Association d'objets
<?php
class
DateTime
{
// identique au précédent exemple
}
class
DateTimePlus
{
var
$_format
;
function
DateTimePlus
(
$format
=
"Y-m-d H:i:s"
) {
$this
->
_format
=
$format
;
}
function
now
() {
return
date
(
$this
->
_format
);
}
}
class
Report
{
var
$_dt
;
// Nous allons garder la référence à DateTime ici
// autre propriété
function
Report
() {
// initialisation
}
function
setDateTime
(&
$dt
) {
$this
->
_dt
=&
$dt
;
}
function
generateReport
() {
$dateTime
=
$this
->
_dt
->
now
();
// autre code ...
}
// autres méthodes ...
}
$rep
= new
Report
();
$dt
= new
DateTime
();
$dtp
= new
DateTimePlus
(
"l, F j, Y (h:i:s a, T)"
);
// Génération du rapport avec une simple date
$rep
->
setDateTime
(&
$dt
);
echo
$rep
->
generateReport
();
// plus loin dans le code
// generation du rapport avec une date designée
$rep
->
setDateTime
(&
$dtp
);
$output
=
$rep
->
generateReport
();
// sauvegarde pour affichage dans la base
// ... etc ...
?>
L'agrégation , d'un autre coté, implique l'encapsulation et le masquage des parties de la combinaison. Nous pouvons agréger des classes en utilisant une méthode statique, grâce aux sous-classes (mais PHP ne supporte pas bien les sous-classes), et, dans ce cas, la définition de la classe agrégée n'est pas accessible, sauf via les méthodes de la classe contenant. L'agrégation d'instances (agrégation d'objets) implique la création dynamique de sous-objets à l'intérieur d'un autre objet et, dans le même temps, l'extension des capacités de l'objet principal (en terme de méthodes accessibles).
L'agrégation d'objets est une méthode naturelle pour représenter des relations de type tout-partie (par exemple, une molécule est une agrégation d'atomes), ou bien peut être utilisée pour obtenir un effet équivalent à l'héritage multiple, sans avoir à lier plusieurs classes et leurs interfaces. En fait, les agrégations d'objets sont plus souples, car nous pouvons alors sélectionner les méthodes et les propriétés qui sont transmises à l'objet agrégé.
Nous définissons trois classes, qui implémentent chacune une méthode de stockage différente :
Exemple 1782. storage_classes.inc
<?php
class
FileStorage
{
var
$data
;
function
FileStorage
(
$data
) {
$this
->
data
=
$data
;
}
function
write
(
$name
) {
$fp
=
fopen
(
name
,
"w"
);
fwrite
(
$fp
,
$this
->
data
);
fclose
(
$data
);
}
}
class
WDDXStorage
{
var
$data
;
var
$version
=
"1.0"
;
var
$_id
;
// variable "privée"
function
WDDXStorage
(
$data
) {
$this
->
data
=
$data
;
$this
->
_id
=
$this
->
_genID
();
}
function
store
() {
if (
$this
->
_id
) {
$pid
=
wddx_packet_start
(
$this
->
_id
);
wddx_add_vars
(
$pid
,
"this->data"
);
$packet
=
wddx_packet_end
(
$pid
);
} else {
$packet
=
wddx_serialize_value
(
$this
->
data
);
}
$dbh
=
dba_open
(
"varstore"
,
"w"
,
"gdbm"
);
dba_insert
(
md5
(
uniqid
(
""
,
true
)),
$packet
,
$dbh
);
dba_close
(
$dbh
);
}
// méthode privée
function
_genID
() {
return
md5
(
uniqid
(
rand
(),
true
));
}
}
class
DBStorage
{
var
$data
;
var
$dbtype
=
"mysql"
;
function
DBStorage
(
$data
) {
$this
->
data
=
$data
;
}
function
save
() {
$dbh
=
mysql_connect
();
mysql_select_db
(
"storage"
,
$dbh
);
$serdata
=
serialize
(
$this
->
data
);
mysql_query
(
"insert into vars ('$serdata',now())"
,
$dbh
);
mysql_close
(
$dbh
);
}
}
?>
Puis, nous "instantions" quelques objets issus de ces classes, et nous réalisons des agrégations et désagrégations, tout en affichant quelques résultats :
Exemple 1783. test_aggregation.php
<?php
include
"storageclasses.inc"
;
// quelques utilitaires
function
p_arr
(
$arr
) {
foreach(
$arr
as
$k
=>
$v
)
$out
[] =
"\t$k => $v"
;
return
implode
(
"\n"
,
$out
);
}
function
object_info
(
$obj
) {
$out
[] =
"Classe : "
.
get_class
(
$obj
);
foreach(
get_object_vars
(
$obj
) as
$var
=>
$val
)
if (
is_array
(
$val
))
$out
[] =
"propriété : $var (array)\n"
.
p_arr
(
$val
);
else
$out
[] =
"propriété : $var = $val"
;
foreach(
get_class_methods
(
$obj
) as
$method
)
$out
[] =
"méthode : $method"
;
return
implode
(
"\n"
,
$out
);
}
$data
= array(
M_PI
,
"kludge != cruft"
);
// créons quelques objets simples
$fs
= new
FileStorage
(
$data
);
$ws
= new
WDDXStorage
(
$data
);
// affichons des informations sur ces objets
echo
"\$fs object\n"
;
echo
object_info
(
$fs
).
"\n"
;
echo
"\n\$ws object\n"
;
echo
object_info
(
$ws
).
"\n"
;
// maintenant, quelques agrégations
echo
"\nagrégeons \$fs avec la classe WDDXStorage\n"
;
aggregate
(
$fs
,
"WDDXStorage"
);
echo
"L'objet \$fs \n"
;
echo
object_info
(
$fs
).
"\n"
;
echo
"\nagrégeons le résultat avec la classe DBStorage \n"
;
aggregate
(
$fs
,
"DBStorage"
);
echo
"L'objet \$fs \n"
;
echo
object_info
(
$fs
).
"\n"
;
echo
"\nEt finalement, désagrégeons WDDXStorage\n"
;
deaggregate
(
$fs
,
"WDDXStorage"
);
echo
"L'objet \$fs \n"
;
echo
object_info
(
$fs
).
"\n"
;
?>
Etudions maintenant le résultat du script pour comprendre les effets secondaires et les limitations des agrégations d'objets en PHP. D'abord, nous avons créé $fs et $ws et ils fournissent le bon résultat (suivant la définition de leur classe). Notez que dans le but de l'agrégation d'objets, les éléments privés d'une classe ou d'un objet doivent commencer par un souligné ("_") , même s'il n'y a pas de distinction réelle entre un objet privé et un objet public.
Exemple 1784. agrégation d'objets
L'objet $fs Classe : filestorage propriété : data (array) 0 => 3.1415926535898 1 => kludge != cruft méthode : filestorage méthode : write L'objet $ws Classe : wddxstorage propriété : data (array) 0 => 3.1415926535898 1 => kludge != cruft propriété : version = 1.0 propriété : _id = ID::9bb2b640764d4370eb04808af8b076a5 méthode : wddxstorage méthode : store méthode : _genid
Nous agrégeons alors $fs
avec la classe WDDXStorage
, et nous affichons les informations. Nous pouvons aussi voir que même si l'objet $fs
est toujours du type FileStorage
, il a maintenant la propriété $version
, et la méthode store()
, qui sont définies dans WDDXStorage
. Une chose importante à noter est que les éléments privés n'ont pas été agrégés, même s'ils sont présents dans l'objet $ws
. Un autre absent est le constructeur de WDDXStorage
, qu'il n'est pas logique d'agréger.
Exemple 1785. Résultat d'agrégation
agrégeons \$fs avec la classe WDDXStorage L'objet $fs Classe : filestorage propriété : data (array) 0 => 3.1415926535898 1 => kludge != cruft propriété : version = 1.0 méthode : filestorage méthode : write méthode : store
Le processus d'agrégation est cumulatif, ce qui fait que lorsque nous agrégeons $fs
avec la classe DBStorage
, nous générons un objet qui peut utiliser n'importe laquelle des méthodes de stockage de ces classes.
Exemple 1786. Accumulation dans une agrégation
agrégeons le résultat avec la classe DBStorage L'objet $fs Classe : filestorage propriété : data (array) 0 => 3.1415926535898 1 => kludge != cruft propriété : version = 1.0 propriété : dbtype = mysql méthode : filestorage méthode : write méthode : store méthode : save
Finalement, de la même façon que nous avons agrégé les méthodes et propriétés dynamiquement, nous pouvons aussi les désagréger. Si nous désagrégeons la classe WDDXStorage
de l'objet $fs
, nous allons obtenir :
Exemple 1787. Désagrégation d'objets
Et finalement, désagrégeons WDDXStorage L'objet $fs Classe : filestorage propriété : data (array) 0 => 3.1415926535898 1 => kludge != cruft propriété : dbtype = mysql méthode : filestorage méthode : write méthode : save
Un point que nous n'avons pas mentionné ci-dessus et que l'agrégation ne va pas écraser les méthodes ou propriétés déjà existantes dans l'objet principal. Par exemple, la classe FileStorage
définit une propriété $data
, et la classe WDDXStorage
aussi. Mais cette dernière ne sera pas impliquée dans l'agrégation.