-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BUGFIX] Convert non-list arrays in API response
Resolves: #81
- Loading branch information
1 parent
26407a3
commit a236656
Showing
14 changed files
with
730 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the TYPO3 CMS extension "personio_jobs". | ||
* | ||
* Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
namespace CPSIT\Typo3PersonioJobs\Exception; | ||
|
||
use Exception; | ||
|
||
/** | ||
* InvalidArrayPathException | ||
* | ||
* @author Elias Häußler <e.haeussler@familie-redlich.de> | ||
* @license GPL-2.0-or-later | ||
*/ | ||
final class InvalidArrayPathException extends Exception | ||
{ | ||
public static function forUnexpectedType(string $path, string $expected, string $actual): self | ||
{ | ||
return new self( | ||
sprintf('Expected %s at array path "%s", got %s instead.', $expected, $path, $actual), | ||
1692177655, | ||
); | ||
} | ||
|
||
public static function forInvalidPathSegment(string $path): self | ||
{ | ||
return new self( | ||
sprintf('The array path segment "%s" is not valid.', $path), | ||
1692178102, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the TYPO3 CMS extension "personio_jobs". | ||
* | ||
* Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
namespace CPSIT\Typo3PersonioJobs\Exception; | ||
|
||
use Exception; | ||
|
||
/** | ||
* MalformedXmlException | ||
* | ||
* @author Elias Häußler <e.haeussler@familie-redlich.de> | ||
* @license GPL-2.0-or-later | ||
*/ | ||
final class MalformedXmlException extends Exception | ||
{ | ||
public static function create(string $input, string $error): self | ||
{ | ||
return new self( | ||
sprintf( | ||
'The string "%s" does not contain valid XML: %s', | ||
mb_strimwidth($input, 0, 100, '…'), | ||
$error, | ||
), | ||
1692170602, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the TYPO3 CMS extension "personio_jobs". | ||
* | ||
* Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
namespace CPSIT\Typo3PersonioJobs\Mapper\Source; | ||
|
||
use ArrayObject; | ||
use CPSIT\Typo3PersonioJobs\Exception\InvalidArrayPathException; | ||
use CPSIT\Typo3PersonioJobs\Exception\MalformedXmlException; | ||
use CPSIT\Typo3PersonioJobs\Utility\ArrayUtility; | ||
use Mtownsend\XmlToArray\XmlToArray; | ||
use Throwable; | ||
|
||
/** | ||
* XmlSource | ||
* | ||
* @author Elias Häußler <e.haeussler@familie-redlich.de> | ||
* @license GPL-2.0-or-later | ||
* | ||
* @extends ArrayObject<string, mixed> | ||
*/ | ||
final class XmlSource extends ArrayObject | ||
{ | ||
/** | ||
* @param array<string, mixed> $source | ||
*/ | ||
public function __construct(array $source) | ||
{ | ||
parent::__construct($source); | ||
} | ||
|
||
/** | ||
* @throws MalformedXmlException | ||
*/ | ||
public static function fromXml(string $xml): self | ||
{ | ||
set_error_handler(static fn (int $code, string $message) => self::handleParseError($xml, $message)); | ||
|
||
try { | ||
$source = XmlToArray::convert($xml); | ||
} catch (Throwable $exception) { | ||
self::handleParseError($xml, $exception->getMessage()); | ||
} finally { | ||
restore_error_handler(); | ||
} | ||
|
||
return new self($source); | ||
} | ||
|
||
/** | ||
* @throws InvalidArrayPathException | ||
*/ | ||
public function asCollection(string $node): self | ||
{ | ||
$clone = clone $this; | ||
$clone->exchangeArray( | ||
ArrayUtility::convertToCollection((array)$clone, $node), | ||
); | ||
|
||
return $clone; | ||
} | ||
|
||
/** | ||
* @throws MalformedXmlException | ||
*/ | ||
private static function handleParseError(string $xml, string $message): never | ||
{ | ||
throw MalformedXmlException::create($xml, $message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the TYPO3 CMS extension "personio_jobs". | ||
* | ||
* Copyright (C) 2023 Elias Häußler <e.haeussler@familie-redlich.de> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
namespace CPSIT\Typo3PersonioJobs\Utility; | ||
|
||
use CPSIT\Typo3PersonioJobs\Exception\InvalidArrayPathException; | ||
|
||
/** | ||
* ArrayUtility | ||
* | ||
* @author Elias Häußler <e.haeussler@familie-redlich.de> | ||
* @license GPL-2.0-or-later | ||
*/ | ||
final class ArrayUtility | ||
{ | ||
/** | ||
* @param array<string, mixed> $array | ||
* @return array<string, mixed> | ||
* @throws InvalidArrayPathException | ||
*/ | ||
public static function convertToCollection(array $array, string $path): array | ||
{ | ||
$reference = &$array; | ||
$pathSegments = str_getcsv($path, '.'); | ||
$remainingSegments = $pathSegments; | ||
$currentPathSegments = []; | ||
|
||
foreach ($pathSegments as $pathSegment) { | ||
$currentPathSegments[] = array_shift($remainingSegments); | ||
|
||
// Validate path segment | ||
if (!is_string($pathSegment) || trim($pathSegment) === '') { | ||
throw InvalidArrayPathException::forInvalidPathSegment(implode('.', $currentPathSegments)); | ||
} | ||
|
||
// Handle non-array values | ||
if (!is_array($reference)) { | ||
throw InvalidArrayPathException::forUnexpectedType( | ||
implode('.', $currentPathSegments), | ||
'array', | ||
gettype($reference), | ||
); | ||
} | ||
|
||
// Handle placeholder for lists | ||
if ($pathSegment === '*') { | ||
$reference = self::convertListToCollection($reference, implode('.', $remainingSegments)); | ||
|
||
return $array; | ||
} | ||
|
||
// Create node value if not exists | ||
if (!array_key_exists($pathSegment, $reference)) { | ||
$reference[$pathSegment] = []; | ||
} | ||
|
||
$reference = &$reference[$pathSegment]; | ||
} | ||
|
||
// Handle non-array values | ||
if (!is_array($reference)) { | ||
throw InvalidArrayPathException::forUnexpectedType($path, 'array', gettype($reference)); | ||
} | ||
|
||
// Convert array to list | ||
if (!array_is_list($reference)) { | ||
$reference = [$reference]; | ||
} | ||
|
||
return $array; | ||
} | ||
|
||
/** | ||
* @param array<mixed> $array | ||
* @return array<int, mixed> | ||
* @throws InvalidArrayPathException | ||
*/ | ||
private static function convertListToCollection(array $array, string $path): array | ||
{ | ||
// Handle non-lists | ||
if (!array_is_list($array)) { | ||
throw InvalidArrayPathException::forUnexpectedType($path, 'list', 'array'); | ||
} | ||
|
||
foreach ($array as $key => $value) { | ||
$array[$key] = self::convertToCollection($value, $path); | ||
} | ||
|
||
return $array; | ||
} | ||
} |
Oops, something went wrong.