summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2017-10-18 09:46:43 (GMT)
committerAlex Pott2017-10-18 09:49:20 (GMT)
commit15c029974f2287b64b4d0e357663199306820686 (patch)
tree9222950d7fdd848f2673815e3d1e8f2cb5efe198
parent43c0eca73b70bf29968448dac94fa948d8e0e853 (diff)
Issue #2910081 by mpdonadio, neclimdul, gambry, jhedstrom, alexpott: DateTimePlus calls should be chainable
-rw-r--r--core/lib/Drupal/Component/Datetime/DateTimePlus.php24
-rw-r--r--core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php49
-rw-r--r--core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php44
3 files changed, 114 insertions, 3 deletions
diff --git a/core/lib/Drupal/Component/Datetime/DateTimePlus.php b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
index 6cdaaee..2e9bc02 100644
--- a/core/lib/Drupal/Component/Datetime/DateTimePlus.php
+++ b/core/lib/Drupal/Component/Datetime/DateTimePlus.php
@@ -306,8 +306,25 @@ class DateTimePlus {
* Implements the magic __call method.
*
* Passes through all unknown calls onto the DateTime object.
+ *
+ * @param string $method
+ * The method to call on the decorated object.
+ * @param array $args
+ * Call arguments.
+ *
+ * @return mixed
+ * The return value from the method on the decorated object. If the proxied
+ * method call returns a DateTime object, then return the original
+ * DateTimePlus object, which allows function chaining to work properly.
+ * Otherwise, the value from the proxied method call is returned.
+ *
+ * @throws \Exception
+ * Thrown when the DateTime object is not set.
+ * @throws \BadMethodCallException
+ * Thrown when there is no corresponding method on the DateTime object to
+ * call.
*/
- public function __call($method, $args) {
+ public function __call($method, array $args) {
// @todo consider using assert() as per https://www.drupal.org/node/2451793.
if (!isset($this->dateTimeObject)) {
throw new \Exception('DateTime object not set.');
@@ -315,7 +332,10 @@ class DateTimePlus {
if (!method_exists($this->dateTimeObject, $method)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
}
- return call_user_func_array([$this->dateTimeObject, $method], $args);
+
+ $result = call_user_func_array([$this->dateTimeObject, $method], $args);
+
+ return $result === $this->dateTimeObject ? $this : $result;
}
/**
diff --git a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
index 9cf0722..2eab8db 100644
--- a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
+++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
@@ -804,7 +804,6 @@ class DateTimePlusTest extends TestCase {
$date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]);
}
-
/**
* Tests setting the default time for date-only objects.
*/
@@ -817,4 +816,52 @@ class DateTimePlusTest extends TestCase {
$this->assertEquals('12:00:00', $date->format('H:i:s'));
}
+ /**
+ * Tests that object methods are chainable.
+ *
+ * @covers ::__call
+ */
+ public function testChainable() {
+ $date = new DateTimePlus('now', 'Australia/Sydney');
+
+ $date->setTimestamp(12345678);
+ $rendered = $date->render();
+ $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered);
+
+ $date->setTimestamp(23456789);
+ $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render();
+ $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered);
+
+ $date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '1970-05-24 07:21:18', new \DateTimeZone('Australia/Sydney'))
+ ->setTimezone(new \DateTimeZone('America/New_York'));
+ $rendered = $date->render();
+ $this->assertInstanceOf(DateTimePlus::class, $date);
+ $this->assertEquals(12345678, $date->getTimestamp());
+ $this->assertEquals('1970-05-23 17:21:18 America/New_York', $rendered);
+ }
+
+ /**
+ * Tests that non-chainable methods work.
+ *
+ * @covers ::__call
+ */
+ public function testChainableNonChainable() {
+ $datetime1 = new DateTimePlus('2009-10-11 12:00:00');
+ $datetime2 = new DateTimePlus('2009-10-13 12:00:00');
+ $interval = $datetime1->diff($datetime2);
+ $this->assertInstanceOf(\DateInterval::class, $interval);
+ $this->assertEquals('+2 days', $interval->format('%R%a days'));
+ }
+
+ /**
+ * Tests that chained calls to non-existent functions throw an exception.
+ *
+ * @covers ::__call
+ */
+ public function testChainableNonCallable() {
+ $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
+ $date = new DateTimePlus('now', 'Australia/Sydney');
+ $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
+ }
+
}
diff --git a/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
index c2359f4..6c47779 100644
--- a/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
+++ b/core/tests/Drupal/Tests/Core/Datetime/DrupalDateTimeTest.php
@@ -168,4 +168,48 @@ class DrupalDateTimeTest extends UnitTestCase {
$this->assertEquals('12:00:00', $date->format('H:i:s'));
}
+ /**
+ * Tests that object methods are chainable.
+ *
+ * @covers ::__call
+ */
+ public function testChainable() {
+ $tz = new \DateTimeZone(date_default_timezone_get());
+ $date = new DrupalDateTime('now', $tz, ['langcode' => 'en']);
+
+ $date->setTimestamp(12345678);
+ $rendered = $date->render();
+ $this->assertEquals('1970-05-24 07:21:18 Australia/Sydney', $rendered);
+
+ $date->setTimestamp(23456789);
+ $rendered = $date->setTimezone(new \DateTimeZone('America/New_York'))->render();
+ $this->assertEquals('1970-09-29 07:46:29 America/New_York', $rendered);
+ }
+
+ /**
+ * Tests that non-chainable methods work.
+ *
+ * @covers ::__call
+ */
+ public function testChainableNonChainable() {
+ $tz = new \DateTimeZone(date_default_timezone_get());
+ $datetime1 = new DrupalDateTime('2009-10-11 12:00:00', $tz, ['langcode' => 'en']);
+ $datetime2 = new DrupalDateTime('2009-10-13 12:00:00', $tz, ['langcode' => 'en']);
+ $interval = $datetime1->diff($datetime2);
+ $this->assertInstanceOf(\DateInterval::class, $interval);
+ $this->assertEquals('+2 days', $interval->format('%R%a days'));
+ }
+
+ /**
+ * Tests that chained calls to non-existent functions throw an exception.
+ *
+ * @covers ::__call
+ */
+ public function testChainableNonCallable() {
+ $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Core\Datetime\DrupalDateTime::nonexistent()');
+ $tz = new \DateTimeZone(date_default_timezone_get());
+ $date = new DrupalDateTime('now', $tz, ['langcode' => 'en']);
+ $date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
+ }
+
}