From fb35f20f6d4667d7717ae14a8a3a30170535bb16 Mon Sep 17 00:00:00 2001 From: Spell6inder Date: Mon, 21 Dec 2020 10:08:09 +0200 Subject: [PATCH] Fix #18435: Change the check order whether an object is an implementation of `Arrayable` or `JsonSerializable` in `\yii\base\ArrayableTrait::toArray()` and `\yii\rest\Serializer::serialize()` --- .github/workflows/build.yml | 1 + framework/CHANGELOG.md | 1 + framework/base/ArrayableTrait.php | 12 ++--- framework/rest/Serializer.php | 4 +- tests/framework/rest/SerializerTest.php | 63 ++++++++++++++++++------- 5 files changed, 55 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a912dbfd1b..1095e78220 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,6 +5,7 @@ on: [push, pull_request] env: DEFAULT_COMPOSER_FLAGS: "--prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi" PHPUNIT_EXCLUDE_GROUP: mssql,oci,wincache,xcache,zenddata,cubrid + XDEBUG_MODE: coverage, develop jobs: phpunit: diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index d28dfa3ab0..31cf9ddb2f 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -16,6 +16,7 @@ Yii Framework 2 Change Log - Bug #18426: Fix check for route's leading slash in `yii\widgets\Menu` (stevekr) - Bug #16492: Fix eager loading Active Record relations when relation key is a subject to a type-casting behavior (bizley) - Bug #18435: Fix ensuring Active Record relation links' keys to be strings (bizley) +- Bug #18435: Change the check order whether an object is an implementation of `Arrayable` or `JsonSerializable` in `\yii\base\ArrayableTrait::toArray()` and `\yii\rest\Serializer::serialize()` (spell6inder) 2.0.39.3 November 23, 2020 diff --git a/framework/base/ArrayableTrait.php b/framework/base/ArrayableTrait.php index 985ce8a03a..d2093fd859 100644 --- a/framework/base/ArrayableTrait.php +++ b/framework/base/ArrayableTrait.php @@ -128,17 +128,17 @@ trait ArrayableTrait if ($recursive) { $nestedFields = $this->extractFieldsFor($fields, $field); $nestedExpand = $this->extractFieldsFor($expand, $field); - if ($attribute instanceof \JsonSerializable) { - $attribute = $attribute->jsonSerialize(); - } elseif ($attribute instanceof Arrayable) { + if ($attribute instanceof Arrayable) { $attribute = $attribute->toArray($nestedFields, $nestedExpand); + } elseif ($attribute instanceof \JsonSerializable) { + $attribute = $attribute->jsonSerialize(); } elseif (is_array($attribute)) { $attribute = array_map( function ($item) use ($nestedFields, $nestedExpand) { - if ($item instanceof \JsonSerializable) { - return $item->jsonSerialize(); - } elseif ($item instanceof Arrayable) { + if ($item instanceof Arrayable) { return $item->toArray($nestedFields, $nestedExpand); + } elseif ($item instanceof \JsonSerializable) { + return $item->jsonSerialize(); } return $item; }, diff --git a/framework/rest/Serializer.php b/framework/rest/Serializer.php index 6cb3ebc35e..2a47511e7f 100644 --- a/framework/rest/Serializer.php +++ b/framework/rest/Serializer.php @@ -146,10 +146,10 @@ class Serializer extends Component { if ($data instanceof Model && $data->hasErrors()) { return $this->serializeModelErrors($data); - } elseif ($data instanceof \JsonSerializable) { - return $data->jsonSerialize(); } elseif ($data instanceof Arrayable) { return $this->serializeModel($data); + } elseif ($data instanceof \JsonSerializable) { + return $data->jsonSerialize(); } elseif ($data instanceof DataProviderInterface) { return $this->serializeDataProvider($data); } elseif (is_array($data)) { diff --git a/tests/framework/rest/SerializerTest.php b/tests/framework/rest/SerializerTest.php index 6872bb983f..6fd7097498 100644 --- a/tests/framework/rest/SerializerTest.php +++ b/tests/framework/rest/SerializerTest.php @@ -421,9 +421,11 @@ class SerializerTest extends TestCase public function testSerializeJsonSerializable() { $serializer = new Serializer(); - $model = new TestModel3(); + $model3 = new TestModel3(); + $model4 = new TestModel4(); - $this->assertEquals(['customField' => 'test3/test4'], $serializer->serialize($model)); + $this->assertEquals(['customField' => 'test3/test4'], $serializer->serialize($model3)); + $this->assertEquals(['customField2' => 'test5/test6'], $serializer->serialize($model4)); } /** @@ -432,15 +434,18 @@ class SerializerTest extends TestCase public function testSerializeArrayableWithJsonSerializableAttribute() { $serializer = new Serializer(); - $model = new TestModel4(); + $model = new TestModel5(); - $this->assertEquals([ - 'field3' => 'test3', - 'field4' => 'test4', - 'testModel3' => ['customField' => 'test3/test4'], - 'testModelArray' => [['customField' => 'test3/test4']], - ], - $serializer->serialize($model)); + $this->assertEquals( + [ + 'field7' => 'test7', + 'field8' => 'test8', + 'testModel3' => ['customField' => 'test3/test4'], + 'testModel4' => ['customField2' => 'test5/test6'], + 'testModelArray' => [['customField' => 'test3/test4'], ['customField2' => 'test5/test6']], + ], + $serializer->serialize($model) + ); } /** @@ -523,7 +528,11 @@ class TestModel3 extends Model implements \JsonSerializable public function fields() { - return static::$fields; + return [ + 'customField' => function() { + return $this->field3.'/'.$this->field4; + }, + ]; } public function extraFields() @@ -531,22 +540,32 @@ class TestModel3 extends Model implements \JsonSerializable return static::$extraFields; } + public function jsonSerialize() + { + return $this->getAttributes(); + } +} +class TestModel4 implements \JsonSerializable +{ + public $field5 = 'test5'; + public $field6 = 'test6'; + public function jsonSerialize() { return [ - 'customField' => $this->field3.'/'.$this->field4, + 'customField2' => $this->field5.'/'.$this->field6, ]; } } -class TestModel4 extends Model +class TestModel5 extends Model { - public static $fields = ['field3', 'field4']; + public static $fields = ['field7', 'field8']; public static $extraFields = []; - public $field3 = 'test3'; - public $field4 = 'test4'; - public $extraField4 = 'testExtra2'; + public $field7 = 'test7'; + public $field8 = 'test8'; + public $extraField4 = 'testExtra4'; public function fields() { @@ -554,8 +573,11 @@ class TestModel4 extends Model $fields['testModel3'] = function() { return $this->getTestModel3(); }; + $fields['testModel4'] = function() { + return $this->getTestModel4(); + }; $fields['testModelArray'] = function() { - return [$this->getTestModel3()]; + return [$this->getTestModel3(), $this->getTestModel4()]; }; return $fields; } @@ -569,4 +591,9 @@ class TestModel4 extends Model { return new TestModel3(); } + + public function getTestModel4() + { + return new TestModel4(); + } }