PHP-Lektionen – Lektion 4 – Arbeiten mit Bildern, GD2-Bibliothek.
In den vorherigen Lektionen haben wir gelernt, wie man Datenbankabfragen schreibt, daher werden wir uns jetzt weniger darauf konzentrieren, wie man Abfragen schreibt, sondern mehr darauf üben. Wir werden auch das Schreiben von Abfragen mit dem Erlernen anderer PHP-Funktionen kombinieren, angefangen mit der Bildverarbeitung. In einer der vorherigen Lektionen haben wir bereits Dateien hochgeladen und eine Tabelle Files für hochgeladene Dateien erstellt. Lassen Sie uns Bilder in dieselbe Tabelle hochladen. Zuerst müssen wir jedoch ein Feld für das Hochladen von Fotos im Formular zur Inhaltserstellung hinzufügen.
$content .= '<label for="bodytext">Nachricht:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea><br />'; $content .= '<label for="bodytext">Datei anhängen:</label><br /><input type="file" name="filename"><br />'; // Datei-Upload-Feld $content .= '<label for="bodytext">Bild anhängen:</label><br /><input type="file" name="image">'; // Bild-Upload-Feld
Nun müssen wir die Datei-Uploads verarbeiten, aber zuerst fügen wir der Tabelle Messages eine weitere Spalte hinzu, in der wir die Bild-fid speichern. Die Bilddaten speichern wir wie bei anderen Dateien in der Tabelle Files. Die Spalte heißt image_fid, hat den Typ INT(11) und erlaubt NULL als Standardwert. Jetzt fügen wir die Formularverarbeitung in die Methode write() ein:
if($_FILES["image"]["size"] > 1024*3*1024){ echo ("Die Dateigröße überschreitet drei Megabyte"); exit; } if(is_uploaded_file($_FILES["image"]["tmp_name"])){ // Prüfen, ob Datei hochgeladen wurde // Wenn erfolgreich hochgeladen, verschieben wir sie // aus dem temporären Verzeichnis in das Zielverzeichnis move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]); } else { echo("Fehler beim Hochladen der Datei"); } $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);
Diese Verarbeitung unterscheidet sich nicht von der normalen Datei-Uploads, obwohl wir in einer der kommenden Lektionen prüfen werden, dass keine unerwünschten Dateitypen (z. B. PDF statt Bild) hochgeladen werden.
Wir haben die Verarbeitung implementiert, jetzt werden zwei Dateien hochgeladen, eine davon ein Bild. So sah der Array $_POST und $_FILES bei mir aus, nachdem ich ihn mit print_r() ausgegeben habe:
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 ) )
Wir müssen auch die Einfüge-Abfrage für die Nachrichten anpassen:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid, image_fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID()-1, LAST_INSERT_ID())';
Zur Bestimmung der fid von der normalen Datei und dem Bild verwende ich die Funktion LAST_INSERT_ID(), da die normale Datei vor dem Bild eingefügt wird, ziehe ich von der letzten ID eins ab.
Jetzt, da die Bilder in der Datenbank gespeichert werden, können wir sie ausgeben. Für die Bildverarbeitung erstellen wir eine Vorlage, eine separate Klasse namens simpleImage. Legen wir eine neue Datei simpleImage.php im Ordner class an und schreiben folgenden Code:
<?php class simpleCMS { } ?>
Wir haben also eine neue Klasse für die Bildverarbeitung erstellt, nun müssen wir Methoden dafür schreiben. Zuerst erkläre ich aber die Arten von Klassenvariablen in PHP.
Klassen-Eigenschaften in PHP 5
Früher in PHP4 wurden alle Klassen-Eigenschaften einfach mit var definiert. In PHP5 gibt es mehr Freiheit bei der Deklaration von Eigenschaften. Wir starten mit dem public-Operator, den wir bereits benutzt haben.
Public
Mit public deklarieren wir eine öffentliche, von außen zugängliche Klassenvariable. So kann man den Wert der Variablen im Objekt einfach ändern, zum Beispiel:
<?php $obj = new simpleCMS(); $obj->db='newDB'; ?>
So greifen wir auf die Eigenschaft des Objekts zu, weil sie als public definiert wurde.
Protected
Mit protected ist die Eigenschaft oder Methode nur innerhalb der Klasse oder ihrer Unterklassen zugänglich (Vererbung folgt später). Wenn wir db als protected definieren und versuchen, von außen darauf zuzugreifen, erhalten wir einen Fehler:
simpleCMS.php ... protected $db = 'testDB'; ... index.php ... $obj = new simpleCMS(); $obj->db='newDB'; ...
Wir fügen jetzt eine geschützte Variable path (Pfad zur Datei) hinzu, die wir später in Methoden verwenden. Den Pfad übergeben wir im Konstruktor.
Konstruktor der Klasse
Der Konstruktor wird in PHP benutzt, um ein Anfangszustand des Objekts zu definieren, z. B. den Pfad zur Bilddatei. Er wird wie andere Methoden definiert, hat aber zwei Unterstriche am Anfang des Namens:
class simpleCMS { protected $path; public function __construct($image){ $this->path = $image; } }
So übergeben wir den Pfad als Parameter im Konstruktor und speichern ihn in der geschützten Variable. Nun ändern wir die Methode public_display in simpleCMS, damit Bilddaten ausgegeben werden:
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); }
So holen wir den Bildpfad aus der DB. Jetzt schreiben wir Methoden zur Größenänderung der Bilder, ähnlich wie in Drupal:
public function scale($width,$height){ } public function scale_and_crop($width,$height){ }
Fangen wir mit scale() an:
public function scale($width, $height){ $path = $this->path; $filepath = explode('/',$this->path); // Ordner und Dateiname trennen $filename = end($filepath); // Dateiname holen $filename = substr($filename, 0, -4); // Dateiendung entfernen $filename = str_replace(' ', '_', $filename); // Leerzeichen entfernen $extenstion = substr($this->path, -4); // Dateiendung prüfen png, gif, jpg 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; } // Originalbreite und -höhe bestimmen $srcWidth = ImageSX( $srcImage ); $srcHeight = ImageSY( $srcImage ); // Verhältnis berechnen, um Proportionen zu erhalten $ratioWidth = $srcWidth/$width; $ratioHeight = $srcHeight/$height; if( $ratioWidth < $ratioHeight){ $destWidth = $srcWidth/$ratioHeight; $destHeight = $height; }else{ $destWidth = $width; $destHeight = $srcHeight/$ratioWidth; } // Neues Bild mit Zielgröße erstellen print $destWidth. '<br />' .$destHeight; $destImage = imagecreate( $destWidth, $destHeight); // Originalbild kopieren und skalieren ImageCopyResized( $destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight ); // JPEG speichern imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); // Ordner presets in files anlegen mit Schreibrechten $newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg'; // Speicher freigeben ImageDestroy( $srcImage ); ImageDestroy( $destImage ); return '<img src="'. $newImagePath . '" width="'. $destWidth.'" height="'. $destHeight . '" />'; // Bild ausgeben }
Die bearbeiteten Bilder speichern wir in files/presets, der Ordner presets muss mit Schreibrechten (777) angelegt werden.
Der Algorithmus ist ausführlich in Kommentaren erklärt. Nun zur Methode scale_and_crop(), die ähnlich ist:
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 . '" />'; }
Wir passen außerdem die Ausgabezeile in der Methode display_public() an:
$content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale_and_crop(100,100); $content .= '</div>';
So sind die Bilder quadratisch und proportional beschnitten.
In beiden Fällen werden die Bilder "on the fly" generiert. Die Bildgenerierung braucht Zeit, deshalb speichert man normalerweise die Pfade zu den verkleinerten Bildern in der Datenbank und zeigt diese direkt an, ohne die Bilder jedes Mal neu zu erzeugen.
In diesem Kurs behandeln wir das nicht, weil wir später im CMS den Zend Framework verwenden, und diese PHP-Lektionen sollen vor allem mit den PHP-Grundlagen vertraut machen.