İçeriğe geç
academia.sh

Ders 09 / 10

Yapılandırma Ayarı

PHP'nin geliştirme ve üretim ayar dosyalarının ayrıştığı yedi yönergeyi ölçmek: sertleştirme neyi kapatır ve kod içinden ini_set() ile geri açılabilir mi.

Yayın Güncelleme

Bu derste

  • Geliştirme ve üretim ayar dosyalarını ayıran yedi yönergeyi ve her birinin değerini sıralayabilmek
  • Çalışırken zend.assertions değerinin üretimde değiştirilemediğini ve istisna izinde argümanların gizlendiğini ölçmek
  • Kod içinden ini_set() ile display_errors açmanın sertleştirmeyi geri alabileceğini yorumlamak
İçindekiler

PHP iki hazır ayar dosyasıyla dağıtılır: biri geliştirme, biri üretim içindir. Bu dersin konusu, o iki dosyanın birbirinden hangi noktada ayrıldığıdır. Ayrım bir tercih listesi değil, sertleştirmedir (hardening): üretimde hata ayrıntısını, çağrı argümanlarını ve geliştirme denetimlerini kapatarak, bir hatanın dışarıya bilgi sızdırmasını engellemek. Ortam değişkenleriyle yapılandırmayı daha önce web uygulaması bağlamında gördünüz (“Yapılandırma ve Ortam Değişkenleri”); burada ayarın kendisi, yani PHP’nin yönergeleri ölçülür.

Ölçü ekseni bu kursun ortak sorusudur: bu davranış üretime çıkıyor mu? Aşağıdaki yan.php tam olarak bu iki dosyayı PROFILLER sabitinde taşır. Dosya bu derste de yalnız diske yazılır.

<?php
// yan.php — bu davranış üretime çıkıyor mu?
declare(strict_types=1);

// PHP'nin dağıttığı iki ayar dosyası (php.ini-development, php.ini-production) yedi
// yönergede ayrışır; yedisi buraya birebir alındı. Üretimdeki son iki satır dağıtım
// dosyasında yorum satırıdır: kod önbelleği komut satırında da açık, dosya denetimi kapalı.
const PROFILLER = [
    'geliştirme' => [
        'display_errors' => '1',
        'display_startup_errors' => '1',
        'error_reporting' => '32767',
        'mysqlnd.collect_memory_statistics' => '1',
        'zend.assertions' => '1',
        'zend.exception_ignore_args' => '0',
        'zend.exception_string_param_max_len' => '15',
    ],
    'üretim' => [
        'display_errors' => '0',
        'display_startup_errors' => '0',
        'error_reporting' => '24575',
        'mysqlnd.collect_memory_statistics' => '0',
        'zend.assertions' => '-1',
        'zend.exception_ignore_args' => '1',
        'zend.exception_string_param_max_len' => '0',
        'opcache.enable_cli' => '1',
        'opcache.validate_timestamps' => '0',
    ],
];

/** Betiği ayar dosyası olmadan, yalnız profilin yönergeleriyle ayrı bir süreçte koşturur. */
function calistir(string $betik, array $yonergeler): bool {
    $komut = ['php', '-n'];
    foreach ($yonergeler as $ad => $deger) {
        array_push($komut, '-d', "$ad=$deger");
    }
    $komut[] = $betik;
    $surec = proc_open($komut, [1 => ['pipe', 'w'], 2 => ['file', '/dev/null', 'w']], $borular);
    $cikti = stream_get_contents($borular[1]);
    fclose($borular[1]);
    proc_close($surec);
    $satirlar = array_values(array_filter(array_map('trim', explode("\n", $cikti))));
    return end($satirlar) === 'evet';
}

/** Davranışın iki koşumda görülüp görülmediğinden yanını çıkarır. */
function yan(bool $gelistirmede, bool $uretimde): string {
    return match (true) {
        $gelistirmede && $uretimde => 'iki yanda da',
        $gelistirmede => 'yalnız geliştirmede',
        $uretimde => 'yalnız üretimde',
        default => 'koşuma girmiyor',
    };
}

function yanRaporu(string $ad, string $betik): string {
    $gelistirmede = calistir($betik, PROFILLER['geliştirme']);
    $uretimde = calistir($betik, PROFILLER['üretim']);
    $sonYan = yan($gelistirmede, $uretimde);
    echo "$ad → $sonYan\n    geliştirme ", $gelistirmede ? 'evet' : 'hayır',
        ' · üretim ', $uretimde ? 'evet' : 'hayır', "\n";
    return $sonYan;
}

Önce yedi farkı PROFILLER sabitinden çıkaralım. Betik iki profildeki değerleri karşılaştırır ve yalnız ayrışan yönergeleri yazar. Profil dizisinde yalnız üretime özgü olan opcache.* yönergeleri bu karşılaştırmanın dışında kalır, çünkü iki dosyanın farkı değil, komut satırında kod önbelleğini açan eklemelerdir.

<?php
// fark.php — iki profilin ayrıştığı yönergeleri yazar
declare(strict_types=1);
require __DIR__ . '/yan.php';

$ayrisan = 0;
foreach (PROFILLER['geliştirme'] as $ad => $deger) {
    $uretim = PROFILLER['üretim'][$ad];
    if ($deger !== $uretim) {
        $ayrisan++;
        echo "$ad: geliştirme $deger · üretim $uretim\n";
    }
}
echo "ayrışan yönerge sayısı: $ayrisan\n";
display_errors: geliştirme 1 · üretim 0
display_startup_errors: geliştirme 1 · üretim 0
error_reporting: geliştirme 32767 · üretim 24575
mysqlnd.collect_memory_statistics: geliştirme 1 · üretim 0
zend.assertions: geliştirme 1 · üretim -1
zend.exception_ignore_args: geliştirme 0 · üretim 1
zend.exception_string_param_max_len: geliştirme 15 · üretim 0
ayrışan yönerge sayısı: 7

Yedi satırın okunuşu şöyledir. İlk üç satır hata görünürlüğünü belirler: display_errors ve display_startup_errors hatayı yanıta yazar, error_reporting hangi türlerin bildirileceğini söyler. mysqlnd.collect_memory_statistics veritabanı sürücüsünün bellek istatistiğini toplar, üretimde kapalıdır. zend.assertions assert() çağrılarının derlenip derlenmeyeceğini belirler. Son iki satır istisna izine aittir: zend.exception_ignore_args izden çağrı argümanlarını atar, zend.exception_string_param_max_len kalan dize argümanlarının uzunluğunu sınırlar.

Yönerge Geliştirme Üretim
display_errors 1 0
display_startup_errors 1 0
error_reporting 32767 24575
mysqlnd.collect_memory_statistics 1 0
zend.assertions 1 -1
zend.exception_ignore_args 0 1
zend.exception_string_param_max_len 15 0

Şimdi ayrımın davranışa nasıl yansıdığına bakalım. Birinci yoklama çalışırken ini_set('zend.assertions', '1') dener ve çağrının reddedildiğini, değerin değişmediğini sorar. İkinci yoklama içinde bir fonksiyon çağrısı olan bir istisna fırlatır ve istisna izinde argümanın saklanıp saklanmadığına bakar. Her iki yoklama da evet yönünde yazıldı: yalnız üretimde görünen davranış evet verir.

<?php
// ayar.php — sertleştirme davranışta nasıl görünür?
declare(strict_types=1);
require __DIR__ . '/yan.php';

file_put_contents('assertion.php', <<<'PHP'
<?php
// assertion.php — çalışırken zend.assertions açılmaya çalışılır
$donen = @ini_set('zend.assertions', '1');
echo $donen === false && ini_get('zend.assertions') === '-1' ? "evet\n" : "hayır\n";
PHP);

file_put_contents('iz.php', <<<'PHP'
<?php
// iz.php — istisna izinde çağrı argümanı saklanır mı?
function at(string $gizli): void {
    throw new RuntimeException();
}
try {
    at('gizli deger');
} catch (RuntimeException $istisna) {
    echo !isset($istisna->getTrace()[0]['args']) ? "evet\n" : "hayır\n";
}
PHP);

yanRaporu('zend.assertions çalışırken açılamaz', 'assertion.php');
yanRaporu('istisna izinde argüman saklanmaz', 'iz.php');
zend.assertions çalışırken açılamaz → yalnız üretimde
    geliştirme hayır · üretim evet
istisna izinde argüman saklanmaz → yalnız üretimde
    geliştirme hayır · üretim evet

Her iki yoklama yalnız üretimde evet verdi: yan yalnız üretimde. Üretimde zend.assertions değeri -1 iken çalışırken açılmaya çalışılan değer reddedildi ve -1 kaldı; bu, üretimde derlenmemiş assert() çağrılarının sonradan etkinleştirilemeyeceği anlamına gelir. İstisna izi ise argümanı taşımadı; geliştirmede aynı iz argümanı taşıdığı için bir hata mesajının yığıt kaydı bir parolayı ya da kullanıcı verisini dışarı çıkarabilirdi. Üretimde bu iki sızıntı yolu kapalıdır. (BA5. Bu yoklama yalnız profil değerinin etkisine bakar; zend.exception_ignore_args yönergesinin çalışırken değiştirilebilirliği aşağıdaki erişim düzeyi bölümünde ölçülür.)

