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
Push — master ( 2dd713...4dc7a4 )
by Dragos
15:08
created

WSDL::setStylesheet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4286
cc 1
eloc 3
nc 1
nop 1
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
    );
67
68
    /**
69
     * Constructor.
70
     *
71
     * @param string $name The name of the web service.
72
     * @param string $uri URI where the WSDL will be available.
73
     * @param string $xslUri The URI to the stylesheet.
74 24
     * @throws RuntimeException If the DOM Document can not be created.
75
     */
76 24
    public function __construct($name, $uri, $xslUri = null)
77 24
    {
78 24
        $this->dom = new DOMDocument('1.0');
79 24
        if ($xslUri !== null) {
80 24
            $this->setStylesheet($xslUri);
81 24
        }
82 24
83 24
        $definitions = $this->dom->createElementNS('http://schemas.xmlsoap.org/wsdl/', 'definitions');
84 24
        $definitions->setAttribute('name', $name);
85 24
        $definitions->setAttribute('targetNamespace', $uri);
86
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:tns', htmlspecialchars($uri));
87 24
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap', 'http://schemas.xmlsoap.org/wsdl/soap/');
88
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsd', 'http://www.w3.org/2001/XMLSchema');
89 24
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:soap-enc', 'http://schemas.xmlsoap.org/soap/encoding/');
90 24
        $definitions->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:wsdl', 'http://schemas.xmlsoap.org/wsdl/');
91
        $this->dom->appendChild($definitions);
92
93 24
        $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...
94 24
95 24
        $this->schema = $this->dom->createElement('xsd:schema');
96
        $this->schema->setAttribute('targetNamespace', $uri);
97 24
98 24
        // Add the import for validation.
99 24
        $import = $this->dom->createElement('xsd:import');
100 24
        $import->setAttribute('namespace', 'http://schemas.xmlsoap.org/soap/encoding/');
101
        $this->schema->appendChild($import);
102
103
        $types = $this->dom->createElement('types');
104
        $types->appendChild($this->schema);
105
        $this->wsdl->appendChild($types);
106
    }
107
108
    /**
109
     * Set the stylesheet for the WSDL.
110 24
     *
111
     * @param string $xslUri The URI to the stylesheet.
112 24
     * @return WSDL
113 24
     */
114
    private function setStylesheet($xslUri)
115 24
    {
116 24
        $xslt = $this->dom->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="' . $xslUri . '"');
117 24
        $this->dom->appendChild($xslt);
118 24
    }
119 24
120 24
    /**
121 24
     * Add a message element to the WSDL.
122 24
     *
123
     * @param string $name The name for the message.
124
     * @param array $parts Array of parts for the message ('name'=>'type' or 'name'=>array('type'=>'type', 'element'=>'element')).
125
     * @return DOMElement
126 24
     * @link http://www.w3.org/TR/wsdl#_messages
127 24
     */
128
    public function addMessage($name, array $parts)
129 24
    {
130
        $message = $this->dom->createElement('message');
131 24
        $message->setAttribute('name', $name);
132
133
        foreach ($parts as $name => $type) {
134
            $part = $this->dom->createElement('part');
135
            $part->setAttribute('name', $name);
136
            if (is_array($type)) {
137
                foreach ($type as $key => $value) {
138
                    $part->setAttribute($key, $value);
139
                }
140 24
            } else {
141
                $part->setAttribute('type', $type);
142 24
            }
143 24
144 24
            $message->appendChild($part);
145
        }
146 24
147
        $this->wsdl->appendChild($message);
148
149
        return $message;
150
    }
151
152
    /**
153
     * Add a portType to element to the WSDL.
154
     *
155
     * @param string $name The name of the portType.
156 24
     * @return DOMElement
157
     */
158 24
    public function addPortType($name)
159 24
    {
160 24
        $portType = $this->dom->createElement('portType');
161
        $portType->setAttribute('name', $name);
162 24
        $this->wsdl->appendChild($portType);
163
164 24
        return $portType;
165
    }
166
167
    /**
168
     * Add a binding element to the WSDL.
169
     *
170
     * @param string $name The name of the binding.
171
     * @param string $portType The portType to bind.
172
     * @return DOMElement
173
     */
174
    public function addBinding($name, $portType)
175
    {
176 24
        $binding = $this->dom->createElement('binding');
177
        $binding->setAttribute('name', $name);
178
        $binding->setAttribute('type', $portType);
179
180
        $this->wsdl->appendChild($binding);
181 24
182 24
        return $binding;
183 24
    }
184
185 24
    /**
186
     * Add a SOAP binding element to the Binding element.
187 24
     *
188
     * @param DOMElement $binding The binding element (from addBinding() method).
189
     * @param string $style The binding style (rpc or document).
190
     * @param string $transport The transport method.
191
     * @return DOMElement
192
     * @link http://www.w3.org/TR/wsdl#_soap:binding
193
     */
194
    public function addSoapBinding(
195
        DOMElement $binding,
196
        $style = 'rpc',
197
        $transport = 'http://schemas.xmlsoap.org/soap/http'
198
    ) {
199
        $soapBinding = $this->dom->createElement('soap:binding');
200 24
        $soapBinding->setAttribute('style', $style);
201
        $soapBinding->setAttribute('transport', $transport);
202 24
203 24
        $binding->appendChild($soapBinding);
204
205 24
        return $soapBinding;
206 24
    }
207 24
208 24
    /**
209 24
     * Add an operation to a binding element.
210 24
     *
211
     * @param DOMElement $binding The binding element (from addBinding() method).
212 24
     * @param string $name The name of the operation.
213 24
     * @param array $input Attributes for the input element (use, namespace, encodingStyle).
214 24
     * @param array $output Attributes for the output element (use, namespace, encodingStyle).
215
     * @return DOMElement
216 24
     * @link http://www.w3.org/TR/wsdl#_soap:body
217 24
     */
218 24
    public function addBindingOperation(DOMElement $binding, $name, array $input = null, array $output = null)
219 24
    {
220 24
        $operation = $this->dom->createElement('operation');
221 24
        $operation->setAttribute('name', $name);
222
223 24
        if (is_array($input)) {
224 24
            $inputElement = $this->dom->createElement('input');
225 24
            $soapElement = $this->dom->createElement('soap:body');
226
            foreach ($input as $name => $value) {
227 24
                $soapElement->setAttribute($name, $value);
228
            }
229 24
230
            $inputElement->appendChild($soapElement);
231
            $operation->appendChild($inputElement);
232
        }
233
234
        if (is_array($output)) {
235
            $outputElement = $this->dom->createElement('output');
236
            $soapElement = $this->dom->createElement('soap:body');
237
            foreach ($output as $name => $value) {
238
                $soapElement->setAttribute($name, $value);
239
            }
240
241
            $outputElement->appendChild($soapElement);
242 24
            $operation->appendChild($outputElement);
243
        }
244 24
245 24
        $binding->appendChild($operation);
246
247 24
        return $operation;
248 24
    }
249 24
250 24
    /**
251 24
     * Add an operation element to a portType element.
252
     *
253 24
     * @param DOMElement $portType The port type element (from addPortType() method).
254 9
     * @param string $name The name of the operation.
255 9
     * @param string $inputMessage The input message.
256 9
     * @param string $outputMessage The output message.
257 9
     * @return DOMElement
258
     * @link http://www.w3.org/TR/wsdl#_request-response
259 24
     */
260
    public function addPortOperation(DOMElement $portType, $name, $inputMessage = null, $outputMessage = null)
