PHP Görsel İşlemleri ve Webp Oluşturma
PHP ile yazılarımıza XML okuma ve dosya oluşturma işlemi sonrasında cURL kütüphanesi ile devam etmiştik.
Sırada, PHP ile temel bazı görsel işlemleri var. Aşağıda detaylarına değineceğim örnekte, elimizdeki bir görselin (gif, png veya jpg uzantılı olsun) nasıl webp olarak kopyalanabileceğine ve hatta bu kopyalama işlemini nasıl sıkıştırarak yapabileceğimize değineceğim1. Bu süreçte 2 farklı yol izleyeceğim. İlk yöntemde işlemlerin çoğu exec()
, diğer yöntemde ise PHP’nin sahip olduğu imagecreatefrompng()
ve imagewebp()
gibi görsel formatları özelindeki fonksiyonlardan faydalanacağız. Ancak öncesinde Fast and Beautiful Modern Image Delivery Techniques başlıklı şu videoyu izlemenizi önereceğim.
Yukarıdaki videoda da sıklıkla vurgulandığı üzere, görseller veri hacmi ve trafiği bağlamında oldukça yoğun bir kullanıma sahip. Bu da beraberinde pek çok problem ve alternatif teknolojiyi getirmekte. Webp bu çözümlerden biri. Ayrıca, responsive images2 ve lazy loading3 yine sıklıkla bahsi geçen ve kullanılan diğer yöntemler. Elbette bu yazı içerisinde tüm bu çözümlere değinmek mümkün olmayacaktır. Ancak, daha önce konuya ilişkin yazdığım şu yazıları ayrıca önerebilirim.
- jpegoptim ve optipng İle Görselleri Optimize Etmek
- Cronjob İle Otomatik Görsel Optimizasyonu
- Google Page Speed Optimizasyonu
- Google Lighthouse Nedir?
- WebP Nedir? Nasıl Kullanılır?
- SIPS (Scriptable Image Processing System)
- CDN (Content Delivery Network) Nedir? Neden Kullanılır?
Şimdi gelelim işleyeceğimiz konuya ve örnek işlemlere. İlk yöntemde The Web Help tarafından PHP Webp image generator and HTML code4 başlıklı yazıda paylaşılan örneğe değineceğim.
WebP
Google WebP kullanımını yaygınlaştırmak amacıyla pek çok çözümü de kullanıma sunmakta5, libwebp6‘nin bir parçası olarak cwebp
7 ve gif2webp8 örnek gösterilebilir. Bahsi geçen bu araçları komut satırı aracılığıyla oldukça pratik bir şekilde kullanılabilmekte.
cwebp [options] gorsel.png|jpg -o gorsel.webp
gif2webp [options] gorsel.gif -o gorsel.webp
Peki, PHP ile bu komutu nasıl kullanabiliriz? Bu soruyu cevaplayabilmek için öncelikle exec()
fonksiyonuna kısaca değinmemiz gerekiyor.
PHP exec() Fonksiyonu
exec()
php dökümanımız içerisinden komut çalıştırmamızı mümkün kılan bir PHP fonksiyonudur9.
echo exec('cd ../ && ls -li')
Örneğin, yukarıdaki kod ile bir üst dizinde yer alan dosyalarımızın listesini php dökümanımız içerisinde görüntüleyebiliriz. Alacağımız dönüş aşağı yukarı şuna benzeyecektir:
54794473 -rw-r--r-- 1 user user 5366981 Sep 30 2018 wp-cli.phar
Daha detaylı bilgi için php: exec9 sayfasını inceleyebilirsiniz. Tekrar asıl konumuza dönelim. PHP exec()
bizim cwebp
ve gif2webp
komutlarını uygulamamızı sağlayacak bir aracı görevi görecek. Nasıl mı?
PHP exec() İle WebP İşlemleri
Tekrar PHP Webp image generator and HTML code4 başlıklı yazıdaki örneğe dönelim. Örneğin ilk bölümünde görüldüğü üzere öncelikle sunucuya libwebp
kütüphanesinin indirilmesi gerekiyor. Bu işlemin ardından cwebp
ve gif2webp
komutları da kullanılabilir hale geliyor. Şimdi, yazıda yer alan kodu adım adım inceleyelim. İlk olarak .htaccess
koduna bir bakalım10 11.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^webp/(.*)\.webp?$ webp_generator.php?file=$1 [QSA]
Yukarıdaki kod <img src="conv2webp.php?file=images/yoda.jpeg" />
gibi bir kullanımda file
ile belirtilen görselin webp
dizini altındaki webp
formatlı varyasyonunu getirir. Aynı işlemi tarayıcınızın adres barına ilgili dosya yolunu yazarak da gerçekleştirebilirsiniz. Gelelim conv2webp.php
isimli dosyanın içeriğine…
$settings_document_root = dirname(__FILE__);
$file = trim(strip_tags($_GET['file']));
if(!is_file($file)){
echo "original file not found";
exit;
}
$settings_document_root = dirname(__FILE__);
ile dizin bilgisini alır ve $file = trim(strip_tags($_GET['file']));
ile query parametresi olan file
içeriğini yine aynı adlı değişkene atarız. Ardından gelen if statement ile dosyayı file
içeriğindeki dizinde ararız. Dosya burada değilse işlemi sonlandırır, dosya bulunmuş ise sonraki adıma geçeriz; dosya uzantısını almak ve webp
klasörünü oluşturmak.
$file_extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
exec("mkdir -p ".escapeshellarg("webp/".dirname($file)));
cwebp ve gif2webp
Artık dosya uzantısını kullanarak ilgili komutlarımızı işleme alabiliriz. gif animasyon niteliği taşıması sebebiyle farklı bir dönüştürme işlemine tabi, bu nedenle diğer formatlardan farklı bir komut üzerinden işleme alınmakta ve sonuç olarak animated webp dosyası oluşturulmakta. -q
opsiyonu ile dönüştürme işlemindeki kalite düzeyini de belirtebilmekteyiz.
if($file_extension=="png" or $file_extension=="jpg" or $file_extension=="jpeg"){
exec("cwebp -q 80 ".escapeshellarg($settings_document_root."/".$file)." -o ".escapeshellarg($settings_document_root."/webp/".$file.".webp"));
}
if($file_extension=="gif"){
exec("gif2webp -q 80 ".escapeshellarg($settings_document_root."/".$file)." -o ".escapeshellarg($settings_document_root."/webp/".$file.".webp"));
}
Görüldüğü üzere kaynak dosyamızın webp
klasörü içerisinde webp uzantılı bir kopyası oluşturulmakta. Örneğin, images
klasörü altında bulunan dosyamız gorsel.png
iken, bu dosyanın webp/images/
dizini altında gorsel.png.webp
şeklinde bir webp türevi oluşturulacaktır. Dönüştürme işlemini gerçekleştiren satırlarımız bunlardan ibaret. Sonraki aşamada yer alan satırlarda ilgili dosyanın webp
klasörü altında webp formatlı türevinin oluşturulup oluşturulmadığı kontrol edilmekte.
if(!file_exists($settings_document_root."/webp/".$file.".webp")){
echo "cannot generate webp file ".escapeshellarg("webp/".$file);
exit;
}
Aşağıdaki exec
işlemleri ise dosyaların belirli zaman dilimlerinde (30 gün) silinmesini sağlamakta. Bir ara not, mtime
sonrasındaki değer n
olarak alınır ve zaman n*24
şeklinde işlenir. Dosyaların silinmesinin ardından webp
klasörü içerisindeki klasörlere bakılır ve boş olanlar silinir.
exec("find webp/ -type f -mtime +30 -delete");
exec("find webp/ -type d -empty -delete");
Dökümanın son satırları ise oluşturulan dosyanın gösterilmesini ve time() ile eklenen zaman etiketi sayesinde sonsuz yönlendirme (infinite loop) durumunun önlenmesini sağlar.
header("Location: /webp/".$file.".webp?".time());
exit;
Peki, her şey bu kadar kolay mı? Değil elbette. Çünkü cwebp
ve gif2webp
çoğu paylaşımlı barındırma alanında bulunmamakta ve yüklenememekte. Diğer yandan, yine paylaşımlı barındırma alanlarında exec()
fonksiyonunun kullanımına güvenlik sebebiyle izin verilmemekte. Ancak, bir çözüm mevcut. Adım adım ilerleyelim. Öncelikle cwebp
ve gif2webp
alternatifine bakalım.
gmagick ve imagick
Çoğu paylaşımlı barındırma alanında ya aktif ya da aktifleştirilebilir olarak gmagick
veya imagick
kütüphanesi sunulmakta. gmagick
aslında imagick
modülünün performans ve kararlılık düzeyi artırılmış bir çatalı (fork)10. Barındırma alanında sadece bu modüllerden birini kullanabilmektesiniz. Benim tercihim genelde imagick
phpinfo()
aracılığıyla görebilirsiniz ya da PHP aracılığı ile kontrol edebiliriz. O halde yukarıdaki kodumuzu güncelleyelim.
if(extension_loaded("cwebp") || extension_loaded('gif2webp')) {
if($file_extension=="png" or $file_extension=="jpg" or $file_extension=="jpeg"){
exec("cwebp -q 80 ".escapeshellarg($settings_document_root."/".$file)." -o ".escapeshellarg($settings_document_root."/webp/".$file.".webp"));
}
if($file_extension=="gif"){
exec("gif2webp -q 80 ".escapeshellarg($settings_document_root."/".$file)." -o ".escapeshellarg($settings_document_root."/webp/".$file.".webp"));
}
} else if(extension_loaded("gmagick") || extension_loaded('imagick')) {
if($file_extension=="png" or $file_extension=="jpg" or $file_extension=="jpeg" or $file_extension=="gif"){
exec("convert ".escapeshellarg($settings_document_root."/".$file)." -quality 80 -define webp:lossless=true ".escapeshellarg($settings_document_root."/webp/".$file.".webp"));
}
} else {
die("Does not have any webp library!");
}
Görüldüğü if-else statement içerisinde extension_loaded()
fonksiyonunu kullanarak ilgili kütüphane/modülün aktif olup olmadığına bakabiliriz. Ardından, bulunan modüle göre exec()
ile komutumuzun işleme alınmasını sağlarız. Evet, güvenlik sebebiyle exec()
kullanımının da sınırlandırılmış olma ihtimaline değinmiştim. Bu duruma çözüm olarak yukarıdaki kod akışını yeniden ele alabilir ve bu amaçla PHP tarafından GD11 altında yer alan imagecreatefromjpeg
, imagecreatefromgif
, imagecreatefrompng
ve imagewebp
fonksiyonlarını kullanabiliriz.
PHP: GD ve Resim İşlevleri
PHP GD ile oldukça kapsamlı görsel işlemler gerçekleştirme imkanı sunmakta. Tüm işlevleri Resim İşleme ve Resim Üretimi > GD11 üzerinden ulaşabilirsiniz. Ben bu yazı bağlamında webp
işlemi için sunulan işlevlerden birkaçını kullanacağım. Tekrar yukarıda olduğu gibi kod içerisindeki adımları anlatmayacağım1 12. Yapıyı mümkün olduğu kadar aynı tutup sadece görsel oluşturma aşamasını değiştirdim. Kodumuzun son hali şöyle;
if(strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) !== false || strpos( $_SERVER['HTTP_USER_AGENT'], ' Chrome/') !== false) {
$settings_document_root = dirname(__FILE__);
$file = trim(strip_tags($_GET['file']));
$dir_name = "webp";
$sub_dir = dirname($file);
$sub_dir_path = $dir_name."/".$sub_dir;
if(!is_dir($dir_name)){
mkdir($dir_name);
} else {
if(!is_dir($sub_dir_path)){
mkdir($sub_dir_path);
}
}
if(!is_file($file)){
echo "original file not found";
exit;
}
function convertImageToWebP($source, $destination, $quality = 80) {
$file_extension = pathinfo($source, PATHINFO_EXTENSION);
if ($file_extension == 'jpeg' || $file_extension == 'jpg') {
$image = imagecreatefromjpeg($source);
} else if ($file_extension == 'gif') {
$image = imagecreatefromgif($source);
} else if ($file_extension == 'png') {
$image = imagecreatefrompng($source);
} else {
die("Unsupported format!");
}
return imagewebp($image, $destination, $quality);
}
convertImageToWebP($file, "webp/".$file.".webp", 80);
header("Location: /webp/".$file.".webp?".time());
exit;
}
Önceki koddan farklı olarak bu kodun ilk satırında bir if statement ile webp desteği kontrol edilmekte ve elbette exec()
fonksiyonları yer almamakta. Tüm bu işlemlerin ardından artık picture
etiketi ile görsellerimizi çağırabiliriz.
<picture>
<source srcset="/webp/images/landscape.jpg.webp" type="image/webp" />
<img src="/images/landscape.jpg" />
</picture>
Son olarak, oluşturduğunuz PHP dosyasını cronjob ile belirlediğiniz periyotlarda çalıştırılmak üzere işleme alabilirsiniz.
- GD vs ImageMagick vs Gmagick for jpg? [closed]. StackOverflow ↩ ↩
- Chris Coyier. (2017). Responsive Images in CSS ↩
- Houssein Djirdeh, Addy Osmani, Mathias Bynens. (2020). Browser-level image lazy-loading for the web ↩
- PHP Webp image generator and HTML code. The Web Help ↩ ↩
- Getting Started. WebP ↩
- chromium/webm/libwebp. Google Git ↩
- cwebp. WebP ↩
- gif2webp. WebP ↩
- exec. PHP Documentation ↩ ↩
- WebP Encoding Options – ImageMagick ↩ ↩
- php: exec ↩ ↩ ↩
- GD ve Resim İşlevleri. PHP Documentation ↩