---
title: 'Dosya Yükleme'
source: 'https://academia.sh/tr/kurslar/php-temelleri/dosya-yukleme'
course: 'PHP Temelleri'
language: tr
updated: '2026-10-21T05:03:34+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Yüklenmemiş bir dosyanın taşınmaya çalışılınca hangi kanaldan duyulduğunu ölçer'
    - 'Yerleşik sunucuya curl ile dosya göndererek $_FILES yapısını ve hata kodlarını gözlemler'
    - 'post_max_size aşımında hata koduyla dönen alanın bile kalmadığını gösterir'
---

# Dosya Yükleme

Dosya yüklemede sinyalin dönüş değeri ve hata koduyla gelmesi: yüklenmemiş dosyada move_uploaded_file, sunucu sınırının aşılması ve post_max_size aşımında boşalan istek.

Form İşleme dersinde geçersiz bir değer `filter_var` fonksiyonundan `false` olarak döndü ve istek 200 ile sürdü. Dosya yükleme aynı kanalı kullanır, ama istekte iki parça vardır: dosyanın kendisi ve PHP'nin ona eşlik ettirdiği durum bilgisi. Soru da genişler: yükleme başarısız olduğunda bu bilgi nerede, ve hiç gelmezse ne olur?

Yüklemenin HTTP tarafını, yani çok parçalı gövdenin yapısını M04, bir isteğin sunucuda nasıl ele alındığını M16/K01 kurdu. Burada ölçülen, PHP'nin bu istekten ne çıkardığıdır. Yüklenen dosya `$_FILES` süper küresel değişkenine konur; her dosya alanı için `name` (istemcinin verdiği ad), `size` (bayt), `error` (durum kodu) ve geçici dosyanın yolunu veren `tmp_name` anahtarları bulunur. Geçici dosya yolu ortama bağlı olduğu için bu derste hiçbir çıktıda basılmaz.

## Yüklenmemiş Dosya Dönüşle Duyulur

Dosyayı kalıcı bir yere taşımanın tek yolu `move_uploaded_file` fonksiyonudur ve yalnız PHP'nin o istekte kendisinin aldığı bir dosya için çalışır. İlk ölçüm komut satırında yapılır; orada hiçbir yükleme olmamıştır, bu yüzden fonksiyon var olan sıradan bir dosyaya uygulanınca da başarısız olmalıdır.

```php
<?php
// kanal_yukleme.php — yuklenmemis bir dosyanin tasinmasi hangi kanaldan duyulur
$uyarilar = [];
set_error_handler(function (int $seviye) use (&$uyarilar): bool {
    $uyarilar[] = $seviye === E_WARNING ? 'E_WARNING' : 'E_' . $seviye;
    return true;
});

file_put_contents('kaynak.txt', "merhaba\n");

try {
    $sonuc = move_uploaded_file('kaynak.txt', 'hedef.txt');
    $sinif = $uyarilar ? 'uyari' : ($sonuc === false ? 'donus' : 'sessiz');
} catch (Throwable $hata) {
    $sinif = 'durdurur';
}
printf("%-24s%-10s%s\n", 'yuklenmemis dosya', $sinif, 'sonuc ' . var_export($sonuc, true));
printf("%-24s%s\n", 'hedef olustu mu', file_exists('hedef.txt') ? 'evet' : 'hayir');
```

```
yuklenmemis dosya       donus     sonuc false
hedef olustu mu         hayir
```

Fonksiyon `false` döndürür; uyarı yok, istisna yok. Dosya hedefe taşınmadı. Bu **dönüş** sınıfıdır ve bu derste ölçülen kanal budur: yürütme, çağıran `false` değerini sorgulamazsa hiçbir şey olmamış gibi sürer. **IY15.** Ölçüm, fonksiyonun yalnız PHP'nin bir isteğin parçası olarak aldığı dosyalarda çalışmasının bilinçli bir güvenlik özelliği olduğunu gösterir: çağıran bir dosya adını elle verse bile fonksiyon onu taşımaz. Bu özelliğin neden var olduğu ve sınıflandırması M24 ile M25'in konusudur.

## Yüklenen Dosyanın Yapısı

Gerçek bir yüklemeyi yerleşik sunucu ve `curl -F` ile ölçeriz. `-F` bayrağı isteği çok parçalı gövdeyle gönderir ve `@` öneki alanın değerini bir dosyadan okutur.

