CakePHP 2.5.0

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (Darwin)
 
 iQEcBAABAgAGBQJTcYCoAAoJEDwHD15Vs66g4PQH+wZkDgvrGK5AoMBU63tLhM68
 txYUfV2g7YCjoWC/q0R5QbEYa7otBAAtREEdPisTU34pWPPGcar4/Q8M+bsztaJv
 UfOWOzmVLqNqDGMfvJKL5Y3kWtuKDpUVGupa4SBdt/jQ0RxwsG5SBolwu7QQ15gr
 BstQjA6RpTmdBjzbHVfX6WO3a//4utY3Nf0G4+aL5Q/y1FDNzyAmBq4OKahbBY0K
 MnaWiANHXN/3YfFFaWxDk+P23gsiWckaqq3Q2ipaGGGQoWq0rMeWFw3OrSc4w4xS
 L94poTkNDPqEzEjO7A4GvyvgXW4NhuuDbGkrSXciArfxihVSyB1mziDWSSYanAI=
 =y8NE
 -----END PGP SIGNATURE-----

CakePHP 2.5.0
This commit is contained in:
Jose Lorenzo Rodriguez 2014-05-13 09:14:43 +02:00
commit 3c57300816
199 changed files with 7235 additions and 2048 deletions

33
.gitattributes vendored Normal file
View file

@ -0,0 +1,33 @@
# Define the line ending behavior of the different file extensions
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.php text
*.default text
*.ctp text
*.sql text
*.md text
*.po text
*.js text
*.css text
*.ini text
*.properties text
*.txt text
*.xml text
*.yml text
.htaccess text
# Declare files that will always have CRLF line endings on checkout.
*.bat eol=crlf
# Declare files that will always have LF line endings on checkout.
*.pem eol=lf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
*.gif binary
*.ico binary
*.mo binary

View file

@ -12,6 +12,9 @@ env:
- DB=pgsql - DB=pgsql
- DB=sqlite - DB=sqlite
services:
- memcached
matrix: matrix:
include: include:
- php: 5.4 - php: 5.4
@ -32,6 +35,7 @@ before_script:
- sudo apt-get install lighttpd - sudo apt-get install lighttpd
- sh -c "if [ '$PHPCS' = '1' ]; then pear channel-discover pear.cakephp.org; fi" - sh -c "if [ '$PHPCS' = '1' ]; then pear channel-discover pear.cakephp.org; fi"
- sh -c "if [ '$PHPCS' = '1' ]; then pear install --alldeps cakephp/CakePHP_CodeSniffer; fi" - sh -c "if [ '$PHPCS' = '1' ]; then pear install --alldeps cakephp/CakePHP_CodeSniffer; fi"
- echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- phpenv rehash - phpenv rehash
- set +H - set +H
- echo "<?php - echo "<?php

View file

@ -80,6 +80,7 @@ Cache::config('default', array('engine' => 'File'));
* *
* Configure::write('Dispatcher.filters', array( * Configure::write('Dispatcher.filters', array(
* 'MyCacheFilter', // will use MyCacheFilter class from the Routing/Filter package in your app. * 'MyCacheFilter', // will use MyCacheFilter class from the Routing/Filter package in your app.
* 'MyCacheFilter' => array('prefix' => 'my_cache_'), // will use MyCacheFilter class from the Routing/Filter package in your app with settings array.
* 'MyPlugin.MyFilter', // will use MyFilter class from the Routing/Filter package in MyPlugin plugin. * 'MyPlugin.MyFilter', // will use MyFilter class from the Routing/Filter package in MyPlugin plugin.
* array('callable' => $aFunction, 'on' => 'before', 'priority' => 9), // A valid PHP callback type to be called on beforeDispatch * array('callable' => $aFunction, 'on' => 'before', 'priority' => 9), // A valid PHP callback type to be called on beforeDispatch
* array('callable' => $anotherMethod, 'on' => 'after'), // A valid PHP callback type to be called on afterDispatch * array('callable' => $anotherMethod, 'on' => 'after'), // A valid PHP callback type to be called on afterDispatch

View file

@ -315,18 +315,20 @@
* 'password' => 'password', //plaintext password (xcache.admin.pass) * 'password' => 'password', //plaintext password (xcache.admin.pass)
* )); * ));
* *
* Memcache (http://www.danga.com/memcached/) * Memcached (http://www.danga.com/memcached/)
*
* Uses the memcached extension. See http://php.net/memcached
* *
* Cache::config('default', array( * Cache::config('default', array(
* 'engine' => 'Memcache', //[required] * 'engine' => 'Memcached', //[required]
* 'duration' => 3600, //[optional] * 'duration' => 3600, //[optional]
* 'probability' => 100, //[optional] * 'probability' => 100, //[optional]
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string * 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
* 'servers' => array( * 'servers' => array(
* '127.0.0.1:11211' // localhost, default port 11211 * '127.0.0.1:11211' // localhost, default port 11211
* ), //[optional] * ), //[optional]
* 'persistent' => true, // [optional] set this to false for non-persistent connections * 'persistent' => 'my_connection', // [optional] The name of the persistent connection.
* 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory) * 'compress' => false, // [optional] compress data in Memcached (slower, but uses less memory)
* )); * ));
* *
* Wincache (http://php.net/wincache) * Wincache (http://php.net/wincache)

View file

@ -58,6 +58,9 @@
* For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html * For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html
* For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html * For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html
* For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx * For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx
*
* flags =>
* A key/value array of driver specific connection options.
*/ */
class DATABASE_CONFIG { class DATABASE_CONFIG {

View file

@ -16,7 +16,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $message; ?></h2>
<p class="error"> <p class="error">
<strong><?php echo __d('cake', 'Error'); ?>: </strong> <strong><?php echo __d('cake', 'Error'); ?>: </strong>
<?php printf( <?php printf(

View file

@ -16,7 +16,7 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $message; ?></h2>
<p class="error"> <p class="error">
<strong><?php echo __d('cake', 'Error'); ?>: </strong> <strong><?php echo __d('cake', 'Error'); ?>: </strong>
<?php echo __d('cake', 'An Internal Error Has Occurred.'); ?> <?php echo __d('cake', 'An Internal Error Has Occurred.'); ?>

View file

@ -17,6 +17,7 @@
*/ */
$cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework'); $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework');
$cakeVersion = __d('cake_dev', 'CakePHP %s', Configure::version())
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -51,9 +52,12 @@ $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework
<?php echo $this->Html->link( <?php echo $this->Html->link(
$this->Html->image('cake.power.gif', array('alt' => $cakeDescription, 'border' => '0')), $this->Html->image('cake.power.gif', array('alt' => $cakeDescription, 'border' => '0')),
'http://www.cakephp.org/', 'http://www.cakephp.org/',
array('target' => '_blank', 'escape' => false) array('target' => '_blank', 'escape' => false, 'id' => 'cake-powered')
); );
?> ?>
<p>
<?php echo $cakeVersion; ?>
</p>
</div> </div>
</div> </div>
<?php echo $this->element('sql_dump'); ?> <?php echo $this->element('sql_dump'); ?>

View file

@ -107,11 +107,13 @@ p {
#footer { #footer {
clear: both; clear: both;
padding: 6px 10px; padding: 6px 10px;
text-align: right;
} }
#header a, #footer a { #header a, #footer a {
color: #fff; color: #fff;
} }
#cake-powered {
float: right;
}
/** containers **/ /** containers **/
div.form, div.form,

View file

@ -130,7 +130,7 @@ class Cache {
} }
if (!empty($settings)) { if (!empty($settings)) {
self::$_config[$name] = array_merge($current, $settings); self::$_config[$name] = $settings + $current;
} }
if (empty(self::$_config[$name]['engine'])) { if (empty(self::$_config[$name]['engine'])) {
@ -253,7 +253,7 @@ class Cache {
if (is_string($settings) && $value !== null) { if (is_string($settings) && $value !== null) {
$settings = array($settings => $value); $settings = array($settings => $value);
} }
$settings = array_merge(self::$_config[$config], $settings); $settings += self::$_config[$config];
if (isset($settings['duration']) && !is_numeric($settings['duration'])) { if (isset($settings['duration']) && !is_numeric($settings['duration'])) {
$settings['duration'] = strtotime($settings['duration']) - time(); $settings['duration'] = strtotime($settings['duration']) - time();
} }
@ -538,4 +538,38 @@ class Cache {
throw new CacheException(__d('cake_dev', 'Invalid cache group %s', $group)); throw new CacheException(__d('cake_dev', 'Invalid cache group %s', $group));
} }
/**
* Provides the ability to easily do read-through caching.
*
* When called if the $key is not set in $config, the $callable function
* will be invoked. The results will then be stored into the cache config
* at key.
*
* Examples:
*
* Using a Closure to provide data, assume $this is a Model:
*
* {{{
* $model = $this;
* $results = Cache::remember('all_articles', function() use ($model) {
* return $model->find('all');
* });
* }}}
*
* @param string $key The cache key to read/store data at.
* @param callable $callable The callable that provides data in the case when
* the cache key is empty. Can be any callable type supported by your PHP.
* @param string $config The cache configuration to use for this operation.
* Defaults to default.
*/
public static function remember($key, $callable, $config = 'default') {
$existing = self::read($key, $config);
if ($existing !== false) {
return $existing;
}
$results = call_user_func($callable);
self::write($key, $results, $config);
return $results;
}
} }

View file

@ -22,6 +22,7 @@
* more information. * more information.
* *
* @package Cake.Cache.Engine * @package Cake.Cache.Engine
* @deprecated You should use the Memcached adapter instead.
*/ */
class MemcacheEngine extends CacheEngine { class MemcacheEngine extends CacheEngine {

View file

@ -0,0 +1,322 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.5.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Memcached storage engine for cache. Memcached has some limitations in the amount of
* control you have over expire times far in the future. See MemcachedEngine::write() for
* more information.
*
* Main advantage of this Memcached engine over the memcached engine is
* support of binary protocol, and igbibnary serialization
* (if memcached extension compiled with --enable-igbinary)
* Compressed keys can also be incremented/decremented
*
* @package Cake.Cache.Engine
*/
class MemcachedEngine extends CacheEngine {
/**
* memcached wrapper.
*
* @var Memcache
*/
protected $_Memcached = null;
/**
* Settings
*
* - servers = string or array of memcached servers, default => 127.0.0.1. If an
* array MemcacheEngine will use them as a pool.
* - compress = boolean, default => false
* - persistent = string The name of the persistent connection. All configurations using
* the same persistent value will share a single underlying connection.
* - serialize = string, default => php. The serializer engine used to serialize data.
* Available engines are php, igbinary and json. Beside php, the memcached extension
* must be compiled with the appropriate serializer support.
*
* @var array
*/
public $settings = array();
/**
* List of available serializer engines
*
* Memcached must be compiled with json and igbinary support to use these engines
*
* @var array
*/
protected $_serializers = array(
'igbinary' => Memcached::SERIALIZER_IGBINARY,
'json' => Memcached::SERIALIZER_JSON,
'php' => Memcached::SERIALIZER_PHP
);
/**
* Initialize the Cache Engine
*
* Called automatically by the cache frontend
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
*
* @param array $settings array of setting for the engine
* @return boolean True if the engine has been successfully initialized, false if not
* @throws CacheException when you try use authentication without Memcached compiled with SASL support
*/
public function init($settings = array()) {
if (!class_exists('Memcached')) {
return false;
}
if (!isset($settings['prefix'])) {
$settings['prefix'] = Inflector::slug(APP_DIR) . '_';
}
if (defined('Memcached::HAVE_MSGPACK') && Memcached::HAVE_MSGPACK) {
$this->_serializers['msgpack'] = Memcached::SERIALIZER_MSGPACK;
}
$settings += array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1'),
'compress' => false,
'persistent' => false,
'login' => null,
'password' => null,
'serialize' => 'php'
);
parent::init($settings);
if (!is_array($this->settings['servers'])) {
$this->settings['servers'] = array($this->settings['servers']);
}
if (isset($this->_Memcached)) {
return true;
}
$this->_Memcached = new Memcached($this->settings['persistent'] ? (string)$this->settings['persistent'] : null);
$this->_setOptions();
if (count($this->_Memcached->getServerList())) {
return true;
}
$servers = array();
foreach ($this->settings['servers'] as $server) {
$servers[] = $this->_parseServerString($server);
}
if (!$this->_Memcached->addServers($servers)) {
return false;
}
if ($this->settings['login'] !== null && $this->settings['password'] !== null) {
if (!method_exists($this->_Memcached, 'setSaslAuthData')) {
throw new CacheException(
__d('cake_dev', 'Memcached extension is not build with SASL support')
);
}
$this->_Memcached->setSaslAuthData($this->settings['login'], $this->settings['password']);
}
return true;
}
/**
* Settings the memcached instance
*
* @throws CacheException when the Memcached extension is not built with the desired serializer engine
*/
protected function _setOptions() {
$this->_Memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$serializer = strtolower($this->settings['serialize']);
if (!isset($this->_serializers[$serializer])) {
throw new CacheException(
__d('cake_dev', '%s is not a valid serializer engine for Memcached', $serializer)
);
}
if ($serializer !== 'php' && !constant('Memcached::HAVE_' . strtoupper($serializer))) {
throw new CacheException(
__d('cake_dev', 'Memcached extension is not compiled with %s support', $serializer)
);
}
$this->_Memcached->setOption(Memcached::OPT_SERIALIZER, $this->_serializers[$serializer]);
// Check for Amazon ElastiCache instance
if (defined('Memcached::OPT_CLIENT_MODE') && defined('Memcached::DYNAMIC_CLIENT_MODE')) {
$this->_Memcached->setOption(Memcached::OPT_CLIENT_MODE, Memcached::DYNAMIC_CLIENT_MODE);
}
$this->_Memcached->setOption(Memcached::OPT_COMPRESSION, (bool)$this->settings['compress']);
}
/**
* Parses the server address into the host/port. Handles both IPv6 and IPv4
* addresses and Unix sockets
*
* @param string $server The server address string.
* @return array Array containing host, port
*/
protected function _parseServerString($server) {
if ($server[0] === 'u') {
return array($server, 0);
}
if (substr($server, 0, 1) === '[') {
$position = strpos($server, ']:');
if ($position !== false) {
$position++;
}
} else {
$position = strpos($server, ':');
}
$port = 11211;
$host = $server;
if ($position !== false) {
$host = substr($server, 0, $position);
$port = substr($server, $position + 1);
}
return array($host, (int)$port);
}
/**
* Write data for key into cache. When using memcached as your cache engine
* remember that the Memcached pecl extension does not support cache expiry times greater
* than 30 days in the future. Any duration greater than 30 days will be treated as never expiring.
*
* @param string $key Identifier for the data
* @param mixed $value Data to be cached
* @param integer $duration How long to cache the data, in seconds
* @return boolean True if the data was successfully cached, false on failure
* @see http://php.net/manual/en/memcache.set.php
*/
public function write($key, $value, $duration) {
if ($duration > 30 * DAY) {
$duration = 0;
}
return $this->_Memcached->set($key, $value, $duration);
}
/**
* Read a key from the cache
*
* @param string $key Identifier for the data
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
*/
public function read($key) {
return $this->_Memcached->get($key);
}
/**
* Increments the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to increment
* @return New incremented value, false otherwise
* @throws CacheException when you try to increment with compress = true
*/
public function increment($key, $offset = 1) {
return $this->_Memcached->increment($key, $offset);
}
/**
* Decrements the value of an integer cached key
*
* @param string $key Identifier for the data
* @param integer $offset How much to subtract
* @return New decremented value, false otherwise
* @throws CacheException when you try to decrement with compress = true
*/
public function decrement($key, $offset = 1) {
return $this->_Memcached->decrement($key, $offset);
}
/**
* Delete a key from the cache
*
* @param string $key Identifier for the data
* @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed
*/
public function delete($key) {
return $this->_Memcached->delete($key);
}
/**
* Delete all keys from the cache
*
* @param boolean $check
* @return boolean True if the cache was successfully cleared, false otherwise
*/
public function clear($check) {
if ($check) {
return true;
}
$keys = $this->_Memcached->getAllKeys();
foreach ($keys as $key) {
if (strpos($key, $this->settings['prefix']) === 0) {
$this->_Memcached->delete($key);
}
}
return true;
}
/**
* Returns the `group value` for each of the configured groups
* If the group initial value was not found, then it initializes
* the group accordingly.
*
* @return array
*/
public function groups() {
if (empty($this->_compiledGroupNames)) {
foreach ($this->settings['groups'] as $group) {
$this->_compiledGroupNames[] = $this->settings['prefix'] . $group;
}
}
$groups = $this->_Memcached->getMulti($this->_compiledGroupNames);
if (count($groups) !== count($this->settings['groups'])) {
foreach ($this->_compiledGroupNames as $group) {
if (!isset($groups[$group])) {
$this->_Memcached->set($group, 1, 0);
$groups[$group] = 1;
}
}
ksort($groups);
}
$result = array();
$groups = array_values($groups);
foreach ($this->settings['groups'] as $i => $group) {
$result[] = $group . $groups[$i];
}
return $result;
}
/**
* Increments the group value to simulate deletion of all keys under a group
* old values will remain in storage until they expire.
*
* @return boolean success
*/
public function clearGroup($group) {
return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group);
}
}

View file

