PHP'yi komut satırından çalıştırmak için php dosya.php yazmanız yeterlidir. Kısa bir kodu dosya oluşturmadan denemek için php -r 'kod', bir dosyanın sözdizimini çalıştırmadan kontrol etmek için php -l dosya.php, etkileşimli kabuk için php -a, yerel geliştirme sunucusu için php -S localhost:8000 kullanılır. Betiğe verilen argümanlar $argv dizisinde, sayıları $argc değişkeninde bulunur; standart giriş ve çıkış için hazır STDIN, STDOUT ve STDERR sabitleri vardır.

PHP'nin komut satırı arayüzüne CLI SAPI denir. Web sunucusu altında çalışan PHP'den bazı önemli farkları vardır: HTTP başlığı yazılmaz, hata mesajları HTML biçimi içermez, çalışma süresi sınırı yoktur ve çalışma dizini betiğin bulunduğu klasöre değiştirilmez. Bu yazıdaki komutlar ve çıktılar resmî PHP 8.5.12 Windows CLI derlemesiyle alındı; komutları Linux veya macOS'ta da aynı biçimde yazarsınız. Sunucu yapılandırması ve php.ini ayarları bu yazının konusu değil, onlar için PHP hosting rehberi yazısına bakabilirsiniz. Serinin tamamı PHP öğrenme rehberi sayfasında.

Sürüm ve kurulumu kontrol etmek

Sürüm ve kurulumu kontrol etmek

İlk adım PHP'nin kurulu olduğunu ve hangi sürümün çalıştığını görmektir. php -v sürümü, derleme bilgisini ve SAPI adını gösterir. Kılavuz, kullandığınız ikili dosyanın CLI mı yoksa CGI mı olduğunu anlamanın yolu olarak da bu komutu önerir; çıktıdaki (cli) ibaresi bunu söyler.

