GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

WSDL::save()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 5
cp 0
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
crap 6
1
<?php
2
3
namespace PHP2WSDL;
4
5
use DOMDocument;
6
use DOMElement;
7
use RuntimeException;
8
use Wingu\OctopusCore\Reflection\ReflectionClass;
9
10
/**
11
 * Create a WSDL.
12
 */
13
class WSDL
14
{
15
16
    /**
17
     * The DOMDocument instance.
18
     *
19
     * @var DOMDocument
20
     */
21
    private $dom;
22
23
    /**
24
     * The WSDL node from the full DOMDocument.
25
     *
26
     * @var DOMDocument
27
     */
28
    private $wsdl;
29
30
    /**
31
     * Schema node of the WSDL.
32
     *
33
     * @var DOMElement
34
     */
35
    private $schema;
36
37
    /**
38
     * Types defined on schema.
39
     *
40
     * @var array
41
     */
42
    private $includedTypes;
43
44
    /**
45
     * Default XSD Types.
46
     *
47
     * @var array
48
     */
49
    protected static $XSDTypes = array(
50
        'string' => 'xsd:string',
51
        'bool' => 'xsd:boolean',
52
        'boolean' => 'xsd:boolean',
53
        'int' => 'xsd:int',
54
        'integer' => 'xsd:int',
55
        'double' => 'xsd:float',
56
        'float' => 'xsd:float',
57
        'decimal' => 'xsd:decimal',
58
        'array' => 'soap-enc:Array',
59
        'time' => 'xsd:time',
60
        'date' => 'xsd:date',
61
        'datetime' => 'xsd:dateTime',
62
        'anytype' => 'xsd:anyType',
63
        'unknown_type' => 'xsd:anyType',
64
        'mixed' => 'xsd:anyType',
65
        'object' => 'xsd:struct',
66
        'base64binary' => 'xsd:base64Binary'
67
    );
68
69
    /**
70
     * Constructor.
71
     *
72
     * @param string $name The name of the web service.
73
     * @param string $uri URI where the WSDL will be available.
74
     * @param string $xslUri The URI to the stylesheet.
75
     * @throws RuntimeException If the DOM Document can not be created.
76
     */
77 33
    public function __construct($name, $uri, $xslUri = null)
78
    {
79 33
        $this->dom = new DOMDocument('1.0');
80 33
        if ($xslUri !== null) {
81 3
            $this->setStylesheet($xslUri);
82 3
        }
83
84 33
        $definitions = $this->dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'definitions');
85 33
        $definitions->setAttribute('name', $name);
86 33
        $definitions->setAttribute('targetNamespace', $uri);
87 33
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:tns', htmlspecialchars($uri));
88 33
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/');
89 33
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
90 33
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap-enc', 'http://schemas.xmlsoap.org/soap/encoding/');
91 33
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:wsdl', 'http://schemas.xmlsoap.org/wsdl/');
92 33
        $this->dom->appendChild($definitions);
93
94 33
        $this->wsdl = $this->dom->documentElement;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->dom->documentElement of type object<DOMElement> is incompatible with the declared type object<DOMDocument> of property $wsdl.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
95
96 33
        $this->schema = $this->dom->createElement('xsd:schema');
97 33
        $this->schema->setAttribute('targetNamespace', $uri);
98
99
        // Add the import for validation.
100 33
        $import = $this->dom->createElement('xsd:import');
101 33
        $import->setAttribute('namespace', 'http://schemas.xmlsoap.org/soap/encoding/');
102 33
        $this->schema->appendChild($import);
103
104 33
        $types = $this->dom->createElement('types');
105 33
        $types->appendChild($this->schema);
106 33
        $this->wsdl->appendChild($types);
107 33
    }
108
109
    /**
110
     * Set the stylesheet for the WSDL.
111
     *
112
     * @param string $xslUri The URI to the stylesheet.
113
     * @return WSDL
114
     */
115 3
    private function setStylesheet($xslUri)
116
    {
117 3
        $xslt = $this->dom->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="' . $xslUri . '"');
118 3
        $this->dom->appendChild($xslt);
119 3
    }
120
121
    /**
122
     * Add a message element to the WSDL.
123
     *
124
     * @param string $name The name for the message.
125
     * @param array $parts Array of parts for the message ('name'=>'type' or 'name'=>array('type'=>'type', 'element'=>'element')).
126
     * @return DOMElement
127
     * @link http://www.w3.org/TR/wsdl#_messages
128
     */