Yönergenin Erişim Düzeyi

Ayar dosyası bir yönergeyi belirler, ama yönergeye sonradan kim dokunabilir sorusu ayrıdır. PHP her yönergeye bir erişim düzeyi (access level) verir: PHP_INI_USER kullanıcı betiğinin ini_set() ile değiştirebileceği, PHP_INI_PERDIR ayar dosyasında ya da dizin düzeyinde değiştirilebileni, PHP_INI_SYSTEM ise yalnız ana ayar dosyasında belirlenebileni gösterir. Üçünü birden taşıyan yönerge PHP_INI_ALL olarak anılır. ini_get_all(null, true) her yönergenin bu düzeyini döndürür. Aşağıdaki yoklama sekiz yönerge için iki şey yazar: erişim düzeyi ve ini_set() çağrısının kabul edilip edilmediği. Kabul, çağrının false dönmemesi demektir. Sekiz yönergeden yedisi profil farkı olan yönergelerdir; sekizincisi opcache.enable_cli üretim profilinin eklediği ve değeri komut satırında kod önbelleğini açan yönergedir. mysqlnd.collect_memory_statistics ölçüm dışında bırakıldı: yönerge veritabanı sürücüsü eklentisine bağlıdır ve bu ders yalnız çekirdek ve kod önbelleği yönergelerinin erişim düzeyini ölçer. Yoklama çocuk süreçte koşar, sonucu profile özgü dosyaya yazar; sürücü ikisini karşılaştırır.

Ek olarak ikinci bir yoklama, zend.exception_ignore_args için ini_set() davranışını doğrudan sınar: üretim profilinde yönerge 1 iken kod içinden 0 yapılırsa, sonradan fırlatılan istisnanın izinde argüman görünür mü?

<?php
// erisim.php — yönergeyi kim değiştirebilir, ini_set() kabul eder mi?
declare(strict_types=1);
require __DIR__ . '/yan.php';

file_put_contents('erisim-olc.php', <<<'PHP'
<?php
// erisim-olc.php — erişim düzeyini ve ini_set() kabulünü profile özgü dosyaya yazar
$profil = ini_get('zend.assertions') === '1' ? 'gelistirme' : 'uretim';
$hedefler = [
    'display_errors' => '1',
    'display_startup_errors' => '1',
    'error_reporting' => '1',
    'zend.assertions' => '0',
    'zend.exception_ignore_args' => '0',
    'zend.exception_string_param_max_len' => '5',
    'opcache.enable_cli' => '1',
    'opcache.validate_timestamps' => '1',
];
$tum = ini_get_all(null, true);
$seviyeler = [1 => 'USER', 2 => 'PERDIR', 4 => 'SYSTEM'];
$satirlar = [];
foreach ($hedefler as $ad => $hedef) {
    $erisim = $tum[$ad]['access'];
    $adlar = [];
    foreach ($seviyeler as $bit => $seviye) {
        if (($erisim & $bit) !== 0) {
            $adlar[] = $seviye;
        }
    }
    $kabul = @ini_set($ad, $hedef) !== false ? 'kabul' : 'ret';
    $satirlar[] = $ad . ': ' . ($erisim === 7 ? 'ALL' : implode('|', $adlar)) . ' · ini_set ' . $kabul;
}
file_put_contents("erisim-$profil.txt", implode("\n", $satirlar) . "\n");
echo "evet\n";
PHP);

file_put_contents('iz-acik.php', <<<'PHP'
<?php
// iz-acik.php — zend.exception_ignore_args kod içinden 0 yapılırsa izde argüman görünür mü?
ini_set('zend.exception_ignore_args', '0');
function at(string $gizli): void {
    throw new RuntimeException();
}
try {
    at('gizli deger');
} catch (RuntimeException $istisna) {
    echo isset($istisna->getTrace()[0]['args']) ? "evet\n" : "hayır\n";
}
PHP);

yanRaporu('erişim ölçümü tamamlanır', 'erisim-olc.php');
echo file_get_contents('erisim-uretim.txt');
$uretimSatirlari = file('erisim-uretim.txt', FILE_IGNORE_NEW_LINES);
foreach (file('erisim-gelistirme.txt', FILE_IGNORE_NEW_LINES) as $satir) {
    if (!in_array($satir, $uretimSatirlari, true)) {
        echo "geliştirmede farklı: $satir\n";
    }
}
yanRaporu('kod içinden 0 yapılan zend.exception_ignore_args izde argümanı geri getirir', 'iz-acik.php');
erişim ölçümü tamamlanır → iki yanda da
    geliştirme evet · üretim evet
display_errors: ALL · ini_set kabul
display_startup_errors: ALL · ini_set kabul
error_reporting: ALL · ini_set kabul
zend.assertions: ALL · ini_set ret
zend.exception_ignore_args: ALL · ini_set kabul
zend.exception_string_param_max_len: ALL · ini_set kabul
opcache.enable_cli: SYSTEM · ini_set ret
opcache.validate_timestamps: ALL · ini_set kabul
geliştirmede farklı: zend.assertions: ALL · ini_set kabul
kod içinden 0 yapılan zend.exception_ignore_args izde argümanı geri getirir → iki yanda da
    geliştirme evet · üretim evet

Tabloyu satır satır okuyalım. Sekiz yönergenin yedisi ALL düzeyindedir; yani PHP_INI_USER, PHP_INI_PERDIR ve PHP_INI_SYSTEM bitlerinin üçü de açıktır ve kod içinden ini_set() ile değiştirilebilir. opcache.enable_cli ise yalnız SYSTEM düzeyindedir: yalnız ana ayar dosyasında ya da komut satırında verilebilir ve çalışırken ini_set() onu reddeder. Tablo ret ile kabul arasındaki farkı gösterir. ret, çağrının false döndüğü, değerin değişmediği anlamına gelir. kabul ise çağrının değeri değiştirdiğidir. Tabloda üretim yanında iki ret var: zend.assertions ve opcache.enable_cli. Geliştirme yanında yalnız biri var: opcache.enable_cli.

Üretim ile geliştirme arasındaki tek fark zend.assertions satırıdır ve sürücünün son satırı bunu açıkça yazar: geliştirmede aynı yönerge kabul verdi. Erişim düzeyi iki profilde de ALL görünüyor, yani yönerge kâğıt üzerinde kod içinden değiştirilebilir; yine de üretimde -1 iken reddediliyor. Bunun sebebi PHP’nin bu yönergeye koyduğu ek kuraldır: -1 değeri derleme zamanında assert() çağrılarının koddan çıkarılması demektir ve bir kez çıkarılan çağrı çalışırken geri getirilemez. Erişim düzeyi tek başına yeterli bilgi vermez: ALL yazan bir yönerge yine de belirli bir değerden belirli değerlere geçişi reddedebilir. Bu yüzden ölçüm yalnız tabloya değil, ini_set() sonucuna da bakar.

Üretimde altı yönergenin kabul çıkması önemli bir uyarıdır: display_errors, error_reporting, zend.exception_ignore_args ve zend.exception_string_param_max_len gibi yönergeler ayar dosyasında sertleştirildiği hâlde, kodun herhangi bir satırı bunları gevşetebilir. Son yoklama bunu zend.exception_ignore_args için doğrudan gösterdi. Üretim profilinde yönerge 1 iken kod içinden 0 yapıldığında, sonradan fırlatılan istisnanın izinde argüman yeniden göründü. (BA7. Yoklama yönergeyi istisna fırlatmadan önce değiştirdi; önceden oluşturulmuş bir istisnanın izi sonradan değişmez, bu ölçülmedi.) Bu yoklama iki profilde de evet verdiği için sınıfı iki yanda dadır; ama çıkardığı sonuç yalnız üretim için önemlidir, çünkü üretimde bu çağrı sertleştirmeyi geri alır.

Gerçek bir uygulamada bunun karşılığı iki somut alışkanlıktır. Birincisi, kodda ini_set() çağrılarını düzenli aramaktır: özellikle kütüphanelerin içinde hata görünürlüğünü ya da istisna izini gevşeten çağrılar, ayar dosyasındaki sertleştirmeyi sessizce geçersiz kılar. İkincisi, bir yönergeyi gerçekten kilitlemek istiyorsanız erişim düzeyine bakmaktır: PHP_INI_SYSTEM düzeyindeki yönergeler kod içinden değişmez, ALL düzeyindekiler değişir. Bazı barındırma kurulumları disable_functions ya da özel ayar dosyalarıyla ini_set() çağrısının kendisini kısıtlar; bu kısıtlamalar bu derste kurulmadı ve ölçülmedi.

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

