Skip to content

Conversation

@jonasdekeukelaere
Copy link
Member

@jonasdekeukelaere jonasdekeukelaere commented Jan 8, 2026

PHP

  • PHP 8.1
  • PHP 8.2
  • PHP 8.3
  • PHP 8.4
  • PHP 8.5

Symfony

  • Symfony 5.4
  • Symfony 6.4?
  • Symfony 7.4?
  • Symfony 8.0?

Jonas De Keukelaere added 9 commits December 31, 2025 11:03
Deprecation: Auto-injection of the container for "\ForkCMS\App\ForkController" is deprecated since Symfony 4.2. Configure it as a service instead.
The "Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent::getException()" method is deprecated since Symfony 4.4, use "getThrowable()" instead.
The Symfony\Bundle\TwigBundle\TwigEngine class is deprecated since version 4.3 and will be removed in 5.0; use \Twig\Environment instead.
@sourcery-ai
Copy link

sourcery-ai bot commented Jan 8, 2026

Reviewer's Guide

Modernizes Fork CMS to run on PHP 8.1 with Symfony 5.4 component split, updates infrastructure (CI, Docker, deploy) to PHP 8.1, adapts kernel/event APIs and templating away from legacy Symfony bundles, and refactors several controllers/commands/tests to use dependency injection and newer Symfony utilities.

Class diagram for updated Common_WebTestCase using KernelBrowser

classDiagram
    class BaseWebTestCase {
    }

    class AppKernel {
    }

    class WebTestCase {
        <<abstract>>
        - static AppKernel kernel
        + static getKernelClass() string
        + static createClient(options array, server array) KernelBrowser
        + importSQL(database SpoonDatabase, sql string) void
        + resetDataBase(client KernelBrowser) void
        + loadFixtures(client KernelBrowser, fixtureClasses array) void
        + putParametersFileBack(filesystem Filesystem, kernelDir string) void
        + submitForm(client KernelBrowser, form Form, data array, setValues bool) void
        + submitEditForm(client KernelBrowser, form Form, data array) void
        + requestWithGetParameters(client KernelBrowser, url string, data array) Crawler
        + login(client KernelBrowser) void
        + logout(client KernelBrowser) void
        + assertIs404(client KernelBrowser) void
        + assertIs200(client KernelBrowser) void
        + assertGetsRedirected(client KernelBrowser, initialUrl string, expectedUrl string, requestMethod string, requestParameters array) void
        + assertPageLoadedCorrectly(client KernelBrowser, url string, expectedContent array, httpStatusCode int, requestMethod string, requestParameters array) void
        + assertClickOnLink(client KernelBrowser, linkText string, expectedContent array, httpStatusCode int, requestMethod string, requestParameters array) void
        + assertCurrentUrlContains(client KernelBrowser, partialUrls string...) void
        + assertCurrentUrlEndsWith(client KernelBrowser, partialUrl string) void
        + assertHttpStatusCode(client KernelBrowser, url string, httpStatusCode int, requestMethod string, requestParameters array) void
        + assertHttpStatusCode200(client KernelBrowser, url string, requestMethod string, requestParameters array) void
        + assertHttpStatusCode404(client KernelBrowser, url string, requestMethod string, requestParameters array) void
        + getFormForSubmitButton(client KernelBrowser, buttonText string, filterSelector string) Form
    }

    class KernelBrowser {
        + request(method string, uri string, parameters array, files array, server array, content string) Response
        + getContainer() ContainerInterface
        + getCrawler() Crawler
        + getHistory() BrowserHistory
        + setMaxRedirects(maxRedirects int) void
    }

    class SpoonDatabase {
        + execute(query string) void
        + getColumn(query string) array
        + drop(tables array) void
    }

    class Filesystem {
        + exists(path string) bool
    }

    class Form {
    }

    class Crawler {
    }

    BaseWebTestCase <|-- WebTestCase
    WebTestCase --> AppKernel : uses
    WebTestCase --> KernelBrowser : creates and uses
    WebTestCase --> SpoonDatabase : uses
    WebTestCase --> Filesystem : uses
    WebTestCase --> Form : submits
    WebTestCase --> Crawler : returns
Loading

Class diagram for Installer bundle controller and commands with dependency injection

classDiagram
    class AbstractController {
        + getParameter(name string) mixed
        + generateUrl(route string, parameters array, referenceType int) string
        + redirect(url string) RedirectResponse
        + render(view string, parameters array) Response
    }

    class InstallerController {
        <<final>>
        - static installationData InstallationData
        - forkInstaller ForkInstaller
        - requirementsChecker RequirementsChecker
        + __construct(forkInstaller ForkInstaller, requirementsChecker RequirementsChecker)
        + step1Action() Response
        + step6Action(request Request) Response
        - getInstallationData(request Request) InstallationData
        - checkInstall() void
        - handleInstallationStep(request Request, step int, formType string) Response
    }

    class ForkInstaller {
        + install(installationData InstallationData) string
    }

    class RequirementsChecker {
        - projectDir string
        + __construct(projectDir string)
        + passes() bool
        + hasErrors() bool
    }

    class PrepareForReinstallCommand {
        <<Command>>
        - database SpoonDatabase
        - kernel KernelInterface
        + __construct(database SpoonDatabase, kernel KernelInterface)
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
        - clearDatabase(io SymfonyStyle) int
        - removeConfiguration(io SymfonyStyle) void
        - clearCache(output OutputInterface, io SymfonyStyle) void
    }

    class Application {
        + __construct(kernel KernelInterface)
        + find(name string) Command
    }

    class KernelInterface {
    }

    class InstallationData {
    }

    class Request {
    }

    class Response {
    }

    class RedirectResponse {
    }

    class SpoonDatabase {
        + getColumn(query string) array
        + execute(query string) void
        + drop(tables array) void
    }

    class SymfonyStyle {
    }

    class InputInterface {
    }

    class OutputInterface {
    }

    class Command {
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
    }

    AbstractController <|-- InstallerController
    InstallerController --> ForkInstaller : uses
    InstallerController --> RequirementsChecker : uses
    InstallerController --> InstallationData : builds
    InstallerController --> Request : reads
    InstallerController --> Response : returns
    PrepareForReinstallCommand <|-- Command
    PrepareForReinstallCommand --> SpoonDatabase : uses
    PrepareForReinstallCommand --> KernelInterface : uses
    PrepareForReinstallCommand --> Application : creates
    Application --> KernelInterface : wraps
    RequirementsChecker ..> projectDir : configured via services.xml
    ForkInstaller ..> service_container : configured via services.xml
Loading

Class diagram for ForkController and application bootstrapping

classDiagram
    class AbstractController {
        + getParameter(name string) mixed
        + render(view string, parameters array) Response
    }

    class ForkController {
        - kernel KernelInterface
        - applicationMapping array
        + __construct(kernel KernelInterface)
        + backendController() Response
        + backendAjaxController() Response
        + frontendController() Response
        + frontendAjaxController() Response
        - handleApplication(application ApplicationInterface) Response
        - initializeBackend(app string) string
        - initializeFrontend(app string) string
    }

    class KernelInterface {
    }

    class BackendInit {
        + __construct(kernel KernelInterface)
        + initialize(app string) void
    }

    class FrontendInit {
        + __construct(kernel KernelInterface)
        + initialize(app string) void
    }

    class ApplicationInterface {
        + display() Response
    }

    class Backend {
        + __construct(kernel KernelInterface)
        + display() Response
    }

    class BackendAjax {
        + __construct(kernel KernelInterface)
        + display() Response
    }

    class Frontend {
        + __construct(kernel KernelInterface)
        + display() Response
    }

    class FrontendAjax {
        + __construct(kernel KernelInterface)
        + display() Response
    }

    class Response {
    }

    AbstractController <|-- ForkController
    ForkController --> KernelInterface : injected
    ForkController --> BackendInit : creates and uses
    ForkController --> FrontendInit : creates and uses
    ForkController --> Backend : bootstraps
    ForkController --> BackendAjax : bootstraps
    ForkController --> Frontend : bootstraps
    ForkController --> FrontendAjax : bootstraps
    BackendInit --> KernelInterface
    FrontendInit --> KernelInterface
    Backend ..|> ApplicationInterface
    BackendAjax ..|> ApplicationInterface
    Frontend ..|> ApplicationInterface
    FrontendAjax ..|> ApplicationInterface
    ForkController --> ApplicationInterface : handleApplication
Loading

Class diagram for Twig templating refactor away from TwigEngine

