PHP-lessen - Les 4 - Werken met afbeeldingen, de GD2-bibliotheek.
In de vorige lessen hebben we geleerd hoe we query’s naar de database kunnen schrijven, dus nu zullen we daar minder aandacht aan besteden en meer oefenen met het schrijven ervan. We gaan ook het schrijven van query’s combineren met het verkennen van andere mogelijkheden van PHP — te beginnen met beeldverwerking. In een eerdere les hebben we al bestanden geüpload, en we hebben zelfs een tabel Files voor de geüploade bestanden. Laten we nu afbeeldingen naar dezelfde tabel uploaden. Maar eerst moeten we een veld toevoegen voor het uploaden van een foto in het formulier voor het aanmaken van content.
$content .= '<label for="bodytext">Bericht:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea><br />'; $content .= '<label for="bodytext">Bestand bijvoegen:</label><br /><input type="file" name="filename"><br />'; // veld voor bestand $content .= '<label for="bodytext">Afbeelding bijvoegen:</label><br /><input type="file" name="image">'; // veld voor afbeelding
Nu moeten we de verwerking van bestanden schrijven, maar eerst voegen we een extra kolom toe aan de tabel Messages, waarin we de fid van afbeeldingen opslaan. De afbeeldingsgegevens zelf worden opgeslagen in de tabel Files, net als bij andere bestanden. De kolom noem ik image_fid met de eigenschappen INT(11) en standaardwaarde NULL. Vervolgens voegen we de verwerking toe aan de methode write()
:
if($_FILES["image"]["size"] > 1024*3*1024){ echo ("Bestandsgrootte overschrijdt drie megabyte"); exit; } if(is_uploaded_file($_FILES["image"]["tmp_name"])){ // Controleren of bestand is geüpload move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]); } else { echo("Fout bij het uploaden van bestand"); } $sql = 'INSERT INTO Files (filename, filepath, filemime, filesize, timestamp) VALUES ("'. $_FILES['image']['name'] . '", "files/' . $_FILES['image']['name'] . '", "'. $_FILES['image']['type'] .'", '. $_FILES['image']['size'] .', '. time() . ')'; mysql_query($sql);
Deze verwerking verschilt niet van het uploaden van gewone bestanden, hoewel we in een van de volgende lessen zullen bespreken hoe we het bestandstype kunnen controleren om te voorkomen dat bijvoorbeeld PDF-documenten als afbeeldingen worden geüpload.
We hebben nu de verwerking toegevoegd, en er zullen twee bestanden worden geüpload, waarvan één een afbeelding is. Dit is een voorbeeld van de arrays $_POST
en $_FILES
zoals ik ze kreeg via print_r()
:
Array ( [title] => test [bodytext] => asdfasf ) Array ( [filename] => Array ( [name] => ip.txt [type] => text/plain [tmp_name] => Y:\tmp\php2C5.tmp [error] => 0 [size] => 13 ) [image] => Array ( [name] => Coat_of_arms_of_Vladimiri_Oblast.png [type] => image/png [tmp_name] => Y:\tmp\php2C6.tmp [error] => 0 [size] => 393743 ) )
We moeten nu ook de query voor het invoegen van het bericht aanpassen:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid, image_fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID()-1, LAST_INSERT_ID())';
Om de fid van een gewoon bestand en van de afbeelding te bepalen, gebruik ik de functie LAST_INSERT_ID(). Omdat het gewone bestand vóór de afbeelding wordt ingevoegd, trek ik één af van het laatste ID.
Nu de afbeeldingen in de database worden ingevoegd, kunnen we ze weergeven. Maar voor de beeldverwerking maken we een aparte klasse, simpleImage. Maak een nieuw bestand simpleImage.php in de map class en schrijf daarin de volgende code:
<?php class simpleCMS { } ?>
We hebben dus een nieuwe klasse aangemaakt voor het werken met afbeeldingen. Nu moeten we methoden toevoegen om ermee te werken. Maar eerst vertel ik iets over klassenvariabelen in PHP.
Eigenschappen van een klasse in PHP 5
In PHP4 was alles eenvoudig: eigenschappen van een klasse werden gedefinieerd met var
. In PHP5 hebben we meer controle over de toegankelijkheid van eigenschappen. We beginnen met de operator public
, omdat we die al eerder hebben gebruikt.
Public
Met de operator public definiëren we een publiek toegankelijke variabele van een klasse. We kunnen dus de waarde van deze variabele in een object wijzigen, bijvoorbeeld:
<?php $obj = new simpleCMS(); $obj->db='newDB'; ?>
Zo kunnen we rechtstreeks een eigenschap van het object wijzigen, omdat het als public is gedefinieerd.
Protected
De operator protected geeft aan dat een eigenschap of methode alleen toegankelijk is binnen de klasse zelf of vanuit een afgeleide klasse (over erven en afgeleide klassen later meer). Als we bijvoorbeeld de eigenschap db als protected definiëren en proberen haar aan te roepen, krijgen we een foutmelding:
simpleCMS.php ... protected $db = 'testDB'; ... index.php ... $obj = new simpleCMS(); $obj->db='newDB'; ...
Laten we nu een beveiligde variabele path (het pad naar het bestand) toevoegen, zodat we deze later in methoden van de klasse kunnen gebruiken. We zullen de waarde van deze variabele doorgeven via de constructor.
Constructor van de klasse
De constructor van een klasse wordt in PHP gebruikt om de initiële toestand van een object in te stellen. In ons geval geven we het pad naar het afbeeldingsbestand door. De constructor wordt gedefinieerd als elke andere methode, maar begint met twee underscores:
class simpleCMS { protected $path; public __construct($image){ $this->path = $image; } }
Zo geven we het pad naar het bestand door via de constructor en slaan we het op in de beveiligde variabele path. Vervolgens wijzigen we de methode public_display
van de klasse simpleCMS
om afbeeldingsgegevens weer te geven:
if(!empty($row['image_fid'])){ $sql = "SELECT * FROM Files WHERE fid=".$row['image_fid']; $image_query = mysql_query($sql) or die(mysql_error()); $image = mysql_fetch_array($image_query); $content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale(100,100); $content .= '</div>'; unset($image); }
Zo halen we het bestandspad uit de database. Nu kunnen we methoden schrijven om de afbeeldingsgrootte aan te passen. We maken functies zoals in Drupal:
public function scale($width,$height){ } public function scale_and_crop($width,$height){ }
Laten we beginnen met de functie scale()
:
public function scale($width, $height){ $path = $this->path; $filepath = explode('/',$this->path); $filename = end($filepath); $filename = substr($filename, 0, -4); $filename = str_replace(' ', '_', $filename); $extenstion = substr($this->path, -4); switch ($extenstion){ case '.png': $srcImage = ImageCreateFromPNG( $path ); break; case '.jpg': $srcImage = ImageCreateFromJPEG( $path ); break; case '.gif': $srcImage = ImageCreateFromGIF( $path ); break; default: $srcImage = ImageCreateFromGIF( $path ); break; } $srcWidth = ImageSX( $srcImage ); $srcHeight = ImageSY( $srcImage ); $ratioWidth = $srcWidth/$width; $ratioHeight = $srcHeight/$height; if( $ratioWidth < $ratioHeight){ $destWidth = $srcWidth/$ratioHeight; $destHeight = $height; }else{ $destWidth = $width; $destHeight = $srcHeight/$ratioWidth; } print $destWidth. '<br />' .$destHeight; $destImage = imagecreate( $destWidth, $destHeight); ImageCopyResized( $destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight ); imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); $newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg'; ImageDestroy( $srcImage ); ImageDestroy( $destImage ); return '<img src="'. $newImagePath . '" width="'. $destWidth.'" height="'. $destHeight . '" />'; }
De verwerkte afbeeldingsbestanden worden opgeslagen in de map files/presets. Zorg ervoor dat deze map bestaat en dat de rechten zijn ingesteld op 777.
De code is al uitvoerig becommentarieerd, dus laten we doorgaan met de methode scale_and_crop()
, die weinig verschilt van scale()
:
public function scale_and_crop($width, $height){ $path = $this->path; $filepath = explode('/',$this->path); $filename = end($filepath); $filename = substr($filename, 0, -4); $filename = str_replace(' ', '_', $filename); $extenstion = substr($this->path, -4); switch ($extenstion){ case '.png': $srcImage = ImageCreateFromPNG( $path ); break; case '.jpg': $srcImage = ImageCreateFromJPEG( $path ); break; case '.gif': $srcImage = ImageCreateFromGIF( $path ); break; default: $srcImage = ImageCreateFromGIF( $path ); break; } $srcWidth = ImageSX( $srcImage ); $srcHeight = ImageSY( $srcImage ); $ratioSource = $srcWidth/$srcHeight; $ratioNew = $width/$height; if( $ratioSource < $ratioNew){ $newWidth = $srcWidth; $newHeight = $srcHeight / $ratioNew * $ratioSource; }else{ $newWidth = $srcWidth / $ratioSource * $ratioNew; $newHeight = $srcHeight; } $destImage = imagecreate($width, $height); imagecopyresampled( $destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight); imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); $newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg'; ImageDestroy( $srcImage ); ImageDestroy( $destImage ); return '<img src="'. $newImagePath . '" />'; }
We moeten nu de weergavecode in de methode display_public()
wijzigen:
$content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale_and_crop(100,100); $content .= '</div>';
Zo worden de afbeeldingen vierkant en bijgesneden, terwijl de verhoudingen behouden blijven.
In beide gevallen worden de afbeeldingen “on the fly” gegenereerd. Het genereren van afbeeldingen kost tijd, dus in de praktijk worden de paden naar de verkleinde afbeeldingen meestal in de database opgeslagen, zodat ze direct kunnen worden weergegeven zonder opnieuw gegenereerd te worden.
We zullen in deze cursus niet behandelen hoe dat werkt, want bij het bouwen van onze eigen CMS zullen we het Zend Framework gebruiken. Deze PHP-lessen zijn in de eerste plaats bedoeld om je kennis te laten maken met de basisprincipes van PHP.