mirror of
https://github.com/symfony/symfony-docs.git
synced 2026-03-24 00:32:14 +01:00
559 lines
32 KiB
ReStructuredText
559 lines
32 KiB
ReStructuredText
Our Backward Compatibility Promise
|
|
==================================
|
|
|
|
Ensuring smooth upgrades of your projects is our first priority. That's why
|
|
we promise you backward compatibility (BC) for all minor Symfony releases.
|
|
You probably recognize this strategy as `Semantic Versioning`_. In short,
|
|
Semantic Versioning means that only major releases (such as 5.0, 6.0 etc.) are
|
|
allowed to break backward compatibility. Minor releases (such as 5.1, 5.2 etc.)
|
|
may introduce new features, but must do so without breaking the existing API of
|
|
that release branch (5.x in the previous example).
|
|
|
|
We also provide deprecation message triggered in the code base to help you with
|
|
the migration process across major releases.
|
|
|
|
However, backward compatibility comes in many different flavors. In fact, almost
|
|
every change that we make to the framework can potentially break an application.
|
|
For example, if we add a new method to a class, this will break an application
|
|
which extended this class and added the same method, but with a different
|
|
method signature.
|
|
|
|
Also, not every BC break has the same impact on application code. While some BC
|
|
breaks require you to make significant changes to your classes or your
|
|
architecture, others are fixed by changing the name of a method.
|
|
|
|
That's why we created this page for you. The section "Using Symfony Code" will
|
|
tell you how you can ensure that your application won't break completely when
|
|
upgrading to a newer version of the same major release branch.
|
|
|
|
The second section, "Working on Symfony Code", is targeted at Symfony
|
|
contributors. This section lists detailed rules that every contributor needs to
|
|
follow to ensure smooth upgrades for our users.
|
|
|
|
.. warning::
|
|
|
|
:doc:`Experimental Features </contributing/code/experimental>` and code
|
|
marked with the ``@internal`` tags are excluded from our Backward
|
|
Compatibility promise.
|
|
|
|
Also note that backward compatibility breaks are tolerated if they are
|
|
required to fix a security issue.
|
|
|
|
Using Symfony Code
|
|
------------------
|
|
|
|
If you are using Symfony in your projects, the following guidelines will help
|
|
you to ensure smooth upgrades to all future minor releases of your Symfony
|
|
version.
|
|
|
|
Using our Interfaces
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
All interfaces shipped with Symfony can be used in type hints. You can also call
|
|
any of the methods that they declare. We guarantee that we won't break code that
|
|
sticks to these rules.
|
|
|
|
.. warning::
|
|
|
|
The exception to this rule are interfaces tagged with ``@internal``. Such
|
|
interfaces should not be used or implemented.
|
|
|
|
If you implement an interface, we promise that we won't ever break your code.
|
|
|
|
The following table explains in detail which use cases are covered by our
|
|
backward compatibility promise:
|
|
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use Case | Backward Compatibility |
|
|
+===============================================+=============================+
|
|
| **If you...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Type hint against the interface | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Call a method | Yes :ref:`[10] <note-10>` |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| **If you implement the interface and...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Implement a method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add an argument to an implemented method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add a default value to an argument | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add a return type to an implemented method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
|
|
Using our Classes
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
All classes provided by Symfony may be instantiated and accessed through their
|
|
public methods and properties.
|
|
|
|
.. warning::
|
|
|
|
Classes, properties and methods that bear the tag ``@internal`` as well as
|
|
the classes located in the various ``*\Tests\`` namespaces are an
|
|
exception to this rule. They are meant for internal use only and should
|
|
not be accessed by your own code.
|
|
|
|
To be on the safe side, check the following table to know which use cases are
|
|
covered by our backward compatibility promise:
|
|
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use Case | Backward Compatibility |
|
|
+===============================================+=============================+
|
|
| **If you...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Type hint against the class | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Create a new instance | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Extend the class | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Access a public property | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Call a public method | Yes :ref:`[10] <note-10>` |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| **If you extend the class and...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Access a protected property | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Call a protected method | Yes :ref:`[10] <note-10>` |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Override a public property | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Override a protected property | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Override a public method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Override a protected method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add a new property | No |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add a new method | No |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add an argument to an overridden method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Add a default value to an argument | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Call a private method (via Reflection) | No |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Access a private property (via Reflection) | No |
|
|
+-----------------------------------------------+-----------------------------+
|
|
|
|
Using our Traits
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
All traits provided by Symfony may be used in your classes.
|
|
|
|
.. warning::
|
|
|
|
The exception to this rule are traits tagged with ``@internal``. Such
|
|
traits should not be used.
|
|
|
|
To be on the safe side, check the following table to know which use cases are
|
|
covered by our backward compatibility promise:
|
|
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use Case | Backward Compatibility |
|
|
+===============================================+=============================+
|
|
| **If you...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use a trait | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| **If you use the trait and...** | **Then we guarantee BC...** |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use it to implement an interface | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use it to implement an abstract method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use it to extend a parent class | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use it to define an abstract class | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use a public, protected or private property | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
| Use a public, protected or private method | Yes |
|
|
+-----------------------------------------------+-----------------------------+
|
|
|
|
Using our Translations
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
All translations provided by Symfony for security and validation errors are
|
|
intended for internal use only. They may be changed or removed at any time.
|
|
Symfony's Backward Compatibility Promise does not apply to internal translations.
|
|
|
|
Working on Symfony Code
|
|
-----------------------
|
|
|
|
Do you want to help us improve Symfony? That's great! However, please stick
|
|
to the rules listed below in order to ensure smooth upgrades for our users.
|
|
|
|
Changing Interfaces
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
This table tells you which changes you are allowed to do when working on
|
|
Symfony's interfaces:
|
|
|
|
============================================== ============== ===============
|
|
Type of Change Change Allowed Notes
|
|
============================================== ============== ===============
|
|
Remove entirely No
|
|
Change name or namespace No
|
|
Add parent interface Yes :ref:`[2] <note-2>`
|
|
Remove parent interface No
|
|
**Methods**
|
|
Add method No
|
|
Remove method No
|
|
Change name No
|
|
Move to parent interface Yes
|
|
Add argument without a default value No
|
|
Add argument with a default value No
|
|
Remove argument No :ref:`[3] <note-3>`
|
|
Add default value to an argument No
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No
|
|
Remove type hint of an argument No
|
|
Change argument type No
|
|
Add return type No
|
|
Remove return type No :ref:`[9] <note-9>`
|
|
Change return type No
|
|
**Static Methods**
|
|
Turn non static into static No
|
|
Turn static into non static No
|
|
**Constants**
|
|
Add constant Yes
|
|
Remove constant No
|
|
Change value of a constant Yes :ref:`[1] <note-1>` :ref:`[5] <note-5>`
|
|
============================================== ============== ===============
|
|
|
|
Changing Classes
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
This table tells you which changes you are allowed to do when working on
|
|
Symfony's classes:
|
|
|
|
======================================================================== ============== ===============
|
|
Type of Change Change Allowed Notes
|
|
======================================================================== ============== ===============
|
|
Remove entirely No
|
|
Make final No :ref:`[6] <note-6>`
|
|
Make abstract No
|
|
Change name or namespace No
|
|
Change parent class Yes :ref:`[4] <note-4>`
|
|
Add interface Yes
|
|
Remove interface No
|
|
**Public Properties**
|
|
Add public property Yes
|
|
Remove public property No
|
|
Reduce visibility No
|
|
Move to parent class Yes
|
|
**Protected Properties**
|
|
Add protected property Yes
|
|
Remove protected property No :ref:`[7] <note-7>`
|
|
Reduce visibility No :ref:`[7] <note-7>`
|
|
Make public No :ref:`[7] <note-7>`
|
|
Move to parent class Yes
|
|
**Private Properties**
|
|
Add private property Yes
|
|
Make public or protected Yes
|
|
Remove private property Yes
|
|
**Constructors**
|
|
Add constructor without mandatory arguments Yes :ref:`[1] <note-1>`
|
|
:ref:`Add argument without a default value <add-argument-public-method>` No
|
|
Add argument with a default value Yes :ref:`[11] <note-11>`
|
|
Remove argument No :ref:`[3] <note-3>`
|
|
Add default value to an argument Yes
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No
|
|
Remove type hint of an argument Yes
|
|
Change argument type No
|
|
Remove constructor No
|
|
Reduce visibility of a public constructor No
|
|
Reduce visibility of a protected constructor No :ref:`[7] <note-7>`
|
|
Move to parent class Yes
|
|
**Destructors**
|
|
Add destructor Yes
|
|
Remove destructor No
|
|
Move to parent class Yes
|
|
**Public Methods**
|
|
Add public method Yes
|
|
Remove public method No
|
|
Change name No
|
|
Reduce visibility No
|
|
Make final No :ref:`[6] <note-6>`
|
|
Move to parent class Yes
|
|
:ref:`Add argument without a default value <add-argument-public-method>` No
|
|
:ref:`Add argument with a default value <add-argument-public-method>` No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Rename argument Yes :ref:`[10] <note-10>`
|
|
Remove argument No :ref:`[3] <note-3>`
|
|
Add default value to an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove type hint of an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Change argument type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Add return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>` :ref:`[9] <note-9>`
|
|
Change return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
**Protected Methods**
|
|
Add protected method Yes
|
|
Remove protected method No :ref:`[7] <note-7>`
|
|
Change name No :ref:`[7] <note-7>`
|
|
Reduce visibility No :ref:`[7] <note-7>`
|
|
Make final No :ref:`[6] <note-6>`
|
|
Make public No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Move to parent class Yes
|
|
:ref:`Add argument without a default value <add-argument-public-method>` No
|
|
:ref:`Add argument with a default value <add-argument-public-method>` No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Rename argument Yes :ref:`[10] <note-10>`
|
|
Remove argument No :ref:`[3] <note-3>`
|
|
Add default value to an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove default value of an argument No :ref:`[7] <note-7>`
|
|
Add type hint to an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove type hint of an argument No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Change argument type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Add return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Remove return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>` :ref:`[9] <note-9>`
|
|
Change return type No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
**Private Methods**
|
|
Add private method Yes
|
|
Remove private method Yes
|
|
Change name Yes
|
|
Make public or protected Yes
|
|
Add argument without a default value Yes
|
|
Add argument with a default value Yes
|
|
Rename argument Yes
|
|
Remove argument Yes
|
|
Add default value to an argument Yes
|
|
Remove default value of an argument Yes
|
|
Add type hint to an argument Yes
|
|
Remove type hint of an argument Yes
|
|
Change argument type Yes
|
|
Add return type Yes
|
|
Remove return type Yes
|
|
Change return type Yes
|
|
**Static Methods and Properties**
|
|
Turn non static into static No :ref:`[7] <note-7>` :ref:`[8] <note-8>`
|
|
Turn static into non static No
|
|
**Constants**
|
|
Add constant Yes
|
|
Remove constant No
|
|
Change value of a constant Yes :ref:`[1] <note-1>` :ref:`[5] <note-5>`
|
|
======================================================================== ============== ===============
|
|
|
|
Changing Traits
|
|
~~~~~~~~~~~~~~~
|
|
|
|
This table tells you which changes you are allowed to do when working on
|
|
Symfony's traits:
|
|
|
|
=============================================================================== ============== ===============
|
|
Type of Change Change Allowed Notes
|
|
=============================================================================== ============== ===============
|
|
Remove entirely No
|
|
Change name or namespace No
|
|
Use another trait Yes
|
|
**Public Properties**
|
|
Add public property Yes
|
|
Remove public property No
|
|
Reduce visibility No
|
|
Move to a used trait Yes
|
|
**Protected Properties**
|
|
Add protected property Yes
|
|
Remove protected property No
|
|
Reduce visibility No
|
|
Make public No
|
|
Move to a used trait Yes
|
|
**Private Properties**
|
|
Add private property Yes
|
|
Remove private property No
|
|
Make public or protected Yes
|
|
Move to a used trait Yes
|
|
**Constructors and destructors**
|
|
Have constructor or destructor No
|
|
**Public Methods**
|
|
Add public method Yes
|
|
Remove public method No
|
|
Change name No
|
|
Reduce visibility No
|
|
Make final No :ref:`[6] <note-6>`
|
|
Move to used trait Yes
|
|
:ref:`Add argument without a default value <add-argument-public-method>` No
|
|
:ref:`Add argument with a default value <add-argument-public-method>` No
|
|
Remove argument No
|
|
Add default value to an argument No
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No
|
|
Remove type hint of an argument No
|
|
Change argument type No
|
|
Change return type No
|
|
**Protected Methods**
|
|
Add protected method Yes
|
|
Remove protected method No
|
|
Change name No
|
|
Reduce visibility No
|
|
Make final No :ref:`[6] <note-6>`
|
|
Make public No :ref:`[8] <note-8>`
|
|
Move to used trait Yes
|
|
:ref:`Add argument without a default value <add-argument-public-method>` No
|
|
:ref:`Add argument with a default value <add-argument-public-method>` No
|
|
Remove argument No
|
|
Add default value to an argument No
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No
|
|
Remove type hint of an argument No
|
|
Change argument type No
|
|
Change return type No
|
|
**Private Methods**
|
|
Add private method Yes
|
|
Remove private method No
|
|
Change name No
|
|
Make public or protected Yes
|
|
Move to used trait Yes
|
|
Add argument without a default value No
|
|
Add argument with a default value No
|
|
Remove argument No
|
|
Add default value to an argument No
|
|
Remove default value of an argument No
|
|
Add type hint to an argument No
|
|
Remove type hint of an argument No
|
|
Change argument type No
|
|
Add return type No
|
|
Remove return type No
|
|
Change return type No
|
|
**Static Methods and Properties**
|
|
Turn non static into static No
|
|
Turn static into non static No
|
|
=============================================================================== ============== ===============
|
|
|
|
Notes
|
|
~~~~~
|
|
|
|
.. _note-1:
|
|
|
|
**[1]** Should be avoided. When done, this change must be documented in the
|
|
UPGRADE file.
|
|
|
|
.. _note-2:
|
|
|
|
**[2]** The added parent interface must not introduce any new methods that don't
|
|
exist in the interface already.
|
|
|
|
.. _note-3:
|
|
|
|
**[3]** Only the last optional argument(s) of a method may be removed, as PHP
|
|
does not care about additional arguments that you pass to a method.
|
|
|
|
.. _note-4:
|
|
|
|
**[4]** When changing the parent class, the original parent class must remain an
|
|
ancestor of the class.
|
|
|
|
.. _note-5:
|
|
|
|
**[5]** The value of a constant may only be changed when the constants aren't
|
|
used in configuration (e.g. Yaml and XML files), as these do not support
|
|
constants and have to hardcode the value. For instance, event name constants
|
|
can't change the value without introducing a BC break. Additionally, if a
|
|
constant will likely be used in objects that are serialized, the value of a
|
|
constant should not be changed.
|
|
|
|
.. _note-6:
|
|
|
|
**[6]** Allowed using the ``@final`` annotation.
|
|
|
|
.. _note-7:
|
|
|
|
**[7]** Allowed if the class is final. Classes that received the ``@final``
|
|
annotation after their first release are considered final in their next major
|
|
version. Changing an argument type is only possible with a parent type. Changing
|
|
a return type is only possible with a child type.
|
|
|
|
.. _note-8:
|
|
|
|
**[8]** Allowed if the method is final. Methods that received the ``@final``
|
|
annotation after their first release are considered final in their next major
|
|
version. Changing an argument type is only possible with a parent type. Changing
|
|
a return type is only possible with a child type.
|
|
|
|
.. _note-9:
|
|
|
|
**[9]** Allowed for the ``void`` return type.
|
|
|
|
.. _note-10:
|
|
|
|
**[10]** Parameter names are only covered by the compatibility promise for
|
|
constructors of Attribute classes. Using PHP named arguments might break your
|
|
code when upgrading to newer Symfony versions.
|
|
|
|
.. _note-11:
|
|
|
|
**[11]** Only optional argument(s) of a constructor at last position may be added.
|
|
|
|
Making Code Changes in a Backward Compatible Way
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
As you read above, many changes are not allowed because they would represent a
|
|
backward compatibility break. However, we want to be able to improve the code and
|
|
its features over time and that can be done thanks to some strategies that
|
|
allow you to still do some unallowed changes in several steps that ensure backward
|
|
compatibility and a smooth upgrade path. Some of them are described in the next
|
|
sections.
|
|
|
|
.. _add-argument-public-method:
|
|
|
|
Adding an Argument to a Public Method
|
|
.....................................
|
|
|
|
Adding a new argument to a public method is possible only if this is the last
|
|
argument of the method.
|
|
|
|
If that's the case, here is how to do it properly in a minor version:
|
|
|
|
#. Add the argument as a comment in the signature::
|
|
|
|
// the new argument can be optional
|
|
public function say(string $text, /* bool $stripWhitespace = true */): void
|
|
{
|
|
}
|
|
|
|
// or required
|
|
public function say(string $text, /* bool $stripWhitespace */): void
|
|
{
|
|
}
|
|
|
|
#. Document the new argument in a PHPDoc::
|
|
|
|
/**
|
|
* @param bool $stripWhitespace
|
|
*/
|
|
|
|
#. Use ``func_num_args`` and ``func_get_arg`` to retrieve the argument in the
|
|
method::
|
|
|
|
$stripWhitespace = 2 <= \func_num_args() ? func_get_arg(1) : false;
|
|
|
|
Note that the default value is ``false`` to keep the current behavior.
|
|
|
|
#. If the argument has a default value that will change the current behavior,
|
|
warn the user::
|
|
|
|
trigger_deprecation('symfony/COMPONENT', 'X.Y', 'Not passing the "bool $stripWhitespace" argument explicitly is deprecated, its default value will change to X in Z.0.');
|
|
|
|
#. If the argument has no default value, warn the user that is going to be
|
|
required in the next major version::
|
|
|
|
if (\func_num_args() < 2) {
|
|
trigger_deprecation('symfony/COMPONENT', 'X.Y', 'The "%s()" method will have a new "bool $stripWhitespace" argument in version Z.0, not defining it is deprecated.', __METHOD__);
|
|
|
|
$stripWhitespace = false;
|
|
} else {
|
|
$stripWhitespace = func_get_arg(1);
|
|
}
|
|
|
|
#. In the next major version (``X.0``), uncomment the argument, remove the
|
|
PHPDoc if there is no need for a description, and remove the
|
|
``func_get_arg`` code and the warning if any.
|
|
|
|
.. _`Semantic Versioning`: https://semver.org/
|