NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Testing for Laravel

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:

expect($configPath)->toSatisfyConfigContract(string|array $srcDirs, array $options = []);
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, has, string, integer, boolean, float, array a collection všade, kde sa metóda volá:

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::integer('pkg.ttl', 3600);                            // package-toolkit's strict readers, static…
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, napríklad ->get(['pkg.a' => $default]).
  • Čítacie metódy balíka package-toolkit-for-laravel, statické aj reťazené: Config::boolean|integer|enum|oneOf|requireString('pkg.key', …); rovnakých päť 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. 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.

// 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ľbaTypÚčinok
excludeFromReverselist<string>Súbory, ktoré konfiguráciu len vykresľujú (napr. obsah about) — vykreslenie nie je čítanie.
sectionVariablesarray<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.
extraReadPrefixeslist<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.
allowUnreadlist<string>Dodané, no nečítané kľúče, ktoré sa tolerujú. Kontrolované proti zastaraniu: položka, ktorá nič neumlčí, zlyhá.
allowUnshippedlist<string>Čítané, no nedodané kľúče, ktoré sa tolerujú. Kontrolované rovnako.
reversebool (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:

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ú:

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:

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

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

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.