bash
php -v
text
PHP 8.5.12 (cli) (built: Oct 26 2026 09:51:07) (NTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Built by The PHP Group
Zend Engine v4.5.12, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.12, Copyright (c), by Zend Technologies

Diğer yararlı bilgi komutları şunlardır: php -m derlenmiş ve yüklenmiş modülleri listeler, php -i phpinfo() çıktısını metin olarak basar, php --ini hangi yapılandırma dosyalarının yüklendiğini gösterir. PHP 8.5 ile php --ini=diff seçeneği eklendi; yüklenen yapılandırmanın varsayılandan farklı olan değerlerini listeler. php --rf strlen bir fonksiyonun, php --rc DateTime bir sınıfın parametre ve metot bilgilerini Reflection ile gösterir. Windows'ta CLI ikili dosyası php.exe adıyla gelir.

Dosya çalıştırmak ve argüman almak: $argv ve $argc

php betik.php ve php -f betik.php aynı işi yapar; -f isteğe bağlıdır. Kılavuza göre çalıştırılan dosyanın .php uzantılı olması da gerekmez. Dosya adından sonra yazılan her şey betiğe argüman olarak geçer ve $argv global dizisinde toplanır. $argv[0] her zaman betiğin komut satırında yazıldığı adıdır. $argc ise $argv dizisinin eleman sayısıdır; kılavuz özellikle bunun betiğe verilen argüman sayısı olmadığını, betik adını da saydığını vurgular.

php
<?php
// argumanlar.php
echo 'SAPI: ', PHP_SAPI, "\n";
echo 'Sürüm: ', PHP_VERSION, "\n";
echo '$argc = ', $argc, "\n";
foreach ($argv as $i => $arg) {
    echo "$i: $arg\n";
}
bash
php argumanlar.php ilk "iki kelime" --mod=hizli
text
SAPI: cli
Sürüm: 8.5.12
$argc = 4
0: argumanlar.php
1: ilk
2: iki kelime
3: --mod=hizli

Tırnak içindeki "iki kelime" kabuk tarafından tek argüman olarak iletildi. --mod=hizli gibi değerler ise PHP tarafından yorumlanmadan string olarak gelir; ayrıştırmak sizin işinizdir. Bunun için aşağıda anlatılan getopt() fonksiyonu vardır. $argv ve $argc değerleri $_SERVER['argv'] ve $_SERVER['argc'] içinde de bulunur. Değişkenlerin kapsamı ve global değişkenler PHP değişkenler ve kapsam yazısında anlatılıyor.

Tire ile başlayan argümanlar ve -- ayırıcısı

Kılavuza göre tire (-) ile başlayan bir argüman sorun çıkarabilir, çünkü PHP onu kendi seçeneği sanabilir. Özellikle -f ya da -r kullanırken, betiğe gidecek argümanları -- ayırıcısından sonra yazarsınız; PHP bu ayırıcıdan sonraki her şeyi dokunmadan betiğe iletir.

bash
php -f argumanlar.php -- -h
php -r 'var_dump($argv);' -- -h
text
SAPI: cli
Sürüm: 8.5.12
$argc = 2
0: argumanlar.php
1: -h
array(2) {
  [0]=>
  string(19) "Standard input code"
  [1]=>
  string(2) "-h"
}

-r ile çalışan kodda $argv[0] değeri "Standard input code" olur; kılavuz PHP 7.2 öncesinde bunun tek bir tire olduğunu belirtir. Komut dosya adıyla, yani -f olmadan php argumanlar.php -h biçiminde verildiğinde de -h betiğe ulaşır, çünkü dosya adından sonraki her şey betiğin argümanıdır.

Seçenekleri ayrıştırmak: getopt

getopt(string $short_options, array $long_options = [], int &$rest_index = null): array|false komut satırı seçeneklerini bir diziye çevirir. Kısa seçenekler tek bir string içinde harflerle tanımlanır: Yalnız harf değer almayan bir bayrağı, harften sonra tek iki nokta zorunlu değeri, iki iki nokta isteğe bağlı değeri belirtir. Uzun seçenekler aynı kurallarla bir dizide verilir. PHP 7.1 ile gelen $rest_index parametresi ayrıştırmanın durduğu konumu yazar; seçenek olmayan geri kalan argümanları bu sayede alabilirsiniz.

php
<?php
// secenekler.php
$secenekler = getopt('vn:', ['yardim', 'dosya:', 'sessiz::'], $kalan);
var_dump($secenekler);
var_dump(array_slice($argv, $kalan));
bash
php secenekler.php -v -n 3 --dosya=rapor.csv --sessiz ek1 ek2
text
array(4) {
  ["v"]=>
  bool(false)
  ["n"]=>
  string(1) "3"
  ["dosya"]=>
  string(9) "rapor.csv"
  ["sessiz"]=>
  bool(false)
}
array(2) {
  [0]=>
  string(3) "ek1"
  [1]=>
  string(3) "ek2"
}

Değer almayan seçenekler dizide false değeriyle yer alır; bu biraz şaşırtıcıdır ama bir bayrağın verilip verilmediğini isset($secenekler['v']) ya da array_key_exists() ile kontrol edersiniz, değerine bakmazsınız. Değerler her zaman string olarak gelir; "3" değerini sayı olarak kullanmadan önce doğrulamak için filter_var iyi bir araçtır. Kılavuz, ayrıştırmanın seçenek olmayan ilk argümanda durduğunu da not eder; seçenekleri her zaman konumsal argümanlardan önce yazın.

Standart giriş ve çıkış: STDIN, STDOUT, STDERR

Standart giriş ve çıkış: STDIN, STDOUT, STDERR

CLI SAPI, komut satırı programlarını kolaylaştırmak için üç akışı hazır açık sabitler olarak tanımlar: STDIN, STDOUT ve STDERR. Bunları fopen('php://stdin', 'r') gibi kendiniz açmanız ya da kapatmanız gerekmez; betik bitince PHP kapatır. Kılavuz bir ayrıntıyı da belirtir: PHP betiğinin kendisi standart girişten okunuyorsa bu sabitler tanımlı değildir. Akışlardan okuma ve yazma, PHP dosya okuma ve yazma yazısında anlatılan fgets() ve fwrite() ile yapılır.

Aşağıdaki betik, Unix felsefesindeki küçük filtre programlarına benzer: Standart girişten satır satır sayı okur, sayı olmayan satırları uyarı olarak standart hataya yazar, sonucu standart çıkışa basar ve hiç sayı okunmadıysa sıfırdan farklı bir çıkış koduyla biter.

php
<?php
// topla.php
$toplam = 0;
$satir = 0;
while (($giris = fgets(STDIN)) !== false) {
    $giris = trim($giris);
    if ($giris === '') {
        continue;
    }
    if (!is_numeric($giris)) {
        fwrite(STDERR, "Atlandı: $giris\n");
        continue;
    }
    $toplam += $giris;
    $satir++;
}
fwrite(STDOUT, "Satır: $satir, Toplam: $toplam\n");
exit($satir > 0 ? 0 : 1);
bash
printf '10\n20\nabc\n\n12.5\n' | php topla.php
echo $?
printf '' | php topla.php
echo $?
text
Atlandı: abc
Satır: 3, Toplam: 42.5
0
Satır: 0, Toplam: 0
1

Hata mesajlarını STDERR akışına yazmanın yararı, çıktıyı bir dosyaya yönlendirdiğinizde (php topla.php < sayilar.txt > sonuc.txt) uyarıların dosyaya karışmayıp ekranda kalmasıdır. $toplam += $giris satırında string olan "12.5" sayısal bağlamda float olarak toplandı; bu dönüşüm kuralları PHP veri tipleri yazısında. Büyük girdileri satır satır işlemek bellek açısından da verimlidir; aynı kalıbı generator ile sarmak için generator ve yield yazısına bakabilirsiniz.

Çıkış kodları ve exit()

Kabuk betikleri, zamanlanmış görevler ve CI araçları bir programın başarılı olup olmadığını çıkış koduna bakarak anlar. Kılavuza göre 0 başarı, diğer değerler hata anlamına gelir. exit() bir tam sayı alırsa o değer çıkış kodu olur; bir string alırsa string yazdırılır ve çıkış kodu 0 olur. Çıkış kodları 0 ile 254 arasında olmalıdır; 255 PHP tarafından ayrılmıştır. Bu yazıdaki denemelerde yakalanmamış bir hatayla biten betikler de 255 koduyla sonlandı.

php
<?php
// selam.php
if (PHP_SAPI !== 'cli') {
    exit("Bu betik yalnızca komut satırından çalışır.\n");
}
$ad = $argv[1] ?? null;
if ($ad === null) {
    fwrite(STDERR, "Kullanım: php {$argv[0]} <ad>\n");
    exit(2);
}
echo "Merhaba, $ad!\n";
bash
php selam.php
echo $?
php selam.php Ayşe
echo $?
text
Kullanım: php selam.php <ad>
2
Merhaba, Ayşe!
0

Betiğin başındaki PHP_SAPI kontrolü, dosyanın yanlışlıkla web üzerinden çağrılmasına karşı bir önlemdir. Kılavuz, $argv değişkeninin varlığının betiğin komut satırından çalıştığını güvenilir biçimde göstermediğini, bunun yerine php_sapi_name() (ya da PHP_SAPI sabiti) değerinin kontrol edilmesi gerektiğini belirtir. ?? operatörü eksik argümanda uyarı almadan varsayılan değer vermeyi sağlar; ayrıntısı Undefined array key uyarısı yazısında.

exit ile finally ve PHP 8.4 değişikliği

Kılavuz, exit() çağrıldığında kapanış fonksiyonlarının (register_shutdown_function()) ve nesne yıkıcılarının çalıştığını, ancak finally bloklarının hiç çalışmadığını belirtir:

php
<?php
register_shutdown_function(function () {
    echo "kapanış fonksiyonu çalıştı\n";
});
try {
    echo "iş yapılıyor\n";
    exit(3);
} finally {
    echo "finally çalıştı\n";
}
/*
iş yapılıyor
kapanış fonksiyonu çalıştı
*/

Bu betik 3 koduyla çıktı ve finally bloğu atlandı. Temizlik işlemlerini finally içine koyup betiği exit() ile bitiriyorsanız bu davranışı hesaba katın. PHP 8.4 ile exit() gerçek bir fonksiyon oldu: Artık standart tip dönüşüm kurallarına uyar, strict_types bildirimine tabidir, isimli argümanla çağrılabilir ve geçersiz bir değerde TypeError fırlatır. Önceden dizi gibi değerler sessizce string'e çevriliyordu.

php
<?php
exit([1]);
/*
Fatal error: Uncaught TypeError: exit(): Argument #1 ($status) must be of type string|int, array given in /yol/ornek.php:2
Stack trace:
#0 /yol/ornek.php(2): exit(Array)
#1 {main}
  thrown in /yol/ornek.php on line 2
*/

Kısa kod denemek: php -r

-r seçeneği, kodu dosya oluşturmadan doğrudan komut satırında çalıştırır. Bu modda <?php ve ?> etiketleri yazılmaz; yazılırsa ayrıştırma hatası alırsınız. Kılavuzun en önemli uyarısı tırnaklarla ilgilidir: Unix kabukları çift tırnak içindeki $degisken ifadelerini PHP'ye ulaşmadan önce kendisi açar. Bu yüzden kodu tek tırnak içine yazmalısınız. Kılavuz ayrıca bu modun yalnızca basit kodlar için düşünüldüğünü ve auto_prepend_file gibi bazı ayarların yok sayıldığını belirtir.

bash
php -r 'echo PHP_VERSION, PHP_EOL;'
php -r '<?php echo 1;'
text
8.5.12

Parse error: syntax error, unexpected token "<", expecting end of file in Command line code on line 1

Sözdizimi kontrolü: php -l

php -l dosyayı çalıştırmadan yalnızca sözdizimini kontrol eder. Başarıda No syntax errors detected in ..., hatada ayrıştırıcı mesajıyla birlikte Errors parsing ... yazar ve çıkış kodunu -1 yapar; kabukta bu değer 255 olarak görünür. PHP 8.3'ten beri tek komutta birden çok dosya kontrol edilebilir.

bash
php -l argumanlar.php hatali.php tanimsiz.php
echo $?
text
No syntax errors detected in argumanlar.php

Parse error: syntax error, unexpected token "echo", expecting "," or ";" in hatali.php on line 3
Errors parsing hatali.php
No syntax errors detected in tanimsiz.php
255

hatali.php dosyasında ikinci satırın sonundaki noktalı virgül unutulmuştu. tanimsiz.php ise yalnızca tanimsizFonksiyon(); çağrısı içeriyordu ve kontrolü geçti. Kılavuz bunu açıkça söyler: -l, tanımsız fonksiyon gibi ancak kod çalışınca ortaya çıkan ölümcül hataları bulamaz. Bu hataların nasıl okunacağı Fatal error ve ParseError çözümü yazısında anlatılıyor. -l seçeneği -r ile birlikte kullanılamaz.

Etkileşimli kabuk: php -a

php -a etkileşimli bir kabuk açar; yazdığınız her satır hemen çalışır, fonksiyon tanımlayıp ardından çağırabilirsiniz. Kılavuza göre bu özellik PHP readline desteğiyle derlendiğinde çalışır; Windows'ta PHP 7.1'den beri readline eklentisi etkinse kullanılabilir. Kabuk fonksiyon, sabit, sınıf ve değişken adlarında sekme (Tab) ile tamamlama sunar ve geçmişi ~/.php_history dosyasında saklar. PHP 8.4 ile geçmiş dosyasının yolu PHP_HISTFILE ortam değişkeniyle değiştirilebilir hâle geldi.

Yerleşik geliştirme sunucusu: php -S

php -S localhost:8000 komutu, bulunduğunuz dizini belge kökü yapan basit bir HTTP sunucusu başlatır. Belge kökünü -t ile değiştirebilirsiniz. Komuttan sonra bir PHP dosyası verilirse bu dosya her istekte önce çalışan bir "router" betiği olur; betik false döndürürse istenen dosya olduğu gibi sunulur.

bash
php -S localhost:8000
php -S localhost:8000 -t public/
php -S localhost:8000 router.php

Bir URI dosya belirtmiyorsa sunucu o dizinde index.php veya index.html arar; bulamazsa üst dizinlere doğru belge köküne kadar aramayı sürdürür. PHP 8.4 ile bu arama, son bölümünde nokta bulunan ama bulunamayan yollar için de yapılır hâle geldi. Sunucu tek bir iş parçacığı ile çalışır, bu yüzden bir istek takılırsa diğerleri de bekler. PHP 7.4'ten beri PHP_CLI_SERVER_WORKERS ortam değişkeniyle birden çok işçi açılabilir, ancak bu Windows'ta desteklenmez.

CLI ile web SAPI arasındaki farklar

CLI ile web SAPI arasındaki farklar: HTTP başlıkları, Hata mesajı biçimi, max_execution_time, Çalışma dizini
KonuCLIWeb (ör. FPM, CGI)
HTTP başlıklarıyazılmazyazılır
Hata mesajı biçimidüz metin (html_errors kapalı)ayara göre HTML
max_execution_time0, yani sınırsızphp.ini değeri
Çalışma dizinideğiştirilmezSAPI'ye göre betiğin dizini olabilir
$argv ve $argcher zaman tanımlıregister_argc_argv ayarına bağlı
GET, POST, dosya yüklemedesteklenmezdesteklenir
Çıktı tamponuimplicit_flush açık, çıktı hemen yazılırayara göre tamponlanır

Tablodaki değerler kılavuzun "Differences from other SAPIs" sayfasından alınmıştır. Kılavuz bu ayarların CLI'de php.ini ile değiştirilemediğini, çalışma anında ise değiştirilebildiğini belirtir. Çalışma dizininin değişmemesi en çok sorun çıkaran farktır: Komutu hangi dizinden çalıştırdıysanız göreli dosya yolları oraya göre çözülür. Betiğin yanındaki dosyalar için __DIR__ kullanın; bu sabit PHP sabitleri yazısında anlatılıyor. PHP 8.5 geçiş notlarına göre, CLI dışındaki SAPI'lerde $_SERVER['argv'] değerinin sorgu dizgisinden türetilmesi kullanımdan kaldırıldı.

Unix sistemlerde betiğin ilk satırına #!/usr/bin/php gibi bir "shebang" satırı yazıp dosyaya çalıştırma izni verirseniz (chmod +x), betiği doğrudan ./betik olarak çalıştırabilirsiniz. Kılavuz, bu satırın Windows'ta zarar vermediğini de belirtir. Windows'ta aynı amaçla php.exe ile betiği çağıran bir .bat dosyası kullanılabilir.

Sık yapılan hatalar

  • $argc değerini argüman sayısı sanmak. Betik adı da sayılır; argüman sayısı $argc - 1 eder.
  • php -r ile çift tırnak kullanmak. Kabuk $ ile başlayan ifadeleri açar; tek tırnak kullanın.
  • php -l geçti diye kodun hatasız olduğunu varsaymak. Yalnızca sözdizimi kontrol edilir.
  • Hata mesajlarını echo ile basmak. Yönlendirilen çıktıya karışmaması için STDERR akışına yazın.
  • Başarısızlıkta da 0 koduyla çıkmak. Otomasyon araçları hatayı fark edemez; exit(1) gibi bir kod döndürün.
  • Göreli yolların betiğin klasörüne göre çözüleceğini düşünmek. CLI çalışma dizinini değiştirmez.
  • Yerleşik sunucuyu (php -S) canlı ortamda kullanmak. Yalnızca geliştirme içindir.

Sık Sorulan Sorular

php komutu bulunamıyor hatası alıyorum, ne yapmalıyım?

PHP'nin kurulu olduğu klasör sistemin PATH değişkeninde değildir. php.exe dosyasının tam yolunu yazarak çalıştırabilir ya da klasörü PATH'e ekleyebilirsiniz. Ardından php -v ile kontrol edin.

Komut satırındaki PHP ile web sunucusundaki PHP aynı mı?

Aynı sürüm olabilir ama farklı SAPI'lerdir ve farklı yapılandırma dosyaları kullanabilirler. php --ini CLI'nin hangi php.ini dosyasını yüklediğini gösterir; web tarafındaki değerler farklı olabilir.

Betiğim bir web isteğinden mi, komut satırından mı çalışıyor?

PHP_SAPI sabitini veya php_sapi_name() fonksiyonunu kontrol edin; CLI'de değer "cli" olur. Kılavuz, $argv varlığına bakmanın güvenilir olmadığını belirtir.

Uzun süren bir betik zaman aşımına uğrar mı?

CLI'de max_execution_time varsayılan olarak 0, yani sınırsızdır. Bellek sınırı ise geçerliliğini korur; bu sınıra takılırsanız memory_limit hatası yazısına bakabilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: PHP öğrenme rehberi · PHP dosya okuma ve yazma · PHP Fatal error çözümü · PHP temel sözdizimi · PHP nedir