Normalde çalışmaz. Sonradan html uzantısını değiştirip php'ye çeviriyorlardır. Videonun ilerleyen zamanlarında hatayı fark edip düzeltirler.
Veya "Bakın html sayfasına PHP kodu yazdım ama çalışmadı. Çünkü html sayfada php kodu çalışmaz." demek için yazdıkları bir an da olabilir bu.
Yok eğer bile isteye bu şekilde yazıyorlarsa bambaşka bir mantık kurmuşlardır.
Diğer açık sekmelerden anladığım kadarıyla burada bir framework geliştiriliyor.
Bu framework'ün çalışma mantığında bu kullanımın bir anlamı olabilir ama bunu bilemeyiz.
Örneğin bir php sayfası, bu default.html sayfasını okuyordur ve regex ile içinde php'ye ait bir kısım varsa bunu replace ediyordur.
Yani aslında php kodunu yine bir php sayfası çalıştırıyordur. Bu html, yalnızca başka bir php sayfasından okunmak ve değiştirilmek üzere yazılıyordur.
Burada ne yapılmak istendiği eğitim programında anlatılıyor olsa gerek.
1) Sunucuya Sahte GET İsteği Atmak
Stackoverflow'da 2012 yılından şöyle bir bilgiye rastladım:
Android uygulamam sunucuya POST istekleri gönderdi ve beklendiği gibi çalışıyordu ancak yaklaşık 30 dakika sonra 408 İstek zaman aşımı hataları yaşamaya başladım.
Ayrıca bir PC'den veya Android cihazdan bir tarayıcı açıp ana sayfamı (PHP Wordpress sitesi) açtığımda sorunun ortadan kalktığını fark ettim.
Web sitesi testte olduğundan web trafiği minimum düzeydeydi ve bu davranışı sunucu günlüklerinde oldukça kolay bir şekilde doğrulayabildim.
POST isteğimden önce sahte bir GET isteği yürüterek bu soruna geçici bir çözüm bulmayı başardım ve bu, web sunucusunu 'uyandırdı' ve sonraki tüm POST isteklerine yanıt verdi.
Buna 2015'te gelen bir yanıtta da şu bilgi veriliyor:
Bağlantıları sıfırlayarak müdahale eden bazı trafik izleme aracıları için tüm POST'lar ve otuz dakika boyunca hiçbir GET'in olmaması şüpheli görünüyor.
Trafiğin kötü niyetli olduğunu düşündükleri takdirde her iki uca da sahte sıfırlamalar gönderen ürünler var.
Bu daha sonra bağlantıyı keserek zaman aşımına neden olur.
2016'da da aynı yanıta söyle bir yorum eklenmiş:
Bana öyle geliyor ki, en azından bazı durumlarda bu sorunlar oturum zaman aşımlarından da kaynaklanıyor olabilir.
Oturum yenilemeleri GET istekleri için gerçekleştiriliyor ancak POST istekleri için gerçekleştirilmiyor olabilir; bu da 'uyanma' davranışını açıklayabilir.
Buna göre, siz de arada bir sunucuya sahte GET istekleri atarak sunucuyu 'uyandırmayı' deneyebilirsiniz.
Kaynak: https://stackoverflow.com/questions/1667720/http-408-request-timeout
2) response.end() Kullanılmamış Olabilir
Postman'dan yanıt alabiliyorsanız sorun bu değildir muhtemelen.
Ama endpoint'inizde veya bir middleware'ınızda response.end() (veya .send() gibi bir istek sonlandırıcı sonlandırıcı fonksiyon) komutuna ulaşılmasını önleyen bir koşul oluşuyor olabilir.
Mesela header'ları kontrol ettiğiniz bir aşamada bir return ifadesi kullanmışsınızdır ve response.end() komutuna ulaşılmasını önlemişsinizdir veya bir if-else yapınız response.end() komutunu atlamanıza neden oluyor olabilir.
Sunucunuza bir console.log() ekleyip gelen header'ı terminale basın.
Sonra siteden ve Postman'dan ayrı ayrı istek atın ve header'lar arasındaki farklara bakın.
Fark yoksa, bir isteğin geçtiği tüm aşamaları takip edip response.end() komutunun çalışmadan işlemlerin bittiği bir durum var mı diye inceleyin.
Kaynak: https://stackoverflow.com/questions/22773927/408-timeout-in-nodejs-app-requesting-github-api
3) İstemci, İsteği Yarıda Kesmiş Olabilir
İsteği gönderiyorsunuzdur ama hemen sonra (belki bir state değişimi yüzünden) isteği iptal ediyorsunuzdur.
Aslında yukarıdaki gibi bir sorun ama bu sefer istemci tarafında...
Çözüm olarak yine sunucuya console.log() atıp gelen isteğin body ve header'larını terminale basın. Sonra postman ve site isteklerinin arasındaki farkları bulmaya çalışın.
Kaynak: https://docs.aws.amazon.com/en_en/elasticloadbalancing/latest/classic/ts-elb-error-message.html#ts-elb-errorcodes-http408
Daha farklı nedenler de olabilir tabi sunucunun zaman aşımı süresini uzatmak gerekiyor olabilir vs. ama sorun orada olsa Postman da yanıt alamazdı.
Burada nesne yönelimli bir çözüm kullanabilirsiniz.
Modelleriniz birer class olmalı. Verdiğiniz örnek üzerinden ilerlersek...
blog.php sayfanızın adı, içereceği class adıyla aynı olsun. Class adları büyük harfle başlayacağı için dosya adı da büyük harfle başlasın.
Blog.php
class Blog {
function vericek(){
global $db;
$bul = $db->prepare("SELECT * FROM blog");
$bul->execute();
$yaz = $bul->fetchAll(PDO::FETCH_ASSOC);
return $yaz;
}
}
Model fonksiyonunuz da bir class olsun:
class Model {
public $model;
public $error;
public function __construct($name) {
// MODELS_DIR klasöründe $name adında bir dosya varsa:
if(file_exists(MODELS_DIR.$name.".php")){
// Model dosyasını çağır
require_once(MODELS_DIR.$name.".php");
// Model dosyasında aynı isimle bir class yer alıyorsa:
if(class_exists($name)) {
// $model olarak bu sınıftan oluşturulan nesneyi belirle.
$this->model = new $name();
$this->error = null;
}
// Model dosyasında aynı isimle bir class yer almıyorsa:
else {
$this->error = "Model dosyasında $name adlı class bulunamadı.";
$this->model = null;
}
}
// MODELS_DIR klasöründe $name adında bir dosya yoksa:
else {
$this->error = "$name adlı model dosyası bulunamadı.";
$this->model = null;
}
}
}
Artık Model class'ınız sayesinde istediğiniz model class'ından bir nesne türetip kullanabilirsiniz:
$blogModel = new Model('Blog');
if($blogModel->error) exit($blogModel->error);
$result = $blogModel->model->vericek();
echo "<pre>";
var_dump($result);
echo "</pre>";
Burada Blog.php dosyasını yeniden include etmeye gerek yok. Çünkü sınıfın bütün özelliklerini taşıyan bir nesnesi $blogModel->model içinde mevcut.
Passing null to parameter #2 ($replace) of type array|string is deprecated.
Yani, 2. parametreye null gelmesi PHP 8 ile artık kaldırıldı. Önceki sürümlerde null verilirse boş string olarak kabul ediliyordu muhtemelen.
Hatayı aslında şu satırda alıyorsunuz:
$request_uri = str_replace([$dirname, $basename], null, $_SERVER['REQUEST_URI']);
Burada null olan parametre array veya string olmalı. Şu şekilde düzeltilebilir:
$request_uri = str_replace([$dirname, $basename], '', $_SERVER['REQUEST_URI']);
Bunu yaptığınızda $_SERVER['REQUEST_URI'] içinde geçen $dirname veya $basename ifadelerininin silinmiş halini $request_uri değişkenine atayacaktır.
Evet, orada da eksik yazmışım.
ana_yorum Düzeltmeler doğru.
// Bu kısmı
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum_id'] ?>">Cevapla</a>
// Bu şekilde değiştirdim.
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum']['yorum_id'] ?>">Cevapla</a>
cevap_yorum burada da cevapla kısmı üsttekiyle aynı olmalı ki hep ana yoruma bağlı kalsın.
// Bu kısmı
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum_id'] ?>">Cevapla</a>
// Bu şekilde değiştirdim.
<a href="#" class="mt-2" data-ust-yorum-id="<?= $altYorum['yorum_id'] ?>">Cevapla</a>
// yerine şöyle olmalı
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum']['yorum_id'] ?>">Cevapla</a>
İlk fonksiyonda eksik yazmışım gibi görünüyor.
Bu yüzden hiyerarşi düzgün oluşamamış.
else içindeki 'yorum_id' kısmını 'ust_yorum_id' olarak düzelttim:
$yorumlar = tumYorumlar($mak_id);
$duzenlenmisYorumlar = [];
foreach($yorumlar as $yorum) {
// Baktığımız yorumun üst yorumu yoksa, ana yorum olarak eklensin.
if($yorum['ust_yorum_id'] === 0) {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['yorum_id'] ] = ['yorum'=>$yorum, 'alt_yorumlar'=>[]];
}
// Baktığımız yorumun üst yorumu varsa, o yorumun alt yorumu olarak eklensin
else {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['ust_yorum_id'] ]['alt_yorumlar'][] = $yorum;
}
}
Ayrıca ikinci foreach içinde de patlama ihtimalini engellemek gerek.
<?php foreach($duzenlenmisYorumlar as $key=>$data) { ?>
<?php if(!isset($data['yorum'])) continue; ?>
<div class="ana_yorum" data-key="<?= $key ?>">
<div><?= $data['yorum']['yorum_adi'] ?></div>
<div><?= $data['yorum']['yorum_icerik'] ?></div>
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum_id'] ?>">Cevapla</a>
<?php foreach($data['alt_yorumlar'] as $altYorum) { ?>
<div class="cevap_yorum">
<div><?= $altYorum['yorum_adi'] ?></div>
<div><?= $altYorum['yorum_icerik'] ?></div>
<a href="#" class="mt-2" data-ust-yorum-id="<?= $data['yorum_id'] ?>">Cevapla</a>
</div>
<?php } ?>
</div>
<?php } ?>
Not: Bu sistem, yazılan bir ana yorum altına cevaplar eklenmesini sağlar.
Ama yoruma verilen cevaba verilen cevap şeklinde düzen oluşturmaz.
Bu nedenle verilen cevaplar her zaman ust_yorum_id'ye bakılarak yapılmalı.
O yüzden belki değiştirip kullanabilmeniz için <a> taglarınıza, üst yorum id'lerini bir attribute olarak ekledim.
Eğer çalışmazsa, showVar() fonksiyonu ile yeniden $duzenlenmisYorumlar dizisine bakabiliriz.
Bu hata, 24.satırda array'in "yorum" diye bir key'ine ulaşılmaya çalışıldığını ama öyle bir key olmadığını söylüyor.
$yorumlar değişkeni içinde nasıl bir veri geliyor?
$duzenlenmisYorumlar değişkeni içinde nasıl bir veri oluşuyor?
Bunları görüp ona göre bir yaklaşım üretebiliriz.
Bu değişkenlerin değerlerini paylaşabilir misiniz?
function showVar($var) {
echo '<pre>';
var_dump($var);
echo '</pre>';
}
Bu şekilde bir fonksiyon oluşturup, bu fonksiyon ile değişkenlerin değerini alabilirsiniz:
$yorumlar = tumYorumlar($mak_id);
$duzenlenmisYorumlar = [];
foreach($yorumlar as $yorum) {
// Baktığımız yorumun üst yorumu yoksa, ana yorum olarak eklensin.
if($yorum['ust_yorum_id'] === 0) {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['yorum_id'] ] = ['yorum'=>$yorum, 'alt_yorumlar'=>[]];
}
// Baktığımız yorumun üst yorumu varsa, o yorumun alt yorumu olarak eklensin
else {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['yorum_id'] ]['alt_yorumlar'][] = $yorum;
}
}
showVar($yorumlar);
showVar($duzenlenmisYorumlar);
- Oluşan hatanın ne olduğunu bilmiyoruz.
- Anasayfadaki sosyal medya linklerinin hangi kod yapısıyla listelendiğini bilmiyoruz.
Evet, if koşulu ile eğer veri yoksa PHP'nin olmayan değeri ekrana basmaya çalışmasını engellemek gerekir.
Örneğin aşağıdaki kod, Trying to access array offset on value of type null şeklinde hata verebilir:
<a href="<?= $linkler["facebook"]["link"] ?>">FACEBOOK</a>
Şu şekilde kontrol edilmesi gerekir:
<?php if(isset($linkler["facebook"]["link"])) { ?>
<a href="<?= $linkler["facebook"]["link"] ?>">FACEBOOK</a>
<?php } ?>
Üst Not: @fatihkurtl benden önce cevap vermiş. Benim cevabım biraz daha farklı oldu. Her yiğidin yoğurt yiyişi farklı tabi. :) Aşağıda benim kendimce cevabım var:
~ Kişi makale oku linkine tıkladığında html sayfasını mı görecek?
~ Evet. Direkt html sayfasını görmeli. PHP'ye hiç iş ilişmemeli. Veritabanını meşgul etmemeli. Çünkü makaleler sürekli güncellenen, anlık işleme girmesi gereken içerik değildirler. Mesela bir e-ticaret sitesinde ürün detay sayfaları da böyledir. Buralarda nadiren yapılan güncellemeler olur. Bu durumda da yeni bir cache dosyası oluşturulur. Ama mesela kullanıcının sepeti, anlık olarak işlem görmesi gereken, hassas bir yerdir. Mutlaka veritabanından kontrolü gerekir.
~ HTML sayfası PHP sayfasına include mu edilecek?
~ Hayır. HTML sayfası direkt bir dosya olarak oluşturulmalı ve mesela FTP'yle sunucudaki dosyalarınıza baktığınızda html uzantılı bir dosya olarak görülebilmeli. PHP olmasa bile kendi başına çalışabilir bir dosya olmalı. Böyle bir çıktıyı nasıl üretebileceğinizi şimdilik geçiyorum ama aslında zor değil. Sayfayı oluşturdunuz diyelim. Bu sayfaya yönlendirme işlemi PHP .htaccess dosyası ile sağlanabilir. Dosyanın adı aslında url'deki adıdır. Mesela yapay-zekanin-yukselisi.html adlı bir dosya. .htaccess dosyasında uygun bir yönlendirmeyle kullanıcının bu sayfaya http://siteadi.com/makale/yapay-zekanin-yukselisi url'siyle ulaşması sağlanmalı.
Örneğin bu dosyaları yönetim panelinden düzenledikten sonra /makaleler adlı bir klasöre PHP yardımıyla kaydettiniz. Şu an /makaleler/yapay-zekanin-yukselisi.html diye bir dosyanız var. Bu dosyaya, http://siteadi.com/makale/yapay-zekanin-yukselisi linki ile ulaşılabilsin istiyorsunuz.
Sitenizin kök klasöründeki .htaccess dosyasında şu şekilde yönlendirme sağlayabilirsiniz:
RewriteEngine On
RewriteRule ^makale/(.+)/?$ /makaleler/$1.html [L,R=301]
Bu sayede kullanıcı http://siteadi.com/makale/yapay-zekanin-yukselisi url'sini çağırırsa sunucu tarafından direkt /makaleler/yapay-zekanin-yukselisi.html dosyasına yönlendirilir.
Tabi CodeIgniter, Laravel gibi framework'ler kullanıyorsanız bu cache mekanizmasının framework'ler içinde kendi çözümleri var.
~ Her makalenin html dosyası olmalı mı?
~ Evet. Her makalenin bir html dosyası olmalı. Tabi binlerce makale olduğunda bu sistemi de optimize etmek gerekir. Bu çok daha detaylı bir konu.
Elbette küçük çaplı projelerde bu kadar optimizasyona gerek yok. Anlık kullanıcı sayısının çok artabileceği, sunucunun isteklere yetişemeyebileceği projeler için tüm bunlar yapılmalı ama zaten anlık 10-15 kullanıcıdan yukarı pek çıkmayan bir proje için bu kadar uğraşmak gerekmez.
Aldığınız hata: foreach() argument must be of type array|object, bool given in ... on line 70
Yani 70. satırda foreach, array veya object değeri ararken bool değerle karşılaşmış.
Muhtemelen 70. satır dediği yer sizin <?php foreach($ust_yorumlar as $ust_yorum): ?> satırınıza denk geliyordur.
Buna göre sanırım 67. satırınıza denk gelen $ust_yorumlar = yorumlar($mak_id, $ust_yorum_id); satırınızda $ust_yorumlar değişkeninize false değeri atanıyor olabilir.
Ama genel olarak kurgunuzda bir sorun var. foreach döngüsü içinde yorumlar() fonksiyonunu çağırmamalısınız. Bu fonksiyon veritabanına istek atıyor. Bu durumda dönünün her adımında veritabanı isteği atmış oluyorsunuz.
Çözüm olarak, makaleye ait tüm yorumları tek seferde çekmelisiniz ve PHP ile bir obje oluşturarak bu yorumların hiyerarşisini düzenlemelisiniz.
Yani mesela "SELECT * FROM yorumlar INNER JOIN uyeler ON uyeler.uye_id = yorumlar.uye_id WHERE makale_id = :mak_id"; şeklinde tüm yorumları tek seferde veren tek bir sorgu.
Bu sorgunun sonucu $yorumlar değişkenine aldınız varsayalım. Sonrasında mesela $duzenlenmisYorumlar adlı boş bir dizi oluşturup, $yorumlar dizinizde dolaşarak gerekli hiyerarşide yorumlarınızı yeni diziye aktarabilirsiniz.
<?php
$yorumlar = tumYorumlar($mak_id);
$duzenlenmisYorumlar = [];
foreach($yorumlar as $yorum) {
// Baktığımız yorumun üst yorumu yoksa, ana yorum olarak eklensin.
if($yorum['ust_yorum_id'] === 0) {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['yorum_id'] ] = ['yorum'=>$yorum, 'alt_yorumlar'=>[]];
}
// Baktığımız yorumun üst yorumu varsa, o yorumun alt yorumu olarak eklensin
else {
$duzenlenmisYorumlar[ 'yorum_'.$yorum['yorum_id'] ]['alt_yorumlar'][] = $yorum;
}
}
?>
Artık foreach ile dolaşıp yorumları dom'a basabilirsiniz.
<?php foreach($duzenlenmisYorumlar as $key=>$data) { ?>
<div class="ana_yorum" data-key="<?= $key ?>">
<div><?= $data['yorum']['yorum_adi'] ?></div>
<div><?= $data['yorum']['yorum_icerik'] ?></div>
<a href="#" class="mt-2">Cevapla</a>
<?php foreach($data['alt_yorumlar'] as $altYorum) { ?>
<div class="cevap_yorum">
<div><?= $altYorum['yorum_adi'] ?></div>
<div><?= $altYorum['yorum_icerik'] ?></div>
<a href="#" class="mt-2">Cevapla</a>
</div>
<?php } ?>
</div>
<?php } ?>
Uzun zamandır PHP kullanmıyorum. Yukarıdaki kodları test etmeden yazdım. Yani hatalı olabilir. Ama kurgusal olarak ne yapmaya çalıştığımı aşağı yukarı belli ediyordur.
Not: PHP'de string ifade belirtirken çift tırnak " ve tek tırnak ' farklı amaçlarla kullanılır.
Eğer string ifade içinde değişken kullanmayacaksanız (Örn: "Benim adım $isim. Seninki ne?") tek tırnak kullanmalısınız. Çünkü PHP, çift tırnak içinde bir değişken olabileceğini varsayar. Ama tek tırnak içinde değişken aramaz. Çok çok küçük bir performans maliyeti olsa da, büyük çaplı projeler için milisaniyeler bile önemli olduğundan, alışkanlık kazanmak iyi olur.
Not: Yanlış anlamayın, bu notları size özel yazmıyorum. Ben cevaplarımda, herkesin okuyabileceğini düşünerek böyle detaylar paylaşıyorum. :)
Tamamen örnek bir senaryo üzerinden yazıyorum:
1) yorum_id | makale_id | uye_id | yorum_icerik | ust_yorum_id
diye bir sütun daha eklenebilir.
Ahmet bir yorum yazdı. yorum_id=1, ust_yorum_id=null
Mehmet, Ahmet'e cevap yazdı. yorum_id=2, ust_yorum_id=1
Ahmet, Mehmet'e cevap yazdı. yorum_id=3, ust_yorum_id=2
Hüseyin de Mehmet'e cevap yazdı. yorum_id=4, ust_yorum_id=2
Mehmet, Hüseyin'e cevap yazdı. yorum_id=5, ust_yorum_id=4
2) Sayfaya sadece makale basılır ve makale en hızlı şekilde kullanıcıya iletilir.
Sayfa yüklendikten sonra yorumları almak için sunucuya makale_id ile birlikte GET isteği atılır.
Not: Yorumlar sayfaya direkt PHP ile basılmaz.
Kullanıcı bu makaleyi görüntülemek istediğinde asıl amacı makaleyi görmektir.
Makale kullanıcıya en hızlı şekilde gösterilmelidir.
O yüzden yorumların da veritabanından çekilmesi, hizaya sokulması vs. ek işlemler için kullanıcı bekletilmez.
Not: Mümkünse yorumlar, sayfa yüklendikten sonra hemen istenmez.
Kullanıcı belki de makalede yorumları okuyacak kadar kalmayacak.
Sunucuya ne kadar az yük bindirirsek o kadar rahat ederiz.
O yüzden, kullanıcı scroll yapıp makalenin sonuna gelip yorumların bulunacağı kısma yaklaşırsa o zaman yorumlar için istek atılır.
Böylece belki sadece 3-5 cümle okuyup sayfadan çıkacak kullanıcılar için gereksiz yere veritabanına iş bindirmemiş oluruz.
Not: Sayfanın kendisi bile php olmamalıdır.
Makale metni oluşturulduktan sonra bir html dosyası olarak kaydedilmelidir.
Kullanıcılar bu makaleyi görüntülemek istediğinde makale verisi her istek atan kullanıcı için veritabanından sorgulanmamalıdır.
Makale bir kez html dosyası olarak kaydedilip (cache'lenip) kullanıcılara doğrudan bu html dosyası iletilmelidir.
3) Sunucu, yorumları makale_id ile veritabanından çektikten sonra yorumlar üzerinde -güvenlik tedbirleri hariç- ek bir işlem yapmadan front-end'e json formatında iletir.
Not: Bu yorumların sıralanması, hiyerarşik olarak hangi yorumun hangi yoruma cevap olarak verildiği vs. gibi işlemler için sunucu yorulmamalıdır.
Bir işlemi sunucu tarafında yapmamız gerekiyorsa muhtemelen güvenlik gerekçesiyle olur. Ama yorumların sıralanması bir güvenlik sorunu değil.
Bu tür işlemleri kullanıcının tarayıcısına, yani kullanıcının bilgisayarının işlemcisine yıkarak sunucumuzu gereksiz iş yükünden koruruz.
4) Front-end'de JavaScript ile yorumlar belli bir hiyerarşi içinde organize edilir ve DOM'a basılır.
Son Not: Araya girdiğim bu notlar, sunucunun çok sayıda kullanıcıyı kaldırabilecek halde tutulması için. 3-5 kullanıcı için sunucu çok hızlı cevaplar verebilir ama 1000 kullanıcı aynı anda istek attığında her biri için veritabanını ayrı php'yi ayrı yorarsak kullanıcıların yanıt alma süresi çok uzar. Kullanıcılar belki hemen görebileceği bir makaleyi -iş sırasına alındığı için- 3-5 saniye beklemek zorunda kalırlar. Ama sayfa html olarak hazırda tutuluyorsa kullanıcılar istek atınca sunucu sadece html dosyasını iletip sıradaki işleme geçebilir. Ayrıca veritabanıyla iletişim minimumda tutulursa güvenlik açığı riski de azaltılmış olur.
Güvenlik demişken tabi bu aşamalarda bazı güvenlik adımları da araya sokulmalı. Çünkü kullanıcıdan alınan bir input'un ekrana basılması güvenlik açısından titiz davranılması gereken bir konu.