BackwardCompatibilityCheck icon indicating copy to clipboard operation
BackwardCompatibilityCheck copied to clipboard

Adding a public method on a non-final class is to be considered a BC break

Open Ocramius opened this issue 7 years ago • 10 comments

Take following example:

class Counter
{
    private $count = 0;
    public function increment() : void {
        $this->count += 1;
    }
    public function count() : int {
        return $this->count;
    }
}

class LoggingCounter extends Counter
{
    private $logger;
    private $originalCounter;
    public function __construct(Logger $logger, Counter $originalCounter)
    {
         $this->logger = $logger;
         $this->originalCounter = $originalCounter;
    }
    public function increment() : void {
        $this->logger->log('increment');
        $this->originalCounter->increment();
    }
}

If Counter is modified to add an increment2 method, LoggingCounter fails (this is partly described in my article "When to declare Classes Final" ).

class Counter
{
    private $count = 0;
    public function increment() : void {
        $this->count += 1;
    }
    public function increment2() : void {
        $this->count += 2;
    }
    public function count() : int {
        return $this->count;
    }
}

Here's a test to show that:

class LoggingCounterTest extends TestCase
{
    public function incrementAndIncrement2() : void
    {
        $counter = new LoggingCounter($this->createMock(Logger::class), new Counter());
        
        $counter->increment();
        $counter->increment2();

        self::assertSame(3, $counter->count());
    }
}

This is because decorators require an update for each parent class public API change. Therefore, we should mark any addition of methods to an open class as a BC break.

Ocramius avatar Oct 21 '18 21:10 Ocramius

It's technically correct, but IMO way too pedantic. 🤔

Majkl578 avatar Oct 21 '18 22:10 Majkl578

Still gonna add it to the default set with a new major: I'll tackle https://github.com/Roave/BackwardCompatibilityCheck/issues/52 as well.

It is true that these are pedantic, but I'm really just trying to categorise the BC breaks here, not saying this will fit for everyone :+1:

Ocramius avatar Oct 21 '18 22:10 Ocramius

Hmm, previous comment got lost due to timeouts.

Wanted to clarify that I'm just categorising and enabling BC breaks as they are discovered/remembered (this one was long known, just forgotten), even if they are extremely strict, so this will appear in the defaults for the next major.

I'll try to tackle https://github.com/Roave/BackwardCompatibilityCheck/issues/52 before implementing this.

Ocramius avatar Oct 21 '18 23:10 Ocramius

It's technically correct, but IMO way too pedantic. thinking

In case you need a real world example that crashed a lot of things: https://github.com/yiisoft/yii2/pull/14441#issuecomment-315127527 ;)

cebe avatar Oct 24 '18 14:10 cebe

We also have one in Doctrine: EntityRepository::count() with different signature than the one that comes from Countable, broke a lot of stuff in userland for people who implemented Countable (which doesn't make much sense, but anyway).

Majkl578 avatar Oct 24 '18 14:10 Majkl578

as @Majkl578 said, adding bc breaks in this case is correct but a bit pedantic. What abound introducing "optional" rules where the user can opt-in? (based on their BC promise)

goetas avatar Nov 16 '18 11:11 goetas

@goetas the library will provide a mathematical set of what is right/wrong, and release a new major version each time a new rule is added to that set: configuring a subset of BC breaks will be up to consumers (see #52)

Ocramius avatar Nov 16 '18 11:11 Ocramius

(see #52)

great

goetas avatar Nov 16 '18 11:11 goetas

Moving out of 3.0.0: won't get to it for now.

Ocramius avatar Apr 23 '19 12:04 Ocramius

Not going to be able to drag this into 6.0 - removing milestone for now.

Ocramius avatar Nov 05 '21 17:11 Ocramius