Skip to content

Commit 76137ca

Browse files
committed
Fix php-curl-class#538: Allow objects implementing JsonSerializable to be serialized to JSON when passed as request data
1 parent 0c6ff28 commit 76137ca

File tree

4 files changed

+76
-25
lines changed

4 files changed

+76
-25
lines changed

src/Curl/Curl.php

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -139,32 +139,39 @@ public function beforeSend($callback)
139139
public function buildPostData($data)
140140
{
141141
$binary_data = false;
142-
if (is_array($data)) {
143-
// Return JSON-encoded string when the request's content-type is JSON.
144-
if (isset($this->headers['Content-Type']) &&
145-
preg_match($this->jsonPattern, $this->headers['Content-Type'])) {
146-
$data = \Curl\json_encode($data);
147-
} else {
148-
// Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch,
149-
// CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are
150-
// referenced.
151-
if (ArrayUtil::is_array_multidim($data)) {
152-
$data = ArrayUtil::array_flatten_multidim($data);
153-
}
154142

155-
// Modify array values to ensure any referenced files are properly handled depending on the support of
156-
// the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the
157-
// @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore
158-
// non-file values prefixed with the @ character.
159-
foreach ($data as $key => $value) {
160-
if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
161-
$binary_data = true;
162-
if (class_exists('CURLFile')) {
163-
$data[$key] = new \CURLFile(substr($value, 1));
164-
}
165-
} elseif ($value instanceof \CURLFile) {
166-
$binary_data = true;
143+
// Return JSON-encoded string when the request's content-type is JSON and the data is serializable.
144+
if (isset($this->headers['Content-Type']) &&
145+
preg_match($this->jsonPattern, $this->headers['Content-Type']) &&
146+
(
147+
is_array($data) ||
148+
(
149+
is_object($data) &&
150+
interface_exists('JsonSerializable', false) &&
151+
$data instanceof \JsonSerializable
152+
)
153+
)) {
154+
$data = \Curl\json_encode($data);
155+
} elseif (is_array($data)) {
156+
// Manually build a single-dimensional array from a multi-dimensional array as using curl_setopt($ch,
157+
// CURLOPT_POSTFIELDS, $data) doesn't correctly handle multi-dimensional arrays when files are
158+
// referenced.
159+
if (ArrayUtil::is_array_multidim($data)) {
160+
$data = ArrayUtil::array_flatten_multidim($data);
161+
}
162+
163+
// Modify array values to ensure any referenced files are properly handled depending on the support of
164+
// the @filename API or CURLFile usage. This also fixes the warning "curl_setopt(): The usage of the
165+
// @filename API for file uploading is deprecated. Please use the CURLFile class instead". Ignore
166+
// non-file values prefixed with the @ character.
167+
foreach ($data as $key => $value) {
168+
if (is_string($value) && strpos($value, '@') === 0 && is_file(substr($value, 1))) {
169+
$binary_data = true;
170+
if (class_exists('CURLFile')) {
171+
$data[$key] = new \CURLFile(substr($value, 1));
167172
}
173+
} elseif ($value instanceof \CURLFile) {
174+
$binary_data = true;
168175
}
169176
}
170177
}

tests/PHPCurlClass/Helper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Helper;
44

5-
use Curl\Curl;
5+
use \Curl\Curl;
66

77
class Test
88
{

tests/PHPCurlClass/PHPCurlClassTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use \Curl\CaseInsensitiveArray;
66
use \Curl\Curl;
77
use \Helper\Test;
8+
use \Helper\User;
89

910
class CurlTest extends \PHPUnit\Framework\TestCase
1011
{
@@ -3715,4 +3716,20 @@ public function testProxySettings()
37153716
$curl->unsetProxy();
37163717
$this->assertNull($curl->getOpt(CURLOPT_PROXY));
37173718
}
3719+
3720+
public function testJsonSerializable()
3721+
{
3722+
if (!interface_exists('JsonSerializable')) {
3723+
$this->markTestSkipped();
3724+
}
3725+
3726+
$expected_response = '{"name":"Alice","email":"alice@example.com"}';
3727+
3728+
$user = new \Helper\User('Alice', 'alice@example.com');
3729+
$this->assertEquals($expected_response, json_encode($user));
3730+
3731+
$test = new Test();
3732+
$test->curl->setHeader('Content-Type', 'application/json');
3733+
$this->assertEquals($expected_response, $test->server('post_json', 'POST', $user));
3734+
}
37183735
}

tests/PHPCurlClass/User.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Helper;
4+
5+
// Check interface exists to fix "Fatal error: Interface 'JsonSerializable' not found in ../tests/PHPCurlClass/User.php
6+
// on line X".
7+
if (interface_exists('JsonSerializable')) {
8+
class User implements \JsonSerializable
9+
{
10+
private $name;
11+
private $email;
12+
13+
public function __construct($name = null, $email = null)
14+
{
15+
$this->name = $name;
16+
$this->email = $email;
17+
}
18+
19+
public function jsonSerialize()
20+
{
21+
return array(
22+
'name' => $this->name,
23+
'email' => $this->email,
24+
);
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)