autowp/autowp

View on GitHub
module/Application/src/ItemNameFormatter.php

Summary

Maintainability
D
2 days
Test Coverage
A
96%
<?php

namespace Application;

use ArrayAccess;
use Exception;
use Laminas\I18n\Translator\TranslatorInterface;
use Laminas\View\Renderer\PhpRenderer;

use function array_merge;
use function array_replace;
use function date;
use function implode;
use function is_array;
use function sprintf;
use function strlen;

class ItemNameFormatter
{
    private TranslatorInterface $translator;

    private PhpRenderer $renderer;

    private string $monthFormat = '<small class="month">%02d.</small>';

    private string $textMonthFormat = '%02d.';

    public function __construct(TranslatorInterface $translator, PhpRenderer $renderer)
    {
        $this->translator = $translator;
        $this->renderer   = $renderer;
    }

    private function translate(string $string, string $language): string
    {
        return $this->translator->translate($string, 'default', $language);
    }

    /**
     * @throws Exception
     */
    public function formatHtml(iterable $item, string $language): string
    {
        if (! $item instanceof ArrayAccess && ! is_array($item)) {
            throw new Exception("`item` must be array or ArrayAccess");
        }

        if ($item instanceof ArrayAccess) {
            $item = (array) $item;
        }

        $defaults = [
            'begin_model_year'          => null,
            'end_model_year'            => null,
            'begin_model_year_fraction' => null,
            'end_model_year_fraction'   => null,
            'spec'                      => null,
            'spec_full'                 => null,
            'body'                      => null,
            'name'                      => null,
            'begin_year'                => null,
            'end_year'                  => null,
            'today'                     => null,
            'begin_month'               => null,
            'end_month'                 => null,
        ];
        $item     = array_replace($defaults, $item);

        $result = $this->renderer->escapeHtml($item['name']);

        if ($item['spec']) {
            $attrs = ['class="badge bg-info text-dark"'];
            if ($item['spec_full']) {
                $attrs = array_merge($attrs, [
                    'title="' . $this->renderer->escapeHtmlAttr($item['spec_full']) . '"',
                    'data-toggle="tooltip"',
                    'data-placement="top"',
                ]);
            }

            $escapedSpec = $this->renderer->escapeHtml($item['spec']);
            $result     .= ' <span ' . implode(' ', $attrs) . '>' . $escapedSpec . '</span>';
        }

        if (strlen($item['body']) > 0) {
            $result .= ' (' . $this->renderer->escapeHtml($item['body']) . ')';
        }

        $by = (int) $item['begin_year'];
        $bm = (int) $item['begin_month'];
        $ey = (int) $item['end_year'];
        $em = (int) $item['end_month'];

        $bmy = (int) $item['begin_model_year'];
        $emy = (int) $item['end_model_year'];

        $bmyf = $item['begin_model_year_fraction'];
        $emyf = $item['end_model_year_fraction'];

        $bs = (int) ($by / 100);
        $es = (int) ($ey / 100);

        $useModelYear = $bmy || $emy;

        $equalS = $bs && $es && ($bs === $es);
        $equalY = $equalS && $by && $ey && ($by === $ey);
        $equalM = $equalY && $bm && $em && ($bm === $em);

        if ($useModelYear) {
            $title  = $this->renderer->escapeHtmlAttr($this->translate('carlist/model-years', $language));
            $result = '<span title="' . $title . '">'
                          . $this->renderer->escapeHtml( // @phan-suppress-current-line PhanUndeclaredMethod
                              $this->getModelYearsPrefix($bmy, $bmyf, $emy, $emyf, $item['today'], $language)
                          )
                      . '</span> '
                      . $result;

            if ($by > 0 || $ey > 0) {
                $title   = $this->renderer->escapeHtmlAttr($this->translate('carlist/years', $language));
                $result .=
                    '<small>'
                        . ' \'<span class="realyears" title="' . $title . '">'
                            . $this->renderYearsHtml(
                                $item['today'],
                                $by,
                                $bm,
                                $ey,
                                $em,
                                $equalS,
                                $equalY,
                                $equalM,
                                $language
                            )
                        . '</span>'
                    . '</small>';
            }
        } else {
            if ($by > 0 || $ey > 0) {
                $result .= " '" . $this->renderYearsHtml(
                    $item['today'],
                    $by,
                    $bm,
                    $ey,
                    $em,
                    $equalS,
                    $equalY,
                    $equalM,
                    $language
                );
            }
        }

        return $result;
    }

    /**
     * @param array|ArrayAccess $input
     * @throws Exception
     */
    public function format($input, string $language): string
    {
        $item = (array) $input;

        $defaults = [
            'begin_model_year'          => null,
            'end_model_year'            => null,
            'begin_model_year_fraction' => null,
            'end_model_year_fraction'   => null,
            'spec'                      => null,
            'spec_full'                 => null,
            'body'                      => null,
            'name'                      => null,
            'begin_year'                => null,
            'end_year'                  => null,
            'today'                     => null,
            'begin_month'               => null,
            'end_month'                 => null,
        ];
        $item     = array_replace($defaults, $item);

        $result = $item['name'];

        if ($item['spec']) {
            $result .= ' [' . $item['spec'] . ']';
        }

        if (strlen($item['body']) > 0) {
            $result .= ' (' . $item['body'] . ')';
        }

        $by = (int) $item['begin_year'];
        $bm = (int) $item['begin_month'];
        $ey = (int) $item['end_year'];
        $em = (int) $item['end_month'];

        $bmy = (int) $item['begin_model_year'];
        $emy = (int) $item['end_model_year'];

        $bmyf = $item['begin_model_year_fraction'];
        $emyf = $item['end_model_year_fraction'];

        $bs = (int) ($by / 100);
        $es = (int) ($ey / 100);

        $useModelYear = $bmy || $emy;

        $equalS = $bs && $es && ($bs === $es);
        $equalY = $equalS && $by && $ey && ($by === $ey);
        $equalM = $equalY && $bm && $em && ($bm === $em);

        if ($useModelYear) {
            $result = $this->getModelYearsPrefix($bmy, $bmyf, $emy, $emyf, $item['today'], $language) . ' ' . $result;
        }

        if ($by > 0 || $ey > 0) {
            $result .= " '" . $this->renderYears(
                $item['today'],
                $by,
                $bm,
                $ey,
                $em,
                $equalS,
                $equalY,
                $equalM,
                $language
            );
        }

        return $result;
    }

    private function getModelYearsPrefix(
        ?int $begin,
        ?string $beginFraction,
        ?int $end,
        ?string $endFraction,
        ?bool $today,
        string $language
    ): string {
        $bms = (int) ($begin / 100);
        $ems = (int) ($end / 100);

        if ($end === $begin && $beginFraction === $endFraction) {
            return $begin . $endFraction;
        }

        if ($bms === $ems) {
            return $begin . $beginFraction . '–' . sprintf('%02d', $end % 100) . $endFraction;
        }

        if (! $begin) {
            return '????–' . $end . $endFraction;
        }

        if ($end) {
            return $begin . $beginFraction . '–' . $end . $endFraction;
        }

        if (! $today) {
            return $begin . $beginFraction . '–??';
        }

        $currentYear = (int) date('Y');

        if ($begin >= $currentYear) {
            return $begin . $beginFraction;
        }

        return $begin . $beginFraction . '–' . $this->translate('present-time-abbr', $language);
    }

    private function monthsRange(int $from, int $to): string
    {
        return ($from ? sprintf('%02d', $from) : '??')
               . '–'
               . ($to ? sprintf('%02d', $to) : '??');
    }

    private function missedEndYearYearsSuffix(?bool $today, int $by, string $language): string
    {
        $cy = (int) date('Y');

        if ($by >= $cy) {
            return '';
        }

        return '–' . ($today ? $this->translate('present-time-abbr', $language) : '????');
    }

    private function renderYears(
        ?bool $today,
        int $by,
        int $bm,
        int $ey,
        int $em,
        bool $equalS,
        bool $equalY,
        bool $equalM,
        string $language
    ): string {
        if ($equalM) {
            return sprintf($this->textMonthFormat, $bm) . $by;
        }

        if ($equalY) {
            if ($bm && $em) {
                return $this->monthsRange($bm, $em) . '.' . $by;
            }

            return (string) $by;
        }

        if ($equalS) {
            return (($bm ? sprintf($this->textMonthFormat, $bm) : '') . $by)
                   . '–'
                   . ($em ? sprintf($this->textMonthFormat, $em) : '') . ($em ? $ey : sprintf('%02d', $ey % 100));
        }

        return (($bm ? sprintf($this->textMonthFormat, $bm) : '') . ($by ?: '????'))
                . (
                    $ey
                        ? '–' . ($em ? sprintf($this->textMonthFormat, $em) : '') . $ey
                        : $this->missedEndYearYearsSuffix($today, $by, $language)
                );
    }

    private function renderYearsHtml(
        ?bool $today,
        int $by,
        int $bm,
        int $ey,
        int $em,
        bool $equalS,
        bool $equalY,
        bool $equalM,
        string $language
    ): string {
        if ($equalM) {
            return sprintf($this->monthFormat, $bm) . $by;
        }

        if ($equalY) {
            if ($bm && $em) {
                return '<small class="month">' . $this->monthsRange($bm, $em) . '.</small>' . $by;
            }

            return (string) $by;
        }

        if ($equalS) {
            return (($bm ? sprintf($this->monthFormat, $bm) : '') . $by)
                   . '–'
                   . ($em ? sprintf($this->monthFormat, $em) : '') . ($em ? $ey : sprintf('%02d', $ey % 100));
        }

        return (($bm ? sprintf($this->monthFormat, $bm) : '') . ($by ?: '????'))
                . (
                    $ey
                        ? '–' . ($em ? sprintf($this->monthFormat, $em) : '') . $ey
                        : $this->renderer->escapeHtml( // @phan-suppress-currenet-line PhanUndeclaredMethod
                            $this->missedEndYearYearsSuffix($today, $by, $language)
                        )
                );
    }
}