diff --git a/docs/classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html b/docs/classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html
index ad2d29b9..70c7e9d7 100644
--- a/docs/classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html
+++ b/docs/classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html
@@ -342,6 +342,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -415,7 +421,7 @@
@@ -1283,6 +1289,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1420,6 +1470,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/classes/LINE-Clients-Insight-ObjectSerializer.html b/docs/classes/LINE-Clients-Insight-ObjectSerializer.html
index db400323..7227b07e 100644
--- a/docs/classes/LINE-Clients-Insight-ObjectSerializer.html
+++ b/docs/classes/LINE-Clients-Insight-ObjectSerializer.html
@@ -342,6 +342,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -415,7 +421,7 @@
@@ -1283,6 +1289,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1420,6 +1470,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/classes/LINE-Clients-Liff-ObjectSerializer.html b/docs/classes/LINE-Clients-Liff-ObjectSerializer.html
index 397e44ec..46666dfa 100644
--- a/docs/classes/LINE-Clients-Liff-ObjectSerializer.html
+++ b/docs/classes/LINE-Clients-Liff-ObjectSerializer.html
@@ -342,6 +342,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -415,7 +421,7 @@
@@ -1283,6 +1289,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1420,6 +1470,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/classes/LINE-Clients-ManageAudience-ObjectSerializer.html b/docs/classes/LINE-Clients-ManageAudience-ObjectSerializer.html
index 96c07e4b..5e1ca051 100644
--- a/docs/classes/LINE-Clients-ManageAudience-ObjectSerializer.html
+++ b/docs/classes/LINE-Clients-ManageAudience-ObjectSerializer.html
@@ -342,6 +342,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -415,7 +421,7 @@
@@ -1283,6 +1289,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1420,6 +1470,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/classes/LINE-Clients-MessagingApi-ObjectSerializer.html b/docs/classes/LINE-Clients-MessagingApi-ObjectSerializer.html
index b1bcf3c3..92f4d862 100644
--- a/docs/classes/LINE-Clients-MessagingApi-ObjectSerializer.html
+++ b/docs/classes/LINE-Clients-MessagingApi-ObjectSerializer.html
@@ -342,6 +342,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -415,7 +421,7 @@
@@ -1283,6 +1289,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1420,6 +1470,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/classes/LINE-Webhook-ObjectSerializer.html b/docs/classes/LINE-Webhook-ObjectSerializer.html
index 4a7a89bc..2dbb8c4f 100644
--- a/docs/classes/LINE-Webhook-ObjectSerializer.html
+++ b/docs/classes/LINE-Webhook-ObjectSerializer.html
@@ -340,6 +340,12 @@
Checks if a value is empty, based on its OpenAPI type.
+
+ objectToArray()
+
+ : mixed
+
+
@@ -413,7 +419,7 @@
@@ -1281,6 +1287,50 @@ Return values
+
+
+
+ objectToArray()
+
+
+
+
+
+
+
+ private
+ static objectToArray(mixed $obj) : mixed
+
+
+
+
+
+ Parameters
+
+ -
+ $obj
+ : mixed
+
+ -
+
+
+
+
+
+
+
+
+
@@ -1418,6 +1468,7 @@ Return values
toQueryValue()
toString()
isEmptyValue()
+ objectToArray()
diff --git a/docs/js/searchIndex.js b/docs/js/searchIndex.js
index 4bf4bdab..3ea7892d 100644
--- a/docs/js/searchIndex.js
+++ b/docs/js/searchIndex.js
@@ -1675,6 +1675,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Clients\\ChannelAccessToken\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Clients-ChannelAccessToken-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Clients\\ChannelAccessToken\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
@@ -5810,6 +5815,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Clients-Insight-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Clients\\Insight\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Clients-Insight-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Clients\\Insight\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
@@ -7885,6 +7895,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Clients-Liff-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Clients\\Liff\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Clients-Liff-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Clients\\Liff\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
@@ -12455,6 +12470,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Clients-ManageAudience-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Clients\\ManageAudience\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Clients-ManageAudience-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Clients\\ManageAudience\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
@@ -45585,6 +45605,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Clients-MessagingApi-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Clients\\MessagingApi\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Clients-MessagingApi-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Clients\\MessagingApi\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
@@ -56265,6 +56290,11 @@ Search.appendIndex(
"name": "buildQuery",
"summary": "Build\u0020a\u0020query\u0020string\u0020from\u0020an\u0020array\u0020of\u0020key\u0020value\u0020pairs.",
"url": "classes/LINE-Webhook-ObjectSerializer.html#method_buildQuery"
+ }, {
+ "fqsen": "\\LINE\\Webhook\\ObjectSerializer\u003A\u003AobjectToArray\u0028\u0029",
+ "name": "objectToArray",
+ "summary": "",
+ "url": "classes/LINE-Webhook-ObjectSerializer.html#method_objectToArray"
}, {
"fqsen": "\\LINE\\Webhook\\ObjectSerializer\u003A\u003A\u0024dateTimeFormat",
"name": "dateTimeFormat",
diff --git a/phpmd.xml b/phpmd.xml
index deac1508..f89a1b29 100644
--- a/phpmd.xml
+++ b/phpmd.xml
@@ -12,6 +12,7 @@
EchoBot/Dependency.php
KitchenSink/Dependency.php
test/EventRequestParserTest.php
+ test/MessagingApiApiTest.php
diff --git a/src/clients/channel-access-token/lib/ObjectSerializer.php b/src/clients/channel-access-token/lib/ObjectSerializer.php
index efc4c77a..26e7ccc9 100644
--- a/src/clients/channel-access-token/lib/ObjectSerializer.php
+++ b/src/clients/channel-access-token/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/src/clients/insight/lib/ObjectSerializer.php b/src/clients/insight/lib/ObjectSerializer.php
index 59994446..a42923d7 100644
--- a/src/clients/insight/lib/ObjectSerializer.php
+++ b/src/clients/insight/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/src/clients/liff/lib/ObjectSerializer.php b/src/clients/liff/lib/ObjectSerializer.php
index 32a49030..147c9281 100644
--- a/src/clients/liff/lib/ObjectSerializer.php
+++ b/src/clients/liff/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/src/clients/manage-audience/lib/ObjectSerializer.php b/src/clients/manage-audience/lib/ObjectSerializer.php
index f5ed5932..9f26f3d9 100644
--- a/src/clients/manage-audience/lib/ObjectSerializer.php
+++ b/src/clients/manage-audience/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/src/clients/messaging-api/lib/ObjectSerializer.php b/src/clients/messaging-api/lib/ObjectSerializer.php
index 7b22a51c..13754167 100644
--- a/src/clients/messaging-api/lib/ObjectSerializer.php
+++ b/src/clients/messaging-api/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/src/webhook/lib/ObjectSerializer.php b/src/webhook/lib/ObjectSerializer.php
index b0bdf09c..6e270327 100644
--- a/src/webhook/lib/ObjectSerializer.php
+++ b/src/webhook/lib/ObjectSerializer.php
@@ -533,6 +533,10 @@ public static function deserialize($data, $class, $httpHeaders = null)
$data = (object)$data;
}
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
// If a discriminator is defined and points to a valid subclass, use it.
$discriminator = $class::DISCRIMINATOR;
if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
@@ -629,4 +633,17 @@ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986):
return $qs ? (string) substr($qs, 0, -1) : '';
}
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
}
diff --git a/test/clients/messaging-api/Api/MessagingApiApiTest.php b/test/clients/messaging-api/Api/MessagingApiApiTest.php
index 7e718a81..7fc5e122 100644
--- a/test/clients/messaging-api/Api/MessagingApiApiTest.php
+++ b/test/clients/messaging-api/Api/MessagingApiApiTest.php
@@ -22,13 +22,25 @@
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use LINE\Clients\MessagingApi\Api\MessagingApiApi;
+use LINE\Clients\MessagingApi\Model\AcquisitionConditionResponse;
+use LINE\Clients\MessagingApi\Model\Action;
+use LINE\Clients\MessagingApi\Model\CouponCashBackRewardResponse;
use LINE\Clients\MessagingApi\Model\CouponCreateRequest;
use LINE\Clients\MessagingApi\Model\CouponCreateResponse;
use LINE\Clients\MessagingApi\Model\CouponDiscountRewardRequest;
+use LINE\Clients\MessagingApi\Model\CouponListResponse;
use LINE\Clients\MessagingApi\Model\CouponResponse;
use LINE\Clients\MessagingApi\Model\DiscountFixedPriceInfoRequest;
+use LINE\Clients\MessagingApi\Model\GetFollowersResponse;
use LINE\Clients\MessagingApi\Model\LotteryAcquisitionConditionRequest;
use LINE\Clients\MessagingApi\Model\MessagingApiPagerCouponListResponse;
+use LINE\Clients\MessagingApi\Model\NormalAcquisitionConditionResponse;
+use LINE\Clients\MessagingApi\Model\PostbackAction;
+use LINE\Clients\MessagingApi\Model\RichMenuArea;
+use LINE\Clients\MessagingApi\Model\RichMenuListResponse;
+use LINE\Clients\MessagingApi\Model\RichMenuResponse;
+use LINE\Clients\MessagingApi\Model\RichMenuSize;
+use LINE\Clients\MessagingApi\Model\URIAction;
use Mockery;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\UriInterface;
@@ -79,6 +91,7 @@ public function testGetFollowers(): void
));
$api = new MessagingApiApi($client);
$followers = $api->getFollowers(limit: 99);
+ $this->assertInstanceOf(GetFollowersResponse::class, $followers);
$this->assertEquals(["Uaaaaaaaa...", "Ubbbbbbbb...", "Ucccccccc..."], $followers->getUserIds());
$this->assertEquals("yANU9IA..", $followers->getNext());
}
@@ -224,7 +237,7 @@ public function testGetCouponDetail(): void
"title": "Test Coupon",
"usageCondition": "Valid at all stores",
"reward": {
- "type": "cashback",
+ "type": "cashBack",
"priceInfo": {
"type": "percentage",
"percentage": 10
@@ -260,6 +273,7 @@ public function testGetCouponDetail(): void
$response = $api->getCouponDetail($couponId, $contentType);
$this->assertInstanceOf(CouponResponse::class, $response);
+ $this->assertInstanceOf(NormalAcquisitionConditionResponse::class, $response->getAcquisitionCondition());
$this->assertEquals('normal', $response->getAcquisitionCondition()->getType());
$this->assertEquals('https://example.com/barcode.png', $response->getBarcodeImageUrl());
$this->assertEquals('UNIQUECODE123', $response->getCouponCode());
@@ -272,23 +286,18 @@ public function testGetCouponDetail(): void
$this->assertEquals(1699996400, $response->getStartTimestamp());
$this->assertEquals('Test Coupon', $response->getTitle());
$this->assertEquals('Valid at all stores', $response->getUsageCondition());
- $this->assertEquals('cashback', $response->getReward()->getType());
+ $this->assertEquals('cashBack', $response->getReward()->getType());
$this->assertEquals('PUBLIC', $response->getVisibility());
$this->assertEquals('ASIA_TOKYO', $response->getTimezone());
$this->assertEquals($couponId, $response->getCouponId());
$this->assertEquals(1699990000, $response->getCreatedTimestamp());
$this->assertEquals('RUNNING', $response->getStatus());
- // TODO: This test should be enabled after we support automatic polymorphism parsing outside of webhook.
- // Right now, polymorphism is only handled automatically in webhook, so this code is commented out for now.
- // $reward = $response->getReward();
- // if ($reward instanceof CouponCashBackRewardResponse) {
- // $priceInfo = $reward->getPriceInfo();
- // $this->assertEquals('percentage', $priceInfo->getType());
- // $this->assertEquals(10, $priceInfo->getPercentage());
- // } else {
- // $this->fail('Reward is not of type CouponCashBackRewardResponse');
- // }
+ $reward = $response->getReward();
+ $this->assertInstanceOf(CouponCashBackRewardResponse::class, $reward);
+ $priceInfo = $reward->getPriceInfo();
+ $this->assertEquals('percentage', $priceInfo->getType());
+ $this->assertEquals(10, $priceInfo->getPercentage());
}
public function testListCoupon(): void
@@ -336,10 +345,240 @@ public function testListCoupon(): void
$this->assertInstanceOf(MessagingApiPagerCouponListResponse::class, $response);
$this->assertCount(2, $response->getItems());
- $this->assertEquals('coupon1', $response->getItems()[0]['couponId']);
- $this->assertEquals('Coupon 1', $response->getItems()[0]['title']);
- $this->assertEquals('coupon2', $response->getItems()[1]['couponId']);
- $this->assertEquals('Coupon 2', $response->getItems()[1]['title']);
+ $coupon1 = $response->getItems()[0];
+ $this->assertInstanceOf(CouponListResponse::class, $coupon1);
+ $this->assertEquals('coupon1', $coupon1['couponId']);
+ $this->assertEquals('Coupon 1', $coupon1['title']);
+ $coupon2 = $response->getItems()[1];
+ $this->assertInstanceOf(CouponListResponse::class, $coupon2);
+ $this->assertEquals('coupon2', $coupon2['couponId']);
+ $this->assertEquals('Coupon 2', $coupon2['title']);
$this->assertEquals('nextToken', $response->getNext());
}
+
+ public function testGetRichMenuList(): void
+ {
+ $expectedResponseBody = <<shouldReceive('send')
+ ->with(
+ Mockery::on(function (Request $request) {
+ $this->assertEquals('GET', $request->getMethod());
+ $this->assertEquals('https://api.line.me/v2/bot/richmenu/list', (string)$request->getUri());
+ return true;
+ }),
+ []
+ )
+ ->once()
+ ->andReturn(new Response(
+ status: 200,
+ headers: [],
+ body: $expectedResponseBody,
+ ));
+ $api = new MessagingApiApi($client);
+ $richMenuListResponse = $api->getRichMenuList();
+ $this->assertInstanceOf(RichMenuListResponse::class, $richMenuListResponse);
+ $richMenus = $richMenuListResponse->getRichmenus();
+ $this->assertCount(2, $richMenus);
+
+ // First rich menu
+ $this->assertInstanceOf(RichMenuResponse::class, $richMenus[0]);
+ $this->assertEquals('{richMenuId}', $richMenus[0]->getRichMenuId());
+ $this->assertEquals('Nice rich menu', $richMenus[0]->getName());
+ $this->assertEquals('Tap to open', $richMenus[0]->getChatBarText());
+ $this->assertFalse($richMenus[0]->getSelected());
+ $this->assertInstanceOf(RichMenuSize::class, $richMenus[0]->getSize());
+ $this->assertEquals(2500, $richMenus[0]->getSize()->getWidth());
+ $this->assertEquals(1686, $richMenus[0]->getSize()->getHeight());
+ $this->assertCount(1, $richMenus[0]->getAreas());
+
+ // First rich menu - area check
+ $area1 = $richMenus[0]->getAreas()[0];
+ $this->assertInstanceOf(RichMenuArea::class, $area1);
+ $this->assertEquals(0, $area1->getBounds()->getX());
+ $this->assertEquals(0, $area1->getBounds()->getY());
+ $this->assertEquals(2500, $area1->getBounds()->getWidth());
+ $this->assertEquals(1686, $area1->getBounds()->getHeight());
+ $this->assertInstanceOf(PostbackAction::class, $area1->getAction());
+ $this->assertEquals('postback', $area1->getAction()->getType());
+ $this->assertEquals('action=buy&itemid=123', $area1->getAction()->getData());
+
+ // Second rich menu
+ $this->assertInstanceOf(RichMenuResponse::class, $richMenus[1]);
+ $this->assertEquals('{richMenuId2}', $richMenus[1]->getRichMenuId());
+ $this->assertEquals('Nice rich menu 2', $richMenus[1]->getName());
+ $this->assertEquals('Tap to open 2', $richMenus[1]->getChatBarText());
+ $this->assertTrue($richMenus[1]->getSelected());
+ $this->assertInstanceOf(RichMenuSize::class, $richMenus[1]->getSize());
+ $this->assertEquals(2501, $richMenus[1]->getSize()->getWidth());
+ $this->assertEquals(1687, $richMenus[1]->getSize()->getHeight());
+ $this->assertCount(2, $richMenus[1]->getAreas());
+
+ // Second rich menu - first area check
+ $area21 = $richMenus[1]->getAreas()[0];
+ $this->assertInstanceOf(RichMenuArea::class, $area21);
+ $this->assertEquals(0, $area21->getBounds()->getX());
+ $this->assertEquals(0, $area21->getBounds()->getY());
+ $this->assertEquals(1501, $area21->getBounds()->getWidth());
+ $this->assertEquals(687, $area21->getBounds()->getHeight());
+ $this->assertInstanceOf(PostbackAction::class, $area21->getAction());
+ $this->assertEquals('postback', $area21->getAction()->getType());
+ $this->assertEquals('action=buy&itemid=123', $area21->getAction()->getData());
+
+ // Second rich menu - second area check
+ $area22 = $richMenus[1]->getAreas()[1];
+ $this->assertInstanceOf(RichMenuArea::class, $area22);
+ $this->assertEquals(1501, $area22->getBounds()->getX());
+ $this->assertEquals(687, $area22->getBounds()->getY());
+ $this->assertEquals(1000, $area22->getBounds()->getWidth());
+ $this->assertEquals(1000, $area22->getBounds()->getHeight());
+ $this->assertInstanceOf(URIAction::class, $area22->getAction());
+ $this->assertEquals('uri', $area22->getAction()->getType());
+ $this->assertEquals('メニューを見る', $area22->getAction()->getLabel());
+ $this->assertEquals('https://example.com/menu', $area22->getAction()->getUri());
+ }
+
+ public function testGetRichMenuListWithUnknownType(): void
+ {
+ $expectedResponseBody = <<shouldReceive('send')
+ ->with(
+ Mockery::on(function (Request $request) {
+ $this->assertEquals('GET', $request->getMethod());
+ $this->assertEquals('https://api.line.me/v2/bot/richmenu/list', (string)$request->getUri());
+ return true;
+ }),
+ []
+ )
+ ->once()
+ ->andReturn(new Response(
+ status: 200,
+ headers: [],
+ body: $expectedResponseBody,
+ ));
+ $api = new MessagingApiApi($client);
+ $richMenuListResponse = $api->getRichMenuList();
+ $this->assertInstanceOf(RichMenuListResponse::class, $richMenuListResponse);
+ $richMenus = $richMenuListResponse->getRichmenus();
+ $this->assertCount(1, $richMenus);
+
+ // First rich menu
+ $this->assertInstanceOf(RichMenuResponse::class, $richMenus[0]);
+ $this->assertEquals('{richMenuId}', $richMenus[0]->getRichMenuId());
+ $this->assertEquals('Nice rich menu', $richMenus[0]->getName());
+ $this->assertEquals('Tap to open', $richMenus[0]->getChatBarText());
+ $this->assertFalse($richMenus[0]->getSelected());
+ $this->assertInstanceOf(RichMenuSize::class, $richMenus[0]->getSize());
+ $this->assertEquals(2500, $richMenus[0]->getSize()->getWidth());
+ $this->assertEquals(1686, $richMenus[0]->getSize()->getHeight());
+ $this->assertCount(1, $richMenus[0]->getAreas());
+
+ // First rich menu - area check
+ $area1 = $richMenus[0]->getAreas()[0];
+ $this->assertInstanceOf(RichMenuArea::class, $area1);
+ $this->assertEquals(0, $area1->getBounds()->getX());
+ $this->assertEquals(0, $area1->getBounds()->getY());
+ $this->assertEquals(2500, $area1->getBounds()->getWidth());
+ $this->assertEquals(1686, $area1->getBounds()->getHeight());
+ $this->assertInstanceOf(Action::class, $area1->getAction());
+ $this->assertEquals('unknown_type', $area1->getAction()->getType());
+ }
}
diff --git a/tools/custom-template/ObjectSerializer.mustache b/tools/custom-template/ObjectSerializer.mustache
new file mode 100644
index 00000000..11fd9c5e
--- /dev/null
+++ b/tools/custom-template/ObjectSerializer.mustache
@@ -0,0 +1,625 @@
+partial_header}}
+/**
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+
+namespace {{invokerPackage}};
+
+use GuzzleHttp\Psr7\Utils;
+use {{modelPackage}}\ModelInterface;
+
+/**
+ * ObjectSerializer Class Doc Comment
+ *
+ * @category Class
+ * @package {{invokerPackage}}
+ * @author OpenAPI Generator team
+ * @link https://openapi-generator.tech
+ */
+class ObjectSerializer
+{
+ /** @var string */
+ private static $dateTimeFormat = \DateTime::ATOM;
+
+ /**
+ * Change the date format
+ *
+ * @param string $format the new date format to use
+ */
+ public static function setDateTimeFormat($format)
+ {
+ self::$dateTimeFormat = $format;
+ }
+
+ /**
+ * Serialize data
+ *
+ * @param mixed $data the data to serialize
+ * @param string|null $type the OpenAPIToolsType of the data
+ * @param string|null $format the format of the OpenAPITools type of the data
+ *
+ * @return scalar|object|array|null serialized form of $data
+ */
+ public static function sanitizeForSerialization($data, $type = null, $format = null)
+ {
+ if (is_scalar($data) || null === $data) {
+ return $data;
+ }
+
+ if ($data instanceof \DateTime) {
+ return ($format === 'date') ? $data->format('Y-m-d') : $data->format(self::$dateTimeFormat);
+ }
+
+ if (is_array($data)) {
+ foreach ($data as $property => $value) {
+ $data[$property] = self::sanitizeForSerialization($value);
+ }
+ return $data;
+ }
+
+ if (is_object($data)) {
+ $values = [];
+ if ($data instanceof ModelInterface) {
+ $formats = $data::openAPIFormats();
+ foreach ($data::openAPITypes() as $property => $openAPIType) {
+ $getter = $data::getters()[$property];
+ $value = $data->$getter();
+ if ($value !== null && !in_array($openAPIType, [{{&primitives}}], true)) {
+ $callable = [$openAPIType, 'getAllowableEnumValues'];
+ if (is_callable($callable)) {
+ /** array $callable */
+ $allowedEnumTypes = $callable();
+ if (!in_array($value, $allowedEnumTypes, true)) {
+ $imploded = implode("', '", $allowedEnumTypes);
+ throw new \InvalidArgumentException("Invalid value for enum '$openAPIType', must be one of: '$imploded'");
+ }
+ }
+ }
+ if (($data::isNullable($property) && $data->isNullableSetToNull($property)) || $value !== null) {
+ $values[$data::attributeMap()[$property]] = self::sanitizeForSerialization($value, $openAPIType, $formats[$property]);
+ }
+ }
+ } else {
+ foreach($data as $property => $value) {
+ $values[$property] = self::sanitizeForSerialization($value);
+ }
+ }
+ return (object)$values;
+ } else {
+ return (string)$data;
+ }
+ }
+
+ /**
+ * Sanitize filename by removing path.
+ * e.g. ../../sun.gif becomes sun.gif
+ *
+ * @param string $filename filename to be sanitized
+ *
+ * @return string the sanitized filename
+ */
+ public static function sanitizeFilename($filename)
+ {
+ if (preg_match("/.*[\/\\\\](.*)$/", $filename, $match)) {
+ return $match[1];
+ } else {
+ return $filename;
+ }
+ }
+
+ /**
+ * Shorter timestamp microseconds to 6 digits length.
+ *
+ * @param string $timestamp Original timestamp
+ *
+ * @return string the shorten timestamp
+ */
+ public static function sanitizeTimestamp($timestamp)
+ {
+ if (!is_string($timestamp)) return $timestamp;
+
+ return preg_replace('/(:\d{2}.\d{6})\d*/', '$1', $timestamp);
+ }
+
+ /**
+ * Take value and turn it into a string suitable for inclusion in
+ * the path, by url-encoding.
+ *
+ * @param string $value a string which will be part of the path
+ *
+ * @return string the serialized object
+ */
+ public static function toPathValue($value)
+ {
+ return rawurlencode(self::toString($value));
+ }
+
+ /**
+ * Checks if a value is empty, based on its OpenAPI type.
+ *
+ * @param mixed $value
+ * @param string $openApiType
+ *
+ * @return bool true if $value is empty
+ */
+ private static function isEmptyValue($value, string $openApiType): bool
+ {
+ # If empty() returns false, it is not empty regardless of its type.
+ if (!empty($value)) {
+ return false;
+ }
+
+ # Null is always empty, as we cannot send a real "null" value in a query parameter.
+ if ($value === null) {
+ return true;
+ }
+
+ switch ($openApiType) {
+ # For numeric values, false and '' are considered empty.
+ # This comparison is safe for floating point values, since the previous call to empty() will
+ # filter out values that don't match 0.
+ case 'int':
+ case 'integer':
+ return $value !== 0;
+
+ case 'number':
+ case 'float':
+ return $value !== 0 && $value !== 0.0;
+
+ # For boolean values, '' is considered empty
+ case 'bool':
+ case 'boolean':
+ return !in_array($value, [false, 0], true);
+
+ # For string values, '' is considered empty.
+ case 'string':
+ return $value === '';
+
+ # For all the other types, any value at this point can be considered empty.
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Take query parameter properties and turn it into an array suitable for
+ * native http_build_query or GuzzleHttp\Psr7\Query::build.
+ *
+ * @param mixed $value Parameter value
+ * @param string $paramName Parameter name
+ * @param string $openApiType OpenAPIType eg. array or object
+ * @param string $style Parameter serialization style
+ * @param bool $explode Parameter explode option
+ * @param bool $required Whether query param is required or not
+ *
+ * @return array
+ */
+ public static function toQueryValue(
+ $value,
+ string $paramName,
+ string $openApiType = 'string',
+ string $style = 'form',
+ bool $explode = true,
+ bool $required = true
+ ): array {
+
+ # Check if we should omit this parameter from the query. This should only happen when:
+ # - Parameter is NOT required; AND
+ # - its value is set to a value that is equivalent to "empty", depending on its OpenAPI type. For
+ # example, 0 as "int" or "boolean" is NOT an empty value.
+ if (self::isEmptyValue($value, $openApiType)) {
+ if ($required) {
+ return ["{$paramName}" => ''];
+ } else {
+ return [];
+ }
+ }
+
+ # Handle DateTime objects in query
+ if($openApiType === "\\DateTime" && $value instanceof \DateTime) {
+ return ["{$paramName}" => $value->format(self::$dateTimeFormat)];
+ }
+
+ $query = [];
+ $value = (in_array($openApiType, ['object', 'array'], true)) ? (array)$value : $value;
+
+ // since \GuzzleHttp\Psr7\Query::build fails with nested arrays
+ // need to flatten array first
+ $flattenArray = function ($arr, $name, &$result = []) use (&$flattenArray, $style, $explode) {
+ if (!is_array($arr)) return $arr;
+
+ foreach ($arr as $k => $v) {
+ $prop = ($style === 'deepObject') ? $prop = "{$name}[{$k}]" : $k;
+
+ if (is_array($v)) {
+ $flattenArray($v, $prop, $result);
+ } else {
+ if ($style !== 'deepObject' && !$explode) {
+ // push key itself
+ $result[] = $prop;
+ }
+ $result[$prop] = $v;
+ }
+ }
+ return $result;
+ };
+
+ $value = $flattenArray($value, $paramName);
+
+ // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#style-values
+ if ($openApiType === 'array' && $style === 'deepObject' && $explode) {
+ return $value;
+ }
+
+ if ($openApiType === 'object' && ($style === 'deepObject' || $explode)) {
+ return $value;
+ }
+
+ if ('boolean' === $openApiType && is_bool($value)) {
+ $value = self::convertBoolToQueryStringFormat($value);
+ }
+
+ // handle style in serializeCollection
+ $query[$paramName] = ($explode) ? $value : self::serializeCollection((array)$value, $style);
+
+ return $query;
+ }
+
+ /**
+ * Convert boolean value to format for query string.
+ *
+ * @param bool $value Boolean value
+ *
+ * @return int|string Boolean value in format
+ */
+ public static function convertBoolToQueryStringFormat(bool $value)
+ {
+ if (Configuration::BOOLEAN_FORMAT_STRING == Configuration::getDefaultConfiguration()->getBooleanFormatForQueryString()) {
+ return $value ? 'true' : 'false';
+ }
+
+ return (int) $value;
+ }
+
+ /**
+ * Take value and turn it into a string suitable for inclusion in
+ * the header. If it's a string, pass through unchanged
+ * If it's a datetime object, format it in ISO8601
+ *
+ * @param string $value a string which will be part of the header
+ *
+ * @return string the header string
+ */
+ public static function toHeaderValue($value)
+ {
+ $callable = [$value, 'toHeaderValue'];
+ if (is_callable($callable)) {
+ return $callable();
+ }
+
+ return self::toString($value);
+ }
+
+ /**
+ * Take value and turn it into a string suitable for inclusion in
+ * the http body (form parameter). If it's a string, pass through unchanged
+ * If it's a datetime object, format it in ISO8601
+ *
+ * @param string|\SplFileObject $value the value of the form parameter
+ *
+ * @return string the form string
+ */
+ public static function toFormValue($value)
+ {
+ if ($value instanceof \SplFileObject) {
+ return $value->getRealPath();
+ } else {
+ return self::toString($value);
+ }
+ }
+
+ /**
+ * Take value and turn it into a string suitable for inclusion in
+ * the parameter. If it's a string, pass through unchanged
+ * If it's a datetime object, format it in ISO8601
+ * If it's a boolean, convert it to "true" or "false".
+ *
+ * @param float|int|bool|\DateTime $value the value of the parameter
+ *
+ * @return string the header string
+ */
+ public static function toString($value)
+ {
+ if ($value instanceof \DateTime) { // datetime in ISO8601 format
+ return $value->format(self::$dateTimeFormat);
+ } elseif (is_bool($value)) {
+ return $value ? 'true' : 'false';
+ } else {
+ return (string) $value;
+ }
+ }
+
+ /**
+ * Serialize an array to a string.
+ *
+ * @param array $collection collection to serialize to a string
+ * @param string $style the format use for serialization (csv,
+ * ssv, tsv, pipes, multi)
+ * @param bool $allowCollectionFormatMulti allow collection format to be a multidimensional array
+ *
+ * @return string
+ */
+ public static function serializeCollection(array $collection, $style, $allowCollectionFormatMulti = false)
+ {
+ if ($allowCollectionFormatMulti && ('multi' === $style)) {
+ // http_build_query() almost does the job for us. We just
+ // need to fix the result of multidimensional arrays.
+ return preg_replace('/%5B[0-9]+%5D=/', '=', http_build_query($collection, '', '&'));
+ }
+ switch ($style) {
+ case 'pipeDelimited':
+ case 'pipes':
+ return implode('|', $collection);
+
+ case 'tsv':
+ return implode("\t", $collection);
+
+ case 'spaceDelimited':
+ case 'ssv':
+ return implode(' ', $collection);
+
+ case 'simple':
+ case 'csv':
+ // Deliberate fall through. CSV is default format.
+ default:
+ return implode(',', $collection);
+ }
+ }
+
+ /**
+ * Deserialize a JSON string into an object
+ *
+ * @param mixed $data object or primitive to be deserialized
+ * @param string $class class name is passed as a string
+ * @param string[]|null $httpHeaders HTTP headers
+ *
+ * @return object|array|null a single or an array of $class instances
+ */
+ public static function deserialize($data, $class, $httpHeaders = null)
+ {
+ if (null === $data) {
+ return null;
+ }
+
+ if (strcasecmp(substr($class, -2), '[]') === 0) {
+ $data = is_string($data) ? json_decode($data) : $data;
+
+ if (!is_array($data)) {
+ throw new \InvalidArgumentException("Invalid array '$class'");
+ }
+
+ $subClass = substr($class, 0, -2);
+ $values = [];
+ foreach ($data as $key => $value) {
+ $values[] = self::deserialize($value, $subClass, null);
+ }
+ return $values;
+ }
+
+ if (preg_match('/^(array<|map\[)/', $class)) { // for associative array e.g. array
+ $data = is_string($data) ? json_decode($data) : $data;
+ settype($data, 'array');
+ $inner = substr($class, 4, -1);
+ $deserialized = [];
+ if (strrpos($inner, ",") !== false) {
+ $subClass_array = explode(',', $inner, 2);
+ $subClass = $subClass_array[1];
+ foreach ($data as $key => $value) {
+ $deserialized[$key] = self::deserialize($value, $subClass, null);
+ }
+ }
+ return $deserialized;
+ }
+
+ if ($class === 'object') {
+ settype($data, 'array');
+ return $data;
+ } elseif ($class === 'mixed') {
+ settype($data, gettype($data));
+ return $data;
+ }
+
+ if ($class === '\DateTime') {
+ // Some APIs return an invalid, empty string as a
+ // date-time property. DateTime::__construct() will return
+ // the current time for empty input which is probably not
+ // what is meant. The invalid empty string is probably to
+ // be interpreted as a missing field/value. Let's handle
+ // this graceful.
+ if (!empty($data)) {
+ try {
+ return new \DateTime($data);
+ } catch (\Exception $exception) {
+ // Some APIs return a date-time with too high nanosecond
+ // precision for php's DateTime to handle.
+ // With provided regexp 6 digits of microseconds saved
+ return new \DateTime(self::sanitizeTimestamp($data));
+ }
+ } else {
+ return null;
+ }
+ }
+
+ if ($class === '\SplFileObject') {
+ $data = Utils::streamFor($data);
+
+ /** @var \Psr\Http\Message\StreamInterface $data */
+
+ // determine file name
+ if (
+ is_array($httpHeaders)
+ && array_key_exists('Content-Disposition', $httpHeaders)
+ && preg_match('/inline; filename=[\'"]?([^\'"\s]+)[\'"]?$/i', $httpHeaders['Content-Disposition'], $match)
+ ) {
+ $filename = Configuration::getDefaultConfiguration()->getTempFolderPath() . DIRECTORY_SEPARATOR . self::sanitizeFilename($match[1]);
+ } else {
+ $filename = tempnam(Configuration::getDefaultConfiguration()->getTempFolderPath(), '');
+ }
+
+ $file = fopen($filename, 'w');
+ while ($chunk = $data->read(200)) {
+ fwrite($file, $chunk);
+ }
+ fclose($file);
+
+ return new \SplFileObject($filename, 'r');
+ }
+
+ /** @psalm-suppress ParadoxicalCondition */
+ if (in_array($class, [{{&primitives}}], true)) {
+ settype($data, $class);
+ return $data;
+ }
+
+
+ if (method_exists($class, 'getAllowableEnumValues')) {
+ if (!in_array($data, $class::getAllowableEnumValues(), true)) {
+ $imploded = implode("', '", $class::getAllowableEnumValues());
+ throw new \InvalidArgumentException("Invalid value for enum '$class', must be one of: '$imploded'");
+ }
+ return $data;
+ } else {
+ $data = is_string($data) ? json_decode($data) : $data;
+
+ if (is_array($data)) {
+ $data = (object)$data;
+ }
+
+ if (is_object($data) && method_exists($class, 'fromAssocArray')) {
+ return $class::fromAssocArray(self::objectToArray($data));
+ }
+
+ // If a discriminator is defined and points to a valid subclass, use it.
+ $discriminator = $class::DISCRIMINATOR;
+ if (!empty($discriminator) && isset($data->{$discriminator}) && is_string($data->{$discriminator})) {
+ $subclass = '\{{invokerPackage}}\Model\\' . $data->{$discriminator};
+ if (is_subclass_of($subclass, $class)) {
+ $class = $subclass;
+ }
+ }
+
+ /** @var ModelInterface $instance */
+ $instance = new $class();
+ foreach ($instance::openAPITypes() as $property => $type) {
+ $propertySetter = $instance::setters()[$property];
+
+ if (!isset($propertySetter)) {
+ continue;
+ }
+
+ if (!isset($data->{$instance::attributeMap()[$property]})) {
+ if ($instance::isNullable($property)) {
+ $instance->$propertySetter(null);
+ }
+
+ continue;
+ }
+
+ if (isset($data->{$instance::attributeMap()[$property]})) {
+ $propertyValue = $data->{$instance::attributeMap()[$property]};
+ $instance->$propertySetter(self::deserialize($propertyValue, $type, null));
+ }
+ }
+ return $instance;
+ }
+ }
+
+ /**
+ * Build a query string from an array of key value pairs.
+ *
+ * This function can use the return value of `parse()` to build a query
+ * string. This function does not modify the provided keys when an array is
+ * encountered (like `http_build_query()` would).
+ *
+ * The function is copied from https://github.com/guzzle/psr7/blob/a243f80a1ca7fe8ceed4deee17f12c1930efe662/src/Query.php#L59-L112
+ * with a modification which is described in https://github.com/guzzle/psr7/pull/603
+ *
+ * @param array $params Query string parameters.
+ * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
+ * to encode using RFC3986, or PHP_QUERY_RFC1738
+ * to encode using RFC1738.
+ */
+ public static function buildQuery(array $params, $encoding = PHP_QUERY_RFC3986): string
+ {
+ if (!$params) {
+ return '';
+ }
+
+ if ($encoding === false) {
+ $encoder = function (string $str): string {
+ return $str;
+ };
+ } elseif ($encoding === PHP_QUERY_RFC3986) {
+ $encoder = 'rawurlencode';
+ } elseif ($encoding === PHP_QUERY_RFC1738) {
+ $encoder = 'urlencode';
+ } else {
+ throw new \InvalidArgumentException('Invalid type');
+ }
+
+ $castBool = Configuration::BOOLEAN_FORMAT_INT == Configuration::getDefaultConfiguration()->getBooleanFormatForQueryString()
+ ? function ($v) { return (int) $v; }
+ : function ($v) { return $v ? 'true' : 'false'; };
+
+ $qs = '';
+ foreach ($params as $k => $v) {
+ $k = $encoder((string) $k);
+ if (!is_array($v)) {
+ $qs .= $k;
+ $v = is_bool($v) ? $castBool($v) : $v;
+ if ($v !== null) {
+ $qs .= '='.$encoder((string) $v);
+ }
+ $qs .= '&';
+ } else {
+ foreach ($v as $vv) {
+ $qs .= $k;
+ $vv = is_bool($vv) ? $castBool($vv) : $vv;
+ if ($vv !== null) {
+ $qs .= '='.$encoder((string) $vv);
+ }
+ $qs .= '&';
+ }
+ }
+ }
+
+ return $qs ? (string) substr($qs, 0, -1) : '';
+ }
+
+
+ private static function objectToArray($obj)
+ {
+ if (is_object($obj)) {
+ $obj = (array)$obj;
+ }
+ if (is_array($obj)) {
+ return array_map(fn($x) => self::objectToArray($x), $obj);
+ } else {
+ return $obj;
+ }
+ }
+}