Passed
Push — master ( f0393b...d8ac0a )
by Mikaël
12:30 queued 13s
created

Service::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
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 WsdlToPhp\PackageGenerator\ConfigurationReader\GeneratorOptions;
8
use WsdlToPhp\PackageGenerator\Container\PhpElement\Constant as ConstantContainer;
9
use WsdlToPhp\PackageGenerator\Container\PhpElement\Property as PropertyContainer;
10
use WsdlToPhp\PackageGenerator\File\Element\PhpFunctionParameter;
11
use WsdlToPhp\PackageGenerator\File\Validation\Rules;
12
use WsdlToPhp\PackageGenerator\Generator\Generator;
13
use WsdlToPhp\PackageGenerator\Model\AbstractModel;
14
use WsdlToPhp\PackageGenerator\Model\Method;
15
use WsdlToPhp\PackageGenerator\Model\Method as MethodModel;
16
use WsdlToPhp\PackageGenerator\Model\Service as ServiceModel;
17
use WsdlToPhp\PackageGenerator\Model\Struct as StructModel;
18
use WsdlToPhp\PackageGenerator\Model\StructAttribute as StructAttributeModel;
19
use WsdlToPhp\PackageGenerator\Parser\Wsdl\TagHeader;
20
use WsdlToPhp\PhpGenerator\Element\PhpAnnotation;
21
use WsdlToPhp\PhpGenerator\Element\PhpAnnotationBlock;
22
use WsdlToPhp\PhpGenerator\Element\PhpConstant;
23
use WsdlToPhp\PhpGenerator\Element\PhpFunctionParameter as PhpFunctionParameterBase;
24
use WsdlToPhp\PhpGenerator\Element\PhpMethod;
25
use WsdlToPhp\PhpGenerator\Element\PhpProperty;
26
27
final class Service extends AbstractModelFile
28
{
29
    public const METHOD_SET_HEADER_PREFIX = 'setSoapHeader';
30
    public const PARAM_SET_HEADER_NAMESPACE = 'namespace';
31
    public const PARAM_SET_HEADER_MUSTUNDERSTAND = 'mustUnderstand';
32
    public const PARAM_SET_HEADER_ACTOR = 'actor';
33
    public const METHOD_GET_RESULT = 'getResult';
34
35
    /**
36
     * Method model can't be found in case the original method's name is unclean:
37
     * - ex: my.operation.name becomes my_operation_name
38
     * thus the Model from Model\Service::getMethod() can't be found
39
     * So we store the generated name associated to the original method object.
40
     */
41
    protected array $methodNames = [];
42
43
    public static function getOperationMethodReturnType(MethodModel $method, Generator $generator): string
44 48
    {
45
        $returnType = $method->getReturnType();
46 48
47
        if (is_null($returnType)) {
48 48
            return 'null';
49 2
        }
50
51
        if ((($struct = $generator->getStructByName($returnType)) instanceof StructModel) && !$struct->isRestriction()) {
52 46
            if ($struct->isStruct()) {
53 40
                $returnType = $struct->getPackagedName(true);
54 40
            } elseif ($struct->isArray()) {
55 8
                if (($structInheritance = $struct->getInheritanceStruct()) instanceof StructModel) {
56 8
                    $returnType = sprintf('%s[]', $structInheritance->getPackagedName(true));
57 8
                } else {
58
                    $returnType = $struct->getInheritance();
59 4
                }
60
            }
61
        }
62
63
        return $returnType;
64 46
    }
65
66
    public function setModel(AbstractModel $model): self
67 50
    {
68
        if (!$model instanceof ServiceModel) {
69 50
            throw new \InvalidArgumentException('Model must be an instance of a Service', __LINE__);
70 2
        }
71
72
        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...
73 48
    }
74
75
    public function getModel(): ?ServiceModel
76 46
    {
77
        return parent::getModel();
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::getModel() could return the type WsdlToPhp\PackageGenerator\Model\AbstractModel which includes types incompatible with the type-hinted return WsdlToPhp\PackageGenerator\Model\Service|null. Consider adding an additional type-check to rule them out.
Loading history...
78 46
    }
79
80
    protected function fillClassConstants(ConstantContainer $constants): void
81
    {
82
    }
83
84 46
    protected function getConstantAnnotationBlock(PhpConstant $constant): ?PhpAnnotationBlock
85
    {
86 46
        return null;
87
    }
88
89
    protected function fillClassProperties(PropertyContainer $properties): void
90
    {
91
    }
92 46
93
    protected function getPropertyAnnotationBlock(PhpProperty $property): ?PhpAnnotationBlock
94 46
    {
95
        return null;
96 46
    }
97
98
    protected function defineUseStatements(): AbstractModelFile
99 46
    {
100
        $this->getFile()->addUse(\SoapFault::class);
101 46
102
        /** @var Method $method */
103
        foreach ($this->getModel()->getMethods() as $method) {
104 46
            $soapHeaderTypes = $method->getMetaValue(TagHeader::META_SOAP_HEADER_TYPES, []);
105
            if (!is_array($soapHeaderTypes)) {
106
                continue;
107 46
            }
108 46
            foreach ($soapHeaderTypes as $soapHeaderType) {
109 46
                $model = $this->getModelByName($soapHeaderType);
110
                if (!$model instanceof StructModel) {
111 46
                    continue;
112
                }
113 46
                if (!$model->isRestriction()) {
114
                    continue;
115 46
                }
116 46
117
                $this->getFile()->addUse(\InvalidArgumentException::class);
118
119 46
                break 2;
120
            }
121
        }
122 46
123
        return parent::defineUseStatements();
124 46
    }
125 46
126 46
    protected function getClassDeclarationLineText(): string
127 46
    {
128 46
        return GeneratorOptions::VALUE_NONE === $this->getGenerator()->getOptionGatherMethods() ? 'This class stands for all operations' : parent::getClassDeclarationLineText();
129 12
    }
130 12
131 12
    protected function fillClassMethods(): void
132 12
    {
133 12
        $this
134
            ->addSoapHeaderMethods()
135
            ->addOperationsMethods()
136
            ->addGetResultMethod()
137
        ;
138 46
    }
139
140
    protected function addSoapHeaderMethods(): self
141 12
    {
142
        foreach ($this->getModel()->getMethods() as $method) {
143
            $this->addSoapHeaderFromMethod($method);
144 12
        }
145 12
146 12
        return $this;
147 12
    }
148 12
149 12
    protected function addSoapHeaderFromMethod(MethodModel $method): self
150
    {
151 12
        $soapHeaderNames = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMES, []);
152 12
        $soapHeaderNamespaces = $method->getMetaValue(TagHeader::META_SOAP_HEADER_NAMESPACES, []);
153 12
        $soapHeaderTypes = $method->getMetaValue(TagHeader::META_SOAP_HEADER_TYPES, []);
154 12
        if (is_array($soapHeaderNames) && is_array($soapHeaderNamespaces) && is_array($soapHeaderTypes)) {
155 12
            foreach ($soapHeaderNames as $index => $soapHeaderName) {
156
                $methodName = $this->getSoapHeaderMethodName($soapHeaderName);
157 12
                if (is_null($this->methods->get($methodName))) {
158
                    $soapHeaderNamespace = array_key_exists($index, $soapHeaderNamespaces) ? $soapHeaderNamespaces[$index] : null;
159
                    $soapHeaderType = array_key_exists($index, $soapHeaderTypes) ? $soapHeaderTypes[$index] : null;
160
                    $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

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

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