Passed
Pull Request — master (#55)
by Tim
01:56
created

QNameValue::getNamespacePrefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XML\Type;
6
7
use DOMElement;
8
use PREG_UNMATCHED_AS_NULL;
0 ignored issues
show
Bug introduced by
The type PREG_UNMATCHED_AS_NULL was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use SimpleSAML\XML\Assert\Assert;
10
use SimpleSAML\XML\Exception\SchemaViolationException;
11
use SimpleSAML\XML\Type\{AnyURIValue, NCNameValue};
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, SimpleSAML\XML\Type\AnyURIValue. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
Bug introduced by
This use statement conflicts with another class in this namespace, SimpleSAML\XML\Type\NCNameValue. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
13
use function preg_match;
14
15
/**
16
 * @package simplesaml/xml-common
17
 */
18
class QNameValue extends AbstractValueType
19
{
20
    protected ?AnyURIValue $namespaceURI;
21
    protected ?NCNameValue $namespacePrefix;
22
    protected NCNameValue $localName;
23
24
    private static string $qname_regex = '/^
25
        (?:
26
          \{                 # Match a literal {
27
            (\S+)            # Match one or more non-whitespace character
28
          \}                 # Match a literal }
29
          (?:
30
            ([\w_][\w.-]*)   # Match a-z or underscore followed by any word-character, dot or dash
31
            :                # Match a literal :
32
          )?
33
        )?                   # Namespace and prefix are optional
34
        ([\w_][\w.-]*)       # Match a-z or underscore followed by any word-character, dot or dash
35
        $/Dimx';
36
37
38
    /**
39
     * Sanitize the value.
40
     *
41
     * @param string $value  The unsanitized value
42
     * @return string
43
     */
44
    protected function sanitizeValue(string $value): string
45
    {
46
        return static::collapseWhitespace(static::normalizeWhitespace($value));
47
    }
48
49
50
    /**
51
     * Validate the value.
52
     *
53
     * @param string $value
54
     * @throws \SimpleSAML\XML\Exception\SchemaViolationException on failure
55
     * @return void
56
     */
57
    protected function validateValue(string $value): void
58
    {
59
        $qName = $this->sanitizeValue($value);
60
61
        /**
62
         * Split our custom format of {<namespaceURI>}<prefix>:<localName> into individual parts
63
         */
64
        $result = preg_match(
65
            self::$qname_regex,
66
            $qName,
67
            $matches,
68
            PREG_UNMATCHED_AS_NULL,
69
        );
70
71
        if ($result && count($matches) === 4) {
72
            list($qName, $namespaceURI, $namespacePrefix, $localName) = $matches;
73
74
            $this->namespaceURI = ($namespaceURI !== null) ? AnyURIValue::fromString($namespaceURI) : null;
75
            $this->namespacePrefix = ($namespacePrefix !== null) ? NCNameValue::fromString($namespacePrefix) : null;
76
            $this->localName = NCNameValue::fromString($localName);
77
        } else {
78
            throw new SchemaViolationException(sprintf('\'%s\' is not a valid xs:QName.', $qName));
79
        }
80
    }
81
82
83
    /**
84
     * Get the value.
85
     *
86
     * @return string
87
     */
88
    public function getValue(): string
89
    {
90
        return $this->getNamespacePrefix() . ':' . $this->getLocalName();
91
    }
92
93
94
    /**
95
     * Get the namespaceURI for this qualified name.
96
     *
97
     * @return \SimpleSAML\SAML11\Type\AnyURIValue|null
0 ignored issues
show
Bug introduced by
The type SimpleSAML\SAML11\Type\AnyURIValue was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
98
     */
99
    public function getNamespaceURI(): ?AnyURIValue
100
    {
101
        return $this->namespaceURI;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->namespaceURI also could return the type SimpleSAML\XML\Type\AnyURIValue which is incompatible with the documented return type SimpleSAML\SAML11\Type\AnyURIValue|null.
Loading history...
102
    }
103
104
105
    /**
106
     * Get the namespace-prefix for this qualified name.
107
     *
108
     * @return \SimpleSAML\XML\Type\NCNameValue|null
109
     */
110
    public function getNamespacePrefix(): ?NCNameValue
111
    {
112
        return $this->namespacePrefix;
113
    }
114
115
116
    /**
117
     * Get the local name for this qualified name.
118
     *
119
     * @return \SimpleSAML\XML\Type\NCNameValue
120
     */
121
    public function getLocalName(): NCNameValue
122
    {
123
        return $this->localName;
124
    }
125
126
127
    /**
128
     * @param \SimpleSAML\XML\Type\NCNameValue $localName
129
     * @param \SimpleSAML\XML\Type\AnyURIValue|null $namespaceURI
130
     * @param \SimpleSAML\XML\Type\NCNameValue|null $namespacePrefix
131
     * @return static
132
     */
133
    public static function fromParts(
134
        NCNameValue $localName,
135
        ?AnyURIValue $namespaceURI,
136
        ?NCNameValue $namespacePrefix,
137
    ): static {
138
        if ($namespaceURI === null) {
139
            // If we don't have a namespace, we can't have a prefix either
140
            Assert::null($namespacePrefix, SchemaViolationException::class);
141
            return new static($localName);
142
        }
143
144
        return new static(
145
            '{' . $namespaceURI->getValue() . '}'
146
            . ($namespacePrefix ? ($namespacePrefix->getValue() . ':') : '')
147
            . $localName,
148
        );
149
    }
150
151
152
    /**
153
     * @param string $qname
154
     */
155
    public static function fromDocument(
156
        string $qName,
157
        DOMElement $element,
158
    ) {
159
        $namespacePrefix = null;
160
        if (str_contains($qName, ':')) {
161
            list($namespacePrefix, $localName) = explode(':', $qName, 2);
162
        } else {
163
            // No prefix
164
            $localName = $qName;
165
        }
166
167
        // Will return the default namespace (if any) when prefix is NULL
168
        $namespaceURI = $element->lookupNamespaceUri($namespacePrefix);
169
170
        return new static('{' . $namespaceURI . '}' . $namespacePrefix . ':' . $localName);
171
    }
172
}
173