Skip to content

Commit

Permalink
Add useImmutable and useMutable to date types.
Browse files Browse the repository at this point in the history
Add methods to enable immutable object creation. This allows us to
retain BC and also enable immutable objects for new apps. The
static::$dateTimeClass property is now deprecated. The new methods allow
better control without the drawbacks that statics incur (shared global
state).
  • Loading branch information
markstory committed Nov 27, 2015
1 parent d9d9ed4 commit cb3821a
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 34 deletions.
58 changes: 46 additions & 12 deletions src/Database/Type/DateTimeType.php
Expand Up @@ -31,7 +31,11 @@ class DateTimeType extends Type
/**
* The class to use for representing date objects
*
* This property can only be used before an instance of this type
* class is constructed. After that use `useMutable()` or `useImmutable()` instead.
*
* @var string
* @deprecated Use DateTimeType::useMutable() or DateTimeType::useImmutable() instead.
*/
public static $dateTimeClass = 'Cake\I18n\Time';

Expand Down Expand Up @@ -65,18 +69,20 @@ class DateTimeType extends Type
*/
protected $_datetimeInstance;

/**
* The classname to use when creating objects.
*
* @var string
*/
protected $_className;

/**
* {@inheritDoc}
*/
public function __construct($name = null)
{
parent::__construct($name);

if (!class_exists(static::$dateTimeClass)) {
static::$dateTimeClass = 'DateTime';
}

$this->_datetimeInstance = new static::$dateTimeClass;
$this->_setClassName(static::$dateTimeClass, 'DateTime');
}

/**
Expand All @@ -92,7 +98,8 @@ public function toDatabase($value, Driver $driver)
return $value;
}
if (is_int($value)) {
$value = new static::$dateTimeClass('@' . $value);
$class = $this->_className;
$value = new $class('@' . $value);
}
return $value->format($this->_format);
}
Expand Down Expand Up @@ -130,7 +137,7 @@ public function marshal($value)
return $value;
}

$class = static::$dateTimeClass;
$class = $this->_className;
try {
$compare = $date = false;
if ($value === '' || $value === null || $value === false || $value === true) {
Expand Down Expand Up @@ -196,12 +203,12 @@ public function useLocaleParser($enable = true)
$this->_useLocaleParser = $enable;
return $this;
}
if (method_exists(static::$dateTimeClass, 'parseDateTime')) {
if (method_exists($this->_className, 'parseDateTime')) {
$this->_useLocaleParser = $enable;
return $this;
}
throw new RuntimeException(
sprintf('Cannot use locale parsing with the %s class', static::$dateTimeClass)
sprintf('Cannot use locale parsing with the %s class', $this->_className)
);
}

Expand All @@ -227,7 +234,34 @@ public function setLocaleFormat($format)
*/
public function useImmutable()
{
static::$dateTimeClass = 'Cake\I18n\FrozenTime';
$this->_setClassName('Cake\I18n\FrozenTime', 'DateTimeImmutable');
return $this;
}

/**
* Set the classname to use when building objects.
*
* @param string $class The classname to use.
* @param string $fallback The classname to use when the preferred class does not exist.
* @return void
*/
protected function _setClassName($class, $fallback)
{
if (!class_exists($class)) {
$class = $fallback;
}
$this->_className = $class;
$this->_datetimeInstance = new $this->_className;
}

/**
* Change the preferred class name to the mutable Time implementation.
*
* @return $this
*/
public function useMutable()
{
$this->_setClassName('Cake\I18n\Time', 'DateTime');
return $this;
}

Expand All @@ -240,7 +274,7 @@ public function useImmutable()
*/
protected function _parseValue($value)
{
$class = static::$dateTimeClass;
$class = $this->_className;
return $class::parseDateTime($value, $this->_localeFormat);
}
}
35 changes: 33 additions & 2 deletions src/Database/Type/DateType.php
Expand Up @@ -22,7 +22,11 @@ class DateType extends DateTimeType
/**
* The class to use for representing date objects
*
* This property can only be used before an instance of this type
* class is constructed. After that use `useMutable()` or `useImmutable()` instead.
*
* @var string
* @deprecated Use DateType::useMutable() or DateType::useImmutable() instead.
*/
public static $dateTimeClass = 'Cake\I18n\Date';

Expand All @@ -40,7 +44,18 @@ class DateType extends DateTimeType
*/
public function useImmutable()
{
static::$dateTimeClass = 'Cake\I18n\FrozenDate';
$this->_setClassName('Cake\I18n\FrozenDate', 'DateTimeImmutable');
return $this;
}

/**
* Change the preferred class name to the mutable Date implementation.
*
* @return $this
*/
public function useMutable()
{
$this->_setClassName('Cake\I18n\Date', 'DateTime');
return $this;
}

Expand Down Expand Up @@ -80,7 +95,23 @@ public function toPHP($value, Driver $driver)
*/
protected function _parseValue($value)
{
$class = static::$dateTimeClass;
$class = $this->_className;
return $class::parseDate($value, $this->_localeFormat);
}

