Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
ComplexTypeStrategy.php
Go to the documentation of this file.
1 <?php
7 
8 use Zend\Soap\Wsdl;
9 use Zend\Soap\Wsdl\ComplexTypeStrategy\AbstractComplexTypeStrategy;
10 
14 class ComplexTypeStrategy extends AbstractComplexTypeStrategy
15 {
19  const ARRAY_ITEM_KEY_NAME = 'item';
20 
24  const APP_INF_NS = 'inf';
25 
29  protected $_typeProcessor;
30 
36  protected $_data;
37 
43  public function __construct(\Magento\Framework\Reflection\TypeProcessor $typeProcessor)
44  {
45  $this->_typeProcessor = $typeProcessor;
46  }
47 
53  protected function _getDom()
54  {
55  return $this->getContext()->toDomDocument();
56  }
57 
66  public function addComplexType($type, $parentCallInfo = [])
67  {
68  if (($soapType = $this->scanRegisteredTypes($type)) !== null) {
69  return $soapType;
70  }
71  $soapType = Wsdl::TYPES_NS . ':' . $type;
72  // Register type here to avoid recursion
73  $this->getContext()->addType($type, $soapType);
74  $complexType = $this->_getDom()->createElement(Wsdl::XSD_NS . ':complexType');
75  $complexType->setAttribute('name', $type);
76  $typeData = $this->_typeProcessor->getTypeData($type);
77  if (isset($typeData['documentation'])) {
78  $this->addAnnotation($complexType, $typeData['documentation']);
79  }
80 
81  if (isset($typeData['parameters']) && is_array($typeData['parameters'])) {
82  $callInfo = isset($typeData['callInfo']) ? $typeData['callInfo'] : $parentCallInfo;
83  $sequence = $this->_processParameters($typeData['parameters'], $callInfo);
84  $complexType->appendChild($sequence);
85  }
86 
87  $this->getContext()->getSchema()->appendChild($complexType);
88  return $soapType;
89  }
90 
98  protected function _processParameters($parameters, $callInfo)
99  {
100  $sequence = $this->_getDom()->createElement(Wsdl::XSD_NS . ':sequence');
101  foreach ($parameters as $parameterName => $parameterData) {
102  $parameterType = $parameterData['type'];
103  $element = $this->_getDom()->createElement(Wsdl::XSD_NS . ':element');
104  $element->setAttribute('name', $parameterName);
105  $isRequired = isset($parameterData['required']) && $parameterData['required'];
106  $default = isset($parameterData['default']) ? $parameterData['default'] : null;
107  $this->_revertRequiredCallInfo($isRequired, $callInfo);
108 
109  if ($this->_typeProcessor->isArrayType($parameterType)) {
110  $this->_processArrayParameter($parameterType, $callInfo);
111  $element->setAttribute(
112  'type',
113  Wsdl::TYPES_NS . ':' . $this->_typeProcessor->translateArrayTypeName($parameterType)
114  );
115  if (!$isRequired) {
116  $element->setAttribute('minOccurs', 0);
117  }
118  } else {
119  $this->_processParameter($element, $isRequired, $parameterData, $parameterType, $callInfo);
120  }
121 
122  $this->addAnnotation($element, $parameterData['documentation'], $default, $callInfo);
123  $sequence->appendChild($element);
124  }
125 
126  return $sequence;
127  }
128 
139  protected function _processParameter(\DOMElement $element, $isRequired, $parameterData, $parameterType, $callInfo)
140  {
141  $element->setAttribute('minOccurs', $isRequired ? 1 : 0);
142  $maxOccurs = isset($parameterData['isArray']) && $parameterData['isArray'] ? 'unbounded' : 1;
143  $element->setAttribute('maxOccurs', $maxOccurs);
144  if ($this->_typeProcessor->isTypeSimple($parameterType) || $this->_typeProcessor->isTypeAny($parameterType)) {
145  $typeNs = Wsdl::XSD_NS;
146  } else {
147  $typeNs = Wsdl::TYPES_NS;
148  $this->addComplexType($parameterType, $callInfo);
149  }
150  $element->setAttribute('type', $typeNs . ':' . $parameterType);
151  }
152 
160  protected function _processArrayParameter($type, $callInfo = [])
161  {
162  $arrayItemType = $this->_typeProcessor->getArrayItemType($type);
163  $arrayTypeName = $this->_typeProcessor->translateArrayTypeName($type);
164  if (!$this->_typeProcessor->isTypeSimple($arrayItemType) && !$this->_typeProcessor->isTypeAny($arrayItemType)) {
165  $this->addComplexType($arrayItemType, $callInfo);
166  }
167  $arrayTypeParameters = [
168  self::ARRAY_ITEM_KEY_NAME => [
169  'type' => $arrayItemType,
170  'required' => false,
171  'isArray' => true,
172  'documentation' => sprintf('An item of %s.', $arrayTypeName),
173  ],
174  ];
175  $arrayTypeData = [
176  'documentation' => sprintf('An array of %s items.', $arrayItemType),
177  'parameters' => $arrayTypeParameters,
178  ];
179  $this->_typeProcessor->setTypeData($arrayTypeName, $arrayTypeData);
180  $this->addComplexType($arrayTypeName, $callInfo);
181  }
182 
190  protected function _revertRequiredCallInfo($isRequired, &$callInfo)
191  {
192  if (!$isRequired) {
193  if (isset($callInfo['requiredInput']['yes'])) {
194  $callInfo['requiredInput']['no']['calls'] = $callInfo['requiredInput']['yes']['calls'];
195  unset($callInfo['requiredInput']['yes']);
196  }
197  if (isset($callInfo['returned']['always'])) {
198  $callInfo['returned']['conditionally']['calls'] = $callInfo['returned']['always']['calls'];
199  unset($callInfo['returned']['always']);
200  }
201  }
202  }
203 
216  public function addAnnotation(\DOMElement $element, $documentation, $default = null, $callInfo = [])
217  {
218  $annotationNode = $this->_getDom()->createElement(Wsdl::XSD_NS . ':annotation');
219 
220  $elementType = $this->_getElementType($element);
221  $appInfoNode = $this->_getDom()->createElement(Wsdl::XSD_NS . ':appinfo');
222  $appInfoNode->setAttributeNS(
223  Wsdl::XML_NS_URI,
224  Wsdl::XML_NS . ':' . self::APP_INF_NS,
225  $this->getContext()->getTargetNamespace()
226  );
227 
228  $this->_processDefaultValueAnnotation($elementType, $default, $appInfoNode);
229  $this->_processElementType($elementType, $documentation, $appInfoNode);
230 
231  if (preg_match_all('/{([a-z]+):(.+)}/Ui', $documentation, $matches)) {
232  $count = count($matches[0]);
233  for ($i = 0; $i < $count; $i++) {
234  $appinfoTag = $matches[0][$i];
235  $tagName = $matches[1][$i];
236  $tagValue = $matches[2][$i];
237  switch ($tagName) {
238  case 'callInfo':
239  $callInfoRegExp = '/([a-z].+):(returned|requiredInput):(yes|no|always|conditionally)/i';
240  if (preg_match($callInfoRegExp, $tagValue)) {
241  list($callName, $direction, $condition) = explode(':', $tagValue);
242  $condition = strtolower($condition);
243  if (preg_match('/allCallsExcept\(([a-zA-Z].+)\)/', $callName, $calls)) {
244  $callInfo[$direction][$condition] = [
245  'allCallsExcept' => $calls[1],
246  ];
247  } elseif (!isset($callInfo[$direction][$condition]['allCallsExcept'])) {
248  $this->_overrideCallInfoName($callInfo, $callName);
249  $callInfo[$direction][$condition]['calls'][] = $callName;
250  }
251  }
252  break;
253  case 'seeLink':
254  $this->_processSeeLink($appInfoNode, $tagValue);
255  break;
256  case 'docInstructions':
257  $this->_processDocInstructions($appInfoNode, $tagValue);
258  break;
259  default:
260  $nodeValue = trim($tagValue);
261  $simpleTextNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $tagName);
262  $simpleTextNode->appendChild($this->_getDom()->createTextNode($nodeValue));
263  $appInfoNode->appendChild($simpleTextNode);
264  break;
265  }
266  $documentation = str_replace($appinfoTag, '', $documentation);
267  }
268  }
269  $this->_processCallInfo($appInfoNode, $callInfo);
270  $documentationNode = $this->_getDom()->createElement(Wsdl::XSD_NS . ':documentation');
271  $documentationText = trim($documentation);
272  $documentationNode->appendChild($this->_getDom()->createTextNode($documentationText));
273  $annotationNode->appendChild($documentationNode);
274  $annotationNode->appendChild($appInfoNode);
275  $element->appendChild($annotationNode);
276  }
277 
286  protected function _processElementType($elementType, $documentation, \DOMElement $appInfoNode)
287  {
288  if ($elementType == 'int') {
289  $this->_processRequiredAnnotation('min', $documentation, $appInfoNode);
290  $this->_processRequiredAnnotation('max', $documentation, $appInfoNode);
291  }
292  if ($elementType == 'string') {
293  $this->_processRequiredAnnotation('maxLength', $documentation, $appInfoNode);
294  }
295 
296  if ($this->_typeProcessor->isArrayType($elementType)) {
297  $natureOfTypeNode = $this->_getDom()->createElement(self::APP_INF_NS . ':natureOfType');
298  $natureOfTypeNode->appendChild($this->_getDom()->createTextNode('array'));
299  $appInfoNode->appendChild($natureOfTypeNode);
300  }
301  }
302 
311  protected function _processDefaultValueAnnotation($elementType, $default, \DOMElement $appInfoNode)
312  {
313  if ($elementType == 'boolean') {
314  $default = (bool)$default ? 'true' : 'false';
315  }
316  if ($default) {
317  $defaultNode = $this->_getDom()->createElement(self::APP_INF_NS . ':default');
318  $defaultNode->appendChild($this->_getDom()->createTextNode($default));
319  $appInfoNode->appendChild($defaultNode);
320  }
321  }
322 
330  protected function _getElementType(\DOMElement $element)
331  {
332  $elementType = null;
333  if ($element->hasAttribute('type')) {
334  list($typeNs, $elementType) = explode(':', $element->getAttribute('type'));
335  }
336  return $elementType;
337  }
338 
347  protected function _processRequiredAnnotation($annotation, $documentation, \DOMElement $appInfoNode)
348  {
349  if (!preg_match("/{{$annotation}:.+}/Ui", $documentation)) {
350  $annotationNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $annotation);
351  $appInfoNode->appendChild($annotationNode);
352  }
353  }
354 
362  protected function _processCallInfo(\DOMElement $appInfoNode, $callInfo)
363  {
364  if (!empty($callInfo)) {
365  foreach ($callInfo as $direction => $conditions) {
366  foreach ($conditions as $condition => $info) {
367  $callInfoNode = $this->_getDom()->createElement(self::APP_INF_NS . ':callInfo');
368  if (isset($info['allCallsExcept'])) {
369  $allExceptNode = $this->_getDom()->createElement(self::APP_INF_NS . ':allCallsExcept');
370  $allExceptNode->appendChild($this->_getDom()->createTextNode($info['allCallsExcept']));
371  $callInfoNode->appendChild($allExceptNode);
372  } elseif (isset($info['calls'])) {
373  foreach ($info['calls'] as $callName) {
374  $callNode = $this->_getDom()->createElement(self::APP_INF_NS . ':callName');
375  $callNode->appendChild($this->_getDom()->createTextNode($callName));
376  $callInfoNode->appendChild($callNode);
377  }
378  }
379  $directionNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $direction);
380  $directionNode->appendChild($this->_getDom()->createTextNode(ucfirst($condition)));
381  $callInfoNode->appendChild($directionNode);
382  $appInfoNode->appendChild($callInfoNode);
383  }
384  }
385  }
386  }
387 
395  protected function _processDocInstructions(\DOMElement $appInfoNode, $tagValue)
396  {
397  if (preg_match('/(input|output):(.+)/', $tagValue, $docMatches)) {
398  $docInstructionsNode = $this->_getDom()->createElement(self::APP_INF_NS . ':docInstructions');
399  $directionNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $docMatches[1]);
400  $directionValueNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $docMatches[2]);
401  $directionNode->appendChild($directionValueNode);
402  $docInstructionsNode->appendChild($directionNode);
403  $appInfoNode->appendChild($docInstructionsNode);
404  }
405  }
406 
414  protected function _processSeeLink(\DOMElement $appInfoNode, $tagValue)
415  {
416  if (preg_match('|([http://]?.+):(.+):(.+)|i', $tagValue, $matches)) {
417  $seeLink = ['url' => $matches[1], 'title' => $matches[2], 'for' => $matches[3]];
418  $seeLinkNode = $this->_getDom()->createElement(self::APP_INF_NS . ':seeLink');
419  foreach (['url', 'title', 'for'] as $subNodeName) {
420  if (isset($seeLink[$subNodeName])) {
421  $seeLinkSubNode = $this->_getDom()->createElement(self::APP_INF_NS . ':' . $subNodeName);
422  $seeLinkSubNode->appendChild($this->_getDom()->createTextNode($seeLink[$subNodeName]));
423  $seeLinkNode->appendChild($seeLinkSubNode);
424  }
425  }
426  $appInfoNode->appendChild($seeLinkNode);
427  }
428  }
429 
437  protected function _overrideCallInfoName(&$callInfo, $callName)
438  {
439  foreach ($callInfo as $direction => &$callInfoData) {
440  foreach ($callInfoData as $condition => &$data) {
441  if (isset($data['calls'])) {
442  $foundCallNameIndex = array_search($callName, $data['calls']);
443  if ($foundCallNameIndex !== false) {
444  unset($data['calls'][$foundCallNameIndex]);
445  if (empty($data['calls'])) {
446  unset($callInfo[$direction][$condition]);
447  }
448  break;
449  }
450  }
451  }
452  }
453  }
454 }
addAnnotation(\DOMElement $element, $documentation, $default=null, $callInfo=[])
__construct(\Magento\Framework\Reflection\TypeProcessor $typeProcessor)
elseif(isset( $params[ 'redirect_parent']))
Definition: iframe.phtml:17
_processParameter(\DOMElement $element, $isRequired, $parameterData, $parameterType, $callInfo)
$count
Definition: recent.phtml:13
_processDefaultValueAnnotation($elementType, $default, \DOMElement $appInfoNode)
_processRequiredAnnotation($annotation, $documentation, \DOMElement $appInfoNode)
_processCallInfo(\DOMElement $appInfoNode, $callInfo)
$type
Definition: item.phtml:13
_processDocInstructions(\DOMElement $appInfoNode, $tagValue)
_processElementType($elementType, $documentation, \DOMElement $appInfoNode)
_processSeeLink(\DOMElement $appInfoNode, $tagValue)
foreach( $_productCollection as $_product)() ?>" class $info
Definition: listing.phtml:52
$i
Definition: gallery.phtml:31
$element
Definition: element.phtml:12