```php
<?php
// yukle.php — yuklenen dosyayi ve alan sayilarini raporlar
$adlar = [
    UPLOAD_ERR_OK => 'UPLOAD_ERR_OK',
    UPLOAD_ERR_INI_SIZE => 'UPLOAD_ERR_INI_SIZE',
    UPLOAD_ERR_NO_FILE => 'UPLOAD_ERR_NO_FILE',
];

echo 'files=', count($_FILES), ' post=', count($_POST), "\n";
foreach ($_FILES as $alan => $dosya) {
    echo $alan, ' name=', $dosya['name'], ' size=', $dosya['size'];
    echo ' error=', $adlar[$dosya['error']] ?? $dosya['error'], "\n";
    if ($dosya['error'] === UPLOAD_ERR_OK) {
        $tasindi = move_uploaded_file($dosya['tmp_name'], 'saklanan.txt');
        echo 'tasindi=', $tasindi ? 'true' : 'false', ' boyut=', filesize('saklanan.txt'), "\n";
    }
}
```

```bash
# sunucu.sh — kucuk bir dosyayi yerlesik sunucuya yukler
printf 'merhaba dunya\n' > rapor.txt
php -S 127.0.0.1:8083 -d display_errors=0 >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yukle.php && break; sleep 0.1; done
curl -s -F 'dosya=@rapor.txt' -F 'baslik=ilk' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
files=1 post=1
dosya name=rapor.txt size=14 error=UPLOAD_ERR_OK
tasindi=true boyut=14
durum=200
```

`$_FILES` bir dosya alanı gördü, `$_POST` ise metin alanını (`baslik`). Alan adı, istemcinin verdiği dosya adı, bayt cinsinden boyut ve `UPLOAD_ERR_OK` hata kodu geldi; `move_uploaded_file` bu kez `true` döndü, çünkü dosya bu isteğin parçasıydı. Dönüş değerinin `true` olduğu tek durum budur: fonksiyon, yürütmenin başında doğru yoldan gelmiş bir dosyayı kabul eder. **IY16.** `tmp_name` bu çıktıda yoktur; değer her koşumda değişir ve ders onu basmaz.

## `$_FILES` Yapısı ve Dosya Seçilmemiş Alan

Şimdiye kadar yalnız üç anahtar okundu. Bir dosya alanının tüm yapısı, anahtar adlarıyla birlikte tek bir betikle görülebilir; `tmp_name` değeri basılmaz, yalnız anahtar adı listelenir. Aynı betik, formda dosya alanı bulunduğu hâlde kullanıcının dosya seçmediği durumu da ölçer: bu durumda tarayıcı alanı boş bir dosya adıyla gönderir.

```php
<?php
// yapi.php — $_FILES alanlarinin tum anahtarlari ve hata kodlari
foreach ($_FILES as $alan => $dosya) {
    $anahtarlar = array_keys($dosya);
    sort($anahtarlar);
    echo $alan, ' anahtarlar=', implode(',', $anahtarlar), "\n";
    echo $alan, ' error=', json_encode($dosya['error']), ' size=', json_encode($dosya['size']), "\n";
}
echo 'dosya_var=', isset($_FILES['dosya']) ? 'evet' : 'hayir', "\n";
```

```bash
# yapi.sh — dolu alan ve dosya secilmemis alan
printf 'merhaba\n' > rapor.txt
php -S 127.0.0.1:8083 -d display_errors=0 >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yapi.php && break; sleep 0.1; done
echo '--- dolu alan'
curl -s -F 'dosya=@rapor.txt' http://127.0.0.1:8083/yapi.php
echo '--- dosya secilmemis'
curl -s -F 'dosya=@/dev/null;filename=' http://127.0.0.1:8083/yapi.php
kill $pid
```

```
--- dolu alan
dosya anahtarlar=error,full_path,name,size,tmp_name,type
dosya error=0 size=8
dosya_var=evet
--- dosya secilmemis
dosya anahtarlar=error,full_path,name,size,tmp_name,type
dosya error=4 size=0
dosya_var=evet
```

Dolu alan `error` 0 ve `size` 8 verir; anahtar listesi ölçülen PHP 8.5 sürümünde `full_path`, `name`, `size`, `tmp_name`, `type` ve `error` anahtarlarını içerir. **IY17.** `full_path` anahtarı 8.1 sürümünden beri vardır; ders anahtar listesini yalnız ölçülen sürüm için gösterir. Dosya seçilmemiş alanda ise `error` `UPLOAD_ERR_NO_FILE` değeri olan 4 gelir ve `size` 0'dır. Bu, `$_FILES['dosya']` anahtarının var olduğu ama yüklemenin yapılmadığı durumdur: `isset` bunu `evet` diye bildirir, yani anahtarın varlığı yüklemenin başarısını göstermez. Başarı, `error` değerinin `UPLOAD_ERR_OK` olmasıyla anlaşılır.

## Çoklu Dosya Dizisi Alan Başına Değil Anahtar Başınadır

