Projet en POO avec php : simplifier la gestion des images

PHP > 5.0

L’auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Salut à tous,

Je développe un projet en POO qui consiste à simplifier la gestion des images en php.
L’idée, c’est d’associer une image à un objet de la classe Image.

Ainsi, il serait très facile de réaliser du traitement d’images.
Par exemple, pour redimensionner et assombrir une image, le code serait :

<?php

include 'tools/Image.php';

$image = new Image('image.png');

$image->redimensionner(250, 200);
$image->assombrir(0.6);

// Afficher l'image au navigateur et libérer la mémoire
$image->afficher();
$image->supprimer();

Le problème, c’est que la classe Image ne fonctionne pas et retourne des erreurs :

<?php

class Image
{
    // Attributs
    private $_im;
    private $_format;
    private $_largeur;
    private $_hauteur;

    public function __construct($path)
    {
        if (preg_match('#\.jpg$#i', $path) === 1)
        {
            $_im = imagecreatefromjpeg($path);
            $_format = 'JPEG';
        }
        else if (preg_match('#\.png$#i', $path) === 1)
        {
            $_im = imagecreatefrompng($path);
            $_format = 'PNG';
        }
        else
        {
            throw new Exception('Extension du fichier « '.$path.' » non reconnue.');
        }

        $_largeur = imagesx($_im);
        $_hauteur = imagesy($_im);
    }

    public function afficher()
    {
        if ($_format === 'PNG')
        {
            header('Content-type: image/png');
            imagepng($_im);
        }
        else if ($_format === 'JPEG')
        {
            header('Content-type: image/jpeg');
            imagejpeg($_im);
        }
    }

    public function supprimer()
    {
        imagedestroy($_im);
    }
}


// --- Code principal ---

$image = new Image('image.png');
$image->afficher();
$image->supprimer();

Concrètement :

  • Pourquoi le programme indique que la variable $_format est indéfinie à la ligne 34 ?
  • Comment pourrait-on mieux concevoir ce programme en évitant un seul if par type d’image (lignes 13 à 22) ?
  • Comment transformer les notices et les avertissements en erreurs fatales afin de bloquer le programme si une opération échoue ?

Désolé pour la longueur du message, un bien grand merci à ceux qui répondront (même partiellement) ! :ange:

+0 -0

Cette réponse a aidé l’auteur du sujet

Hello, je passe en coup de vent. :ange:

  • Pourquoi le programme indique que la variable $_format est indéfinie à la ligne 34 ?
LysPrintemps

Parce qu’elle n’est pas définie. :p

Il faut écrire $this->$_format et non pas $_format.

Edit : A chaque fois que tu appel une variable qui appartient à une classe (me souviens plus du nom…) il faut précéder l’appel par $this->.

Édité par FougereBle

Çà brille, c’est debout sur un tonneau, c’est une lampe !

+0 -0
Auteur du sujet

Il faut écrire $this->$_format et non pas $_format.

Les variables sont encore indéfinies ! :euh:

<?php

class Image
{
    // Attributs
    private $_im;
    private $_format;
    private $_largeur;
    private $_hauteur;

    public function __construct($path)
    {
        if (preg_match('#\.jpg$#i', $path) === 1)
        {
            $this->$_im = imagecreatefromjpeg($path);
            $this->$_format = 'JPEG';
        }
        else if (preg_match('#\.png$#i', $path) === 1)
        {
            $this->$_im = imagecreatefrompng($path);
            $this->$_format = 'PNG';
        }
        else
        {
            throw new Exception('Extension du fichier « '.$path.' » non reconnue.');
        }

        $this->$_largeur = imagesx($this->$_im);
        $this->$_hauteur = imagesy($this->$_im);
    }

    public function afficher()
    {
        if ($this->$_format === 'PNG')
        {
            header('Content-type: image/png');
            imagepng($this->$_im);
        }
        else if ($this->$_format === 'JPEG')
        {
            header('Content-type: image/jpeg');
            imagejpeg($this->$_im);
        }
    }

    public function supprimer()
    {
        imagedestroy($this->$_im);
    }
}



// --- Code principal ---

$image = new Image('image.png');
$image->afficher();
$image->supprimer();
+0 -0

Cette réponse a aidé l’auteur du sujet

Oups, j’ai fait une erreur :ange: . Il faut enlever le $ devant le nom de la variable. Comme ceci $this->_ma_variable.

Édité par FougereBle

Çà brille, c’est debout sur un tonneau, c’est une lampe !

+0 -0
Auteur du sujet

Cela fonctionne, merci bien ! ^^

Je reste preneur pour les questions suivantes :

  • Comment pourrait-on mieux concevoir ce programme en évitant un seul if par type d’image (lignes 13 à 22) ?
  • Comment transformer les notices et les avertissements en erreurs fatales afin de bloquer le programme si une opération échoue ?
+0 -0

Cette réponse a aidé l’auteur du sujet

pour supprimer les conditions, je pense qu’un

$arr = array('jpg', 'png', 'jpeg');
$extension = pathinfo($path, PATHINFO_EXTENSION);
if (in_array($extension, $arr)) {
  echo $extension;
}

pourrait aider :) je te laisse modifier $arr pour en faire un tableaux multidimentionel et récupérer les valeurs désire en fonction de l’extension ;)

+0 -0

Cette réponse a aidé l’auteur du sujet

pour supprimer les conditions, je pense qu’un

$arr = array('jpg', 'png', 'jpeg');
$extension = pathinfo($path, PATHINFO_EXTENSION);
if (in_array($extension, $arr)) {
  echo $extension;
}

pourrait aider :) je te laisse modifier $arr pour en faire un tableaux multidimentionel et récupérer les valeurs désire en fonction de l’extension ;)

