diff options
Diffstat (limited to '')
-rw-r--r-- | vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php b/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php new file mode 100644 index 0000000..0fdef0b --- /dev/null +++ b/vendor/simshaun/recurr/src/Recurr/Transformer/TextTransformer.php @@ -0,0 +1,502 @@ +<?php + +namespace Recurr\Transformer; + +use Recurr\Rule; + +class TextTransformer +{ + protected $fragments = array(); + protected $translator; + + public function __construct(TranslatorInterface $translator = null) + { + $this->translator = $translator ?: new Translator('en'); + } + + public function transform(Rule $rule) + { + $this->fragments = array(); + + switch ($rule->getFreq()) { + case 0: + $this->addYearly($rule); + break; + case 1: + $this->addMonthly($rule); + break; + case 2: + $this->addWeekly($rule); + break; + case 3: + $this->addDaily($rule); + break; + case 4: + $this->addHourly($rule); + break; + case 5: + case 6: + return $this->translator->trans('Unable to fully convert this rrule to text.'); + } + + $until = $rule->getUntil(); + $count = $rule->getCount(); + if ($until instanceof \DateTimeInterface) { + $dateFormatted = $this->translator->trans('day_date', array('date' => $until->format('U'))); + $this->addFragment($this->translator->trans('until %date%', array('date' => $dateFormatted))); + } else if (!empty($count)) { + if ($this->isPlural($count)) { + $this->addFragment($this->translator->trans('for %count% times', array('count' => $count))); + } else { + $this->addFragment($this->translator->trans('for one time')); + } + } + + if (!$this->isFullyConvertible($rule)) { + $this->addFragment($this->translator->trans('(~ approximate)')); + } + + return implode(' ', $this->fragments); + } + + protected function isFullyConvertible(Rule $rule) + { + if ($rule->getFreq() >= 5) { + return false; + } + + $until = $rule->getUntil(); + $count = $rule->getCount(); + if (!empty($until) && !empty($count)) { + return false; + } + + $bySecond = $rule->getBySecond(); + $byMinute = $rule->getByMinute(); + $byHour = $rule->getByHour(); + + if (!empty($bySecond) || !empty($byMinute) || !empty($byHour)) { + return false; + } + + $byWeekNum = $rule->getByWeekNumber(); + $byYearDay = $rule->getByYearDay(); + if ($rule->getFreq() != 0 && (!empty($byWeekNum) || !empty($byYearDay))) { + return false; + } + + return true; + } + + protected function addYearly(Rule $rule) + { + $interval = $rule->getInterval(); + $byMonth = $rule->getByMonth(); + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + $byYearDay = $rule->getByYearDay(); + $byWeekNum = $rule->getByWeekNumber(); + + if (!empty($byMonth) && count($byMonth) > 1 && $interval == 1) { + $this->addFragment($this->translator->trans('every_month_list')); + } else { + $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% years' : 'every year', array('count' => $interval))); + } + + $hasNoOrOneByMonth = is_null($byMonth) || count($byMonth) <= 1; + if ($hasNoOrOneByMonth && empty($byMonthDay) && empty($byDay) && empty($byYearDay) && empty($byWeekNum)) { + $this->addFragment($this->translator->trans('on')); + $monthNum = (is_array($byMonth) && count($byMonth)) ? $byMonth[0] : $rule->getStartDate()->format('n'); + $this->addFragment( + $this->translator->trans('day_month', array('month' => $monthNum, 'day' => $rule->getStartDate()->format('d'))) + ); + } elseif (!empty($byMonth)) { + if ($interval != 1) { + $this->addFragment($this->translator->trans('in_month')); + } + + $this->addByMonth($rule); + } + + if (!empty($byMonthDay)) { + $this->addByMonthDay($rule); + $this->addFragment($this->translator->trans('of_the_month')); + } else if (!empty($byDay)) { + $this->addByDay($rule); + } + + if (!empty($byYearDay)) { + $this->addFragment($this->translator->trans('on the')); + $this->addFragment($this->getByYearDayAsText($byYearDay)); + $this->addFragment($this->translator->trans('day')); + } + + if (!empty($byWeekNum)) { + $this->addFragment($this->translator->trans('in_week')); + $this->addFragment($this->translator->trans($this->isPlural(count($byWeekNum)) ? 'weeks' : 'week')); + $this->addFragment($this->getByWeekNumberAsText($byWeekNum)); + } + + if (empty($byMonthDay) && empty($byYearDay) && empty($byDay) && !empty($byWeekNum)) { + $this->addDayOfWeek($rule); + } + } + + protected function addMonthly(Rule $rule) + { + $interval = $rule->getInterval(); + $byMonth = $rule->getByMonth(); + + if (!empty($byMonth) && $interval == 1) { + $this->addFragment($this->translator->trans('every_month_list')); + } else { + $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% months' : 'every month', array('count' => $interval))); + } + + if (!empty($byMonth)) { + if ($interval != 1) { + $this->addFragment($this->translator->trans('in_month')); + } + + $this->addByMonth($rule); + } + + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + if (!empty($byMonthDay)) { + $this->addByMonthDay($rule); + } else if (!empty($byDay)) { + $this->addByDay($rule); + } + } + + protected function addWeekly(Rule $rule) + { + $interval = $rule->getInterval(); + $byMonth = $rule->getByMonth(); + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + + $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% weeks' : 'every week', array('count' => $interval))); + + if (empty($byMonthDay) && empty($byDay)) { + $this->addDayOfWeek($rule); + } + + if (!empty($byMonth)) { + $this->addFragment($this->translator->trans('in_month')); + $this->addByMonth($rule); + } + + if (!empty($byMonthDay)) { + $this->addByMonthDay($rule); + $this->addFragment($this->translator->trans('of_the_month')); + } else if (!empty($byDay)) { + $this->addByDay($rule); + } + } + + protected function addDaily(Rule $rule) + { + $interval = $rule->getInterval(); + $byMonth = $rule->getByMonth(); + + $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% days' : 'every day', array('count' => $interval))); + + if (!empty($byMonth)) { + $this->addFragment($this->translator->trans('in_month')); + $this->addByMonth($rule); + } + + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + if (!empty($byMonthDay)) { + $this->addByMonthDay($rule); + $this->addFragment($this->translator->trans('of_the_month')); + } else if (!empty($byDay)) { + $this->addByDay($rule); + } + } + + protected function addHourly(Rule $rule) + { + $interval = $rule->getInterval(); + $byMonth = $rule->getByMonth(); + + $this->addFragment($this->translator->trans($this->isPlural($interval) ? 'every %count% hours' : 'every hour', array('count' => $interval))); + + if (!empty($byMonth)) { + $this->addFragment($this->translator->trans('in_month')); + $this->addByMonth($rule); + } + + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + if (!empty($byMonthDay)) { + $this->addByMonthDay($rule); + $this->addFragment($this->translator->trans('of_the_month')); + } else if (!empty($byDay)) { + $this->addByDay($rule); + } + } + + protected function addByMonth(Rule $rule) + { + $byMonth = $rule->getByMonth(); + + if (empty($byMonth)) { + return; + } + + $this->addFragment($this->getByMonthAsText($byMonth)); + } + + protected function addByMonthDay(Rule $rule) + { + $byMonthDay = $rule->getByMonthDay(); + $byDay = $rule->getByDay(); + + if (!empty($byDay)) { + $this->addFragment($this->translator->trans('on')); + $this->addFragment($this->getByDayAsText($byDay, 'or')); + $this->addFragment($this->translator->trans('the_for_monthday')); + $this->addFragment($this->getByMonthDayAsText($byMonthDay, 'or')); + } else { + $this->addFragment($this->translator->trans('on the')); + $this->addFragment($this->getByMonthDayAsText($byMonthDay, 'and')); + } + } + + protected function addByDay(Rule $rule) + { + $byDay = $rule->getByDay(); + + $this->addFragment($this->translator->trans('on')); + $this->addFragment($this->getByDayAsText($byDay)); + } + + protected function addDayOfWeek(Rule $rule) + { + $this->addFragment($this->translator->trans('on')); + $dayNames = $this->translator->trans('day_names'); + $this->addFragment($dayNames[$rule->getStartDate()->format('w')]); + } + + public function getByMonthAsText($byMonth) + { + if (empty($byMonth)) { + return ''; + } + + if (count($byMonth) > 1) { + sort($byMonth); + } + + $monthNames = $this->translator->trans('month_names'); + $byMonth = array_map( + function ($monthInt) use ($monthNames) { + return $monthNames[$monthInt - 1]; + }, + $byMonth + ); + + return $this->getListStringFromArray($byMonth); + } + + public function getByDayAsText($byDay, $listSeparator = 'and') + { + if (empty($byDay)) { + return ''; + } + + $map = array( + 'SU' => null, + 'MO' => null, + 'TU' => null, + 'WE' => null, + 'TH' => null, + 'FR' => null, + 'SA' => null + ); + + $dayNames = $this->translator->trans('day_names'); + $timestamp = mktime(1, 1, 1, 1, 12, 2014); // A Sunday + foreach (array_keys($map) as $short) { + $long = $dayNames[date('w', $timestamp)]; + $map[$short] = $long; + $timestamp += 86400; + } + + $numOrdinals = 0; + foreach ($byDay as $key => $short) { + $day = strtoupper($short); + $string = ''; + + if (preg_match('/([+-]?)([0-9]*)([A-Z]+)/', $short, $parts)) { + $symbol = $parts[1]; + $nth = $parts[2]; + $day = $parts[3]; + + if (!empty($nth)) { + ++$numOrdinals; + $string .= $this->getOrdinalNumber($symbol == '-' ? -$nth : $nth); + } + } + + if (!isset($map[$day])) { + throw new \RuntimeException("byDay $short could not be transformed"); + } + + if (!empty($string)) { + $string .= ' '; + } + + $byDay[$key] = ltrim($string.$map[$day]); + } + + $output = $numOrdinals ? $this->translator->trans('the_for_weekday') . ' ' : ''; + if ($output == ' ') { + $output = ''; + } + $output .= $this->getListStringFromArray($byDay, $listSeparator); + + return $output; + } + + public function getByMonthDayAsText($byMonthDay, $listSeparator = 'and') + { + if (empty($byMonthDay)) { + return ''; + } + + // sort negative indices in reverse order so we get e.g. 1st, 2nd, 4th, 3rd last, last day + usort($byMonthDay, function ($a, $b) { + if (($a < 0 && $b < 0) || ($a >= 0 && $b >= 0)) { + return $a - $b; + } + + return $b - $a; + }); + + // generate ordinal numbers and insert a "on the" for clarity in the middle if we have both + // positive and negative ordinals. This is to avoid confusing situations like: + // + // monthly on the 1st and 2nd to the last day + // + // which gets clarified to: + // + // monthly on the 1st day and on the 2nd to the last day + $hadPositives = false; + $hadNegatives = false; + foreach ($byMonthDay as $index => $day) { + $prefix = ''; + if ($day >= 0) { + $hadPositives = true; + } + if ($day < 0) { + if ($hadPositives && !$hadNegatives && $listSeparator === 'and') { + $prefix = $this->translator->trans('on the') . ' '; + } + $hadNegatives = true; + } + $byMonthDay[$index] = $prefix . $this->getOrdinalNumber($day, end($byMonthDay) < 0, true); + } + + return $this->getListStringFromArray($byMonthDay, $listSeparator); + } + + public function getByYearDayAsText($byYearDay) + { + if (empty($byYearDay)) { + return ''; + } + + // sort negative indices in reverse order so we get e.g. 1st, 2nd, 4th, 3rd last, last day + usort($byYearDay, function ($a, $b) { + if (($a < 0 && $b < 0) || ($a >= 0 && $b >= 0)) { + return $a - $b; + } + + return $b - $a; + }); + + $byYearDay = array_map( + array($this, 'getOrdinalNumber'), + $byYearDay, + array_fill(0, count($byYearDay), end($byYearDay) < 0) + ); + + return $this->getListStringFromArray($byYearDay); + } + + public function getByWeekNumberAsText($byWeekNum) + { + if (empty($byWeekNum)) { + return ''; + } + + if (count($byWeekNum) > 1) { + sort($byWeekNum); + } + + return $this->getListStringFromArray($byWeekNum); + } + + protected function addFragment($fragment) + { + if ($fragment && $fragment !== ' ') { + $this->fragments[] = $fragment; + } + } + + public function resetFragments() + { + $this->fragments = array(); + } + + protected function isPlural($number) + { + return $number % 100 != 1; + } + + + protected function getOrdinalNumber($number, $hasNegatives = false, $dayInMonth = false) + { + if (!preg_match('{^-?\d+$}D', $number)) { + throw new \RuntimeException('$number must be a whole number'); + } + + return $this->translator->trans('ordinal_number', array('number' => $number, 'has_negatives' => $hasNegatives, 'day_in_month' => $dayInMonth)); + } + + protected function getListStringFromArray($values, $separator = 'and') + { + $separator = $this->translator->trans($separator); + + if (!is_array($values)) { + throw new \RuntimeException('$values must be an array.'); + } + + $numValues = count($values); + + if (!$numValues) { + return ''; + } + + if ($numValues == 1) { + reset($values); + + return current($values); + } + + if ($numValues == 2) { + return implode(" $separator ", $values); + } + + $lastValue = array_pop($values); + $output = implode(', ', $values); + $output .= " $separator ".$lastValue; + + return $output; + } +} |