Alan adı `dosya[]` ile verilince PHP her dosya için ayrı bir kayıt oluşturmaz. Bunun yerine her anahtarın (`name`, `size`, `error`) altında dosyaların dizisini tutar; yani yapı dosya başına bir kayıt gibi değil, anahtar başına bir dizi gibi görünür.

```php
<?php
// coklu.php — dosya[] alaninin $_FILES'taki yapisi
$dosya = $_FILES['dosya'];
echo 'name_tipi=', get_debug_type($dosya['name']), ' adet=', count($dosya['name']), "\n";
foreach ($dosya['name'] as $sira => $ad) {
    echo $sira, ' ', $ad, ' size=', $dosya['size'][$sira], ' error=', $dosya['error'][$sira], "\n";
}
```

```bash
# coklu.sh — iki dosyayi tek alan adiyla yukler
printf 'bir\n' > a.txt
printf 'iki iki\n' > b.txt
php -S 127.0.0.1:8083 -d display_errors=0 >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/coklu.php && break; sleep 0.1; done
curl -s -F 'dosya[]=@a.txt' -F 'dosya[]=@b.txt' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/coklu.php
kill $pid
```

```
name_tipi=array adet=2
0 a.txt size=4 error=0
1 b.txt size=8 error=0
durum=200
```

`name` bir `array` olur ve her dosyanın bilgisi aynı sıra numarasıyla üç ayrı diziden okunur. Tek dosya için yazılan `$dosya['name']` okuması, çoklu yüklemede bir diziyi dizgi gibi kullanmaya çalışır; bu, Süper Küresel Değişkenler dersindeki `strlen` ve dizi ölçümünün aynı hatasıdır. **IY18.** İstemci alan adını `dosya[]` yaparak yapıyı değiştirebildiği için, tek dosya bekleyen kod için yapı yine istemcinin seçimidir.

## Hedef Yol Yoksa Taşıma Uyarı Verir

Taşıma fonksiyonu iki ayrı nedenle `false` döndürebilir: dosya bu isteğin parçası değildir ya da hedef yazılamaz. İlki sessizdir. İkincisi aynı dönüş değerini bir uyarıyla birlikte verir. Aşağıdaki betik gerçek bir yükleme alır ve dosyayı olmayan bir dizine taşımaya çalışır; uyarının metni basılmaz, yalnız seviye adı basılır.

```php
<?php
// hedef.php — yuklenen dosyayi olmayan bir dizine tasima denemesi
$seviyeler = [];
set_error_handler(function (int $seviye) use (&$seviyeler): bool {
    $seviyeler[] = $seviye === E_WARNING ? 'E_WARNING' : 'E_' . $seviye;
    return true;
});

$sonuc = move_uploaded_file($_FILES['dosya']['tmp_name'], 'olmayan_dizin/hedef.txt');
echo 'sonuc=', var_export($sonuc, true), ' uyari=', implode(',', $seviyeler), "\n";
```

```bash
# hedef.sh — dosyayi gonderir, tasima olmayan dizine yapilir
printf 'merhaba\n' > rapor.txt
php -S 127.0.0.1:8083 -d display_errors=0 >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/hedef.php && break; sleep 0.1; done
curl -s -F 'dosya=@rapor.txt' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/hedef.php
kill $pid
```

```
sonuc=false uyari=E_WARNING,E_WARNING
durum=200
```

Fonksiyon `false` döndürür ve ek olarak iki `E_WARNING` bildirir: biri hedefin açılamamasını, öteki taşımanın başarısızlığını söyler. Aynı dönüş değeri, ilk bölümdeki yüklenmemiş dosyada uyarısız gelmişti; burada sınıf, **uyarı**ya kayar. **IY19.** Bu fark dönüş değerine bakan kod için görünmezdir: ikisi de `false`'tur. Yalnız hata işleyicisi ya da kayıt dosyası olan kod, taşımanın neden başarısız olduğunu ayırt edebilir; durum kodu yine 200'dür.

## Sunucu Sınırı Aşılırsa

PHP iki ini değeriyle yüklemeyi sınırlar: `upload_max_filesize` tek bir dosyanın, `post_max_size` ise tüm isteğin üst sınırıdır. Ders hiçbir ayarın varsayılanına güvenmez; sınır `-d` ile verilir. İlk ölçümde tek dosya sınırı küçük tutulur ve büyük bir dosya gönderilir. İkinci ölçümde dosya alanı hiç gönderilmez.