261 24
    {
262
        $operation = $this->dom->createElement('operation');
263
        $operation->setAttribute('name', $name);
264
265
        if (is_string($inputMessage) && (strlen(trim($inputMessage)) >= 1)) {
266
            $inputElement = $this->dom->createElement('input');
267
            $inputElement->setAttribute('message', $inputMessage);
268
            $operation->appendChild($inputElement);
269
        }
270
271
        if (is_string($outputMessage) && (strlen(trim($outputMessage)) >= 1)) {
272 24
            $outputElement = $this->dom->createElement('output');
273
            $outputElement->setAttribute('message', $outputMessage);
274 24
            $operation->appendChild($outputElement);
275 24
        }
276
277 24
        $portType->appendChild($operation);
278
279 24
        return $operation;
280
    }
281
282
    /**
283
     * Add a SOAP operation to an operation element.
284
     *
285
     * @param DOMElement $binding The binding element (from addBindingOperation() method).
286
     * @param string $soapAction SOAP Action.
287
     * @return DOMElement
288
     * @link http://www.w3.org/TR/wsdl#_soap:operation
289
     */
290
    public function addSoapOperation(DOMElement $binding, $soapAction)
291
    {
292 24
        $soapOperation = $this->dom->createElement('soap:operation');
293
        $soapOperation->setAttribute('soapAction', $soapAction);
294 24
295 24
        $binding->insertBefore($soapOperation, $binding->firstChild);
296
297 24
        return $soapOperation;
298 24
    }
299 24
300
    /**
301 24
     * Add a service element to the WSDL.
302 24
     *
303
     * @param string $name Service name.
304 24
     * @param string $portName Port name.
305 24
     * @param string $binding Binding for the port.
306
     * @param string $location SOAP Address location.
307 24
     * @return DOMElement
308
     * @link http://www.w3.org/TR/wsdl#_services
309 24
     */
310
    public function addService($name, $portName, $binding, $location)
311
    {
312
        $service = $this->dom->createElement('service');
313
        $service->setAttribute('name', $name);
314
315
        $port = $this->dom->createElement('port');
316
        $port->setAttribute('name', $portName);
317
        $port->setAttribute('binding', $binding);
318
319
        $soapAddress = $this->dom->createElement('soap:address');
320 9
        $soapAddress->setAttribute('location', $location);
321
322 9
        $port->appendChild($soapAddress);
323
        $service->appendChild($port);
324
325 9
        $this->wsdl->appendChild($service);
326
327
        return $service;
328 9
    }
329 9
330 9
    /**
331
     * Add a documentation element to another element in the WSDL.
332 9
     *
333 9
     * @param DOMElement $inputElement The DOMElement element to add the documentation.
334 9
     * @param string $documentation The documentation text.
335
     * @return DOMElement
336
     * @link http://www.w3.org/TR/wsdl#_documentation
337
     */
338 9
    public function addDocumentation(DOMElement $inputElement, $documentation)
339
    {
340
        if ($inputElement === $this) {
341
            $element = $this->dom->documentElement;
342
        } else {
343
            $element = $inputElement;
344
        }
345
346
        $doc = $this->dom->createElement('documentation');
347 12
        $cdata = $this->dom->createTextNode($documentation);
348
        $doc->appendChild($cdata);
349 12
350 12
        if ($element->hasChildNodes()) {
351
            $element->insertBefore($doc, $element->firstChild);
352
        } else {
353
            $element->appendChild($doc);
354
        }
355
356
        return $doc;
357
    }
358 24
359
    /**
360 24
     * Add a complex type.
361 21
     *
362 15
     * @param string $type The type.
363 15
     * @param string $wsdlType The WSDL type.
364 6
     */
365 3
    public function addType($type, $wsdlType)
366
    {
367 3
        $this->includedTypes[$type] = $wsdlType;
368
    }
369 12
370
    /**
371
     * Get the XSD Type from a PHP type.
372
     *
373
     * @param string $type The type to get the XSD type from.
374
     * @return string
375
     */
376
    public function getXSDType($type)
377
    {
378
        if ($this->isXDSType($type)) {
379
            return self::$XSDTypes[strtolower($type)];
380
        } elseif ($type) {
381 24
            if (strpos($type, '[]')) {
382
                if ($this->isXDSType(str_replace('[]', '', $type))) {
383 24
                    return self::$XSDTypes['array'];
384 24
                }
385
            }
386
387
            return $this->addComplexType($type);
388
        } else {
389
            return null;
390
        }
391
    }
