Browse Source

Merge pull request #17703 from cakephp/fix-removebehavior-17697

Fix removeBehavior not clearing state
Mark Story 1 year ago
parent
commit
d1ac2aa511

+ 25 - 0
src/ORM/BehaviorRegistry.php

@@ -212,6 +212,31 @@ class BehaviorRegistry extends ObjectRegistry implements EventDispatcherInterfac
     }
 
     /**
+     * Remove an object from the registry.
+     *
+     * If this registry has an event manager, the object will be detached from any events as well.
+     *
+     * @param string $name The name of the object to remove from the registry.
+     * @return $this
+     */
+    public function unload(string $name)
+    {
+        $instance = $this->get($name);
+        $result = parent::unload($name);
+
+        $methods = $instance->implementedMethods();
+        foreach ($methods as $method) {
+            unset($this->_methodMap[$method]);
+        }
+        $finders = $instance->implementedFinders();
+        foreach ($finders as $finder) {
+            unset($this->_finderMap[$finder]);
+        }
+
+        return $result;
+    }
+
+    /**
      * Check if any loaded behavior implements a method.
      *
      * Will return true if any behavior provides a public non-finder method

+ 10 - 10
tests/TestCase/ORM/BehaviorRegistryTest.php

@@ -17,6 +17,7 @@ declare(strict_types=1);
 namespace Cake\Test\TestCase\ORM;
 
 use BadMethodCallException;
+use Cake\Core\Exception\CakeException;
 use Cake\ORM\BehaviorRegistry;
 use Cake\ORM\Exception\MissingBehaviorException;
 use Cake\ORM\Query\SelectQuery;
@@ -258,14 +259,8 @@ class BehaviorRegistryTest extends TestCase
     public function testCall(): void
     {
         $this->Behaviors->load('Sluggable');
-        $mockedBehavior = Mockery::mock('Cake\ORM\Behavior')->makePartial();
-        $this->Behaviors->set('Sluggable', $mockedBehavior);
-
-        $mockedBehavior->shouldReceive('slugify')
-            ->with(['some value'])
-            ->andReturn('some-thing');
-        $return = $this->Behaviors->call('slugify', [['some value']]);
-        $this->assertSame('some-thing', $return);
+        $return = $this->Behaviors->call('slugify', ['some value']);
+        $this->assertSame('some-value', $return);
     }
 
     /**
@@ -321,8 +316,11 @@ class BehaviorRegistryTest extends TestCase
         $this->expectException(BadMethodCallException::class);
         $this->expectExceptionMessage('Cannot call `slugify`, it does not belong to any attached behavior.');
         $this->Behaviors->load('Sluggable');
+
+        $this->assertTrue($this->Behaviors->hasMethod('slugify'));
         $this->Behaviors->unload('Sluggable');
 
+        $this->assertFalse($this->Behaviors->hasMethod('slugify'), 'should not have method anymore');
         $this->Behaviors->call('slugify');
     }
 
@@ -334,9 +332,11 @@ class BehaviorRegistryTest extends TestCase
         $this->expectException(BadMethodCallException::class);
         $this->expectExceptionMessage('Cannot call finder `noslug`, it does not belong to any attached behavior.');
         $this->Behaviors->load('Sluggable');
+        $this->assertTrue($this->Behaviors->hasFinder('noSlug'));
         $this->Behaviors->unload('Sluggable');
 
         $this->Behaviors->callFinder('noSlug', new SelectQuery($this->Table));
+        $this->assertFalse($this->Behaviors->hasFinder('noSlug'));
     }
 
     /**
@@ -371,8 +371,8 @@ class BehaviorRegistryTest extends TestCase
      */
     public function testUnloadUnknown(): void
     {
-        $this->expectException(MissingBehaviorException::class);
-        $this->expectExceptionMessage('Behavior class `FooBehavior` could not be found.');
+        $this->expectException(CakeException::class);
+        $this->expectExceptionMessage('Unknown object `Foo`');
         $this->Behaviors->unload('Foo');
     }
 

+ 14 - 0
tests/TestCase/ORM/TableTest.php

@@ -1973,6 +1973,20 @@ class TableTest extends TestCase
     }
 
     /**
+     * Test removing a behavior from a table clears the method map for the behavior
+     */
+    public function testRemoveBehaviorMethodMapCleared(): void
+    {
+        $table = new Table(['table' => 'articles']);
+        $table->addBehavior('Sluggable');
+        $this->assertTrue($table->behaviors()->hasMethod('slugify'), 'slugify should be mapped');
+        $this->assertSame('foo-bar', $table->slugify('foo bar'));
+
+        $table->removeBehavior('Sluggable');
+        $this->assertFalse($table->behaviors()->hasMethod('slugify'), 'slugify should not be callable');
+    }
+
+    /**
      * Test adding multiple behaviors to a table.
      */
     public function testAddBehaviors(): void