diff --git a/lib/Cake/Console/Command/SchemaShell.php b/lib/Cake/Console/Command/SchemaShell.php index 16abb600d..8589d1238 100644 --- a/lib/Cake/Console/Command/SchemaShell.php +++ b/lib/Cake/Console/Command/SchemaShell.php @@ -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'; } diff --git a/lib/Cake/Controller/Component/RequestHandlerComponent.php b/lib/Cake/Controller/Component/RequestHandlerComponent.php index 9d5581487..271b56773 100644 --- a/lib/Cake/Controller/Component/RequestHandlerComponent.php +++ b/lib/Cake/Controller/Component/RequestHandlerComponent.php @@ -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); } diff --git a/lib/Cake/Core/CakePlugin.php b/lib/Cake/Core/CakePlugin.php index 96ce894fd..5f30887f4 100644 --- a/lib/Cake/Core/CakePlugin.php +++ b/lib/Cake/Core/CakePlugin.php @@ -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 diff --git a/lib/Cake/Model/Datasource/DboSource.php b/lib/Cake/Model/Datasource/DboSource.php index d98c2b71f..4e95822b1 100644 --- a/lib/Cake/Model/Datasource/DboSource.php +++ b/lib/Cake/Model/Datasource/DboSource.php @@ -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; } diff --git a/lib/Cake/Network/CakeResponse.php b/lib/Cake/Network/CakeResponse.php index 6c8bc2a7e..d10814003 100644 --- a/lib/Cake/Network/CakeResponse.php +++ b/lib/Cake/Network/CakeResponse.php @@ -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; } /** diff --git a/lib/Cake/Test/Case/Model/ModelReadTest.php b/lib/Cake/Test/Case/Model/ModelReadTest.php index b684f06d0..255371253 100644 --- a/lib/Cake/Test/Case/Model/ModelReadTest.php +++ b/lib/Cake/Test/Case/Model/ModelReadTest.php @@ -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() * diff --git a/lib/Cake/Test/Case/Network/CakeResponseTest.php b/lib/Cake/Test/Case/Network/CakeResponseTest.php index a811b89e5..82a2f0419 100644 --- a/lib/Cake/Test/Case/Network/CakeResponseTest.php +++ b/lib/Cake/Test/Case/Network/CakeResponseTest.php @@ -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'); diff --git a/lib/Cake/Test/Case/Utility/InflectorTest.php b/lib/Cake/Test/Case/Utility/InflectorTest.php index caf085ad1..c1abaf15a 100644 --- a/lib/Cake/Test/Case/Utility/InflectorTest.php +++ b/lib/Cake/Test/Case/Utility/InflectorTest.php @@ -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'); } /** diff --git a/lib/Cake/Test/Case/Utility/XmlTest.php b/lib/Cake/Test/Case/Utility/XmlTest.php index 273801892..614a22a05 100644 --- a/lib/Cake/Test/Case/Utility/XmlTest.php +++ b/lib/Cake/Test/Case/Utility/XmlTest.php @@ -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 * diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php index 10cbbcc53..806297af7 100644 --- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php @@ -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); } /** diff --git a/lib/Cake/Test/Case/View/JsonViewTest.php b/lib/Cake/Test/Case/View/JsonViewTest.php index 4e4eb2574..aa5027ae1 100644 --- a/lib/Cake/Test/Case/View/JsonViewTest.php +++ b/lib/Cake/Test/Case/View/JsonViewTest.php @@ -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' => ' \'quote\' "double-quote" &'); + $serialize = 'rfc4627_escape'; + $expected = json_encode(' \'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. * diff --git a/lib/Cake/Test/Case/View/XmlViewTest.php b/lib/Cake/Test/Case/View/XmlViewTest.php index 89399beef..549813730 100644 --- a/lib/Cake/Test/Case/View/XmlViewTest.php +++ b/lib/Cake/Test/Case/View/XmlViewTest.php @@ -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 * diff --git a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php index 8b8b1e3ca..d61b232a4 100644 --- a/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php +++ b/lib/Cake/TestSuite/CakeTestSuiteDispatcher.php @@ -37,7 +37,7 @@ class CakeTestSuiteDispatcher { 'codeCoverage' => false, 'case' => null, 'core' => false, - 'app' => true, + 'app' => false, 'plugin' => null, 'output' => 'html', 'show' => 'groups', diff --git a/lib/Cake/Utility/Inflector.php b/lib/Cake/Utility/Inflector.php index 33e4ecf1c..61a63c2a1 100644 --- a/lib/Cake/Utility/Inflector.php +++ b/lib/Cake/Utility/Inflector.php @@ -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; diff --git a/lib/Cake/Utility/Xml.php b/lib/Cake/Utility/Xml.php index e3bb4282d..74d88494d 100644 --- a/lib/Cake/Utility/Xml.php +++ b/lib/Cake/Utility/Xml.php @@ -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 ` '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 { * * `1defectdescription` * - * And calling `Xml::fromArray($value, 'attribute');` Will generate: + * And calling `Xml::fromArray($value, 'attributes');` Will generate: * * `description` * @@ -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 */ diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index e91299e73..35aefff44 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -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' ); /** diff --git a/lib/Cake/View/JsonView.php b/lib/Cake/View/JsonView.php index 7953803de..26d7e0049 100644 --- a/lib/Cake/View/JsonView.php +++ b/lib/Cake/View/JsonView.php @@ -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; diff --git a/lib/Cake/View/XmlView.php b/lib/Cake/View/XmlView.php index 385543373..021854fc4 100644 --- a/lib/Cake/View/XmlView.php +++ b/lib/Cake/View/XmlView.php @@ -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; }