Completed
Push — master ( dd7fb7...8b5d0e )
by Dieter
06:44
created

WsdlAnalyser::loadMessagesAndVersions()   C

Complexity

Conditions 9
Paths 12

Size

Total Lines 50
Code Lines 28

Duplication

Lines 15
Ratio 30 %

Code Coverage

Tests 37
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 15
loc 50
ccs 37
cts 37
cp 1
rs 6
c 1
b 0
f 0
cc 9
eloc 28
nc 12
nop 1
crap 9
1
<?php
2
/**
3
 * amadeus-ws-client
4
 *
5
 * Copyright 2015 Amadeus Benelux NV
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 * http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 *
19
 * @package Amadeus
20
 * @license https://opensource.org/licenses/Apache-2.0 Apache 2.0
21
 */
22
23
namespace Amadeus\Client\Session\Handler;
24
25
use Amadeus\Client\InvalidWsdlFileException;
26
27
/**
28
 * Wsdl Analyser
29
 *
30
 * Analyses the given WSDL or WSDL's for the WSAP
31
 * Extracts available messages & versions from it
32
 *
33
 * @package Amadeus\Client\Session\Handler
34
 * @author Dieter Devlieghere <[email protected]>
35
 */
36
class WsdlAnalyser
37
{
38
    /**
39
     * XPATH query to retrieve all operations from a WSDL
40
     *
41
     * @var string
42
     */
43
    const XPATH_ALL_OPERATIONS = '/wsdl:definitions/wsdl:portType/wsdl:operation/@name';
44
45
    /**
46
     * XPATH query to retrieve WSDL imports from a WSDL.
47
     */
48
    const XPATH_IMPORTS = '/wsdl:definitions/wsdl:import/@location';
49
50
    /**
51
     * XPATH query to retrieve the full operation name + version from a WSDL for a given operation.
52
     *
53
     * @var string
54
     */
55
    const XPATH_VERSION_FOR_OPERATION = "string(/wsdl:definitions/wsdl:message[contains(./@name, '%s_')]/@name)";
56
57
    /**
58
     * Alternate XPATH query to retrieve the full operation name + version from a WSDL for a given operation
59
     *
60
     * Necessary for "interface" wsdls
61
     *
62
     * @var string
63
     */
64
    const XPATH_ALT_VERSION_FOR_OPERATION = "string(//wsdl:operation[contains(./@name, '%s')]/wsdl:input/@message)";
65
66
    /**
67
     * List of WSDL ID's and their path
68
     *
69
     * format:
70
     * [
71
     *      '7d36c7b8' => '/path/to/wsdl.wsdl'
72
     * ]
73
     *
74
     * @var array
75
     */
76
    public static $wsdlIds = [];
77
78
    /**
79
     * Dom Document array where the WSDL's contents will be loaded
80
     *
81
     * format:
82
     * [
83
     *     '7d36c7b8' => \DOMDocument,
84
     *     '7e84f2537' => \DOMDocument
85
     * ]
86
     *
87
     * @var array
88
     */
89
    protected static $wsdlDomDoc;
90
91
    /**
92
     * To query the WSDL contents
93
     *
94
     * format:
95
     * [
96
     *     '7d36c7b8' => \DOMXpath,
97
     *     '7e84f2537' => \DOMXpath
98
     * ]
99
     *
100
     * @var array
101
     */
102
    protected static $wsdlDomXpath;
103
104
    /**
105
     * Loads messages & versions from WSDL.
106
     *
107
     * @return array
108
     */
109 31
    public static function loadMessagesAndVersions($wsdls)
110
    {
111 31
        $msgAndVer = [];
112
113 31
        foreach ($wsdls as $wsdl) {
114 31
            $wsdlIdentifier = self::makeWsdlIdentifier($wsdl);
115
116 31
            self::$wsdlIds[$wsdlIdentifier] = $wsdl;
117
118 31
            self::loadWsdlXpath($wsdl, $wsdlIdentifier);
119
120 31
            $operations = self::$wsdlDomXpath[$wsdlIdentifier]->query(self::XPATH_ALL_OPERATIONS);
121 31
            if ($operations->length === 0) {
122
                //No operations found - are there any external WSDLs being imported?
123 2
                $imports = self::$wsdlDomXpath[$wsdlIdentifier]->query(self::XPATH_IMPORTS);
124 2
                $operations = [];
125
126 2
                foreach ($imports as $import) {
127 2
                    if (!empty($import->value)) {
128 2
                        $tmpMsg = self::getMessagesAndVersionsFromImportedWsdl(
129 2
                            $import->value,
130 2
                            $wsdl,
131
                            $wsdlIdentifier
132 2
                        );
133 1
                        foreach ($tmpMsg as $msgName => $msgInfo) {
134 1
                            $msgAndVer[$msgName] = $msgInfo;
135 1
                        }
136 1
                    }
137 1
                }
138 1
            }
139
140 31 View Code Duplication
            foreach ($operations as $operation) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
141 31
                if (!empty($operation->value)) {
142 31
                    $fullVersion = self::$wsdlDomXpath[$wsdlIdentifier]->evaluate(
143 31
                        sprintf(self::XPATH_VERSION_FOR_OPERATION, $operation->value)
144 31
                    );
145
146 31
                    if (!empty($fullVersion)) {
147 31
                        $extractedVersion = self::extractMessageVersion($fullVersion);
148 31
                        $msgAndVer[$operation->value] = [
149 31
                            'version' => $extractedVersion,
150
                            'wsdl' => $wsdlIdentifier
151 31
                        ];
152 31
                    }
153 31
                }
154 31
            }
155 31
        }
156
157 29
        return $msgAndVer;
158
    }
