From e1a49cfb6de5c2e78ec600566544d77bd10ecc3d Mon Sep 17 00:00:00 2001 From: Brandon Kelly Date: Fri, 22 Jun 2018 11:19:21 -0700 Subject: [PATCH] Fixes #16247: Cloning components will now clone their behaviors as well --- framework/CHANGELOG.md | 1 + framework/base/Component.php | 11 ++++- tests/framework/base/ComponentTest.php | 3 +- tests/framework/behaviors/CloningBehaviorTest.php | 57 +++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/framework/behaviors/CloningBehaviorTest.php diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b9a8cfb..f352e8c 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -53,6 +53,7 @@ Yii Framework 2 Change Log - Enh #16054: Callback execution with mutex synchronization (zhuravljov) - Enh #16126: Allows to configure `Connection::dsn` by config array (leandrogehlen) - Chg #11397: `yii\i18n\MessageFormatter` polyfills and `yii\i18n\MessageFormatter::parse()` method were removed resulting in performance boost. See UPGRADE for compatibility notes (samdark) +- Chg #16247: Cloning components will now clone their behaviors as well (brandonkelly) 2.0.14.2 under development ------------------------ diff --git a/framework/base/Component.php b/framework/base/Component.php index 7fae458..767e432 100644 --- a/framework/base/Component.php +++ b/framework/base/Component.php @@ -302,13 +302,20 @@ class Component extends BaseObject /** * This method is called after the object is created by cloning an existing one. - * It removes all behaviors because they are attached to the old object. + * It clones all behaviors as well, and attaches them to the new object. */ public function __clone() { $this->_events = []; $this->_eventWildcards = []; - $this->_behaviors = null; + + if ($this->_behaviors !== null) { + $behaviors = $this->_behaviors; + $this->_behaviors = null; + foreach ($behaviors as $name => $behavior) { + $this->attachBehavior($name, clone $behavior); + } + } } /** diff --git a/tests/framework/base/ComponentTest.php b/tests/framework/base/ComponentTest.php index 6f1501d..0d262ab 100644 --- a/tests/framework/base/ComponentTest.php +++ b/tests/framework/base/ComponentTest.php @@ -61,7 +61,8 @@ class ComponentTest extends TestCase $clone = clone $component; $this->assertNotSame($component, $clone); - $this->assertNull($clone->getBehavior('a')); + $this->assertNotNull($clone->getBehavior('a')); + $this->assertNotSame($behavior, $clone->getBehavior('a')); $this->assertFalse($clone->hasEventHandlers('test')); $this->assertFalse($clone->hasEventHandlers('foo')); $this->assertFalse($clone->hasEventHandlers('*')); diff --git a/tests/framework/behaviors/CloningBehaviorTest.php b/tests/framework/behaviors/CloningBehaviorTest.php new file mode 100644 index 0000000..af4d3e1 --- /dev/null +++ b/tests/framework/behaviors/CloningBehaviorTest.php @@ -0,0 +1,57 @@ +myBehaviorProperty = 'foo'; + $model2 = clone $model1; + + $this->assertEquals($model1->myBehaviorProperty, $model2->myBehaviorProperty); + } +} + +/** + * Test Model class with a behavior attached. + * + * @mixin BehaviorWithProperty + */ +class ModelWithBehavior extends Model +{ + /** + * {@inheritdoc} + */ + public function behaviors() + { + return [ + 'myBehavior' => BehaviorWithProperty::class + ]; + } +} + +/** + * Test Behavior class with property. + * + */ +class BehaviorWithProperty extends Behavior +{ + public $myBehaviorProperty; +} +