diff --git a/app/Contracts/ArticleParserInterface.php b/app/Contracts/ArticleParserInterface.php index 14b651a..ab6c89f 100644 --- a/app/Contracts/ArticleParserInterface.php +++ b/app/Contracts/ArticleParserInterface.php @@ -11,6 +11,7 @@ public function canParse(string $url): bool; /** * Extract article data from HTML + * @return array */ public function extractData(string $html): array; diff --git a/app/Contracts/HomepageParserInterface.php b/app/Contracts/HomepageParserInterface.php index a940663..50301d6 100644 --- a/app/Contracts/HomepageParserInterface.php +++ b/app/Contracts/HomepageParserInterface.php @@ -11,6 +11,7 @@ public function canParse(string $url): bool; /** * Extract article URLs from homepage HTML + * @return array */ public function extractArticleUrls(string $html): array; diff --git a/app/Events/ExceptionOccurred.php b/app/Events/ExceptionOccurred.php index ff559b2..c611ebf 100644 --- a/app/Events/ExceptionOccurred.php +++ b/app/Events/ExceptionOccurred.php @@ -15,6 +15,7 @@ public function __construct( public Throwable $exception, public LogLevelEnum $level, public string $message, + /** @var array */ public array $context = [] ) { } diff --git a/app/Http/Controllers/RoutingController.php b/app/Http/Controllers/RoutingController.php index 1d2db44..a2b3504 100644 --- a/app/Http/Controllers/RoutingController.php +++ b/app/Http/Controllers/RoutingController.php @@ -149,6 +149,9 @@ public function toggle(Request $request, Feed $feed, PlatformChannel $channel): ->with('success', "Routing {$status} successfully!"); } + /** + * @return array|null + */ private function parseJsonFilters(?string $json): ?array { if (empty($json)) { diff --git a/app/Http/Requests/StoreFeedRequest.php b/app/Http/Requests/StoreFeedRequest.php index d7a3983..e5c5390 100644 --- a/app/Http/Requests/StoreFeedRequest.php +++ b/app/Http/Requests/StoreFeedRequest.php @@ -11,6 +11,9 @@ public function authorize(): bool return true; } + /** + * @return array + */ public function rules(): array { return [ diff --git a/app/Http/Requests/UpdateFeedRequest.php b/app/Http/Requests/UpdateFeedRequest.php index 8cb8571..588be14 100644 --- a/app/Http/Requests/UpdateFeedRequest.php +++ b/app/Http/Requests/UpdateFeedRequest.php @@ -11,6 +11,9 @@ public function authorize(): bool return true; } + /** + * @return array + */ public function rules(): array { return [ diff --git a/app/LogLevelEnum.php b/app/LogLevelEnum.php index 7897106..620df68 100644 --- a/app/LogLevelEnum.php +++ b/app/LogLevelEnum.php @@ -10,6 +10,9 @@ enum LogLevelEnum: string case ERROR = 'error'; case CRITICAL = 'critical'; + /** + * @return array + */ public static function toArray(): array { return [ diff --git a/app/Models/Article.php b/app/Models/Article.php index daaf7f2..521f392 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -11,9 +11,9 @@ use Illuminate\Support\Carbon; /** - * @method static firstOrCreate(string[] $array) + * @method static firstOrCreate(array $array) * @method static where(string $string, string $url) - * @method static create(string[] $array) + * @method static create(array $array) * @property integer $id * @property int $feed_id * @property Feed $feed @@ -40,6 +40,9 @@ class Article extends Model 'validated_at', ]; + /** + * @return array + */ public function casts(): array { return [ @@ -65,11 +68,17 @@ public function isValid(): bool return $this->is_valid; } + /** + * @return HasOne + */ public function articlePublication(): HasOne { return $this->hasOne(ArticlePublication::class); } + /** + * @return BelongsTo + */ public function feed(): BelongsTo { return $this->belongsTo(Feed::class); diff --git a/app/Models/ArticlePublication.php b/app/Models/ArticlePublication.php index c0e98ea..7ce6984 100644 --- a/app/Models/ArticlePublication.php +++ b/app/Models/ArticlePublication.php @@ -10,7 +10,7 @@ * @property integer $platform_channel_id * @property integer $post_id * - * @method static create(array $array) + * @method static create(array $array) */ class ArticlePublication extends Model { @@ -29,6 +29,9 @@ class ArticlePublication extends Model 'publication_data' => 'array', ]; + /** + * @return BelongsTo + */ public function article(): BelongsTo { return $this->belongsTo(Article::class); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 8628df2..20efe87 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -18,12 +18,12 @@ * @property int $language_id * @property Language|null $language * @property string $description - * @property array $settings + * @property array $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 $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 + */ public function channels(): BelongsToMany { return $this->belongsToMany(PlatformChannel::class, 'feed_platform_channels') @@ -90,6 +93,9 @@ public function channels(): BelongsToMany ->withTimestamps(); } + /** + * @return BelongsToMany + */ public function activeChannels(): BelongsToMany { return $this->channels() @@ -97,11 +103,17 @@ public function activeChannels(): BelongsToMany ->orderByPivot('priority', 'desc'); } + /** + * @return HasMany + */ public function articles(): HasMany { return $this->hasMany(Article::class); } + /** + * @return BelongsTo + */ public function language(): BelongsTo { return $this->belongsTo(Language::class); diff --git a/app/Models/FeedPlatformChannel.php b/app/Models/FeedPlatformChannel.php index 9bf99bd..8da0f85 100644 --- a/app/Models/FeedPlatformChannel.php +++ b/app/Models/FeedPlatformChannel.php @@ -12,10 +12,10 @@ * @property int $platform_channel_id * @property bool $is_active * @property int $priority - * @property array $filters + * @property array $filters * @property Carbon $created_at * @property Carbon $updated_at - * @method static create(array $array) + * @method static create(array $array) */ class FeedPlatformChannel extends Pivot { @@ -36,16 +36,25 @@ class FeedPlatformChannel extends Pivot 'filters' => 'array' ]; + /** + * @return BelongsTo + */ public function feed(): BelongsTo { return $this->belongsTo(Feed::class); } + /** + * @return BelongsTo + */ public function platformChannel(): BelongsTo { return $this->belongsTo(PlatformChannel::class); } + /** + * @return HasMany + */ public function keywords(): HasMany { return $this->hasMany(Keyword::class, 'feed_id', 'feed_id') diff --git a/app/Models/Keyword.php b/app/Models/Keyword.php index 153e69c..91d22b0 100644 --- a/app/Models/Keyword.php +++ b/app/Models/Keyword.php @@ -30,11 +30,17 @@ class Keyword extends Model 'is_active' => 'boolean' ]; + /** + * @return BelongsTo + */ public function feed(): BelongsTo { return $this->belongsTo(Feed::class); } + /** + * @return BelongsTo + */ public function platformChannel(): BelongsTo { return $this->belongsTo(PlatformChannel::class); diff --git a/app/Models/Language.php b/app/Models/Language.php index e7168c9..f95f47c 100644 --- a/app/Models/Language.php +++ b/app/Models/Language.php @@ -24,6 +24,9 @@ class Language extends Model 'is_active' => 'boolean' ]; + /** + * @return BelongsToMany + */ public function platformInstances(): BelongsToMany { return $this->belongsToMany(PlatformInstance::class) @@ -31,11 +34,17 @@ public function platformInstances(): BelongsToMany ->withTimestamps(); } + /** + * @return HasMany + */ public function platformChannels(): HasMany { return $this->hasMany(PlatformChannel::class); } + /** + * @return HasMany + */ public function feeds(): HasMany { return $this->hasMany(Feed::class); diff --git a/app/Models/Log.php b/app/Models/Log.php index 0d2dcd1..81e77d5 100644 --- a/app/Models/Log.php +++ b/app/Models/Log.php @@ -7,10 +7,10 @@ use Illuminate\Support\Carbon; /** - * @method static create(array $array) + * @method static create(array $array) * @property LogLevelEnum $level * @property string $message - * @property array $context + * @property array $context * @property Carbon $created_at * @property Carbon $updated_at */ diff --git a/app/Models/PlatformAccount.php b/app/Models/PlatformAccount.php index 2a65403..c450ede 100644 --- a/app/Models/PlatformAccount.php +++ b/app/Models/PlatformAccount.php @@ -24,10 +24,10 @@ * @property string $status * @property Carbon $created_at * @property Carbon $updated_at - * @property Collection $activeChannels + * @property Collection $activeChannels * @method static where(string $string, PlatformEnum $platform) * @method static orderBy(string $string) - * @method static create(array $validated) + * @method static create(array $validated) */ class PlatformAccount extends Model { @@ -54,6 +54,9 @@ class PlatformAccount extends Model ]; // Encrypt password when storing + /** + * @return Attribute + */ protected function password(): Attribute { return Attribute::make( @@ -63,6 +66,9 @@ protected function password(): Attribute } // Encrypt API token when storing + /** + * @return Attribute + */ 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 + */ 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 + */ public function channels(): BelongsToMany { return $this->belongsToMany(PlatformChannel::class, 'platform_account_channels') @@ -98,6 +110,9 @@ public function channels(): BelongsToMany ->withTimestamps(); } + /** + * @return BelongsToMany + */ public function activeChannels(): BelongsToMany { return $this->channels() diff --git a/app/Models/PlatformChannel.php b/app/Models/PlatformChannel.php index 3023d95..6dd5dba 100644 --- a/app/Models/PlatformChannel.php +++ b/app/Models/PlatformChannel.php @@ -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 $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 + */ public function platformInstance(): BelongsTo { return $this->belongsTo(PlatformInstance::class); } + /** + * @return BelongsToMany + */ public function platformAccounts(): BelongsToMany { return $this->belongsToMany(PlatformAccount::class, 'platform_account_channels') @@ -54,6 +59,9 @@ public function platformAccounts(): BelongsToMany ->withTimestamps(); } + /** + * @return BelongsToMany + */ 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 + */ public function feeds(): BelongsToMany { return $this->belongsToMany(Feed::class, 'feed_platform_channels') @@ -73,6 +84,9 @@ public function feeds(): BelongsToMany ->withTimestamps(); } + /** + * @return BelongsToMany + */ public function activeFeeds(): BelongsToMany { return $this->feeds() @@ -80,6 +94,9 @@ public function activeFeeds(): BelongsToMany ->orderByPivot('priority', 'desc'); } + /** + * @return BelongsTo + */ public function language(): BelongsTo { return $this->belongsTo(Language::class); diff --git a/app/Models/PlatformChannelPost.php b/app/Models/PlatformChannelPost.php index 700e3ae..d774a4f 100644 --- a/app/Models/PlatformChannelPost.php +++ b/app/Models/PlatformChannelPost.php @@ -7,7 +7,7 @@ /** * @method static where(string $string, PlatformEnum $platform) - * @method static updateOrCreate(array $array, array $array1) + * @method static updateOrCreate(array $array, array $array1) */ class PlatformChannelPost extends Model { @@ -21,6 +21,9 @@ class PlatformChannelPost extends Model 'posted_at', ]; + /** + * @return array + */ protected function casts(): array { return [ diff --git a/app/Models/PlatformInstance.php b/app/Models/PlatformInstance.php index e6a1a6f..fa2d242 100644 --- a/app/Models/PlatformInstance.php +++ b/app/Models/PlatformInstance.php @@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; /** - * @method static updateOrCreate(array $array, $instanceData) + * @method static updateOrCreate(array $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 + */ public function channels(): HasMany { return $this->hasMany(PlatformChannel::class); } + /** + * @return BelongsToMany + */ public function languages(): BelongsToMany { return $this->belongsToMany(Language::class) diff --git a/app/Modules/Lemmy/LemmyRequest.php b/app/Modules/Lemmy/LemmyRequest.php index 1199a35..5bdb5d8 100644 --- a/app/Modules/Lemmy/LemmyRequest.php +++ b/app/Modules/Lemmy/LemmyRequest.php @@ -16,6 +16,9 @@ public function __construct(string $instance, ?string $token = null) $this->token = $token; } + /** + * @param array $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 $data + */ public function post(string $endpoint, array $data = []): Response { $url = "https://{$this->instance}/api/v3/{$endpoint}"; diff --git a/app/Modules/Lemmy/Services/LemmyApiService.php b/app/Modules/Lemmy/Services/LemmyApiService.php index 87f75f0..108c431 100644 --- a/app/Modules/Lemmy/Services/LemmyApiService.php +++ b/app/Modules/Lemmy/Services/LemmyApiService.php @@ -107,6 +107,9 @@ public function syncChannelPosts(string $token, int $platformChannelId, string $ } } + /** + * @return array + */ 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 + */ public function getLanguages(): array { try { diff --git a/app/Modules/Lemmy/Services/LemmyPublisher.php b/app/Modules/Lemmy/Services/LemmyPublisher.php index 17dfdd3..68a2651 100644 --- a/app/Modules/Lemmy/Services/LemmyPublisher.php +++ b/app/Modules/Lemmy/Services/LemmyPublisher.php @@ -21,6 +21,8 @@ public function __construct(PlatformAccount $account) } /** + * @param array $extractedData + * @return array * @throws PlatformAuthException * @throws Exception */ diff --git a/app/Services/Article/ArticleFetcher.php b/app/Services/Article/ArticleFetcher.php index 6f95ab7..badb899 100644 --- a/app/Services/Article/ArticleFetcher.php +++ b/app/Services/Article/ArticleFetcher.php @@ -13,6 +13,9 @@ class ArticleFetcher { + /** + * @return Collection + */ 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 + */ 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 + */ private static function getArticlesFromWebsiteFeed(Feed $feed): Collection { try { @@ -68,6 +77,9 @@ private static function getArticlesFromWebsiteFeed(Feed $feed): Collection } } + /** + * @return array + */ public static function fetchArticleData(Article $article): array { try { diff --git a/app/Services/Factories/ArticleParserFactory.php b/app/Services/Factories/ArticleParserFactory.php index 3496a60..bea617c 100644 --- a/app/Services/Factories/ArticleParserFactory.php +++ b/app/Services/Factories/ArticleParserFactory.php @@ -9,6 +9,9 @@ class ArticleParserFactory { + /** + * @var array> + */ 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 + */ public static function getSupportedSources(): array { return array_map(function($parserClass) { diff --git a/app/Services/Factories/HomepageParserFactory.php b/app/Services/Factories/HomepageParserFactory.php index e6c1477..0e5e90b 100644 --- a/app/Services/Factories/HomepageParserFactory.php +++ b/app/Services/Factories/HomepageParserFactory.php @@ -10,6 +10,9 @@ class HomepageParserFactory { + /** + * @var array> + */ private static array $parsers = [ VrtHomepageParserAdapter::class, BelgaHomepageParserAdapter::class, diff --git a/app/Services/Http/HttpFetcher.php b/app/Services/Http/HttpFetcher.php index 1d263b6..ddf0485 100644 --- a/app/Services/Http/HttpFetcher.php +++ b/app/Services/Http/HttpFetcher.php @@ -26,6 +26,10 @@ public static function fetchHtml(string $url): string } } + /** + * @param array $urls + * @return array> + */ public static function fetchMultipleUrls(array $urls): array { try { diff --git a/app/Services/Log/LogSaver.php b/app/Services/Log/LogSaver.php index 6e6b11d..a94d178 100644 --- a/app/Services/Log/LogSaver.php +++ b/app/Services/Log/LogSaver.php @@ -8,26 +8,41 @@ class LogSaver { + /** + * @param array $context + */ public static function info(string $message, ?PlatformChannel $channel = null, array $context = []): void { self::log(LogLevelEnum::INFO, $message, $channel, $context); } + /** + * @param array $context + */ public static function error(string $message, ?PlatformChannel $channel = null, array $context = []): void { self::log(LogLevelEnum::ERROR, $message, $channel, $context); } + /** + * @param array $context + */ public static function warning(string $message, ?PlatformChannel $channel = null, array $context = []): void { self::log(LogLevelEnum::WARNING, $message, $channel, $context); } + /** + * @param array $context + */ public static function debug(string $message, ?PlatformChannel $channel = null, array $context = []): void { self::log(LogLevelEnum::DEBUG, $message, $channel, $context); } + /** + * @param array $context + */ private static function log(LogLevelEnum $level, string $message, ?PlatformChannel $channel = null, array $context = []): void { $logContext = $context; diff --git a/app/Services/Parsers/BelgaArticlePageParser.php b/app/Services/Parsers/BelgaArticlePageParser.php index 14fdca7..0a2d2dd 100644 --- a/app/Services/Parsers/BelgaArticlePageParser.php +++ b/app/Services/Parsers/BelgaArticlePageParser.php @@ -95,6 +95,9 @@ public static function extractThumbnail(string $html): ?string return null; } + /** + * @return array + */ public static function extractData(string $html): array { return [ diff --git a/app/Services/Parsers/BelgaHomepageParser.php b/app/Services/Parsers/BelgaHomepageParser.php index 7f374ef..3f3c5b5 100644 --- a/app/Services/Parsers/BelgaHomepageParser.php +++ b/app/Services/Parsers/BelgaHomepageParser.php @@ -4,6 +4,9 @@ class BelgaHomepageParser { + /** + * @return array + */ public static function extractArticleUrls(string $html): array { preg_match_all('/href="(https:\/\/www\.belganewsagency\.eu\/[a-z0-9-]+)"/', $html, $matches); diff --git a/app/Services/Parsers/VrtArticlePageParser.php b/app/Services/Parsers/VrtArticlePageParser.php index 2a408b1..323713d 100644 --- a/app/Services/Parsers/VrtArticlePageParser.php +++ b/app/Services/Parsers/VrtArticlePageParser.php @@ -77,6 +77,9 @@ public static function extractThumbnail(string $html): ?string return null; } + /** + * @return array + */ public static function extractData(string $html): array { return [ diff --git a/app/Services/Parsers/VrtHomepageParser.php b/app/Services/Parsers/VrtHomepageParser.php index 3b47232..8ca4d96 100644 --- a/app/Services/Parsers/VrtHomepageParser.php +++ b/app/Services/Parsers/VrtHomepageParser.php @@ -4,6 +4,9 @@ class VrtHomepageParser { + /** + * @return array + */ public static function extractArticleUrls(string $html): array { // Extract article links using regex diff --git a/app/Services/Publishing/ArticlePublishingService.php b/app/Services/Publishing/ArticlePublishingService.php index db216e6..f46955d 100644 --- a/app/Services/Publishing/ArticlePublishingService.php +++ b/app/Services/Publishing/ArticlePublishingService.php @@ -17,9 +17,11 @@ class ArticlePublishingService { /** + * @param array $extractedData + * @return EloquentCollection * @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 $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; diff --git a/app/Services/RoutingValidationService.php b/app/Services/RoutingValidationService.php index dddac21..7ddff2a 100644 --- a/app/Services/RoutingValidationService.php +++ b/app/Services/RoutingValidationService.php @@ -4,11 +4,13 @@ use App\Exceptions\RoutingMismatchException; use App\Models\Feed; +use App\Models\PlatformChannel; use Illuminate\Support\Collection; class RoutingValidationService { /** + * @param Collection $channels * @throws RoutingMismatchException */ public function validateLanguageCompatibility(Feed $feed, Collection $channels): void diff --git a/phpstan.neon b/phpstan.neon index d54bde2..09bcdfb 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,11 +2,11 @@ includes: - vendor/larastan/larastan/extension.neon parameters: - level: 5 + level: 6 paths: - app/ - tests/ - + excludePaths: - bootstrap/*.php - - storage/* \ No newline at end of file + - storage/*