Merge branch '2.x' into 2.next

This commit is contained in:
mark_story 2016-12-13 22:48:44 -05:00
commit 837741db66
33 changed files with 575 additions and 56 deletions

View file

@ -6,6 +6,7 @@ php:
- 5.5
- 5.6
- 7.0
- 7.1
env:
- DB=mysql
@ -22,7 +23,7 @@ matrix:
- php: 5.4
env: DB=sqlite
- php: 5.4
- php: 7.0
env: PHPCS=1

View file

@ -41,8 +41,9 @@ class PagesController extends AppController {
* Displays a view
*
* @return void
* @throws ForbiddenException When a directory traversal attempt.
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
* or MissingViewException in debug mode.
*/
public function display() {
$path = func_get_args();
@ -51,6 +52,9 @@ class PagesController extends AppController {
if (!$count) {
return $this->redirect('/');
}
if (in_array('..', $path, true) || in_array('.', $path, true)) {
throw new ForbiddenException();
}
$page = $subpage = $title_for_layout = null;
if (!empty($path[0])) {

View file

@ -9,4 +9,4 @@ build.dir = build
dist.dir = dist
# Server
pirum.dir = /home/cakephp/www-live/pear.cakephp.org
pirum.dir = /var/lib/dokku/data/storage/pear

View file

@ -213,10 +213,10 @@
-->
<target name="distribute" depends="prepare" description="Upload pear packages to pear.cakephp.org">
<echo msg="Uploading tgz file to cakephp.org" />
<exec command="scp ${dist.dir}/${pear.package}.tgz cakephp@pear.cakephp.org:${pirum.dir}" dir="." checkreturn="true" />
<exec command="scp ${dist.dir}/${pear.package}.tgz root@new.cakephp.org:${pirum.dir}" dir="." checkreturn="true" />
<echo msg="Adding new release to pirum" />
<exec command="ssh cakephp@pear.cakephp.org pirum add ${pirum.dir} ${pirum.dir}/${pear.package}.tgz" checkreturn="true" />
<echo msg="Rebuilding pear.cakephp.org container" />
<exec command="ssh root@new.cakephp.org dokku ps:rebuild pear" checkreturn="true" />
</target>
<target name="codestyle" description="Check codestyle (human readable format)">

View file

@ -616,4 +616,18 @@ class Cache {
self::set(null, $config);
return $success;
}
/**
* Fetch the engine attached to a specific configuration name.
*
* @param string $config Optional string configuration name to get an engine for. Defaults to 'default'.
* @return null|CacheEngine Null if the engine has not been initialized or the engine.
*/
public static function engine($config = 'default') {
if (self::isInitialized($config)) {
return self::$_engines[$config];
}
return null;
}
}

View file

@ -54,6 +54,7 @@ class CommandListShell extends AppShell {
$this->out(" -working: " . rtrim(APP, DS));
$this->out(" -root: " . rtrim(ROOT, DS));
$this->out(" -core: " . rtrim(CORE_PATH, DS));
$this->out(" -webroot: " . rtrim(WWW_ROOT, DS));
$this->out("");
$this->out(__d('cake_console', "<info>Changing Paths:</info>"), 2);
$this->out(__d('cake_console', "Your working path should be the same as your application path. To change your path use the '-app' param."));

View file

@ -129,12 +129,19 @@ class ShellDispatcher {
define('APP', $this->params['working'] . DS);
}
if (!defined('WWW_ROOT')) {
define('WWW_ROOT', APP . $this->params['webroot'] . DS);
if (!$this->_isAbsolutePath($this->params['webroot'])) {
$webroot = realpath(APP . $this->params['webroot']);
} else {
$webroot = $this->params['webroot'];
}
define('WWW_ROOT', $webroot . DS);
}
if (!defined('TMP') && !is_dir(APP . 'tmp')) {
define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'Console' . DS . 'Templates' . DS . 'skel' . DS . 'tmp' . DS);
}
// $boot is used by Cake/bootstrap.php file
$boot = file_exists(ROOT . DS . APP_DIR . DS . 'Config' . DS . 'bootstrap.php');
require CORE_PATH . 'Cake' . DS . 'bootstrap.php';
if (!file_exists(APP . 'Config' . DS . 'core.php')) {
@ -305,25 +312,45 @@ class ShellDispatcher {
}
}
if ($params['app'][0] === '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) {
if ($this->_isAbsolutePath($params['app'])) {
$params['root'] = dirname($params['app']);
} elseif (strpos($params['app'], '/')) {
$params['root'] .= '/' . dirname($params['app']);
}
$isWindowsAppPath = $this->_isWindowsPath($params['app']);
$params['app'] = basename($params['app']);
$params['working'] = rtrim($params['root'], '/');
if (!$isWin || !preg_match('/^[A-Z]:$/i', $params['app'])) {
$params['working'] .= '/' . $params['app'];
}
if (!empty($matches[0]) || !empty($isWin)) {
if ($isWindowsAppPath || !empty($isWin)) {
$params = str_replace('/', '\\', $params);
}
$this->params = $params + $this->params;
}
/**
* Checks whether the given path is absolute or relative.
*
* @param string $path absolute or relative path.
* @return bool
*/
protected function _isAbsolutePath($path) {
return $path[0] === '/' || $this->_isWindowsPath($path);
}
/**
* Checks whether the given path is Window OS path.
*
* @param string $path absolute path.
* @return bool
*/
protected function _isWindowsPath($path) {
return preg_match('/([a-z])(:)/i', $path) == 1;
}
/**
* Parses out the paths from from the argv
*
@ -332,7 +359,7 @@ class ShellDispatcher {
*/
protected function _parsePaths($args) {
$parsed = array();
$keys = array('-working', '--working', '-app', '--app', '-root', '--root');
$keys = array('-working', '--working', '-app', '--app', '-root', '--root', '-webroot', '--webroot');
$args = (array)$args;
foreach ($keys as $key) {
while (($index = array_search($key, $args)) !== false) {

View file

@ -32,6 +32,7 @@ class PagesController extends AppController {
* Displays a view
*
* @return void
* @throws ForbiddenException When a directory traversal attempt.
* @throws NotFoundException When the view file could not be found
* or MissingViewException in debug mode.
*/
@ -42,6 +43,9 @@ class PagesController extends AppController {
if (!$count) {
return $this->redirect('/');
}
if (in_array('..', $path, true) || in_array('.', $path, true)) {
throw new ForbiddenException();
}
$page = $subpage = $title_for_layout = null;
if (!empty($path[0])) {

View file

@ -741,7 +741,7 @@ class AuthComponent extends Component {
$this->Session->delete('Auth.redirect');
if (Router::normalize($redir) === Router::normalize($this->loginAction)) {
$redir = $this->loginRedirect;
$redir = $this->loginRedirect ?: '/';
}
} elseif ($this->loginRedirect) {
$redir = $this->loginRedirect;

View file

@ -283,8 +283,11 @@ class CookieComponent extends Component {
return null;
}
if (!empty($names[1]) && is_array($this->_values[$this->name][$key])) {
return Hash::get($this->_values[$this->name][$key], $names[1]);
if (!empty($names[1])) {
if (is_array($this->_values[$this->name][$key])) {
return Hash::get($this->_values[$this->name][$key], $names[1]);
}
return null;
}
return $this->_values[$this->name][$key];
}
@ -336,7 +339,7 @@ class CookieComponent extends Component {
return;
}
$names = explode('.', $key, 2);
if (isset($this->_values[$this->name][$names[0]])) {
if (isset($this->_values[$this->name][$names[0]]) && is_array($this->_values[$this->name][$names[0]])) {
$this->_values[$this->name][$names[0]] = Hash::remove($this->_values[$this->name][$names[0]], $names[1]);
}
$this->_delete('[' . implode('][', $names) . ']');

View file

@ -269,6 +269,7 @@ class L10n {
'hi' => array('language' => 'Hindi', 'locale' => 'hin', 'localeFallback' => 'hin', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hr' => array('language' => 'Croatian', 'locale' => 'hrv', 'localeFallback' => 'hrv', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hu' => array('language' => 'Hungarian', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hu-hu' => array('language' => 'Hungarian (Hungary)', 'locale' => 'hun', 'localeFallback' => 'hun', 'charset' => 'utf-8', 'direction' => 'ltr'),
'hy' => array('language' => 'Armenian - Armenia', 'locale' => 'hye', 'localeFallback' => 'hye', 'charset' => 'utf-8', 'direction' => 'ltr'),
'id' => array('language' => 'Indonesian', 'locale' => 'ind', 'localeFallback' => 'ind', 'charset' => 'utf-8', 'direction' => 'ltr'),
'is' => array('language' => 'Icelandic', 'locale' => 'isl', 'localeFallback' => 'isl', 'charset' => 'utf-8', 'direction' => 'ltr'),
@ -286,6 +287,7 @@ class L10n {
'li' => array('language' => 'Limburgish', 'locale' => 'lim', 'localeFallback' => 'nld', 'charset' => 'utf-8', 'direction' => 'ltr'),
'lt' => array('language' => 'Lithuanian', 'locale' => 'lit', 'localeFallback' => 'lit', 'charset' => 'utf-8', 'direction' => 'ltr'),
'lv' => array('language' => 'Latvian', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'),
'lv-lv' => array('language' => 'Latvian (Latvia)', 'locale' => 'lav', 'localeFallback' => 'lav', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk' => array('language' => 'FYRO Macedonian', 'locale' => 'mkd', 'localeFallback' => 'mkd', 'charset' => 'utf-8', 'direction' => 'ltr'),
'mk-mk' => array('language' => 'Macedonian', 'locale' => 'mk_mk', 'localeFallback' => 'mkd', 'charset' => 'utf-8', 'direction' => 'ltr'),
'ms' => array('language' => 'Malaysian', 'locale' => 'msa', 'localeFallback' => 'msa', 'charset' => 'utf-8', 'direction' => 'ltr'),

View file

@ -39,13 +39,18 @@ class AclNode extends Model {
/**
* Constructor
*
* @param bool|int|string|array $id Set this ID for this model on startup,
* can also be an array of options, see above.
* @param string $table Name of database table to use.
* @param string $ds DataSource connection name.
*/
public function __construct() {
public function __construct($id = false, $table = null, $ds = null) {
$config = Configure::read('Acl.database');
if (isset($config)) {
$this->useDbConfig = $config;
}
parent::__construct();
parent::__construct($id, $table, $ds);
}
/**

View file

@ -112,11 +112,12 @@ class TreeBehavior extends ModelBehavior {
* @return void
*/
protected function _setChildrenLevel(Model $Model, $id) {
$settings = $Model->Behaviors->Tree->settings[$Model->alias];
$settings = $this->settings[$Model->alias];
$primaryKey = $Model->primaryKey;
$depths = array($id => (int)$Model->data[$Model->alias][$settings['level']]);
$children = $Model->children(
$children = $this->children(
$Model,
$id,
false,
array($primaryKey, $settings['parent'], $settings['level']),

View file

@ -51,17 +51,19 @@ class DboSource extends DataSource {
public $alias = 'AS ';
/**
* Caches result from query parsing operations. Cached results for both DboSource::name() and
* DboSource::conditions() will be stored here. Method caching uses `md5()`. If you have
* problems with collisions, set DboSource::$cacheMethods to false.
* Caches result from query parsing operations. Cached results for both DboSource::name() and DboSource::fields()
* will be stored here.
*
* Method caching uses `md5` (by default) to construct cache keys. If you have problems with collisions,
* try a different hashing algorithm by overriding DboSource::cacheMethodHasher or set DboSource::$cacheMethods to false.
*
* @var array
*/
public static $methodCache = array();
/**
* Whether or not to cache the results of DboSource::name() and DboSource::conditions()
* into the memory cache. Set to false to disable the use of the memory cache.
* Whether or not to cache the results of DboSource::name() and DboSource::fields() into the memory cache.
* Set to false to disable the use of the memory cache.
*
* @var bool
*/
@ -786,10 +788,72 @@ class DboSource extends DataSource {
if ($value === null) {
return (isset(static::$methodCache[$method][$key])) ? static::$methodCache[$method][$key] : null;
}
if (!$this->cacheMethodFilter($method, $key, $value)) {
return $value;
}
$this->_methodCacheChange = true;
return static::$methodCache[$method][$key] = $value;
}
/**
* Filters to apply to the results of `name` and `fields`. When the filter for a given method does not return `true`
* then the result is not added to the memory cache.
*
* Some examples:
*
* ```
* // For method fields, do not cache values that contain floats
* if ($method === 'fields') {
* $hasFloat = preg_grep('/(\d+)?\.\d+/', $value);
*
* return count($hasFloat) === 0;
* }
*
* return true;
* ```
*
* ```
* // For method name, do not cache values that have the name created
* if ($method === 'name') {
* return preg_match('/^`created`$/', $value) !== 1;
* }
*
* return true;
* ```
*
* ```
* // For method name, do not cache values that have the key 472551d38e1f8bbc78d7dfd28106166f
* if ($key === '472551d38e1f8bbc78d7dfd28106166f') {
* return false;
* }
*
* return true;
* ```
*
* @param string $method Name of the method being cached.
* @param string $key The key name for the cache operation.
* @param mixed $value The value to cache into memory.
* @return bool Whether or not to cache
*/
public function cacheMethodFilter($method, $key, $value) {
return true;
}
/**
* Hashes a given value.
*
* Method caching uses `md5` (by default) to construct cache keys. If you have problems with collisions,
* try a different hashing algorithm or set DboSource::$cacheMethods to false.
*
* @param string $value Value to hash
* @return string Hashed value
* @see http://php.net/manual/en/function.hash-algos.php
* @see http://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
*/
public function cacheMethodHasher($value) {
return md5($value);
}
/**
* Returns a quoted name of $data for use in an SQL statement.
* Strips fields out of SQL functions before quoting.
@ -815,7 +879,7 @@ class DboSource extends DataSource {
}
return $data;
}
$cacheKey = md5($this->startQuote . $data . $this->endQuote);
$cacheKey = $this->cacheMethodHasher($this->startQuote . $data . $this->endQuote);
if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) {
return $return;
}
@ -2533,7 +2597,7 @@ class DboSource extends DataSource {
$Model->schemaName,
$Model->table
);
$cacheKey = md5(serialize($cacheKey));
$cacheKey = $this->cacheMethodHasher(serialize($cacheKey));
if ($return = $this->cacheMethod(__FUNCTION__, $cacheKey)) {
return $return;
}

View file

@ -538,4 +538,65 @@ class CacheTest extends CakeTestCase {
$result = Cache::add('test_add_key', 'test data 2', 'default');
$this->assertFalse($result);
}
/**
* Test engine method.
*
* Success, default engine.
*
* @return void
*/
public function testEngineSuccess() {
$actual = Cache::engine();
$this->assertInstanceOf('CacheEngine', $actual);
$actual = Cache::engine('default');
$this->assertInstanceOf('CacheEngine', $actual);
}
/**
* Test engine method.
*
* Success, memcached engine.
*
* @return void
*/
public function testEngineSuccessMemcached() {
$this->skipIf(!class_exists('Memcached'), 'Memcached is not installed or configured properly.');
// @codingStandardsIgnoreStart
$socket = @fsockopen('127.0.0.1', 11211, $errno, $errstr, 1);
// @codingStandardsIgnoreEnd
$this->skipIf(!$socket, 'Memcached is not running.');
fclose($socket);
Cache::config('memcached', array(
'engine' => 'Memcached',
'prefix' => 'cake_',
'duration' => 3600
));
$actual = Cache::engine('memcached');
$this->assertInstanceOf('MemcachedEngine', $actual);
$this->assertTrue($actual->add('test_add_key', 'test data', 10));
$this->assertFalse($actual->add('test_add_key', 'test data', 10));
$this->assertTrue($actual->delete('test_add_key'));
}
/**
* Test engine method.
*
* Failure.
*
* @return void
*/
public function testEngineFailure() {
$actual = Cache::engine('some_config_that_does_not_exist');
$this->assertNull($actual);
Configure::write('Cache.disable', true);
$actual = Cache::engine();
$this->assertNull($actual);
}
}

View file

@ -137,9 +137,8 @@ class ShellDispatcherTest extends CakeTestCase {
*
* @return void
*/
public function testParseParams() {
public function testParseParamsAppWorkingAbsolute() {
$Dispatcher = new TestShellDispatcher();
$params = array(
'/cake/1.2.x.x/cake/console/cake.php',
'bake',
@ -156,7 +155,15 @@ class ShellDispatcherTest extends CakeTestCase {
);
$Dispatcher->parseParams($params);
$this->assertEquals($expected, $Dispatcher->params);
}
/**
* testParseParams method
*
* @return void
*/
public function testParseParamsNone() {
$Dispatcher = new TestShellDispatcher();
$params = array('cake.php');
$expected = array(
'app' => 'app',
@ -167,7 +174,15 @@ class ShellDispatcherTest extends CakeTestCase {
$Dispatcher->params = $Dispatcher->args = array();
$Dispatcher->parseParams($params);
$this->assertEquals($expected, $Dispatcher->params);
}
/**
* testParseParams method
*
* @return void
*/
public function testParseParamsApp() {
$Dispatcher = new TestShellDispatcher();
$params = array(
'cake.php',
'-app',
@ -182,7 +197,15 @@ class ShellDispatcherTest extends CakeTestCase {
$Dispatcher->params = $Dispatcher->args = array();
$Dispatcher->parseParams($params);
$this->assertEquals($expected, $Dispatcher->params);
}
/**
* testParseParams method
*
* @return void
*/
public function testParseParamsAppWorkingRelative() {
$Dispatcher = new TestShellDispatcher();
$params = array(
'./cake.php',
'bake',
@ -191,17 +214,24 @@ class ShellDispatcherTest extends CakeTestCase {
'-working',
'/cake/1.2.x.x/cake/console'
);
$expected = array(
'app' => 'new',
'webroot' => 'webroot',
'working' => str_replace('\\', DS, dirname(CAKE_CORE_INCLUDE_PATH) . DS . 'new'),
'root' => str_replace('\\', DS, dirname(CAKE_CORE_INCLUDE_PATH))
);
$Dispatcher->params = $Dispatcher->args = array();
$Dispatcher->parseParams($params);
$this->assertEquals($expected, $Dispatcher->params);
}
/**
* testParseParams method
*
* @return void
*/
public function testParseParams() {
$Dispatcher = new TestShellDispatcher();
$params = array(
'./console/cake.php',

View file

@ -48,14 +48,15 @@ class ControllerAuthorizeTest extends CakeTestCase {
* testControllerTypeError
*
* @expectedException PHPUnit_Framework_Error
* @throws PHPUnit_Framework_Error
* @return void
* @throws PHPUnit_Framework_Error
*/
public function testControllerTypeError() {
try {
$this->auth->controller(new StdClass());
} catch (Throwable $t) {
throw new PHPUnit_Framework_Error($t);
$this->fail('No exception thrown');
} catch (TypeError $e) {
throw new PHPUnit_Framework_Error('Raised an error', 100, __FILE__, __LINE__);
}
}

View file

@ -1652,6 +1652,20 @@ class AuthComponentTest extends CakeTestCase {
Router::reload();
}
/**
* Test that redirectUrl() returns '/' if loginRedirect is empty
* and Auth.redirect is the login page.
*
* @return void
*/
public function testRedirectUrlWithoutLoginRedirect() {
$this->Auth->loginRedirect = null;
$this->Auth->Session->write('Auth.redirect', '/users/login');
$this->Auth->request->addParams(Router::parse('/users/login'));
$result = $this->Auth->redirectUrl();
$this->assertEquals('/', $result);
}
/**
* test password hashing
*

View file

@ -153,6 +153,24 @@ class CookieComponentTest extends CakeTestCase {
$this->assertEquals($expected, $data);
}
/**
* test read operations on corrupted cookie data.
*
* @return void
*/
public function testReadCorruptedCookieData() {
$this->Cookie->type('aes');
$this->Cookie->key = sha1('some bad key');
$data = $this->_implode(array('name' => 'jill', 'age' => 24));
// Corrupt the cookie data by slicing some bytes off.
$_COOKIE['CakeTestCookie'] = array(
'BadData' => substr(Security::encrypt($data, $this->Cookie->key), 0, -5)
);
$this->assertFalse($this->Cookie->check('BadData.name'), 'Key does not exist');
$this->assertNull($this->Cookie->read('BadData.name'), 'Key does not exist');
}
/**
* testReadPlainCookieData
*
@ -169,6 +187,19 @@ class CookieComponentTest extends CakeTestCase {
$this->assertEquals($expected, $data);
}
/**
* test read array keys from string data.
*
* @return void
*/
public function testReadNestedDataFromStrings() {
$_COOKIE['CakeTestCookie'] = array(
'User' => 'bad data'
);
$this->assertFalse($this->Cookie->check('User.name'), 'No key');
$this->assertNull($this->Cookie->read('User.name'), 'No key');
}
/**
* test read() after switching the cookie name.
*
@ -207,6 +238,7 @@ class CookieComponentTest extends CakeTestCase {
* @return void
*/
public function testWriteWithFalseyValue() {
$this->skipIf(!extension_loaded('mcrypt'), 'No Mcrypt, skipping.');
$this->Cookie->type('aes');
$this->Cookie->key = 'qSI232qs*&sXOw!adre@34SAv!@*(XSL#$%)asGb$@11~_+!@#HKis~#^';
@ -451,6 +483,25 @@ class CookieComponentTest extends CakeTestCase {
$this->assertNull($data);
}
/**
* test delete() on corrupted/truncated cookie data.
*
* @return void
*/
public function testDeleteCorruptedCookieData() {
$this->Cookie->type('aes');
$this->Cookie->key = sha1('some bad key');
$data = $this->_implode(array('name' => 'jill', 'age' => 24));
// Corrupt the cookie data by slicing some bytes off.
$_COOKIE['CakeTestCookie'] = array(
'BadData' => substr(Security::encrypt($data, $this->Cookie->key), 0, -5)
);
$this->assertNull($this->Cookie->delete('BadData.name'));
$this->assertNull($this->Cookie->read('BadData.name'));
}
/**
* testReadingCookieArray
*

View file

@ -75,4 +75,21 @@ class PagesControllerTest extends CakeTestCase {
$Pages = new PagesController(new CakeRequest(null, false), new CakeResponse());
$Pages->display('non_existing_page');
}
/**
* Test directory traversal protection
*
* @expectedException ForbiddenException
* @expectedExceptionCode 403
* @return void
*/
public function testDirectoryTraversalProtection() {
App::build(array(
'View' => array(
CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS
)
));
$Pages = new PagesController(new CakeRequest(null, false), new CakeResponse());
$Pages->display('..', 'Posts', 'index');
}
}

View file

@ -450,16 +450,16 @@ class ConfigureTest extends CakeTestCase {
* test reader() throwing exceptions on missing interface.
*
* @expectedException PHPUnit_Framework_Error
* @throws PHPUnit_Framework_Error
* @return void
* @throws PHPUnit_Framework_Error
*/
public function testReaderExceptionOnIncorrectClass() {
$reader = new StdClass();
try {
Configure::config('test', $reader);
} catch (Throwable $t) {
throw new PHPUnit_Framework_Error($t);
} catch (TypeError $e) {
throw new PHPUnit_Framework_Error('Raised an error', 100, __FILE__, __LINE__);
}
}

View file

@ -2909,14 +2909,15 @@ SQL;
* testDropSchemaNoSchema method
*
* @expectedException PHPUnit_Framework_Error
* @throws PHPUnit_Framework_Error
* @return void
* @throws PHPUnit_Framework_Error
*/
public function testDropSchemaNoSchema() {
try {
$this->Dbo->dropSchema(null);
} catch (Throwable $t) {
throw new PHPUnit_Framework_Error($t);
$this->fail('No exception');
} catch (TypeError $e) {
throw new PHPUnit_Framework_Error('Raised an error', 100, __FILE__, __LINE__);
}
}

View file

@ -109,6 +109,52 @@ class DboSecondTestSource extends DboSource {
}
/**
* DboThirdTestSource
*
* @package Cake.Test.Case.Model.Datasource
*/
class DboThirdTestSource extends DboSource {
public function connect($config = array()) {
$this->connected = true;
}
public function cacheMethodHasher($value) {
return hash('sha1', $value);
}
}
/**
* DboFourthTestSource
*
* @package Cake.Test.Case.Model.Datasource
*/
class DboFourthTestSource extends DboSource {
public function connect($config = array()) {
$this->connected = true;
}
public function cacheMethodFilter($method, $key, $value) {
if ($method === 'name') {
if ($value === '`menus`') {
return false;
} elseif ($key === '1fca740733997f1ebbedacfc7678592a') {
return false;
}
} elseif ($method === 'fields') {
$endsWithName = preg_grep('/`name`$/', $value);
return count($endsWithName) === 0;
}
return true;
}
}
/**
* DboSourceTest class
*
@ -737,6 +783,106 @@ class DboSourceTest extends CakeTestCase {
$this->assertNull($result);
}
/**
* Test that cacheMethodFilter does not filter by default.
*
* @return void
*/
public function testCacheMethodFilter() {
$method = 'name';
$key = '49d9207adfce6df1dd3ee8c30c434414';
$value = '`menus`';
$actual = $this->testDb->cacheMethodFilter($method, $key, $value);
$this->assertTrue($actual);
$method = 'fields';
$key = '2b57253ab1fffb3e95fa4f95299220b1';
$value = array("`Menu`.`id`", "`Menu`.`name`");
$actual = $this->testDb->cacheMethodFilter($method, $key, $value);
$this->assertTrue($actual);
$method = 'non-existing';
$key = '';
$value = '``';
$actual = $this->testDb->cacheMethodFilter($method, $key, $value);
$this->assertTrue($actual);
}
/**
* Test that cacheMethodFilter can be overridden to do actual filtering.
*
* @return void
*/
public function testCacheMethodFilterOverridden() {
$testDb = new DboFourthTestSource();
$method = 'name';
$key = '49d9207adfce6df1dd3ee8c30c434414';
$value = '`menus`';
$actual = $testDb->cacheMethodFilter($method, $key, $value);
$this->assertFalse($actual);
$method = 'name';
$key = '1fca740733997f1ebbedacfc7678592a';
$value = '`Menu`.`id`';
$actual = $testDb->cacheMethodFilter($method, $key, $value);
$this->assertFalse($actual);
$method = 'fields';
$key = '2b57253ab1fffb3e95fa4f95299220b1';
$value = array("`Menu`.`id`", "`Menu`.`name`");
$actual = $testDb->cacheMethodFilter($method, $key, $value);
$this->assertFalse($actual);
$method = 'name';
$key = 'd2bc458620afb092c61ab4383b7475e0';
$value = '`Menu`';
$actual = $testDb->cacheMethodFilter($method, $key, $value);
$this->assertTrue($actual);
$method = 'non-existing';
$key = '';
$value = '``';
$actual = $testDb->cacheMethodFilter($method, $key, $value);
$this->assertTrue($actual);
}
/**
* Test that cacheMethodHasher uses md5 by default.
*
* @return void
*/
public function testCacheMethodHasher() {
$name = 'Model.fieldlbqndkezcoapfgirmjsh';
$actual = $this->testDb->cacheMethodHasher($name);
$expected = '4a45dc9ed52f98c393d04ac424ee5078';
$this->assertEquals($expected, $actual);
}
/**
* Test that cacheMethodHasher can be overridden to use a different hashing algorithm.
*
* @return void
*/
public function testCacheMethodHasherOverridden() {
$testDb = new DboThirdTestSource();
$name = 'Model.fieldlbqndkezcoapfgirmjsh';
$actual = $testDb->cacheMethodHasher($name);
$expected = 'beb8b6469359285b7c2865dce0ef743feb16cb71';
$this->assertEquals($expected, $actual);
}
/**
* Test that rare collisions do not happen with method caching
*

View file

@ -2224,14 +2224,15 @@ class ModelValidationTest extends BaseModelTest {
* Test that type hint exception is thrown
*
* @expectedException PHPUnit_Framework_Error
* @throws PHPUnit_Framework_Error
* @return void
* @throws PHPUnit_Framework_Error
*/
public function testValidatorTypehintException() {
try {
new ModelValidator('asdasds');
} catch (Throwable $t) {
throw new PHPUnit_Framework_Error($t);
$this->fail('No exeption raised');
} catch (TypeError $e) {
throw new PHPUnit_Framework_Error('Raised an error', 100, __FILE__, __LINE__);
}
}

View file

@ -147,7 +147,7 @@ class ClassRegistryTest extends CakeTestCase {
$this->assertSame($Tag, $TagCopy);
$NewTag = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
$this->assertInstanceOf('RegisterArticleTag', $Tag);
$this->assertInstanceOf('RegisterArticleTag', $NewTag);
$NewTagCopy = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
@ -182,6 +182,35 @@ class ClassRegistryTest extends CakeTestCase {
$this->assertEquals('ParentCategory', $ParentCategory->alias);
}
/**
* Test that init() can make models with alias set properly
*
* @return void
*/
public function testAddModelWithAlias() {
$tag = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'NewTag'));
$this->assertInstanceOf('RegisterArticleTag', $tag);
$this->assertSame('NewTag', $tag->alias);
$this->assertSame('RegisterArticleTag', $tag->name);
$newTag = ClassRegistry::init(array('class' => 'RegisterArticleTag', 'alias' => 'OtherTag'));
$this->assertInstanceOf('RegisterArticleTag', $tag);
$this->assertSame('OtherTag', $newTag->alias);
$this->assertSame('RegisterArticleTag', $newTag->name);
}
/**
* Test that init() can make the Aco models with alias set properly
*
* @return void
*/
public function testAddModelWithAliasAco() {
$aco = ClassRegistry::init(array('class' => 'Aco', 'alias' => 'CustomAco'));
$this->assertInstanceOf('Aco', $aco);
$this->assertSame('Aco', $aco->name);
$this->assertSame('CustomAco', $aco->alias);
}
/**
* testClassRegistryFlush method
*

View file

@ -155,6 +155,24 @@ class DebuggerTest extends CakeTestCase {
$this->assertContains('$wrong = &#039;&#039;', $result[3], 'Context should be HTML escaped.');
}
/**
* test encodes error messages
*
* @return void
*/
public function testOutputEncodeDescription() {
set_error_handler('Debugger::showError');
$this->_restoreError = true;
ob_start();
$a = array();
$b = $a['<script>alert(1)</script>'];
$result = ob_get_clean();
$this->assertNotContains('<script>alert(1)', $result);
$this->assertContains('&lt;script&gt;alert(1)', $result);
}
/**
* Tests that changes in output formats using Debugger::output() change the templates used.
*

View file

@ -328,6 +328,7 @@ class SecurityTest extends CakeTestCase {
* @return void
*/
public function testEncryptDecrypt() {
$this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed');
$txt = 'The quick brown fox';
$key = 'This key is longer than 32 bytes long.';
$result = Security::encrypt($txt, $key);
@ -342,6 +343,7 @@ class SecurityTest extends CakeTestCase {
* @return void
*/
public function testDecryptKeyFailure() {
$this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed');
$txt = 'The quick brown fox';
$key = 'This key is longer than 32 bytes long.';
Security::encrypt($txt, $key);
@ -356,6 +358,7 @@ class SecurityTest extends CakeTestCase {
* @return void
*/
public function testDecryptHmacFailure() {
$this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed');
$txt = 'The quick brown fox';
$key = 'This key is quite long and works well.';
$salt = 'this is a delicious salt!';
@ -372,6 +375,7 @@ class SecurityTest extends CakeTestCase {
* @return void
*/
public function testDecryptHmacSaltFailure() {
$this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed');
$txt = 'The quick brown fox';
$key = 'This key is quite long and works well.';
$salt = 'this is a delicious salt!';
@ -400,6 +404,7 @@ class SecurityTest extends CakeTestCase {
* @return void
*/
public function testEncryptDecryptFalseyData() {
$this->skipIf(!extension_loaded('mcrypt'), 'This test requires mcrypt to be installed');
$key = 'This is a key that is long enough to be ok.';
$result = Security::encrypt('', $key);

View file

@ -395,7 +395,15 @@ XML;
$obj = Xml::fromArray($xml, 'attributes');
$xmlText = '<' . '?xml version="1.0" encoding="UTF-8"?><tags><tag id="1">defect</tag></tags>';
$this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
}
/**
* Test fromArray() with zero values.
*
* @return void
*/
public function testFromArrayZeroValue()
{
$xml = array(
'tag' => array(
'@' => 0,
@ -406,6 +414,16 @@ XML;
$xmlText = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<tag test="A test">0</tag>
XML;
$this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
$xml = array(
'tag' => array('0')
);
$obj = Xml::fromArray($xml);
$xmlText = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<tag>0</tag>
XML;
$this->assertXmlStringEqualsXmlString($xmlText, $obj->asXML());
}

View file

@ -544,7 +544,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $result
* @param mixed $expected
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertEqual($result, $expected, $message = '') {
@ -557,7 +557,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $result
* @param mixed $expected
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertNotEqual($result, $expected, $message = '') {
@ -570,7 +570,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $pattern a regular expression
* @param string $string the text to be matched
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertPattern($pattern, $string, $message = '') {
@ -583,7 +583,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $actual
* @param mixed $expected
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertIdentical($actual, $expected, $message = '') {
@ -596,7 +596,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $actual
* @param mixed $expected
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertNotIdentical($actual, $expected, $message = '') {
@ -609,7 +609,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $pattern a regular expression
* @param string $string the text to be matched
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertNoPattern($pattern, $string, $message = '') {
@ -619,7 +619,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
/**
* assert no errors
*
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected function assertNoErrors() {
@ -630,7 +630,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
*
* @param mixed $expected the name of the Exception or error
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected function expectError($expected = false, $message = '') {
@ -658,7 +658,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param mixed $first
* @param mixed $second
* @param string $message the text to display if the assertion is not correct
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertReference(&$first, &$second, $message = '') {
@ -671,7 +671,7 @@ abstract class CakeTestCase extends PHPUnit_Framework_TestCase {
* @param string $object
* @param string $type
* @param string $message
* @deprecated 3.0.0 This is a compatiblity wrapper for 1.x. It will be removed in 3.0
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
protected static function assertIsA($object, $type, $message = '') {

View file

@ -774,6 +774,7 @@ class Debugger {
if (!empty($tpl['escapeContext'])) {
$context = h($context);
$data['description'] = h($data['description']);
}
$infoData = compact('code', 'context', 'trace');

View file

@ -171,7 +171,7 @@ class Security {
/**
* Get random bytes from a secure source.
*
* This method will fall back to an insecure source an trigger a warning
* This method will fall back to an insecure source and trigger a warning,
* if it cannot find a secure source of random data.
*
* @param int $length The number of bytes you want.

View file

@ -312,7 +312,7 @@ class Xml {
$childNS = $value['xmlns:'];
unset($value['xmlns:']);
}
} elseif (!empty($value) || $value === 0) {
} elseif (!empty($value) || $value === 0 || $value === '0') {
$childValue = (string)$value;
}

View file

@ -564,7 +564,7 @@ class View extends CakeObject {
$type = $response->mapType($response->type());
if (Configure::read('debug') > 0 && $type === 'html') {
echo "<!-- Cached Render Time: " . round(microtime(true) - $timeStart, 4) . "s -->";
echo "<!-- Cached Render Time: " . round(microtime(true) - (int)$timeStart, 4) . "s -->";
}
$out = ob_get_clean();