PHP lekcije - lekcija 4 - Rad sa slikama, GD2 biblioteka.
U prethodnim lekcijama smo naučili kako pisati upite prema bazi podataka, tako da ćemo se sada manje baviti pisanjem upita, a više ćemo ih vežbati. Takođe ćemo kombinovati pisanje upita sa učenjem drugih mogućnosti PHP-a, pa ćemo početi sa obradom slika. U jednoj od prethodnih lekcija već smo učitali fajlove, imamo čak i tabelu Files za učitane fajlove. Hajde sada da učitavamo slike u istu tabelu. Ali prvo treba da dodamo polje za učitavanje fotografije u formu za kreiranje sadržaja.
$content .= '<label for="bodytext">Poruka:</label><br />'; $content .= '<textarea name="bodytext" id="bodytext"></textarea><br />'; $content .= '<label for="bodytext">Priloži fajl:</label><br /><input type="file" name="filename"><br />'; // polje za upload fajla $content .= '<label for="bodytext">Priloži sliku:</label><br /><input type="file" name="image">'; // polje za upload slike
Sada treba da napišemo obradu fajlova, ali prvo ćemo dodati još jednu kolonu u tabelu Messages, u koju ćemo upisivati fid slike, dok će podaci o slici i dalje biti upisivani u tabelu Files, kao i za ostale fajlove. Kolonu sam nazvao image_fid, karakteristike su INT(11) i NULL kao podrazumevana vrednost. Sada ćemo ubaciti obradu forme u metodu write():
if($_FILES["image"]["size"] > 1024*3*1024){ echo ("Veličina fajla prelazi tri megabajta"); exit; } if(is_uploaded_file($_FILES["image"]["tmp_name"])){ // Proveravamo da li je fajl upload-ovan // Ako je fajl uspešno upload-ovan, pomeramo ga // iz privremene fascikle u krajnju move_uploaded_file($_FILES["image"]["tmp_name"], "./files/".$_FILES["filename"]["name"]); } else { echo("Greška pri upload-u fajla"); } $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);
Ova obrada se neće razlikovati od upload-a običnih fajlova, mada ćemo u jednoj od narednih lekcija videti kako proveriti koji tip fajla se uploaduje, da se na primer pod maskom slike ne bi uploadovao pdf dokument.
Obradu smo napisali, sada će se upload-ovati dva fajla, od kojih je jedan slika. Ovako izgleda $_POST i $_FILES niz pri ispisu preko 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 ) )
Još treba promeniti upit za unos poruke:
$sql = 'INSERT INTO Messages (title, bodytext, created, fid, image_fid) VALUES ("'. $p["title"] . '", "' . $p["bodytext"] . '", ' . time() . ',LAST_INSERT_ID()-1, LAST_INSERT_ID())';
Za određivanje fid običnog fajla i slike koristim funkciju LAST_INSERT_ID(), pošto se običan fajl ubacuje pre slike, oduzimam jedan od poslednjeg id-a.
Sada kada se slike ubacuju u bazu, možemo ih i prikazivati. Ali za rad sa slikama napravićemo osnovu, poseban klasu, koju ćemo nazvati simpleImage. Kreiraćemo novi fajl simpleImage.php u fascikli class i tu ćemo pisati kod naše klase.
Otvaramo simpleImage.php i upisujemo:
<?php class simpleCMS { } ?>
Napravili smo novu klasu za rad sa slikama, sada treba da dodamo metode za rad sa njom. Ali prvo ću vam ispričati o vrstama promenljivih u klasama u PHP-u.
Osobine klase u PHP 5
Pre je u PHP4 bilo jednostavno, osobine klase su definisane sa var, a u PHP5 se pojavio veći stepen kontrole nad pristupom osobinama klase. Počećemo sa public, jer ga već koristimo.
Public
Public operator definiše otvorenu, javno dostupnu promenljivu klase. Tako možemo menjati vrednost te promenljive u objektu tako što jednostavno dodelimo vrednost, na primer:
<?php $obj = new simpleCMS(); $obj->db='newDB'; ?>
Tako pristupamo promenljivoj objekta, jer je definisana kao public.
Protected
Protected operator znači da je osobina ili metoda dostupna unutar klase i njenih izvedenih klasa (nasleđivanje i izvedene klase ćemo kasnije obrađivati). Ako definišemo osobinu db kao protected i pokušamo da joj pristupimo spolja, izazvaće grešku:
simpleCMS.php ... protected $db = 'testDB'; ... index.php ... $obj = new simpleCMS(); $obj->db='newDB'; ...
Sada ćemo dodati zaštićenu promenljivu path (putanja do fajla), koju ćemo koristiti u metodama klase. Putanju fajla ćemo prenositi preko konstruktora.
Konstruktor klase
Konstruktor klase se koristi u PHP-u da bi se definisalo početno stanje objekta, na primer da se dodeli putanja fajla za objekat slike. Konstruktor ima posebno ime sa dva donja crtica na početku:
class simpleCMS { protected $path; public __construct($image){ $this->path = $image; } }
Na ovaj način prosleđujemo putanju fajla kroz parametar konstruktora i čuvamo je u zaštićenoj promenljivoj klase. Sada treba da izmenimo metodu public_display u klasi simpleCMS da bi prikazala podatke o slikama.
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); }
Ovako dobijamo putanju do fajla iz baze. Sada kada smo izbacili putanju, možemo pisati metode za obradu dimenzija slike. Napravićemo funkcije kao u Drupal-u:
public function scale($width,$height){ } public function scale_and_crop($width,$height){ }
Počećemo sa funkcijom za skaliranje scale().
public function scale($width, $height){ $path = $this->path; $filepath = explode('/',$this->path); // razdvajamo foldere i ime fajla $filename = end($filepath); // uzimamo poslednji element - ime fajla $filename = substr($filename, 0, -4); // uklanjamo ekstenziju $filename = str_replace(' ', '_', $filename); // uklanjamo razmake iz imena fajla $extenstion = substr($this->path, -4); // proveravamo ekstenziju fajla: 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; } // određujemo početnu širinu i visinu slike $srcWidth = ImageSX( $srcImage ); $srcHeight = ImageSY( $srcImage ); // sledeći kod proverava da li je širina veća od visine // ili visina veća od širine slike, tako da // pri promeni dimenzija ostane pravi odnos proporcija $ratioWidth = $srcWidth/$width; $ratioHeight = $srcHeight/$height; if( $ratioWidth < $ratioHeight){ $destWidth = $srcWidth/$ratioHeight; $destHeight = $height; }else{ $destWidth = $width; $destHeight = $srcHeight/$ratioWidth; } // kreiramo novu sliku sa krajnjim dimenzijama print $destWidth. '<br />' .$destHeight; $destImage = imagecreate( $destWidth, $destHeight); // kopiramo srcImage (izvorna) u destImage (konačna) ImageCopyResized( $destImage, $srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight ); // kreiramo jpeg imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); // potrebno kreirati folder presets u folderu files $newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg'; // oslobađamo memoriju ImageDestroy( $srcImage ); ImageDestroy( $destImage ); return '<img src="'. $newImagePath . '" width="'. $destWidth.'" height="'. $destHeight . '" />'; // prikazujemo sliku }
Obrađene slike ćemo čuvati u folderu files/presets, potrebno je da folder presets postoji i da ima prava 777.
Mislim da sam detaljno opisao algoritam u komentarima, zato prelazimo na metodu scale_and_crop(), koja se malo razlikuje od scale():
public function scale_and_crop($width, $height){ $path = $this->path; $filepath = explode('/',$this->path); // razdvajamo foldere i ime fajla $filename = end($filepath); // uzimamo poslednji element - ime fajla $filename = substr($filename, 0, -4); // uklanjamo ekstenziju $filename = str_replace(' ', '_', $filename); // uklanjamo razmake iz imena fajla $extenstion = substr($this->path, -4); // proveravamo ekstenziju fajla: 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; } // određujemo početnu širinu i visinu slike $srcWidth = ImageSX( $srcImage ); $srcHeight = ImageSY( $srcImage ); // sledeći kod proverava da li je širina veća od visine // ili visina veća od širine slike, tako da // pri promeni dimenzija ostane pravi odnos proporcija $ratioSource = $srcWidth/$srcHeight; $ratioNew = $width/$height; if( $ratioSource < $ratioNew){ $newWidth = $srcWidth; $newHeight = $srcHeight / $ratioNew * $ratioSource; // računamo novu visinu slike proporcionalno zadatoj širini }else{ $newWidth = $srcWidth / $ratioSource * $ratioNew; // računamo novu širinu slike proporcionalno zadatoj visini $newHeight = $srcHeight; } // kreiramo novu sliku sa krajnjim dimenzijama $destImage = imagecreate($width, $height); // razlika u odnosu na scale() // kopiramo srcImage (izvorna) u destImage (konačna) imagecopyresampled( $destImage, $srcImage, 0, 0, 0, 0, $width, $height, $newWidth, $newHeight); // kreiramo jpeg imagejpeg( $destImage, 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg' ,100 ); // potrebno kreirati folder presets u folderu files $newImagePath = 'files/presets/'.$width.'_'.$height.'_'. $filename . '.jpg'; // oslobađamo memoriju ImageDestroy( $srcImage ); ImageDestroy( $destImage ); return '<img src="'. $newImagePath . '" />'; // prikazujemo sliku }
Takođe treba promeniti liniju za prikaz u metodi display_public():
$content .= '<div class="image">'; $image = new simpleImage($image['filepath']); $content .= $image->scale_and_crop(100,100); $content .= '</div>';
Na ovaj način slike će biti kvadratne i isečene da zadrže proporcije.
U oba slučaja slike se generišu u hodu. Generisanje slika traje, zato se obično putanje ka umanjenim slikama pamte u bazi i direktno prikazuju bez ponovne obrade.
Mi to nećemo obrađivati u ovom kursu, jer ćemo za izgradnju svoje CMS koristiti Zend framework, a ove PHP lekcije prvenstveno treba da vas upoznaju sa osnovama PHP-a.