Merge branch '2.6' into 2.7

This commit is contained in:
mark_story 2015-05-28 19:34:59 -04:00
commit d7d8b90986
18 changed files with 329 additions and 33 deletions

View file

@ -158,7 +158,7 @@ class SchemaShell extends AppShell {
}
if ($snapshot === true) {
$fileName = rtrim($this->params['file'], '.php');
$fileName = basename($this->params['file'], '.php');
$Folder = new Folder($this->Schema->path);
$result = $Folder->read();
@ -285,7 +285,7 @@ class SchemaShell extends AppShell {
'connection' => $this->params['connection'],
);
if (!empty($this->params['snapshot'])) {
$fileName = rtrim($this->Schema->file, '.php');
$fileName = basename($this->Schema->file, '.php');
$options['file'] = $fileName . '_' . $this->params['snapshot'] . '.php';
}

View file

@ -229,7 +229,7 @@ class RequestHandlerComponent extends Component {
*/
public function convertXml($xml) {
try {
$xml = Xml::build($xml);
$xml = Xml::build($xml, array('readFile' => false));
if (isset($xml->data)) {
return Xml::toArray($xml->data);
}

View file

@ -48,8 +48,13 @@ class CakePlugin {
*
* It is also possible to load multiple plugins at once. Examples:
*
* `CakePlugin::load(array('DebugKit', 'ApiGenerator'))` will load the DebugKit and ApiGenerator plugins
* `CakePlugin::load(array('DebugKit', 'ApiGenerator'), array('bootstrap' => true))` will load bootstrap file for both plugins
* `CakePlugin::load(array('DebugKit', 'ApiGenerator'))`
*
* will load the DebugKit and ApiGenerator plugins
*
* `CakePlugin::load(array('DebugKit', 'ApiGenerator'), array('bootstrap' => true))`
*
* will load bootstrap file for both plugins
*
* ```
* CakePlugin::load(array(
@ -107,13 +112,26 @@ class CakePlugin {
*
* ```
* CakePlugin::loadAll(array(
* array('bootstrap' => true),
* array('bootstrap' => 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 and will not look for any bootstrap script.
* the routes file and will not look for any bootstrap script. If you are loading
* many plugins that inconsistently support routes/bootstrap files, instead of detailing
* each plugin you can use the `ignoreMissing` option:
*
* ```
* CakePlugin::loadAll(array(
* 'ignoreMissing' => true,
* 'bootstrap' => true,
* 'routes' => true,
* ));
* ```
*
* The ignoreMissing option will do additional file_exists() calls but is simpler
* to use.
*
* @param array $options Options list. See CakePlugin::load() for valid options.
* @return void

View file

@ -2872,12 +2872,19 @@ class DboSource extends DataSource {
if (!empty($this->endQuote)) {
$end = preg_quote($this->endQuote);
}
// Remove quotes and requote all the Model.field names.
$conditions = str_replace(array($start, $end), '', $conditions);
$conditions = preg_replace_callback(
'/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_][a-z0-9\\-_]*\\.[a-z0-9_][a-z0-9_\\-]*)/i',
array(&$this, '_quoteMatchedField'),
$conditions
);
// Quote `table_name AS Alias`
$conditions = preg_replace(
'/(\s[a-z0-9\\-_.' . $start . $end . ']*' . $end . ')\s+AS\s+([a-z0-9\\-_]+)/i',
'\1 AS ' . $this->startQuote . '\2' . $this->endQuote,
$conditions
);
if ($conditions !== null) {
return $conditions;
}

View file

@ -68,6 +68,7 @@ class CakeResponse {
415 => 'Unsupported Media Type',
416 => 'Requested range not satisfiable',
417 => 'Expectation Failed',
429 => 'Too Many Requests',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
@ -1495,9 +1496,10 @@ class CakeResponse {
* @return bool
*/
protected function _clearBuffer() {
//@codingStandardsIgnoreStart
return @ob_end_clean();
//@codingStandardsIgnoreEnd
if (ob_get_length()) {
return ob_end_clean();
}
return true;
}
/**

View file

@ -7836,6 +7836,38 @@ class ModelReadTest extends BaseModelTest {
$this->assertEquals(4, $result);
}
/**
* Test virtualfields that contain subqueries get correctly
* quoted allowing reserved words to be used.
*
* @return void
*/
public function testVirtualFieldSubqueryReservedWords() {
$this->loadFixtures('User');
$user = ClassRegistry::init('User');
$user->cacheMethods = false;
$ds = $user->getDataSource();
$sub = $ds->buildStatement(
array(
'fields' => array('Table.user'),
'table' => $ds->fullTableName($user),
'alias' => 'Table',
'limit' => 1,
'conditions' => array(
"Table.id > 1"
)
),
$user
);
$user->virtualFields = array(
'sub_test' => $sub
);
$result = $user->find('first');
$this->assertNotEmpty($result);
}
/**
* testVirtualFieldsOrder()
*

View file

@ -404,7 +404,7 @@ class CakeResponseTest extends CakeTestCase {
public function testHttpCodes() {
$response = new CakeResponse();
$result = $response->httpCodes();
$this->assertEquals(40, count($result));
$this->assertEquals(41, count($result));
$result = $response->httpCodes(100);
$expected = array(100 => 'Continue');
@ -417,7 +417,7 @@ class CakeResponseTest extends CakeTestCase {
$result = $response->httpCodes($codes);
$this->assertTrue($result);
$this->assertEquals(42, count($response->httpCodes()));
$this->assertEquals(43, count($response->httpCodes()));
$result = $response->httpCodes(381);
$expected = array(381 => 'Unicorn Moved');
@ -426,7 +426,7 @@ class CakeResponseTest extends CakeTestCase {
$codes = array(404 => 'Sorry Bro');
$result = $response->httpCodes($codes);
$this->assertTrue($result);
$this->assertEquals(42, count($response->httpCodes()));
$this->assertEquals(43, count($response->httpCodes()));
$result = $response->httpCodes(404);
$expected = array(404 => 'Sorry Bro');

View file

@ -123,6 +123,7 @@ class InflectorTest extends CakeTestCase {
$this->assertEquals(Inflector::singularize('fungi'), 'fungus');
$this->assertEquals(Inflector::singularize('nuclei'), 'nucleus');
$this->assertEquals(Inflector::singularize('octopuses'), 'octopus');
$this->assertEquals(Inflector::singularize('octopuses'), 'octopus');
$this->assertEquals(Inflector::singularize('radii'), 'radius');
$this->assertEquals(Inflector::singularize('stimuli'), 'stimulus');
$this->assertEquals(Inflector::singularize('syllabi'), 'syllabus');
@ -178,6 +179,7 @@ class InflectorTest extends CakeTestCase {
$this->assertEquals(Inflector::singularize('metadata'), 'metadata');
$this->assertEquals(Inflector::singularize('files_metadata'), 'files_metadata');
$this->assertEquals(Inflector::singularize('sieves'), 'sieve');
$this->assertEquals(Inflector::singularize('blue_octopuses'), 'blue_octopus');
$this->assertEquals(Inflector::singularize(''), '');
}
@ -250,9 +252,35 @@ class InflectorTest extends CakeTestCase {
$this->assertEquals(Inflector::pluralize('files_metadata'), 'files_metadata');
$this->assertEquals(Inflector::pluralize('stadia'), 'stadia');
$this->assertEquals(Inflector::pluralize('sieve'), 'sieves');
$this->assertEquals(Inflector::pluralize('blue_octopus'), 'blue_octopuses');
$this->assertEquals(Inflector::pluralize(''), '');
}
/**
* testInflectingMultiWordIrregulars
*
* @return void
*/
public function testInflectingMultiWordIrregulars() {
// unset the default rules in order to avoid them possibly matching
// the words in case the irregular regex won't match, the tests
// should fail in that case
Inflector::rules('plural', array(
'rules' => array(),
));
Inflector::rules('singular', array(
'rules' => array(),
));
$this->assertEquals(Inflector::singularize('wisdom teeth'), 'wisdom tooth');
$this->assertEquals(Inflector::singularize('wisdom-teeth'), 'wisdom-tooth');
$this->assertEquals(Inflector::singularize('wisdom_teeth'), 'wisdom_tooth');
$this->assertEquals(Inflector::pluralize('sweet potato'), 'sweet potatoes');
$this->assertEquals(Inflector::pluralize('sweet-potato'), 'sweet-potatoes');
$this->assertEquals(Inflector::pluralize('sweet_potato'), 'sweet_potatoes');
}
/**
* testInflectorSlug method
*
@ -371,12 +399,14 @@ class InflectorTest extends CakeTestCase {
$this->assertSame(Inflector::underscore('testThing'), 'test_thing');
$this->assertSame(Inflector::underscore('TestThingExtra'), 'test_thing_extra');
$this->assertSame(Inflector::underscore('testThingExtra'), 'test_thing_extra');
$this->assertSame(Inflector::underscore('testThingExtrå'), 'test_thing_extrå');
// Identical checks test the cache code path.
$this->assertSame(Inflector::underscore('TestThing'), 'test_thing');
$this->assertSame(Inflector::underscore('testThing'), 'test_thing');
$this->assertSame(Inflector::underscore('TestThingExtra'), 'test_thing_extra');
$this->assertSame(Inflector::underscore('testThingExtra'), 'test_thing_extra');
$this->assertSame(Inflector::underscore('testThingExtrå'), 'test_thing_extrå');
// Test stupid values
$this->assertSame(Inflector::underscore(''), '');
@ -429,6 +459,8 @@ class InflectorTest extends CakeTestCase {
$this->assertEquals(Inflector::humanize('posts'), 'Posts');
$this->assertEquals(Inflector::humanize('posts_tags'), 'Posts Tags');
$this->assertEquals(Inflector::humanize('file_systems'), 'File Systems');
$this->assertEquals(Inflector::humanize('hello_wörld'), 'Hello Wörld');
$this->assertEquals(Inflector::humanize('福岡_city'), '福岡 City');
}
/**

View file

@ -167,6 +167,28 @@ class XmlTest extends CakeTestCase {
$this->assertNotRegExp('/encoding/', $obj->saveXML());
}
/**
* Test that the readFile option disables local file parsing.
*
* @expectedException XmlException
* @return void
*/
public function testBuildFromFileWhenDisabled() {
$xml = CAKE . 'Test' . DS . 'Fixture' . DS . 'sample.xml';
$obj = Xml::build($xml, array('readFile' => false));
}
/**
* Test that the readFile option disables local file parsing.
*
* @expectedException XmlException
* @return void
*/
public function testBuildFromUrlWhenDisabled() {
$xml = 'http://www.google.com';
$obj = Xml::build($xml, array('readFile' => false));
}
/**
* data provider function for testBuildInvalidData
*

View file

@ -3902,6 +3902,25 @@ class FormHelperTest extends CakeTestCase {
'/fieldset'
);
$this->assertTags($result, $expected);
$result = $this->Form->radio(
'Model.multibyte',
array('男性' => '男性')
);
$expected = array(
'input' => array(
'type' => 'hidden', 'name' => 'data[Model][multibyte]',
'id' => 'ModelMultibyte_', 'value' => '',
),
array('input' => array(
'type' => 'radio', 'name' => 'data[Model][multibyte]',
'id' => 'ModelMultibyte男性', 'value' => '男性')
),
array('label' => array('for' => 'ModelMultibyte男性')),
'男性',
'/label',
);
$this->assertTags($result, $expected);
}
/**

View file

@ -194,6 +194,31 @@ class JsonViewTest extends CakeTestCase {
$this->assertSame($expected, $output);
}
/**
* Test render with _jsonOptions setting.
*
* @return void
*/
public function testRenderWithoutViewJsonOptions() {
$this->skipIf(!version_compare(PHP_VERSION, '5.3.0', '>='), 'Needs PHP5.3+ for these constants to be tested');
$Request = new CakeRequest();
$Response = new CakeResponse();
$Controller = new Controller($Request, $Response);
// Test render with encode <, >, ', &, and " for RFC4627-compliant to be serialized.
$data = array('rfc4627_escape' => '<tag> \'quote\' "double-quote" &');
$serialize = 'rfc4627_escape';
$expected = json_encode('<tag> \'quote\' "double-quote" &', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
$Controller->set($data);
$Controller->set('_serialize', $serialize);
$Controller->set('_jsonOptions', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
$View = new JsonView($Controller);
$output = $View->render(false);
$this->assertSame($expected, $output);
}
/**
* Test that rendering with _serialize does not load helpers.
*

View file

@ -96,6 +96,76 @@ class XmlViewTest extends CakeTestCase {
$this->assertFalse(isset($View->Html), 'No helper loaded.');
}
/**
* Test that rendering with _serialize respects XML options.
*
* @return void
*/
public function testRenderSerializeWithOptions() {
$Request = new CakeRequest();
$Response = new CakeResponse();
$Controller = new Controller($Request, $Response);
$data = array(
'_serialize' => array('tags'),
'_xmlOptions' => array('format' => 'attributes'),
'tags' => array(
'tag' => array(
array(
'id' => '1',
'name' => 'defect'
),
array(
'id' => '2',
'name' => 'enhancement'
)
)
)
);
$Controller->set($data);
$Controller->viewClass = 'Xml';
$View = new XmlView($Controller);
$result = $View->render();
$expected = Xml::build(array('response' => array('tags' => $data['tags'])), $data['_xmlOptions'])->asXML();
$this->assertSame($expected, $result);
}
/**
* Test that rendering with _serialize can work with string setting.
*
* @return void
*/
public function testRenderSerializeWithString() {
$Request = new CakeRequest();
$Response = new CakeResponse();
$Controller = new Controller($Request, $Response);
$data = array(
'_serialize' => 'tags',
'_xmlOptions' => array('format' => 'attributes'),
'tags' => array(
'tags' => array(
'tag' => array(
array(
'id' => '1',
'name' => 'defect'
),
array(
'id' => '2',
'name' => 'enhancement'
)
)
)
)
);
$Controller->set($data);
$Controller->viewClass = 'Xml';
$View = new XmlView($Controller);
$result = $View->render();
$expected = Xml::build($data['tags'], $data['_xmlOptions'])->asXML();
$this->assertSame($expected, $result);
}
/**
* Test render with an array in _serialize
*

View file

@ -37,7 +37,7 @@ class CakeTestSuiteDispatcher {
'codeCoverage' => false,
'case' => null,
'core' => false,
'app' => true,
'app' => false,
'plugin' => null,
'output' => 'html',
'show' => 'groups',

View file

@ -386,8 +386,8 @@ class Inflector {
self::$_plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$_plural['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . self::$_plural['cacheIrregular'] . ')$/i', $word, $regs)) {
self::$_cache['pluralize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_plural['merged']['irregular'][strtolower($regs[2])], 1);
if (preg_match('/(.*(?:\\b|_))(' . self::$_plural['cacheIrregular'] . ')$/i', $word, $regs)) {
self::$_cache['pluralize'][$word] = $regs[1] . substr($regs[2], 0, 1) . substr(self::$_plural['merged']['irregular'][strtolower($regs[2])], 1);
return self::$_cache['pluralize'][$word];
}
@ -435,8 +435,8 @@ class Inflector {
self::$_singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$_singular['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . self::$_singular['cacheIrregular'] . ')$/i', $word, $regs)) {
self::$_cache['singularize'][$word] = $regs[1] . substr($word, 0, 1) . substr(self::$_singular['merged']['irregular'][strtolower($regs[2])], 1);
if (preg_match('/(.*(?:\\b|_))(' . self::$_singular['cacheIrregular'] . ')$/i', $word, $regs)) {
self::$_cache['singularize'][$word] = $regs[1] . substr($regs[2], 0, 1) . substr(self::$_singular['merged']['irregular'][strtolower($regs[2])], 1);
return self::$_cache['singularize'][$word];
}
@ -479,7 +479,8 @@ class Inflector {
*/
public static function underscore($camelCasedWord) {
if (!($result = self::_cache(__FUNCTION__, $camelCasedWord))) {
$result = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord));
$underscoredWord = preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $camelCasedWord);
$result = mb_strtolower($underscoredWord);
self::_cache(__FUNCTION__, $camelCasedWord, $result);
}
return $result;
@ -495,7 +496,12 @@ class Inflector {
*/
public static function humanize($lowerCaseAndUnderscoredWord) {
if (!($result = self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord))) {
$result = ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
$lowerCaseAndUnderscoredWord = self::underscore($lowerCaseAndUnderscoredWord);
$result = explode(' ', str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
foreach ($result as &$word) {
$word = mb_strtoupper(mb_substr($word, 0, 1)) . mb_substr($word, 1);
}
$result = implode(' ', $result);
self::_cache(__FUNCTION__, $lowerCaseAndUnderscoredWord, $result);
}
return $result;

View file

@ -77,6 +77,9 @@ class Xml {
* - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
* - `loadEntities` Defaults to false. Set to true to enable loading of `<!ENTITY` definitions. This
* is disabled by default for security reasons.
* - `readFile` Set to false to disable file reading. This is important to disable when
* putting user data into Xml::build(). If enabled local & remote files will be read if they exist.
* Defaults to true for backwards compatibility reasons.
* - If using array as input, you can pass `options` from Xml::fromArray.
*
* @param string|array $input XML string, a path to a file, a URL or an array
@ -91,6 +94,7 @@ class Xml {
$defaults = array(
'return' => 'simplexml',
'loadEntities' => false,
'readFile' => true
);
$options += $defaults;
@ -98,9 +102,9 @@ class Xml {
return self::fromArray((array)$input, $options);
} elseif (strpos($input, '<') !== false) {
return self::_loadXml($input, $options);
} elseif (file_exists($input)) {
} elseif ($options['readFile'] && file_exists($input)) {
return self::_loadXml(file_get_contents($input), $options);
} elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
} elseif ($options['readFile'] && strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
try {
$socket = new HttpSocket(array('request' => array('redirect' => 10)));
$response = $socket->get($input);
@ -156,7 +160,7 @@ class Xml {
*
* ### Options
*
* - `format` If create childs ('tags') or attributes ('attribute').
* - `format` If create childs ('tags') or attributes ('attributes').
* - `pretty` Returns formatted Xml when set to `true`. Defaults to `false`
* - `version` Version of XML document. Default is 1.0.
* - `encoding` Encoding of XML document. If null remove from XML header. Default is the some of application.
@ -180,7 +184,7 @@ class Xml {
*
* `<root><tag><id>1</id><value>defect</value>description</tag></root>`
*
* And calling `Xml::fromArray($value, 'attribute');` Will generate:
* And calling `Xml::fromArray($value, 'attributes');` Will generate:
*
* `<root><tag id="1" value="defect">description</tag></root>`
*
@ -229,7 +233,7 @@ class Xml {
* @param DOMDocument $dom Handler to DOMDocument
* @param DOMElement $node Handler to DOMElement (child)
* @param array &$data Array of data to append to the $node.
* @param string $format Either 'attribute' or 'tags'. This determines where nested keys go.
* @param string $format Either 'attributes' or 'tags'. This determines where nested keys go.
* @return void
* @throws XmlException
*/

View file

@ -145,9 +145,48 @@ class Helper extends Object {
* @var array
*/
protected $_minimizedAttributes = array(
'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
'autoplay', 'controls', 'loop', 'muted', 'required', 'novalidate', 'formnovalidate'
'allowfullscreen',
'async',
'autofocus',
'autoplay',
'checked',
'compact',
'controls',
'declare',
'default',
'defaultchecked',
'defaultmuted',
'defaultselected',
'defer',
'disabled',
'enabled',
'formnovalidate',
'hidden',
'indeterminate',
'inert',
'ismap',
'itemscope',
'loop',
'multiple',
'muted',
'nohref',
'noresize',
'noshade',
'novalidate',
'nowrap',
'open',
'pauseonexit',
'readonly',
'required',
'reversed',
'scoped',
'seamless',
'selected',
'sortable',
'spellcheck',
'truespeed',
'typemustmatch',
'visible'
);
/**

View file

@ -125,6 +125,10 @@ class JsonView extends View {
/**
* Serialize view vars
*
* ### Special parameters
* `_jsonOptions` You can set custom options for json_encode() this way,
* e.g. `JSON_HEX_TAG | JSON_HEX_APOS`.
*
* @param array $serialize The viewVars that need to be serialized
* @throws CakeException
* @return string The serialized data
@ -145,15 +149,24 @@ class JsonView extends View {
$data = isset($this->viewVars[$serialize]) ? $this->viewVars[$serialize] : null;
}
if (version_compare(PHP_VERSION, '5.4.0', '>=') && Configure::read('debug')) {
$json = json_encode($data, JSON_PRETTY_PRINT);
} else {
$json = json_encode($data);
$jsonOptions = 0;
if (isset($this->viewVars['_jsonOptions'])) {
if ($this->viewVars['_jsonOptions'] === false) {
$jsonOptions = 0;
} else {
$jsonOptions = $this->viewVars['_jsonOptions'];
}
}
if (version_compare(PHP_VERSION, '5.4.0', '>=') && Configure::read('debug')) {
$jsonOptions = $jsonOptions | JSON_PRETTY_PRINT;
}
$json = json_encode($data, $jsonOptions);
if (function_exists('json_last_error') && json_last_error() !== JSON_ERROR_NONE) {
throw new CakeException(json_last_error_msg());
} elseif ($json === false) {
}
if ($json === false) {
throw new CakeException('Failed to parse JSON');
}
return $json;

View file

@ -109,6 +109,10 @@ class XmlView extends View {
/**
* Serialize view vars.
*
* ### Special parameters
* `_xmlOptions` You can set an array of custom options for Xml::fromArray() this way, e.g.
* 'format' as 'attributes' instead of 'tags'.
*
* @param array $serialize The viewVars that need to be serialized.
* @return string The serialized data
*/
@ -131,6 +135,9 @@ class XmlView extends View {
}
$options = array();
if (isset($this->viewVars['_xmlOptions'])) {
$options = $this->viewVars['_xmlOptions'];
}
if (Configure::read('debug')) {
$options['pretty'] = true;
}