diff --git a/.travis.yml b/.travis.yml index 2070d62da..eac15fc90 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,8 @@ before_script: - sh -c "if [ '$PHPCS' = '1' ]; then composer global require 'cakephp/cakephp-codesniffer:1.*'; fi" - sh -c "if [ '$PHPCS' = '1' ]; then ~/.composer/vendor/bin/phpcs --config-set installed_paths ~/.composer/vendor/cakephp/cakephp-codesniffer; fi" - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]] ; then print "yes" | pecl install apcu-5.1.3; else print "yes" | pecl install apcu-4.0.11; fi + - if [[ ${TRAVIS_PHP_VERSION:0:1} == "7" ]] ; then echo "yes" | pecl install apcu-5.1.3 || true; fi + - if [[ ${TRAVIS_PHP_VERSION:0:1} == "5" ]] ; then echo "yes" | pecl install apcu-4.0.11 || true; fi - echo -e "extension = apcu.so\napc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - phpenv rehash - set +H diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0917011e9..8c7ceb48b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ Help us keep CakePHP open and inclusive. Please read and follow our [Code of Con * Core test cases should continue to pass. You can run tests locally or enable [travis-ci](https://travis-ci.org/) for your fork, so all tests and codesniffs will be executed. -* Your work should apply the [CakePHP coding standards](http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html). +* Your work should apply the [CakePHP coding standards](https://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html). ## Which branch to base the work @@ -54,7 +54,7 @@ Help us keep CakePHP open and inclusive. Please read and follow our [Code of Con ## Test cases and codesniffer -CakePHP tests requires [PHPUnit](http://www.phpunit.de/manual/current/en/installation.html) +CakePHP tests requires [PHPUnit](https://phpunit.de/manual/current/en/installation.html) 3.7, version 4 is not compatible. To run the test cases locally use the following command: ./lib/Cake/Console/cake test core AllTests --stderr @@ -69,11 +69,11 @@ for the sniff and phpcs. ## Reporting a Security Issue -If you've found a security related issue in CakePHP, please don't open an issue in GitHub. Instead contact us at security@cakephp.org. For more information on how we handle security issues, [see the CakePHP Security Issue Process](http://book.cakephp.org/2.0/en/contributing/tickets.html#reporting-security-issues). +If you've found a security related issue in CakePHP, please don't open an issue in GitHub. Instead contact us at security@cakephp.org. For more information on how we handle security issues, [see the CakePHP Security Issue Process](https://book.cakephp.org/2.0/en/contributing/tickets.html#reporting-security-issues). # Additional Resources -* [CakePHP coding standards](http://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html) +* [CakePHP coding standards](https://book.cakephp.org/2.0/en/contributing/cakephp-coding-conventions.html) * [Existing issues](https://github.com/cakephp/cakephp/issues) * [Development Roadmaps](https://github.com/cakephp/cakephp/wiki#roadmaps) * [General GitHub documentation](https://help.github.com/) diff --git a/README.md b/README.md index 4e1ccb19b..eeccb2152 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ [![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) -[![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/) +[![Bake Status](https://secure.travis-ci.org/cakephp/cakephp.png?branch=master)](https://travis-ci.org/cakephp/cakephp) +[![Code consistency](https://squizlabs.github.io/PHP_CodeSniffer/analysis/cakephp/cakephp/grade.svg)](https://squizlabs.github.io/PHP_CodeSniffer/analysis/cakephp/cakephp/) CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC. Our primary goal is to provide a structured framework that enables PHP users at all levels to rapidly develop robust web applications, without any loss to flexibility. @@ -11,28 +11,28 @@ Our primary goal is to provide a structured framework that enables PHP users at ## Some Handy Links -[CakePHP](http://www.cakephp.org) - The rapid development PHP framework +[CakePHP](https://cakephp.org) - The rapid development PHP framework -[CookBook](http://book.cakephp.org) - THE CakePHP user documentation; start learning here! +[CookBook](https://book.cakephp.org) - THE CakePHP user documentation; start learning here! -[API](http://api.cakephp.org) - A reference to CakePHP's classes +[API](https://api.cakephp.org) - A reference to CakePHP's classes -[Plugins](http://plugins.cakephp.org/) - A repository of extensions to the framework +[Plugins](https://plugins.cakephp.org) - A repository of extensions to the framework -[The Bakery](http://bakery.cakephp.org) - Tips, tutorials and articles +[The Bakery](https://bakery.cakephp.org) - Tips, tutorials and articles -[Community Center](http://community.cakephp.org) - A source for everything community related +[Community Center](https://community.cakephp.org) - A source for everything community related -[Training](http://training.cakephp.org) - Join a live session and get skilled with the framework +[Training](https://training.cakephp.org) - Join a live session and get skilled with the framework -[CakeFest](http://cakefest.org) - Don't miss our annual CakePHP conference +[CakeFest](https://cakefest.org) - Don't miss our annual CakePHP conference -[Cake Software Foundation](http://cakefoundation.org) - Promoting development related to CakePHP +[Cake Software Foundation](https://cakefoundation.org) - Promoting development related to CakePHP ## Get Support! -[#cakephp](http://webchat.freenode.net/?channels=#cakephp) on irc.freenode.net - Come chat with us, we have cake +[#cakephp](https://webchat.freenode.net/?channels=#cakephp) on irc.freenode.net - Come chat with us, we have cake [Google Group](https://groups.google.com/group/cake-php) - Community mailing list and forum @@ -45,4 +45,4 @@ Our primary goal is to provide a structured framework that enables PHP users at [CONTRIBUTING.md](CONTRIBUTING.md) - Quick pointers for contributing to the CakePHP project -[CookBook "Contributing" Section (2.x)](http://book.cakephp.org/2.0/en/contributing.html) [(3.0)](http://book.cakephp.org/3.0/en/contributing.html) - Version-specific details about contributing to the project +[CookBook "Contributing" Section (2.x)](https://book.cakephp.org/2.0/en/contributing.html) [(3.x)](https://book.cakephp.org/3.0/en/contributing.html) - Version-specific details about contributing to the project diff --git a/app/Controller/PagesController.php b/app/Controller/PagesController.php index f4bc815f9..e9ba9d9bd 100644 --- a/app/Controller/PagesController.php +++ b/app/Controller/PagesController.php @@ -40,7 +40,7 @@ class PagesController extends AppController { /** * Displays a view * - * @return void + * @return \Cake\Network\Response|null * @throws ForbiddenException When a directory traversal attempt. * @throws NotFoundException When the view file could not be found * or MissingViewException in debug mode. diff --git a/app/composer.json b/app/composer.json index bf5c67cc8..d1590e56c 100644 --- a/app/composer.json +++ b/app/composer.json @@ -3,7 +3,7 @@ "description": "CakePHP Application skeleton", "type": "library", "keywords": ["application", "cakephp"], - "homepage": "http://cakephp.org", + "homepage": "https://cakephp.org", "license": "MIT", "authors": [ { @@ -13,17 +13,17 @@ ], "support": { "issues": "https://github.com/cakephp/cakephp/issues", - "forum": "http://stackoverflow.com/tags/cakephp", + "forum": "https://stackoverflow.com/tags/cakephp", "irc": "irc://irc.freenode.org/cakephp", "source": "https://github.com/cakephp/cakephp" }, "require": { "php": ">=5.3.0", - "ext-mcrypt": "*" + "ext-mcrypt": "*", + "cakephp/cakephp": "~2.9" }, "require-dev": { - "phpunit/phpunit": "3.7.*", - "cakephp/cakephp": "~2.9" + "phpunit/phpunit": "3.7.*" }, "suggest": { "cakephp/cakephp-codesniffer": "Easily check code formatting against the CakePHP coding standards." diff --git a/app/webroot/index.php b/app/webroot/index.php index 2bf59fd46..15643e4c6 100644 --- a/app/webroot/index.php +++ b/app/webroot/index.php @@ -82,7 +82,7 @@ if (!defined('WWW_ROOT')) { // For the built-in server if (PHP_SAPI === 'cli-server') { - if ($_SERVER['REQUEST_URI'] !== '/' && file_exists(WWW_ROOT . $_SERVER['PHP_SELF'])) { + if ($_SERVER['PHP_SELF'] !== '/' . basename(__FILE__) && file_exists(WWW_ROOT . $_SERVER['PHP_SELF'])) { return false; } $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); diff --git a/composer.json b/composer.json index 3f3ead208..5b3a2624f 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "The CakePHP framework", "type": "library", "keywords": ["framework"], - "homepage": "http://cakephp.org", + "homepage": "https://cakephp.org", "license": "MIT", "authors": [ { @@ -13,7 +13,7 @@ ], "support": { "issues": "https://github.com/cakephp/cakephp/issues", - "forum": "http://stackoverflow.com/tags/cakephp", + "forum": "https://stackoverflow.com/tags/cakephp", "irc": "irc://irc.freenode.org/cakephp", "source": "https://github.com/cakephp/cakephp" }, diff --git a/lib/Cake/Console/TaskCollection.php b/lib/Cake/Console/TaskCollection.php index 3fafddd13..3c237d26e 100644 --- a/lib/Cake/Console/TaskCollection.php +++ b/lib/Cake/Console/TaskCollection.php @@ -64,7 +64,7 @@ class TaskCollection extends ObjectCollection { * * @param string $task Task name to load * @param array $settings Settings for the task. - * @return Task A task object, Either the existing loaded task or a new one. + * @return AppShell A task object, Either the existing loaded task or a new one. * @throws MissingTaskException when the task could not be found */ public function load($task, $settings = array()) { diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index 54e7034ab..de14b5c07 100644 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -645,6 +645,7 @@ class AuthComponent extends Component { foreach ($this->_authenticateObjects as $auth) { $auth->logout($user); } + static::$_user = array(); $this->Session->delete(static::$sessionKey); $this->Session->delete('Auth.redirect'); $this->Session->renew(); diff --git a/lib/Cake/I18n/L10n.php b/lib/Cake/I18n/L10n.php index fe84bc9fd..c2bc64f56 100644 --- a/lib/Cake/I18n/L10n.php +++ b/lib/Cake/I18n/L10n.php @@ -309,6 +309,7 @@ class L10n { 'ro-ro' => array('language' => 'Romanian (Romania)', 'locale' => 'ro_ro', 'localeFallback' => 'ron', 'charset' => 'utf-8', 'direction' => 'ltr'), 'ru' => array('language' => 'Russian', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'), 'ru-mo' => array('language' => 'Russian (Moldavia)', 'locale' => 'ru_mo', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'ru-ru' => array('language' => 'Russian (Russia)', 'locale' => 'rus', 'localeFallback' => 'rus', 'charset' => 'utf-8', 'direction' => 'ltr'), 'sb' => array('language' => 'Sorbian', 'locale' => 'wen', 'localeFallback' => 'wen', 'charset' => 'utf-8', 'direction' => 'ltr'), 'sk' => array('language' => 'Slovak', 'locale' => 'slk', 'localeFallback' => 'slk', 'charset' => 'utf-8', 'direction' => 'ltr'), 'sl' => array('language' => 'Slovenian', 'locale' => 'slv', 'localeFallback' => 'slv', 'charset' => 'utf-8', 'direction' => 'ltr'), diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index 8d1058a14..96a0183d1 100644 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -223,7 +223,7 @@ class CakeRequest implements ArrayAccess { unset($query[$unsetUrl]); unset($query[$this->base . $unsetUrl]); if (strpos($this->url, '?') !== false) { - list(, $querystr) = explode('?', $this->url); + list($this->url, $querystr) = explode('?', $this->url); parse_str($querystr, $queryArgs); $query += $queryArgs; } @@ -1006,7 +1006,7 @@ class CakeRequest implements ArrayAccess { * @param string $callback A decoding callback that will convert the string data to another * representation. Leave empty to access the raw input data. You can also * supply additional parameters for the decoding callback using var args, see above. - * @return The decoded/processed request data. + * @return mixed The decoded/processed request data. */ public function input($callback = null) { $input = $this->_readInput(); diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index c0141a53f..596bc46f0 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -806,8 +806,8 @@ class CakeResponse { /** * Sets the correct headers to instruct the client to cache the response. * - * @param string $since a valid time since the response text has not been modified - * @param string $time a valid time for cache expiry + * @param string|int $since a valid time since the response text has not been modified + * @param string|int $time a valid time for cache expiry * @return void */ public function cache($since, $time = '+1 day') { @@ -1067,7 +1067,7 @@ class CakeResponse { * Returns a DateTime object initialized at the $time param and using UTC * as timezone * - * @param string|DateTime $time Valid time string or unix timestamp or DateTime object. + * @param DateTime|int|string $time Valid time string or unix timestamp or DateTime object. * @return DateTime */ protected function _getUTCDate($time = null) { diff --git a/lib/Cake/Network/Email/MailTransport.php b/lib/Cake/Network/Email/MailTransport.php index 3b40f21c1..4471394e6 100644 --- a/lib/Cake/Network/Email/MailTransport.php +++ b/lib/Cake/Network/Email/MailTransport.php @@ -72,12 +72,12 @@ class MailTransport extends AbstractTransport { //@codingStandardsIgnoreStart if (!@mail($to, $subject, $message, $headers)) { $error = error_get_last(); - $msg = 'Could not send email: ' . isset($error['message']) ? $error['message'] : 'unknown'; + $msg = 'Could not send email: ' . (isset($error['message']) ? $error['message'] : 'unknown'); throw new SocketException($msg); } } elseif (!@mail($to, $subject, $message, $headers, $params)) { $error = error_get_last(); - $msg = 'Could not send email: ' . isset($error['message']) ? $error['message'] : 'unknown'; + $msg = 'Could not send email: ' . (isset($error['message']) ? $error['message'] : 'unknown'); //@codingStandardsIgnoreEnd throw new SocketException($msg); } diff --git a/lib/Cake/Network/Email/SmtpTransport.php b/lib/Cake/Network/Email/SmtpTransport.php index b0761d496..6e5e6f959 100644 --- a/lib/Cake/Network/Email/SmtpTransport.php +++ b/lib/Cake/Network/Email/SmtpTransport.php @@ -352,7 +352,11 @@ class SmtpTransport extends AbstractTransport { $response = ''; $startTime = time(); while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) { - $response .= $this->_socket->read(); + $bytes = $this->_socket->read(); + if ($bytes === false || $bytes === null) { + break; + } + $response .= $bytes; } if (substr($response, -2) !== "\r\n") { throw new SocketException(__d('cake_dev', 'SMTP timeout.')); diff --git a/lib/Cake/Routing/Router.php b/lib/Cake/Routing/Router.php index b464728c8..3c0688240 100644 --- a/lib/Cake/Routing/Router.php +++ b/lib/Cake/Routing/Router.php @@ -1087,6 +1087,40 @@ class Router { return $out; } +/** + * Reverses a parsed parameter array into an array. + * + * Works similarly to Router::url(), but since parsed URL's contain additional + * 'pass' and 'named' as well as 'url.url' keys. Those keys need to be specially + * handled in order to reverse a params array into a string URL. + * + * This will strip out 'autoRender', 'bare', 'requested', and 'return' param names as those + * are used for CakePHP internals and should not normally be part of an output URL. + * + * @param CakeRequest|array $params The params array or CakeRequest object that needs to be reversed. + * @return array The URL array ready to be used for redirect or HTML link. + */ + public static function reverseToArray($params) { + if ($params instanceof CakeRequest) { + $url = $params->query; + $params = $params->params; + } else { + $url = $params['url']; + } + $pass = isset($params['pass']) ? $params['pass'] : array(); + $named = isset($params['named']) ? $params['named'] : array(); + unset( + $params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'], + $params['autoRender'], $params['bare'], $params['requested'], $params['return'], + $params['_Token'] + ); + $params = array_merge($params, $pass, $named); + if (!empty($url)) { + $params['?'] = $url; + } + return $params; + } + /** * Reverses a parsed parameter array into a string. * @@ -1103,24 +1137,7 @@ class Router { * @return string The string that is the reversed result of the array */ public static function reverse($params, $full = false) { - if ($params instanceof CakeRequest) { - $url = $params->query; - $params = $params->params; - } else { - $url = $params['url']; - } - $pass = isset($params['pass']) ? $params['pass'] : array(); - $named = isset($params['named']) ? $params['named'] : array(); - - unset( - $params['pass'], $params['named'], $params['paging'], $params['models'], $params['url'], $url['url'], - $params['autoRender'], $params['bare'], $params['requested'], $params['return'], - $params['_Token'] - ); - $params = array_merge($params, $pass, $named); - if (!empty($url)) { - $params['?'] = $url; - } + $params = Router::reverseToArray($params, $full); return Router::url($params, $full); } diff --git a/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php index da5b8d7cb..a93514005 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ModelTaskTest.php @@ -315,7 +315,7 @@ class ModelTaskTest extends CakeTestCase { $this->Task->initValidations(); $this->Task->interactive = true; $this->Task->expects($this->any())->method('in') - ->will($this->onConsecutiveCalls('24', 'y', '18', 'n')); + ->will($this->onConsecutiveCalls('26', 'y', '18', 'n')); $result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false)); $expected = array('notBlank' => 'notBlank', 'maxLength' => 'maxLength'); @@ -333,7 +333,7 @@ class ModelTaskTest extends CakeTestCase { $this->Task->interactive = true; $this->Task->expects($this->any())->method('in') - ->will($this->onConsecutiveCalls('999999', '24', 'n')); + ->will($this->onConsecutiveCalls('999999', '26', 'n')); $this->Task->expects($this->at(10))->method('out') ->with($this->stringContains('make a valid')); @@ -368,7 +368,7 @@ class ModelTaskTest extends CakeTestCase { $this->Task->initValidations(); $this->Task->interactive = true; $this->Task->expects($this->any())->method('in') - ->will($this->onConsecutiveCalls('24', 'y', 's')); + ->will($this->onConsecutiveCalls('26', 'y', 's')); $result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false)); $expected = array('notBlank' => 'notBlank', '_skipFields' => true); @@ -384,7 +384,7 @@ class ModelTaskTest extends CakeTestCase { $this->Task->initValidations(); $this->Task->interactive = true; $this->Task->expects($this->any())->method('in') - ->will($this->onConsecutiveCalls('24', 's')); + ->will($this->onConsecutiveCalls('26', 's')); $result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false)); $expected = array('notBlank' => 'notBlank', '_skipFields' => true); @@ -400,7 +400,7 @@ class ModelTaskTest extends CakeTestCase { public function testInteractiveDoValidationWithSkipping() { $this->Task->expects($this->any()) ->method('in') - ->will($this->onConsecutiveCalls('35', '24', 'n', '10', 's')); + ->will($this->onConsecutiveCalls('37', '26', 'n', '10', 's')); $this->Task->interactive = true; $Model = $this->getMock('Model'); $Model->primaryKey = 'id'; diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 5da15d1e9..83202b65b 100644 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -1428,6 +1428,23 @@ class AuthComponentTest extends CakeTestCase { $this->assertNull($this->Auth->Session->read('Auth.redirect')); } +/** + * test that logout removes the active user data as well for stateless auth + * + * @return void + */ + public function testLogoutRemoveUser() { + $oldKey = AuthComponent::$sessionKey; + AuthComponent::$sessionKey = false; + $this->Auth->login(array('id' => 1, 'username' => 'mariano')); + $this->assertSame('mariano', $this->Auth->user('username')); + + $this->Auth->logout(); + AuthComponent::$sessionKey = $oldKey; + + $this->assertNull($this->Auth->user('username')); + } + /** * Logout should trigger a logout method on authentication objects. * diff --git a/lib/Cake/Test/Case/Core/CakeObjectTest.php b/lib/Cake/Test/Case/Core/CakeObjectTest.php index ace73535a..d8fb48c5a 100644 --- a/lib/Cake/Test/Case/Core/CakeObjectTest.php +++ b/lib/Cake/Test/Case/Core/CakeObjectTest.php @@ -86,7 +86,7 @@ class RequestActionController extends Controller { * @return string $this->here. */ public function return_here() { - return $this->here; + return $this->request->here(); } /** @@ -483,6 +483,17 @@ class ObjectTest extends CakeTestCase { $this->assertNull(Router::getRequest(), 'requests were not popped off the stack, this will break url generation'); } +/** + * Test that here() is calculated correctly in requestAction + * + * @return void + */ + public function testRequestActionHere() { + $url = '/request_action/return_here?key=value'; + $result = $this->object->requestAction($url); + $this->assertStringEndsWith($url, $result); + } + /** * test requestAction() and plugins. * diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index 817f8b69c..5c8f72922 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -6987,6 +6987,34 @@ class ModelReadTest extends BaseModelTest { $this->assertEquals($expected, $result); } +/** + * test find('list') method + * + * @return void + */ + public function testFindListZeroValue() { + $this->loadFixtures('Article'); + + $model = new Article(); + $model->displayField = 'title'; + $model->save(array( + 'title' => 'Zeroth Article', + 'user_id' => 0, + 'published' => 'Y' + )); + + $result = $model->find('list', array( + 'fields' => array('title', 'user_id') + )); + $expected = array( + 'Zeroth Article' => 0, + 'First Article' => 1, + 'Second Article' => 3, + 'Third Article' => 1, + ); + $this->assertEquals($expected, $result); + } + /** * Test that find(list) works with array conditions that have only one element. * diff --git a/lib/Cake/Test/Case/Network/CakeRequestTest.php b/lib/Cake/Test/Case/Network/CakeRequestTest.php index 46774d8ce..64f8a14f9 100644 --- a/lib/Cake/Test/Case/Network/CakeRequestTest.php +++ b/lib/Cake/Test/Case/Network/CakeRequestTest.php @@ -193,7 +193,8 @@ class CakeRequestTest extends CakeTestCase { $request = new CakeRequest('some/path?one=something&two=else'); $expected = array('one' => 'something', 'two' => 'else'); $this->assertEquals($expected, $request->query); - $this->assertEquals('some/path?one=something&two=else', $request->url); + $this->assertEquals('some/path', $request->url); + $this->assertStringEndsWith('/some/path?one=something&two=else', $request->here()); } /** diff --git a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php index 712ea54ae..ef5f75b5e 100644 --- a/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php +++ b/lib/Cake/Test/Case/Network/Email/SmtpTransportTest.php @@ -83,11 +83,13 @@ class SmtpTransportTest extends CakeTestCase { */ public function testConnectEhlo() { $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); - $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->any()) + ->method('read') + ->will($this->onConsecutiveCalls( + "220 Welcome message\r\n", + "250 Accepted\r\n" + )); + $this->socket->expects($this->once())->method('write')->with("EHLO localhost\r\n"); $this->SmtpTransport->connect(); } @@ -99,18 +101,14 @@ class SmtpTransportTest extends CakeTestCase { public function testConnectEhloTls() { $this->SmtpTransport->config(array('tls' => true)); $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n")); - $this->socket->expects($this->at(5))->method('write')->with("STARTTLS\r\n"); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("220 Server ready\r\n")); - $this->socket->expects($this->at(8))->method('enableCrypto')->with('tls')->will($this->returnValue(true)); - $this->socket->expects($this->at(9))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(10))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("STARTTLS\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("220 Server ready\r\n")); + $this->socket->expects($this->at(6))->method('enableCrypto')->with('tls')->will($this->returnValue(true)); + $this->socket->expects($this->at(7))->method('write')->with("EHLO localhost\r\n"); + $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250 Accepted\r\n")); $this->SmtpTransport->connect(); } @@ -124,14 +122,11 @@ class SmtpTransportTest extends CakeTestCase { public function testConnectEhloTlsOnNonTlsServer() { $this->SmtpTransport->config(array('tls' => true)); $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n")); - $this->socket->expects($this->at(5))->method('write')->with("STARTTLS\r\n"); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("500 5.3.3 Unrecognized command\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("STARTTLS\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("500 5.3.3 Unrecognized command\r\n")); $this->SmtpTransport->connect(); } @@ -145,14 +140,11 @@ class SmtpTransportTest extends CakeTestCase { public function testConnectEhloNoTlsOnRequiredTlsServer() { $this->SmtpTransport->config(array('tls' => false, 'username' => 'user', 'password' => 'pass')); $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250 Accepted\r\n")); - $this->socket->expects($this->at(5))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized authentication type\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("AUTH LOGIN\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("504 5.7.4 Unrecognized authentication type\r\n")); $this->SmtpTransport->connect(); $this->SmtpTransport->auth(); } @@ -164,14 +156,11 @@ class SmtpTransportTest extends CakeTestCase { */ public function testConnectHelo() { $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); - $this->socket->expects($this->at(5))->method('write')->with("HELO localhost\r\n"); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250 Accepted\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("HELO localhost\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 Accepted\r\n")); $this->SmtpTransport->connect(); } @@ -184,14 +173,11 @@ class SmtpTransportTest extends CakeTestCase { */ public function testConnectFail() { $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); - $this->socket->expects($this->at(5))->method('write')->with("HELO localhost\r\n"); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("HELO localhost\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("200 Not Accepted\r\n")); $this->SmtpTransport->connect(); } @@ -202,14 +188,11 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuth() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("334 Login\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("bWFyaw==\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("334 Pass\r\n")); - $this->socket->expects($this->at(6))->method('write')->with("c3Rvcnk=\r\n"); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("235 OK\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Pass\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("c3Rvcnk=\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("235 OK\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -223,8 +206,7 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuthNotRecognized() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("500 5.3.3 Unrecognized command\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("500 5.3.3 Unrecognized command\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -238,8 +220,8 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuthNotImplemented() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("502 5.3.3 Command not implemented\r\n")); + $this->socket->expects($this->at(1))->method('read') + ->will($this->returnValue("502 5.3.3 Command not implemented\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -253,8 +235,8 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuthBadSequence() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("503 5.5.1 Already authenticated\r\n")); + $this->socket->expects($this->at(1))->method('read') + ->will($this->returnValue("503 5.5.1 Already authenticated\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -268,11 +250,9 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuthBadUsername() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("334 Login\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("bWFyaw==\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -286,14 +266,11 @@ class SmtpTransportTest extends CakeTestCase { */ public function testAuthBadPassword() { $this->socket->expects($this->at(0))->method('write')->with("AUTH LOGIN\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("334 Login\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("bWFyaw==\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("334 Pass\r\n")); - $this->socket->expects($this->at(6))->method('write')->with("c3Rvcnk=\r\n"); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("334 Login\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("bWFyaw==\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("334 Pass\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("c3Rvcnk=\r\n"); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("535 5.7.8 Authentication failed\r\n")); $this->SmtpTransport->config(array('username' => 'mark', 'password' => 'story')); $this->SmtpTransport->auth(); } @@ -323,20 +300,15 @@ class SmtpTransportTest extends CakeTestCase { $email->cc(array('mark@cakephp.org' => 'Mark Story', 'juan@cakephp.org' => 'Juan Basso')); $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("RCPT TO:\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(4))->method('write')->with("RCPT TO:\r\n"); $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(6))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(9))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(10))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(12))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(13))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(14))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(6))->method('write')->with("RCPT TO:\r\n"); + $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(8))->method('write')->with("RCPT TO:\r\n"); + $this->socket->expects($this->at(9))->method('read')->will($this->returnValue("250 OK\r\n")); $this->SmtpTransport->sendRcpt($email); } @@ -353,11 +325,9 @@ class SmtpTransportTest extends CakeTestCase { $email->returnPath('pleasereply@cakephp.org', 'CakePHP Return'); $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("RCPT TO:\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 OK\r\n")); $this->SmtpTransport->sendRcpt($email); } @@ -398,11 +368,9 @@ class SmtpTransportTest extends CakeTestCase { $data .= "\r\n\r\n.\r\n"; $this->socket->expects($this->at(0))->method('write')->with("DATA\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("354 OK\r\n")); - $this->socket->expects($this->at(3))->method('write')->with($data); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("354 OK\r\n")); + $this->socket->expects($this->at(2))->method('write')->with($data); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 OK\r\n")); $this->SmtpTransport->sendData($email); } @@ -443,20 +411,18 @@ class SmtpTransportTest extends CakeTestCase { $this->assertEmpty($this->SmtpTransport->getLastResponse()); $this->socket->expects($this->any())->method('connect')->will($this->returnValue(true)); - $this->socket->expects($this->at(0))->method('read')->will($this->returnValue(false)); $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("220 Welcome message\r\n")); $this->socket->expects($this->at(2))->method('write')->with("EHLO localhost\r\n"); - $this->socket->expects($this->at(3))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250-PIPELINING\r\n")); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250-SIZE 102400000\r\n")); - $this->socket->expects($this->at(6))->method('read')->will($this->returnValue("250-VRFY\r\n")); - $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250-ETRN\r\n")); - $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250-STARTTLS\r\n")); - $this->socket->expects($this->at(9))->method('read')->will($this->returnValue("250-AUTH PLAIN LOGIN\r\n")); - $this->socket->expects($this->at(10))->method('read')->will($this->returnValue("250-AUTH=PLAIN LOGIN\r\n")); - $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250-ENHANCEDSTATUSCODES\r\n")); - $this->socket->expects($this->at(12))->method('read')->will($this->returnValue("250-8BITMIME\r\n")); - $this->socket->expects($this->at(13))->method('read')->will($this->returnValue("250 DSN\r\n")); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250-PIPELINING\r\n")); + $this->socket->expects($this->at(4))->method('read')->will($this->returnValue("250-SIZE 102400000\r\n")); + $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250-VRFY\r\n")); + $this->socket->expects($this->at(6))->method('read')->will($this->returnValue("250-ETRN\r\n")); + $this->socket->expects($this->at(7))->method('read')->will($this->returnValue("250-STARTTLS\r\n")); + $this->socket->expects($this->at(8))->method('read')->will($this->returnValue("250-AUTH PLAIN LOGIN\r\n")); + $this->socket->expects($this->at(9))->method('read')->will($this->returnValue("250-AUTH=PLAIN LOGIN\r\n")); + $this->socket->expects($this->at(10))->method('read')->will($this->returnValue("250-ENHANCEDSTATUSCODES\r\n")); + $this->socket->expects($this->at(11))->method('read')->will($this->returnValue("250-8BITMIME\r\n")); + $this->socket->expects($this->at(12))->method('read')->will($this->returnValue("250 DSN\r\n")); $this->SmtpTransport->connect(); $expected = array( @@ -479,11 +445,9 @@ class SmtpTransportTest extends CakeTestCase { $email->to('cake@cakephp.org', 'CakePHP'); $this->socket->expects($this->at(0))->method('write')->with("MAIL FROM:\r\n"); - $this->socket->expects($this->at(1))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(2))->method('read')->will($this->returnValue("250 OK\r\n")); - $this->socket->expects($this->at(3))->method('write')->with("RCPT TO:\r\n"); - $this->socket->expects($this->at(4))->method('read')->will($this->returnValue(false)); - $this->socket->expects($this->at(5))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(1))->method('read')->will($this->returnValue("250 OK\r\n")); + $this->socket->expects($this->at(2))->method('write')->with("RCPT TO:\r\n"); + $this->socket->expects($this->at(3))->method('read')->will($this->returnValue("250 OK\r\n")); $this->SmtpTransport->sendRcpt($email); diff --git a/lib/Cake/Test/Case/Routing/RouterTest.php b/lib/Cake/Test/Case/Routing/RouterTest.php index fc4298246..46d89fac6 100644 --- a/lib/Cake/Test/Case/Routing/RouterTest.php +++ b/lib/Cake/Test/Case/Routing/RouterTest.php @@ -2464,9 +2464,8 @@ class RouterTest extends CakeTestCase { * * @return void */ - public function testRouterReverse() { + public function testReverseToken() { Router::$initialized = true; - $params = array( 'controller' => 'posts', 'action' => 'view', @@ -2481,17 +2480,21 @@ class RouterTest extends CakeTestCase { ); $result = Router::reverse($params); $this->assertEquals('/posts/view/1', $result); + } + public function testReverseNamed() { $params = array( 'controller' => 'posts', 'action' => 'index', 'pass' => array(1), 'named' => array('page' => 1, 'sort' => 'Article.title', 'direction' => 'desc'), - 'url' => array() + 'url' => array(), ); $result = Router::reverse($params); $this->assertEquals('/posts/index/1/page:1/sort:Article.title/direction:desc', $result); + } + public function testReverseLocalized() { Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}')); $params = array( 'lang' => 'eng', @@ -2499,11 +2502,14 @@ class RouterTest extends CakeTestCase { 'action' => 'view', 'pass' => array(1), 'named' => array(), - 'url' => array('url' => 'eng/posts/view/1') + 'url' => array('url' => 'eng/posts/view/1'), ); $result = Router::reverse($params); $this->assertEquals('/eng/posts/view/1', $result); + } + public function testReverseArrayQuery() { + Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}')); $params = array( 'lang' => 'eng', 'controller' => 'posts', @@ -2512,11 +2518,14 @@ class RouterTest extends CakeTestCase { 'named' => array(), 'url' => array('url' => 'eng/posts/view/1', 'foo' => 'bar', 'baz' => 'quu'), 'paging' => array(), - 'models' => array() + 'models' => array(), ); $result = Router::reverse($params); $this->assertEquals('/eng/posts/view/1?foo=bar&baz=quu', $result); + } + public function testReverseCakeRequestQuery() { + Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}')); $request = new CakeRequest('/eng/posts/view/1'); $request->addParams(array( 'lang' => 'eng', @@ -2529,19 +2538,64 @@ class RouterTest extends CakeTestCase { $result = Router::reverse($request); $expected = '/eng/posts/view/1?test=value'; $this->assertEquals($expected, $result); + } + public function testReverseFull() { + Router::connect('/:lang/:controller/:action/*', array(), array('lang' => '[a-z]{3}')); $params = array( 'lang' => 'eng', 'controller' => 'posts', 'action' => 'view', 'pass' => array(1), 'named' => array(), - 'url' => array('url' => 'eng/posts/view/1') + 'url' => array('url' => 'eng/posts/view/1'), ); $result = Router::reverse($params, true); $this->assertRegExp('/^http(s)?:\/\//', $result); } + public function testReverseToArrayNamed() { + $params = array( + 'controller' => 'posts', + 'action' => 'index', + 'pass' => array(123), + 'named' => array('page' => 123, 'sort' => 'Article.title', 'direction' => 'desc'), + 'url' => array(), + ); + $result = Router::reverseToArray($params); + $expected = array( + 'controller' => 'posts', + 'action' => 'index', + 123, + 'page' => 123, + 'sort' => 'Article.title', + 'direction' => 'desc', + ); + $this->assertEquals($expected, $result); + } + + public function testReverseToArrayCakeRequestQuery() { + $request = new CakeRequest('/posts/view/123'); + $request->addParams(array( + 'controller' => 'posts', + 'action' => 'view', + 'pass' => array(123), + 'named' => array(), + )); + $request->query = array('url' => 'eng/posts/view/123', 'test' => 'value'); + $result = Router::reverseToArray($request); + $expected = array( + 'plugin' => null, + 'controller' => 'posts', + 'action' => 'view', + 123, + '?' => array( + 'test' => 'value', + ), + ); + $this->assertEquals($expected, $result); + } + /** * Test that extensions work with Router::reverse() * diff --git a/lib/Cake/Test/Case/Utility/HashTest.php b/lib/Cake/Test/Case/Utility/HashTest.php index 53b1d40c8..74954b6a0 100644 --- a/lib/Cake/Test/Case/Utility/HashTest.php +++ b/lib/Cake/Test/Case/Utility/HashTest.php @@ -236,10 +236,13 @@ class HashTest extends CakeTestCase { */ public function testGetEmptyKey() { $data = array( - '' => 'some value' + true => 'true value', + false => 'false value', + '' => 'some value', ); - $result = Hash::get($data, ''); - $this->assertSame($data[''], $result); + $this->assertSame($data[''], Hash::get($data, '')); + $this->assertSame($data[false], Hash::get($data, false)); + $this->assertSame($data[true], Hash::get($data, true)); } /** @@ -249,7 +252,7 @@ class HashTest extends CakeTestCase { * @return void */ public function testGetInvalidPath() { - Hash::get(array('one' => 'two'), true); + Hash::get(array('one' => 'two'), new StdClass()); } /** @@ -652,8 +655,21 @@ class HashTest extends CakeTestCase { * @return void */ public function testFilter() { - $result = Hash::filter(array('0', false, true, 0, array('one thing', 'I can tell you', 'is you got to be', false))); - $expected = array('0', 2 => true, 3 => 0, 4 => array('one thing', 'I can tell you', 'is you got to be')); + $result = Hash::filter(array( + '0', + false, + true, + 0, + 0.0, + array('one thing', 'I can tell you', 'is you got to be', false) + )); + $expected = array( + '0', + 2 => true, + 3 => 0, + 4 => 0.0, + 5 => array('one thing', 'I can tell you', 'is you got to be') + ); $this->assertSame($expected, $result); $result = Hash::filter(array(1, array(false))); diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index 9b34c03b1..bec844dcb 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -1825,6 +1825,20 @@ class ValidationTest extends CakeTestCase { $this->assertFalse(Validation::maxLength('ÆΔΩЖÇ', 3)); } +/** + * maxLengthBytes method + * + * @return void + */ + public function testMaxLengthBytes() { + $this->assertTrue(Validation::maxLengthBytes('ab', 3)); + $this->assertTrue(Validation::maxLengthBytes('abc', 3)); + $this->assertTrue(Validation::maxLengthBytes('ÆΔΩЖÇ', 10)); + $this->assertTrue(Validation::maxLengthBytes('ÆΔΩЖÇ', 11)); + $this->assertFalse(Validation::maxLengthBytes('abcd', 3)); + $this->assertFalse(Validation::maxLengthBytes('ÆΔΩЖÇ', 9)); + } + /** * testMinLength method * @@ -1839,6 +1853,20 @@ class ValidationTest extends CakeTestCase { $this->assertTrue(Validation::minLength('ÆΔΩЖÇ', 2)); } +/** + * minLengthBytes method + * + * @return void + */ + public function testMinLengthBytes() { + $this->assertFalse(Validation::minLengthBytes('ab', 3)); + $this->assertFalse(Validation::minLengthBytes('ÆΔΩЖÇ', 11)); + $this->assertTrue(Validation::minLengthBytes('abc', 3)); + $this->assertTrue(Validation::minLengthBytes('abcd', 3)); + $this->assertTrue(Validation::minLengthBytes('ÆΔΩЖÇ', 10)); + $this->assertTrue(Validation::minLengthBytes('ÆΔΩЖÇ', 9)); + } + /** * testUrl method * diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index a33663f75..01b13eac6 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -167,6 +167,18 @@ class XmlTest extends CakeTestCase { $this->assertNotRegExp('/encoding/', $obj->saveXML()); } +/** + * test build() method with huge option + * + * @return void + */ + public function testBuildHuge() { + $xml = 'value'; + $obj = Xml::build($xml, array('parseHuge' => true)); + $this->assertEquals('tag', $obj->getName()); + $this->assertEquals('value', (string)$obj); + } + /** * Test that the readFile option disables local file parsing. * diff --git a/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php b/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php index e78cda0c6..7c31c2cd1 100644 --- a/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FlashHelperTest.php @@ -88,6 +88,14 @@ class FlashHelperTest extends CakeTestCase { 'element' => 'flash_classy', 'params' => array() ) + ), + 'default' => array( + array( + 'key' => 'default', + 'message' => 'Default', + 'element' => 'default', + 'params' => array() + ) ) ) )); @@ -166,4 +174,13 @@ class FlashHelperTest extends CakeTestCase { $expected = 'this is the plugin element'; $this->assertContains($expected, $result); } + +/** + * Test that the default element fallbacks to the Flash/default element. + */ + public function testFlashFallback() { + $result = $this->Flash->render('default'); + $expected = '
Default
'; + $this->assertContains($expected, $result); + } } diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index d38f7e79c..9169333d5 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -5291,6 +5291,60 @@ class FormHelperTest extends CakeTestCase { $this->assertTags($result, $expected); } +/** + * test that select() with numeric label of optiongroup. + * + * @return void + */ + public function testSelectOptionGroupWithNumericLabel() { + $options = array( + 1 => array( + 1 => '1Foo', + 2 => '2Bar', + 3 => '3Baz', + 4 => '1', + 5 => '2', + 6 => '3', + 7 => 1, + 8 => 2, + 9 => 3, + ), + 2 => array( + 11 => '1Foo', + 12 => '2Bar', + 13 => '3Baz', + 14 => '1', + 15 => '2', + 16 => '3', + 17 => 1, + 18 => 2, + 19 => 3, + ), + ); + $result = $this->Form->select('Model.field', $options, array('empty' => false)); + $expected = array( + 'select' => array('name' => 'data[Model][field]', 'id' => 'ModelField'), + array('optgroup' => array('label' => '1')), + array('option' => array('value' => '1')), '1Foo', '/option', + array('option' => array('value' => '2')), '2Bar', '/option', + array('option' => array('value' => '3')), '3Baz', '/option', + array('option' => array('value' => '6')), '3', '/option', + array('option' => array('value' => '9')), '3', '/option', + '/optgroup', + array('optgroup' => array('label' => '2')), + array('option' => array('value' => '11')), '1Foo', '/option', + array('option' => array('value' => '12')), '2Bar', '/option', + array('option' => array('value' => '13')), '3Baz', '/option', + array('option' => array('value' => '14')), '1', '/option', + array('option' => array('value' => '16')), '3', '/option', + array('option' => array('value' => '17')), '1', '/option', + array('option' => array('value' => '19')), '3', '/option', + '/optgroup', + '/select' + ); + $this->assertTags($result, $expected); + } + /** * test that select() with optiongroups listens to the escape param. * @@ -11029,4 +11083,72 @@ class FormHelperTest extends CakeTestCase { $this->assertAttributeEquals($here, '_lastAction', $this->Form, "_lastAction shouldn't be empty."); } +/** + * Tests the 'errorClass' option when error is returned. + * + * @return void + */ + public function testInputErrorClass() { + $Contact = ClassRegistry::getObject('Contact'); + $Contact->validationErrors['field'] = array('Badness!'); + + $result = $this->Form->input('Contact.field', array( + 'type' => 'text', + 'div' => array('errorClass' => 'has-error') + )); + $expected = array( + 'div' => array('class' => 'input text has-error'), + 'label' => array('for' => 'ContactField'), + 'Field', + '/label', + 'input' => array( + 'type' => 'text', 'name' => 'data[Contact][field]', + 'id' => 'ContactField', 'class' => 'form-error' + ), + array('div' => array('class' => 'error-message')), + 'Badness!', + '/div' + ); + $this->assertTags($result, $expected); + + $result = $this->Form->input('Contact.field', array( + 'type' => 'text', + 'error' => array('attributes' => array('class' => 'error')) + )); + $expected = array( + 'div' => array('class' => 'input text error'), + 'label' => array('for' => 'ContactField'), + 'Field', + '/label', + 'input' => array( + 'type' => 'text', 'name' => 'data[Contact][field]', + 'id' => 'ContactField', 'class' => 'form-error' + ), + array('div' => array('class' => 'error')), + 'Badness!', + '/div' + ); + $this->assertTags($result, $expected); + + $result = $this->Form->input('Contact.field', array( + 'type' => 'text', + 'div' => array('errorClass' => 'has-error'), + 'error' => array('attributes' => array('class' => 'form-control-feedback')) + )); + $expected = array( + 'div' => array('class' => 'input text has-error'), + 'label' => array('for' => 'ContactField'), + 'Field', + '/label', + 'input' => array( + 'type' => 'text', 'name' => 'data[Contact][field]', + 'id' => 'ContactField', 'class' => 'form-error' + ), + array('div' => array('class' => 'form-control-feedback')), + 'Badness!', + '/div' + ); + $this->assertTags($result, $expected); + } + } diff --git a/lib/Cake/Utility/Hash.php b/lib/Cake/Utility/Hash.php index 2ca530ece..258fbdf67 100644 --- a/lib/Cake/Utility/Hash.php +++ b/lib/Cake/Utility/Hash.php @@ -48,11 +48,13 @@ class Hash { } if (is_string($path) || is_numeric($path)) { $parts = explode('.', $path); + } elseif (is_bool($path) || $path === null) { + $parts = array($path); } else { if (!is_array($path)) { throw new InvalidArgumentException(__d('cake_dev', - 'Invalid Parameter %s, should be dot separated path or array.', - $path + 'Invalid path parameter: %s, should be dot separated path or array.', + var_export($path, true) )); } $parts = $path; @@ -571,7 +573,7 @@ class Hash { * @return bool */ protected static function _filter($var) { - if ($var === 0 || $var === '0' || !empty($var)) { + if ($var === 0 || $var === 0.0 || $var === '0' || !empty($var)) { return true; } return false; diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 123388463..da614de1d 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -539,7 +539,7 @@ class Validation { } /** - * Checks whether the length of a string is greater or equal to a minimal length. + * Checks whether the length of a string (in characters) is greater or equal to a minimal length. * * @param string $check The string to test * @param int $min The minimal string length @@ -550,7 +550,7 @@ class Validation { } /** - * Checks whether the length of a string is smaller or equal to a maximal length.. + * Checks whether the length of a string (in characters) is smaller or equal to a maximal length.. * * @param string $check The string to test * @param int $max The maximal string length @@ -560,6 +560,28 @@ class Validation { return mb_strlen($check) <= $max; } +/** + * Checks whether the length of a string (in bytes) is greater or equal to a minimal length. + * + * @param string $check The string to test + * @param int $min The minimal string length + * @return bool Success + */ + public static function minLengthBytes($check, $min) { + return strlen($check) >= $min; + } + +/** + * Checks whether the length of a string (in bytes) is smaller or equal to a maximal length.. + * + * @param string $check The string to test + * @param int $max The maximal string length + * @return bool Success + */ + public static function maxLengthBytes($check, $max) { + return strlen($check) <= $max; + } + /** * Checks that a value is a monetary amount. * diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index f27fc1497..a5a6876e3 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -80,7 +80,9 @@ class Xml { * - `readFile` Set to false to disable file reading. This is important to disable when * putting user data into Xml::build(). If enabled local & remote files will be read if they exist. * Defaults to true for backwards compatibility reasons. - * - If using array as input, you can pass `options` from Xml::fromArray. + * - `parseHuge` Enable the `LIBXML_PARSEHUGE` + * + * If using array as input, you can pass `options` from Xml::fromArray. * * @param string|array $input XML string, a path to a file, a URL or an array * @param array $options The options to use @@ -94,7 +96,8 @@ class Xml { $defaults = array( 'return' => 'simplexml', 'loadEntities' => false, - 'readFile' => true + 'readFile' => true, + 'parseHuge' => true ); $options += $defaults; @@ -135,6 +138,10 @@ class Xml { if ($hasDisable && !$options['loadEntities']) { libxml_disable_entity_loader(true); } + $flags = LIBXML_NOCDATA; + if (!empty($options['parseHuge'])) { + $flags |= LIBXML_PARSEHUGE; + } try { if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') { $xml = new SimpleXMLElement($input, LIBXML_NOCDATA); diff --git a/lib/Cake/View/Helper/FlashHelper.php b/lib/Cake/View/Helper/FlashHelper.php index 3675a8245..774c2d94d 100644 --- a/lib/Cake/View/Helper/FlashHelper.php +++ b/lib/Cake/View/Helper/FlashHelper.php @@ -88,6 +88,9 @@ class FlashHelper extends AppHelper { foreach ($flash as $message) { $message['key'] = $key; $message = $options + $message; + if ($message['element'] === 'default') { + $message['element'] = 'Flash/default'; + } $out .= $this->_View->element($message['element'], $message); } diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index c828d141c..0bb58a9be 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -1089,7 +1089,7 @@ class FormHelper extends AppHelper { if ($type !== 'hidden' && $error !== false) { $errMsg = $this->error($fieldName, $error); if ($errMsg) { - $divOptions = $this->addClass($divOptions, 'error'); + $divOptions = $this->addClass($divOptions, Hash::get($divOptions, 'errorClass', 'error')); if ($errorMessage) { $out['error'] = $errMsg; } @@ -1109,7 +1109,7 @@ class FormHelper extends AppHelper { if (!empty($divOptions['tag'])) { $tag = $divOptions['tag']; - unset($divOptions['tag']); + unset($divOptions['tag'], $divOptions['errorClass']); $output = $this->Html->tag($tag, $output, $divOptions); } return $output; @@ -2860,7 +2860,7 @@ class FormHelper extends AppHelper { } else { $select[] = $this->Html->useTag('optiongroupend'); } - $parents[] = $name; + $parents[] = (string)$name; } $select = array_merge($select, $this->_selectOptions( $title, $parents, $showParents, $attributes