Velmi často se s intervalem pracuje tak, že se prostě předají dvě hodnoty: dva parametry do funkce, dvě proměnné, nebo prostě "pole" o dvou položkách.
Tento přístup ale trpí dvěma neduhy:
Řešení je snadné: interval vždy reprezentovat jako value object.
composer install achse/php-math-interval
Základní myšlenkou knihovny je poskytnout kompletní matematickou reprezentaci intervalu. Umí tedy:
Knihovna přichází s těmito předpřipravenými typy:
DateTimeImmutable
a DateTime
,SingleDay
(reprezentuje interval času v kontextu jednoho dne, užitečný pro otevírací hodiny),Integer
(převážně pro ukázky a testy).Protože PHP nemá generiky (které by se v tomto místě náramně hodily), je nutné každý typ
naimplementovat zvlášť. Knihovna php-math-interval
, ale poskytuje setup, kde je přidání nového typu
hračka.
Architektura přináší abstraktní třídu intervalu
s kompletní implementací všech operací nehledě na typ, jaký obsahuje.
Jediné, co požaduje je to, aby byly porovnatelné (implementují rozhraní IComparable
).
Knihovna disponuje bohatým toolingem.
Parsování intervalu ze stringu je šikovné pro ukázky a testy. Mnohem častěji se ale využije factory nebo konstruktor.
Test zda je element součátní intervalu:
$interval = Parser::parse('[1, 2]');
$interval->isContainingElement(new Integer(2)); // true
$interval->isContainingElement(new Integer(3)); // false
Průnik dvou intervalů:
// (1, 3) ∩ (2, 4) ⟺ (2, 3)
Parser::parse('(1, 3)')->getIntersection(Parser::parse('(2, 4)')); // (2, 3)
Sjednocení dvou intervalů
// Překrývající se intervaly
Parser::parse('[1, 3]')->getUnion(Parser::parse('[2, 4]')); // ['[1, 4]']
// Přesně navazující intervaly
Parser::parse('[1, 2)')->getUnion(Parser::parse('[3, 4]')); // ['[1, 4]']
// Sjednocení dvou vzdálených intervalů vrátí pole oněch dvou intervalů
Parser::parse('[1, 2]')->getUnion(Parser::parse('[3, 4]')); // ['[1, 2], [3, 4]']
Rozdíl dvou intervalů:
// [1, 4] \ [2, 3]
Parser::parse('[1, 4]')->getDifference(Parser::parse('[2, 3]')); // ['[1,2)', '(3, 4]']
Test zda jeden interval obsahuje druhý:
// [1, 4] contains [2, 3]
Parser::parse('[1, 4]')->isContaining(Parser::parse('[2, 3]')); // true
// [2, 3] NOT contains [1, 4]
Parser::parse('[2, 3]')->isContaining(Parser::parse('[1, 4]')); // false
Test zda na sebe intervaly zprava navazují:
Parser::parse('[1, 2]')->isOverlappedFromRightBy(Parser::parse('[2, 3]')); // true
Parser::parse('[2, 3]')->isOverlappedFromRightBy(Parser::parse('[1, 2]')); // false
// (1, 2) ~ [2, 3]
Parser::parse('(1, 2)')->isOverlappedFromRightBy(Parser::parse('[2, 3]')); // false
Test zda spolu intervaly kolidují (neprázdný průnik):
Parser::parse('[2, 3]')->isColliding(Parser::parse('[1, 2]')); // true
Parser::parse('[1, 2]')->isColliding(Parser::parse('(2, 3)')); // false
Pro DateTimeImmutable
, DateTime
a SingleDayTimeInterval
knihovna disponuje metodou pro zjištění návaznosti intervalů:
$first = Parser::parse('[2014-12-31 00:00:00, 2014-12-31 23:59:59)');
$second = Parser::parse('[2015-01-01 00:00:00, 2015-01-01 02:00:00)');
$first->isFollowedBy($second, IntervalUtils::PRECISION_ON_SECOND); // true
$second->isFollowedBy($first, IntervalUtils::PRECISION_ON_SECOND); // false
Za zmínku ještě stojí metoda isFollowedByAtMidnight
pro testování návaznosti intervalů mezi dny.
Máte nějaký dobrý postřeh nebo komentář k článku či knihovně?
Tweetněte mi o tom
nebo zanechte issue v githubu
.
Těším se na feedback.