392
393 12
    /**
394
     * Check if a type is a XDS.
395 12
     *
396
     * @param string $type The type to check.
397
     * @return boolean
398
     */
399 12
    private function isXDSType($type)
400 3
    {
401
        $typeToLowerString = strtolower($type);
402
        return isset(self::$XSDTypes[$typeToLowerString]);
403 12
    }
404
405 12
    /**
406 12
     * Add a complex type.
407
     *
408 12
     * @param string $type Name of the class.
409
     * @return string
410 12
     */
411 12
    public function addComplexType($type)
412 9
    {
413 9
        if (isset($this->includedTypes[$type])) {
414 9
            return $this->includedTypes[$type];
415 9
        }
416 9
417 9
        if (strpos($type, '[]') !== false) {
418 9
            return $this->addComplexTypeArray(str_replace('[]', '', $type), $type);
419
        }
420
421 9
        $class = new ReflectionClass($type);
422 6
423 6
        $soapTypeName = static::typeToQName($type);
424 6
        $soapType = 'tns:' . $soapTypeName;
425 3
426 3
        $this->addType($type, $soapType);
427 6
428 9
        $all = $this->dom->createElement('xsd:all');
429 6
        foreach ($class->getProperties() as $property) {
430 6
            $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...
431 6
            if ($property->isPublic() && $annotationsCollection->hasAnnotationTag('var')) {
432 3
                $element = $this->dom->createElement('xsd:element');
433 3
                $element->setAttribute('name', $property->getName());
434 6
                $propertyVarAnnotation = $annotationsCollection->getAnnotation('var');
435 9
                $element->setAttribute('type', $this->getXSDType(reset($propertyVarAnnotation)->getVarType()));
436 9
                if ($annotationsCollection->hasAnnotationTag('nillable')) {
437 12
                    $element->setAttribute('nillable', 'true');
438
                }
439 12
                if ($annotationsCollection->hasAnnotationTag('minOccurs')) {
440 12
                    $minOccurs = intval($annotationsCollection->getAnnotation('minOccurs')[0]->getDescription());
441 12
                    $element->setAttribute('minOccurs', $minOccurs > 0 ? $minOccurs : 0);
442
                    if ($minOccurs > 1) {
443 12
                        $all = $this->changeAllToSequence($all);
444
                    }
445 12
                }
446
                if ($annotationsCollection->hasAnnotationTag('maxOccurs')) {
447
                    $maxOccurs = intval($annotationsCollection->getAnnotation('maxOccurs')[0]->getDescription());
448
                    $element->setAttribute('maxOccurs', $maxOccurs > 0 ? $maxOccurs : 'unbounded');
449
                    if ($maxOccurs !== 1) {
450
                        $all = $this->changeAllToSequence($all);
451
                    }
452
                }
453
                $all->appendChild($element);
454
            }
455 3
        }
456
457 3
        $complexType = $this->dom->createElement('xsd:complexType');
458 3
        $complexType->setAttribute('name', $soapTypeName);
459
        $complexType->appendChild($all);
460
461 3
        $this->schema->appendChild($complexType);
462
463
        return $soapType;
464 3
    }
465
466
    /**
467 3
     * Add an array of complex type.
468 3
     *
469
     * @param string $singularType The type name without the '[]'.
470 3
     * @param string $type The original type name.
471 3
     * @return string
472
     */
473 3
    protected function addComplexTypeArray($singularType, $type)
474 3
    {
475 3
        $xsdComplexTypeName = 'ArrayOf' . static::typeToQName($singularType);
476
        $xsdComplexType = 'tns:' . $xsdComplexTypeName;
477 3
478 3
        // Register type here to avoid recursion.
479 3
        $this->addType($type, $xsdComplexType);
480 3
481
        // Process singular type using DefaultComplexType strategy.
482 3
        $this->addComplexType($singularType);
483
484 3
        // Add array type structure to WSDL document.
485
        $complexType = $this->dom->createElement('xsd:complexType');
486
        $complexType->setAttribute('name', $xsdComplexTypeName);
487
488
        $complexContent = $this->dom->createElement('xsd:complexContent');
489
        $complexType->appendChild($complexContent);
490
491
        $xsdRestriction = $this->dom->createElement('xsd:restriction');
492
        $xsdRestriction->setAttribute('base', 'soap-enc:Array');
493
        $complexContent->appendChild($xsdRestriction);
494
495
        $xsdAttribute = $this->dom->createElement('xsd:attribute');
496
        $xsdAttribute->setAttribute('ref', 'soap-enc:arrayType');
497
        $xsdAttribute->setAttribute('wsdl:arrayType', 'tns:' . static::typeToQName($singularType) . '[]');
498
        $xsdRestriction->appendChild($xsdAttribute);
499
500
        $this->schema->appendChild($complexType);
501
502
        return $xsdComplexType;
503
    }
504
505
    /**
506
     * Parse an xsd:element represented as an array into a DOMElement.
507
     *
508
     * @param array $element An xsd:element represented as an array.
509
     * @return DOMElement
510
     */
511
    private function parseElement(array $element)
512
    {
513
        $elementXml = $this->dom->createElement('xsd:element');
514
        foreach ($element as $key => $value) {
515
            if (in_array($key, array('sequence', 'all', 'choice'))) {
516
                if (is_array($value)) {
517
                    $complexType = $this->dom->createElement('xsd:complexType');
518
                    if (count($value) > 0) {
519
                        $container = $this->dom->createElement('xsd:' . $key);
520
                        foreach ($value as $subElement) {
521
                            $subElementXml = $this->parseElement($subElement);
522
                            $container->appendChild($subElementXml);
523
                        }
524
525
                        $complexType->appendChild($container);
526
                    }
527
528
                    $elementXml->appendChild($complexType);
529
                }
530
            } else {
531
                $elementXml->setAttribute($key, $value);
532
            }
533
        }
534
535
        return $elementXml;
536
    }
537
538 24
    /**
539
     * Add an xsd:element represented as an array to the schema.
540 24
     *
541 24
     * @param array $element An xsd:element represented as an array.
542
     * @return string
543
     */
544
    public function addElement(array $element)
545
    {
546
        $elementXml = $this->parseElement($element);
547
        $this->schema->appendChild($elementXml);
548
        return 'tns:' . $element['name'];
549
    }
550 24
551
    /**
552 24
     * Dump the WSDL as XML string.
553 12
     *
554 12
     * @return string
555
     */
556 24
    public function dump()
557
    {
558
        $this->dom->formatOutput = true;
559
        return $this->dom->saveXML();
560
    }
561
562
    /**
563
     * Convert a PHP type into QName.
564
     *
565 6
     * @param string $type The PHP type.
566
     * @return string
567 6
     */
568 3
    public static function typeToQName($type)
569
    {
570 6
        if ($type[0] === '\\') {
571 6
            $type = substr($type, 1);
572
        }
573
574
        return str_replace('\\', '.', $type);
575
    }
576 6
577 6
    /**
578 6
     * Changes the xs:all to an xs:sequence node
579 6
     *
580
     * @param \DOMElement $all
581
     * @return \DOMElement
582
     */
583
    private function changeAllToSequence($all)
584
    {
585
        if ($all->nodeName !== 'xsd:all') {
586
            return $all;
587
        }
588
        $sequence = $all->ownerDocument->createElement('xsd:sequence');
589
        if ($all->attributes->length) {
590
            foreach ($all->attributes as $attribute) {
591
                $sequence->setAttribute($attribute->nodeName, $attribute->nodeValue);
592
            }
593
        }
594
        while ($all->firstChild) {
595
            $sequence->appendChild($all->firstChild);
596
        }
597
        return $sequence;
598
    }
599
600
}
601