```bash
# sinir.sh — dosya sinirinin asilmasi ve dosya alaninin gonderilmemesi
head -c 3000 /dev/zero | tr '\0' 'a' > buyuk.txt
php -S 127.0.0.1:8083 -d display_errors=0 -d upload_max_filesize=1K >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yukle.php && break; sleep 0.1; done
echo '--- 3000 bayt, sinir 1K'
curl -s -F 'dosya=@buyuk.txt' -F 'baslik=ikinci' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
echo '--- dosya alani yok'
curl -s -F 'baslik=ucuncu' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
--- 3000 bayt, sinir 1K
files=1 post=1
dosya name=buyuk.txt size=0 error=UPLOAD_ERR_INI_SIZE
durum=200
--- dosya alani yok
files=0 post=1
durum=200
```

Sınır aşıldığında alan `$_FILES` içinde yine vardır: adı korunur, `size` 0 olur ve `error` `UPLOAD_ERR_INI_SIZE` olur. Sinyal bu kez hata kodunda gelir; kod okunmazsa dosya hiç gelmemiş gibi sürülür. İstek yine 200 verir. Dosya alanı hiç gönderilmediğinde ise `$_FILES` boştur ve `$_POST` doludur: kodun `$_FILES['dosya']`'yı doğrudan okuması hem uyarı hem `null` demektir, yani Süper Küresel Değişkenler dersindeki eksik anahtardır.

Dosyayı saklarken istemcinin verdiği adı yola doğrudan koymamak, türü yalnız uzantıdan çıkarmamak ve kalıcı yeri belge köküne koymamak gerekir; bu ilkelerin gerekçesi M16/K01'de, sınıflandırması ve savunma yordamları M24 ile M25'te anlatılır. Bu derste `saklanan.txt` sabit bir ad olarak yalnız ölçümü tekrarlanabilir kılmak için kullanıldı.

## Sınırlayıcı Ölçüm

Önceki bölümde hata koduyla dönen alan vardı. Sınırı daha da aşağı çekersek bu da kalmaz. `post_max_size` aşılınca PHP isteğin gövdesini hiç ayrıştırmaz: ne dosya ne metin alanı elde kalır.

```bash
# tavan.sh — post_max_size asildiginda ne kaliyor
head -c 5000 /dev/zero | tr '\0' 'a' > buyuk.txt
php -S 127.0.0.1:8083 -d display_errors=0 -d post_max_size=1K >/dev/null 2>&1 &
pid=$!
for i in $(seq 1 50); do curl -s -o /dev/null http://127.0.0.1:8083/yukle.php && break; sleep 0.1; done
curl -s -F 'dosya=@buyuk.txt' -F 'baslik=dorduncu' -w 'durum=%{http_code}\n' http://127.0.0.1:8083/yukle.php
kill $pid
```

```
files=0 post=0
durum=200
```

`$_FILES` da `$_POST` da boştur ve durum kodu 200'dür. Hata koduyla gelen bir alan bile yok: istek, hiç alan göndermemiş bir istekten ayırt edilemez. **IY20.** `display_errors=0` ile ölçüldü; ayar açıkken PHP'nin başlangıçta ürettiği uyarı gövdeye düşebilir ve ders o metni basmaz. Bu, önceki bölümdeki sınıfın alt sınırıdır: dönüş kanalı, kanalın konuşacağı değerin kendisi geldiğinde işe yarar. Değer hiç gelmediğinde ise okunacak hata kodu da kalmaz. Bu durumu kodun fark etmesinin tek yolu, beklediği alanların yokluğunu ayrı bir koşul olarak sınamasıdır.

## Özet

- `$_FILES` her dosya alanı için `name`, `size`, `error` ve `tmp_name` taşır; `move_uploaded_file` yalnız bu isteğin aldığı dosyayı taşır.
- Bu dersin ölçü sınıfı **dönüş**tür: yüklenmemiş dosyada `move_uploaded_file` uyarı vermeden `false` döndürür.
- `upload_max_filesize` aşılınca alan `$_FILES` içinde kalır, `size` 0 ve `error` `UPLOAD_ERR_INI_SIZE` olur; istek yine 200 verir.
- Dosya alanı gönderilmezse `$_FILES` boş, `$_POST` dolu kalır.
- `post_max_size` aşılınca `$_POST` ve `$_FILES` ikisi de boşalır; hata kodu taşıyan alan bile kalmaz ve istek sinyalsiz boş bir istek gibi görünür.

## Sonraki Adım

Yükleme dersinde kod, `yukle.php` gibi bir betiğe kendi yazdığı dosyayı verip bir yanıt aldı. Bir betiğin başka bir dosyayı kendi içine çekmesi ise ayrı bir işlemdir ve aynı sorunun yeni bir biçimini getirir: çekilecek dosya bulunamazsa ne olur? Dosya Dahil Etme dersi, bulunamayan bir dosyanın dahil edilmesinin hangi kanaldan duyulduğunu ve bu kanalın dahil etme türüne göre nasıl değiştiğini ölçüyor.
