class DromXMLGenerator
{
protected $types = [];
protected $dictionaries = [
"car" => [],
"bus" => [],
"motorhome" => [],
"mobile_crane" => [],
"bulldozer" => [],
"atv" => [],
"truck" => [],
"building" => [],
"misc" => [],
"municipal" => [],
"forestry" => [],
"loader" => [],
"semi_trailer" => [],
"trailer" => [],
"truck_tractor" => [],
"tractor" => [],
"excavator" => [],
];
protected $special_frames_mapping = [
"BUS" => "bus",
"TRUCK" => "truck",
"TRUCK_TRACTOR" => "truck_tractor",
"MOTORHOME" => "motorhome",
"BUILDING" => "building",
"CRANE" => "mobile_crane",
"LOADER" => "loader",
"TRAILER" => "trailer",
"SEMITRAILER" => "semi_trailer",
"EXCAVATOR" => "excavator",
"FARM" => "tractor",
"MISC" => "misc",
"FORESTRY" => "forestry",
"MUNICIPAL" => "municipal",
"ATV" => "atv",
"BULLDOZER" => "bulldozer",
"QUADBIKE" => "atv",
"SNOWMOBILE" => "atv",
"SCOOTER" => "misc",
];
protected $special_frames = [];
protected $xml_cars = null;
protected $xml_special = null;
protected $offers_cars = null;
protected $offers_special = null;
protected $total = 0;
protected $found_cars = 0;
protected $found_special = 0;
protected $valid_cars = 0;
protected $valid_special = 0;
protected $invalid_special = 0;
protected $not_found_special = 0;
protected $p1 = 0;
protected $p2 = 0;
protected $p3 = 0;
protected $p4 = 0;
protected $fuels = [
"БЕНЗИН" => "GASOLINE",
"ГАЗ" => "GAS",
"ДИЗЕЛЬ" => "DIESEL",
"ЭЛЕКТРИЧЕСКИЙ" => "ELECTRICITY",
];
protected $errors = "";
function __construct()
{
$this->xml_cars = new SimpleXMLElement("");
$this->offers_cars = $this->xml_cars->addChild('Offers');
$this->xml_special = new SimpleXMLElement("");
$this->offers_special = $this->xml_special->addChild('SpecOffers');
$this->loadTypes();
foreach($this->dictionaries AS $type => $v)
{
$structure = [
"brands" => [],
"models" => [],
"cities" => [],
"bodies" => [],
"colors" => [],
"transmissions" => [],
"engines" => [],
"wheels" => [],
"fuels" => [],
];
$url = "https://www.drom.ru/cached_files/autoload/files/";
if($type === "car")
{
$url = $url."ref.xml";
}
else
{
$url = $url."ref_".$type.".xml";
}
$response_xml = file_get_contents($url);
file_put_contents("/home/bitrix/www/local/ref_".$type.".xml", $response_xml);
$structure_object = new SimpleXMLElement($response_xml);
foreach($structure_object->MarksSpec->MarkSpec AS $mark)
{
$k = mb_strtoupper((string) $mark->sMarkSpec);
$n = (int) $mark->idMarkSpec;
$models = [];
foreach($structure_object->ModelsSpec->ModelSpec AS $model)
{
$mmn = (int) $model->idMarkSpec;
if($mmn === $n)
{
$km = mb_strtoupper((string) $model->sModelSpec);
$nm = (int) $model->idModelSpec;
$models[ $km ] = $nm;
$structure['models'][ $km ] = $nm;
}
}
$structure['brands'][ $k ] = [ "id" => $n, "models" => $models ];
}
foreach($structure_object->Marks->Mark AS $mark)
{
$k = mb_strtoupper((string) $mark->sMark);
$n = (int) $mark->idMark;
$models = [];
foreach($structure_object->Models->Model AS $model)
{
$mmn = (int) $model->idMark;
if($mmn === $n)
{
$km = mb_strtoupper((string) $model->sModel);
$nm = (int) $model->idModel;
$models[ $km ] = $nm;
}
}
$structure['brands'][ $k ] = [ "id" => $n, "models" => $models ];
}
foreach($structure_object->Cities->City AS $city)
{
$k = mb_strtoupper((string) $city->sCity);
$n = (int) $city->idCity;
$structure['cities'][ $k ] = $n;
}
foreach($structure_object->FrameTypes->FrameType AS $frame_type)
{
$k = mb_strtoupper((string) $frame_type->sFrameType);
$n = (int) $frame_type->idFrameType;
$structure['bodies'][ $k ] = $n;
}
foreach($structure_object->Colors->Color AS $color)
{
$k = mb_strtoupper((string) $color->sColor);
$n = (int) $color->idColor;
$structure['colors'][ $k ] = $n;
}
foreach($structure_object->WheelFormulaTypes->WheelFormulaType AS $wheel_type)
{
$k = mb_strtoupper((string) $wheel_type->sWheelFormulaType);
$n = (int) $wheel_type->idWheelFormulaType;
$structure['wheels'][ $k ] = $n;
}
foreach($structure_object->EngineTypesSpec->EngineTypeSpec AS $engine_type)
{
$k = mb_strtoupper((string) $engine_type->sEngineTypeSpec);
$n = (int) $engine_type->idEngineTypeSpec;
$structure['fuels'][ $k ] = $n;
}
$this->dictionaries[ $type ] = $structure;
}
}
function addOffer($entry)
{
$this->total++;
$type = $entry['PROPERTIES']['VEHICLE_SUBTYPE']['VALUE'];
//print $type."\n";
if($type === "Легковой автомобиль")
{
$this->found_cars++;
$this->buildOffer(false, [ 'group' => 'car' ], $entry);
/*
$idMark = $this->getDictionaryRecordId($this->dictionaries['car']['brands'], mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']));
//print $entry['CODE']." - ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." - ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." - [".$idMark."] - [".$idModel."]\n";
//if(is_null($idMark)) { $this->errors .= "Не найдена марка ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."\n"; continue; }
if($idMark !== null)
{
//* id марки авто (см. файл-справочник); если не можете указать id, то укажите название марки текстом в поле sMark
$offer['idMark'] = $idMark;
}
else
{
//* название марки авто (Toyota, Honda, BMW и т.п.); указывать обязательно только в том случае, если не можете указать idMark
$offer['sMark'] = $entry['PROPERTIES']['BRAND']['RELATED']['NAME'];
}
$idModel = $this->getDictionaryRecordId($this->dictionaries['car']['models'], mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
//if(is_null($idModel)) { $this->errors .= "Не найдена модель ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." для ".".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."."\n"; continue; }
if($idModel !== null)
{
//* id модели авто (см. файл-справочник); если не можете указать id, то укажите название модели текстом в поле sModel
$offer['idModel'] = $idModel;
}
else
{
//* название модели авто (Land Cruiser, Accord, X5 и т.п.); указывать обязательно только в том случае, если не можете указать idModel
$offer['sModel'] = $entry['PROPERTIES']['MODEL']['RELATED']['NAME'];
}
*/
/*
$payload = [
"idOffer" => $entry['CODE'], //* уникальный id объявления в вашей базе данных
"idMark" => "", //* id марки авто (см. файл-справочник); если не можете указать id, то укажите название марки текстом в поле sMark
"idModel" => "", //* id модели авто (см. файл-справочник); если не можете указать id, то укажите название модели текстом в поле sModel
"sMark" => "", //* название марки авто (Toyota, Honda, BMW и т.п.); указывать обязательно только в том случае, если не можете указать idMark
"sModel" => "", //* название модели авто (Land Cruiser, Accord, X5 и т.п.); указывать обязательно только в том случае, если не можете указать idModel
"idCountry" => "", //id страны, где находится авто (см. файл-справочник, по умолчанию — 0)
"idCity" => "", //id города, в котором находится авто (см. файл-справочник)
"sCity" => "", //название города, в котором находится авто (см. файл-справочник)
"YearOfMade" => "", //год выпуска ТС (только год, 4 цифры: 1995, 2003, 2018 и т.п.)
"VIN" => $entry['PROPERTIES']['VIN']['VALUE'], //* VIN или номер кузова, если VIN отсутствует
"Price" => $entry['PROPERTIES']['PRICE']['VALUE'], //цена в рублях
"NewType" => 0, //признак, что автомобиль новый; 1 — новый, 0 — не новый (с пробегом)
"Volume" => $entry['PROPERTIES']['ENGINE_VOLUME']['VALUE'], //объем двигателя в куб.см (пример: 599, 1300, 2631 и т.п.)
"FrameType" => "???", //тип кузова (см. файл-справочник)
"Color" => "???", //цвет кузова (см. файл-справочник)
"idTransmission" => "???", //id типа трансмиссии (см. файл-справочник)
"sTransmission" => "???", //название типа трансмиссии
"idEngineType" => "", //id типа двигателя (см. файл-справочник)
"sEngineType" => "", //название типа двигателя
"idHybridType" => "", //гибрид (см. файл-справочник)
"sHybridType" => "", //гибрид (да, нет)
"idGbo" => "", //ГБО (см. файл-справочник)
"sGbo" => "", //ГБО (да, нет)
"idDriveType" => "???", //id типа двигателя (см. файл-справочник)
"sDriveType" => "", //название типа двигателя
"sWheelType" => "", //положение руля (левый, правый)
"Haul" => "", //пробег (показания одометра в километрах)
"sComplectation" => "", //название комплектации (в соответствии с каталогом)
"idComplectation" => "", //id комплектации на Дроме (в соответствии с каталогом)
"Additional" => "", //текстовое описание ТС (комплектация, оснащение, тюнинг и т.п.), здесь можно указать сссылку на видео, размещенное на YouTube
"Photos" => "", //секция со списком фотографий (ниже есть подробное описание секции)
"Phone" => "", //номер телефона (поле не должно быть больше 20 символов)
"Whereabouts" => "", //авто в наличии или под заказ (см. файл-справочник)
"Power" => $entry['PROPERTIES']['ENGINE_POWER']['VALUE'], //мощность автомобиля в лошадиных силах
"idCertProgram" => "", //программа сертификации (см. файл-справочник)
"DamagedType" => "", //признак для поврежденных автомобилей (0 - небитые, 1 - битые или не на ходу)
"SOR" => "", //номер свидетельства о регистрации ТС для авто с пробегом (10 символов)
];
*/
}
else
{
$this->found_special++;
$frame = $this->getFrameType(mb_strtoupper($type), mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
if(!is_null($frame))
{
$this->buildOffer(true, $frame, $entry);
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕ УДАЛОСЬ ОПРЕДЕЛИТЬ ТИП: ".mb_strtoupper($type)." \t\t[ ".$entry['CODE']." ][ ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ][ ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." ]\n";
//$this->not_found_special++;
}
}
}
private function buildOffer($special = false, $frame, $entry)
{
//print $entry['CODE']." - ".$type."\n";
$payload = [
"idOffer" => $entry['CODE'], //* уникальный id объявления в вашей базе данных
"YearOfMade" => $entry['PROPERTIES']['YEAR']['VALUE'], //год выпуска ТС (только год, 4 цифры: 1995, 2003, 2018 и т.п.)
"VIN" => $entry['PROPERTIES']['VIN']['VALUE'], //* VIN или номер кузова, если VIN отсутствует
"Price" => $entry['PROPERTIES']['PRICE']['VALUE'], //цена в рублях
"NewType" => 0, //признак, что автомобиль новый; 1 — новый, 0 — не новый (с пробегом)
"Volume" => $entry['PROPERTIES']['ENGINE_VOLUME']['VALUE'], //объем двигателя в куб.см (пример: 599, 1300, 2631 и т.п.)
"Power" => $entry['PROPERTIES']['ENGINE_POWER']['VALUE'], //мощность автомобиля в лошадиных силах
];
if(!empty($entry['PROPERTIES']['MILEAGE']['VALUE']))
{
$payload["Haul"] = $entry['PROPERTIES']['MILEAGE']['VALUE'];
}
if(!empty($entry['PROPERTIES']['AFTER_ACCIDENT']['VALUE']))
{
$payload[ $special ? "idDamagedType" : "DamagedType" ] = $entry['PROPERTIES']['AFTER_ACCIDENT']['XML_VALUE'] == "YES" ? 1 : 0;
}
if($special)
{
$payload['idFrameTypeSpec'] = $frame['id'];
$payload['idWheelType'] = 2;
if(in_array($frame['group'], [ "bus", "motorhome" ]))
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО УКАЗААТЬ DriveTypeSpec: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
if(in_array($frame['group'], [ "trailer", "semi_trailer" ]))
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО УКААЗАТЬ КОЛИЧЕСТВО ОСЕЙ Axles: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
if(in_array($frame['group'], [ "truck", "truck_tractor", "mobile_crane", "municipal", ]))
{
$item_name = str_replace("Х", "X", mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
preg_match('/\dX\d/', $item_name, $wheel_type_matches);
$idWheelFormulaType = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['wheels'], $wheel_type_matches[0]);
if(!is_null($idWheelFormulaType))
{
$payload['idWheelFormulaType'] = $idWheelFormulaType;
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕТ КОЛЕСНОЙ ФОРМУЛЫ: ".$entry['CODE']." - ".$item_name." [".$wheel_type_matches[0]."]\n";
return;
}
}
if(in_array($frame['group'], [ "bus", "truck", "truck_tractor", "mobile_crane", "loader", "municipal", "atv", "motorhome" ]))
{
if(!empty($entry['PROPERTIES']['ENGINE_FUEL']['VALUE']))
{
$f = mb_strtoupper($entry['PROPERTIES']['ENGINE_FUEL']['VALUE']);
if(isset($this->fuels[$f]))
{
$idEngineTypeSpec = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['fuels'], $this->fuels[$f]);
if(!is_null($idEngineTypeSpec))
{
$payload['idEngineTypeSpec'] = $idEngineTypeSpec;
}
}
}
}
}
if($special)
{
$patterns = array('/\s\dX\d/i', '/\s\dХ\d/i', '/САМОСВАЛ/i');
$replacements = array('', '', '');
$model_name = preg_replace($patterns, $replacements, mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
$model_name = ltrim(rtrim($model_name));
$model_name_chunks = explode(" ", $model_name);
array_unshift($model_name_chunks, implode('', $model_name_chunks));
$idModel = null;
foreach($model_name_chunks AS $c)
{
$idModel = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['models'], $c);
if(!is_null($idModel))
{
break;
}
}
if(!is_null($idModel))
{
$payload['idModelSpec'] = $idModel;
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ МОДЕЛЬ ДЛЯ МАРКИ ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME'].": ".$entry['CODE']." - ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
}
else
{
$idMark = $this->findInDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'], mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']));
//print $type." - ".$frame['group']." - ".$idMark['id']."\n";
if(!is_null($idMark))
{
if(!$special) { $payload['idMark'] = $idMark['id']; }
$this->p1++;
$patterns = array('/\s\dX\d/i', '/\s\dХ\d/i', '/САМОСВАЛ/i');
$replacements = array('', '', '');
$model_name = preg_replace($patterns, $replacements, mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
$model_name = ltrim(rtrim($model_name));
$model_name_chunks = explode(" ", $model_name);
array_unshift($model_name_chunks, implode('', $model_name_chunks));
$idModel = null;
foreach($model_name_chunks AS $c)
{
$idModel = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'][ mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']) ]['models'], $c);
if(!is_null($idModel))
{
$idModel = $this->findInDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'][ mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']) ]['models'], mb_strtoupper($c));
if(!is_null($idModel))
{
break;
}
}
}
if(!is_null($idModel))
{
$payload['idModel'] = $idModel;
}
else
{
$payload['sModel'] = $model_name;
}
}
else
{
$this->p2++;
if(!$special)
{
$payload['sMark'] = $entry['PROPERTIES']['BRAND']['RELATED']['NAME'];
$payload['sModel'] = $entry['PROPERTIES']['MODEL']['RELATED']['NAME'];
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ МАРКУ: ".$entry['CODE']." (ТИП ".$frame['group'].") - ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
//print $entry['PROPERTIES']['BRAND']['RELATED']['NAME']."\n";
}
}
$idCity = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['cities'], mb_strtoupper($entry['PROPERTIES']['PARKING_CITY']['VALUE']));
if($idCity !== null)
{
//* id города, в котором находится авто (см. файл-справочник)
$payload['idCity'] = $idCity;
}
else
{
if(!$special)
{
//* название города, в котором находится авто (см. файл-справочник)
$payload['sCity'] = $entry['PROPERTIES']['PARKING_CITY']['VALUE'];
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ ГОРОД: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
//if(is_empty($idModel)) { $this->errors .= "Не найдена модель ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." для ".".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."."\n"; continue; }
}
//"idCountry" => "", //id страны, где находится авто (см. файл-справочник, по умолчанию — 0)
//"idCity" => "", //id города, в котором находится авто (см. файл-справочник)
//"sCity" => "", //название города, в котором находится авто (см. файл-справочник)
if($special)
{
$this->valid_special++;
}
else
{
$this->valid_cars++;
}
$offer = $special ? $this->offers_special->addChild('SpecOffer') : $this->offers_cars->addChild('Offer');
foreach($payload AS $k => $v)
{
$offer->addChild($k, $v);
}
$offer->addChild("Additional", "Артикул: ".$entry['CODE']."\n".htmlspecialchars(str_replace([" ", "
", "
"], [" ", "", ""], strip_tags($entry['DETAIL_TEXT']))));
if(count($entry['PHOTOS_1080']) > 0)
{
$photos = $offer->addChild("Photos");
$photos->addAttribute('PhotoMain', "https://".SITE_SERVER_NAME.$entry['PHOTOS_1080'][0]);
foreach($entry['PHOTOS_1080'] AS $photo)
{
$photos->addChild("Photo", "https://".SITE_SERVER_NAME.$photo);
}
}
//print $type." MATCH"."\n";
}
private function normalize_string($str)
{
return str_replace(["Ё", "C"], ["Е", "С"], $str);
}
private function getFrameType($type, $model)
{
if($type === "ПРИЦЕП") { $type = "ДРУГИЕ ПРИЦЕПЫ"; }
if($type === "ТЯГАЧ") { $type = "СЕДЕЛЬНЫЙ ТЯГАЧ"; }
if(isset($this->special_frames[ $type ]))
{
return $this->special_frames[ $type ];
}
else
{
$found = [];
foreach($this->special_frames AS $k => $v)
{
if(strpos($this->normalize_string($model), $k) > -1)
{
$found[ strlen($k) ] = $this->special_frames[ $k ];
}
}
if(count($found) > 0)
{
krsort($found);
// print_r($found);
foreach($found AS $f)
{
return $f;
}
// return $found[0];
}
}
//print "[".$this->normalize_string(mb_strtoupper($k))."] <---\n";
return null;
}
private function getDictionaryRecordId($branch, $name)
{
if(isset($branch[ $name ]))
{
return $branch[ $name ];
}
return null;
}
private function findInDictionaryRecordId($branch, $name)
{
foreach($branch AS $k => $v)
{
if(strpos($name, $k) > -1)
{
return $v;
}
}
return null;
}
function save()
{
if($this->total > 0)
{
print "ИТОГО: валидно ".$this->valid_cars + $this->valid_special." из ".$this->total." - ".number_format(($this->valid_cars + $this->valid_special) / (($this->found_cars + $this->found_special) / 100), 2, ".")."%\n";
if($this->found_cars > 0)
{
print "ЛКТ: валидно ".$this->valid_cars." из ".$this->found_cars." - ".number_format($this->valid_cars / ($this->found_cars / 100), 2, ".")."%\n";
}
if($this->found_special > 0)
{
print "СПЕЦТЕХНИКА: валидно ".$this->valid_special." из ".$this->found_special." - ".number_format($this->valid_special / ($this->found_special / 100), 2, ".")."%\n";
}
file_put_contents("/home/bitrix/www/drom-offers.xml", $this->xml_cars->asXML());
file_put_contents("/home/bitrix/www/drom-offers-special.xml", $this->xml_special->asXML());
}
}
private function loadTypes()
{
$types_url = "https://www.drom.ru/cached_files/autoload/files/ref_frame_types.xml";
$response_types_xml = file_get_contents($types_url);
file_put_contents("/home/bitrix/www/local/ref_frame_types.xml", $response_types_xml);
$types_object = new SimpleXMLElement($response_types_xml);
foreach($types_object AS $group => $branch)
{
foreach($branch AS $spec)
{
$g = (string) $group;
$k = (string) $spec->sFrameTypeSpec;
$n = (int) $spec->idFrameTypeSpec;
//print $g." - ".$k."\n";
$this->special_frames[ mb_strtoupper($k) ] = [
"group" => $this->special_frames_mapping[ $g ],
"id" => $n,
];
}
}
}
}