Bug Report Checklist
Description
PHP client generation produces invalid runtime validation for type: string, format: date-time properties when minLength or maxLength is present.
The PHP generator maps these properties to \DateTime, but still emits string length checks using mb_strlen(). During response deserialization, ObjectSerializer correctly converts the JSON string into a \DateTime. The generated model setter then receives that \DateTime and calls mb_strlen() on it, causing a runtime TypeError.
Actual runtime error:
mb_strlen(): Argument #1 ($string) must be of type string, DateTime given
Generated PHP snippet:
public function setStartsAt($starts_at)
{
if (is_null($starts_at)) {
throw new \InvalidArgumentException('non-nullable starts_at cannot be null');
}
if ((mb_strlen($starts_at) > 25)) {
throw new \InvalidArgumentException('invalid length for $starts_at when calling ProductDealV1., must be smaller than or equal to 25.');
}
if ((mb_strlen($starts_at) < 20)) {
throw new \InvalidArgumentException('invalid length for $starts_at when calling ProductDealV1., must be bigger than or equal to 20.');
}
$this->container['starts_at'] = $starts_at;
return $this;
}
Same issue is generated in listInvalidProperties():
if ((mb_strlen($this->container['starts_at']) > 25)) {
$invalidProperties[] = "invalid value for 'starts_at', the character length must be smaller than or equal to 25.";
}
if ((mb_strlen($this->container['starts_at']) < 20)) {
$invalidProperties[] = "invalid value for 'starts_at', the character length must be bigger than or equal to 20.";
}
The serializer maps the value to \DateTime first:
if ($class === '\DateTime') {
if (!empty($data)) {
try {
return new \DateTime($data);
} catch (\Exception $exception) {
return new \DateTime(self::sanitizeTimestamp($data));
}
} else {
return null;
}
}
Then hydration calls the generated setter with that \DateTime instance:
$propertyValue = $data->{$instance::attributeMap()[$property]};
$instance->$propertySetter(self::deserialize($propertyValue, $type, null));
Expected behavior:
The generated PHP client should deserialize valid date-time strings without throwing a TypeError.
Actual behavior:
The generated PHP client throws because mb_strlen() is called on a \DateTime.
openapi-generator version
Observed in generated PHP client with header:
Generator version: 7.24.0-SNAPSHOT
The same class of issue was previously observed for C# in older generator versions, where date / date-time fields mapped to DateTime or DateTimeOffset still received .Length checks.
OpenAPI declaration file content or url
Minimal reproduction:
openapi: 3.0.3
info:
title: DateTime Length PHP Reproduction
version: 1.0.0
paths:
/deals:
get:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/GetDealsResponse'
components:
schemas:
GetDealsResponse:
type: object
required:
- deals
properties:
deals:
type: array
items:
$ref: '#/components/schemas/ProductDeal'
ProductDeal:
type: object
required:
- startsAt
properties:
startsAt:
type: string
format: date-time
minLength: 20
maxLength: 25
endsAt:
type: string
format: date-time
minLength: 20
maxLength: 25
This produces PHP model mappings like:
protected static $openAPITypes = [
'starts_at' => '\DateTime',
'ends_at' => '\DateTime',
];
protected static $openAPIFormats = [
'starts_at' => 'date-time',
'ends_at' => 'date-time',
];
but also generates mb_strlen() checks for the same fields.
Generation Details
Generate a PHP client from the spec above.
Example:
openapi-generator-cli generate \
-i openapi.yaml \
-g php \
-o generated-php-client
The issue occurs with normal generated response deserialization, for example through a generated endpoint method returning the model containing the date-time property.
Steps to reproduce
- Save the minimal OpenAPI spec above as
openapi.yaml.
- Generate a PHP client:
openapi-generator-cli generate -i openapi.yaml -g php -o generated-php-client
- Use the generated client against a response like:
{
"deals": [
{
"startsAt": "2024-01-01T00:00:00Z",
"endsAt": "2099-01-01T00:00:00Z"
}
]
}
- Let the generated client deserialize the response into the generated model.
Actual result:
mb_strlen(): Argument #1 ($string) must be of type string, DateTime given
Expected result:
The generated client should deserialize the response successfully into a model containing \DateTime values.
Related issues/PRs
Related discussion around minLength / maxLength on formatted string values:
This appears similar in shape to the C# generator issue where date / date-time fields mapped to DateTime or DateTimeOffset, but generated .Length validation.
Suggest a fix
For the PHP generator, do not emit mb_strlen() validation for properties generated as \DateTime.
Possible fixes:
-
If type: string, format: date or format: date-time maps to \DateTime, skip minLength and maxLength validation in generated setters and listInvalidProperties().
-
Alternatively, apply minLength / maxLength validation before deserializing the raw string into \DateTime.
-
Or generate the property as string when string length constraints are present, although this would be a larger behavior change.
The core issue is that generated validation should match the generated PHP type. Once a schema property is generated as \DateTime, calling mb_strlen() on it is invalid and causes runtime failure for otherwise valid API responses.
Note: Some of this was LLM generated, but me, a human reviewed the entire output and the Problem.
Bug Report Checklist
Description
PHP client generation produces invalid runtime validation for
type: string,format: date-timeproperties whenminLengthormaxLengthis present.The PHP generator maps these properties to
\DateTime, but still emits string length checks usingmb_strlen(). During response deserialization,ObjectSerializercorrectly converts the JSON string into a\DateTime. The generated model setter then receives that\DateTimeand callsmb_strlen()on it, causing a runtimeTypeError.Actual runtime error:
Generated PHP snippet:
Same issue is generated in
listInvalidProperties():The serializer maps the value to
\DateTimefirst:Then hydration calls the generated setter with that
\DateTimeinstance:Expected behavior:
The generated PHP client should deserialize valid date-time strings without throwing a
TypeError.Actual behavior:
The generated PHP client throws because
mb_strlen()is called on a\DateTime.openapi-generator version
Observed in generated PHP client with header:
The same class of issue was previously observed for C# in older generator versions, where
date/date-timefields mapped toDateTimeorDateTimeOffsetstill received.Lengthchecks.OpenAPI declaration file content or url
Minimal reproduction:
This produces PHP model mappings like:
but also generates
mb_strlen()checks for the same fields.Generation Details
Generate a PHP client from the spec above.
Example:
The issue occurs with normal generated response deserialization, for example through a generated endpoint method returning the model containing the date-time property.
Steps to reproduce
openapi.yaml.{ "deals": [ { "startsAt": "2024-01-01T00:00:00Z", "endsAt": "2099-01-01T00:00:00Z" } ] }Actual result:
Expected result:
The generated client should deserialize the response successfully into a model containing
\DateTimevalues.Related issues/PRs
Related discussion around
minLength/maxLengthon formatted string values:This appears similar in shape to the C# generator issue where
date/date-timefields mapped toDateTimeorDateTimeOffset, but generated.Lengthvalidation.Suggest a fix
For the PHP generator, do not emit
mb_strlen()validation for properties generated as\DateTime.Possible fixes:
If
type: string,format: dateorformat: date-timemaps to\DateTime, skipminLengthandmaxLengthvalidation in generated setters andlistInvalidProperties().Alternatively, apply
minLength/maxLengthvalidation before deserializing the raw string into\DateTime.Or generate the property as
stringwhen string length constraints are present, although this would be a larger behavior change.The core issue is that generated validation should match the generated PHP type. Once a schema property is generated as
\DateTime, callingmb_strlen()on it is invalid and causes runtime failure for otherwise valid API responses.Note: Some of this was LLM generated, but me, a human reviewed the entire output and the Problem.