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.
Completed
Pull Request — master (#12)
by
unknown
05:29
created

WSDL::addSoapOperation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
cc 1
eloc 5
nc 1
nop 2
crap 1
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 30
    public function __construct($name, $uri, $xslUri = null)
78
    {
79 30
        $this->dom = new DOMDocument('1.0');
80 30
        if ($xslUri !== null) {
81 3
            $this->setStylesheet($xslUri);
82 3
        }
83
84 30
        $definitions = $this->dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'definitions');
85 30
        $definitions->setAttribute('name', $name);
86 30
        $definitions->setAttribute('targetNamespace', $uri);
87 30
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:tns', htmlspecialchars($uri));
88 30
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/');
89 30
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
90 30
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap-enc', 'http://schemas.xmlsoap.org/soap/encoding/');
91 30
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:wsdl', 'http://schemas.xmlsoap.org/wsdl/');
92 30
        $this->dom->appendChild($definitions);
93
94 30
        $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 30
        $this->schema = $this->dom->createElement('xsd:schema');
97 30
        $this->schema->setAttribute('targetNamespace', $uri);
98
99
        // Add the import for validation.
100 30
        $import = $this->dom->createElement('xsd:import');
101 30
        $import->setAttribute('namespace', 'http://schemas.xmlsoap.org/soap/encoding/');
102 30
        $this->schema->appendChild($import);
103
104 30
        $types = $this->dom->createElement('types');
105 30
        $types->appendChild($this->schema);
106 30
        $this->wsdl->appendChild($types);
107 30
    }
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 27
    public function addMessage($name, array $parts)
