diff --git a/.stickler.yml b/.stickler.yml new file mode 100644 index 00000000000..61243f09079 --- /dev/null +++ b/.stickler.yml @@ -0,0 +1,2 @@ +branches: + ignore: ['2.x', '2.next'] diff --git a/.travis.yml b/.travis.yml index 900901156f3..b43db1d259b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ before_script: - sh -c "composer global require 'phpunit/phpunit=3.7.33'" - sh -c "ln -s ~/.composer/vendor/phpunit/phpunit/PHPUnit ./vendors/PHPUnit" - sudo locale-gen de_DE + - sudo locale-gen es_ES - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test2;'; fi" - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test3;'; fi" @@ -41,6 +42,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 + - echo -e "extension = apcu.so\napc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - phpenv rehash - set +H - echo "getMessage(); if (method_exists($connectionError, 'getAttributes')): $attributes = $connectionError->getAttributes(); - if (isset($errorMsg['message'])): + if (isset($attributes['message'])): $errorMsg .= '
' . $attributes['message']; endif; endif; @@ -209,8 +209,10 @@ You can also add some CSS styles for your pages at: %s.',
  • -
  • -
  • +
  • CakePHP Official Forum +
  • +
  • +
  • irc.freenode.net #cakephp
  • diff --git a/app/composer.json b/app/composer.json new file mode 100644 index 00000000000..e05128e7247 --- /dev/null +++ b/app/composer.json @@ -0,0 +1,37 @@ +{ + "name": "cakephp/app", + "description": "CakePHP Application skeleton", + "type": "library", + "keywords": ["application", "cakephp"], + "homepage": "http://cakephp.org", + "license": "MIT", + "authors": [ + { + "name": "CakePHP Community", + "homepage": "https://github.com/cakephp/cakephp/graphs/contributors" + } + ], + "support": { + "issues": "https://github.com/cakephp/cakephp/issues", + "forum": "http://stackoverflow.com/tags/cakephp", + "irc": "irc://irc.freenode.org/cakephp", + "source": "https://github.com/cakephp/cakephp" + }, + "require": { + "php": ">=5.3.0", + "ext-mcrypt": "*" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "cakephp/cakephp": "~2.8" + }, + "suggest": { + "cakephp/cakephp-codesniffer": "Easily check code formatting against the CakePHP coding standards." + }, + "bin": [ + "lib/Cake/Console/cake" + ], + "config": { + "vendor-dir": "Vendor/" + } +} diff --git a/build.xml b/build.xml index 786916b99d4..98c48bae936 100644 --- a/build.xml +++ b/build.xml @@ -213,10 +213,10 @@ --> - + - + diff --git a/composer.json b/composer.json index 5c2ac669232..3f3ead20804 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,11 @@ }, "require-dev": { "phpunit/phpunit": "3.7.*", - "cakephp/debug_kit" : "^2.2.0", "cakephp/cakephp-codesniffer": "^1.0.0" }, + "config": { + "vendor-dir": "vendors/" + }, "bin": [ "lib/Cake/Console/cake" ] diff --git a/lib/Cake/Cache/Engine/ApcEngine.php b/lib/Cake/Cache/Engine/ApcEngine.php index da31651e31c..8500560dfe5 100644 --- a/lib/Cake/Cache/Engine/ApcEngine.php +++ b/lib/Cake/Cache/Engine/ApcEngine.php @@ -31,6 +31,13 @@ class ApcEngine extends CacheEngine { */ protected $_compiledGroupNames = array(); +/** + * APC or APCu extension + * + * @var string + */ + protected $_apcExtension = 'apc'; + /** * Initialize the Cache Engine * @@ -47,6 +54,10 @@ public function init($settings = array()) { } $settings += array('engine' => 'Apc'); parent::init($settings); + if (function_exists('apcu_dec')) { + $this->_apcExtension = 'apcu'; + return true; + } return function_exists('apc_dec'); } @@ -63,8 +74,9 @@ public function write($key, $value, $duration) { if ($duration) { $expires = time() + $duration; } - apc_store($key . '_expires', $expires, $duration); - return apc_store($key, $value, $duration); + $func = $this->_apcExtension . '_store'; + $func($key . '_expires', $expires, $duration); + return $func($key, $value, $duration); } /** @@ -75,11 +87,12 @@ public function write($key, $value, $duration) { */ public function read($key) { $time = time(); - $cachetime = (int)apc_fetch($key . '_expires'); + $func = $this->_apcExtension . '_fetch'; + $cachetime = (int)$func($key . '_expires'); if ($cachetime !== 0 && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { return false; } - return apc_fetch($key); + return $func($key); } /** @@ -90,7 +103,8 @@ public function read($key) { * @return New incremented value, false otherwise */ public function increment($key, $offset = 1) { - return apc_inc($key, $offset); + $func = $this->_apcExtension . '_inc'; + return $func($key, $offset); } /** @@ -101,7 +115,8 @@ public function increment($key, $offset = 1) { * @return New decremented value, false otherwise */ public function decrement($key, $offset = 1) { - return apc_dec($key, $offset); + $func = $this->_apcExtension . '_dec'; + return $func($key, $offset); } /** @@ -111,7 +126,8 @@ public function decrement($key, $offset = 1) { * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { - return apc_delete($key); + $func = $this->_apcExtension . '_delete'; + return $func($key); } /** @@ -125,19 +141,20 @@ public function clear($check) { if ($check) { return true; } + $func = $this->_apcExtension . '_delete'; if (class_exists('APCIterator', false)) { $iterator = new APCIterator( 'user', '/^' . preg_quote($this->settings['prefix'], '/') . '/', APC_ITER_NONE ); - apc_delete($iterator); + $func($iterator); return true; } - $cache = apc_cache_info('user'); + $cache = $this->_apcExtension === 'apc' ? apc_cache_info('user') : apcu_cache_info(); foreach ($cache['cache_list'] as $key) { if (strpos($key['info'], $this->settings['prefix']) === 0) { - apc_delete($key['info']); + $func($key['info']); } } return true; @@ -157,11 +174,13 @@ public function groups() { } } - $groups = apc_fetch($this->_compiledGroupNames); + $fetchFunc = $this->_apcExtension . '_fetch'; + $storeFunc = $this->_apcExtension . '_store'; + $groups = $fetchFunc($this->_compiledGroupNames); if (count($groups) !== count($this->settings['groups'])) { foreach ($this->_compiledGroupNames as $group) { if (!isset($groups[$group])) { - apc_store($group, 1); + $storeFunc($group, 1); $groups[$group] = 1; } } @@ -184,7 +203,8 @@ public function groups() { * @return bool success */ public function clearGroup($group) { - apc_inc($this->settings['prefix'] . $group, 1, $success); + $func = $this->_apcExtension . '_inc'; + $func($this->settings['prefix'] . $group, 1, $success); return $success; } @@ -203,7 +223,8 @@ public function add($key, $value, $duration) { if ($duration) { $expires = time() + $duration; } - apc_add($key . '_expires', $expires, $duration); - return apc_add($key, $value, $duration); + $func = $this->_apcExtension . '_add'; + $func($key . '_expires', $expires, $duration); + return $func($key, $value, $duration); } } diff --git a/lib/Cake/Cache/Engine/FileEngine.php b/lib/Cake/Cache/Engine/FileEngine.php index d650e60ee6c..5bbbd937e87 100644 --- a/lib/Cake/Cache/Engine/FileEngine.php +++ b/lib/Cake/Cache/Engine/FileEngine.php @@ -132,7 +132,7 @@ public function write($key, $data, $duration) { } $expires = time() + $duration; - $contents = $expires . $lineBreak . $data . $lineBreak; + $contents = implode(array($expires, $lineBreak, $data, $lineBreak)); if ($this->settings['lock']) { $this->_File->flock(LOCK_EX); diff --git a/lib/Cake/Console/Command/Task/ControllerTask.php b/lib/Cake/Console/Command/Task/ControllerTask.php index fc2d9134348..f9f8f70ed49 100644 --- a/lib/Cake/Console/Command/Task/ControllerTask.php +++ b/lib/Cake/Console/Command/Task/ControllerTask.php @@ -182,11 +182,11 @@ protected function _interactive() { $components = $this->doComponents(); $wannaUseSession = $this->in( - __d('cake_console', "Would you like to use Session flash messages?"), array('y', 'n'), 'y' + __d('cake_console', "Would you like to use the FlashComponent to display flash messages?"), array('y', 'n'), 'y' ); if (strtolower($wannaUseSession) === 'y') { - array_push($components, 'Session'); + array_push($components, 'Session', 'Flash'); } array_unique($components); } @@ -384,9 +384,9 @@ public function doHelpers() { * @return array Components the user wants to use. */ public function doComponents() { - $components = array('Paginator', 'Flash'); + $components = array('Paginator'); return array_merge($components, $this->_doPropertyChoices( - __d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent and FlashComponent?"), + __d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent?"), __d('cake_console', "Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'") )); } diff --git a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp index ad84a25539e..6484c83d937 100644 --- a/lib/Cake/Console/Templates/skel/View/Pages/home.ctp +++ b/lib/Cake/Console/Templates/skel/View/Pages/home.ctp @@ -214,6 +214,8 @@ You can also add some CSS styles for your pages at: %s.',
  • +
  • +
  • irc.freenode.net #cakephp
  • diff --git a/lib/Cake/Controller/Component/AuthComponent.php b/lib/Cake/Controller/Component/AuthComponent.php index 3381d68f1fc..3e62e2cc9b6 100644 --- a/lib/Cake/Controller/Component/AuthComponent.php +++ b/lib/Cake/Controller/Component/AuthComponent.php @@ -359,7 +359,7 @@ protected function _unauthenticated(Controller $controller) { return true; } - if (!$controller->request->is('ajax')) { + if (!$controller->request->is('ajax') && !$controller->request->is('json')) { $this->flash($this->authError); $this->Session->write('Auth.redirect', $controller->request->here(false)); $controller->redirect($this->loginAction); @@ -611,8 +611,12 @@ public function login($user = null) { $user = $this->identify($this->request, $this->response); } if ($user) { - $this->Session->renew(); - $this->Session->write(static::$sessionKey, $user); + if (static::$sessionKey) { + $this->Session->renew(); + $this->Session->write(static::$sessionKey, $user); + } else { + static::$_user = $user; + } $event = new CakeEvent('Auth.afterIdentify', $this, array('user' => $user)); $this->_Collection->getController()->getEventManager()->dispatch($event); } diff --git a/lib/Cake/Controller/Component/RequestHandlerComponent.php b/lib/Cake/Controller/Component/RequestHandlerComponent.php index ea1d727045b..9c3bc56a283 100644 --- a/lib/Cake/Controller/Component/RequestHandlerComponent.php +++ b/lib/Cake/Controller/Component/RequestHandlerComponent.php @@ -505,7 +505,12 @@ public function accepts($type = null) { * in the request content type will be returned. */ public function requestedWith($type = null) { - if (!$this->request->is('post') && !$this->request->is('put') && !$this->request->is('delete')) { + if ( + !$this->request->is('patch') && + !$this->request->is('post') && + !$this->request->is('put') && + !$this->request->is('delete') + ) { return null; } if (is_array($type)) { diff --git a/lib/Cake/I18n/L10n.php b/lib/Cake/I18n/L10n.php index 7e57a36fbd0..a87be16c022 100644 --- a/lib/Cake/I18n/L10n.php +++ b/lib/Cake/I18n/L10n.php @@ -272,6 +272,7 @@ class L10n { 'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'), 'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'), 'is' => array('language' => 'Icelandic', 'locale' => 'isl', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'), + 'is-is' => array('language' => 'Icelandic (Iceland)', 'locale' => 'is_is', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'), 'it' => array('language' => 'Italian', 'locale' => 'ita', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), 'it-ch' => array('language' => 'Italian (Swiss) ', 'locale' => 'it_ch', 'localeFallback' => 'ita', 'charset' => 'utf-8', 'direction' => 'ltr'), 'ja' => array('language' => 'Japanese', 'locale' => 'jpn', 'localeFallback' => 'jpn', 'charset' => 'utf-8', 'direction' => 'ltr'), diff --git a/lib/Cake/Model/Datasource/Database/Sqlite.php b/lib/Cake/Model/Datasource/Database/Sqlite.php index 9bdc81c4cf7..7dd81f8a461 100644 --- a/lib/Cake/Model/Datasource/Database/Sqlite.php +++ b/lib/Cake/Model/Datasource/Database/Sqlite.php @@ -185,6 +185,9 @@ public function describe($model) { 'default' => $default, 'length' => $this->length($column['type']) ); + if (in_array($fields[$column['name']]['type'], array('timestamp', 'datetime')) && strtoupper($fields[$column['name']]['default']) === 'CURRENT_TIMESTAMP') { + $fields[$column['name']]['default'] = null; + } if ($column['pk'] == 1) { $fields[$column['name']]['key'] = $this->index['PRI']; $fields[$column['name']]['null'] = false; diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index 357f902e834..bdd76e98641 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -2302,6 +2302,7 @@ public function begin() { $this->_transactionNesting = 0; if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery('BEGIN'); } return $this->_transactionStarted = $this->_connection->beginTransaction(); @@ -2315,6 +2316,7 @@ public function begin() { protected function _beginNested() { $query = 'SAVEPOINT LEVEL' . ++$this->_transactionNesting; if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery($query); } $this->_connection->exec($query); @@ -2335,6 +2337,7 @@ public function commit() { if ($this->_transactionNesting === 0) { if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery('COMMIT'); } $this->_transactionStarted = false; @@ -2357,6 +2360,7 @@ public function commit() { protected function _commitNested() { $query = 'RELEASE SAVEPOINT LEVEL' . $this->_transactionNesting--; if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery($query); } $this->_connection->exec($query); @@ -2377,6 +2381,7 @@ public function rollback() { if ($this->_transactionNesting === 0) { if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery('ROLLBACK'); } $this->_transactionStarted = false; @@ -2399,6 +2404,7 @@ public function rollback() { protected function _rollbackNested() { $query = 'ROLLBACK TO SAVEPOINT LEVEL' . $this->_transactionNesting--; if ($this->fullDebug) { + $this->took = $this->numRows = $this->affected = false; $this->logQuery($query); } $this->_connection->exec($query); @@ -3190,10 +3196,13 @@ public function insertMulti($table, $fields, $values) { $statement->bindValue($i, $val, $columnMap[$col]); $i += 1; } + $t = microtime(true); $statement->execute(); $statement->closeCursor(); if ($this->fullDebug) { + $this->took = round((microtime(true) - $t) * 1000, 0); + $this->numRows = $this->affected = $statement->rowCount(); $this->logQuery($sql, $value); } } diff --git a/lib/Cake/Network/CakeRequest.php b/lib/Cake/Network/CakeRequest.php index 8bd2b3c5074..db97a04ea55 100644 --- a/lib/Cake/Network/CakeRequest.php +++ b/lib/Cake/Network/CakeRequest.php @@ -97,6 +97,7 @@ class CakeRequest implements ArrayAccess { */ protected $_detectors = array( 'get' => array('env' => 'REQUEST_METHOD', 'value' => 'GET'), + 'patch' => array('env' => 'REQUEST_METHOD', 'value' => 'PATCH'), 'post' => array('env' => 'REQUEST_METHOD', 'value' => 'POST'), 'put' => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'), 'delete' => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'), @@ -1124,6 +1125,9 @@ public function offsetSet($name, $value) { * @return bool */ public function offsetExists($name) { + if ($name === 'url' || $name === 'data') { + return true; + } return isset($this->params[$name]); } diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index bf62be85a5b..33e37ca68c3 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -1203,7 +1203,7 @@ public function send($content = null) { * @throws SocketException */ public static function deliver($to = null, $subject = null, $message = null, $transportConfig = 'fast', $send = true) { - $class = __CLASS__; + $class = get_called_class(); /** @var CakeEmail $instance */ $instance = new $class($transportConfig); if ($to !== null) { diff --git a/lib/Cake/Network/Email/DebugTransport.php b/lib/Cake/Network/Email/DebugTransport.php index 5f6ae298dba..8ab64e1b007 100644 --- a/lib/Cake/Network/Email/DebugTransport.php +++ b/lib/Cake/Network/Email/DebugTransport.php @@ -15,6 +15,7 @@ * @since CakePHP(tm) v 2.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +App::uses('AbstractTransport', 'Network/Email'); /** * Debug Transport class, useful for emulate the email sending process and inspect the resulted diff --git a/lib/Cake/Network/Email/MailTransport.php b/lib/Cake/Network/Email/MailTransport.php index 9e27899d3a9..3b40f21c133 100644 --- a/lib/Cake/Network/Email/MailTransport.php +++ b/lib/Cake/Network/Email/MailTransport.php @@ -15,6 +15,7 @@ * @since CakePHP(tm) v 2.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +App::uses('AbstractTransport', 'Network/Email'); /** * Send mail using mail() function diff --git a/lib/Cake/Network/Email/SmtpTransport.php b/lib/Cake/Network/Email/SmtpTransport.php index 64d0090b57c..c581c9a2f52 100644 --- a/lib/Cake/Network/Email/SmtpTransport.php +++ b/lib/Cake/Network/Email/SmtpTransport.php @@ -15,7 +15,7 @@ * @since CakePHP(tm) v 2.0.0 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - +App::uses('AbstractTransport', 'Network/Email'); App::uses('CakeSocket', 'Network'); /** diff --git a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php index 171e9d551bb..6ae6d643a74 100644 --- a/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php +++ b/lib/Cake/Test/Case/Cache/Engine/ApcEngineTest.php @@ -25,6 +25,13 @@ */ class ApcEngineTest extends CakeTestCase { +/** + * APC extension to be used + * + * @var string + */ + protected $_apcExtension = 'apc'; + /** * setUp method * @@ -32,12 +39,17 @@ class ApcEngineTest extends CakeTestCase { */ public function setUp() { parent::setUp(); - $this->skipIf(!function_exists('apc_store'), 'Apc is not installed or configured properly.'); + $hasApc = extension_loaded('apc') || extension_loaded('apcu'); + $this->skipIf(!$hasApc, 'Apc is not installed or configured properly.'); if (PHP_SAPI === 'cli') { $this->skipIf(!ini_get('apc.enable_cli'), 'APC is not enabled for the CLI.'); } + if (extension_loaded('apcu')) { + $this->_apcExtension = 'apcu'; + } + $this->_cacheDisable = Configure::read('Cache.disable'); Configure::write('Cache.disable', false); Cache::config('apc', array('engine' => 'Apc', 'prefix' => 'cake_')); @@ -147,7 +159,8 @@ public function testDeleteCache() { * @return void */ public function testDecrement() { - $this->skipIf(!function_exists('apc_dec'), 'No apc_dec() function, cannot test decrement().'); + $hasSupport = function_exists('apc_dec') || function_exists('apcu_dec'); + $this->skipIf(!$hasSupport, 'No apc_dec()/apcu_dec() function, cannot test decrement().'); $result = Cache::write('test_decrement', 5, 'apc'); $this->assertTrue($result); @@ -171,7 +184,8 @@ public function testDecrement() { * @return void */ public function testIncrement() { - $this->skipIf(!function_exists('apc_inc'), 'No apc_inc() function, cannot test increment().'); + $hasSupport = function_exists('apc_inc') || function_exists('apcu_inc'); + $this->skipIf(!function_exists('apc_inc'), 'No apc_inc()/apcu_inc() function, cannot test increment().'); $result = Cache::write('test_increment', 5, 'apc'); $this->assertTrue($result); @@ -195,14 +209,18 @@ public function testIncrement() { * @return void */ public function testClear() { - apc_store('not_cake', 'survive'); + $storeFunc = $this->_apcExtension . '_store'; + $fetchFunc = $this->_apcExtension . '_fetch'; + $deleteFunc = $this->_apcExtension . '_delete'; + + $storeFunc('not_cake', 'survive'); Cache::write('some_value', 'value', 'apc'); $result = Cache::clear(false, 'apc'); $this->assertTrue($result); $this->assertFalse(Cache::read('some_value', 'apc')); - $this->assertEquals('survive', apc_fetch('not_cake')); - apc_delete('not_cake'); + $this->assertEquals('survive', $fetchFunc('not_cake')); + $deleteFunc('not_cake'); } /** @@ -213,6 +231,7 @@ public function testClear() { * @return void */ public function testGroupsReadWrite() { + $incFunc = $this->_apcExtension . '_inc'; Cache::config('apc_groups', array( 'engine' => 'Apc', 'duration' => 0, @@ -222,12 +241,12 @@ public function testGroupsReadWrite() { $this->assertTrue(Cache::write('test_groups', 'value', 'apc_groups')); $this->assertEquals('value', Cache::read('test_groups', 'apc_groups')); - apc_inc('test_group_a'); + $incFunc('test_group_a'); $this->assertFalse(Cache::read('test_groups', 'apc_groups')); $this->assertTrue(Cache::write('test_groups', 'value2', 'apc_groups')); $this->assertEquals('value2', Cache::read('test_groups', 'apc_groups')); - apc_inc('test_group_b'); + $incFunc('test_group_b'); $this->assertFalse(Cache::read('test_groups', 'apc_groups')); $this->assertTrue(Cache::write('test_groups', 'value3', 'apc_groups')); $this->assertEquals('value3', Cache::read('test_groups', 'apc_groups')); diff --git a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php index 9d8c9e2b28d..ded7e9596e7 100644 --- a/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php +++ b/lib/Cake/Test/Case/Console/Command/Task/ControllerTaskTest.php @@ -222,7 +222,7 @@ public function testDoHelpersTrailingCommas() { public function testDoComponentsNo() { $this->Task->expects($this->any())->method('in')->will($this->returnValue('n')); $result = $this->Task->doComponents(); - $this->assertSame(array('Paginator', 'Flash'), $result); + $this->assertSame(array('Paginator'), $result); } /** @@ -235,7 +235,7 @@ public function testDoComponentsTrailingSpaces() { $this->Task->expects($this->at(1))->method('in')->will($this->returnValue(' RequestHandler, Security ')); $result = $this->Task->doComponents(); - $expected = array('Paginator', 'Flash', 'RequestHandler', 'Security'); + $expected = array('Paginator', 'RequestHandler', 'Security'); $this->assertEquals($expected, $result); } @@ -249,7 +249,7 @@ public function testDoComponentsTrailingCommas() { $this->Task->expects($this->at(1))->method('in')->will($this->returnValue(' RequestHandler, Security, , ')); $result = $this->Task->doComponents(); - $expected = array('Paginator', 'Flash', 'RequestHandler', 'Security'); + $expected = array('Paginator', 'RequestHandler', 'Security'); $this->assertEquals($expected, $result); } diff --git a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php index 0ff514df614..00e1b35598f 100644 --- a/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/AuthComponentTest.php @@ -1721,6 +1721,27 @@ public function testStatelessAuthNoRedirect() { $this->Auth->startup($this->Controller); } +/** + * testStatelessLoginSetUserNoSessionStart method + * + * @return void + */ + public function testStatelessLoginSetUserNoSessionStart() { + $user = array( + 'id' => 1, + 'username' => 'mark' + ); + + AuthComponent::$sessionKey = false; + $result = $this->Auth->login($user); + $this->assertTrue($result); + + $this->assertTrue($this->Auth->loggedIn()); + $this->assertEquals($user, $this->Auth->user()); + + $this->assertFalse($this->Auth->Session->started()); + } + /** * testStatelessAuthNoSessionStart method * diff --git a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php index 1e9f8daaf69..028729fae38 100644 --- a/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/RequestHandlerComponentTest.php @@ -618,6 +618,9 @@ public function testRequestContentTypes() { $_SERVER['REQUEST_METHOD'] = 'DELETE'; $this->assertEquals('json', $this->RequestHandler->requestedWith()); + $_SERVER['REQUEST_METHOD'] = 'PATCH'; + $this->assertEquals('json', $this->RequestHandler->requestedWith()); + $_SERVER['REQUEST_METHOD'] = 'POST'; unset($_SERVER['CONTENT_TYPE']); $_SERVER['HTTP_CONTENT_TYPE'] = 'application/json'; diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/SqliteTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/SqliteTest.php index 14758c7d7ac..46950760f95 100644 --- a/lib/Cake/Test/Case/Model/Datasource/Database/SqliteTest.php +++ b/lib/Cake/Test/Case/Model/Datasource/Database/SqliteTest.php @@ -168,7 +168,13 @@ public function testCacheKeyName() { $dbName = 'db' . rand() . '$(*%&).db'; $this->assertFalse(file_exists(TMP . $dbName)); - $db = new Sqlite(array_merge($this->Dbo->config, array('database' => TMP . $dbName))); + try { + $db = new Sqlite(array_merge($this->Dbo->config, array('database' => TMP . $dbName))); + } catch (MissingConnectionException $e) { + // This might be caused by NTFS file systems, where '*' is a forbidden character. Repeat without this character. + $dbName = str_replace('*', '', $dbName); + $db = new Sqlite(array_merge($this->Dbo->config, array('database' => TMP . $dbName))); + } $this->assertTrue(file_exists(TMP . $dbName)); $db->execute("CREATE TABLE test_list (id VARCHAR(255));"); @@ -422,6 +428,40 @@ public function testDescribeWithUuidPrimaryKey() { $this->Dbo->query('DROP TABLE ' . $tableName); } +/** + * Test that describe ignores `default current_timestamp` in timestamp columns. + * + * @return void + */ + public function testDescribeHandleCurrentTimestamp() { + $name = $this->Dbo->fullTableName('timestamp_default_values'); + $sql = <<Dbo->execute($sql); + $model = new Model(array( + 'table' => 'timestamp_default_values', + 'ds' => 'test', + 'alias' => 'TimestampDefaultValue' + )); + $result = $this->Dbo->describe($model); + $this->Dbo->execute('DROP TABLE ' . $name); + + $this->assertNull($result['limit_date']['default']); + + $schema = new CakeSchema(array( + 'connection' => 'test', + 'testdescribes' => $result + )); + $result = $this->Dbo->createSchema($schema); + $this->assertContains('"limit_date" timestamp NOT NULL', $result); + } + /** * Test virtualFields with functions. * @@ -466,6 +506,7 @@ public function testUuidPrimaryKeyInsertion() { * @return void */ public function testNestedTransaction() { + $this->Dbo->useNestedTransactions = true; $this->skipIf($this->Dbo->nestedTransactionSupported() === false, 'The Sqlite version do not support nested transaction'); $this->loadFixtures('User'); diff --git a/lib/Cake/Test/Case/Network/CakeSocketTest.php b/lib/Cake/Test/Case/Network/CakeSocketTest.php index e93882d06a1..c678d9c5b72 100644 --- a/lib/Cake/Test/Case/Network/CakeSocketTest.php +++ b/lib/Cake/Test/Case/Network/CakeSocketTest.php @@ -318,11 +318,6 @@ public function testEnableCryptoBadMode() { * @return void */ public function testEnableCrypto() { - // testing on ssl server - $this->_connectSocketToSslTls(); - $this->assertTrue($this->Socket->enableCrypto('sslv3', 'client')); - $this->Socket->disconnect(); - // testing on tls server $this->_connectSocketToSslTls(); $this->assertTrue($this->Socket->enableCrypto('tls', 'client')); diff --git a/lib/Cake/Test/Case/Utility/CakeTimeTest.php b/lib/Cake/Test/Case/Utility/CakeTimeTest.php index 2620f789707..dc2a8887c6d 100644 --- a/lib/Cake/Test/Case/Utility/CakeTimeTest.php +++ b/lib/Cake/Test/Case/Utility/CakeTimeTest.php @@ -454,6 +454,19 @@ public function testNiceShort() { $this->_restoreSystemTimezone(); } +/** + * testNiceShort translations + * + * @return void + */ + public function testNiceShortI18n() { + $restore = setlocale(LC_ALL, 0); + setlocale(LC_ALL, 'es_ES'); + $time = strtotime('2015-01-07 03:05:00'); + $this->assertEquals('ene 7th 2015, 03:05', $this->Time->niceShort($time)); + setlocale(LC_ALL, $restore); + } + /** * testDaysAsSql method * diff --git a/lib/Cake/Test/Case/Utility/FolderTest.php b/lib/Cake/Test/Case/Utility/FolderTest.php index 55292281803..1d53713ae86 100644 --- a/lib/Cake/Test/Case/Utility/FolderTest.php +++ b/lib/Cake/Test/Case/Utility/FolderTest.php @@ -1227,4 +1227,85 @@ public function testMoveWithSkip() { $Folder->delete(); } +/** + * testSortByTime method + * + * Verify that the order using modified time is correct. + * + * @return void + */ + public function testSortByTime() { + $Folder = new Folder(TMP . 'test_sort_by_time', true); + + $file2 = new File($Folder->pwd() . DS . 'file_2.tmp'); + $file2->create(); + + sleep(1); + + $file1 = new File($Folder->pwd() . DS . 'file_1.tmp'); + $file1->create(); + + $expected = array('file_2.tmp', 'file_1.tmp'); + $result = $Folder->find('.*', Folder::SORT_TIME); + $this->assertSame($expected, $result); + + $Folder->delete(); + } + +/** + * testSortByTime2 method + * + * Verify that the sort order using modified time is correct. + * + * @return void + */ + public function testSortByTime2() { + $Folder = new Folder(TMP . 'test_sort_by_time2', true); + + $fileC = new File($Folder->pwd() . DS . 'c.txt'); + $fileC->create(); + + sleep(1); + + $fileA = new File($Folder->pwd() . DS . 'a.txt'); + $fileA->create(); + + sleep(1); + + $fileB = new File($Folder->pwd() . DS . 'b.txt'); + $fileB->create(); + + $expected = array('c.txt', 'a.txt', 'b.txt'); + $result = $Folder->find('.*', Folder::SORT_TIME); + $this->assertSame($expected, $result); + + $Folder->delete(); + } + +/** + * Verify that the sort order using name is correct. + * + * @return void + */ + public function testSortByName() { + $Folder = new Folder(TMP . 'test_sort_by_name', true); + + $fileA = new File($Folder->pwd() . DS . 'a.txt'); + $fileA->create(); + + $fileC = new File($Folder->pwd() . DS . 'c.txt'); + $fileC->create(); + + sleep(1); + + $fileB = new File($Folder->pwd() . DS . 'b.txt'); + $fileB->create(); + + $expected = array('a.txt', 'b.txt', 'c.txt'); + $result = $Folder->find('.*', Folder::SORT_NAME); + $this->assertSame($expected, $result); + + $Folder->delete(); + } + } diff --git a/lib/Cake/Test/Case/Utility/ValidationTest.php b/lib/Cake/Test/Case/Utility/ValidationTest.php index e3f7f25cd75..9b34c03b107 100644 --- a/lib/Cake/Test/Case/Utility/ValidationTest.php +++ b/lib/Cake/Test/Case/Utility/ValidationTest.php @@ -403,6 +403,12 @@ public function testCc() { $this->assertTrue(Validation::cc('5467639122779531', array('mc'))); $this->assertTrue(Validation::cc('5297350261550024', array('mc'))); $this->assertTrue(Validation::cc('5162739131368058', array('mc'))); + //Mastercard (additional 2016 BIN) + $this->assertTrue(Validation::cc('2221000000000009', array('mc'))); + $this->assertTrue(Validation::cc('2720999999999996', array('mc'))); + $this->assertTrue(Validation::cc('2223000010005798', array('mc'))); + $this->assertTrue(Validation::cc('2623430710235708', array('mc'))); + $this->assertTrue(Validation::cc('2420452519835723', array('mc'))); //Solo 16 $this->assertTrue(Validation::cc('6767432107064987', array('solo'))); $this->assertTrue(Validation::cc('6334667758225411', array('solo'))); diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 2ca6217122c..6ca879919db 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -1597,6 +1597,51 @@ public function testUnlockFieldRemovingFromFields() { $this->assertEquals(array(), $this->Form->fields); } +/** + * test reset unlockFields, when create new form. + * + * @return void + */ + public function testResetUnlockFields() { + $this->Form->request['_Token'] = array( + 'key' => 'testKey', + 'unlockedFields' => array() + ); + + $this->Form->unlockField('Contact.id'); + $this->Form->create('Contact'); + $this->Form->hidden('Contact.id', array('value' => 1)); + $this->assertEmpty($this->Form->fields, 'Field should be unlocked'); + $this->Form->end(); + + $this->Form->create('Contact'); + $this->Form->hidden('Contact.id', array('value' => 1)); + $this->assertEquals(1, $this->Form->fields['Contact.id'], 'Hidden input should be secured.'); + } + +/** + * test unlockField removing from fields array. multiple field version. + * + * @return void + */ + public function testUnlockMultipleFieldRemovingFromFields() { + $this->Form->request['_Token'] = array( + 'key' => 'testKey', + 'unlockedFields' => array() + ); + $this->Form->create('Order'); + $this->Form->hidden('Order.id', array('value' => 1)); + $this->Form->checkbox('Ticked.id.'); + $this->Form->checkbox('Ticked.id.'); + + $this->assertEquals(1, $this->Form->fields['Order.id'], 'Hidden input should be secured.'); + $this->assertTrue(in_array('Ticked.id', $this->Form->fields), 'Field should be secured.'); + + $this->Form->unlockField('Order.id'); + $this->Form->unlockField('Ticked.id'); + $this->assertEquals(array(), $this->Form->fields); + } + /** * testTagIsInvalid method * @@ -3914,6 +3959,25 @@ public function testRadio() { ); $this->assertTags($result, $expected); + $result = $this->Form->radio('Model.field', array('option A', 'option B'), array('fieldset' => 'classy-stuff')); + $expected = array( + 'fieldset' => array('class' => 'classy-stuff'), + 'legend' => array(), + 'Field', + '/legend', + 'input' => array('type' => 'hidden', 'name' => 'data[Model][field]', 'value' => '', 'id' => 'ModelField_'), + array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '0', 'id' => 'ModelField0')), + array('label' => array('for' => 'ModelField0')), + 'option A', + '/label', + array('input' => array('type' => 'radio', 'name' => 'data[Model][field]', 'value' => '1', 'id' => 'ModelField1')), + array('label' => array('for' => 'ModelField1')), + 'option B', + '/label', + '/fieldset' + ); + $this->assertTags($result, $expected); + $result = $this->Form->radio( 'Employee.gender', array('male' => 'Male', 'female' => 'Female'), @@ -8699,6 +8763,36 @@ public function testCreateNoUrl() { $this->assertTags($result, $expected); } +/** + * Test that the action key still uses the model as the implicit controller + * when the url option is undefined. While the action parameter is deprecated + * we need it to continue working for the duration of 2.x + * + * @return void + */ + public function testCreateUrlImpliedController() { + $restore = error_reporting(E_ALL ^ E_USER_DEPRECATED); + $this->Form->request['controller'] = 'posts'; + $result = $this->Form->create('Comment', array( + 'action' => 'addComment', + 'id' => 'addCommentForm', + 'type' => 'POST' + )); + $expected = array( + 'form' => array( + 'action' => '/comments/addComment', + 'id' => 'addCommentForm', + 'method' => 'post', + 'accept-charset' => strtolower(Configure::read('App.encoding')) + ), + 'div' => array('style' => 'display:none;'), + 'input' => array('type' => 'hidden', 'name' => '_method', 'value' => 'POST'), + '/div' + ); + $this->assertTags($result, $expected); + error_reporting($restore); + } + /** * Test the onsubmit option for create() * diff --git a/lib/Cake/Utility/Folder.php b/lib/Cake/Utility/Folder.php index e23f70e5a72..1b59ba15554 100644 --- a/lib/Cake/Utility/Folder.php +++ b/lib/Cake/Utility/Folder.php @@ -46,6 +46,16 @@ class Folder { */ const SKIP = 'skip'; +/** + * Sort mode by name + */ + const SORT_NAME = 'name'; + +/** + * Sort mode by time + */ + const SORT_TIME = 'time'; + /** * Path to Folder. * @@ -71,6 +81,14 @@ class Folder { */ public $mode = 0755; +/** + * Functions array to be called depending on the sort type chosen. + */ + protected $_fsorts = array( + self::SORT_NAME => 'getPathname', + self::SORT_TIME => 'getCTime' + ); + /** * Holds messages from last method. * @@ -155,14 +173,14 @@ public function cd($path) { * Returns an array of the contents of the current directory. * The returned array holds two arrays: One of directories and one of files. * - * @param bool $sort Whether you want the results sorted, set this and the sort property + * @param string|bool $sort Whether you want the results sorted, set this and the sort property * to false to get unsorted results. * @param array|bool $exceptions Either an array or boolean true will not grab dot files * @param bool $fullPath True returns the full path * @return mixed Contents of current directory as an array, an empty array on failure * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::read */ - public function read($sort = true, $exceptions = false, $fullPath = false) { + public function read($sort = self::SORT_NAME, $exceptions = false, $fullPath = false) { $dirs = $files = array(); if (!$this->pwd()) { @@ -178,6 +196,11 @@ public function read($sort = true, $exceptions = false, $fullPath = false) { } catch (Exception $e) { return array($dirs, $files); } + if (!is_bool($sort) && isset($this->_fsorts[$sort])) { + $methodName = $this->_fsorts[$sort]; + } else { + $methodName = $this->_fsorts[self::SORT_NAME]; + } foreach ($iterator as $item) { if ($item->isDot()) { @@ -191,14 +214,22 @@ public function read($sort = true, $exceptions = false, $fullPath = false) { $name = $item->getPathName(); } if ($item->isDir()) { - $dirs[] = $name; + $dirs[$item->{$methodName}()][] = $name; } else { - $files[] = $name; + $files[$item->{$methodName}()][] = $name; } } + if ($sort || $this->sort) { - sort($dirs); - sort($files); + ksort($dirs); + ksort($files); + } + + if ($dirs) { + $dirs = call_user_func_array('array_merge', $dirs); + } + if ($files) { + $files = call_user_func_array('array_merge', $files); } return array($dirs, $files); } diff --git a/lib/Cake/Utility/Security.php b/lib/Cake/Utility/Security.php index 01ade1ef984..be128e64a24 100644 --- a/lib/Cake/Utility/Security.php +++ b/lib/Cake/Utility/Security.php @@ -191,8 +191,10 @@ public static function randomBytes($length) { E_USER_WARNING ); $bytes = ''; - while ($bytes < $length) { + $byteLength = 0; + while ($byteLength < $length) { $bytes .= static::hash(CakeText::uuid() . uniqid(mt_rand(), true), 'sha512', true); + $byteLength = strlen($bytes); } return substr($bytes, 0, $length); } diff --git a/lib/Cake/Utility/Validation.php b/lib/Cake/Utility/Validation.php index 48bf8714772..12338846311 100644 --- a/lib/Cake/Utility/Validation.php +++ b/lib/Cake/Utility/Validation.php @@ -182,7 +182,7 @@ public static function cc($check, $type = 'fast', $deep = false, $regex = null) '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})?)$/', diff --git a/lib/Cake/VERSION.txt b/lib/Cake/VERSION.txt index fa6a5f1094c..9a3a91b5ce0 100644 --- a/lib/Cake/VERSION.txt +++ b/lib/Cake/VERSION.txt @@ -17,4 +17,4 @@ // @license http://www.opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -2.8.3 +2.8.5 diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 5d583a9c440..ea190ca6bf9 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -382,6 +382,7 @@ public function create($model = null, $options = array()) { if (isset($options['action'])) { trigger_error('Using key `action` is deprecated, use `url` directly instead.', E_USER_DEPRECATED); } + if (is_array($options['url']) && isset($options['url']['action'])) { $options['action'] = $options['url']['action']; } @@ -393,7 +394,7 @@ public function create($model = null, $options = array()) { if ($options['action'] === null && $options['url'] === null) { $options['action'] = $this->request->here(false); - } elseif (is_array($options['url'])) { + } elseif (empty($options['url']) || is_array($options['url'])) { if (empty($options['url']['controller'])) { if (!empty($model)) { $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model)); @@ -559,6 +560,7 @@ public function end($options = null, $secureAttributes = array()) { $this->setEntity(null); $out .= $this->Html->useTag('formend'); + $this->_unlockedFields = array(); $this->_View->modelScope = false; $this->requestType = null; return $out; @@ -661,7 +663,10 @@ protected function _secure($lock, $field = null, $value = null) { if (!$field) { $field = $this->entity(); } elseif (is_string($field)) { - $field = Hash::filter(explode('.', $field)); + $field = explode('.', $field); + } + if (is_array($field)) { + $field = Hash::filter($field); } foreach ($this->_unlockedFields as $unlockField) { @@ -931,9 +936,12 @@ public function inputs($fields = null, $blacklist = null, $options = array()) { if (isset($options['legend'])) { $legend = $options['legend']; + unset($options['legend']); } + if (isset($options['fieldset'])) { $fieldset = $options['fieldset']; + unset($options['fieldset']); } if (empty($fields)) { @@ -971,7 +979,7 @@ public function inputs($fields = null, $blacklist = null, $options = array()) { } if (is_string($fieldset)) { - $fieldsetClass = sprintf(' class="%s"', $fieldset); + $fieldsetClass = array('class' => $fieldset); } else { $fieldsetClass = ''; } @@ -1509,6 +1517,7 @@ public function checkbox($fieldName, $options = array()) { * - `between` - the string between legend and input set or array of strings to insert * strings between each input block * - `legend` - control whether or not the widget set has a fieldset & legend + * - `fieldset` - sets the class of the fieldset. Fieldset is only generated if legend attribute is provided * - `value` - indicate a value that is should be checked * - `label` - boolean to indicate whether or not labels for widgets show be displayed * - `hiddenField` - boolean to indicate if you want the results of radio() to include @@ -1543,6 +1552,12 @@ public function radio($fieldName, $options = array(), $attributes = array()) { $legend = __(Inflector::humanize($this->field())); } + $fieldsetAttrs = ''; + if (isset($attributes['fieldset'])) { + $fieldsetAttrs = array('class' => $attributes['fieldset']); + unset($attributes['fieldset']); + } + $label = true; if (isset($attributes['label'])) { $label = $attributes['label']; @@ -1636,8 +1651,10 @@ public function radio($fieldName, $options = array(), $attributes = array()) { if (is_array($between)) { $between = ''; } + if ($legend) { - $out = $this->Html->useTag('fieldset', '', $this->Html->useTag('legend', $legend) . $between . $out); + $out = $this->Html->useTag('legend', $legend) . $between . $out; + $out = $this->Html->useTag('fieldset', $fieldsetAttrs, $out); } return $out; }