From 9ef4c6160c402f46129f731d4531e936fdec6847 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Fri, 24 Jan 2025 11:51:34 +0700 Subject: [PATCH] Prioritize `getXXX()` and `setXXX()` over magic properties --- src/Trait/MagicPropertiesTrait.php | 39 ++++++++++++------------- tests/MagicActiveRecordTest.php | 14 +++++++++ tests/Stubs/MagicActiveRecord/Order.php | 14 +++++++++ 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/Trait/MagicPropertiesTrait.php b/src/Trait/MagicPropertiesTrait.php index a953dbdec..e8a29c80f 100644 --- a/src/Trait/MagicPropertiesTrait.php +++ b/src/Trait/MagicPropertiesTrait.php @@ -18,7 +18,6 @@ use function in_array; use function method_exists; use function property_exists; -use function ucfirst; /** * Trait to define magic methods to access values of an ActiveRecord instance. @@ -62,6 +61,11 @@ trait MagicPropertiesTrait */ public function __get(string $name) { + if (method_exists($this, $getter = "get$name")) { + /** Read getter, e.g., getName() */ + return $this->$getter(); + } + if ($this->hasProperty($name)) { return $this->get($name); } @@ -70,17 +74,12 @@ public function __get(string $name) return $this->getRelatedRecords()[$name]; } - if (method_exists($this, $getter = 'get' . ucfirst($name))) { - /** Read getter, e.g., getName() */ - return $this->$getter(); - } - - if (method_exists($this, 'get' . ucfirst($name) . 'Query')) { + if (method_exists($this, "get{$name}Query")) { /** Read relation query getter, e.g., getUserQuery() */ return $this->retrieveRelation($name); } - if (method_exists($this, 'set' . ucfirst($name))) { + if (method_exists($this, "set$name")) { throw new InvalidCallException('Getting write-only property: ' . static::class . '::' . $name); } @@ -131,19 +130,19 @@ public function __unset(string $name): void */ public function __set(string $name, mixed $value): void { - if ($this->hasProperty($name)) { - parent::set($name, $value); + if (method_exists($this, $setter = "set$name")) { + $this->$setter($value); return; } - if (method_exists($this, $setter = 'set' . ucfirst($name))) { - $this->$setter($value); + if ($this->hasProperty($name)) { + parent::set($name, $value); return; } if ( - method_exists($this, 'get' . ucfirst($name)) - || method_exists($this, 'get' . ucfirst($name) . 'Query') + method_exists($this, "get$name") + || method_exists($this, "get{$name}Query") ) { throw new InvalidCallException('Setting read-only property: ' . static::class . '::' . $name); } @@ -184,24 +183,24 @@ public function set(string $propertyName, mixed $value): void */ public function isProperty(string $name, bool $checkVars = true): bool { - return method_exists($this, 'get' . ucfirst($name)) - || method_exists($this, 'set' . ucfirst($name)) - || method_exists($this, 'get' . ucfirst($name) . 'Query') + return method_exists($this, "get$name") + || method_exists($this, "set$name") + || method_exists($this, "get{$name}Query") || ($checkVars && property_exists($this, $name)) || $this->hasProperty($name); } public function canGetProperty(string $name, bool $checkVars = true): bool { - return method_exists($this, 'get' . ucfirst($name)) - || method_exists($this, 'get' . ucfirst($name) . 'Query') + return method_exists($this, "get$name") + || method_exists($this, "get{$name}Query") || ($checkVars && property_exists($this, $name)) || $this->hasProperty($name); } public function canSetProperty(string $name, bool $checkVars = true): bool { - return method_exists($this, 'set' . ucfirst($name)) + return method_exists($this, "set$name") || ($checkVars && property_exists($this, $name)) || $this->hasProperty($name); } diff --git a/tests/MagicActiveRecordTest.php b/tests/MagicActiveRecordTest.php index e8c3ba64b..65ff98903 100644 --- a/tests/MagicActiveRecordTest.php +++ b/tests/MagicActiveRecordTest.php @@ -4,6 +4,7 @@ namespace Yiisoft\ActiveRecord\Tests; +use DateTimeImmutable; use DivisionByZeroError; use ReflectionException; use Yiisoft\ActiveRecord\ActiveQuery; @@ -882,4 +883,17 @@ public function testRelationNames(): void 'items2', ], $customer->relationNames()); } + + public function testGetSetMethodsPriority(): void + { + $this->checkFixture($this->db(), 'order'); + + $datetime = DateTimeImmutable::createFromFormat('U', '1325502201'); + + $order = new Order(); + $order->created_at = $datetime; + + $this->assertSame(1_325_502_201, $order->get('created_at')); + $this->assertEquals($datetime, $order->getCreated_at()); + } } diff --git a/tests/Stubs/MagicActiveRecord/Order.php b/tests/Stubs/MagicActiveRecord/Order.php index 26bec457a..00276d823 100644 --- a/tests/Stubs/MagicActiveRecord/Order.php +++ b/tests/Stubs/MagicActiveRecord/Order.php @@ -4,6 +4,8 @@ namespace Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord; +use DateTimeImmutable; +use DateTimeInterface; use Yiisoft\ActiveRecord\ActiveQuery; use Yiisoft\ActiveRecord\Tests\Stubs\MagicActiveRecord; @@ -26,6 +28,18 @@ public function getTableName(): string return self::TABLE_NAME; } + public function getCreated_at(): DateTimeImmutable + { + return DateTimeImmutable::createFromFormat('U', (string) $this->get('created_at')); + } + + public function setCreated_at(DateTimeInterface|int $createdAt): void + { + $this->set('created_at', $createdAt instanceof DateTimeInterface + ? $createdAt->getTimestamp() + : $createdAt); + } + public function setVirtualCustomerId(string|int|null $virtualCustomerId = null): void { $this->virtualCustomerId = $virtualCustomerId;