This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php declare(strict_types=1); |
||
2 | |||
3 | namespace OpenStack\Common\Service; |
||
4 | |||
5 | use GuzzleHttp\Client; |
||
6 | use GuzzleHttp\ClientInterface; |
||
7 | use GuzzleHttp\Middleware as GuzzleMiddleware; |
||
8 | use OpenStack\Common\Auth\IdentityService; |
||
9 | use OpenStack\Common\Auth\Token; |
||
10 | use OpenStack\Common\Transport\HandlerStack; |
||
11 | use OpenStack\Common\Transport\Middleware; |
||
12 | use OpenStack\Common\Transport\Utils; |
||
13 | |||
14 | /** |
||
15 | * A Builder for easily creating OpenStack services. |
||
16 | * |
||
17 | * @package OpenStack\Common\Service |
||
18 | */ |
||
19 | class Builder |
||
20 | { |
||
21 | /** |
||
22 | * Global options that will be applied to every service created by this builder. |
||
23 | * |
||
24 | * @var array |
||
25 | */ |
||
26 | private $globalOptions = []; |
||
27 | |||
28 | /** @var string */ |
||
29 | private $rootNamespace; |
||
30 | |||
31 | /** |
||
32 | * Defaults that will be applied to options if no values are provided by the user. |
||
33 | * |
||
34 | * @var array |
||
35 | */ |
||
36 | private $defaults = ['urlType' => 'publicURL']; |
||
37 | |||
38 | /** |
||
39 | * @param array $globalOptions Options that will be applied to every service created by this builder. |
||
40 | * Eventually they will be merged (and if necessary overridden) by the |
||
41 | 9 | * service-specific options passed in. |
|
42 | * @param string $rootNamespace API classes' root namespace |
||
43 | 9 | */ |
|
44 | 9 | public function __construct(array $globalOptions = [], $rootNamespace = 'OpenStack') |
|
45 | { |
||
46 | $this->globalOptions = $globalOptions; |
||
47 | $this->rootNamespace = $rootNamespace; |
||
48 | } |
||
49 | |||
50 | private function getClasses($namespace) |
||
51 | { |
||
52 | $namespace = $this->rootNamespace . '\\' . $namespace; |
||
53 | $classes = [$namespace.'\\Api', $namespace.'\\Service']; |
||
54 | 2 | ||
55 | foreach ($classes as $class) { |
||
56 | 2 | if (!class_exists($class)) { |
|
57 | throw new \RuntimeException(sprintf("%s does not exist", $class)); |
||
58 | } |
||
59 | 2 | } |
|
60 | 2 | ||
61 | 2 | return $classes; |
|
62 | } |
||
63 | |||
64 | /** |
||
65 | * This method will return an OpenStack service ready fully built and ready for use. There is |
||
66 | * some initial setup that may prohibit users from directly instantiating the service class |
||
67 | * directly - this setup includes the configuration of the HTTP client's base URL, and the |
||
68 | * attachment of an authentication handler. |
||
69 | * |
||
70 | * @param string $namespace The namespace of the service |
||
71 | * @param array $serviceOptions The service-specific options to use |
||
72 | * |
||
73 | * @return \OpenStack\Common\Service\ServiceInterface |
||
74 | * |
||
75 | * @throws \Exception |
||
76 | */ |
||
77 | public function createService(string $namespace, array $serviceOptions = []): ServiceInterface |
||
78 | 9 | { |
|
79 | $options = $this->mergeOptions($serviceOptions); |
||
80 | 9 | ||
81 | $this->stockAuthHandler($options); |
||
82 | 6 | $this->stockHttpClient($options, $namespace); |
|
83 | 6 | ||
84 | 6 | list($apiClass, $serviceClass) = $this->getClasses($namespace); |
|
85 | |||
86 | 2 | return new $serviceClass($options['httpClient'], new $apiClass()); |
|
87 | } |
||
88 | 2 | ||
89 | private function stockHttpClient(array &$options, string $serviceName) |
||
90 | { |
||
91 | 6 | if (!isset($options['httpClient']) || !($options['httpClient'] instanceof ClientInterface)) { |
|
92 | if (stripos($serviceName, 'identity') !== false) { |
||
93 | 6 | $baseUrl = $options['authUrl']; |
|
94 | 6 | $stack = $this->getStack($options['authHandler']); |
|
95 | 1 | } else { |
|
96 | 1 | list($token, $baseUrl) = $options['identityService']->authenticate($options); |
|
97 | 1 | $stack = $this->getStack($options['authHandler'], $token); |
|
98 | 5 | } |
|
99 | 1 | ||
100 | $microVersion = $options['microVersion'] ?? null; |
||
101 | |||
102 | 2 | $this->addDebugMiddleware($options, $stack); |
|
103 | |||
104 | 2 | $options['httpClient'] = $this->httpClient($baseUrl, $stack, $options['catalogType'], $microVersion); |
|
105 | 2 | } |
|
106 | 2 | } |
|
107 | |||
108 | /** |
||
109 | * @codeCoverageIgnore |
||
110 | */ |
||
111 | private function addDebugMiddleware(array $options, HandlerStack &$stack) |
||
112 | { |
||
113 | View Code Duplication | if (!empty($options['debugLog']) |
|
0 ignored issues
–
show
|
|||
114 | && !empty($options['logger']) |
||
115 | && !empty($options['messageFormatter']) |
||
116 | ) { |
||
117 | $stack->push(GuzzleMiddleware::log($options['logger'], $options['messageFormatter'])); |
||
118 | } |
||
119 | } |
||
120 | |||
121 | 6 | /** |
|
122 | * @param array $options |
||
123 | 6 | * |
|
124 | 5 | * @codeCoverageIgnore |
|
125 | 5 | */ |
|
126 | 5 | private function stockAuthHandler(array &$options) |
|
127 | 6 | { |
|
128 | if (!isset($options['authHandler'])) { |
||
129 | $options['authHandler'] = function () use ($options) { |
||
130 | return $options['identityService']->authenticate($options)[0]; |
||
131 | }; |
||
132 | } |
||
133 | } |
||
134 | |||
135 | private function getStack(callable $authHandler, Token $token = null): HandlerStack |
||
136 | { |
||
137 | $stack = HandlerStack::create(); |
||
138 | $stack->push(Middleware::authHandler($authHandler, $token)); |
||
139 | return $stack; |
||
140 | } |
||
141 | |||
142 | 2 | private function httpClient(string $baseUrl, HandlerStack $stack, string $serviceType = null, string $microVersion = null): ClientInterface |
|
143 | { |
||
144 | 2 | $clientOptions = [ |
|
145 | 2 | 'base_uri' => Utils::normalizeUrl($baseUrl), |
|
146 | 2 | 'handler' => $stack, |
|
147 | ]; |
||
148 | |||
149 | 6 | if ($microVersion && $serviceType) { |
|
0 ignored issues
–
show
The expression
$microVersion of type null|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() The expression
$serviceType of type null|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
150 | $clientOptions['headers']['OpenStack-API-Version'] = sprintf('%s %s', $serviceType, $microVersion); |
||
151 | 6 | } |
|
152 | 6 | ||
153 | 6 | if (isset($this->globalOptions['requestOptions'])) { |
|
154 | 6 | $clientOptions = array_merge($this->globalOptions['requestOptions'], $clientOptions); |
|
155 | } |
||
156 | return new Client($clientOptions); |
||
157 | 9 | } |
|
158 | |||
159 | 9 | private function mergeOptions(array $serviceOptions): array |
|
160 | { |
||
161 | 9 | $options = array_merge($this->defaults, $this->globalOptions, $serviceOptions); |
|
162 | 3 | ||
163 | if (!isset($options['authUrl'])) { |
||
164 | throw new \InvalidArgumentException('"authUrl" is a required option'); |
||
165 | 6 | } |
|
166 | |||
167 | if (!isset($options['identityService']) || !($options['identityService'] instanceof IdentityService)) { |
||
168 | throw new \InvalidArgumentException(sprintf( |
||
169 | '"identityService" must be specified and implement %s', IdentityService::class |
||
170 | )); |
||
171 | } |
||
172 | |||
173 | return $options; |
||
174 | } |
||
175 | } |
||
176 |
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.