Issues (209)

src/LightSaml/Model/AbstractSamlModel.php (7 issues)

1
<?php
2
3
/*
4
 * This file is part of the LightSAML-Core package.
5
 *
6
 * (c) Milos Tomic <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace LightSaml\Model;
13
14
use LightSaml\Error\LightSamlXmlException;
15
use LightSaml\Model\Context\DeserializationContext;
16
use LightSaml\Model\Context\SerializationContext;
17
18
abstract class AbstractSamlModel implements SamlElementInterface
19
{
20
    /**
21
     * @param string      $name
22
     * @param string|null $namespace
23
     *
24
     * @return \DOMElement
25
     */
26
    protected function createElement($name, $namespace, \DOMNode $parent, SerializationContext $context)
27
    {
28
        if ($namespace) {
29
            $result = $context->getDocument()->createElementNS($namespace, $name);
30
        } else {
31
            $result = $context->getDocument()->createElement($name);
32
        }
33
        $parent->appendChild($result);
34
35
        return $result;
36
    }
37
38
    /**
39
     * @param string      $name
40
     * @param string|null $namespace
41
     *
42
     * @throws \LogicException
43
     */
44
    private function oneElementToXml($name, \DOMNode $parent, SerializationContext $context, $namespace = null)
45
    {
46
        $value = $this->getPropertyValue($name);
47
        if (null == $value) {
48
            return;
49
        }
50
        if ($value instanceof SamlElementInterface) {
51
            $value->serialize($parent, $context);
52
        } elseif (is_string($value)) {
53
            if ($namespace) {
54
                $node = $context->getDocument()->createElementNS($namespace, $name, $value);
55
            } else {
56
                $node = $context->getDocument()->createElement($name, $value);
57
            }
58
            $parent->appendChild($node);
59
        } else {
60
            throw new \LogicException(sprintf("Element '%s' must implement SamlElementInterface or be a string", $name));
61
        }
62
    }
63
64
    /**
65
     * @param array|string[] $names
66
     * @param string|null    $namespace
67
     */
68
    protected function singleElementsToXml(array $names, \DOMNode $parent, SerializationContext $context, $namespace = null)
69
    {
70
        foreach ($names as $name) {
71
            $this->oneElementToXml($name, $parent, $context, $namespace);
72
        }
73
    }
74
75
    /**
76
     * @param array|null  $value
77
     * @param string|null $nodeName
78
     * @param string|null $namespaceUri
79
     *
80
     * @throws \LogicException
81
     */
82
    protected function manyElementsToXml($value, \DOMNode $node, SerializationContext $context, $nodeName = null, $namespaceUri = null)
83
    {
84
        if (false == $value) {
85
            return;
86
        }
87
88
        if (false == is_array($value)) {
0 ignored issues
show
The condition false == is_array($value) is always false.
Loading history...
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
89
            throw new \LogicException('value must be array or null');
90
        }
91
92
        foreach ($value as $object) {
93
            if ($object instanceof SamlElementInterface) {
94
                if ($nodeName) {
95
                    throw new \LogicException('nodeName should not be specified when serializing array of SamlElementInterface');
96
                }
97
                $object->serialize($node, $context);
98
            } elseif ($nodeName) {
99
                if ($namespaceUri) {
100
                    $child = $context->getDocument()->createElementNS($namespaceUri, $nodeName, (string) $object);
101
                } else {
102
                    $child = $context->getDocument()->createElement($nodeName, (string) $object);
103
                }
104
                $node->appendChild($child);
105
            } else {
106
                throw new \LogicException('Can handle only array of AbstractSamlModel or strings with nodeName parameter specified');
107
            }
108
        }
109
    }
110
111
    /**
112
     * @param string      $nodeName
113
     * @param string|null $namespacePrefix
114
     * @param string      $class
115
     * @param string      $methodName
116
     *
117
     * @throws \LogicException
118
     */
119
    protected function manyElementsFromXml(\DOMElement $node, DeserializationContext $context, $nodeName, $namespacePrefix, $class, $methodName)
120
    {
121
        if ($namespacePrefix) {
122
            $query = sprintf('%s:%s', $namespacePrefix, $nodeName);
123
        } else {
124
            $query = sprintf('%s', $nodeName);
125
        }
126
127
        foreach ($context->getXpath()->query($query, $node) as $xml) {
128
            /* @var \DOMElement $xml */
129
            if ($class) {
130
                /** @var SamlElementInterface $object */
131
                $object = new $class();
132
                if (false == $object instanceof SamlElementInterface) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
133
                    throw new \LogicException(sprintf("Node '%s' class '%s' must implement SamlElementInterface", $nodeName, $class));
134
                }
135
                $object->deserialize($xml, $context);
136
                $this->{$methodName}($object);
137
            } else {
138
                $object = $xml->textContent;
139
                $this->{$methodName}($object);
140
            }
141
        }
142
    }
143
144
    /**
145
     * @param string $name
146
     *
147
     * @throws \LogicException
148
     *
149
     * @return bool True if property value is not empty and attribute was set to the element
150
     */