Leeroy Jenkins

Si on veut vraiment aller dans le POO à fond, on pourrait même aller plus loin et définir la classe Image comme une classe abstraite, laquelle définirait une méthode abstraite generate() par exemple, à implémenter dans les classes filles, comme ceci :

Diagramme de classe : la classe Image devient abstraite, ImagePNG et ImageJpeg étendent Image
Diagramme de classe : la classe Image devient abstraite, ImagePNG et ImageJpeg étendent Image

L’avantage, c’est que comme ça, tu as un contrôles bien plus grand sur ce que tu reçois dans ton constructeur : dans ImagePNG, tu ne gères que le PNG, de même pour ImageJpeg, et cela sans la moindre condition :)

A graphical interface is like a joke: if you have to explain it, that’s shit.

+0 -0
Auteur du sujet

Ouaw, merci pour vos conseils ! :)

J’essaierai les deux méthodes pour voir celle qui me convient le mieux.
@Jérôme Deuchnord : pourquoi la classe abstraite Image possède-t-elle une méthode afficher() si cette méthode se retrouve déjà dans les classes filles ?

Édité par anonyme

+0 -0

Cette réponse a aidé l’auteur du sujet

@Jérôme Deuchnord : pourquoi la classe abstraite Image possède-t-elle une méthode afficher() si cette méthode se retrouve déjà dans les classes filles ?

LysPrintemps

C’est une méthode abstraite, c’est-à-dire que tu définis sa signature, mais pas son corps. PHP comprendra ainsi que c’est une méthode qui doit être implémentée dans les classes filles, sans quoi ton programme plantera (on dit que l’on remplit un contrat) :)

Dans ta classes Image, la méthode afficher() ressemblera donc à ça :

public abstract function afficher();

Édité par Jérôme Deuchnord

A graphical interface is like a joke: if you have to explain it, that’s shit.

+0 -0
Auteur du sujet

J’ai été me renseigner un peu sur les classes et méthodes abstraites, mais je ne comprends pas l’utilité des méthodes abstraites : on pourrait s’en passer et se contenter d’implémenter les méthodes (non abstraites) dans les classes filles, non ?

Exemple (testé avec php7) :

<?php

abstract class Image
{
    private $_im;
    private $_format;
    private $_largeur;
    private $_hauteur;

    public function supprimer()
    {
        imagedestroy($this->_im);
    }
}

// ImagePNG est un mot-clef réservé
// pour une fonction GD
class Image_PNG extends Image
{
    public function __construct($path)
    {
        $this->_im = imagecreatefrompng($path);
    }

    public function afficher()
    {
        header('Content-type: image/png');
        imagepng($this->_im);
    }
}



// --- Code principal ---

$image = new Image_PNG('image.png');
$image->afficher();
$image->supprimer();
+0 -0

Cette réponse a aidé l’auteur du sujet

Dans ton cas, c’est vrai qu’on pourrait s’en passer, mais l’idée à retenir, c’est que ta classe abstraite forme une sorte de "contrat" : une nouvelle classe-fille (disons ImageSvg) ne peut pas étendre ta classe abstraite sans avoir sa propre méthode afficher(). Si tu oublies de le faire, PHP te le signalera, car il considérera que c’est une erreur.

C’est très pratique, car tu peux utiliser ta classe abstraite pour faire par exemple ceci :

function obtenirImageAuHasard(array $images): Image
{
    return $images[rand(0, count($images) - 1)];
}

$image = obtenirImageAuHasard([...]);
// Ici, on sait qu'on a reçu une Image, mais on ne sait pas de quel type exactement
// Mais puisque la méthode est définie dans la classe abstraite, je peux faire ceci directement,
// car je sais qu'elle est forcément implémentée :
$image->afficher();

Si tu ne définis pas la méthode abstraite, il te faut alors vérifier que la méthode est bien implémentée, par exemple avec un instanceof, ce qui est peu élégant.

Une autre utilité, très répandue, des méthodes abstraites, est d’appeler ladite méthode abstraite depuis une méthode qui, elle, a déjà été définie. Voici un exemple un peu bancal, mais qui devrait te permettre de comprendre :

abstract class Livre
{
    private $titre;
    private $auteur;
    private $editions;
    private $nbPages;

    public function seLitDeDroiteAGauche(): boolean
    {
        return $this->estJaponais();
    }

    public abstract function estJaponais(): boolean;

    // Getters et setters ici
}

class Manga extends Livre
{
    // Constructeur ici

    public function estJaponais(): boolean
    {
        return true;
    }
}

$manga = new Manga();
echo $manga->seLitDeDroiteAGauche(); // Affichera '1', ce qui équivaut à la valeur booléenne true

Si tu ne définis pas la méthode abstraite ligne 13, PHP plantera, car il ne trouvera pas la méthode estJaponais() appelée dans la méthode au-dessus. L’intérêt ici, c’est de ne pas redéfinir une méthode dans les classes filles si tu sais que leur contenu ne changera pas ou pas souvent, et ce même si cette méthode a besoin d’une valeur retournée par une méthode dont la logique n’est pas encore connue au moment de l’écriture de la classe abstraite :)

A graphical interface is like a joke: if you have to explain it, that’s shit.

+0 -0
Auteur du sujet

Comment transformer les notices et les avertissements en erreurs fatales afin de bloquer le programme si une opération échoue ?

Selon cette source, il est possible d’utiliser ce code :

<?php

function errHandle($errNo, $errStr, $errFile, $errLine) {
    $msg = "$errStr in $errFile on line $errLine";
    if ($errNo == E_NOTICE || $errNo == E_WARNING) {
        throw new ErrorException($msg, $errNo);
    }
    echo $msg;
}

set_error_handler('errHandle');
+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte