diff --git a/phpstan.neon b/phpstan.neon index 9971cb7..11e720d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 8 + level: 9 polluteScopeWithAlwaysIterableForeach: false polluteScopeWithLoopInitialAssignments: false paths: diff --git a/src/JsonParseNode.php b/src/JsonParseNode.php index d84ae09..cfc9a7c 100644 --- a/src/JsonParseNode.php +++ b/src/JsonParseNode.php @@ -14,6 +14,7 @@ use Microsoft\Kiota\Abstractions\Types\Date; use Microsoft\Kiota\Abstractions\Types\Time; use Psr\Http\Message\StreamInterface; +use RuntimeException; /** * @method onBeforeAssignFieldValues(Parsable $result) @@ -40,17 +41,17 @@ public function __construct($content) { * @inheritDoc */ public function getChildNode(string $identifier): ?ParseNode { - if ($this->jsonNode === null || !($this->jsonNode[$identifier] ?? null)) { + if ((!is_array($this->jsonNode)) || (($this->jsonNode[$identifier] ?? null) === null)) { return null; } - return new self($this->jsonNode[$identifier] ?? null); + return new self($this->jsonNode[$identifier]); } /** * @inheritDoc */ public function getStringValue(): ?string { - return $this->jsonNode !== null ? addcslashes($this->jsonNode, "\\\r\n") : null; + return $this->jsonNode !== null ? addcslashes(strval($this->jsonNode), "\\\r\n") : null; } /** @@ -64,14 +65,14 @@ public function getBooleanValue(): ?bool { * @inheritDoc */ public function getIntegerValue(): ?int { - return $this->jsonNode !== null ? (int)$this->jsonNode : null; + return $this->jsonNode !== null ? intval($this->jsonNode) : null; } /** * @inheritDoc */ public function getFloatValue(): ?float { - return $this->jsonNode !== null ? (float)$this->jsonNode : null; + return $this->jsonNode !== null ? floatval($this->jsonNode) : null; } /** @@ -79,7 +80,7 @@ public function getFloatValue(): ?float { * @throws Exception */ public function getCollectionOfObjectValues(array $type): ?array { - if ($this->jsonNode === null) { + if (!is_array($this->jsonNode)) { return null; } return array_map(static function ($val) use($type) { @@ -101,9 +102,8 @@ public function getObjectValue(array $type): ?Parsable { throw new InvalidArgumentException("Invalid type $type[0] provided."); } if (!is_callable($type, true, $callableString)) { - throw new \RuntimeException('Undefined method '. $type[1]); + throw new InvalidArgumentException('Undefined method '. $type[1]); } - /** @var Parsable $result */ $result = $callableString($this); if($this->getOnBeforeAssignFieldValues() !== null) { $this->getOnBeforeAssignFieldValues()($result); @@ -130,14 +130,16 @@ private function assignFieldValues($result): void { $isAdditionalDataHolder = true; $additionalData = $result->getAdditionalData() ?? []; } - foreach ($this->jsonNode as $key => $value){ - $deserializer = $fieldDeserializers[$key] ?? null; + if (is_array($this->jsonNode)) { + foreach ($this->jsonNode as $key => $value) { + $deserializer = $fieldDeserializers[$key] ?? null; - if ($deserializer !== null){ - $deserializer(new JsonParseNode($value)); - } else { - $key = (string)$key; - $additionalData[$key] = $value; + if ($deserializer !== null) { + $deserializer(new JsonParseNode($value)); + } else { + $key = (string)$key; + $additionalData[$key] = $value; + } } } @@ -163,7 +165,7 @@ public function getEnumValue(string $targetEnum): ?Enum{ * @return array|null */ public function getCollectionOfEnumValues(string $targetClass): ?array { - if ($this->jsonNode === null) { + if (!is_array($this->jsonNode)) { return null; } return array_map(static function ($val) use($targetClass) { @@ -206,7 +208,7 @@ public function setOnBeforeAssignFieldValues(callable $value): void { * @throws Exception */ public function getCollectionOfPrimitiveValues(?string $typeName = null): ?array { - if ($this->jsonNode === null){ + if (!is_array($this->jsonNode)) { return null; } return array_map(static function ($x) use ($typeName) { @@ -232,7 +234,7 @@ public function getAnyValue(string $type) { case 'null': return null; case 'array': - return $this->getCollectionOfPrimitiveValues(null); + return $this->getCollectionOfPrimitiveValues(); case Date::class: return $this->getDateValue(); case Time::class: @@ -254,7 +256,7 @@ public function getAnyValue(string $type) { * @throws Exception */ public function getDateValue(): ?Date { - return ($this->jsonNode !== null) ? new Date($this->jsonNode) : null; + return ($this->jsonNode !== null) ? new Date(strval($this->jsonNode)) : null; } /** @@ -262,7 +264,7 @@ public function getDateValue(): ?Date { * @throws Exception */ public function getTimeValue(): ?Time { - return ($this->jsonNode !== null) ? new Time($this->jsonNode) : null; + return ($this->jsonNode !== null) ? new Time(strval($this->jsonNode)) : null; } /** @@ -270,7 +272,7 @@ public function getTimeValue(): ?Time { * @throws Exception */ public function getDateTimeValue(): ?DateTime { - return ($this->jsonNode !== null) ? new DateTime($this->jsonNode) : null; + return ($this->jsonNode !== null) ? new DateTime(strval($this->jsonNode)) : null; } /** @@ -278,10 +280,10 @@ public function getDateTimeValue(): ?DateTime { * @throws Exception */ public function getDateIntervalValue(): ?DateInterval{ - return ($this->jsonNode !== null) ? new DateInterval($this->jsonNode) : null; + return ($this->jsonNode !== null) ? new DateInterval(strval($this->jsonNode)) : null; } public function getBinaryContent(): ?StreamInterface { - return ($this->jsonNode !== null) ? Utils::streamFor($this->jsonNode) : null; + return ($this->jsonNode !== null) ? Utils::streamFor(strval($this->jsonNode)) : null; } } diff --git a/src/JsonSerializationWriter.php b/src/JsonSerializationWriter.php index 01d0170..9bc4e6c 100644 --- a/src/JsonSerializationWriter.php +++ b/src/JsonSerializationWriter.php @@ -6,13 +6,14 @@ use DateTime; use DateTimeInterface; use GuzzleHttp\Psr7\Utils; +use InvalidArgumentException; use Microsoft\Kiota\Abstractions\Enum; use Microsoft\Kiota\Abstractions\Serialization\Parsable; use Microsoft\Kiota\Abstractions\Serialization\SerializationWriter; use Microsoft\Kiota\Abstractions\Types\Date; use Microsoft\Kiota\Abstractions\Types\Time; use Psr\Http\Message\StreamInterface; -use RuntimeException; +use stdClass; /** * @method onBeforeObjectSerialization(?Parsable $value); @@ -37,7 +38,7 @@ class JsonSerializationWriter implements SerializationWriter private $onBeforeObjectSerialization; private function writePropertyName(string $propertyName): void { - $this->writer []= "\"{$propertyName}\":"; + $this->writer []= "\"$propertyName\":"; } /** @@ -114,16 +115,15 @@ public function writeDateValue(?string $key, ?Date $value): void { $this->writePropertyName($key); } $valueString = (string)$value; - $this->writePropertyValue($key, "\"{$valueString}\""); + $this->writePropertyValue($key, "\"$valueString\""); } } public function writeBinaryContent(?string $key, ?StreamInterface $value): void { if ($value !== null) { - if (!empty($key)) { - $this->writePropertyName($key); - } - $this->writePropertyValue($key, "\"{$value->getContents()}\""); + $this->writeStringValue($key, $value->getContents()); + } else { + $this->writeNullValue($key); } } @@ -307,66 +307,51 @@ public function getOnStartObjectSerialization(): ?callable { * @param mixed $value */ public function writeAnyValue(?string $key, $value): void{ - $type = get_debug_type($value); - switch ($type) { - case 'float': - $this->writeFloatValue($key, $value); - break; - case 'string': - $this->writeStringValue($key, $value); - break; - case 'int': - $this->writeIntegerValue($key, $value); - break; - case 'bool': - $this->writeBooleanValue($key, $value); - break; - case 'null': - $this->writeNullValue($key); - break; - case Date::class: - $this->writeDateValue($key, $value); - break; - case Time::class: - $this->writeTimeValue($key, $value); - break; - case DateTime::class: - $this->writeDateTimeValue($key, $value); - break; - case DateInterval::class: - $this->writeDateIntervalValue($key, $value); - break; - case 'stdClass': + if (is_null($value)) { + $this->writeNullValue($key); + } elseif (is_float($value)) { + $this->writeFloatValue($key, $value); + } elseif (is_string($value)) { + $this->writeStringValue($key, $value); + } elseif (is_int($value)) { + $this->writeIntegerValue($key, $value); + } elseif (is_bool($value)) { + $this->writeBooleanValue($key, $value); + } elseif ($value instanceof Date) { + $this->writeDateValue($key, $value); + } elseif ($value instanceof Time) { + $this->writeTimeValue($key, $value); + } elseif ($value instanceof DateInterval) { + $this->writeDateIntervalValue($key, $value); + } elseif ($value instanceof DateTime) { + $this->writeDateTimeValue($key, $value); + } elseif (is_array($value)) { + $keys = array_filter(array_keys($value), 'is_string'); + // If there are string keys then that means this is a single + // object we are dealing with + // otherwise it is a collection of objects. + if (!empty($keys)) { $this->writeNonParsableObjectValue($key, (object)$value); - break; - case 'array': - $keys = array_filter(array_keys($value), 'is_string'); - // If there are string keys then that means this is a single - // object we are dealing with - // otherwise it is a collection of objects. - if (!empty($keys)){ - $this->writeNonParsableObjectValue($key, (object)$value); - } else if (!empty($value)){ - if (is_subclass_of($value[0], Parsable::class)) { - $this->writeCollectionOfObjectValues($key, $value); - } else{ - $this->writeCollectionOfPrimitiveValues($key, $value); - } - } - break; - default: - if (is_a($value, Parsable::class)) { - $this->writeObjectValue($key, $value); - } else if(is_subclass_of($type, Enum::class)){ - $this->writeEnumValue($key, $value); - } else if(is_subclass_of($type, DateTimeInterface::class)){ - $this->writeDateTimeValue($key, $value); - } else if(is_a($value, StreamInterface::class) || is_subclass_of($type, StreamInterface::class)) { - $this->writeStringValue($key, $value->getContents()); + } elseif (!empty($value)) { + if ($value[0] instanceof Parsable) { + $this->writeCollectionOfObjectValues($key, $value); + } elseif ($value[0] instanceof Enum) { + $this->writeCollectionOfEnumValues($key, $value); } else { - throw new RuntimeException("Could not serialize the object of type {$type}"); + $this->writeCollectionOfPrimitiveValues($key, $value); } - break; + } + } elseif ($value instanceof stdClass) { + $this->writeNonParsableObjectValue($key, $value); + } elseif ($value instanceof Parsable) { + $this->writeObjectValue($key, $value); + } elseif ($value instanceof Enum) { + $this->writeEnumValue($key, $value); + } elseif ($value instanceof StreamInterface) { + $this->writeStringValue($key, $value->getContents()); + } else { + $type = gettype($value); + throw new InvalidArgumentException("Could not serialize the object of type $type "); } } @@ -443,7 +428,7 @@ public function writeTimeValue(?string $key, ?Time $value): void { if (!empty($key)) { $this->writePropertyName($key); } - $val = "\"{$value}\""; + $val = "\"$value\""; $this->writePropertyValue($key, $val); } } @@ -454,7 +439,7 @@ public function writeDateIntervalValue(?string $key, ?DateInterval $value): void $this->writePropertyName($key); } $res = "P{$value->y}Y{$value->y}M{$value->d}DT{$value->h}H{$value->i}M{$value->s}S"; - $val = "\"{$res}\"" ; + $val = "\"$res\"" ; $this->writePropertyValue($key, $val); } } diff --git a/tests/JsonSerializationWriterTest.php b/tests/JsonSerializationWriterTest.php index facfcce..7eec08b 100644 --- a/tests/JsonSerializationWriterTest.php +++ b/tests/JsonSerializationWriterTest.php @@ -2,6 +2,8 @@ namespace Microsoft\Kiota\Serialization\Tests; +use DateInterval; +use GuzzleHttp\Psr7\Utils; use Microsoft\Kiota\Abstractions\Serialization\SerializationWriter; use Microsoft\Kiota\Abstractions\Types\Date; use Microsoft\Kiota\Abstractions\Types\Time; @@ -39,7 +41,7 @@ public function testWriteLongValue(): void { public function testWriteDateOnlyValue(): void { $this->jsonSerializationWriter = new JsonSerializationWriter(); $date = Date::createFrom(2012, 12, 3); - $this->jsonSerializationWriter->writeDateValue("date", $date); + $this->jsonSerializationWriter->writeAnyValue("date", $date); $expected = '"date":"2012-12-03"'; $actual = $this->jsonSerializationWriter->getSerializedContent()->getContents(); $this->assertEquals($expected, $actual); @@ -70,7 +72,7 @@ public function testWriteCollectionOfNonParsableObjectValues(): void{ public function testWriteFloatValue(): void{ $this->jsonSerializationWriter = new JsonSerializationWriter(); - $this->jsonSerializationWriter->writeFloatValue("height", 12.394); + $this->jsonSerializationWriter->writeAnyValue("height", 12.394); $expected = '"height":12.394'; $actual = $this->jsonSerializationWriter->getSerializedContent()->getContents(); $this->assertEquals($expected, $actual); @@ -88,7 +90,7 @@ public function testWriteEnumSetValue(): void{ public function testWriteNullValue(): void{ $this->jsonSerializationWriter = new JsonSerializationWriter(); - $this->jsonSerializationWriter->writeNullValue("nextPage"); + $this->jsonSerializationWriter->writeAnyValue("nextPage", null); $expected = '"nextPage":null'; $actual = $this->jsonSerializationWriter->getSerializedContent()->getContents(); $this->assertEquals($expected, $actual); @@ -126,8 +128,8 @@ public function testWriteObjectValue(): void{ public function testWriteEnumValue(): void{ $this->jsonSerializationWriter = new JsonSerializationWriter(); - $this->jsonSerializationWriter->writeEnumValue("status", new MaritalStatus('married')); - $expected = '"status":"married"'; + $this->jsonSerializationWriter->writeAnyValue("status", [new MaritalStatus('married'), new MaritalStatus('single')]); + $expected = '"status":["married","single"]'; $actual = $this->jsonSerializationWriter->getSerializedContent()->getContents(); $this->assertEquals($expected, $actual); } @@ -146,7 +148,7 @@ public function testWriteAnyValue(): void { */ public function testWriteNonParsableObjectValue(): void{ $this->jsonSerializationWriter = new JsonSerializationWriter(); - $this->jsonSerializationWriter->writeAnyValue("times", [ + $this->jsonSerializationWriter->writeAnyValue("times", (object)[ "start" => Time::createFrom(12,0, 23), "end" => Time::createFrom(13, 45, 12)]); $expected = '"times":{"start":"12:00:23","end":"13:45:12"}'; @@ -206,4 +208,29 @@ public function testWriteStringValue(): void { $actual = $this->jsonSerializationWriter->getSerializedContent()->getContents(); $this->assertEquals($expected, $actual); } + + /** + * @throws \Exception + */ + public function testWriteDateIntervalValue(): void + { + $this->jsonSerializationWriter = new JsonSerializationWriter(); + $interval = new DateInterval('P300DT100S'); + $this->jsonSerializationWriter->writeAnyValue('timeTaken', $interval); + + $content = $this->jsonSerializationWriter->getSerializedContent(); + $this->assertEquals('"timeTaken":"P0Y0M300DT0H0M100S"', $content->getContents()); + } + + public function testWriteBinaryContentValue(): void + { + $this->jsonSerializationWriter = new JsonSerializationWriter(); + $stream = Utils::streamFor("Hello world!!!\r\t\t\t\n"); + $this->jsonSerializationWriter->writeBinaryContent('body', $stream); + $stream->rewind(); + $this->jsonSerializationWriter->writeAnyValue('body3', $stream); + $this->jsonSerializationWriter->writeBinaryContent('body2', null); + $content = $this->jsonSerializationWriter->getSerializedContent(); + $this->assertEquals("\"body\":\"Hello world!!!\\r\\t\\t\\t\\n\",\"body3\":\"Hello world!!!\\r\\t\\t\\t\\n\",\"body2\":null", $content->getContents()); + } }