eliashaeussler/valinor-xml

View on GitHub
src/Mapper/Source/XmlSource.php

Summary

Maintainability
A
0 mins
Test Coverage
A
92%
<?php

declare(strict_types=1);

/*
 * This file is part of the Composer package "eliashaeussler/valinor-xml".
 *
 * Copyright (C) 2024 Elias Häußler <elias@haeussler.dev>
 *
 * 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 3 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 EliasHaeussler\ValinorXml\Mapper\Source;

use ArrayObject;
use EliasHaeussler\ValinorXml\Exception;
use EliasHaeussler\ValinorXml\Helper;
use Mtownsend\XmlToArray;
use Throwable;

use function restore_error_handler;
use function set_error_handler;

/**
 * XmlSource.
 *
 * @author Elias Häußler <elias@haeussler.dev>
 * @license GPL-3.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 Exception\XmlIsMalformed
     */
    public static function fromXmlString(string $xml): self
    {
        set_error_handler(static fn (int $code, string $message) => self::handleParseError($xml, $message));

        try {
            $source = XmlToArray\XmlToArray::convert($xml);
        } catch (Throwable $exception) {
            self::handleParseError($xml, $exception->getMessage());
        } finally {
            restore_error_handler();
        }

        return new self($source);
    }

    /**
     * @throws Exception\FileDoesNotExist
     * @throws Exception\FileIsNotReadable
     * @throws Exception\XmlIsMalformed
     */
    public static function fromXmlFile(string $file): self
    {
        if (!file_exists($file)) {
            throw new Exception\FileDoesNotExist($file);
        }

        if (!is_readable($file)) {
            throw new Exception\FileIsNotReadable($file);
        }

        $xml = file_get_contents($file);

        if (false === $xml) {
            throw new Exception\FileIsNotReadable($file);
        }

        return self::fromXmlString($xml);
    }

    /**
     * @throws Exception\ArrayPathHasUnexpectedType
     * @throws Exception\ArrayPathIsInvalid
     */
    public function asCollection(string $node): self
    {
        $clone = clone $this;
        $clone->exchangeArray(
            Helper\ArrayHelper::convertToCollection((array) $clone, $node),
        );

        return $clone;
    }

    /**
     * @throws Exception\XmlIsMalformed
     */
    private static function handleParseError(string $xml, string $message): never
    {
        throw new Exception\XmlIsMalformed($xml, $message);
    }
}