catalog = new Catalog(); $this->products = new Products($this->catalog, new Images()); $this->offers = new Offers($this->catalog); } /** * @return bool true — файл обработан полностью, false — нужен ещё один шаг. */ public function run(string $path, string $relative): bool { $reader = Xml::open($path); $position = $this->state->position($relative); $index = 0; $completed = true; $inCatalog = false; wp_defer_term_counting(true); try { $ok = $reader->read(); while ($ok) { if ($reader->nodeType !== XMLReader::ELEMENT) { $ok = $reader->read(); continue; } switch ($reader->localName) { case 'Каталог': // Признак полной выгрузки / только изменений (атрибут в 2.05+, элемент в старых схемах). $inCatalog = true; Session::markCatalogType($reader->getAttribute('СодержитТолькоИзменения') === 'true'); break; case 'ПакетПредложений': $inCatalog = false; break; case 'СодержитТолькоИзменения': if ($inCatalog) { Session::markCatalogType(trim($reader->readString()) === 'true'); } break; case 'Классификатор': if (!$this->state->classifierDone($relative)) { $this->catalog->importClassifier(Xml::expand($reader)); $this->state->markClassifier($relative); $this->state->save(); } $ok = $reader->next(); continue 2; case 'ТипыЦен': $this->catalog->importPriceTypes(Xml::expand($reader)); $ok = $reader->next(); continue 2; case 'Товар': case 'Предложение': if (++$index > $position) { $this->importItem($reader); $this->state->setPosition($relative, $index); if ($index % self::SAVE_EVERY === 0) { $this->state->save(); } if ($this->clock->expired()) { $completed = false; break 2; } } $ok = $reader->next(); continue 2; } $ok = $reader->read(); } if ($completed) { $error = Xml::lastError(); if ($error !== null) { throw new ExchangeException('Ошибка XML в ' . $relative . ', ' . $error); } $this->state->markDone($relative); } } finally { $reader->close(); $this->offers->flush(); wp_defer_term_counting(false); $this->state->save(); } return $completed; } private function importItem(XMLReader $reader): void { $name = $reader->localName; $node = Xml::expand($reader); try { $name === 'Товар' ? $this->products->import($node) : $this->offers->import($node); } catch (ExchangeException $e) { throw $e; } catch (\Throwable $e) { // Ошибка одного товара не должна останавливать весь обмен. Log::error(sprintf('%s (Ид %s): %s', $name, Xml::text($node, 'Ид'), $e->getMessage())); } } }