Passed
Pull Request — develop (#267)
by Arnaud
08:10
created

Service::getConstantAnnotationBlock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 1
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace WsdlToPhp\PackageGenerator\File;
6
7
use InvalidArgumentException;
8
use SoapFault;
9
use WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
10
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
11
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
12
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
13
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
14
use WsdlToPhp\PackageGenerator\Generator\Generator;
15
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
16
use WsdlToPhp\PackageGenerator\Model\Method;
17
use WsdlToPhp\PackageGenerator\Model\Method as MethodModel;
18
use WsdlToPhp\PackageGenerator\Model\Service as ServiceModel;
19
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
20
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
21
use WsdlToPhp\PackageGenerator\Parser\Wsdl\TagHeader;
22
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
23
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
24
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
25
use WsdlToPhp\PhpGenerator\Element\PhpFunctionParameter as PhpFunctionParameterBase;
26
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
27
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
28
29
final class Service extends AbstractModelFile
30
{
31
    public const METHOD_SET_HEADER_PREFIX = 'setSoapHeader';
32
    public const PARAM_SET_HEADER_NAMESPACE = 'namespace';
33
    public const PARAM_SET_HEADER_MUSTUNDERSTAND = 'mustUnderstand';
34
    public const PARAM_SET_HEADER_ACTOR = 'actor';
35
    public const METHOD_GET_RESULT = 'getResult';
36
37
    /**
38
     * Method model can't be found in case the original method's name is unclean:
39
     * - ex: my.operation.name becomes my_operation_name
40
     * thus the Model from Model\Service::getMethod() can't be found
41
     * So we store the generated name associated to the original method object.
42
     */
43
    protected array $methodNames = [];
44 48
45
    public static function getOperationMethodReturnType(MethodModel $method, Generator $generator): string
46 48
    {
47
        $returnType = $method->getReturnType();
48 48
49 2
        if (is_null($returnType)) {
50
            return 'null';
51
        }
52 46
53 40
        if ((($struct = $generator->getStructByName($returnType)) instanceof StructModel) && !$struct->isRestriction()) {
54 40
            if ($struct->isStruct()) {
55 8
                $returnType = $struct->getPackagedName(true);
56 8
            } elseif ($struct->isArray()) {
57 8
                if (($structInheritance = $struct->getInheritanceStruct()) instanceof StructModel) {
58
                    $returnType = sprintf('%s[]', $structInheritance->getPackagedName(true));
59 4
                } else {
60
                    $returnType = $struct->getInheritance();
61
                }
62
            }
63
        }
64 46
65
        return $returnType;
66
    }
67 50
68
    public function setModel(AbstractModel $model): self
69 50
    {
70 2
        if (!$model instanceof ServiceModel) {
71
            throw new InvalidArgumentException('Model must be an instance of a Service', __LINE__);
72
        }
73 48
74
        return parent::setModel($model);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::setModel($model) returns the type WsdlToPhp\PackageGenerator\File\AbstractModelFile which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\File\Service.
Loading history...
75
    }
76 46
77
    protected function fillClassConstants(ConstantContainer $constants): void
78 46
    {
79
    }
80
81
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
82
    {
83
        return null;
84
    }
85 46
86
    protected function fillClassProperties(PropertyContainer $properties): void
87 46
    {
88
    }
89
90
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
91
    {
92
        return null;
93
    }
94 46
95
    protected function defineUseStatements(): AbstractModelFile
96 46
    {
97
        $this->getFile()->addUse(SoapFault::class);
98 46
99
        /** @var Method $method */
100
        foreach ($this->getModel()->getMethods() as $method) {
0 ignored issues
show
Bug introduced by
The method getMethods() does not exist on WsdlToPhp\PackageGenerator\Model\AbstractModel. Did you maybe mean getMeta()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

100
        foreach ($this->getModel()->/** @scrutinizer ignore-call */ getMethods() as $method) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
101 46
            $soapHeaderTypes = $method->getMetaValue(TagHeader::META_SOAP_HEADER_TYPES, []);
102
            if (!is_array($soapHeaderTypes)) {
103 46
                continue;
104
            }
105
            foreach ($soapHeaderTypes as $soapHeaderType) {
106 46
                $model = $this->getModelByName($soapHeaderType);
107
                if (!$model instanceof StructModel) {
108
                    continue;
109 46
                }
110 46
                if (!$model->isRestriction()) {
111 46
                    continue;
112
                }
113 46
114
                $this->getFile()->addUse(InvalidArgumentException::class);
115 46
116
                break 2;
117 46
            }
118 46
        }
119
120
        return parent::defineUseStatements();
121 46
    }
122
123
    protected function getClassDeclarationLineText(): string
124 46
    {
125
        return GeneratorOptions::VALUE_NONE === $this->getGenerator()->getOptionGatherMethods() ? 'This class stands for all operations' : parent::getClassDeclarationLineText();
126 46
    }
127 46
128 46
    protected function fillClassMethods(): void
129 46
    {
130 46
        $this
131 12
            ->addSoapHeaderMethods()
132 12
            ->addOperationsMethods()
133 12
            ->addGetResultMethod()
134 12
        ;
135 12
    }
136
137
    protected function addSoapHeaderMethods(): self
138
    {
139
        foreach ($this->getModel()->getMethods() as $method) {
140 46
            $this->addSoapHeaderFromMethod($method);
141
        }
142
143 12
        return $this;
144
    }
145
146 12
    protected function addSoapHeaderFromMethod(MethodModel $method): self
147 12
    {
148 12
        $soapHeaderNames = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMES, []);
149 12
        $soapHeaderNamespaces = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMESPACES, []);
150 12
        $soapHeaderTypes = $method->getMetaValue(TagHeader::META_SOAP_HEADER_TYPES, []);
151 12
        if (is_array($soapHeaderNames) && is_array($soapHeaderNamespaces) && is_array($soapHeaderTypes)) {
152
            foreach ($soapHeaderNames as $index => $soapHeaderName) {
153 12
                $methodName = $this->getSoapHeaderMethodName($soapHeaderName);
154 12
                if (is_null($this->methods->get($methodName))) {
155 12
                    $soapHeaderNamespace = array_key_exists($index, $soapHeaderNamespaces) ? $soapHeaderNamespaces[$index] : null;
156 12
                    $soapHeaderType = array_key_exists($index, $soapHeaderTypes) ? $soapHeaderTypes[$index] : null;
157 12
                    $this->methods->add($this->getSoapHeaderMethod($methodName, $soapHeaderName, $soapHeaderNamespace, $soapHeaderType));
0 ignored issues
show
Bug introduced by
It seems like $soapHeaderNamespace can also be of type null; however, parameter $soapHeaderNamespace of WsdlToPhp\PackageGenerat...::getSoapHeaderMethod() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
                    $this->methods->add($this->getSoapHeaderMethod($methodName, $soapHeaderName, /** @scrutinizer ignore-type */ $soapHeaderNamespace, $soapHeaderType));
Loading history...
Bug introduced by
It seems like $soapHeaderType can also be of type null; however, parameter $soapHeaderType of WsdlToPhp\PackageGenerat...::getSoapHeaderMethod() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
                    $this->methods->add($this->getSoapHeaderMethod($methodName, $soapHeaderName, $soapHeaderNamespace, /** @scrutinizer ignore-type */ $soapHeaderType));
Loading history...
158
                }
159 12
            }
160
        }
161
162
        return $this;
163
    }
164 12
165
    protected function getSoapHeaderMethod(string $methodName, string $soapHeaderName, string $soapHeaderNamespace, string $soapHeaderType): PhpMethod
166
    {
167 12
        try {
168
            $method = new PhpMethod($methodName, [
169 12
                $firstParameter = new PhpFunctionParameter(lcfirst($soapHeaderName), PhpFunctionParameterBase::NO_VALUE, $this->getTypeFromName($soapHeaderType)),
170 12
                new PhpFunctionParameterBase(self::PARAM_SET_HEADER_NAMESPACE, $soapHeaderNamespace, self::TYPE_STRING),
171 12
                new PhpFunctionParameterBase(self::PARAM_SET_HEADER_MUSTUNDERSTAND, false, self::TYPE_BOOL),
172 12
                new PhpFunctionParameterBase(self::PARAM_SET_HEADER_ACTOR, null, '?'.self::TYPE_STRING),
173
            ], self::TYPE_SELF);
174
175
            $model = $this->getModelByName($soapHeaderType);
176 12
            if ($model instanceof StructModel) {
177
                $rules = new Rules($this, $method, new StructAttributeModel($model->getGenerator(), $soapHeaderType, $model->getName(), $model), $this->methods);
178 12
                $rules->applyRules(lcfirst($soapHeaderName));
179
                $firstParameter->setModel($model);
180
            }
181 46
            $method->addChild(sprintf('return $this->%s($%s, \'%s\', $%s, $%s, $%s);', self::METHOD_SET_HEADER_PREFIX, self::PARAM_SET_HEADER_NAMESPACE, $soapHeaderName, lcfirst($soapHeaderName), self::PARAM_SET_HEADER_MUSTUNDERSTAND, self::PARAM_SET_HEADER_ACTOR));
182
        } catch (InvalidArgumentException $exception) {
183 46
            throw new InvalidArgumentException(sprintf('Unable to create function parameter for service "%s" with type "%s"', $this->getModel()->getName(), var_export($this->getTypeFromName($soapHeaderName), true)), __LINE__, $exception);
184 46
        }
185
186
        return $method;
187 46
    }
188
189
    protected function getTypeFromName(string $name): ?string
190 46
    {
191
        return self::getPhpType(
192 46
            $this->getStructAttributeTypeAsPhpType(new StructAttributeModel($this->generator, 'any', $name)),
193 46
            $this->getGenerator()->getOptionXsdTypesPath(),
194 46
            $this->getStructAttributeTypeAsPhpType(new StructAttributeModel($this->generator, 'any', $name))
195
        );
196 46
    }
197
198
    protected function getSoapHeaderMethodName(string $soapHeaderName): string
199 46
    {
200
        return sprintf('%s%s', self::METHOD_SET_HEADER_PREFIX, ucfirst($soapHeaderName));
201 46
    }
202 46
203 46
    protected function addOperationsMethods(): self
204 46
    {
205
        foreach ($this->getModel()->getMethods() as $method) {
206 46
            $this->addMainMethod($method);
207
        }
208
209 46
        return $this;
210
    }
211 46
212 46
    protected function addGetResultMethod(): self
213 12
    {
214 46
        $method = new PhpMethod(self::METHOD_GET_RESULT);
215 46
        $method->addChild('return parent::getResult();');
216
        $this->methods->add($method);
217 46
218
        return $this;
219
    }
220 46
221
    protected function addMainMethod(MethodModel $method): self
222
    {
223 12
        $methodFile = new Operation($method, $this->getGenerator());
224
        $mainMethod = $methodFile->getMainMethod();
225 12
        $this->methods->add($mainMethod);
226 12
        $this->setModelFromMethod($mainMethod, $method);
227 12
228 12
        return $this;
229 12
    }
230 12
231 12
    protected function getMethodAnnotationBlock(PhpMethod $method): PhpAnnotationBlock
232 12
    {
233
        $annotationBlock = new PhpAnnotationBlock();
234 2
        if (0 === mb_stripos($method->getName(), self::METHOD_SET_HEADER_PREFIX)) {
235 2
            $this->addAnnotationBlockForSoapHeaderMethod($annotationBlock, $method);
236 2
        } elseif (self::METHOD_GET_RESULT === $method->getName()) {
237
            $this->addAnnotationBlockForgetResultMethod($annotationBlock);
238
        } else {
239
            $this->addAnnotationBlockForOperationMethod($annotationBlock, $method);
240
        }
241 12
242 12
        return $annotationBlock;
243 12
    }
244 12
245 12
    protected function addAnnotationBlockForSoapHeaderMethod(PhpAnnotationBlock $annotationBlock, PhpMethod $method): self