151
    protected function singleAttributeToXml($name, \DOMElement $element)
152
    {
153
        $value = $this->getPropertyValue($name);
154
        if (null !== $value && '' !== $value) {
155
            if (is_bool($value)) {
156
                $element->setAttribute($name, $value ? 'true' : 'false');
157
            } else {
158
                $element->setAttribute($name, $value);
159
            }
160
161
            return true;
162
        }
163
164
        return false;
165
    }
166
167
    /**
168
     * @param array|string[] $names
169
     */
170
    protected function attributesToXml(array $names, \DOMElement $element)
171
    {
172
        foreach ($names as $name) {
173
            $this->singleAttributeToXml($name, $element);
174
        }
175
    }
176
177
    /**
178
     * @param string $expectedName
179
     * @param string $expectedNamespaceUri
180
     */
181
    protected function checkXmlNodeName(\DOMNode &$node, $expectedName, $expectedNamespaceUri)
182
    {
183
        if ($node instanceof \DOMDocument) {
184
            $node = $node->firstChild;
185
        }
186
        while ($node && $node instanceof \DOMComment) {
187
            $node = $node->nextSibling;
188
        }
189
        if (null === $node) {
190
            throw new LightSamlXmlException(sprintf("Unable to find expected '%s' xml node and '%s' namespace", $expectedName, $expectedNamespaceUri));
191
        } elseif ($node->localName != $expectedName || $node->namespaceURI != $expectedNamespaceUri) {
192
            throw new LightSamlXmlException(sprintf("Expected '%s' xml node and '%s' namespace but got node '%s' and namespace '%s'", $expectedName, $expectedNamespaceUri, $node->localName, $node->namespaceURI));
193
        }
194
    }
195
196
    /**
197
     * @param string $attributeName
198
     */
199
    protected function singleAttributeFromXml(\DOMElement $node, $attributeName)
200
    {
201
        $value = $node->getAttribute($attributeName);
202
        if ('' !== $value) {
203
            $setter = 'set'.$attributeName;
204
            if (method_exists($this, $setter)) {
205
                $this->{$setter}($value);
206
            }
207
        }
208
    }
209
210
    /**
211
     * @param string $elementName
212
     * @param string $class
213
     * @param string $namespacePrefix
214
     *
215
     * @throws \LogicException
216
     */
217
    protected function oneElementFromXml(\DOMElement $node, DeserializationContext $context, $elementName, $class, $namespacePrefix)
218
    {
219
        if ($namespacePrefix) {
220
            $query = sprintf('./%s:%s', $namespacePrefix, $elementName);
221
        } else {
222
            $query = sprintf('./%s', $elementName);
223
        }
224
        $arr = $context->getXpath()->query($query, $node);
225
        $value = $arr->length > 0 ? $arr->item(0) : null;
226
227
        if ($value) {
228
            $setter = 'set'.$elementName;
229
            if (false == method_exists($this, $setter)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
230
                throw new \LogicException(sprintf("Unable to find setter for element '%s' in class '%s'", $elementName, get_class($this)));
231
            }
232
233
            if ($class) {
234
                /** @var AbstractSamlModel $object */
235
                $object = new $class();
236
                if (false == $object instanceof \LightSaml\Model\SamlElementInterface) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
237
                    throw new \LogicException(sprintf("Specified class '%s' for element '%s' must implement SamlElementInterface", $class, $elementName));
238
                }
239
240
                $object->deserialize($value, $context);
241
            } else {
242
                $object = $value->textContent;
243
            }
244
245
            $this->{$setter}($object);
246
        }
247
    }
248
249
    /**
250
     * @param array $options elementName=>class
251
     */
252
    protected function singleElementsFromXml(\DOMElement $node, DeserializationContext $context, array $options)
253
    {
254
        foreach ($options as $elementName => $info) {
255
            $this->oneElementFromXml($node, $context, $elementName, $info[1], $info[0]);
256
        }
257
    }
258
259
    protected function attributesFromXml(\DOMElement $node, array $attributeNames)
260
    {
261
        foreach ($attributeNames as $attributeName) {
262
            $this->singleAttributeFromXml($node, $attributeName);
263
        }
264
    }
265
266
    /**
267
     * @param string $name
268
     *
269
     * @return mixed
270
     *
271
     * @throws \LogicException
272
     */
273
    private function getPropertyValue($name)
274
    {
275
        if (false !== ($pos = strpos($name, ':'))) {
276
            $name = substr($name, $pos + 1);
277
        }
278
        $getter = 'get'.$name.'String';
279
        if (false == method_exists($this, $getter)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
280
            $getter = 'get'.$name;
281
        }
282
        if (false == method_exists($this, $getter)) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
283
            throw new \LogicException(sprintf("Unable to find getter method for '%s' on '%s'", $name, get_class($this)));
284
        }
285
        $value = $this->{$getter}();
286
287
        return $value;
288
    }
289
}
290