Sertleştirme ayar dosyasında yapılır; kod içinden geri alınamaz mı? display_errors bir kod satırıyla yeniden açılabilen yönergelerden biridir. Üretim profiliyle başlayıp betik içinde ini_set('display_errors', '1') çağırırsak hata yanıta yazılır mı? Yoklama hata iletisini yakalar ve metnini basmaz; yalnız bir uyarı iletisinin yakalanıp yakalanmadığını söyler.

<?php
// sinir.php — kod içi ini_set() display_errors'u geri açar mı?
declare(strict_types=1);
require __DIR__ . '/yan.php';

file_put_contents('gorunur.php', <<<'PHP'
<?php
// gorunur.php — display_errors kod içinden açılır, uyarı yakalanır
$donen = ini_set('display_errors', '1');
ob_start();
echo $tanimsiz;
$yakalanan = ob_get_clean();
echo $donen !== false && ini_get('display_errors') === '1' && str_contains($yakalanan, 'Warning') ? "evet\n" : "hayır\n";
PHP);

yanRaporu('ini_set() ile açılan display_errors uyarıyı yanıta yazar', 'gorunur.php');
ini_set() ile açılan display_errors uyarıyı yanıta yazar → iki yanda da
    geliştirme evet · üretim evet

Yoklama iki profilde de evet verdi: üretim profilinde başlayan süreç, kod içinden display_errors açıldığında uyarıyı yanıta yazdı. Yani ayar dosyasındaki display_errors=0 bir kilit değildir; bir kütüphane ya da bir betik satırı onu geri açabilir. Karşıt örnek zend.assertions idi: o yönerge -1 iken çalışırken açılamaz. Sertleştirmenin gücü yönergeden yönergeye değişir, bu yüzden üretimde yalnız ayar dosyasına güvenmek yetmez. Kodda ini_set() çağrılarını aramak ve hata görünürlüğünü bu kayıtların dışında da denetlemek gerekir.

Dersin dürüst sınırı şudur: ölçülen şey yedi yönergenin değerleri ve bu değerlerin yukarıdaki yoklamalarda görülen davranışlarıdır. Bir üretim kurulumunda başka yönergeler de açılır ya da kapanır; liste burada hazır dosyaların farkıdır, bir kurulum kontrol listesi değil.

Dersin ölçümlerini birbirine bağlayalım. Yedi yönergelik fark tablosu, hazır iki dosyanın profile göre nerede ayrıştığını söyler. Davranış yoklamaları, o ayrımın üretimde somut olarak neyi kapattığını gösterir: çalışırken açılamayan assert() denetimi ve argümansız istisna izi. Erişim düzeyi ölçümü ise bu kapanışın ne kadar sağlam olduğunu sınar: bazıları kilitlidir, bazıları değildir. Sınırlayıcı ölçüm de aynı soruyu display_errors için sorar ve yanıtın olumsuz olduğunu gösterir. Birlikte okununca ortaya şu çıkar: üretim ayarı bir sertleştirme listesidir, ama her maddesinin aynı sağlamlıkta olduğunu varsaymak yanlıştır.

Özet

  • Geliştirme ve üretim ayar dosyaları yedi yönergede ayrışır: display_errors, display_startup_errors, error_reporting, mysqlnd.collect_memory_statistics, zend.assertions, zend.exception_ignore_args ve zend.exception_string_param_max_len.
  • Bu dersin ölçü sınıfı yalnız üretimdedir: üretimde zend.assertions çalışırken açılamaz ve istisna izinde argüman saklanmaz; geliştirmede ikisi de görülmez.
  • Ayrımın amacı sertleştirmedir: hata ayrıntısı, çağrı argümanları ve geliştirme denetimleri üretimde dışarı sızmaz.
  • display_errors kod içinden ini_set() ile geri açılabildi (iki yanda da); ayar dosyasındaki değer her yönerge için kilit değildir.
  • Erişim düzeyi tek başına yetmez: zend.assertions ALL görünür ama üretimde -1 iken ini_set() reddedilir, geliştirmede kabul edilir; opcache.enable_cli her iki profilde de SYSTEM düzeyindedir ve reddedilir (ölçü sınıfı: yalnız üretimde).
  • Kod içinden 0 yapılan zend.exception_ignore_args, üretimde istisna izine argümanı geri getirir (ölçü sınıfı: iki yanda da).

Sonraki Adım

Bu kursun son dersi dağıtım iş akışıdır: kodu üretime alırken ayar, kod önbelleği ve süreç yöneticisinin tek bir akışta nasıl bir araya geldiği. Önceki beş derste ölçülen her davranış orada bir adıma karşılık gelir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat