---
title: 'Bağımlılık Yöneticisi'
source: 'https://academia.sh/tr/kurslar/php-nesne-modeli/bagimlilik-yoneticisi'
course: 'Nesneye Dayalı PHP ve Standartlar'
language: tr
updated: '2026-10-21T05:03:35+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Kilit dosyası olmayan bir bağımlılığın alt sürümünün çıktıyı değiştirdiğini ölçer'
    - 'Kilit dosyasının sürümü sabitleyip değişikliği durdurduğunu gösterir'
    - 'Sürüm kısıtının ana sürüm değişikliğine karşı korurken alt sürüm davranışına karşı korumadığını ayırt eder'
---

# Bağımlılık Yöneticisi

Bildirim dosyası, sürüm kısıtı ve kilit dosyası; kilitsiz bir bağımlılığın alt sürümünün çıktıyı sessizce değiştirmesi; kilit dosyasının sürümü sabitlemesi; ana sürüm sınırının kısıt tarafından korunması ve anlamlı sürümlemenin bir söz olması.

Ortak Arayüz Standartları dersinde gerçekleştirim, kodu bizim yazdığımız bir sınıftı. Bu derste gerçekleştirim başka birinin yayımladığı bir pakettir ve değişiklik sizin deponuzda değil, paketin yayım sürümünde olur. Soru aynı: **bu değişiklik nereye ulaşıyor?**

Bir **paket yöneticisi** üç şeyi birlikte tutar: hangi paketlere bağlı olduğunuzu söyleyen **bildirim dosyası**, hangi sürüm aralığını kabul ettiğinizi söyleyen **sürüm kısıtı** ve çözülen kesin sürümleri kaydeden **kilit dosyası**. PHP'nin yaygın paket yöneticisi de bu üç parçayla çalışır. Bu derste yöneticinin kendisi koşturulmuyor; ağa çıkılmaz. Ölçüm, aynı üç parçayı yerel bir dizinde taklit eden küçük bir çözücüyle yapılır.

**ST4.** Ölçümdeki çözücü `^1.4` biçimli tek kısıtı anlar: `1.4`'ten büyük ya da eşit, `2.0.0`'dan küçük. `^` ile `~` karşılaştırması ayrı bir fonksiyonla modellenir. Gerçek bir yöneticinin sıfırlı ana sürümler (`0.x`) için daha dar çalışan kuralı, birden çok pakete bağlı çözümleme ve bağımlılığın bağımlılıkları modellenmez.

## Ölçüm Düzeneği

`yayilim.php` önceki derslerle aynıdır: aynı programı değişiklikten önce ve sonra kurup koşturur. Sınıfı sırayla sorar: çıkış 0 değilse **kıran**, çıktı farklıysa **örtük**, düzenlenen dosya bağlantı listesindeyse **bağlantıda**, hiçbiri değilse **yerinde**. Önceki sürüm 0 ile çıkmalıdır ve kullanan `main.php` düzenlenmez.

```php
<?php
// yayilim.php — bir degisikligin nereye ulastigini olcen yardimcilar

function yaz(string $dizin, array $dosyalar): void
{
    foreach ($dosyalar as $ad => $icerik) {
        if ($icerik === null) {
            continue; // null: dosya silindi
        }
        $yol = "$dizin/$ad";
        if (!is_dir(dirname($yol))) {
            mkdir(dirname($yol), 0777, true);
        }
        file_put_contents($yol, $icerik);
    }
}

function oku(string $dizin): array
{
    $dosyalar = [];
    $gezgin = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dizin, FilesystemIterator::SKIP_DOTS)
    );
    foreach ($gezgin as $dosya) {
        $ad = substr($dosya->getPathname(), strlen($dizin) + 1);
        $dosyalar[$ad] = file_get_contents($dosya->getPathname());
    }
    return $dosyalar;
}

function kostur(string $dizin): array
{
    $komut = 'cd ' . escapeshellarg($dizin) . ' && php -d display_errors=0 main.php 2>/dev/null';
    exec($komut, $satirlar, $kod);
    return [$kod, implode("\n", $satirlar)];
}

function olc(string $ad, array $taban, array $degisiklik, array $baglanti = []): string
{
    if (array_key_exists('main.php', $degisiklik)) {
        throw new LogicException("$ad: kullanan duzenlenmez");
    }
    yaz("olcum/$ad/once", $taban);
    yaz("olcum/$ad/sonra", array_merge($taban, $degisiklik));
    [$kodOnce, $ciktiOnce] = kostur("olcum/$ad/once");
    [$kodSonra, $ciktiSonra] = kostur("olcum/$ad/sonra");
    if ($kodOnce !== 0) {
        throw new LogicException("$ad: taban calismadi");
    }
    $sinif = match (true) {
        $kodSonra !== 0 => 'kiran',
        $ciktiOnce !== $ciktiSonra => 'ortuk',
        array_intersect(array_keys($degisiklik), $baglanti) !== [] => 'baglantida',
        default => 'yerinde',
    };
    printf(
        "%-18s %d dosya  cikis=%-3d cikti %-6s -> %s\n",
        $ad,
        count($degisiklik),
        $kodSonra,
        $ciktiOnce === $ciktiSonra ? 'ayni' : 'farkli',
        $sinif
    );
    return $sinif;
}
```

## Kilitsiz Bağımlılık: Örtük

Gözlem `kilitsiz-surum`: `bagimliliklar.json` `{"tarih": "^1.4"}` diyor, yani `Tarih` paketinin 1.4 ve üstünü, 2.0.0'ın altında kabul ediyor. `cozucu.php` paketler dizinindeki sürümlerden kısıta uyan en yükseğini seçip `kaynak.php` dosyasını yükler. `main.php` tarihi `Tarih::bicimle(2026, 10, 15)` ile biçimlendirir. Tabanda yalnız `paketler/tarih-1.4.0` var ve çözücü onu seçiyor. Değişiklik: paketin 1.5.0 sürümü yayımlanıyor. Sizin deponuzda hiçbir dosya düzenlenmiyor; yalnız `paketler/tarih-1.5.0/kaynak.php` ekleniyor. Kilit dosyası yok.

```php
<?php
// kilitsiz.php — kilit dosyasi olmayan bagimlilik yeni alt surumu alir
require_once 'yayilim.php';

// 11. Bagimliligin yeni bir alt surumu yayimlanir; kilit dosyasi yok
$siniflar[] = olc('kilitsiz-surum', [
    'bagimliliklar.json' => '{"tarih": "^1.4"}',
    'paketler/tarih-1.4.0/kaynak.php' => <<<'PHP'
        <?php
        class Tarih
        {
            public static function bicimle(int $yil, int $ay, int $gun): string
            {
                return sprintf('%04d-%02d-%02d', $yil, $ay, $gun);
            }
        }
        PHP,
    'cozucu.php' => <<<'PHP'
        <?php
        function surumSec(string $paket, string $kisit): string
        {
            $alt = ltrim($kisit, '^');
            $ust = ((int) explode('.', $alt)[0] + 1) . '.0.0';
            $adaylar = [];
            foreach (glob("paketler/$paket-*") as $dizin) {
                $surum = substr($dizin, strlen("paketler/$paket-"));
                if (version_compare($surum, $alt, '>=') && version_compare($surum, $ust, '<')) {
                    $adaylar[] = $surum;
                }
            }
            usort($adaylar, 'version_compare');
            return end($adaylar);
        }
        $istek = json_decode(file_get_contents('bagimliliklar.json'), true);
        $kilit = is_file('kilit.json') ? json_decode(file_get_contents('kilit.json'), true) : [];
        foreach ($istek as $paket => $kisit) {
            require "paketler/$paket-" . ($kilit[$paket] ?? surumSec($paket, $kisit)) . '/kaynak.php';
        }
        PHP,
    'main.php' => <<<'PHP'
        <?php
        require 'cozucu.php';
        echo Tarih::bicimle(2026, 10, 15), "\n";
        PHP,
], [
    'paketler/tarih-1.5.0/kaynak.php' => <<<'PHP'
        <?php
        class Tarih
        {
            public static function bicimle(int $yil, int $ay, int $gun): string
            {
                return sprintf('%02d.%02d.%04d', $gun, $ay, $yil);
            }
        }
        PHP,
]);

echo kostur('olcum/kilitsiz-surum/once')[1], ' -> ', kostur('olcum/kilitsiz-surum/sonra')[1], "\n";
$adlar = array_keys(oku('olcum/kilitsiz-surum/once'));
sort($adlar);
echo implode(', ', $adlar), "\n";
```

```
kilitsiz-surum     1 dosya  cikis=0   cikti farkli -> ortuk
2026-10-15 -> 15.10.2026
bagimliliklar.json, cozucu.php, main.php, paketler/tarih-1.4.0/kaynak.php
```

