From 45ad8f6ce053642cc29877cadabdabefa92f60eb Mon Sep 17 00:00:00 2001
From: Belisoful <belisoful@icloud.com>
Date: Thu, 27 Apr 2023 09:52:08 -0700
Subject: [PATCH] #910 TComponent behavior names made case insensitive (#916)

---
 framework/TComponent.php            | 10 ++++++++
 framework/Util/TBaseBehavior.php    |  4 ++--
 framework/Util/TBehaviorsModule.php |  2 +-
 tests/unit/TComponentTest.php       | 36 ++++++++++++++++++-----------
 4 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/framework/TComponent.php b/framework/TComponent.php
index 89858afe3..cf8978ca7 100644
--- a/framework/TComponent.php
+++ b/framework/TComponent.php
@@ -788,6 +788,7 @@ public function __get($name)
 			return self::$_ue[$name];
 		} elseif ($this->getBehaviorsEnabled()) {
 			// getting a behavior property/event (handler list)
+			$name = strtolower($name);
 			if (isset($this->_m[$name])) {
 				return $this->_m[$name];
 			} elseif ($this->_m !== null) {
@@ -876,6 +877,7 @@ public function __isset($name)
 			$name = strtolower($name);
 			return isset(self::$_ue[$name]) && self::$_ue[$name]->getCount();
 		} elseif ($this->_m !== null && $this->_m->getCount() > 0 && $this->getBehaviorsEnabled()) {
+			$name = strtolower($name);
 			if (isset($this->_m[$name])) {
 				return true;
 			}
@@ -909,6 +911,7 @@ public function __unset($name)
 		} elseif (strncasecmp($name, 'fx', 2) === 0) {
 			$this->getEventHandlers($name)->remove([$this, $name]);
 		} elseif ($this->_m !== null && $this->_m->getCount() > 0 && $this->getBehaviorsEnabled()) {
+			$name = strtolower($name);
 			if (isset($this->_m[$name])) {
 				$this->detachBehavior($name);
 			} else {
@@ -1682,6 +1685,7 @@ public static function attachClassBehavior($name, $behavior, $class = null, $pri
 		if (empty(self::$_um[$class])) {
 			self::$_um[$class] = [];
 		}
+		$name = strtolower($name);
 		if (isset(self::$_um[$class][$name])) {
 			throw new TInvalidOperationException('component_class_behavior_defined', $class, $name);
 		}
@@ -1722,6 +1726,7 @@ public static function detachClassBehavior($name, $class = null, $priority = fal
 		if (!is_string($name)) {
 			$name = $name::class;
 		}
+		$name = strtolower($name);
 		if (empty(self::$_um[$class]) || !isset(self::$_um[$class][$name])) {
 			return null;
 		}
@@ -1744,6 +1749,7 @@ public static function detachClassBehavior($name, $class = null, $priority = fal
 	 */
 	public function asa($behaviorname)
 	{
+		$behaviorname = strtolower($behaviorname);
 		return $this->_m[$behaviorname] ?? null;
 	}
 
@@ -1891,6 +1897,7 @@ public function clearBehaviors()
 	 */
 	public function attachBehavior($name, $behavior, $priority = null)
 	{
+		$name = strtolower($name);
 		if ($this->_m && isset($this->_m[$name])) {
 			$this->detachBehavior($name);
 		}
@@ -1926,6 +1933,7 @@ public function attachBehavior($name, $behavior, $priority = null)
 	 */
 	public function detachBehavior($name, $priority = false)
 	{
+		$name = strtolower($name);
 		if ($this->_m != null && ($behavior = $this->_m->itemAt($name, $priority))) {
 			$this->callBehaviorsMethod('dyDetachBehavior', $return, $name, $behavior);
 			$behavior->detach($this);
@@ -2014,6 +2022,7 @@ public function getBehaviorsEnabled()
 	 */
 	public function enableBehavior($name): bool
 	{
+		$name = strtolower($name);
 		if ($this->_m != null && isset($this->_m[$name])) {
 			$behavior = $this->_m[$name];
 			if ($behavior->getEnabled() === false) {
@@ -2045,6 +2054,7 @@ public function enableBehavior($name): bool
 	 */
 	public function disableBehavior($name): bool
 	{
+		$name = strtolower($name);
 		if ($this->_m != null && isset($this->_m[$name])) {
 			$behavior = $this->_m[$name];
 			if ($behavior->getEnabled() === true) {
diff --git a/framework/Util/TBaseBehavior.php b/framework/Util/TBaseBehavior.php
index e6c3c5c86..384439fd2 100644
--- a/framework/Util/TBaseBehavior.php
+++ b/framework/Util/TBaseBehavior.php
@@ -206,8 +206,8 @@ public function getName(): ?string
 	public function setName($value)
 	{
 		if (!$this->hasOwner()) {
-			$this->_name = TPropertyValue::ensureString($value);
-		} elseif ($value !== $this->_name) {
+			$this->_name = $value;
+		} elseif (strtolower($value) !== $this->_name) {
 			throw new TInvalidOperationException('basebehavior_cannot_setname_with_owner', $this->_name, $value);
 		}
 	}
diff --git a/framework/Util/TBehaviorsModule.php b/framework/Util/TBehaviorsModule.php
index e25fc0b2d..0b1a7068a 100644
--- a/framework/Util/TBehaviorsModule.php
+++ b/framework/Util/TBehaviorsModule.php
@@ -186,7 +186,7 @@ protected function loadBehaviors($config)
 				}
 			}
 			$properties[IBaseBehavior::CONFIG_KEY] = $element;
-			$name = $properties['name'];
+			$name = $properties['name'] ?? null;
 			unset($properties['name']);
 			if (!$name) {
 				throw new TConfigurationException('behaviormodule_behaviorname_required');
diff --git a/tests/unit/TComponentTest.php b/tests/unit/TComponentTest.php
index 3ac79ea26..cf132592e 100755
--- a/tests/unit/TComponentTest.php
+++ b/tests/unit/TComponentTest.php
@@ -970,6 +970,7 @@ public function testAsA()
 
 		//Check that the component has only the class behavior assigned
 		$this->assertNotNull($this->component->asa('FooClassBehavior'));
+		$this->assertNotNull($this->component->asa(strtoupper('FooClassBehavior')));
 		$this->assertNull($this->component->asa('FooFooClassBehavior'));
 		$this->assertNull($this->component->asa($barClassName));
 		$this->assertNull($this->component->asa('NonExistantClassBehavior'));
@@ -1134,14 +1135,14 @@ public function testGetBehaviors()
 		$this->assertEquals([], $this->component->getBehaviors());
 		$b = new FooBarBehavior();
 		$this->assertEquals($b, $this->component->attachBehavior('FooBarBehavior', $b));
-		$this->assertEquals(['FooBarBehavior' => $b], $this->component->getBehaviors());
+		$this->assertEquals(['foobarbehavior' => $b], $this->component->getBehaviors());
 		$b->setEnabled(false);
-		$this->assertEquals(['FooBarBehavior' => $b], $this->component->getBehaviors());
+		$this->assertEquals(['foobarbehavior' => $b], $this->component->getBehaviors());
 		$b->setEnabled(true);
-		$this->assertEquals(['FooBarBehavior' => $b], $this->component->getBehaviors());
+		$this->assertEquals(['foobarbehavior' => $b], $this->component->getBehaviors());
 		$this->assertEquals(false, is_object($this->component->getBehaviors()));
 		$this->assertEquals(true, is_array($this->component->getBehaviors()));
-		$this->assertEquals($b, $this->component->detachBehavior('FooBarBehavior'));
+		$this->assertEquals($b, $this->component->detachBehavior('foobarbehavior'));
 		$this->assertEquals([], $this->component->getBehaviors());
 	}
 
@@ -1237,8 +1238,9 @@ public function testAttachDetachBehavior()
 		$this->assertFalse($this->component->isa(BarBehavior::class));
 		
 		
-		$this->component->attachBehavior('FooBehavior', 'FooBehavior');
+		$this->component->attachBehavior(strtoupper('FooBehavior'), 'FooBehavior');
 
+		$this->assertNotNull($this->component->asa(strtoupper('FooBehavior')));
 		$this->assertNotNull($this->component->asa('FooBehavior'));
 		$this->assertTrue($this->component->isa(FooBehavior::class));
 		$this->assertNull($this->component->asa('BarBehavior'));
@@ -1261,7 +1263,7 @@ public function testAttachDetachBehavior()
 		$this->assertFalse($this->component->isa(BarBehavior::class));
 		$this->assertEquals('value',$this->component->asa('FooBehavior')->PropertyA);
 		
-		$this->component->detachBehavior('FooBehavior');
+		$this->component->detachBehavior(strtoupper('FooBehavior'));
 
 		$this->assertNull($this->component->asa('FooBehavior'));
 		$this->assertFalse($this->component->isa(FooBehavior::class));
@@ -1447,7 +1449,8 @@ public function testEnableDisableBehavior()
 		$fooB->syncEventHandlers(null, false);
 		$this->assertEquals(2, $this->component->onMyEvent->getCount());
 
-		$this->assertTrue($this->component->disableBehavior($behaviorName));
+		//Test upper case name as well.
+		$this->assertTrue($this->component->disableBehavior(strtoupper($behaviorName)));
 		$this->assertEquals(0, $this->component->onMyEvent->getCount());
 		$this->assertFalse($this->component->isa(FooBehavior::class));
 		$this->assertEquals(0, $this->component->onMyEvent->getCount());
@@ -1462,7 +1465,8 @@ public function testEnableDisableBehavior()
 		} catch (TApplicationException $e) {
 		}
 		
-		$this->assertTrue($this->component->enableBehavior($behaviorName));
+		//Test upper case name as well.
+		$this->assertTrue($this->component->enableBehavior(strtoupper($behaviorName)));
 		$this->assertTrue($this->component->isa(FooBehavior::class));
 		$this->assertEquals(2, $this->component->onMyEvent->getCount());
 
@@ -1845,6 +1849,8 @@ public function testGetProperty()
 		$this->component->disableBehavior('BehaviorTestBehavior');
 
 		$this->assertEquals($behavior, $this->component->BehaviorTestBehavior);
+		$this->assertEquals($behavior, $this->component->behaviortestbehavior);
+		$this->assertEquals($behavior, $this->component->BEHAVIORTESTBEHAVIOR);
 		try {
 			$behavior = $this->component->BehaviorTestBehavior2;
 			$this->fail('exception not raised when getting undefined property');
@@ -1984,9 +1990,11 @@ public function testIsSetFunction()
 		$this->assertFalse(isset($this->component->BehaviorTestBehavior));
 		$this->assertFalse(isset($this->component->onBehaviorEvent));
 
-		$this->component->attachBehavior('BehaviorTestBehavior', new BehaviorTestBehavior);
+		$this->component->attachBehavior('BehaviorTestBehavior', new BehaviorTestBehavior());
 
+		$this->assertTrue(isset($this->component->behaviortestbehavior));
 		$this->assertTrue(isset($this->component->BehaviorTestBehavior));
+		$this->assertTrue(isset($this->component->BEHAVIORTESTBEHAVIOR));
 		$this->assertFalse(isset($this->component->onBehaviorEvent));
 
 		$this->component->attachEventHandler('onBehaviorEvent', 'foo');
@@ -2080,7 +2088,7 @@ public function testUnsetFunction()
 		$this->assertEquals(0, count($this->component->BehaviorTestBehavior->onBehaviorEvent));
 
 		// Remove behavior via unset
-		unset($this->component->BehaviorTestBehavior);
+		unset($this->component->behaviortestbehavior);
 		$this->assertFalse($this->component->asa('BehaviorTestBehavior') instanceof BehaviorTestBehavior);
 	}
 
@@ -2826,7 +2834,7 @@ public function testClone()
 		$copy = clone $this->component;
 		$copy->Text = 'copyObject';
 		
-		$this->assertNotNull($copy->asa('CopyBehavior'));
+		$this->assertNotNull($copy->asa('COPYBehavior'));
 		$this->assertEquals($this->component, $this->component->CopyBehavior->getOwner());
 		$this->assertEquals($copy, $copy->CopyBehavior->getOwner());
 		$this->assertTrue($copy->CopyBehavior !== $this->component->CopyBehavior);
@@ -2844,9 +2852,9 @@ public function testWakeUp()
 		NewComponent::attachClassBehavior('ClassBehavior2', $cb2 = new FooFooClassBehavior());
 		NewComponent::attachClassBehavior('ClassBehavior4', $cb4 = new FooClassBehavior());
 		$cb4->propertya = '4th value';
-		$this->assertEquals('ClassBehavior1', $cb1->getName());
-		$this->assertEquals('ClassBehavior2', $cb2->getName());
-		$this->assertEquals('ClassBehavior4', $cb4->getName());
+		$this->assertEquals('classbehavior1', $cb1->getName());
+		$this->assertEquals('classbehavior2', $cb2->getName());
+		$this->assertEquals('classbehavior4', $cb4->getName());
 		
 		$obj = new NewComponent();
 		$this->component = new NewComponent();