---
title: "Konfiguračný kontrakt — Testing for Laravel | Roundly"
description: "Overte obojsmerne, že konfigurácia obsahuje presne kľúče, ktoré kód číta — z PHP tokenov, s kontrolovanými výnimkami a režimom pre aplikácie."
url: https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel/konfiguracny-kontrakt
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Testing for Laravel](https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel.md)

# Konfiguračný kontrakt

Obojsmerne overí, že konfiguračný súbor obsahuje presne tie kľúče, ktoré kód číta. Čítania sa zisťujú z tokenov PHP zdrojového kódu — nikdy nie regexom — takže kľúč spomenutý len v docblocku sa nikdy nezapočíta. Prefix kľúčov je predvolene názov konfiguračného súboru:

```php
expect($configPath)->toSatisfyConfigContract(string|array $srcDirs, array $options = []);
```

```php
expect(config_path('passkeys.php'))->toSatisfyConfigContract(__DIR__.'/../../src', [
    'excludeFromReverse' => ['PasskeysServiceProvider.php'], // renders keys; a render is not a read
    'sectionVariables'   => ['PasskeyConfig.php' => ['$rp' => 'passkeys.rp']], // DTO array-offset reads
]);
```

## Dva smery

- Dopredu — každý kľúč, ktorý kód číta, musí byť v súbore. Zachytí funkciu, ktorá čítala shops.payments.\*, kým súbor obsahoval payment.\*: prepínač v env nerobil nič a 330 testov zostalo zelených, lebo sada nastavovala ten istý nesprávny kľúč.
- Spätne — každý dodaný list musí niečo čítať. Zachytí zdokumentované kľúče, ktoré nič nečíta, napríklad limit max\_file\_size, ktorý sa nikdy neuplatnil. Čítanie celej nadradenej sekcie nedokazuje použitie konkrétneho listu, takže mŕtvy podkľúč zostáva odhaliteľný.

Oba smery sa počítajú z jednej množiny čítaní a hlásia sa spolu, takže oprava jedného nikdy neodkryje novú dávku zistení z druhého.

## Čo sa počíta ako čítanie

Čítania sa počítajú cez rodinu metód get, getMany, has, string, integer, boolean, float, array a collection všade, kde sa metóda volá, s kľúčom zadaným pozične aj menom — config(key: 'pkg.x'), Config::get(default: 1, key: 'pkg.x'), getMany(keys: \[...\]); návestie key: sa nikdy nečíta ako súčasť kľúča:

```php
config('pkg.key');                          // the helper (also \config())
Config::get('pkg.key');                     // the facade — bare, \Config, fully qualified or aliased
$this->config->get('pkg.key');              // an injected Illuminate\Contracts\Config\Repository
config()->string('pkg.key');                // the repository reached through an expression
app('config')->get('pkg.key');
$app['config']->get('pkg.key');
$this->config->get(['pkg.a' => $default]);  // each literal key of an array handed to a read method
config()->getMany(['pkg.a', 'pkg.b']);      // getMany() — listed keys count too
Config::get(default: 1, key: 'pkg.key');    // the key passed by name

Config::integer('pkg.ttl', 3600);                            // package-toolkit's strict readers, static…
Config::requireSecret('pkg.api_key');                        // …its secret readers (toolkit 1.3)…
Config::using(PkgException::class)->enum('pkg.mode', Mode::class); // …and chained on a ConfigValidator
KeyType::fromConfig('pkg.key_type');
ModelResolver::for('pkg.model');

config(['pkg.x' => true]);                  // a runtime write — skipped, never counted as a read
```

