[PHP] Fix string length validation (#7953)

* Add a test case which reproduces the issue

https://github.com/swagger-api/swagger-codegen/issues/7846

* Change `strlen` -> `mb_strlen` in order to count the length correctly

* Regenerate the samples
This commit is contained in:
Akihito Nakano 2018-04-06 15:20:05 +09:00 committed by William Cheng
parent 37faaf9266
commit 4a5d16b236
8 changed files with 519 additions and 13 deletions

View File

@ -205,13 +205,13 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}{{^pa
{{/isEnum}}
{{#hasValidation}}
{{#maxLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(strlen($this->container['{{name}}']) > {{maxLength}})) {
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) > {{maxLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be smaller than or equal to {{{maxLength}}}.";
}
{{/maxLength}}
{{#minLength}}
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(strlen($this->container['{{name}}']) < {{minLength}})) {
if ({{^required}}!is_null($this->container['{{name}}']) && {{/required}}(mb_strlen($this->container['{{name}}']) < {{minLength}})) {
$invalidProperties[] = "invalid value for '{{name}}', the character length must be bigger than or equal to {{{minLength}}}.";
}
@ -281,12 +281,12 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}{{^pa
{{/isEnum}}
{{#hasValidation}}
{{#maxLength}}
if (strlen($this->container['{{name}}']) > {{maxLength}}) {
if (mb_strlen($this->container['{{name}}']) > {{maxLength}}) {
return false;
}
{{/maxLength}}
{{#minLength}}
if (strlen($this->container['{{name}}']) < {{minLength}}) {
if (mb_strlen($this->container['{{name}}']) < {{minLength}}) {
return false;
}
{{/minLength}}
@ -366,11 +366,11 @@ class {{classname}} {{#parentSchema}}extends {{{parent}}} {{/parentSchema}}{{^pa
{{/isEnum}}
{{#hasValidation}}
{{#maxLength}}
if ({{^required}}!is_null(${{name}}) && {{/required}}(strlen(${{name}}) > {{maxLength}})) {
if ({{^required}}!is_null(${{name}}) && {{/required}}(mb_strlen(${{name}}) > {{maxLength}})) {
throw new \InvalidArgumentException('invalid length for ${{name}} when calling {{classname}}.{{operationId}}, must be smaller than or equal to {{maxLength}}.');
}{{/maxLength}}
{{#minLength}}
if ({{^required}}!is_null(${{name}}) && {{/required}}(strlen(${{name}}) < {{minLength}})) {
if ({{^required}}!is_null(${{name}}) && {{/required}}(mb_strlen(${{name}}) < {{minLength}})) {
throw new \InvalidArgumentException('invalid length for ${{name}} when calling {{classname}}.{{operationId}}, must be bigger than or equal to {{minLength}}.');
}
{{/minLength}}

View File

@ -271,7 +271,7 @@ class ObjectSerializer
// determine file name
if (array_key_exists('Content-Disposition', $httpHeaders) &&
preg_match('/inline; filename=[\'"]?([^\'"\s]+)[\'"]?$/i', $httpHeaders['Content-Disposition'], $match)) {
$filename = Configuration::getDefaultConfiguration()->getTempFolderPath() . self::sanitizeFilename($match[1]);
$filename = Configuration::getDefaultConfiguration()->getTempFolderPath() . DIRECTORY_SEPARATOR . self::sanitizeFilename($match[1]);
} else {
$filename = tempnam(Configuration::getDefaultConfiguration()->getTempFolderPath(), '');
}

View File

@ -80,6 +80,7 @@ All URIs are relative to *http://petstore.swagger.io:80/v2*
Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
*AnotherFakeApi* | [**testSpecialTags**](docs/Api/AnotherFakeApi.md#testspecialtags) | **PATCH** /another-fake/dummy | To test special tags
*DefaultApi* | [**testBodyWithQueryParams**](docs/Api/DefaultApi.md#testbodywithqueryparams) | **PUT** /fake/body-with-query-params |
*FakeApi* | [**fakeOuterBooleanSerialize**](docs/Api/FakeApi.md#fakeouterbooleanserialize) | **POST** /fake/outer/boolean |
*FakeApi* | [**fakeOuterCompositeSerialize**](docs/Api/FakeApi.md#fakeoutercompositeserialize) | **POST** /fake/outer/composite |
*FakeApi* | [**fakeOuterNumberSerialize**](docs/Api/FakeApi.md#fakeouternumberserialize) | **POST** /fake/outer/number |

View File

@ -0,0 +1,57 @@
# Swagger\Client\DefaultApi
All URIs are relative to *http://petstore.swagger.io:80/v2*
Method | HTTP request | Description
------------- | ------------- | -------------
[**testBodyWithQueryParams**](DefaultApi.md#testBodyWithQueryParams) | **PUT** /fake/body-with-query-params |
# **testBodyWithQueryParams**
> testBodyWithQueryParams($body, $query)
### Example
```php
<?php
require_once(__DIR__ . '/vendor/autoload.php');
$apiInstance = new Swagger\Client\Api\DefaultApi(
// If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`.
// This is optional, `GuzzleHttp\Client` will be used as default.
new GuzzleHttp\Client()
);
$body = new \Swagger\Client\Model\User(); // \Swagger\Client\Model\User |
$query = "query_example"; // string |
try {
$apiInstance->testBodyWithQueryParams($body, $query);
} catch (Exception $e) {
echo 'Exception when calling DefaultApi->testBodyWithQueryParams: ', $e->getMessage(), PHP_EOL;
}
?>
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**body** | [**\Swagger\Client\Model\User**](../Model/User.md)| |
**query** | **string**| |
### Return type
void (empty response body)
### Authorization
No authorization required
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: Not defined
[[Back to top]](#) [[Back to API list]](../../README.md#documentation-for-api-endpoints) [[Back to Model list]](../../README.md#documentation-for-models) [[Back to README]](../../README.md)

View File

@ -0,0 +1,339 @@
<?php
/**
* DefaultApi
* PHP version 5
*
* @category Class
* @package Swagger\Client
* @author Swagger Codegen team
* @link https://github.com/swagger-api/swagger-codegen
*/
/**
* Swagger Petstore
*
* This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
* Generated by: https://github.com/swagger-api/swagger-codegen.git
* Swagger Codegen version: 2.4.0-SNAPSHOT
*/
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
* Do not edit the class manually.
*/
namespace Swagger\Client\Api;
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\MultipartStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use Swagger\Client\ApiException;
use Swagger\Client\Configuration;
use Swagger\Client\HeaderSelector;
use Swagger\Client\ObjectSerializer;
/**
* DefaultApi Class Doc Comment
*
* @category Class
* @package Swagger\Client
* @author Swagger Codegen team
* @link https://github.com/swagger-api/swagger-codegen
*/
class DefaultApi
{
/**
* @var ClientInterface
*/
protected $client;
/**
* @var Configuration
*/
protected $config;
/**
* @var HeaderSelector
*/
protected $headerSelector;
/**
* @param ClientInterface $client
* @param Configuration $config
* @param HeaderSelector $selector
*/
public function __construct(
ClientInterface $client = null,
Configuration $config = null,
HeaderSelector $selector = null
) {
$this->client = $client ?: new Client();
$this->config = $config ?: new Configuration();
$this->headerSelector = $selector ?: new HeaderSelector();
}
/**
* @return Configuration
*/
public function getConfig()
{
return $this->config;
}
/**
* Operation testBodyWithQueryParams
*
* @param \Swagger\Client\Model\User $body body (required)
* @param string $query query (required)
*
* @throws \Swagger\Client\ApiException on non-2xx response
* @throws \InvalidArgumentException
* @return void
*/
public function testBodyWithQueryParams($body, $query)
{
$this->testBodyWithQueryParamsWithHttpInfo($body, $query);
}
/**
* Operation testBodyWithQueryParamsWithHttpInfo
*
* @param \Swagger\Client\Model\User $body (required)
* @param string $query (required)
*
* @throws \Swagger\Client\ApiException on non-2xx response
* @throws \InvalidArgumentException
* @return array of null, HTTP status code, HTTP response headers (array of strings)
*/
public function testBodyWithQueryParamsWithHttpInfo($body, $query)
{
$returnType = '';
$request = $this->testBodyWithQueryParamsRequest($body, $query);
try {
$options = $this->createHttpClientOption();
try {
$response = $this->client->send($request, $options);
} catch (RequestException $e) {
throw new ApiException(
"[{$e->getCode()}] {$e->getMessage()}",
$e->getCode(),
$e->getResponse() ? $e->getResponse()->getHeaders() : null,
$e->getResponse() ? $e->getResponse()->getBody()->getContents() : null
);
}
$statusCode = $response->getStatusCode();
if ($statusCode < 200 || $statusCode > 299) {
throw new ApiException(
sprintf(
'[%d] Error connecting to the API (%s)',
$statusCode,
$request->getUri()
),
$statusCode,
$response->getHeaders(),
$response->getBody()
);
}
return [null, $statusCode, $response->getHeaders()];
} catch (ApiException $e) {
switch ($e->getCode()) {
}
throw $e;
}
}
/**
* Operation testBodyWithQueryParamsAsync
*
*
*
* @param \Swagger\Client\Model\User $body (required)
* @param string $query (required)
*
* @throws \InvalidArgumentException
* @return \GuzzleHttp\Promise\PromiseInterface
*/
public function testBodyWithQueryParamsAsync($body, $query)
{
return $this->testBodyWithQueryParamsAsyncWithHttpInfo($body, $query)
->then(
function ($response) {
return $response[0];
}
);
}
/**
* Operation testBodyWithQueryParamsAsyncWithHttpInfo
*
*
*
* @param \Swagger\Client\Model\User $body (required)
* @param string $query (required)
*
* @throws \InvalidArgumentException
* @return \GuzzleHttp\Promise\PromiseInterface
*/
public function testBodyWithQueryParamsAsyncWithHttpInfo($body, $query)
{
$returnType = '';
$request = $this->testBodyWithQueryParamsRequest($body, $query);
return $this->client
->sendAsync($request, $this->createHttpClientOption())
->then(
function ($response) use ($returnType) {
return [null, $response->getStatusCode(), $response->getHeaders()];
},
function ($exception) {
$response = $exception->getResponse();
$statusCode = $response->getStatusCode();
throw new ApiException(
sprintf(
'[%d] Error connecting to the API (%s)',
$statusCode,
$exception->getRequest()->getUri()
),
$statusCode,
$response->getHeaders(),
$response->getBody()
);
}
);
}
/**
* Create request for operation 'testBodyWithQueryParams'
*
* @param \Swagger\Client\Model\User $body (required)
* @param string $query (required)
*
* @throws \InvalidArgumentException
* @return \GuzzleHttp\Psr7\Request
*/
protected function testBodyWithQueryParamsRequest($body, $query)
{
// verify the required parameter 'body' is set
if ($body === null || (is_array($body) && count($body) === 0)) {
throw new \InvalidArgumentException(
'Missing the required parameter $body when calling testBodyWithQueryParams'
);
}
// verify the required parameter 'query' is set
if ($query === null || (is_array($query) && count($query) === 0)) {
throw new \InvalidArgumentException(
'Missing the required parameter $query when calling testBodyWithQueryParams'
);
}
$resourcePath = '/fake/body-with-query-params';
$formParams = [];
$queryParams = [];
$headerParams = [];
$httpBody = '';
$multipart = false;
// query params
if ($query !== null) {
$queryParams['query'] = ObjectSerializer::toQueryValue($query);
}
// body params
$_tempBody = null;
if (isset($body)) {
$_tempBody = $body;
}
if ($multipart) {
$headers = $this->headerSelector->selectHeadersForMultipart(
[]
);
} else {
$headers = $this->headerSelector->selectHeaders(
[],
['application/json']
);
}
// for model (json/xml)
if (isset($_tempBody)) {
// $_tempBody is the method argument, if present
$httpBody = $_tempBody;
// \stdClass has no __toString(), so we should encode it manually
if ($httpBody instanceof \stdClass && $headers['Content-Type'] === 'application/json') {
$httpBody = \GuzzleHttp\json_encode($httpBody);
}
} elseif (count($formParams) > 0) {
if ($multipart) {
$multipartContents = [];
foreach ($formParams as $formParamName => $formParamValue) {
$multipartContents[] = [
'name' => $formParamName,
'contents' => $formParamValue
];
}
// for HTTP post (form)
$httpBody = new MultipartStream($multipartContents);
} elseif ($headers['Content-Type'] === 'application/json') {
$httpBody = \GuzzleHttp\json_encode($formParams);
} else {
// for HTTP post (form)
$httpBody = \GuzzleHttp\Psr7\build_query($formParams);
}
}
$defaultHeaders = [];
if ($this->config->getUserAgent()) {
$defaultHeaders['User-Agent'] = $this->config->getUserAgent();
}
$headers = array_merge(
$defaultHeaders,
$headerParams,
$headers
);
$query = \GuzzleHttp\Psr7\build_query($queryParams);
return new Request(
'PUT',
$this->config->getHost() . $resourcePath . ($query ? "?{$query}" : ''),
$headers,
$httpBody
);
}
/**
* Create http client option
*
* @throws \RuntimeException on file opening failure
* @return array of http client options
*/
protected function createHttpClientOption()
{
$options = [];
if ($this->config->getDebug()) {
$options[RequestOptions::DEBUG] = fopen($this->config->getDebugFile(), 'a');
if (!$options[RequestOptions::DEBUG]) {
throw new \RuntimeException('Failed to open the debug file: ' . $this->config->getDebugFile());
}
}
return $options;
}
}

View File

@ -321,11 +321,11 @@ class FormatTest implements ModelInterface, ArrayAccess
if ($this->container['password'] === null) {
$invalidProperties[] = "'password' can't be null";
}
if ((strlen($this->container['password']) > 64)) {
if ((mb_strlen($this->container['password']) > 64)) {
$invalidProperties[] = "invalid value for 'password', the character length must be smaller than or equal to 64.";
}
if ((strlen($this->container['password']) < 10)) {
if ((mb_strlen($this->container['password']) < 10)) {
$invalidProperties[] = "invalid value for 'password', the character length must be bigger than or equal to 10.";
}
@ -389,10 +389,10 @@ class FormatTest implements ModelInterface, ArrayAccess
if ($this->container['password'] === null) {
return false;
}
if (strlen($this->container['password']) > 64) {
if (mb_strlen($this->container['password']) > 64) {
return false;
}
if (strlen($this->container['password']) < 10) {
if (mb_strlen($this->container['password']) < 10) {
return false;
}
return true;
@ -756,10 +756,10 @@ class FormatTest implements ModelInterface, ArrayAccess
*/
public function setPassword($password)
{
if ((strlen($password) > 64)) {
if ((mb_strlen($password) > 64)) {
throw new \InvalidArgumentException('invalid length for $password when calling FormatTest., must be smaller than or equal to 64.');
}
if ((strlen($password) < 10)) {
if ((mb_strlen($password) < 10)) {
throw new \InvalidArgumentException('invalid length for $password when calling FormatTest., must be bigger than or equal to 10.');
}

View File

@ -0,0 +1,83 @@
<?php
/**
* DefaultApiTest
* PHP version 5
*
* @category Class
* @package Swagger\Client
* @author Swagger Codegen team
* @link https://github.com/swagger-api/swagger-codegen
*/
/**
* Swagger Petstore
*
* This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\
*
* OpenAPI spec version: 1.0.0
* Contact: apiteam@swagger.io
* Generated by: https://github.com/swagger-api/swagger-codegen.git
* Swagger Codegen version: 2.4.0-SNAPSHOT
*/
/**
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen
* Please update the test case below to test the endpoint.
*/
namespace Swagger\Client;
use \Swagger\Client\Configuration;
use \Swagger\Client\ApiException;
use \Swagger\Client\ObjectSerializer;
/**
* DefaultApiTest Class Doc Comment
*
* @category Class
* @package Swagger\Client
* @author Swagger Codegen team
* @link https://github.com/swagger-api/swagger-codegen
*/
class DefaultApiTest extends \PHPUnit_Framework_TestCase
{
/**
* Setup before running any test cases
*/
public static function setUpBeforeClass()
{
}
/**
* Setup before running each test case
*/
public function setUp()
{
}
/**
* Clean up after running each test case
*/
public function tearDown()
{
}
/**
* Clean up after running all test cases
*/
public static function tearDownAfterClass()
{
}
/**
* Test case for testBodyWithQueryParams
*
* .
*
*/
public function testTestBodyWithQueryParams()
{
}
}

View File

@ -0,0 +1,26 @@
<?php
use Swagger\Client\Model\FormatTest;
class FormatTestTest extends \PHPUnit_Framework_TestCase
{
public function testCountTheLengthOfMultiByteStringsCorrectly()
{
$the64MultiByteStrings = '';
// Pass the string via constructor.
$formatTest = new FormatTest([
'password' => $the64MultiByteStrings,
// mandatory parameters
'number' => 500,
'byte' => base64_encode('test'),
'date' => new DateTime(),
]);
$this->assertEmpty($formatTest->listInvalidProperties());
// Pass the strings via setter.
// Throws InvalidArgumentException if it doesn't count the length correctly.
$formatTest->setPassword($the64MultiByteStrings);
}
}