fix: resolve static analysis issues and bump PHPStan to level 1

- Replace \Log:: with proper Log facade imports in Channel.php and EncryptArrayObject.php
- Remove unnecessary null coalescing on $_GET/$_POST superglobals in Input.php
- Add @property annotations to SeoMetadata and ImageOptimization models
- Add @property-read annotations for Livewire computed properties in ConfigPanel and WorkspaceConfig
- Bump PHPStan level from 0 to 1
- Remove Log facade from Psalm suppressions (now properly imported)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-29 23:09:27 +00:00
parent 1ab03b7c59
commit 7e367803fc
9 changed files with 63 additions and 8 deletions

View file

@ -1,7 +1,7 @@
parameters:
paths:
- src
level: 0
level: 1
ignoreErrors:
- '#Unsafe usage of new static#'
- '#env\(\).*outside of the config directory#'

View file

@ -31,8 +31,6 @@
<referencedClass name="Laravel\Horizon\Contracts\MasterSupervisorRepository" />
<referencedClass name="Flux\Flux" />
<referencedClass name="Flux\AssetManager" />
<!-- Laravel facades (global aliases) -->
<referencedClass name="Log" />
<!-- Runtime aliased classes (App\* namespace) -->
<referencedClass name="App\Traits\HasCdnUrls" />
<referencedClass name="App\Support\UtmHelper" />

View file

@ -15,6 +15,7 @@ use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Log;
/**
* Configuration channel (voice/context substrate).
@ -123,7 +124,7 @@ class Channel extends Model
while ($current->parent_id !== null) {
if (isset($seen[$current->parent_id])) {
\Log::error('Circular reference detected in channel inheritance', [
Log::error('Circular reference detected in channel inheritance', [
'channel_id' => $this->id,
'cycle_at' => $current->parent_id,
]);

View file

@ -19,6 +19,13 @@ use Livewire\Attributes\Computed;
use Livewire\Attributes\Url;
use Livewire\Component;
/**
* Configuration panel for managing config keys and values.
*
* @property-read ConfigProfile $activeProfile
* @property-read array<string> $categories
* @property-read \Illuminate\Database\Eloquent\Collection $workspaces
*/
class ConfigPanel extends Component
{
#[Url]

View file

@ -19,6 +19,17 @@ use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
/**
* Workspace configuration panel.
*
* @property-read int $depth
* @property-read array<string> $namespaces
* @property-read ConfigProfile|null $workspaceProfile
* @property-read ConfigProfile $systemProfile
* @property-read object|null $workspace
* @property-read string $prefix
* @property-read array<int, array{namespace: string, label: string, keys: \Illuminate\Support\Collection}> $tabs
*/
class WorkspaceConfig extends Component
{
public ?string $path = null;

View file

@ -14,6 +14,7 @@ namespace Core\Crypt;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Casts\ArrayObject;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
/**
* Cast for storing encrypted array data as ArrayObject.
@ -36,7 +37,7 @@ class EncryptArrayObject implements CastsAttributes
try {
$decrypted = Crypt::decryptString($attributes[$key]);
} catch (\Illuminate\Contracts\Encryption\DecryptException $e) {
\Log::warning('Failed to decrypt array object', ['key' => $key, 'error' => $e->getMessage()]);
Log::warning('Failed to decrypt array object', ['key' => $key, 'error' => $e->getMessage()]);
return null;
}
@ -44,7 +45,7 @@ class EncryptArrayObject implements CastsAttributes
$decoded = json_decode($decrypted, true);
if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) {
\Log::warning('Failed to decode encrypted array', ['key' => $key, 'error' => json_last_error_msg()]);
Log::warning('Failed to decode encrypted array', ['key' => $key, 'error' => json_last_error_msg()]);
return null;
}

View file

@ -22,8 +22,8 @@ class Input
{
$sanitiser = new Sanitiser;
$_GET = $sanitiser->filter($_GET ?? []);
$_POST = $sanitiser->filter($_POST ?? []);
$_GET = $sanitiser->filter($_GET);
$_POST = $sanitiser->filter($_POST);
return Request::capture();
}

View file

@ -16,6 +16,23 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* Image optimisation record.
*
* @property int $id
* @property string $path
* @property string|null $original_path
* @property int $original_size
* @property int $optimized_size
* @property int $percentage_saved
* @property string|null $driver
* @property int|null $quality
* @property int|null $workspace_id
* @property string|null $optimizable_type
* @property int|null $optimizable_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
*/
class ImageOptimization extends Model
{
use HasFactory;

View file

@ -14,6 +14,26 @@ namespace Core\Seo;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* SEO metadata for any model.
*
* @property int $id
* @property string $seoable_type
* @property int $seoable_id
* @property string|null $title
* @property string|null $description
* @property string|null $canonical_url
* @property array<string, mixed>|null $og_data
* @property array<string, mixed>|null $twitter_data
* @property array<string, mixed>|null $schema_markup
* @property string|null $robots
* @property string|null $focus_keyword
* @property int|null $seo_score
* @property array<string>|null $seo_issues
* @property array<string>|null $seo_suggestions
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
*/
class SeoMetadata extends Model
{
protected $table = 'seo_metadata';