130
    {
131 27
        $message = $this->dom->createElement('message');
132 27
        $message->setAttribute('name', $name);
133
134 27
        foreach ($parts as $name => $type) {
135 27
            $part = $this->dom->createElement('part');
136 27
            $part->setAttribute('name', $name);
137 27
            if (is_array($type)) {
138 27
                foreach ($type as $key => $value) {
139 27
                    $part->setAttribute($key, $value);
140 27
                }
141 27
            } else {
142
                $part->setAttribute('type', $type);
143
            }
144
145 27
            $message->appendChild($part);
146 27
        }
147
148 27
        $this->wsdl->appendChild($message);
149
150 27
        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 30
    public function addPortType($name)
160
    {
161 30
        $portType = $this->dom->createElement('portType');
162 30
        $portType->setAttribute('name', $name);
163 30
        $this->wsdl->appendChild($portType);
164
165 30
        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 30
    public function addBinding($name, $portType)
176
    {
177 30
        $binding = $this->dom->createElement('binding');
178 30
        $binding->setAttribute('name', $name);
179 30
        $binding->setAttribute('type', $portType);
180
181 30
        $this->wsdl->appendChild($binding);
182
183 30
        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 30
    public function addSoapBinding(
196
        DOMElement $binding,
197
        $style = 'rpc',
198
        $transport = 'http://schemas.xmlsoap.org/soap/http'
199
    ) {
200 30
        $soapBinding = $this->dom->createElement('soap:binding');
201 30
        $soapBinding->setAttribute('style', $style);
202 30
        $soapBinding->setAttribute('transport', $transport);
203
204 30
        $binding->appendChild($soapBinding);
205
206 30
        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 27
    public function addBindingOperation(DOMElement $binding, $name, array $input = null, array $output = null)
220
    {
221 27
        $operation = $this->dom->createElement('operation');
222 27
        $operation->setAttribute('name', $name);
223
224 27
        if (is_array($input)) {
225 27
            $inputElement = $this->dom->createElement('input');
226 27
            $soapElement = $this->dom->createElement('soap:body');
227 27
            foreach ($input as $name => $value) {
228 27
                $soapElement->setAttribute($name, $value);
229 27
            }
230
231 27
            $inputElement->appendChild($soapElement);
232 27
            $operation->appendChild($inputElement);
233 27
        }
234
235 27
        if (is_array($output)) {
236 27
            $outputElement = $this->dom->createElement('output');
237 27
            $soapElement = $this->dom->createElement('soap:body');
238 27
            foreach ($output as $name => $value) {
239 27
                $soapElement->setAttribute($name, $value);
240 27
            }
241
242 27
            $outputElement->appendChild($soapElement);
243 27
            $operation->appendChild($outputElement);
244 27
        }
245
246 27
        $binding->appendChild($operation);
247
248 27
        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 27
    public function addPortOperation(DOMElement $portType, $name, $inputMessage = null, $outputMessage = null)
262
    {
263 27
        $operation = $this->dom->createElement('operation');
264 27
        $operation->setAttribute('name', $name);
265
266 27
        if (is_string($inputMessage) && (strlen(trim($inputMessage)) >= 1)) {
267 27
            $inputElement = $this->dom->createElement('input');
268 27
            $inputElement->setAttribute('message', $inputMessage);
269 27
            $operation->appendChild($inputElement);
270 27
        }
271
272 27
        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 27
        $portType->appendChild($operation);
279
280 27
        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 27
    public function addSoapOperation(DOMElement $binding, $soapAction)
292
    {
293 27
        $soapOperation = $this->dom->createElement('soap:operation');
294 27
        $soapOperation->setAttribute('soapAction', $soapAction);
295
296 27
        $binding->insertBefore($soapOperation, $binding->firstChild);
297
298 27
        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 30
    public function addService($name, $portName, $binding, $location)
312
    {
313 30
        $service = $this->dom->createElement('service');
314 30
        $service->setAttribute('name', $name);
315
316 30
        $port = $this->dom->createElement('port');
317 30
        $port->setAttribute('name', $portName);
318 30
        $port->setAttribute('binding', $binding);
319
320 30
        $soapAddress = $this->dom->createElement('soap:address');
321 30
        $soapAddress->setAttribute('location', $location);
322
323 30
        $port->appendChild($soapAddress);
324 30
        $service->appendChild($port);
325
326 30
        $this->wsdl->appendChild($service);
327
328 30
        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 27
    public function getXSDType($type)
378
    {
379 27
        if ($this->isXDSType($type)) {
380 24
            return self::$XSDTypes[strtolower($type)];
381 18
        } elseif ($type) {
382 18
            if (strpos($type, '[]')) {
383 6
                if ($this->isXDSType(str_replace('[]', '', $type))) {
384 3
                    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 27
    private function isXDSType($type)
401
    {
402 27
        $typeToLowerString = strtolower($type);
403 27
        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
     * @return string
556
     */
557 30
    public function dump()
558
    {
559 30
        $this->dom->formatOutput = true;
560 30
        return $this->dom->saveXML();
561
    }
562
563
    /**
564
     * Convert a PHP type into QName.
565
     *
566
     * @param string $type The PHP type.
567
     * @return string
568
     */
569 30
    public static function typeToQName($type)
570
    {
571 30
        if ($type[0] === '\\') {
572 15
            $type = substr($type, 1);
573 15
        }
574
575 30
        return str_replace('\\', '.', $type);
576
    }
577
578
    /**
579
     * Changes the xs:all to an xs:sequence node
580
     *
581
     * @param \DOMElement $all
582
     * @return \DOMElement
583
     */
584 6
    private function changeAllToSequence($all)
585
    {
586 6
        if ($all->nodeName !== 'xsd:all') {
587 3
            return $all;
588
        }
589 6
        $sequence = $all->ownerDocument->createElement('xsd:sequence');
590 6
        if ($all->attributes->length) {
591
            foreach ($all->attributes as $attribute) {
592
                $sequence->setAttribute($attribute->nodeName, $attribute->nodeValue);
593
            }
594
        }
595 6
        while ($all->firstChild) {
596 6
            $sequence->appendChild($all->firstChild);
597 6
        }
598 6
        return $sequence;
599
    }
600
601
}
602