129 30
    public function addMessage($name, array $parts)
130
    {
131 30
        $message = $this->dom->createElement('message');
132 30
        $message->setAttribute('name', $name);
133
134 30
        foreach ($parts as $name => $type) {
135 30
            $part = $this->dom->createElement('part');
136 30
            $part->setAttribute('name', $name);
137 30
            if (is_array($type)) {
138 30
                foreach ($type as $key => $value) {
139 30
                    $part->setAttribute($key, $value);
140 30
                }
141 30
            } else {
142
                $part->setAttribute('type', $type);
143
            }
144
145 30
            $message->appendChild($part);
146 30
        }
147
148 30
        $this->wsdl->appendChild($message);
149
150 30
        return $message;
151
    }
152
153
    /**
154
     * Add a portType to element to the WSDL.
155
     *
156
     * @param string $name The name of the portType.
157
     * @return DOMElement
158
     */
159 33
    public function addPortType($name)
160
    {
161 33
        $portType = $this->dom->createElement('portType');
162 33
        $portType->setAttribute('name', $name);
163 33
        $this->wsdl->appendChild($portType);
164
165 33
        return $portType;
166
    }
167
168
    /**
169
     * Add a binding element to the WSDL.
170
     *
171
     * @param string $name The name of the binding.
172
     * @param string $portType The portType to bind.
173
     * @return DOMElement
174
     */
175 33
    public function addBinding($name, $portType)
176
    {
177 33
        $binding = $this->dom->createElement('binding');
178 33
        $binding->setAttribute('name', $name);
179 33
        $binding->setAttribute('type', $portType);
180
181 33
        $this->wsdl->appendChild($binding);
182
183 33
        return $binding;
184
    }
185
186
    /**
187
     * Add a SOAP binding element to the Binding element.
188
     *
189
     * @param DOMElement $binding The binding element (from addBinding() method).
190
     * @param string $style The binding style (rpc or document).
191
     * @param string $transport The transport method.
192
     * @return DOMElement
193
     * @link http://www.w3.org/TR/wsdl#_soap:binding
194
     */
195 33
    public function addSoapBinding(
196
        DOMElement $binding,
197
        $style = 'rpc',
198
        $transport = 'http://schemas.xmlsoap.org/soap/http'
199
    ) {
200 33
        $soapBinding = $this->dom->createElement('soap:binding');
201 33
        $soapBinding->setAttribute('style', $style);
202 33
        $soapBinding->setAttribute('transport', $transport);
203
204 33
        $binding->appendChild($soapBinding);
205
206 33
        return $soapBinding;
207
    }
208
209
    /**
210
     * Add an operation to a binding element.
211
     *
212
     * @param DOMElement $binding The binding element (from addBinding() method).
213
     * @param string $name The name of the operation.
214
     * @param array $input Attributes for the input element (use, namespace, encodingStyle).
215
     * @param array $output Attributes for the output element (use, namespace, encodingStyle).
216
     * @return DOMElement
217
     * @link http://www.w3.org/TR/wsdl#_soap:body
218
     */
219 30
    public function addBindingOperation(DOMElement $binding, $name, array $input = null, array $output = null)
220
    {
221 30
        $operation = $this->dom->createElement('operation');
222 30
        $operation->setAttribute('name', $name);
223
224 30
        if (is_array($input)) {
225 30
            $inputElement = $this->dom->createElement('input');
226 30
            $soapElement = $this->dom->createElement('soap:body');
227 30
            foreach ($input as $name => $value) {
228 30
                $soapElement->setAttribute($name, $value);
229 30
            }
230
231 30
            $inputElement->appendChild($soapElement);
232 30
            $operation->appendChild($inputElement);
233 30
        }
234
235 30
        if (is_array($output)) {
236 30
            $outputElement = $this->dom->createElement('output');
237 30
            $soapElement = $this->dom->createElement('soap:body');
238 30
            foreach ($output as $name => $value) {
239 30
                $soapElement->setAttribute($name, $value);
240 30
            }
241
242 30
            $outputElement->appendChild($soapElement);
243 30
            $operation->appendChild($outputElement);
244 30
        }
245
246 30
        $binding->appendChild($operation);
247
248 30
        return $operation;
249
    }
250
251
    /**
252
     * Add an operation element to a portType element.
253
     *
254
     * @param DOMElement $portType The port type element (from addPortType() method).
255
     * @param string $name The name of the operation.
256
     * @param string $inputMessage The input message.
257
     * @param string $outputMessage The output message.
258
     * @return DOMElement
259
     * @link http://www.w3.org/TR/wsdl#_request-response
260
     */
