Browse Source

Add useImmutable and useMutable to date types.

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).
Mark Story 10 years ago
parent
commit
cb3821aed3

+ 46 - 12
src/Database/Type/DateTimeType.php

@@ -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';
 
@@ -66,17 +70,19 @@ 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');
     }
 
     /**
@@ -92,7 +98,8 @@ class DateTimeType extends Type
             return $value;
         }
         if (is_int($value)) {
-            $value = new static::$dateTimeClass('@' . $value);
+            $class = $this->_className;
+            $value = new $class('@' . $value);
         }
         return $value->format($this->_format);
     }
@@ -130,7 +137,7 @@ class DateTimeType extends Type
             return $value;
         }
 
-        $class = static::$dateTimeClass;
+        $class = $this->_className;
         try {
             $compare = $date = false;
             if ($value === '' || $value === null || $value === false || $value === true) {
@@ -196,12 +203,12 @@ class DateTimeType extends Type
             $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)
         );
     }
 
@@ -227,7 +234,34 @@ class DateTimeType extends Type
      */
     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;
     }
 
@@ -240,7 +274,7 @@ class DateTimeType extends Type
      */
     protected function _parseValue($value)
     {
-        $class = static::$dateTimeClass;
+        $class = $this->_className;
         return $class::parseDateTime($value, $this->_localeFormat);
     }
 }

+ 33 - 2
src/Database/Type/DateType.php

@@ -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';
 
@@ -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;
     }
 
@@ -80,7 +95,23 @@ class DateType extends DateTimeType
      */
     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));
+    }
 }

+ 1 - 1
src/Database/Type/TimeType.php

@@ -34,7 +34,7 @@ class TimeType extends DateTimeType
      */
     protected function _parseValue($value)
     {
-        $class = static::$dateTimeClass;
+        $class = $this->_className;
         return $class::parseTime($value, $this->_localeFormat);
     }
 }

+ 17 - 15
tests/TestCase/Database/Type/DateTimeTypeTest.php

@@ -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;
@@ -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);
     }
 
     /**
@@ -247,4 +233,20 @@ class DateTimeTypeTest extends TestCase
         $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));
+    }
 }

+ 17 - 2
tests/TestCase/Database/Type/DateTypeTest.php

@@ -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;
@@ -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');
     }
 
@@ -194,4 +193,20 @@ class DateTypeTest extends TestCase
         $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));
+    }
 }

+ 18 - 2
tests/TestCase/Database/Type/TimeTypeTest.php

@@ -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;
@@ -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');
     }
 
@@ -166,6 +165,7 @@ class TimeTypeTest extends TestCase
         $result = $this->type->marshal($value);
         if (is_object($expected)) {
             $this->assertEquals($expected, $result);
+            $this->assertInstanceOf('DateTime', $result);
         } else {
             $this->assertSame($expected, $result);
         }
@@ -185,4 +185,20 @@ class TimeTypeTest extends TestCase
 
         $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));
+    }
 }