159
160
    /**
161
     * Get Messages & Versions from an imported WSDL file
162
     *
163
     * Imported wsdl's are a little different, they require a different query
164
     * to extract the version nrs.
165
     *
166
     * @param string $import
167
     * @param string $wsdlPath
168
     * @param string $wsdlIdentifier
169
     * @return array
170
     * @throws InvalidWsdlFileException when the WSDL import could not be loaded.
171
     */
172 2
    protected static function getMessagesAndVersionsFromImportedWsdl($import, $wsdlPath, $wsdlIdentifier)
173
    {
174 2
        $msgAndVer = [];
175 2
        $domXpath = null;
176
177 2
        $importPath = realpath(dirname($wsdlPath)).DIRECTORY_SEPARATOR.$import;
178 2
        $wsdlContent = file_get_contents($importPath);
179
180 2
        if ($wsdlContent !== false) {
181 1
            $domDoc = new \DOMDocument('1.0', 'UTF-8');
182 1
            $ok = $domDoc->loadXML($wsdlContent);
183
184 1
            if ($ok === true) {
185 1
                $domXpath = new \DOMXPath($domDoc);
186 1
                $domXpath->registerNamespace(
187 1
                    'wsdl',
188
                    'http://schemas.xmlsoap.org/wsdl/'
189 1
                );
190 1
                $domXpath->registerNamespace(
191 1
                    'soap',
192
                    'http://schemas.xmlsoap.org/wsdl/soap/'
193 1
                );
194 1
            }
195 1
        } else {
196 1
            throw new InvalidWsdlFileException('WSDL '.$importPath.' import could not be loaded');
197
        }
198
199 1
        if ($domXpath instanceof \DOMXPath) {
200 1
            $nodeList = $domXpath->query(self::XPATH_ALL_OPERATIONS);
201
202 1 View Code Duplication
            foreach ($nodeList as $operation) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203 1
                if (!empty($operation->value)) {
204 1
                    $fullVersion = $domXpath->evaluate(
205 1
                        sprintf(self::XPATH_ALT_VERSION_FOR_OPERATION, $operation->value)
206 1
                    );
207
208 1
                    if (!empty($fullVersion)) {
209 1
                        $extractedVersion = self::extractMessageVersion($fullVersion);
210 1
                        $msgAndVer[$operation->value] = [
211 1
                            'version' => $extractedVersion,
212
                            'wsdl' => $wsdlIdentifier
213 1
                        ];
214 1
                    }
215 1
                }
216 1
            }
217 1
        }
218
219 1
        return $msgAndVer;
220
    }
221
222
    /**
223
     * Load the WSDL contents to a queryable DOMXpath.
224
     *
225
     * @param string $wsdlFilePath
226
     * @param string $wsdlId
227
     * @uses $this->wsdlDomDoc
228
     * @uses $this->wsdlDomXpath
229
     * @throws InvalidWsdlFileException when WSDL cannot be found.
230
     */
231 31
    public static function loadWsdlXpath($wsdlFilePath, $wsdlId)
232
    {
233 31
        if (!isset(self::$wsdlDomXpath[$wsdlId]) || is_null(self::$wsdlDomXpath[$wsdlId])) {
234 6
            $wsdlContent = file_get_contents($wsdlFilePath);
235
236 6
            if ($wsdlContent !== false) {
237 5
                self::$wsdlDomDoc[$wsdlId] = new \DOMDocument('1.0', 'UTF-8');
238 5
                self::$wsdlDomDoc[$wsdlId]->loadXML($wsdlContent);
239 5
                self::$wsdlDomXpath[$wsdlId] = new \DOMXPath(self::$wsdlDomDoc[$wsdlId]);
240 5
                self::$wsdlDomXpath[$wsdlId]->registerNamespace(
241 5
                    'wsdl',
242
                    'http://schemas.xmlsoap.org/wsdl/'
243 5
                );
244 5
                self::$wsdlDomXpath[$wsdlId]->registerNamespace(
245 5
                    'soap',
246
                    'http://schemas.xmlsoap.org/wsdl/soap/'
247 5
                );
248 5
            } else {
249 1
                throw new InvalidWsdlFileException('WSDL '.$wsdlFilePath.' could not be loaded');
250
            }
251 5
        }
252 31
    }
253
254
    /**
255
     * extractMessageVersion
256
     *
257
     * extracts "4.1" from a string like "Security_SignOut_4_1"
258
     * or "1.00" from a string like "tns:AMA_MediaGetMediaRQ_1.000"
259
     *
260
     * @param string $fullVersionString
261
     * @return string
262
     */
263 31
    protected static function extractMessageVersion($fullVersionString)
264
    {
265 31
        $marker = strpos($fullVersionString, '_', strpos($fullVersionString, '_') + 1);
266
267 31
        $num = substr($fullVersionString, $marker + 1);
268
269 31
        return str_replace('_', '.', $num);
270
    }
271
272
    /**
273
     * Generates a unique identifier for a wsdl based on its path.
274
     *
275
     * @param string $wsdlPath
276
     *
277
     * @return string
278
     */
279 31
    protected static function makeWsdlIdentifier($wsdlPath)
280
    {
281 31
        return sprintf('%x', crc32($wsdlPath));
282
    }
283
284
    /**
285
     * Evaluate an XPATH query on a given WSDL
286
     *
287
     * @param string $wsdlId
288
     * @param string $wsdlFilePath
289
     * @param string $xpath XPATH query
290
     * @return string|null
291
     */
292 11
    public static function exaluateXpathQueryOnWsdl($wsdlId, $wsdlFilePath, $xpath)
293
    {
294 11
        WsdlAnalyser::loadWsdlXpath($wsdlFilePath, $wsdlId);
295
296 11
        return self::$wsdlDomXpath[$wsdlId]->evaluate($xpath);
297
    }
298
}
299