XmlFilter   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 94
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 29
c 1
b 0
f 0
dl 0
loc 94
rs 10
wmc 9

5 Methods

Rating   Name   Duplication   Size   Complexity  
A extract() 0 16 3
A filter() 0 3 1
A formatXmlError() 0 4 1
A validate() 0 15 2
A toSimpleXmlElement() 0 6 2
1
<?php
2
3
namespace TraderInteractive\Filter;
4
5
use LibXMLError;
6
use SimpleXMLElement;
7
use Throwable;
8
use TraderInteractive\Exceptions\FilterException;
9
10
final class XmlFilter
11
{
12
    /**
13
     * @var string
14
     */
15
    const LIBXML_ERROR_FORMAT = '%s on line %d at column %d';
16
17
    /**
18
     * @var string
19
     */
20
    const EXTRACT_NO_ELEMENT_FOUND_ERROR_FORMAT = "No element found at xpath '%s'";
21
22
    /**
23
     * @var string
24
     */
25
    const EXTRACT_MULTIPLE_ELEMENTS_FOUND_ERROR_FORMAT = "Multiple elements found at xpath '%s'";
26
27
    /**
28
     * @param string $xml            The value to be filtered.
29
     * @param string $schemaFilePath The full path to the XSD file used for validation.
30
     *
31
     * @return string
32
     *
33
     * @throws FilterException Thrown if the given value cannot be filtered.
34
     */
35
    public static function validate(string $xml, string $schemaFilePath) : string
36
    {
37
        $previousLibxmlUserInternalErrors = libxml_use_internal_errors(true);
38
        try {
39
            libxml_clear_errors();
40
41
            $document = dom_import_simplexml(self::toSimpleXmlElement($xml))->ownerDocument;
42
            if ($document->schemaValidate($schemaFilePath)) {
0 ignored issues
show
Bug introduced by
The method schemaValidate() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

42
            if ($document->/** @scrutinizer ignore-call */ schemaValidate($schemaFilePath)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
43
                return $xml;
44
            }
45
46
            $formattedXmlError = self::formatXmlError(libxml_get_last_error());
47
            throw new FilterException($formattedXmlError);
48
        } finally {
49
            libxml_use_internal_errors($previousLibxmlUserInternalErrors);
50
        }
51
    } //@codeCoverageIgnore
52
53
    /**
54
     * @param string $xml   The value to be filtered.
55
     * @param string $xpath The xpath to the element to be extracted.
56
     *
57
     * @return string
58
     *
59
     * @throws FilterException Thrown if the value cannot be filtered.
60
     */
61
    public static function extract(string $xml, string $xpath) : string
62
    {
63
        $simpleXmlElement = self::toSimpleXmlElement($xml);
64
        $elements = $simpleXmlElement->xpath($xpath);
65
66
        $elementCount = count($elements);
67
68
        if ($elementCount === 0) {
69
            throw new FilterException(sprintf(self::EXTRACT_NO_ELEMENT_FOUND_ERROR_FORMAT, $xpath));
70
        }
71
72
        if ($elementCount > 1) {
73
            throw new FilterException(sprintf(self::EXTRACT_MULTIPLE_ELEMENTS_FOUND_ERROR_FORMAT, $xpath));
74
        }
75
76
        return $elements[0]->asXML();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $elements[0]->asXML() could return the type true which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
77
    }
78
79
    /**
80
     * @param string $xml The value to be filtered.
81
     *
82
     * @return string
83
     *
84
     * @throws FilterException Thrown if the given string cannot be parsed as xml.
85
     */
86
    public static function filter(string $xml) : string
87
    {
88
        return self::toSimpleXmlElement($xml)->asXML();
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::toSimpleXmlElement($xml)->asXML() could return the type true which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
89
    }
90
91
    private static function toSimpleXmlElement(string $xml) : SimpleXMLElement
92
    {
93
        try {
94
            return new SimpleXMLElement($xml);
95
        } catch (Throwable $throwable) {
96
            throw new FilterException($throwable->getMessage());
97
        }
98
    }
99
100
    private static function formatXmlError(LibXMLError $error) : string
101
    {
102
        $message = trim($error->message);
103
        return sprintf(self::LIBXML_ERROR_FORMAT, $message, $error->line, $error->column);
104
    }
105
}
106