Completed
Push — master ( 8b5d0e...0e2dcd )
by Dieter
06:29
created

WsdlAnalyser::loopOperationsWithQuery()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 22
ccs 16
cts 16
cp 1
rs 8.9197
cc 4
eloc 12
nc 4
nop 4
crap 4
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
            $msgAndVer = array_merge(
141 31
                $msgAndVer,
142 31
                self::loopOperationsWithQuery(
143 31
                    $operations,
144 31
                    self::XPATH_VERSION_FOR_OPERATION,
145 31
                    $wsdlIdentifier,
146 31
                    self::$wsdlDomXpath[$wsdlIdentifier]
147 31
                )
148 31
            );
149 31
        }
150
151 29
        return $msgAndVer;
152
    }
153
154
    /**
155
     * Get Messages & Versions from an imported WSDL file
156
     *
157
     * Imported wsdl's are a little different, they require a different query
158
     * to extract the version nrs.
159
     *
160
     * @param string $import
161
     * @param string $wsdlPath
162
     * @param string $wsdlIdentifier
163
     * @return array
164
     * @throws InvalidWsdlFileException when the WSDL import could not be loaded.
165
     */
166 2
    protected static function getMessagesAndVersionsFromImportedWsdl($import, $wsdlPath, $wsdlIdentifier)
167
    {
168 2
        $msgAndVer = [];
169 2
        $domXpath = null;
170
171 2
        $importPath = realpath(dirname($wsdlPath)).DIRECTORY_SEPARATOR.$import;
172 2
        $wsdlContent = file_get_contents($importPath);
173
174 2
        if ($wsdlContent !== false) {
175 1
            $domDoc = new \DOMDocument('1.0', 'UTF-8');
176 1
            $ok = $domDoc->loadXML($wsdlContent);
177
178 1
            if ($ok === true) {
179 1
                $domXpath = new \DOMXPath($domDoc);
180 1
                $domXpath->registerNamespace(
181 1
                    'wsdl',
182
                    'http://schemas.xmlsoap.org/wsdl/'
183 1
                );
184 1
                $domXpath->registerNamespace(
185 1
                    'soap',
186
                    'http://schemas.xmlsoap.org/wsdl/soap/'
187 1
                );
188 1
            }
189 1
        } else {
190 1
            throw new InvalidWsdlFileException('WSDL '.$importPath.' import could not be loaded');
191
        }
192
193 1
        if ($domXpath instanceof \DOMXPath) {
194 1
            $nodeList = $domXpath->query(self::XPATH_ALL_OPERATIONS);
195
196 1
            $msgAndVer = array_merge(
197 1
                $msgAndVer,
198 1
                self::loopOperationsWithQuery(
199 1
                    $nodeList,
200 1
                    self::XPATH_ALT_VERSION_FOR_OPERATION,
201 1
                    $wsdlIdentifier,
202
                    $domXpath
203 1
                )
204 1
            );
205 1
        }
206
207 1
        return $msgAndVer;
208
    }
209
210
    /**
211
     * Load the WSDL contents to a queryable DOMXpath.
212
     *
213
     * @param string $wsdlFilePath
214
     * @param string $wsdlId
215
     * @uses $this->wsdlDomDoc
216
     * @uses $this->wsdlDomXpath
217
     * @throws InvalidWsdlFileException when WSDL cannot be found.
218
     */
219 31
    public static function loadWsdlXpath($wsdlFilePath, $wsdlId)
220
    {
221 31
        if (!isset(self::$wsdlDomXpath[$wsdlId]) || is_null(self::$wsdlDomXpath[$wsdlId])) {
222 6
            $wsdlContent = file_get_contents($wsdlFilePath);
223
224 6
            if ($wsdlContent !== false) {
225 5
                self::$wsdlDomDoc[$wsdlId] = new \DOMDocument('1.0', 'UTF-8');
226 5
                self::$wsdlDomDoc[$wsdlId]->loadXML($wsdlContent);
227 5
                self::$wsdlDomXpath[$wsdlId] = new \DOMXPath(self::$wsdlDomDoc[$wsdlId]);
228 5
                self::$wsdlDomXpath[$wsdlId]->registerNamespace(
229 5
                    'wsdl',
230
                    'http://schemas.xmlsoap.org/wsdl/'
231 5
                );
232 5
                self::$wsdlDomXpath[$wsdlId]->registerNamespace(
233 5
                    'soap',
234
                    'http://schemas.xmlsoap.org/wsdl/soap/'
235 5
                );
236 5
            } else {
237 1
                throw new InvalidWsdlFileException('WSDL '.$wsdlFilePath.' could not be loaded');
238
            }
239 5
        }
240 31
    }
241
242
    /**
243
     * extractMessageVersion
244
     *
245
     * extracts "4.1" from a string like "Security_SignOut_4_1"
246
     * or "1.00" from a string like "tns:AMA_MediaGetMediaRQ_1.000"
247
     *
248
     * @param string $fullVersionString
249
     * @return string
250
     */
251 31
    protected static function extractMessageVersion($fullVersionString)
252
    {
253 31
        $marker = strpos($fullVersionString, '_', strpos($fullVersionString, '_') + 1);
254
255 31
        $num = substr($fullVersionString, $marker + 1);
256
257 31
        return str_replace('_', '.', $num);
258
    }
259
260
    /**
261
     * Generates a unique identifier for a wsdl based on its path.
262
     *
263
     * @param string $wsdlPath
264
     *
265
     * @return string
266
     */
267 31
    protected static function makeWsdlIdentifier($wsdlPath)
268
    {
269 31
        return sprintf('%x', crc32($wsdlPath));
270
    }
271
272
    /**
273
     * Evaluate an XPATH query on a given WSDL
274
     *
275
     * @param string $wsdlId
276
     * @param string $wsdlFilePath
277
     * @param string $xpath XPATH query
278
     * @return string|null
279
     */
280 11
    public static function exaluateXpathQueryOnWsdl($wsdlId, $wsdlFilePath, $xpath)
281
    {
282 11
        WsdlAnalyser::loadWsdlXpath($wsdlFilePath, $wsdlId);
283
284 11
        return self::$wsdlDomXpath[$wsdlId]->evaluate($xpath);
285
    }
286
287
    /**
288
     * Loop all extracted operations from a wsdl and find their message versions
289
     *
290
     * @param \DOMNodeList $operations
291
     * @param string $query
292
     * @param string $wsdlIdentifier
293
     * @param \DOMXPath $domXpath
294
     * @return array
295
     */
296 31
    protected static function loopOperationsWithQuery($operations, $query, $wsdlIdentifier, $domXpath)
297
    {
298 31
        $msgAndVer = [];
299
300 31
        foreach ($operations as $operation) {
301 31
            if (!empty($operation->value)) {
302 31
                $fullVersion = $domXpath->evaluate(
303 31
                    sprintf($query, $operation->value)
304 31
                );
305
306 31
                if (!empty($fullVersion)) {
307 31
                    $extractedVersion = self::extractMessageVersion($fullVersion);
308 31
                    $msgAndVer[$operation->value] = [
309 31
                        'version' => $extractedVersion,
310
                        'wsdl' => $wsdlIdentifier
311 31
                    ];
312 31
                }
313 31
            }
314 31
        }
315
316 31
        return $msgAndVer;
317
    }
318
}
319