发布于 2015-08-27 16:41:38 | 168 次阅读 | 评论: 0 | 来源: 网络整理
Ensuring smooth upgrades of your projects is our first priority. That’s why we promise you backwards 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 2.0, 3.0 etc.) are allowed to break backwards compatibility. Minor releases (such as 2.5, 2.6 etc.) may introduce new features, but must do so without breaking the existing API of that release branch (2.x in the previous example).
警告
This promise was introduced with Symfony 2.3 and does not apply to previous versions of Symfony.
However, backwards 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 as easily as 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.
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.
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.
警告
The exception to this rule are interfaces tagged with @internal
. Such
interfaces should not be used or implemented.
If you want to implement an interface, you should first make sure that the
interface is an API interface. You can recognize API interfaces by the @api
tag in their source code:
/**
* HttpKernelInterface handles a Request to convert it to a Response.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
interface HttpKernelInterface
{
// ...
}
If you implement an API interface, we promise that we won’t ever break your code. Regular interfaces, by contrast, may be extended between minor releases, for example by adding a new method. Be prepared to upgrade your code manually if you implement a regular interface.
注解
Even if we do changes that require manual upgrades, we limit ourselves to changes that can be upgraded easily. We will always document the precise upgrade instructions in the UPGRADE file in Symfony’s root directory.
The following table explains in detail which use cases are covered by our backwards compatibility promise:
Use Case | Regular | API |
---|---|---|
If you... | Then we guarantee BC... | |
Type hint against the interface | Yes | Yes |
Call a method | Yes | Yes |
If you implement the interface and... | Then we guarantee BC... | |
Implement a method | No [1] | Yes |
Add an argument to an implemented method | No [1] | Yes |
Add a default value to an argument | Yes | Yes |
注解
If you think that one of our regular classes should have an @api
tag,
put your request into a new ticket on GitHub. We will then evaluate
whether we can add the tag or not.
All classes provided by Symfony may be instantiated and accessed through their public methods and properties.
警告
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.
Just like with interfaces, we also distinguish between regular and API classes.
Like API interfaces, API classes are marked with an @api
tag:
/**
* Request represents an HTTP request.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @api
*/
class Request
{
// ...
}
The difference between regular and API classes is that we guarantee full backwards compatibility if you extend an API class and override its methods. We can’t give the same promise for regular classes, because there we may, for example, add an optional argument to a method. Consequently, the signature of your overridden method wouldn’t match anymore and generate a fatal error.
注解
As with interfaces, we limit ourselves to changes that can be upgraded easily. We will document the precise upgrade instructions in the UPGRADE file in Symfony’s root directory.
In some cases, only specific properties and methods are tagged with the @api
tag, even though their class is not. In these cases, we guarantee full backwards
compatibility for the tagged properties and methods (as indicated in the column
“API” below), but not for the rest of the class.
To be on the safe side, check the following table to know which use cases are covered by our backwards compatibility promise:
Use Case | Regular | API |
---|---|---|
If you... | Then we guarantee BC... | |
Type hint against the class | Yes | Yes |
Create a new instance | Yes | Yes |
Extend the class | Yes | Yes |
Access a public property | Yes | Yes |
Call a public method | Yes | Yes |
If you extend the class and... | Then we guarantee BC... | |
Access a protected property | No [1] | Yes |
Call a protected method | No [1] | Yes |
Override a public property | Yes | Yes |
Override a protected property | No [1] | Yes |
Override a public method | No [1] | Yes |
Override a protected method | No [1] | Yes |
Add a new property | No | No |
Add a new method | No | No |
Add an argument to an overridden method | No [1] | Yes |
Add a default value to an argument | Yes | Yes |
Call a private method (via Reflection) | No | No |
Access a private property (via Reflection) | No | No |
注解
If you think that one of our regular classes should have an @api
tag,
put your request into a new ticket on GitHub. We will then evaluate
whether we can add the tag or not.
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.
This table tells you which changes you are allowed to do when working on Symfony’s interfaces:
Type of Change | Regular | API |
---|---|---|
Remove entirely | No | No |
Change name or namespace | No | No |
Add parent interface | Yes [2] | Yes [3] |
Remove parent interface | No | No |
Methods | ||
Add method | Yes [2] | No |
Remove method | No | No |
Change name | No | No |
Move to parent interface | Yes | Yes |
Add argument without a default value | No | No |
Add argument with a default value | Yes [2] | No |
Remove argument | Yes [4] | Yes [4] |
Add default value to an argument | Yes [2] | No |
Remove default value of an argument | No | No |
Add type hint to an argument | No | No |
Remove type hint of an argument | Yes [2] | No |
Change argument type | Yes [2] [5] | No |
Change return type | Yes [2] [6] | No |
This table tells you which changes you are allowed to do when working on Symfony’s classes:
Type of Change | Regular | API |
---|---|---|
Remove entirely | No | No |
Make final | No | No |
Make abstract | No | No |
Change name or namespace | No | No |
Change parent class | Yes [7] | Yes [7] |
Add interface | Yes | Yes |
Remove interface | No | No |
Public Properties | ||
Add public property | Yes | Yes |
Remove public property | No | No |
Reduce visibility | No | No |
Move to parent class | Yes | Yes |
Protected Properties | ||
Add protected property | Yes | Yes |
Remove protected property | Yes [2] | No |
Reduce visibility | Yes [2] | No |
Move to parent class | Yes | Yes |
Private Properties | ||
Add private property | Yes | Yes |
Remove private property | Yes | Yes |
Constructors | ||
Add constructor without mandatory arguments | Yes [2] | Yes [2] |
Remove constructor | Yes [2] | No |
Reduce visibility of a public constructor | No | No |
Reduce visibility of a protected constructor | Yes [2] | No |
Move to parent class | Yes | Yes |
Public Methods | ||
Add public method | Yes | Yes |
Remove public method | No | No |
Change name | No | No |
Reduce visibility | No | No |
Move to parent class | Yes | Yes |
Add argument without a default value | No | No |
Add argument with a default value | Yes [2] | No |
Remove argument | Yes [4] | Yes [4] |
Add default value to an argument | Yes [2] | No |
Remove default value of an argument | No | No |
Add type hint to an argument | Yes [8] | No |
Remove type hint of an argument | Yes [2] | No |
Change argument type | Yes [2] [5] | No |
Change return type | Yes [2] [6] | No |
Protected Methods | ||
Add protected method | Yes | Yes |
Remove protected method | Yes [2] | No |
Change name | No | No |
Reduce visibility | Yes [2] | No |
Move to parent class | Yes | Yes |
Add argument without a default value | Yes [2] | No |
Add argument with a default value | Yes [2] | No |
Remove argument | Yes [4] | Yes [4] |
Add default value to an argument | Yes [2] | No |
Remove default value of an argument | Yes [2] | No |
Add type hint to an argument | Yes [2] | No |
Remove type hint of an argument | Yes [2] | No |
Change argument type | Yes [2] [5] | No |
Change return type | Yes [2] [6] | No |
Private Methods | ||
Add private method | Yes | Yes |
Remove private method | Yes | Yes |
Change name | Yes | Yes |
Reduce visibility | Yes | Yes |
Add argument without a default value | Yes | Yes |
Add argument with a default value | Yes | Yes |
Remove argument | Yes | Yes |
Add default value to an argument | Yes | Yes |
Remove default value of an argument | Yes | Yes |
Add type hint to an argument | Yes | Yes |
Remove type hint of an argument | Yes | Yes |
Change argument type | Yes | Yes |
Change return type | Yes | Yes |
Static Methods | ||
Turn non static into static | No | No |
Turn static into non static | No | No |
[1] | (1, 2, 3, 4, 5, 6, 7, 8) Your code may be broken by changes in the Symfony code. Such changes will however be documented in the UPGRADE file. |
[2] | (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28) Should be avoided. When done, this change must be documented in the UPGRADE file. |
[3] | The added parent interface must not introduce any new methods that don’t exist in the interface already. |
[4] | (1, 2, 3, 4, 5, 6) Only the last argument(s) of a method may be removed, as PHP does not care about additional arguments that you pass to a method. |
[5] | (1, 2, 3) The argument type may only be changed to a compatible or less specific type. The following type changes are allowed:
|
[6] | (1, 2, 3) The return type may only be changed to a compatible or more specific type. The following type changes are allowed:
|
[7] | (1, 2) When changing the parent class, the original parent class must remain an ancestor of the class. |
[8] | A type hint may only be added if passing a value with a different type previously generated a fatal error. |