src/EventListener/MultipartFormDeserializeListener.php line 147

Open in your IDE?
  1. <?php
  2. namespace App\EventListener;
  3. use Closure;
  4. use ReflectionClass;
  5. use App\Entity\Mobility;
  6. use ReflectionException;
  7. use App\Entity\Registers;
  8. use RecursiveArrayIterator;
  9. use RecursiveIteratorIterator;
  10. use App\Entity\EasyPermitForm2;
  11. use App\Entity\OrganicPlotFiles;
  12. use App\Entity\SurveillanceCamera;
  13. use App\Entity\OrganicPlotRequests;
  14. use App\Entity\GardenProjectActions;
  15. use App\Entity\SurveillanceCameraType;
  16. use App\Entity\PermitEnvironmentalForm;
  17. use Doctrine\ORM\EntityManagerInterface;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\Security\Core\Security;
  20. use Doctrine\Common\Annotations\AnnotationReader;
  21. use Symfony\Component\HttpFoundation\ParameterBag;
  22. use Symfony\Component\HttpKernel\Event\RequestEvent;
  23. use ApiPlatform\Core\Util\RequestAttributesExtractor;
  24. use Symfony\Component\Serializer\SerializerInterface;
  25. use Symfony\Component\HttpFoundation\File\UploadedFile;
  26. use App\Services\Api\Security\SurveillanceCameraTypeService;
  27. use Symfony\Component\Serializer\Exception\ExceptionInterface;
  28. use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
  29. use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
  30. use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
  31. use ApiPlatform\Core\EventListener\DeserializeListener as DecoratedListener;
  32. /**
  33.  * Class MultipartFormDeserializeListener
  34.  *
  35.  * @package App\EventListener
  36.  */
  37. class MultipartFormDeserializeListener
  38. {
  39.     /**
  40.      * @var SerializerContextBuilderInterface
  41.      */
  42.     private $serializerContextBuilder;
  43.     /**
  44.      * @var DecoratedListener
  45.      */
  46.     private $decorated;
  47.     /**
  48.      * @var SerializerInterface
  49.      */
  50.     private $serializer;
  51.     /**
  52.      * @var EntityManagerInterface
  53.      */
  54.     private $entityManager;
  55.     /**
  56.      * MultipartFormDeserializeListener constructor.
  57.      *
  58.      * @param SerializerContextBuilderInterface $serializerContextBuilder
  59.      * @param DecoratedListener $decorated
  60.      * @param SerializerInterface $serializer
  61.      * @param EntityManagerInterface $entityManager
  62.      */
  63.     public function __construct(
  64.         SerializerContextBuilderInterface $serializerContextBuilder,
  65.         DecoratedListener $decorated,
  66.         SerializerInterface $serializer,
  67.         EntityManagerInterface $entityManager
  68.     ) {
  69.         $this->serializerContextBuilder $serializerContextBuilder;
  70.         $this->decorated $decorated;
  71.         $this->serializer $serializer;
  72.         $this->entityManager $entityManager;
  73.     }
  74.     /**
  75.      * @param array<mixed> $arrays
  76.      * @return array<mixed>
  77.      */
  78.     public static function mergeArray(array $arrays): array
  79.     {
  80.         $result = array();
  81.         foreach ($arrays as $array) {
  82.             foreach ($array as $key => $value) {
  83.                 if (is_integer($key)) {
  84.                     $result[] = $value;
  85.                 } elseif (isset($result[$key]) && is_array($result[$key])) {
  86.                     if (self::findKey($result[$key], array_key_first($value)) === true) {
  87.                         $result[$key][][array_key_first($value)] = current($value);
  88.                     } else {
  89.                         $result[$key][array_key_first($value)] = current($value);
  90.                     }
  91.                 } elseif (is_array($value) && !is_integer(array_key_first($value))) {
  92.                     $result[$key] = $value;
  93.                 } else {
  94.                     $result[$key] = $value;
  95.                 }
  96.             }
  97.         }
  98.         return $result;
  99.     }
  100.     /**
  101.      * @param array<mixed> $array
  102.      * @param mixed $keyToSearch
  103.      * @return bool
  104.      */
  105.     public static function findKey(array $array$keyToSearch): bool
  106.     {
  107.         $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::CHILD_FIRST);
  108.         $path = [];
  109.         $value null;
  110.         $depthOfTheFoundKey null;
  111.         foreach ($iterator as $key => $current) {
  112.             if (
  113.                 $key === $keyToSearch
  114.                 || $iterator->getDepth() < $depthOfTheFoundKey
  115.             ) {
  116.                 if (is_null($depthOfTheFoundKey)) {
  117.                     $value $current;
  118.                 }
  119.                 array_unshift($path$key);
  120.                 $depthOfTheFoundKey $iterator->getDepth();
  121.             }
  122.         }
  123.         if (is_null($depthOfTheFoundKey)) {
  124.             return false;
  125.         }
  126.         return true;
  127.     }
  128.     /**
  129.      * @param RequestEvent $event
  130.      * @return void
  131.      */
  132.     public function onKernelRequest(RequestEvent $event)
  133.     {
  134.         $request $event->getRequest();
  135.        
  136.         if (
  137.             $request->isMethodSafe()
  138.             || $request->isMethod(Request::METHOD_DELETE)
  139.             || !($attributes RequestAttributesExtractor::extractAttributes($request))
  140.             || !$attributes['receive']
  141.             || ('' === ($requestContent $request->getContent()) && $request->isMethod(Request::METHOD_PUT))
  142.         ) {
  143.             return;
  144.         }
  145.         if ('form' === $request->getContentType()) {
  146.             $this->denormalizeFormRequest($request);
  147.         } else {
  148.             $this->decorated->onKernelRequest($event);
  149.         }
  150.     }
  151.     /**
  152.      * @param Request $request
  153.      * @return void
  154.      * @throws ExceptionInterface
  155.      */
  156.     private function denormalizeFormRequest(Request $request)
  157.     {
  158.         if (!$attributes RequestAttributesExtractor::extractAttributes($request)) {
  159.             return;
  160.         }
  161.         $context $this->serializerContextBuilder->createFromRequest($requestfalse$attributes);
  162.         $objectToPopulate $request->attributes->get('data');
  163.         if (null !== $objectToPopulate) {
  164.             $objectToPopulatesss $this->entityManager->getRepository($attributes['resource_class'])->findOneBy(['id' => $objectToPopulate->getId()]);
  165.             $context[AbstractNormalizer::OBJECT_TO_POPULATE] = $objectToPopulatesss;
  166.         }
  167.         if ($request->getContent()) {
  168.             $request $this->handle($request, function ($request) {
  169.                 return $request;
  170.             });
  171.         }
  172.         $dataFiles = [];
  173.         $dataRequests = [];
  174.         $requestFiles $request->files;
  175.         $requestDatas $request->request;
  176.         foreach ($requestDatas as $key => $requestData) {
  177.             $requestData $requestData === "true" true $requestData;
  178.             $requestData $requestData === "false" false $requestData;
  179.             if (is_numeric($requestData)) {
  180.                 $requestData strpos($requestData'.') ? (float) $requestData : (int) $requestData;
  181.             }
  182.             
  183.             if ($attributes['resource_class'] === GardenProjectActions::class) {
  184.                 if ($key === "actionPrice") {
  185.                     $requestData = (float) $requestData;
  186.                 }
  187.             }
  188.             $dataRequests[$key] = $requestData;
  189.         }
  190.         foreach ($requestFiles as $key => $requestFile) {
  191.             $dataFiles[$key] = $requestFile;
  192.         }
  193.         
  194.         $data array_merge_recursive($dataFiles$dataRequests);
  195.         if ($attributes['resource_class'] === PermitEnvironmentalForm::class || $attributes['resource_class'] === EasyPermitForm2::class) {
  196.             $data = [];
  197.             foreach ($dataFiles as $key => $listAnnexe) {
  198.                 if (!key_exists($key$dataRequests)) {
  199.                     foreach ($listAnnexe as $anne) {
  200.                         $dataRequests[$key][] = [];
  201.                     }
  202.                 }
  203.             }
  204.             foreach ($dataRequests as $key => $permit) {
  205.                 if (!isset($dataFiles[$key])) {
  206.                     $data[$key] = $permit;
  207.                 } else {
  208.                     foreach ($permit as $j => $value) {
  209.                         $data[$key][] = array_merge_recursive($value$dataFiles[$key][0]);
  210.                     }
  211.                 }
  212.             }
  213.         }
  214.         if ($attributes['resource_class'] === Mobility::class) {
  215.             if (isset($data['fromCoordinate']['latitude'])) {
  216.                 $data['fromCoordinate']['latitude'] = (float)$data['fromCoordinate']['latitude'];
  217.             }
  218.             if (isset($data['fromCoordinate']['longitude'])) {
  219.                 $data['fromCoordinate']['longitude'] = (float)$data['fromCoordinate']['longitude'];
  220.             }
  221.             if (isset($data['toCoordinate']['latitude'])) {
  222.                 $data['toCoordinate']['latitude'] = (float)$data['toCoordinate']['latitude'];
  223.             }
  224.             if (isset($data['toCoordinate']['longitude'])) {
  225.                 $data['toCoordinate']['longitude'] = (float)$data['toCoordinate']['longitude'];
  226.             }
  227.             if (!isset($data['status'])) {
  228.                 $data['status'] = false;
  229.             }
  230.         }
  231.         if ($attributes['resource_class'] == SurveillanceCamera::class) {
  232.             $document $data['document'];
  233.             unset($data['document']);
  234.             $entity $this->serializer->denormalize($data$attributes['resource_class'], null$context);
  235.             $entity->setDocument($document);
  236.             $surveillanceCameraType $this->entityManager->getRepository(SurveillanceCameraType::class)
  237.                                                           ->findOneBy(['name' => $data['typeName']]);
  238.             if (null === $surveillanceCameraType) {
  239.                 $surveillanceCameraType = new SurveillanceCameraType();
  240.                 $surveillanceCameraType->setName($data['typeName']);
  241.                 $this->entityManager->persist($surveillanceCameraType);
  242.                 $this->entityManager->flush();
  243.             }
  244.             $entity->setType($surveillanceCameraType);
  245.         } else if ($attributes['resource_class'] == OrganicPlotRequests::class) {
  246.             foreach ($data as $value) {
  247.                 foreach ($value as $item) {
  248.                     $file = new OrganicPlotFiles();
  249.                     $file->setFile($item["file"]);
  250.                     $this->entityManager->persist($file);
  251.                     $files[] = $file;
  252.                 }
  253.             }
  254.             unset($data["requestFiles"]);
  255.             $entity $this->serializer->denormalize($data$attributes['resource_class'], null$context);
  256.             foreach ($files as $file) {
  257.                 $entity->addRequestFile($file);
  258.             }
  259.             $this->entityManager->flush();
  260.         } else {
  261.             $entity $this->serializer->denormalize($data$attributes['resource_class'], null$context);
  262.         }
  263.         $request->attributes->set('data'$entity);
  264.     }
  265.     /**
  266.      * @param Request $request
  267.      * @param Closure $next
  268.      *
  269.      * @return mixed
  270.      */
  271.     public function handle(Request $requestClosure $next)
  272.     {
  273.         if ($request->getMethod() == 'POST' or $request->getMethod() == 'GET') {
  274.             return $next($request);
  275.         }
  276.         if (
  277.             preg_match('/multipart\/form-data/'$request->headers->get('Content-Type')) or
  278.             preg_match('/multipart\/form-data/'$request->headers->get('content-type'))
  279.         ) {
  280.             $parameters $this->getRequestContent();
  281.             $request->request->add($parameters['inputs']);
  282.             $request->files->add($parameters['files']);
  283.         }
  284.         return $next($request);
  285.     }
  286.     /**
  287.      * @return array<mixed>
  288.      */
  289.     private function getRequestContent()
  290.     {
  291.         $files = [];
  292.         $data = [];
  293.         // Fetch content and determine boundary
  294.         $rawData file_get_contents('php://input');
  295.         $boundary substr($rawData0strpos($rawData"\r\n"));
  296.         // Fetch and process each part
  297.         $parts $rawData array_slice(explode($boundary$rawData), 1) : [];
  298.         foreach ($parts as $part) {
  299.             // If this is the last part, break
  300.             if ($part == "--\r\n") {
  301.                 break;
  302.             }
  303.             // Separate content from headers
  304.             $part ltrim($part"\r\n");
  305.             list($rawHeaders$content) = explode("\r\n\r\n"$part2);
  306.             $content substr($content0strlen($content) - 2);
  307.             // Parse the headers list
  308.             $rawHeaders explode("\r\n"$rawHeaders);
  309.             $headers = array();
  310.             foreach ($rawHeaders as $header) {
  311.                 list($name$value) = explode(':'$header);
  312.                 $headers[strtolower($name)] = ltrim($value' ');
  313.             }
  314.             // Parse the Content-Disposition to get the field name, etc.
  315.             if (isset($headers['content-disposition'])) {
  316.                 preg_match(
  317.                     '/^form-data; *name="([^"]+)"(; *filename="([^"]+)")?/',
  318.                     $headers['content-disposition'],
  319.                     $matches
  320.                 );
  321.                 $fieldName $matches[1];
  322.                 $fileName = (isset($matches[3]) ? $matches[3] : null);
  323.                 // If we have a file, save it. Otherwise, save the data.
  324.                 if ($fileName !== null) {
  325.                     $tmp_dir ini_get('upload_tmp_dir');
  326.                     $localFileName tempnam($tmp_dir'php');
  327.                     file_put_contents($localFileName$content);
  328.                     $uploadedFile = new UploadedFile($localFileName$fileName$headers['content-type'], 0true);
  329.                     $files $this->transformData($fieldName$uploadedFile$files);
  330.                 } else {
  331.                     $data $this->transformData($fieldName$content$data);
  332.                 }
  333.             }
  334.         }
  335.         $data $this->merge($data);
  336.         $fields = new ParameterBag($data);
  337.         return ["inputs" => $fields->all(), "files" => $files];
  338.     }
  339.     /**
  340.      * @param string $name
  341.      * @param mixed $value
  342.      * @param array<mixed> $data
  343.      * @return array<mixed>
  344.      */
  345.     private function transformData(string $name$value, array $data = [])
  346.     {
  347.         $names explode('['$name);
  348.         $arrayKeys array_reverse($names);
  349.         $result $value;
  350.         foreach ($arrayKeys as $key) {
  351.             $key rtrim($key"]");
  352.             $result $result === "true" true $result;
  353.             $result $result === "false" false $result;
  354.             $result = ($key === "" or is_numeric($key)) ? [$result] : [rtrim($key"]") => $result];
  355.         }
  356.         return array_merge_recursive($result$data);
  357.     }
  358.     /**
  359.      * @param array<mixed> $array
  360.      * @return array<mixed>
  361.      */
  362.     public function merge(array $array): array
  363.     {
  364.         $newArray = [];
  365.         foreach ($array as $key => $data) {
  366.             if (is_array($data) && count($data) > 0) {
  367.                 if ($this->is_multidimensional($data)) {
  368.                     $arrayMerged array_merge_recursive(...$data);
  369.                     if ($this->is_multidimensional($arrayMerged)) {
  370.                         $result array_map(null, ...array_values($arrayMerged));
  371.                         $keys array_keys($arrayMerged);
  372.                         $result array_map(function ($v) use ($keys) {
  373.                             return array_combine($keys$v);
  374.                         }, $result);
  375.                         $newArray[$key] = $result;
  376.                     } else {
  377.                         $newArray[$key] = [$arrayMerged];
  378.                     }
  379.                 } else {
  380.                     $newArray[$key] = $data;
  381.                 }
  382.             } else {
  383.                 $newArray[$key] = $data;
  384.             }
  385.         }
  386.         return $newArray;
  387.     }
  388.     /**
  389.      * @param array<mixed> $array
  390.      * @return bool
  391.      */
  392.     public function is_multidimensional(array $array): bool
  393.     {
  394.         return count($array) !== count($arrayCOUNT_RECURSIVE);
  395.     }
  396. }