/**
* Test that toImmutable changes all the methods to create frozen time instances.
*
* @return void
*/
public function testToImmutableAndToMutable()
{
$this->type->useImmutable();
$this->assertInstanceOf('DateTimeImmutable', $this->type->marshal('2015-11-01'));
$this->assertInstanceOf('DateTimeImmutable', $this->type->toPhp('2015-11-01', $this->driver));

$this->type->useMutable();
$this->assertInstanceOf('DateTime', $this->type->marshal('2015-11-01'));
$this->assertInstanceOf('DateTime', $this->type->toPhp('2015-11-01', $this->driver));
}
}
2 changes: 1 addition & 1 deletion src/Database/Type/TimeType.php
Expand Up @@ -34,7 +34,7 @@ class TimeType extends DateTimeType
*/
protected function _parseValue($value)
{
$class = static::$dateTimeClass;
$class = $this->_className;
return $class::parseTime($value, $this->_localeFormat);
}
}
32 changes: 17 additions & 15 deletions tests/TestCase/Database/Type/DateTimeTypeTest.php
Expand Up @@ -14,7 +14,6 @@
*/
namespace Cake\Test\TestCase\Database\Type;

use Cake\Database\Type;
use Cake\Database\Type\DateTimeType;
use Cake\I18n\Time;
use Cake\TestSuite\TestCase;
Expand All @@ -40,21 +39,8 @@ class DateTimeTypeTest extends TestCase
public function setUp()
{
parent::setUp();
$this->type = Type::build('datetime');
$this->type = new DateTimeType();
$this->driver = $this->getMock('Cake\Database\Driver');
$this->_originalMap = Type::map();
}

/**
* Restores Type class state
*
* @return void
*/
public function tearDown()
{
parent::tearDown();

Type::map($this->_originalMap);
}

/**
Expand Down Expand Up @@ -247,4 +233,20 @@ public function testMarshalWithLocaleParsingWithFormat()
$result = $this->type->marshal('13 Oct, 2013 01:54pm');
$this->assertEquals($expected, $result);
}

/**
* Test that toImmutable changes all the methods to create frozen time instances.
*
* @return void
*/
public function testToImmutableAndToMutable()
{
$this->type->useImmutable();
$this->assertInstanceOf('DateTimeImmutable', $this->type->marshal('2015-11-01 11:23:00'));
$this->assertInstanceOf('DateTimeImmutable', $this->type->toPhp('2015-11-01 11:23:00', $this->driver));

$this->type->useMutable();
$this->assertInstanceOf('DateTime', $this->type->marshal('2015-11-01 11:23:00'));
$this->assertInstanceOf('DateTime', $this->type->toPhp('2015-11-01 11:23:00', $this->driver));
}
}
19 changes: 17 additions & 2 deletions tests/TestCase/Database/Type/DateTypeTest.php
Expand Up @@ -15,7 +15,6 @@
namespace Cake\Test\TestCase\Database\Type;

use Cake\Chronos\Date;
use Cake\Database\Type;
use Cake\Database\Type\DateType;
use Cake\I18n\Time;
use Cake\TestSuite\TestCase;
Expand All @@ -34,7 +33,7 @@ class DateTypeTest extends TestCase
public function setUp()
{
parent::setUp();
$this->type = Type::build('date');
$this->type = new DateType();
$this->driver = $this->getMock('Cake\Database\Driver');
}

Expand Down Expand Up @@ -194,4 +193,20 @@ public function testMarshalWithLocaleParsingWithFormat()
$result = $this->type->marshal('13 Oct, 2013');
$this->assertEquals($expected->format('Y-m-d'), $result->format('Y-m-d'));
}

/**
* Test that toImmutable changes all the methods to create frozen time instances.
*
* @return void
*/
public function testToImmutableAndToMutable()
{
$this->type->useImmutable();
$this->assertInstanceOf('DateTimeImmutable', $this->type->marshal('2015-11-01'));
$this->assertInstanceOf('DateTimeImmutable', $this->type->toPhp('2015-11-01', $this->driver));

$this->type->useMutable();
$this->assertInstanceOf('DateTime', $this->type->marshal('2015-11-01'));
$this->assertInstanceOf('DateTime', $this->type->toPhp('2015-11-01', $this->driver));
}
}
20 changes: 18 additions & 2 deletions tests/TestCase/Database/Type/TimeTypeTest.php
Expand Up @@ -14,7 +14,6 @@
*/
namespace Cake\Test\TestCase\Database\Type;

use Cake\Database\Type;
use Cake\Database\Type\TimeType;
use Cake\I18n\Time;
use Cake\TestSuite\TestCase;
Expand All @@ -33,7 +32,7 @@ class TimeTypeTest extends TestCase
public function setUp()
{
parent::setUp();
$this->type = Type::build('time');
$this->type = new TimeType();
$this->driver = $this->getMock('Cake\Database\Driver');
}

Expand Down Expand Up @@ -166,6 +165,7 @@ public function testMarshal($value, $expected)
$result = $this->type->marshal($value);
if (is_object($expected)) {
$this->assertEquals($expected, $result);
$this->assertInstanceOf('DateTime', $result);
} else {
$this->assertSame($expected, $result);
}
Expand All @@ -185,4 +185,20 @@ public function testMarshalWithLocaleParsing()

$this->assertNull($this->type->marshal('derp:23'));
}

/**
* Test that toImmutable changes all the methods to create frozen time instances.
*
* @return void
*/
public function testToImmutableAndToMutable()
{
$this->type->useImmutable();
$this->assertInstanceOf('DateTimeImmutable', $this->type->marshal('11:23:12'));
$this->assertInstanceOf('DateTimeImmutable', $this->type->toPhp('11:23:12', $this->driver));

$this->type->useMutable();
$this->assertInstanceOf('DateTime', $this->type->marshal('11:23:12'));
$this->assertInstanceOf('DateTime', $this->type->toPhp('11:23:12', $this->driver));
}
}

0 comments on commit cb3821a

Please sign in to comment.