From 49e7f7e4f61add2693b87b05c51e95d807c5f2bc Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 21 Jan 2017 01:05:07 +0100 Subject: [PATCH 1/6] DateTime::createFromFormat() returns NULL instead of FALSE on error (BC break) --- src/Utils/DateTime.php | 4 ++-- tests/Utils/DateTime.createFromFormat.phpt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index 60f102a3e..345dce490 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -117,7 +117,7 @@ public function getTimestamp() * @param string The format the $time parameter should be in * @param string String representing the time * @param string|\DateTimeZone desired timezone (default timezone is used if NULL is passed) - * @return static|FALSE + * @return static|NULL */ public static function createFromFormat($format, $time, $timezone = NULL) { @@ -132,7 +132,7 @@ public static function createFromFormat($format, $time, $timezone = NULL) } $date = parent::createFromFormat($format, $time, $timezone); - return $date ? static::from($date) : FALSE; + return $date ? static::from($date) : NULL; } diff --git a/tests/Utils/DateTime.createFromFormat.phpt b/tests/Utils/DateTime.createFromFormat.phpt index 2ae02822f..55d650e40 100644 --- a/tests/Utils/DateTime.createFromFormat.phpt +++ b/tests/Utils/DateTime.createFromFormat.phpt @@ -26,4 +26,4 @@ Assert::error(function () { DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00', 5); }, Nette\InvalidArgumentException::class, 'Invalid timezone given'); -Assert::false(DateTime::createFromFormat('Y-m-d', '2014-10')); +Assert::null(DateTime::createFromFormat('Y-m-d', '2014-10')); From e820ea02ebe30604344f31d127dcb41077e4f777 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Sat, 21 Jan 2017 01:07:09 +0100 Subject: [PATCH 2/6] Strings::before(), after(), indexOf() and pos() return NULL instead of FALSE if the needle was not found (BC break) --- src/Utils/Strings.php | 24 ++++++++++++------------ tests/Utils/Strings.after().phpt | 8 ++++---- tests/Utils/Strings.before().phpt | 8 ++++---- tests/Utils/Strings.indexOf().phpt | 8 ++++---- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index c25919a79..d66f66f38 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -353,51 +353,51 @@ public static function reverse(string $s): string /** * Returns part of $haystack before $nth occurence of $needle (negative value means searching from the end). - * @return string|FALSE returns FALSE if the needle was not found + * @return string|NULL returns NULL if the needle was not found */ public static function before(string $haystack, string $needle, int $nth = 1) { $pos = self::pos($haystack, $needle, $nth); - return $pos === FALSE - ? FALSE + return $pos === NULL + ? NULL : substr($haystack, 0, $pos); } /** * Returns part of $haystack after $nth occurence of $needle (negative value means searching from the end). - * @return string|FALSE returns FALSE if the needle was not found + * @return string|NULL returns NULL if the needle was not found */ public static function after(string $haystack, string $needle, int $nth = 1) { $pos = self::pos($haystack, $needle, $nth); - return $pos === FALSE - ? FALSE + return $pos === NULL + ? NULL : substr($haystack, $pos + strlen($needle)); } /** * Returns position of $nth occurence of $needle in $haystack (negative value means searching from the end). - * @return int|FALSE offset in characters or FALSE if the needle was not found + * @return int|NULL offset in characters or NULL if the needle was not found */ public static function indexOf(string $haystack, string $needle, int $nth = 1) { $pos = self::pos($haystack, $needle, $nth); - return $pos === FALSE - ? FALSE + return $pos === NULL + ? NULL : self::length(substr($haystack, 0, $pos)); } /** * Returns position of $nth occurence of $needle in $haystack. - * @return int|FALSE offset in bytes or FALSE if the needle was not found + * @return int|NULL offset in bytes or NULL if the needle was not found */ private static function pos(string $haystack, string $needle, int $nth = 1) { if (!$nth) { - return FALSE; + return NULL; } elseif ($nth > 0) { if (strlen($needle) === 0) { return 0; @@ -416,7 +416,7 @@ private static function pos(string $haystack, string $needle, int $nth = 1) $pos--; } } - return $pos; + return $pos === FALSE ? NULL : $pos; } diff --git a/tests/Utils/Strings.after().phpt b/tests/Utils/Strings.after().phpt index e333ed5ce..eae7621da 100644 --- a/tests/Utils/Strings.after().phpt +++ b/tests/Utils/Strings.after().phpt @@ -27,10 +27,10 @@ test(function () { Assert::same('c', Strings::after($foo, '789', 3)); Assert::same('a123456789b123456789c', Strings::after($foo, '9', -3)); Assert::same('a123456789b123456789c', Strings::after($foo, '789', -3)); - Assert::false(Strings::after($foo, '9', 0)); - Assert::false(Strings::after($foo, 'not-in-string')); - Assert::false(Strings::after($foo, 'b', -2)); - Assert::false(Strings::after($foo, 'b', 2)); + Assert::null(Strings::after($foo, '9', 0)); + Assert::null(Strings::after($foo, 'not-in-string')); + Assert::null(Strings::after($foo, 'b', -2)); + Assert::null(Strings::after($foo, 'b', 2)); }); diff --git a/tests/Utils/Strings.before().phpt b/tests/Utils/Strings.before().phpt index 1866dcfa2..68eff6cfc 100644 --- a/tests/Utils/Strings.before().phpt +++ b/tests/Utils/Strings.before().phpt @@ -26,10 +26,10 @@ test(function () { Assert::same('0123456789a123456789b123456', Strings::before($foo, '789', 3)); Assert::same('012345678', Strings::before($foo, '9', -3)); Assert::same('0123456', Strings::before($foo, '789', -3)); - Assert::false(Strings::before($foo, '9', 0)); - Assert::false(Strings::before($foo, 'not-in-string')); - Assert::false(Strings::before($foo, 'b', -2)); - Assert::false(Strings::before($foo, 'b', 2)); + Assert::null(Strings::before($foo, '9', 0)); + Assert::null(Strings::before($foo, 'not-in-string')); + Assert::null(Strings::before($foo, 'b', -2)); + Assert::null(Strings::before($foo, 'b', 2)); }); diff --git a/tests/Utils/Strings.indexOf().phpt b/tests/Utils/Strings.indexOf().phpt index eceae79d9..8738b0386 100644 --- a/tests/Utils/Strings.indexOf().phpt +++ b/tests/Utils/Strings.indexOf().phpt @@ -27,10 +27,10 @@ test(function () { Assert::same(27, Strings::indexOf($foo, '789', 3)); Assert::same(9, Strings::indexOf($foo, '9', -3)); Assert::same(7, Strings::indexOf($foo, '789', -3)); - Assert::false(Strings::indexOf($foo, '9', 0)); - Assert::false(Strings::indexOf($foo, 'not-in-string')); - Assert::false(Strings::indexOf($foo, 'b', -2)); - Assert::false(Strings::indexOf($foo, 'b', 2)); + Assert::null(Strings::indexOf($foo, '9', 0)); + Assert::null(Strings::indexOf($foo, 'not-in-string')); + Assert::null(Strings::indexOf($foo, 'b', -2)); + Assert::null(Strings::indexOf($foo, 'b', 2)); }); From 8a9b344247644271b7e915a61d85d5cad793d5c0 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Wed, 1 Feb 2017 15:16:47 +0100 Subject: [PATCH 3/6] typo --- tests/Utils/DateTime.createFromFormat.phpt | 6 +++--- tests/Utils/DateTime.from.phpt | 2 +- tests/Utils/DateTime.modifyClone.phpt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Utils/DateTime.createFromFormat.phpt b/tests/Utils/DateTime.createFromFormat.phpt index 55d650e40..9a2ddc845 100644 --- a/tests/Utils/DateTime.createFromFormat.phpt +++ b/tests/Utils/DateTime.createFromFormat.phpt @@ -1,7 +1,7 @@ <?php /** - * Test: Nette\DateTime::createFromFormat(). + * Test: Nette\Utils\DateTime::createFromFormat(). */ declare(strict_types=1); @@ -14,8 +14,8 @@ require __DIR__ . '/../bootstrap.php'; date_default_timezone_set('Europe/Prague'); -Assert::type(Nette\Utils\DateTime::class, DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00')); -Assert::type(Nette\Utils\DateTime::class, DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00', new DateTimeZone('Europe/Prague'))); +Assert::type(DateTime::class, DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00')); +Assert::type(DateTime::class, DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00', new DateTimeZone('Europe/Prague'))); Assert::same('2050-08-13 11:40:00.123450', DateTime::createFromFormat('Y-m-d H:i:s.u', '2050-08-13 11:40:00.12345')->format('Y-m-d H:i:s.u')); diff --git a/tests/Utils/DateTime.from.phpt b/tests/Utils/DateTime.from.phpt index ff8f31b19..4dbf1ff83 100644 --- a/tests/Utils/DateTime.from.phpt +++ b/tests/Utils/DateTime.from.phpt @@ -24,6 +24,6 @@ Assert::same(is_int(2544000000) ? 2544000000 : '2544000000', DateTime::from(2544 Assert::same('1978-05-05 00:00:00', (string) DateTime::from('1978-05-05')); -Assert::type('DateTime', DateTime::from(new DateTime('1978-05-05'))); +Assert::type(DateTime::class, DateTime::from(new \DateTime('1978-05-05'))); Assert::same('1978-05-05 12:00:00.123450', DateTime::from(new DateTime('1978-05-05 12:00:00.12345'))->format('Y-m-d H:i:s.u')); diff --git a/tests/Utils/DateTime.modifyClone.phpt b/tests/Utils/DateTime.modifyClone.phpt index b3c3f7d07..7bdd14d80 100644 --- a/tests/Utils/DateTime.modifyClone.phpt +++ b/tests/Utils/DateTime.modifyClone.phpt @@ -1,7 +1,7 @@ <?php /** - * Test: Nette\DateTime::modifyClone(). + * Test: Nette\Utils\DateTime::modifyClone(). */ declare(strict_types=1); From 61c58f0d48d1057efe256497c7494196b3c29d73 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Wed, 1 Feb 2017 16:13:29 +0100 Subject: [PATCH 4/6] DateTime::from() deprecated relative timestamps --- src/Utils/DateTime.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index 345dce490..c6e6a4bea 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -28,13 +28,13 @@ class DateTime extends \DateTime implements \JsonSerializable /** day in seconds */ const DAY = 24 * self::HOUR; - /** week in seconds */ + /** @deprecated */ const WEEK = 7 * self::DAY; - /** average month in seconds */ + /** @deprecated */ const MONTH = 2629800; - /** average year in seconds */ + /** @deprecated */ const YEAR = 31557600; @@ -50,6 +50,7 @@ public static function from($time) } elseif (is_numeric($time)) { if ($time <= self::YEAR) { + trigger_error(__METHOD__ . '() and relative timestamp is deprecated.', E_USER_DEPRECATED); $time += time(); } return (new static('@' . $time))->setTimeZone(new \DateTimeZone(date_default_timezone_get())); From 9da5ed52315f33af58f08d2d24c9722f76f33374 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Wed, 1 Feb 2017 16:08:55 +0100 Subject: [PATCH 5/6] DateTime is immutable (BC break!) --- src/Utils/DateTime.php | 58 ++++++++++++++++++++++- tests/Utils/DateTime.immutable.check.phpt | 26 ++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/Utils/DateTime.immutable.check.phpt diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index c6e6a4bea..af41c52d2 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -15,7 +15,7 @@ /** * DateTime. */ -class DateTime extends \DateTime implements \JsonSerializable +class DateTime extends \DateTimeImmutable implements \JsonSerializable { use Nette\SmartObject; @@ -145,4 +145,60 @@ public function jsonSerialize(): string return $this->format('c'); } + + /********************* immutable usage detector ****************d*g**/ + + + public function __destruct() + { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); + if (isset($trace[0]['file'], $trace[1]['function']) && $trace[0]['file'] === __FILE__ && $trace[1]['function'] !== 'from') { + trigger_error(__CLASS__ . ' is immutable now, check how it is used in ' . $trace[1]['file'] . ':' . $trace[1]['line'], E_USER_WARNING); + } + } + + + public function add($interval) + { + return parent::add($interval); + } + + + public function modify($modify) + { + return parent::modify($modify); + } + + + public function setDate($year, $month, $day) + { + return parent::setDate($year, $month, $day); + } + + + public function setISODate($year, $week, $day = 1) + { + return parent::setISODate($year, $week, $day); + } + + + public function setTime($hour, $minute, $second = 0, $micro = 0) + { + return PHP_VERSION_ID < 70100 + ? parent::setTime($hour, $minute, $second) + : parent::setTime($hour, $minute, $second, $micro); + } + + + public function setTimezone($timezone) + { + return parent::setTimezone($timezone); + } + + + public function sub($interval) + { + return parent::sub($interval); + } + } diff --git a/tests/Utils/DateTime.immutable.check.phpt b/tests/Utils/DateTime.immutable.check.phpt new file mode 100644 index 000000000..a56d3fa9b --- /dev/null +++ b/tests/Utils/DateTime.immutable.check.phpt @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +use Tester\Assert; +use Nette\Utils\DateTime; + +require __DIR__ . '/../bootstrap.php'; + +Assert::noError(function () { + $x = DateTime::from(254400000); +}); + +Assert::noError(function () { + $x = DateTime::from(254400000); + $x = $x->setTimestamp(254400000); +}); + +Assert::noError(function () { + $x = DateTime::from(254400000)->format('U'); +}); + +Assert::error(function () { + $x = DateTime::from(254400000); + $x->setTimestamp(254400000); +}, E_USER_WARNING, 'Nette\Utils\DateTime is immutable now, check how it is used in ' . __FILE__ . ':' . (__LINE__ - 1)); From 207ea60a6d3df8eb8d674f5eaac7c833518c5270 Mon Sep 17 00:00:00 2001 From: David Grudl <david@grudl.com> Date: Wed, 1 Feb 2017 16:10:17 +0100 Subject: [PATCH 6/6] DateTime::modifyClone() is deprecated (BC break) --- src/Utils/DateTime.php | 1 + tests/Utils/DateTime.modifyClone.phpt | 27 --------------------------- 2 files changed, 1 insertion(+), 27 deletions(-) delete mode 100644 tests/Utils/DateTime.modifyClone.phpt diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index af41c52d2..876ca6122 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -86,6 +86,7 @@ public function __toString(): string */ public function modifyClone(string $modify = '') { + trigger_error(__METHOD__ . '() is deprecated, use modify()', E_USER_DEPRECATED); $dolly = clone $this; return $modify ? $dolly->modify($modify) : $dolly; } diff --git a/tests/Utils/DateTime.modifyClone.phpt b/tests/Utils/DateTime.modifyClone.phpt deleted file mode 100644 index 7bdd14d80..000000000 --- a/tests/Utils/DateTime.modifyClone.phpt +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * Test: Nette\Utils\DateTime::modifyClone(). - */ - -declare(strict_types=1); - -use Tester\Assert; -use Nette\Utils\DateTime; - -require __DIR__ . '/../bootstrap.php'; - - -date_default_timezone_set('Europe/Prague'); - -$date = DateTime::from(254400000); -$dolly = $date->modifyClone(); -Assert::type(DateTime::class, $dolly); -Assert::notSame($date, $dolly); -Assert::same((string) $date, (string) $dolly); - - -$dolly2 = $date->modifyClone('+1 hour'); -Assert::type(DateTime::class, $dolly2); -Assert::notSame($date, $dolly2); -Assert::notSame((string) $date, (string) $dolly2);