diff --git a/application/Espo/Core/Api/RequestWrapper.php b/application/Espo/Core/Api/RequestWrapper.php index bc58918498..83a5e9df5c 100644 --- a/application/Espo/Core/Api/RequestWrapper.php +++ b/application/Espo/Core/Api/RequestWrapper.php @@ -162,7 +162,7 @@ class RequestWrapper implements ApiRequest $this->initParsedBody(); } - return $this->parsedBody; + return Util::cloneObject($this->parsedBody); } protected function initParsedBody() diff --git a/application/Espo/Core/Api/Util.php b/application/Espo/Core/Api/Util.php new file mode 100644 index 0000000000..5884b6b64a --- /dev/null +++ b/application/Espo/Core/Api/Util.php @@ -0,0 +1,69 @@ + $v) { + $cloned->$k = self::cloneObjectItem($v); + } + + return $cloned; + } + + private static function cloneObjectItem($item) + { + if (is_array($item)) { + $cloned = []; + + foreach ($item as $v) { + $cloned[] = self::cloneObjectItem($v); + } + + return $cloned; + } + + if ($item instanceof StdClass) { + return self::cloneObject($item); + } + + if (is_object($item)) { + return clone $item; + } + + return $item; + } +} diff --git a/tests/unit/Espo/Core/Api/RequestTest.php b/tests/unit/Espo/Core/Api/RequestTest.php index 14e07cad4a..344677c7a3 100644 --- a/tests/unit/Espo/Core/Api/RequestTest.php +++ b/tests/unit/Espo/Core/Api/RequestTest.php @@ -31,6 +31,7 @@ namespace tests\unit\Espo\Core\Api; use Psr\Http\Message\{ ServerRequestInterface as Psr7Request, + StreamInterface, }; use Espo\Core\Api\RequestWrapper; @@ -112,4 +113,73 @@ class RequestTest extends \PHPUnit\Framework\TestCase $this->assertEquals('2', $request->get('id')); } + + protected function createRequestWithBody(string $contents) : RequestWrapper + { + $body = $this->createMock(StreamInterface::class); + + $body + ->expects($this->any()) + ->method('getContents') + ->willReturn($contents); + + $this->request + ->expects($this->any()) + ->method('getBody') + ->willReturn($body); + + $this->request + ->expects($this->any()) + ->method('hasHeader') + ->with('Content-Type') + ->willReturn(true); + + $this->request + ->expects($this->any()) + ->method('getHeader') + ->with('Content-Type') + ->willReturn(['application/json']); + + return new RequestWrapper($this->request); + } + + public function testGetParsedBody() + { + $original = (object) [ + 'key1' => '1', + 'key2' => (object) [ + 'key21' => [ + '211', + '212', + (object) [ + '2111' => '1', + ], + ], + ], + 'key3' => [ + '31', + '32', + null, + ], + 'key4' => null, + ]; + + $contents = json_encode($original); + + $request = $this->createRequestWithBody($contents); + + $parsed = $request->getParsedBody(); + + $anotherParsed = $request->getParsedBody(); + + $this->assertEquals($parsed, $original); + + $this->assertEquals($parsed, $anotherParsed); + + $this->assertNotSame($parsed, $anotherParsed); + + $this->assertNotSame($parsed->key2, $anotherParsed->key2); + + $this->assertNotSame($parsed->key2->key21[2], $anotherParsed->key2->key21[2]); + } }