@ -34,9 +34,11 @@ class RedisEngine extends CacheEngine {
* Settings * Settings
* *
* - server = string URL or ip to the Redis server host * - server = string URL or ip to the Redis server host
* - database = integer database number to use for connection
* - port = integer port number to the Redis server (default: 6379) * - port = integer port number to the Redis server (default: 6379)
* - timeout = float timeout in seconds (default: 0) * - timeout = float timeout in seconds (default: 0)
* - persistent = boolean Connects to the Redis server with a persistent connection (default: true) * - persistent = boolean Connects to the Redis server with a persistent connection (default: true)
* - unix_socket = path to the unix socket file (default: false)
* *
* @var array * @var array
*/ */
@ -59,10 +61,12 @@ class RedisEngine extends CacheEngine {
'engine' => 'Redis', 'engine' => 'Redis',
'prefix' => null, 'prefix' => null,
'server' => '127.0.0.1', 'server' => '127.0.0.1',
'database' => 0,
'port' => 6379, 'port' => 6379,
'password' => false, 'password' => false,
'timeout' => 0, 'timeout' => 0,
'persistent' => true 'persistent' => true,
'unix_socket' => false
), $settings) ), $settings)
); );
@ -78,10 +82,13 @@ class RedisEngine extends CacheEngine {
$return = false; $return = false;
try { try {
$this->_Redis = new Redis(); $this->_Redis = new Redis();
if (empty($this->settings['persistent'])) { if (!empty($this->settings['unix_socket'])) {
$return = $this->_Redis->connect($this->settings['unix_socket']);
} elseif (empty($this->settings['persistent'])) {
$return = $this->_Redis->connect($this->settings['server'], $this->settings['port'], $this->settings['timeout']); $return = $this->_Redis->connect($this->settings['server'], $this->settings['port'], $this->settings['timeout']);
} else { } else {
$return = $this->_Redis->pconnect($this->settings['server'], $this->settings['port'], $this->settings['timeout']); $persistentId = $this->settings['port'] . $this->settings['timeout'] . $this->settings['database'];
$return = $this->_Redis->pconnect($this->settings['server'], $this->settings['port'], $this->settings['timeout'], $persistentId);
} }
} catch (RedisException $e) { } catch (RedisException $e) {
return false; return false;
@ -89,6 +96,9 @@ class RedisEngine extends CacheEngine {
if ($return && $this->settings['password']) { if ($return && $this->settings['password']) {
$return = $this->_Redis->auth($this->settings['password']); $return = $this->_Redis->auth($this->settings['password']);
} }
if ($return) {
$return = $this->_Redis->select($this->settings['database']);
}
return $return; return $return;
} }

View file

@ -363,9 +363,9 @@ class AclShell extends AppShell {
} }
/** /**
* Get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
@ -501,8 +501,7 @@ class AclShell extends AppShell {
) )
))->addSubcommand('initdb', array( ))->addSubcommand('initdb', array(
'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl') 'help' => __d('cake_console', 'Initialize the DbAcl tables. Uses this command : cake schema create DbAcl')
))->epilog( ))->epilog(array(
array(
'Node and parent arguments can be in one of the following formats:', 'Node and parent arguments can be in one of the following formats:',
'', '',
' - <model>.<id> - The node will be bound to a specific record of the given model.', ' - <model>.<id> - The node will be bound to a specific record of the given model.',
@ -512,8 +511,8 @@ class AclShell extends AppShell {
" i.e. <group>/<subgroup>/<parent>.", " i.e. <group>/<subgroup>/<parent>.",
'', '',
"To add a node at the root level, enter 'root' or '/' as the <parent> parameter." "To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
) ));
);
return $parser; return $parser;
} }

View file

@ -137,20 +137,24 @@ class ApiShell extends AppShell {
} }
/** /**
* Get and configure the optionparser. * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
$parser->addArgument('type', array(
$parser->description(
__d('cake_console', 'Lookup doc block comments for classes in CakePHP.')
)->addArgument('type', array(
'help' => __d('cake_console', 'Either a full path or type of class (model, behavior, controller, component, view, helper)') 'help' => __d('cake_console', 'Either a full path or type of class (model, behavior, controller, component, view, helper)')
))->addArgument('className', array( ))->addArgument('className', array(
'help' => __d('cake_console', 'A CakePHP core class name (e.g: Component, HtmlHelper).') 'help' => __d('cake_console', 'A CakePHP core class name (e.g: Component, HtmlHelper).')
))->addOption('method', array( ))->addOption('method', array(
'short' => 'm', 'short' => 'm',
'help' => __d('cake_console', 'The specific method you want help on.') 'help' => __d('cake_console', 'The specific method you want help on.')
))->description(__d('cake_console', 'Lookup doc block comments for classes in CakePHP.')); ));
return $parser; return $parser;
} }

View file

@ -203,18 +203,19 @@ class BakeShell extends AppShell {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(__d('cake_console',
'The Bake script generates controllers, views and models for your application.' . $parser->description(
__d('cake_console', 'The Bake script generates controllers, views and models for your application.' .
' If run with no command line arguments, Bake guides the user through the class creation process.' . ' If run with no command line arguments, Bake guides the user through the class creation process.' .
' You can customize the generation process by telling Bake where different parts of your application are using command line arguments.' ' You can customize the generation process by telling Bake where different parts of your application are using command line arguments.')
))->addSubcommand('all', array( )->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake a complete MVC. optional <name> of a Model'), 'help' => __d('cake_console', 'Bake a complete MVC. optional <name> of a Model')
))->addSubcommand('project', array( ))->addSubcommand('project', array(
'help' => __d('cake_console', 'Bake a new app folder in the path supplied or in current directory if no path is specified'), 'help' => __d('cake_console', 'Bake a new app folder in the path supplied or in current directory if no path is specified'),
'parser' => $this->Project->getOptionParser() 'parser' => $this->Project->getOptionParser()
@ -247,6 +248,8 @@ class BakeShell extends AppShell {
'short' => 't', 'short' => 't',
'help' => __d('cake_console', 'Theme to use when baking code.') 'help' => __d('cake_console', 'Theme to use when baking code.')
)); ));
return $parser;
} }
} }

View file

@ -24,6 +24,13 @@ App::uses('Inflector', 'Utility');
*/ */
class CommandListShell extends AppShell { class CommandListShell extends AppShell {
/**
* Contains tasks to load and instantiate
*
* @var array
*/
public $tasks = array('Command');
/** /**
* startup * startup
* *
@ -55,7 +62,7 @@ class CommandListShell extends AppShell {
$this->out(__d('cake_console', "<info>Available Shells:</info>"), 2); $this->out(__d('cake_console', "<info>Available Shells:</info>"), 2);
} }
$shellList = $this->_getShellList(); $shellList = $this->Command->getShellList();
if (empty($shellList)) { if (empty($shellList)) {
return; return;
} }
@ -67,48 +74,6 @@ class CommandListShell extends AppShell {
} }
} }
/**
* Gets the shell command listing.
*
* @return array
*/
protected function _getShellList() {
$skipFiles = array('AppShell');
$plugins = CakePlugin::loaded();
$shellList = array_fill_keys($plugins, null) + array('CORE' => null, 'app' => null);
$corePath = App::core('Console/Command');
$shells = App::objects('file', $corePath[0]);
$shells = array_diff($shells, $skipFiles);
$this->_appendShells('CORE', $shells, $shellList);
$appShells = App::objects('Console/Command', null, false);
$appShells = array_diff($appShells, $shells, $skipFiles);
$this->_appendShells('app', $appShells, $shellList);
foreach ($plugins as $plugin) {
$pluginShells = App::objects($plugin . '.Console/Command');
$this->_appendShells($plugin, $pluginShells, $shellList);
}
return array_filter($shellList);
}
/**
* Scan the provided paths for shells, and append them into $shellList
*
* @param string $type
* @param array $shells
* @param array $shellList
* @return void
*/
protected function _appendShells($type, $shells, &$shellList) {
foreach ($shells as $shell) {
$shellList[$type][] = Inflector::underscore(str_replace('Shell', '', $shell));
}
}
/** /**
* Output text. * Output text.
* *
@ -155,21 +120,24 @@ class CommandListShell extends AppShell {
} }
/** /**
* get the option parser * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(__d('cake_console', 'Get the list of available shells for this CakePHP application.'))
->addOption('sort', array( $parser->description(
__d('cake_console', 'Get the list of available shells for this CakePHP application.')
)->addOption('sort', array(
'help' => __d('cake_console', 'Does nothing (deprecated)'), 'help' => __d('cake_console', 'Does nothing (deprecated)'),
'boolean' => true 'boolean' => true
)) ))->addOption('xml', array(
->addOption('xml', array(
'help' => __d('cake_console', 'Get the listing as XML.'), 'help' => __d('cake_console', 'Get the listing as XML.'),
'boolean' => true 'boolean' => true
)); ));
return $parser;
} }
} }

View file

@ -0,0 +1,155 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP Project
* @package Cake.Console.Command
* @since CakePHP v 2.5
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('AppShell', 'Console/Command');
/**
* Provide command completion shells such as bash.
*
* @package Cake.Console.Command
*/
class CompletionShell extends AppShell {
/**
* Contains tasks to load and instantiate
*
* @var array
*/
public $tasks = array('Command');
/**
* Echo no header by overriding the startup method
*
* @return void
*/
public function startup() {
}
/**
* Not called by the autocomplete shell - this is for curious users
*
* @return void
*/
public function main() {
return $this->out($this->getOptionParser()->help());
}
/**
* list commands
*
* @return void
*/
public function commands() {
$options = $this->Command->commands();
return $this->_output($options);
}
/**
* list options for the named command
*
* @return void
*/
public function options() {
$commandName = '';
if (!empty($this->args[0])) {
$commandName = $this->args[0];
}
$options = $this->Command->options($commandName);
return $this->_output($options);
}
/**
* list subcommands for the named command
*
* @return void
*/
public function subCommands() {
if (!$this->args) {
return $this->_output();
}
$options = $this->Command->subCommands($this->args[0]);
return $this->_output($options);
}
/**
* Guess autocomplete from the whole argument string
*
* @return void
*/
public function fuzzy() {
return $this->_output();
}
/**
* Gets the option parser instance and configures it.
*
* @return ConsoleOptionParser
*/
public function getOptionParser() {
$parser = parent::getOptionParser();
$parser->description(
__d('cake_console', 'Used by shells like bash to autocomplete command name, options and arguments')
)->addSubcommand('commands', array(
'help' => __d('cake_console', 'Output a list of available commands'),
'parser' => array(
'description' => __d('cake_console', 'List all availables'),
'arguments' => array(
)
)
))->addSubcommand('subcommands', array(
'help' => __d('cake_console', 'Output a list of available subcommands'),
'parser' => array(
'description' => __d('cake_console', 'List subcommands for a command'),
'arguments' => array(
'command' => array(
'help' => __d('cake_console', 'The command name'),
'required' => true,
)
)
)
))->addSubcommand('options', array(
'help' => __d('cake_console', 'Output a list of available options'),
'parser' => array(
'description' => __d('cake_console', 'List options'),
'arguments' => array(
'command' => array(
'help' => __d('cake_console', 'The command name'),
'required' => false,
)
)
)
))->epilog(
__d('cake_console', 'This command is not intended to be called manually')
);
return $parser;
}
/**
* Emit results as a string, space delimited
*
* @param array $options
* @return void
*/
protected function _output($options = array()) {
if ($options) {
return $this->out(implode($options, ' '));
}
}
}

View file

@ -105,19 +105,19 @@ class ConsoleShell extends AppShell {
} }
/** /**
* getOptionParser * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$description = array( $parser = parent::getOptionParser();
$parser->description(array(
'The interactive console is a tool for testing parts of your', 'The interactive console is a tool for testing parts of your',
'app before you write code.', 'app before you write code.',
'', '',
'See below for a list of supported commands.' 'See below for a list of supported commands.'
); ))->epilog(array(
$epilog = array(
'<info>Model testing</info>', '<info>Model testing</info>',
'', '',
'To test model results, use the name of your model without a leading $', 'To test model results, use the name of your model without a leading $',
@ -176,10 +176,9 @@ class ConsoleShell extends AppShell {
'To show all connected routes, do the following:', 'To show all connected routes, do the following:',
'', '',
"\tRoutes show", "\tRoutes show",
); ));
return parent::getOptionParser()
->description($description) return $parser;
->epilog($epilog);
} }
/** /**
* Prints the help message * Prints the help message

View file

@ -100,13 +100,14 @@ class I18nShell extends AppShell {
} }
/** /**
* Get and configure the Option parser * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.') __d('cake_console', 'I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')
)->addSubcommand('initdb', array( )->addSubcommand('initdb', array(
'help' => __d('cake_console', 'Initialize the i18n table.') 'help' => __d('cake_console', 'Initialize the i18n table.')
@ -114,6 +115,8 @@ class I18nShell extends AppShell {
'help' => __d('cake_console', 'Extract the po translations from your application'), 'help' => __d('cake_console', 'Extract the po translations from your application'),
'parser' => $this->Extract->getOptionParser() 'parser' => $this->Extract->getOptionParser()
)); ));
return $parser;
} }
} }

View file

@ -337,7 +337,10 @@ class SchemaShell extends AppShell {
$this->out("\n" . __d('cake_console', 'The following table(s) will be dropped.')); $this->out("\n" . __d('cake_console', 'The following table(s) will be dropped.'));
$this->out(array_keys($drop)); $this->out(array_keys($drop));
if ($this->in(__d('cake_console', 'Are you sure you want to drop the table(s)?'), array('y', 'n'), 'n') === 'y') { if (
!empty($this->params['yes']) ||
$this->in(__d('cake_console', 'Are you sure you want to drop the table(s)?'), array('y', 'n'), 'n') === 'y'
) {
$this->out(__d('cake_console', 'Dropping table(s).')); $this->out(__d('cake_console', 'Dropping table(s).'));
$this->_run($drop, 'drop', $Schema); $this->_run($drop, 'drop', $Schema);
} }
@ -345,7 +348,10 @@ class SchemaShell extends AppShell {
$this->out("\n" . __d('cake_console', 'The following table(s) will be created.')); $this->out("\n" . __d('cake_console', 'The following table(s) will be created.'));
$this->out(array_keys($create)); $this->out(array_keys($create));
if ($this->in(__d('cake_console', 'Are you sure you want to create the table(s)?'), array('y', 'n'), 'y') === 'y') { if (
!empty($this->params['yes']) ||
$this->in(__d('cake_console', 'Are you sure you want to create the table(s)?'), array('y', 'n'), 'y') === 'y'
) {
$this->out(__d('cake_console', 'Creating table(s).')); $this->out(__d('cake_console', 'Creating table(s).'));
$this->_run($create, 'create', $Schema); $this->_run($create, 'create', $Schema);
} }
@ -396,7 +402,10 @@ class SchemaShell extends AppShell {
$this->out("\n" . __d('cake_console', 'The following statements will run.')); $this->out("\n" . __d('cake_console', 'The following statements will run.'));
$this->out(array_map('trim', $contents)); $this->out(array_map('trim', $contents));
if ($this->in(__d('cake_console', 'Are you sure you want to alter the tables?'), array('y', 'n'), 'n') === 'y') { if (
!empty($this->params['yes']) ||
$this->in(__d('cake_console', 'Are you sure you want to alter the tables?'), array('y', 'n'), 'n') === 'y'
) {
$this->out(); $this->out();
$this->out(__d('cake_console', 'Updating Database...')); $this->out(__d('cake_console', 'Updating Database...'));
$this->_run($contents, 'update', $Schema); $this->_run($contents, 'update', $Schema);
@ -452,11 +461,13 @@ class SchemaShell extends AppShell {
} }
/** /**
* get the option parser * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser();
$plugin = array( $plugin = array(
'short' => 'p', 'short' => 'p',
'help' => __d('cake_console', 'The plugin to use.'), 'help' => __d('cake_console', 'The plugin to use.'),
@ -475,7 +486,9 @@ class SchemaShell extends AppShell {
'default' => 'schema.php' 'default' => 'schema.php'
); );
$name = array( $name = array(
'help' => __d('cake_console', 'Classname to use. If its Plugin.class, both name and plugin options will be set.') 'help' => __d('cake_console',
'Classname to use. If its Plugin.class, both name and plugin options will be set.'
)
); );
$snapshot = array( $snapshot = array(
'short' => 's', 'short' => 's',
@ -486,7 +499,9 @@ class SchemaShell extends AppShell {
'help' => __d('cake_console', 'Specify models as comma separated list.'), 'help' => __d('cake_console', 'Specify models as comma separated list.'),
); );
$dry = array( $dry = array(
'help' => __d('cake_console', 'Perform a dry run on create and update commands. Queries will be output instead of run.'), 'help' => __d('cake_console',
'Perform a dry run on create and update commands. Queries will be output instead of run.'
),
'boolean' => true 'boolean' => true
); );
$force = array( $force = array(
@ -500,8 +515,12 @@ class SchemaShell extends AppShell {
$exclude = array( $exclude = array(
'help' => __d('cake_console', 'Tables to exclude as comma separated list.') 'help' => __d('cake_console', 'Tables to exclude as comma separated list.')
); );
$yes = array(
'short' => 'y',
'help' => __d('cake_console', 'Do not prompt for confirmation. Be careful!'),
'boolean' => true
);
$parser = parent::getOptionParser();
$parser->description( $parser->description(
__d('cake_console', 'The Schema Shell generates a schema object from the database and updates the database from the schema.') __d('cake_console', 'The Schema Shell generates a schema object from the database and updates the database from the schema.')
)->addSubcommand('view', array( )->addSubcommand('view', array(
@ -527,7 +546,7 @@ class SchemaShell extends AppShell {
))->addSubcommand('create', array( ))->addSubcommand('create', array(
'help' => __d('cake_console', 'Drop and create tables based on the schema file.'), 'help' => __d('cake_console', 'Drop and create tables based on the schema file.'),
'parser' => array( 'parser' => array(
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot'), 'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot', 'yes'),
'args' => array( 'args' => array(
'name' => array( 'name' => array(
'help' => __d('cake_console', 'Name of schema to use.') 'help' => __d('cake_console', 'Name of schema to use.')
@ -540,7 +559,7 @@ class SchemaShell extends AppShell {
))->addSubcommand('update', array( ))->addSubcommand('update', array(
'help' => __d('cake_console', 'Alter the tables based on the schema file.'), 'help' => __d('cake_console', 'Alter the tables based on the schema file.'),
'parser' => array( 'parser' => array(
'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot', 'force'), 'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot', 'force', 'yes'),
'args' => array( 'args' => array(
'name' => array( 'name' => array(
'help' => __d('cake_console', 'Name of schema to use.') 'help' => __d('cake_console', 'Name of schema to use.')
@ -551,6 +570,7 @@ class SchemaShell extends AppShell {
) )
) )
)); ));
return $parser; return $parser;
} }

View file

@ -141,29 +141,25 @@ class ServerShell extends AppShell {
} }
/** /**
* Get and configure the optionparser. * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
$parser->addOption('host', array(
'short' => 'H',
'help' => __d('cake_console', 'ServerHost')
));
$parser->addOption('port', array(
'short' => 'p',
'help' => __d('cake_console', 'ListenPort')
));
$parser->addOption('document_root', array(
'short' => 'd',
'help' => __d('cake_console', 'DocumentRoot')
));
$parser->description(array( $parser->description(array(
__d('cake_console', 'PHP Built-in Server for CakePHP'), __d('cake_console', 'PHP Built-in Server for CakePHP'),
__d('cake_console', '<warning>[WARN] Don\'t use this at the production environment</warning>'), __d('cake_console', '<warning>[WARN] Don\'t use this at the production environment</warning>')
))->addOption('host', array(
'short' => 'H',
'help' => __d('cake_console', 'ServerHost')
))->addOption('port', array(
'short' => 'p',
'help' => __d('cake_console', 'ListenPort')
))->addOption('document_root', array(
'short' => 'd',
'help' => __d('cake_console', 'DocumentRoot')
)); ));
return $parser; return $parser;

View file

@ -0,0 +1,183 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.5
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('AppShell', 'Console/Command');
/**
* Base class for Shell Command reflection.
*
* @package Cake.Console.Command.Task
*/
class CommandTask extends AppShell {
/**
* Gets the shell command listing.
*
* @return array
*/
public function getShellList() {
$skipFiles = array('AppShell');
$plugins = CakePlugin::loaded();
$shellList = array_fill_keys($plugins, null) + array('CORE' => null, 'app' => null);
$corePath = App::core('Console/Command');
$shells = App::objects('file', $corePath[0]);
$shells = array_diff($shells, $skipFiles);
$this->_appendShells('CORE', $shells, $shellList);
$appShells = App::objects('Console/Command', null, false);
$appShells = array_diff($appShells, $shells, $skipFiles);
$this->_appendShells('app', $appShells, $shellList);
foreach ($plugins as $plugin) {
$pluginShells = App::objects($plugin . '.Console/Command');
$this->_appendShells($plugin, $pluginShells, $shellList);
}
return array_filter($shellList);
}
/**
* Scan the provided paths for shells, and append them into $shellList
*
* @param string $type
* @param array $shells
* @param array $shellList
* @return void
*/
protected function _appendShells($type, $shells, &$shellList) {
foreach ($shells as $shell) {
$shellList[$type][] = Inflector::underscore(str_replace('Shell', '', $shell));
}
}
/**
* Return a list of all commands
*
* @return array
*/
public function commands() {
$shellList = $this->getShellList();
$options = array();
foreach ($shellList as $type => $commands) {
$prefix = '';
if (!in_array(strtolower($type), array('app', 'core'))) {
$prefix = $type . '.';
}
foreach ($commands as $shell) {
$options[] = $prefix . $shell;
}
}
return $options;
}
/**
* Return a list of subcommands for a given command
*
* @param string $commandName
* @return array
*/
public function subCommands($commandName) {
$Shell = $this->getShell($commandName);
if (!$Shell) {
return array();
}
$taskMap = TaskCollection::normalizeObjectArray((array)$Shell->tasks);
$return = array_keys($taskMap);
$return = array_map('Inflector::underscore', $return);
$ShellReflection = new ReflectionClass('AppShell');
$shellMethods = $ShellReflection->getMethods(ReflectionMethod::IS_PUBLIC);
$shellMethodNames = array('main', 'help');
foreach ($shellMethods as $method) {
$shellMethodNames[] = $method->getName();
}
$Reflection = new ReflectionClass($Shell);
$methods = $Reflection->getMethods(ReflectionMethod::IS_PUBLIC);
$methodNames = array();
foreach ($methods as $method) {
$methodNames[] = $method->getName();
}
$return += array_diff($methodNames, $shellMethodNames);
sort($return);
return $return;
}
/**
* Get Shell instance for the given command
*
* @param mixed $commandName
* @return mixed
*/
public function getShell($commandName) {
list($pluginDot, $name) = pluginSplit($commandName, true);
if (in_array(strtolower($pluginDot), array('app.', 'core.'))) {
$commandName = $name;
$pluginDot = '';
}
if (!in_array($commandName, $this->commands())) {
return false;
}
$name = Inflector::camelize($name);
$pluginDot = Inflector::camelize($pluginDot);
$class = $name . 'Shell';
App::uses($class, $pluginDot . 'Console/Command');
$Shell = new $class();
$Shell->plugin = trim($pluginDot, '.');
$Shell->initialize();
return $Shell;
}
/**
* Get Shell instance for the given command
*
* @param mixed $commandName
* @return array
*/
public function options($commandName) {
$Shell = $this->getShell($commandName);
if (!$Shell) {
$parser = new ConsoleOptionParser();
} else {
$parser = $Shell->getOptionParser();
}
$options = array();
$array = $parser->options();
foreach ($array as $name => $obj) {
$options[] = "--$name";
$short = $obj->short();
if ($short) {
$options[] = "-$short";
}
}
return $options;
}
}

View file

@ -466,15 +466,16 @@ class ControllerTask extends BakeTask {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
__d('cake_console', 'Bake a controller for a model. Using options you can bake public, admin or both.') $parser->description(
)->addArgument('name', array( __d('cake_console', 'Bake a controller for a model. Using options you can bake public, admin or both.'
))->addArgument('name', array(
'help' => __d('cake_console', 'Name of the controller to bake. Can use Plugin.name to bake controllers into plugins.') 'help' => __d('cake_console', 'Name of the controller to bake. Can use Plugin.name to bake controllers into plugins.')
))->addOption('public', array( ))->addOption('public', array(
'help' => __d('cake_console', 'Bake a controller with basic crud actions (index, view, add, edit, delete).'), 'help' => __d('cake_console', 'Bake a controller with basic crud actions (index, view, add, edit, delete).'),
@ -496,7 +497,11 @@ class ControllerTask extends BakeTask {
'help' => __d('cake_console', 'Force overwriting existing files without prompting.') 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addSubcommand('all', array( ))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all controllers with CRUD methods.') 'help' => __d('cake_console', 'Bake all controllers with CRUD methods.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);
return $parser;
} }
} }

View file

@ -203,7 +203,7 @@ class DbConfigTask extends AppShell {
* @return boolean True if user says it looks good, false otherwise * @return boolean True if user says it looks good, false otherwise
*/ */
protected function _verify($config) { protected function _verify($config) {
$config = array_merge($this->_defaultConfig, $config); $config += $this->_defaultConfig;
extract($config); extract($config);
$this->out(); $this->out();
$this->hr(); $this->hr();
@ -264,7 +264,7 @@ class DbConfigTask extends AppShell {
$temp = get_class_vars(get_class($db)); $temp = get_class_vars(get_class($db));
foreach ($temp as $configName => $info) { foreach ($temp as $configName => $info) {
$info = array_merge($this->_defaultConfig, $info); $info += $this->_defaultConfig;
if (!isset($info['schema'])) { if (!isset($info['schema'])) {
$info['schema'] = null; $info['schema'] = null;
@ -307,7 +307,7 @@ class DbConfigTask extends AppShell {
$out .= "class DATABASE_CONFIG {\n\n"; $out .= "class DATABASE_CONFIG {\n\n";
foreach ($configs as $config) { foreach ($configs as $config) {
$config = array_merge($this->_defaultConfig, $config); $config += $this->_defaultConfig;
extract($config); extract($config);
if (strpos($datasource, 'Database/') === false) { if (strpos($datasource, 'Database/') === false) {
@ -368,15 +368,18 @@ class DbConfigTask extends AppShell {
} }
/** /**
* get the option parser * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'Bake new database configuration settings.') __d('cake_console', 'Bake new database configuration settings.')
); );
return $parser;
} }
} }

View file

@ -296,52 +296,55 @@ class ExtractTask extends AppShell {
} }
/** /**
* Get & configure the option parser * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(__d('cake_console', 'CakePHP Language String Extraction:'))
->addOption('app', array('help' => __d('cake_console', 'Directory where your application is located.'))) $parser->description(
->addOption('paths', array('help' => __d('cake_console', 'Comma separated list of paths.'))) __d('cake_console', 'CakePHP Language String Extraction:')
->addOption('merge', array( )->addOption('app', array(
'help' => __d('cake_console', 'Directory where your application is located.')
))->addOption('paths', array(
'help' => __d('cake_console', 'Comma separated list of paths.')
))->addOption('merge', array(
'help' => __d('cake_console', 'Merge all domain and category strings into the default.po file.'), 'help' => __d('cake_console', 'Merge all domain and category strings into the default.po file.'),
'choices' => array('yes', 'no') 'choices' => array('yes', 'no')
)) ))->addOption('output', array(
->addOption('output', array('help' => __d('cake_console', 'Full path to output directory.'))) 'help' => __d('cake_console', 'Full path to output directory.')
->addOption('files', array('help' => __d('cake_console', 'Comma separated list of files.'))) ))->addOption('files', array(
->addOption('exclude-plugins', array( 'help' => __d('cake_console', 'Comma separated list of files.')
))->addOption('exclude-plugins', array(
'boolean' => true, 'boolean' => true,
'default' => true, 'default' => true,
'help' => __d('cake_console', 'Ignores all files in plugins if this command is run inside from the same app directory.') 'help' => __d('cake_console', 'Ignores all files in plugins if this command is run inside from the same app directory.')
)) ))->addOption('plugin', array(
->addOption('plugin', array(
'help' => __d('cake_console', 'Extracts tokens only from the plugin specified and puts the result in the plugin\'s Locale directory.') 'help' => __d('cake_console', 'Extracts tokens only from the plugin specified and puts the result in the plugin\'s Locale directory.')
)) ))->addOption('ignore-model-validation', array(
->addOption('ignore-model-validation', array(
'boolean' => true, 'boolean' => true,
'default' => false, 'default' => false,
'help' => __d('cake_console', 'Ignores validation messages in the $validate property.' . 'help' => __d('cake_console', 'Ignores validation messages in the $validate property.' .
' If this flag is not set and the command is run from the same app directory,' . ' If this flag is not set and the command is run from the same app directory,' .
' all messages in model validation rules will be extracted as tokens.') ' all messages in model validation rules will be extracted as tokens.'
)) )
->addOption('validation-domain', array( ))->addOption('validation-domain', array(
'help' => __d('cake_console', 'If set to a value, the localization domain to be used for model validation messages.') 'help' => __d('cake_console', 'If set to a value, the localization domain to be used for model validation messages.')
)) ))->addOption('exclude', array(
->addOption('exclude', array(
'help' => __d('cake_console', 'Comma separated list of directories to exclude.' . 'help' => __d('cake_console', 'Comma separated list of directories to exclude.' .
' Any path containing a path segment with the provided values will be skipped. E.g. test,vendors') ' Any path containing a path segment with the provided values will be skipped. E.g. test,vendors'
)) )
->addOption('overwrite', array( ))->addOption('overwrite', array(
'boolean' => true, 'boolean' => true,
'default' => false, 'default' => false,
'help' => __d('cake_console', 'Always overwrite existing .pot files.') 'help' => __d('cake_console', 'Always overwrite existing .pot files.')
)) ))->addOption('extract-core', array(
->addOption('extract-core', array(
'help' => __d('cake_console', 'Extract messages from the CakePHP core libs.'), 'help' => __d('cake_console', 'Extract messages from the CakePHP core libs.'),
'choices' => array('yes', 'no') 'choices' => array('yes', 'no')
)); ));
return $parser;
} }
/** /**

View file

@ -60,13 +60,14 @@ class FixtureTask extends BakeTask {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.') __d('cake_console', 'Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.')
)->addArgument('name', array( )->addArgument('name', array(
'help' => __d('cake_console', 'Name of the fixture to bake. Can use Plugin.name to bake plugin fixtures.') 'help' => __d('cake_console', 'Name of the fixture to bake. Can use Plugin.name to bake plugin fixtures.')
@ -80,7 +81,7 @@ class FixtureTask extends BakeTask {
'default' => 'default' 'default' => 'default'
))->addOption('plugin', array( ))->addOption('plugin', array(
'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'), 'help' => __d('cake_console', 'CamelCased name of the plugin to bake fixtures for.'),
'short' => 'p', 'short' => 'p'
))->addOption('schema', array( ))->addOption('schema', array(
'help' => __d('cake_console', 'Importing schema for fixtures rather than hardcoding it.'), 'help' => __d('cake_console', 'Importing schema for fixtures rather than hardcoding it.'),
'short' => 's', 'short' => 's',
@ -92,10 +93,15 @@ class FixtureTask extends BakeTask {
'short' => 'f', 'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.') 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addOption('records', array( ))->addOption('records', array(
'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10.'), 'help' => __d('cake_console', 'Used with --count and <name>/all commands to pull [n] records from the live tables, ' .
'where [n] is either --count or the default of 10.'),
'short' => 'r', 'short' => 'r',
'boolean' => true 'boolean' => true
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);
return $parser;
} }
/** /**

View file

@ -348,18 +348,27 @@ class ModelTask extends BakeTask {
if (!$model instanceof Model) { if (!$model instanceof Model) {
return false; return false;
} }
$fields = $model->schema();
$fields = $model->schema();
if (empty($fields)) { if (empty($fields)) {
return false; return false;
} }
$skipFields = false;
$validate = array(); $validate = array();
$this->initValidations(); $this->initValidations();
foreach ($fields as $fieldName => $field) { foreach ($fields as $fieldName => $field) {
$validation = $this->fieldValidation($fieldName, $field, $model->primaryKey); $validation = $this->fieldValidation($fieldName, $field, $model->primaryKey);
if (isset($validation['_skipFields'])) {
unset($validation['_skipFields']);
$skipFields = true;
}
if (!empty($validation)) { if (!empty($validation)) {
$validate[$fieldName] = $validation; $validate[$fieldName] = $validation;
} }
if ($skipFields) {
return $validate;
}
} }
return $validate; return $validate;
} }
@ -399,6 +408,11 @@ class ModelTask extends BakeTask {
$defaultChoice = count($this->_validations); $defaultChoice = count($this->_validations);
$validate = $alreadyChosen = array(); $validate = $alreadyChosen = array();
$prompt = __d('cake_console',
"or enter in a valid regex validation string.\nAlternatively [s] skip the rest of the fields.\n"
);
$methods = array_flip($this->_validations);
$anotherValidator = 'y'; $anotherValidator = 'y';
while ($anotherValidator === 'y') { while ($anotherValidator === 'y') {
if ($this->interactive) { if ($this->interactive) {
@ -422,8 +436,6 @@ class ModelTask extends BakeTask {
$this->hr(); $this->hr();
} }
$prompt = __d('cake_console', "... or enter in a valid regex validation string.\n");
$methods = array_flip($this->_validations);
$guess = $defaultChoice; $guess = $defaultChoice;
if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) { if ($metaData['null'] != 1 && !in_array($fieldName, array($primaryKey, 'created', 'modified', 'updated'))) {
if ($fieldName === 'email') { if ($fieldName === 'email') {
@ -453,6 +465,10 @@ class ModelTask extends BakeTask {
if ($this->interactive === true) { if ($this->interactive === true) {
$choice = $this->in($prompt, null, $guess); $choice = $this->in($prompt, null, $guess);
if ($choice === 's') {
$validate['_skipFields'] = true;
return $validate;
}
if (in_array($choice, $alreadyChosen)) { if (in_array($choice, $alreadyChosen)) {
$this->out(__d('cake_console', "You have already chosen that validation rule,\nplease choose again")); $this->out(__d('cake_console', "You have already chosen that validation rule,\nplease choose again"));
continue; continue;
@ -480,7 +496,12 @@ class ModelTask extends BakeTask {
} }
$anotherValidator = 'n'; $anotherValidator = 'n';
if ($this->interactive && $choice != $defaultChoice) { if ($this->interactive && $choice != $defaultChoice) {
$anotherValidator = $this->in(__d('cake_console', 'Would you like to add another validation rule?'), array('y', 'n'), 'n'); $anotherValidator = $this->in(__d('cake_console', "Would you like to add another validation rule\n" .
"or skip the rest of the fields?"), array('y', 'n', 's'), 'n');
if ($anotherValidator === 's') {
$validate['_skipFields'] = true;
return $validate;
}
} }
} }
return $validate; return $validate;
@ -978,13 +999,14 @@ class ModelTask extends BakeTask {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'Bake models.') __d('cake_console', 'Bake models.')
)->addArgument('name', array( )->addArgument('name', array(
'help' => __d('cake_console', 'Name of the model to bake. Can use Plugin.name to bake plugin models.') 'help' => __d('cake_console', 'Name of the model to bake. Can use Plugin.name to bake plugin models.')
@ -1002,7 +1024,11 @@ class ModelTask extends BakeTask {
))->addOption('force', array( ))->addOption('force', array(
'short' => 'f', 'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.') 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);
return $parser;
} }
/** /**

View file

@ -211,18 +211,21 @@ class PluginTask extends AppShell {
} }
/** /**
* get the option parser for the plugin task * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(__d('cake_console',
'Create the directory structure, AppModel and AppController classes for a new plugin. ' . $parser->description(
'Can create plugins in any of your bootstrapped plugin paths.' __d('cake_console', 'Create the directory structure, AppModel and AppController classes for a new plugin. ' .
))->addArgument('name', array( 'Can create plugins in any of your bootstrapped plugin paths.')
)->addArgument('name', array(
'help' => __d('cake_console', 'CamelCased name of the plugin to create.') 'help' => __d('cake_console', 'CamelCased name of the plugin to create.')
)); ));
return $parser;
} }
} }

View file

@ -419,13 +419,14 @@ class ProjectTask extends AppShell {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'Generate a new CakePHP project skeleton.') __d('cake_console', 'Generate a new CakePHP project skeleton.')
)->addArgument('name', array( )->addArgument('name', array(
'help' => __d('cake_console', 'Application directory to make, if it starts with "/" the path is absolute.') 'help' => __d('cake_console', 'Application directory to make, if it starts with "/" the path is absolute.')
@ -437,8 +438,11 @@ class ProjectTask extends AppShell {
'help' => __d('cake_console', 'Theme to use when baking code.') 'help' => __d('cake_console', 'Theme to use when baking code.')
))->addOption('skel', array( ))->addOption('skel', array(
'default' => current(App::core('Console')) . 'Templates' . DS . 'skel', 'default' => current(App::core('Console')) . 'Templates' . DS . 'skel',
'help' => __d('cake_console', 'The directory layout to use for the new application skeleton. Defaults to cake/Console/Templates/skel of CakePHP used to create the project.') 'help' => __d('cake_console', 'The directory layout to use for the new application skeleton.' .
' Defaults to cake/Console/Templates/skel of CakePHP used to create the project.')
)); ));
return $parser;
} }
} }

View file

@ -543,14 +543,16 @@ class TestTask extends BakeTask {
} }
/** /**
* get the option parser. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(__d('cake_console', 'Bake test case skeletons for classes.'))
->addArgument('type', array( $parser->description(
__d('cake_console', 'Bake test case skeletons for classes.')
)->addArgument('type', array(
'help' => __d('cake_console', 'Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'), 'help' => __d('cake_console', 'Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'),
'choices' => array( 'choices' => array(
'Controller', 'controller', 'Controller', 'controller',
@ -570,7 +572,11 @@ class TestTask extends BakeTask {
))->addOption('force', array( ))->addOption('force', array(
'short' => 'f', 'short' => 'f',
'help' => __d('cake_console', 'Force overwriting existing files without prompting.') 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);
return $parser;
} }
} }

View file

@ -412,13 +412,14 @@ class ViewTask extends BakeTask {
} }
/** /**
* get the option parser for this task * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
return $parser->description(
$parser->description(
__d('cake_console', 'Bake views for a controller, using built-in or custom templates.') __d('cake_console', 'Bake views for a controller, using built-in or custom templates.')
)->addArgument('controller', array( )->addArgument('controller', array(
'help' => __d('cake_console', 'Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.') 'help' => __d('cake_console', 'Name of the controller views to bake. Can be Plugin.name as a shortcut for plugin baking.')
@ -443,7 +444,11 @@ class ViewTask extends BakeTask {
'help' => __d('cake_console', 'Force overwriting existing files without prompting.') 'help' => __d('cake_console', 'Force overwriting existing files without prompting.')
))->addSubcommand('all', array( ))->addSubcommand('all', array(
'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.') 'help' => __d('cake_console', 'Bake all CRUD action views for all controllers. Requires models and controllers to exist.')
))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')); ))->epilog(
__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.')
);
return $parser;
} }
/** /**

View file

@ -38,20 +38,21 @@ class TestShell extends Shell {
protected $_dispatcher = null; protected $_dispatcher = null;
/** /**
* get the option parser for the test suite. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = new ConsoleOptionParser($this->name); $parser = new ConsoleOptionParser($this->name);
$parser->description(array(
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'), $parser->description(
))->addArgument('category', array( __d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line')
)->addArgument('category', array(
'help' => __d('cake_console', 'The category for the test, or test file, to test.'), 'help' => __d('cake_console', 'The category for the test, or test file, to test.'),
'required' => false, 'required' => false
))->addArgument('file', array( ))->addArgument('file', array(
'help' => __d('cake_console', 'The path to the file, or test file, to test.'), 'help' => __d('cake_console', 'The path to the file, or test file, to test.'),
'required' => false, 'required' => false
))->addOption('log-junit', array( ))->addOption('log-junit', array(
'help' => __d('cake_console', '<file> Log test execution in JUnit XML format to file.'), 'help' => __d('cake_console', '<file> Log test execution in JUnit XML format to file.'),
'default' => false 'default' => false
@ -153,9 +154,9 @@ class TestShell extends Shell {
'help' => __d('cake_console', 'key[=value] Sets a php.ini value.'), 'help' => __d('cake_console', 'key[=value] Sets a php.ini value.'),
'default' => false 'default' => false
))->addOption('fixture', array( ))->addOption('fixture', array(
'help' => __d('cake_console', 'Choose a custom fixture manager.'), 'help' => __d('cake_console', 'Choose a custom fixture manager.')
))->addOption('debug', array( ))->addOption('debug', array(
'help' => __d('cake_console', 'More verbose output.'), 'help' => __d('cake_console', 'More verbose output.')
)); ));
return $parser; return $parser;

View file

@ -32,15 +32,16 @@ App::uses('CakeTestLoader', 'TestSuite');
class TestsuiteShell extends TestShell { class TestsuiteShell extends TestShell {
/** /**
* get the option parser for the test suite. * Gets the option parser instance and configures it.
* *
* @return void * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser(); $parser = parent::getOptionParser();
$parser->description(array( $parser->description(array(
__d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'), __d('cake_console', 'The CakePHP Testsuite allows you to run test cases from the command line'),
__d('cake_console', "<warning>This shell is for backwards-compatibility only</warning>\nuse the test shell instead"), __d('cake_console', "<warning>This shell is for backwards-compatibility only</warning>\nuse the test shell instead")
)); ));
return $parser; return $parser;

View file

@ -199,7 +199,7 @@ class UpgradeShell extends AppShell {
$dir = $options; $dir = $options;
$options = array(); $options = array();
} }
$options = array_merge($defaultOptions, $options); $options += $defaultOptions;
$this->_movePhpFiles($dir, $options); $this->_movePhpFiles($dir, $options);
} }
} }
@ -392,6 +392,11 @@ class UpgradeShell extends AppShell {
'/(\$this->action\b(?!\())/', '/(\$this->action\b(?!\())/',
'$this->request->action' '$this->request->action'
), ),
array(
'$this->request->onlyAllow() -> $this->request->allowMethod()',
'/\$this->request->onlyAllow\(/',
'$this->request->allowMethod('
)
); );
$this->_filesRegexpUpdate($patterns); $this->_filesRegexpUpdate($patterns);
} }
@ -500,6 +505,29 @@ class UpgradeShell extends AppShell {
$this->_filesRegexpUpdate($patterns); $this->_filesRegexpUpdate($patterns);
} }
/**
* Update controller redirects.
*
* - Make redirect statements return early.
*
* @return void
*/
public function controller_redirects() {
$this->_paths = App::Path('Controller');
if (!empty($this->params['plugin'])) {
$this->_paths = App::Path('Controller', $this->params['plugin']);
}
$patterns = array(
array(
'$this->redirect() to return $this->redirect()',
'/\t\$this-\>redirect\(/',
"\t" . 'return $this->redirect('
),
);
$this->_filesRegexpUpdate($patterns);
}
/** /**
* Update components. * Update components.
* *
@ -779,11 +807,13 @@ class UpgradeShell extends AppShell {
} }
/** /**
* get the option parser * Gets the option parser instance and configures it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser
*/ */
public function getOptionParser() { public function getOptionParser() {
$parser = parent::getOptionParser();
$subcommandParser = array( $subcommandParser = array(
'options' => array( 'options' => array(
'plugin' => array( 'plugin' => array(
@ -808,53 +838,48 @@ class UpgradeShell extends AppShell {
) )
); );
return parent::getOptionParser() $parser->description(
->description(__d('cake_console', "A shell to help automate upgrading from CakePHP 1.3 to 2.0. \n" . __d('cake_console', "A shell to help automate upgrading from CakePHP 1.3 to 2.0. \n" .
"Be sure to have a backup of your application before running these commands.")) "Be sure to have a backup of your application before running these commands."
->addSubcommand('all', array( ))->addSubcommand('all', array(
'help' => __d('cake_console', 'Run all upgrade commands.'), 'help' => __d('cake_console', 'Run all upgrade commands.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('tests', array(
->addSubcommand('tests', array(
'help' => __d('cake_console', 'Update tests class names to FooTest rather than FooTestCase.'), 'help' => __d('cake_console', 'Update tests class names to FooTest rather than FooTestCase.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('locations', array(
->addSubcommand('locations', array(
'help' => __d('cake_console', 'Move files and folders to their new homes.'), 'help' => __d('cake_console', 'Move files and folders to their new homes.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('i18n', array(
->addSubcommand('i18n', array(
'help' => __d('cake_console', 'Update the i18n translation method calls.'), 'help' => __d('cake_console', 'Update the i18n translation method calls.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('helpers', array(
->addSubcommand('helpers', array(
'help' => __d('cake_console', 'Update calls to helpers.'), 'help' => __d('cake_console', 'Update calls to helpers.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('basics', array(
->addSubcommand('basics', array(
'help' => __d('cake_console', 'Update removed basics functions to PHP native functions.'), 'help' => __d('cake_console', 'Update removed basics functions to PHP native functions.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('request', array(
->addSubcommand('request', array(
'help' => __d('cake_console', 'Update removed request access, and replace with $this->request.'), 'help' => __d('cake_console', 'Update removed request access, and replace with $this->request.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('configure', array(
->addSubcommand('configure', array(
'help' => __d('cake_console', "Update Configure::read() to Configure::read('debug')"), 'help' => __d('cake_console', "Update Configure::read() to Configure::read('debug')"),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('constants', array(
->addSubcommand('constants', array(
'help' => __d('cake_console', "Replace Obsolete constants"), 'help' => __d('cake_console', "Replace Obsolete constants"),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('controller_redirects', array(
->addSubcommand('components', array( 'help' => __d('cake_console', 'Return early on controller redirect calls.'),
'parser' => $subcommandParser
))->addSubcommand('components', array(
'help' => __d('cake_console', 'Update components to extend Component class.'), 'help' => __d('cake_console', 'Update components to extend Component class.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)) ))->addSubcommand('exceptions', array(
->addSubcommand('exceptions', array(
'help' => __d('cake_console', 'Replace use of cakeError with exceptions.'), 'help' => __d('cake_console', 'Replace use of cakeError with exceptions.'),
'parser' => $subcommandParser 'parser' => $subcommandParser
)); ));
return $parser;
} }
} }

View file

@ -299,7 +299,7 @@ class ConsoleOptionParser {
'boolean' => false, 'boolean' => false,
'choices' => array() 'choices' => array()
); );
$options = array_merge($defaults, $options); $options += $defaults;
$option = new ConsoleInputOption($options); $option = new ConsoleInputOption($options);
} }
$this->_options[$name] = $option; $this->_options[$name] = $option;
@ -338,7 +338,7 @@ class ConsoleOptionParser {
'required' => false, 'required' => false,
'choices' => array() 'choices' => array()
); );
$options = array_merge($defaults, $params); $options = $params + $defaults;
$index = $options['index']; $index = $options['index'];
unset($options['index']); unset($options['index']);
$arg = new ConsoleInputArgument($options); $arg = new ConsoleInputArgument($options);
@ -403,7 +403,7 @@ class ConsoleOptionParser {
'help' => '', 'help' => '',
'parser' => null 'parser' => null
); );
$options = array_merge($defaults, $options); $options += $defaults;
$command = new ConsoleInputSubcommand($options); $command = new ConsoleInputSubcommand($options);
} }
$this->_subcommands[$name] = $command; $this->_subcommands[$name] = $command;

View file

@ -462,6 +462,7 @@ class Shell extends Object {
/** /**
* Gets the option parser instance and configures it. * Gets the option parser instance and configures it.
*
* By overriding this method you can configure the ConsoleOptionParser before returning it. * By overriding this method you can configure the ConsoleOptionParser before returning it.
* *
* @return ConsoleOptionParser * @return ConsoleOptionParser

View file

@ -311,7 +311,7 @@ class ShellDispatcher {
$params = str_replace('/', '\\', $params); $params = str_replace('/', '\\', $params);
} }
$this->params = array_merge($this->params, $params); $this->params = $params + $this->params;
} }
/** /**

View file

@ -135,7 +135,7 @@
if (!$this-><?php echo $currentModelName; ?>->exists()) { if (!$this-><?php echo $currentModelName; ?>->exists()) {
throw new NotFoundException(__('Invalid <?php echo strtolower($singularHumanName); ?>')); throw new NotFoundException(__('Invalid <?php echo strtolower($singularHumanName); ?>'));
} }
$this->request->onlyAllow('post', 'delete'); $this->request->allowMethod('post', 'delete');
if ($this-><?php echo $currentModelName; ?>->delete()) { if ($this-><?php echo $currentModelName; ?>->delete()) {
<?php if ($wannaUseSession): ?> <?php if ($wannaUseSession): ?>
$this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> has been deleted.')); $this->Session->setFlash(__('The <?php echo strtolower($singularHumanName); ?> has been deleted.'));

View file

@ -75,6 +75,7 @@ class <?php echo $fullClassName; ?>Test extends CakeTestCase {
* @return void * @return void
*/ */
public function test<?php echo Inflector::camelize($method); ?>() { public function test<?php echo Inflector::camelize($method); ?>() {
$this->markTestIncomplete('test<?php echo Inflector::camelize($method); ?> not implemented.');
} }
<?php endforeach; ?> <?php endforeach; ?>

View file

@ -47,7 +47,7 @@
echo "\t\t<td class=\"actions\">\n"; echo "\t\t<td class=\"actions\">\n";
echo "\t\t\t<?php echo \$this->Html->link(__('View'), array('action' => 'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; echo "\t\t\t<?php echo \$this->Html->link(__('View'), array('action' => 'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
echo "\t\t\t<?php echo \$this->Html->link(__('Edit'), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; echo "\t\t\t<?php echo \$this->Html->link(__('Edit'), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
echo "\t\t\t<?php echo \$this->Form->postLink(__('Delete'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; echo "\t\t\t<?php echo \$this->Form->postLink(__('Delete'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), array(), __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n";
echo "\t\t</td>\n"; echo "\t\t</td>\n";
echo "\t</tr>\n"; echo "\t</tr>\n";

View file

@ -41,12 +41,15 @@
* *
* unix_socket => * unix_socket =>
* For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port` * For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port`
*
* settings => * settings =>
* Array of key/value pairs, on connection it executes SET statements for each pair * Array of key/value pairs, on connection it executes SET statements for each pair
* For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html * For MySQL : http://dev.mysql.com/doc/refman/5.6/en/set-statement.html
* For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html * For Postgres : http://www.postgresql.org/docs/9.2/static/sql-set.html
* For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx * For Sql Server : http://msdn.microsoft.com/en-us/library/ms190356.aspx
*
* flags =>
* A key/value array of driver specific connection options.
*/ */
class DATABASE_CONFIG { class DATABASE_CONFIG {

View file

@ -0,0 +1,26 @@
<?php
/**
* AllTests file
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @package app.Test.Case
* @since CakePHP(tm) v 2.5
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
class AllTestsTest extends CakeTestSuite {
public static function suite() {
$suite = new CakeTestSuite('All application tests');
$suite->addTestDirectoryRecursive(TESTS . 'Case');
return $suite;
}
}

View file

@ -7,7 +7,7 @@
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $message; ?></h2>
<p class="error"> <p class="error">
<strong><?php echo __d('cake', 'Error'); ?>: </strong> <strong><?php echo __d('cake', 'Error'); ?>: </strong>
<?php printf( <?php printf(

View file

@ -7,7 +7,7 @@
* @since CakePHP(tm) v 0.10.0.1076 * @since CakePHP(tm) v 0.10.0.1076
*/ */
?> ?>
<h2><?php echo $name; ?></h2> <h2><?php echo $message; ?></h2>
<p class="error"> <p class="error">
<strong><?php echo __d('cake', 'Error'); ?>: </strong> <strong><?php echo __d('cake', 'Error'); ?>: </strong>
<?php echo __d('cake', 'An Internal Error Has Occurred.'); ?> <?php echo __d('cake', 'An Internal Error Has Occurred.'); ?>

View file

@ -10,7 +10,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html> <html>
<head> <head>
<title><?php echo $title_for_layout; ?></title> <title><?php echo $this->fetch('title'); ?></title>
</head> </head>
<body> <body>
<?php echo $this->fetch('content'); ?> <?php echo $this->fetch('content'); ?>

View file

@ -15,7 +15,7 @@ $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework
<?php echo $this->Html->charset(); ?> <?php echo $this->Html->charset(); ?>
<title> <title>
<?php echo $cakeDescription ?>: <?php echo $cakeDescription ?>:
<?php echo $title_for_layout; ?> <?php echo $this->fetch('title'); ?>
</title> </title>
<?php <?php
echo $this->Html->meta('icon'); echo $this->Html->meta('icon');

View file

@ -15,7 +15,7 @@ $cakeDescription = __d('cake_dev', 'CakePHP: the rapid development php framework
<?php echo $this->Html->charset(); ?> <?php echo $this->Html->charset(); ?>
<title> <title>
<?php echo $cakeDescription ?>: <?php echo $cakeDescription ?>:
<?php echo $title_for_layout; ?> <?php echo $this->fetch('title'); ?>
</title> </title>
<?php <?php
echo $this->Html->meta('icon'); echo $this->Html->meta('icon');

View file

@ -3,7 +3,7 @@ if (!isset($channel)):
$channel = array(); $channel = array();
endif; endif;
if (!isset($channel['title'])): if (!isset($channel['title'])):
$channel['title'] = $title_for_layout; $channel['title'] = $this->fetch('title');
endif; endif;
echo $this->Rss->document( echo $this->Rss->document(

View file

@ -92,7 +92,7 @@ class Component extends Object {
*/ */
public function __get($name) { public function __get($name) {
if (isset($this->_componentMap[$name]) && !isset($this->{$name})) { if (isset($this->_componentMap[$name]) && !isset($this->{$name})) {
$settings = array_merge((array)$this->_componentMap[$name]['settings'], array('enabled' => false)); $settings = array('enabled' => false) + (array)$this->_componentMap[$name]['settings'];
$this->{$name} = $this->_Collection->load($this->_componentMap[$name]['class'], $settings); $this->{$name} = $this->_Collection->load($this->_componentMap[$name]['class'], $settings);
} }
if (isset($this->{$name})) { if (isset($this->{$name})) {

View file

@ -81,7 +81,7 @@ class PhpAcl extends Object implements AclInterface {
*/ */
public function initialize(Component $Component) { public function initialize(Component $Component) {
if (!empty($Component->settings['adapter'])) { if (!empty($Component->settings['adapter'])) {
$this->options = array_merge($this->options, $Component->settings['adapter']); $this->options = $Component->settings['adapter'] + $this->options;
} }
App::uses('PhpReader', 'Configure'); App::uses('PhpReader', 'Configure');
@ -546,7 +546,7 @@ class PhpAro {
* @return void * @return void
*/ */
public function addAlias(array $alias) { public function addAlias(array $alias) {
$this->aliases = array_merge($this->aliases, $alias); $this->aliases = $alias + $this->aliases;
} }
/** /**

View file

@ -365,6 +365,7 @@ class AuthComponent extends Component {
return false; return false;
} }
if (!empty($this->ajaxLogin)) { if (!empty($this->ajaxLogin)) {
$controller->response->statusCode(403);
$controller->viewPath = 'Elements'; $controller->viewPath = 'Elements';
echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout); echo $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$this->_stop(); $this->_stop();
@ -775,6 +776,10 @@ class AuthComponent extends Component {
unset($config[AuthComponent::ALL]); unset($config[AuthComponent::ALL]);
} }
foreach ($config as $class => $settings) { foreach ($config as $class => $settings) {
if (!empty($settings['className'])) {
$class = $settings['className'];
unset($settings['className']);
}
list($plugin, $class) = pluginSplit($class, true); list($plugin, $class) = pluginSplit($class, true);
$className = $class . 'Authenticate'; $className = $class . 'Authenticate';
App::uses($className, $plugin . 'Controller/Component/Auth'); App::uses($className, $plugin . 'Controller/Component/Auth');
@ -808,6 +813,7 @@ class AuthComponent extends Component {
* Check whether or not the current user has data in the session, and is considered logged in. * Check whether or not the current user has data in the session, and is considered logged in.
* *
* @return boolean true if the user is logged in, false otherwise * @return boolean true if the user is logged in, false otherwise
* @deprecated Since 2.5. Use AuthComponent::user() directly.
*/ */
public function loggedIn() { public function loggedIn() {
return (bool)$this->user(); return (bool)$this->user();

View file

@ -130,7 +130,9 @@ class CookieComponent extends Component {
* Type of encryption to use. * Type of encryption to use.
* *
* Currently two methods are available: cipher and rijndael * Currently two methods are available: cipher and rijndael
* Defaults to Security::cipher(); * Defaults to Security::cipher(). Cipher is horribly insecure and only
* the default because of backwards compatibility. In new applications you should
* always change this to 'aes' or 'rijndael'.
* *
* @var string * @var string
*/ */
@ -378,10 +380,11 @@ class CookieComponent extends Component {
public function type($type = 'cipher') { public function type($type = 'cipher') {
$availableTypes = array( $availableTypes = array(
'cipher', 'cipher',
'rijndael' 'rijndael',
'aes'
); );
if (!in_array($type, $availableTypes)) { if (!in_array($type, $availableTypes)) {
trigger_error(__d('cake_dev', 'You must use cipher or rijndael for cookie encryption type'), E_USER_WARNING); trigger_error(__d('cake_dev', 'You must use cipher, rijndael or aes for cookie encryption type'), E_USER_WARNING);
$type = 'cipher'; $type = 'cipher';
} }
$this->_type = $type; $this->_type = $type;
@ -469,13 +472,21 @@ class CookieComponent extends Component {
if (is_array($value)) { if (is_array($value)) {
$value = $this->_implode($value); $value = $this->_implode($value);
} }
if (!$this->_encrypted) {
if ($this->_encrypted === true) {
$type = $this->_type;
$value = "Q2FrZQ==." . base64_encode(Security::$type($value, $this->key, 'encrypt'));
}
return $value; return $value;
} }
$prefix = "Q2FrZQ==.";
if ($this->_type === 'rijndael') {
$cipher = Security::rijndael($value, $this->key, 'encrypt');
}
if ($this->_type === 'cipher') {
$cipher = Security::cipher($value, $this->key);
}
if ($this->_type === 'aes') {
$cipher = Security::encrypt($value, $this->key);
}
return $prefix . base64_encode($cipher);
}
/** /**
* Decrypts $value using public $type method in Security class * Decrypts $value using public $type method in Security class
@ -490,27 +501,40 @@ class CookieComponent extends Component {
foreach ((array)$values as $name => $value) { foreach ((array)$values as $name => $value) {
if (is_array($value)) { if (is_array($value)) {
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
$pos = strpos($val, 'Q2FrZQ==.'); $decrypted[$name][$key] = $this->_decode($val);
$decrypted[$name][$key] = $this->_explode($val);
if ($pos !== false) {
$val = substr($val, 8);
$decrypted[$name][$key] = $this->_explode(Security::$type(base64_decode($val), $this->key, 'decrypt'));
}
} }
} else { } else {
$pos = strpos($value, 'Q2FrZQ==.'); $decrypted[$name] = $this->_decode($value);
$decrypted[$name] = $this->_explode($value);
if ($pos !== false) {
$value = substr($value, 8);
$decrypted[$name] = $this->_explode(Security::$type(base64_decode($value), $this->key, 'decrypt'));
}
} }
} }
return $decrypted; return $decrypted;
} }
/**
* Decodes and decrypts a single value.
*
* @param string $value The value to decode & decrypt.
* @return string Decoded value.
*/
protected function _decode($value) {
$prefix = 'Q2FrZQ==.';
$pos = strpos($value, $prefix);
if ($pos === false) {
return $this->_explode($value);
}
$value = base64_decode(substr($value, strlen($prefix)));
if ($this->_type === 'rijndael') {
$plain = Security::rijndael($value, $this->key, 'decrypt');
}
if ($this->_type === 'cipher') {
$plain = Security::cipher($value, $this->key);
}
if ($this->_type === 'aes') {
$plain = Security::decrypt($value, $this->key);
}
return $this->_explode($plain);
}
/** /**
* Implode method to keep keys are multidimensional arrays * Implode method to keep keys are multidimensional arrays
* *

View file

@ -202,6 +202,8 @@ class PaginatorComponent extends Component {
$count = 0; $count = 0;
} elseif ($object->hasMethod('paginateCount')) { } elseif ($object->hasMethod('paginateCount')) {
$count = $object->paginateCount($conditions, $recursive, $extra); $count = $object->paginateCount($conditions, $recursive, $extra);
} elseif ($page === 1 && count($results) < $limit) {
$count = count($results);
} else { } else {
$parameters = compact('conditions'); $parameters = compact('conditions');
if ($recursive != $object->recursive) { if ($recursive != $object->recursive) {

View file

@ -603,13 +603,12 @@ class RequestHandlerComponent extends Component {
if (Configure::read('App.encoding') !== null) { if (Configure::read('App.encoding') !== null) {
$defaults['charset'] = Configure::read('App.encoding'); $defaults['charset'] = Configure::read('App.encoding');
} }
$options = array_merge($defaults, $options); $options += $defaults;
if ($type === 'ajax') { if ($type === 'ajax') {
$controller->layout = $this->ajaxLayout; $controller->layout = $this->ajaxLayout;
return $this->respondAs('html', $options); return $this->respondAs('html', $options);
} }
$controller->ext = '.ctp';
$pluginDot = null; $pluginDot = null;
$viewClassMap = $this->viewClassMap(); $viewClassMap = $this->viewClassMap();

View file

@ -29,6 +29,7 @@ App::uses('Scaffold', 'View');
* scaffolded actions with custom-made ones. * scaffolded actions with custom-made ones.
* *
* @package Cake.Controller * @package Cake.Controller
* @deprecated Dynamic scaffolding will be removed and replaced in 3.0
*/ */
class Scaffold { class Scaffold {

View file

@ -108,12 +108,12 @@ class CakePlugin {
* {{{ * {{{
* CakePlugin::loadAll(array( * CakePlugin::loadAll(array(
* array('bootstrap' => true), * array('bootstrap' => true),
* 'DebugKit' => array('routes' => true), * 'DebugKit' => array('routes' => true, 'bootstrap' => false),
* )) * ))
* }}} * }}}
* *
* The above example will load the bootstrap file for all plugins, but for DebugKit it will only load the routes file * The above example will load the bootstrap file for all plugins, but for DebugKit it will only load
* and will not look for any bootstrap script. * the routes file and will not look for any bootstrap script.
* *
* @param array $options * @param array $options
* @return void * @return void
@ -121,11 +121,11 @@ class CakePlugin {
public static function loadAll($options = array()) { public static function loadAll($options = array()) {
$plugins = App::objects('plugins'); $plugins = App::objects('plugins');
foreach ($plugins as $p) { foreach ($plugins as $p) {
$opts = isset($options[$p]) ? $options[$p] : null; $opts = isset($options[$p]) ? (array)$options[$p] : array();
if ($opts === null && isset($options[0])) { if (isset($options[0])) {
$opts = $options[0]; $opts += $options[0];
} }
self::load($p, (array)$opts); self::load($p, $opts);
} }
} }

View file

@ -287,7 +287,7 @@ class Configure {
* @param string $key name of configuration resource to load. * @param string $key name of configuration resource to load.
* @param string $config Name of the configured reader to use to read the resource identified by $key. * @param string $config Name of the configured reader to use to read the resource identified by $key.
* @param boolean $merge if config files should be merged instead of simply overridden * @param boolean $merge if config files should be merged instead of simply overridden
* @return mixed false if file not found, void if load successful. * @return boolean False if file not found, true if load successful.
* @throws ConfigureException Will throw any exceptions the reader raises. * @throws ConfigureException Will throw any exceptions the reader raises.
*/ */
public static function load($key, $config = 'default', $merge = true) { public static function load($key, $config = 'default', $merge = true) {
@ -424,6 +424,7 @@ class Configure {
self::$_values = array(); self::$_values = array();
return true; return true;
} }
/** /**
* Set the error and exception handlers. * Set the error and exception handlers.
* *
@ -444,4 +445,5 @@ class Configure {
set_exception_handler($exception['handler']); set_exception_handler($exception['handler']);
} }
} }
} }

View file

@ -84,7 +84,7 @@ class Object {
if ($arrayUrl && !isset($extra['data'])) { if ($arrayUrl && !isset($extra['data'])) {
$extra['data'] = array(); $extra['data'] = array();
} }
$extra = array_merge(array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1), $extra); $extra += array('autoRender' => 0, 'return' => 1, 'bare' => 1, 'requested' => 1);
$data = isset($extra['data']) ? $extra['data'] : null; $data = isset($extra['data']) ? $extra['data'] : null;
unset($extra['data']); unset($extra['data']);
@ -95,7 +95,7 @@ class Object {
$request = new CakeRequest($url); $request = new CakeRequest($url);
} elseif (is_array($url)) { } elseif (is_array($url)) {
$params = $url + array('pass' => array(), 'named' => array(), 'base' => false); $params = $url + array('pass' => array(), 'named' => array(), 'base' => false);
$params = array_merge($params, $extra); $params = $extra + $params;
$request = new CakeRequest(Router::reverse($params)); $request = new CakeRequest(Router::reverse($params));
} }
if (isset($data)) { if (isset($data)) {

View file

@ -189,10 +189,11 @@ class ExceptionRenderer {
$this->controller->response->statusCode($code); $this->controller->response->statusCode($code);
$this->controller->set(array( $this->controller->set(array(
'code' => $code, 'code' => $code,
'url' => h($url),
'name' => h($error->getMessage()), 'name' => h($error->getMessage()),
'message' => h($error->getMessage()),
'url' => h($url),
'error' => $error, 'error' => $error,
'_serialize' => array('code', 'url', 'name') '_serialize' => array('code', 'name', 'message', 'url')
)); ));
$this->controller->set($error->getAttributes()); $this->controller->set($error->getAttributes());
$this->_outputMessage($this->template); $this->_outputMessage($this->template);
@ -213,9 +214,10 @@ class ExceptionRenderer {
$this->controller->response->statusCode($error->getCode()); $this->controller->response->statusCode($error->getCode());
$this->controller->set(array( $this->controller->set(array(
'name' => h($message), 'name' => h($message),
'message' => h($message),
'url' => h($url), 'url' => h($url),
'error' => $error, 'error' => $error,
'_serialize' => array('name', 'url') '_serialize' => array('name', 'message', 'url')
)); ));
$this->_outputMessage('error400'); $this->_outputMessage('error400');
} }
@ -236,9 +238,10 @@ class ExceptionRenderer {
$this->controller->response->statusCode($code); $this->controller->response->statusCode($code);
$this->controller->set(array( $this->controller->set(array(
'name' => h($message), 'name' => h($message),
'message' => h($url), 'message' => h($message),
'url' => h($url),
'error' => $error, 'error' => $error,
'_serialize' => array('name', 'message') '_serialize' => array('name', 'message', 'url')
)); ));
$this->_outputMessage('error500'); $this->_outputMessage('error500');
} }
@ -255,10 +258,11 @@ class ExceptionRenderer {
$this->controller->response->statusCode($code); $this->controller->response->statusCode($code);
$this->controller->set(array( $this->controller->set(array(
'code' => $code, 'code' => $code,
'url' => h($url),
'name' => h($error->getMessage()), 'name' => h($error->getMessage()),
'message' => h($error->getMessage()),
'url' => h($url),
'error' => $error, 'error' => $error,
'_serialize' => array('code', 'url', 'name', 'error') '_serialize' => array('code', 'name', 'message', 'url', 'error')
)); ));
$this->_outputMessage($this->template); $this->_outputMessage($this->template);
} }

View file

@ -222,22 +222,19 @@ class CakeEventManager {
* Dispatches a new event to all configured listeners * Dispatches a new event to all configured listeners
* *
* @param string|CakeEvent $event the event key name or instance of CakeEvent * @param string|CakeEvent $event the event key name or instance of CakeEvent
* @return void * @return CakeEvent
*/ */
public function dispatch($event) { public function dispatch($event) {
if (is_string($event)) { if (is_string($event)) {
$event = new CakeEvent($event); $event = new CakeEvent($event);
} }
if (!$this->_isGlobal) { $listeners = $this->listeners($event->name());
self::instance()->dispatch($event); if (empty($listeners)) {
return $event;
} }
if (empty($this->_listeners[$event->name()])) { foreach ($listeners as $listener) {
return;
}
foreach ($this->listeners($event->name()) as $listener) {
if ($event->isStopped()) { if ($event->isStopped()) {
break; break;
} }
@ -253,6 +250,7 @@ class CakeEventManager {
$event->result = $result; $event->result = $result;
} }
} }
return $event;
} }
/** /**
@ -262,15 +260,41 @@ class CakeEventManager {
* @return array * @return array
*/ */
public function listeners($eventKey) { public function listeners($eventKey) {
if (empty($this->_listeners[$eventKey])) { $localListeners = array();
return array(); $priorities = array();
if (!$this->_isGlobal) {
$localListeners = $this->prioritisedListeners($eventKey);
$localListeners = empty($localListeners) ? array() : $localListeners;
} }
ksort($this->_listeners[$eventKey]); $globalListeners = self::instance()->prioritisedListeners($eventKey);
$globalListeners = empty($globalListeners) ? array() : $globalListeners;
$priorities = array_merge(array_keys($globalListeners), array_keys($localListeners));
$priorities = array_unique($priorities);
asort($priorities);
$result = array(); $result = array();
foreach ($this->_listeners[$eventKey] as $priorityQ) { foreach ($priorities as $priority) {
$result = array_merge($result, $priorityQ); if (isset($globalListeners[$priority])) {
$result = array_merge($result, $globalListeners[$priority]);
}
if (isset($localListeners[$priority])) {
$result = array_merge($result, $localListeners[$priority]);
}
} }
return $result; return $result;
} }
/**
* Returns the listeners for the specified event key indexed by priority
*
* @param string $eventKey
* @return array
*/
public function prioritisedListeners($eventKey) {
if (empty($this->_listeners[$eventKey])) {
return array();
}
return $this->_listeners[$eventKey];
}
} }

View file

@ -87,6 +87,68 @@ class I18n {
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES' 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
); );
/**
* Constants for the translation categories.
*
* The constants may be used in translation fetching
* instead of hardcoded integers.
* Example:
* {{{
* I18n::translate('CakePHP is awesome.', null, null, I18n::LC_MESSAGES)
* }}}
*
* To keep the code more readable, I18n constants are preferred over
* hardcoded integers.
*/
/**
* Constant for LC_ALL.
*
* @var int
*/
const LC_ALL = 0;
/**
* Constant for LC_COLLATE.
*
* @var int
*/
const LC_COLLATE = 1;
/**
* Constant for LC_CTYPE.
*
* @var int
*/
const LC_CTYPE = 2;
/**
* Constant for LC_MONETARY.
*
* @var int
*/
const LC_MONETARY = 3;
/**
* Constant for LC_NUMERIC.
*
* @var int
*/
const LC_NUMERIC = 4;
/**
* Constant for LC_TIME.
*
* @var int
*/
const LC_TIME = 5;
/**
* Constant for LC_MESSAGES.
*
* @var int
*/
const LC_MESSAGES = 6;
/** /**
* Escape string * Escape string
* *
@ -129,7 +191,7 @@ class I18n {
* @return string translated string. * @return string translated string.
* @throws CakeException When '' is provided as a domain. * @throws CakeException When '' is provided as a domain.
*/ */
public static function translate($singular, $plural = null, $domain = null, $category = 6, $count = null, $language = null) { public static function translate($singular, $plural = null, $domain = null, $category = self::LC_MESSAGES, $count = null, $language = null) {
$_this = I18n::getInstance(); $_this = I18n::getInstance();
if (strpos($singular, "\r\n") !== false) { if (strpos($singular, "\r\n") !== false) {

View file

@ -370,18 +370,6 @@ class CakeLog {
return false; return false;
} }
/**
* Configures the automatic/default stream a FileLog.
*
* @return void
*/
protected static function _autoConfig() {
self::$_Collection->load('default', array(
'engine' => 'File',
'path' => LOGS,
));
}
/** /**
* Writes the given message and type to all of the configured log adapters. * Writes the given message and type to all of the configured log adapters.
* Configured adapters are passed both the $type and $message variables. $type * Configured adapters are passed both the $type and $message variables. $type
@ -453,11 +441,7 @@ class CakeLog {
$logged = true; $logged = true;
} }
} }
if (!$logged) { return $logged;
self::_autoConfig();
self::stream('default')->write($type, $message);
}
return true;
} }
/** /**

View file

@ -67,7 +67,7 @@ class TreeBehavior extends ModelBehavior {
$config['type'] = $config[0]; $config['type'] = $config[0];
unset($config[0]); unset($config[0]);
} }
$settings = array_merge($this->_defaults, $config); $settings = $config + $this->_defaults;
if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) { if (in_array($settings['scope'], $Model->getAssociated('belongsTo'))) {
$data = $Model->getAssociated($settings['scope']); $data = $Model->getAssociated($settings['scope']);
@ -744,7 +744,7 @@ class TreeBehavior extends ModelBehavior {
* @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::reorder * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::reorder
*/ */
public function reorder(Model $Model, $options = array()) { public function reorder(Model $Model, $options = array()) {
$options = array_merge(array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true), $options); $options += array('id' => null, 'field' => $Model->displayField, 'order' => 'ASC', 'verify' => true);
extract($options); extract($options);
if ($verify && !$this->verify($Model)) { if ($verify && !$this->verify($Model)) {
return false; return false;

View file

@ -127,6 +127,13 @@ class CakeSession {
*/ */
protected static $_initialized = false; protected static $_initialized = false;
/**
* Session cookie name
*
* @var string
*/
protected static $_cookieName = null;
/** /**
* Pseudo constructor. * Pseudo constructor.
* *
@ -221,12 +228,10 @@ class CakeSession {
* @return boolean True if variable is there * @return boolean True if variable is there
*/ */
public static function check($name = null) { public static function check($name = null) {
if (!self::start()) { if (empty($name) || !self::_hasSession() || !self::start()) {
return false;
}
if (empty($name)) {
return false; return false;
} }
return Hash::get($_SESSION, $name) !== null; return Hash::get($_SESSION, $name) !== null;
} }
@ -320,7 +325,7 @@ class CakeSession {
* @return boolean Success * @return boolean Success
*/ */
public static function valid() { public static function valid() {
if (self::read('Config')) { if (self::start() && self::read('Config')) {
if (self::_validAgentAndTime() && self::$error === false) { if (self::_validAgentAndTime() && self::$error === false) {
self::$valid = true; self::$valid = true;
} else { } else {
@ -368,18 +373,19 @@ class CakeSession {
* Returns given session variable, or all of them, if no parameters given. * Returns given session variable, or all of them, if no parameters given.
* *
* @param string|array $name The name of the session variable (or a path as sent to Set.extract) * @param string|array $name The name of the session variable (or a path as sent to Set.extract)
* @return mixed The value of the session variable * @return mixed The value of the session variable, null if session not available,
* session not started, or provided name not found in the session.
*/ */
public static function read($name = null) { public static function read($name = null) {
if (!self::start()) { if (empty($name) && $name !== null) {
return false; return false;
} }
if (!self::_hasSession() || !self::start()) {
return null;
}
if ($name === null) { if ($name === null) {
return self::_returnSessionVars(); return self::_returnSessionVars();
} }
if (empty($name)) {
return false;
}
$result = Hash::get($_SESSION, $name); $result = Hash::get($_SESSION, $name);
if (isset($result)) { if (isset($result)) {
@ -409,12 +415,10 @@ class CakeSession {
* @return boolean True if the write was successful, false if the write failed * @return boolean True if the write was successful, false if the write failed
*/ */
public static function write($name, $value = null) { public static function write($name, $value = null) {
if (!self::start()) { if (empty($name) || !self::start()) {
return false;
}
if (empty($name)) {
return false; return false;
} }
$write = $name; $write = $name;
if (!is_array($name)) { if (!is_array($name)) {
$write = array($name => $value); $write = array($name => $value);
@ -442,6 +446,7 @@ class CakeSession {
$_SESSION = null; $_SESSION = null;
self::$id = null; self::$id = null;
self::$_cookieName = null;
} }
/** /**
@ -481,9 +486,12 @@ class CakeSession {
if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) { if (!isset($sessionConfig['ini']['session.cookie_lifetime'])) {
$sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60; $sessionConfig['ini']['session.cookie_lifetime'] = $sessionConfig['cookieTimeout'] * 60;
} }
if (!isset($sessionConfig['ini']['session.name'])) { if (!isset($sessionConfig['ini']['session.name'])) {
$sessionConfig['ini']['session.name'] = $sessionConfig['cookie']; $sessionConfig['ini']['session.name'] = $sessionConfig['cookie'];
} }
self::$_cookieName = $sessionConfig['ini']['session.name'];
if (!empty($sessionConfig['handler'])) { if (!empty($sessionConfig['handler'])) {
$sessionConfig['ini']['session.save_handler'] = 'user'; $sessionConfig['ini']['session.save_handler'] = 'user';
} }
@ -521,6 +529,30 @@ class CakeSession {
self::$sessionTime = self::$time + ($sessionConfig['timeout'] * 60); self::$sessionTime = self::$time + ($sessionConfig['timeout'] * 60);
} }
/**
* Get session cookie name.
*
* @return string
*/
protected static function _cookieName() {
if (self::$_cookieName !== null) {
return self::$_cookieName;
}
self::init();
self::_configureSession();
return self::$_cookieName = session_name();
}
/**
* Returns whether a session exists
* @return boolean
*/
protected static function _hasSession() {
return self::started() || isset($_COOKIE[self::_cookieName()]);
}
/** /**
* Find the handler class and make sure it implements the correct interface. * Find the handler class and make sure it implements the correct interface.
* *

View file

@ -183,12 +183,12 @@ class DataSource extends Object {
* *
* To-be-overridden in subclasses. * To-be-overridden in subclasses.
* *
* @param Model $model The Model to be created. * @param Model $Model The Model to be created.
* @param array $fields An Array of fields to be saved. * @param array $fields An Array of fields to be saved.
* @param array $values An Array of values to save. * @param array $values An Array of values to save.
* @return boolean success * @return boolean success
*/ */
public function create(Model $model, $fields = null, $values = null) { public function create(Model $Model, $fields = null, $values = null) {
return false; return false;
} }
@ -197,12 +197,12 @@ class DataSource extends Object {
* *
* To-be-overridden in subclasses. * To-be-overridden in subclasses.
* *
* @param Model $model The model being read. * @param Model $Model The model being read.
* @param array $queryData An array of query data used to find the data you want * @param array $queryData An array of query data used to find the data you want
* @param integer $recursive Number of levels of association * @param integer $recursive Number of levels of association
* @return mixed * @return mixed
*/ */
public function read(Model $model, $queryData = array(), $recursive = null) { public function read(Model $Model, $queryData = array(), $recursive = null) {
return false; return false;
} }
@ -211,13 +211,13 @@ class DataSource extends Object {
* *
* To-be-overridden in subclasses. * To-be-overridden in subclasses.
* *
* @param Model $model Instance of the model class being updated * @param Model $Model Instance of the model class being updated
* @param array $fields Array of fields to be updated * @param array $fields Array of fields to be updated
* @param array $values Array of values to be update $fields to. * @param array $values Array of values to be update $fields to.
* @param mixed $conditions * @param mixed $conditions
* @return boolean Success * @return boolean Success
*/ */
public function update(Model $model, $fields = null, $values = null, $conditions = null) { public function update(Model $Model, $fields = null, $values = null, $conditions = null) {
return false; return false;
} }
@ -226,11 +226,11 @@ class DataSource extends Object {
* *
* To-be-overridden in subclasses. * To-be-overridden in subclasses.
* *
* @param Model $model The model class having record(s) deleted * @param Model $Model The model class having record(s) deleted
* @param mixed $conditions The conditions to use for deleting. * @param mixed $conditions The conditions to use for deleting.
* @return boolean Success * @return boolean Success
*/ */
public function delete(Model $model, $conditions = null) { public function delete(Model $Model, $conditions = null) {
return false; return false;
} }
@ -318,95 +318,90 @@ class DataSource extends Object {
* *
* @param string $query Query string needing replacements done. * @param string $query Query string needing replacements done.
* @param array $data Array of data with values that will be inserted in placeholders. * @param array $data Array of data with values that will be inserted in placeholders.
* @param string $association Name of association model being replaced * @param string $association Name of association model being replaced.
* @param array $assocData * @param Model $Model Model instance.
* @param Model $model Instance of the model to replace $__cakeID__$
* @param Model $linkModel Instance of model to replace $__cakeForeignKey__$
* @param array $stack * @param array $stack
* @return string String of query data with placeholders replaced. * @return mixed String of query data with placeholders replaced, or false on failure.
*/ */
public function insertQueryData($query, $data, $association, $assocData, Model $model, Model $linkModel, $stack) { public function insertQueryData($query, $data, $association, Model $Model, $stack) {
$keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}'); $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
foreach ($keys as $key) { $modelAlias = $Model->alias;
$val = null;
$type = null;
if (strpos($query, $key) !== false) { foreach ($keys as $key) {
if (strpos($query, $key) === false) {
continue;
}
$insertKey = $InsertModel = null;
switch ($key) { switch ($key) {
case '{$__cakeID__$}': case '{$__cakeID__$}':
if (isset($data[$model->alias]) || isset($data[$association])) { $InsertModel = $Model;
if (isset($data[$model->alias][$model->primaryKey])) { $insertKey = $Model->primaryKey;
$val = $data[$model->alias][$model->primaryKey];
} elseif (isset($data[$association][$model->primaryKey])) {
$val = $data[$association][$model->primaryKey];
}
} else {
$found = false;
foreach (array_reverse($stack) as $assoc) {
if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
$val = $data[$assoc][$model->primaryKey];
$found = true;
break;
}
}
if (!$found) {
$val = '';
}
}
$type = $model->getColumnType($model->primaryKey);
break; break;
case '{$__cakeForeignKey__$}': case '{$__cakeForeignKey__$}':
foreach ($model->associations() as $name) { foreach ($Model->associations() as $type) {
foreach ($model->$name as $assocName => $assoc) { foreach ($Model->{$type} as $assoc => $assocData) {
if ($assocName === $association) { if ($assoc !== $association) {
if (isset($assoc['foreignKey'])) { continue;
$foreignKey = $assoc['foreignKey']; }
$assocModel = $model->$assocName;
$type = $assocModel->getColumnType($assocModel->primaryKey);
if (isset($data[$model->alias][$foreignKey])) { if (isset($assocData['foreignKey'])) {
$val = $data[$model->alias][$foreignKey]; $InsertModel = $Model->{$assoc};
} elseif (isset($data[$association][$foreignKey])) { $insertKey = $assocData['foreignKey'];
$val = $data[$association][$foreignKey];
} else {
$found = false;
foreach (array_reverse($stack) as $assoc) {
if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
$val = $data[$assoc][$foreignKey];
$found = true;
break;
}
}
if (!$found) {
$val = '';
}
}
} }
break 3; break 3;
} }
} }
}
break; break;
} }
$val = $dataType = null;
if (!empty($insertKey) && !empty($InsertModel)) {
if (isset($data[$modelAlias][$insertKey])) {
$val = $data[$modelAlias][$insertKey];
} elseif (isset($data[$association][$insertKey])) {
$val = $data[$association][$insertKey];
} else {
$found = false;
foreach (array_reverse($stack) as $assocData) {
if (isset($data[$assocData]) && isset($data[$assocData][$insertKey])) {
$val = $data[$assocData][$insertKey];
$found = true;
break;
}
}
if (!$found) {
$val = '';
}
}
$dataType = $InsertModel->getColumnType($InsertModel->primaryKey);
}
if (empty($val) && $val !== '0') { if (empty($val) && $val !== '0') {
return false; return false;
} }
$query = str_replace($key, $this->value($val, $type), $query);
} $query = str_replace($key, $this->value($val, $dataType), $query);
} }
return $query; return $query;
} }
/** /**
* To-be-overridden in subclasses. * To-be-overridden in subclasses.
* *
* @param Model $model Model instance * @param Model $Model Model instance
* @param string $key Key name to make * @param string $key Key name to make
* @return string Key name for model. * @return string Key name for model.
*/ */
public function resolveKey(Model $model, $key) { public function resolveKey(Model $Model, $key) {
return $model->alias . $key; return $Model->alias . $key;
} }
/** /**

View file

@ -45,7 +45,8 @@ class Mysql extends DboSource {
'login' => 'root', 'login' => 'root',
'password' => '', 'password' => '',
'database' => 'cake', 'database' => 'cake',
'port' => '3306' 'port' => '3306',
'flags' => array()
); );
/** /**
@ -84,7 +85,13 @@ class Mysql extends DboSource {
public $fieldParameters = array( public $fieldParameters = array(
'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'), 'charset' => array('value' => 'CHARACTER SET', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault'),
'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'), 'collate' => array('value' => 'COLLATE', 'quote' => false, 'join' => ' ', 'column' => 'Collation', 'position' => 'beforeDefault'),
'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault') 'comment' => array('value' => 'COMMENT', 'quote' => true, 'join' => ' ', 'column' => 'Comment', 'position' => 'afterDefault'),
'unsigned' => array(
'value' => 'UNSIGNED', 'quote' => false, 'join' => ' ', 'column' => false, 'position' => 'beforeDefault',
'noVal' => true,
'options' => array(true),
'types' => array('integer', 'float', 'decimal', 'biginteger')
)
); );
/** /**
@ -110,6 +117,7 @@ class Mysql extends DboSource {
'biginteger' => array('name' => 'bigint', 'limit' => '20'), 'biginteger' => array('name' => 'bigint', 'limit' => '20'),
'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'), 'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
'float' => array('name' => 'float', 'formatter' => 'floatval'), 'float' => array('name' => 'float', 'formatter' => 'floatval'),
'decimal' => array('name' => 'decimal', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
@ -144,7 +152,7 @@ class Mysql extends DboSource {
$config = $this->config; $config = $this->config;
$this->connected = false; $this->connected = false;
$flags = array( $flags = $config['flags'] + array(
PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_PERSISTENT => $config['persistent'],
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
@ -338,8 +346,11 @@ class Mysql extends DboSource {
'type' => $this->column($column->Type), 'type' => $this->column($column->Type),
'null' => ($column->Null === 'YES' ? true : false), 'null' => ($column->Null === 'YES' ? true : false),
'default' => $column->Default, 'default' => $column->Default,
'length' => $this->length($column->Type), 'length' => $this->length($column->Type)
); );
if (in_array($fields[$column->Field]['type'], $this->fieldParameters['unsigned']['types'], true)) {
$fields[$column->Field]['unsigned'] = $this->_unsigned($column->Type);
}
if (!empty($column->Key) && isset($this->index[$column->Key])) { if (!empty($column->Key) && isset($this->index[$column->Key])) {
$fields[$column->Field]['key'] = $this->index[$column->Key]; $fields[$column->Field]['key'] = $this->index[$column->Key];
} }
@ -761,9 +772,12 @@ class Mysql extends DboSource {
if (strpos($col, 'blob') !== false || $col === 'binary') { if (strpos($col, 'blob') !== false || $col === 'binary') {
return 'binary'; return 'binary';
} }
if (strpos($col, 'float') !== false || strpos($col, 'double') !== false || strpos($col, 'decimal') !== false) { if (strpos($col, 'float') !== false || strpos($col, 'double') !== false) {
return 'float'; return 'float';
} }
if (strpos($col, 'decimal') !== false || strpos($col, 'numeric') !== false) {
return 'decimal';
}
if (strpos($col, 'enum') !== false) { if (strpos($col, 'enum') !== false) {
return "enum($vals)"; return "enum($vals)";
} }
@ -788,4 +802,14 @@ class Mysql extends DboSource {
return $this->useNestedTransactions && version_compare($this->getVersion(), '4.1', '>='); return $this->useNestedTransactions && version_compare($this->getVersion(), '4.1', '>=');
} }
/**
* Check if column type is unsigned
*
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return bool True if column is unsigned, false otherwise
*/
protected function _unsigned($real) {
return strpos(strtolower($real), 'unsigned') !== false;
}
} }

View file

@ -45,7 +45,8 @@ class Postgres extends DboSource {
'database' => 'cake', 'database' => 'cake',
'schema' => 'public', 'schema' => 'public',
'port' => 5432, 'port' => 5432,
'encoding' => '' 'encoding' => '',
'flags' => array()
); );
/** /**
@ -60,6 +61,7 @@ class Postgres extends DboSource {
'integer' => array('name' => 'integer', 'formatter' => 'intval'), 'integer' => array('name' => 'integer', 'formatter' => 'intval'),
'biginteger' => array('name' => 'bigint', 'limit' => '20'), 'biginteger' => array('name' => 'bigint', 'limit' => '20'),
'float' => array('name' => 'float', 'formatter' => 'floatval'), 'float' => array('name' => 'float', 'formatter' => 'floatval'),
'decimal' => array('name' => 'decimal', 'formatter' => 'floatval'),
'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'datetime' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
@ -109,7 +111,7 @@ class Postgres extends DboSource {
$config = $this->config; $config = $this->config;
$this->connected = false; $this->connected = false;
$flags = array( $flags = $config['flags'] + array(
PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_PERSISTENT => $config['persistent'],
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
); );
@ -675,7 +677,7 @@ class Postgres extends DboSource {
} }
$floats = array( $floats = array(
'float', 'float4', 'float8', 'double', 'double precision', 'decimal', 'real', 'numeric' 'float', 'float4', 'float8', 'double', 'double precision', 'real'
); );
switch (true) { switch (true) {
@ -695,6 +697,8 @@ class Postgres extends DboSource {
return 'text'; return 'text';
case (strpos($col, 'bytea') !== false): case (strpos($col, 'bytea') !== false):
return 'binary'; return 'binary';
case ($col === 'decimal' || $col === 'numeric'):
return 'decimal';
case (in_array($col, $floats)): case (in_array($col, $floats)):
return 'float'; return 'float';
default: default:

View file

@ -56,7 +56,8 @@ class Sqlite extends DboSource {
*/ */
protected $_baseConfig = array( protected $_baseConfig = array(
'persistent' => false, 'persistent' => false,
'database' => null 'database' => null,
'flags' => array()
); );
/** /**
@ -71,6 +72,7 @@ class Sqlite extends DboSource {
'integer' => array('name' => 'integer', 'limit' => null, 'formatter' => 'intval'), 'integer' => array('name' => 'integer', 'limit' => null, 'formatter' => 'intval'),
'biginteger' => array('name' => 'bigint', 'limit' => 20), 'biginteger' => array('name' => 'bigint', 'limit' => 20),
'float' => array('name' => 'float', 'formatter' => 'floatval'), 'float' => array('name' => 'float', 'formatter' => 'floatval'),
'decimal' => array('name' => 'decimal', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'), 'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
@ -105,7 +107,7 @@ class Sqlite extends DboSource {
*/ */
public function connect() { public function connect() {
$config = $this->config; $config = $this->config;
$flags = array( $flags = $config['flags'] + array(
PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_PERSISTENT => $config['persistent'],
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
); );
@ -277,7 +279,7 @@ class Sqlite extends DboSource {
return 'binary'; return 'binary';
} }
if (strpos($col, 'numeric') !== false || strpos($col, 'decimal') !== false) { if (strpos($col, 'numeric') !== false || strpos($col, 'decimal') !== false) {
return 'float'; return 'decimal';
} }
return 'text'; return 'text';
} }
@ -393,7 +395,7 @@ class Sqlite extends DboSource {
*/ */
public function buildColumn($column) { public function buildColumn($column) {
$name = $type = null; $name = $type = null;
$column = array_merge(array('null' => true), $column); $column += array('null' => true);
extract($column); extract($column);
if (empty($name) || empty($type)) { if (empty($name) || empty($type)) {

View file

@ -78,6 +78,7 @@ class Sqlserver extends DboSource {
'password' => '', 'password' => '',
'database' => 'cake', 'database' => 'cake',
'schema' => '', 'schema' => '',
'flags' => array()
); );
/** /**
@ -91,7 +92,10 @@ class Sqlserver extends DboSource {
'text' => array('name' => 'nvarchar', 'limit' => 'MAX'), 'text' => array('name' => 'nvarchar', 'limit' => 'MAX'),
'integer' => array('name' => 'int', 'formatter' => 'intval'), 'integer' => array('name' => 'int', 'formatter' => 'intval'),
'biginteger' => array('name' => 'bigint'), 'biginteger' => array('name' => 'bigint'),
'float' => array('name' => 'numeric', 'formatter' => 'floatval'), 'numeric' => array('name' => 'decimal', 'formatter' => 'floatval'),
'decimal' => array('name' => 'decimal', 'formatter' => 'floatval'),
'float' => array('name' => 'float', 'formatter' => 'floatval'),
'real' => array('name' => 'float', 'formatter' => 'floatval'),
'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'), 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'), 'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
@ -118,7 +122,7 @@ class Sqlserver extends DboSource {
$config = $this->config; $config = $this->config;
$this->connected = false; $this->connected = false;
$flags = array( $flags = $config['flags'] + array(
PDO::ATTR_PERSISTENT => $config['persistent'], PDO::ATTR_PERSISTENT => $config['persistent'],
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
); );
@ -204,7 +208,7 @@ class Sqlserver extends DboSource {
} }
$fields = array(); $fields = array();
$schema = $model->schemaName; $schema = is_object($model) ? $model->schemaName : false;
$cols = $this->_execute( $cols = $this->_execute(
"SELECT "SELECT
@ -446,9 +450,12 @@ class Sqlserver extends DboSource {
if (strpos($col, 'binary') !== false || $col === 'image') { if (strpos($col, 'binary') !== false || $col === 'image') {
return 'binary'; return 'binary';
} }
if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) { if (in_array($col, array('float', 'real'))) {
return 'float'; return 'float';
} }
if (in_array($col, array('decimal', 'numeric'))) {
return 'decimal';
}
return 'text'; return 'text';
} }

File diff suppressed because it is too large Load diff

View file

@ -952,9 +952,11 @@ class Model extends Object implements CakeEventListener {
* Example: Turn off the associated Model Support request, * Example: Turn off the associated Model Support request,
* to temporarily lighten the User model: * to temporarily lighten the User model:
* *
* `$this->User->unbindModel(array('hasMany' => array('Supportrequest')));` * `$this->User->unbindModel(array('hasMany' => array('SupportRequest')));`
* Or alternatively:
* `$this->User->unbindModel(array('hasMany' => 'SupportRequest'));`
* *
* unbound models that are not made permanent will reset with the next call to Model::find() * Unbound models that are not made permanent will reset with the next call to Model::find()
* *
* @param array $params Set of bindings to unbind (indexed by binding type) * @param array $params Set of bindings to unbind (indexed by binding type)
* @param boolean $reset Set to false to make the unbinding permanent * @param boolean $reset Set to false to make the unbinding permanent
@ -966,7 +968,7 @@ class Model extends Object implements CakeEventListener {
if ($reset === true && !isset($this->__backAssociation[$assoc])) { if ($reset === true && !isset($this->__backAssociation[$assoc])) {
$this->__backAssociation[$assoc] = $this->{$assoc}; $this->__backAssociation[$assoc] = $this->{$assoc};
} }
$models = Hash::normalize((array)$models, false);
foreach ($models as $model) { foreach ($models as $model) {
if ($reset === false && isset($this->__backAssociation[$assoc][$model])) { if ($reset === false && isset($this->__backAssociation[$assoc][$model])) {
unset($this->__backAssociation[$assoc][$model]); unset($this->__backAssociation[$assoc][$model]);
@ -1661,7 +1663,7 @@ class Model extends Object implements CakeEventListener {
$options = array('validate' => $validate, 'fieldList' => array($name)); $options = array('validate' => $validate, 'fieldList' => array($name));
if (is_array($validate)) { if (is_array($validate)) {
$options = array_merge(array('validate' => false, 'fieldList' => array($name)), $validate); $options = $validate + array('validate' => false, 'fieldList' => array($name));
} }
return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options); return $this->save(array($this->alias => array($this->primaryKey => $id, $name => $value)), $options);
@ -1695,9 +1697,9 @@ class Model extends Object implements CakeEventListener {
$fields = array(); $fields = array();
if (!is_array($validate)) { if (!is_array($validate)) {
$options = array_merge($defaults, compact('validate', 'fieldList')); $options = compact('validate', 'fieldList') + $defaults;
} else { } else {
$options = array_merge($defaults, $validate); $options = $validate + $defaults;
} }
if (!empty($options['fieldList'])) { if (!empty($options['fieldList'])) {
@ -2162,7 +2164,7 @@ class Model extends Object implements CakeEventListener {
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array
*/ */
public function saveAll($data = array(), $options = array()) { public function saveAll($data = array(), $options = array()) {
$options = array_merge(array('validate' => 'first'), $options); $options += array('validate' => 'first');
if (Hash::numeric(array_keys($data))) { if (Hash::numeric(array_keys($data))) {
if ($options['validate'] === 'only') { if ($options['validate'] === 'only') {
return $this->validateMany($data, $options); return $this->validateMany($data, $options);
@ -2204,7 +2206,7 @@ class Model extends Object implements CakeEventListener {
$data = $this->data; $data = $this->data;
} }
$options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options); $options += array('validate' => 'first', 'atomic' => true, 'deep' => false);
$this->validationErrors = $validationErrors = array(); $this->validationErrors = $validationErrors = array();
if (empty($data) && $options['validate'] !== false) { if (empty($data) && $options['validate'] !== false) {
@ -2326,7 +2328,7 @@ class Model extends Object implements CakeEventListener {
$data = $this->data; $data = $this->data;
} }
$options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options); $options += array('validate' => 'first', 'atomic' => true, 'deep' => false);
$this->validationErrors = $validationErrors = array(); $this->validationErrors = $validationErrors = array();
if (empty($data) && $options['validate'] !== false) { if (empty($data) && $options['validate'] !== false) {
@ -2367,9 +2369,9 @@ class Model extends Object implements CakeEventListener {
$saved = false; $saved = false;
if ($validates) { if ($validates) {
if ($options['deep']) { if ($options['deep']) {
$saved = $Model->saveAssociated($values, array_merge($options, array('atomic' => false))); $saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
} else { } else {
$saved = $Model->save($values, array_merge($options, array('atomic' => false))); $saved = $Model->save($values, array('atomic' => false) + $options);
} }
$validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true))); $validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true)));
} }
@ -2423,7 +2425,7 @@ class Model extends Object implements CakeEventListener {
if ($validates) { if ($validates) {
$options = $Model->_addToWhiteList($key, $options); $options = $Model->_addToWhiteList($key, $options);
if ($options['deep']) { if ($options['deep']) {
$saved = $Model->saveAssociated($values, array_merge($options, array('atomic' => false))); $saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
} else { } else {
$saved = $Model->save($values, $options); $saved = $Model->save($values, $options);
} }
@ -2446,7 +2448,7 @@ class Model extends Object implements CakeEventListener {
} }
$options = $Model->_addToWhiteList($key, $options); $options = $Model->_addToWhiteList($key, $options);
$_return = $Model->saveMany($values, array_merge($options, array('atomic' => false))); $_return = $Model->saveMany($values, array('atomic' => false) + $options);
if (in_array(false, $_return, true)) { if (in_array(false, $_return, true)) {
$validationErrors[$association] = $Model->validationErrors; $validationErrors[$association] = $Model->validationErrors;
$validates = false; $validates = false;

View file

@ -127,7 +127,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable {
*/ */
public function validateAssociated(&$data, $options = array()) { public function validateAssociated(&$data, $options = array()) {
$model = $this->getModel(); $model = $this->getModel();
$options = array_merge(array('atomic' => true, 'deep' => false), $options); $options += array('atomic' => true, 'deep' => false);
$model->validationErrors = $validationErrors = $return = array(); $model->validationErrors = $validationErrors = $return = array();
$model->create(null); $model->create(null);
$return[$model->alias] = true; $return[$model->alias] = true;
@ -204,7 +204,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable {
*/ */
public function validateMany(&$data, $options = array()) { public function validateMany(&$data, $options = array()) {
$model = $this->getModel(); $model = $this->getModel();
$options = array_merge(array('atomic' => true, 'deep' => false), $options); $options += array('atomic' => true, 'deep' => false);
$model->validationErrors = $validationErrors = $return = array(); $model->validationErrors = $validationErrors = $return = array();
foreach ($data as $key => &$record) { foreach ($data as $key => &$record) {
if ($options['deep']) { if ($options['deep']) {
@ -247,7 +247,15 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable {
return $model->validationErrors; return $model->validationErrors;
} }
$fieldList = isset($options['fieldList']) ? $options['fieldList'] : array(); $fieldList = $model->whitelist;
if (empty($fieldList) && !empty($options['fieldList'])) {
if (!empty($options['fieldList'][$model->alias]) && is_array($options['fieldList'][$model->alias])) {
$fieldList = $options['fieldList'][$model->alias];
} else {
$fieldList = $options['fieldList'];
}
}
$exists = $model->exists(); $exists = $model->exists();
$methods = $this->getMethods(); $methods = $this->getMethods();
$fields = $this->_validationList($fieldList); $fields = $this->_validationList($fieldList);
@ -374,32 +382,19 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable {
} }
/** /**
* Processes the Model's whitelist or passed fieldList and returns the list of fields * Processes the passed fieldList and returns the list of fields to be validated
* to be validated
* *
* @param array $fieldList list of fields to be used for validation * @param array $fieldList list of fields to be used for validation
* @return array List of validation rules to be applied * @return array List of validation rules to be applied
*/ */
protected function _validationList($fieldList = array()) { protected function _validationList($fieldList = array()) {
$model = $this->getModel(); if (empty($fieldList) || Hash::dimensions($fieldList) > 1) {
$whitelist = $model->whitelist;
if (!empty($fieldList)) {
if (!empty($fieldList[$model->alias]) && is_array($fieldList[$model->alias])) {
$whitelist = $fieldList[$model->alias];
} else {
$whitelist = $fieldList;
}
}
unset($fieldList);
if (empty($whitelist) || Hash::dimensions($whitelist) > 1) {
return $this->_fields; return $this->_fields;
} }
$validateList = array(); $validateList = array();
$this->validationErrors = array(); $this->validationErrors = array();
foreach ((array)$whitelist as $f) { foreach ((array)$fieldList as $f) {
if (!empty($this->_fields[$f])) { if (!empty($this->_fields[$f])) {
$validateList[$f] = $this->_fields[$f]; $validateList[$f] = $this->_fields[$f];
} }

View file

@ -83,7 +83,7 @@ class Permission extends AppModel {
$acoPath = $this->Aco->node($aco); $acoPath = $this->Aco->node($aco);
if (!$aroPath) { if (!$aroPath) {
trigger_error(__d('cake_dev', $this->log(__d('cake_dev',
"%s - Failed ARO node lookup in permissions check. Node references:\nAro: %s\nAco: %s", "%s - Failed ARO node lookup in permissions check. Node references:\nAro: %s\nAco: %s",
'DbAcl::check()', 'DbAcl::check()',
print_r($aro, true), print_r($aro, true),
@ -94,7 +94,7 @@ class Permission extends AppModel {
} }
if (!$acoPath) { if (!$acoPath) {
trigger_error(__d('cake_dev', $this->log(__d('cake_dev',
"%s - Failed ACO node lookup in permissions check. Node references:\nAro: %s\nAco: %s", "%s - Failed ACO node lookup in permissions check. Node references:\nAro: %s\nAco: %s",
'DbAcl::check()', 'DbAcl::check()',
print_r($aro, true), print_r($aro, true),
@ -105,7 +105,7 @@ class Permission extends AppModel {
} }
if ($action !== '*' && !in_array('_' . $action, $permKeys)) { if ($action !== '*' && !in_array('_' . $action, $permKeys)) {
trigger_error(__d('cake_dev', "ACO permissions key %s does not exist in %s", $action, 'DbAcl::check()'), E_USER_NOTICE); $this->log(__d('cake_dev', "ACO permissions key %s does not exist in %s", $action, 'DbAcl::check()'), E_USER_NOTICE);
return false; return false;
} }
@ -176,7 +176,7 @@ class Permission extends AppModel {
$save = array(); $save = array();
if (!$perms) { if (!$perms) {
trigger_error(__d('cake_dev', '%s - Invalid node', 'DbAcl::allow()'), E_USER_WARNING); $this->log(__d('cake_dev', '%s - Invalid node', 'DbAcl::allow()'), E_USER_WARNING);
return false; return false;
} }
if (isset($perms[0])) { if (isset($perms[0])) {

View file

@ -325,7 +325,12 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable {
} }
/** /**
* Sets or replace a validation rule * Sets or replace a validation rule.
*
* This is a wrapper for ArrayAccess. Use setRule() directly for
* chainable access.
*
* @see http://www.php.net/manual/en/arrayobject.offsetset.php
* *
* @param string $index name of the rule * @param string $index name of the rule
* @param CakeValidationRule|array rule to add to $index * @param CakeValidationRule|array rule to add to $index

View file

@ -512,9 +512,14 @@ class CakeRequest implements ArrayAccess {
} }
if (isset($detect['param'])) { if (isset($detect['param'])) {
$key = $detect['param']; $key = $detect['param'];
if (isset($detect['value'])) {
$value = $detect['value']; $value = $detect['value'];
return isset($this->params[$key]) ? $this->params[$key] == $value : false; return isset($this->params[$key]) ? $this->params[$key] == $value : false;
} }
if (isset($detect['options'])) {
return isset($this->params[$key]) ? in_array($this->params[$key], $detect['options']) : false;
}
}
if (isset($detect['callback']) && is_callable($detect['callback'])) { if (isset($detect['callback']) && is_callable($detect['callback'])) {
return call_user_func($detect['callback'], $this); return call_user_func($detect['callback'], $this);
} }
@ -572,7 +577,13 @@ class CakeRequest implements ArrayAccess {
* *
* Allows for custom detectors on the request parameters. * Allows for custom detectors on the request parameters.
* *
* e.g `addDetector('post', array('param' => 'requested', 'value' => 1)` * e.g `addDetector('requested', array('param' => 'requested', 'value' => 1)`
*
* You can also make parameter detectors that accept multiple values
* using the `options` key. This is useful when you want to check
* if a request parameter is in a list of options.
*
* `addDetector('extension', array('param' => 'ext', 'options' => array('pdf', 'csv'))`
* *
* @param string $name The name of the detector. * @param string $name The name of the detector.
* @param array $options The options for the detector definition. See above. * @param array $options The options for the detector definition. See above.
@ -900,23 +911,23 @@ class CakeRequest implements ArrayAccess {
} }
/** /**
* Only allow certain HTTP request methods, if the request method does not match * Allow only certain HTTP request methods. If the request method does not match
* a 405 error will be shown and the required "Allow" response header will be set. * a 405 error will be shown and the required "Allow" response header will be set.
* *
* Example: * Example:
* *
* $this->request->onlyAllow('post', 'delete'); * $this->request->allowMethod('post', 'delete');
* or * or
* $this->request->onlyAllow(array('post', 'delete')); * $this->request->allowMethod(array('post', 'delete'));
* *
* If the request would be GET, response header "Allow: POST, DELETE" will be set * If the request would be GET, response header "Allow: POST, DELETE" will be set
* and a 405 error will be returned * and a 405 error will be returned.
* *
* @param string|array $methods Allowed HTTP request methods * @param string|array $methods Allowed HTTP request methods.
* @return boolean true * @return boolean true
* @throws MethodNotAllowedException * @throws MethodNotAllowedException
*/ */
public function onlyAllow($methods) { public function allowMethod($methods) {
if (!is_array($methods)) { if (!is_array($methods)) {
$methods = func_get_args(); $methods = func_get_args();
} }
@ -931,6 +942,22 @@ class CakeRequest implements ArrayAccess {
throw $e; throw $e;
} }
/**
* Alias of CakeRequest::allowMethod() for backwards compatibility.
*
* @see CakeRequest::allowMethod()
* @deprecated 2.5 Use CakeRequest::allowMethod() instead.
* @param string|array $methods Allowed HTTP request methods.
* @return boolean true
* @throws MethodNotAllowedException
*/
public function onlyAllow($methods) {
if (!is_array($methods)) {
$methods = func_get_args();
}
return $this->allowMethod($methods);
}
/** /**
* Read data from php://input, mocked in tests. * Read data from php://input, mocked in tests.
* *

View file

@ -379,6 +379,7 @@ class CakeResponse {
* *
* @param array $options list of parameters to setup the response. Possible values are: * @param array $options list of parameters to setup the response. Possible values are:
* - body: the response text that should be sent to the client * - body: the response text that should be sent to the client
* - statusCodes: additional allowable response codes
* - status: the HTTP status code to respond with * - status: the HTTP status code to respond with
* - type: a complete mime-type string or an extension mapped in this class * - type: a complete mime-type string or an extension mapped in this class
* - charset: the charset for the response body * - charset: the charset for the response body
@ -387,6 +388,9 @@ class CakeResponse {
if (isset($options['body'])) { if (isset($options['body'])) {
$this->body($options['body']); $this->body($options['body']);
} }
if (isset($options['statusCodes'])) {
$this->httpCodes($options['statusCodes']);
}
if (isset($options['status'])) { if (isset($options['status'])) {
$this->statusCode($options['status']); $this->statusCode($options['status']);
} }
@ -1236,6 +1240,75 @@ class CakeResponse {
$this->_cookies[$options['name']] = $options; $this->_cookies[$options['name']] = $options;
} }
/**
* Setup access for origin and methods on cross origin requests
*
* This method allow multiple ways to setup the domains, see the examples
*
* ### Full URI
* e.g `cors($request, 'http://www.cakephp.org');`
*
* ### URI with wildcard
* e.g `cors($request, 'http://*.cakephp.org');`
*
* ### Ignoring the requested protocol
* e.g `cors($request, 'www.cakephp.org');`
*
* ### Any URI
* e.g `cors($request, '*');`
*
* ### Whitelist of URIs
* e.g `cors($request, array('http://www.cakephp.org', '*.google.com', 'https://myproject.github.io'));`
*
* @param CakeRequest $request Request object
* @param string|array $allowedDomains List of allowed domains, see method description for more details
* @param string|array $allowedMethods List of HTTP verbs allowed
* @param string|array $allowedHeaders List of HTTP headers allowed
* @return void
*/
public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = array(), $allowedHeaders = array()) {
$origin = $request->header('Origin');
if (!$origin) {
return;
}
$allowedDomains = $this->_normalizeCorsDomains((array)$allowedDomains, $request->is('ssl'));
foreach ($allowedDomains as $domain) {
if (!preg_match($domain['preg'], $origin)) {
continue;
}
$this->header('Access-Control-Allow-Origin', $domain['original'] === '*' ? '*' : $origin);
$allowedMethods && $this->header('Access-Control-Allow-Methods', implode(', ', (array)$allowedMethods));
$allowedHeaders && $this->header('Access-Control-Allow-Headers', implode(', ', (array)$allowedHeaders));
break;
}
}
/**
* Normalize the origin to regular expressions and put in an array format
*
* @param array $domains
* @param boolean $requestIsSSL
* @return array
*/
protected function _normalizeCorsDomains($domains, $requestIsSSL = false) {
$result = array();
foreach ($domains as $domain) {
if ($domain === '*') {
$result[] = array('preg' => '@.@', 'original' => '*');
continue;
}
$original = $preg = $domain;
if (strpos($domain, '://') === false) {
$preg = ($requestIsSSL ? 'https://' : 'http://') . $domain;
}
$preg = '@' . str_replace('*', '.*', $domain) . '@';
$result[] = compact('original', 'preg');
}
return $result;
}
/** /**
* Setup for display or download the given file. * Setup for display or download the given file.
* *

View file

@ -14,7 +14,6 @@
* @license http://www.opensource.org/licenses/mit-license.php MIT License * @license http://www.opensource.org/licenses/mit-license.php MIT License
*/ */
App::uses('Validation', 'Utility');
App::uses('Multibyte', 'I18n'); App::uses('Multibyte', 'I18n');
App::uses('AbstractTransport', 'Network/Email'); App::uses('AbstractTransport', 'Network/Email');
App::uses('File', 'Utility'); App::uses('File', 'Utility');
@ -315,7 +314,7 @@ class CakeEmail {
/** /**
* Regex for email validation * Regex for email validation
* If null, it will use built in regex * If null, filter_var() will be used.
* *
* @var string * @var string
*/ */
@ -552,13 +551,10 @@ class CakeEmail {
* @param string|array $email * @param string|array $email
* @param string $name * @param string $name
* @return CakeEmail $this * @return CakeEmail $this
* @throws SocketException
*/ */
protected function _setEmail($varName, $email, $name) { protected function _setEmail($varName, $email, $name) {
if (!is_array($email)) { if (!is_array($email)) {
if (!Validation::email($email, false, $this->_emailPattern)) { $this->_validateEmail($email);
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
}
if ($name === null) { if ($name === null) {
$name = $email; $name = $email;
} }
@ -570,15 +566,30 @@ class CakeEmail {
if (is_int($key)) { if (is_int($key)) {
$key = $value; $key = $value;
} }
if (!Validation::email($key, false, $this->_emailPattern)) { $this->_validateEmail($key);
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
}
$list[$key] = $value; $list[$key] = $value;
} }
$this->{$varName} = $list; $this->{$varName} = $list;
return $this; return $this;
} }
/**
* Validate email address
*
* @param string $email
* @return void
* @throws SocketException If email address does not validate
*/
protected function _validateEmail($email) {
$valid = (($this->_emailPattern !== null &&
preg_match($this->_emailPattern, $email)) ||
filter_var($email, FILTER_VALIDATE_EMAIL)
);
if (!$valid) {
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
}
}
/** /**
* Set only 1 email * Set only 1 email
* *
@ -610,9 +621,7 @@ class CakeEmail {
*/ */
protected function _addEmail($varName, $email, $name) { protected function _addEmail($varName, $email, $name) {
if (!is_array($email)) { if (!is_array($email)) {
if (!Validation::email($email, false, $this->_emailPattern)) { $this->_validateEmail($email);
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
}
if ($name === null) { if ($name === null) {
$name = $email; $name = $email;
} }
@ -624,9 +633,7 @@ class CakeEmail {
if (is_int($key)) { if (is_int($key)) {
$key = $value; $key = $value;
} }
if (!Validation::email($key, false, $this->_emailPattern)) { $this->_validateEmail($key);
throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
}
$list[$key] = $value; $list[$key] = $value;
} }
$this->{$varName} = array_merge($this->{$varName}, $list); $this->{$varName} = array_merge($this->{$varName}, $list);
@ -1128,7 +1135,7 @@ class CakeEmail {
if (!is_array($this->_config['log'])) { if (!is_array($this->_config['log'])) {
$this->_config['log'] = array('level' => $this->_config['log']); $this->_config['log'] = array('level' => $this->_config['log']);
} }
$config = array_merge($config, $this->_config['log']); $config = $this->_config['log'] + $config;
} }
CakeLog::write( CakeLog::write(
$config['level'], $config['level'],
@ -1192,7 +1199,7 @@ class CakeEmail {
} }
$config = $configs->{$config}; $config = $configs->{$config};
} }
$this->_config = array_merge($this->_config, $config); $this->_config = $config + $this->_config;
if (!empty($config['charset'])) { if (!empty($config['charset'])) {
$this->charset = $config['charset']; $this->charset = $config['charset'];
} }
@ -1217,15 +1224,14 @@ class CakeEmail {
$this->setHeaders($config['headers']); $this->setHeaders($config['headers']);
unset($config['headers']); unset($config['headers']);
} }
if (array_key_exists('template', $config)) { if (array_key_exists('template', $config)) {
$layout = false; $this->_template = $config['template'];
}
if (array_key_exists('layout', $config)) { if (array_key_exists('layout', $config)) {
$layout = $config['layout']; $this->_layout = $config['layout'];
unset($config['layout']);
}
$this->template($config['template'], $layout);
unset($config['template']);
} }
$this->transportClass()->config($config); $this->transportClass()->config($config);
} }

View file

@ -46,6 +46,42 @@ class SmtpTransport extends AbstractTransport {
*/ */
protected $_content; protected $_content;
/**
* The response of the last sent SMTP command.
*
* @var array
*/
protected $_lastResponse = array();
/**
* Returns the response of the last sent SMTP command.
*
* A response consists of one or more lines containing a response
* code and an optional response message text:
* {{{
* array(
* array(
* 'code' => '250',
* 'message' => 'mail.example.com'
* ),
* array(
* 'code' => '250',
* 'message' => 'PIPELINING'
* ),
* array(
* 'code' => '250',
* 'message' => '8BITMIME'
* ),
* // etc...
* )
* }}}
*
* @return array
*/
public function getLastResponse() {
return $this->_lastResponse;
}
/** /**
* Send mail * Send mail
* *
@ -88,6 +124,25 @@ class SmtpTransport extends AbstractTransport {
return $this->_config; return $this->_config;
} }
/**
* Parses and stores the reponse lines in `'code' => 'message'` format.
*
* @param array $responseLines
* @return void
*/
protected function _bufferResponseLines(array $responseLines) {
$response = array();
foreach ($responseLines as $responseLine) {
if (preg_match('/^(\d{3})(?:[ -]+(.*))?$/', $responseLine, $match)) {
$response[] = array(
'code' => $match[1],
'message' => isset($match[2]) ? $match[2] : null
);
}
}
$this->_lastResponse = array_merge($this->_lastResponse, $response);
}
/** /**
* Connect to SMTP Server * Connect to SMTP Server
* *
@ -152,6 +207,78 @@ class SmtpTransport extends AbstractTransport {
} }
} }
/**
* Prepares the `MAIL FROM` SMTP command.
*
* @param string $email The email address to send with the command.
* @return string
*/
protected function _prepareFromCmd($email) {
return 'MAIL FROM:<' . $email . '>';
}
/**
* Prepares the `RCPT TO` SMTP command.
*
* @param string $email The email address to send with the command.
* @return string
*/
protected function _prepareRcptCmd($email) {
return 'RCPT TO:<' . $email . '>';
}
/**
* Prepares the `from` email address.
*
* @return array
*/
protected function _prepareFromAddress() {
$from = $this->_cakeEmail->returnPath();
if (empty($from)) {
$from = $this->_cakeEmail->from();
}
return $from;
}
/**
* Prepares the recipient email addresses.
*
* @return array
*/
protected function _prepareRecipientAddresses() {
$to = $this->_cakeEmail->to();
$cc = $this->_cakeEmail->cc();
$bcc = $this->_cakeEmail->bcc();
return array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
}
/**
* Prepares the message headers.
*
* @return array
*/
protected function _prepareMessageHeaders() {
return $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
}
/**
* Prepares the message body.
*
* @return string
*/
protected function _prepareMessage() {
$lines = $this->_cakeEmail->message();
$messages = array();
foreach ($lines as $line) {
if ((!empty($line)) && ($line[0] === '.')) {
$messages[] = '.' . $line;
} else {
$messages[] = $line;
}
}
return implode("\r\n", $messages);
}
/** /**
* Send emails * Send emails
* *
@ -159,18 +286,12 @@ class SmtpTransport extends AbstractTransport {
* @throws SocketException * @throws SocketException
*/ */
protected function _sendRcpt() { protected function _sendRcpt() {
$from = $this->_cakeEmail->returnPath(); $from = $this->_prepareFromAddress();
if (empty($from)) { $this->_smtpSend($this->_prepareFromCmd(key($from)));
$from = $this->_cakeEmail->from();
}
$this->_smtpSend('MAIL FROM:<' . key($from) . '>');
$to = $this->_cakeEmail->to(); $emails = $this->_prepareRecipientAddresses();
$cc = $this->_cakeEmail->cc();
$bcc = $this->_cakeEmail->bcc();
$emails = array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
foreach ($emails as $email) { foreach ($emails as $email) {
$this->_smtpSend('RCPT TO:<' . $email . '>'); $this->_smtpSend($this->_prepareRcptCmd($email));
} }
} }
@ -183,18 +304,9 @@ class SmtpTransport extends AbstractTransport {
protected function _sendData() { protected function _sendData() {
$this->_smtpSend('DATA', '354'); $this->_smtpSend('DATA', '354');
$headers = $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject')); $headers = $this->_headersToString($this->_prepareMessageHeaders());
$headers = $this->_headersToString($headers); $message = $this->_prepareMessage();
$lines = $this->_cakeEmail->message();
$messages = array();
foreach ($lines as $line) {
if ((!empty($line)) && ($line[0] === '.')) {
$messages[] = '.' . $line;
} else {
$messages[] = $line;
}
}
$message = implode("\r\n", $messages);
$this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n."); $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
$this->_content = array('headers' => $headers, 'message' => $message); $this->_content = array('headers' => $headers, 'message' => $message);
} }
@ -229,6 +341,8 @@ class SmtpTransport extends AbstractTransport {
* @throws SocketException * @throws SocketException
*/ */
protected function _smtpSend($data, $checkCode = '250') { protected function _smtpSend($data, $checkCode = '250') {
$this->_lastResponse = array();
if ($data !== null) { if ($data !== null) {
$this->_socket->write($data . "\r\n"); $this->_socket->write($data . "\r\n");
} }
@ -244,6 +358,8 @@ class SmtpTransport extends AbstractTransport {
$responseLines = explode("\r\n", rtrim($response, "\r\n")); $responseLines = explode("\r\n", rtrim($response, "\r\n"));
$response = end($responseLines); $response = end($responseLines);
$this->_bufferResponseLines($responseLines);
if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) { if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
if ($code[2] === '-') { if ($code[2] === '-') {
continue; continue;

View file

@ -893,7 +893,7 @@ class HttpSocket extends CakeSocket {
} }
$request['uri'] = $this->_parseUri($request['uri']); $request['uri'] = $this->_parseUri($request['uri']);
$request = array_merge(array('method' => 'GET'), $request); $request += array('method' => 'GET');
if (!empty($this->_proxy['host'])) { if (!empty($this->_proxy['host'])) {
$request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query'); $request['uri'] = $this->_buildUri($request['uri'], '%scheme://%host:%port/%path?%query');
} else { } else {

View file

@ -95,7 +95,12 @@ class Dispatcher implements CakeEventListener {
return; return;
} }
foreach ($filters as $filter) { foreach ($filters as $index => $filter) {
$settings = array();
if (is_array($filter) && !is_int($index)) {
$settings = $filter;
$filter = $index;
}
if (is_string($filter)) { if (is_string($filter)) {
$filter = array('callable' => $filter); $filter = array('callable' => $filter);
} }
@ -105,7 +110,7 @@ class Dispatcher implements CakeEventListener {
if (!class_exists($callable)) { if (!class_exists($callable)) {
throw new MissingDispatcherFilterException($callable); throw new MissingDispatcherFilterException($callable);
} }
$manager->attach(new $callable); $manager->attach(new $callable($settings));
} else { } else {
$on = strtolower($filter['on']); $on = strtolower($filter['on']);
$options = array(); $options = array();
@ -157,7 +162,7 @@ class Dispatcher implements CakeEventListener {
)); ));
} }
$response = $this->_invoke($controller, $request, $response); $response = $this->_invoke($controller, $request);
if (isset($request->params['return'])) { if (isset($request->params['return'])) {
return $response->body(); return $response->body();
} }
@ -169,18 +174,19 @@ class Dispatcher implements CakeEventListener {
/** /**
* Initializes the components and models a controller will be using. * Initializes the components and models a controller will be using.
* Triggers the controller action, and invokes the rendering if Controller::$autoRender is true and echo's the output. * Triggers the controller action, and invokes the rendering if Controller::$autoRender
* Otherwise the return value of the controller action are returned. * is true and echo's the output. Otherwise the return value of the controller
* action are returned.
* *
* @param Controller $controller Controller to invoke * @param Controller $controller Controller to invoke
* @param CakeRequest $request The request object to invoke the controller for. * @param CakeRequest $request The request object to invoke the controller for.
* @param CakeResponse $response The response object to receive the output
* @return CakeResponse the resulting response object * @return CakeResponse the resulting response object
*/ */
protected function _invoke(Controller $controller, CakeRequest $request, CakeResponse $response) { protected function _invoke(Controller $controller, CakeRequest $request) {
$controller->constructClasses(); $controller->constructClasses();
$controller->startupProcess(); $controller->startupProcess();
$response = $controller->response;
$render = true; $render = true;
$result = $controller->invokeAction($request); $result = $controller->invokeAction($request);
if ($result instanceof CakeResponse) { if ($result instanceof CakeResponse) {

View file

@ -34,6 +34,22 @@ abstract class DispatcherFilter implements CakeEventListener {
*/ */
public $priority = 10; public $priority = 10;
/**
* Settings for this filter
*
* @var array
*/
public $settings = array();
/**
* Constructor.
*
* @param string $setting Configuration settings for the filter.
*/
public function __construct($settings = array()) {
$this->settings = Hash::merge($this->settings, $settings);
}
/** /**
* Returns the list of events this filter listens to. * Returns the list of events this filter listens to.
* Dispatcher notifies 2 different events `Dispatcher.before` and `Dispatcher.after`. * Dispatcher notifies 2 different events `Dispatcher.before` and `Dispatcher.after`.

View file

@ -1,7 +1,5 @@
<?php <?php
/** /**
*
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* *
@ -38,7 +36,8 @@ class AssetDispatcher extends DispatcherFilter {
* Checks if a requested asset exists and sends it to the browser * Checks if a requested asset exists and sends it to the browser
* *
* @param CakeEvent $event containing the request and response object * @param CakeEvent $event containing the request and response object
* @return CakeResponse if the client is requesting a recognized asset, null otherwise * @return mixed The resulting response.
* @throws NotFoundException When asset not found
*/ */
public function beforeDispatch(CakeEvent $event) { public function beforeDispatch(CakeEvent $event) {
$url = urldecode($event->data['request']->url); $url = urldecode($event->data['request']->url);
@ -55,7 +54,6 @@ class AssetDispatcher extends DispatcherFilter {
if ($assetFile === null || !file_exists($assetFile)) { if ($assetFile === null || !file_exists($assetFile)) {
return null; return null;
} }
$response = $event->data['response']; $response = $event->data['response'];
$event->stopPropagation(); $event->stopPropagation();
@ -66,6 +64,7 @@ class AssetDispatcher extends DispatcherFilter {
$pathSegments = explode('.', $url); $pathSegments = explode('.', $url);
$ext = array_pop($pathSegments); $ext = array_pop($pathSegments);
$this->_deliverAsset($response, $assetFile, $ext); $this->_deliverAsset($response, $assetFile, $ext);
return $response; return $response;
} }

View file

@ -480,10 +480,10 @@ class Router {
} }
if ($named === true || $named === false) { if ($named === true || $named === false) {
$options = array_merge(array('default' => $named, 'reset' => true, 'greedy' => $named), $options); $options += array('default' => $named, 'reset' => true, 'greedy' => $named);
$named = array(); $named = array();
} else { } else {
$options = array_merge(array('default' => false, 'reset' => false, 'greedy' => true), $options); $options += array('default' => false, 'reset' => false, 'greedy' => true);
} }
if ($options['reset'] || self::$_namedConfig['rules'] === false) { if ($options['reset'] || self::$_namedConfig['rules'] === false) {
@ -532,12 +532,15 @@ class Router {
*/ */
public static function mapResources($controller, $options = array()) { public static function mapResources($controller, $options = array()) {
$hasPrefix = isset($options['prefix']); $hasPrefix = isset($options['prefix']);
$options = array_merge(array( $options += array(
'connectOptions' => array(),
'prefix' => '/', 'prefix' => '/',
'id' => self::ID . '|' . self::UUID 'id' => self::ID . '|' . self::UUID
), $options); );
$prefix = $options['prefix']; $prefix = $options['prefix'];
$connectOptions = $options['connectOptions'];
unset($options['connectOptions']);
if (strpos($prefix, '/') !== 0) { if (strpos($prefix, '/') !== 0) {
$prefix = '/' . $prefix; $prefix = '/' . $prefix;
} }
@ -563,7 +566,10 @@ class Router {
'action' => $params['action'], 'action' => $params['action'],
'[method]' => $params['method'] '[method]' => $params['method']
), ),
array('id' => $options['id'], 'pass' => array('id')) array_merge(
array('id' => $options['id'], 'pass' => array('id')),
$connectOptions
)
); );
} }
self::$_resourceMapped[] = $urlName; self::$_resourceMapped[] = $urlName;
@ -1074,7 +1080,7 @@ class Router {
} }
$addition = http_build_query($q, null, $join); $addition = http_build_query($q, null, $join);
if ($out && $addition && substr($out, strlen($join) * -1, strlen($join)) != $join) { if ($out && $addition && substr($out, strlen($join) * -1, strlen($join)) !== $join) {
$out .= $join; $out .= $join;
} }

View file

@ -397,6 +397,181 @@ class BasicsTest extends CakeTestCase {
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }
/**
* testTranslatePercent
*
* @return void
*/
public function testTranslatePercent() {
$result = __('%s are 100% real fruit', 'Apples');
$expected = 'Apples are 100% real fruit';
$this->assertEquals($expected, $result, 'Percent sign at end of word should be considered literal');
$result = __('%s are %d% real fruit', 'Apples', 100);
$expected = 'Apples are 100% real fruit';
$this->assertEquals($expected, $result, 'A digit marker should not be misinterpreted');
$result = __('%s are %s% real fruit', 'Apples', 100);
$expected = 'Apples are 100% real fruit';
$this->assertEquals($expected, $result, 'A string marker should not be misinterpreted');
$result = __('%nonsense %s', 'Apples');
$expected = '%nonsense Apples';
$this->assertEquals($expected, $result, 'A percent sign at the start of the string should be considered literal');
$result = __('%s are awesome%', 'Apples');
$expected = 'Apples are awesome%';
$this->assertEquals($expected, $result, 'A percent sign at the end of the string should be considered literal');
$result = __('%2$d %1$s entered the bowl', 'Apples', 2);
$expected = '2 Apples entered the bowl';
$this->assertEquals($expected, $result, 'Positional replacement markers should not be misinterpreted');
$result = __('%.2f% of all %s agree', 99.44444, 'Cats');
$expected = '99.44% of all Cats agree';
$this->assertEquals($expected, $result, 'significant-digit placeholder should not be misinterpreted');
}
/**
* testTranslateWithFormatSpecifiers
*
* @return void
*/
public function testTranslateWithFormatSpecifiers() {
$expected = 'Check, one, two, three';
$result = __('Check, %+10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, +1, two, three';
$result = __('Check, %+5d, two, three', 1);
$this->assertEquals($expected, $result);
$expected = 'Check, @@one, two, three';
$result = __('Check, %\'@+10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, one, two , three';
$result = __('Check, %-10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, one, two##, three';
$result = __('Check, %\'#-10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, one, two, three';
$result = __d('default', 'Check, %+10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, @@one, two, three';
$result = __d('default', 'Check, %\'@+10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, one, two , three';
$result = __d('default', 'Check, %-10s, three', 'one, two');
$this->assertEquals($expected, $result);
$expected = 'Check, one, two##, three';
$result = __d('default', 'Check, %\'#-10s, three', 'one, two');
$this->assertEquals($expected, $result);
}
/**
* testTranslateDomainPluralWithFormatSpecifiers
*
* @return void
*/
public function testTranslateDomainPluralWithFormatSpecifiers() {
$result = __dn('core', '%+5d item.', '%+5d items.', 1, 1);
$expected = ' +1 item.';
$this->assertEquals($expected, $result);
$result = __dn('core', '%-5d item.', '%-5d items.', 10, 10);
$expected = '10 items.';
$this->assertEquals($expected, $result);
$result = __dn('core', '%\'#+5d item.', '%\'*+5d items.', 1, 1);
$expected = '###+1 item.';
$this->assertEquals($expected, $result);
$result = __dn('core', '%\'#+5d item.', '%\'*+5d items.', 90, 90);
$expected = '**+90 items.';
$this->assertEquals($expected, $result);
$result = __dn('core', '%\'#+5d item.', '%\'*+5d items.', 9000, 9000);
$expected = '+9000 items.';
$this->assertEquals($expected, $result);
}
/**
* test testTranslatePluralWithFormatSpecifiers
*
* @return void
*/
public function testTranslatePluralWithFormatSpecifiers() {
Configure::write('Config.language', 'rule_1_po');
$result = __n('%-5d = 1', '%-5d = 0 or > 1', 10);
$expected = '%-5d = 0 or > 1 (translated)';
$this->assertEquals($expected, $result);
}
/**
* test testTranslateDomainCategoryWithFormatSpecifiers
*
* @return void
*/
public function testTranslateDomainCategoryWithFormatSpecifiers() {
Configure::write('Config.language', 'rule_1_po');
$result = __dc('default', '%+10s world', 6, 'hello');
$expected = ' hello world';
$this->assertEquals($expected, $result);
$result = __dc('default', '%-10s world', 6, 'hello');
$expected = 'hello world';
$this->assertEquals($expected, $result);
$result = __dc('default', '%\'@-10s world', 6, 'hello');
$expected = 'hello@@@@@ world';
$this->assertEquals($expected, $result);
}
/**
* test testTranslateDomainCategoryPluralWithFormatSpecifiers
*
* @return void
*/
public function testTranslateDomainCategoryPluralWithFormatSpecifiers() {
Configure::write('Config.language', 'rule_1_po');
$result = __dcn('default', '%-5d = 1', '%-5d = 0 or > 1', 0, 6);
$expected = '%-5d = 0 or > 1 (translated)';
$this->assertEquals($expected, $result);
$result = __dcn('default', '%-5d = 1', '%-5d = 0 or > 1', 1, 6);
$expected = '%-5d = 1 (translated)';
$this->assertEquals($expected, $result);
}
/**
* test testTranslateCategoryWithFormatSpecifiers
*
* @return void
*/
public function testTranslateCategoryWithFormatSpecifiers() {
$result = __c('Some string with %+10s', 6, 'arguments');
$expected = 'Some string with arguments';
$this->assertEquals($expected, $result);
$result = __c('Some string with %-10s: args', 6, 'arguments');
$expected = 'Some string with arguments : args';
$this->assertEquals($expected, $result);
$result = __c('Some string with %\'*-10s: args', 6, 'arguments');
$expected = 'Some string with arguments*: args';
$this->assertEquals($expected, $result);
}
/** /**
* test __n() * test __n()
* *

View file

@ -25,6 +25,8 @@ App::uses('Cache', 'Cache');
*/ */
class CacheTest extends CakeTestCase { class CacheTest extends CakeTestCase {
protected $_count = 0;
/** /**
* setUp method * setUp method
* *
@ -493,4 +495,28 @@ class CacheTest extends CakeTestCase {
$this->assertEquals('test_file_', $settings['prefix']); $this->assertEquals('test_file_', $settings['prefix']);
$this->assertEquals(strtotime('+1 year') - time(), $settings['duration']); $this->assertEquals(strtotime('+1 year') - time(), $settings['duration']);
} }
/**
* test remember method.
*
* @return void
*/
public function testRemember() {
$expected = 'This is some data 0';
$result = Cache::remember('test_key', array($this, 'cacher'), 'default');
$this->assertEquals($expected, $result);
$this->_count = 1;
$result = Cache::remember('test_key', array($this, 'cacher'), 'default');
$this->assertEquals($expected, $result);
}
/**
* Method for testing Cache::remember()
*
* @return string
*/
public function cacher() {
return 'This is some data ' . $this->_count;
}
} }

View file

@ -0,0 +1,776 @@
<?php
/**
* MemcachedEngineTest file
*
* PHP 5
*
* CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
* @package Cake.Test.Case.Cache.Engine
* @since CakePHP(tm) v 2.5.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('Cache', 'Cache');
App::uses('MemcachedEngine', 'Cache/Engine');
/**
* Class TestMemcachedEngine
*
* @package Cake.Test.Case.Cache.Engine
*/
class TestMemcachedEngine extends MemcachedEngine {
/**
* public accessor to _parseServerString
*
* @param string $server
* @return array
*/
public function parseServerString($server) {
return $this->_parseServerString($server);
}
public function setMemcached($memcached) {
$this->_Memcached = $memcached;
}
public function getMemcached() {
return $this->_Memcached;
}
}
/**
* MemcachedEngineTest class
*
* @package Cake.Test.Case.Cache.Engine
*/
class MemcachedEngineTest extends CakeTestCase {
/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
$this->skipIf(!class_exists('Memcached'), 'Memcached is not installed or configured properly.');
Cache::config('memcached', array(
'engine' => 'Memcached',
'prefix' => 'cake_',
'duration' => 3600
));
}
/**
* tearDown method
*
* @return void
*/
public function tearDown() {
parent::tearDown();
Cache::drop('memcached');
Cache::drop('memcached_groups');
Cache::drop('memcached_helper');
Cache::config('default');
}
/**
* testSettings method
*
* @return void
*/
public function testSettings() {
$settings = Cache::settings('memcached');
unset($settings['path']);
$expecting = array(
'prefix' => 'cake_',
'duration' => 3600,
'probability' => 100,
'servers' => array('127.0.0.1'),
'persistent' => false,
'compress' => false,
'engine' => 'Memcached',
'login' => null,
'password' => null,
'groups' => array(),
'serialize' => 'php'
);
$this->assertEquals($expecting, $settings);
}
/**
* testCompressionSetting method
*
* @return void
*/
public function testCompressionSetting() {
$Memcached = new TestMemcachedEngine();
$Memcached->init(array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'compress' => false
));
$this->assertFalse($Memcached->getMemcached()->getOption(Memcached::OPT_COMPRESSION));
$MemcachedCompressed = new TestMemcachedEngine();
$MemcachedCompressed->init(array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'compress' => true
));
$this->assertTrue($MemcachedCompressed->getMemcached()->getOption(Memcached::OPT_COMPRESSION));
}
/**
* test accepts only valid serializer engine
*
* @return void
*/
public function testInvalidSerializerSetting() {
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'invalid_serializer'
);
$this->setExpectedException(
'CacheException', 'invalid_serializer is not a valid serializer engine for Memcached'
);
$Memcached->init($settings);
}
/**
* testPhpSerializerSetting method
*
* @return void
*/
public function testPhpSerializerSetting() {
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'php'
);
$Memcached->init($settings);
$this->assertEquals(Memcached::SERIALIZER_PHP, $Memcached->getMemcached()->getOption(Memcached::OPT_SERIALIZER));
}
/**
* testJsonSerializerSetting method
*
* @return void
*/
public function testJsonSerializerSetting() {
$this->skipIf(
!Memcached::HAVE_JSON,
'Memcached extension is not compiled with json support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'json'
);
$Memcached->init($settings);
$this->assertEquals(Memcached::SERIALIZER_JSON, $Memcached->getMemcached()->getOption(Memcached::OPT_SERIALIZER));
}
/**
* testIgbinarySerializerSetting method
*
* @return void
*/
public function testIgbinarySerializerSetting() {
$this->skipIf(
!Memcached::HAVE_IGBINARY,
'Memcached extension is not compiled with igbinary support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'igbinary'
);
$Memcached->init($settings);
$this->assertEquals(Memcached::SERIALIZER_IGBINARY, $Memcached->getMemcached()->getOption(Memcached::OPT_SERIALIZER));
}
/**
* testMsgpackSerializerSetting method
*
* @return void
*/
public function testMsgpackSerializerSetting() {
$this->skipIf(
!defined('Memcached::HAVE_MSGPACK') || !Memcached::HAVE_MSGPACK,
'Memcached extension is not compiled with msgpack support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'msgpack'
);
$Memcached->init($settings);
$this->assertEquals(Memcached::SERIALIZER_MSGPACK, $Memcached->getMemcached()->getOption(Memcached::OPT_SERIALIZER));
}
/**
* testJsonSerializerThrowException method
*
* @return void
*/
public function testJsonSerializerThrowException() {
$this->skipIf(
Memcached::HAVE_JSON,
'Memcached extension is compiled with json support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'json'
);
$this->setExpectedException(
'CacheException', 'Memcached extension is not compiled with json support'
);
$Memcached->init($settings);
}
/**
* testMsgpackSerializerThrowException method
*
* @return void
*/
public function testMsgpackSerializerThrowException() {
$this->skipIf(
defined('Memcached::HAVE_MSGPACK') && Memcached::HAVE_MSGPACK,
'Memcached extension is compiled with msgpack support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'msgpack'
);
$this->setExpectedException(
'CacheException', 'msgpack is not a valid serializer engine for Memcached'
);
$Memcached->init($settings);
}
/**
* testIgbinarySerializerThrowException method
*
* @return void
*/
public function testIgbinarySerializerThrowException() {
$this->skipIf(
Memcached::HAVE_IGBINARY,
'Memcached extension is compiled with igbinary support'
);
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'serialize' => 'igbinary'
);
$this->setExpectedException(
'CacheException', 'Memcached extension is not compiled with igbinary support'
);
$Memcached->init($settings);
}
/**
* test using authentication without memcached installed with SASL support
* throw an exception
*
* @return void
*/
public function testSaslAuthException() {
$Memcached = new TestMemcachedEngine();
$settings = array(
'engine' => 'Memcached',
'servers' => array('127.0.0.1:11211'),
'persistent' => false,
'login' => 'test',
'password' => 'password'
);
$this->skipIf(
method_exists($Memcached->getMemcached(), 'setSaslAuthData'),
'Memcached extension is installed with SASL support'
);
$this->setExpectedException(
'CacheException', 'Memcached extension is not build with SASL support'
);
$Memcached->init($settings);
}
/**
* testSettings method
*
* @return void
*/
public function testMultipleServers() {
$servers = array('127.0.0.1:11211', '127.0.0.1:11222');
$available = true;
$Memcached = new Memcached();
foreach ($servers as $server) {
list($host, $port) = explode(':', $server);
//@codingStandardsIgnoreStart
if (!$Memcached->addServer($host, $port)) {
$available = false;
}
//@codingStandardsIgnoreEnd
}
$this->skipIf(!$available, 'Need memcached servers at ' . implode(', ', $servers) . ' to run this test.');
$Memcached = new MemcachedEngine();
$Memcached->init(array('engine' => 'Memcached', 'servers' => $servers));
$settings = $Memcached->settings();
$this->assertEquals($settings['servers'], $servers);
Cache::drop('dual_server');
}
/**
* test connecting to an ipv6 server.
*
* @return void
*/
public function testConnectIpv6() {
$Memcached = new MemcachedEngine();
$result = $Memcached->init(array(
'prefix' => 'cake_',
'duration' => 200,
'engine' => 'Memcached',
'servers' => array(
'[::1]:11211'
)
));
$this->assertTrue($result);
}
/**
* test non latin domains.
*
* @return void
*/
public function testParseServerStringNonLatin() {
$Memcached = new TestMemcachedEngine();
$result = $Memcached->parseServerString('schülervz.net:13211');
$this->assertEquals(array('schülervz.net', '13211'), $result);
$result = $Memcached->parseServerString('sülül:1111');
$this->assertEquals(array('sülül', '1111'), $result);
}
/**
* test unix sockets.
*
* @return void
*/
public function testParseServerStringUnix() {
$Memcached = new TestMemcachedEngine();
$result = $Memcached->parseServerString('unix:///path/to/memcachedd.sock');
$this->assertEquals(array('unix:///path/to/memcachedd.sock', 0), $result);
}
/**
* testReadAndWriteCache method
*
* @return void
*/
public function testReadAndWriteCache() {
Cache::set(array('duration' => 1), null, 'memcached');
$result = Cache::read('test', 'memcached');
$expecting = '';
$this->assertEquals($expecting, $result);
$data = 'this is a test of the emergency broadcasting system';
$result = Cache::write('test', $data, 'memcached');
$this->assertTrue($result);
$result = Cache::read('test', 'memcached');
$expecting = $data;
$this->assertEquals($expecting, $result);
Cache::delete('test', 'memcached');
}
/**
* testExpiry method
*
* @return void
*/
public function testExpiry() {
Cache::set(array('duration' => 1), 'memcached');
$result = Cache::read('test', 'memcached');
$this->assertFalse($result);
$data = 'this is a test of the emergency broadcasting system';
$result = Cache::write('other_test', $data, 'memcached');
$this->assertTrue($result);
sleep(2);
$result = Cache::read('other_test', 'memcached');
$this->assertFalse($result);
Cache::set(array('duration' => "+1 second"), 'memcached');
$data = 'this is a test of the emergency broadcasting system';
$result = Cache::write('other_test', $data, 'memcached');
$this->assertTrue($result);
sleep(3);
$result = Cache::read('other_test', 'memcached');
$this->assertFalse($result);
Cache::config('memcached', array('duration' => '+1 second'));
$result = Cache::read('other_test', 'memcached');
$this->assertFalse($result);
Cache::config('memcached', array('duration' => '+29 days'));
$data = 'this is a test of the emergency broadcasting system';
$result = Cache::write('long_expiry_test', $data, 'memcached');
$this->assertTrue($result);
sleep(2);
$result = Cache::read('long_expiry_test', 'memcached');
$expecting = $data;
$this->assertEquals($expecting, $result);
Cache::config('memcached', array('duration' => 3600));
}
/**
* testDeleteCache method
*
* @return void
*/
public function testDeleteCache() {
$data = 'this is a test of the emergency broadcasting system';
$result = Cache::write('delete_test', $data, 'memcached');
$this->assertTrue($result);
$result = Cache::delete('delete_test', 'memcached');
$this->assertTrue($result);
}
/**
* testDecrement method
*
* @return void
*/
public function testDecrement() {
$result = Cache::write('test_decrement', 5, 'memcached');
$this->assertTrue($result);
$result = Cache::decrement('test_decrement', 1, 'memcached');
$this->assertEquals(4, $result);
$result = Cache::read('test_decrement', 'memcached');
$this->assertEquals(4, $result);
$result = Cache::decrement('test_decrement', 2, 'memcached');
$this->assertEquals(2, $result);
$result = Cache::read('test_decrement', 'memcached');
$this->assertEquals(2, $result);
Cache::delete('test_decrement', 'memcached');
}
/**
* test decrementing compressed keys
*
* @return void
*/
public function testDecrementCompressedKeys() {
Cache::config('compressed_memcached', array(
'engine' => 'Memcached',
'duration' => '+2 seconds',
'servers' => array('127.0.0.1:11211'),
'compress' => true
));
$result = Cache::write('test_decrement', 5, 'compressed_memcached');
$this->assertTrue($result);
$result = Cache::decrement('test_decrement', 1, 'compressed_memcached');
$this->assertEquals(4, $result);
$result = Cache::read('test_decrement', 'compressed_memcached');
$this->assertEquals(4, $result);
$result = Cache::decrement('test_decrement', 2, 'compressed_memcached');
$this->assertEquals(2, $result);
$result = Cache::read('test_decrement', 'compressed_memcached');
$this->assertEquals(2, $result);
Cache::delete('test_decrement', 'compressed_memcached');
}
/**
* testIncrement method
*
* @return void
*/
public function testIncrement() {
$result = Cache::write('test_increment', 5, 'memcached');
$this->assertTrue($result);
$result = Cache::increment('test_increment', 1, 'memcached');
$this->assertEquals(6, $result);
$result = Cache::read('test_increment', 'memcached');
$this->assertEquals(6, $result);
$result = Cache::increment('test_increment', 2, 'memcached');
$this->assertEquals(8, $result);
$result = Cache::read('test_increment', 'memcached');
$this->assertEquals(8, $result);
Cache::delete('test_increment', 'memcached');
}
/**
* test incrementing compressed keys
*
* @return void
*/
public function testIncrementCompressedKeys() {
Cache::config('compressed_memcached', array(
'engine' => 'Memcached',
'duration' => '+2 seconds',
'servers' => array('127.0.0.1:11211'),
'compress' => true
));
$result = Cache::write('test_increment', 5, 'compressed_memcached');
$this->assertTrue($result);
$result = Cache::increment('test_increment', 1, 'compressed_memcached');
$this->assertEquals(6, $result);
$result = Cache::read('test_increment', 'compressed_memcached');
$this->assertEquals(6, $result);
$result = Cache::increment('test_increment', 2, 'compressed_memcached');
$this->assertEquals(8, $result);
$result = Cache::read('test_increment', 'compressed_memcached');
$this->assertEquals(8, $result);
Cache::delete('test_increment', 'compressed_memcached');
}
/**
* test that configurations don't conflict, when a file engine is declared after a memcached one.
*
* @return void
*/
public function testConfigurationConflict() {
Cache::config('long_memcached', array(
'engine' => 'Memcached',
'duration' => '+2 seconds',
'servers' => array('127.0.0.1:11211'),
));
Cache::config('short_memcached', array(
'engine' => 'Memcached',
'duration' => '+1 seconds',
'servers' => array('127.0.0.1:11211'),
));
Cache::config('some_file', array('engine' => 'File'));
$this->assertTrue(Cache::write('duration_test', 'yay', 'long_memcached'));
$this->assertTrue(Cache::write('short_duration_test', 'boo', 'short_memcached'));
$this->assertEquals('yay', Cache::read('duration_test', 'long_memcached'), 'Value was not read %s');
$this->assertEquals('boo', Cache::read('short_duration_test', 'short_memcached'), 'Value was not read %s');
sleep(1);
$this->assertEquals('yay', Cache::read('duration_test', 'long_memcached'), 'Value was not read %s');
sleep(2);
$this->assertFalse(Cache::read('short_duration_test', 'short_memcached'), 'Cache was not invalidated %s');
$this->assertFalse(Cache::read('duration_test', 'long_memcached'), 'Value did not expire %s');
Cache::delete('duration_test', 'long_memcached');
Cache::delete('short_duration_test', 'short_memcached');
}
/**
* test clearing memcached.
*
* @return void
*/
public function testClear() {
Cache::config('memcached2', array(
'engine' => 'Memcached',
'prefix' => 'cake2_',
'duration' => 3600
));
Cache::write('some_value', 'cache1', 'memcached');
$result = Cache::clear(true, 'memcached');
$this->assertTrue($result);
$this->assertEquals('cache1', Cache::read('some_value', 'memcached'));
Cache::write('some_value', 'cache2', 'memcached2');
$result = Cache::clear(false, 'memcached');
$this->assertTrue($result);
$this->assertFalse(Cache::read('some_value', 'memcached'));
$this->assertEquals('cache2', Cache::read('some_value', 'memcached2'));
Cache::clear(false, 'memcached2');
}
/**
* test that a 0 duration can successfully write.
*
* @return void
*/
public function testZeroDuration() {
Cache::config('memcached', array('duration' => 0));
$result = Cache::write('test_key', 'written!', 'memcached');
$this->assertTrue($result);
$result = Cache::read('test_key', 'memcached');
$this->assertEquals('written!', $result);
}
/**
* test that durations greater than 30 days never expire
*
* @return void
*/
public function testLongDurationEqualToZero() {
$memcached = new TestMemcachedEngine();
$memcached->settings['compress'] = false;
$mock = $this->getMock('Memcached');
$memcached->setMemcached($mock);
$mock->expects($this->once())
->method('set')
->with('key', 'value', 0);
$value = 'value';
$memcached->write('key', $value, 50 * DAY);
}
/**
* Tests that configuring groups for stored keys return the correct values when read/written
* Shows that altering the group value is equivalent to deleting all keys under the same
* group
*
* @return void
*/
public function testGroupReadWrite() {
Cache::config('memcached_groups', array(
'engine' => 'Memcached',
'duration' => 3600,
'groups' => array('group_a', 'group_b'),
'prefix' => 'test_'
));
Cache::config('memcached_helper', array(
'engine' => 'Memcached',
'duration' => 3600,
'prefix' => 'test_'
));
$this->assertTrue(Cache::write('test_groups', 'value', 'memcached_groups'));
$this->assertEquals('value', Cache::read('test_groups', 'memcached_groups'));
Cache::increment('group_a', 1, 'memcached_helper');
$this->assertFalse(Cache::read('test_groups', 'memcached_groups'));
$this->assertTrue(Cache::write('test_groups', 'value2', 'memcached_groups'));
$this->assertEquals('value2', Cache::read('test_groups', 'memcached_groups'));
Cache::increment('group_b', 1, 'memcached_helper');
$this->assertFalse(Cache::read('test_groups', 'memcached_groups'));
$this->assertTrue(Cache::write('test_groups', 'value3', 'memcached_groups'));
$this->assertEquals('value3', Cache::read('test_groups', 'memcached_groups'));
}
/**
* Tests that deleteing from a groups-enabled config is possible
*
* @return void
*/
public function testGroupDelete() {
Cache::config('memcached_groups', array(
'engine' => 'Memcached',
'duration' => 3600,
'groups' => array('group_a', 'group_b')
));
$this->assertTrue(Cache::write('test_groups', 'value', 'memcached_groups'));
$this->assertEquals('value', Cache::read('test_groups', 'memcached_groups'));
$this->assertTrue(Cache::delete('test_groups', 'memcached_groups'));
$this->assertFalse(Cache::read('test_groups', 'memcached_groups'));
}
/**
* Test clearing a cache group
*
* @return void
*/
public function testGroupClear() {
Cache::config('memcached_groups', array(
'engine' => 'Memcached',
'duration' => 3600,
'groups' => array('group_a', 'group_b')
));
$this->assertTrue(Cache::write('test_groups', 'value', 'memcached_groups'));
$this->assertTrue(Cache::clearGroup('group_a', 'memcached_groups'));
$this->assertFalse(Cache::read('test_groups', 'memcached_groups'));
$this->assertTrue(Cache::write('test_groups', 'value2', 'memcached_groups'));
$this->assertTrue(Cache::clearGroup('group_b', 'memcached_groups'));
$this->assertFalse(Cache::read('test_groups', 'memcached_groups'));
}
}

View file

@ -76,6 +76,7 @@ class RedisEngineTest extends CakeTestCase {
'timeout' => 0, 'timeout' => 0,
'persistent' => true, 'persistent' => true,
'password' => false, 'password' => false,
'database' => 0,
); );
$this->assertEquals($expecting, $settings); $this->assertEquals($expecting, $settings);
} }
@ -90,6 +91,51 @@ class RedisEngineTest extends CakeTestCase {
$this->assertTrue($Redis->init(Cache::settings('redis'))); $this->assertTrue($Redis->init(Cache::settings('redis')));
} }
/**
* testMultiDatabaseOperations method
*
* @return void
*/
public function testMultiDatabaseOperations() {
Cache::config('redisdb0', array(
'engine' => 'Redis',
'prefix' => 'cake2_',
'duration' => 3600,
'persistent' => false,
));
Cache::config('redisdb1', array(
'engine' => 'Redis',
'database' => 1,
'prefix' => 'cake2_',
'duration' => 3600,
'persistent' => false,
));
$result = Cache::write('save_in_0', true, 'redisdb0');
$exist = Cache::read('save_in_0', 'redisdb0');
$this->assertTrue($result);
$this->assertTrue($exist);
$result = Cache::write('save_in_1', true, 'redisdb1');
$this->assertTrue($result);
$exist = Cache::read('save_in_0', 'redisdb1');
$this->assertFalse($exist);
$exist = Cache::read('save_in_1', 'redisdb1');
$this->assertTrue($exist);
Cache::delete('save_in_0', 'redisdb0');
$exist = Cache::read('save_in_0', 'redisdb0');
$this->assertFalse($exist);
Cache::delete('save_in_1', 'redisdb1');
$exist = Cache::read('save_in_1', 'redisdb1');
$this->assertFalse($exist);
Cache::drop('redisdb0');
Cache::drop('redisdb1');
}
/** /**
* testReadAndWriteCache method * testReadAndWriteCache method
* *

View file

@ -20,6 +20,7 @@ App::uses('CommandListShell', 'Console/Command');
App::uses('ConsoleOutput', 'Console'); App::uses('ConsoleOutput', 'Console');
App::uses('ConsoleInput', 'Console'); App::uses('ConsoleInput', 'Console');
App::uses('Shell', 'Console'); App::uses('Shell', 'Console');
App::uses('CommandTask', 'Console/Command/Task');
/** /**
* Class TestStringOutput * Class TestStringOutput
@ -68,6 +69,12 @@ class CommandListShellTest extends CakeTestCase {
array('in', '_stop', 'clear'), array('in', '_stop', 'clear'),
array($out, $out, $in) array($out, $out, $in)
); );
$this->Shell->Command = $this->getMock(
'CommandTask',
array('in', '_stop', 'clear'),
array($out, $out, $in)
);
} }
/** /**
@ -96,7 +103,7 @@ class CommandListShellTest extends CakeTestCase {
$expected = "/\[.*TestPluginTwo.*\] example, welcome/"; $expected = "/\[.*TestPluginTwo.*\] example, welcome/";
$this->assertRegExp($expected, $output); $this->assertRegExp($expected, $output);
$expected = "/\[.*CORE.*\] acl, api, bake, command_list, console, i18n, schema, server, test, testsuite, upgrade/"; $expected = "/\[.*CORE.*\] acl, api, bake, command_list, completion, console, i18n, schema, server, test, testsuite, upgrade/";
$this->assertRegExp($expected, $output); $this->assertRegExp($expected, $output);
$expected = "/\[.*app.*\] sample/"; $expected = "/\[.*app.*\] sample/";

View file

@ -0,0 +1,261 @@
<?php
/**
* CompletionShellTest file
*
* PHP 5
*
* CakePHP : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP Project
* @package Cake.Test.Case.Console.Command
* @since CakePHP v 2.5
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('CompletionShell', 'Console/Command');
App::uses('ConsoleOutput', 'Console');
App::uses('ConsoleInput', 'Console');
App::uses('Shell', 'Console');
App::uses('CommandTask', 'Console/Command/Task');
/**
* Class TestCompletionStringOutput
*
* @package Cake.Test.Case.Console.Command
*/
class TestCompletionStringOutput extends ConsoleOutput {
public $output = '';
protected function _write($message) {
$this->output .= $message;
}
}
/**
* Class CompletionShellTest
*
* @package Cake.Test.Case.Console.Command
*/
class CompletionShellTest extends CakeTestCase {
/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
App::build(array(
'Plugin' => array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS
),
'Console/Command' => array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Console' . DS . 'Command' . DS
)
), App::RESET);
CakePlugin::load(array('TestPlugin', 'TestPluginTwo'));
$out = new TestCompletionStringOutput();
$in = $this->getMock('ConsoleInput', array(), array(), '', false);
$this->Shell = $this->getMock(
'CompletionShell',
array('in', '_stop', 'clear'),
array($out, $out, $in)
);
$this->Shell->Command = $this->getMock(
'CommandTask',
array('in', '_stop', 'clear'),
array($out, $out, $in)
);
}
/**
* tearDown
*
* @return void
*/
public function tearDown() {
parent::tearDown();
unset($this->Shell);
CakePlugin::unload();
}
/**
* test that the startup method supresses the shell header
*
* @return void
*/
public function testStartup() {
$this->Shell->runCommand('main', array());
$output = $this->Shell->stdout->output;
$needle = 'Welcome to CakePHP';
$this->assertTextNotContains($needle, $output);
}
/**
* test that main displays a warning
*
* @return void
*/
public function testMain() {
$this->Shell->runCommand('main', array());
$output = $this->Shell->stdout->output;
$expected = "/This command is not intended to be called manually/";
$this->assertRegExp($expected, $output);
}
/**
* test commands method that list all available commands
*
* @return void
*/
public function testCommands() {
$this->Shell->runCommand('commands', array());
$output = $this->Shell->stdout->output;
$expected = "TestPlugin.example TestPluginTwo.example TestPluginTwo.welcome acl api bake command_list completion console i18n schema server test testsuite upgrade sample\n";
$this->assertEquals($expected, $output);
}
/**
* test that options without argument returns the default options
*
* @return void
*/
public function testOptionsNoArguments() {
$this->Shell->runCommand('options', array());
$output = $this->Shell->stdout->output;
$expected = "--help -h --verbose -v --quiet -q\n";
$this->assertEquals($expected, $output);
}
/**
* test that options with a nonexisting command returns the default options
*
* @return void
*/
public function testOptionsNonExistingCommand() {
$this->Shell->runCommand('options', array('options', 'foo'));
$output = $this->Shell->stdout->output;
$expected = "--help -h --verbose -v --quiet -q\n";
$this->assertEquals($expected, $output);
}
/**
* test that options with a existing command returns the proper options
*
* @return void
*/
public function testOptions() {
$this->Shell->runCommand('options', array('options', 'bake'));
$output = $this->Shell->stdout->output;
$expected = "--help -h --verbose -v --quiet -q --connection -c --theme -t\n";
$this->assertEquals($expected, $output);
}
/**
* test that subCommands with a existing CORE command returns the proper sub commands
*
* @return void
*/
public function testSubCommandsCorePlugin() {
$this->Shell->runCommand('subCommands', array('subCommands', 'CORE.bake'));
$output = $this->Shell->stdout->output;
$expected = "controller db_config fixture model plugin project test view\n";
$this->assertEquals($expected, $output);
}
/**
* test that subCommands with a existing APP command returns the proper sub commands (in this case none)
*
* @return void
*/
public function testSubCommandsAppPlugin() {
$this->Shell->runCommand('subCommands', array('subCommands', 'app.sample'));
$output = $this->Shell->stdout->output;
$expected = '';
$this->assertEquals($expected, $output);
}
/**
* test that subCommands with a existing plugin command returns the proper sub commands
*
* @return void
*/
public function testSubCommandsPlugin() {
$this->Shell->runCommand('subCommands', array('subCommands', 'TestPluginTwo.welcome'));
$output = $this->Shell->stdout->output;
$expected = "say_hello\n";
$this->assertEquals($expected, $output);
}
/**
* test that subcommands without arguments returns nothing
*
* @return void
*/
public function testSubCommandsNoArguments() {
$this->Shell->runCommand('subCommands', array());
$output = $this->Shell->stdout->output;
$expected = '';
$this->assertEquals($expected, $output);
}
/**
* test that subcommands with a nonexisting command returns nothing
*
* @return void
*/
public function testSubCommandsNonExistingCommand() {
$this->Shell->runCommand('subCommands', array('subCommands', 'foo'));
$output = $this->Shell->stdout->output;
$expected = '';
$this->assertEquals($expected, $output);
}
/**
* test that subcommands returns the available subcommands for the given command
*
* @return void
*/
public function testSubCommands() {
$this->Shell->runCommand('subCommands', array('subCommands', 'bake'));
$output = $this->Shell->stdout->output;
$expected = "controller db_config fixture model plugin project test view\n";
$this->assertEquals($expected, $output);
}
/**
* test that fuzzy returns nothing
*
* @return void
*/
public function testFuzzy() {
$this->Shell->runCommand('fuzzy', array());
$output = $this->Shell->stdout->output;
$expected = '';
$this->assertEquals($expected, $output);
}
}

View file

@ -424,6 +424,29 @@ class SchemaShellTest extends CakeTestCase {
$this->assertContains('public $aros_acos = array(', $contents); $this->assertContains('public $aros_acos = array(', $contents);
} }
/**
* Test schema run create with --yes option
*
* @return void
*/
public function testCreateOptionYes() {
$this->Shell = $this->getMock(
'SchemaShell',
array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop', '_run'),
array(&$this->Dispatcher)
);
$this->Shell->params = array(
'connection' => 'test',
'yes' => true,
);
$this->Shell->args = array('i18n');
$this->Shell->expects($this->never())->method('in');
$this->Shell->expects($this->exactly(2))->method('_run');
$this->Shell->startup();
$this->Shell->create();
}
/** /**
* Test schema run create with no table args. * Test schema run create with no table args.
* *
@ -534,6 +557,33 @@ class SchemaShellTest extends CakeTestCase {
$this->Shell->update(); $this->Shell->update();
} }
/**
* test run update with --yes option
*
* @return void
*/
public function testUpdateWithOptionYes() {
$this->Shell = $this->getMock(
'SchemaShell',
array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop', '_run'),
array(&$this->Dispatcher)
);
$this->Shell->params = array(
'connection' => 'test',
'force' => true,
'yes' => true,
);
$this->Shell->args = array('SchemaShellTest', 'articles');
$this->Shell->startup();
$this->Shell->expects($this->never())->method('in');
$this->Shell->expects($this->once())
->method('_run')
->with($this->arrayHasKey('articles'), 'update', $this->isInstanceOf('CakeSchema'));
$this->Shell->update();
}
/** /**
* test that the plugin param creates the correct path in the schema object. * test that the plugin param creates the correct path in the schema object.
* *

View file

@ -0,0 +1,240 @@
<?php
/**
* CakePHP : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP Project
* @package Cake.Test.Case.Console.Command
* @since CakePHP v 2.5
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
App::uses('CommandTask', 'Console/Command/Task');
/**
* CommandTaskTest class
*
* @package Cake.Test.Case.Console.Command.Task
*/
class CommandTaskTest extends CakeTestCase {
/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
App::build(array(
'Plugin' => array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS
),
'Console/Command' => array(
CAKE . 'Test' . DS . 'test_app' . DS . 'Console' . DS . 'Command' . DS
)
), App::RESET);
CakePlugin::load(array('TestPlugin', 'TestPluginTwo'));
$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
$in = $this->getMock('ConsoleInput', array(), array(), '', false);
$this->CommandTask = $this->getMock(
'CommandTask',
array('in', '_stop', 'clear'),
array($out, $out, $in)
);
}
/**
* tearDown
*
* @return void
*/
public function tearDown() {
parent::tearDown();
unset($this->CommandTask);
CakePlugin::unload();
}
/**
* Test the resulting list of shells
*
* @return void
*/
public function testGetShellList() {
$result = $this->CommandTask->getShellList();
$expected = array(
'CORE' => array(
'acl',
'api',
'bake',
'command_list',
'completion',
'console',
'i18n',
'schema',
'server',
'test',
'testsuite',
'upgrade'
),
'TestPlugin' => array(
'example'
),
'TestPluginTwo' => array(
'example',
'welcome'
),
'app' => array(
'sample'
),
);
$this->assertEquals($expected, $result);
}
/**
* Test the resulting list of commands
*
* @return void
*/
public function testCommands() {
$result = $this->CommandTask->commands();
$expected = array(
'TestPlugin.example',
'TestPluginTwo.example',
'TestPluginTwo.welcome',
'acl',
'api',
'bake',
'command_list',
'completion',
'console',
'i18n',
'schema',
'server',
'test',
'testsuite',
'upgrade',
'sample'
);
$this->assertEquals($expected, $result);
}
/**
* Test the resulting list of subcommands for the given command
*
* @return void
*/
public function testSubCommands() {
$result = $this->CommandTask->subCommands('acl');
$expected = array(
'check',
'create',
'db_config',
'delete',
'deny',
'getPath',
'grant',
'inherit',
'initdb',
'nodeExists',
'parseIdentifier',
'setParent',
'view'
);
$this->assertEquals($expected, $result);
}
/**
* Test that unknown commands return an empty array
*
* @return void
*/
public function testSubCommandsUnknownCommand() {
$result = $this->CommandTask->subCommands('yoghurt');
$expected = array();
$this->assertEquals($expected, $result);
}
/**
* Test that getting a existing shell returns the shell instance
*
* @return void
*/
public function testGetShell() {
$result = $this->CommandTask->getShell('acl');
$this->assertInstanceOf('AclShell', $result);
}
/**
* Test that getting a non-existing shell returns false
*
* @return void
*/
public function testGetShellNonExisting() {
$result = $this->CommandTask->getShell('strawberry');
$this->assertFalse($result);
}
/**
* Test that getting a existing core shell with 'core.' prefix returns the correct shell instance
*
* @return void
*/
public function testGetShellCore() {
$result = $this->CommandTask->getShell('core.bake');
$this->assertInstanceOf('BakeShell', $result);
}
/**
* Test the options array for a known command
*
* @return void
*/
public function testOptions() {
$result = $this->CommandTask->options('bake');
$expected = array(
'--help',
'-h',
'--verbose',
'-v',
'--quiet',
'-q',
'--connection',
'-c',
'--theme',
'-t'
);
$this->assertEquals($expected, $result);
}
/**
* Test the options array for an unknown command
*
* @return void
*/
public function testOptionsUnknownCommand() {
$result = $this->CommandTask->options('pie');
$expected = array(
'--help',
'-h',
'--verbose',
'-v',
'--quiet',
'-q'
);
$this->assertEquals($expected, $result);
}
}

View file

@ -30,6 +30,7 @@ App::uses('ModelTask', 'Console/Command/Task');
* ModelTaskTest class * ModelTaskTest class
* *
* @package Cake.Test.Case.Console.Command.Task * @package Cake.Test.Case.Console.Command.Task
* @property ModelTask $Task
*/ */
class ModelTaskTest extends CakeTestCase { class ModelTaskTest extends CakeTestCase {
@ -359,14 +360,53 @@ class ModelTaskTest extends CakeTestCase {
} }
/** /**
* test the validation Generation routine * Test that skipping fields during rule choice works when doing interactive field validation.
* *
* @return void * @return void
*/ */
public function testNonInteractiveDoValidation() { public function testSkippingChoiceInteractiveFieldValidation() {
$this->Task->initValidations();
$this->Task->interactive = true;
$this->Task->expects($this->any())->method('in')
->will($this->onConsecutiveCalls('24', 'y', 's'));
$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
$expected = array('notEmpty' => 'notEmpty', '_skipFields' => true);
$this->assertEquals($expected, $result);
}
/**
* Test that skipping fields after rule choice works when doing interactive field validation.
*
* @return void
*/
public function testSkippingAnotherInteractiveFieldValidation() {
$this->Task->initValidations();
$this->Task->interactive = true;
$this->Task->expects($this->any())->method('in')
->will($this->onConsecutiveCalls('24', 's'));
$result = $this->Task->fieldValidation('text', array('type' => 'string', 'length' => 10, 'null' => false));
$expected = array('notEmpty' => 'notEmpty', '_skipFields' => true);
$this->assertEquals($expected, $result);
}
/**
* Test the validation generation routine with skipping the rest of the fields
* when doing interactive field validation.
*
* @return void
*/
public function testInteractiveDoValidationWithSkipping() {
$this->Task->expects($this->any())
->method('in')
->will($this->onConsecutiveCalls('35', '24', 'n', '11', 's'));
$this->Task->interactive = true;
$Model = $this->getMock('Model'); $Model = $this->getMock('Model');
$Model->primaryKey = 'id'; $Model->primaryKey = 'id';
$Model->expects($this->any())->method('schema')->will($this->returnValue(array( $Model->expects($this->any())
->method('schema')
->will($this->returnValue(array(
'id' => array( 'id' => array(
'type' => 'integer', 'type' => 'integer',
'length' => 11, 'length' => 11,
@ -398,7 +438,65 @@ class ModelTaskTest extends CakeTestCase {
'length' => '', 'length' => '',
'null' => false, 'null' => false,
) )
))); )
));
$result = $this->Task->doValidation($Model);
$expected = array(
'name' => array(
'notEmpty' => 'notEmpty'
),
'email' => array(
'email' => 'email',
),
);
$this->assertEquals($expected, $result);
}
/**
* test the validation Generation routine
*
* @return void
*/
public function testNonInteractiveDoValidation() {
$Model = $this->getMock('Model');
$Model->primaryKey = 'id';
$Model->expects($this->any())
->method('schema')
->will($this->returnValue(array(
'id' => array(
'type' => 'integer',
'length' => 11,
'null' => false,
'key' => 'primary',
),
'name' => array(
'type' => 'string',
'length' => 20,
'null' => false,
),
'email' => array(
'type' => 'string',
'length' => 255,
'null' => false,
),
'some_date' => array(
'type' => 'date',
'length' => '',
'null' => false,
),
'some_time' => array(
'type' => 'time',
'length' => '',
'null' => false,
),
'created' => array(
'type' => 'datetime',
'length' => '',
'null' => false,
)
)
));
$this->Task->interactive = false; $this->Task->interactive = false;
$result = $this->Task->doValidation($Model); $result = $this->Task->doValidation($Model);

View file

@ -413,6 +413,8 @@ class TestTaskTest extends CakeTestCase {
$this->assertContains('function testDoSomething()', $result); $this->assertContains('function testDoSomething()', $result);
$this->assertContains('function testDoSomethingElse()', $result); $this->assertContains('function testDoSomethingElse()', $result);
$this->assertContains('$this->markTestIncomplete(\'testDoSomething not implemented.\')', $result);
$this->assertContains('$this->markTestIncomplete(\'testDoSomethingElse not implemented.\')', $result);
$this->assertContains("'app.test_task_article'", $result); $this->assertContains("'app.test_task_article'", $result);
$this->assertContains("'app.test_task_comment'", $result); $this->assertContains("'app.test_task_comment'", $result);

View file

@ -256,7 +256,6 @@ class DbAclTest extends CakeTestCase {
/** /**
* testDbAclAllow method * testDbAclAllow method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testAllow() { public function testAllow() {
@ -297,17 +296,16 @@ class DbAclTest extends CakeTestCase {
* @return void * @return void
*/ */
public function testAllowInvalidPermission() { public function testAllowInvalidPermission() {
$this->Acl->allow('Micheal', 'tpsReports', 'derp'); $this->assertFalse($this->Acl->allow('Micheal', 'tpsReports', 'derp'));
} }
/** /**
* testAllowInvalidNode method * testAllowInvalidNode method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testAllowInvalidNode() { public function testAllowInvalidNode() {
$this->Acl->allow('Homer', 'tpsReports', 'create'); $this->assertFalse($this->Acl->allow('Homer', 'tpsReports', 'create'));
} }
/** /**
@ -333,7 +331,6 @@ class DbAclTest extends CakeTestCase {
/** /**
* testCheckInvalidNode method * testCheckInvalidNode method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testCheckInvalidNode() { public function testCheckInvalidNode() {
@ -343,21 +340,19 @@ class DbAclTest extends CakeTestCase {
/** /**
* testCheckInvalidPermission method * testCheckInvalidPermission method
* *
* @expectedException PHPUnit_Framework_Error_Notice
* @return void * @return void
*/ */
public function testCheckInvalidPermission() { public function testCheckInvalidPermission() {
$this->Acl->check('Lumbergh', 'smash', 'foobar'); $this->assertFalse($this->Acl->check('Lumbergh', 'smash', 'foobar'));
} }
/** /**
* testCheckMissingPermission method * testCheckMissingPermission method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testCheckMissingPermission() { public function testCheckMissingPermission() {
$this->Acl->check('users', 'NonExistent', 'read'); $this->assertFalse($this->Acl->check('users', 'NonExistent', 'read'));
} }
/** /**
@ -380,7 +375,6 @@ class DbAclTest extends CakeTestCase {
/** /**
* testDbAclDeny method * testDbAclDeny method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testDeny() { public function testDeny() {
@ -450,7 +444,6 @@ class DbAclTest extends CakeTestCase {
/** /**
* testDbGrant method * testDbGrant method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testGrant() { public function testGrant() {
@ -471,7 +464,6 @@ class DbAclTest extends CakeTestCase {
/** /**
* testDbRevoke method * testDbRevoke method
* *
* @expectedException PHPUnit_Framework_Error_Warning
* @return void * @return void
*/ */
public function testRevoke() { public function testRevoke() {

View file

@ -590,6 +590,27 @@ class AuthComponentTest extends CakeTestCase {
$this->assertEquals('AuthUser', $result->settings['userModel']); $this->assertEquals('AuthUser', $result->settings['userModel']);
} }
/**
* test defining the same Authenticate object but with different password hashers
*
* @return void
*/
public function testSameAuthenticateWithDifferentHashers() {
$this->Controller->Auth->authenticate = array(
'FormSimple' => array('className' => 'Form', 'passwordHasher' => 'Simple'),
'FormBlowfish' => array('className' => 'Form', 'passwordHasher' => 'Blowfish'),
);
$objects = $this->Controller->Auth->constructAuthenticate();
$this->assertEquals(2, count($objects));
$this->assertInstanceOf('FormAuthenticate', $objects[0]);
$this->assertInstanceOf('FormAuthenticate', $objects[1]);
$this->assertInstanceOf('SimplePasswordHasher', $objects[0]->passwordHasher());
$this->assertInstanceOf('BlowfishPasswordHasher', $objects[1]->passwordHasher());
}
/** /**
* Tests that deny always takes precedence over allow * Tests that deny always takes precedence over allow
* *
@ -1123,11 +1144,13 @@ class AuthComponentTest extends CakeTestCase {
App::uses('Dispatcher', 'Routing'); App::uses('Dispatcher', 'Routing');
$Response = new CakeResponse();
ob_start(); ob_start();
$Dispatcher = new Dispatcher(); $Dispatcher = new Dispatcher();
$Dispatcher->dispatch(new CakeRequest('/ajax_auth/add'), new CakeResponse(), array('return' => 1)); $Dispatcher->dispatch(new CakeRequest('/ajax_auth/add'), $Response, array('return' => 1));
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEquals(403, $Response->statusCode());
$this->assertEquals("Ajax!\nthis is the test element", str_replace("\r\n", "\n", $result)); $this->assertEquals("Ajax!\nthis is the test element", str_replace("\r\n", "\n", $result));
unset($_SERVER['HTTP_X_REQUESTED_WITH']); unset($_SERVER['HTTP_X_REQUESTED_WITH']);
} }

View file

@ -201,6 +201,40 @@ class CookieComponentTest extends CakeTestCase {
$this->assertEquals('value', $result); $this->assertEquals('value', $result);
} }
/**
* test write() encrypted data with falsey value
*
* @return void
*/
public function testWriteWithFalseyValue() {
$this->Cookie->type('aes');
$this->Cookie->key = 'qSI232qs*&sXOw!adre@34SAv!@*(XSL#$%)asGb$@11~_+!@#HKis~#^';
$this->Cookie->write('Testing');
$result = $this->Cookie->read('Testing');
$this->assertNull($result);
$this->Cookie->write('Testing', '');
$result = $this->Cookie->read('Testing');
$this->assertEquals('', $result);
$this->Cookie->write('Testing', false);
$result = $this->Cookie->read('Testing');
$this->assertFalse($result);
$this->Cookie->write('Testing', 1);
$result = $this->Cookie->read('Testing');
$this->assertEquals(1, $result);
$this->Cookie->write('Testing', '0');
$result = $this->Cookie->read('Testing');
$this->assertSame('0', $result);
$this->Cookie->write('Testing', 0);
$result = $this->Cookie->read('Testing');
$this->assertSame(0, $result);
}
/** /**
* test that two write() calls use the expiry. * test that two write() calls use the expiry.
* *

View file

@ -191,14 +191,14 @@ This is the body of the message
MSGBLOC; MSGBLOC;
$this->Controller->EmailTest->sendAs = 'text'; $this->Controller->EmailTest->sendAs = 'text';
$expect = str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $message); $expected = str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $message);
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals(DebugCompTransport::$lastEmail, $expect); $this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
$this->Controller->EmailTest->sendAs = 'html'; $this->Controller->EmailTest->sendAs = 'html';
$expect = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message); $expected = str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $message);
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals(DebugCompTransport::$lastEmail, $expect); $this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
} }
/** /**
@ -262,18 +262,18 @@ TEXTBLOC;
HTMLBLOC; HTMLBLOC;
$this->Controller->EmailTest->sendAs = 'text'; $this->Controller->EmailTest->sendAs = 'text';
$expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '</pre>'; $expected = '<pre>' . str_replace('{CONTENTTYPE}', 'text/plain; charset=UTF-8', $header) . $text . "\n" . '</pre>';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals(DebugCompTransport::$lastEmail, $expect); $this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
$this->Controller->EmailTest->sendAs = 'html'; $this->Controller->EmailTest->sendAs = 'html';
$expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . "\n" . '</pre>'; $expected = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . "\n" . '</pre>';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals(DebugCompTransport::$lastEmail, $expect); $this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
$this->Controller->EmailTest->sendAs = 'both'; $this->Controller->EmailTest->sendAs = 'both';
$expect = str_replace('{CONTENTTYPE}', 'multipart/mixed; boundary="{boundary}"', $header); $expected = str_replace('{CONTENTTYPE}', 'multipart/mixed; boundary="{boundary}"', $header);
$expect .= "--{boundary}\n" . $expected .= "--{boundary}\n" .
'Content-Type: multipart/alternative; boundary="alt-{boundary}"' . "\n\n" . 'Content-Type: multipart/alternative; boundary="alt-{boundary}"' . "\n\n" .
'--alt-{boundary}' . "\n" . '--alt-{boundary}' . "\n" .
'Content-Type: text/plain; charset=UTF-8' . "\n" . 'Content-Type: text/plain; charset=UTF-8' . "\n" .
@ -288,11 +288,11 @@ HTMLBLOC;
'--alt-{boundary}--' . "\n\n\n" . '--alt-{boundary}--' . "\n\n\n" .
'--{boundary}--' . "\n"; '--{boundary}--' . "\n";
$expect = '<pre>' . $expect . '</pre>'; $expected = '<pre>' . $expected . '</pre>';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals( $this->assertTextEquals(
$expect, $expected,
preg_replace('/[a-z0-9]{32}/i', '{boundary}', DebugCompTransport::$lastEmail) preg_replace('/[a-z0-9]{32}/i', '{boundary}', DebugCompTransport::$lastEmail)
); );
@ -313,9 +313,9 @@ HTMLBLOC;
HTMLBLOC; HTMLBLOC;
$this->Controller->EmailTest->sendAs = 'html'; $this->Controller->EmailTest->sendAs = 'html';
$expect = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . '</pre>'; $expected = '<pre>' . str_replace('{CONTENTTYPE}', 'text/html; charset=UTF-8', $header) . $html . '</pre>';
$this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin')); $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin'));
$this->assertTextEquals(DebugCompTransport::$lastEmail, $expect); $this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
} }
/** /**

View file

@ -358,7 +358,7 @@ class PaginatorComponentTest extends CakeTestCase {
$this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']); $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
$this->assertEquals(array(3, 2, 1), $results); $this->assertEquals(array(3, 2, 1), $results);
$Controller->request->params['named'] = array('sort' => 'NotExisting.field', 'direction' => 'desc'); $Controller->request->params['named'] = array('sort' => 'NotExisting.field', 'direction' => 'desc', 'limit' => 2);
$Controller->Paginator->paginate('PaginatorControllerPost'); $Controller->Paginator->paginate('PaginatorControllerPost');
$this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']); $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
$this->assertEquals(array(), $Controller->PaginatorControllerPost->lastQueries[1]['order'][0], 'no order should be set.'); $this->assertEquals(array(), $Controller->PaginatorControllerPost->lastQueries[1]['order'][0], 'no order should be set.');
@ -367,7 +367,7 @@ class PaginatorComponentTest extends CakeTestCase {
'sort' => 'PaginatorControllerPost.author_id', 'direction' => 'allYourBase' 'sort' => 'PaginatorControllerPost.author_id', 'direction' => 'allYourBase'
); );
$results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id'); $results = Hash::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
$this->assertEquals(array('PaginatorControllerPost.author_id' => 'asc'), $Controller->PaginatorControllerPost->lastQueries[1]['order'][0]); $this->assertEquals(array('PaginatorControllerPost.author_id' => 'asc'), $Controller->PaginatorControllerPost->lastQueries[0]['order'][0]);
$this->assertEquals(array(1, 3, 2), $results); $this->assertEquals(array(1, 3, 2), $results);
$Controller->request->params['named'] = array(); $Controller->request->params['named'] = array();
@ -466,7 +466,7 @@ class PaginatorComponentTest extends CakeTestCase {
$result = $Controller->Paginator->paginate('PaginatorControllerPost'); $result = $Controller->Paginator->paginate('PaginatorControllerPost');
$this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']); $this->assertEquals(1, $Controller->params['paging']['PaginatorControllerPost']['page']);
$this->assertEquals(array(1, 2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id')); $this->assertEquals(array(1, 2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
$this->assertTrue(isset($Controller->PaginatorControllerPost->lastQueries[1]['contain'])); $this->assertTrue(isset($Controller->PaginatorControllerPost->lastQueries[0]['contain']));
$Controller->Paginator->settings = array( $Controller->Paginator->settings = array(
'PaginatorControllerPost' => array( 'PaginatorControllerPost' => array(
@ -475,14 +475,14 @@ class PaginatorComponentTest extends CakeTestCase {
); );
$result = $Controller->Paginator->paginate('PaginatorControllerPost'); $result = $Controller->Paginator->paginate('PaginatorControllerPost');
$this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id')); $this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
$this->assertEquals(array('PaginatorControllerPost.id > ' => '1'), $Controller->PaginatorControllerPost->lastQueries[1]['conditions']); $this->assertEquals(array('PaginatorControllerPost.id > ' => '1'), $Controller->PaginatorControllerPost->lastQueries[0]['conditions']);
$Controller->request->params['named'] = array('limit' => 12); $Controller->request->params['named'] = array('limit' => 12);
$Controller->Paginator->settings = array('limit' => 30, 'maxLimit' => 100, 'paramType' => 'named'); $Controller->Paginator->settings = array('limit' => 30, 'maxLimit' => 100, 'paramType' => 'named');
$result = $Controller->Paginator->paginate('PaginatorControllerPost'); $result = $Controller->Paginator->paginate('PaginatorControllerPost');
$paging = $Controller->params['paging']['PaginatorControllerPost']; $paging = $Controller->params['paging']['PaginatorControllerPost'];
$this->assertEquals(12, $Controller->PaginatorControllerPost->lastQueries[1]['limit']); $this->assertEquals(12, $Controller->PaginatorControllerPost->lastQueries[0]['limit']);
$this->assertEquals(12, $paging['options']['limit']); $this->assertEquals(12, $paging['options']['limit']);
$Controller = new PaginatorTestController($this->request); $Controller = new PaginatorTestController($this->request);
@ -551,7 +551,7 @@ class PaginatorComponentTest extends CakeTestCase {
$this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id')); $this->assertEquals(array(2, 3), Hash::extract($result, '{n}.PaginatorControllerPost.id'));
$this->assertEquals( $this->assertEquals(
$Controller->PaginatorControllerPost->lastQueries[1]['conditions'], $Controller->PaginatorControllerPost->lastQueries[0]['conditions'],
array('PaginatorControllerPost.id > ' => '1') array('PaginatorControllerPost.id > ' => '1')
); );
$this->assertFalse(isset($Controller->params['paging']['PaginatorControllerPost']['options'][0])); $this->assertFalse(isset($Controller->params['paging']['PaginatorControllerPost']['options'][0]));

View file

@ -352,19 +352,6 @@ class RequestHandlerComponentTest extends CakeTestCase {
$this->assertEquals(true, $this->Controller->params['isAjax']); $this->assertEquals(true, $this->Controller->params['isAjax']);
} }
/**
* testAutoResponseType method
*
* @return void
*/
public function testAutoResponseType() {
$this->Controller->ext = '.thtml';
$this->Controller->request->params['ext'] = 'rss';
$this->RequestHandler->initialize($this->Controller);
$this->RequestHandler->startup($this->Controller);
$this->assertEquals('.ctp', $this->Controller->ext);
}
/** /**
* testAutoAjaxLayout method * testAutoAjaxLayout method
* *

Some files were not shown because too many files have changed in this diff Show more