Raise PHPStan level tp 6

This commit is contained in:
myrmidex 2025-07-07 00:51:32 +02:00
parent 52ddf8c055
commit 07745b170e
33 changed files with 198 additions and 21 deletions

View file

@ -11,6 +11,7 @@ public function canParse(string $url): bool;
/**
* Extract article data from HTML
* @return array<string, mixed>
*/
public function extractData(string $html): array;

View file

@ -11,6 +11,7 @@ public function canParse(string $url): bool;
/**
* Extract article URLs from homepage HTML
* @return array<int, string>
*/
public function extractArticleUrls(string $html): array;

View file

@ -15,6 +15,7 @@ public function __construct(
public Throwable $exception,
public LogLevelEnum $level,
public string $message,
/** @var array<string, mixed> */
public array $context = []
) {
}

View file

@ -149,6 +149,9 @@ public function toggle(Request $request, Feed $feed, PlatformChannel $channel):
->with('success', "Routing {$status} successfully!");
}
/**
* @return array<string, mixed>|null
*/
private function parseJsonFilters(?string $json): ?array
{
if (empty($json)) {

View file

@ -11,6 +11,9 @@ public function authorize(): bool
return true;
}
/**
* @return array<string, string>
*/
public function rules(): array
{
return [

View file

@ -11,6 +11,9 @@ public function authorize(): bool
return true;
}
/**
* @return array<string, string>
*/
public function rules(): array
{
return [

View file

@ -10,6 +10,9 @@ enum LogLevelEnum: string
case ERROR = 'error';
case CRITICAL = 'critical';
/**
* @return array<int, string>
*/
public static function toArray(): array
{
return [

View file

@ -11,9 +11,9 @@
use Illuminate\Support\Carbon;
/**
* @method static firstOrCreate(string[] $array)
* @method static firstOrCreate(array<string, mixed> $array)
* @method static where(string $string, string $url)
* @method static create(string[] $array)
* @method static create(array<string, mixed> $array)
* @property integer $id
* @property int $feed_id
* @property Feed $feed
@ -40,6 +40,9 @@ class Article extends Model
'validated_at',
];
/**
* @return array<string, string>
*/
public function casts(): array
{
return [
@ -65,11 +68,17 @@ public function isValid(): bool
return $this->is_valid;
}
/**
* @return HasOne<ArticlePublication, $this>
*/
public function articlePublication(): HasOne
{
return $this->hasOne(ArticlePublication::class);
}
/**
* @return BelongsTo<Feed, $this>
*/
public function feed(): BelongsTo
{
return $this->belongsTo(Feed::class);

View file

@ -10,7 +10,7 @@
* @property integer $platform_channel_id
* @property integer $post_id
*
* @method static create(array $array)
* @method static create(array<string, mixed> $array)
*/
class ArticlePublication extends Model
{
@ -29,6 +29,9 @@ class ArticlePublication extends Model
'publication_data' => 'array',
];
/**
* @return BelongsTo<Article, $this>
*/
public function article(): BelongsTo
{
return $this->belongsTo(Article::class);

View file

@ -18,12 +18,12 @@
* @property int $language_id
* @property Language|null $language
* @property string $description
* @property array $settings
* @property array<string, mixed> $settings
* @property bool $is_active
* @property Carbon|null $last_fetched_at
* @property Carbon $created_at
* @property Carbon $updated_at
* @method static create(array $validated)
* @method static create(array<string, mixed> $validated)
* @method static orderBy(string $string, string $string1)
* @method static where(string $string, true $true)
* @method static findOrFail(mixed $feed_id)
@ -82,6 +82,9 @@ public function getStatusAttribute(): string
}
}
/**
* @return BelongsToMany<PlatformChannel, $this, FeedPlatformChannel>
*/
public function channels(): BelongsToMany
{
return $this->belongsToMany(PlatformChannel::class, 'feed_platform_channels')
@ -90,6 +93,9 @@ public function channels(): BelongsToMany
->withTimestamps();
}
/**
* @return BelongsToMany<PlatformChannel, $this, FeedPlatformChannel>
*/
public function activeChannels(): BelongsToMany
{
return $this->channels()
@ -97,11 +103,17 @@ public function activeChannels(): BelongsToMany
->orderByPivot('priority', 'desc');
}
/**
* @return HasMany<Article, $this>
*/
public function articles(): HasMany
{
return $this->hasMany(Article::class);
}
/**
* @return BelongsTo<Language, $this>
*/
public function language(): BelongsTo
{
return $this->belongsTo(Language::class);

View file

@ -12,10 +12,10 @@
* @property int $platform_channel_id
* @property bool $is_active
* @property int $priority
* @property array $filters
* @property array<string, mixed> $filters
* @property Carbon $created_at
* @property Carbon $updated_at
* @method static create(array $array)
* @method static create(array<string, mixed> $array)
*/
class FeedPlatformChannel extends Pivot
{
@ -36,16 +36,25 @@ class FeedPlatformChannel extends Pivot
'filters' => 'array'
];
/**
* @return BelongsTo<Feed, $this>
*/
public function feed(): BelongsTo
{
return $this->belongsTo(Feed::class);
}
/**
* @return BelongsTo<PlatformChannel, $this>
*/
public function platformChannel(): BelongsTo
{
return $this->belongsTo(PlatformChannel::class);
}
/**
* @return HasMany<Keyword, $this>
*/
public function keywords(): HasMany
{
return $this->hasMany(Keyword::class, 'feed_id', 'feed_id')

View file

@ -30,11 +30,17 @@ class Keyword extends Model
'is_active' => 'boolean'
];
/**
* @return BelongsTo<Feed, $this>
*/
public function feed(): BelongsTo
{
return $this->belongsTo(Feed::class);
}
/**
* @return BelongsTo<PlatformChannel, $this>
*/
public function platformChannel(): BelongsTo
{
return $this->belongsTo(PlatformChannel::class);

View file

@ -24,6 +24,9 @@ class Language extends Model
'is_active' => 'boolean'
];
/**
* @return BelongsToMany<PlatformInstance, $this>
*/
public function platformInstances(): BelongsToMany
{
return $this->belongsToMany(PlatformInstance::class)
@ -31,11 +34,17 @@ public function platformInstances(): BelongsToMany
->withTimestamps();
}
/**
* @return HasMany<PlatformChannel, $this>
*/
public function platformChannels(): HasMany
{
return $this->hasMany(PlatformChannel::class);
}
/**
* @return HasMany<Feed, $this>
*/
public function feeds(): HasMany
{
return $this->hasMany(Feed::class);

View file

@ -7,10 +7,10 @@
use Illuminate\Support\Carbon;
/**
* @method static create(array $array)
* @method static create(array<string, mixed> $array)
* @property LogLevelEnum $level
* @property string $message
* @property array $context
* @property array<string, mixed> $context
* @property Carbon $created_at
* @property Carbon $updated_at
*/

View file

@ -24,10 +24,10 @@
* @property string $status
* @property Carbon $created_at
* @property Carbon $updated_at
* @property Collection $activeChannels
* @property Collection<int, PlatformChannel> $activeChannels
* @method static where(string $string, PlatformEnum $platform)
* @method static orderBy(string $string)
* @method static create(array $validated)
* @method static create(array<string, mixed> $validated)
*/
class PlatformAccount extends Model
{
@ -54,6 +54,9 @@ class PlatformAccount extends Model
];
// Encrypt password when storing
/**
* @return Attribute<string|null, string|null>
*/
protected function password(): Attribute
{
return Attribute::make(
@ -63,6 +66,9 @@ protected function password(): Attribute
}
// Encrypt API token when storing
/**
* @return Attribute<string|null, string|null>
*/
protected function apiToken(): Attribute
{
return Attribute::make(
@ -72,6 +78,9 @@ protected function apiToken(): Attribute
}
// Get the active accounts for a platform (returns collection)
/**
* @return Collection<int, PlatformAccount>
*/
public static function getActive(PlatformEnum $platform): Collection
{
return static::where('platform', $platform)
@ -91,6 +100,9 @@ public function setAsActive(): void
$this->update(['is_active' => true]);
}
/**
* @return BelongsToMany<PlatformChannel, $this>
*/
public function channels(): BelongsToMany
{
return $this->belongsToMany(PlatformChannel::class, 'platform_account_channels')
@ -98,6 +110,9 @@ public function channels(): BelongsToMany
->withTimestamps();
}
/**
* @return BelongsToMany<PlatformChannel, $this>
*/
public function activeChannels(): BelongsToMany
{
return $this->channels()

View file

@ -2,7 +2,6 @@
namespace App\Models;
use App\Enums\PlatformEnum;
use Database\Factories\PlatformChannelFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -10,7 +9,7 @@
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @method static create(array $validated)
* @method static create(array<string, mixed> $validated)
* @method static findMany(mixed $channel_ids)
* @property integer $id
* @property integer $platform_instance_id
@ -42,11 +41,17 @@ class PlatformChannel extends Model
'is_active' => 'boolean'
];
/**
* @return BelongsTo<PlatformInstance, $this>
*/
public function platformInstance(): BelongsTo
{
return $this->belongsTo(PlatformInstance::class);
}
/**
* @return BelongsToMany<PlatformAccount, $this>
*/
public function platformAccounts(): BelongsToMany
{
return $this->belongsToMany(PlatformAccount::class, 'platform_account_channels')
@ -54,6 +59,9 @@ public function platformAccounts(): BelongsToMany
->withTimestamps();
}
/**
* @return BelongsToMany<PlatformAccount, $this>
*/
public function activePlatformAccounts(): BelongsToMany
{
return $this->platformAccounts()->where('is_active', true);
@ -65,6 +73,9 @@ public function getFullNameAttribute(): string
return $this->platformInstance->url . '/c/' . $this->name;
}
/**
* @return BelongsToMany<Feed, $this, FeedPlatformChannel>
*/
public function feeds(): BelongsToMany
{
return $this->belongsToMany(Feed::class, 'feed_platform_channels')
@ -73,6 +84,9 @@ public function feeds(): BelongsToMany
->withTimestamps();
}
/**
* @return BelongsToMany<Feed, $this, FeedPlatformChannel>
*/
public function activeFeeds(): BelongsToMany
{
return $this->feeds()
@ -80,6 +94,9 @@ public function activeFeeds(): BelongsToMany
->orderByPivot('priority', 'desc');
}
/**
* @return BelongsTo<Language, $this>
*/
public function language(): BelongsTo
{
return $this->belongsTo(Language::class);

View file

@ -7,7 +7,7 @@
/**
* @method static where(string $string, PlatformEnum $platform)
* @method static updateOrCreate(array $array, array $array1)
* @method static updateOrCreate(array<string, mixed> $array, array<string, mixed> $array1)
*/
class PlatformChannelPost extends Model
{
@ -21,6 +21,9 @@ class PlatformChannelPost extends Model
'posted_at',
];
/**
* @return array<string, string>
*/
protected function casts(): array
{
return [

View file

@ -10,7 +10,7 @@
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @method static updateOrCreate(array $array, $instanceData)
* @method static updateOrCreate(array<string, mixed> $array, $instanceData)
* @method static where(string $string, mixed $operator)
* @property PlatformEnum $platform
* @property string $url
@ -36,11 +36,17 @@ class PlatformInstance extends Model
'is_active' => 'boolean'
];
/**
* @return HasMany<PlatformChannel, $this>
*/
public function channels(): HasMany
{
return $this->hasMany(PlatformChannel::class);
}
/**
* @return BelongsToMany<Language, $this>
*/
public function languages(): BelongsToMany
{
return $this->belongsToMany(Language::class)

View file

@ -16,6 +16,9 @@ public function __construct(string $instance, ?string $token = null)
$this->token = $token;
}
/**
* @param array<string, mixed> $params
*/
public function get(string $endpoint, array $params = []): Response
{
$url = "https://{$this->instance}/api/v3/{$endpoint}";
@ -29,6 +32,9 @@ public function get(string $endpoint, array $params = []): Response
return $request->get($url, $params);
}
/**
* @param array<string, mixed> $data
*/
public function post(string $endpoint, array $data = []): Response
{
$url = "https://{$this->instance}/api/v3/{$endpoint}";

View file

@ -107,6 +107,9 @@ public function syncChannelPosts(string $token, int $platformChannelId, string $
}
}
/**
* @return array<string, mixed>
*/
public function createPost(string $token, string $title, string $body, int $platformChannelId, ?string $url = null, ?string $thumbnail = null, ?int $languageId = null): array
{
try {
@ -143,6 +146,9 @@ public function createPost(string $token, string $title, string $body, int $plat
}
}
/**
* @return array<int, mixed>
*/
public function getLanguages(): array
{
try {

View file

@ -21,6 +21,8 @@ public function __construct(PlatformAccount $account)
}
/**
* @param array<string, mixed> $extractedData
* @return array<string, mixed>
* @throws PlatformAuthException
* @throws Exception
*/

View file

@ -13,6 +13,9 @@
class ArticleFetcher
{
/**
* @return Collection<int, Article>
*/
public static function getArticlesFromFeed(Feed $feed): Collection
{
if ($feed->type === 'rss') {
@ -29,6 +32,9 @@ public static function getArticlesFromFeed(Feed $feed): Collection
return collect();
}
/**
* @return Collection<int, Article>
*/
private static function getArticlesFromRssFeed(Feed $feed): Collection
{
// TODO: Implement RSS feed parsing
@ -36,6 +42,9 @@ private static function getArticlesFromRssFeed(Feed $feed): Collection
return collect();
}
/**
* @return Collection<int, Article>
*/
private static function getArticlesFromWebsiteFeed(Feed $feed): Collection
{
try {
@ -68,6 +77,9 @@ private static function getArticlesFromWebsiteFeed(Feed $feed): Collection
}
}
/**
* @return array<string, mixed>
*/
public static function fetchArticleData(Article $article): array
{
try {

View file

@ -9,6 +9,9 @@
class ArticleParserFactory
{
/**
* @var array<int, class-string<ArticleParserInterface>>
*/
private static array $parsers = [
VrtArticleParser::class,
BelgaArticleParser::class,
@ -27,6 +30,9 @@ public static function getParser(string $url): ArticleParserInterface
throw new Exception("No parser found for URL: {$url}");
}
/**
* @return array<int, string>
*/
public static function getSupportedSources(): array
{
return array_map(function($parserClass) {

View file

@ -10,6 +10,9 @@
class HomepageParserFactory
{
/**
* @var array<int, class-string<HomepageParserInterface>>
*/
private static array $parsers = [
VrtHomepageParserAdapter::class,
BelgaHomepageParserAdapter::class,

View file

@ -26,6 +26,10 @@ public static function fetchHtml(string $url): string
}
}
/**
* @param array<int, string> $urls
* @return array<int, array<string, mixed>>
*/
public static function fetchMultipleUrls(array $urls): array
{
try {

View file

@ -8,26 +8,41 @@
class LogSaver
{
/**
* @param array<string, mixed> $context
*/
public static function info(string $message, ?PlatformChannel $channel = null, array $context = []): void
{
self::log(LogLevelEnum::INFO, $message, $channel, $context);
}
/**
* @param array<string, mixed> $context
*/
public static function error(string $message, ?PlatformChannel $channel = null, array $context = []): void
{
self::log(LogLevelEnum::ERROR, $message, $channel, $context);
}
/**
* @param array<string, mixed> $context
*/
public static function warning(string $message, ?PlatformChannel $channel = null, array $context = []): void
{
self::log(LogLevelEnum::WARNING, $message, $channel, $context);
}
/**
* @param array<string, mixed> $context
*/
public static function debug(string $message, ?PlatformChannel $channel = null, array $context = []): void
{
self::log(LogLevelEnum::DEBUG, $message, $channel, $context);
}
/**
* @param array<string, mixed> $context
*/
private static function log(LogLevelEnum $level, string $message, ?PlatformChannel $channel = null, array $context = []): void
{
$logContext = $context;

View file

@ -95,6 +95,9 @@ public static function extractThumbnail(string $html): ?string
return null;
}
/**
* @return array<string, string|null>
*/
public static function extractData(string $html): array
{
return [

View file

@ -4,6 +4,9 @@
class BelgaHomepageParser
{
/**
* @return array<int, string>
*/
public static function extractArticleUrls(string $html): array
{
preg_match_all('/href="(https:\/\/www\.belganewsagency\.eu\/[a-z0-9-]+)"/', $html, $matches);

View file

@ -77,6 +77,9 @@ public static function extractThumbnail(string $html): ?string
return null;
}
/**
* @return array<string, string|null>
*/
public static function extractData(string $html): array
{
return [

View file

@ -4,6 +4,9 @@
class VrtHomepageParser
{
/**
* @return array<int, string>
*/
public static function extractArticleUrls(string $html): array
{
// Extract article links using regex

View file

@ -17,9 +17,11 @@
class ArticlePublishingService
{
/**
* @param array<string, mixed> $extractedData
* @return EloquentCollection<int, ArticlePublication>
* @throws PublishException
*/
public function publishToRoutedChannels(Article $article, array $extractedData): Collection
public function publishToRoutedChannels(Article $article, array $extractedData): EloquentCollection
{
if (! $article->is_valid) {
throw new PublishException($article, PlatformEnum::LEMMY, new RuntimeException('CANNOT_PUBLISH_INVALID_ARTICLE'));
@ -46,7 +48,10 @@ public function publishToRoutedChannels(Article $article, array $extractedData):
->filter();
}
private function publishToChannel(Article $article, array $extractedData, $channel, $account): ?ArticlePublication
/**
* @param array<string, mixed> $extractedData
*/
private function publishToChannel(Article $article, array $extractedData, PlatformChannel $channel, mixed $account): ?ArticlePublication
{
try {
$publisher = new LemmyPublisher($account);
@ -64,7 +69,7 @@ private function publishToChannel(Article $article, array $extractedData, $chann
LogSaver::info('Published to channel via routing', $channel, [
'article_id' => $article->id,
'priority' => $channel->pivot->priority
'priority' => $channel->pivot->priority ?? null
]);
return $publication;

View file

@ -4,11 +4,13 @@
use App\Exceptions\RoutingMismatchException;
use App\Models\Feed;
use App\Models\PlatformChannel;
use Illuminate\Support\Collection;
class RoutingValidationService
{
/**
* @param Collection<int, PlatformChannel> $channels
* @throws RoutingMismatchException
*/
public function validateLanguageCompatibility(Feed $feed, Collection $channels): void

View file

@ -2,7 +2,7 @@ includes:
- vendor/larastan/larastan/extension.neon
parameters:
level: 5
level: 6
paths:
- app/
- tests/