refactor: fix all 105 PHPStan 2 strict type findings across core and modules
Resolve all non-false-positive PHPStan 2 findings, reducing the baseline from 486 to 382 entries (only unused-public false positives remain). Categories fixed: - Remove 39 redundant type checks (is_string, is_array, is_object, method_exists) - Remove 12 no-op array_values() on already-sequential lists - Simplify 13 null-coalesce on guaranteed offsets - Remove 8 always-true instanceof checks - Replace 12 redundant test assertions (assertTrue(true) → addToAssertionCount) - Simplify 6 unnecessary nullsafe operators (?-> → ->) - Fix 6 always-true !== '' comparisons - Fix remaining: unset.offset, return.unusedType, staticMethod narrowing 53 files changed across core/, modules/, pages/, and tests/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -65,17 +65,17 @@ class UserLifecycleAuditRepository
|
||||
$actions = RepoQuery::normalizeStringList(
|
||||
$filters['actions'] ?? '',
|
||||
self::FILTER_OPTIONS_LIMIT_MAX,
|
||||
static fn (string $value): string => UserLifecycleAction::tryNormalize($value)?->value ?? ''
|
||||
static fn (string $value): string => UserLifecycleAction::tryNormalize($value)->value ?? ''
|
||||
);
|
||||
$statuses = RepoQuery::normalizeStringList(
|
||||
$filters['statuses'] ?? '',
|
||||
self::FILTER_OPTIONS_LIMIT_MAX,
|
||||
static fn (string $value): string => UserLifecycleStatus::tryNormalize($value)?->value ?? ''
|
||||
static fn (string $value): string => UserLifecycleStatus::tryNormalize($value)->value ?? ''
|
||||
);
|
||||
$triggerTypes = RepoQuery::normalizeStringList(
|
||||
$filters['trigger_types'] ?? '',
|
||||
self::FILTER_OPTIONS_LIMIT_MAX,
|
||||
static fn (string $value): string => UserLifecycleTriggerType::tryNormalize($value)?->value ?? ''
|
||||
static fn (string $value): string => UserLifecycleTriggerType::tryNormalize($value)->value ?? ''
|
||||
);
|
||||
$actorUserIds = array_slice(
|
||||
RepoQuery::normalizeIdList($filters['actor_user_ids'] ?? ''),
|
||||
|
||||
@@ -485,11 +485,7 @@ class FrontendTelemetryIngestService
|
||||
|
||||
$cutoff = $now - self::DEDUPE_WINDOW_SECONDS;
|
||||
foreach ($seen as $key => $timestamp) {
|
||||
if (!is_string($key)) {
|
||||
unset($seen[$key]);
|
||||
continue;
|
||||
}
|
||||
if (!is_int($timestamp) || $timestamp < $cutoff) {
|
||||
if ($timestamp < $cutoff) {
|
||||
unset($seen[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,18 +28,10 @@ if (!Session::checkCsrfToken()) {
|
||||
}
|
||||
|
||||
$body = requestInput()->bodyAll();
|
||||
if (!is_array($body)) {
|
||||
http_response_code(204);
|
||||
return;
|
||||
}
|
||||
|
||||
$allowedKeys = ['event_type', 'severity', 'message', 'fingerprint', 'meta', 'occurred_at'];
|
||||
$csrfKey = Session::$csrfSessionKey;
|
||||
foreach ($body as $key => $value) {
|
||||
if (!is_string($key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($key === $csrfKey) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -141,9 +141,7 @@ class BcODataGateway
|
||||
. '&$count=true'
|
||||
. '&$orderby=' . rawurlencode($sortField . ' ' . $sortDir)
|
||||
. $select;
|
||||
if ($clauses !== []) {
|
||||
$url .= '&$filter=' . rawurlencode(implode(' and ', $clauses));
|
||||
}
|
||||
$url .= '&$filter=' . rawurlencode(implode(' and ', $clauses));
|
||||
|
||||
try {
|
||||
$response = $this->request('GET', $url);
|
||||
@@ -469,9 +467,6 @@ class BcODataGateway
|
||||
$rows = $this->extractODataValues($response);
|
||||
$mapped = [];
|
||||
foreach ($rows as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$ticketNo = trim((string) ($row['No'] ?? ''));
|
||||
if ($ticketNo === '') {
|
||||
continue;
|
||||
@@ -1271,9 +1266,6 @@ class BcODataGateway
|
||||
{
|
||||
$uniqueRows = [];
|
||||
foreach ($rows as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$no = trim((string) ($row['No'] ?? ''));
|
||||
$rowKey = $no !== '' ? $no : md5((string) json_encode($row));
|
||||
|
||||
@@ -645,7 +645,7 @@ class DebitorDetailService
|
||||
|
||||
$billToNo = trim((string) ($contract['Bill_to_Cust_No'] ?? ''));
|
||||
$sellToNo = trim((string) ($contract['Sell_to_Cust_No'] ?? ''));
|
||||
if ($customerNo !== '' && $billToNo !== $customerNo && $sellToNo !== $customerNo) {
|
||||
if ($billToNo !== $customerNo && $sellToNo !== $customerNo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1178,7 +1178,7 @@ class DebitorDetailService
|
||||
$days = isset($matches[1]) && $matches[1] !== '' ? (int) $matches[1] : 0;
|
||||
$hours = isset($matches[2]) && $matches[2] !== '' ? (int) $matches[2] : 0;
|
||||
$minutes = isset($matches[3]) && $matches[3] !== '' ? (int) $matches[3] : 0;
|
||||
$secondsFloat = isset($matches[4]) && $matches[4] !== '' ? (float) $matches[4] : 0.0;
|
||||
$secondsFloat = isset($matches[4]) ? (float) $matches[4] : 0.0;
|
||||
$seconds = (int) floor($secondsFloat);
|
||||
|
||||
return ($days * 86400) + ($hours * 3600) + ($minutes * 60) + $seconds;
|
||||
|
||||
@@ -332,7 +332,7 @@ final class RiskRadarService
|
||||
$days = isset($m[1]) && $m[1] !== '' ? (int) $m[1] : 0;
|
||||
$hours = isset($m[2]) && $m[2] !== '' ? (int) $m[2] : 0;
|
||||
$minutes = isset($m[3]) && $m[3] !== '' ? (int) $m[3] : 0;
|
||||
$seconds = isset($m[4]) && $m[4] !== '' ? (int) floor((float) $m[4]) : 0;
|
||||
$seconds = isset($m[4]) ? (int) floor((float) $m[4]) : 0;
|
||||
|
||||
return ($days * 86400) + ($hours * 3600) + ($minutes * 60) + $seconds;
|
||||
}
|
||||
@@ -355,7 +355,7 @@ final class RiskRadarService
|
||||
private static function resolveActivityTimestamp(array $ticket): ?int
|
||||
{
|
||||
$lastActivity = trim((string) ($ticket['Last_Activity_Date'] ?? ''));
|
||||
if ($lastActivity !== '' && !str_starts_with($lastActivity, '0001-01-01')) {
|
||||
if (!str_starts_with($lastActivity, '0001-01-01')) {
|
||||
$ts = strtotime($lastActivity);
|
||||
if (is_int($ts)) {
|
||||
return $ts;
|
||||
|
||||
@@ -244,7 +244,7 @@ class SystemRecommendationEngine
|
||||
return [
|
||||
'recommendations' => array_slice($deduped, 0, $maxItems),
|
||||
'meta' => [
|
||||
'applied_rules' => array_values(array_keys($appliedRulesMap)),
|
||||
'applied_rules' => array_keys($appliedRulesMap),
|
||||
'max_items' => $maxItems,
|
||||
'escalation_data_available' => $escalationAvailable,
|
||||
],
|
||||
@@ -379,9 +379,6 @@ class SystemRecommendationEngine
|
||||
$openTickets = [];
|
||||
|
||||
foreach ($tickets as $ticket) {
|
||||
if (!is_array($ticket)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$state = trim((string) ($ticket['Ticket_State'] ?? ''));
|
||||
if (in_array($state, self::CLOSED_TICKET_STATES, true)) {
|
||||
|
||||
@@ -193,9 +193,6 @@ class TicketCommunicationService
|
||||
// Collect support user name → ticket code mappings
|
||||
$supportUserNamesByTicket = [];
|
||||
foreach ($tickets as $ticket) {
|
||||
if (!is_array($ticket)) {
|
||||
continue;
|
||||
}
|
||||
$no = trim((string) ($ticket['No'] ?? ''));
|
||||
$name = trim((string) ($ticket['Support_User_Name'] ?? ''));
|
||||
if ($no !== '' && $name !== '') {
|
||||
@@ -784,7 +781,7 @@ class TicketCommunicationService
|
||||
}
|
||||
|
||||
if (preg_match('/^(\d{2}:\d{2})(:\d{2})?/', $time, $m) === 1) {
|
||||
return isset($m[2]) && $m[2] !== '' ? ($m[1] . $m[2]) : ($m[1] . ':00');
|
||||
return isset($m[2]) ? ($m[1] . $m[2]) : ($m[1] . ':00');
|
||||
}
|
||||
|
||||
return $time;
|
||||
@@ -902,9 +899,6 @@ class TicketCommunicationService
|
||||
|
||||
$nameMap = [];
|
||||
foreach ($contacts as $contact) {
|
||||
if (!is_array($contact)) {
|
||||
continue;
|
||||
}
|
||||
$contactNo = strtoupper(trim((string) ($contact['No'] ?? '')));
|
||||
$contactName = trim((string) ($contact['Name'] ?? ''));
|
||||
if ($contactNo === '' || $contactName === '') {
|
||||
@@ -1075,7 +1069,7 @@ class TicketCommunicationService
|
||||
}
|
||||
}
|
||||
|
||||
return $trimmed !== '' ? $trimmed : '';
|
||||
return $trimmed;
|
||||
}
|
||||
|
||||
private function normalizeActorRole(string $createdByType, string $actor): string
|
||||
|
||||
@@ -184,7 +184,7 @@ foreach ($rows as $contact) {
|
||||
}
|
||||
|
||||
Router::json([
|
||||
'data' => array_values($preparedRows),
|
||||
'data' => $preparedRows,
|
||||
'total' => max(0, $total),
|
||||
'type_options' => $typeOptions,
|
||||
'meta' => [
|
||||
|
||||
@@ -115,9 +115,9 @@ usort($contactValues, 'strnatcasecmp');
|
||||
|
||||
Router::json([
|
||||
'ok' => true,
|
||||
'categories' => array_values($categoryValues),
|
||||
'support_users' => array_values($supportUserValues),
|
||||
'contacts' => array_values($contactValues),
|
||||
'categories' => $categoryValues,
|
||||
'support_users' => $supportUserValues,
|
||||
'contacts' => $contactValues,
|
||||
'meta' => [
|
||||
'cache_used' => $cacheUsed,
|
||||
'cache_bypassed' => $refreshRequested,
|
||||
|
||||
@@ -254,7 +254,7 @@ foreach ($rows as $ticket) {
|
||||
}
|
||||
|
||||
Router::json([
|
||||
'data' => array_values($preparedRows),
|
||||
'data' => $preparedRows,
|
||||
'total' => max(0, $total),
|
||||
'meta' => [
|
||||
'cache_used' => $cacheUsed,
|
||||
|
||||
@@ -193,7 +193,7 @@ class BcODataGatewayTest extends TestCase
|
||||
$this->fail('Umlauts should be allowed in search queries');
|
||||
} catch (\RuntimeException) {
|
||||
// Expected: cURL fails against fake URL
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ class BcODataGatewayTest extends TestCase
|
||||
$this->fail('Numbers and dashes should be allowed in search queries');
|
||||
} catch (\RuntimeException) {
|
||||
// Expected: cURL fails against fake URL
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,13 +242,13 @@ class BcODataGatewayTest extends TestCase
|
||||
{
|
||||
$gateway = $this->createGatewayWithSettings();
|
||||
try {
|
||||
$results = $gateway->getContactsForCustomer('10636', 'Behindertenwerkstätten Oberpfalz – Betreuungs GmbH');
|
||||
$this->assertIsArray($results);
|
||||
$gateway->getContactsForCustomer('10636', 'Behindertenwerkstätten Oberpfalz – Betreuungs GmbH');
|
||||
$this->addToAssertionCount(1);
|
||||
} catch (\InvalidArgumentException) {
|
||||
$this->fail('Trusted customer literals should not be rejected for contact loads.');
|
||||
} catch (\RuntimeException) {
|
||||
// Expected: cURL fails against fake URL.
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,13 +256,13 @@ class BcODataGatewayTest extends TestCase
|
||||
{
|
||||
$gateway = $this->createGatewayWithSettings();
|
||||
try {
|
||||
$results = $gateway->getTicketsForCustomer('10494', "J.G. Knopf´s Sohn Gmbh & Co. KG");
|
||||
$this->assertIsArray($results);
|
||||
$gateway->getTicketsForCustomer('10494', "J.G. Knopf´s Sohn Gmbh & Co. KG");
|
||||
$this->addToAssertionCount(1);
|
||||
} catch (\InvalidArgumentException) {
|
||||
$this->fail('Trusted customer literals should not be rejected for ticket loads.');
|
||||
} catch (\RuntimeException) {
|
||||
// Expected: cURL fails against fake URL.
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ class BcODataGatewayTest extends TestCase
|
||||
$threw = true;
|
||||
}
|
||||
// Either cURL failed (expected) or returned empty — both are fine
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testGetContractLinesForCustomerAcceptsCustomerNoWithDash(): void
|
||||
@@ -410,6 +410,6 @@ class BcODataGatewayTest extends TestCase
|
||||
} catch (\RuntimeException) {
|
||||
$threw = true;
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +537,6 @@ class DebitorDetailServiceTest extends TestCase
|
||||
|
||||
$result = DebitorDetailService::buildSupportDashboard($tickets, 48);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(4, $result['health']['total_tickets']);
|
||||
$this->assertSame(3, $result['health']['open_tickets']);
|
||||
$this->assertSame(1, $result['health']['critical_tickets']);
|
||||
@@ -566,7 +565,6 @@ class DebitorDetailServiceTest extends TestCase
|
||||
|
||||
$result = DebitorDetailService::buildSupportDashboard($tickets, 48);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(0, $result['health']['open_tickets']);
|
||||
$this->assertSame(0, $result['health']['critical_tickets']);
|
||||
$this->assertNull($result['health']['oldest_open_age_hours']);
|
||||
@@ -639,7 +637,6 @@ class DebitorDetailServiceTest extends TestCase
|
||||
'definitions_count' => 15,
|
||||
]);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertSame(4, $result['health']['escalation_count']);
|
||||
$this->assertTrue($result['health']['escalation_available']);
|
||||
$this->assertSame(6, $result['meta']['escalation_tickets_considered']);
|
||||
|
||||
@@ -36,8 +36,7 @@ class TicketCommunicationServiceTest extends TestCase
|
||||
$service = new TicketCommunicationService($soapGateway, $odataGateway);
|
||||
$result = $service->loadTicketCommunicationFeed('T26-1216', 40);
|
||||
|
||||
$this->assertTrue($result['ok']);
|
||||
$this->assertIsArray($result['entries']);
|
||||
$this->assertNotEmpty($result['entries']);
|
||||
$this->assertGreaterThanOrEqual(3, count($result['entries']));
|
||||
$this->assertSame(true, $result['meta']['soap_used']);
|
||||
$this->assertSame(false, $result['meta']['fallback_used']);
|
||||
|
||||
@@ -134,7 +134,7 @@ final class NotificationMessage
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($normalized);
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -377,7 +377,7 @@ class NotificationService
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($normalized);
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user