Skip to content

Commit a92b4fc

Browse files
dimitropoulosgugu
andauthored
feat: PHP JSON body encoding (Kong#291)
* php/curl: use json_encode for CURLOPT_POSTFIELDS * php/http1: use json_encode when body is JSON * php/http2: use json_encode when body is JSON Co-authored-by: Andrii Kostenko <[email protected]>
1 parent 4ac5253 commit a92b4fc

12 files changed

Lines changed: 155 additions & 80 deletions

src/targets/php/curl/client.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
import { CodeBuilder } from '../../../helpers/code-builder';
1212
import { Client } from '../../targets';
13+
import { convertType } from '../helpers';
1314

1415
export interface CurlOptions {
1516
closingTag?: boolean;
1617
maxRedirects?: number;
17-
nameErrors?: boolean;
18+
namedErrors?: boolean;
1819
noTags?: boolean;
1920
shortTags?: boolean;
2021
timeout?: number;
@@ -27,22 +28,24 @@ export const curl: Client<CurlOptions> = {
2728
link: 'http://php.net/manual/en/book.curl.php',
2829
description: 'PHP with ext-curl',
2930
},
30-
convert: ({ uriObj, postData, fullUrl, method, httpVersion, cookies, headersObj }, options) => {
31-
const opts = {
32-
closingTag: false,
33-
indent: ' ',
34-
maxRedirects: 10,
35-
namedErrors: false,
36-
noTags: false,
37-
shortTags: false,
38-
timeout: 30,
39-
...options,
40-
};
41-
42-
const { push, blank, join } = new CodeBuilder({ indent: opts.indent });
43-
44-
if (!opts.noTags) {
45-
push(opts.shortTags ? '<?' : '<?php');
31+
convert: (
32+
{ uriObj, postData, fullUrl, method, httpVersion, cookies, headersObj },
33+
options = {},
34+
) => {
35+
const {
36+
closingTag = false,
37+
indent = ' ',
38+
maxRedirects = 10,
39+
namedErrors = false,
40+
noTags = false,
41+
shortTags = false,
42+
timeout = 30,
43+
} = options;
44+
45+
const { push, blank, join } = new CodeBuilder({ indent });
46+
47+
if (!noTags) {
48+
push(shortTags ? '<?' : '<?php');
4649
blank();
4750
}
4851

@@ -73,12 +76,12 @@ export const curl: Client<CurlOptions> = {
7376
{
7477
escape: false,
7578
name: 'CURLOPT_MAXREDIRS',
76-
value: opts.maxRedirects,
79+
value: maxRedirects,
7780
},
7881
{
7982
escape: false,
8083
name: 'CURLOPT_TIMEOUT',
81-
value: opts.timeout,
84+
value: timeout,
8285
},
8386
{
8487
escape: false,
@@ -91,15 +94,19 @@ export const curl: Client<CurlOptions> = {
9194
value: method,
9295
},
9396
{
94-
escape: true,
97+
escape: !postData.jsonObj,
9598
name: 'CURLOPT_POSTFIELDS',
96-
value: postData ? postData.text : undefined,
99+
value: postData
100+
? postData.jsonObj
101+
? `json_encode(${convertType(postData.jsonObj, indent.repeat(2), indent)})`
102+
: postData.text
103+
: undefined,
97104
},
98105
];
99106

100107
push('curl_setopt_array($curl, [');
101108

102-
const curlopts = new CodeBuilder({ indent: opts.indent, join: `\n${opts.indent}` });
109+
const curlopts = new CodeBuilder({ indent, join: `\n${indent}` });
103110

104111
curlOptions.forEach(({ value, name, escape }) => {
105112
if (value !== null && value !== undefined) {
@@ -122,7 +129,7 @@ export const curl: Client<CurlOptions> = {
122129

123130
if (headers.length) {
124131
curlopts.push('CURLOPT_HTTPHEADER => [');
125-
curlopts.push(headers.join(`,\n${opts.indent}${opts.indent}`), 1);
132+
curlopts.push(headers.join(`,\n${indent}${indent}`), 1);
126133
curlopts.push('],');
127134
}
128135

@@ -136,7 +143,7 @@ export const curl: Client<CurlOptions> = {
136143
blank();
137144
push('if ($err) {');
138145

139-
if (opts.namedErrors) {
146+
if (namedErrors) {
140147
push('echo array_flip(get_defined_constants(true)["curl"])[$err];', 1);
141148
} else {
142149
push('echo "cURL Error #:" . $err;', 1);
@@ -146,7 +153,7 @@ export const curl: Client<CurlOptions> = {
146153
push('echo $response;', 1);
147154
push('}');
148155

149-
if (!opts.noTags && opts.closingTag) {
156+
if (!noTags && closingTag) {
150157
blank();
151158
push('?>');
152159
}

src/targets/php/curl/fixtures/application-json.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,28 @@
1010
CURLOPT_TIMEOUT => 30,
1111
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
1212
CURLOPT_CUSTOMREQUEST => "POST",
13-
CURLOPT_POSTFIELDS => "{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}],\"boolean\":false}",
13+
CURLOPT_POSTFIELDS => json_encode([
14+
'number' => 1,
15+
'string' => 'f"oo',
16+
'arr' => [
17+
1,
18+
2,
19+
3
20+
],
21+
'nested' => [
22+
'a' => 'b'
23+
],
24+
'arr_mix' => [
25+
1,
26+
'a',
27+
[
28+
'arr_mix_nested' => [
29+
30+
]
31+
]
32+
],
33+
'boolean' => null
34+
]),
1435
CURLOPT_HTTPHEADER => [
1536
"content-type: application/json"
1637
],

src/targets/php/curl/fixtures/jsonObj-multiline.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
CURLOPT_TIMEOUT => 30,
1111
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
1212
CURLOPT_CUSTOMREQUEST => "POST",
13-
CURLOPT_POSTFIELDS => "{\n \"foo\": \"bar\"\n}",
13+
CURLOPT_POSTFIELDS => json_encode([
14+
'foo' => 'bar'
15+
]),
1416
CURLOPT_HTTPHEADER => [
1517
"content-type: application/json"
1618
],

src/targets/php/curl/fixtures/jsonObj-null-value.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
CURLOPT_TIMEOUT => 30,
1111
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
1212
CURLOPT_CUSTOMREQUEST => "POST",
13-
CURLOPT_POSTFIELDS => "{\"foo\":null}",
13+
CURLOPT_POSTFIELDS => json_encode([
14+
'foo' => null
15+
]),
1416
CURLOPT_HTTPHEADER => [
1517
"content-type: application/json"
1618
],

src/targets/php/http1/client.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,13 @@ export const http1: Client<Http1Options> = {
2525
link: 'http://php.net/manual/en/book.http.php',
2626
description: 'PHP with pecl/http v1',
2727
},
28-
convert: ({ method, url, postData, queryObj, headersObj, cookiesObj }, options) => {
29-
const opts = {
30-
closingTag: false,
31-
indent: ' ',
32-
noTags: false,
33-
shortTags: false,
34-
...options,
35-
};
36-
37-
const { push, blank, join } = new CodeBuilder({ indent: opts.indent });
38-
39-
if (!opts.noTags) {
40-
push(opts.shortTags ? '<?' : '<?php');
28+
convert: ({ method, url, postData, queryObj, headersObj, cookiesObj }, options = {}) => {
29+
const { closingTag = false, indent = ' ', noTags = false, shortTags = false } = options;
30+
31+
const { push, blank, join } = new CodeBuilder({ indent });
32+
33+
if (!noTags) {
34+
push(shortTags ? '<?' : '<?php');
4135
blank();
4236
}
4337

@@ -57,24 +51,30 @@ export const http1: Client<Http1Options> = {
5751
blank();
5852

5953
if (Object.keys(queryObj).length) {
60-
push(`$request->setQueryData(${convertType(queryObj, opts.indent)});`);
54+
push(`$request->setQueryData(${convertType(queryObj, indent)});`);
6155
blank();
6256
}
6357

6458
if (Object.keys(headersObj).length) {
65-
push(`$request->setHeaders(${convertType(headersObj, opts.indent)});`);
59+
push(`$request->setHeaders(${convertType(headersObj, indent)});`);
6660
blank();
6761
}
6862

6963
if (Object.keys(cookiesObj).length) {
70-
push(`$request->setCookies(${convertType(cookiesObj, opts.indent)});`);
64+
push(`$request->setCookies(${convertType(cookiesObj, indent)});`);
7165
blank();
7266
}
7367

7468
switch (postData.mimeType) {
7569
case 'application/x-www-form-urlencoded':
7670
push(`$request->setContentType(${convertType(postData.mimeType)});`);
77-
push(`$request->setPostFields(${convertType(postData.paramsObj, opts.indent)});`);
71+
push(`$request->setPostFields(${convertType(postData.paramsObj, indent)});`);
72+
blank();
73+
break;
74+
75+
case 'application/json':
76+
push(`$request->setContentType(${convertType(postData.mimeType)});`);
77+
push(`$request->setBody(json_encode(${convertType(postData.jsonObj, indent)}));`);
7878
blank();
7979
break;
8080

@@ -93,7 +93,7 @@ export const http1: Client<Http1Options> = {
9393
push('echo $ex;', 1);
9494
push('}');
9595

96-
if (!opts.noTags && opts.closingTag) {
96+
if (!noTags && closingTag) {
9797
blank();
9898
push('?>');
9999
}

src/targets/php/http1/fixtures/application-json.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,29 @@
88
'content-type' => 'application/json'
99
]);
1010

11-
$request->setBody('{"number":1,"string":"f\\"oo","arr":[1,2,3],"nested":{"a":"b"},"arr_mix":[1,"a",{"arr_mix_nested":{}}],"boolean":false}');
11+
$request->setContentType('application/json');
12+
$request->setBody(json_encode([
13+
'number' => 1,
14+
'string' => 'f"oo',
15+
'arr' => [
16+
1,
17+
2,
18+
3
19+
],
20+
'nested' => [
21+
'a' => 'b'
22+
],
23+
'arr_mix' => [
24+
1,
25+
'a',
26+
[
27+
'arr_mix_nested' => [
28+
29+
]
30+
]
31+
],
32+
'boolean' => null
33+
]));
1234

1335
try {
1436
$response = $request->send();

src/targets/php/http1/fixtures/jsonObj-multiline.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
'content-type' => 'application/json'
99
]);
1010

11-
$request->setBody('{
12-
"foo": "bar"
13-
}');
11+
$request->setContentType('application/json');
12+
$request->setBody(json_encode([
13+
'foo' => 'bar'
14+
]));
1415

1516
try {
1617
$response = $request->send();

src/targets/php/http1/fixtures/jsonObj-null-value.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
'content-type' => 'application/json'
99
]);
1010

11-
$request->setBody('{"foo":null}');
11+
$request->setContentType('application/json');
12+
$request->setBody(json_encode([
13+
'foo' => null
14+
]));
1215

1316
try {
1417
$response = $request->send();

src/targets/php/http2/client.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,14 @@ export const http2: Client<Http2Options> = {
2626
link: 'http://devel-m6w6.rhcloud.com/mdref/http',
2727
description: 'PHP with pecl/http v2',
2828
},
29-
convert: ({ postData, headersObj, method, queryObj, cookiesObj, url }, options) => {
30-
const opts = {
31-
closingTag: false,
32-
indent: ' ',
33-
noTags: false,
34-
shortTags: false,
35-
...options,
36-
};
37-
38-
const { push, blank, join } = new CodeBuilder({ indent: opts.indent });
29+
convert: ({ postData, headersObj, method, queryObj, cookiesObj, url }, options = {}) => {
30+
const { closingTag = false, indent = ' ', noTags = false, shortTags = false } = options;
31+
32+
const { push, blank, join } = new CodeBuilder({ indent });
3933
let hasBody = false;
4034

41-
if (!opts.noTags) {
42-
push(opts.shortTags ? '<?' : '<?php');
35+
if (!noTags) {
36+
push(shortTags ? '<?' : '<?php');
4337
blank();
4438
}
4539

@@ -50,9 +44,7 @@ export const http2: Client<Http2Options> = {
5044
switch (postData.mimeType) {
5145
case 'application/x-www-form-urlencoded':
5246
push('$body = new http\\Message\\Body;');
53-
push(
54-
`$body->append(new http\\QueryString(${convertType(postData.paramsObj, opts.indent)}));`,
55-
);
47+
push(`$body->append(new http\\QueryString(${convertType(postData.paramsObj, indent)}));`);
5648
blank();
5749
hasBody = true;
5850
break;
@@ -85,8 +77,8 @@ export const http2: Client<Http2Options> = {
8577
}
8678
});
8779

88-
const field = Object.keys(fields).length ? convertType(fields, opts.indent) : 'null';
89-
const formValue = files.length ? convertType(files, opts.indent) : 'null';
80+
const field = Object.keys(fields).length ? convertType(fields, indent) : 'null';
81+
const formValue = files.length ? convertType(files, indent) : 'null';
9082

9183
push('$body = new http\\Message\\Body;');
9284
push(`$body->addForm(${field}, ${formValue});`);
@@ -106,6 +98,11 @@ export const http2: Client<Http2Options> = {
10698
hasBody = true;
10799
break;
108100
}
101+
case 'application/json':
102+
push('$body = new http\\Message\\Body;');
103+
push(`$body->append(json_encode(${convertType(postData.jsonObj, indent)}));`);
104+
hasBody = true;
105+
break;
109106

110107
default:
111108
if (postData.text) {
@@ -125,18 +122,18 @@ export const http2: Client<Http2Options> = {
125122
}
126123

127124
if (Object.keys(queryObj).length) {
128-
push(`$request->setQuery(new http\\QueryString(${convertType(queryObj, opts.indent)}));`);
125+
push(`$request->setQuery(new http\\QueryString(${convertType(queryObj, indent)}));`);
129126
blank();
130127
}
131128

132129
if (Object.keys(headersObj).length) {
133-
push(`$request->setHeaders(${convertType(headersObj, opts.indent)});`);
130+
push(`$request->setHeaders(${convertType(headersObj, indent)});`);
134131
blank();
135132
}
136133

137134
if (Object.keys(cookiesObj).length) {
138135
blank();
139-
push(`$client->setCookies(${convertType(cookiesObj, opts.indent)});`);
136+
push(`$client->setCookies(${convertType(cookiesObj, indent)});`);
140137
blank();
141138
}
142139

@@ -145,7 +142,7 @@ export const http2: Client<Http2Options> = {
145142
blank();
146143
push('echo $response->getBody();');
147144

148-
if (!opts.noTags && opts.closingTag) {
145+
if (!noTags && closingTag) {
149146
blank();
150147
push('?>');
151148
}

0 commit comments

Comments
 (0)