261 30
    public function addPortOperation(DOMElement $portType, $name, $inputMessage = null, $outputMessage = null)
262
    {
263 30
        $operation = $this->dom->createElement('operation');
264 30
        $operation->setAttribute('name', $name);
265
266 30
        if (is_string($inputMessage) && (strlen(trim($inputMessage)) >= 1)) {
267 30
            $inputElement = $this->dom->createElement('input');
268 30
            $inputElement->setAttribute('message', $inputMessage);
269 30
            $operation->appendChild($inputElement);
270 30
        }
271
272 30
        if (is_string($outputMessage) && (strlen(trim($outputMessage)) >= 1)) {
273 9
            $outputElement = $this->dom->createElement('output');
274 9
            $outputElement->setAttribute('message', $outputMessage);
275 9
            $operation->appendChild($outputElement);
276 9
        }
277
278 30
        $portType->appendChild($operation);
279
280 30
        return $operation;
281
    }
282
283
    /**
284
     * Add a SOAP operation to an operation element.
285
     *
286
     * @param DOMElement $binding The binding element (from addBindingOperation() method).
287
     * @param string $soapAction SOAP Action.
288
     * @return DOMElement
289
     * @link http://www.w3.org/TR/wsdl#_soap:operation
290
     */
291 30
    public function addSoapOperation(DOMElement $binding, $soapAction)
292
    {
293 30
        $soapOperation = $this->dom->createElement('soap:operation');
294 30
        $soapOperation->setAttribute('soapAction', $soapAction);
295
296 30
        $binding->insertBefore($soapOperation, $binding->firstChild);
297
298 30
        return $soapOperation;
299
    }
300
301
    /**
302
     * Add a service element to the WSDL.
303
     *
304
     * @param string $name Service name.
305
     * @param string $portName Port name.
306
     * @param string $binding Binding for the port.
307
     * @param string $location SOAP Address location.
308
     * @return DOMElement
309
     * @link http://www.w3.org/TR/wsdl#_services
310
     */
311 33
    public function addService($name, $portName, $binding, $location)
312
    {
313 33
        $service = $this->dom->createElement('service');
314 33
        $service->setAttribute('name', $name);
315
316 33
        $port = $this->dom->createElement('port');
317 33
        $port->setAttribute('name', $portName);
318 33
        $port->setAttribute('binding', $binding);
319
320 33
        $soapAddress = $this->dom->createElement('soap:address');
321 33
        $soapAddress->setAttribute('location', $location);
322
323 33
        $port->appendChild($soapAddress);
324 33
        $service->appendChild($port);
325
326 33
        $this->wsdl->appendChild($service);
327
328 33
        return $service;
329
    }
330
331
    /**
332
     * Add a documentation element to another element in the WSDL.
333
     *
334
     * @param DOMElement $inputElement The DOMElement element to add the documentation.
335
     * @param string $documentation The documentation text.
336
     * @return DOMElement
337
     * @link http://www.w3.org/TR/wsdl#_documentation
338
     */
339 9
    public function addDocumentation(DOMElement $inputElement, $documentation)
340
    {
341 9
        if ($inputElement === $this) {
342
            $element = $this->dom->documentElement;
343
        } else {
344 9
            $element = $inputElement;
345
        }
346
347 9
        $doc = $this->dom->createElement('documentation');
348 9
        $cdata = $this->dom->createTextNode($documentation);
349 9
        $doc->appendChild($cdata);
350
351 9
        if ($element->hasChildNodes()) {
352 9
            $element->insertBefore($doc, $element->firstChild);
353 9
        } else {
354
            $element->appendChild($doc);
355
        }
356
357 9
        return $doc;
358
    }
359
360
    /**
361
     * Add a complex type.
362
     *
363
     * @param string $type The type.
364
     * @param string $wsdlType The WSDL type.
365
     */
366 15
    public function addType($type, $wsdlType)
367
    {
368 15
        $this->includedTypes[$type] = $wsdlType;
369 15
    }
370
371
    /**
372
     * Get the XSD Type from a PHP type.
373
     *
374
     * @param string $type The type to get the XSD type from.
375
     * @return string
376
     */
377 30
    public function getXSDType($type)
