Passed
Push — feature/issue-124 ( 91e4aa...dde646 )
by Mikaël
09:46
created

Service::addSoapHeaderFromMethod()   B

Complexity

Conditions 8
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 11
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 17
ccs 12
cts 12
cp 1
crap 8
rs 8.4444
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 as MethodModel;
17
use WsdlToPhp\PackageGenerator\Model\Service as ServiceModel;
18
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
19
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
20
use WsdlToPhp\PackageGenerator\Parser\Wsdl\TagHeader;
21
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
22
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
23
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
24
use WsdlToPhp\PhpGenerator\Element\PhpFunctionParameter as PhpFunctionParameterBase;
25
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
26
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
27
28
final class Service extends AbstractModelFile
29
{
30
    public const METHOD_SET_HEADER_PREFIX = 'setSoapHeader';
31
    public const PARAM_SET_HEADER_NAMESPACE = 'namespace';
32
    public const PARAM_SET_HEADER_MUSTUNDERSTAND = 'mustUnderstand';
33
    public const PARAM_SET_HEADER_ACTOR = 'actor';
34
    public const METHOD_GET_RESULT = 'getResult';
35
36
    /**
37
     * Method model can't be found in case the original method's name is unclean:
38
     * - ex: my.operation.name becomes my_operation_name
39
     * thus the Model from Model\Service::getMethod() can't be found
40
     * So we store the generated name associated to the original method object.
41
     */
42
    protected array $methodNames = [];
43
44 49
    public static function getOperationMethodReturnType(MethodModel $method, Generator $generator): string
45
    {
46 49
        $returnType = $method->getReturnType();
47
48 49
        if (is_null($returnType)) {
49 2
            return 'null';
50
        }
51
52 47
        if ((($struct = $generator->getStructByName($returnType)) instanceof StructModel) && !$struct->isRestriction()) {
53 41
            if ($struct->isStruct()) {
54 41
                $returnType = $struct->getPackagedName(true);
55 8
            } elseif ($struct->isArray()) {
56 8
                if (($structInheritance = $struct->getInheritanceStruct()) instanceof StructModel) {
57 8
                    $returnType = sprintf('%s[]', $structInheritance->getPackagedName(true));
58
                } else {
59 4
                    $returnType = $struct->getInheritance();
60
                }
61
            }
62
        }
63
64 47
        return $returnType;
65
    }
66
67 51
    public function setModel(AbstractModel $model): self
68
    {
69 51
        if (!$model instanceof ServiceModel) {
70 2
            throw new InvalidArgumentException('Model must be an instance of a Service', __LINE__);
71
        }
72
73 49
        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...
74
    }
75
76 47
    protected function fillClassConstants(ConstantContainer $constants): void
77
    {
78 47
    }
79
80
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
81
    {
82
    }
83
84 47
    protected function fillClassProperties(PropertyContainer $properties): void
85
    {
86 47
    }
87
88
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
89
    {
90
    }
91
92 47
    protected function defineUseStatements(): AbstractModelFile
93
    {
94 47
        $this->getFile()->addUse(SoapFault::class);
95
96 47
        return parent::defineUseStatements();
97
    }
98
99 47
    protected function getClassDeclarationLineText(): string
100
    {
101 47
        return GeneratorOptions::VALUE_NONE === $this->getGenerator()->getOptionGatherMethods() ? 'This class stands for all operations' : parent::getClassDeclarationLineText();
102
    }
103
104 47
    protected function fillClassMethods(): void
105
    {
106
        $this
107 47
            ->addSoapHeaderMethods()
108 47
            ->addOperationsMethods()
109 47
            ->addGetResultMethod()
110
        ;
111 47
    }
112
113 47
    protected function addSoapHeaderMethods(): self
114
    {
115 47
        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

115
        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...
116 47
            $this->addSoapHeaderFromMethod($method);
117
        }
118
119 47
        return $this;
120
    }
121
122 47
    protected function addSoapHeaderFromMethod(MethodModel $method): self
123
    {
124 47
        $soapHeaderNames = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMES, []);
125 47
        $soapHeaderNamespaces = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMESPACES, []);
126 47
        $soapHeaderTypes = $method->getMetaValue(TagHeader::META_SOAP_HEADER_TYPES, []);
127 47
        if (is_array($soapHeaderNames) && is_array($soapHeaderNamespaces) && is_array($soapHeaderTypes)) {
128 47
            foreach ($soapHeaderNames as $index => $soapHeaderName) {
129 12
                $methodName = $this->getSoapHeaderMethodName($soapHeaderName);
130 12
                if (is_null($this->methods->get($methodName))) {
131 12
                    $soapHeaderNamespace = array_key_exists($index, $soapHeaderNamespaces) ? $soapHeaderNamespaces[$index] : null;
132 12
                    $soapHeaderType = array_key_exists($index, $soapHeaderTypes) ? $soapHeaderTypes[$index] : null;
133 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

133
                    $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

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