diff --git a/cake/basics.php b/cake/basics.php
index 787aaf0c6..ca262cb0a 100644
--- a/cake/basics.php
+++ b/cake/basics.php
@@ -143,13 +143,18 @@ if (!function_exists('sortByKey')) {
* Convenience method for htmlspecialchars.
*
* @param string $text Text to wrap through htmlspecialchars
+ * @param boolean $double Encode existing html entities
* @param string $charset Character set to use when escaping. Defaults to config value in 'App.encoding' or 'UTF-8'
* @return string Wrapped text
* @link http://book.cakephp.org/view/1132/h
*/
- function h($text, $charset = null) {
+ function h($text, $double = true, $charset = null) {
if (is_array($text)) {
- return array_map('h', $text);
+ $texts = array();
+ foreach ($text as $t) {
+ $texts[] = h($t, $double, $charset);
+ }
+ return $texts;
}
static $defaultCharset = false;
@@ -159,10 +164,13 @@ if (!function_exists('sortByKey')) {
$defaultCharset = 'UTF-8';
}
}
+ if (is_string($double)) {
+ $charset = $double;
+ }
if ($charset) {
- return htmlspecialchars($text, ENT_QUOTES, $charset);
+ return htmlspecialchars($text, ENT_QUOTES, $charset, $double);
} else {
- return htmlspecialchars($text, ENT_QUOTES, $defaultCharset);
+ return htmlspecialchars($text, ENT_QUOTES, $defaultCharset, $double);
}
}
diff --git a/cake/console/libs/acl.php b/cake/console/libs/acl.php
index c2b419f9d..99f117ad2 100644
--- a/cake/console/libs/acl.php
+++ b/cake/console/libs/acl.php
@@ -21,7 +21,8 @@ App::import('Component', 'Acl');
App::import('Model', 'DbAcl');
/**
- * Shell for ACL management.
+ * Shell for ACL management. This console is known to have issues with zend.ze1_compatibility_mode
+ * being enabled. Be sure to turn it off when using this shell.
*
* @package cake
* @subpackage cake.cake.console.libs
diff --git a/cake/libs/cache/memcache.php b/cake/libs/cache/memcache.php
index 0508df127..7314fca0b 100644
--- a/cake/libs/cache/memcache.php
+++ b/cake/libs/cache/memcache.php
@@ -80,12 +80,7 @@ class MemcacheEngine extends CacheEngine {
$return = false;
$this->__Memcache =& new Memcache();
foreach ($this->settings['servers'] as $server) {
- $parts = explode(':', $server);
- $host = $parts[0];
- $port = 11211;
- if (isset($parts[1])) {
- $port = $parts[1];
- }
+ list($host, $port) = $this->_parseServerString($server);
if ($this->__Memcache->addServer($host, $port)) {
$return = true;
}
@@ -95,6 +90,31 @@ class MemcacheEngine extends CacheEngine {
return true;
}
+/**
+ * Parses the server address into the host/port. Handles both IPv6 and IPv4
+ * addresses
+ *
+ * @param string $server The server address string.
+ * @return array Array containing host, port
+ */
+ function _parseServerString($server) {
+ 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, $port);
+ }
+
/**
* Write data for key into cache. When using memcache as your cache engine
* remember that the Memcache pecl extension does not support cache expiry times greater
diff --git a/cake/libs/controller/components/request_handler.php b/cake/libs/controller/components/request_handler.php
index 2f31eb5df..d8a50c3e4 100644
--- a/cake/libs/controller/components/request_handler.php
+++ b/cake/libs/controller/components/request_handler.php
@@ -172,15 +172,15 @@ class RequestHandlerComponent extends Component {
}
if ($this->requestedWith('xml')) {
- if (!class_exists('XmlNode')) {
+ if (!class_exists('Xml')) {
App::import('Core', 'Xml');
}
- $xml = new Xml(trim(file_get_contents('php://input')));
+ $xml = Xml::build(trim(file_get_contents('php://input')));
- if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
- $controller->data = $dataNode->toArray();
+ if (isset($xml->data)) {
+ $controller->data = Xml::toArray($xml->data);
} else {
- $controller->data = $xml->toArray();
+ $controller->data = Xml::toArray($xml);
}
}
}
diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php
index 7d1615fd4..c63b8ffda 100644
--- a/cake/libs/controller/components/security.php
+++ b/cake/libs/controller/components/security.php
@@ -389,7 +389,7 @@ class SecurityComponent extends Component {
$keys = array();
$match = array();
$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
- preg_match_all('@(\w+)=([\'"]?)([a-zA-Z0-9=./\_-]+)\2@', $digest, $match, PREG_SET_ORDER);
+ preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
foreach ($match as $i) {
$keys[$i[1]] = $i[3];
diff --git a/cake/libs/file.php b/cake/libs/file.php
index 91a0cca5e..fbd305082 100644
--- a/cake/libs/file.php
+++ b/cake/libs/file.php
@@ -98,7 +98,7 @@ class File {
$this->name = basename($path);
}
$this->pwd();
- !$this->exists() && $create && $this->safe($path) && $this->create();
+ $create && !$this->exists() && $this->safe($path) && $this->create();
}
/**
diff --git a/cake/libs/model/cake_schema.php b/cake/libs/model/cake_schema.php
index 1ac235536..961fc0eb5 100644
--- a/cake/libs/model/cake_schema.php
+++ b/cake/libs/model/cake_schema.php
@@ -232,13 +232,21 @@ class CakeSchema extends Object {
if (is_array($models)) {
foreach ($models as $model) {
+ $importModel = $model;
if (isset($this->plugin)) {
- $model = $this->plugin . '.' . $model;
+ $importModel = $this->plugin . '.' . $model;
}
- $Object = ClassRegistry::init(array('class' => $model, 'ds' => null));
+ if (!App::import('Model', $importModel)) {
+ continue;
+ }
+ $vars = get_class_vars($model);
+ if (empty($vars['useDbConfig']) || $vars['useDbConfig'] != $connection) {
+ continue;
+ }
+
+ $Object = ClassRegistry::init(array('class' => $model, 'ds' => $connection));
if (is_object($Object) && $Object->useTable !== false) {
- $Object->setDataSource($connection);
$table = $db->fullTableName($Object, false);
if (in_array($table, $currentTables)) {
$key = array_search($table, $currentTables);
diff --git a/cake/libs/model/datasources/dbo_source.php b/cake/libs/model/datasources/dbo_source.php
index 2ea57ee62..cccc1b8ba 100755
--- a/cake/libs/model/datasources/dbo_source.php
+++ b/cake/libs/model/datasources/dbo_source.php
@@ -151,7 +151,7 @@ class DboSource extends DataSource {
* @param array $config An array defining the new configuration settings
* @return boolean True on success, false on failure
*/
- public function reconnect($config = null) {
+ function reconnect($config = array()) {
$this->disconnect();
$this->setConfig($config);
$this->_sources = null;
diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php
index 22c186219..823f8d2d6 100644
--- a/cake/libs/model/model.php
+++ b/cake/libs/model/model.php
@@ -1687,6 +1687,7 @@ class Model extends Object {
}
}
}
+
if (!$this->__save($data, $options)) {
$validationErrors[$this->alias] = $this->validationErrors;
$validates = false;
@@ -1757,7 +1758,6 @@ class Model extends Object {
case ($options['validate'] === 'first'):
$options['validate'] = true;
$return = array();
- continue;
break;
default:
if ($options['atomic']) {
@@ -1770,6 +1770,10 @@ class Model extends Object {
return $return;
break;
}
+ if ($options['atomic'] && !$validates) {
+ $db->rollback($this);
+ return false;
+ }
}
return $return;
}
@@ -1821,14 +1825,15 @@ class Model extends Object {
}
$id = $this->id;
- if ($this->exists() && $this->beforeDelete($cascade)) {
- $db = $this->getDataSource();
+ if ($this->beforeDelete($cascade)) {
$filters = $this->Behaviors->trigger($this, 'beforeDelete', array($cascade), array(
'break' => true, 'breakOn' => false
));
- if (!$filters) {
+ if (!$filters || !$this->exists()) {
return false;
}
+ $db =& ConnectionManager::getDataSource($this->useDbConfig);
+
$this->_deleteDependent($id, $cascade);
$this->_deleteLinks($id);
$this->id = $id;
diff --git a/cake/libs/sanitize.php b/cake/libs/sanitize.php
index c4de9bd28..34ed32e79 100644
--- a/cake/libs/sanitize.php
+++ b/cake/libs/sanitize.php
@@ -85,6 +85,7 @@ class Sanitize {
* - remove (boolean) if true strips all HTML tags before encoding
* - charset (string) the charset used to encode the string
* - quotes (int) see http://php.net/manual/en/function.htmlentities.php
+ * - double (boolean) doube encode html entities
*
* @param string $string String from where to strip tags
* @param array $options Array of options to use.
@@ -101,7 +102,8 @@ class Sanitize {
$default = array(
'remove' => false,
'charset' => $defaultCharset,
- 'quotes' => ENT_QUOTES
+ 'quotes' => ENT_QUOTES,
+ 'double' => true
);
$options = array_merge($default, $options);
@@ -110,7 +112,7 @@ class Sanitize {
$string = strip_tags($string);
}
- return htmlentities($string, $options['quotes'], $options['charset']);
+ return htmlentities($string, $options['quotes'], $options['charset'], $options['double']);
}
/**
diff --git a/cake/libs/set.php b/cake/libs/set.php
index 05afbf06b..cc7f53e6a 100644
--- a/cake/libs/set.php
+++ b/cake/libs/set.php
@@ -368,11 +368,11 @@ class Set {
$options = array_merge(array('flatten' => true), $options);
if (!isset($contexts[0])) {
$current = current($data);
- if ((is_array($current) && count($data) <= 1) || !is_array($current) || !Set::numeric(array_keys($data))) {
+ if ((is_array($current) && count($data) < 1) || !is_array($current) || !Set::numeric(array_keys($data))) {
$contexts = array($data);
}
}
- $tokens = array_slice(preg_split('/(?toArray();
- return $out;
+ if ($object instanceof SimpleXMLElement) {
+ return Xml::toArray($object);
} else if (is_object($object)) {
$keys = get_object_vars($object);
if (isset($keys['_name_'])) {
diff --git a/cake/libs/view/helpers/form.php b/cake/libs/view/helpers/form.php
index 002cbb4b8..43a95fe7c 100644
--- a/cake/libs/view/helpers/form.php
+++ b/cake/libs/view/helpers/form.php
@@ -254,12 +254,14 @@ class FormHelper extends AppHelper {
'plugin' => $this->plugin,
'controller' => $this->_View->viewPath,
'action' => $options['action'],
- 0 => $id
);
if (!empty($options['action']) && !isset($options['id'])) {
$options['id'] = $this->domId($options['action'] . 'Form');
}
$options['action'] = array_merge($actionDefaults, (array)$options['url']);
+ if (empty($options['action'][0])) {
+ $options['action'][0] = $id;
+ }
} elseif (is_string($options['url'])) {
$options['action'] = $options['url'];
}
@@ -582,7 +584,7 @@ class FormHelper extends AppHelper {
*
* In addition to fields control, inputs() allows you to use a few additional options.
*
- * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
+ * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
* the classname for the fieldset element.
* - `legend` Set to false to disable the legend for the generated input set. Or supply a string
* to customize the legend text.
@@ -1078,7 +1080,7 @@ class FormHelper extends AppHelper {
array('name', 'type', 'id'), '', ' '
);
$tagName = Inflector::camelize(
- $attributes['id'] . '_' . Inflector::underscore($optValue)
+ $attributes['id'] . '_' . Inflector::slug($optValue)
);
if ($label) {
@@ -1766,7 +1768,7 @@ class FormHelper extends AppHelper {
* - `separator` The contents of the string between select elements. Defaults to '-'
* - `empty` - If true, the empty select option is shown. If a string,
* that string is displayed as the empty element.
- * - `value` | `default` The default value to be used by the input. A value in `$this->data`
+ * - `value` | `default` The default value to be used by the input. A value in `$this->data`
* matching the field name will override this value. If no default is provided `time()` will be used.
*
* @param string $fieldName Prefix name for the SELECT element
@@ -1986,6 +1988,7 @@ class FormHelper extends AppHelper {
));
if (!empty($name)) {
+ $name = $attributes['escape'] ? h($name) : $name;
if ($attributes['style'] === 'checkbox') {
$select[] = sprintf($this->Html->tags['fieldsetstart'], $name);
} else {
@@ -2019,7 +2022,7 @@ class FormHelper extends AppHelper {
$htmlOptions['value'] = $name;
$tagName = Inflector::camelize(
- $this->model() . '_' . $this->field().'_'.Inflector::underscore($name)
+ $this->model() . '_' . $this->field().'_'.Inflector::slug($name)
);
$htmlOptions['id'] = $tagName;
$label = array('for' => $tagName);
diff --git a/cake/libs/view/helpers/jquery_engine.php b/cake/libs/view/helpers/jquery_engine.php
index e440f16c8..049091fa6 100644
--- a/cake/libs/view/helpers/jquery_engine.php
+++ b/cake/libs/view/helpers/jquery_engine.php
@@ -251,14 +251,13 @@ class JqueryEngineHelper extends JsBaseEngineHelper {
$options['url'] = $url;
if (isset($options['update'])) {
$wrapCallbacks = isset($options['wrapCallbacks']) ? $options['wrapCallbacks'] : true;
- if ($wrapCallbacks) {
- $success = $this->jQueryObject . '("' . $options['update'] . '").html(data);';
- } else {
- $success = sprintf(
- 'function (data, textStatus) {%s("%s").html(data);}',
- $this->jQueryObject,
- $options['update']
- );
+ $success = '';
+ if(isset($options['success']) AND !empty($options['success'])) {
+ $success .= $options['success'];
+ }
+ $success .= $this->jQueryObject . '("' . $options['update'] . '").html(data);';
+ if (!$wrapCallbacks) {
+ $success = 'function (data, textStatus) {' . $success . '}';
}
$options['dataType'] = 'html';
$options['success'] = $success;
diff --git a/cake/libs/view/helpers/rss.php b/cake/libs/view/helpers/rss.php
index 3009dbac1..b6c773d0f 100644
--- a/cake/libs/view/helpers/rss.php
+++ b/cake/libs/view/helpers/rss.php
@@ -17,18 +17,16 @@
* @since CakePHP(tm) v 1.2
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
-App::import('Helper', 'Xml');
+App::import('Core', 'Xml');
/**
- * XML Helper class for easy output of XML structures.
- *
- * XmlHelper encloses all methods needed while working with XML documents.
+ * RSS Helper class for easy output RSS structures.
*
* @package cake
* @subpackage cake.cake.libs.view.helpers
* @link http://book.cakephp.org/view/1460/RSS
*/
-class RssHelper extends XmlHelper {
+class RssHelper extends AppHelper {
/**
* Helpers used by RSS Helper
@@ -270,7 +268,7 @@ class RssHelper extends XmlHelper {
if (!empty($elements)) {
$content = implode('', $elements);
}
- return $this->elem('item', $att, $content, !($content === null));
+ return $this->elem('item', (array)$att, $content, !($content === null));
}
/**
@@ -283,4 +281,62 @@ class RssHelper extends XmlHelper {
function time($time) {
return $this->Time->toRSS($time);
}
+
+/**
+ * Generates an XML element
+ *
+ * @param string $name The name of the XML element
+ * @param array $attrib The attributes of the XML element
+ * @param mixed $content XML element content
+ * @param boolean $endTag Whether the end tag of the element should be printed
+ * @return string XML
+ */
+ function elem($name, $attrib = array(), $content = null, $endTag = true) {
+ $namespace = null;
+ if (isset($attrib['namespace'])) {
+ $namespace = $attrib['namespace'];
+ unset($attrib['namespace']);
+ }
+ $cdata = false;
+ if (is_array($content) && isset($content['cdata'])) {
+ $cdata = true;
+ unset($content['cdata']);
+ }
+ if (is_array($content) && array_key_exists('value', $content)) {
+ $content = $content['value'];
+ }
+ $children = array();
+ if (is_array($content)) {
+ $children = $content;
+ $content = null;
+ }
+
+ $xml = '<' . $name;
+ if (!empty($namespace)) {
+ $xml .= ' xmlns:"' . $namespace . '"';
+ }
+ if (strpos($name, ':') !== false) {
+ list($prefix, ) = explode(':', $name, 2);
+ switch ($prefix) {
+ case 'atom':
+ $xml .= ' xmlns:atom="http://www.w3.org/2005/Atom"';
+ break;
+ }
+ }
+ if ($cdata && !empty($content)) {
+ $content = '';
+ }
+ $xml .= '>' . $content . '' . $name. '>';
+ $elem = Xml::build($xml);
+ foreach ($attrib as $key => $value) {
+ $elem->addAttribute($key, $value);
+ }
+ foreach ($children as $child) {
+ $elem->addChild($child);
+ }
+
+ $xml = $elem->asXML();
+ $xml = trim(substr($xml, strpos($xml, '?>') + 2));
+ return $xml;
+ }
}
diff --git a/cake/libs/view/helpers/xml.php b/cake/libs/view/helpers/xml.php
deleted file mode 100644
index 58e0aefa4..000000000
--- a/cake/libs/view/helpers/xml.php
+++ /dev/null
@@ -1,179 +0,0 @@
-Xml =& new Xml();
- $this->Xml->options(array('verifyNs' => false));
- }
-
-/**
- * Returns an XML document header
- *
- * @param array $attrib Header tag attributes
- * @return string XML header
- * @access public
- * @link http://book.cakephp.org/view/1476/header
- */
- public function header($attrib = array()) {
- if (Configure::read('App.encoding') !== null) {
- $this->encoding = Configure::read('App.encoding');
- }
-
- if (is_array($attrib)) {
- $attrib = array_merge(array('encoding' => $this->encoding), $attrib);
- }
- if (is_string($attrib) && strpos($attrib, 'xml') !== 0) {
- $attrib = 'xml ' . $attrib;
- }
-
- return $this->Xml->header($attrib);
- }
-
-/**
- * Adds a namespace to any documents generated
- *
- * @param string $name The namespace name
- * @param string $url The namespace URI; can be empty if in the default namespace map
- * @return boolean False if no URL is specified, and the namespace does not exist
- * default namespace map, otherwise true
- * @deprecated
- * @see Xml::addNs()
- */
- function addNs($name, $url = null) {
- return $this->Xml->addNamespace($name, $url);
- }
-
-/**
- * Removes a namespace added in addNs()
- *
- * @param string $name The namespace name or URI
- * @deprecated
- * @see Xml::removeNs()
- */
- public function removeNs($name) {
- return $this->Xml->removeGlobalNamespace($name);
- }
-
-/**
- * Generates an XML element
- *
- * @param string $name The name of the XML element
- * @param array $attrib The attributes of the XML element
- * @param mixed $content XML element content
- * @param boolean $endTag Whether the end tag of the element should be printed
- * @return string XML
- * @access public
- * @link http://book.cakephp.org/view/1475/elem
- */
- public function elem($name, $attrib = array(), $content = null, $endTag = true) {
- $namespace = null;
- if (isset($attrib['namespace'])) {
- $namespace = $attrib['namespace'];
- unset($attrib['namespace']);
- }
- $cdata = false;
- if (is_array($content) && isset($content['cdata'])) {
- $cdata = true;
- unset($content['cdata']);
- }
- if (is_array($content) && array_key_exists('value', $content)) {
- $content = $content['value'];
- }
- $children = array();
- if (is_array($content)) {
- $children = $content;
- $content = null;
- }
-
- $elem =& $this->Xml->createElement($name, $content, $attrib, $namespace);
- foreach ($children as $child) {
- $elem->createElement($child);
- }
- $out = $elem->toString(array('cdata' => $cdata, 'leaveOpen' => !$endTag));
-
- if (!$endTag) {
- $this->Xml =& $elem;
- }
- return $out;
- }
-
-/**
- * Create closing tag for current element
- *
- * @return string
- */
- public function closeElem() {
- $name = $this->Xml->name();
- if ($parent =& $this->Xml->parent()) {
- $this->Xml =& $parent;
- }
- return '' . $name . '>';
- }
-
-/**
- * Serializes a model resultset into XML
- *
- * @param mixed $data The content to be converted to XML
- * @param array $options The data formatting options. For a list of valid options, see
- * Xml::__construct().
- * @return string A copy of $data in XML format
- * @see Xml::__construct()
- * @access public
- * @link http://book.cakephp.org/view/1474/serialize
- */
- public function serialize($data, $options = array()) {
- $options += array('attributes' => false, 'format' => 'attributes');
- $data =& new Xml($data, $options);
- return $data->toString($options + array('header' => false));
- }
-}
diff --git a/cake/libs/view/view.php b/cake/libs/view/view.php
index eae0222f0..013506e61 100644
--- a/cake/libs/view/view.php
+++ b/cake/libs/view/view.php
@@ -390,8 +390,8 @@ class View extends Object {
if ($layout && $this->autoLayout) {
$out = $this->renderLayout($out, $layout);
$isCached = (
- isset($this->Helpers->Cache) &&
- (($this->cacheAction != false)) && (Configure::read('Cache.check') === true)
+ isset($this->Helpers->Cache) ||
+ Configure::read('Cache.check') === true
);
if ($isCached) {
@@ -562,6 +562,7 @@ class View extends Object {
if (
($count == 1 && !empty($this->association)) ||
($count == 1 && $this->model != $this->entityPath) ||
+ ($count == 1 && empty($this->association) && !empty($this->field)) ||
($count == 2 && !empty($this->fieldSuffix)) ||
is_numeric($path[0]) && !empty($assoc)
) {
diff --git a/cake/libs/xml.php b/cake/libs/xml.php
index c7bc7754d..9b1905b82 100644
--- a/cake/libs/xml.php
+++ b/cake/libs/xml.php
@@ -19,1421 +19,315 @@
* @since CakePHP v .0.10.3.1400
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
-App::import('Core', 'Set');
+
+class Xml {
/**
- * XML node.
+ * Initialize SimpleXMLElement or DOMDocument from a given XML string, file path, URL or array.
*
- * Single XML node in an XML tree.
+ * ### Usage:
*
- * @package cake
- * @subpackage cake.cake.libs
- * @since CakePHP v .0.10.3.1400
- */
-class XmlNode extends Object {
-
-/**
- * Name of node
+ * Building XML from a string:
*
- * @var string
- * @access public
- */
- public $name = null;
-
-/**
- * Node namespace
+ * `$xml = Xml::build('text');`
*
- * @var string
- * @access public
- */
- public $namespace = null;
-
-/**
- * Namespaces defined for this node and all child nodes
+ * Building XML from string (output DOMDocument):
*
- * @var array
- * @access public
- */
- public $namespaces = array();
-
-/**
- * Value of node
+ * `$xml = Xml::build('text', array('return' => 'domdocument'));`
*
- * @var string
- * @access public
- */
- public $value;
-
-/**
- * Attributes on this node
+ * Building XML from a file path:
*
- * @var array
- * @access public
- */
- public $attributes = array();
-
-/**
- * This node's children
+ * `$xml = Xml::build('/path/to/an/xml/file.xml');`
*
- * @var array
- * @access public
- */
- public $children = array();
-
-/**
- * Reference to parent node.
+ * Building from a remote URL:
*
- * @var XmlNode
- * @access private
- */
- private $__parent = null;
-
-/**
- * Constructor.
+ * `$xml = Xml::build('http://example.com/example.xml');`
*
- * @param string $name Node name
- * @param array $attributes Node attributes
- * @param mixed $value Node contents (text)
- * @param array $children Node children
- */
- function __construct($name = null, $value = null, $namespace = null) {
- if (strpos($name, ':') !== false) {
- list($prefix, $name) = explode(':', $name);
- if (!$namespace) {
- $namespace = $prefix;
- }
- }
- $this->name = $name;
- if ($namespace) {
- $this->namespace = $namespace;
- }
-
- if (is_array($value) || is_object($value)) {
- $this->normalize($value);
- } elseif (!empty($value) || $value === 0 || $value === '0') {
- $this->createTextNode($value);
- }
- }
-/**
- * Adds a namespace to the current node
+ * Building from an array:
*
- * @param string $prefix The namespace prefix
- * @param string $url The namespace DTD URL
- * @return void
- */
- function addNamespace($prefix, $url) {
- if ($ns = Xml::addGlobalNs($prefix, $url)) {
- $this->namespaces = array_merge($this->namespaces, $ns);
- return true;
- }
- return false;
- }
-
-/**
- * Adds a namespace to the current node
- *
- * @param string $prefix The namespace prefix
- * @param string $url The namespace DTD URL
- * @return void
- */
- function removeNamespace($prefix) {
- if (Xml::removeGlobalNs($prefix)) {
- return true;
- }
- return false;
- }
-
-/**
- * Creates an XmlNode object that can be appended to this document or a node in it
- *
- * @param string $name Node name
- * @param string $value Node value
- * @param string $namespace Node namespace
- * @return object XmlNode
- */
- function &createNode($name = null, $value = null, $namespace = false) {
- $node =& new XmlNode($name, $value, $namespace);
- $node->setParent($this);
- return $node;
- }
-
-/**
- * Creates an XmlElement object that can be appended to this document or a node in it
- *
- * @param string $name Element name
- * @param string $value Element value
- * @param array $attributes Element attributes
- * @param string $namespace Node namespace
- * @return object XmlElement
- */
- function &createElement($name = null, $value = null, $attributes = array(), $namespace = false) {
- $element =& new XmlElement($name, $value, $attributes, $namespace);
- $element->setParent($this);
- return $element;
- }
-
-/**
- * Creates an XmlTextNode object that can be appended to this document or a node in it
- *
- * @param string $value Node value
- * @return object XmlTextNode
- */
- function &createTextNode($value = null) {
- $node = new XmlTextNode($value);
- $node->setParent($this);
- return $node;
- }
-
-/**
- * Gets the XML element properties from an object.
- *
- * @param object $object Object to get properties from
- * @return array Properties from object
- */
- public function normalize($object, $keyName = null, $options = array()) {
- if (is_a($object, 'XmlNode')) {
- return $object;
- }
- $name = null;
- $options += array('format' => 'attributes');
-
- if ($keyName !== null && !is_numeric($keyName)) {
- $name = $keyName;
- } elseif (!empty($object->_name_)) {
- $name = $object->_name_;
- } elseif (isset($object->name)) {
- $name = $object->name;
- } elseif ($options['format'] == 'attributes') {
- $name = get_class($object);
- }
-
- $tagOpts = $this->__tagOptions($name);
-
- if ($tagOpts === false) {
- return;
- }
-
- if (isset($tagOpts['name'])) {
- $name = $tagOpts['name'];
- } elseif ($name != strtolower($name)) {
- $name = Inflector::slug(Inflector::underscore($name));
- }
-
- if (!empty($name)) {
- $node =& $this->createElement($name);
- } else {
- $node =& $this;
- }
-
- $namespace = array();
- $attributes = array();
- $children = array();
- $chldObjs = array();
-
- if (is_object($object)) {
- $chldObjs = get_object_vars($object);
- } elseif (is_array($object)) {
- $chldObjs = $object;
- } elseif (!empty($object) || $object === 0 || $object === '0') {
- $node->createTextNode($object);
- }
- $attr = array();
-
- if (isset($tagOpts['attributes'])) {
- $attr = $tagOpts['attributes'];
- }
- if (isset($tagOpts['value']) && isset($chldObjs[$tagOpts['value']])) {
- $node->createTextNode($chldObjs[$tagOpts['value']]);
- unset($chldObjs[$tagOpts['value']]);
- }
-
- $n = $name;
- if (isset($chldObjs['_name_'])) {
- $n = null;
- unset($chldObjs['_name_']);
- }
- $c = 0;
-
- foreach ($chldObjs as $key => $val) {
- if (in_array($key, $attr) && !is_object($val) && !is_array($val)) {
- $attributes[$key] = $val;
- } else {
- if (!isset($tagOpts['children']) || $tagOpts['children'] === array() || (is_array($tagOpts['children']) && in_array($key, $tagOpts['children']))) {
- if (!is_numeric($key)) {
- $n = $key;
- }
- if (is_array($val)) {
- foreach ($val as $n2 => $obj2) {
- if (is_numeric($n2)) {
- $n2 = $n;
- }
- $node->normalize($obj2, $n2, $options);
- }
- } else {
- if (is_object($val)) {
-
- $node->normalize($val, $n, $options);
- } elseif ($options['format'] == 'tags' && $this->__tagOptions($key) !== false) {
- $tmp =& $node->createElement($key);
- if (!empty($val) || $val === 0 || $val === '0') {
- $tmp->createTextNode($val);
- }
- } elseif ($options['format'] == 'attributes') {
- $node->addAttribute($key, $val);
- }
- }
- }
- }
- $c++;
- }
- if (!empty($name)) {
- return $node;
- }
- return $children;
- }
-
-/**
- * Gets the tag-specific options for the given node name
- *
- * @param string $name XML tag name
- * @param string $option The specific option to query. Omit for all options
- * @return mixed A specific option value if $option is specified, otherwise an array of all options
- * @access private
- */
- function __tagOptions($name, $option = null) {
- if (isset($this->_tags[$name])) {
- $tagOpts = $this->_tags[$name];
- } elseif (isset($this->_tags[strtolower($name)])) {
- $tagOpts = $this->_tags[strtolower($name)];
- } else {
- return null;
- }
- if ($tagOpts === false) {
- return false;
- }
- if (empty($option)) {
- return $tagOpts;
- }
- if (isset($tagOpts[$option])) {
- return $tagOpts[$option];
- }
- return null;
- }
-
-/**
- * Returns the fully-qualified XML node name, with namespace
- *
- */
- public function name() {
- if (!empty($this->namespace)) {
- $_this =& XmlManager::getInstance();
- if (!isset($_this->options['verifyNs']) || !$_this->options['verifyNs'] || in_array($this->namespace, array_keys($_this->namespaces))) {
- return $this->namespace . ':' . $this->name;
- }
- }
- return $this->name;
- }
-
-/**
- * Sets the parent node of this XmlNode.
- *
- */
- public function setParent(&$parent) {
- if (strtolower(get_class($this)) == 'xml') {
- return;
- }
- if (isset($this->__parent) && is_object($this->__parent)) {
- if ($this->__parent->compare($parent)) {
- return;
- }
- foreach ($this->__parent->children as $i => $child) {
- if ($this->compare($child)) {
- array_splice($this->__parent->children, $i, 1);
- break;
- }
- }
- }
- if ($parent == null) {
- unset($this->__parent);
- } else {
- $parent->children[] =& $this;
- $this->__parent =& $parent;
- }
- }
-
-/**
- * Returns a copy of self.
- *
- * @return object Cloned instance
- */
- public function cloneNode() {
- return clone($this);
- }
-
-/**
- * Compares $node to this XmlNode object
- *
- * @param object An XmlNode or subclass instance
- * @return boolean True if the nodes match, false otherwise
- */
- public function compare($node) {
- $keys = array(get_object_vars($this), get_object_vars($node));
- return ($keys[0] === $keys[1]);
- }
-
-/**
- * Append given node as a child.
- *
- * @param object $child XmlNode with appended child
- * @param array $options XML generator options for objects and arrays
- * @return object A reference to the appended child node
- */
- public function &append(&$child, $options = array()) {
- if (empty($child)) {
- $return = false;
- return $return;
- }
-
- if (is_object($child)) {
- if ($this->compare($child)) {
- trigger_error(__('Cannot append a node to itself.'));
- $return = false;
- return $return;
- }
- } else if (is_array($child)) {
- $child = Set::map($child);
- if (is_array($child)) {
- if (!is_a(current($child), 'XmlNode')) {
- foreach ($child as $i => $childNode) {
- $child[$i] = $this->normalize($childNode, null, $options);
- }
- } else {
- foreach ($child as $childNode) {
- $this->append($childNode, $options);
- }
- }
- return $child;
- }
- } else {
- $attributes = array();
- if (func_num_args() >= 2) {
- $attributes = func_get_arg(1);
- }
- $child =& $this->createNode($child, null, $attributes);
- }
-
- $child = $this->normalize($child, null, $options);
-
- if (empty($child->namespace) && !empty($this->namespace)) {
- $child->namespace = $this->namespace;
- }
-
- if (is_a($child, 'XmlNode')) {
- $child->setParent($this);
- }
-
- return $child;
- }
-
-/**
- * Returns first child node, or null if empty.
- *
- * @return object First XmlNode
- */
- public function &first() {
- if (isset($this->children[0])) {
- return $this->children[0];
- } else {
- $return = null;
- return $return;
- }
- }
-
-/**
- * Returns last child node, or null if empty.
- *
- * @return object Last XmlNode
- */
- public function &last() {
- if (count($this->children) > 0) {
- return $this->children[count($this->children) - 1];
- } else {
- $return = null;
- return $return;
- }
- }
-
-/**
- * Returns child node with given ID.
- *
- * @param string $id Name of child node
- * @return object Child XmlNode
- */
- public function &child($id) {
- $null = null;
-
- if (is_int($id)) {
- if (isset($this->children[$id])) {
- return $this->children[$id];
- } else {
- return null;
- }
- } elseif (is_string($id)) {
- for ($i = 0; $i < count($this->children); $i++) {
- if ($this->children[$i]->name == $id) {
- return $this->children[$i];
- }
- }
- }
- return $null;
- }
-
-/**
- * Gets a list of childnodes with the given tag name.
- *
- * @param string $name Tag name of child nodes
- * @return array An array of XmlNodes with the given tag name
- */
- public function children($name) {
- $nodes = array();
- $count = count($this->children);
- for ($i = 0; $i < $count; $i++) {
- if ($this->children[$i]->name == $name) {
- $nodes[] =& $this->children[$i];
- }
- }
- return $nodes;
- }
-
-/**
- * Gets a reference to the next child node in the list of this node's parent.
- *
- * @return object A reference to the XmlNode object
- */
- public function &nextSibling() {
- $null = null;
- $count = count($this->__parent->children);
- for ($i = 0; $i < $count; $i++) {
- if ($this->__parent->children[$i] == $this) {
- if ($i >= $count - 1 || !isset($this->__parent->children[$i + 1])) {
- return $null;
- }
- return $this->__parent->children[$i + 1];
- }
- }
- return $null;
- }
-
-/**
- * Gets a reference to the previous child node in the list of this node's parent.
- *
- * @return object A reference to the XmlNode object
- */
- public function &previousSibling() {
- $null = null;
- $count = count($this->__parent->children);
- for ($i = 0; $i < $count; $i++) {
- if ($this->__parent->children[$i] == $this) {
- if ($i == 0 || !isset($this->__parent->children[$i - 1])) {
- return $null;
- }
- return $this->__parent->children[$i - 1];
- }
- }
- return $null;
- }
-
-/**
- * Returns parent node.
- *
- * @return object Parent XmlNode
- */
- public function &parent() {
- return $this->__parent;
- }
-
-/**
- * Returns the XML document to which this node belongs
- *
- * @return object Parent XML object
- */
- public function &document() {
- $document =& $this;
- while (true) {
- if (get_class($document) == 'Xml' || $document == null) {
- break;
- }
- $document =& $document->parent();
- }
- return $document;
- }
-
-/**
- * Returns true if this structure has child nodes.
- *
- * @return bool
- */
- public function hasChildren() {
- if (is_array($this->children) && !empty($this->children)) {
- return true;
- }
- return false;
- }
-
-/**
- * Returns this XML structure as a string.
- *
- * @return string String representation of the XML structure.
- */
- public function toString($options = array(), $depth = 0) {
- if (is_int($options)) {
- $depth = $options;
- $options = array();
- }
- $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false, 'showEmpty' => true, 'leaveOpen' => false);
- $options = array_merge($defaults, Xml::options(), $options);
- $tag = !(strpos($this->name, '#') === 0);
- $d = '';
-
- if ($tag) {
- if ($options['whitespace']) {
- $d .= str_repeat("\t", $depth);
- }
-
- $d .= '<' . $this->name();
- if (!empty($this->namespaces) > 0) {
- foreach ($this->namespaces as $key => $val) {
- $val = str_replace('"', '\"', $val);
- $d .= ' xmlns:' . $key . '="' . $val . '"';
- }
- }
-
- $parent =& $this->parent();
- if ($parent->name === '#document' && !empty($parent->namespaces)) {
- foreach ($parent->namespaces as $key => $val) {
- $val = str_replace('"', '\"', $val);
- $d .= ' xmlns:' . $key . '="' . $val . '"';
- }
- }
-
- if (is_array($this->attributes) && !empty($this->attributes)) {
- foreach ($this->attributes as $key => $val) {
- if (is_bool($val) && $val === false) {
- $val = 0;
- }
- $d .= ' ' . $key . '="' . htmlspecialchars($val, ENT_QUOTES, Configure::read('App.encoding')) . '"';
- }
- }
- }
-
- if (!$this->hasChildren() && empty($this->value) && $this->value !== 0 && $tag) {
- if (!$options['leaveOpen']) {
- $d .= ' />';
- }
- if ($options['whitespace']) {
- $d .= "\n";
- }
- } elseif ($tag || $this->hasChildren()) {
- if ($tag) {
- $d .= '>';
- }
- if ($this->hasChildren()) {
- if ($options['whitespace']) {
- $d .= "\n";
- }
- $count = count($this->children);
- $cDepth = $depth + 1;
- for ($i = 0; $i < $count; $i++) {
- $d .= $this->children[$i]->toString($options, $cDepth);
- }
- if ($tag) {
- if ($options['whitespace'] && $tag) {
- $d .= str_repeat("\t", $depth);
- }
- if (!$options['leaveOpen']) {
- $d .= '' . $this->name() . '>';
- }
- if ($options['whitespace']) {
- $d .= "\n";
- }
- }
- }
- }
- return $d;
- }
-
-/**
- * Return array representation of current object.
- *
- * @param boolean $camelize true will camelize child nodes, false will not alter node names
- * @return array Array representation
- */
- public function toArray($camelize = true) {
- $out = $this->attributes;
- $multi = null;
-
- foreach ($this->children as $child) {
- $key = $camelize ? Inflector::camelize($child->name) : $child->name;
-
- if (is_a($child, 'XmlTextNode')) {
- $out['value'] = $child->value;
- continue;
- } elseif (isset($child->children[0]) && is_a($child->children[0], 'XmlTextNode')) {
- $value = $child->children[0]->value;
- if ($child->attributes) {
- $value = array_merge(array('value' => $value), $child->attributes);
- }
- if (isset($out[$child->name]) || isset($multi[$key])) {
- if (!isset($multi[$key])) {
- $multi[$key] = array($out[$child->name]);
- unset($out[$child->name]);
- }
- $multi[$key][] = $value;
- } else {
- $out[$child->name] = $value;
- }
- continue;
- } elseif (count($child->children) === 0 && $child->value == '') {
- $value = $child->attributes;
- if (isset($out[$key]) || isset($multi[$key])) {
- if (!isset($multi[$key])) {
- $multi[$key] = array($out[$key]);
- //unset($out[$key]);
- }
- $multi[$key][] = $value;
- } elseif (!empty($value)) {
- $out[$key] = $value;
- } else {
- $out[$child->name] = $value;
- }
- continue;
- } else {
- $value = $child->toArray($camelize);
- }
-
- if (!isset($out[$key])) {
- $out[$key] = $value;
- } else {
- if (!is_array($out[$key]) || !isset($out[$key][0])) {
- $out[$key] = array($out[$key]);
- }
- $out[$key][] = $value;
- }
- }
-
- if (isset($multi)) {
- $out = array_merge($out, $multi);
- }
- return $out;
- }
-
-/**
- * Returns data from toString when this object is converted to a string.
- *
- * @return string String representation of this structure.
- * @access private
- */
- function __toString() {
- return $this->toString();
- }
-
-/**
- * Debug method. Deletes the parent. Also deletes this node's children,
- * if given the $recursive parameter.
- *
- * @param boolean $recursive Recursively delete elements.
- */
- protected function _killParent($recursive = true) {
- unset($this->__parent, $this->_log);
- if ($recursive && $this->hasChildren()) {
- for ($i = 0; $i < count($this->children); $i++) {
- $this->children[$i]->_killParent(true);
- }
- }
- }
-}
-
-/**
- * Main XML class.
- *
- * Parses and stores XML data, representing the root of an XML document
- *
- * @package cake
- * @subpackage cake.cake.libs
- * @since CakePHP v .0.10.3.1400
- */
-class Xml extends XmlNode {
-
-/**
- * Resource handle to XML parser.
- *
- * @var resource
- * @access private
- */
- private $__parser;
-
-/**
- * File handle to XML indata file.
- *
- * @var resource
- * @access private
- */
- private $__file;
-
-/**
- * Raw XML string data (for loading purposes)
- *
- * @var string
- * @access private
- */
- private $__rawData = null;
-
-/**
- * XML document header
- *
- * @var string
- * @access protected
- */
- protected $_header = null;
-
-/**
- * Default array keys/object properties to use as tag names when converting objects or array
- * structures to XML. Set by passing $options['tags'] to this object's constructor.
- *
- * @var array
- * @access protected
- */
- protected $_tags = array();
-
-/**
- * XML document version
- *
- * @var string
- * @access private
- */
- public $version = '1.0';
-
-/**
- * XML document encoding
- *
- * @var string
- * @access private
- */
- public $encoding = 'UTF-8';
-
-/**
- * Constructor. Sets up the XML parser with options, gives it this object as
- * its XML object, and sets some variables.
+ * {{{
+ * $value = array(
+ * 'tags' => array(
+ * 'tag' => array(
+ * array(
+ * 'id' => '1',
+ * 'name' => 'defect'
+ * ),
+ * array(
+ * 'id' => '2',
+ * 'name' => 'enhancement'
+ * )
+ * )
+ * )
+ * );
+ * $xml = Xml::build($value);
+ * }}}
+ *
+ * When building XML from an array ensure that there is only one top level element.
*
* ### Options
- * - 'root': The name of the root element, defaults to '#document'
- * - 'version': The XML version, defaults to '1.0'
- * - 'encoding': Document encoding, defaults to 'UTF-8'
- * - 'namespaces': An array of namespaces (as strings) used in this document
- * - 'format': Specifies the format this document converts to when parsed or
- * rendered out as text, either 'attributes' or 'tags', defaults to 'attributes'
- * - 'tags': An array specifying any tag-specific formatting options, indexed
- * by tag name. See XmlNode::normalize().
- * @param mixed $input The content with which this XML document should be initialized. Can be a
- * string, array or object. If a string is specified, it may be a literal XML
- * document, or a URL or file path to read from.
- * @param array $options Options to set up with, for valid options see above:
- * @see XmlNode::normalize()
+ *
+ * - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
+ * - If using array as input, you can pass `options` from Xml::fromArray.
+ *
+ * @param mixed $input XML string, a path to a file, an URL or an array
+ * @param array $options The options to use
+ * @return object SimpleXMLElement or DOMDocument
+ * @throws Exception
*/
- function __construct($input = null, $options = array()) {
+ public static function build($input, $options = array()) {
+ if (!is_array($options)) {
+ $options = array('return' => (string)$options);
+ }
$defaults = array(
- 'root' => '#document', 'tags' => array(), 'namespaces' => array(),
- 'version' => '1.0', 'encoding' => 'UTF-8', 'format' => 'attributes'
+ 'return' => 'simplexml'
);
- $options = array_merge($defaults, Xml::options(), $options);
+ $options = array_merge($defaults, $options);
- foreach (array('version', 'encoding', 'namespaces') as $key) {
- $this->{$key} = $options[$key];
- }
- $this->_tags = $options['tags'];
- parent::__construct('#document');
-
- if ($options['root'] !== '#document') {
- $Root =& $this->createNode($options['root']);
- } else {
- $Root =& $this;
- }
-
- if (!empty($input)) {
- if (is_string($input)) {
- $Root->load($input);
- } elseif (is_array($input) || is_object($input)) {
- $Root->append($input, $options);
+ if (is_array($input) || is_object($input)) {
+ return self::fromArray((array)$input, $options);
+ } elseif (strpos($input, '<') !== false) {
+ if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+ return new SimpleXMLElement($input);
}
- }
- }
-
-/**
- * Initialize XML object from a given XML string. Returns false on error.
- *
- * @param string $input XML string, a path to a file, or an HTTP resource to load
- * @return boolean Success
- */
- public function load($input) {
- if (!is_string($input)) {
- return false;
- }
- $this->__rawData = null;
- $this->_header = null;
-
- if (strstr($input, "<")) {
- $this->__rawData = $input;
- } elseif (strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
- App::import('Core', 'HttpSocket');
- $socket = new HttpSocket();
- $this->__rawData = $socket->get($input);
- } elseif (file_exists($input)) {
- $this->__rawData = file_get_contents($input);
- } else {
- trigger_error(__('XML cannot be read'));
- return false;
- }
- return $this->parse();
- }
-
-/**
- * Parses and creates XML nodes from the __rawData property.
- *
- * @return boolean Success
- * @access public
- * @see Xml::load()
- * @todo figure out how to link attributes and namespaces
- */
- function parse() {
- $this->__initParser();
- $this->__rawData = trim($this->__rawData);
- $this->_header = trim(str_replace(
- array('<' . '?', '?' . '>'),
- array('', ''),
- substr($this->__rawData, 0, strpos($this->__rawData, '?' . '>'))
- ));
-
- xml_parse_into_struct($this->__parser, $this->__rawData, $vals);
- $xml =& $this;
- $count = count($vals);
-
- for ($i = 0; $i < $count; $i++) {
- $data = $vals[$i];
- $data += array('tag' => null, 'value' => null, 'attributes' => array());
- switch ($data['type']) {
- case "open" :
- $xml =& $xml->createElement($data['tag'], $data['value'], $data['attributes']);
- break;
- case "close" :
- $xml =& $xml->parent();
- break;
- case "complete" :
- $xml->createElement($data['tag'], $data['value'], $data['attributes']);
- break;
- case 'cdata':
- $xml->createTextNode($data['value']);
- break;
+ $dom = new DOMDocument();
+ $dom->loadXML($input);
+ return $dom;
+ } elseif (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
+ if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+ return new SimpleXMLElement($input, null, true);
}
+ $dom = new DOMDocument();
+ $dom->load($input);
+ return $dom;
+ } elseif (!is_string($input)) {
+ throw new Exception(__('Invalid input.'));
}
- xml_parser_free($this->__parser);
- $this->__parser = null;
- return true;
+ throw new Exception(__('XML cannot be read.'));
}
/**
- * Initializes the XML parser resource
+ * Transform an array into a SimpleXMLElement
*
- * @return void
- * @access private
+ * ### Options
+ *
+ * - `format` If create childs ('tags') or attributes ('attribute').
+ * - `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.
+ * - `return` If return object of SimpleXMLElement ('simplexml') or DOMDocument ('domdocument'). Default is SimpleXMLElement.
+ *
+ * Using the following data:
+ *
+ * {{{
+ * $value = array(
+ * 'root' => array(
+ * 'tag' => array(
+ * 'id' => 1,
+ * 'value' => 'defect',
+ * '@' => 'description'
+ * )
+ * )
+ * );
+ * }}}
+ *
+ * Calling `Xml::fromArray($value, 'tags');` Will generate:
+ *
+ * `1defectdescription`
+ *
+ * And calling `Xml::fromArray($value, 'attribute');` Will generate:
+ *
+ * `description`
+ *
+ * @param array $input Array with data
+ * @param array $options The options to use
+ * @return object SimpleXMLElement or DOMDocument
*/
- function __initParser() {
- if (empty($this->__parser)) {
- $this->__parser = xml_parser_create();
- xml_set_object($this->__parser, $this);
- xml_parser_set_option($this->__parser, XML_OPTION_CASE_FOLDING, 0);
- xml_parser_set_option($this->__parser, XML_OPTION_SKIP_WHITE, 1);
+ public static function fromArray($input, $options = array()) {
+ if (!is_array($input) || count($input) !== 1) {
+ throw new Exception(__('Invalid input.'));
}
- }
-
-/**
- * Returns a string representation of the XML object
- *
- * @param mixed $options If boolean: whether to include the XML header with the document
- * (defaults to true); if an array, overrides the default XML generation options
- * @return string XML data
- * @access public
- * @deprecated
- * @see Xml::toString()
- */
- function compose($options = array()) {
- return $this->toString($options);
- }
-
-/**
- * If debug mode is on, this method echoes an error message.
- *
- * @param string $msg Error message
- * @param integer $code Error code
- * @param integer $line Line in file
- */
- public function error($msg, $code = 0, $line = 0) {
- if (Configure::read('debug')) {
- echo $msg . " " . $code . " " . $line;
+ $key = key($input);
+ if (is_integer($key)) {
+ throw new Exception(__('The key of input must be alphanumeric'));
}
+
+ if (!is_array($options)) {
+ $options = array('format' => (string)$options);
+ }
+ $defaults = array(
+ 'format' => 'tags',
+ 'version' => '1.0',
+ 'encoding' => Configure::read('App.encoding'),
+ 'return' => 'simplexml'
+ );
+ $options = array_merge($defaults, $options);
+
+ $dom = new DOMDocument($options['version'], $options['encoding']);
+ self::_fromArray($dom, $dom, $input, $options['format']);
+
+ $options['return'] = strtolower($options['return']);
+ if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+ return new SimpleXMLElement($dom->saveXML());
+ }
+ return $dom;
}
/**
- * Returns a string with a textual description of the error code, or FALSE if no description was found.
+ * Recursive method to create childs from array
*
- * @param integer $code Error code
- * @return string Error message
- */
- public function getError($code) {
- $r = @xml_error_string($code);
- return $r;
- }
-
-// Overridden functions from superclass
-
-/**
- * Get next element. NOT implemented.
- *
- * @return object
- */
- public function &next() {
- $return = null;
- return $return;
- }
-
-/**
- * Get previous element. NOT implemented.
- *
- * @return object
- */
- public function &previous() {
- $return = null;
- return $return;
- }
-
-/**
- * Get parent element. NOT implemented.
- *
- * @return object
- */
- public function &parent() {
- $return = null;
- return $return;
- }
-
-/**
- * Adds a namespace to the current document
- *
- * @param string $prefix The namespace prefix
- * @param string $url The namespace DTD URL
+ * @param object $dom Handler to DOMDocument
+ * @param object $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.
* @return void
*/
- function addNamespace($prefix, $url) {
- if ($count = count($this->children)) {
- for ($i = 0; $i < $count; $i++) {
- $this->children[$i]->addNamespace($prefix, $url);
- }
- return true;
+ protected function _fromArray(&$dom, &$node, &$data, $format) {
+ if (empty($data) || !is_array($data)) {
+ return;
}
- return parent::addNamespace($prefix, $url);
- }
-
-/**
- * Removes a namespace to the current document
- *
- * @param string $prefix The namespace prefix
- * @return void
- */
- function removeNamespace($prefix) {
- if ($count = count($this->children)) {
- for ($i = 0; $i < $count; $i++) {
- $this->children[$i]->removeNamespace($prefix);
- }
- return true;
- }
- return parent::removeNamespace($prefix);
- }
-
-/**
- * Return string representation of current object.
- *
- * @return string String representation
- */
- public function toString($options = array()) {
- if (is_bool($options)) {
- $options = array('header' => $options);
- }
-
- $defaults = array('header' => false, 'encoding' => $this->encoding);
- $options = array_merge($defaults, Xml::options(), $options);
- $data = parent::toString($options, 0);
-
- if ($options['header']) {
- if (!empty($this->_header)) {
- return $this->header($this->_header) . "\n" . $data;
- }
- return $this->header() . "\n" . $data;
- }
-
- return $data;
- }
-
-/**
- * Return a header used on the first line of the xml file
- *
- * @param mixed $attrib attributes of the header element
- * @return string formated header
- */
- function header($attrib = array()) {
- $header = 'xml';
- if (is_string($attrib)) {
- $header = $attrib;
- } else {
-
- $attrib = array_merge(array('version' => $this->version, 'encoding' => $this->encoding), $attrib);
- foreach ($attrib as $key=>$val) {
- $header .= ' ' . $key . '="' . $val . '"';
- }
- }
- return '<' . '?' . $header . ' ?' . '>';
- }
-
-/**
- * Destructor, used to free resources.
- *
- * @access private
- */
- function __destruct() {
- $this->_killParent(true);
- }
-
-/**
- * Adds a namespace to any XML documents generated or parsed
- *
- * @param string $name The namespace name
- * @param string $url The namespace URI; can be empty if in the default namespace map
- * @return boolean False if no URL is specified, and the namespace does not exist
- * default namespace map, otherwise true
- * @access public
- * @static
- */
- function addGlobalNs($name, $url = null) {
- $_this =& XmlManager::getInstance();
- if ($ns = Xml::resolveNamespace($name, $url)) {
- $_this->namespaces = array_merge($_this->namespaces, $ns);
- return $ns;
- }
- return false;
- }
-
-/**
- * Resolves current namespace
- *
- * @param string $name
- * @param string $url
- * @return array
- */
- function resolveNamespace($name, $url) {
- $_this =& XmlManager::getInstance();
- if ($url == null && isset($_this->defaultNamespaceMap[$name])) {
- $url = $_this->defaultNamespaceMap[$name];
- } elseif ($url == null) {
- return false;
- }
-
- if (!strpos($url, '://') && isset($_this->defaultNamespaceMap[$name])) {
- $_url = $_this->defaultNamespaceMap[$name];
- $name = $url;
- $url = $_url;
- }
- return array($name => $url);
- }
-
-/**
- * Alias to Xml::addNs
- *
- * @access public
- * @static
- */
- function addGlobalNamespace($name, $url = null) {
- return Xml::addGlobalNs($name, $url);
- }
-
-/**
- * Removes a namespace added in addNs()
- *
- * @param string $name The namespace name or URI
- * @access public
- * @static
- */
- function removeGlobalNs($name) {
- $_this =& XmlManager::getInstance();
- if (isset($_this->namespaces[$name])) {
- unset($_this->namespaces[$name]);
- unset($this->namespaces[$name]);
- return true;
- } elseif (in_array($name, $_this->namespaces)) {
- $keys = array_keys($_this->namespaces);
- $count = count($keys);
- for ($i = 0; $i < $count; $i++) {
- if ($_this->namespaces[$keys[$i]] == $name) {
- unset($_this->namespaces[$keys[$i]]);
- unset($this->namespaces[$keys[$i]]);
- return true;
- }
- }
- }
- return false;
- }
-
-/**
- * Alias to Xml::removeNs
- *
- * @access public
- * @static
- */
- function removeGlobalNamespace($name) {
- return Xml::removeGlobalNs($name);
- }
-
-/**
- * Sets/gets global XML options
- *
- * @param array $options
- * @return array
- * @access public
- * @static
- */
- function options($options = array()) {
- $_this =& XmlManager::getInstance();
- $_this->options = array_merge($_this->options, $options);
- return $_this->options;
- }
-}
-
-/**
- * The XML Element
- *
- */
-class XmlElement extends XmlNode {
-
-/**
- * Construct an Xml element
- *
- * @param string $name name of the node
- * @param string $value value of the node
- * @param array $attributes
- * @param string $namespace
- * @return string A copy of $data in XML format
- */
- function __construct($name = null, $value = null, $attributes = array(), $namespace = false) {
- parent::__construct($name, $value, $namespace);
- $this->addAttribute($attributes);
- }
-
-/**
- * Get all the attributes for this element
- *
- * @return array
- */
- function attributes() {
- return $this->attributes;
- }
-
-/**
- * Add attributes to this element
- *
- * @param string $name name of the node
- * @param string $value value of the node
- * @return boolean
- */
- function addAttribute($name, $val = null) {
- if (is_object($name)) {
- $name = get_object_vars($name);
- }
- if (is_array($name)) {
- foreach ($name as $key => $val) {
- $this->addAttribute($key, $val);
- }
- return true;
- }
- if (is_numeric($name)) {
- $name = $val;
- $val = null;
- }
- if (!empty($name)) {
- if (strpos($name, 'xmlns') === 0) {
- if ($name == 'xmlns') {
- $this->namespace = $val;
+ foreach ($data as $key => $value) {
+ if (is_string($key)) {
+ if (!is_array($value)) {
+ if (is_bool($value)) {
+ $value = (int)$value;
+ } elseif ($value === null) {
+ $value = '';
+ }
+ $isNamespace = strpos($key, 'xmlns:');
+ $nsUri = null;
+ if ($isNamespace !== false) {
+ $node->setAttributeNS('http://www.w3.org/2000/xmlns/', $key, $value);
+ continue;
+ }
+ if ($key[0] !== '@' && $format === 'tags') {
+ $child = $dom->createElement($key, $value);
+ $node->appendChild($child);
+ } else {
+ if ($key[0] === '@') {
+ $key = substr($key, 1);
+ }
+ $attribute = $dom->createAttribute($key);
+ $attribute->appendChild($dom->createTextNode($value));
+ $node->appendChild($attribute);
+ }
} else {
- list($pre, $prefix) = explode(':', $name);
- $this->addNamespace($prefix, $val);
- return true;
+ if ($key[0] === '@') {
+ throw new Exception(__('Invalid array'));
+ }
+ if (array_keys($value) === range(0, count($value) - 1)) { // List
+ foreach ($value as $item) {
+ $data = compact('dom', 'node', 'key', 'format');
+ $data['value'] = $item;
+ self::__createChild($data);
+ }
+ } else { // Struct
+ self::__createChild(compact('dom', 'node', 'key', 'value', 'format'));
+ }
}
+ } else {
+ throw new Exception(__('Invalid array'));
}
- $this->attributes[$name] = $val;
- return true;
}
- return false;
}
/**
- * Remove attributes to this element
+ * Helper to _fromArray(). It will create childs of arrays
*
- * @param string $name name of the node
- * @return boolean
+ * @param array $data Array with informations to create childs
+ * @return void
*/
- function removeAttribute($attr) {
- if (array_key_exists($attr, $this->attributes)) {
- unset($this->attributes[$attr]);
- return true;
+ private function __createChild($data) {
+ extract($data);
+ $childNS = $childValue = null;
+ if (is_array($value)) {
+ if (isset($value['@'])) {
+ $childValue = (string)$value['@'];
+ unset($value['@']);
+ }
+ if (isset($value['xmlns:'])) {
+ $childNS = $value['xmlns:'];
+ unset($value['xmlns:']);
+ }
+ } elseif (!empty($value) || $value === 0) {
+ $childValue = (string)$value;
}
- return false;
- }
-}
-/**
- * XML text or CDATA node
- *
- * Stores XML text data according to the encoding of the parent document
- *
- * @package cake
- * @subpackage cake.cake.libs
- * @since CakePHP v .1.2.6000
- */
-class XmlTextNode extends XmlNode {
+ if ($childValue) {
+ $child = $dom->createElement($key, $childValue);
+ } else {
+ $child = $dom->createElement($key);
+ }
+ if ($childNS) {
+ $child->setAttribute('xmlns', $childNS);
+ }
-/**
- * Harcoded XML node name, represents this object as a text node
- *
- * @var string
- */
- public $name = '#text';
-
-/**
- * The text/data value which this node contains
- *
- * @var string
- */
- public $value = null;
-
-/**
- * Construct text node with the given parent object and data
- *
- * @param object $parent Parent XmlNode/XmlElement object
- * @param mixed $value Node value
- */
- function __construct($value = null) {
- $this->value = $value;
+ self::_fromArray($dom, $child, $value, $format);
+ $node->appendChild($child);
}
/**
- * Looks for child nodes in this element
+ * Returns this XML structure as a array.
*
- * @return boolean False - not supported
+ * @param object $obj SimpleXMLElement, DOMDocument or DOMNode instance
+ * @return array Array representation of the XML structure.
*/
- function hasChildren() {
- return false;
+ public static function toArray($obj) {
+ if ($obj instanceof DOMNode) {
+ $obj = simplexml_import_dom($obj);
+ }
+ if (!($obj instanceof SimpleXMLElement)) {
+ throw new Exception(__('The input is not instance of SimpleXMLElement, DOMDocument or DOMNode.'));
+ }
+ $result = array();
+ $namespaces = array_merge(array('' => ''), $obj->getNamespaces(true));
+ self::_toArray($obj, $result, '', array_keys($namespaces));
+ return $result;
}
/**
- * Append an XML node: XmlTextNode does not support this operation
+ * Recursive method to toArray
*
- * @return boolean False - not supported
- * @todo make convertEntities work without mb support, convert entities to number entities
+ * @param object $xml SimpleXMLElement object
+ * @param array $parentData Parent array with data
+ * @param string $ns Namespace of current child
+ * @param array $namespaces List of namespaces in XML
+ * @return void
*/
- function append() {
- return false;
+ protected static function _toArray($xml, &$parentData, $ns, $namespaces) {
+ $data = array();
+
+ foreach ($namespaces as $namespace) {
+ foreach ($xml->attributes($namespace, true) as $key => $value) {
+ if (!empty($namespace)) {
+ $key = $namespace . ':' . $key;
+ }
+ $data['@' . $key] = (string)$value;
+ }
+
+ foreach ($xml->children($namespace, true) as $child) {
+ self::_toArray($child, $data, $namespace, $namespaces);
+ }
+ }
+
+ $asString = trim((string)$xml);
+ if (empty($data)) {
+ $data = $asString;
+ } elseif (!empty($asString)) {
+ $data['@'] = $asString;
+ }
+
+ if (!empty($ns)) {
+ $ns .= ':';
+ }
+ $name = $ns . $xml->getName();
+ if (isset($parentData[$name])) {
+ if (!is_array($parentData[$name]) || !isset($parentData[$name][0])) {
+ $parentData[$name] = array($parentData[$name]);
+ }
+ $parentData[$name][] = $data;
+ } else {
+ $parentData[$name] = $data;
+ }
}
-/**
- * Return string representation of current text node object.
- *
- * @return string String representation
- */
- public function toString($options = array(), $depth = 0) {
- if (is_int($options)) {
- $depth = $options;
- $options = array();
- }
-
- $defaults = array('cdata' => true, 'whitespace' => false, 'convertEntities' => false);
- $options = array_merge($defaults, Xml::options(), $options);
- $val = $this->value;
-
- if ($options['convertEntities'] && function_exists('mb_convert_encoding')) {
- $val = mb_convert_encoding($val,'UTF-8', 'HTML-ENTITIES');
- }
-
- if ($options['cdata'] === true && !is_numeric($val)) {
- $val = '';
- }
-
- if ($options['whitespace']) {
- return str_repeat("\t", $depth) . $val . "\n";
- }
- return $val;
- }
-}
-
-/**
- * Manages application-wide namespaces and XML parsing/generation settings.
- * Private class, used exclusively within scope of XML class.
- *
- * @access private
- */
-class XmlManager {
-
-/**
- * Global XML namespaces. Used in all XML documents processed by this application
- *
- * @var array
- * @access public
- */
- public $namespaces = array();
-
-/**
- * Global XML document parsing/generation settings.
- *
- * @var array
- * @access public
- */
- public $options = array();
-
-/**
- * Map of common namespace URIs
- *
- * @access private
- * @var array
- */
- public $defaultNamespaceMap = array(
- 'dc' => 'http://purl.org/dc/elements/1.1/', // Dublin Core
- 'dct' => 'http://purl.org/dc/terms/', // Dublin Core Terms
- 'g' => 'http://base.google.com/ns/1.0', // Google Base
- 'rc' => 'http://purl.org/rss/1.0/modules/content/', // RSS 1.0 Content Module
- 'wf' => 'http://wellformedweb.org/CommentAPI/', // Well-Formed Web Comment API
- 'fb' => 'http://rssnamespace.org/feedburner/ext/1.0', // FeedBurner extensions
- 'lj' => 'http://www.livejournal.org/rss/lj/1.0/', // Live Journal
- 'itunes' => 'http://www.itunes.com/dtds/podcast-1.0.dtd', // iTunes
- 'xhtml' => 'http://www.w3.org/1999/xhtml', // XHTML,
- 'atom' => 'http://www.w3.org/2005/Atom' // Atom
- );
-
-/**
- * Returns a reference to the global XML object that manages app-wide XML settings
- *
- * @return object
- */
- public function &getInstance() {
- static $instance = array();
-
- if (!$instance) {
- $instance[0] =& new XmlManager();
- }
- return $instance[0];
- }
-}
+}
\ No newline at end of file
diff --git a/cake/tests/cases/basics.test.php b/cake/tests/cases/basics.test.php
index f66f98986..8df308ba9 100644
--- a/cake/tests/cases/basics.test.php
+++ b/cake/tests/cases/basics.test.php
@@ -200,6 +200,34 @@ class BasicsTest extends CakeTestCase {
$result = h($in);
$expected = array('this & that', '<p>Which one</p>');
$this->assertEqual($expected, $result);
+
+ $string = ' & ';
+ $result = h($string);
+ $this->assertEqual('<foo> & ', $result);
+
+ $string = ' & ';
+ $result = h($string, false);
+ $this->assertEqual('<foo> & ', $result);
+
+ $string = ' & ';
+ $result = h($string, 'UTF-8');
+ $this->assertEqual('<foo> & ', $result);
+
+ $arr = array('', ' ');
+ $result = h($arr);
+ $expected = array(
+ '<foo>',
+ ' '
+ );
+ $this->assertEqual($expected, $result);
+
+ $arr = array('', ' ');
+ $result = h($arr, false);
+ $expected = array(
+ '<foo>',
+ ' '
+ );
+ $this->assertEqual($expected, $result);
}
/**
diff --git a/cake/tests/cases/libs/all_xml.test.php b/cake/tests/cases/libs/all_xml.test.php
index 70625ae31..aed5b21d1 100644
--- a/cake/tests/cases/libs/all_xml.test.php
+++ b/cake/tests/cases/libs/all_xml.test.php
@@ -38,7 +38,6 @@ class AllXmlTest extends PHPUnit_Framework_TestSuite {
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'xml.test.php');
$suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers' . DS . 'rss.test.php');
- $suite->addTestFile(CORE_TEST_CASES . DS . 'libs' . DS . 'view' . DS . 'helpers' . DS . 'xml.test.php');
return $suite;
}
}
\ No newline at end of file
diff --git a/cake/tests/cases/libs/cache/memcache.test.php b/cake/tests/cases/libs/cache/memcache.test.php
index f88291105..f0c77be85 100644
--- a/cake/tests/cases/libs/cache/memcache.test.php
+++ b/cake/tests/cases/libs/cache/memcache.test.php
@@ -20,6 +20,20 @@
if (!class_exists('Cache')) {
require LIBS . 'cache.php';
}
+App::import('Core', 'cache/Memcache');
+
+
+class TestMemcacheEngine extends MemcacheEngine {
+/**
+ * public accessor to _parseServerString
+ *
+ * @param string $server
+ * @return array
+ */
+ function parseServerString($server) {
+ return $this->_parseServerString($server);
+ }
+}
/**
* MemcacheEngineTest class
@@ -121,6 +135,38 @@ class MemcacheEngineTest extends CakeTestCase {
$this->assertTrue($result);
}
+/**
+ * test connecting to an ipv6 server.
+ *
+ * @return void
+ */
+ function testConnectIpv6() {
+ $Memcache =& new MemcacheEngine();
+ $result = $Memcache->init(array(
+ 'prefix' => 'cake_',
+ 'duration' => 200,
+ 'engine' => 'Memcache',
+ 'servers' => array(
+ '[::1]:11211'
+ )
+ ));
+ $this->assertTrue($result);
+ }
+
+/**
+ * test non latin domains.
+ *
+ * @return void
+ */
+ function testParseServerStringNonLatin() {
+ $Memcache =& new TestMemcacheEngine();
+ $result = $Memcache->parseServerString('schülervz.net:13211');
+ $this->assertEqual($result, array('schülervz.net', '13211'));
+
+ $result = $Memcache->parseServerString('sülül:1111');
+ $this->assertEqual($result, array('sülül', '1111'));
+ }
+
/**
* testReadAndWriteCache method
*
diff --git a/cake/tests/cases/libs/configure.test.php b/cake/tests/cases/libs/configure.test.php
index 2d002d9d5..599e37b01 100644
--- a/cake/tests/cases/libs/configure.test.php
+++ b/cake/tests/cases/libs/configure.test.php
@@ -565,7 +565,15 @@ class AppImportTest extends CakeTestCase {
$this->assertTrue($file);
$this->assertTrue(class_exists('DboSource'));
}
+ App::build();
+ }
+/**
+ * test import() with plugins
+ *
+ * @return void
+ */
+ function testPluginImporting() {
App::build(array(
'libs' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'libs' . DS),
'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
@@ -587,10 +595,15 @@ class AppImportTest extends CakeTestCase {
$result = App::import('Helper', 'TestPlugin.OtherHelper');
$this->assertTrue($result);
$this->assertTrue(class_exists('OtherHelperHelper'));
+
+ $result = App::import('Helper', 'TestPlugin.TestPluginApp');
+ $this->assertTrue($result);
+ $this->assertTrue(class_exists('TestPluginAppHelper'));
$result = App::import('Datasource', 'TestPlugin.TestSource');
$this->assertTrue($result);
$this->assertTrue(class_exists('TestSource'));
+
App::build();
}
diff --git a/cake/tests/cases/libs/controller/components/request_handler.test.php b/cake/tests/cases/libs/controller/components/request_handler.test.php
index fabe6412c..d58ca6688 100644
--- a/cake/tests/cases/libs/controller/components/request_handler.test.php
+++ b/cake/tests/cases/libs/controller/components/request_handler.test.php
@@ -331,11 +331,11 @@ class RequestHandlerComponentTest extends CakeTestCase {
* @return void
*/
function testRenderAs() {
- $this->assertFalse(in_array('Xml', $this->Controller->helpers));
- $this->RequestHandler->renderAs($this->Controller, 'xml');
- $this->assertTrue(in_array('Xml', $this->Controller->helpers));
+ $this->assertFalse(in_array('Rss', $this->Controller->helpers));
+ $this->RequestHandler->renderAs($this->Controller, 'rss');
+ $this->assertTrue(in_array('Rss', $this->Controller->helpers));
- $this->Controller->viewPath = 'request_handler_test\\xml';
+ $this->Controller->viewPath = 'request_handler_test\\rss';
$this->RequestHandler->renderAs($this->Controller, 'js');
$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'js');
}
@@ -425,8 +425,6 @@ class RequestHandlerComponentTest extends CakeTestCase {
$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'xml');
$this->assertEqual($this->Controller->layoutPath, 'xml');
- $this->assertTrue(in_array('Xml', $this->Controller->helpers));
-
$this->RequestHandler->renderAs($this->Controller, 'js');
$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'js');
$this->assertEqual($this->Controller->layoutPath, 'js');
diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php
index 6b725da77..e0506805c 100644
--- a/cake/tests/cases/libs/controller/components/security.test.php
+++ b/cake/tests/cases/libs/controller/components/security.test.php
@@ -1054,6 +1054,7 @@ DIGEST;
DIGEST;
$expected = array(
'username' => 'Mufasa',
+ 'realm' => 'testrealm@host.com',
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
'uri' => '/dir/index.html',
'qop' => 'auth',
@@ -1088,6 +1089,7 @@ DIGEST;
DIGEST;
$expected = array(
'username' => 'Mufasa',
+ 'realm' => 'testrealm@host.com',
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
'uri' => '/dir/index.html',
'qop' => 'auth',
@@ -1103,6 +1105,39 @@ DIGEST;
$this->assertNull($result);
}
+/**
+ * test parsing digest information with email addresses
+ *
+ * @return void
+ */
+ function testParseDigestAuthEmailAddress() {
+ $this->Controller->Security->startup($this->Controller);
+ $digest = << 'mark@example.com',
+ 'realm' => 'testrealm@host.com',
+ 'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
+ 'uri' => '/dir/index.html',
+ 'qop' => 'auth',
+ 'nc' => '00000001',
+ 'cnonce' => '0a4f113b',
+ 'response' => '6629fae49393a05397450978507c4ef1',
+ 'opaque' => '5ccc069c403ebaf9f0171e9517f40e41'
+ );
+ $result = $this->Controller->Security->parseDigestAuthData($digest);
+ $this->assertIdentical($result, $expected);
+ }
+
/**
* testFormDisabledFields method
*
diff --git a/cake/tests/cases/libs/model/cake_schema.test.php b/cake/tests/cases/libs/model/cake_schema.test.php
index 05396f265..0be67982d 100644
--- a/cake/tests/cases/libs/model/cake_schema.test.php
+++ b/cake/tests/cases/libs/model/cake_schema.test.php
@@ -680,17 +680,17 @@ class CakeSchemaTest extends CakeTestCase {
'name' => 'TestApp',
'models' => array('SchemaCrossDatabase', 'SchemaPost')
));
- unset($read['tables']['missing']);
$this->assertTrue(isset($read['tables']['posts']));
- $this->assertFalse(isset($read['tables']['cross_database']));
+ $this->assertFalse(isset($read['tables']['cross_database']), 'Cross database should not appear');
+ $this->assertFalse(isset($read['tables']['missing']['cross_database']), 'Cross database should not appear');
$read = $this->Schema->read(array(
'connection' => 'test2',
'name' => 'TestApp',
'models' => array('SchemaCrossDatabase', 'SchemaPost')
));
- unset($read['tables']['missing']);
- $this->assertFalse(isset($read['tables']['posts']));
+ $this->assertFalse(isset($read['tables']['posts']), 'Posts should not appear');
+ $this->assertFalse(isset($read['tables']['posts']), 'Posts should not appear');
$this->assertTrue(isset($read['tables']['cross_database']));
$fixture->drop($db2);
diff --git a/cake/tests/cases/libs/model/model_delete.test.php b/cake/tests/cases/libs/model/model_delete.test.php
index 7cc92aaf7..be790427f 100644
--- a/cake/tests/cases/libs/model/model_delete.test.php
+++ b/cake/tests/cases/libs/model/model_delete.test.php
@@ -267,446 +267,489 @@ class ModelDeleteTest extends BaseModelTest {
}
- /**
- * test that delete() updates the correct records counterCache() records.
- *
- * @return void
- */
- function testDeleteUpdatingCounterCacheCorrectly() {
- $this->loadFixtures('CounterCacheUser', 'CounterCachePost');
- $User = new CounterCacheUser();
+/**
+ * test that delete() updates the correct records counterCache() records.
+ *
+ * @return void
+ */
+ function testDeleteUpdatingCounterCacheCorrectly() {
+ $this->loadFixtures('CounterCacheUser', 'CounterCachePost');
+ $User = new CounterCacheUser();
- $User->Post->delete(3);
- $result = $User->read(null, 301);
- $this->assertEqual($result['User']['post_count'], 0);
+ $User->Post->delete(3);
+ $result = $User->read(null, 301);
+ $this->assertEqual($result['User']['post_count'], 0);
- $result = $User->read(null, 66);
- $this->assertEqual($result['User']['post_count'], 2);
- }
+ $result = $User->read(null, 66);
+ $this->assertEqual($result['User']['post_count'], 2);
+ }
- /**
- * testDeleteAll method
- *
- * @access public
- * @return void
- */
- function testDeleteAll() {
- $this->loadFixtures('Article');
- $TestModel = new Article();
+/**
+ * testDeleteAll method
+ *
+ * @access public
+ * @return void
+ */
+ function testDeleteAll() {
+ $this->loadFixtures('Article');
+ $TestModel = new Article();
- $data = array('Article' => array(
- 'user_id' => 2,
+ $data = array('Article' => array(
+ 'user_id' => 2,
+ 'id' => 4,
+ 'title' => 'Fourth Article',
+ 'published' => 'N'
+ ));
+ $result = $TestModel->set($data) && $TestModel->save();
+ $this->assertTrue($result);
+
+ $data = array('Article' => array(
+ 'user_id' => 2,
+ 'id' => 5,
+ 'title' => 'Fifth Article',
+ 'published' => 'Y'
+ ));
+ $result = $TestModel->set($data) && $TestModel->save();
+ $this->assertTrue($result);
+
+ $data = array('Article' => array(
+ 'user_id' => 1,
+ 'id' => 6,
+ 'title' => 'Sixth Article',
+ 'published' => 'N'
+ ));
+ $result = $TestModel->set($data) && $TestModel->save();
+ $this->assertTrue($result);
+
+ $TestModel->recursive = -1;
+ $result = $TestModel->find('all', array(
+ 'fields' => array('id', 'user_id', 'title', 'published')
+ ));
+
+ $expected = array(
+ array('Article' => array(
+ 'id' => 1,
+ 'user_id' => 1,
+ 'title' => 'First Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 2,
+ 'user_id' => 3,
+ 'title' => 'Second Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 3,
+ 'user_id' => 1,
+ 'title' => 'Third Article',
+ 'published' => 'Y')),
+ array('Article' => array(
'id' => 4,
+ 'user_id' => 2,
'title' => 'Fourth Article',
'published' => 'N'
- ));
- $result = $TestModel->set($data) && $TestModel->save();
- $this->assertTrue($result);
-
- $data = array('Article' => array(
- 'user_id' => 2,
+ )),
+ array('Article' => array(
'id' => 5,
+ 'user_id' => 2,
'title' => 'Fifth Article',
'published' => 'Y'
- ));
- $result = $TestModel->set($data) && $TestModel->save();
- $this->assertTrue($result);
-
- $data = array('Article' => array(
- 'user_id' => 1,
+ )),
+ array('Article' => array(
'id' => 6,
+ 'user_id' => 1,
'title' => 'Sixth Article',
'published' => 'N'
- ));
- $result = $TestModel->set($data) && $TestModel->save();
- $this->assertTrue($result);
+ )));
- $TestModel->recursive = -1;
- $result = $TestModel->find('all', array(
- 'fields' => array('id', 'user_id', 'title', 'published')
- ));
+ $this->assertEqual($result, $expected);
- $expected = array(
- array('Article' => array(
- 'id' => 1,
- 'user_id' => 1,
- 'title' => 'First Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 2,
- 'user_id' => 3,
- 'title' => 'Second Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 3,
- 'user_id' => 1,
- 'title' => 'Third Article',
- 'published' => 'Y')),
- array('Article' => array(
- 'id' => 4,
- 'user_id' => 2,
- 'title' => 'Fourth Article',
- 'published' => 'N'
- )),
- array('Article' => array(
- 'id' => 5,
- 'user_id' => 2,
- 'title' => 'Fifth Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 6,
- 'user_id' => 1,
- 'title' => 'Sixth Article',
- 'published' => 'N'
- )));
+ $result = $TestModel->deleteAll(array('Article.published' => 'N'));
+ $this->assertTrue($result);
- $this->assertEqual($result, $expected);
+ $TestModel->recursive = -1;
+ $result = $TestModel->find('all', array(
+ 'fields' => array('id', 'user_id', 'title', 'published')
+ ));
+ $expected = array(
+ array('Article' => array(
+ 'id' => 1,
+ 'user_id' => 1,
+ 'title' => 'First Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 2,
+ 'user_id' => 3,
+ 'title' => 'Second Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 3,
+ 'user_id' => 1,
+ 'title' => 'Third Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 5,
+ 'user_id' => 2,
+ 'title' => 'Fifth Article',
+ 'published' => 'Y'
+ )));
+ $this->assertEqual($result, $expected);
- $result = $TestModel->deleteAll(array('Article.published' => 'N'));
- $this->assertTrue($result);
+ $data = array('Article.user_id' => array(2, 3));
+ $result = $TestModel->deleteAll($data, true, true);
+ $this->assertTrue($result);
- $TestModel->recursive = -1;
- $result = $TestModel->find('all', array(
- 'fields' => array('id', 'user_id', 'title', 'published')
- ));
- $expected = array(
- array('Article' => array(
- 'id' => 1,
- 'user_id' => 1,
- 'title' => 'First Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 2,
- 'user_id' => 3,
- 'title' => 'Second Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 3,
- 'user_id' => 1,
- 'title' => 'Third Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 5,
- 'user_id' => 2,
- 'title' => 'Fifth Article',
- 'published' => 'Y'
- )));
- $this->assertEqual($result, $expected);
+ $TestModel->recursive = -1;
+ $result = $TestModel->find('all', array(
+ 'fields' => array('id', 'user_id', 'title', 'published')
+ ));
+ $expected = array(
+ array('Article' => array(
+ 'id' => 1,
+ 'user_id' => 1,
+ 'title' => 'First Article',
+ 'published' => 'Y'
+ )),
+ array('Article' => array(
+ 'id' => 3,
+ 'user_id' => 1,
+ 'title' => 'Third Article',
+ 'published' => 'Y'
+ )));
+ $this->assertEqual($result, $expected);
- $data = array('Article.user_id' => array(2, 3));
- $result = $TestModel->deleteAll($data, true, true);
- $this->assertTrue($result);
+ $result = $TestModel->deleteAll(array('Article.user_id' => 999));
+ $this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
- $TestModel->recursive = -1;
- $result = $TestModel->find('all', array(
- 'fields' => array('id', 'user_id', 'title', 'published')
- ));
- $expected = array(
- array('Article' => array(
- 'id' => 1,
- 'user_id' => 1,
- 'title' => 'First Article',
- 'published' => 'Y'
- )),
- array('Article' => array(
- 'id' => 3,
- 'user_id' => 1,
- 'title' => 'Third Article',
- 'published' => 'Y'
- )));
- $this->assertEqual($result, $expected);
+ $this->expectError();
+ ob_start();
+ $result = $TestModel->deleteAll(array('Article.non_existent_field' => 999));
+ ob_get_clean();
+ $this->assertFalse($result, 'deleteAll returned true when find query generated sql error. %s');
+ }
- $result = $TestModel->deleteAll(array('Article.user_id' => 999));
- $this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
+/**
+ * testRecursiveDel method
+ *
+ * @access public
+ * @return void
+ */
+ function testRecursiveDel() {
+ $this->loadFixtures('Article', 'Comment', 'Attachment');
+ $TestModel = new Article();
- $this->expectError();
- ob_start();
- $result = $TestModel->deleteAll(array('Article.non_existent_field' => 999));
- ob_get_clean();
- $this->assertFalse($result, 'deleteAll returned true when find query generated sql error. %s');
- }
+ $result = $TestModel->delete(2);
+ $this->assertTrue($result);
- /**
- * testRecursiveDel method
- *
- * @access public
- * @return void
- */
- function testRecursiveDel() {
- $this->loadFixtures('Article', 'Comment', 'Attachment');
- $TestModel = new Article();
+ $TestModel->recursive = 2;
+ $result = $TestModel->read(null, 2);
+ $this->assertFalse($result);
- $result = $TestModel->delete(2);
- $this->assertTrue($result);
+ $result = $TestModel->Comment->read(null, 5);
+ $this->assertFalse($result);
- $TestModel->recursive = 2;
- $result = $TestModel->read(null, 2);
- $this->assertFalse($result);
+ $result = $TestModel->Comment->read(null, 6);
+ $this->assertFalse($result);
- $result = $TestModel->Comment->read(null, 5);
- $this->assertFalse($result);
+ $result = $TestModel->Comment->Attachment->read(null, 1);
+ $this->assertFalse($result);
- $result = $TestModel->Comment->read(null, 6);
- $this->assertFalse($result);
+ $result = $TestModel->find('count');
+ $this->assertEqual($result, 2);
- $result = $TestModel->Comment->Attachment->read(null, 1);
- $this->assertFalse($result);
+ $result = $TestModel->Comment->find('count');
+ $this->assertEqual($result, 4);
- $result = $TestModel->find('count');
- $this->assertEqual($result, 2);
+ $result = $TestModel->Comment->Attachment->find('count');
+ $this->assertEqual($result, 0);
+ }
- $result = $TestModel->Comment->find('count');
- $this->assertEqual($result, 4);
+/**
+ * testDependentExclusiveDelete method
+ *
+ * @access public
+ * @return void
+ */
+ function testDependentExclusiveDelete() {
+ $this->loadFixtures('Article', 'Comment');
+ $TestModel = new Article10();
- $result = $TestModel->Comment->Attachment->find('count');
- $this->assertEqual($result, 0);
- }
+ $result = $TestModel->find('all');
+ $this->assertEqual(count($result[0]['Comment']), 4);
+ $this->assertEqual(count($result[1]['Comment']), 2);
+ $this->assertEqual($TestModel->Comment->find('count'), 6);
- /**
- * testDependentExclusiveDelete method
- *
- * @access public
- * @return void
- */
- function testDependentExclusiveDelete() {
- $this->loadFixtures('Article', 'Comment');
- $TestModel = new Article10();
+ $TestModel->delete(1);
+ $this->assertEqual($TestModel->Comment->find('count'), 2);
+ }
- $result = $TestModel->find('all');
- $this->assertEqual(count($result[0]['Comment']), 4);
- $this->assertEqual(count($result[1]['Comment']), 2);
- $this->assertEqual($TestModel->Comment->find('count'), 6);
+/**
+ * testDeleteLinks method
+ *
+ * @access public
+ * @return void
+ */
+ function testDeleteLinks() {
+ $this->loadFixtures('Article', 'ArticlesTag', 'Tag');
+ $TestModel = new Article();
- $TestModel->delete(1);
- $this->assertEqual($TestModel->Comment->find('count'), 2);
- }
+ $result = $TestModel->ArticlesTag->find('all');
+ $expected = array(
+ array('ArticlesTag' => array(
+ 'article_id' => '1',
+ 'tag_id' => '1'
+ )),
+ array('ArticlesTag' => array(
+ 'article_id' => '1',
+ 'tag_id' => '2'
+ )),
+ array('ArticlesTag' => array(
+ 'article_id' => '2',
+ 'tag_id' => '1'
+ )),
+ array('ArticlesTag' => array(
+ 'article_id' => '2',
+ 'tag_id' => '3'
+ )));
+ $this->assertEqual($result, $expected);
- /**
- * testDeleteLinks method
- *
- * @access public
- * @return void
- */
- function testDeleteLinks() {
- $this->loadFixtures('Article', 'ArticlesTag', 'Tag');
- $TestModel = new Article();
+ $TestModel->delete(1);
+ $result = $TestModel->ArticlesTag->find('all');
- $result = $TestModel->ArticlesTag->find('all');
- $expected = array(
- array('ArticlesTag' => array(
- 'article_id' => '1',
- 'tag_id' => '1'
- )),
- array('ArticlesTag' => array(
- 'article_id' => '1',
- 'tag_id' => '2'
- )),
- array('ArticlesTag' => array(
- 'article_id' => '2',
- 'tag_id' => '1'
- )),
- array('ArticlesTag' => array(
- 'article_id' => '2',
- 'tag_id' => '3'
- )));
- $this->assertEqual($result, $expected);
+ $expected = array(
+ array('ArticlesTag' => array(
+ 'article_id' => '2',
+ 'tag_id' => '1'
+ )),
+ array('ArticlesTag' => array(
+ 'article_id' => '2',
+ 'tag_id' => '3'
+ )));
+ $this->assertEqual($result, $expected);
- $TestModel->delete(1);
- $result = $TestModel->ArticlesTag->find('all');
+ $result = $TestModel->deleteAll(array('Article.user_id' => 999));
+ $this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
+ }
- $expected = array(
- array('ArticlesTag' => array(
- 'article_id' => '2',
- 'tag_id' => '1'
- )),
- array('ArticlesTag' => array(
- 'article_id' => '2',
- 'tag_id' => '3'
- )));
- $this->assertEqual($result, $expected);
+/**
+ * test deleteLinks with Multiple habtm associations
+ *
+ * @return void
+ */
+ function testDeleteLinksWithMultipleHabtmAssociations() {
+ $this->loadFixtures('JoinA', 'JoinB', 'JoinC', 'JoinAB', 'JoinAC');
+ $JoinA = new JoinA();
- $result = $TestModel->deleteAll(array('Article.user_id' => 999));
- $this->assertTrue($result, 'deleteAll returned false when all no records matched conditions. %s');
- }
+ //create two new join records to expose the issue.
+ $JoinA->JoinAsJoinC->create(array(
+ 'join_a_id' => 1,
+ 'join_c_id' => 2,
+ ));
+ $JoinA->JoinAsJoinC->save();
+ $JoinA->JoinAsJoinB->create(array(
+ 'join_a_id' => 1,
+ 'join_b_id' => 2,
+ ));
+ $JoinA->JoinAsJoinB->save();
- /**
- * test deleteLinks with Multiple habtm associations
- *
- * @return void
- */
- function testDeleteLinksWithMultipleHabtmAssociations() {
- $this->loadFixtures('JoinA', 'JoinB', 'JoinC', 'JoinAB', 'JoinAC');
- $JoinA = new JoinA();
+ $result = $JoinA->delete(1);
+ $this->assertTrue($result, 'Delete failed %s');
- //create two new join records to expose the issue.
- $JoinA->JoinAsJoinC->create(array(
- 'join_a_id' => 1,
- 'join_c_id' => 2,
- ));
- $JoinA->JoinAsJoinC->save();
- $JoinA->JoinAsJoinB->create(array(
- 'join_a_id' => 1,
- 'join_b_id' => 2,
- ));
- $JoinA->JoinAsJoinB->save();
+ $joinedBs = $JoinA->JoinAsJoinB->find('count', array(
+ 'conditions' => array('JoinAsJoinB.join_a_id' => 1)
+ ));
+ $this->assertEqual($joinedBs, 0, 'JoinA/JoinB link records left over. %s');
- $result = $JoinA->delete(1);
- $this->assertTrue($result, 'Delete failed %s');
+ $joinedBs = $JoinA->JoinAsJoinC->find('count', array(
+ 'conditions' => array('JoinAsJoinC.join_a_id' => 1)
+ ));
+ $this->assertEqual($joinedBs, 0, 'JoinA/JoinC link records left over. %s');
+ }
- $joinedBs = $JoinA->JoinAsJoinB->find('count', array(
- 'conditions' => array('JoinAsJoinB.join_a_id' => 1)
- ));
- $this->assertEqual($joinedBs, 0, 'JoinA/JoinB link records left over. %s');
+/**
+ * testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable method
+ *
+ * @access public
+ * @return void
+ */
+ function testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable() {
- $joinedBs = $JoinA->JoinAsJoinC->find('count', array(
- 'conditions' => array('JoinAsJoinC.join_a_id' => 1)
- ));
- $this->assertEqual($joinedBs, 0, 'JoinA/JoinC link records left over. %s');
- }
+ $this->loadFixtures('Apple', 'Device', 'ThePaperMonkies');
+ $ThePaper = new ThePaper();
+ $ThePaper->id = 1;
+ $ThePaper->save(array('Monkey' => array(2, 3)));
- /**
- * testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable method
- *
- * @access public
- * @return void
- */
- function testHabtmDeleteLinksWhenNoPrimaryKeyInJoinTable() {
+ $result = $ThePaper->findById(1);
+ $expected = array(
+ array(
+ 'id' => '2',
+ 'device_type_id' => '1',
+ 'name' => 'Device 2',
+ 'typ' => '1'
+ ),
+ array(
+ 'id' => '3',
+ 'device_type_id' => '1',
+ 'name' => 'Device 3',
+ 'typ' => '2'
+ ));
+ $this->assertEqual($result['Monkey'], $expected);
- $this->loadFixtures('Apple', 'Device', 'ThePaperMonkies');
- $ThePaper = new ThePaper();
- $ThePaper->id = 1;
- $ThePaper->save(array('Monkey' => array(2, 3)));
+ $ThePaper = new ThePaper();
+ $ThePaper->id = 2;
+ $ThePaper->save(array('Monkey' => array(2, 3)));
- $result = $ThePaper->findById(1);
- $expected = array(
- array(
- 'id' => '2',
- 'device_type_id' => '1',
- 'name' => 'Device 2',
- 'typ' => '1'
- ),
- array(
- 'id' => '3',
- 'device_type_id' => '1',
- 'name' => 'Device 3',
- 'typ' => '2'
- ));
- $this->assertEqual($result['Monkey'], $expected);
+ $result = $ThePaper->findById(2);
+ $expected = array(
+ array(
+ 'id' => '2',
+ 'device_type_id' => '1',
+ 'name' => 'Device 2',
+ 'typ' => '1'
+ ),
+ array(
+ 'id' => '3',
+ 'device_type_id' => '1',
+ 'name' => 'Device 3',
+ 'typ' => '2'
+ ));
+ $this->assertEqual($result['Monkey'], $expected);
- $ThePaper = new ThePaper();
- $ThePaper->id = 2;
- $ThePaper->save(array('Monkey' => array(2, 3)));
+ $ThePaper->delete(1);
+ $result = $ThePaper->findById(2);
+ $expected = array(
+ array(
+ 'id' => '2',
+ 'device_type_id' => '1',
+ 'name' => 'Device 2',
+ 'typ' => '1'
+ ),
+ array(
+ 'id' => '3',
+ 'device_type_id' => '1',
+ 'name' => 'Device 3',
+ 'typ' => '2'
+ ));
+ $this->assertEqual($result['Monkey'], $expected);
+ }
- $result = $ThePaper->findById(2);
- $expected = array(
- array(
- 'id' => '2',
- 'device_type_id' => '1',
- 'name' => 'Device 2',
- 'typ' => '1'
- ),
- array(
- 'id' => '3',
- 'device_type_id' => '1',
- 'name' => 'Device 3',
- 'typ' => '2'
- ));
- $this->assertEqual($result['Monkey'], $expected);
+/**
+ * test that beforeDelete returning false can abort deletion.
+ *
+ * @return void
+ */
+ function testBeforeDeleteDeleteAbortion() {
+ $this->loadFixtures('Post');
+ $Model = new CallbackPostTestModel();
+ $Model->beforeDeleteReturn = false;
- $ThePaper->delete(1);
- $result = $ThePaper->findById(2);
- $expected = array(
- array(
- 'id' => '2',
- 'device_type_id' => '1',
- 'name' => 'Device 2',
- 'typ' => '1'
- ),
- array(
- 'id' => '3',
- 'device_type_id' => '1',
- 'name' => 'Device 3',
- 'typ' => '2'
- ));
- $this->assertEqual($result['Monkey'], $expected);
- }
+ $result = $Model->delete(1);
+ $this->assertFalse($result);
- /**
- * test that beforeDelete returning false can abort deletion.
- *
- * @return void
- */
- function testBeforeDeleteDeleteAbortion() {
- $this->loadFixtures('Post');
- $Model = new CallbackPostTestModel();
- $Model->beforeDeleteReturn = false;
+ $exists = $Model->findById(1);
+ $this->assertTrue(is_array($exists));
+ }
- $result = $Model->delete(1);
- $this->assertFalse($result);
+/**
+ * test for a habtm deletion error that occurs in postgres but should not.
+ * And should not occur in any dbo.
+ *
+ * @return void
+ */
+ function testDeleteHabtmPostgresFailure() {
+ $this->loadFixtures('Article', 'Tag', 'ArticlesTag');
- $exists = $Model->findById(1);
- $this->assertTrue(is_array($exists));
- }
+ $Article = ClassRegistry::init('Article');
+ $Article->hasAndBelongsToMany['Tag']['unique'] = true;
- /**
- * test for a habtm deletion error that occurs in postgres but should not.
- * And should not occur in any dbo.
- *
- * @return void
- */
- function testDeleteHabtmPostgresFailure() {
- $this->loadFixtures('Article', 'Tag', 'ArticlesTag');
+ $Tag = ClassRegistry::init('Tag');
+ $Tag->bindModel(array('hasAndBelongsToMany' => array(
+ 'Article' => array(
+ 'className' => 'Article',
+ 'unique' => true
+ )
+ )), true);
- $Article = ClassRegistry::init('Article');
- $Article->hasAndBelongsToMany['Tag']['unique'] = true;
+ // Article 1 should have Tag.1 and Tag.2
+ $before = $Article->find("all", array(
+ "conditions" => array("Article.id" => 1),
+ ));
+ $this->assertEqual(count($before[0]['Tag']), 2, 'Tag count for Article.id = 1 is incorrect, should be 2 %s');
- $Tag = ClassRegistry::init('Tag');
- $Tag->bindModel(array('hasAndBelongsToMany' => array(
- 'Article' => array(
- 'className' => 'Article',
- 'unique' => true
- )
- )), true);
+ // From now on, Tag #1 is only associated with Post #1
+ $submitted_data = array(
+ "Tag" => array("id" => 1, 'tag' => 'tag1'),
+ "Article" => array(
+ "Article" => array(1)
+ )
+ );
+ $Tag->save($submitted_data);
- // Article 1 should have Tag.1 and Tag.2
- $before = $Article->find("all", array(
- "conditions" => array("Article.id" => 1),
- ));
- $this->assertEqual(count($before[0]['Tag']), 2, 'Tag count for Article.id = 1 is incorrect, should be 2 %s');
+ // One more submission (The other way around) to make sure the reverse save looks good.
+ $submitted_data = array(
+ "Article" => array("id" => 2, 'title' => 'second article'),
+ "Tag" => array(
+ "Tag" => array(2, 3)
+ )
+ );
+ // ERROR:
+ // Postgresql: DELETE FROM "articles_tags" WHERE tag_id IN ('1', '3')
+ // MySQL: DELETE `ArticlesTag` FROM `articles_tags` AS `ArticlesTag` WHERE `ArticlesTag`.`article_id` = 2 AND `ArticlesTag`.`tag_id` IN (1, 3)
+ $Article->save($submitted_data);
- // From now on, Tag #1 is only associated with Post #1
- $submitted_data = array(
- "Tag" => array("id" => 1, 'tag' => 'tag1'),
- "Article" => array(
- "Article" => array(1)
- )
- );
- $Tag->save($submitted_data);
+ // Want to make sure Article #1 has Tag #1 and Tag #2 still.
+ $after = $Article->find("all", array(
+ "conditions" => array("Article.id" => 1),
+ ));
- // One more submission (The other way around) to make sure the reverse save looks good.
- $submitted_data = array(
- "Article" => array("id" => 2, 'title' => 'second article'),
- "Tag" => array(
- "Tag" => array(2, 3)
- )
- );
- // ERROR:
- // Postgresql: DELETE FROM "articles_tags" WHERE tag_id IN ('1', '3')
- // MySQL: DELETE `ArticlesTag` FROM `articles_tags` AS `ArticlesTag` WHERE `ArticlesTag`.`article_id` = 2 AND `ArticlesTag`.`tag_id` IN (1, 3)
- $Article->save($submitted_data);
+ // Removing Article #2 from Tag #1 is all that should have happened.
+ $this->assertEqual(count($before[0]["Tag"]), count($after[0]["Tag"]));
+ }
- // Want to make sure Article #1 has Tag #1 and Tag #2 still.
- $after = $Article->find("all", array(
- "conditions" => array("Article.id" => 1),
- ));
+/**
+ * test that deleting records inside the beforeDelete doesn't truncate the table.
+ *
+ * @return void
+ */
+ function testBeforeDeleteWipingTable() {
+ $this->loadFixtures('Comment');
- // Removing Article #2 from Tag #1 is all that should have happened.
- $this->assertEqual(count($before[0]["Tag"]), count($after[0]["Tag"]));
- }
+ $Comment =& new BeforeDeleteComment();
+ // Delete 3 records.
+ $Comment->delete(4);
+ $result = $Comment->find('count');
+
+ $this->assertTrue($result > 1, 'Comments are all gone.');
+ $Comment->create(array(
+ 'article_id' => 1,
+ 'user_id' => 2,
+ 'comment' => 'new record',
+ 'published' => 'Y'
+ ));
+ $Comment->save();
+
+ $Comment->delete(5);
+ $result = $Comment->find('count');
+
+ $this->assertTrue($result > 1, 'Comments are all gone.');
+ }
+
+/**
+ * test that deleting the same record from the beforeDelete and the delete doesn't truncate the table.
+ *
+ * @return void
+ */
+ function testBeforeDeleteWipingTableWithDuplicateDelete() {
+ $this->loadFixtures('Comment');
+
+ $Comment =& new BeforeDeleteComment();
+ $Comment->delete(1);
+
+ $result = $Comment->find('count');
+ $this->assertTrue($result > 1, 'Comments are all gone.');
+ }
}
diff --git a/cake/tests/cases/libs/model/model_validation.test.php b/cake/tests/cases/libs/model/model_validation.test.php
index e4b9e1c3b..1481c0bfc 100644
--- a/cake/tests/cases/libs/model/model_validation.test.php
+++ b/cake/tests/cases/libs/model/model_validation.test.php
@@ -631,7 +631,7 @@ class ModelValidationTest extends BaseModelTest {
$Something->create();
$result = $Something->saveAll($data, array('validate' => 'first'));
- $this->assertEquals($result, array());
+ $this->assertFalse($result);
$this->assertEqual($JoinThing->validationErrors, $expectedError);
$count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id'])));
diff --git a/cake/tests/cases/libs/model/model_write.test.php b/cake/tests/cases/libs/model/model_write.test.php
index 32f70e8db..fcf7c8e8f 100644
--- a/cake/tests/cases/libs/model/model_write.test.php
+++ b/cake/tests/cases/libs/model/model_write.test.php
@@ -2737,7 +2737,7 @@ class ModelWriteTest extends BaseModelTest {
'Attachment' => array('attachment' => '')
),
array('validate' => 'first')
- ), array());
+ ), false);
$expected = array(
'Comment' => array('comment' => 'This field cannot be left blank'),
'Attachment' => array('attachment' => 'This field cannot be left blank')
@@ -2954,7 +2954,7 @@ class ModelWriteTest extends BaseModelTest {
*
* @return void
*/
- function testSaveAllTransactionNoRollback() {
+ function testSaveAllManyRowsTransactionNoRollback() {
$this->loadFixtures('Post');
$this->getMock('DboSource', array(), array(), 'MockTransactionDboSource');
@@ -2984,6 +2984,54 @@ class ModelWriteTest extends BaseModelTest {
$Post->saveAll($data, array('atomic' => true));
}
+/**
+ * test saveAll with transactions and ensure there is no missing rollback.
+ *
+ * @return void
+ */
+ function testSaveAllAssociatedTransactionNoRollback() {
+ $testDb = ConnectionManager::getDataSource('test');
+
+ $mock = $this->getMock('DboSource', array(), array(), 'MockTransactionAssociatedDboSource', false);
+ $db =& ConnectionManager::create('mock_transaction_assoc', array(
+ 'datasource' => 'MockTransactionAssociatedDbo',
+ ));
+ $this->mockObjects[] = $db;
+ $db->columns = $testDb->columns;
+
+ $db->expects($this->once())->method('rollback');
+ $db->expects($this->any())->method('isInterfaceSupported')
+ ->will($this->returnValue(true));
+ $db->expects($this->any())->method('describe')
+ ->will($this->returnValue(array(
+ 'id' => array('type' => 'integer'),
+ 'title' => array('type' => 'string'),
+ 'body' => array('type' => 'text'),
+ 'published' => array('type' => 'string')
+ )));
+
+ $Post =& new Post();
+ $Post->useDbConfig = 'mock_transaction_assoc';
+ $Post->Author->useDbConfig = 'mock_transaction_assoc';
+
+ $Post->Author->validate = array(
+ 'user' => array('rule' => array('notEmpty'))
+ );
+
+ $data = array(
+ 'Post' => array(
+ 'title' => 'New post',
+ 'body' => 'Content',
+ 'published' => 'Y'
+ ),
+ 'Author' => array(
+ 'user' => '',
+ 'password' => "sekret"
+ )
+ );
+ $Post->saveAll($data);
+ }
+
/**
* testSaveAllTransaction method
*
@@ -3451,7 +3499,7 @@ class ModelWriteTest extends BaseModelTest {
)
), array('validate' => 'first'));
- $this->assertEquals($result, array());
+ $this->assertFalse($result);
$result = $model->find('all');
$this->assertEqual($result, array());
diff --git a/cake/tests/cases/libs/model/models.php b/cake/tests/cases/libs/model/models.php
index 4e3c44b20..8770e2c01 100644
--- a/cake/tests/cases/libs/model/models.php
+++ b/cake/tests/cases/libs/model/models.php
@@ -284,6 +284,24 @@ class Article extends CakeTestModel {
}
}
+/**
+ * Model stub for beforeDelete testing
+ *
+ * @see #250
+ * @package cake.tests
+ */
+class BeforeDeleteComment extends CakeTestModel {
+ var $name = 'BeforeDeleteComment';
+
+ var $useTable = 'comments';
+
+ function beforeDelete($cascade = true) {
+ $db =& $this->getDataSource();
+ $db->delete($this, array($this->alias . '.' . $this->primaryKey => array(1, 3)));
+ return true;
+ }
+}
+
/**
* NumericArticle class
*
diff --git a/cake/tests/cases/libs/sanitize.test.php b/cake/tests/cases/libs/sanitize.test.php
index 30a46564b..311964fba 100644
--- a/cake/tests/cases/libs/sanitize.test.php
+++ b/cake/tests/cases/libs/sanitize.test.php
@@ -236,6 +236,16 @@ class SanitizeTest extends CakeTestCase {
$expected = 'The "lazy" dog 'jumped' & flew over the moon. If (1+1) = 2 <em>is</em> true, (2-1) = 1 is also true';
$result = Sanitize::html($string);
$this->assertEqual($result, $expected);
+
+ $string = 'The "lazy" dog & his friend Apple® conquered the world';
+ $expected = 'The "lazy" dog & his friend Apple® conquered the world';
+ $result = Sanitize::html($string);
+ $this->assertEqual($result, $expected);
+
+ $string = 'The "lazy" dog & his friend Apple® conquered the world';
+ $expected = 'The "lazy" dog & his friend Apple® conquered the world';
+ $result = Sanitize::html($string, array('double' => false));
+ $this->assertEqual($result, $expected);
}
/**
diff --git a/cake/tests/cases/libs/set.test.php b/cake/tests/cases/libs/set.test.php
index 9e3b536f1..29f250ca2 100644
--- a/cake/tests/cases/libs/set.test.php
+++ b/cake/tests/cases/libs/set.test.php
@@ -855,6 +855,39 @@ class SetTest extends CakeTestCase {
$r = Set::extract('/file/.[type=application/zip]', $f);
$this->assertEqual($r, $expected);
+ $f = array(
+ array(
+ 'file' => array(
+ 'name' => 'zipfile.zip',
+ 'type' => 'application/zip',
+ 'tmp_name' => '/tmp/php178.tmp',
+ 'error' => 0,
+ 'size' => '564647'
+ )
+ ),
+ array(
+ 'file' => array(
+ 'name' => 'zipfile2.zip',
+ 'type' => 'application/x zip compressed',
+ 'tmp_name' => '/tmp/php179.tmp',
+ 'error' => 0,
+ 'size' => '354784'
+ )
+ ),
+ array(
+ 'file' => array(
+ 'name' => 'picture.jpg',
+ 'type' => 'image/jpeg',
+ 'tmp_name' => '/tmp/php180.tmp',
+ 'error' => 0,
+ 'size' => '21324'
+ )
+ )
+ );
+ $expected = array(array('name' => 'zipfile2.zip','type' => 'application/x zip compressed','tmp_name' => '/tmp/php179.tmp','error' => 0,'size' => '354784'));
+ $r = Set::extract('/file/.[type=application/x zip compressed]', $f);
+ $this->assertEqual($r, $expected);
+
$hasMany = array(
'Node' => array(
'id' => 1,
@@ -1132,6 +1165,27 @@ class SetTest extends CakeTestCase {
$expected = array(0 => array('Article' => array('id' => 1, 'approved' => 1)));
$result = Set::extract('/Article[approved=1]', $startingAtOne);
$this->assertEqual($result, $expected);
+
+ $items = array(
+ 240 => array(
+ 'A' => array(
+ 'field1' => 'a240',
+ 'field2' => 'a240',
+ ),
+ 'B' => array(
+ 'field1' => 'b240',
+ 'field2' => 'b240'
+ ),
+ )
+ );
+
+ $expected = array(
+ 0 => 'b240'
+ );
+
+ $result = Set::extract('/B/field1', $items);
+ $this->assertIdentical($result, $expected);
+ $this->assertIdentical($result, Set::extract('{n}.B.field1', $items));
}
/**
* testExtractWithArrays method
@@ -2674,20 +2728,20 @@ class SetTest extends CakeTestCase {
';
- $xml = new Xml($string);
+ $xml = Xml::build($string);
$result = Set::reverse($xml);
- $expected = array('Rss' => array(
+ $expected = array('rss' => array(
'version' => '2.0',
- 'Channel' => array(
+ 'channel' => array(
'title' => 'Cake PHP Google Group',
'link' => 'http://groups.google.com/group/cake-php',
'description' => 'Search this group before posting anything. There are over 20,000 posts and it's very likely your question was answered before. Visit the IRC channel #cakephp at irc.freenode.net for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
'language' => 'en',
- 'Item' => array(
+ 'item' => array(
array(
'title' => 'constructng result array when using findall',
'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
- 'description' => "i'm using cakephp to construct a logical data model array that will be passed to a flex app. I have the following model association: ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what the current output from my findall is something like this example:
Array( [0] => Array(",
+ 'description' => "i'm using cakephp to construct a logical data model array that will be passed to a flex app. I have the following model association: ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what the current output from my findall is something like this example:
Array( [0] => Array(",
'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
'author' => 'bmil...@gmail.com(bpscrugs)',
'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
@@ -2706,43 +2760,43 @@ class SetTest extends CakeTestCase {
$this->assertEqual($result, $expected);
$string ='';
- $xml = new Xml($string);
+ $xml = Xml::build($string);
$result = Set::reverse($xml);
- $expected = array('Data' => array('Post' => array('title' => 'Title of this post', 'description' => 'cool')));
+ $expected = array('data' => array('post' => array('title' => 'Title of this post', 'description' => 'cool')));
$this->assertEqual($result, $expected);
- $xml = new Xml('An example of a correctly reversed XMLNode');
+ $xml = Xml::build('An example of a correctly reversed SimpleXMLElement');
$result = Set::reverse($xml);
- $expected = array('Example' =>
+ $expected = array('example' =>
array(
- 'Item' => array(
- 'title' => 'An example of a correctly reversed XMLNode',
- 'desc' => array(),
+ 'item' => array(
+ 'title' => 'An example of a correctly reversed SimpleXMLElement',
+ 'desc' => '',
)
)
);
$this->assertEquals($result, $expected);
- $xml = new Xml('title1title2');
+ $xml = Xml::build('title1title2');
$result = Set::reverse($xml);
$expected =
- array('Example' => array(
- 'Item' => array(
+ array('example' => array(
+ 'item' => array(
'attr' => '123',
- 'Titles' => array(
- 'Title' => array('title1', 'title2')
+ 'titles' => array(
+ 'title' => array('title1', 'title2')
)
)
)
);
$this->assertEquals($result, $expected);
- $xml = new Xml('listtextforitems');
+ $xml = Xml::build('listtextforitems');
$result = Set::reverse($xml);
$expected =
- array('Example' => array(
+ array('example' => array(
'attr' => 'ex_attr',
- 'Item' => array(
+ 'item' => array(
'attr' => '123',
'titles' => 'list',
'value' => 'textforitems'
@@ -2752,7 +2806,7 @@ class SetTest extends CakeTestCase {
$this->assertEquals($result, $expected);
$string = '
-
+ Cake PHP Google Group
http://groups.google.com/group/cake-php
@@ -2783,23 +2837,23 @@ class SetTest extends CakeTestCase {
';
- $xml = new Xml($string);
+ $xml = Xml::build($string);
$result = Set::reverse($xml);
- $expected = array('Rss' => array(
+ $expected = array('rss' => array(
'version' => '2.0',
- 'Channel' => array(
+ 'channel' => array(
'title' => 'Cake PHP Google Group',
'link' => 'http://groups.google.com/group/cake-php',
'description' => 'Search this group before posting anything. There are over 20,000 posts and it's very likely your question was answered before. Visit the IRC channel #cakephp at irc.freenode.net for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
'language' => 'en',
- 'Item' => array(
+ 'item' => array(
array(
'title' => 'constructng result array when using findall',
'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
- 'description' => "i'm using cakephp to construct a logical data model array that will be passed to a flex app. I have the following model association: ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what the current output from my findall is something like this example:
Array( [0] => Array(",
+ 'description' => "i'm using cakephp to construct a logical data model array that will be passed to a flex app. I have the following model association: ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what the current output from my findall is something like this example:
Array( [0] => Array(",
- 'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f'),
- 'author' => 'bmil...@gmail.com(bpscrugs)',
- 'pubDate' => 'Fri, 28 Dec 2007 00:44:14 UT',
+ '@id' => '1',
+ 'name' => 'defect'
),
array(
- 'title' => 'Re: share views between actions?',
- 'link' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8',
- 'description' => 'Then perhaps you might do us all a favour and refrain from replying to things you do not understand. That goes especially for asinine comments. Indeed. To sum up: No comment. In my day, a simple "RTFM" would suffice. I\'ll keep in mind to ignore any further responses from you. You (and I) were referring to the *online documentation*, not other',
- 'guid' => array('isPermaLink' => 'true', 'value' => 'http://groups.google.com/group/cake-php/msg/8b350d898707dad8'),
- 'author' => 'subtropolis.z...@gmail.com(subtropolis zijn)',
- 'pubDate' => 'Fri, 28 Dec 2007 00:45:01 UT'
- )
- )
- )
- ));
- $this->assertEqual($result, $expected);
-
- $string ='';
- $xml = new Xml($string);
- $result = $xml->toArray();
- $expected = array('Data' => array('Post' => array('title' => 'Title of this post', 'description' => 'cool')));
- $this->assertEqual($result, $expected);
-
- $xml = new Xml('An example of a correctly reversed XMLNode');
- $result = Set::reverse($xml);
- $expected = array(
- 'Example' => array(
- 'Item' => array(
- 'title' => 'An example of a correctly reversed XMLNode',
- 'desc' => array(),
- )
- )
- );
- $this->assertIdentical($result, $expected);
-
- $xml = new Xml('title1title2');
- $result = $xml->toArray();
- $expected = array(
- 'Example' => array(
- 'Item' => array(
- 'attr' => '123',
- 'Titles' => array(
- 'Title' => array('title1', 'title2')
+ '@id' => '2',
+ 'name' => 'enhancement'
)
)
)
);
- $this->assertIdentical($result, $expected);
+ $this->assertEqual(Xml::toArray($obj), $expected);
- $xml = new Xml('listtextforitems');
- $result = $xml->toArray();
- $expected = array(
- 'Example' => array(
- 'attr' => 'ex_attr',
- 'Item' => array(
- 'attr' => '123',
- 'titles' => 'list',
- 'value' => 'textforitems'
- )
- )
- );
- $this->assertIdentical($result, $expected);
-
- $xml = new Xml('listtextforitems');
- $example = $xml->child('example');
- $item = $example->child('item');
- $result = $item->toArray();
-
- $expected = array(
- 'attr' => '123',
- 'titles' => 'list',
- 'value' => 'textforitems'
- );
- $this->assertIdentical($result, $expected);
-
- $string = '
-
-
- Cake PHP Google Group
- http://groups.google.com/group/cake-php
- Search this group before posting anything. There are over 20,000 posts and it's very likely your question was answered before. Visit the IRC channel #cakephp at irc.freenode.net for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.
- en
-
- constructng result array when using findall
- http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f
- i'm using cakephp to construct a logical data model array that will be <br> passed to a flex app. I have the following model association: <br> ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what <br> the current output from my findall is something like this example: <br> <p>Array( <br> [0] => Array(
- cakephp
-
-
- http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f
- bmil...@gmail.com(bpscrugs)
- Fri, 28 Dec 2007 00:44:14 UT
-
-
- Re: share views between actions?
- http://groups.google.com/group/cake-php/msg/8b350d898707dad8
- Then perhaps you might do us all a favour and refrain from replying to <br> things you do not understand. That goes especially for asinine comments. <br> Indeed. <br> To sum up: <br> No comment. <br> In my day, a simple "RTFM" would suffice. I'll keep in mind to ignore any <br> further responses from you. <br> You (and I) were referring to the *online documentation*, not other
- cakephp
-
-
- http://groups.google.com/group/cake-php/msg/8b350d898707dad8
- subtropolis.z...@gmail.com(subtropolis zijn)
- Fri, 28 Dec 2007 00:45:01 UT
-
-
- ';
-
- $xml = new Xml($string);
- $result = $xml->toArray();
-
- $expected = array('Rss' => array(
- 'version' => '2.0',
- 'Channel' => array(
- 'title' => 'Cake PHP Google Group',
- 'link' => 'http://groups.google.com/group/cake-php',
- 'description' => 'Search this group before posting anything. There are over 20,000 posts and it's very likely your question was answered before. Visit the IRC channel #cakephp at irc.freenode.net for live chat with users and developers of Cake. If you post, tell us the version of Cake, PHP, and database.',
- 'language' => 'en',
- 'Item' => array(
+ $array = array(
+ 'tags' => array(
+ 'tag' => array(
array(
- 'title' => 'constructng result array when using findall',
- 'link' => 'http://groups.google.com/group/cake-php/msg/49bc00f3bc651b4f',
- 'description' => "i'm using cakephp to construct a logical data model array that will be passed to a flex app. I have the following model association: ServiceDay->(hasMany)ServiceTi me->(hasMany)ServiceTimePrice. So what the current output from my findall is something like this example: