Merge branch 'master' into 2.6

Conflicts:
	lib/Cake/VERSION.txt
This commit is contained in:
mark_story 2014-10-13 21:11:38 -04:00
commit 87fcc9f0b9
10 changed files with 193 additions and 20 deletions

View file

@ -1,8 +1,9 @@
# CakePHP # CakePHP
[![Bake Status](https://secure.travis-ci.org/cakephp/cakephp.png?branch=master)](http://travis-ci.org/cakephp/cakephp)
[![Latest Stable Version](https://poser.pugx.org/cakephp/cakephp/v/stable.svg)](https://packagist.org/packages/cakephp/cakephp) [![Latest Stable Version](https://poser.pugx.org/cakephp/cakephp/v/stable.svg)](https://packagist.org/packages/cakephp/cakephp)
[![License](https://poser.pugx.org/cakephp/cakephp/license.svg)](https://packagist.org/packages/cakephp/cakephp) [![License](https://poser.pugx.org/cakephp/cakephp/license.svg)](https://packagist.org/packages/cakephp/cakephp)
[![Bake Status](https://secure.travis-ci.org/cakephp/cakephp.png?branch=master)](http://travis-ci.org/cakephp/cakephp)
[![Code consistency](http://squizlabs.github.io/PHP_CodeSniffer/analysis/cakephp/cakephp/grade.svg)](http://squizlabs.github.io/PHP_CodeSniffer/analysis/cakephp/cakephp/)
[![CakePHP](http://cakephp.org/img/cake-logo.png)](http://www.cakephp.org) [![CakePHP](http://cakephp.org/img/cake-logo.png)](http://www.cakephp.org)

View file

@ -611,7 +611,7 @@ class Controller extends Object implements CakeEventListener {
/** /**
* Returns a list of all events that will fire in the controller during its lifecycle. * Returns a list of all events that will fire in the controller during its lifecycle.
* You can override this function to add you own listener callbacks * You can override this function to add your own listener callbacks
* *
* @return array * @return array
*/ */

View file

@ -33,7 +33,7 @@ App::uses('CakePlugin', 'Core');
* CakePHP is organized around the idea of packages, each class belongs to a package or folder where other * CakePHP is organized around the idea of packages, each class belongs to a package or folder where other
* classes reside. You can configure each package location in your application using `App::build('APackage/SubPackage', $paths)` * classes reside. You can configure each package location in your application using `App::build('APackage/SubPackage', $paths)`
* to inform the framework where should each class be loaded. Almost every class in the CakePHP framework can be swapped * to inform the framework where should each class be loaded. Almost every class in the CakePHP framework can be swapped
* by your own compatible implementation. If you wish to use you own class instead of the classes the framework provides, * by your own compatible implementation. If you wish to use your own class instead of the classes the framework provides,
* just add the class to your libs folder mocking the directory location of where CakePHP expects to find it. * just add the class to your libs folder mocking the directory location of where CakePHP expects to find it.
* *
* For instance if you'd like to use your own HttpSocket class, put it under * For instance if you'd like to use your own HttpSocket class, put it under

View file

@ -378,7 +378,7 @@ class CakeSession {
*/ */
public static function read($name = null) { public static function read($name = null) {
if (empty($name) && $name !== null) { if (empty($name) && $name !== null) {
return false; return null;
} }
if (!self::_hasSession() || !self::start()) { if (!self::_hasSession() || !self::start()) {
return null; return null;

View file

@ -396,7 +396,7 @@ class DboSource extends DataSource {
* *
* @param string $sql SQL statement * @param string $sql SQL statement
* @param array $params Additional options for the query. * @param array $params Additional options for the query.
* @return bool * @return mixed Resource or object representing the result set, or false on failure
*/ */
public function rawQuery($sql, $params = array()) { public function rawQuery($sql, $params = array()) {
$this->took = $this->numRows = false; $this->took = $this->numRows = false;

View file

@ -776,7 +776,7 @@ class Model extends Object implements CakeEventListener {
/** /**
* Returns a list of all events that will fire in the model during it's lifecycle. * Returns a list of all events that will fire in the model during it's lifecycle.
* You can override this function to add you own listener callbacks * You can override this function to add your own listener callbacks
* *
* @return array * @return array
*/ */
@ -3361,11 +3361,26 @@ class Model extends Object implements CakeEventListener {
/** /**
* Returns false if any fields passed match any (by default, all if $or = false) of their matching values. * Returns false if any fields passed match any (by default, all if $or = false) of their matching values.
* *
* Can be used as a validation method. When used as a validation method, the `$or` parameter
* contains an array of fields to be validated.
*
* @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data) * @param array $fields Field/value pairs to search (if no values specified, they are pulled from $this->data)
* @param bool $or If false, all fields specified must match in order for a false return value * @param bool|array $or If false, all fields specified must match in order for a false return value
* @return bool False if any records matching any fields are found * @return bool False if any records matching any fields are found
*/ */
public function isUnique($fields, $or = true) { public function isUnique($fields, $or = true) {
if (is_array($or)) {
$isRule = (
array_key_exists('rule', $or) &&
array_key_exists('required', $or) &&
array_key_exists('message', $or)
);
if (!$isRule) {
$args = func_get_args();
$fields = $args[1];
$or = isset($args[2]) ? $args[2] : true;
}
}
if (!is_array($fields)) { if (!is_array($fields)) {
$fields = func_get_args(); $fields = func_get_args();
if (is_bool($fields[count($fields) - 1])) { if (is_bool($fields[count($fields) - 1])) {

View file

@ -280,7 +280,7 @@ class CakeSessionTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testReadyEmpty() { public function testReadyEmpty() {
$this->assertFalse(TestCakeSession::read('')); $this->assertNull(TestCakeSession::read(''));
} }
/** /**

View file

@ -6550,12 +6550,43 @@ class ModelReadTest extends BaseModelTest {
} }
} }
/**
* Test that find() with array conditions works when there is only one element.
*
* @return void
*/
public function testFindAllArrayConditions() {
$this->loadFixtures('User');
$TestModel = new User();
$TestModel->cacheQueries = false;
$result = $TestModel->find('all', array(
'conditions' => array('User.id' => array(3)),
));
$expected = array(
array(
'User' => array(
'id' => '3',
'user' => 'larry',
'password' => '5f4dcc3b5aa765d61d8327deb882cf99',
'created' => '2007-03-17 01:20:23',
'updated' => '2007-03-17 01:22:31'
))
);
$this->assertEquals($expected, $result);
$result = $TestModel->find('all', array(
'conditions' => array('User.user' => array('larry')),
));
$this->assertEquals($expected, $result);
}
/** /**
* test find('list') method * test find('list') method
* *
* @return void * @return void
*/ */
public function testGenerateFindList() { public function testFindList() {
$this->loadFixtures('Article', 'Apple', 'Post', 'Author', 'User', 'Comment'); $this->loadFixtures('Article', 'Apple', 'Post', 'Author', 'User', 'Comment');
$TestModel = new Article(); $TestModel = new Article();
@ -6825,6 +6856,32 @@ class ModelReadTest extends BaseModelTest {
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* Test that find(list) works with array conditions that have only one element.
*
* @return void
*/
public function testFindListArrayCondition() {
$this->loadFixtures('User');
$TestModel = new User();
$TestModel->cacheQueries = false;
$result = $TestModel->find('list', array(
'fields' => array('id', 'user'),
'conditions' => array('User.id' => array(3)),
));
$expected = array(
3 => 'larry'
);
$this->assertEquals($expected, $result);
$result = $TestModel->find('list', array(
'fields' => array('id', 'user'),
'conditions' => array('User.user' => array('larry')),
));
$this->assertEquals($expected, $result);
}
/** /**
* testFindField method * testFindField method
* *

View file

@ -2405,6 +2405,77 @@ class ModelValidationTest extends BaseModelTest {
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* Test the isUnique method when used as a validator for multiple fields.
*
* @return void
*/
public function testIsUniqueValidator() {
$this->loadFixtures('Article');
$Article = ClassRegistry::init('Article');
$Article->validate = array(
'user_id' => array(
'duplicate' => array(
'rule' => array('isUnique', array('user_id', 'title'), false)
)
)
);
$data = array(
'user_id' => 1,
'title' => 'First Article',
);
$Article->create($data);
$this->assertFalse($Article->validates(), 'Contains a dupe');
$data = array(
'user_id' => 1,
'title' => 'Unique Article',
);
$Article->create($data);
$this->assertTrue($Article->validates(), 'Should pass');
$Article->validate = array(
'user_id' => array(
'duplicate' => array(
'rule' => array('isUnique', array('user_id', 'title'))
)
)
);
$data = array(
'user_id' => 1,
'title' => 'Unique Article',
);
$Article->create($data);
$this->assertFalse($Article->validates(), 'Should fail, conditions are combined with or');
}
/**
* Test backward compatibility of the isUnique method when used as a validator for a single field.
*
* @return void
*/
public function testBackwardCompatIsUniqueValidator() {
$this->loadFixtures('Article');
$Article = ClassRegistry::init('Article');
$Article->validate = array(
'title' => array(
'duplicate' => array(
'rule' => 'isUnique',
'message' => 'Title must be unique',
),
'minLength' => array(
'rule' => array('minLength', 1),
'message' => 'Title cannot be empty',
),
)
);
$data = array(
'title' => 'First Article',
);
$data = $Article->create($data);
$this->assertFalse($Article->validates(), 'Contains a dupe');
}
} }
/** /**

View file

@ -632,6 +632,9 @@ class Hash {
*/ */
public static function expand($data, $separator = '.') { public static function expand($data, $separator = '.') {
$result = array(); $result = array();
$stack = array();
foreach ($data as $flat => $value) { foreach ($data as $flat => $value) {
$keys = explode($separator, $flat); $keys = explode($separator, $flat);
$keys = array_reverse($keys); $keys = array_reverse($keys);
@ -644,7 +647,24 @@ class Hash {
$k => $child $k => $child
); );
} }
$result = self::merge($result, $child);
$stack[] = array($child, &$result);
while (!empty($stack)) {
foreach ($stack as $curKey => &$curMerge) {
foreach ($curMerge[0] as $key => &$val) {
if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
$stack[] = array(&$val, &$curMerge[1][$key]);
} elseif ((int)$key === $key && isset($curMerge[1][$key])) {
$curMerge[1][] = $val;
} else {
$curMerge[1][$key] = $val;
}
}
unset($stack[$curKey]);
}
unset($curMerge);
}
} }
return $result; return $result;
} }
@ -664,19 +684,28 @@ class Hash {
* @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::merge * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::merge
*/ */
public static function merge(array $data, $merge) { public static function merge(array $data, $merge) {
$args = func_get_args(); $args = array_slice(func_get_args(), 1);
$return = current($args); $return = $data;
while (($arg = next($args)) !== false) { foreach ($args as &$curArg) {
foreach ((array)$arg as $key => $val) { $stack[] = array((array)$curArg, &$return);
if (!empty($return[$key]) && is_array($return[$key]) && is_array($val)) { }
$return[$key] = self::merge($return[$key], $val); unset($curArg);
} elseif (is_int($key) && isset($return[$key])) {
$return[] = $val; while (!empty($stack)) {
foreach ($stack as $curKey => &$curMerge) {
foreach ($curMerge[0] as $key => &$val) {
if (!empty($curMerge[1][$key]) && (array)$curMerge[1][$key] === $curMerge[1][$key] && (array)$val === $val) {
$stack[] = array(&$val, &$curMerge[1][$key]);
} elseif ((int)$key === $key && isset($curMerge[1][$key])) {
$curMerge[1][] = $val;
} else { } else {
$return[$key] = $val; $curMerge[1][$key] = $val;
} }
} }
unset($stack[$curKey]);
}
unset($curMerge);
} }
return $return; return $return;
} }