378
    {
379 30
        if ($this->isXDSType($type)) {
380 27
            return self::$XSDTypes[strtolower($type)];
381 21
        } elseif ($type) {
382 21
            if (strpos($type, '[]')) {
383 9
                if ($this->isXDSType(str_replace('[]', '', $type))) {
384 6
                    return self::$XSDTypes['array'];
385
                }
386 3
            }
387
388 15
            return $this->addComplexType($type);
389
        } else {
390
            return null;
391
        }
392
    }
393
394
    /**
395
     * Check if a type is a XDS.
396
     *
397
     * @param string $type The type to check.
398
     * @return boolean
399
     */
400 30
    private function isXDSType($type)
401
    {
402 30
        $typeToLowerString = strtolower($type);
403 30
        return isset(self::$XSDTypes[$typeToLowerString]);
404
    }
405
406
    /**
407
     * Add a complex type.
408
     *
409
     * @param string $type Name of the class.
410
     * @return string
411
     */
412 15
    public function addComplexType($type)
413
    {
414 15
        if (isset($this->includedTypes[$type])) {
415
            return $this->includedTypes[$type];
416
        }
417
418 15
        if (strpos($type, '[]') !== false) {
419 3
            return $this->addComplexTypeArray(str_replace('[]', '', $type), $type);
420
        }
421
422 15
        $class = new ReflectionClass($type);
423
424 15
        $soapTypeName = static::typeToQName($type);
425 15
        $soapType = 'tns:' . $soapTypeName;
426
427 15
        $this->addType($type, $soapType);
428
429 15
        $all = $this->dom->createElement('xsd:all');
430 15
        foreach ($class->getProperties() as $property) {
431 9
            $annotationsCollection = $property->getReflectionDocComment()->getAnnotationsCollection();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ReflectionProperty as the method getReflectionDocComment() does only exist in the following sub-classes of ReflectionProperty: Wingu\OctopusCore\Reflection\ReflectionProperty. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
432 9
            if ($property->isPublic() && $annotationsCollection->hasAnnotationTag('var')) {
433 9
                $element = $this->dom->createElement('xsd:element');
434 9
                $element->setAttribute('name', $property->getName());
435 9
                $propertyVarAnnotation = $annotationsCollection->getAnnotation('var');
436 9
                $element->setAttribute('type', $this->getXSDType(reset($propertyVarAnnotation)->getVarType()));
437 9
                if ($annotationsCollection->hasAnnotationTag('nillable')) {
438
                    $element->setAttribute('nillable', 'true');
439
                }
440 9
                if ($annotationsCollection->hasAnnotationTag('minOccurs')) {
441 6
                    $minOccurs = intval($annotationsCollection->getAnnotation('minOccurs')[0]->getDescription());
442 6
                    $element->setAttribute('minOccurs', $minOccurs > 0 ? $minOccurs : 0);
443 6
                    if ($minOccurs > 1) {
444 3
                        $all = $this->changeAllToSequence($all);
445 3
                    }
446 6
                }
447 9
                if ($annotationsCollection->hasAnnotationTag('maxOccurs')) {
448 6
                    $maxOccurs = intval($annotationsCollection->getAnnotation('maxOccurs')[0]->getDescription());
449 6
                    $element->setAttribute('maxOccurs', $maxOccurs > 0 ? $maxOccurs : 'unbounded');
450 6
                    if ($maxOccurs !== 1) {
451 3
                        $all = $this->changeAllToSequence($all);
452 3
                    }
453 6
                }
454 9
                $all->appendChild($element);
455 9
            }
456 15
        }
457
458 15
        $complexType = $this->dom->createElement('xsd:complexType');
459 15
        $complexType->setAttribute('name', $soapTypeName);
460 15
        $complexType->appendChild($all);
461
462 15
        $this->schema->appendChild($complexType);
463
464 15
        return $soapType;
465
    }
466
467
    /**
468
     * Add an array of complex type.
469
     *
470
     * @param string $singularType The type name without the '[]'.
471
     * @param string $type The original type name.
472
     * @return string
473
     */
474 3
    protected function addComplexTypeArray($singularType, $type)
