How to deprecate classes, methods, arguments and hooks in the TYPO3 core

TYPO3 core development policy states that public API will not be changed without a grace period where extension authors can adapt to the changes. In that grace period a deprecation warning will be thrown. If you want to remove or change functionality in the TYPO3 core, it has to be deprecated first.

Here is how:

Deprecate a class

  • Add a @deprecated annotation to the class doc comment
  • Deprecate the constructor - see next section about deprecating a method

Deprecate method

  • Add a @deprecated annotation to the method doc comment
  • Add a trigger_error call to the method, describing the migration path and triggering an error of type E_USER_DEPRECATED
trigger_error(
   'Content with <link> syntax was found, update your content to use the t3:// syntax, and migrate your content via the upgrade wizard in the install tool',
   E_USER_DEPRECATED
);

Deprecate method arguments

If you want to deprecate method arguments, check for argument existence and trigger an error the same way you would for a method. If you want to deprecate and remove an unused argument, use func_get_args() or func_num_args() (see example below):

public function TS_AtagToAbs($value)
{
   if (func_num_args() > 1) {
      trigger_error('Second argument of TS_AtagToAbs() is not in use and is removed, however the argument in the callers code can be removed without side-effects.', E_USER_DEPRECATED);
   }
   // ...
}

Make class properties protected

To reach full encapsulation of classes, the public access to properties should be removed. The property access should be done by public methods only.

Public properties that should be protected can be migrated to protected or private and wrapped with getters/setters as needed.

During a phase of deprecation, entries into the deprecation log are triggered, if an extension accesses a previously public property. The code still keeps working until the next major release, when the deprecation and public access fallback are removed from the code.

To deprecate usage of a (still) public property that should be changed to protected in the future:

  1. Add the TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait trait to the class.
  2. Add the property $deprecatedPublicMethods to the class and list all properties that should no longer be accessed from outside of the class.
  3. Make the property private/protected.
+use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
+
class RecordHistory
{
+       use PublicPropertyDeprecationTrait;
+
+       /**
+         * @var string[]
+         */
+        private $deprecatedPublicProperties = [
+            'changeLog' => 'Using changeLog is deprecated and will not be possible anymore in TYPO3 v11.0. Use getChangeLog() instead.',
+       ];
+
        /**
         * @var array
         */
-       public $changeLog = [];
+       protected $changeLog = [];

Make class methods protected

This works in a similar way as Make class properties protected: The trait TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait allows to make public methods protected or private without breaking extensions.

This can be used in case the public methods may still be called by extensions. This will then trigger a deprecation.

In order to make a method private or protected:

  1. Add the TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait trait to the class.
  2. Add the property $deprecatedPublicMethods to the class and list all methods that should no longer be accessed from outside of the class.
  3. Make the method private/protected.
+use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
+
class PageRepository
{
+       use PublicMethodDeprecationTrait;
+
+       /**
+         * @var string[]
+         */
+        private $deprecatedPublicMethods = [
+            'init' => 'init() is now called implicitly on object creation, and is not necessary anymore to be called explicitly. Calling init() will throw an error in TYPO3 v10.0.',
+       ];
+

-       public function init($show_hidden)
+       protected function init($show_hidden)

Deprecate a hook

As hooks provide extension points throughout the core, deprecating and removing them should be carefully considered, but sometimes the placement of a hook doesn’t make sense anymore as the functionality around it changed or other hooks / mechanisms replaced it’s purpose. If you have to deprecate a hook, call trigger_error before the hook call:

if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc'])
   && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc'])) {
   trigger_error(
      'The hook "t3lib/class.t3lib_parsehtml_proc.php->modifyParams_LinksDb_PostProc" will be removed in TYPO3 v10, use LinkService syntax to modify links to be stored in the database.',
      E_USER_DEPRECATED
   );
   $parameters = [
      'currentBlock' => $v,
      'linkInformation' => $linkInformation,
      'url' => $linkInformation['href'],
      'attributes' => $tagAttributes
   ];
   foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc'] as $className) {
      $processor = GeneralUtility::makeInstance($className);
      $blockSplit[$k] = $processor->modifyParamsLinksDb($parameters, $this);
   }
}

Deprecate methods still called by the TYPO3 Core

If you want to deprecate a method that still has to be used by the core itself to provide the functionality for the time being, you can achieve that by adding a new method parameter that will prevent the deprecation warning message being triggered and add that parameter to the callees in the core.

Example:

/**
 * Transformation handler: 'ts_links' / direction: "rte"
 * Converting TYPO3-specific <link> tags to <a> tags
 *
 * This functionality is only used to convert legacy <link> tags to the new linking syntax using <a> tags, and will
 * not be converted back to <link> tags anymore.
 *
 * @param string $value Content input
 * @param bool $internallyCalledFromCore internal option for calls where the Core is still using this function, to supress method deprecations
 * @return string Content output
 * @deprecated will be removed in TYPO3 v10, only ->TS_AtagToAbs() should be called directly, <link> syntax is deprecated
 */
public function TS_links_rte($value, $internallyCalledFromCore = null)
{
   if ($internallyCalledFromCore === null) {
      trigger_error('This method will be removed in TYPO3 v10, use TS_AtagToAbs() directly and do not use <link> syntax anymore', E_USER_DEPRECATED);
   }
   $hasLinkTags = false;
   $value = $this->TS_AtagToAbs($value);
   // ...
}