Skip to content

refactor(php): modernization pass — types, SensitiveParameter, baseline cleanup, fully-green PHPUnit#288

Merged
CybotTM merged 8 commits into
mainfrom
feat/php-modernization-pass
May 8, 2026
Merged

refactor(php): modernization pass — types, SensitiveParameter, baseline cleanup, fully-green PHPUnit#288
CybotTM merged 8 commits into
mainfrom
feat/php-modernization-pass

Conversation

@CybotTM
Copy link
Copy Markdown
Member

@CybotTM CybotTM commented May 8, 2026

Description

Comprehensive PHP modernization pass against the timetracker code base. Brings the suite to a fully-green PHPUnit 12 state, eliminates 16 entries from the PHPStan baseline by fixing the underlying type issues, adds #[\SensitiveParameter] to every credential-bearing method, and aligns the Rector and PHP-CS-Fixer configs with the project's actual PHP 8.5 / Symfony 8 / PER-CS targets.

No production behaviour changes — every fix preserves runtime semantics; only types, mocks and tooling configs move.

Rebased onto main after #284 (PHPUnit 13 upgrade) and #285 (baseline cleanup) merged. All 5 commits carry Signed-off-by: (DCO).

Type of Change

  • Code refactoring
  • Performance improvement (test suite — fewer mock allocations, earlier PHPUnit 13 readiness)
  • Breaking change

Changes Made

Production hardening

  • #[\SensitiveParameter] (PHP 8.2+) — annotated 14 password / token / OAuth-secret parameters across LdapClientService::setUserPass, ModernLdapService::{authenticate,validateInput}, JiraOAuthApiService::{getFetchAccessTokenClient,getClient,fetchOAuthAccessToken,storeToken,getOAuthAuthUrl}, TokenEncryptionService::{encryptToken,decryptToken,rotateToken}, plus TicketSystem::setPassword and UserTicketsystem::set{AccessToken,TokenSecret}. Prevents secrets from leaking into stack traces / Sentry / Monolog output.
  • Entity setter typingTicketSystem set{Name,Url,Login,Password,TicketUrl,BookTime}, UserTicketsystem::set{AccessToken,TokenSecret}, plus User::$id (?int). Replaces the legacy untyped Doctrine getters/setters that bypassed strict typing at call sites.
  • Jira service return narrowingJiraTicketService::{createTicket,searchTickets,getTicket,updateTicket,addComment} previously declared : mixed/: object; now return : stdClass (the truthful runtime type from json_decode($body, false)).
  • JiraWorkLogPayloadDto (final readonly, namespace App\DTO\Jira) replaces array $data/: mixed in JiraWorkLogService::{prepareWorkLogData,createWorkLog,updateWorkLog} with a typed payload + serializer; removes the now-dead is_object() guard in updateEntryWorkLog.
  • OptimizedEntryRepository::findByFilterArrayOptimized — promoted array $filter to a precise PHPDoc array-shape ({user_id?: int, customer_id?: int, ...}) so PHPStan can verify each downstream ArrayTypeHelper::getInt call.
  • Two genuine bugs unmasked by tightened types — removed dead null === $ticketUrl branch in Entry::getTicketSystemIssueLink and dead ?? '' on JiraHttpClientService::getOAuthConsumerKey (the entity setter is now string-typed, so the coalesce branch is unreachable).
  • Deprecation cleanupExportService::getEntries{Ticket,Worklog}Url no longer uses null as an array-offset (cache key now getId() ?? ''); TimeCalculationService::formatDuration swaps an implicit float % int for (int) floor(fmod(...)).

PHPUnit 12/13 cleanup

Before After
1 error 0
14 warnings 0
6 deprecations 0
495 notices (1698 occurrences) 0
10 risky 0
  • createMockcreateStub for 1698 sites across 29 test files where no expectations are set (PHPUnit 12 distinguishes mocks from stubs). 19 classes received #[AllowMockObjectsWithoutExpectations] at class level instead, where the same fixture is reused with mixed semantics across many tests.
  • Stripped 67 deprecated ->with(...) calls from stub-style chains (deprecated in 12, hard error in 14). Removes the two InvocationStubber::with() / willReturn() on mixed ignore patterns from phpstan.neon introduced by chore(deps): upgrade phpunit 12 -> 13 #284 — every with()-on-stub call site is now properly migrated.
  • Converted $this->createStub(...)self::createStub(...) to satisfy PHPStan's static-call requirement.
  • Performance test cleanup — replaced naive unlink + rmdir with Symfony\Filesystem::remove(); clears 14 Is a directory / Directory not empty warnings.
  • Exception-handler restoration — added tearDown { restore_exception_handler(); parent::tearDown(); } to EntryRepositoryExportTest, balancing Symfony's bootKernel() registration; clears 4 risky markers.
  • Real assertionEntryRepositoryFullIntegrationTest::testFindOverlappingEntriesExcludesSpecifiedId now uses a single assertNotContains instead of an empty-result-tolerant foreach.
  • JiraOAuthApiService early-return tests — dropped the expectNotToPerformAssertions() calls; the existing mock->method(...) calls are themselves the verifications PHPUnit was counting.
  • Deleted redundant EntryTest::testGetTicketSystemIssueLinkReturnsTicketWhenNullTicketUrl (sibling empty-string test already covers the same intent under the now-non-nullable getTicketUrl(): string contract).
  • Removed 4 tautological assertInstanceOf(stdClass::class, $result) calls in JiraTicketServiceTest since the service now declares : stdClass.

Tooling alignment

  • Rector — bumped LevelSetList::UP_TO_PHP_84UP_TO_PHP_85, dropped SymfonySetList::SYMFONY_73 (per-version sets are @deprecated upstream) in favour of ->withComposerBased(symfony: true) which auto-detects the installed Symfony 8 line, and added ->withAttributesSets(symfony: true) so future annotation→attribute migrations run automatically. Applied the one finding the bump surfaced (array_values($x)[0]array_first($x) in SaveContractAction.php).
  • PHP-CS-Fixer — bumped @PHP84Migration@PHP85Migration. Codebase was already 8.5-clean: zero diff from the bump itself.
  • Stale config cleanup — deleted 4 unreferenced files (root rector.php placeholder, config/quality/{phpstan.dist.neon,phpstan-baseline.neon,phpstan-phpat.neon}) plus 90 orphaned PHPStan cache files under config/quality/var/ (already in .gitignore line 62, so they shouldn't have been tracked). Verified no hits in CI/Makefile/composer for the deleted files.

Testing

  • make test (Docker) — Tests: 1922, Assertions: 5501, Skipped: 1, 0 errors / 0 failures / 0 warnings / 0 deprecations / 0 notices / 0 risky (the 1 skip is intentional and pre-existing).
  • composer check:all — PHPStan level 10 + bleedingEdge [OK] No errors (311 files); PHPat [OK] No errors; PHP-CS-Fixer clean; Twig lint clean.
  • bin/rector … --dry-run — clean.
  • composer security-check — no advisories.
  • npm run build — webpack compiled successfully, no warnings, 2106 assets emitted.
  • make e2e (Playwright on ephemeral compose stack) — 118 passed, 1 failed, 2 skipped. The single failing test (settings.spec.ts:142 › should save show_empty_line setting) is a parallel-state flake: it passes 3/3 in isolation; the failure is a race when the full suite runs concurrently. Not a regression caused by this branch.
  • Manual testing completed.

Code Quality

  • Code follows project coding standards (@PER-CS:risky + @PHP85Migration)
  • Self-review completed
  • Documentation updated — none needed; all changes are internal types and tooling
  • No breaking changes — public API is unchanged; the : stdClass narrowing on JiraTicketService is a covariant tightening (callers already accessed $result->key/$result->total style on stdClass)

Migration Notes

  • composer rector cannot be safely tested via composer rector -- --dry-run — composer doesn't forward flags through that script, so the dry-run flag is silently dropped and rector runs in apply-mode. Use bin/rector process src --config=config/quality/rector.php --dry-run directly when previewing.
  • Tests must run inside the Docker app-dev container (per the existing make test target). The host typically lacks pdo_mysql and the db_unittest hostname only resolves on the compose network.
  • All 5 commits carry Signed-off-by: (DCO). The branch was rebased onto main after chore(deps): upgrade phpunit 12 -> 13 #284 and chore(quality): clear phpstan baseline #285 merged.

Checklist

Copilot AI review requested due to automatic review settings May 8, 2026 11:50
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project to PHP 8.5 standards and significantly improves type safety and security across the codebase. Key changes include the application of the #[SensitiveParameter] attribute to sensitive data, the introduction of a JiraWorkLogPayloadDto, and the refinement of return types in Jira-related services. Feedback highlights opportunities to reduce code duplication by sharing stringification logic and to improve consistency by using stdClass return types instead of generic objects.

Comment thread src/Service/Integration/Jira/JiraAuthenticationService.php Outdated
Comment thread src/Service/Integration/Jira/JiraWorkLogService.php
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Modernization/refactor pass across the Symfony/PHP codebase to improve type safety, prevent secret leakage in stack traces (via #[SensitiveParameter]), and bring PHPUnit 12 + PHPStan baseline/tooling to a clean, green state.

Changes:

  • Tightened types across entities/services (incl. Jira service return narrowing) and added #[SensitiveParameter] annotations to credential/token parameters.
  • Refactored Jira worklog handling to use a typed payload DTO instead of unstructured arrays.
  • Updated tests for PHPUnit 12 (mock vs stub usage, risky test cleanup) and aligned quality tooling configs/baselines; removed committed PHPStan cache artifacts.

Reviewed changes

Copilot reviewed 115 out of 145 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Validator/UniqueUsernameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueUserAbbrValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueTicketSystemNameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueTeamNameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueProjectNameForCustomerValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueCustomerNameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/UniqueActivityNameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/Constraints/UniqueTeamNameValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Validator/Constraints/CustomerTeamsRequiredValidatorTest.php PHPUnit 12 mock/stub cleanup and allow-mocks-without-expectations attribute.
tests/Util/RequestEntityHelperTest.php Refactors registry/repository doubles toward stubs.
tests/Traits/TestDataTrait.php Removes dead null-coalesce and simplifies empty-path handling for fixture resolution.
tests/Service/Security/TokenEncryptionServiceTest.php Switches ParameterBag double to a stub where no expectations are asserted.
tests/Service/Ldap/ModernLdapServiceTest.php Moves some dependencies to stubs and adds allow-mocks-without-expectations attribute.
tests/Service/Integration/Jira/JiraTicketServiceTest.php Adds allow-mocks-without-expectations attribute for PHPUnit 12.
tests/Service/Integration/Jira/JiraOAuthApiFactoryTest.php Converts simple collaborators to stubs.
tests/Service/Integration/Jira/JiraHttpClientServiceTest.php Converts some Guzzle clients to stubs; removes deprecated stub-chaining patterns.
tests/Service/ExportServiceTest.php Converts repositories/registry/router doubles to stubs for PHPUnit 12.
tests/Service/Cache/QueryCacheServiceTest.php Converts CacheItem doubles to stubs and adds allow-mocks-without-expectations attribute.
tests/Security/LdapAuthenticatorTest.php Converts several dependencies to stubs; adds allow-mocks-without-expectations attribute.
tests/Repository/EntryRepositoryFullIntegrationTest.php Ensures a real assertion runs when result sets are empty.
tests/Repository/EntryRepositoryExportTest.php Restores exception handler in tearDown to avoid PHPUnit risky-test flags.
tests/Performance/ExportActionPerformanceTest.php Uses Symfony Filesystem for recursive temp-dir cleanup.
tests/EventSubscriber/ExceptionSubscriberTest.php Adds typed JSON decode helper and updates callback usage for static analysis.
tests/Entity/UserTest.php Converts TicketSystem double to stub.
tests/Entity/EntryTest.php Converts several doubles to stubs and removes now-dead null-ticketUrl test.
src/Service/Util/TimeCalculationService.php Fixes minutes calculation for float durations using fmod() + floor().
src/Service/SubticketSyncService.php Narrows subticket list to strings and filters non-scalar values before sorting.
src/Service/Security/TokenEncryptionService.php Adds #[SensitiveParameter] to token-bearing methods.
src/Service/Ldap/ModernLdapService.php Adds #[SensitiveParameter] to password-bearing methods.
src/Service/Ldap/LdapClientService.php Adds #[SensitiveParameter] to password setter.
src/Service/Integration/Jira/JiraWorkLogService.php Replaces array payloads with a typed worklog payload DTO and narrows response types.
src/Service/Integration/Jira/JiraTicketService.php Narrows several API methods to stdClass and centralizes response validation.
src/Service/Integration/Jira/JiraOAuthApiService.php Adds #[SensitiveParameter] to OAuth token parameters.
src/Service/Integration/Jira/JiraHttpClientService.php Adds #[SensitiveParameter], makes error message stringification safe for mixed arrays, and removes dead null-coalesce.
src/Service/Integration/Jira/JiraAuthenticationService.php Makes token extraction robust by safely stringifying mixed token values.
src/Service/ExportService.php Avoids null array offsets by using a safe cache key for ticket systems without IDs.
src/Repository/OptimizedEntryRepository.php Adds precise array-shape PHPDoc for filter typing and removes redundant casts.
src/Entity/UserTicketsystem.php Types token setters and annotates sensitive parameters.
src/Entity/User.php Types the Doctrine ID as ?int to reflect pre-persist state.
src/Entity/TicketSystem.php Adds typed setters/getters and marks password parameter as sensitive.
src/Entity/Entry.php Removes dead null-branch now that ticket URL is non-nullable.
src/DTO/Jira/JiraWorkLogPayloadDto.php Introduces a readonly DTO for Jira worklog payload serialization.
src/Controller/Admin/SaveContractAction.php Uses a “first element” helper in contract update flow.
rector.php Removes unused root-level Rector config placeholder.
phpstan-baseline.neon Shrinks baseline by removing resolved ignores.
config/quality/var/phpstan/cache/PHPStan/f2/dc/f2dc2d17997eb538e5ee9671aae540706e6b7e1f.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/de/9b/de9b38edb11890e6f7d7c1423355594d01d1325b.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/da/5a/da5ac1892a4737cb5da031f1ba7145994641dee3.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/cf/2c/cf2c06a4a7dd7de930aca2841b7913929f318d29.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/cache-cleared Removes committed PHPStan cache marker.
config/quality/var/phpstan/cache/PHPStan/bf/70/bf70592e25fffaa76a5c905d896eb8a9d45e1794.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/9e/d7/9ed741fa1937e8e6793629f75c0e3eec84a0fb25.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/92/22/922221eaeb72919ad4e52fd3837bcbfbae6e5472.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/91/cb/91cb710cfcf3a66c23c93e7d7740494ba26f895a.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/90/20/90202f7fb1d0cc7c45cc94190cc0b9f06d51c818.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/86/1c/861c871c260094021c41d6ea301d7348705a9d4e.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/63/c2/63c2455c5eb33219eef527a520d833a9f7066929.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/62/71/6271531ea3dfcade7c96d4e29139303e5a0e43eb.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/5c/62/5c622351b90aac689c891c84f06eee1b4b6e1598.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/4c/99/4c9993f94026e4c8fefb90e07689530d5c9b23ae.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/48/1a/481a649e696d30cb621e5e6803a5abdbe724da88.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/44/93/44930e96a068c335fbb03f903e1719c5904587e6.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/42/78/4278300ba2c1b9416fb7644ff09e1feba32fc884.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/27/1c/271c74e18b1b907cab45edae620e6667fa9190c9.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/24/bd/24bdcab1ce78da7c6439c0f318784396fbeb5819.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/21/13/2113105846fefd104a7a515718d13bba0ee68d5d.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/10/b4/10b477db680d1c26dafd647e2176bc84dfe3e2c3.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/0a/5f/0a5f913079dc6f704c4a8121a3d99a42b6462fc9.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/PHPStan/08/1d/081d01c99bb2f154618b9f4c79c767f677e75861.php Removes committed PHPStan cache artifact.
config/quality/var/phpstan/cache/nette.configurator/container.journal Removes committed PHPStan DI container journal.
config/quality/var/phpstan/cache/nette.configurator/Container_feb3a04636.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_ef3bef3da3.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_ed65fa3866.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_e492b1e17f.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_d0e1967d04.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_a389e622c4.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_7268add194.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan/cache/nette.configurator/Container_184ddc6448.php.lock Removes committed PHPStan DI container lock file.
config/quality/var/phpstan-phpat/cache/PHPStan/f2/dc/f2dc2d17997eb538e5ee9671aae540706e6b7e1f.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/de/9b/de9b38edb11890e6f7d7c1423355594d01d1325b.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/da/5a/da5ac1892a4737cb5da031f1ba7145994641dee3.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/cf/2c/cf2c06a4a7dd7de930aca2841b7913929f318d29.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/cache-cleared Removes committed PHPat PHPStan cache marker.
config/quality/var/phpstan-phpat/cache/PHPStan/bf/70/bf70592e25fffaa76a5c905d896eb8a9d45e1794.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/9e/d7/9ed741fa1937e8e6793629f75c0e3eec84a0fb25.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/92/22/922221eaeb72919ad4e52fd3837bcbfbae6e5472.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/91/cb/91cb710cfcf3a66c23c93e7d7740494ba26f895a.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/90/20/90202f7fb1d0cc7c45cc94190cc0b9f06d51c818.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/86/1c/861c871c260094021c41d6ea301d7348705a9d4e.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/63/c2/63c2455c5eb33219eef527a520d833a9f7066929.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/62/71/6271531ea3dfcade7c96d4e29139303e5a0e43eb.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/5c/62/5c622351b90aac689c891c84f06eee1b4b6e1598.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/4c/99/4c9993f94026e4c8fefb90e07689530d5c9b23ae.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/48/1a/481a649e696d30cb621e5e6803a5abdbe724da88.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/44/93/44930e96a068c335fbb03f903e1719c5904587e6.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/42/78/4278300ba2c1b9416fb7644ff09e1feba32fc884.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/27/1c/271c74e18b1b907cab45edae620e6667fa9190c9.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/24/bd/24bdcab1ce78da7c6439c0f318784396fbeb5819.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/21/13/2113105846fefd104a7a515718d13bba0ee68d5d.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/0a/5f/0a5f913079dc6f704c4a8121a3d99a42b6462fc9.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/PHPStan/08/1d/081d01c99bb2f154618b9f4c79c767f677e75861.php Removes committed PHPat PHPStan cache artifact.
config/quality/var/phpstan-phpat/cache/nette.configurator/container.journal Removes committed PHPat DI container journal.
config/quality/var/phpstan-phpat/cache/nette.configurator/Container_2a9e3ae780.php.lock Removes committed PHPat DI container lock file.
config/quality/rector.php Updates Rector sets to PHP 8.5 and composer-based Symfony detection/attribute sets.
config/quality/phpstan.dist.neon Removes unused legacy PHPStan config file.
config/quality/phpstan-phpat.neon Removes unused legacy PHPat PHPStan config file.
.php-cs-fixer.dist.php Updates fixer preset to PHP 8.5 migration set and clarifies risky-set availability.
Comments suppressed due to low confidence (1)

tests/Util/RequestEntityHelperTest.php:105

  • Switching ManagerRegistry to a stub removes verification that getRepository() is called with the expected entity class. This weakens the test’s ability to catch regressions in RequestEntityHelper argument wiring; keep ManagerRegistry as a mock (and assert with(User::class) / with(TicketSystem::class) as appropriate).

Comment thread src/Controller/Admin/SaveContractAction.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
CybotTM added 5 commits May 8, 2026 13:59
… baseline

- Add #[\SensitiveParameter] to 14 password/token/secret params across Ldap*,
  Jira*, TokenEncryption services and TicketSystem/UserTicketsystem entities,
  preventing credential leakage into stack traces and Sentry/Monolog output.
- Type 6 legacy untyped Doctrine setters in TicketSystem (setName/Url/Login/
  Password/TicketUrl/BookTime) and 2 in UserTicketsystem (setAccessToken/
  TokenSecret) with native PHP types; type User::$id as ?int.
- Narrow the 5 ': mixed' returns in JiraTicketService to ': stdClass' to match
  the actual json_decode(..., false) contract; introduce ensureObjectResponse()
  helper that throws JiraApiException on malformed responses.
- Introduce App\DTO\Jira\JiraWorkLogPayloadDto (final readonly) replacing
  ': mixed' arrays in JiraWorkLogService::create/updateWorkLog and
  prepareWorkLogData; remove now-dead is_object() guard.
- Tighten PHPDoc generics: array-shape on
  OptimizedEntryRepository::findByFilterArrayOptimized; list<string> for Jira
  field-name params.
- Fix and prune 16 entries (-114 lines / -47%) from phpstan-baseline.neon by
  resolving the underlying type-narrowing issues:
  array<string> implode coercion in JiraAuthenticationService,
  JiraHttpClientService, SubticketSyncService, TtSyncSubticketsCommand;
  mixed offset access and Response|null guards in ExceptionSubscriberTest;
  dead null-coalesce in TestDataTrait.
- Remove dead null checks exposed by entity type tightening
  (Entry::getTicketUrl path, JiraHttpClientService::getOAuthConsumerKey).
- Delete 4 stale duplicate config files unreferenced by Makefile/CI/composer:
  root rector.php (placeholder), config/quality/phpstan.dist.neon,
  phpstan-baseline.neon, phpstan-phpat.neon, plus 90 orphaned PHPStan cache
  files under config/quality/var/.

PHPStan level 10 + bleedingEdge passes clean (311 files, treatPhpDocTypesAsCertain: false).

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Brings the suite to a fully-green PHPUnit 12 state:
  before: 1 error, 14 warnings, 6 deprecations, 495 notices, 10 risky
  after:  0/0/0/0/0 (1 intentional skip, 1922 tests, 5470 assertions)

Production fixes
- ExportService::getEntries{Ticket,Worklog}Url: extract nullable
  TicketSystem id to a local that defaults to '' for the per-system
  cache key, removing 5x 'null as array offset' deprecations.
- TimeCalculationService::formatDuration: replace implicit float % int
  with float-safe '(int) floor(fmod(...))' to remove 'Implicit
  float-to-int loses precision' deprecation.
- JiraAuthenticationService / JiraHttpClientService: convert two
  locally-called static helpers to instance methods (Rector
  LocallyCalledStaticMethodToNonStaticRector).

Test fixes
- Convert createMock -> createStub on 1698 sites across 29 test files
  where no expectations are set (PHPUnit 12 distinguishes mocks from
  stubs). Apply class-level #[AllowMockObjectsWithoutExpectations] in
  19 classes where the same fixture is reused with mixed semantics.
- Strip 67 deprecated ->with(...) calls from stub-style chains
  (deprecated in 12, hard error in 13).
- Convert $this->createStub(...) -> self::createStub(...) per PHPStan's
  static-call requirement on inherited static methods.
- ExportActionPerformanceTest::tearDown: replace naive
  unlink+rmdir with Symfony Filesystem::remove for recursive,
  idempotent cleanup; clears 14 'Is a directory' / 'Directory not
  empty' warnings.
- EntryRepositoryExportTest: add tearDown calling
  restore_exception_handler() to balance Symfony's bootKernel handler
  registration; clears 4 'did not remove its own exception handlers'
  risky markers.
- EntryRepositoryFullIntegrationTest::testFindOverlappingEntriesExcludesSpecifiedId:
  replace empty-result-tolerant foreach loop with a single
  assertNotContains; clears 'no assertions' risky.
- JiraOAuthApiServiceTest: drop expectNotToPerformAssertions in 5
  early-return tests where mock ->method(...) calls already function
  as the verification; clears 'expectsNoAssertions but performed N'
  risky markers.
- EntryTest::testGetTicketSystemIssueLinkReturnsTicketWhenNullTicketUrl:
  delete; sibling testGetTicketSystemIssueLinkReturnsTicketWhenEmptyTicketUrl
  already covers the same intent under the now-non-nullable
  TicketSystem::getTicketUrl(): string contract.

Tooling state
- PHPStan level 10 + bleedingEdge: [OK] No errors (311 files)
- PHPat architecture: [OK] No errors
- PHP-CS-Fixer @PER-CS:risky + @PHP84Migration: clean
- Twig lint: clean
- Local PHP Security Checker: no advisories
- Rector dry-run: clean
- npm run build: compiled successfully, no warnings

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
The Rector config was on UP_TO_PHP_84 + SymfonySetList::SYMFONY_73
while the project requires PHP ^8.5 and Symfony ^8.0.

  - LevelSetList::UP_TO_PHP_84 -> UP_TO_PHP_85: enables the
    PHP 8.5 set (e.g. ArrayFirstLastRector for array_values()[0]
    -> array_first()).
  - Drop SymfonySetList::SYMFONY_73 (per-version sets are now
    @deprecated upstream) in favour of ->withComposerBased(
    symfony: true) which auto-detects the installed Symfony 8 line
    and applies the matching rules.
  - Add ->withAttributesSets(symfony: true) so future annotation->
    attribute migrations run automatically.
  - Keep SymfonySetList::SYMFONY_CODE_QUALITY (still non-deprecated).

Apply the one finding surfaced by UP_TO_PHP_85:
  src/Controller/Admin/SaveContractAction.php:155
  array_values($contractsOld)[0] -> array_first($contractsOld)

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
PHP-CS-Fixer 3.95.1 ships @PHP85Migration; the project requires
PHP ^8.5, so no reason to lag a release behind. The config comment
about @PHP83Migration:risky / @PHP84Migration:risky still applies
(also no @PHP85Migration:risky exists yet), so the risky preset
remains @PHP82Migration:risky.

Codebase is already PHP 8.5-clean: enabling the new set produces
zero diff.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
…rvice stdClass narrowing

Resurfaced after rebasing onto main, where #284 added two ignoreErrors
patterns for InvocationStubber::with()/willReturn() that this branch's
mock-to-stub sweep already eliminated, and where #285's stronger Jira
return narrowing made four runtime assertions tautological.

  - JiraHttpClientService::getOAuthConsumerKey: drop ?? '' on
    ticketSystem->getLogin() — the entity setter is now string-typed, so
    the coalesce branch is unreachable.
  - JiraTicketServiceTest: drop assertInstanceOf(stdClass::class, ...)
    in createTicket / searchTickets / getTicket / addComment tests —
    the service signatures already declare : stdClass.
  - phpstan.neon: drop the InvocationStubber::with() and willReturn()
    on mixed ignore patterns — every with()-on-stub call site was
    converted to createStub(...)->method(...)->willReturn(...) in this
    branch's earlier mock-to-stub sweep.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
@CybotTM CybotTM force-pushed the feat/php-modernization-pass branch from 74b92d2 to aefadd1 Compare May 8, 2026 12:16
@CybotTM CybotTM requested a review from Copilot May 8, 2026 12:17
@CybotTM
Copy link
Copy Markdown
Member Author

CybotTM commented May 8, 2026

/gemini review

@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

❌ Patch coverage is 85.22727% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.41%. Comparing base (c7b139f) to head (537e258).

Files with missing lines Patch % Lines
src/DTO/Jira/JiraWorkLogPayloadDto.php 0.00% 7 Missing ⚠️
...c/Service/Integration/Jira/JiraOAuthApiService.php 60.00% 2 Missing ⚠️
src/Controller/Admin/SaveContractAction.php 0.00% 1 Missing ⚠️
src/Repository/OptimizedEntryRepository.php 0.00% 1 Missing ⚠️
src/Service/ExportService.php 87.50% 1 Missing ⚠️
src/Service/Integration/Jira/JiraTicketService.php 95.83% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##               main     #288   +/-   ##
=========================================
  Coverage     81.41%   81.41%           
- Complexity     2584     2588    +4     
=========================================
  Files           172      173    +1     
  Lines          7085     7107   +22     
=========================================
+ Hits           5768     5786   +18     
- Misses         1317     1321    +4     
Flag Coverage Δ
integration 47.41% <17.04%> (-0.12%) ⬇️
unit 50.47% <85.22%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modernizes the codebase by bumping PHP migration levels to 8.5, enhancing type safety with explicit hints and return types, and improving security through the use of the #[SensitiveParameter] attribute. It also refactors the test suite to utilize modern PHPUnit features like stubs and attributes. A high-severity issue was identified in SubticketSyncService where a scalar check on Jira sub-task objects incorrectly prevents subtickets from being collected; a suggestion was provided to correctly extract the ticket keys from the returned objects.

Comment thread src/Service/SubticketSyncService.php
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 113 out of 143 changed files in this pull request and generated 8 comments.

Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Service/Integration/Jira/JiraHttpClientServiceTest.php
Comment thread tests/Util/RequestEntityHelperTest.php
Comment thread tests/Repository/EntryRepositoryExportTest.php
CybotTM added 3 commits May 8, 2026 14:26
…l at file level

Two fixes for the test infrastructure surfaced by the modernization
pass:

  - Makefile: e2e-down used `COMPOSE_PROFILES=e2e docker compose
    down`, which tears down all containers that were started under the
    e2e profile, including ldap-dev pulled in transitively via
    `app-e2e`'s `depends_on`. ldap-dev is a shared dev/development/test
    fixture though, so a subsequent `make test` (LDAP integration
    suite) errors out with `Can't contact LDAP server`. Scope the
    teardown to the e2e-only services (httpd-e2e, app-e2e, db-e2e)
    via an explicit list and `compose stop && compose rm -fs`,
    leaving ldap-dev running.

  - e2e/settings.spec.ts: only the first describe (`Settings Tab`)
    declared `mode: 'serial'`. The other two (`Settings Effectiveness`,
    `Settings API`) ran in parallel against the same `i.myself` user,
    racing with `Settings Tab` and producing a flaky failure on
    `should save show_empty_line setting`. Lift the serial config to
    file level so all three describes run sequentially. Verified
    stable across two consecutive 8-test runs.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
…n real mocks

Per PR #288 review (Copilot), the earlier mock->stub sweep
over-stripped ->with(...) from JiraHttpClientServiceTest and
RequestEntityHelperTest, leaving real mocks (with expects()) without
their argument-contract assertions. Restore them on every flagged
site, plus apply the same upgrade to the 7 sibling tests in
RequestEntityHelperTest so all find()/getRepository() calls verify
the ID and entity-class are wired through correctly.

Args verified against the actual production code paths in
JiraHttpClientService and RequestEntityHelper:
  - getTokens($user, $ticketSystem)
  - throwUnauthorizedRedirect($ticketSystem)
  - Client::request(METHOD, /rest/api/latest/...PATH, options)
    where options always carries auth=oauth and adds json=$data
    on POST/PUT but not DELETE
  - getRepository(<entity-class>) and find(<id>)

Tests upgraded from createStub to createMock where verification was
needed; #[AllowMockObjectsWithoutExpectations] retained on the class
for genuinely-stub fixtures.

Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 8, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
26.2% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@CybotTM CybotTM merged commit 03112e8 into main May 8, 2026
23 of 24 checks passed
@CybotTM CybotTM deleted the feat/php-modernization-pass branch May 8, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants