Browse Source

Merge branch 'master' into 3.next

ADmad 10 years ago
parent
commit
6ba01e8f51

+ 1 - 1
src/Cache/Engine/FileEngine.php

@@ -151,7 +151,7 @@ class FileEngine extends CacheEngine
 
         $duration = $this->_config['duration'];
         $expires = time() + $duration;
-        $contents = $expires . $lineBreak . $data . $lineBreak;
+        $contents = implode([$expires, $lineBreak, $data, $lineBreak]);
 
         if ($this->_config['lock']) {
             $this->_File->flock(LOCK_EX);

+ 24 - 1
src/Database/Schema/SqliteSchema.php

@@ -127,7 +127,7 @@ class SqliteSchema extends BaseSchema
         $field = $this->_convertColumn($row['type']);
         $field += [
             'null' => !$row['notnull'],
-            'default' => $row['dflt_value'] === null ? null : trim($row['dflt_value'], "'"),
+            'default' => $this->_defaultValue($row['dflt_value']),
         ];
         $primary = $table->constraint('primary');
 
@@ -154,6 +154,29 @@ class SqliteSchema extends BaseSchema
     }
 
     /**
+     * Manipulate the default value.
+     *
+     * Sqlite includes quotes and bared NULLs in default values.
+     * We need to remove those.
+     *
+     * @param string|null $default The default value.
+     * @return string|null
+     */
+    protected function _defaultValue($default)
+    {
+        if ($default === 'NULL') {
+            return null;
+        }
+
+        // Remove quotes
+        if (preg_match("/^'(.*)'$/", $default, $matches)) {
+            return str_replace("''", "'", $matches[1]);
+        }
+
+        return $default;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public function describeIndexSql($tableName, $config)

+ 19 - 1
src/Validation/Validation.php

@@ -180,7 +180,7 @@ class Validation
                 'enroute' => '/^2(?:014|149)\\d{11}$/',
                 'jcb' => '/^(3\\d{4}|2100|1800)\\d{11}$/',
                 'maestro' => '/^(?:5020|6\\d{3})\\d{12}$/',
-                'mc' => '/^5[1-5]\\d{14}$/',
+                'mc' => '/^(5[1-5]\\d{14})|(2(?:22[1-9]|2[3-9][0-9]|[3-6][0-9]{2}|7[0-1][0-9]|720)\\d{12})$/',
                 'solo' => '/^(6334[5-9][0-9]|6767[0-9]{2})\\d{10}(\\d{2,3})?$/',
                 'switch' => '/^(?:49(03(0[2-9]|3[5-9])|11(0[1-2]|7[4-9]|8[1-2])|36[0-9]{2})\\d{10}(\\d{2,3})?)|(?:564182\\d{10}(\\d{2,3})?)|(6(3(33[0-4][0-9])|759[0-9]{2})\\d{10}(\\d{2,3})?)$/',
                 'visa' => '/^4\\d{12}(\\d{3})?$/',
@@ -216,6 +216,24 @@ class Validation
     }
 
     /**
+     * Used to check the count of a given value of type array or Countable.
+     *
+     * @param array|\Countable $check The value to check the count on.
+     * @param string $operator Can be either a word or operand
+     *    is greater >, is less <, greater or equal >=
+     *    less or equal <=, is less <, equal to ==, not equal !=
+     * @param int $expectedCount The expected count value.
+     * @return bool Success
+     */
+    public static function numElements($check, $operator, $expectedCount)
+    {
+        if (!is_array($check) && !$check instanceof \Countable) {
+            return false;
+        }
+        return self::comparison(count($check), $operator, $expectedCount);
+    }
+
+    /**
      * Used to compare 2 numeric values.
      *
      * @param string $check1 if string is passed for, a string must also be passed for $check2

+ 50 - 0
src/Validation/Validator.php

@@ -1350,6 +1350,56 @@ class Validator implements ArrayAccess, IteratorAggregate, Countable
     }
 
     /**
+     * Add a validation rule to ensure that a field is an array containing at least
+     * the specified amount of elements
+     *
+     * @param string $field The field you want to apply the rule to.
+     * @param int $count The number of elements the array should at least have
+     * @param string|null $message The error message when the rule fails.
+     * @param string|callable|null $when Either 'create' or 'update' or a callable that returns
+     *   true when the validation rule should be applied.
+     * @see \Cake\Validation\Validation::numElements()
+     * @return $this
+     */
+    public function hasAtLeast($field, $count, $message = null, $when = null)
+    {
+        $extra = array_filter(['on' => $when, 'message' => $message]);
+        return $this->add($field, 'hasAtLeast', $extra + [
+            'rule' => function ($value) use ($count) {
+                if (is_array($value) && isset($value['_ids'])) {
+                    $value = $value['_ids'];
+                }
+                return Validation::numElements($value, '>=', $count);
+            }
+        ]);
+    }
+
+    /**
+     * Add a validation rule to ensure that a field is an array containing at most
+     * the specified amount of elements
+     *
+     * @param string $field The field you want to apply the rule to.
+     * @param int $count The number maximim amount of elements the field should have
+     * @param string|null $message The error message when the rule fails.
+     * @param string|callable|null $when Either 'create' or 'update' or a callable that returns
+     *   true when the validation rule should be applied.
+     * @see \Cake\Validation\Validation::numElements()
+     * @return $this
+     */
+    public function hasAtMost($field, $count, $message = null, $when = null)
+    {
+        $extra = array_filter(['on' => $when, 'message' => $message]);
+        return $this->add($field, 'hasAtMost', $extra + [
+            'rule' => function ($value) use ($count) {
+                if (is_array($value) && isset($value['_ids'])) {
+                    $value = $value['_ids'];
+                }
+                return Validation::numElements($value, '<=', $count);
+            }
+        ]);
+    }
+
+    /**
      * Returns whether or not a field can be left empty for a new or already existing
      * record.
      *

+ 22 - 2
tests/TestCase/Database/Schema/SqliteSchemaTest.php

@@ -221,11 +221,13 @@ SQL;
         $table = <<<SQL
 CREATE TABLE schema_articles (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
-title VARCHAR(20) DEFAULT 'testing',
+title VARCHAR(20) DEFAULT 'Let ''em eat cake',
 body TEXT,
 author_id INT(11) NOT NULL,
 published BOOLEAN DEFAULT 0,
 created DATETIME,
+field1 VARCHAR(10) DEFAULT NULL,
+field2 VARCHAR(10) DEFAULT 'NULL',
 CONSTRAINT "title_idx" UNIQUE ("title", "body")
 CONSTRAINT "author_idx" FOREIGN KEY ("author_id") REFERENCES "schema_authors" ("id") ON UPDATE CASCADE ON DELETE RESTRICT
 );
@@ -288,7 +290,7 @@ SQL;
             'title' => [
                 'type' => 'string',
                 'null' => true,
-                'default' => 'testing',
+                'default' => 'Let \'em eat cake',
                 'length' => 20,
                 'precision' => null,
                 'fixed' => null,
@@ -328,6 +330,24 @@ SQL;
                 'precision' => null,
                 'comment' => null,
             ],
+            'field1' => [
+                'type' => 'string',
+                'null' => true,
+                'default' => null,
+                'length' => 10,
+                'precision' => null,
+                'fixed' => null,
+                'comment' => null,
+            ],
+            'field2' => [
+                'type' => 'string',
+                'null' => true,
+                'default' => 'NULL',
+                'length' => 10,
+                'precision' => null,
+                'fixed' => null,
+                'comment' => null,
+            ],
         ];
         $this->assertInstanceOf('Cake\Database\Schema\Table', $result);
         $this->assertEquals(['id'], $result->primaryKey());

+ 29 - 0
tests/TestCase/Validation/ValidationTest.php

@@ -339,6 +339,12 @@ class ValidationTest extends TestCase
         $this->assertTrue(Validation::cc('5467639122779531', ['mc']));
         $this->assertTrue(Validation::cc('5297350261550024', ['mc']));
         $this->assertTrue(Validation::cc('5162739131368058', ['mc']));
+        //Mastercard (additional 2016 BIN)
+        $this->assertTrue(Validation::cc('2221000000000009', ['mc']));
+        $this->assertTrue(Validation::cc('2720999999999996', ['mc']));
+        $this->assertTrue(Validation::cc('2223000010005798', ['mc']));
+        $this->assertTrue(Validation::cc('2623430710235708', ['mc']));
+        $this->assertTrue(Validation::cc('2420452519835723', ['mc']));
         //Solo 16
         $this->assertTrue(Validation::cc('6767432107064987', ['solo']));
         $this->assertTrue(Validation::cc('6334667758225411', ['solo']));
@@ -2762,4 +2768,27 @@ class ValidationTest extends TestCase
         // Grinning face
         $this->assertTrue(Validation::utf8('some' . "\xf0\x9f\x98\x80" . 'value', ['extended' => true]));
     }
+
+    /**
+     * Test numElements
+     *
+     * @return void
+     */
+    public function testNumElements()
+    {
+        $array = ['cake', 'php'];
+        $this->assertTrue(Validation::numElements($array, '==', 2));
+        $this->assertFalse(Validation::numElements($array, '>', 3));
+        $this->assertFalse(Validation::numElements($array, '<', 1));
+
+        $callable = function () {
+            return '';
+        };
+
+        $this->assertFalse(Validation::numElements(null, '==', 0));
+        $this->assertFalse(Validation::numElements(new \stdClass(), '==', 0));
+        $this->assertFalse(Validation::numElements($callable, '==', 0));
+        $this->assertFalse(Validation::numElements(false, '==', 0));
+        $this->assertFalse(Validation::numElements(true, '==', 0));
+    }
 }

+ 40 - 0
tests/TestCase/Validation/ValidatorTest.php

@@ -1614,6 +1614,46 @@ class ValidatorTest extends TestCase
         $this->assertNotEmpty($validator->errors(['username' => '']));
     }
 
+    /**
+     * Tests the hasAtLeast method
+     *
+     * @return void
+     */
+    public function testHasAtLeast()
+    {
+        $validator = new Validator();
+        $validator->hasAtLeast('things', 3);
+        $this->assertEmpty($validator->errors(['things' => [1, 2, 3]]));
+        $this->assertEmpty($validator->errors(['things' => [1, 2, 3, 4]]));
+        $this->assertNotEmpty($validator->errors(['things' => [1, 2]]));
+        $this->assertNotEmpty($validator->errors(['things' => []]));
+        $this->assertNotEmpty($validator->errors(['things' => 'string']));
+
+        $this->assertEmpty($validator->errors(['things' => ['_ids' => [1, 2, 3]]]));
+        $this->assertEmpty($validator->errors(['things' => ['_ids' => [1, 2, 3, 4]]]));
+        $this->assertNotEmpty($validator->errors(['things' => ['_ids' => [1, 2]]]));
+        $this->assertNotEmpty($validator->errors(['things' => ['_ids' => []]]));
+        $this->assertNotEmpty($validator->errors(['things' => ['_ids' => 'string']]));
+    }
+
+    /**
+     * Tests the hasAtMost method
+     *
+     * @return void
+     */
+    public function testHasAtMost()
+    {
+        $validator = new Validator();
+        $validator->hasAtMost('things', 3);
+        $this->assertEmpty($validator->errors(['things' => [1, 2, 3]]));
+        $this->assertEmpty($validator->errors(['things' => [1]]));
+        $this->assertNotEmpty($validator->errors(['things' => [1, 2, 3, 4]]));
+
+        $this->assertEmpty($validator->errors(['things' => ['_ids' => [1, 2, 3]]]));
+        $this->assertEmpty($validator->errors(['things' => ['_ids' => [1, 2]]]));
+        $this->assertNotEmpty($validator->errors(['things' => ['_ids' => [1, 2, 3, 4]]]));
+    }
+
     protected function assertProxyMethod($validator, $method, $extra = null, $pass = [], $name = null)
     {
         $name = $name ?: $method;