246 12
    {
247
        $methodParameters = $method->getParameters();
248
        $firstParameter = array_shift($methodParameters);
249
        if ($firstParameter instanceof PhpFunctionParameter) {
250 12
            $annotationBlock->addChild(sprintf('Sets the %s SoapHeader param', ucfirst($firstParameter->getName())));
251
            $firstParameterType = $firstParameter->getType();
252
            if ($firstParameter->getModel() instanceof StructModel) {
253 46
                $firstParameterType = $this->getStructAttributeTypeAsPhpType(new StructAttributeModel($firstParameter->getModel()->getGenerator(), $firstParameter->getName(), $firstParameter->getModel()->getName(), $firstParameter->getModel()));
254
                if ($firstParameter->getModel()->isRestriction()) {
255 46
                    $annotationBlock
256 46
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $firstParameter->getModel()->getPackagedName(true), StructEnum::METHOD_VALUE_IS_VALID)))
257 46
                        ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $firstParameter->getModel()->getPackagedName(true), StructEnum::METHOD_GET_VALID_VALUES)))
258
                        ->addChild(new PhpAnnotation(self::ANNOTATION_THROWS, InvalidArgumentException::class))
259
                    ;
260 46
                }
261
            }
262
            $annotationBlock
263 46
                ->addChild(new PhpAnnotation(self::ANNOTATION_USES, sprintf('%s::%s()', $this->getModel()->getExtends(true), self::METHOD_SET_HEADER_PREFIX)))
264
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', $firstParameterType, $firstParameter->getName())))
265
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', self::TYPE_STRING, self::PARAM_SET_HEADER_NAMESPACE)))
266 46
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s $%s', self::TYPE_BOOL, self::PARAM_SET_HEADER_MUSTUNDERSTAND)))
267 46
                ->addChild(new PhpAnnotation(self::ANNOTATION_PARAM, sprintf('%s|null $%s', self::TYPE_STRING, self::PARAM_SET_HEADER_ACTOR)))
268
                ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getModel()->getPackagedName(true)))
269
            ;
270 46
        }
271
272
        return $this;
273 46
    }
274
275 46
    protected function addAnnotationBlockForOperationMethod(PhpAnnotationBlock $annotationBlock, PhpMethod $method): self
276 46
    {
277 46
        if (($model = $this->getModelFromMethod($method)) instanceof MethodModel) {
278
            $operationAnnotationBlock = new OperationAnnotationBlock($model, $this->getGenerator());
279 46
            $operationAnnotationBlock->addAnnotationBlockForOperationMethod($annotationBlock);
280
        }
281 46
282
        return $this;
283
    }
284 46
285
    protected function addAnnotationBlockForgetResultMethod(PhpAnnotationBlock $annotationBlock): self
286 46
    {
287 46
        $annotationBlock
288 8
            ->addChild('Returns the result')->addChild(new PhpAnnotation(self::ANNOTATION_SEE, sprintf('%s::getResult()', $this->getModel()->getExtends(true))))
289
            ->addChild(new PhpAnnotation(self::ANNOTATION_RETURN, $this->getServiceReturnTypes()))
290
        ;
291 46
292
        return $this;
293
    }
294 46
295
    protected function getServiceReturnTypes(): string
296 46
    {
297
        $returnTypes = [];
298 46
        foreach ($this->getModel()->getMethods() as $method) {
299
            $returnTypes[] = self::getOperationMethodReturnType($method, $this->getGenerator());
300
        }
301
        natcasesort($returnTypes);
302
303
        return implode('|', array_unique($returnTypes));
304
    }
305
306
    protected function getModelFromMethod(PhpMethod $method): ?MethodModel
307
    {
308
        $model = $this->getGenerator()->getServiceMethod($method->getName());
309
        if (!$model instanceof MethodModel) {
310
            $model = array_key_exists($method->getName(), $this->methodNames) ? $this->methodNames[$method->getName()] : null;
311
        }
312
313
        return $model;
314
    }
315
316
    protected function setModelFromMethod(PhpMethod $phpMethod, MethodModel $methodModel): self
317
    {
318
        $this->methodNames[$phpMethod->getName()] = $methodModel;
319
320
        return $this;
321
    }
322
}
323