technik: Statische Analyse wieder aussagekräftig machen
phpstan lief mit 390 Meldungen außerhalb der Baseline, der größte Teil davon Falschmeldungen. Ursache: Larastan löste keinen einzigen Cast auf, weil alle Models ihre Casts über die casts()-METHODE deklarieren (Laravel-11-Stil). Laravel führt die erst zur Laufzeit mit $casts zusammen, Larastan instanziiert Models aber ohne Konstruktor — $pressRelease->status galt deshalb als string und jeder Enum-Vergleich als "immer falsch". Dafür gibt es den Schalter parseModelCastsMethod, dessen Larastan-Default false ist. Zweite Sammelursache waren fehlende Generics an den Relations-Methoden: Ohne sie landete phpstan auf Illuminate\Database\Eloquent\Model und meldete jede Modell-Eigenschaft und jeden eigenen Scope als unbekannt. Alle 102 Relationen tragen jetzt ihr @return <Related, $this>; das verbessert nebenbei die Vervollständigung in der IDE. Zusammen fällt die Zahl von 390 auf 169. Die neu erzeugte Baseline umfasst 166 Einträge, überwiegend Closure-Parameter, die als blankes Builder typisiert sind, sowie zwei nachgeprüfte Falschmeldungs-Gruppen: die Concerns unter app/Livewire werden ausschließlich aus Volt-Komponenten eingebunden, die phpstan nicht analysiert, und Cashier typisiert findBillable() gegen den TRAIT Billable, was phpstan nicht mit User vereinbaren kann — die Laufzeitprüfung dort ist richtig und bleibt. Was die Analyse an echten toten Zweigen zutage gefördert hat, ist mit entfernt: eine doppelte Null-Prüfung im Nachweis-PDF, Prüfungen gegen NOT-NULL-Spalten in der Rechnungsstellung und im Vorschau-Controller, eine immer wahre Bedingung im Logo-Pfad und in RetireAgencyCompanyType, ein überflüssiges ?? [] auf einer Symfony-Methode, die immer ein Array liefert, sowie der unerreichbare null-Arm in publicPortalLinks(). MigrateLegacyMedia deklariert seine Medientypen jetzt als Literal-Union, womit das match ohne default nachweislich vollständig ist statt nur faktisch. Damit das nicht wieder abdriftet — die Baseline stand seit Mai unverändert — läuft die Analyse ab jetzt als eigener Schritt in der Test-Pipeline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f6934f54e1
commit
e783adc54e
48 changed files with 780 additions and 228 deletions
|
|
@ -343,21 +343,33 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
->implode('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne<Profile, $this>
|
||||
*/
|
||||
public function profile(): HasOne
|
||||
{
|
||||
return $this->hasOne(Profile::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<MagicLink, $this>
|
||||
*/
|
||||
public function magicLinks(): HasMany
|
||||
{
|
||||
return $this->hasMany(MagicLink::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<Company, $this>
|
||||
*/
|
||||
public function ownedCompanies(): HasMany
|
||||
{
|
||||
return $this->hasMany(Company::class, 'owner_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany<Company, $this>
|
||||
*/
|
||||
public function companies(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Company::class)
|
||||
|
|
@ -365,22 +377,34 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany<Contact, $this>
|
||||
*/
|
||||
public function contacts(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Contact::class)
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<PressRelease, $this>
|
||||
*/
|
||||
public function pressReleases(): HasMany
|
||||
{
|
||||
return $this->hasMany(PressRelease::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<NewsletterSubscription, $this>
|
||||
*/
|
||||
public function newsletterSubscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(NewsletterSubscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<EmailNotificationOptOut, $this>
|
||||
*/
|
||||
public function emailNotificationOptOuts(): HasMany
|
||||
{
|
||||
return $this->hasMany(EmailNotificationOptOut::class);
|
||||
|
|
@ -408,36 +432,57 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
$this->emailNotificationOptOuts()->where('type', $type->value)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne<BillingAddress, $this>
|
||||
*/
|
||||
public function billingAddress(): HasOne
|
||||
{
|
||||
return $this->hasOne(BillingAddress::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<UserPaymentOption, $this>
|
||||
*/
|
||||
public function userPaymentOptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserPaymentOption::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<SinglePurchase, $this>
|
||||
*/
|
||||
public function singlePurchases(): HasMany
|
||||
{
|
||||
return $this->hasMany(SinglePurchase::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasOne<CreditWallet, $this>
|
||||
*/
|
||||
public function creditWallet(): HasOne
|
||||
{
|
||||
return $this->hasOne(CreditWallet::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<CreditTransaction, $this>
|
||||
*/
|
||||
public function creditTransactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(CreditTransaction::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<ReviewCheck, $this>
|
||||
*/
|
||||
public function reviewChecks(): HasMany
|
||||
{
|
||||
return $this->hasMany(ReviewCheck::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<CreditTopup, $this>
|
||||
*/
|
||||
public function creditTopups(): HasMany
|
||||
{
|
||||
return $this->hasMany(CreditTopup::class);
|
||||
|
|
@ -456,12 +501,17 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
* Lokale Rechnungen (STR- und MAN-Kreis). Überschreibt bewusst die
|
||||
* gleichnamige Cashier-Methode — Stripe-Rechnungen werden beim
|
||||
* Webhook-Sync (9E) in diese Tabelle gespiegelt.
|
||||
*
|
||||
* @return HasMany<Invoice, $this>
|
||||
*/
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<LegacyInvoice, $this>
|
||||
*/
|
||||
public function legacyInvoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(LegacyInvoice::class);
|
||||
|
|
@ -476,6 +526,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||
return $this->legacyInvoices()->latestFirst()->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<UserFilterPreset, $this>
|
||||
*/
|
||||
public function filterPresets(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserFilterPreset::class);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue