1 | <?php |
||
2 | |||
3 | namespace carono\exchange1c\controllers; |
||
4 | |||
5 | use carono\exchange1c\behaviors\BomBehavior; |
||
6 | use carono\exchange1c\ExchangeEvent; |
||
7 | use carono\exchange1c\ExchangeModule; |
||
8 | use carono\exchange1c\helpers\ByteHelper; |
||
9 | use carono\exchange1c\helpers\NodeHelper; |
||
10 | use carono\exchange1c\helpers\SerializeHelper; |
||
11 | use carono\exchange1c\interfaces\DocumentInterface; |
||
12 | use carono\exchange1c\interfaces\OfferInterface; |
||
13 | use carono\exchange1c\interfaces\ProductInterface; |
||
14 | use Yii; |
||
15 | use yii\db\ActiveRecord; |
||
16 | use yii\helpers\FileHelper; |
||
17 | use yii\web\Response; |
||
18 | use Zenwalker\CommerceML\CommerceML; |
||
19 | use Zenwalker\CommerceML\Model\Classifier; |
||
20 | use Zenwalker\CommerceML\Model\Group; |
||
21 | use Zenwalker\CommerceML\Model\Image; |
||
22 | use Zenwalker\CommerceML\Model\Offer; |
||
23 | use Zenwalker\CommerceML\Model\Product; |
||
24 | use Zenwalker\CommerceML\Model\PropertyCollection; |
||
25 | use Zenwalker\CommerceML\Model\Simple; |
||
26 | use Zenwalker\CommerceML\Model\RequisiteCollection; |
||
27 | |||
28 | /** |
||
29 | * Default controller for the `api` module |
||
30 | * |
||
31 | * @property ExchangeModule $module |
||
32 | */ |
||
33 | class ApiController extends Controller |
||
34 | { |
||
35 | public $enableCsrfValidation = false; |
||
36 | public $commerceMLVersion = '2.10'; |
||
37 | |||
38 | const EVENT_BEFORE_UPDATE_PRODUCT = 'beforeUpdateProduct'; |
||
39 | const EVENT_AFTER_UPDATE_PRODUCT = 'afterUpdateProduct'; |
||
40 | const EVENT_BEFORE_UPDATE_OFFER = 'beforeUpdateOffer'; |
||
41 | const EVENT_AFTER_UPDATE_OFFER = 'afterUpdateOffer'; |
||
42 | const EVENT_BEFORE_PRODUCT_SYNC = 'beforeProductSync'; |
||
43 | const EVENT_AFTER_PRODUCT_SYNC = 'afterProductSync'; |
||
44 | const EVENT_BEFORE_OFFER_SYNC = 'beforeOfferSync'; |
||
45 | const EVENT_AFTER_OFFER_SYNC = 'afterOfferSync'; |
||
46 | const EVENT_AFTER_FINISH_UPLOAD_FILE = 'afterFinishUploadFile'; |
||
47 | const EVENT_AFTER_EXPORT_ORDERS = 'afterExportOrders'; |
||
48 | |||
49 | protected $_ids; |
||
50 | |||
51 | public function init() |
||
52 | { |
||
53 | set_time_limit($this->module->timeLimit); |
||
54 | if ($this->module->memoryLimit) { |
||
55 | ini_set('memory_limit', $this->module->memoryLimit); |
||
56 | } |
||
57 | parent::init(); |
||
58 | } |
||
59 | |||
60 | |||
61 | /** |
||
62 | * @return array |
||
63 | */ |
||
64 | public function behaviors() |
||
65 | { |
||
66 | return array_merge(parent::behaviors(), [ |
||
67 | 'bom' => [ |
||
68 | 'class' => BomBehavior::class, |
||
69 | 'only' => ['query'], |
||
70 | ], |
||
71 | ]); |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * @param \yii\base\Action $action |
||
76 | * @param mixed $result |
||
77 | * @return mixed|string |
||
78 | */ |
||
79 | public function afterAction($action, $result) |
||
80 | { |
||
81 | Yii::$app->response->headers->set('uid', Yii::$app->user->getId()); |
||
82 | if (is_bool($result)) { |
||
83 | return $result ? 'success' : 'failure'; |
||
84 | } |
||
85 | |||
86 | if (is_array($result)) { |
||
87 | $r = []; |
||
88 | foreach ($result as $key => $value) { |
||
89 | $r[] = is_int($key) ? $value : $key . '=' . $value; |
||
90 | } |
||
91 | return implode("\n", $r); |
||
92 | } |
||
93 | |||
94 | return parent::afterAction($action, $result); |
||
95 | } |
||
96 | |||
97 | /** |
||
98 | * @param $type |
||
99 | * @return array|bool |
||
100 | */ |
||
101 | public function actionCheckauth($type) |
||
102 | { |
||
103 | if (Yii::$app->user->isGuest) { |
||
104 | return false; |
||
105 | } |
||
106 | |||
107 | return [ |
||
108 | 'success', |
||
109 | 'PHPSESSID', |
||
110 | Yii::$app->session->getId(), |
||
111 | ]; |
||
112 | } |
||
113 | |||
114 | /** |
||
115 | * @return float|int|false |
||
116 | */ |
||
117 | protected function getFileLimit() |
||
118 | { |
||
119 | $limit = ByteHelper::maximum_upload_size(); |
||
120 | if (!($limit % 2)) { |
||
121 | $limit--; |
||
122 | } |
||
123 | return $limit; |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * @return array |
||
128 | */ |
||
129 | public function actionInit($type) |
||
130 | { |
||
131 | switch ($type) { |
||
132 | case 'catalog': |
||
133 | $pattern = '*.xml'; |
||
134 | $dir = $this->module->getTmpDir() . DIRECTORY_SEPARATOR . $pattern; |
||
135 | foreach (glob($dir) as $file) { |
||
136 | if ($this->module->debug) { |
||
137 | rename($file, str_replace('.xml', date("_Y-m-d H.i.s", filemtime($file)) . '.xml.bak', $file)); |
||
138 | } else { |
||
139 | FileHelper::unlink($file); |
||
140 | } |
||
141 | } |
||
142 | break; |
||
143 | } |
||
144 | return [ |
||
145 | 'zip' => class_exists('ZipArchive') && $this->module->useZip ? 'yes' : 'no', |
||
146 | 'file_limit' => $this->getFileLimit(), |
||
147 | ]; |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * @param $type |
||
152 | * @param $filename |
||
153 | * @return bool |
||
154 | */ |
||
155 | public function actionFile($type, $filename) |
||
156 | { |
||
157 | $body = Yii::$app->request->getRawBody(); |
||
158 | $filePath = $this->module->getTmpDir() . DIRECTORY_SEPARATOR . $filename; |
||
159 | if (!is_dir(dirname($filePath))) { |
||
160 | FileHelper::createDirectory(dirname($filePath)); |
||
161 | } |
||
162 | $isArchive = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)) === 'zip'; |
||
163 | file_put_contents($filePath, $body, FILE_APPEND); |
||
164 | if ((int)Yii::$app->request->headers->get('Content-Length') != $this->getFileLimit()) { |
||
165 | if ($isArchive) { |
||
166 | $this->extractArchive($filePath); |
||
167 | } |
||
168 | $this->afterFinishUploadFile($filePath); |
||
169 | } |
||
170 | return true; |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * @param $file |
||
175 | */ |
||
176 | public function parsingImport($file) |
||
177 | { |
||
178 | $this->_ids = []; |
||
179 | $commerce = new CommerceML(); |
||
180 | $commerce->loadImportXml($file); |
||
181 | $classifierFile = Yii::getAlias($this->module->tmpDir . '/classifier.xml'); |
||
182 | if ($commerce->classifier->xml) { |
||
183 | $commerce->classifier->xml->saveXML($classifierFile); |
||
184 | } else { |
||
185 | $commerce->classifier->xml = simplexml_load_string(file_get_contents($classifierFile)); |
||
0 ignored issues
–
show
|
|||
186 | } |
||
187 | $this->beforeProductSync(); |
||
188 | if ($groupClass = $this->getGroupClass()) { |
||
189 | $groupClass::createTree1c($commerce->classifier->getGroups()); |
||
190 | } |
||
191 | $productClass = $this->getProductClass(); |
||
192 | $productClass::createProperties1c($commerce->classifier->getProperties()); |
||
193 | foreach ($commerce->catalog->getProducts() as $product) { |
||
194 | if (!$model = $productClass::createModel1c($product)) { |
||
195 | Yii::error("Модель продукта не найдена, проверьте реализацию $productClass::createModel1c", |
||
196 | 'exchange1c'); |
||
197 | continue; |
||
198 | } |
||
199 | $this->parseProduct($model, $product); |
||
200 | $this->_ids[] = $model->getPrimaryKey(); |
||
201 | $model = null; |
||
202 | unset($model, $product); |
||
203 | gc_collect_cycles(); |
||
204 | } |
||
205 | $this->afterProductSync(); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * @param $file |
||
210 | */ |
||
211 | public function parsingOffer($file) |
||
212 | { |
||
213 | $this->_ids = []; |
||
214 | $commerce = new CommerceML(); |
||
215 | $commerce->loadOffersXml($file); |
||
216 | if ($offerClass = $this->getOfferClass()) { |
||
217 | $offerClass::createPriceTypes1c($commerce->offerPackage->getPriceTypes()); |
||
218 | } |
||
219 | $this->beforeOfferSync(); |
||
220 | foreach ($commerce->offerPackage->getOffers() as $offer) { |
||
221 | $product_id = $offer->getClearId(); |
||
222 | if ($product = $this->findProductModelById($product_id)) { |
||
223 | $model = $product->getOffer1c($offer); |
||
224 | $this->parseProductOffer($model, $offer); |
||
225 | $this->_ids[] = $model->getPrimaryKey(); |
||
226 | } else { |
||
227 | Yii::warning("Продукт $product_id не найден в базе", 'exchange1c'); |
||
228 | continue; |
||
229 | } |
||
230 | unset($model); |
||
231 | } |
||
232 | $this->afterOfferSync(); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * @param $file |
||
237 | */ |
||
238 | public function parsingOrder($file) |
||
239 | { |
||
240 | /** |
||
241 | * @var DocumentInterface $documentModel |
||
242 | */ |
||
243 | $commerce = new CommerceML(); |
||
244 | $commerce->addXmls(false, false, $file); |
||
245 | $documentClass = $this->module->documentClass; |
||
246 | foreach ($commerce->order->documents as $document) { |
||
247 | if ($documentModel = $documentClass::findOne((string)$document->Номер)) { |
||
248 | $documentModel->setRaw1cData($commerce, $document); |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * @param $filePath |
||
255 | */ |
||
256 | private function extractArchive($filePath) |
||
257 | { |
||
258 | $zip = new \ZipArchive(); |
||
259 | $zip->open($filePath); |
||
260 | $zip->extractTo(dirname($filePath)); |
||
261 | $zip->close(); |
||
262 | if (!$this->module->debug) { |
||
263 | FileHelper::unlink($filePath); |
||
264 | } |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * @param $type |
||
269 | * @param $filename |
||
270 | * @return bool |
||
271 | */ |
||
272 | public function actionImport($type, $filename) |
||
273 | { |
||
274 | $file = $this->module->getTmpDir() . DIRECTORY_SEPARATOR . $filename; |
||
275 | switch ($type) { |
||
276 | case 'catalog': |
||
277 | if (strpos($file, 'offer') !== false) { |
||
278 | $this->parsingOffer($file); |
||
279 | } elseif (strpos($file, 'import') !== false) { |
||
280 | $this->parsingImport($file); |
||
281 | } |
||
282 | break; |
||
283 | case 'sale': |
||
284 | if (strpos($file, 'order') !== false) { |
||
285 | $this->parsingOrder($file); |
||
286 | } |
||
287 | break; |
||
288 | } |
||
289 | if (!$this->module->debug) { |
||
290 | FileHelper::unlink($file); |
||
291 | } |
||
292 | return true; |
||
293 | } |
||
294 | |||
295 | protected function clearTmp() |
||
296 | { |
||
297 | FileHelper::removeDirectory($this->module->getTmpDir()); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * @param $type |
||
302 | * @return mixed |
||
303 | */ |
||
304 | public function actionQuery($type) |
||
305 | { |
||
306 | /** |
||
307 | * @var DocumentInterface $document |
||
308 | */ |
||
309 | $response = Yii::$app->response; |
||
0 ignored issues
–
show
It seems like
Yii::app->response can also be of type yii\web\Response . However, the property $response is declared as type yii\console\Response . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
310 | $response->format = Response::FORMAT_RAW; |
||
311 | $response->getHeaders()->set('Content-Type', 'application/xml; charset=windows-1251'); |
||
312 | |||
313 | $root = new \SimpleXMLElement('<КоммерческаяИнформация></КоммерческаяИнформация>'); |
||
314 | $root->addAttribute('ВерсияСхемы', $this->commerceMLVersion); |
||
315 | $root->addAttribute('ДатаФормирования', date('Y-m-d\TH:i:s')); |
||
316 | |||
317 | $ids = []; |
||
318 | if ($this->module->exchangeDocuments) { |
||
319 | $document = $this->module->documentClass; |
||
320 | foreach ($document::findDocuments1c() as $order) { |
||
321 | $ids[] = $order->getPrimaryKey(); |
||
322 | NodeHelper::appendNode($root, SerializeHelper::serializeDocument($order)); |
||
323 | } |
||
324 | if ($this->module->debug) { |
||
325 | $xml = $root->asXML(); |
||
326 | $xml = html_entity_decode($xml, ENT_NOQUOTES, 'UTF-8'); |
||
327 | file_put_contents($this->module->getTmpDir() . '/query.xml', $xml); |
||
328 | } |
||
329 | } |
||
330 | $this->afterExportOrders($ids); |
||
331 | return $root->asXML(); |
||
332 | } |
||
333 | |||
334 | /** |
||
335 | * @param $type |
||
336 | * @return bool |
||
337 | */ |
||
338 | public function actionSuccess($type) |
||
339 | { |
||
340 | return true; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * @param $name |
||
345 | * @param $value |
||
346 | */ |
||
347 | protected static function setData($name, $value) |
||
348 | { |
||
349 | Yii::$app->session->set($name, $value); |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * @param $name |
||
354 | * @param null $default |
||
0 ignored issues
–
show
|
|||
355 | * @return mixed |
||
356 | */ |
||
357 | protected static function getData($name, $default = null) |
||
358 | { |
||
359 | return Yii::$app->session->get($name, $default); |
||
360 | } |
||
361 | |||
362 | /** |
||
363 | * @return bool |
||
364 | */ |
||
365 | protected static function clearData() |
||
366 | { |
||
367 | return Yii::$app->session->closeSession(); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * @param ProductInterface $model |
||
372 | * @param \Zenwalker\CommerceML\Model\Product $product |
||
373 | */ |
||
374 | protected function parseProduct($model, $product) |
||
375 | { |
||
376 | $this->beforeUpdateProduct($model); |
||
377 | $model->setRaw1cData($product->owner, $product); |
||
378 | $this->parseGroups($model, $product); |
||
379 | $this->parseProperties($model, $product); |
||
380 | $this->parseRequisites($model, $product); |
||
381 | $this->parseImage($model, $product); |
||
382 | $this->afterUpdateProduct($model); |
||
383 | unset($group); |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * @param OfferInterface $model |
||
388 | * @param Offer $offer |
||
389 | */ |
||
390 | protected function parseProductOffer($model, $offer) |
||
391 | { |
||
392 | $this->beforeUpdateOffer($model, $offer); |
||
393 | $this->parseSpecifications($model, $offer); |
||
394 | $this->parsePrice($model, $offer); |
||
395 | $model->{$model::getIdFieldName1c()} = $offer->id; |
||
396 | $model->save(); |
||
397 | $this->afterUpdateOffer($model, $offer); |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * @param string $id |
||
402 | * |
||
403 | * @return ProductInterface|null |
||
404 | */ |
||
405 | protected function findProductModelById($id) |
||
406 | { |
||
407 | /** |
||
408 | * @var $class ProductInterface |
||
409 | */ |
||
410 | $class = $this->getProductClass(); |
||
411 | return $class::find()->andWhere([$class::getIdFieldName1c() => $id])->one(); |
||
412 | } |
||
413 | |||
414 | /** |
||
415 | * @param Offer $offer |
||
416 | * |
||
417 | * @return OfferInterface|null |
||
418 | */ |
||
419 | protected function findOfferModel($offer) |
||
420 | { |
||
421 | /** |
||
422 | * @var $class ProductInterface |
||
423 | */ |
||
424 | $class = $this->getOfferClass(); |
||
425 | return $class::find()->andWhere([$class::getIdFieldName1c() => $offer->id])->one(); |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * @return ActiveRecord |
||
430 | */ |
||
431 | protected function createProductModel($data) |
||
432 | { |
||
433 | $class = $this->getProductClass(); |
||
434 | if ($model = $class::createModel1c($data)) { |
||
435 | return $model; |
||
436 | } |
||
437 | |||
438 | return Yii::createObject(['class' => $class]); |
||
439 | } |
||
440 | |||
441 | /** |
||
442 | * @param OfferInterface $model |
||
443 | * @param Offer $offer |
||
444 | */ |
||
445 | protected function parsePrice($model, $offer) |
||
446 | { |
||
447 | foreach ($offer->getPrices() as $price) { |
||
448 | $model->setPrice1c($price); |
||
449 | } |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * @param ProductInterface $model |
||
454 | * @param Product $product |
||
455 | */ |
||
456 | protected function parseImage($model, $product) |
||
457 | { |
||
458 | $images = $product->getImages(); |
||
459 | foreach ($images as $image) { |
||
460 | $path = realpath($this->module->getTmpDir() . DIRECTORY_SEPARATOR . $image->path); |
||
461 | if (file_exists($path)) { |
||
462 | $model->addImage1c($path, $image->caption); |
||
463 | } |
||
464 | } |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * @param ProductInterface $model |
||
469 | * @param Product $product |
||
470 | */ |
||
471 | protected function parseGroups($model, $product) |
||
472 | { |
||
473 | $group = $product->getGroup(); |
||
474 | $model->setGroup1c($group); |
||
475 | } |
||
476 | |||
477 | /** |
||
478 | * @param ProductInterface $model |
||
479 | * @param Product $product |
||
480 | */ |
||
481 | protected function parseRequisites($model, $product) |
||
482 | { |
||
483 | $requisites = $product->getRequisites(); |
||
484 | foreach ($requisites as $requisite) { |
||
485 | $model->setRequisite1c($requisite->name, $requisite->value); |
||
486 | } |
||
487 | } |
||
488 | |||
489 | /** |
||
490 | * @param OfferInterface $model |
||
491 | * @param Offer $offer |
||
492 | */ |
||
493 | protected function parseSpecifications($model, $offer) |
||
494 | { |
||
495 | foreach ($offer->getSpecifications() as $specification) { |
||
496 | $model->setSpecification1c($specification); |
||
497 | } |
||
498 | } |
||
499 | |||
500 | /** |
||
501 | * @param ProductInterface $model |
||
502 | * @param Product $product |
||
503 | */ |
||
504 | protected function parseProperties($model, $product) |
||
505 | { |
||
506 | $properties = $product->getProperties(); |
||
507 | foreach ($properties as $property) { |
||
508 | $model->setProperty1c($property); |
||
509 | } |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * @return OfferInterface |
||
514 | */ |
||
515 | protected function getOfferClass() |
||
516 | { |
||
517 | return $this->module->offerClass; |
||
518 | } |
||
519 | |||
520 | /** |
||
521 | * @return ProductInterface |
||
522 | */ |
||
523 | protected function getProductClass() |
||
524 | { |
||
525 | return $this->module->productClass; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * @return DocumentInterface |
||
530 | */ |
||
531 | protected function getDocumentClass() |
||
532 | { |
||
533 | return $this->module->documentClass; |
||
534 | } |
||
535 | |||
536 | /** |
||
537 | * @return \carono\exchange1c\interfaces\GroupInterface |
||
538 | */ |
||
539 | protected function getGroupClass() |
||
540 | { |
||
541 | return $this->module->groupClass; |
||
542 | } |
||
543 | |||
544 | /** |
||
545 | * @return bool |
||
546 | */ |
||
547 | public function actionError() |
||
548 | { |
||
549 | return false; |
||
550 | } |
||
551 | |||
552 | /** |
||
553 | * @param $filePath |
||
554 | */ |
||
555 | public function afterFinishUploadFile($filePath) |
||
556 | { |
||
557 | $this->module->trigger(self::EVENT_AFTER_FINISH_UPLOAD_FILE, new ExchangeEvent(['filePath' => $filePath])); |
||
558 | } |
||
559 | |||
560 | public function beforeProductSync() |
||
561 | { |
||
562 | $this->module->trigger(self::EVENT_BEFORE_PRODUCT_SYNC, new ExchangeEvent()); |
||
563 | } |
||
564 | |||
565 | public function afterProductSync() |
||
566 | { |
||
567 | $this->module->trigger(self::EVENT_AFTER_PRODUCT_SYNC, new ExchangeEvent(['ids' => $this->_ids])); |
||
568 | } |
||
569 | |||
570 | public function beforeOfferSync() |
||
571 | { |
||
572 | $this->module->trigger(self::EVENT_BEFORE_OFFER_SYNC, new ExchangeEvent()); |
||
573 | } |
||
574 | |||
575 | public function afterOfferSync() |
||
576 | { |
||
577 | $this->module->trigger(self::EVENT_AFTER_OFFER_SYNC, new ExchangeEvent(['ids' => $this->_ids])); |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * @param $model |
||
582 | */ |
||
583 | public function afterUpdateProduct($model) |
||
584 | { |
||
585 | $this->module->trigger(self::EVENT_AFTER_UPDATE_PRODUCT, new ExchangeEvent(['model' => $model])); |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * @param $model |
||
590 | */ |
||
591 | public function beforeUpdateProduct($model) |
||
592 | { |
||
593 | $this->module->trigger(self::EVENT_BEFORE_UPDATE_PRODUCT, new ExchangeEvent(['model' => $model])); |
||
594 | } |
||
595 | |||
596 | /** |
||
597 | * @param $model |
||
598 | * @param $offer |
||
599 | */ |
||
600 | public function beforeUpdateOffer($model, $offer) |
||
601 | { |
||
602 | $this->module->trigger(self::EVENT_BEFORE_UPDATE_OFFER, new ExchangeEvent([ |
||
603 | 'model' => $model, |
||
604 | 'ml' => $offer, |
||
605 | ])); |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * @param $model |
||
610 | * @param $offer |
||
611 | */ |
||
612 | public function afterUpdateOffer($model, $offer) |
||
613 | { |
||
614 | $this->module->trigger(self::EVENT_AFTER_UPDATE_OFFER, new ExchangeEvent(['model' => $model, 'ml' => $offer])); |
||
615 | } |
||
616 | |||
617 | /** |
||
618 | * @param $ids |
||
619 | */ |
||
620 | public function afterExportOrders($ids) |
||
621 | { |
||
622 | $this->module->trigger(self::EVENT_AFTER_EXPORT_ORDERS, new ExchangeEvent(['ids' => $ids])); |
||
623 | } |
||
624 | } |
||
625 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.