classDiagram
    class BaseTwigTemplate {
        - environment Environment
        - debugMode bool
        - forkSettings ModulesSettings
        + assign(key string, value mixed) void
        + display(template string) Response
        + getContent(template string) string
    }

    class Backend_TwigTemplate {
        - debugMode bool
        + __construct(addToReference bool)
        - buildTwigEnvironmentForTheBackend() Environment
    }

    class Frontend_TwigTemplate {
        - forkSettings ModulesSettings
        - language string
        - debugMode bool
        + __construct(environment Environment)
    }

    class Environment {
    }

    class TemplateLocator {
    }

    class Model {
        + getContainer() ContainerInterface
    }

    class ModulesSettings {
    }

    class ContainerInterface {
        + get(id string) mixed
        + set(id string, service mixed) void
        + getParameter(name string) mixed
    }

    class Response {
    }

    BaseTwigTemplate <|-- Backend_TwigTemplate
    BaseTwigTemplate <|-- Frontend_TwigTemplate
    Backend_TwigTemplate --> Environment : builds
    Backend_TwigTemplate --> Model : uses
    Backend_TwigTemplate --> ContainerInterface : uses
    Frontend_TwigTemplate --> Environment : injected
    Frontend_TwigTemplate --> ModulesSettings : uses
    Frontend_TwigTemplate --> Model : uses
    BaseTwigTemplate --> Environment : holds
    BaseTwigTemplate --> ModulesSettings : may use
Loading

Class diagram for updated console commands using dependency injection

classDiagram
    class Command {
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
    }

    class PrepareForReinstallCommand {
        <<Command>>
        + const RETURN_SUCCESS : int
        + const RETURN_DID_NOT_REINSTALL : int
        + const RETURN_DID_NOT_CLEAR_DATABASE : int
        - database SpoonDatabase
        - kernel KernelInterface
        + __construct(database SpoonDatabase, kernel KernelInterface)
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
        - clearDatabase(io SymfonyStyle) int
        - removeConfiguration(io SymfonyStyle) void
        - clearCache(output OutputInterface, io SymfonyStyle) void
    }

    class MediaGalleryDeleteAllCommand {
        <<Command>>
        - deleteMediaItems bool
        - mediaGalleryRepository MediaGalleryRepository
        - commandBus CommandBus
        + __construct(mediaGalleryRepository MediaGalleryRepository, commandBus CommandBus)
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
        - checkOptions(input InputInterface) void
        - deleteMediaGalleries() void
    }

    class CacheClearCommand {
        <<Command>>
        - clearAll bool
        - fileManager FileManager
        + __construct(fileManager FileManager)
        + configure() void
        + execute(input InputInterface, output OutputInterface) int
        - checkOptions(input InputInterface) void
        - deleteCachedFolders() void
        - getFoldersToDelete() array
    }

    class MediaGalleryRepository {
        + findAll() array
    }

    class CommandBus {
        + handle(command object) void
    }

    class DeleteMediaGallery {
        + __construct(mediaGallery MediaGallery)
    }

    class MediaGallery {
    }

    class FileManager {
        + deleteFolder(path string) void
    }

    class SpoonDatabase {
        + getColumn(query string) array
        + execute(query string) void
        + drop(tables array) void
    }

    class KernelInterface {
    }

    class SymfonyStyle {
    }

    class InputInterface {
    }

    class OutputInterface {
    }

    Command <|-- PrepareForReinstallCommand
    Command <|-- MediaGalleryDeleteAllCommand
    Command <|-- CacheClearCommand

    PrepareForReinstallCommand --> SpoonDatabase : uses
    PrepareForReinstallCommand --> KernelInterface : uses

    MediaGalleryDeleteAllCommand --> MediaGalleryRepository : uses
    MediaGalleryDeleteAllCommand --> CommandBus : uses
    MediaGalleryDeleteAllCommand --> DeleteMediaGallery : creates
    DeleteMediaGallery --> MediaGallery : wraps

    CacheClearCommand --> FileManager : uses
Loading

File-Level Changes

Change Details Files
Raise platform requirements to PHP 8.1 and Symfony 5.4 and update dependencies/infrastructure accordingly.
  • Increase composer php requirement to ^8.1 and switch from symfony/symfony to explicit Symfony 5.4 components (console, framework-bundle, twig-bundle, etc.).
  • Upgrade key packages (guzzle 7.x, sentry/sentry-symfony 5.x), remove unused flysystem AWS adapter, and relax composer minimum-stability to stable.
  • Add dom-crawler and browser-kit (for tests) and align dev dependencies (debug-bundle, web-profiler-bundle, var-dumper, phpunit-bridge) to Symfony 5.4.
  • Update GitHub Actions, GitLab CI images, PHPStan job, Scrutinizer, Dockerfile and Deployer config to use PHP 8.1 instead of 7.4/8.0, dropping coverage-only 7.4 jobs and old cachetool override.
  • Enhance composer config with allow-plugins for php-http/discovery and an audit block.
composer.json
.github/workflows/run-tests.yml
.gitlab-ci.yml
.scrutinizer.yml
Dockerfile
deploy.php
Adapt to Symfony 4.4/5.x API changes (kernel/browser, events, HTTP kernel events, debug/error handler) and modern Twig/profiler integration.
  • Replace deprecated Symfony Client in tests and WebTestCase helpers with KernelBrowser and require symfony/browser-kit and dom-crawler.
  • Migrate HttpKernel events from GetResponse*/FilterResponse*/GetResponseForControllerResultEvent and GetResponseEvent to RequestEvent, ResponseEvent, ViewEvent, and ExceptionEvent.
  • Switch event base class usage from Symfony EventDispatcher Event to symfony/contracts Event, and drop string event names when dispatching (rely on FQCN).
  • Update CKFinder integration to new event dispatch signatures, HttpKernel event types, and simplify dispatcher->dispatch calls.
  • Replace Symfony Debug with Symfony ErrorHandler Debug in front controller, and set framework.test: true in test config.
  • Switch WebProfiler/Twig template paths from bundle notation (WebProfilerBundle:...) to namespaced paths (@WebProfiler/...).
src/Common/WebTestCase.php
src/Backend/Core/Js/ckfinder/core/connector/php/CKSource/CKFinder/*.php
src/Backend/Core/Js/ckfinder/core/connector/php/CKSource/CKFinder/Event/CKFinderEvent.php
src/Backend/Core/Js/ckfinder/core/connector/php/CKSource/CKFinder/ExceptionHandler.php
src/Common/EventListener/ForkCookieSetter.php
src/Common/EventListener/ResponseSecurer.php
src/Common/Mailer/Configurator.php
src/Common/DataCollector/*.php
src/Common/Events/ForkSessionIdChangedEvent.php
src/Common/Tests/Mailer/ConfiguratorTest.php
app/Resources/views/database_data_collector.html.twig
app/Resources/views/page_context_data_collector.html.twig
index.php
app/config/config_test.yml
Refactor controllers, console commands, and services to use dependency injection and autowiring instead of ContainerAware/get().
  • Convert InstallerController to extend AbstractController, inject ForkInstaller and RequirementsChecker, access parameters via getParameter(), and update template paths to new locations.
  • Make PrepareForReinstallCommand, MediaGalleryDeleteAllCommand, and MediaLibrary CacheClearCommand extend Command, inject their dependencies (database, kernel, repositories, command bus, file manager), and return int status codes from execute().
  • Register new constructor arguments in installer and media services configuration (services.xml and console.yml) and autowire ForkCMS controllers (ForkController, InstallerController).
  • Update ForkController to inject KernelInterface via constructor and stop fetching kernel from the container.
src/ForkCMS/Bundle/InstallerBundle/Controller/InstallerController.php
src/ForkCMS/Bundle/InstallerBundle/Console/PrepareForReinstallCommand.php
src/Backend/Modules/MediaGalleries/Console/MediaGalleryDeleteAllCommand.php
src/Backend/Modules/MediaLibrary/Console/CacheClearCommand.php
src/ForkCMS/Bundle/InstallerBundle/Resources/config/services.xml
src/Backend/Modules/MediaGalleries/Resources/config/console.yml
src/Backend/Modules/MediaLibrary/Resources/config/console.yml
app/ForkController.php
Modernize Twig/templating integration and move installer & profiler templates to the standard templates/ directory.
  • Remove framework.templating configuration and associated templating services (name_parser, locator) in favor of Twig-only setup.
  • Change Common/Core/Twig/BaseTwigTemplate to hold a Twig Environment instead of extending TwigEngine, and adjust Backend/Frontend TwigTemplate classes to no longer depend on templating name parser or locator.
  • Register ForkCMS\App\ForkController and ForkCMS installer controller as public, autowired, autoconfigured services.
  • Relocate installer and profiler twig templates from bundle-style paths to templates/Installer and templates root, updating extends/imports/form_theme and includes accordingly.
app/config/config.yml
app/config/config_install.yml
src/Common/Core/Twig/BaseTwigTemplate.php
src/Backend/Core/Engine/TwigTemplate.php
src/Frontend/Core/Engine/TwigTemplate.php
app/Resources/views/database_data_collector.html.twig
app/Resources/views/page_context_data_collector.html.twig
src/ForkCMS/Bundle/InstallerBundle/Resources/views/Installer/*.html.twig
templates/Installer/*.html.twig
templates/page_context_data_collector.html.twig
templates/database_data_collector.html.twig
Update domain code to Symfony Intl, form and translation contracts, and add serialization helpers for locale objects.
  • Replace deprecated Symfony Intl facade usage (Intl::getRegionBundle()) with Symfony\Component\Intl\Countries for country name lookups in various backend/frontend actions and Geolocation.
  • Adjust DateTypeExtension to use getExtendedTypes() instead of getExtendedType() and switch TranslatorInterface imports in MetaType to Symfony Contracts.
  • Remove obsolete Language::transChoice helper now that pluralization is handled differently in newer Symfony versions.
  • Add __serialize/__unserialize wrappers in Backend and Frontend Locale classes to ensure proper serialization under PHP 8.1.
src/Frontend/Modules/Profiles/Actions/Settings.php
src/Backend/Modules/Location/Actions/Add.php
src/Backend/Modules/Location/Actions/Edit.php
src/Backend/Modules/Profiles/Actions/Add.php
src/Backend/Modules/Profiles/Actions/Edit.php
src/ForkCMS/Utility/Geolocation.php
src/Common/Form/Extension/DateTypeExtension.php
src/Backend/Form/Type/MetaType.php
src/Common/Language.php
src/Backend/Core/Language/Locale.php
src/Frontend/Core/Language/Locale.php
Improve authentication and session handling and adjust event usage when logging in/out.
  • Introduce a static cached flag in Backend Authentication::isLoggedIn() and stop storing a 'logged_in' flag in the DI container; rely on the new cache instead.
  • Update Backend Authentication::loginUser() and logout() and Frontend Profiles Authentication::login()/logout() to dispatch ForkSessionIdChangedEvent instances without a string event name, matching contracts-based dispatcher expectations.
  • Ensure some tests (e.g. Authentication tests, installer tests) explicitly reset or skip around flaky PHP 8.1 behaviors and kernel boot state.
src/Backend/Core/Engine/Authentication.php
src/Frontend/Modules/Profiles/Engine/Authentication.php
src/Common/Events/ForkSessionIdChangedEvent.php
src/Backend/Modules/Authentication/Tests/Actions/*.php
src/ForkCMS/Bundle/InstallerBundle/Tests/Controller/InstallerControllerTest.php
Refresh HTTP client/upload and MIME handling for Symfony 5.4 and PHP 8.1.
  • Switch media upload MIME-type detection from deprecated MimeTypeExtensionGuesser to Symfony\Component\Mime\MimeTypes and use UploadedFile::getSize() instead of getClientSize().
  • Adjust CKFinder upload and proxy code to match newer HttpFoundation expectations and event dispatch signatures.
src/Backend/Modules/MediaLibrary/Component/UploadHandler.php
src/Backend/Core/Js/ckfinder/core/connector/php/CKSource/CKFinder/Command/Proxy.php
Systematically migrate functional and unit tests from Client to KernelBrowser and address PHP 8.1-specific issues.
  • Change all test method signatures and imports to use Symfony\Bundle\FrameworkBundle\KernelBrowser instead of Client across backend and frontend tests.
  • Update BackendWebTestCase teardown and helpers to type-check KernelBrowser, and add ensureKernelShutdown()/boot flags where needed to stabilize tests.
  • Mark a few tests as skipped due to intermittent PHP preg_match warnings in PHPUnit on PHP 8.1 (e.g. Blog comment creation, installer controller).
  • Fix some assertions and expected payloads to match new types/HTML (e.g. string vs int IDs, self-closing tag spacing).
src/Backend/Core/Tests/BackendWebTestCase.php
src/Common/WebTestCase.php
src/Backend/Modules/*/Tests/**/*.php
src/Frontend/Core/Tests/**/*.php
src/ForkCMS/Bundle/InstallerBundle/Tests/Controller/InstallerControllerTest.php

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jonasdekeukelaere jonasdekeukelaere force-pushed the php85 branch 2 times, most recently from 7f2c880 to 2712205 Compare January 13, 2026 06:58
@jonasdekeukelaere jonasdekeukelaere force-pushed the php85 branch 4 times, most recently from ef761af to bd8bf09 Compare January 13, 2026 07:22
@jonasdekeukelaere jonasdekeukelaere marked this pull request as ready for review January 14, 2026 12:17
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Sorry, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 538

@jonasdekeukelaere jonasdekeukelaere merged commit 414c7a6 into master Jan 14, 2026
11 of 12 checks passed
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.

3 participants