- Helper — config('pkg.key') alebo \\config('pkg.key').
- Fasáda — Config::get('pkg.key'), \\Config::get(…), \\Illuminate\\Support\\Facades\\Config::get(…) alebo fasáda pod aliasom importu.
- Injektovaný Illuminate\\Contracts\\Config\\Repository — $this->config->get('pkg.key').
- Repozitár získaný cez výraz — config()->string(…), app('config')->get(…), app(Repository::class)->get(…), resolve('config')->…, ->make('config')->… a $app\['config'\]->….
- Každý literálny kľúč poľa odovzdaného metóde na čítanie — getMany(\[...\]) alebo get(\[...\]), čo je getMany —, či je uvedený samostatne, alebo ako 'pkg.key' => $default.
- Čítacie metódy balíka package-toolkit-for-laravel, statické aj reťazené: Config::boolean|integer|float|string|list|enum|oneOf|requireString('pkg.key', …) a metódy na tajomstvá Config::secret|requireSecret|secretList('pkg.key', …), v akejkoľvek veľkosti písmen; rovnakých jedenásť na ConfigValidator — Config::using(X::class)->integer('pkg.key', …), Config::for($values)->enum(…), ConfigValidator::forRepository()->…, metóda deklarovaná tak, že ho vracia (self::validator()->…), alebo premenná, ktorá je ním deklarovaná či priradená.
- KeyType::fromConfig('pkg.key\_type'), ModelResolver::for('pkg.model') / ::newModel(…) a $this->modelClass(…) / ->newModel(…) v triede, ktorá používa ResolvesModels.
- V PackageServiceProvider: $this->bindFromConfig(Contract::class, 'pkg.key', …), $this->observesModel('pkg.model', …) a prepínač v $package->hasRoutes('pkg.php', enabledVia: 'pkg.routes.enabled') / ->hasFacadeAlias(X::class, 'pkg.alias') — nikdy nie názov súboru s routami vedľa neho.
- Čítania cez indexy poľa pomocou sectionVariables, sledované do ľubovoľnej hĺbky: pri \['$rl' => 'pkg.rate\_limiters'\] je $rl\['public'\]\['enabled'\] čítaním pkg.rate\_limiters.public.enabled.

Väzba repozitára sa určuje z deklarovaného typu alebo z väzby 'config', ktorú výraz uvádza, takže $cache->get('pkg.x') ani app('cache')->get('pkg.x') sa správne za čítanie konfigurácie nepovažujú. Čítacie metódy toolkitu sa určujú rovnako — cez importy súboru a deklarované typy, nikdy len podľa názvu metódy —, takže ani Rules::enum('pkg.x') či netypované $cache->requireString('pkg.x') nie je čítaním. Najviac na tom záleží pri float, string a list (toolkit 1.2) a pri secret, requireSecret a secretList (toolkit 1.3): počítajú sa len na triedach a objektoch toolkitu, takže $request->string('pkg.x') na requeste, $vault->secret('pkg.x') na klientovi trezoru, rovnomenná statická metóda inej triedy ani destrukturalizácia list(…) = $values čítaním nie sú. Vlastná čítacia metóda balíka, ktorá dostane kľúč ako argument (Support\\PkgConfig::string('pkg.key', …)), sa nesleduje: takéto kľúče uveďte v extraReadPrefixes, každý presne v samostatnej položke. Zápisy sa nikdy nepočítajú: pole odovzdané helperu — config(\['pkg.x' => true\]) — je zápis za behu, ktorý sa preskočí a nehlási sa ako nerozlíšiteľný, a set() ani push() nie sú dôkazom, že kľúč niečo používa.

## Čítanie dopredu musí trafiť dodanú cestu

Pomenovať nadradenú sekciu je v poriadku, rovnako ako čítať do listu, ktorý môže obsahovať viac, než dodáva — zoznam, prázdnu mapu či zástupné null. Čítať pod skalárom však nie: hodnota je vždy null a zistenie v smere dopredu to uvedie.

```php
// config/pkg.php ships: 'cache' => 'redis'
config('pkg.rp');           // fine — naming a parent
config('pkg.cache.store');  // always null: a read below a scalar is a forward finding
```

## Voľby

| Voľba | Typ | Účinok |
| --- | --- | --- |
| `excludeFromReverse` | list<string> | Súbory, ktoré konfiguráciu len vykresľujú (napr. obsah about) — vykreslenie nie je čítanie. |
| `sectionVariables` | array<string, array<string, string>> | Pre daný súbor namapuje lokálnu premennú alebo vlastnosť na sekciu konfigurácie; jej indexy sa počítajú ako čítania listov. |
| `extraReadPrefixes` | list<string> | Bodkové prefixy, ktorých literály sa počítajú ako čítania kdekoľvek — pre kľúče, ktoré vlastná čítacia metóda balíka dostane ako argument, napríklad Support\\PkgConfig::string('pkg.key'). Každý kľúč presne v samostatnej položke. |
| `allowUnread` | list<string> | Dodané, no nečítané kľúče, ktoré sa tolerujú. Kontrolované proti zastaraniu: položka, ktorá nič neumlčí, zlyhá. |
| `allowUnshipped` | list<string> | Čítané, no nedodané kľúče, ktoré sa tolerujú. Kontrolované rovnako. |
| `reverse` | bool (true) | false prepne na kontrolu len dopredu — režim pre aplikácie. |

