processXmlHeader($xml), details: $this->processDetails( $xml->{self::XML_PORTFOLIO_SECTION}->{self::XML_PORTFOLIO_TABLE} ), movements: $this->processMovements( $xml->{self::XML_MOVEMENTS_SECTION} ), ); } private function processXmlHeader(\SimpleXMLElement $xml): PortfolioHeader { $attrs = []; foreach ([self::XML_HEADER_AGRMT, self::XML_HEADER_PERIOD] as $section) { foreach ($xml->{$section}->attributes() as $key => $value) { $attrs[$key] = get_object_vars($value)[0]; } } if (array_key_exists(self::ATTR_START_DATE, $attrs)) { $startDate = \DateTimeImmutable::createFromFormat( self::DATE_FORMAT, $attrs[self::ATTR_START_DATE], ); $endDate = \DateTimeImmutable::createFromFormat( self::DATE_FORMAT, $attrs[self::ATTR_END_DATE], ); } elseif (array_key_exists(self::ATTR_PERIOD_LINE, $attrs)) { $chunks = explode(' ', $attrs[self::ATTR_PERIOD_LINE]); if (count($chunks) !== 4) { throw new \RuntimeException('Unexpected period format'); } $startDate = \DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $chunks[1]); $endDate = \DateTimeImmutable::createFromFormat(self::DATE_FORMAT, $chunks[3]); } else { throw new \RuntimeException('Cannot detect XML header version'); } if (!$startDate || !$endDate) { throw new \RuntimeException('Invalid date format in XML header'); } return new PortfolioHeader( clientAgreement: $attrs[self::ATTR_CLIENT], startDate: $startDate, endDate: $endDate, ); } private function processDetails(\SimpleXMLElement $xml): array { $items = []; foreach (get_object_vars($xml) as $key => $value) { if (mb_strpos($key, self::PREFIX_DETAILS) === false) { continue; } if (!is_array($value)) { $value = [$value]; } foreach ($value as $line) { $attr = []; foreach ($line->attributes() as $attrName => $attrValue) { $attr[$attrName] = get_object_vars($attrValue)[0]; } $items[] = new PortfolioDetailItem( issuer: $attr[self::ATTR_ISSUER], security: $attr[self::ATTR_SECURITY], priceStart: $attr[self::ATTR_PRICE_START], priceEnd: $attr[self::ATTR_PRICE_END], quantityStart: (int) $attr[self::ATTR_QTY_START], quantityEnd: (int) $attr[self::ATTR_QTY_END], sumStart: $attr[self::ATTR_SUM_START], sumEnd: $attr[self::ATTR_SUM_END], sumTotal: $attr[self::ATTR_SUM_TOTAL], ); } } return $items; } private function processMovements(\SimpleXMLElement $xml): array { $items = []; foreach (get_object_vars($xml) as $key => $value) { if (mb_strpos($key, self::PREFIX_DETAILS) === false) { continue; } if (!is_array($value)) { $value = [$value]; } foreach ($value as $line) { $attr = []; foreach ($line->attributes() as $attrName => $attrValue) { $attr[$attrName] = get_object_vars($attrValue)[0]; } $items[] = new PortfolioMovementItem( security: $attr[self::ATTR_SECURITY], period: new \DateTimeImmutable($attr[self::ATTR_PERIOD]), quantityStart: (int) $attr[self::ATTR_QTY_START_MV], quantityEnd: (int) $attr[self::ATTR_QTY_END_MV], quantityIncome: (int) $attr[self::ATTR_QTY_INCOME], quantityOutcome:(int) $attr[self::ATTR_QTY_OUTCOME], ); } } return $items; } }