<?php
namespace App\EventListener;
use Closure;
use ReflectionClass;
use App\Entity\Mobility;
use ReflectionException;
use App\Entity\Registers;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use App\Entity\EasyPermitForm2;
use App\Entity\OrganicPlotFiles;
use App\Entity\SurveillanceCamera;
use App\Entity\OrganicPlotRequests;
use App\Entity\GardenProjectActions;
use App\Entity\SurveillanceCameraType;
use App\Entity\PermitEnvironmentalForm;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use App\Services\Api\Security\SurveillanceCameraTypeService;
use Symfony\Component\Serializer\Exception\ExceptionInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use ApiPlatform\Core\EventListener\DeserializeListener as DecoratedListener;
/**
* Class MultipartFormDeserializeListener
*
* @package App\EventListener
*/
class MultipartFormDeserializeListener
{
/**
* @var SerializerContextBuilderInterface
*/
private $serializerContextBuilder;
/**
* @var DecoratedListener
*/
private $decorated;
/**
* @var SerializerInterface
*/
private $serializer;
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* MultipartFormDeserializeListener constructor.
*
* @param SerializerContextBuilderInterface $serializerContextBuilder
* @param DecoratedListener $decorated
* @param SerializerInterface $serializer
* @param EntityManagerInterface $entityManager
*/
public function __construct(
SerializerContextBuilderInterface $serializerContextBuilder,
DecoratedListener $decorated,
SerializerInterface $serializer,
EntityManagerInterface $entityManager
) {
$this->serializerContextBuilder = $serializerContextBuilder;
$this->decorated = $decorated;
$this->serializer = $serializer;
$this->entityManager = $entityManager;
}
/**
* @param array<mixed> $arrays
* @return array<mixed>
*/
public static function mergeArray(array $arrays): array
{
$result = array();
foreach ($arrays as $array) {
foreach ($array as $key => $value) {
if (is_integer($key)) {
$result[] = $value;
} elseif (isset($result[$key]) && is_array($result[$key])) {
if (self::findKey($result[$key], array_key_first($value)) === true) {
$result[$key][][array_key_first($value)] = current($value);
} else {
$result[$key][array_key_first($value)] = current($value);
}
} elseif (is_array($value) && !is_integer(array_key_first($value))) {
$result[$key] = $value;
} else {
$result[$key] = $value;
}
}
}
return $result;
}
/**
* @param array<mixed> $array
* @param mixed $keyToSearch
* @return bool
*/
public static function findKey(array $array, $keyToSearch): bool
{
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::CHILD_FIRST);
$path = [];
$value = null;
$depthOfTheFoundKey = null;
foreach ($iterator as $key => $current) {
if (
$key === $keyToSearch
|| $iterator->getDepth() < $depthOfTheFoundKey
) {
if (is_null($depthOfTheFoundKey)) {
$value = $current;
}
array_unshift($path, $key);
$depthOfTheFoundKey = $iterator->getDepth();
}
}
if (is_null($depthOfTheFoundKey)) {
return false;
}
return true;
}
/**
* @param RequestEvent $event
* @return void
*/
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
if (
$request->isMethodSafe()
|| $request->isMethod(Request::METHOD_DELETE)
|| !($attributes = RequestAttributesExtractor::extractAttributes($request))
|| !$attributes['receive']
|| ('' === ($requestContent = $request->getContent()) && $request->isMethod(Request::METHOD_PUT))
) {
return;
}
if ('form' === $request->getContentType()) {
$this->denormalizeFormRequest($request);
} else {
$this->decorated->onKernelRequest($event);
}
}
/**
* @param Request $request
* @return void
* @throws ExceptionInterface
*/
private function denormalizeFormRequest(Request $request)
{
if (!$attributes = RequestAttributesExtractor::extractAttributes($request)) {
return;
}
$context = $this->serializerContextBuilder->createFromRequest($request, false, $attributes);
$objectToPopulate = $request->attributes->get('data');
if (null !== $objectToPopulate) {
$objectToPopulatesss = $this->entityManager->getRepository($attributes['resource_class'])->findOneBy(['id' => $objectToPopulate->getId()]);
$context[AbstractNormalizer::OBJECT_TO_POPULATE] = $objectToPopulatesss;
}
if ($request->getContent()) {
$request = $this->handle($request, function ($request) {
return $request;
});
}
$dataFiles = [];
$dataRequests = [];
$requestFiles = $request->files;
$requestDatas = $request->request;
foreach ($requestDatas as $key => $requestData) {
$requestData = $requestData === "true" ? true : $requestData;
$requestData = $requestData === "false" ? false : $requestData;
if (is_numeric($requestData)) {
$requestData = strpos($requestData, '.') ? (float) $requestData : (int) $requestData;
}
if ($attributes['resource_class'] === GardenProjectActions::class) {
if ($key === "actionPrice") {
$requestData = (float) $requestData;
}
}
$dataRequests[$key] = $requestData;
}
foreach ($requestFiles as $key => $requestFile) {
$dataFiles[$key] = $requestFile;
}
$data = array_merge_recursive($dataFiles, $dataRequests);
if ($attributes['resource_class'] === PermitEnvironmentalForm::class || $attributes['resource_class'] === EasyPermitForm2::class) {
$data = [];
foreach ($dataFiles as $key => $listAnnexe) {
if (!key_exists($key, $dataRequests)) {
foreach ($listAnnexe as $anne) {
$dataRequests[$key][] = [];
}
}
}
foreach ($dataRequests as $key => $permit) {
if (!isset($dataFiles[$key])) {
$data[$key] = $permit;
} else {
foreach ($permit as $j => $value) {
$data[$key][] = array_merge_recursive($value, $dataFiles[$key][0]);
}
}
}
}
if ($attributes['resource_class'] === Mobility::class) {
if (isset($data['fromCoordinate']['latitude'])) {
$data['fromCoordinate']['latitude'] = (float)$data['fromCoordinate']['latitude'];
}
if (isset($data['fromCoordinate']['longitude'])) {
$data['fromCoordinate']['longitude'] = (float)$data['fromCoordinate']['longitude'];
}
if (isset($data['toCoordinate']['latitude'])) {
$data['toCoordinate']['latitude'] = (float)$data['toCoordinate']['latitude'];
}
if (isset($data['toCoordinate']['longitude'])) {
$data['toCoordinate']['longitude'] = (float)$data['toCoordinate']['longitude'];
}
if (!isset($data['status'])) {
$data['status'] = false;
}
}
if ($attributes['resource_class'] == SurveillanceCamera::class) {
$document = $data['document'];
unset($data['document']);
$entity = $this->serializer->denormalize($data, $attributes['resource_class'], null, $context);
$entity->setDocument($document);
$surveillanceCameraType = $this->entityManager->getRepository(SurveillanceCameraType::class)
->findOneBy(['name' => $data['typeName']]);
if (null === $surveillanceCameraType) {
$surveillanceCameraType = new SurveillanceCameraType();
$surveillanceCameraType->setName($data['typeName']);
$this->entityManager->persist($surveillanceCameraType);
$this->entityManager->flush();
}
$entity->setType($surveillanceCameraType);
} else if ($attributes['resource_class'] == OrganicPlotRequests::class) {
foreach ($data as $value) {
foreach ($value as $item) {
$file = new OrganicPlotFiles();
$file->setFile($item["file"]);
$this->entityManager->persist($file);
$files[] = $file;
}
}
unset($data["requestFiles"]);
$entity = $this->serializer->denormalize($data, $attributes['resource_class'], null, $context);
foreach ($files as $file) {
$entity->addRequestFile($file);
}
$this->entityManager->flush();
} else {
$entity = $this->serializer->denormalize($data, $attributes['resource_class'], null, $context);
}
$request->attributes->set('data', $entity);
}
/**
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if ($request->getMethod() == 'POST' or $request->getMethod() == 'GET') {
return $next($request);
}
if (
preg_match('/multipart\/form-data/', $request->headers->get('Content-Type')) or
preg_match('/multipart\/form-data/', $request->headers->get('content-type'))
) {
$parameters = $this->getRequestContent();
$request->request->add($parameters['inputs']);
$request->files->add($parameters['files']);
}
return $next($request);
}
/**
* @return array<mixed>
*/
private function getRequestContent()
{
$files = [];
$data = [];
// Fetch content and determine boundary
$rawData = file_get_contents('php://input');
$boundary = substr($rawData, 0, strpos($rawData, "\r\n"));
// Fetch and process each part
$parts = $rawData ? array_slice(explode($boundary, $rawData), 1) : [];
foreach ($parts as $part) {
// If this is the last part, break
if ($part == "--\r\n") {
break;
}
// Separate content from headers
$part = ltrim($part, "\r\n");
list($rawHeaders, $content) = explode("\r\n\r\n", $part, 2);
$content = substr($content, 0, strlen($content) - 2);
// Parse the headers list
$rawHeaders = explode("\r\n", $rawHeaders);
$headers = array();
foreach ($rawHeaders as $header) {
list($name, $value) = explode(':', $header);
$headers[strtolower($name)] = ltrim($value, ' ');
}
// Parse the Content-Disposition to get the field name, etc.
if (isset($headers['content-disposition'])) {
preg_match(
'/^form-data; *name="([^"]+)"(; *filename="([^"]+)")?/',
$headers['content-disposition'],
$matches
);
$fieldName = $matches[1];
$fileName = (isset($matches[3]) ? $matches[3] : null);
// If we have a file, save it. Otherwise, save the data.
if ($fileName !== null) {
$tmp_dir = ini_get('upload_tmp_dir');
$localFileName = tempnam($tmp_dir, 'php');
file_put_contents($localFileName, $content);
$uploadedFile = new UploadedFile($localFileName, $fileName, $headers['content-type'], 0, true);
$files = $this->transformData($fieldName, $uploadedFile, $files);
} else {
$data = $this->transformData($fieldName, $content, $data);
}
}
}
$data = $this->merge($data);
$fields = new ParameterBag($data);
return ["inputs" => $fields->all(), "files" => $files];
}
/**
* @param string $name
* @param mixed $value
* @param array<mixed> $data
* @return array<mixed>
*/
private function transformData(string $name, $value, array $data = [])
{
$names = explode('[', $name);
$arrayKeys = array_reverse($names);
$result = $value;
foreach ($arrayKeys as $key) {
$key = rtrim($key, "]");
$result = $result === "true" ? true : $result;
$result = $result === "false" ? false : $result;
$result = ($key === "" or is_numeric($key)) ? [$result] : [rtrim($key, "]") => $result];
}
return array_merge_recursive($result, $data);
}
/**
* @param array<mixed> $array
* @return array<mixed>
*/
public function merge(array $array): array
{
$newArray = [];
foreach ($array as $key => $data) {
if (is_array($data) && count($data) > 0) {
if ($this->is_multidimensional($data)) {
$arrayMerged = array_merge_recursive(...$data);
if ($this->is_multidimensional($arrayMerged)) {
$result = array_map(null, ...array_values($arrayMerged));
$keys = array_keys($arrayMerged);
$result = array_map(function ($v) use ($keys) {
return array_combine($keys, $v);
}, $result);
$newArray[$key] = $result;
} else {
$newArray[$key] = [$arrayMerged];
}
} else {
$newArray[$key] = $data;
}
} else {
$newArray[$key] = $data;
}
}
return $newArray;
}
/**
* @param array<mixed> $array
* @return bool
*/
public function is_multidimensional(array $array): bool
{
return count($array) !== count($array, COUNT_RECURSIVE);
}
}