## Rozsah prehľadávania

Prehľadá sa každá položka $srcDirs a k nej susedné adresáre database/ a routes/, ak existujú. Spätné zistenie teda znamená „žiadny čitateľ v prehľadaných adresároch“ — nikdy nie „žiadny čitateľ nikde“. Chybová hláška vypíše prehľadané adresáre. Ak uvádza kľúč, ktorého čitateľa vidíte, chyba je v rozsahu, nie v kľúči — pridajte adresár čitateľa:

```php
expect(config_path('media.php'))->toSatisfyConfigContract([
    __DIR__.'/../../src',
    __DIR__.'/../../resources/views',   // a directory the default scope misses
]);
```

Súbor .blade.php sa číta cez časti, ktoré Blade vykonáva ako PHP — výpisy {{ … }} a {!! … !!}, argumenty @directive( … ), bloky @php … @endphp a <?php … ?> a väzby :attribute="…" na komponentoch <x-…>. Komentáre {{-- … --}}, escapované výpisy @{{ … }}, escapované direktívy @@directive a bloky @verbatim čítaním nie sú a značkovanie sa nikdy netokenizuje — {{ config('media.max\_file\_size') }} vo view sa teda započíta, len čo odovzdáte jeho adresár.

allowUnread použite len pre kľúč, ktorý sa naozaj nečíta alebo ho scraper nevie namapovať — nikdy na umlčanie kľúča, o ktorom viete, že sa číta v neprehľadanom adresári.

## Sekcie podľa drivera

Sekcia s kľúčom podľa názvu drivera za behu — tvar, aký má database.connections.<name> v samotnom Laravel — sa číta interpoláciou a zistí sa ako zástupný vzor. List sa však stále dokazuje: dodaný providers.<driver>.timeout, ktorý žiadne čítanie neuvádza, zlyhá. Čítanie, ktoré sa zastaví na mieste interpolácie, je čítaním celej sekcie; na dôkaz listov namapujte lokálnu premennú:

```php
config("git.providers.{$key}.url")   // scraped as: git.providers.*.url

$http = config("git.providers.{$this->key()}", []);   // wholesale: proves no leaf
$http['timeout'];                                     // proves git.providers.*.timeout, once mapped

'sectionVariables' => ['BaseProvider.php' => ['$http' => 'git.providers.*']],
```

Zástupný základ, ktorý nezodpovedá ničomu dodanému (preklep ako git.provider.\*), zlyhá. Známa medzera, ktorú priznávame: vzor dokazuje list, nikdy nie driver — dodať konfiguráciu pre driver, ktorý si hostiteľ nevyberie, je správne, nie mŕtve.

## Nerozlíšiteľné kľúče

Kľúč, z ktorého nemožno odvodiť overiteľný vzor, zlyhá — a zámerne preň neexistuje výnimka. Namiesto toho použite literál, čítanie podľa drivera s uvedeným listom alebo index cez sectionVariables:

```php
config("pkg.drivers.{$name}x");   // a hole that does not fill a whole segment
config($this->keyFor('x'));       // a key not built from literals and holes
config("pkg.{$x}");               // a hole directly under the root
```

## extraReadPrefixes

extraReadPrefixes započíta zodpovedajúci literál kdekoľvek — aj tam, kde vôbec nejde o konfiguračný kľúč, napríklad pri názve súboru s routami. Zistenie v smere dopredu preto uvedie literál, súbor, z ktorého pochádza, aj to, že sa započítal kvôli prefixu. Kľúče pomenúvajte presne namiesto plošných prefixov:

```
FORWARD — the code reads 'purchases.' keys that the config file does not ship:
  - purchases.php  [read in PurchasesServiceProvider.php (counted because it matches extraReadPrefixes)]
```

## Režim pre aplikácie

V aplikácii zadajte 'reverse' => false. Konfigurácia aplikácie oprávnene obsahuje kľúče, ktoré čítajú balíky tretích strán mimo vašich zdrojových adresárov, takže platí len smer dopredu.

## Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

[Ďalšie spôsoby podpory vrátane kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/testing-for-laravel.md)