475
    {
476 3
        $xsdComplexTypeName = 'ArrayOf' . static::typeToQName($singularType);
477 3
        $xsdComplexType = 'tns:' . $xsdComplexTypeName;
478
479
        // Register type here to avoid recursion.
480 3
        $this->addType($type, $xsdComplexType);
481
482
        // Process singular type using DefaultComplexType strategy.
483 3
        $this->addComplexType($singularType);
484
485
        // Add array type structure to WSDL document.
486 3
        $complexType = $this->dom->createElement('xsd:complexType');
487 3
        $complexType->setAttribute('name', $xsdComplexTypeName);
488
489 3
        $complexContent = $this->dom->createElement('xsd:complexContent');
490 3
        $complexType->appendChild($complexContent);
491
492 3
        $xsdRestriction = $this->dom->createElement('xsd:restriction');
493 3
        $xsdRestriction->setAttribute('base', 'soap-enc:Array');
494 3
        $complexContent->appendChild($xsdRestriction);
495
496 3
        $xsdAttribute = $this->dom->createElement('xsd:attribute');
497 3
        $xsdAttribute->setAttribute('ref', 'soap-enc:arrayType');
498 3
        $xsdAttribute->setAttribute('wsdl:arrayType', 'tns:' . static::typeToQName($singularType) . '[]');
499 3
        $xsdRestriction->appendChild($xsdAttribute);
500
501 3
        $this->schema->appendChild($complexType);
502
503 3
        return $xsdComplexType;
504
    }
505
506
    /**
507
     * Parse an xsd:element represented as an array into a DOMElement.
508
     *
509
     * @param array $element An xsd:element represented as an array.
510
     * @return DOMElement
511
     */
512
    private function parseElement(array $element)
513
    {
514
        $elementXml = $this->dom->createElement('xsd:element');
515
        foreach ($element as $key => $value) {
516
            if (in_array($key, array('sequence', 'all', 'choice'))) {
517
                if (is_array($value)) {
518
                    $complexType = $this->dom->createElement('xsd:complexType');
519
                    if (count($value) > 0) {
520
                        $container = $this->dom->createElement('xsd:' . $key);
521
                        foreach ($value as $subElement) {
522
                            $subElementXml = $this->parseElement($subElement);
523
                            $container->appendChild($subElementXml);
524
                        }
525
526
                        $complexType->appendChild($container);
527
                    }
528
529
                    $elementXml->appendChild($complexType);
530
                }
531
            } else {
532
                $elementXml->setAttribute($key, $value);
533
            }
534
        }
535
536
        return $elementXml;
537
    }
538
539
    /**
540
     * Add an xsd:element represented as an array to the schema.
541
     *
542
     * @param array $element An xsd:element represented as an array.
543
     * @return string
544
     */
545
    public function addElement(array $element)
546
    {
547
        $elementXml = $this->parseElement($element);
548
        $this->schema->appendChild($elementXml);
549
        return 'tns:' . $element['name'];
550
    }
551
552
    /**
553
     * Dump the WSDL as XML string.
554
     *
555
     * @param bool $formatOutput Format output
556
     * @return mixed
557
     */
558 33
    public function dump($formatOutput = true)
559
    {
560
        // Format output
561 33
        if ($formatOutput === true) {
562 33
            $this->dom->formatOutput = true;
563 33
        }
564
565 33
        return $this->dom->saveXML();
566
    }
567
568
    /**
569
     * Dump the WSDL as file.
570
     *
571
     * @param string $filename Filename to dump
572
     * @param bool $formatOutput Format output
573
     * @return mixed
574
     */
575
    public function save($filename, $formatOutput = true)
576
    {
577
        // Format output
578
        if ($formatOutput === true) {
579
            $this->dom->formatOutput = true;
580
        }
581
582
        return $this->dom->save($filename);
583
    }
584
585
    /**
586
     * Convert a PHP type into QName.
587
     *
588
     * @param string $type The PHP type.
589
     * @return string
590
     */
591 33
    public static function typeToQName($type)
592
    {
593 33
        if ($type[0] === '\\') {
594 15
            $type = substr($type, 1);
595 15
        }
596
597 33
        return str_replace('\\', '.', $type);
598
    }
599
600
    /**
601
     * Changes the xs:all to an xs:sequence node
602
     *
603
     * @param \DOMElement $all
604
     * @return \DOMElement
605
     */
606 6
    private function changeAllToSequence($all)
607
    {
608 6
        if ($all->nodeName !== 'xsd:all') {
609 3
            return $all;
610
        }
611 6
        $sequence = $all->ownerDocument->createElement('xsd:sequence');
612 6
        if ($all->attributes->length) {
613
            foreach ($all->attributes as $attribute) {
614
                $sequence->setAttribute($attribute->nodeName, $attribute->nodeValue);
615
            }
616
        }
617 6
        while ($all->firstChild) {
618 6
            $sequence->appendChild($all->firstChild);
619 6
        }
620 6
        return $sequence;
621
    }
622
623
}
624