Sınıf **örtük**: bir dosya eklendi, çıkış 0 kaldı, ama çıktı `2026-10-15` iken `15.10.2026` oldu. Sizin deponuzda tek satır düzenlenmedi, program çökmedi ve tarih biçimi değişti. Çözücü 1.5.0'ı kısıta uygun en yüksek sürüm olarak seçti; kısıt bunu yasakladığı için değil izin verdiği için oldu. Son satır tabanın dosya listesidir: `kilit.json` listede yok.

## Kilit Dosyası: Yerinde

Karşıtı `kilitli`: aynı yayım, ama taban `kilit.json` dosyası taşıyor ve dosya `tarih` paketini `1.4.0` sürümüne sabitliyor. Çözücü kilit dosyasında kayıt varsa kısıta bakmadan o sürümü yükler. Değişiklik yine paketin 1.5.0 sürümünün yayımlanmasıdır.

```php
<?php
// kilitli.php — kilit dosyasi sabitlenen surumu korur
require_once 'yayilim.php';

$taban = oku('olcum/kilitsiz-surum/once');
$taban['kilit.json'] = '{"tarih": "1.4.0"}';
$sonra = oku('olcum/kilitsiz-surum/sonra');
olc('kilitli', $taban, [
    'paketler/tarih-1.5.0/kaynak.php' => $sonra['paketler/tarih-1.5.0/kaynak.php'],
]);

echo kostur('olcum/kilitli/once')[1], ' -> ', kostur('olcum/kilitli/sonra')[1], "\n";
```

```
kilitli            1 dosya  cikis=0   cikti ayni   -> yerinde
2026-10-15 -> 2026-10-15
```

Sınıf **yerinde**: bir dosya eklendi, çıkış 0 kaldı, çıktı `2026-10-15` kaldı. Kilit dosyası olmayan ölçümde aynı yayım çıktıyı değiştirmişti; burada kilit çözücüyü 1.4.0'da tuttu. Fark tek dosyadır: sabitleme kararı kilit dosyasında durur ve yeni sürümü ancak o dosyanın güncellenmesi alır.

## Kısıtın İki Yazılışı: Şapka ve Dalga

Kısıt iki ayrı işaretle yazılabilir ve ikisi aynı şeyi söylemez. `^` işareti sol baştaki sıfır olmayan sürüm hanesini sabitler: `^1.4.0`, 1.4.0 ve üstünü, 2.0.0'ın altında kabul eder. `~` işareti ise yazılan hane sayısına bakar. Üç haneli `~1.4.0`, 1.4.0 ve üstünü yalnız 1.5.0'ın altında kabul eder; iki haneli `~1.4` ise `^1.4` ile aynı aralığı verir. Aralığı hesaplayan yardımcı dosya aşağıda; sonraki iki ölçüm onu kullanır. Bu bir ölçüm değil kaynaktır, çıktı bloğu yoktur.

```php
<?php
// surum.php — ^ ve ~ kisitlarini aralik olarak okur
function aralik(string $kisit): array
{
    $isaret = $kisit[0];
    $parcalar = array_map('intval', explode('.', substr($kisit, 1)));
    $alt = implode('.', array_pad($parcalar, 3, 0));
    if ($isaret === '^' || count($parcalar) < 3) {
        $ust = ($parcalar[0] + 1) . '.0.0';
    } else {
        $ust = $parcalar[0] . '.' . ($parcalar[1] + 1) . '.0';
    }
    return [$alt, $ust];
}

function enYuksek(string $kisit, array $surumler): ?string
{
    [$alt, $ust] = aralik($kisit);
    $uygun = array_filter(
        $surumler,
        fn(string $s) => version_compare($s, $alt, '>=') && version_compare($s, $ust, '<')
    );
    usort($uygun, 'version_compare');
    return $uygun === [] ? null : end($uygun);
}
```

Aynı beş yayım üç kısıtla sınanıyor. Çıktıdaki ilk sütun kısıt, sonrası seçilen sürümdür.

```php
<?php
// ayrim.php — ayni yayimlar uc kisitla farkli surum secer
require_once 'surum.php';

$yayimlar = ['1.4.0', '1.4.7', '1.5.0', '1.9.2', '2.0.0'];
foreach (['^1.4.0', '~1.4.0', '~1.4'] as $kisit) {
    printf("%-7s -> %s\n", $kisit, enYuksek($kisit, $yayimlar));
}
```

```
^1.4.0  -> 1.9.2
~1.4.0  -> 1.4.7
~1.4    -> 1.9.2
```

Üç kısıt da 1.4.0 ile başlıyor, ama `~1.4.0` yama sürümlerinde kalıyor ve 1.4.7'de duruyor; `^1.4.0` alt sürümleri de alıyor ve 1.9.2'ye çıkıyor. Hane sayısını bir eksiltmek, yani `~1.4.0` yerine `~1.4` yazmak, kısıtı yeniden `^` genişliğine getiriyor. Hiçbiri 2.0.0'ı almıyor. Ölçüm, `kilitsiz-surum` gözlemindeki riskin bir kısıt seçimiyle daraltılabileceğini gösterir; ama risk ortadan kalkmaz, yalnız yama sürümlerine iner.

## Bir Ekibin Dört Adımı

Kilit dosyasının neden deponuza girdiğini bir akış anlatır. Bildirim `^1.4.0`, paket 1.4.0'da ve kilit dosyası henüz yok. Ekipte iki kişi var: siz ve bir arkadaşınız. Akış dosya sistemi kullanmaz; bildirimi, kilit kaydını ve paketin yayım listesini üç değişkende tutar.

```php
<?php
// akis.php — ekipte kilit dosyasi depoda ise herkes ayni surumu alir
require_once 'surum.php';

function kur(string $kisit, array $yayimlar, ?string $kilit): array
{
    if ($kilit !== null) {
        return [$kilit, $kilit, 'kilitten'];
    }
    $surum = enYuksek($kisit, $yayimlar);
    return [$surum, $surum, 'cozuldu'];
}

$kisit = '^1.4.0';
$yayimlar = ['1.4.0'];

[$surum, $kilit, $kaynak] = kur($kisit, $yayimlar, null);
printf("1 ilk kurulum        %s  %s\n", $surum, $kaynak);

$yayimlar[] = '1.4.7';
[$surum, , $kaynak] = kur($kisit, $yayimlar, $kilit);
printf("2 arkadas, kilitli   %s  %s\n", $surum, $kaynak);
[$surum, , $kaynak] = kur($kisit, $yayimlar, null);
printf("2 arkadas, kilitsiz  %s  %s\n", $surum, $kaynak);

[$surum, $kilit, $kaynak] = kur($kisit, $yayimlar, null);
printf("3 guncelleme         %s  %s\n", $surum, $kaynak);

[$surum, , $kaynak] = kur($kisit, $yayimlar, $kilit);
printf("4 arkadas tekrar     %s  %s\n", $surum, $kaynak);
```

```
1 ilk kurulum        1.4.0  cozuldu
2 arkadas, kilitli   1.4.0  kilitten
2 arkadas, kilitsiz  1.4.7  cozuldu
3 guncelleme         1.4.7  cozuldu
4 arkadas tekrar     1.4.7  kilitten
```

İlk kurulumda kilit yoktu, çözücü 1.4.0'ı seçti ve kilit kaydı `1.4.0` oldu. Paket 1.4.7'yi yayımlayınca kilidi depodan alan arkadaşınız yine 1.4.0'ı kurdu; kilit olmasaydı 1.4.7'yi kurardı ve iki makinede iki sürüm çalışırdı. Güncelleme, kilidi bilerek yok sayan adımdır: çözücü 1.4.7'yi seçti ve kilit kaydını yeniden yazdı. Arkadaşınız bu yeni kaydı depodan alınca 1.4.7'de buluştu.

Kilit dosyasının depoya girmesinin sebebi budur: sabitleme kararı kodla birlikte dağıtılmazsa her makine kendi çözümünü yapar. Güncellemek de ayrı bir düzenlemedir; kilit dosyasındaki fark, hangi sürümlerin değiştiğini bir commit içinde gösterir.

## Anlamlı Sürümlemenin Üç Hanesi

`^` ve `~` işaretlerinin ayırt ettiği haneler birer sözdür. **Ana** hane, geriye uyumsuz bir değişiklikte artar. **Alt** hane, geriye uyumlu bir özellik eklendiğinde artar. **Yama** hanesi, geriye uyumlu bir hata düzeltmesinde artar. `^` bu sözün ilk ikisine güvenir: ana hane sabit kaldıkça kodunuzun çalışmaya devam edeceğini varsayar. `~1.4.0` yalnız üçüncüsüne güvenir.

`kilitsiz-surum` ölçümünde 1.4.0'dan 1.5.0'a geçiş alt hane artışıydı ve söz, çıktının değişmemesini gerektirirdi. Tarih biçimi değiştiğine göre ya paket sözü tutmadı ya da sizin kodunuz, sözün kapsamadığı bir şeye, bu durumda çıktının metnine dayanıyordu. Bu ayrım pratikte önemlidir: söz bozulduysa sorun paketin sahibine bildirilir ve kısıt daraltılır; sizin koddan kaynaklandıysa düzeltme sizin deponuzda yapılır. İki durumda da önce hangisi olduğunu bilmek gerekir, bunun için de değişikliğin ne zaman geldiğini bilmek gerekir. Çözücü hangisi olduğunu bilemez. Bilmesi de gerekmez: kilit dosyası bu belirsizliği ortadan kaldırmaz, ama değişikliğin ne zaman ve sizin onayınızla geleceğini kesinleştirir.

Bu yüzden iki karar ayrı verilir. Kısıt, kodunuzun hangi aralıkta çalışmasını beklediğinizi söyler ve nadiren değişir. Kilit dosyası, o aralıkta bugün hangi sürümü kullandığınızı söyler ve her güncellemede değişir. Birincisini yanlış seçerseniz kilit dosyası sizi korur; kilit dosyasını depoya koymazsanız hiçbir kısıt sizi aynı sürümde tutmaz. Sınırlayıcı ölçüm, kilit yokken kısıtın neye karşı yine de koruduğunu sınar.

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

Kısıt, ana sürüm değişikliğine karşı korur. Bu ölçüm onu doğruluyor: kilit dosyası yok, taban `kilitsiz-surum` tabanı, ve yayımlanan paket 2.0.0. `^1.4` kısıtı 2.0.0'ı dışarıda bırakıyor.

```php
<?php
// ana-surum.php — kisit ana surum degisikligine karsi korur
require_once 'yayilim.php';

$taban = oku('olcum/kilitsiz-surum/once');
olc('ana-surum', $taban, [
    'paketler/tarih-2.0.0/kaynak.php' => <<<'PHP'
        <?php
        class Tarih
        {
            public static function bicimle(int $yil, int $ay, int $gun): string
            {
                return "$gun/$ay/$yil";
            }
        }
        PHP,
]);

echo kostur('olcum/ana-surum/once')[1], ' -> ', kostur('olcum/ana-surum/sonra')[1], "\n";
```

```
ana-surum          1 dosya  cikis=0   cikti ayni   -> yerinde
2026-10-15 -> 2026-10-15
```

Sınıf **yerinde**: 2.0.0 yayımlandı, çözücü yine 1.4.0'ı seçti ve çıktı `2026-10-15` kaldı. `^1.4` kısıtı ana sürüm değişikliğine karşı korur; ama alt sürümdeki davranış değişikliğine karşı korumaz, `kilitsiz-surum` ölçümü bunu gösterdi. Alt sürümün çıktıyı değiştirmeyeceği **anlamlı sürümleme** denen bir sözdür: paketin sahibi verir, çözücü denetlemez. Söz tutulursa ana sürüm sınırı yeter; tutulmazsa yalnız kilit dosyası korur.

## Özet

- Bu dersin ölçü sınıfı **örtük**. `kilitsiz-surum` gözleminde paketin 1.5.0 sürümü yayımlandı, deponuzda hiçbir dosya düzenlenmedi, çıkış 0 kaldı, çıktı `2026-10-15` iken `15.10.2026` oldu.
- Karşıtı `kilitli` **yerinde**: `kilit.json` sürümü `1.4.0`'da tuttu, çıktı değişmedi.
- Sınırlayıcı ölçüm `ana-surum` **yerinde**: `^1.4` kısıtı 2.0.0'ı dışarıda bıraktı. Kısıt ana sürüme karşı korur, alt sürümün davranışına karşı korumaz.
- `ayrim` ve `akis` ölçümleri iki ayrımı gösterdi: `~1.4.0` yama sürümünde, `^1.4.0` alt sürümde durur; kilit dosyası depodaysa ekibin herkesi aynı sürümü kurar, güncelleme kilidi bilerek yeniden yazar.
- Bildirim dosyası ne istediğinizi, kilit dosyası ne aldığınızı kaydeder; sürüm kısıtı bir aralık, anlamlı sürümleme bir sözdür.

## Sonraki Adım

Paket yöneticisi, bağımlılığın hangi sürümünün yükleneceğini tek yerde topladı. Sınıfın bağımlılığını ise kim kuruyor? Bağımlılık değişince hangi dosya düzenleniyor? Bağımlılık Enjeksiyonu dersi bunu ölçüyor.
