16. Ağ işlevleri
Şimdi, PHP’in ağ işlevlerini ele alacağız; bu işlevler sayesinde TCP / IP (Aktarım Kontrol Protokolü / İnternet Protokolü) programlaması yapabiliyoruz.

16.1. İnternet programlamasının temelleri
16.1.1. Genel Bilgiler
A ve B adlı iki uzak makine arasındaki iletişimi ele alalım:

A makinesindeki bir AppA uygulaması, İnternet'teki B makinesindeki bir AppB uygulamasıyla iletişim kurmak istediğinde, birkaç şeyi bilmesi gerekir:
- B makinesinin IP (İnternet Protokolü) adresi veya adı;
- AppB uygulamasının çalıştığı bağlantı noktası numarası. Zira B makinesi, İnternet üzerinde çalışan çok sayıda uygulamayı destekleyebilir. Ağdan gelen bilgileri aldığında, bu bilgilerin hangi uygulamaya yönelik olduğunu bilmesi gerekir. B makinesindeki uygulamalar, iletişim portları olarak da adlandırılan arayüzler aracılığıyla ağa erişir. Bu bilgi, B makinesi tarafından alınan pakette bulunur ve böylece paket doğru uygulamaya iletilebilir;
- makine B tarafından anlaşılan iletişim protokolleri. Çalışmamızda yalnızca TCP-IP protokollerini kullanacağız;
- AppB uygulaması tarafından kabul edilen iletişim protokolü. Nitekim, A ve B makineleri birbirleriyle “iletişim kuracak”. Birbirlerine iletecekleri bilgiler, TCP-IP protokolleri içinde kapsüllenecektir. Bununla birlikte, zincirin sonunda AppB uygulaması, AppA uygulaması tarafından gönderilen bilgiyi aldığında, bu bilgiyi yorumlayabilmesi gerekir. Bu durum, A ve B adlı iki kişinin telefonla iletişim kurmasına benzemektedir: diyalogları telefon aracılığıyla aktarılır. Konuşma, A'nın telefonunda sinyal biçiminde kodlanır, telefon hatları üzerinden taşınır ve B'nin telefonuna ulaşarak orada kod çözülür. B o zaman konuşmayı duyar. İşte burada diyalog protokolü kavramı devreye girer: A Fransızca konuşuyorsa ve B bu dili anlamıyorsa, A ve B anlamlı bir şekilde diyalog kuramazlar;
Bu nedenle, iletişim kuran iki uygulama, hangi iletişim türünü kullanacakları konusunda anlaşmalıdır. Örneğin, ftp hizmeti ile yapılan diyalog, pop hizmeti ile yapılan diyalogla aynı değildir: bu iki hizmet aynı komutları kabul etmez. Farklı bir diyalog protokolüne sahiptirler;
16.1.2. TCP protokolünün özellikleri
Burada yalnızca TCP aktarım protokolünü kullanan ağ iletişimlerini inceleyeceğiz; bu protokolün başlıca özellikleri şunlardır:
- Gönderim yapmak isteyen işlem, öncelikle göndereceği bilgilerin alıcı işlemiyle bir bağlantı kurar. Bu bağlantı, gönderen makinenin bir bağlantı noktası ile alıcı makinenin bir bağlantı noktası arasında kurulur. İki bağlantı noktası arasında böylece sanal bir yol oluşturulur ve bu yol, bağlantıyı kuran sadece bu iki işleme ayrılır;
- kaynak işlem tarafından gönderilen tüm paketler bu sanal yolu takip eder ve gönderildikleri sırayla ulaşır;
- gönderilen bilgi sürekli bir yapıya sahiptir. Gönderen süreç, kendi hızında bilgi gönderir. Bu bilgiler mutlaka hemen gönderilmez: TCP protokolü, gönderilmek üzere yeterli miktarda bilgi birikene kadar bekler. Bu bilgiler, TCP segmenti adı verilen bir yapıda depolanır. Bu segment dolduğunda, IP katmanına iletilir ve burada bir IP paketine kapsüllenir;
- TCP protokolüyle gönderilen her segment numaralandırılır. Alıcı TCP protokolü, segmentleri sırayla aldığını doğrular. Düzgün bir şekilde alınan her segment için, gönderene bir alım onayı gönderir;
- gönderici bunu aldığında, bunu gönderen işleme bildirir. Böylece gönderen işleme, bir segmentin başarıyla ulaştığını öğrenebilir;
- eğer belirli bir süre geçtikten sonra, bir segmenti gönderen TCP protokolü bir onay almazsa, söz konusu segmenti yeniden gönderir ve böylece bilgi aktarım hizmetinin kalitesini garanti eder;
- İletişim kuran iki işlem arasında kurulan sanal devre full-duplex'tir: bu, bilginin her iki yönde de aktarılabileceği anlamına gelir. Böylece, kaynak işlem bilgi göndermeye devam ederken bile hedef işlem onay mesajları gönderebilir. Bu, örneğin kaynak TCP protokolünün, onay mesajını beklemeden birden fazla segment göndermesine olanak tanır. Belirli bir süre sonra, n numaralı bir segmentin onayını almadığını fark ederse, segment gönderimini bu noktadan itibaren yeniden başlatır;
16.1.3. İstemci-sunucu ilişkisi
Çoğu zaman, İnternet üzerindeki iletişim asimetriktir: A makinesi, B makinesinden bir hizmet talep etmek üzere bir bağlantı başlatır; B makinesinin SB1 hizmetiyle bir bağlantı kurmak istediğini belirtir. B makinesi bunu kabul eder veya reddeder. Kabul ederse, A makinesi isteklerini SB1 hizmetine gönderebilir. Bu istekler, SB1 hizmeti tarafından anlaşılan iletişim protokolüne uymak zorundadır. Böylece, istemci makine olarak adlandırılan A makinesi ile sunucu makine olarak adlandırılan B makinesi arasında bir istek-yanıt diyaloğu kurulur. İki tarafın biri bağlantıyı sonlandıracaktır.
16.1.4. Bir istemcinin mimarisi
Bir sunucu uygulamasının hizmetlerini talep eden bir ağ programının mimarisi şu şekilde olacaktır:
ouvrir la connexion avec le service SB1 de la machine B
si réussite alors
tant que ce n'est pas fini
préparer une demande
l'émettre vers la machine B
attendre et récupérer la réponse
la traiter
fin tant que
finsi
fermer la connexion
16.1.5. Bir sunucunun mimarisi
Hizmet sunan bir programın mimarisi şu şekilde olacaktır:
ouvrir le service sur la machine locale
tant que le service est ouvert
se mettre à l'écoute des demandes de connexion sur un port dit port d'écoute
lorsqu'il y a une demande, la faire traiter par une autre tâche sur un autre port dit port de service
fin tant que
Sunucu programı, bir istemcinin ilk bağlantı talebini, hizmet almak amacıyla yaptığı sonraki taleplerinden farklı şekilde işler. Program, hizmeti kendisi sağlamaz. Eğer bunu yapsaydı, hizmet süresince bağlantı taleplerini dinleyemezdi ve istemcilere hizmet verilemezdi. Bu nedenle farklı bir yol izler: Dinleme bağlantı noktasında bir bağlantı isteği alındığında ve kabul edildiğinde, sunucu, istemcinin talep ettiği hizmeti sunmakla görevli bir görev oluşturur. Bu hizmet, sunucu makinesinin hizmet bağlantı noktası olarak adlandırılan başka bir bağlantı noktası üzerinden sunulur. Böylelikle aynı anda birden fazla istemciye hizmet verilebilir.
Bir hizmet görevi aşağıdaki yapıya sahip olacaktır:
tant que le service n'a pas été rendu totalement
attendre une demande sur le port de service
lorsqu'il y en a une, élaborer la réponse
transmettre la réponse via le port de service
fin tant que
libérer le port de service
16.2. İnternet iletişim protokollerini keşfetmek
16.2.1. Giriş
Bir istemci bir sunucuya bağlandığında, aralarında bir iletişim kurulur. Bu iletişimin niteliği, sunucu iletişim protokolü olarak adlandırılır. İnternette en yaygın kullanılan protokoller arasında şunlar yer alır:
- HTTP: HyperText Transfer Protokolü - bir web sunucusuyla (HTTP sunucusu) iletişim kurmak için kullanılan protokol;
- SMTP: Basit Posta Aktarım Protokolü (Simple Mail Transfer Protocol) – e-posta gönderme sunucusuyla (SMTP sunucusu) iletişim kurmak için kullanılan protokol;
- POP: Post Office Protocol (POST) - e-posta depolama sunucusuyla iletişim protokolü (POP sunucusu). Bu protokol, e-postaları göndermek yerine alınan e-postaları almak içindir;
- IMAP: Internet Message Access Protocol (İnternet Mesaj Erişim Protokolü) – bir e-posta depolama sunucusuyla iletişim kurmak için kullanılan protokol (IMAP sunucusu). Bu protokol, daha eski olan POP protokolünün yerini kademeli olarak almıştır;
- FTP: Dosya Aktarım Protokolü (File Transfer Protocol) – bir dosya depolama sunucusuyla (FTP sunucusu) iletişim kurmak için kullanılan protokol;
Tüm bu protokoller, metin satırlı protokoller olma özelliğine sahiptir: istemci ve sunucu birbirleriyle metin satırları alışverişinde bulunur. Eğer şu işlemleri yapabilen bir istemci varsa:
- TCP sunucusuyla bağlantı kurabilen;
- sunucunun gönderdiği metin satırlarını konsolda görüntülemek;
- kullanıcının klavyeden gireceği metin satırlarını sunucuya göndermek;
böylece, bu protokolün kurallarını bildiğimiz sürece, metin satırları protokolüne sahip bir TCP sunucusuyla iletişim kurabiliriz.
16.2.2. TCP Yardımcı Programları

Bu belgeyle ilişkili kodlarda, iki adet TCP iletişim yardımcı programı bulunur:
- [RawTcpClient], bir S sunucusunun P bağlantı noktasına bağlanmayı sağlar;
- [RawTcpServer], P numaralı bağlantı noktasında istemcileri bekleyen bir sunucu oluşturulmasını sağlar;
TCP ve [RawTcpServer] sunucuları[RawTcpServeur port] sözdizimiyle çağrılır ve yerel makinenin (çalıştığınız bilgisayarın) [port] bağlantı noktasında TCP hizmeti oluşturur:
- sunucu aynı anda birden fazla istemciye hizmet verebilir;
- sunucu, kullanıcının klavyeden girdiği komutları yürütür. Bu komutlar şunlardır:
- list: Sunucuya şu anda bağlı olan istemcileri listeler. Bunlar [id=x-nom=y] biçiminde görüntülenir. [id] alanı, istemcileri tanımlamak için kullanılır;
- send x [texte]: x numaralı (id=x) müşteriye metin gönderir. Köşeli parantezler [] gönderilmez. Bunlar komutta gereklidir. Müşteriye gönderilen metni görsel olarak sınırlandırmaya yararlar;
- close x: x numaralı müşteriyle olan bağlantıyı kapatır;
- quit: tüm bağlantıları kapatır ve hizmeti durdurur;
- müşteri tarafından sunucuya gönderilen satırlar konsolda görüntülenir;
- tüm iletişim, [machine-portService.txt] adlı bir metin dosyasına kaydedilir; burada
- [machine], kodun çalıştığı makinenin adıdır;
- [port], istemcinin isteklerine yanıt veren hizmet bağlantı noktasıdır;
TCP [RawTcpClient] istemcisi, [RawTcpClient serveur port] sözdizimiyle çağrılarak [serveur] sunucusunun [port] bağlantı noktasına bağlanır:
- kullanıcının klavyeden girdiği satırlar sunucuya gönderilir;
- sunucu tarafından gönderilen satırlar konsolda görüntülenir;
- tüm iletişim, [serveur-port.txt] adlı bir metin dosyasına kaydedilir;
Bir örnek inceleyelim. İki Windows komut penceresi açıyoruz ve her ikisinde de yardımcı programlar klasörüne gidiyoruz. Pencerelerden birinde, 100 numaralı bağlantı noktasında [RawTcpServer] sunucusunu başlatıyoruz:

- [1]'te, yardımcı programlar klasöründeyiz;
- [2]'te, TCP sunucusunu 100 numaralı bağlantı noktasında başlatıyoruz;
- [3] komutunda, sunucu TCP istemcisini beklemeye geçer;
- [4] komutunda, sunucu kullanıcının klavyeden gireceği bir komutu bekler;
Diğer komut penceresinde, TCP istemcisi başlatılır:

- [5]'te, yardımcı programlar klasörüne yönlendiriliyoruz;
- [6] komutunda, TCP istemcisini başlatıyoruz: ona yerel makinenin (çalıştığınız makine) 100 numaralı bağlantı noktasına bağlanmasını söylüyoruz;
- [7]'te, istemci sunucuya bağlanmayı başarmıştır. İstemcinin bilgileri belirtilir: [DESKTOP-528I5CU] makinesindedir (bu örnekte yerel makine) ve sunucuyla iletişim kurmak için [50405] bağlantı noktasını kullanır:
- [8]'te, istemci kullanıcının klavyeden gireceği bir komutu bekliyor;
Sunucu penceresine geri dönelim. İçeriği değişmiştir:

- [9] olarak, bir istemci algılandı. Sunucu ona 1 numarasını verdi. Sunucu, uzak istemciyi (makine ve bağlantı noktası) doğru bir şekilde tanımladı;
- [10]'te, sunucu yeni bir istemci bekleme moduna geri dönüyor;
İstemci penceresine geri dönelim ve sunucuya bir komut gönderelim:

- [11]'te, sunucuya gönderilen komut;
Sunucu penceresine geri dönelim. İçeriği değişmiştir:

- [12], köşeli parantez içinde, sunucu tarafından alınan mesaj;
İstemciye bir yanıt gönderelim:

- [13] olarak, istemciye gönderilen yanıt 1. Yalnızca köşeli parantezler arasındaki metin gönderilir, köşeli parantezlerin kendisi gönderilmez;
Müşteri penceresine geri dönelim:

- [14] olarak, müşteri tarafından alınan yanıt. Alınan metin köşeli parantezlerin içindeki metindir;
Diğer komutları görmek için sunucu penceresine geri dönelim:

- [15], istemci listesini istiyoruz;
- [16], yanıt;
- [17] ile 1 numaralı müşteri ile bağlantıyı kesiyoruz;
- [18]'te sunucudan onay alıyoruz;
- [19]'te, sunucuyu kapatıyoruz;
- [20]'te, sunucudan gelen onay;
İstemci penceresine geri dönelim:

- [21]'te, istemci hizmetin sona erdiğini algıladı;
İki günlük dosyası oluşturuldu; biri sunucu, diğeri istemci için:

- [25], sunucu günlükleri: dosya adı, istemcinin adı olan [machine-port]'tir;
- [26], istemci günlükleri: dosya adı, sunucunun adıdır ([machine-port]);
Sunucu günlükleri şunlardır:
İstemci günlükleri şunlardır:
16.3. İnternetteki bir makinenin adını veya adresini (IP) elde etme

İnternetteki makineler bir adresle (IP, IPv4 veya IPv6) ve çoğu zaman bir adla tanımlanır. Ancak sonuçta yalnızca adres kullanılır. Bu nedenle, bazen adıyla tanımlanan bir makinenin IP adresini bilmek gerekebilir.
[ip-01.php] komut dosyası şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyulması
declare (strict_types=1);
//
// hata yönetimi
error_reporting(E_ALL & E_STRICT);
ini_set("display_errors", "on");
//
// sabitler
$HOTES = array("istia.univ-angers.fr", "www.univ-angers.fr", "www.ibm.com", "localhost", "", "xx");
// IP adresleri ve $HOTES makinelerinin adları
for ($i = 0; $i < count($HOTES); $i++) {
getIPandName($HOTES[$i]);
}
// son
print "Terminé\n";
exit;
//------------------------------------------------
function getIPandName(string $nomMachine): void {
//$nomMachine: adresini almak istediğimiz makinenin adı IP
//
// nomMachine-->IP adresi
$ip = gethostbyname($nomMachine);
print "---------------\n";
if ($ip !== $nomMachine) {
print "ip[$nomMachine]=$ip\n";
// adres IP --> nomMachine
$name = gethostbyaddr($ip);
if ($name !== $ip) {
print "name[$ip]=$name\n";
} else {
print "Erreur, machine[$ip] non trouvée\n";
}
} else {
print "Erreur, machine[$nomMachine] non trouvée\n";
}
}
Açıklamalar
- 7-8. satırlar: PHP'in tüm hataları (E_ALL ve E_STRICT) bildirmesi ve bunların görüntülenmesi istenir. Bu mod, PHP uyarılarıyla kodu iyileştirmek amacıyla yalnızca geliştirme modunda önerilir. Üretim modunda, 8. satırda "off" yazılmalıdır. PHP 5.4 sürümünden itibaren, E_STRICT seviyesi E_ALL'e dahil edilmiştir;
- 11. satır: Adı ve IP adresi istenen makinelerin listesi;
PHP ağ işlevleri, 21. satırdaki getIpandName işlevinde kullanılmaktadır.
- 25. satır: gethostbyname($nom) işlevi, $nom adlı makinenin "ip3.ip2.ip1.ip0" adresini elde etmeyi sağlar. $nom adlı makine mevcut değilse, işlev $nom sonucunu döndürür;
- 30. satır: gethostbyaddr($ip) işlevi, $ip adresine sahip makinenin adını "ip3.ip2.ip1.ip0" biçiminde elde etmeyi sağlar. $ip adlı makine mevcut değilse, işlev $ip sonucunu döndürür;
Sonuçlar:
---------------
ip[istia.univ-angers.fr]=193.49.144.41
name[193.49.144.41]=ametys-fo-2.univ-angers.fr
---------------
ip[www.univ-angers.fr]=193.49.144.41
name[193.49.144.41]=ametys-fo-2.univ-angers.fr
---------------
ip[www.ibm.com]=2.18.220.211
name[2.18.220.211]=a2-18-220-211.deploy.static.akamaitechnologies.com
---------------
ip[localhost]=127.0.0.1
name[127.0.0.1]=DESKTOP-528I5CU
---------------
ip[]=192.168.1.38
name[192.168.1.38]=DESKTOP-528I5CU.home
---------------
Erreur, machine[xx] non trouvée
Terminé
16.4. HTTP protokolü (HyperText Aktarım Protokolü)
16.4.1. Örnek 1

Bir tarayıcı bir URL görüntülediğinde, o tarayıcı bir web sunucusunun, başka bir deyişle bir HTTP sunucusunun istemcisidir. İnisiyatif tarayıcıdadır ve tarayıcı, sunucuya bir dizi komut göndererek başlar. Bu ilk örnekte:
- sunucu, [RawTcpServer] yardımcı programı olacaktır;
- istemci ise bir tarayıcı olacaktır;
Öncelikle sunucuyu 100 numaralı bağlantı noktasında başlatıyoruz:

Ardından bir tarayıcıyla URL [localhost:100]'i talep ediyoruz, yani sorgulanan HTTP sunucusunun yerel makinenin 100 numaralı bağlantı noktasında çalıştığını belirtiyoruz:

Sunucu penceresine geri dönelim:

- [3]'te, bağlanan istemci;
- [4-7], onun gönderdiği metin satırları dizisi:
- [4]: Bu satırın biçimi [GET URL HTTP/1.1]'tir. URL / komutunu ister ve sunucudan HTTP 1.1 protokolünü kullanmasını talep eder;
- [5]'te: bu satırın biçimi [Host: serveur:port]'tir. [Host] komutundaki büyük/küçük harf ayrımı önemli değildir. Burada, istemcinin 100 numaralı bağlantı noktasında çalışan yerel bir sunucuya sorgu gönderdiğini hatırlatmak isteriz;
- [User-Agent] komutu, istemcinin kimliğini belirtir;
- [Accept] komutu, istemci tarafından hangi belge türlerinin kabul edildiğini belirtir;
- [Accept-Language] komutu, istenen belgelerin birden fazla dilde mevcut olması durumunda hangi dilde olmasını istediğinizi belirtir;
- [Connection] komutu, istenen bağlantı modunu belirtir: [keep-alive], veri alışverişi tamamlanana kadar bağlantının sürdürülmesi gerektiğini belirtir;
- [7] komutunda: müşteri komutlarını boş bir satırla sonlandırır;
Sunucuyu kapatarak bağlantıyı sonlandırıyoruz:

16.4.2. Örnek 2
Artık bir tarayıcının URL'i talep etmek için gönderdiği komutları bildiğimize göre, URL'i TCP [RawTcpClient] istemcimizle talep edeceğiz. Laragon’daki Apache sunucusu, web sunucumuz olacak.
Laragon’u ve ardından Apache web sunucusunu başlatalım:


Şimdi bir tarayıcı kullanarak URL ve [http://localhost:80] adreslerini ziyaret edelim. Burada sadece [localhost:80] sunucusunu belirtmişiz, herhangi bir URL belgeyi belirtmiyoruz. Bu durumda, URL / adresi, yani web sunucusunun kök dizini istenmektedir:

- [1]'e, yani istenen URL'e dönüştürülür. Başlangıçta [http://localhost:80] yazılmıştı ve tarayıcı (burada Firefox) bunu basitçe [localhost]'e dönüştürdü; çünkü protokol belirtilmediğinde [http] protokolü varsayılır ve bağlantı noktası belirtilmediğinde [80] bağlantı noktası varsayılır;
- [2]'te, sorgulanan web sunucusunun / kök sayfası;
Şimdi, tarayıcı tarafından alınan metni inceleyelim:

- Alınan sayfaya sağ tıklayıp [2] seçeneğini seçin. Aşağıdaki kaynak kodu elde edilir:
Şimdi URL ve [http://localhost:80] dosyalarını TCP istemcimizle isteyelim:
![]()
- [1]'te, localhost sunucusunun 80 numaralı bağlantı noktasına bağlanıyoruz. Laragon'un web sunucusu burada çalışıyor;
Şimdi önceki paragrafta öğrendiğimiz komutları giriyoruz:

- [1] adresinde, [GET] komutunu giriyoruz. Web sunucusunun / kök dizinini istiyoruz;
- [2] komutunu, [Host] komutunu;
- bunlar gerekli olan tek iki komuttur. Diğer komutlar için web sunucusu varsayılan değerleri kullanacaktır;
- [3]'te, istemci komutlarını sonlandırması gereken boş satır;
- 3. satırın altında, web sunucusunun yanıtı gelir;
- [4]'ten boş satır [5]'e kadar olan kısımda, sunucu yanıtının başlıkları yer alır;
- [5] satırından sonra, [6] ile istenen HTML belgesi gelir;
İstemciyi sonlandırmak için [quit] komutunu giriyoruz ve [localhost-80.txt] günlük dosyasını yüklüyoruz:
- 11-79. satırlar: HTML belgesi alındı. Önceki örnekte, Firefox da aynısını almıştı;
Artık bir TCP istemcisi programlamak için temel bilgilere sahibiz; bu istemci bir URL isteğinde bulunacaktır.
16.4.3. Örnek 3

[http-01.php] betiği, jSON dosyası tarafından yapılandırılmış bir HTTP istemcisidir. Bu dosyanın içeriği şöyledir:
- 2. satır: erişilecek web sunucusunu barındıran makinenin adı;
- 3. satır: Bu web sunucusunun çalıştığı bağlantı noktası;
- 4. satır: istenen belgenin URL numarası;
- 5. satır: hedef makine, "makine:bağlantı noktası" biçiminde;
- 6. satır: HTTP istemci kimliği: buraya istediğiniz herhangi bir değer girebilirsiniz;
- 7. satır: istemci tarafından kabul edilen belge türü; burada metin HTML;
- 8. satır: istenen belge için tercih edilen dil;
- 9. satır: istemci tarafından gönderilen komutlar için satır sonu işareti: sunucunun Unix (\n) veya Windows (\r\n) sisteminde olmasına göre bu işaret farklılık gösterebilir;
[http-01.php] komut dosyası şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulması
declare (strict_types=1);
//
// hata yönetimi
// error_reporting(E_ALL & E_STRICT);
// ini_set("display_errors", "on");
//
// sabitler
const CONFIG_FILE_NAME = "config-http-01.json";
//
// yapılandırma alınır
$config = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// yapılandırma dosyasındaki URL metnini HTML olarak alma
foreach ($config as $site => $protocole) {
// $ite sitesinin ana sayfasını okuma
$résultat = getURL($site, $protocole);
// sonucun görüntülenmesi
print "$résultat\n";
}//için
// son
exit;
//-----------------------------------------------------------------------
function getURL(string $site, array $protocole, $suivi = TRUE): string {
// $site["GET"] dosyasını okur ve $site.HTML dosyasına kaydeder
// istemci/sunucu iletişimi $protocole protokolüne göre gerçekleşir
//
// $site bağlantı noktasında bir bağlantı açılır
$erreurNumber = 0;
$erreur = "";
$connexion = fsockopen($site, $protocole["port"], $erreurNumber, $erreur);
// hata durumunda geri dönüş
if ($connexion === FALSE) {
return "Echec de la connexion au site (" . $site . " ," . $protocole["port"] . " : $erreur";
}
// $connexion, iki yönlü bir iletişim akışını temsil eder
// istemci (bu program) ile bağlanılan web sunucusu arasında
// bu kanal, komut ve bilgi alışverişi için kullanılır
// iletişim protokolü HTTP'tir
//
// $site.HTML dosyasının oluşturulması
$HTML = fopen("output/$site.HTML", "w");
if ($HTML === FALSE) {
// istemci/sunucu bağlantısı kapatılıyor
fclose($connexion);
// hata geri bildirimi
return "Erreur lors de la création du fichier $site.HTML";
}
// istemci, sunucuyla HTTP diyaloğunu başlatacak
if ($suivi) {
print "Client : début de la communication avec le serveur [$site] ----------------------------\n";
}
// sunuculara bağlı olarak, istemci satırları \n veya \r\n ile bitmelidir
$endOfLine = $protocole["endOfLine"];
// basitleştirme amacıyla, istemci/sunucu iletişimi sırasında hata durumları test edilmez
// İstemci, $protocole["GET"] protokolünü talep etmek için GET komutunu gönderir
// GET URL HTTP/1.1 sözdizimi
$commande = "GET " . $protocole["GET"] . " HTTP/1.1$endOfLine";
// takip?
if ($suivi) {
print "--> $commande";
}
// komut sunucuya gönderiliyor
fputs($connexion, $commande);
// diğer başlıkların gönderilmesi HTTP
foreach ($protocole as $verb => $value) {
if ($verb !== "GET" && $verb != "port"" && $verb !="endOfLine") {
// komut oluşturuluyor
$commande = "$verb: $value$endOfLine";
// takip?
if ($suivi) {
print "--> $commande";
}
// komut sunucuya gönderilir
fputs($connexion, $commande);
}
}
// HTTP protokolünün başlıkları (headers) boş bir satırla bitmelidir
fputs($connexion, $endOfLine);
//
// sunucu şimdi $connexion kanalından yanıt verecek. Tüm
// verilerini gönderecek ve ardından kanalı kapatacaktır. Dolayısıyla istemci, $connexion
// kanal kapanana kadar gelen tüm verileri okur
//
// öncelikle sunucu tarafından gönderilen başlıklar okunur HTTP
// bunlar da boş bir satırla sona erer
if ($suivi) {
print "Réponse du serveur [$site] ----------------------------\n";
}
$fini = FALSE;
while (!$fini && $ligne = fgets($connexion, 1000)) {
// boş bir satır var mı?
$champs = [];
preg_match("/^(.*?)\s+$/", $ligne, $champs);
if ($champs[1] !== "") {
if ($suivi) {
// HTTP başlığını görüntülüyoruz
print "<-- " . $champs[1] . "\n";
}
} else {
// bu boş satırdı - HTTP başlıkları sona erdi
$fini = TRUE;
}
}
// boş satırın ardından gelecek olan HTML belgesini okuyoruz
while ($ligne = fgets($connexion, 1000)) {
// satır, sitedeki HTML dosyasına kaydediliyor
fputs($HTML, $ligne);
}
// sunucu bağlantıyı kapattı - istemci de bağlantıyı kapattı
fclose($connexion);
// $HTML dosyasının kapatılması
fclose($HTML);
// geri dönüş
return "Fin de la communication avec le site [$site]. Vérifiez le fichier [$site.HTML]";
}
Kod açıklamaları:
- 14. satır: Yapılandırma dosyası, bir sözlük oluşturmak için kullanılır:
- sözlüğün anahtarları, sorgulanacak web sunucularıdır;
- değerler ise uyulması gereken HTTP protokolünü belirler;
- 16-21. satırlar: yapılandırmadaki web sunucuları listesi üzerinde döngü yapılır;
- 26. satır: getURL işlevi($site,$protocole,$suivi) işlevi, $site web sitesinden bir belge ister ve bunu $site metin dosyasına kaydeder.HTML.Par varsayılan olarak, istemci/sunucu arasındaki iletişim konsola kaydedilir ($suivi=TRUE);
- 33. satır: fsockopen($site,$port,$errNumber,$erreur) işlevi, $site makinesinin $port bağlantı noktasında çalışan TCP / IP hizmetiyle bir bağlantı kurulmasını sağlar. Bağlantı başarısız olursa, [$errNumber] bir hata numarasıdır ve [$erreur] ise ilgili hata mesajıdır. İstemci/sunucu bağlantısı kurulduktan sonra, birçok TCP / IP hizmeti metin satırları alışverişinde bulunur. Burada söz konusu olan, HTTP protokolüdür (HyperText Aktarım Protokolü). Sunucudan istemciye gelen veri akışı, [fgets] ile okunan bir metin dosyası olarak işlenebilir. Aynı durum, istemciden sunucuya giden veri akışı için de geçerlidir; bu akış, [fputs] ile yazılabilir;
- 44-50. satırlar: Alınan HTML belgesinin depolanacağı [$site.HTML] dosyasının oluşturulması;
- 60. satır: Müşterinin ilk komutu [GET URL HTTP/1.1] komutu olmalıdır;
- 66. satır: fputs işlevi, müşterinin sunucuya veri göndermesini sağlar. Burada gönderilen metin satırının anlamı şöyledir: "Bağlı olduğum web sitesinin [URL] sayfasını (GET) istiyorum. HTTP protokolünün 1.1 sürümüyle çalışıyorum";
- 68-79. satırlar: HTTP [Host, User-Agent, Accept, Accept-Language] protokolünün diğer satırları gönderilir. Sıraları önemli değildir;
- 81. satır: İstemcinin HTTP başlıklarını göndermeyi tamamladığını ve artık istenen belgeyi beklediğini belirtmek için sunucuya boş bir satır gönderilir;
- 92-106. satırlar: Sunucu öncelikle, istenen belge hakkında çeşitli bilgiler veren bir dizi HTTP başlığı gönderecektir. Bu başlıklar boş bir satırla sona erer;
- 93. satır: PHP [fgets] işleviyle sunucu tarafından gönderilen bir satır okunur;
- 96. satır: satır sonundaki boşluklar (boşluk karakterleri, satır sonu işaretleri) hariç olmak üzere satır gövdesi alınır;
- satır 97: Sunucu tarafından gönderilen HTTP başlıklarının sonunu belirten boş satırın alınmış olup olmadığına bakılır;
- 98-101. satırlar: [suivi] modundaysak, alınan HTTP başlığı konsola görüntülenir;
- satır 108-111: Sunucunun yanıtındaki metin satırları, bir döngü kullanılarak satır satır okunabilir ve while metin dosyasına kaydedilebilir. Web sunucusu, talep edilen sayfanın tamamını gönderdiğinde, istemciyle olan bağlantısını kapatır. İstemci tarafında bu durum, dosya sonu olarak algılanır;
Sonuçlar:
Konsol aşağıdaki günlükleri görüntüler:
Client : début de la communication avec le serveur [localhost] ----------------------------
--> GET / HTTP/1.1
--> Host: localhost:80
--> User-Agent: client PHP
--> Accept: text/HTML
--> Accept-Language: fr
Réponse du serveur [localhost] ----------------------------
<-- HTTP/1.1 200 OK
<-- Date: Thu, 16 May 2019 15:43:18 GMT
<-- Server: Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11
<-- X-Powered-By: PHP/7.2.11
<-- Content-Length: 1781
<-- Content-Type: text/HTML; charset=UTF-8
Fin de la communication avec le site [localhost]. Vérifiez le fichier [localhost.HTML]
Örneğimizde, alınan [output/localhost.HTML] dosyası şöyledir:
<!DOCTYPE HTML>
<HTML>
<head>
<title>Laragon</title>
<link href="https://fonts.googleapis.com/css?family=Karla:400" rel="stylesheet" type="text/css">
<style>
HTML, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
display: table;
font-weight: 100;
font-family: 'Karla';
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 96px;
}
.opt {
margin-top: 30px;
}
.opt a {
text-decoration: none;
font-size: 150%;
}
a:hover {
color: red;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title" title="Laragon">Laragon</div>
<div class="info"><br />
Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11<br />
PHP version: 7.2.11 <span><a title="phpinfo()" href="/?q=info">info</a></span><br />
Document Root: C:/myprograms/laragon-lite/www<br />
</div>
<div class="opt">
<div><a title="Getting Started" href="https://laragon.org/docs">Getting Started</a></div>
</div>
</div>
</div>
</body>
</HTML>
Firefox tarayıcısıyla elde ettiğimizle aynı belgeyi aldık.
16.4.4. Örnek 4
Bu örnekte, yazdığımız HTTP istemcisinin yetersiz olduğunu göstereceğiz. [config-http-01.json] yapılandırma dosyasını şu şekilde değiştirelim:
Burada, URL yerine [http://tahe.developpez.com:443/]'i talep edeceğiz. [tahe.developpez.com] makinesinin 443 numaralı bağlantı noktası, https olarak adlandırılan güvenli http protokolü için kullanılan bir bağlantı noktasıdır. Bu protokolde, istemci/sunucu iletişimi, bağlantıyı güvenli hale getirecek bir bilgi alışverişiyle başlar. Bu durumda istemci, [HTTP] protokolünü değil, [HTTPS] protokolünü kullanmalıdır; ancak istemcimiz bunu yapmamaktadır.
Bu yapılandırma dosyasıyla konsol çıktısı şu şekildedir:
Client : début de la communication avec le serveur [tahe.developpez.com] ----------------------------
--> GET / HTTP/1.1
--> Host: sergetahe.com:443
--> User-Agent: script PHP 7
--> Accept: text/HTML
--> Accept-Language: fr
Réponse du serveur [tahe.developpez.com] ----------------------------
<-- HTTP/1.1 400 Bad Request
<-- Date: Fri, 17 May 2019 13:02:26 GMT
<-- Server: Apache/2.4.25 (Debian)
<-- Content-Length: 454
<-- Connection: close
<-- Content-Type: text/HTML; charset=iso-8859-1
Fin de la communication avec le site [tahe.developpez.com]. Vérifiez le fichier [output/tahe.developpez.com.HTML]
- 8. satır: [tahe.developpez.com] sunucusu, müşterinin isteğinin hatalı olduğunu bildirdi;
[output/tahe.developpez.com.HTML] dosyasının içeriği ise şu şekildedir:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
Reason: You're speaking plain HTTP to an SSL-enabled server port.<br />
Instead use the HTTPS scheme to access this URL, please.<br />
</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at 2eurocents.developpez.com Port 443</address>
</body></HTML>
Sunucu, doğru protokolü kullanmadığımızı açıkça belirtmektedir.
Şimdi şu yapılandırma dosyasını kullanalım:
Konsol çıktıları şu şekildedir:
Client : début de la communication avec le serveur [sergetahe.com] ----------------------------
--> GET /cours-tutoriels-de-programmation/ HTTP/1.1
--> Host: sergetahe.com:80
--> User-Agent: script PHP 7
--> Accept: text/HTML
--> Accept-Language: fr
Réponse du serveur [sergetahe.com] ----------------------------
<-- HTTP/1.1 200 OK
<-- Date: Fri, 17 May 2019 13:36:06 GMT
<-- Content-Type: text/HTML; charset=UTF-8
<-- Transfer-Encoding: chunked
<-- Server: Apache
<-- X-Powered-By: PHP/7.0
<-- Vary: Accept-Encoding
<-- Set-Cookie: SERVERID68971=2621207|XN64y|XN64y; path=/
<-- Cache-control: private
<-- X-IPLB-Instance: 17106
Fin de la communication avec le site [sergetahe.com]. Vérifiez le fichier [output/sergetahe.com.HTML]
- 11. satır, sunucunun belgeyi parçalar halinde gönderdiğini gösteriyor;
Bu durum, müşteriye gönderilen akışta sayıların bulunmasıyla ortaya çıkar: her sayı, müşteriye sunucu tarafından gönderilecek bir sonraki parçanın karakter sayısını belirtir. [output/sergetahe.com.HTML] dosyasında durum şu şekildedir:

- [1] ve [2] dosyalarında, belgenin 1. ve 2. parçalarının onaltılık boyutları;
Düzgün çalışan bir HTTP istemcisi, bu sayıları nihai HTML belgesinde bırakmamalıdır.
İşte başka bir örnek:
Bu örnek öncekine benziyor, ancak 4. satırda istenen URL'in sonu / karakteriyle bitmiyor. Bunlar aynı URL'ler değil. HTTP istemcisinin çalıştırılması sonucunda konsolda şu çıktılar elde edilir:
Client : début de la communication avec le serveur [sergetahe.com] ----------------------------
--> GET /cours-tutoriels-de-programmation HTTP/1.1
--> Host: sergetahe.com:80
--> User-Agent: script PHP 7
--> Accept: text/HTML
--> Accept-Language: fr
Réponse du serveur [sergetahe.com] ----------------------------
<-- HTTP/1.1 301 Moved Permanently
<-- Date: Fri, 17 May 2019 13:47:00 GMT
<-- Content-Type: text/HTML; charset=iso-8859-1
<-- Content-Length: 262
<-- Server: Apache
<-- Location: http://sergetahe.com:80/programlama-dersleri-ve-öğreticiler/
<-- Set-Cookie: SERVERID68971=2621207|XN67V|XN67V; path=/
<-- Cache-control: private
<-- X-IPLB-Instance: 17095
Fin de la communication avec le site [sergetahe.com]. Vérifiez le fichier [output/sergetahe.com.HTML]
- 8. satır, istenen belgenin URL olarak değiştiğini gösterir. Yeni URL, 13. satırda verilmiştir. Bu sefer, yeni URL'i sonlandıran / karakterine dikkat edin;
[output/serge.tahe.com.HTML] dosyası şu şekildedir:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<HTML><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://sergetahe.com/cours-tutoriels-de-programmation/">here</a>.</p>
</body></HTML>
HTTP numaralı bir müşteri, yönlendirmeleri takip edebilmelidir. Burada, sistem otomatik olarak yeni URL ve [http://sergetahe.com/cours-tutoriels-de-programmation/] numaralarını yeniden istemelidir.
16.4.5. Örnek 5
Önceki örnekler, HTTP istemcimizin yetersiz olduğunu göstermiştir. Şimdi, bahsedilen zorlukları (https protokolü, parçalar halinde gönderilen belge, yönlendirmeler vb.) yöneterek web belgelerini alabilen [curl] adlı bir aracı tanıtacağız: [curl] aracı Laragon ile kurulmuştur:

Bir Laragon [1] terminali açalım:

Terminalde şu komutu yazalım:

- [1], konsol türü;
- [2], geçerli klasör. Bu klasör özel bir öneme sahiptir: Laragon’un Apache sunucusu, kendisinden istenen belgeleri buradan alır. Bu nedenle bu klasörü gereksiz dosyalarla doldurmamaya özen gösterin;
- [3], girilen komut;
[curl --help] komutu bir hata verebilir. Bunun en olası nedeni, doğru terminal türünü kullanmamanızdır. Bu durumda, [4-6] komutuyla başka bir terminal açın;
[curl --help] komutu, [curl]'in tüm yapılandırma seçeneklerini görüntüler. Bunların sayısı onlarcadır. Bunlardan çok azını kullanacağız. URL dosyasını istemek için [curl URL] komutunu yazmanız yeterlidir. Bu komut, istenen belgeyi konsolda görüntüler. Ayrıca, istemci ile sunucu arasındaki HTTP veri alışverişini görmek istersek, [curl --verbose URL] komutunu yazacağız. Son olarak, istenen HTML belgesini bir dosyaya kaydetmek için [curl --verbose --output fichier URL] komutunu yazacağız.
Laragon'daki [www] klasörünü doldurmamak için, dosya sisteminin başka bir konumuna geçelim:

- [1] dosyasına geldiğimizde, [c:\temp] klasörüne geçin. Bu klasör yoksa, yeni bir klasör oluşturabilir veya başka bir klasör seçebilirsiniz;
- [2]'te, [curl] adlı bir klasör oluşturuyoruz;
- [3]'te, bu klasörün üzerine gelin;
- [4]'te, içeriğini listeleyin. Boş olacaktır;
Laragon'daki Apache sunucusunun çalıştığından emin olun ve [curl] komutuyla URL ve [http://localhost/] dizinlerini [curl –verbose –output localhost.HTML http://localhost/] komutuyla sorgulayın. Aşağıdaki sonuçlar elde edilir:
c:\Temp\curl
λ curl --verbose --output localhost.HTML http://localhost/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying ::1…
* TCP_NODELAY set
* Connected to localhost (::1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.63.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 17 May 2019 14:32:47 GMT
< Server: Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11
< X-Powered-By: PHP/7.2.11
< Content-Length: 1781
< Content-Type: text/HTML; charset=UTF-8
<
{ [1781 bytes data]
100 1781 100 1781 0 0 14248 0 --:--:-- --:--:-- --:--:-- 14248
* Connection #0, localhost ana bilgisayarı olduğu gibi bırakıldı
- 8-12. satırlar: [curl] tarafından [localhost] sunucusuna gönderilen satırlar. HTTP protokolü tanınmaktadır;
- 13-19. satırlar: sunucu tarafından yanıt olarak gönderilen satırlar;
- 13. satır: istenen belgenin başarıyla alındığını gösterir;
[localhost.HTML] dosyası istenen belgeyi içerir. Dosyayı bir metin düzenleyicisine yükleyerek bunu doğrulayabilirsiniz.
Şimdi URL ve [https://tahe.developpez.com:443/] dosyalarını isteyelim. Bu URL dosyasını alabilmek için, HTTP istemcisinin HTTPS dilini bilmesi gerekir. [curl] istemcisi bu koşulu karşılıyor.
Konsol çıktıları şu şekildedir:
c:\Temp\curl
λ curl --verbose --output tahe.developpez.com.HTML https://tahe.developpez.com:443/
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 87.98.130.52…
* TCP_NODELAY set
* Connected to tahe.developpez.com (87.98.130.52) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: C:\myprograms\laragon-lite\bin\laragon\utils\curl-ca-bundle.crt
CApath: none
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [108 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2558 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [333 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [70 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=*.developpez.com
* start date: Apr 4 08:25:09 2019 GMT
* expire date: Jul 3 08:25:09 2019 GMT
* subjectAltName: host "tahe.developpez.com" matched cert's "*.developpez.com"
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
} [5 bytes data]
> GET / HTTP/1.1
> Host: tahe.developpez.com
> User-Agent: curl/7.63.0
> Accept: */*
>
{ [5 bytes data]
< HTTP/1.1 200 OK
< Date: Fri, 17 May 2019 14:39:41 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/5.3.29
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/HTML
<
{ [6 bytes data]
100 96559 0 96559 0 0 163k 0 --:--:-- --:--:-- --:--:-- 163k
* Connection #0, tahe.developpez.com ana bilgisayarına olduğu gibi bırakıldı
- 10-40. satırlar: Bağlantıyı güvenli hale getirmek için istemci ile sunucu arasındaki iletişim; bağlantı şifrelenecektir;
- 42-45. satırlar: HTTP istemcisi tarafından sunucuya gönderilen başlıklar;
- 48. satır: İstenen belge başarıyla bulundu;
- satır 53: belge parçalar halinde gönderiliyor;
[curl], hem güvenli protokolü (HTTPS) hem de belgenin parçalar halinde gönderilmesini doğru bir şekilde yönetir. Gönderilen belge, burada [tahe.developpez.com.HTML] dosyasında bulunacaktır.
Şimdi URL ve [http://sergetahe.com/cours-tutoriels-de-programmation]’i sorgulayalım. Daha önce, bu URL için URL ve [http://sergetahe.com/cours-tutoriels-de-programmation/]’e (sonunda / olan) bir yönlendirme olduğunu görmüştük.
Konsol çıktıları şu şekildedir:
c:\Temp\curl
λ curl --verbose --output sergetahe.com.HTML --location http://sergetahe.com/programlama-dersleri-ve-eğitimleri
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 87.98.154.146…
* TCP_NODELAY set
* Connected to sergetahe.com (87.98.154.146) port 80 (#0)
> GET /cours-tutoriels-de-programmation HTTP/1.1
> Host: sergetahe.com
> User-Agent: curl/7.63.0
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Date: Fri, 17 May 2019 15:13:03 GMT
< Content-Type: text/HTML; charset=iso-8859-1
< Content-Length: 262
< Server: Apache
< Location: http://sergetahe.com/programlama-dersleri-ve-öğreticiler/
< Set-Cookie: SERVERID68971=2621207|XN7Pg|XN7Pg; path=/
< Cache-control: private
< X-IPLB-Instance: 17095
<
* Ignoring the response-body
{ [262 bytes data]
100 262 100 262 0 0 1401 0 --:--:-- --:--:-- --:--:-- 1401
* Connection #0, sergetahe.com'i olduğu gibi bırakmak
* Issue another request to this URL: 'http://sergetahe.com/programlama-dersleri-ve-öğreticiler/'
* Found bundle for host sergetahe.com: 0x1c88548 [can pipeline]
* Could pipeline, but not asked to!
* Re-using existing connection! (#0) ana sunucu sergetahe.com ile
* Connected to sergetahe.com (87.98.154.146) port 80 (#0)
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
> GET /cours-tutoriels-de-programmation/ HTTP/1.1
> Host: sergetahe.com
> User-Agent: curl/7.63.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 17 May 2019 15:13:04 GMT
< Content-Type: text/HTML; charset=UTF-8
< Transfer-Encoding: chunked
< Server: Apache
< X-Powered-By: PHP/7.0
< Vary: Accept-Encoding
< Set-Cookie: SERVERID68971=2621207|XN7Pg|XN7Pg; path=/
< Cache-control: private
< X-IPLB-Instance: 17095
<
{ [14205 bytes data]
100 43101 0 43101 0 0 78795 0 --:--:-- --:--:-- --:--:-- 168k
* Connection #0, sergetahe.com ana sunucusuna olduğu gibi bırakıldı
- 2. satır: Sunucu tarafından gönderilen yönlendirmeleri takip etmek istediğimizi belirtmek için [--location] seçeneği kullanılır;
- 13. satır: sunucu, istenen belgenin URL olarak değiştiğini bildirir;
- 18. satır: İstenen belgenin yeni URL adresini belirtir;
- 27. satır: [curl], bu sefer yeni URL adresine yeni bir istek gönderir;
- 33. satır: yeni URL kullanılır;
- 38. satır: sunucu, istenen belgeyi bulduğunu bildirir;
- 41. satır: belge parçalar halinde gönderilir;
İstenen belge, [sergetahe.com.HTML] dosyasında bulunacaktır.
16.4.6. Örnek 6
PHP, [libcurl] adlı bir uzantıya sahiptir; bu uzantı, [curl] aracının yeteneklerinin PHP programında kullanılmasını sağlar. Öncelikle, bağlantı paragrafında açıklanan [php.ini] dosyasında bu uzantının etkinleştirildiğinden emin olunmalıdır:

Yukarıdaki 889. satırın yorum satırı olarak işaretlenmediğinden emin olun.
Aşağıdaki jSON yapılandırma dosyasını kullanacak bir [http-02.php] komut dosyası yazacağız:
[clé, valeur] sözlüğündeki her bir öğe aşağıdaki yapıya sahiptir:
- clé: bir web sunucusunun adı;
- valeur, aşağıdaki anahtarları içeren bir sözlüktür:
- timeout: sunucudan yanıt için maksimum bekleme süresi. Bu süre aşıldığında, istemci bağlantısını kesecektir;
- url: İstenen belgenin URL değeri;
[http-02.php] komut dosyasının kodu şöyledir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyuldu
declare (strict_types=1);
//
// hata yönetimi
//error_reporting(E_ALL & E_STRICT);
//ini_set("display_errors", "on");
//
// sabitler
const CONFIG_FILE_NAME = "config-http-02.json";
//
// yapılandırmayı alıyoruz
$config = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// yapılandırma dosyasından URL metnini alma
foreach ($config as $site => $infos) {
// URL'i $ite sitesinden okuma
$résultat = getUrl($site, $infos["url"], $infos["timeout"]);
// sonuç gösterimi
print "$résultat\n";
}//için
// son
exit;
//-----------------------------------------------------------------------
function getUrl(string $site, string $url, int $timeout, $suivi = TRUE): string {
//, URL $url dosyasını okur ve output/$site.HTML dosyasına kaydeder
//
// takip
print "Client : début de la communication avec le serveur [$site] ----------------------------\n";
// cURL oturumu başlatılıyor
$curl = curl_init($url);
if ($curl === FALSE) {
// bir hata oluştu
return "Erreur lors de l'initialisation de la session cURL pour le site [$site]";
}
// curl seçenekleri
$options = [
// ayrıntılı mod
CURLOPT_VERBOSE => true,
// yeni bağlantı - önbellek yok
CURLOPT_FRESH_CONNECT => true,
// istek zaman aşımı (saniye cinsinden)
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $timeout,
// sertifikaların geçerliliğini kontrol etme SSL
CURLOPT_SSL_VERIFYPEER => false,
// yönlendirmeleri takip et
CURLOPT_FOLLOWLOCATION => true,
// istenen belgeyi bir karakter dizisi olarak al
CURLOPT_RETURNTRANSFER => true
];
// curl ayarları
curl_setopt_array($curl, $options);
// İsteğin yürütülmesi
$page_content = curl_exec($curl);
// Oturumun kapatılması cURL
curl_close($curl);
// sonucun işlenmesi
if ($page_content !== FALSE) {
// sonucun $site dosyasına kaydedilmesi.HTML
$result = file_put_contents("output/$site.HTML", $page_content);
if ($result === FALSE) {
// hata mesajı
return "Erreur lors de la création du fichier [output/$site.HTML]";
}
// işlem başarıyla tamamlandı
return "Fin de la communication avec le serveur [$site]. Vérifiez le fichier [output/$site.HTML]";
} else {
// bir iletişim hatası oluştu
return "Erreur de communication avec le serveur [$site]";
}
}
Açıklamalar
- 14. satır: [$config] sözlüğünü oluşturmak için yapılandırma dosyası kullanılır;
- 17-22. satırlar: yapılandırmada bulunan site listesi üzerinde döngü yapılır;
- 19. satır: Her bir site için,URL $infos[«url»] dosyalarını bir zaman aşımı süresiyle indirir;
- 34. satır: [curl] oturumu başlatılır. [curl_init] henüz web sunucusuna bağlanmamıştır. Bu işlev, sonraki tüm [curl] işlevleri için parametre olacak olan [$curl] kaynağını döndürür;
- 35-38. satırlar: [curl] oturumunun başlatılması başarısız olursa, [curl_init] işlevi FALSE boole değerini döndürür;
- satır 40-54: [$options] sözlüğü, sunucuya [curl] bağlantısını yapılandıracaktır;
- 57. satır: Bağlantı seçenekleri [$curl] kaynağına iletilir;
- satır 59: Tanımlanan seçeneklerle URL'e bağlantı talebi gönderilir. [CURLOPT_RETURNTRANSFER => true] seçeneği nedeniyle, [curl_exec] işlevi, sunucu tarafından gönderilen belgeyi bir karakter dizisi olarak sonuç olarak döndürür. [curl_exec] işlevi, bağlantı başarısız olursa FALSE boole değerini döndürür;
- 64. satır: [curl_exec] işlevinin sonucu analiz edilir;
- 66. satır: Alınan sayfa yerel bir dosyaya kaydedilir;
- 69, 72, 75. satırlar: [getUrl] işlevinin sonucu döndürülür;
[http-02.php] komut dosyasını çalıştırdığımızda konsolda şu sonuçlar elde edilir:
* Rebuilt URL to: http://sergetahe.com/
Client : début de la communication avec le serveur [sergetahe.com] ----------------------------
* Trying 87.98.154.146…
* TCP_NODELAY set
* Connected to sergetahe.com (87.98.154.146) port 80 (#0)
> GET / HTTP/1.1
Host: sergetahe.com
Accept: */*
< HTTP/1.1 302 Found
< Date: Sat, 18 May 2019 08:46:38 GMT
< Content-Type: text/HTML; charset=UTF-8
< Transfer-Encoding: chunked
< Server: Apache
< X-Powered-By: PHP/7.0
< Location: http://sergetahe.com/programlama-dersleri-ve-öğreticiler
< Set-Cookie: SERVERID68971=2621236|XN/Gc|XN/Gc; path=/
< X-IPLB-Instance: 17097
<
* Ignoring the response-body
* Connection #0, sergetahe.com'i olduğu gibi bırakmak
* Issue another request to this URL: 'http://sergetahe.com/programlama-dersleri-ve-öğreticiler'
* Found bundle for host sergetahe.com: 0x1fee4ebe090 [can pipeline]
* Re-using existing connection! (#0) ana sunucu sergetahe.com ile
* Connected to sergetahe.com (87.98.154.146) port 80 (#0)
> GET /cours-tutoriels-de-programmation HTTP/1.1
Host: sergetahe.com
Accept: */*
< HTTP/1.1 301 Moved Permanently
< Date: Sat, 18 May 2019 08:46:38 GMT
< Content-Type: text/HTML; charset=iso-8859-1
< Content-Length: 262
< Server: Apache
< Location: http://sergetahe.com/programlama-dersleri-ve-öğreticiler/
< Set-Cookie: SERVERID68971=2621236|XN/Gc|XN/Gc; path=/
< Cache-control: private
< X-IPLB-Instance: 17097
<
* Ignoring the response-body
* Connection #0, sergetahe.com ana sunucusuna olduğu gibi bırakıldı
* Issue another request to this URL: 'http://sergetahe.com/programlama-dersleri-ve-öğreticiler/'
* Found bundle for host sergetahe.com: 0x1fee4ebe090 [can pipeline]
* Re-using existing connection! (#0) ana sunucu sergetahe.com ile
* Connected to sergetahe.com (87.98.154.146) port 80 (#0)
> GET /cours-tutoriels-de-programmation/ HTTP/1.1
Host: sergetahe.com
Accept: */*
< HTTP/1.1 200 OK
< Date: Sat, 18 May 2019 08:46:39 GMT
< Content-Type: text/HTML; charset=UTF-8
< Transfer-Encoding: chunked
< Server: Apache
< X-Powered-By: PHP/7.0
< Link: <http://sergetahe.com/programlama-dersleri-ve-öğreticiler/wp-json/>; rel="https://api.w.org/"
< Link: <http://sergetahe.com/programlama-dersleri-ve-öğreticiler/>; rel=shortlink
< Vary: Accept-Encoding
< Set-Cookie: SERVERID68971=2621236|XN/Gc|XN/Gc; path=/
< Cache-control: private
< X-IPLB-Instance: 17097
<
Fin de la communication avec le serveur [sergetahe.com]. Vérifiez le fichier [output/sergetahe.com.HTML]
Client : début de la communication avec le serveur [tahe.developpez.com] ----------------------------
* Connection #0, sergetahe.com'i olduğu gibi bırakmak
* Rebuilt URL to: https://tahe.developpez.com/
* Trying 87.98.130.52…
* TCP_NODELAY set
* Connected to tahe.developpez.com (87.98.130.52) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: C:\myprograms\laragon-lite\etc\ssl\cacert.pem
CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: CN=*.developpez.com
* start date: Apr 4 08:25:09 2019 GMT
* expire date: Jul 3 08:25:09 2019 GMT
* subjectAltName: host "tahe.developpez.com" matched cert's "*.developpez.com"
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
> GET / HTTP/1.1
Host: tahe.developpez.com
Accept: */*
< HTTP/1.1 200 OK
< Date: Sat, 18 May 2019 08:46:42 GMT
< Server: Apache/2.4.25 (Debian)
< X-Powered-By: PHP/5.3.29
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/HTML
<
Fin de la communication avec le serveur [tahe.developpez.com]. Vérifiez le fichier [output/tahe.developpez.com.HTML]
Client : début de la communication avec le serveur [www.polytech-angers.fr] ----------------------------
* Connection #0, tahe.developpez.com ana sunucusuna aktarılırken değiştirilmeden bırakıldı
* Rebuilt URL to: http://www.polytech-angers.fr/
* Trying 193.49.144.41…
* TCP_NODELAY set
* Connected to www.polytech-angers.fr (193.49.144.41) port 80 (#0)
> GET / HTTP/1.1
Host: www.polytech-angers.fr
Accept: */*
< HTTP/1.1 301 Moved Permanently
< Date: Sat, 18 May 2019 08:46:45 GMT
< Server: Apache/2.4.29 (Ubuntu)
< Location: http://www.polytech-angers.fr/fr/index.HTML
< Cache-Control: max-age=1
< Expires: Sat, 18 May 2019 08:46:46 GMT
< Content-Length: 339
< Content-Type: text/HTML; charset=iso-8859-1
<
* Ignoring the response-body
* Connection #0, www.polytech-angers.fr'i olduğu gibi bırakmak
* Issue another request to this URL: 'http://www.polytech-angers.fr/fr/index.HTML'
* Found bundle for host www.polytech-angers.fr: 0x1fee4ebe390 [can pipeline]
* Re-using existing connection! (#0) ana bilgisayar www.polytech-angers.fr ile
* Connected to www.polytech-angers.fr (193.49.144.41) port 80 (#0)
> GET /fr/index.HTML HTTP/1.1
Host: www.polytech-angers.fr
Accept: */*
< HTTP/1.1 200
< Date: Sat, 18 May 2019 08:46:46 GMT
< Server: Apache/2.4.29 (Ubuntu)
< X-Cocoon-Version: 2.1.13-dev
< Accept-Ranges: bytes
< Last-Modified: Sat, 18 May 2019 08:01:36 GMT
< Content-Type: text/HTML; charset=UTF-8
< Content-Length: 47372
< Vary: Accept-Encoding
< Cache-Control: max-age=1
< Expires: Sat, 18 May 2019 08:46:47 GMT
< Content-Language: fr
<
* Connection #0, www.polytech-angers.fr ana sunucusuna olduğu gibi bırakıldı
Fin de la communication avec le serveur [www.polytech-angers.fr]. Vérifiez le fichier [output/www.polytech-angers.fr.HTML]
Client : début de la communication avec le serveur [localhost] ----------------------------
* Rebuilt URL to: http://localhost/
* Trying ::1…
* TCP_NODELAY set
* Connected to localhost (::1) port 80 (#0)
> GET / HTTP/1.1
Host: localhost
Accept: */*
< HTTP/1.1 200 OK
< Date: Sat, 18 May 2019 08:46:47 GMT
< Server: Apache/2.4.35 (Win64) OpenSSL/1.1.0i PHP/7.2.11
< X-Powered-By: PHP/7.2.11
< Content-Length: 1781
< Content-Type: text/HTML; charset=UTF-8
<
* Connection #0, localhost sunucusuna yönlendirildi, değiştirilmedi
Fin de la communication avec le serveur [localhost]. Vérifiez le fichier [output/localhost.HTML]
Yorumlar
- [curl] aracıyla yapılan işlemlerle aynı veriler elde edilir;
- yeşil renkli kısımlar, komut dosyasının günlükleri;
- mavi renkli olanlar, sunucuya gönderilen komutlar;
- sarı renkte, istemci tarafından yanıt olarak alınan komutlar;
16.4.7. Sonuç
Bu paragrafta, HTTP protokolünü inceledik ve web'den bir URL dosyasını indirebilen bir [http-02.php] komut dosyası yazdık.
16.5. SMTP protokolü (Basit Posta Aktarım Protokolü)
16.5.1. Giriş

Bu bölümde:
- [Serveur B], kuracağımız yerel bir SMTP sunucusu olacaktır;
- [Client A], çeşitli biçimlerdeki bir SMTP istemcisi olacaktır:
- [RawTcpClient] istemcisi, SMTP protokolünü keşfetmek için;
- [RawTcpClient] istemcisinin SMTP protokolünü yeniden canlandıran bir PHP komut dosyası;
- her türlü e-postayı göndermek için [SwiftMailServer] kütüphanesini kullanan bir PHP komut dosyası;
16.5.2. [gmail] adresinin oluşturulması
SMTP testlerimizi yapmak için, e-posta göndereceğimiz bir adrese ihtiyacımız olacak. Bunun için Gmail'de bir adres oluşturacağız:

- [5] adresinde, [php7parlexemple] kullanıcı adını oluşturuyoruz (başka bir şey seçin);
- [6]'te şifre [PHP7parlexemple] olacaktır (başka bir şey seçin);
- [7]'te bu bilgileri onaylıyoruz;

- [9-10] alanlarını doldurun ve onaylayın (11);
- Google kullanım koşullarını kabul edin (12-13) ve ardından onaylayın (14);

- [15]'te, [PHP7] kullanıcısının gelen kutusu (Inbox) (16);
- [17]'e, bu kullanıcının gelen kutusu boş;
- [18-19]'te, [php7parlexemple@gmail.com] kullanıcısının Google hesabına giriş yapın. Hesap güvenliğini yapılandıracağız;

- [21]'te, Google uygulamaları dışındaki diğer uygulamaların [php7parlexemple] hesabını kullanmasına izin verin. Bunu yapmazsak, yerel e-posta sunucumuz [hMailServer], Gmail’in SMTP sunucusuyla iletişim kuramayacaktır;

16.5.3. SMTP sunucusunun kurulumu
Testlerimiz için, hem e-posta göndermeye olanak tanıyan bir SMTP sunucusu hem de sunucuda depolanan e-postaları okumaya yarayan bir POP3 (Post Office Protocol) sunucusu ve yine sunucuda depolanan e-postaları okumaya yarayan ancak bunun ötesine geçen bir IMAP (Internet Message Access Protocol) sunucusudur. Özellikle sunucudaki e-postaların depolanmasını yönetmeye olanak tanır.
[hMailServer] e-posta sunucusu, URL ve [https://www.hmailserver.com/] adreslerinde kullanılabilir (Mayıs 2019).

Kurulum sırasında sizden bazı bilgiler istenecektir:

- [1-2]'te hem e-posta sunucusunu hem de onu yönetmek için gerekli araçları seçin;
- kurulum sırasında sizden yönetici şifresi istenecektir: bunu not edin, çünkü ileride ihtiyacınız olacak;
[hMailServer], bilgisayarın açılışında otomatik olarak başlatılan bir Windows hizmeti olarak kurulur. Manuel başlatma seçeneğini tercih etmeniz önerilir:
- [3]'te, durum çubuğundaki giriş alanına [services] yazın;

- [4-8]'te, hizmeti [manuel] (6) moduna getirip başlatın (7);
Başlatıldıktan sonra, [hMailServer] sunucusu yapılandırılmalıdır. Sunucu, [hMailServer Administrator] yönetim programı ile kurulmuştur:

- [2]'te, durum çubuğundaki giriş alanına [hmailserver] yazın;
- [3]'te, yöneticiyi başlatın;
- [4]'te, yöneticiyi [hMailServer] sunucusuna bağlayın;
- [5]'te, [hMailServer]'in kurulumu sırasında girilen şifreyi yazın;

Bir kullanıcı hesabı oluşturacağız:
- [Accounts] (7) üzerine sağ tıklayın, ardından (8) seçeneğini seçerek yeni bir kullanıcı ekleyin;
- [General] (9) sekmesinde, [guest] (10) adlı bir kullanıcıyı [guest] (11) şifresiyle tanımlayacağız. Bu kullanıcının e-posta adresi [guest@localhost] (10) olacaktır;
- [12]'te, [guest] kullanıcısı etkinleştirilmiştir;


- [15]'te, e-posta sunucusunun SMTP protokolü yapılandırılır;
- [16]'te, e-postaların dağıtımı yapılandırılır;
- [17]'te, ana makineye (localhost) gönderilecek e-postaların dağıtımı yapılandırılır;
- [18]'te, yerel makinenin adı (localhost) belirlenir. Bağlantı paragrafındaki komut dosyası bu adı elde etmenizi sağlar;
- [19]'te, bir SMTP aktarma sunucusu yapılandırılır: bu, yerel makineye (localhost) gönderilmeyen e-postaların dağıtımını üstlenecek sunucudur;
- [20]'te, Gmail'in SMTP sunucusu. Bağlantı paragrafında bir hesap oluşturduğumuz için Gmail'i kullanıyoruz;
- [21]'te, Gmail'in SMTP bağlantı noktası;
- [22]'te, Gmail'in SMTP hizmeti güvenli bir hizmettir: bu hizmete erişmek için bir Gmail hesabı gereklidir;
- [23]'te, bağlantı paragrafında oluşturulan [php7parlexemple] kullanıcısı;
- [24]'te, bu kullanıcının şifresi: bağlantı paragrafında oluşturulan [PHP7parlexemple];
- [25]'te, Gmail tarafından kullanılan güvenlik protokolünün türü belirtilir;

- [27]'te, SMTP hizmetinin bağlantı noktası;
- [28]'te, bu hizmet için kimlik doğrulama gerekmez;
- [30] kısmına, SMTP sunucusunun müşterilerine göndereceği karşılama mesajını girin;
16.5.4. SMTP protokolü

Aşağıdaki ortamda SMTP protokolünü inceleyeceğiz:
- A istemcisi, TCP genel istemcisi [RawTcpClient] olacaktır;
- sunucu B, [hMailServer] e-posta sunucusu olacaktır;
- A istemcisi, B sunucusundan [php7parlexemple@gmail.com] kullanıcısına bir e-posta iletmesini isteyecek;
- bu kullanıcının gönderilen e-postayı aldığını doğrulayacağız;
İstemciyi şu şekilde başlatıyoruz:
![]()
- [1] olarak, [hMailServer]'in SMTP hizmetinin çalıştığı yerel makinenin 25 numaralı bağlantı noktasına bağlanıyoruz. [--quit bye] argümanı, kullanıcının [bye] komutunu girerek programdan çıkacağını belirtir. Bu argüman olmadan, programı sonlandırma komutu [quit]'tir. Ancak [quit], aynı zamanda SMTP protokolünün bir komutudur. Dolayısıyla bu belirsizliği önlememiz gerekir;
- [2]'te istemci başarıyla bağlanmıştır;
- [3]'te, istemci klavyeden girilecek komutları bekliyor;
- [4]'te, sunucu ona hoş geldin mesajını gönderir;

- [5]'te, istemci [EHLO nom-de-la-machine-client] komutunu gönderir. Sunucu, [250-xx] (6) biçiminde bir dizi mesajla yanıt verir. [250] kodu, istemci tarafından gönderilen komutun başarılı olduğunu gösterir;
- [7]'te, istemci mesajın gönderenini belirtir; burada [guest@localhost]. Bu kullanıcı, [hMailServer] posta sunucusunda mevcut olmalıdır. Burada durum böyledir, çünkü bu kullanıcıyı daha önce oluşturmuştuk;
- [8]'te, sunucunun yanıtı yer alır;
- [9]'te, mesajın alıcısı belirtilir; burada Gmail kullanıcısı [php7parlexemple@gmail.com];
- [10]'te, sunucunun yanıtı;
- [11]'te, [DATA] komutu sunucuya, istemcinin mesajın içeriğini göndereceğini bildirir;
- [12]'te, sunucunun yanıtı;
- [13-16] formatında, müşteri, sonunda yalnızca tek bir nokta içeren bir satırla biten metin satırlarından oluşan bir liste göndermelidir. Mesaj, sırasıyla mesajın konusunu, göndereni ve alıcıyı tanımlamak için [Subject :, From :, To :] (13) satırları içerebilir;
- [14]'te, önceki başlık satırlarının ardından boş bir satır gelmelidir;
- [15] satırında mesaj metni yer almalıdır;
- [16] satırında, mesajın sonunu belirten tek bir nokta yer almalıdır;
- [17]'te, sunucu sadece tek bir nokta içeren satırı aldığında, mesajı kuyruğa alır;
- [18]'te, istemci sunucuya işini bitirdiğini bildirir;
- [19]'te, sunucunun yanıtı;
- [20]'te, sunucunun istemciyle olan bağlantısını kapattığı görülmektedir;
Şimdi, [php7parlexemple@gmail.com] kullanıcısının mesajı aldığını kontrol edelim:

- [2]'te, [php7parlexemple@gmail.com] kullanıcısının mesajı aldığını görüyoruz;



- [7]'te, e-postanın göndereni görülüyor. Bunun [guest@localhost] olmadığı anlaşılıyor. Bunun nedeni, mesajı teslim edenin [hmailServer]'in yapılandırmasında tanımlanan aktarma sunucusu olmasıdır. Oysa bu aktarma sunucusu, Gmail kullanıcısı [php7parlexemple@gmail.com]'in kimlik bilgileriyle ilişkili olan [smtp.gmail.com]'tir. [hMailServer] adresinden gelen tüm e-postalar, [php7parlexemple@gmail.com] kullanıcısından geliyormuş gibi görünecektir. Burada istediğimiz bu değildi, ancak bu aktarım sunucusunu kullanmazsak, Gmail’in SMTP hizmeti, [hMailServer] tarafından gönderilen e-postaları reddeder; çünkü Gmail’in SMTP hizmeti, [hMailServer]’in göndermediği bir kimlik doğrulama bilgisi talep eder. Muhtemelen bu sorunu aşmanın bir yolu vardır ama ben bulamadım;
- [8]'te, e-postanın [hMailServer] e-posta sunucusunu barındıran [DESKTOP-528I5CU] makinesinden alındığı görülüyor;
- [9]'te ise mesajın göndereni görülüyor. Bunun [guest@localhost] olmadığı anlaşılıyor;
- [10], mesajın asıl göndereni. Bu sefer gerçekten de [guest@localhost];
- [11], konu;
- [12], alıcı;
- [13]'te mesaj;
Sonunda, gönderenle ilgili bir sorun yaşansa da [RawTcpClient] istemcimiz mesajı başarıyla gönderdi. PHP ile yazılmış bir SMTP istemcisi oluşturmak için gerekli temellere sahibiz.
16.5.5. PHP ile yazılmış temel bir SMTP istemcisi
Daha önce SMTP protokolünden öğrendiklerimizi PHP'te uygulayacağız.

[smtp-01.php] betiği, aşağıdaki jSON [config-smtp-01.json] dosyası ile yapılandırılır:
{
"mail to localhost via localhost": {
"smtp-server": "localhost",
"smtp-port": "25",
"from": "guest@localhost",
"to": "guest@localhost",
"subject": "to localhost via localhost",
"message": "ligne 1\nligne 2\nligne 3"
},
"mail to gmail via localhost": {
"smtp-server": "localhost",
"smtp-port": "25",
"from": "guest@localhost",
"to": "php7parlexemple@gmail.com",
"subject": "to gmail via localhost",
"message": "ligne 1\nligne 2\nligne 3"
},
"mail to gmail via gmail": {
"smtp-server": "smtp.gmail.com",
"smtp-port": "587",
"from": "guest@localhost",
"to": "php7parlexemple@gmail.com",
"subject": "to gmail via gmail",
"message": "ligne 1\nligne 2\nligne 3"
}
}
[config-smtp-01.json], her bir elemanı [nom=>infos] türünde bir sözlük olan bir dizidir. [infos] değeri ise aşağıdaki anahtar ve değerlere sahip bir sözlüktür:
- [smtp-server]: Kullanılacak SMTP sunucusunun adı;
- [smtp-port]: SMTP hizmetinin bağlantı noktası numarası;
- [from]: mesajın göndereni;
- [to]: mesajın alıcısı;
- [subject]: mesajın konusu;
- [message]: gönderilecek mesaj;
- 1. öğe, SMTP [localhost] sunucusunu kullanarak [localhost]'teki bir kullanıcıya e-posta gönderir;
- 2. öğe, SMTP ve [localhost] sunucularını kullanarak [Gmail] sunucusundaki bir kullanıcıya e-posta gönderir;
- 3. öğe, SMTP ve [Gmail] sunucularını kullanarak [Gmail]'teki bir kullanıcıya e-posta gönderir;
SMTP istemcisinin [smtp-01.php] kodu şöyledir:
<?php
// SMTP istemcisi (SendMail Aktarım Protokolü) bir mesajın gönderilmesini sağlar
// SMTP istemci-sunucu iletişim protokolü
// -> istemci, SMTP sunucusunun 25 numaralı bağlantı noktasına bağlanır
// <- sunucu, istemciye bir hoş geldin mesajı gönderir
// -> istemci, EHLO komutunu ve makinesinin adını gönderir
// <- sunucu OK yanıtını gönderir veya göndermez
// -> istemci MAIL FROM: <gönderen> komutunu gönderir
// <- sunucu OK yanıtını verir mi, vermez mi
// -> istemci RCPT komutunu gönderir TO: <alıcı>
// <- sunucu OK yanıtını verir veya vermez
// -> istemci DATA komutunu gönderir
// <- sunucu OK yanıtını verir veya vermez
// -> istemci, mesajının tüm satırlarını gönderir ve sonunda
// tek karakterini içeren bir satırla bitirir.
// <- sunucu OK yanıtını verir ya da vermez
// -> istemci QUIT komutunu gönderir
// <- sunucu OK yanıtını verir ya da vermez
// sunucunun yanıtları xxx metin biçimindedir; burada xxx, 3 basamaklı bir sayıdır. Herhangi bir
// xxx sayısı 500 veya üzerindeyse bir hata olduğunu gösterir.
// Yanıt, son satır hariç hepsi xxx ile başlayan birden fazla satır içerebilir
// şeklinde xxx(boşluk)
// değiş tokuş edilen metin satırları RC(#13) ve LF(#10) karakterleriyle bitmelidir
//
// SMTP istemcisi (SendMail Aktarım Protokolü) mesaj göndermeye olanak tanır
//
// hata yönetimi
//ini_set ("error_reporting", E_ALL & ~ E_WARNING & ~E_DEPRECATED & ~E_NOTICE);
//ini_set("display_errors", "off");
//
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulması
declare (strict_types=1);
//
// e-posta gönderme parametreleri
const CONFIG_FILE_NAME = "config-smtp-01.json";
// yapılandırmanın alınması
$mails = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// e-postaların gönderilmesi
foreach ($mails as $name => $infos) {
// takip
print "Envoi du mail [$name]\n";
// posta gönderimi
$résultat = sendmail($name, $infos, TRUE);
// sonuç görüntüleme
print "$résultat\n";
}//for
// son
exit;
//sendmail
//-----------------------------------------------------------------------
function sendmail(string $name, array $infos, bool $verbose = TRUE): string {
// mesajı gönderir [$name,$infos]. Eğer $verbose=TRUE ise, istemci-sunucu arasındaki iletişimi takip eder
// istemcinin adı alınır
$client = gethostbyaddr(gethostbyname(""));
// sunucuyla bağlantı kurulur SMTP
$connexion = fsockopen($infos["smtp-server"], (int) $infos["smtp-port"]);
// hata durumunda geri dönüş
if ($connexion === FALSE) {
return sprintf("Echec de la connexion au site (%s,%s) : %s", $infos["smtp-server"], $infos["smtp-port"]);
}
// $connexion, iki yönlü bir iletişim akışını temsil eder
// istemci (bu program) ile bağlantı kurulan smtp sunucusu arasında
// bu kanal, komut ve bilgi alışverişi için kullanılır
// bağlantı kurulduktan sonra sunucu, okunan bir karşılama mesajı gönderir
$erreur = sendCommand($connexion, "", $verbose, TRUE);
if ($erreur !== "") {
// bağlantının sonlandırılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut EHLO
$erreur = sendCommand($connexion, "EHLO $client", $verbose, TRUE);
if ($erreur !== "") {
// bağlantı kapatılıyor
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut MAIL FROM:
$erreur = sendCommand($connexion, sprintf("MAIL FROM: <%s>", $infos["from"]), $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri
return $erreur;
}
// komut RCPT TO:
$erreur = sendCommand($connexion, sprintf("RCPT TO: <%s>", $infos["to"]), $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri
return $erreur;
}
// komut DATA
$erreur = sendCommand($connexion, "DATA", $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// gönderilecek mesajın hazırlanması
// şu satırları içermelidir
// From: gönderen
// To: alıcı
// Konu:
// boş satır
// Mesaj
// .
$data = sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n.\r\n", $infos["from"], $infos["to"], $infos["subject"], $infos["message"]);
$erreur = sendCommand($connexion, $data, $verbose, FALSE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// çıkış komutu
$erreur = sendCommand($connexion, "QUIT", $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri
return $erreur;
}
// son
fclose($connexion);
return "Message envoyé";
}
// --------------------------------------------------------------------------
function sendCommand($connexion, string $commande, bool $verbose, bool $withRCLF): string {
// $commande'i $connexion kanalına gönderir
// $verbose=1 ise ayrıntılı mod
// $withRCLF=1 ise, RCLF dizisini takasa ekler
// veriler
if ($withRCLF) {
$RCLF = "\r\n";
} else {
$RCLF = "";
}
// $commande boş değilse komut gönder
if ($commande!=="") {
fputs($connexion, "$commande$RCLF");
// olası yankı
if ($verbose) {
affiche($commande, 1);
}
}//eğer
// yanıt okuma
$réponse = fgets($connexion, 1000);
// olası yankı
if ($verbose) {
affiche($réponse, 2);
}
// hata kodunun alınması
$codeErreur = (int) substr($réponse, 0, 3);
// yanıtın son satırı mı?
while (substr($réponse, 3, 1) === "-") {
// yanıt okuma
$réponse = fgets($connexion, 1000);
// olası yankı
if ($verbose) {
affiche($réponse, 2);
}
}//while
// yanıt tamamlandı
// sunucu tarafından gönderilen hata mı?
if ($codeErreur >= 500) {
return substr($réponse, 4);
}
// hatasız dönüş
return "";
}
// --------------------------------------------------------------------------
function affiche($échange, $sens) {
// ekrana $échange yazdır
// eğer $sens=1 ise -->$echange'i görüntüler
// eğer $sens=2 ise, son 2 karakteri hariç <-- $échange görüntüler RCLF
switch ($sens) {
case 1:
print "--> [$échange]\n";
break;
case 2:
$L = strlen($échange);
print "<-- [" . substr($échange, 0, $L - 2) . "]\n";
break;
}//anahtarı
}
Açıklamalar
- 39. satır: yapılandırma dosyası kullanılır;
- 42. satır: [mails] dizisinin elemanları üzerinde döngü yapılır. Her eleman, [name=>infos] adlı bir sözlükten oluşur; burada [name] herhangi bir isim olabilir ve [infos], e-posta gönderimi için gerekli bilgileri içeren bir sözlüktür;
- 46. satır: E-postanın gönderilmesi, üç parametre kabul eden [sendmail] işlevi tarafından gerçekleştirilir:
- $name: Bu gönderime verilen ad;
- $infos: gönderim için gerekli bilgileri içeren sözlük;
- verbose: istemci/sunucu arasındaki iletişimlerin konsolda günlüğe kaydedilip kaydedilmeyeceğini belirten bir boole değeri;
- 46. satır: [sendmail] işlevi, hata olmaması durumunda boş bir hata mesajı döndürür;
- satır 56: [sendmail] işlevi, bir SMTP istemcisinin göndermesi gereken çeşitli komutları gönderir:
- satır 77-84: EHLO komutu;
- satır 85-92: MAIL FROM komutu: ;
- 93-100. satırlar: RCPT TO: komutu;
- satır 101-108: DATA komutu;
- 117-124. satırlar: mesajın gönderilmesi (Gönderen, Alıcı, Konu, metin);
- satır 125-132: QUIT komutu;
- satır 140: [sendCommand] işlevi, istemcinin komutlarını SMTP sunucusuna göndermekle görevlidir. Dört parametre kabul eder:
- [$connexion]: İstemciyi sunucuya bağlayan bağlantı;
- [$commande]: gönderilecek komut;
- [$verbose]: TRUE ayarlanmışsa, istemci/sunucu arasındaki iletişim konsola kaydedilir;
- [$withRCLF]: TRUE ise, komutu \r\n dizisiyle sonlandırarak gönderir. Bu, SMTP protokolündeki tüm komutlar için gereklidir, ancak [sendCommand] komutu da mesajı göndermek için kullanılır. Burada \r\n dizisi eklenmez;
- 150-157. satırlar: komut sunucuya gönderilir;
- 158-163. satırlar: yanıtın ilk satırı okunur. Yanıt birkaç satırdan oluşabilir. Her satır XXX-YYY biçimindedir; burada XXX sayısal bir koddur, ancak yanıtın son satırı XXX YYY biçimindedir (- karakteri yoktur);
- 167-174. satırlar: yanıtın tüm satırlarının okunması;
- 177. satır: XXX sayısal kodu 500'den büyükse, sunucu bir hata döndürmüştür;
Sonuçlar
Komut dosyasının çalıştırılması sonucunda konsolda şu çıktılar elde edilir:
Envoi du mail [mail to localhost via localhost]
<-- [220 Bienvenue sur sergetahe@localhost]
--> [EHLO DESKTOP-528I5CU.home]
<-- [250-DESKTOP-528I5CU]
<-- [250-SIZE 20480000]
<-- [250-AUTH LOGIN]
<-- [250 HELP]
--> [MAIL FROM: <guest@localhost>]
<-- [250 OK]
--> [RCPT TO: <guest@localhost>]
<-- [250 OK]
--> [DATA]
<-- [354 OK, send.]
--> [From: guest@localhost
To: guest@localhost
Subject: to localhost via localhost
ligne 1
ligne 2
ligne 3
.
]
<-- [250 Queued (0.016 seconds)]
--> [QUIT]
<-- [221 goodbye]
Message envoyé
Envoi du mail [mail to gmail via localhost]
<-- [220 Bienvenue sur sergetahe@localhost]
--> [EHLO DESKTOP-528I5CU.home]
<-- [250-DESKTOP-528I5CU]
<-- [250-SIZE 20480000]
<-- [250-AUTH LOGIN]
<-- [250 HELP]
--> [MAIL FROM: <guest@localhost>]
<-- [250 OK]
--> [RCPT TO: <php7parlexemple@gmail.com>]
<-- [250 OK]
--> [DATA]
<-- [354 OK, send.]
--> [From: guest@localhost
To: php7parlexemple@gmail.com
Subject: to gmail via localhost
ligne 1
ligne 2
ligne 3
.
]
<-- [250 Queued (0.000 seconds)]
--> [QUIT]
<-- [221 goodbye]
Message envoyé
Envoi du mail [mail to gmail via gmail]
<-- [220 smtp.gmail.com ESMTP d9sm21623375wro.26 - gsmtp]
--> [EHLO DESKTOP-528I5CU.home]
<-- [250-smtp.gmail.com at your service, [90.93.230.110]]
<-- [250-SIZE 35882577]
<-- [250-8BITMIME]
<-- [250-STARTTLS]
<-- [250-ENHANCEDSTATUSCODES]
<-- [250-PIPELINING]
<-- [250-CHUNKING]
<-- [250 SMTPUTF8]
--> [MAIL FROM: <guest@localhost>]
<-- [530 5.7.0 Must issue a STARTTLS command first. d9sm21623375wro.26 - gsmtp]
5.7.0 Must issue a STARTTLS command first. d9sm21623375wro.26 - gsmtp
Done.
- satır 1-26: SMTP [hMailServer] sunucusunun [guest@localhost] adresine e-posta göndermek için kullanılması sorunsuz gerçekleşir;
- 27-52. satırlar: SMTP ve [hMailServer] sunucularının kullanılarak [php7parlexemple@gmail.com] adresine e-posta gönderilmesi sorunsuz gerçekleşiyor;
- 53-65. satırlar: SMTP ve [Gmail] sunucularının [php7parlexemple@gmail.com] adresine e-posta göndermek için kullanılması sorunsuz gerçekleşmiyor: 65. satırda, SMTP sunucusu, hata mesajıyla birlikte 530 hata kodunu gönderiyor. Bu hata mesajı, SMTP istemcisinin öncelikle güvenli bir bağlantı üzerinden kimlik doğrulaması yapması gerektiğini belirtiyor. İstemcimiz bunu yapmadığı için reddediliyor;
16.5.6. İkinci bir SMTP istemcisi, [SwiftMailer] kütüphanesini kullanarak yazıyor
Önceki istemcinin en az iki eksikliği vardır:
- sunucu talep ettiğinde güvenli bir bağlantı kullanmayı bilmiyor;
- mesaja ek ekleyemiyor;
Yeni betiğimizde [SwiftMailer] ve [https://swiftmailer.symfony.com/] kütüphanelerini kullanacağız (Mayıs 2019). [SwiftMailer]'in kurulum yöntemi, URL ve [https://swiftmailer.symfony.com/docs/introduction.HTML] (Mayıs 2019) belgelerinde açıklanmıştır.
Öncelikle Laragon'u başlatın:

- [1]'te bir terminal açın;

- [3]'e geçin, <laragon>'un Laragon'un kurulum klasörü olduğu [<laragon>/www] klasöründe olduğunuzu kontrol edin;
- [3]'te belirtilen komutu girin (Mayıs 2019). URL ve [https://swiftmailer.symfony.com/docs/introduction.HTML]'te komutun tam olarak doğru olup olmadığını kontrol edin;
- [4] dosyasında, herhangi bir kurulum veya güncelleme yapılmadığı belirtilmiştir. Bunun nedeni, kütüphanenin bu bilgisayara daha önce yüklenmiş olmasıdır;
- [5]'te, [swiftmailer] ve [6]'in kurulum klasörü;
- [7]'te, komut dosyamızda ihtiyaç duyacağımız bir dosya;
Bu işlem tamamlandıktan sonra, [<laragon>/www/vendor] ve [5] klasörlerinin NetBeans'in [Include Path] dalında yer aldığını kontrol edin (bkz. bağlantı paragrafı).
Son olarak, [SwiftMailer] kütüphanesi, PHP [mbstring] uzantısının etkin olmasını gerektirir. Bunun için [php.ini] dosyasını kontrol edin (bkz. bağlantı paragrafı):

[smtp-02.php] komut dosyası, aşağıdaki jSON ve [config-smtp-02.json] yapılandırma dosyalarını kullanacaktır:
[config-smtp-01.json] dosyasında bulunan başlıkların aynısı, iki ek başlık ile birlikte burada da yer almaktadır:
- [tls]: TRUE'te, SMTP sunucusuyla güvenli bir bağlantı kullanılması gerektiğini belirtir. [tls]'in değeri TRUE ise, iki alan eklenmelidir:
- [user]: Bağlantıyı doğrulayan kullanıcının adı;
- [password]: şifresi;
Örneğimizde, Gmail sunucusuna bağlanmak için [php7parlexemple@gmail.com] kullanıcı kimliğini kullandık. Siz de kendi kimlik bilgilerinizi kullanın;
- [attachments]: e-postaya eklenecek dosyaların adlarını belirtir;
[smtp-02.php] komut dosyasının kodu şöyledir:
<?php
// SMTP istemcisi (SendMail Aktarım Protokolü) mesaj göndermeye olanak tanır
//
// hata yönetimi
//ini_set ("error_reporting", E_ALL & ~ E_WARNING & ~E_DEPRECATED & ~E_NOTICE);
//ini_set("display_errors", "off");
//
// bağımlılıklar
require_once 'C:/myprograms/laragon-lite/www/vendor/autoload.php';
//
// e-posta gönderim ayarları
const CONFIG_FILE_NAME = "config-smtp-02.json";
// yapılandırmayı alıyoruz
$mails = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// e-postaların gönderilmesi
foreach ($mails as $name => $infos) {
// takip
print "Envoi du mail [$name]\n";
// posta gönderimi
$résultat = sendmail($name, $infos);
// sonuç görüntüleme
print "$résultat\n";
}//for
// son
exit;
//-----------------------------------------------------------------------
function sendmail($name, $infos) {
// $infos[message]'i $infos[smtp-server] SMTP sunucusuna $infosQZXW2HTMLBW3NtdC1wb3J0XQZ bağlantı noktası üzerinden gönderirQX
// eğer $infos[tls] doğruysa, TLS desteği kullanılacaktır
// e-posta, $infos[from] adına gönderilir
// alıcı $infos['to']'e
// $info[attachment] adlı belge mesaja eklenmiştir
// mesajın konusu $infos[subject]
//
// HTML biçiminde mesaj
$messageHTML = str_replace("\n", "<br/>", $infos["message"]);
try {
// mesajın oluşturulması
$message = (new \Swift_Message())
// mesajın konusu
->setSubject($infos["subject"])
// gönderen
->setFrom($infos["from"])
// sözlük içeren alıcılar (setTo/setCc/setBcc)
->setTo($infos["to"])
// mesaj metni
->setBody($infos["message"])
// HTML sürümü
->addPart("<b>$messageHTML</b>", 'text/html')
;
// ekler
foreach ($infos["attachments"] as $attachment) {
// eki yol
$fileName = __DIR__ . $attachment;
// dosyanın varlığı kontrol edilir
if (file_exists($fileName)) {
// belge mesaja ekleniyor
$message->attach(\Swift_Attachment::fromPath($fileName));
} else {
// hata
print "L'attachement [$fileName] n'existe pas\n";
}
}
// TLS protokolü mü?
if ($infos["tls"] === "TRUE") {
// TLS
$transport = (new \Swift_SmtpTransport($infos["smtp-server"], $infos["smtp-port"], 'tls'))
->setUsername($infos["user"])
->setPassword($infos["password"]);
} else {
// TLS yok
$transport = (new \Swift_SmtpTransport($infos["smtp-server"], $infos["smtp-port"]));
}
// gönderim yöneticisi
$mailer = new \Swift_Mailer($transport);
// mesajın gönderilmesi
$result = $mailer->send($message);
// son
return "Message [$name] envoyé";
} catch (\Throwable $ex) {
// hata
return "Erreur lors de l'envoi du message [$name] : " . $ex->getMessage();
}
}
Yorumlar
- 10. satır: [<lagagon>/www/vendor] klasöründe bulunan [autoload.php] dosyasını yüklüyoruz; burada <laragon>, Laragon’un kurulum klasörüdür. Bu dosya, [SwiftMailer] sınıf tanım dosyalarının, bu sınıflar ilk kez kullanıldığında yüklenmesini sağlayacaktır. Bu sayede, kullanacağımız SwiftMailer sınıf ve arayüzlerinin sayısı kadar [require] dosyası eklemekten kurtuluruz;
- 32. satır: İki parametreye sahip yeni [sendmail] işlevi:
- [$name], mesajları birbirinden ayırt etmek için kullanılır;
- [$infos]: mesajı alıcıya göndermek için gerekli bilgiler;
- 42. satır: Mesajın iki versiyonu olacak: biri düz metin, diğeri HTML formatında. Burada satır sonu işaretlerini HTML <br/> koduna dönüştürüyoruz;
- satır 45-69: Mesajı [\SwiftMessage] sınıfını kullanarak tanımlıyoruz;
- 47. satır: [SwiftMessage→setSubject] yöntemi, mesajın konusunu belirlemek için kullanılır;
- satır 49: [SwiftMessage→setFrom] yöntemi, mesajın gönderenini belirlemek için kullanılır;
- satır 51: [SwiftMessage→setTo] yöntemi, mesajın alıcısını belirlemek için kullanılır;
- satır 53: [SwiftMessage→setBody] yöntemi, mesajın gövdesini belirlemek için kullanılır;
- satır 55: [SwiftMessage→addPart] yöntemi, mesajın farklı sürümlerini belirlemek için kullanılır; burada mesaj HTML biçimindedir. Mesajın farklı sürümleri olduğunda, e-posta okuyucuları kullanıcının tercih ettiği sürümü görüntüler;
- satır 58-69: [SwiftMessage→addAttachment] (64) yöntemi, mesaja bir dosya eklemeye olanak tanır;
- 70-79. satırlar: Gönderilecek mesaj belirlendikten sonra, nasıl gönderileceği de belirlenmelidir. Mesajın aktarım şekli, [\Swift_SmtpTransport] sınıfı ile tanımlanır. En az iki bilgi sağlanmalıdır: SMTP sunucusunun nom ve port değerleri. Üçüncü bir bilgi de vardır: SMTP sunucusu güvenli kimlik doğrulama gerektiriyor mu?
- 73-75. satırlar: SMTP sunucusuna güvenli bağlantı için [\Swift_SmtpTransport] örneği;
- 78. satır: [\Swift_SmtpTransport] örneği, SMTP sunucusuna güvenli olmayan bir bağlantı için;
- satır 81: Mesajları gönderen [\SwiftMailer] sınıfıdır. Bu sınıfa seçilen aktarım modu aktarılmalıdır;
- satır 83: [\SwiftMessage] mesajı, seçilen [\Swift_SmtpTransport] aktarım yöntemi aracılığıyla gönderilir. Mesaj gönderilemediğinde, [SwiftMailer→send] yöntemi FALSE boole değerini döndürür;
- 86-89. satırlar: [SwiftMailer] kütüphanesi, herhangi bir sorun oluştuğunda bir istisna atar;
Not: [SwiftMailer] kütüphanesindeki sınıfların ad alanı kök dizin \'dir. Bunu hatırlatmak için [\SwiftMessage, \Swift_SmtpTransport, \SwiftMailer] sınıfları açıkça belirtilmiştir;
Sonuçlar
[smtp-02.php] betiği çalıştırıldığında konsolda aşağıdaki sonuçlar elde edilir:
[php7parlexemple] kullanıcısının Gmail hesabına bakıldığında şunlar görülür:

- [1], konu;
- [2]'te, gönderen;
- [3]'te alıcı;
- [4]'te mesaj;
- [5-10]'te ekler;
Orijinal mesajı görmek istendiğinde, aşağıdaki belge elde edilir:
Return-Path: <php7parlexemple@gmail.com>
Received: from [127.0.0.1] (lfbn-1-11924-110.w90-93.abo.wanadoo.fr. [90.93.230.110])
by smtp.gmail.com with ESMTPSA id e14sm7773816wma.41.2019.05.26.03.11.53
for <php7parlexemple@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Sun, 26 May 2019 03:11:54 -0700 (PDT)
Message-ID: <e613c47a421a66e2cf7f8e319616ec49@swift.generated>
Date: Sun, 26 May 2019 10:11:53 +0000
Subject: test-gmail-via-gmail
From: php7parlexemple@gmail.com
To: php7parlexemple@gmail.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: multipart/alternative; boundary="_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_"
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
ligne 1
ligne 2
ligne 3
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_
Content-Type: text/HTML; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<b>ligne 1<br/>ligne 2<br/>ligne 3</b>
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_--
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name="Hello from SwiftMailer.docx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.docx"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: application/pdf; name="Hello from SwiftMailer.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.pdf"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: application/vnd.oasis.opendocument.text; name="Hello from SwiftMailer.odt"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.odt"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: image/png; name="Cours-Tutoriels-Serge-Tahé-1568x268.png"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Cours-Tutoriels-Serge-Tahé-1568x268.png"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: message/rfc822; name=test-localhost.eml
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=test-localhost.eml
Return-Path: guest@localhost
Received: from [127.0.0.1] (localhost [127.0.0.1]) by DESKTOP-528I5CU with ESMTP ; Sat, 25 May 2019 09:48:23 +0200
Message-ID: <620f4628882b011feebe4faa30b45092@swift.generated>
Date: Sat, 25 May 2019 07:48:22 +0000
Subject: test-localhost
From: guest@localhost
To: guest@localhost
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_"
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_
Content-Type: multipart/alternative; boundary="_=_swift_1558770503_3561ca315f33bd15ef6556e98db4a5b8_=_"
--_=_swift_1558770503_3561ca315f33bd15ef6556e98db4a5b8_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
j'ai =C3=A9t=C3=A9 invit=C3=A9 =C3=A0 d=C3=A9je=C3=BBner
--_=_swift_1558770503_3561ca315f33bd15ef6556e98db4a5b8_=_
Content-Type: text/HTML; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<b>j'ai =C3=A9t=C3=A9 invit=C3=A9 =C3=A0 d=C3=A9je=C3=BBner</b>
--_=_swift_1558770503_3561ca315f33bd15ef6556e98db4a5b8_=_--
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_
Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document; name="Hello from SwiftMailer.docx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.docx"
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_
Content-Type: application/pdf; name="Hello from SwiftMailer.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.pdf"
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_
Content-Type: application/vnd.oasis.opendocument.text; name="Hello from SwiftMailer.odt"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Hello from SwiftMailer.odt"
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_
Content-Type: image/png; name="Cours-Tutoriels-Serge-Tahé-1568x268.png"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Cours-Tutoriels-Serge-Tahé-1568x268.png"
--_=_swift_1558770502_c4b808c99c27ded04595bd11f4bad11b_=_--
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_--
- 9. satır: konu;
- 10. satır: gönderen;
- 11. satır: alıcı;
- 13. satır: Mesaj, [--_=_swift_xx] etiketleriyle sınırlandırılmış birkaç bölümden oluşur;
- 19-24. satırlar: düz metin biçimindeki mesaj;
- 27-30. satırlar: HTML biçimindeki mesaj;
- 34-36. satırlar: [Hello from SwiftMailer.docx] uzantılı ek dosya;
- satır 40-42: ekli dosya [Hello from SwiftMailer.pdf];
- 46-48. satırlar: [Hello from SwiftMailer.odt] ekli dosya;
- satır 58-60: ekli dosya [Cours-Tutoriels-Serge-Tahé-1568x268.png];
- satır 58-60: ekli dosya [test-localhost.eml];
- satır 62-114: ekli dosya [test-localhost.eml], içeriği satır 62-114'te görüntülenen bir mesajdır. Bu mesajın da kendi içinde ekler içerdiği görülebilir;
16.6. POP3 (Post Office Protocol) ve IMAP (Internet Message Access Protocol) protokolleri
16.6.1. Giriş
Bir posta sunucusunda depolanan e-postaları okumak için iki protokol mevcuttur:
- POP3 (Post Office Protocol) protokolü; tarihsel olarak ilk protokol olmakla birlikte günümüzde pek kullanılmamaktadır;
- IMAP (İnternet Mesaj Erişim Protokolü) protokolü, POP3'ten daha yeni ve şu anda en yaygın olarak kullanılan protokoldür;
POP3 protokolünü incelemek için şu mimariyi kullanacağız:

- [Serveur B], POP3 / IMAP sunucusu tarafından uygulanan yerel bir [Serveur B] sunucusu olacaktır;
- [Client A], çeşitli biçimlerdeki bir POP3 / IMAP istemcisi olacaktır:
- [RawTcpClient] istemcisi, POP3 protokolünü keşfetmek için;
- [RawTcpClient] istemcisinin POP3 protokolünü yeniden canlandıran bir PHP komut dosyası;
- PHP'in IMAP kütüphanesini kullanan ve hem IMAP hem de POP3 istemcilerini uygulamaya olanak tanıyan PHP komut dosyası;
16.6.2. POP3 protokolünün keşfi
Öncelikle, [smtp-01.php] komut dosyasını kullanarak [guest@localhost] kullanıcısına bir e-posta gönderiyoruz. Komut dosyasıyla ilgili testleri yaptıysanız, bu kullanıcı normalde e-postaları almış olmalı, ancak bunu doğrulayamadık. Ona yeni bir e-posta göndermek için, örneğin aşağıdaki [config-smtp-01.json] yapılandırma dosyasını kullanın:
Şimdi [RawTcpClient] istemcisiyle [guest@localhost] kullanıcısının posta kutusunu nasıl okuyabileceğimize bakalım:
C:\Data\st-2019\dev\php7\php5-exemples\exemples\inet\utilitaires>RawTcpClient --quit bye localhost 110
Client [DESKTOP-528I5CU:55593] connecté au serveur [localhost-110]
Tapez vos commandes (bye pour arrêter) :
<-- [+OK Bienvenue sur sergetahe@localhost]
USER guest@localhost
<-- [+OK Send your password]
PASS guest
<-- [+OK Mailbox locked and ready]
LIST
<-- [+OK 2 messages (610 octets)]
<-- [1 305]
<-- [2 305]
<-- [.]
RETR 1
<-- [+OK 305 octets]
<-- [Return-Path: guest@localhost]
<-- [Received: from DESKTOP-528I5CU.home (localhost [127.0.0.1])]
<-- [ by DESKTOP-528I5CU with ESMTP]
<-- [ ; Tue, 21 May 2019 12:59:11 +0200]
<-- [Message-ID: <1356373A-33C9-4F31-BA43-2B119E128CE3@DESKTOP-528I5CU>]
<-- [From: guest@localhost]
<-- [To: guest@localhost]
<-- [Subject: to localhost via localhost]
<-- []
<-- [ligne 1]
<-- [ligne 2]
<-- [ligne 3]
<-- [.]
DELE 1
<-- [+OK msg deleted]
LIST
<-- [+OK 1 messages (305 octets)]
<-- [2 305]
<-- [.]
DELE 2
<-- [+OK msg deleted]
LIST
<-- [+OK 0 messages (0 octets)]
<-- [.]
QUIT
<-- [+OK POP3 server saying goodbye…]
Perte de la connexion avec le serveur…
- 1. satır: POP3 sunucusu genellikle 110 numaralı bağlantı noktasıyla çalışır. Burada da durum böyledir;
- 5. satır: [USER] komutu, e-posta kutusunu okumak istediğiniz kullanıcıyı tanımlamak için kullanılır;
- 7. satır: [PASS] komutu, kullanıcının şifresini tanımlamak için kullanılır;
- 9. satır: [LIST] komutu, kullanıcının posta kutusunda bulunan mesajların listesini ister;
- 14. satır: [RETR] komutu, numarası verilen mesajı görüntülemek için kullanılır;
- 29. satır: [DELE] komutu, numarası verilen mesajın silinmesini ister;
- satır 40: [QUIT] komutu, sunucuya işlemin tamamlandığını bildirir;
Sunucunun yanıtı çeşitli şekillerde olabilir:
- [+OK] ile başlayan tek bir satır, müşterinin önceki komutunun başarılı olduğunu belirtir;
- [-ERR] ile başlayan tek bir satır, müşterinin önceki komutunun başarısız olduğunu belirtir;
- aşağıdaki gibi birkaç satır:
- 1. satır [+OK] ile başlar;
- son satır tek bir noktadan oluşur;
16.6.3. POP3 protokolünü uygulayan basit bir komut dosyası

POP3 protokolü, SMTP protokolüyle aynı yapıya sahip olduğundan, [pop3-01.php] betiği, [smtp-01.php] betiğinin bir uyarlamasıdır. Aşağıdaki [config-pop3-01.json] yapılandırma dosyasına sahip olacaktır:
- 3-4. satırlar: Sorgulanan POP3 sunucusu, yerel [hMailServer] sunucusudur;
- 5-6. satırlar: [guest@localhost] kullanıcısının posta kutusu okunmak istenmektedir;
- 7. satır: En fazla 5 e-posta okunacaktır;
[pop3-01.php] komut dosyası şöyledir:
<?php
// POP3 istemcisi (Post Office Protocol), bir posta kutusundaki mesajları okumaya olanak tanır
// iletişim protokolü POP3 istemci-sunucu
// -> istemci, SMTP sunucusunun 110 numaralı bağlantı noktasına bağlanır
// <- sunucu, istemciye bir hoş geldin mesajı gönderir
// -> istemci, USER kullanıcı komutunu gönderir
// <- sunucu OK yanıtını gönderir veya göndermez
// -> istemci PASS mot_de_passe komutunu gönderir
// <- sunucu OK yanıtını verir mi, vermez mi
// -> istemci LIST komutunu gönderir
// <- sunucu OK yanıtını verir veya vermez
// -> istemci, her bir e-posta için RETR numaralı komutu gönderir
// <- sunucu OK yanıtını gönderir veya yanıt vermez. OK yanıtı gelirse, istenen e-postanın içeriği gönderilir
// -> sunucu e-postanın tüm satırlarını gönderir ve sonunda
// tek karakterini içeren bir satırla biter.
// -> istemci, bir e-postayı silmek için DELE numaralı komutu gönderir
// <- sunucu OK yanıtını verir veya vermez
// // -> istemci, sunucuyla diyaloğu sonlandırmak için QUIT komutunu gönderir
// <- sunucu OK yanıtını verir veya vermez
// sunucunun yanıtları +OK metin veya -ERR metin biçimindedir
// Yanıt birden fazla satırdan oluşabilir. Bu durumda son satır tek bir noktadan oluşur
// Alışverişi yapılan metin satırları, RC(#13) ve LF(#10) karakterleriyle bitmelidir
//
// POP3 istemcisi (SendMail Aktarım Protokolü), e-postaları okumaya olanak tanır
//
// hata yönetimi
//ini_set ("error_reporting", E_ALL & ~ E_WARNING & ~E_DEPRECATED & ~E_NOTICE);
//ini_set("display_errors", "off");
//
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulması
declare (strict_types=1);
//
// e-posta gönderme parametreleri
const CONFIG_FILE_NAME = "config-pop3-01.json";
// yapılandırmanın alınması
$mailboxes = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// posta kutularını okuma
foreach ($mailboxes as $name => $infos) {
// takip
print "Lecture de la boîte à lettres [$name]\n";
// posta kutusunu okuma
$résultat = readmail($name, $infos, TRUE);
// sonuç görüntüleme
print "$résultat\n";
}//for
// son
exit;
//readmail
//-----------------------------------------------------------------------
function readmail(string $name, array $infos, bool $verbose = TRUE): string {
// posta kutusunun içeriğini okur [$name]
// tüm mesajları içe aktarır
// her mesaj okunduktan sonra silinir
// $verbose=1 ise, istemci-sunucu iletişimini izler
//
// SMTP sunucusuyla bağlantı kurulur
$connexion = fsockopen($infos["server"], (int) $infos["port"]);
// Hata durumunda geri dönüş
if ($connexion === FALSE) {
return sprintf("Echec de la connexion au site (%s,%s) : %s", $infos["smtp-server"], $infos["smtp-port"]);
}
// $connexion, iki yönlü bir iletişim akışını temsil eder
// istemci (bu program) ile bağlanılan pop3 sunucusu arasında
// bu kanal, komut ve bilgi alışverişi için kullanılır
// bağlantı kurulduktan sonra sunucu bir karşılama mesajı gönderir ve bu mesaj okunur
$erreur = sendCommand($connexion, "", $verbose, TRUE);
if ($erreur !== "") {
// bağlantının sonlandırılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut USER
$erreur = sendCommand($connexion, "USER {$infos["user"]}", $verbose, TRUE);
if ($erreur !== "") {
// bağlantı sonlandırılıyor
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut PASS
$erreur = sendCommand($connexion, "PASS {$infos["password"]}", $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut LIST
$premièreLigne = "";
$erreur = sendCommand($connexion, "LIST", $verbose, TRUE, $premièreLigne);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// mesaj sayısını öğrenmek için 1. satırı analiz eder
$champs = [];
preg_match("/^\+OK (\d+)/", $premièreLigne, $champs);
$nbMessages = (int) $champs[1];
// mesajlar üzerinde döngü
$iMessage = 0;
while ($iMessage < $nbMessages && $iMessage < $infos["maxmails"]) {
// RETR komutu
$erreur = sendCommand($connexion, "RETR " . ($iMessage + 1), $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri dönüş
return $erreur;
}
// komut DELE
$erreur = sendCommand($connexion, "DELE " . ($iMessage + 1), $verbose, TRUE);
if ($erreur !== "") {
// bağlantının kapatılması
fclose($connexion);
// geri
return $erreur;
}
// sonraki mesaj
$iMessage++;
}
// komut QUIT
$erreur = sendCommand($connexion, "QUIT", $verbose, TRUE);
if ($erreur !== "") {
// bağlantı kapatılıyor
fclose($connexion);
// geri
return $erreur;
}
// son
fclose($connexion);
return "Terminé";
}
// --------------------------------------------------------------------------
function sendCommand($connexion, string $commande, bool $verbose, bool $withRCLF, string &$premièreLigne = ""): string {
// $commande'i $connexion kanalına gönderir
// $verbose=1 ise ayrıntılı mod
// $withRCLF=1 ise, RCLF dizisini takasa ekler
// yanıtın ilk satırını [$premièreLigne] içine yerleştir
// ]
// verileri
if ($withRCLF) {
$RCLF = "\r\n";
} else {
$RCLF = "";
}
// $commande boş değilse komut gönder
if ($commande !== "") {
fputs($connexion, "$commande$RCLF");
// olası yankı
if ($verbose) {
affiche($commande, 1);
}
}//if
// yanıt okuma
$réponse = fgets($connexion, 1000);
// 1. satırı hafızaya al
$premièreLigne = $réponse;
// olası yankı
if ($verbose) {
affiche($réponse, 2);
}
// hata kodunun alınması
$codeErreur = substr($réponse, 0, 1);
if ($codeErreur === "-") {
// bir hata oluştu
return substr($réponse, 5);
}
// RETR ve LIST komutlarının, yanıtları birden fazla satırdan oluşan özel durumları
$commande = substr(strtolower($commande), 0, 4);
if ($commande === "list" || $commande === "retr") {
// yanıtın son satırı mı?
$champs = [];
$match = preg_match("/^\.\s+$/", $réponse, $champs);
while (!$match) {
// yanıtın okunması
$réponse = fgets($connexion, 1000);
// olası yankı
if ($verbose) {
affiche($réponse, 2);
}
// yanıtın analizi
$champs = [];
$match = preg_match("/^\.\s+$/", $réponse, $champs);
}//while
}
// hatasız dönüş
return "";
}
// --------------------------------------------------------------------------
function affiche($échange, $sens) {
// $échange'i ekrana yazdır
// eğer $sens=1 ise -->$echange'i görüntüler
// eğer $sens=2 ise, son 2 karakteri (RCLF) hariç <-- $échange'i görüntüler
switch ($sens) {
case 1:
print "--> [$échange]\n";
break;
case 2:
$L = strlen($échange);
print "<-- [" . substr($échange, 0, $L - 2) . "]\n";
break;
}//anahtarı
}
Yorumlar
Daha önce de belirttiğimiz gibi, [pop3-01.php], daha önce incelediğimiz [smtp-01.php] betiğinin bir uyarlamasıdır. Yalnızca temel farklılıkları ele alacağız:
- 55. satır: [readmail] işlevi, posta kutusundaki e-postaları okumakla görevlidir. Bu posta kutusuna bağlanmak için gerekli bilgiler [$infos] sözlüğünde yer almaktadır;
- 61-66. satırlar: POP3 sunucusuyla bağlantı kurulması;
- 71-77. satırlar: sunucu tarafından gönderilen karşılama mesajının okunması;
- 78-85. satırlar: E-postalarını almak istediğimiz kullanıcıyı tanımlamak için [USER] komutu gönderilir;
- satır 86-93: bu kullanıcının şifresini girmek için [PASS] komutu gönderilir;
- 94-102. satırlar: Bu kullanıcının posta kutusunda kaç tane e-posta olduğunu öğrenmek için [LIST] komutu gönderilir.
- 96. satır: [readmail] işlevinin parametrelerine [$premièreLigne] parametresini ekliyoruz. Sunucu, LIST komutuna verdiği yanıtın ilk satırında, posta kutusunda kaç adet mesaj olduğunu belirtir;
- 104-106. satırlar: Yanıtın ilk satırından mesaj sayısını alınır;
- satır 109-128: Her bir mesaj üzerinde döngü yapılır. Her biri için iki komut gönderilir:
- RETR i: i numaralı mesajı almak için (satır 111-117);
- DELE i: mesaj okunduktan sonra silmek için (satır 118-125);
- 129-136. satırlar: sunucuya işlemin tamamlandığını bildirmek için [QUIT] komutu gönderilir;
- satır 178-194: [LIST] ve [RETR] komutları için sunucunun yanıtı birkaç satırdan oluşur; son satır tek bir noktadan oluşur;
Sonuçlar
Çalıştırıldığında şu sonuçlar elde edilir:
Lecture de la boîte à lettres [localhost:110]
<-- [+OK Bienvenue sur sergetahe@localhost]
--> [USER guest@localhost]
<-- [+OK Send your password]
--> [PASS guest]
<-- [+OK Mailbox locked and ready]
--> [LIST]
<-- [+OK 1 messages (305 octets)]
<-- [1 305]
<-- [.]
--> [RETR 1]
<-- [+OK 305 octets]
<-- [Return-Path: guest@localhost]
<-- [Received: from DESKTOP-528I5CU.home (localhost [127.0.0.1])]
<-- [ by DESKTOP-528I5CU with ESMTP]
<-- [ ; Tue, 21 May 2019 14:25:39 +0200]
<-- [Message-ID: <5F912826-F9C4-41B6-BDA7-4A29537781C9@DESKTOP-528I5CU>]
<-- [From: guest@localhost]
<-- [To: guest@localhost]
<-- [Subject: to localhost via localhost]
<-- []
<-- [ligne ]
<-- [ligne ]
<-- [ligne 3]
<-- [.]
--> [DELE 1]
<-- [+OK msg deleted]
--> [QUIT]
<-- [+OK POP3 server saying goodbye…]
Terminé
Done.
Burada, bazı yeteneklerden yoksun basit bir POP3 istemcisi ile karşı karşıyayız:
- güvenli bir POP3 sunucusuyla iletişim kurma yeteneği;
- bir mesaja ekli dosyaları okuma yeteneği;
İlk özelliği, PHP'in [imap] işlevlerini kullanarak uygulayacağız.
16.6.4. PHP'in [imap] işlevleriyle uygulanan POP3 / IMAP istemcisi
Öncelikle, kullandığımız PHP sürümünde [imap] işlevlerinin mevcut olup olmadığını kontrol etmeliyiz. "Bağlantı" başlıklı paragrafta açıklanan [php.ini] dosyasını açıyoruz ve [imap] ile ilgili satırları arıyoruz:

- satırda, [imap] uzantısının etkinleştirildiğinden emin olun.
[imap-01.php] komut dosyası, aşağıdaki jSON ve [config-imap-01.json] dosyalarını işleyecektir:
[config-imap-01.json] dosyası, bağlantı kurulacak IMAP / POP3 sunucularının bir dizisini tanımlar. Her bir öğe bir [clé:valeur] yapısıdır; burada:
- [clé]: İletişim kurulacak sunucudur. Burada iki tane bulunmaktadır:
- [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]: 993 numaralı bağlantı noktasında dinleme yapan [imap.gmail.com] sunucusunu belirtir. İstemci/sunucu protokolü IMAP'tir. /ssl parametresi, istemci-sunucu iletişiminin güvenli olduğunu belirtir. /novalidate-cert parametresi ise istemciden, sunucunun göndereceği güvenlik sertifikasını doğrulamamasını ister. Son olarak, IMAP sunucusu, aynı kullanıcı için bir dizi posta kutusunu yönetir. IMAP sunucusunun URL adresinde INBOX değerini belirterek, normalde yeni mesajların geldiği INBOX adlı posta kutusuyla ilgilendiğimizi belirtmiş oluruz;
- [{localhost:110/pop3}INBOX]: 110 numaralı bağlantı noktasını dinleyen [localhost] sunucusunu belirtir. Buradaki istemci/sunucu protokolü POP3’tir;
- [valeur]: Aşağıdaki bilgileri içeren bir sözlük;
- [imap-server]: IMAP veya POP3 sunucusunun adı;
- [imap-port]: IMAP veya POP3 sunucusunun bağlantı noktası;
- [user]: Posta kutusunu okumak istediğiniz hesap sahibi;
- [password]: şifresi;
- [output-dir]: mesajların kaydedileceği klasör;
- [prefix]: Mesajların kaydedileceği dosyaların adı prefixN biçiminde olacaktır; burada N, mesaj numarasıdır;
- [pop3]: TRUE'e bir boole değeri atayarak, kullanılan protokolün POP3 olduğunu belirtir. Bu durumda, bir mesaj okunduktan sonra silinecektir. Bu, POP3 sunucularının olağan çalışma şeklidir: okunan bir mesaj sunucuda saklanmaz;
[imap-01.php] komut dosyası şu şekildedir:
<?php
// e-postaları okumaya olanak tanıyan IMAP (İnternet Mesaj Erişim Protokolü) istemcisi
//
// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyulması
declare (strict_types=1);
// hata yönetimi
error_reporting(E_ALL & ~ E_WARNING & ~E_DEPRECATED & ~E_NOTICE);
//ini_set("display_errors", "off");
//
//
// posta okuma parametreleri
const CONFIG_FILE_NAME = "config-imap-01.json";
// yapılandırmayı alıyoruz
$mailboxes = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// posta kutularını okuma
foreach ($mailboxes as $name => $infos) {
// izleme
print "------------Lecture de la boîte à lettres [$name]\n";
// posta kutusunu okuma
readmailbox($name, $infos);
}
// son
exit;
//-----------------------------------------------------------------------
function readmailbox(string $name, array $infos): void {
// Bağlantı denemesi
$imapResource = imap_open($name, $infos["user"], $infos["password"]);
// imap_open() işlevinin dönüşünü test etme
if (!$imapResource) {
// Başarısız
print "La connexion au serveur [$name] a échoué : " . imap_last_error() . "\n";
} else {
// Bağlantı kuruldu
print "Connexion établie avec le serveur [$name].\n";
// posta kutusundaki toplam mesaj sayısı
$nbmsg = imap_num_msg($imapResource);
print "Il y a [$nbmsg] messages dans la boîte à lettres [$name]\n";
// Mevcut posta kutusundaki okunmamış mesajlar
if ($nbmsg > 0) {
print "Récupération de la liste des messages non lus de la boîte à lettres [$name]\n";
$msgNumbers = imap_search($imapResource, 'UNSEEN');
if ($msgNumbers === FALSE) {
print "Il n'y a pas de nouveaux messages dans la boîte à lettres [$name]\n";
} else {
foreach ($msgNumbers as $msgNumber) {
// $msgNumber numaralı mesajla ilgili bilgiler alınıyor
$infosMail = imap_headerinfo($imapResource, $msgNumber);
if ($infosMail === FALSE) {
print "Statut du message n° [$msgNumber] de la boîte à lettres [$name] non récupéré : " . imap_last_error() . "\n";
} else {
print "Statut du message n° [$msgNumber] de la boîte à lettres [$name]\n";
print_r($infosMail);
}
// $msgNumber numaralı mesajın gövdesi alınır
getMailBody($imapResource, $msgNumber, $infos);
// protokol POP3 ise, mesaj siliniyor
$pop3 = $infos["pop3"];
if ($pop3 !== NULL) {
// mesaj iki aşamada silinir
imap_delete($imapResource, $msgNumber);
imap_expunge($imapResource);
}
}
}
}
}
// bağlantı kapatılır
$imapClose = imap_close($imapResource);
if (!$imapClose) {
// Başarısız
print "La fermeture de la connexion a échoué : " . imap_last_error() . "\n";
} else {
// başarılı
print "Fermeture de la connexion réussie.\n";
}
}
function getMailBody($imapResource, int $msgNumber, array $infos): void {
// $msgNumber numaralı mesajın gövdesi alınır
$corpsMail = imap_body($imapResource, $msgNumber);
print "Enregistrement du message dans le fichier {$infos["output-dir"]}/{$infos["prefix"]}$msgNumber\n";
// Gerekirse klasör oluşturuluyor
if (!file_exists($infos["output-dir"])) {
mkdir($infos["output-dir"]);
}
// mesaj kaydedilir
if (!file_put_contents($infos["output-dir"] . "/" . $infos["prefix"] . $msgNumber, $corpsMail)) {
print "Echec de l'enregistrement\n";
}
}
Açıklamalar
- 19-24. satırlar: yapılandırma dosyasında bulunan tüm sunucular üzerinde döngü yapılır;
- 32. satır: [raedmailbox] işlevi, [$name]'te belirtilen posta kutusunu okur;
- satır 32: IMAP bağlantısı açılır;
- 1. parametre, okunacak posta kutusunun URL IMAP değeridir;
- ikinci parametre, bu posta kutusunun sahibi olan kullanıcının adıdır;
- üçüncü parametre, kullanıcının şifresidir;
[imap_open] işlevi, posta kutusunun URL IMAP değerinde /ssl parametresi varsa bağlantının güvenliğini sağlar;
- 41. satır: [imap_num_msg] işlevi, posta kutusundaki toplam mesaj sayısını almayı sağlar;
- 46. satır: [imap_search] işlevi, belirli mesajları aramaya olanak tanır. Burada, henüz okunmamış mesajları arıyoruz (UNSEEN). İkinci parametre bir seçim kriteridir. Bu tür kriterlerden yirmiden fazlası mevcuttur. [imap_search] işlevi, mesaj numaralarından oluşan bir dizi döndürür. Bu numaralar iki şekilde olabilir: sıra numarası veya UID mesaj kimliği. Varsayılan olarak, [imap_search] işlevi sıra numaralarından oluşan bir dizi döndürür. Üçüncü bir [SE_UID] parametresi eklenirse, mesajların UID tanımlayıcıları elde edilir;
- 47. satır: [imap_search] işlevi, herhangi bir mesaj bulamadığında FALSE boole değerini döndürür;
- 50. satır: Okunmamış tüm mesajlar üzerinde döngü yapılır;
- 52. satır: Bir mesajın başlıkları, [imap_headerinfo] işleviyle elde edilebilir. Bu işlevin ikinci parametresi normalde bir mesaj sıra numarasıdır. Bir mesaja UID tanımlayıcısını atamak istiyorsak, üçüncü parametreyi [FT_UID] olarak ayarlamalıyız;
- 53. satır: [imap_headerinfo] işlevi, işini yapamadığı takdirde FALSE boole değerini döndürür. Aksi takdirde, 57. satırdaki [print_r] işleviyle görüntülenen karmaşık bir nesne döndürür;
- 60. satır: Başlıklarından sonra, şimdi [imap_body] işleviyle mesajın gövdesi isteniyor. Bu işlev, işini yapamadığı takdirde NULL değerini döndürür;
- 84-87. satırlar: Mesaj gövdesi yerel bir dosyaya kaydedilir;
- 63-68. satırlar: Kullanılan protokol POP3 ise, az önce okunan mesaj silinir:
- [imap_delete] işlevi, mesajı "silinecek" olarak işaretler ancak silmez;
- [imap_expunge] işlevi, "silinecek" olarak işaretlenen tüm mesajları fiziksel olarak siler;
- 74. satır: IMAP sunucusuyla bağlantı kesilir. Bunun için [imap_close] işlevi kullanılır;
- 86. satır: [imap_body] işlevi, numarasına göre belirlenen bir mesajın gövdesini almayı sağlar;
Gmail'deki [php7parlexemple] kullanıcısı ve [localhost]'teki [guest] kullanıcısının yeni mesajları olması için [smtp-02.json] komut dosyasını çalıştıralım. Bunu yaptıktan sonra, [imap-01.php] komut dosyasını çalıştırarak posta kutularını okuyalım.
Konsol çıktıları şu şekildedir:
------------Lecture de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Connexion établie avec le serveur [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX].
Il y a [27] messages dans la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Récupération de la liste des messages non lus de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Statut du message n° [26] de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
stdClass Object
(
[date] => Wed, 22 May 2019 10:08:24 +0000
[Date] => Wed, 22 May 2019 10:08:24 +0000
[subject] => test-gmail-via-gmail
[Subject] => test-gmail-via-gmail
[message_id] => <d8405cac62d57bd9c531ea79c146c72d@swift.generated>
[toaddress] => php7parlexemple@gmail.com
[to] => Array
(
[0] => stdClass Object
(
[mailbox] => php7parlexemple
[host] => gmail.com
)
)
[fromaddress] => php7parlexemple@gmail.com
[from] => Array
(
[0] => stdClass Object
(
[mailbox] => php7parlexemple
[host] => gmail.com
)
)
[reply_toaddress] => php7parlexemple@gmail.com
[reply_to] => Array
(
[0] => stdClass Object
(
[mailbox] => php7parlexemple
[host] => gmail.com
)
)
[senderaddress] => php7parlexemple@gmail.com
[sender] => Array
(
[0] => stdClass Object
(
[mailbox] => php7parlexemple
[host] => gmail.com
)
)
[Recent] =>
[Unseen] => U
[Flagged] =>
[Answered] =>
[Deleted] =>
[Draft] =>
[Msgno] => 26
[MailDate] => 22-May-2019 10:08:29 +0000
[Size] => 19086
[udate] => 1558519709
)
Enregistrement du message dans le fichier output/gmail-imap/message-26
Statut du message n° [27] de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
stdClass Object
(
…
)
Enregistrement du message dans le fichier output/gmail-imap/message-27
Fermeture de la connexion réussie.
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [1] messages dans la boîte à lettres [{localhost:110/pop3}]
Récupération de la liste des messages non lus de la boîte à lettres [{localhost:110/pop3}]
Statut du message n° [1] de la boîte à lettres [{localhost:110/pop3}]
stdClass Object
(
…
)
Enregistrement du message dans le fichier output/localhost-pop3/message-1
Fermeture de la connexion réussie.
Done.
Bu sonuçların hemen ardından [imap-01.php] komut dosyasını yeniden çalıştırırsak, sonuçlar şu şekilde olur:
------------Lecture de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Connexion établie avec le serveur [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX].
Il y a [27] messages dans la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Récupération de la liste des messages non lus de la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Il n'y a pas de nouveaux messages dans la boîte à lettres [{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX]
Fermeture de la connexion réussie.
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [0] messages dans la boîte à lettres [{localhost:110/pop3}]
Fermeture de la connexion réussie.
- 3. satır: Gmail posta kutusunda hala aynı sayıda mesaj var, ancak artık okunmamış yeni mesaj kalmadı (5. satır). Bu, önceki çalıştırmanın okunmuş mesajların durumunu "okunmamış"tan "okunmuş"a değiştirdiğini gösterir;
- 9. satır: [guest@localhost] kullanıcı hesabında artık mesaj kalmamıştır. Bunun nedeni, önceki işlemde [localhost] hesabındaki okunmuş mesajların silinmiş olmasıdır;
Mesajlar yerel olarak kaydedildi:

Örneğin, Gmail'deki 26 numaralı mesajın içeriğine bakıldığında şunlar görülür:
--_=_swift_1558519704_f31b373d6e416dc88eb4db0e45fb3a95_=_
Content-Type: multipart/alternative;
boundary="_=_swift_1558519706_9bffb48891232e50ab645383ca62242d_=_"
--_=_swift_1558519706_9bffb48891232e50ab645383ca62242d_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
ligne 1
ligne 2
ligne 3
--_=_swift_1558519706_9bffb48891232e50ab645383ca62242d_=_
Content-Type: text/HTML; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<b>ligne 1<br/>ligne 2<br/>ligne 3</b>
--_=_swift_1558519706_9bffb48891232e50ab645383ca62242d_=_--
--_=_swift_1558519704_f31b373d6e416dc88eb4db0e45fb3a95_=_
Content-Type: application/pdf; name=Hello.pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=Hello.pdf
JVBERi0xLjUKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURl
Y29kZT4+CnN0cmVhbQp4nHWPuQoCQQyG+3mK1MKMyThHFoaAq7uF3cKAhdh5gIXgNr6+swcWshII
……………………………….…
OTQwODU4RDUzRDVENjU0QzJCNTM3Mjc+IF0KL0RvY0NoZWNrc3VtIC9DMjU3MUY1MUNDRjgwQ0Ex
ODU0OUI0RTQ4NDkwMDM3OAo+PgpzdGFydHhyZWYKMTIzMjYKJSVFT0YK
--_=_swift_1558519704_f31b373d6e416dc88eb4db0e45fb3a95_=_--
- 11-13. satırlar: düz metin mesaj;
- 19. satır: HTML mesajı;
- 25. satır: ekli dosya;
Farklı mesaj türlerini ve ekleri ayrı dosyalarda elde etmek için bu komut dosyasını geliştirmeye çalışalım.
16.6.5. Geliştirilmiş POP3 / IMAP istemcisi
[imap-01.php] komut dosyasında, i numaralı mesajın gövdesi, hem farklı mesaj türlerini hem de çeşitli eklerin kodlanmış içeriğini içeren bir metin dosyası olarak görüntülenir. Mesajın yapısını elde ederek bu farklı bölümleri belirlemek mümkündür. [imap-02.php] komut dosyasında, [getMailBody] işlevini şu şekilde değiştiriyoruz:
function getMailBody($imapResource, int $msgNumber, array $infos): void {
// mesajın yapısı alınır
$structure=imap_fetchstructure($imapResource, $msgNumber);
// mesaj görüntülenir
print_r($structure);
}
- 3. satır: Mesajın yapısını talep ediyoruz;
- 5. satır: Yapıyı görüntülüyoruz;
Buradaki amaç, bir mesajın yapısında yer alan bilgileri öğrenerek, mesajın farklı bölümlerini nasıl elde edebileceğimizi görmektir. Örneğimizde, mesaj [smtp-02.php] komut dosyası tarafından aşağıdaki [config-smtp-02.json] yapılandırmasıyla gönderilmektedir:
Dolayısıyla, [guest@localhost]'e (11-15. satırlar) beş ek içeren bir mesaj gönderilir. [imap-02.php] komut dosyası, aşağıdaki [config-imap-01.json] yapılandırmasıyla çalıştırılır:
Dolayısıyla, [guest@localhost] posta kutusu istismar edilmektedir (5. satır). Ardından, [imap-02.php] komut dosyası, [smtp-02.php] tarafından gönderilen mesajın yapısını görüntüler. Konsola görüntülenen bu yapı şöyledir:
stdClass Object
(
[type] => 1
[encoding] => 0
[ifsubtype] => 1
[subtype] => MIXED
[ifdescription] => 0
[ifid] => 0
[bytes] => 253599
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => BOUNDARY
[value] => _=_swift_1558872295_5bc8ee2ca8b3723c0b39ca8bbfbebdeb_=_
)
)
[parts] => Array
(
[0] => stdClass Object
(
[type] => 1
[encoding] => 0
[ifsubtype] => 1
[subtype] => ALTERNATIVE
[ifdescription] => 0
[ifid] => 0
[bytes] => 429
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => BOUNDARY
[value] => _=_swift_1558872296_1e51aae79dfca4e7e0af112489fe8734_=_
)
)
[parts] => Array
(
[0] => stdClass Object
(
[type] => 0
[encoding] => 4
[ifsubtype] => 1
[subtype] => PLAIN
[ifdescription] => 0
[ifid] => 0
[lines] => 3
[bytes] => 27
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => CHARSET
[value] => utf-8
)
)
)
[1] => stdClass Object
(
[type] => 0
[encoding] => 4
[ifsubtype] => 1
[subtype] => HTML
[ifdescription] => 0
[ifid] => 0
[lines] => 1
[bytes] => 40
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => CHARSET
[value] => utf-8
)
)
)
)
)
[1] => stdClass Object
(
[type] => 3
[encoding] => 3
[ifsubtype] => 1
[subtype] => VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT
[ifdescription] => 0
[ifid] => 0
[bytes] => 16302
[ifdisposition] => 1
[disposition] => ATTACHMENT
[ifdparameters] => 1
[dparameters] => Array
(
[0] => stdClass Object
(
[attribute] => FILENAME
[value] => Hello from SwiftMailer.docx
)
)
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => NAME
[value] => Hello from SwiftMailer.docx
)
)
)
[2] => stdClass Object
(
[type] => 3
[encoding] => 3
[ifsubtype] => 1
[subtype] => PDF
[ifdescription] => 0
[ifid] => 0
[bytes] => 17514
[ifdisposition] => 1
[disposition] => ATTACHMENT
[ifdparameters] => 1
[dparameters] => Array
(
[0] => stdClass Object
(
[attribute] => FILENAME
[value] => Hello from SwiftMailer.pdf
)
)
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => NAME
[value] => Hello from SwiftMailer.pdf
)
)
)
[3] => stdClass Object
(
…
)
[4] => stdClass Object
(
…
)
[5] => stdClass Object
(
[type] => 2
[encoding] => 3
[ifsubtype] => 1
[subtype] => RFC822
[ifdescription] => 0
[ifid] => 0
[lines] => 1881
[bytes] => 146682
[ifdisposition] => 1
[disposition] => ATTACHMENT
[ifdparameters] => 1
[dparameters] => Array
(
[0] => stdClass Object
(
[attribute] => FILENAME
[value] => test-localhost.eml
)
)
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => NAME
[value] => test-localhost.eml
)
)
[parts] => Array
(
…
)
)
)
)
Açıklamalar
- [imap_fetchstructure] işlevinin PHP belgeleri, işlev tarafından döndürülen nesnenin çeşitli alanlarının anlamlarını açıklamaktadır:

[type] alanındaki sayısal değerlerin anlamları şunlardır:

[encoding] alanındaki sayısal değerlerin anlamları şöyledir:

[imap-01.php] tarafından kaydedilen mesaj, şu metinle başlıyordu:
Return-Path: <php7parlexemple@gmail.com>
Received: from [127.0.0.1] (lfbn-1-11924-110.w90-93.abo.wanadoo.fr. [90.93.230.110])
by smtp.gmail.com with ESMTPSA id e14sm7773816wma.41.2019.05.26.03.11.53
for <php7parlexemple@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Sun, 26 May 2019 03:11:54 -0700 (PDT)
Message-ID: <e613c47a421a66e2cf7f8e319616ec49@swift.generated>
Date: Sun, 26 May 2019 10:11:53 +0000
Subject: test-gmail-via-gmail
From: php7parlexemple@gmail.com
To: php7parlexemple@gmail.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_"
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
Content-Type: multipart/alternative; boundary="_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_"
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
ligne 1
ligne 2
ligne 3
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_
Content-Type: text/HTML; charset=utf-8
Content-Transfer-Encoding: quoted-printable
<b>ligne 1<br/>ligne 2<br/>ligne 3</b>
--_=_swift_1558865513_43c6d2a54065e4917fb06e3327f8d927_=_--
--_=_swift_1558865513_a3a939017128a4cfb867e968bce5df49_=_
- 15. ve 33. satırlar, [multipart/mixed] türündeki mesajı sınırlar (m satırı);
- 18. ve 16. satırlar, mesajın 1. bölümünü sınırlar: düz metin mesajı;
- 26. ve 32. satırlar, mesajın ikinci bölümünü sınırlar: HTML mesajı;
Yukarıdaki mesajın çeşitli bilgilerini, [imap_fetchstructure] tarafından döndürülen nesnede buluyoruz:
stdClass Object
(
[type] => 1
[encoding] => 0
[ifsubtype] => 1
[subtype] => MIXED
[ifdescription] => 0
[ifid] => 0
[bytes] => 253599
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => BOUNDARY
[value] => _=_swift_1558872295_5bc8ee2ca8b3723c0b39ca8bbfbebdeb_=_
)
)
[parts] => Array
(
[0] => stdClass Object
(
[type] => 1
[encoding] => 0
[ifsubtype] => 1
[subtype] => ALTERNATIVE
[ifdescription] => 0
[ifid] => 0
[bytes] => 429
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => BOUNDARY
[value] => _=_swift_1558872296_1e51aae79dfca4e7e0af112489fe8734_=_
)
)
[parts] => Array
(
[0] => stdClass Object
(
[type] => 0
[encoding] => 4
[ifsubtype] => 1
[subtype] => PLAIN
[ifdescription] => 0
[ifid] => 0
[lines] => 3
[bytes] => 27
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => CHARSET
[value] => utf-8
)
)
)
[1] => stdClass Object
(
[type] => 0
[encoding] => 4
[ifsubtype] => 1
[subtype] => HTML
[ifdescription] => 0
[ifid] => 0
[lines] => 1
[bytes] => 40
[ifdisposition] => 0
[ifdparameters] => 0
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => CHARSET
[value] => utf-8
)
)
)
)
)
- 3. satır: mesajın türü MIME (Multipurpose Internet Mail Extensions) [multipart];
- 4. satır: Mesaj 7 bit olarak kodlanmıştır;
- 5. satır: [ifsubtype]=1, yapıda bir [subtype] alanı olduğunu gösterir;
- 6. satır: [subtype] alanı, bir MIME alt türünü belirtir; burada [mixed] türüdür. Sonuç olarak, belgenin MIME türü, [multipart/mixed]'tir;
- 7. satır: [ifdescription]=0, yapıda [description] alanı olmadığını gösterir;
- 8. satır: [ifid]=0, yapıda [id] alanı olmadığını gösterir;
- 10. satır: [ifdisposition]=0, yapıda [disposition] alanı olmadığını gösterir;
- 11. satır: [ifdparameters]=0, yapıda [dparameters] alanı olmadığını gösterir;
- 12. satır: [ifparameters]=1, yapıda [parameters] alanı olduğunu gösterir;
- 13. satır: [parameters] alanı, mesajın parametrelerini tanımlar. Burada sadece bir tane vardır;
- satır 15-19: Bu nesne, metin mesajının bir sonraki satırını tanımlar:
Bu satırlar mesajı sınırlandırmak için kullanılır. [imap-01.php] tarafından alınan mesajda, az önce açıklanan mesaj kısmı m) satırına karşılık gelir. [boundary] özniteliği aynı değildir, çünkü ekran görüntüleri aynı mesaja aittir ancak farklı zamanlarda gönderilmiştir;
- 23. satır: Mesajın farklı bölümlerinin yapısı burada başlar;
- 25-45. satırlar: Bu ilk bölüm [multipart/alternative] türündedir. Mesaj metninin p) satırına karşılık gelir;
- 47. satır: Bu ilk bölümün de kendi alt bölümleri vardır;
- 47-70. satırlar: Bu ilk alt bölüm, [text/plain] (51. ve 54. satırlar), [ENCQUOTEDPRINTABLE] türüyle kodlanmıştır (52. satır) ve bir [charset=utf-8] parametresine sahiptir (66-67. satırlar);
- 49-72. satırlar, metin mesajının s-x satırlarını tanımlar;
- 74-99. satırlar: [multipart/alternative] bölümünün ikinci alt bölümünü tanımlar;
- 74-99. satırlar: Bu ikinci alt bölüm, [text/HTML] türündedir (76. ve 79. satırlar), [ENCQUOTEDPRINTABLE] (satır 77) ve bir [charset=utf-8] parametresine sahiptir (satır 89-93);
- 74-99. satırlar, metin mesajının aa-ad satırlarını tanımlar;
[multipart/alternative] bölümü artık sona ermiştir. Aşağıdaki metinle açıklanan [application/vnd.openxmlformats-officedocument.wordprocessingml.document] bölümü başlıyor:
Burada da bu bilgiler, [imap_fetchstructure] işlevi tarafından bildirilen nesnede bulunur:
[1] => stdClass Object
(
[type] => 3
[encoding] => 3
[ifsubtype] => 1
[subtype] => VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT
[ifdescription] => 0
[ifid] => 0
[bytes] => 16302
[ifdisposition] => 1
[disposition] => ATTACHMENT
[ifdparameters] => 1
[dparameters] => Array
(
[0] => stdClass Object
(
[attribute] => FILENAME
[value] => Hello from SwiftMailer.docx
)
)
[ifparameters] => 1
[parameters] => Array
(
[0] => stdClass Object
(
[attribute] => NAME
[value] => Hello from SwiftMailer.docx
)
)
)
- 1. satır: Bu, genel mesajın ikinci kısmıdır. Hatırlanacağı üzere, ilk kısım [multipart/alternative] türündeydi;
- 3-6. satırlar: Bu ikinci kısım, [application/vnd.openxmlformats-officedocument.wordprocessingml.document] türündedir (3. ve 6. satırlar) ve Base 64 ile kodlanmıştır (4. satır);
- 11. satır: Bu ikinci kısım bir ektir (11. satır) ve iki parametreye sahiptir: [filename=Hello from SwiftMailer.docx] (15-21. satırlar) ve [name=Hello from SwiftMailer.docx] (26-32. satırlar). Bu son parametrenin metin mesajında bulunmadığına dikkat edilmelidir. Dolayısıyla, [imap_fetchstructure] işlevine eklenmiştir;
Mesajın beş eki için de 1-36. satırlar tekrarlanır.
Dolayısıyla, [imap_fetch_structure] işlevi bize bir mesajın yapısını elde etmemizi sağlar. Bu yapı, kendileri de alt bölümlere sahip olabilen bölümleri tanımlar. Bir bölümün veya alt bölümün metnini elde etmek için [imap_fetchbody] işlevi kullanılır.
Mesajın gövdesini elde etmemizi sağlayan [getMailBody] işlevini şu şekilde değiştiriyoruz:
function getMailBody($imapResource, int $msgNumber, array $infos, object $infosMail): void {
// mesajın yapısı alınır
$structure = imap_fetchstructure($imapResource, $msgNumber);
if ($structure !== FALSE) {
// bu farklı bölümleri alınır
getParts($imapResource, $msgNumber, $infos, $infosMail, $structure);
}
}
function getParts($imapResource, int $msgNumber, array $infos, object $infosMail, stdclass $part, string $sectionNumber = "0"): void {
// bölüm numarası hesaplanır
if (substr($sectionNumber, 0, 2) === "0.") {
$sectionNumber = substr($sectionNumber, 2);
}
print "-----contenu de la partie n° [$sectionNumber]\n";
// içerik türü
print "Content-Type: ";
switch ($part->type) {
case TYPETEXT:
print "TEXT/{$part->subtype}\n";
break;
case TYPEMULTIPART:
print "MULTIPART/{$part->subtype}\n";
break;
case TYPEAPPLICATION:
print "APPLICATION/{$part->subtype}\n";
break;
case TYPEMESSAGE:
print "MESSAGE/{$part->subtype}\n";
break;
default:
print "UNKNOWN/{$part->subtype}\n";
break;
}
// kodlama türü
$encodings=["7 bits", "8 bits", "binaire", "base 64", "quoted-printable", "autre"];
print "Transfer-Encoding : ".$encodings[$part->encoding]."\n";
// varsa alt bölümlere geçilir
if (isset($part->parts)) {
for ($i = 1; $i <= count($part->parts); $i++) {
// mesajın yeni bir bölümü
$subpart = $part->parts[$i - 1];
// özyinelemeli çağrı - [$subpart] bölümünün gövdesi istenir
getParts($imapResource, $msgNumber, $infos, $infosMail, $subpart, "$sectionNumber.$i");
}
}
}
Açıklamalar
- 3. satır: Mesajın yapısını alıyoruz;
- 6. satır: Yapının [parts] tablosunda bulunan farklı bölümlerini görüntülemek istiyoruz;
- 10. satır: [getParts] işlevi aşağıdaki parametreleri alır:
- [$imapResource]: IMAP sunucusuna bağlantı;
- [$msgNumber]: parçaları alınmak istenen mesajın sıra numarası;
- [$infos]: Bulunacak bölümlerin yerel dosya sisteminde nereye kaydedileceğine dair bilgiler;
- [$infosMail]: e-posta hakkında genel bilgiler (gönderen, alıcı(lar), konu…;
- [$part]: mesajın bir bölümünü temsil eden bir nesne;
- [$sectionNumber]: mesajın bir bölüm (veya kısım) numarası;
- 17-34. satırlar: Mesajın [$section] numaralı bölümünün içerik türü görüntülenir. Bunun için [$part] bölümündeki [$part→type] ve [$part→subtype] alanlarından yararlanılır;
- satır 36-37: [$sectionNumber] bölümünün kodlama türü görüntülenir;
- satır 40-47: az önce bilgilerini görüntülediğimiz bölümün kendisinin de alt bölümleri olabilir;
- satır 41-46: Eğer öyleyse, az önce görüntülediğimiz bölümün çeşitli alt bölümlerinin içerik türlerini görmek istenir. Burada, [getParts] işlevine özyinelemeli bir çağrı yapılır;
Yine, [smtp-02.php] komut dosyası ile [php7parlexemple@gmail.com] adlı Gmail kullanıcısına bir e-posta gönderiyoruz ve bunu önceki [imap-02.php] komut dosyası ile okuyoruz. Bu işlem, konsolda aşağıdaki sonuçları verir:
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [1] messages dans la boîte à lettres [{localhost:110/pop3}]
Récupération de la liste des messages non lus de la boîte à lettres [{localhost:110/pop3}]
-----contenu de la partie n° [0]
Content-Type: MULTIPART/MIXED
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1]
Content-Type: MULTIPART/ALTERNATIVE
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1.1]
Content-Type: TEXT/PLAIN
Transfer-Encoding : quoted-printable
-----contenu de la partie n° [1.2]
Content-Type: TEXT/HTML
Transfer-Encoding : quoted-printable
-----contenu de la partie n° [2]
Content-Type: APPLICATION/VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT
Transfer-Encoding : base 64
-----contenu de la partie n° [3]
Content-Type: APPLICATION/PDF
Transfer-Encoding : base 64
-----contenu de la partie n° [4]
Content-Type: APPLICATION/VND.OASIS.OPENDOCUMENT.TEXT
Transfer-Encoding : base 64
-----contenu de la partie n° [5]
Content-Type: UNKNOWN/PNG
Transfer-Encoding : base 64
-----contenu de la partie n° [6]
Content-Type: MESSAGE/RFC822
Transfer-Encoding : base 64
-----contenu de la partie n° [6.1]
Content-Type: TEXT/PLAIN
Transfer-Encoding : 7 bits
Fermeture de la connexion réussie.
Mesajın farklı içerik türlerini ve kodlama türlerini başarıyla elde edebiliyoruz. Bölümlerin numaralandırılması şu kurala göre yapılır:
- 6-7. satırlar: Mesajın tamamını temsil eden [multipart/mixed] bölümü 0 numarasını taşır. Bu nesnenin farklı bölümleri ise sırasıyla 1, 2… numaralarını taşır.
Mesajın toplamda beş bölümü vardır:
- 9-10. satırlar: 1 numaralı [multipart/alternative] bölümü;
- 17-18. satırlar: 2 numaralı [APPLICATION/VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT] bölümü. Bu, bir Word dosyasının eki;
- 20-21. satırlar: 3 numaralı [APPLICATION/PDF] bölümü. Bu, PDF dosyasının eki;
- 23-24. satırlar: 4 numaralı [APPLICATION/VND.OASIS.OPENDOCUMENT.TEXT] bölümü. Bu, OpenOffice dosyasının eki;
- 26-27. satırlar: 5 numaralı [UNKNOWN/PNG] bölümü. Bu, bir resim dosyasının eki;
- 30-31. satırlar: 6 numaralı [MESSAGE/RFC822] bölümü. Bu, bir e-postanın eki;
Bir bölümün alt bölümleri varsa, bunlar x.1, x.2… şeklinde numaralandırılır; burada x, üst bölümün numarasıdır. Dolayısıyla:
- 11-12. satırlar: [multipart/alternative] bölümünün 1. bölümü 1.1 numarasını taşır. Bu, [text/plain] türünde bir içeriktir: e-postanın mesajı;
- 14-15. satırlar: [multipart/alternative] parçasının 2. bölümü 1.2 numaralıdır. Bu, [text/HTML] türünde bir içeriktir: HTML formatındaki e-posta mesajı;
- 32-33. satırlar: [MESSAGE/RFC822] ek dosyasının 1. bölümü 6.1 numaralıdır. Bu, [text/plain] türünde bir içeriktir. Aslında MIME standardına göre, bir e-posta eki [MESSAGE/RFC822]'in bölümlerinin numaralandırılması, daha önce açıklanan kuraldan farklıdır. Dolayısıyla, [MESSAGE/RFC822] ek dosyasının 1. bölümü 6.1 numarasını değil, başka bir numarayı taşır;
Artık bir e-postanın farklı bölümlerini ve alt bölümlerini nasıl tespit edeceğimizi bildiğimize göre, geriye sadece bunların içeriğini almamız kalıyor.
Komut dosyasının kodu şu şekilde değişir:
function getParts($imapResource, int $msgNumber, array $infos, object $infosMail, stdclass $part, string $sectionNumber = "0"): void {
// bölüm numarasının hesaplanması
if (substr($sectionNumber, 0, 2) === "0.") {
$sectionNumber = substr($sectionNumber, 2);
}
print "-----contenu de la partie n° [$sectionNumber]\n";
// içerik türü
print "Content-Type: ";
switch ($part->type) {
case TYPETEXT:
print "TEXT/{$part->subtype}\n";
break;
case TYPEMULTIPART:
print "MULTIPART/{$part->subtype}\n";
break;
case TYPEAPPLICATION:
print "APPLICATION/{$part->subtype}\n";
break;
case TYPEMESSAGE:
print "MESSAGE/{$part->subtype}\n";
break;
default:
print "UNKNOWN/{$part->subtype}\n";
break;
}
// kodlama türü
$encodings = ["7 bits", "8 bits", "binaire", "base 64", "quoted-printable", "autre"];
print "Transfer-Encoding : " . $encodings[$part->encoding] . "\n";
// bu bir mesaj mı?
if ($part->type === TYPEMESSAGE) {
// bu mesajın alt bölümleri (ekli e-posta) işlenmeyecek
// ekli e-postanın gövdesi görüntülenir
print imap_fetchbody($imapResource, $msgNumber, $sectionNumber);
} else {
// olası alt bölümlere geçiyoruz
if (isset($part->parts)) {
for ($i = 1; $i <= count($part->parts); $i++) {
// mesajın yeni bir bölümü
$subpart = $part->parts[$i - 1];
// özyinelemeli çağrı - [$subpart] bölümünün gövdesi isteniyor
getParts($imapResource, $msgNumber, $infos, $infosMail, $subpart, "$sectionNumber.$i");
}
} else {
// alt bölüm yok - bu durumda mesaj gövdesi görüntülenir
print imap_fetchbody($imapResource, $msgNumber, $sectionNumber);
}
}
}
Açıklamalar
- 46. satır: [imap_fetchbody] işlevi, mesajın [$sectionNumber] numaralı bölümünün gövdesini alır. Bir mesajın bölümlerinin numaralandırılması, daha önce açıklanan kurala göre yapılır;
- 1. satır: “0” bölümüyle başlanır;
- satır 41: Bu bölümün alt bölümleri “0.1”, “0.2” olarak numaralandırılırken, aslında “1”, “2” olarak numaralandırılmaları gerekir…
- 3-5. satırlar: bu tutarsızlık düzeltilir;
- satır 37-43: Eğer mevcut bölümün alt bölümleri varsa, her biri için döngü yapılır (satır 38-43). Bu alt bölümlerin bölüm numarası [$sectionNumber.$i]'tir;
- satır 44-47: Artık alt bölüm kalmadığında, [imap_fetchbody] işleviyle mevcut bölümün ana metni görüntülenir. Örneğimizde bunlar, [text/plain], [text/HTML] bölümleri ve eklerdir;
Bu komut dosyasının çalıştırılması aşağıdaki sonuçları verir:
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [1] messages dans la boîte à lettres [{localhost:110/pop3}]
Récupération de la liste des messages non lus de la boîte à lettres [{localhost:110/pop3}]
-----contenu de la partie n° [0]
Content-Type: MULTIPART/MIXED
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1]
Content-Type: MULTIPART/ALTERNATIVE
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1.1]
Content-Type: TEXT/PLAIN
Transfer-Encoding : quoted-printable
ligne 1
ligne 2
ligne 3
-----contenu de la partie n° [1.2]
Content-Type: TEXT/HTML
Transfer-Encoding : quoted-printable
<b>ligne 1<br/>ligne 2<br/>ligne 3</b>
-----contenu de la partie n° [2]
Content-Type: APPLICATION/VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT
Transfer-Encoding : base 64
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAAC
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
…
AAAAAAAAAF0mAABkb2NQcm9wcy9jb3JlLnhtbFBLAQItABQABgAIAAAAIQCdxkmwcgEAAMcCAAAQ
AAAAAAAAAAAAAAAAAAgpAABkb2NQcm9wcy9hcHAueG1sUEsFBgAAAAALAAsAwQIAALArAAAAAA==
-----contenu de la partie n° [3]
Content-Type: APPLICATION/PDF
Transfer-Encoding : base 64
JVBERi0xLjUKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURl
Y29kZT4+CnN0cmVhbQp4nHWNvQoCMRCE+zzF1sLF2WSTSyAEPD0Lu4OAhdj5AxaC1/j6Rk4s5GSa
…
PDcxQUJGQ0JGQURGODYxM0NBNUJDODNFMDNDNjI1QkQwPgo8NzFBQkZDQkZBREY4NjEzQ0E1QkM4
M0UwM0M2MjVCRDA+IF0KL0RvY0NoZWNrc3VtIC9DMTRCN0Q5N0YwNUU1OTYxQzhDODg0NEI3NkNF
OEIwRQo+PgpzdGFydHhyZWYKMTIzMTQKJSVFT0YK
-----contenu de la partie n° [4]
Content-Type: APPLICATION/VND.OASIS.OPENDOCUMENT.TEXT
Transfer-Encoding : base 64
UEsDBBQAAAgAAAs9uU5exjIMJwAAACcAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQub2Fz
aXMub3BlbmRvY3VtZW50LnRleHRQSwMEFAAACAAACz25TgAAAAAAAAAAAAAAABwAAABDb25maWd1
…
AQIUABQACAgIAAs9uU42l0SORAQAABIRAAALAAAAAAAAAAAAAAAAAI8bAABjb250ZW50LnhtbFBL
AQIUABQACAgIAAs9uU4Uf52+LgEAACUEAAAVAAAAAAAAAAAAAAAAAAwgAABNRVRBLUlORi9tYW5p
ZmVzdC54bWxQSwUGAAAAABEAEQBlBAAAfSEAAAAA
-----contenu de la partie n° [5]
Content-Type: UNKNOWN/PNG
Transfer-Encoding : base 64
iVBORw0KGgoAAAANSUhEUgAABiAAAAEMCAYAAABN1n5OAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAg
AElEQVR4nOy9e5TdV3Xn+Zm7aqprlBq1Rq1Wq7XU6opGrXaMMI6jAcfj9ihu4hAehkAghBASICF0
…
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAA2Mb8f9Q5r2ohJn6/AAAAAElFTkSuQmCC
-----contenu de la partie n° [6]
Content-Type: MESSAGE/RFC822
Transfer-Encoding : base 64
UmV0dXJuLVBhdGg6IGd1ZXN0QGxvY2FsaG9zdA0KUmVjZWl2ZWQ6IGZyb20gWzEyNy4wLjAuMV0g
KGxvY2FsaG9zdCBbMTI3LjAuMC4xXSkNCglieSBERVNLVE9QLTUyOEk1Q1Ugd2l0aCBFU01UUA0K
…
cjJvaEpuNi9BQUFBQUVsRlRrU3VRbUNDDQotLV89X3N3aWZ0XzE1NTg3NzA1MDJfYzRiODA4Yzk5
YzI3ZGVkMDQ1OTViZDExZjRiYWQxMWJfPV8tLQ0K
Fermeture de la connexion réussie.
Açıklamalar
- 14-16. satırlar: [quoted-printable] (13. satır) ile kodlanmış metin mesajının içeriği;
- 20. satır: [quoted-printable] (19. satır) ile kodlanmış HTML mesajının içeriği;
- 24-28. satırlar: [base64] (23. satır) ile kodlanmış Word dosyasının içeriği;
- satır 32-37: PDF dosyasının içeriği, [base64] olarak kodlanmıştır (satır 31);
- satır 41-45: [base64] (satır 40) ile kodlanmış OpenOffice dosyasının içeriği;
- 50-55. satırlar: [base64] olarak kodlanan resim dosyasının içeriği (49. satır);
- satır 59-63: [base64] (satır 58) olarak kodlanmış e-posta ek dosyasının içeriği;
Artık:
- bir e-postanın farklı bölümlerindeki metinleri nasıl bulacağımızı biliyoruz;
- bu metinlerin kodlamasını biliyoruz;
bu metinleri dosyalara kaydedebiliriz.
Kod şu şekilde değişir:
function getParts($imapResource, int $msgNumber, array $infos, object $infosMail, stdclass $part, string $sectionNumber = "0"): void {
// bölüm numarasının hesaplanması
if (substr($sectionNumber, 0, 2) === "0.") {
$sectionNumber = substr($sectionNumber, 2);
}
print "-----contenu de la partie n° [$sectionNumber]\n";
// içerik türü
print "Content-Type: ";
switch ($part->type) {
case TYPETEXT:
print "TEXT/{$part->subtype}\n";
break;
case TYPEMULTIPART:
print "MULTIPART/{$part->subtype}\n";
break;
case TYPEAPPLICATION:
print "APPLICATION/{$part->subtype}\n";
break;
case TYPEMESSAGE:
print "MESSAGE/{$part->subtype}\n";
break;
default:
print "UNKNOWN/{$part->subtype}\n";
break;
}
// kodlama türü
$encodings = ["7 bits", "8 bits", "binaire", "base 64", "quoted-printable", "autre"];
print "Transfer-Encoding : " . $encodings[$part->encoding] . "\n";
// bu bir mesaj mı?
if ($part->type === TYPEMESSAGE) {
// Bu mesajın alt bölümleri işlenmeyecek
savePart($imapResource, $msgNumber, $sectionNumber, $infos, $infosMail);
} else {
// olası alt bölümlere geçiyoruz
if (isset($part->parts)) {
for ($i = 1; $i <= count($part->parts); $i++) {
// mesajın yeni bir bölümü
$subpart = $part->parts[$i - 1];
// rekürsif çağrı - [$subpart] bölümünün gövdesini istiyoruz
getParts($imapResource, $msgNumber, $infos, $infosMail, $subpart, "$sectionNumber.$i");
}
} else {
// alt bölüm yok - bu durumda mesaj gövdesi kaydedilir
savePart($imapResource, $msgNumber, $sectionNumber, $infos, $infosMail);
}
}
}
- 33. ve 45. satırlar: E-postanın [$imapResource, $msgNumber, $sectionNumber] bölümündeki metnin görüntülenmesi artık bir dosyaya kaydedilmesi ile değiştirilmiştir;
[savePart] işlevi şu şekildedir:
// mesaj bölümünün kaydedilmesi
function savePart($imapResource, int $msgNumber, string $sectionNumber, array $infos, object $infosMail): void {
// kaydetme klasörü
$outputDir = $infos["output-dir"] . "/message-$msgNumber";
// klasör yoksa, oluşturulur
if (!file_exists($outputDir)) {
mkdir($outputDir);
}
// kaydedilecek bölümün yapısı
$struct = imap_bodystruct($imapResource, $msgNumber, $sectionNumber);
// belge türü
$type = $struct->type;
// belge alt türü
$subtype = "";
if (isset($struct->subtype)) {
$subtype = strtolower($struct->subtype);
}
// kısmın türü analiz edilir
switch ($type) {
case TYPETEXT:
// metin mesajı durumu: text/xxx
switch ($subtype) {
case plain:
saveText("$outputDir/message.txt", 0, imap_fetchBody($imapResource, $msgNumber, $sectionNumber), $infosMail, $struct);
break;
case HTML:
saveText("$outputDir/message.HTML", 1, imap_fetchBody($imapResource, $msgNumber, $sectionNumber), $infosMail, $struct);
break;
}
break;
default:
// diğer durumlar - yalnızca eklerle ilgilenilir
if (isset($struct->disposition)) {
$disposition = strtolower($struct->disposition);
if ($disposition === "attachment") {
// bir ek ile karşı karşıyayız - bunu kaydediyoruz
saveAttachment($imapResource, $msgNumber, $sectionNumber, $outputDir, $struct);
}
} else {
// bu bölüm işlenmeyecek
print "Partie [$sectionNumber] ignorée\n";
}
break;
}
}
- 3-8. satırlar: Yedekleme dosyasının oluşturulması. Bu dosya, parçaları analiz edilen mesajın numarasını taşır;
- 10. satır: Yedeklenecek mesaj bölümü, [$imapResource, $msgNumber, $sectionNumber] adlı üç parametre ile benzersiz bir şekilde tanımlanır. Bu bölümün yapısı, [imap_bodystruct] işlevi ile sorgulanır;
- 12. satır: Mesaj bölümünün ana türü alınır;
- satır 13-17: alt türü alınır;
- satır 20-30: iki içerik türü işlenir: [text/plain] (satır 23-25) ve [text/HTML] (satır 26-28). Diğer [text/xx] türleri göz ardı edilir;
- 24. satır: [text/plain] bölümündeki metin [message.txt] adlı bir dosyaya kaydedilecektir;
- satır 27: [text/HTML] bölümündeki metin [message.HTML] adlı bir dosyaya kaydedilecektir;
- 31-43. satırlar: ana türü [text] olmayan bölümler işlenir;
- satır 35: Yalnızca mesajın ekleri dikkate alınır;
- 37. satır: ekler, [saveAttachment] işlevi kullanılarak bir dosyaya kaydedilir;
Yukarıdaki kodu özetlersek:
- [saveText] işlevi kullanılarak [text/plain] ve [text/HTML] parçaları kaydedilir. Bu parçalar e-postanın içeriğini temsil eder;
- [saveAttachment] fonksiyonu kullanılarak çeşitli ekler kaydedilir;
[saveText] işlevi şu şekildedir:
// mesajın metni [$text] kaydedilir
function saveText(string $fileName, int $type, string $text, object $infosMail, object $struct) {
// kaydedilecek metnin hazırlanması
// $text kodlanmıştır - kodunu çözüyoruz
switch ($struct->encoding) {
case ENCBASE64:
$text = base64_decode($text);
break;
case ENCQUOTEDPRINTABLE:
$text = quoted_printable_decode($text);
break;
}
// mesaj başlıkları
// gönderen
$from = "From: ";
foreach ($infosMail->from as $expéditeur) {
$from .= $expéditeur->mailbox . "@" . $expéditeur->host . ";";
}
// kime
$to = "To: ";
foreach ($infosMail->to as $destinataire) {
$to .= $destinataire->mailbox . "@" . $destinataire->host . ";";
}
// konu
$subject = "Subject: " . $infosMail->subject;
// kaydedilecek metnin oluşturulması
switch ($type) {
case 0:
// text/plain
$contents = "$from\n$to\n$subject\n\n$text";
break;
case 1:
// text/HTML
$contents = "$from<br/>\n$to<br/>\n$subject<br/>\n<br/>\n$text";
break;
}
// dosyanın oluşturulması
print "sauvegarde d'un message dans [$fileName]\n";
// dosyanın oluşturulması
if (! file_put_contents($fileName, $contents)) {
// dosya oluşturma başarısız
print "Impossible de créer le fichier [$fileName]\n";
}
}
Açıklamalar
- 1. satır:
- [$fileName], [$text] metninin kaydedileceği dosyanın adıdır;
- [$type]: metin dosyası için 0, HTML dosyası için 1 değerindedir;
- [$text]: kaydedilecek metindir. Ancak kodlanmış olduğu için önce kodunun çözülmesi gerekir;
- [$infosMail]: e-posta hakkında genel bilgiler içerir. [from, to, subject] alanlarını kullanacağız;
- [$struct]: Kaydedilmekte olan e-posta bölümünü tanımlayan yapıdır. Bu, kaydedilecek metnin kodlama türünü belirlememizi sağlayacaktır;
- 4-12. satırlar: kaydedilecek metni kod çözüyoruz;
- 13-25. satırlar: e-postadaki [from, to, subject] bilgilerini alıyoruz;
- 27-36. satırlar: Kaydedilecek metnin türüne (0 veya 1) göre düz metin (30. satır) veya HTML metni (34. satır) oluşturulur;
- 40. satır: metnin tamamı [$fileName] dosyasına kaydedilir;
Ekler ise aşağıdaki [saveAttachment] işleviyle kaydedilir:
// ek dosyanın kaydedilmesi
function saveAttachment($imapResource, int $msgNumber, string $sectionNumber, string $outputDir, object $struct) {
// ek dosyanın yapısı analiz ediliyor
// eki kaydedilecek dosyanın adı aranıyor
// bu ad, yapının [dparameters] kısmında bulunuyor
if (isset($struct->dparameters)) {
// [dparameters] öğeleri alınır
$dparameters = $struct->dparameters;
$fileName = "";
// [dparameters] tablosu taranır
foreach ($dparameters as $dparameter) {
// her bir [dparameter], iki özniteliğe sahip bir nesnedir: [attribute, value]
$attribute = strtolower($dparameter->attribute);
// [filename] özniteliği, oluşturulacak dosyanın adına karşılık gelir
// bu durumda dosya adı [$dparameter->value]'tedir
if ($attribute === "filename") {
$fileName = $dparameter->value;
break;
}
}
// eğer bir dosya adı bulunamadıysa, yapının [parameters] özniteliğine bakılır
if ($fileName === "" && isset($struct->parameters)) {
// [parameters] değerini alırız
$parameters = $struct->parameters;
foreach ($parameters as $parameter) {
// Her parametre, iki anahtarlı bir sözlükten oluşur: [attribute, value]
$attribute = strtolower($parameter->attribute);
// eğer öznitelik [name] ise, o zaman [value] dosya adıdır
if ($attribute === "name") {
$fileName = $parameter->value;
// dosya adı kodlanmış olabilir
// örneğin =?utf-8?Q?Dersler-Öğreticiler-Serge-Tah=C3=A9-1568x268=2Ep
// düzenli ifade kullanarak kodlamayı elde ederiz
$champs = [];
$match = preg_match("/=\?(.+?)\?/", $fileName, $champs);
// eşleşme varsa, dosya adını deşifre ediyoruz
if ($match) {
$fileName = iconv_mime_decode($fileName, 0, $champs[1]);
}
break;
}
}
}
}
// dosya adı bulunursa, eki kaydedilir
if ($fileName !== "") {
// eki kaydedme
$fileName = "$outputDir/$fileName";
print "sauvegarde de l'attachement dans [$fileName]\n";
// dosya oluşturma
if ($file = fopen($fileName, "w")) {
// eki kodlanmış metni alınır
$text = imap_fetchbody($imapResource, $msgNumber, $sectionNumber);
// Ek kodlanmıştır - kodunu çözülür
switch ($struct->encoding) {
// base 64
case ENCBASE64:
$text = base64_decode($text);
break;
// quoted printable
case ENCQUOTEDPRINTABLE:
$text = quoted_printable_decode($text);
break;
default:
// diğer durumlar göz ardı edilir
break;
}
// metin dosyaya yazılır
fputs($file, $text);
// dosya kapatılıyor
fclose($file);
} else {
// dosya oluşturma başarısız
print "L'attachement n'a pu être sauvegardé dans [$fileName]\n";
}
}
}
Açıklamalar
- satır 2: [saveAttachment] işlevi aşağıdaki parametreleri kabul eder:
- [$imapResource, int $msgNumber, string $sectionNumber], kaydedilecek IMAP bölümünü benzersiz bir şekilde tanımlar;
- [string $outputDir], yedekleme klasörüdür;
- [object $struct], kaydedilecek mesaj bölümünün yapısını tanımlar;
- satır 6-44: Eklemeyle ilişkili dosya adı aranır. Kaydetme işlemi için bu dosya adı kullanılır. Eklemenin dosya adı, [$struct→dparameters] tablosunda veya [$struct→parameters] tablosunda, hatta her ikisinde de bulunur;
- satır 30-40: Dosya adı 7 bit ile kodlanamayan karakterler içeriyorsa, bu dosya [quoted-printable] formatında kodlanmıştır. Bu durumda, [$struct→dparameters]'te öznitelik, [fileName] yerine [fileName*] olarak adlandırılır. Bu, 16. satırdaki koşulu karşılamadığı anlamına gelir. Ardından, dosya adı [$struct→parameters] tablosunda aranır;
- 32. satır: Kodlanmış bir dosya adı örneği. Şu biçimdedir: =?codage_original?codage_actuel?nom_encodé. Dolayısıyla [=?utf-8?Q?Cours-Tutoriels-Serge-Tah=C3=A9-1568x268=2Ep] adı, dosya adının daha önce UTF-8 olduğunu ve şu anda [quoted-printable] (Q) olduğunu ifade eder;
- 38. satır: Dosya adı, burada üç parametre kabul eden [iconv_mime_decode] işleviyle kod çözülür:
- kod çözülecek dize;
- varsayılan olarak 0 olarak bırakılır;
- kod çözülmüş dizeyi temsil etmek için kullanılacak karakter kümesi. Bu parametre, kod çözülmesi gereken dizede bulunur. 34-35. satırlardaki düzenli ifade ile elde edilir;
- 45-75. satırlar: eki, bulunan adla bir dosyaya kaydedilir;
[imap-02.php] betiğini test etmek için, önce [guest@localhost]'e aşağıdaki yapılandırmayla bir e-posta gönderilir:
Böylece beş ek oluşur.
[imap-02.php] ile aşağıdaki yapılandırmayı kullanarak gönderilen e-postayı okuruz:
Konsol çıktıları şu şekildedir:
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [1] messages dans la boîte à lettres [{localhost:110/pop3}]
Récupération de la liste des messages non lus de la boîte à lettres [{localhost:110/pop3}]
-----contenu de la partie n° [0]
Content-Type: MULTIPART/MIXED
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1]
Content-Type: MULTIPART/ALTERNATIVE
Transfer-Encoding : 7 bits
-----contenu de la partie n° [1.1]
Content-Type: TEXT/PLAIN
Transfer-Encoding : quoted-printable
sauvegarde d'un message dans [output/localhost-pop3/message-1/message.txt]
-----contenu de la partie n° [1.2]
Content-Type: TEXT/HTML
Transfer-Encoding : quoted-printable
sauvegarde d'un message dans [output/localhost-pop3/message-1/message.HTML]
-----contenu de la partie n° [2]
Content-Type: APPLICATION/VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT
Transfer-Encoding : base 64
sauvegarde de l'attachement dans [output/localhost-pop3/message-1/Hello from SwiftMailer.docx]
-----contenu de la partie n° [3]
Content-Type: APPLICATION/PDF
Transfer-Encoding : base 64
sauvegarde de l'attachement dans [output/localhost-pop3/message-1/Hello from SwiftMailer.pdf]
-----contenu de la partie n° [4]
Content-Type: APPLICATION/VND.OASIS.OPENDOCUMENT.TEXT
Transfer-Encoding : base 64
sauvegarde de l'attachement dans [output/localhost-pop3/message-1/Hello from SwiftMailer.odt]
-----contenu de la partie n° [5]
Content-Type: UNKNOWN/PNG
Transfer-Encoding : base 64
sauvegarde de l'attachement dans [output/localhost-pop3/message-1/Cours-Tutoriels-Serge-Tahé-1568x268.png]
-----contenu de la partie n° [6]
Content-Type: MESSAGE/RFC822
Transfer-Encoding : base 64
sauvegarde de l'attachement dans [output/localhost-pop3/message-1/test-localhost.eml]
Fermeture de la connexion réussie.
Done.
Kaydedilen dosyalar [output/localhost-pop3/message-N] klasöründe bulunur:

16.6.6. POP3 / IMAP istemcisi, [php-mime-mail-parser] kütüphanesi ile
Önceki [imap-02.php] komut dosyasında şunları kaydedebildik:
- e-postanın [text/plain] ve [text/HTML] içeriklerini;
- e-postadaki ekleri;
[message/rfc822] türündeki bir ek için, ekin içeriğini de kaydettik. Ancak bu tür bir ek, kendisi de bir e-postadır ve bu e-postanın da [text/plain] ve [text/HTML] içerikleri ile ekleri bulunmaktadır. Bu durumda şu durumla karşılaşılabilir:
- yapısı [message/rfc822] türündeki bir eke benzeyen bir [mail 1];
- 1 numaralı e-postaya ekli bir [mail 2] dosyası;
- 2 numaralı e-postaya ekli bir [mail 3];
- vb…
[imap-02.php] komut dosyası, [mail 1] dosyasının içeriğini (metinler ve ekler) kaydeder. [mail 2] dosyasını ek belge olarak kaydeder, ancak işlem burada sona erer. [mail 2] dosyasını analiz ederek metinleri ve ekleri çıkarmaya çalışmaz. [mail 1] dosyası için yapılan işlemin aynısını [mail 2] dosyasına uygulamak yeterli olabilir diye düşünülebilir. [mail 1] dosyasını işleyen yönteme yinelemeli bir çağrı yapmak, birbirinin içine geçmiş tüm e-postaların içeriğini elde etmek için yeterli olabilir. Ne yazık ki, [mail 2]’in bölümleri, [mail 1] için kullanılan mantıktan farklı bir mantıkla numaralandırılmıştır; bu da, iç içe geçmiş e-postalar kümesindeki konumuna bakılmaksızın bir e-postanın bölüm numaralarını hesaplamak için oldukça karmaşık bir mantık kullanılmadıkça, her iki durumda da aynı algoritmanın kullanılmasını engellemektedir. e-postanın iç içe geçmiş e-postalar kümesindeki konumu ne olursa olsun.
[imap-02.php] komut dosyası zaten karmaşıktı. İç içe geçmiş e-postaların içeriğini yönetmek için komut dosyasını daha da karmaşık hale getirmekten kaçınmak amacıyla, Github’da (Mayıs 2019) URL ve [https://github.com/php-mime-mail-parser/php-mime-mail-parser] adlarıyla bulunan ve Vincent Dauce tarafından yazılan [php-mime-mail-parser] kütüphanesini kullanacağız.
16.6.6.1. [php-mime-mail-parser] kütüphanesinin kurulumu
Kütüphanenin tanıtım sayfasında, Windows'ta nasıl kurulacağı açıklanmaktadır:

Windows için OS kütüphanesinin kurulumu iki adımdan oluşur:
télécharger une DLL ;
modifier le fichier [php.ini] qui configure PHP ;
LA ve DLL, [mailparse] kütüphanesinde URL ve [http://pecl.php.net/package/mailparse] (Mayıs 2019) sürümlerinde mevcuttur;

- [2]'te, kütüphanenin en yeni ve kararlı sürümünü seçin;

- [3] yerine, kullandığınız PHP sürümünü seçin (bu belgede PHP 7.2'dir);
- [4]'te, Windows sürümünüzü seçin (burada 64 bit Windows kullanılmaktadır). [Thread Safe] sürümünü seçin;
Laragon ile indirilen PHP sürümünü öğrenmek için, Laragon penceresinden bir [Terminal] açın ve aşağıdaki komutu yazın:
C:\myprograms\laragon-lite\www
λ php -v
PHP 7.2.11 (cli) (built: Oct 10 2018 02:04:07) ( ZTS MSVC15 (Visual C++ 2017) x64 )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
PHP 7.2.11 sürümü 3. satırda belirtilmiştir. Aynı satırda derleme için kullanılan Windows sürümü (32 veya 74 bit) de gösterilir.
DLL dosyasını elde ettikten sonra, bunu [<laragon>/bin/php/<version-php>/ext] [5] klasörüne kopyalamanız gerekir:

Bu işlem tamamlandıktan sonra, PHP dosyasını yapılandıran [php.ini] dosyasında bu uzantıyı etkinleştirmeniz gerekir (bkz. bağlantı paragrafı):

[7] satırı muhtemelen mevcut olmayacaktır ve bunu kendiniz eklemeniz gerekecektir.
Eklenti etkinleştirildikten sonra, Laragon terminalinde aşağıdaki komutu girerek geçerliliğini kontrol edebilirsiniz:
C:\myprograms\laragon-lite\www
λ php --ini
Configuration File (php.ini) Path: C:\windows
Loaded Configuration File: C:\myprograms\laragon-lite\bin\php\php-7.2.11-Win32-VC15-x64\php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
[php –-ini] komutu, 4. satırdaki yapılandırma dosyasını yükler. Ardından, [php.ini] içinde etkinleştirilmiş tüm uzantıların DLL dosyalarını yükler. Bunlardan herhangi biri hatalıysa, bu durum bildirilir. Böylece, DLL dosyasına eklenen [php_mailparse.dll] dosyasının geçerliliği kontrol edilir. Bu dosya çeşitli nedenlerle hatalı olarak değerlendirilebilir; en sık karşılaşılan nedenler şunlardır:
- kullandığınız PHP sürümüne uymayan bir DLL dosyasını indirdiyseniz;
- 64 bitlik bir PHP dosyanız varken 32 bitlik bir DLL dosyası indirdiyseniz veya tam tersi bir durum söz konusuysa;
Eklenti etkinleştirilip doğrulandıktan sonra, [php-mime-mail-parser] kütüphanesinin kurulumuna geçilebilir:

[8] komutu, Laragon terminaline girilmelidir (bkz. bağlantı paragrafı):

- [1] komutunda, [<laragon>/www] klasöründe olduğunuzdan emin olun;
- [2] komutunda, [php-mime-mail-parser] kütüphanesinin kurulum komutu;
- [3]'te, [php-mime-mail-parser] kütüphanesi zaten kurulu olduğu için buraya hiçbir şey kurulmamıştır;
[php-mime-mail-parser] kütüphanesinin kurulumu, [<laragon>/www/vendor] klasörüne yapılır:


- [2-3]'te, [php-mime-mail-parser] kütüphanesinin kaynakları;
Çalışma ortamı kurulduğuna göre, artık [imap-03.php] komut dosyasını yazmaya geçebiliriz.
16.6.6.2. [imap-03.php] betiği
[imap-03.php] betiği, önceki betiklerle aynı [config-imap-01.json] yapılandırma dosyasını kullanır:
[imap-03.php] komut dosyası şöyledir:
<?php
// e-postaları okumaya olanak tanıyan IMAP istemcisi (İnternet Mesaj Erişim Protokolü)
// [php-mime-mail-parser] kütüphanesi ile yazılmıştır
// şu adreste mevcuttur:URL [https://github.com/php-mime-mail-parser/php-mime-mail-parser] (Mayıs 2019)
//
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulması
declare (strict_types=1);
// hata yönetimi
error_reporting(E_ALL & ~ E_WARNING & ~E_DEPRECATED & ~E_NOTICE);
//ini_set("display_errors", "off");
//
// bağımlılıklar
require_once 'C:/myprograms/laragon-lite/www/vendor/autoload.php';
// e-posta okuma ayarları
const CONFIG_FILE_NAME = "config-imap-01.json";
// yapılandırmayı alıyoruz
if (!file_exists(CONFIG_FILE_NAME)) {
print "Le fichier de configuration " . CONFIG_FILE_NAME . " n'existe pas";
exit;
}
$mailboxes = \json_decode(\file_get_contents(CONFIG_FILE_NAME), true);
// posta kutularını okuma
foreach ($mailboxes as $name => $infos) {
// izleme
print "------------Lecture de la boîte à lettres [$name]\n";
// posta kutusunu okuma
readmailbox($name, $infos);
}
// son
exit;
Yorumlar
- 18-23. satırlar: Yapılandırma dosyasının içeriği [$mailboxes] sözlüğüne eklenir;
- 26-31. satırlar: Her posta kutusu, [readmailbox] işlevi tarafından okunur (30. satır). Bu işlev, aslında posta kutusundaki okunmamış mesajları okur. Bir posta kutusu, belirli bir kullanıcının e-posta adresine karşılık gelir;
[readmailbox] işlevi şu şekildedir:
function readmailbox(string $name, array $infos): void {
// bağlanılıyor
$imapResource = imap_open($name, $infos["user"], $infos["password"]);
if (!$imapResource) {
// başarısız
print "La connexion au serveur [$name] a échoué : " . imap_last_error() . "\n";
exit;
}
// Bağlantı kuruldu
print "Connexion établie avec le serveur [$name].\n";
// posta kutusundaki toplam mesaj sayısı
$nbmsg = imap_num_msg($imapResource);
print "Il y a [$nbmsg] messages dans la boîte à lettres [$name]\n";
// geçerli posta kutusundaki okunmamış mesajlar
if ($nbmsg > 0) {
print "Récupération de la liste des messages non lus de la boîte à lettres [$name]\n";
$msgNumbers = imap_search($imapResource, 'UNSEEN');
if ($msgNumbers === FALSE) {
print "Il n'y a pas de nouveaux messages dans la boîte à lettres [$name]\n";
} else {
// okunmamış mesaj listesi taranıyor
foreach ($msgNumbers as $msgNumber) {
print "---message n° [$msgNumber]\n";
// $msgNumber numaralı mesajın gövdesi alınıyor
getMailBody($imapResource, $msgNumber, $infos);
// protokol POP3 ise, mesajı aldıktan sonra siliniyor
$pop3 = $infos["pop3"];
if ($pop3 !== NULL) {
// mesajı "silinecek" olarak işaretlenir
imap_delete($imapResource, $msgNumber);
}
}
// okunmamış mesajların okunması tamamlanır
if ($pop3 !== NULL) {
// "silinecek" olarak işaretlenen mesajlar silinir
imap_expunge($imapResource);
}
}
}
// bağlantı kapatılıyor
$imapClose = imap_close($imapResource);
if (!$imapClose) {
// başarısızlık
print "La fermeture de la connexion a échoué : " . imap_last_error() . "\n";
} else {
// başarılı
print "Fermeture de la connexion réussie.\n";
}
}
Açıklamalar
[readmailbox] işlevinin kodu, önceki komut dosyalarındakiyle aynıdır.
Bir mesajın gövdesini (içerik + ekler) analiz eden [getMailBody] işlevi (25. satır) şöyledir:
// mesaj gövdesinin analiz edilmesi
function getMailBody($imapResource, int $msgNumber, array $infos): void {
// mesajın tüm metni alınır
$text = imap_fetchbody($imapResource, $msgNumber, "");
if ($text === FALSE) {
print "Le corps du message [$msgNumber] n'a pu être récupéré";
return;
}
// mesaj metnini analiz edecek bir ayrıştırıcı oluşturuluyor
$parser = (new PhpMimeMailParser\Parser())->setText($text);
// mesajın farklı bölümlerini alıyoruz
$outputDir = $infos["output-dir"] . "/message-$msgNumber";
getParts($parser, $msgNumber, $outputDir);
}
Açıklamalar
- 2. satır: [getMailBody] işlevi üç parametre kabul eder:
- [$imapResource]: Bağlı olunan IMAP kaynağı;
- [$msgNumber]: işlenecek mesajın numarası (posta kutusunda);
- [$infos]: işlenecek posta kutusuna ilişkin çeşitli bilgiler;
- 4. satır: [$msgNumber] numaralı mesajın tamamı alınır;
- satır 5-8: mesajın içeriğinin alınamadığı durum;
- 10. satır: [php-mime-mail-parser] kütüphanesini kullanmaya başlıyoruz. [$parser] nesnesi, mesajın metnini analiz etmekle görevlendirilecek;
- 12. satır: [$outputDir], [$msgNumber] numaralı mesajın metin içeriği ve eklerinin kaydedileceği klasör olacaktır;
- 13. satır: [getParts] işlevinden, [$msgNumber] numaralı mesajın farklı bölümlerini (metin içeriği ve ekler) bulması ve bunları [$outputDir] klasörüne kaydetmesi istenir;
[getParts] işlevi şu şekildedir:
// mesajın farklı bölümlerinin alınması
function getParts(PhpMimeMailParser\Parser $parser, int $msgNumber, string $outputDir): void {
// gerekirse mesajın yedekleme klasörü oluşturulur
if (!file_exists($outputDir)) {
if (!mkdir($outputDir)) {
print "Le dossier [$outputDir] n'a pu être créé\n";
return;
}
}
// mesajın başlık bilgilerini alıyoruz
$arrayHeaders = $parser->getHeaders();
// metin mesajları yedeklenir
$parts = $parser->getInlineParts("text");
for ($i = 1; $i <= count($parts); $i++) {
print "-- Sauvegarde d'un message de type [text/plain]\n";
saveMessage($parts[$i - 1], 0, $arrayHeaders, "$outputDir/message_$i.txt");
}
// HTML mesajları kaydedilir
$parts = $parser->getInlineParts("html");
for ($i = 1; $i <= count($parts); $i++) {
print "-- Sauvegarde d'un message de type [text/html]\n";
saveMessage($parts[$i - 1], 1, $arrayHeaders, "$outputDir/message_$i.html");
}
// mesajın ekleri alınır
$attachments = $parser->getAttachments();
// eki numarası
$iAttachment = 0;
// eklerin listesi taranıyor
foreach ($attachments as $attachment) {
// ek türü
$fileType = $attachment->getContentType();
print "-- Sauvegarde d'un attachement de type [$fileType] dans le fichier [$outputDir/{$attachment->getFilename()}]\n";
// eki kaydedilir
try {
$attachment->save($outputDir, PhpMimeMailParser\Parser::ATTACHMENT_DUPLICATE_SUFFIX);
} catch (Exception $e) {
print "L'attachement n'a pu être sauvegardé : " . $e->getMessage() . "\n";
}
// message/rfc822 türünün özel durumu
if ($fileType === "message/rfc822") {
// ek dosyanın kendisi bir mesajdır - bunu da ayrıştıracağız
// kaydetme dizini değiştiriliyor
$iAttachment++;
$outputDir = $outputDir . "/rfc822-$iAttachment";
// Ayrıştırılacak içerik değiştiriliyor
$parser->setText($attachment->getContent());
// mesajı özyinelemeli olarak analiz ediyoruz
getParts($parser, $msgNumber, $outputDir);
}
}
}
Açıklamalar
- 2. satır: [getParts] işlevi üç parametre kabul eder:
- analiz edilecek mesajın tam metninin aktarıldığı bir [$parser] ayrıştırıcısı;
- [$msgNumber], analiz edilmekte olan mesajın numarasıdır;
- [$outputDir], mesajın içeriğinin ve eklerinin kaydedileceği klasördür;
- 4-9. satırlar: [$outputDir] klasörünün oluşturulması;
- 11. satır: analiz edilen mesajın başlık bilgileri (gönderen, alıcı, konu…) alınır;
- 13. satır: [text/plain] türündeki e-posta bölümleri alınır. Bir tablo elde edilir;
- satır 14-17: Alınan tablonun tüm öğeleri, her birine farklı bir dosya adı verilerek kaydedilir;
- 19. satır: [text/html] türündeki e-posta bölümleri alınır. Bir dizi elde edilir;
- 20-23. satırlar: Alınan dizinin tüm öğelerini, her birine farklı bir dosya adı vererek kaydediyoruz;
- 25. satır: Analiz edilen mesajın eklerinin listesi alınır;
- 29. satır: Bu liste üzerinde geziniyoruz;
- 24. satır: Ekin türünü (Content-Type özniteliği) alınır;
- satır 34-38: ek, [$outputDir] klasörüne kaydedilir. İkinci parametre olan [PhpMimeMailParser\Parser::ATTACHMENT_DUPLICATE_SUFFIX], eklerin adlandırılma stratejisidir. Eğer [$attachment→getFilename()] değeri X ise ve X dosyası zaten mevcutsa, o zaman [php-mime-mail-parser] kütüphanesi, [X_1], [X_2], vb. adları dener ve henüz mevcut olmayan bir dosya adı bulana kadar devam eder;
- 40. satır: Ekteki dosyanın bir e-posta olup olmadığına bakılır;
- 41-48. satırlar: eğer öyleyse, bu e-posta da sırayla analiz edilerek içeriği ve ekleri çıkarılır;
- 44. satır: [$outputDir] değeri X ise ve analiz edilen iletinin ekleri arasında iki e-posta varsa, ilki [$outputDir/rfc822-1] klasörüne, ikincisi ise [$outputDir/rfc822-2] klasörüne kaydedilir;
- 46. satır: Ekteki e-postanın içeriği, ayrıştırılacak yeni metin haline gelir;
- 48. satır: Yeni metni analiz etmek için [getParts] işlevi özyinelemeli olarak çağrılır;
[saveMessage] fonksiyonu, analiz edilecek mesajın metin içeriğini kaydeder:
// bir metin mesajını kaydediyoruz
function saveMessage(string $text, int $type, array $arrayHeaders, string $filename): void {
// kaydedilecek içerik
$contents = "";
// başlıkların eklenmesi
switch ($type) {
case 0:
// text/plain
foreach ($arrayHeaders as $key => $value) {
$contents .= "$key: $value\n";
}
$contents .= "\n";
break;
case 1:
// text/HTML
foreach ($arrayHeaders as $key => $value) {
$contents .= "$key: $value<br/>\n";
}
$contents .= "<br/>\n";
}
// mesaj metninin eklenmesi
$contents .= $text;
// hepsini kaydet
if (!file_put_contents($filename, $contents)) {
// başarısızlık
print "Le message n'a pu être sauvegardé dans le fichier [$filename]\n";
} else {
// başarılı
print "Le message a été sauvegardé dans le fichier [$filename]\n";
}
}
Açıklamalar
- [saveMessage] işlevi aşağıdaki parametreleri kabul eder:
- [$text]: kaydedilecek metin;
- [$type]: metin türü (0: text/plain, 1: text/HTML);
- [$arrayHeaders]: analiz edilen mesajın başlıkları;
- [$filename]: [$text]'in kaydedileceği dosyanın adı;
- 4. satır: [$contents], kaydedilecek metnin tamamını temsil edecektir;
- 6-20. satırlar: önce mesajın tüm başlıkları (from, to, subject…) kaydedilecektir;
- 16-19. satırlar: HTML metni söz konusu olduğunda, her başlığın tarayıcıda kendi satırında tek başına görünmesi için her satır <br/> etiketiyle sonlandırılır;
- 22. satır: Başlıklara, kaydedilecek mesaj metni eklenir;
- 24-30. satırlar: tüm içerik [$filename] dosyasına kaydedilir;
[php-mime-mail-parser] kütüphanesinin kullanılması, e-posta okuma komut dosyasının yazılmasını önemli ölçüde kolaylaştırır.
[smtp-02.php] betiği, aşağıdaki yapılandırma ile [guest@localhost] kullanıcısına e-posta göndermek için kullanılır:
- 11-15. satırlar: beş ek vardır;
- 15. satır: [test-localhost-2.eml], aşağıdaki şekilde yapılandırılmış bir e-postadır:
- [test-localhost-2.eml], 4 ek (11-14. satırdakilerle aynı) ve bir ekli e-posta içerir;
- [test-localhost-2.eml]'e ekli e-posta 4 ek içerir (11-14. satırlardakilerle aynı);
[imap-03.php] komut dosyası, [guest@localhost] kullanıcısının posta kutusunu aşağıdaki yapılandırma ile okumak için kullanılır:
Çalıştırıldıktan sonra, [output/localhost-pop3] klasör ağacı aşağıdaki gibi oldu:

- [1]'te, [guest@localhost] tarafından alınan e-postanın 5 eki;
- [2]'e, [1]'ten gelen [test-localhost-2.eml] e-postasının 5 eki;
- [3]'te, [2]'ten gelen [test-localhost.eml] e-postasının 4 eki;
Konsol çıktıları şu şekildedir:
------------Lecture de la boîte à lettres [{localhost:110/pop3}]
Connexion établie avec le serveur [{localhost:110/pop3}].
Il y a [1] messages dans la boîte à lettres [{localhost:110/pop3}]
Récupération de la liste des messages non lus de la boîte à lettres [{localhost:110/pop3}]
---message n° [1]
-- Sauvegarde d'un message de type [text/plain]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/message_1.txt]
-- Sauvegarde d'un message de type [text/html]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/message_1.html]
-- Sauvegarde d'un attachement de type [application/vnd.openxmlformats-officedocument.wordprocessingml.document] dans le fichier [output/localhost-pop3/message-1/Hello from SwiftMailer.docx]
-- Sauvegarde d'un attachement de type [application/pdf] dans le fichier [output/localhost-pop3/message-1/Hello from SwiftMailer.pdf]
-- Sauvegarde d'un attachement de type [application/vnd.oasis.opendocument.text] dans le fichier [output/localhost-pop3/message-1/Hello from SwiftMailer.odt]
-- Sauvegarde d'un attachement de type [image/png] dans le fichier [output/localhost-pop3/message-1/Cours-Tutoriels-Serge-Tahé-1568x268.png]
-- Sauvegarde d'un attachement de type [message/rfc822] dans le fichier [output/localhost-pop3/message-1/test-localhost-2.eml]
-- Sauvegarde d'un message de type [text/plain]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/rfc822-1/message_1.txt]
-- Sauvegarde d'un message de type [text/html]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/rfc822-1/message_1.html]
-- Sauvegarde d'un attachement de type [application/vnd.openxmlformats-officedocument.wordprocessingml.document] dans le fichier [output/localhost-pop3/message-1/rfc822-1/Hello from SwiftMailer.docx]
-- Sauvegarde d'un attachement de type [application/pdf] dans le fichier [output/localhost-pop3/message-1/rfc822-1/Hello from SwiftMailer.pdf]
-- Sauvegarde d'un attachement de type [application/vnd.oasis.opendocument.text] dans le fichier [output/localhost-pop3/message-1/rfc822-1/Hello from SwiftMailer.odt]
-- Sauvegarde d'un attachement de type [image/png] dans le fichier [output/localhost-pop3/message-1/rfc822-1/Cours-Tutoriels-Serge-Tahé-1568x268.png]
-- Sauvegarde d'un attachement de type [message/rfc822] dans le fichier [output/localhost-pop3/message-1/rfc822-1/test-localhost.eml]
-- Sauvegarde d'un message de type [text/plain]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/message_1.txt]
-- Sauvegarde d'un message de type [text/html]
Le message a été sauvegardé dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/message_1.html]
-- Sauvegarde d'un attachement de type [application/vnd.openxmlformats-officedocument.wordprocessingml.document] dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/Hello from SwiftMailer.docx]
-- Sauvegarde d'un attachement de type [application/pdf] dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/Hello from SwiftMailer.pdf]
-- Sauvegarde d'un attachement de type [application/vnd.oasis.opendocument.text] dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/Hello from SwiftMailer.odt]
-- Sauvegarde d'un attachement de type [image/png] dans le fichier [output/localhost-pop3/message-1/rfc822-1/rfc822-1/Cours-Tutoriels-Serge-Tahé-1568x268.png]
Fermeture de la connexion réussie.
[3]'ten gelen [message_1.HTML] dosyasını bir tarayıcıda görüntülediğimizde, şu sonuç elde edilir:
