Applying Core patches

At some point you may be required to apply changes to TYPO3's core. For example you may be testing a colleague's feature or working on a patch of your own.

Never change the code found in the Core directly. This includes all files in typo3/sysext and vendor.

Any manual changes you make to TYPO3's Core will be overwritten as soon as the Core is updated.

Changes that need to be applied to the Core should be stored in *.diff files and reapplied after each update.

Automatic patch application with cweagans/composer-patches

Note

Automatic application of patches with this method only works with Composer.

To automatically apply patches first install cweagans/composer-patches:

composer req cweagans/composer-patches

Choose a folder to store all patches in. This folder should ideally be outside of the webroot. Here we use the folder patches on the same level as the project's main composer.json. Each patch can be applied to exactly one composer package. The paths used in the patch must be relative to the packages path.

Edit your project's main composer.json. Add a section patches within the section extra. If there is no section extra yet, add one.

The patch itself looks like this:

patches/Bug-98106.diff (Simplified)
diff --git a/Classes/Utility/GeneralUtility.php b/Classes/Utility/GeneralUtility.php
index be47cfe..08fd6fc 100644
--- a/Classes/Utility/GeneralUtility.php
+++ b/Classes/Utility/GeneralUtility.php
@@ -2282,17 +2282,24 @@
      */
     public static function createVersionNumberedFilename($file)
     {
+        $isFrontend = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
+            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend();
         $lookupFile = explode('?', $file);
         $path = $lookupFile[0];

-        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
-            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
-        ) {
+        if ($isFrontend) {
             $mode = strtolower($GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']);
             if ($mode === 'embed') {
                 $mode = true;

Apply the patch by running the following command:

composer install

Note

Changes to the patch file after the patch was applied successfully will not be automatically applied. In that case delete the installed sources and execute composer install once more.

If applying the patch fails, you may get a cryptic error message like:

You can get a more verbose error message by calling:

Very, very verbose error message
composer install -vvv

Creating a diff from a Core change

You can choose between two methods:

Apply a core patch manually

In case a new Core version has not been released yet, but you urgently need to apply a certain patch, you can download that patch from the corresponding change on https://review.typo3.org/.

Choose Download patch from the option menu (3 dots on top of each other):

Download patch

Download patch in the option menu

Then choose your preferred format from the section Patch file.

../_images/ReviewDownloadDiff.png

Download Patch file

Unzip the diff file and put it into the folder patches of your project.

Core diff files are by default relative to the typo3 web-dir directory. And they can contain changes to more than one system extension. Furthermore they often contain changes to files in the directory Tests that is not present in a Composer based installation.

When you plan to apply the diff by Automatic patch application with cweagans/composer-patches you will need to manually adjust the patch file:

Remove all changes to the directory Tests and other files or directories that are not present in your installation's source. Change all paths to be relative to the path of the extension that should be changed. If more then one extension needs to be changed split up the patch in several parts, one for each system extension.

For example the following patch contains links relative to the web root and contains a test:

patches/ChangeFromCore.diff (Simplified)
diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
index be47cfe..08fd6fc 100644
--- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php
+++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php
@@ -2282,17 +2282,24 @@
      */
     public static function createVersionNumberedFilename($file)
     {
+        $isFrontend = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
+            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend();
         $lookupFile = explode('?', $file);
         $path = $lookupFile[0];
-        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
-            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
-        ) {
+        if ($isFrontend) {
             $mode = strtolower($GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']);
             if ($mode === 'embed') {
                 $mode = true;
diff --git a/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
index 68e356e..0ef4b80 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
@@ -4099,4 +4102,42 @@

         self::assertMatchesRegularExpression('/^.*\/tests\/' . $uniqueFilename . '\.[0-9]+\.css/', $versionedFilename);
     }
+
+    /**
+     * @test
+     */
+    public function createVersionNumberedFilenameKeepsInvalidAbsolutePathInFrontendAndAddsQueryString(): void
+    {
+        doSomething();
+    }

Remove the tests and adjust the paths to be relative to the system extension Core:

patches/Bug-98106.diff (Simplified)
diff --git a/Classes/Utility/GeneralUtility.php b/Classes/Utility/GeneralUtility.php
index be47cfe..08fd6fc 100644
--- a/Classes/Utility/GeneralUtility.php
+++ b/Classes/Utility/GeneralUtility.php
@@ -2282,17 +2282,24 @@
      */
     public static function createVersionNumberedFilename($file)
     {
+        $isFrontend = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
+            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend();
         $lookupFile = explode('?', $file);
         $path = $lookupFile[0];

-        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
-            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
-        ) {
+        if ($isFrontend) {
             $mode = strtolower($GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename']);
             if ($mode === 'embed') {
                 $mode = true;

Apply a core patch automatically via gilbertsoft/typo3-core-patches

With the help of the Composer package gilbertsoft/typo3-core-patches a Core patch can be applied automatically. It works on top of cweagans/composer-patches. You need at least PHP 7.4 and composer 2.0.

First, install the package:

composer req gilbertsoft/typo3-core-patches

Then look up the change ID on review.typo3.org <https://review.typo3.org/>. You can find it in the URL or left of the title of the change. In the example it's 75368.

Look up the change id

Look up the change id

Now execute the following command with your change ID:

composer typo3:patch:apply <change-id>

You can find more information about the package and its usage in the documentation.