diff --git a/lib/Cake/Console/ConsoleOptionParser.php b/lib/Cake/Console/ConsoleOptionParser.php
index 16371165a..97726f0be 100644
--- a/lib/Cake/Console/ConsoleOptionParser.php
+++ b/lib/Cake/Console/ConsoleOptionParser.php
@@ -585,7 +585,8 @@ class ConsoleOptionParser {
 		$option = $this->_options[$name];
 		$isBoolean = $option->isBoolean();
 		$nextValue = $this->_nextToken();
-		if (!$isBoolean && !empty($nextValue) && !$this->_optionExists($nextValue)) {
+		$emptyNextValue = (empty($nextValue) && $nextValue !== '0');
+		if (!$isBoolean && !$emptyNextValue && !$this->_optionExists($nextValue)) {
 			array_shift($this->_tokens);
 			$value = $nextValue;
 		} elseif ($isBoolean) {
diff --git a/lib/Cake/Model/CakeSchema.php b/lib/Cake/Model/CakeSchema.php
index 4208cfbd7..0e7771d52 100644
--- a/lib/Cake/Model/CakeSchema.php
+++ b/lib/Cake/Model/CakeSchema.php
@@ -580,13 +580,17 @@ class CakeSchema extends Object {
 		if (is_array($values)) {
 			foreach ($values as $key => $val) {
 				if (is_array($val)) {
-					$vals[] = "'{$key}' => array('" . implode("', '", $val) . "')";
-				} elseif (!is_numeric($key)) {
+					$vals[] = "'{$key}' => array(" . implode(", ",  $this->_values($val)) . ")";
+				} else {
 					$val = var_export($val, true);
 					if ($val === 'NULL') {
 						$val = 'null';
 					}
-					$vals[] = "'{$key}' => {$val}";
+					if (!is_numeric($key)) {
+						$vals[] = "'{$key}' => {$val}";
+					} else {
+						$vals[] = "{$val}";
+					}
 				}
 			}
 		}
diff --git a/lib/Cake/Model/Datasource/Database/Mysql.php b/lib/Cake/Model/Datasource/Database/Mysql.php
index 2f274e9eb..b19df98ac 100644
--- a/lib/Cake/Model/Datasource/Database/Mysql.php
+++ b/lib/Cake/Model/Datasource/Database/Mysql.php
@@ -466,6 +466,12 @@ class Mysql extends DboSource {
 					$col[] = $idx->Column_name;
 					$index[$idx->Key_name]['column'] = $col;
 				}
+				if (!empty($idx->Sub_part)) {
+					if (!isset($index[$idx->Key_name]['length'])) {
+						$index[$idx->Key_name]['length'] = array();
+					}
+					$index[$idx->Key_name]['length'][$idx->Column_name] = $idx->Sub_part;
+				}
 			}
 			// @codingStandardsIgnoreEnd
 			$indexes->closeCursor();
@@ -566,6 +572,55 @@ class Mysql extends DboSource {
 		return array();
 	}
 
+/**
+ * Format indexes for create table
+ *
+ * @param array $indexes An array of indexes to generate SQL from
+ * @param string $table Optional table name, not used
+ * @return array An array of SQL statements for indexes
+ * @see DboSource::buildIndex()
+ */
+	public function buildIndex($indexes, $table = null) {
+		$join = array();
+		foreach ($indexes as $name => $value) {
+			$out = '';
+			if ($name === 'PRIMARY') {
+				$out .= 'PRIMARY ';
+				$name = null;
+			} else {
+				if (!empty($value['unique'])) {
+					$out .= 'UNIQUE ';
+				}
+				$name = $this->startQuote . $name . $this->endQuote;
+			}
+			// length attribute only used for MySQL datasource, for TEXT/BLOB index columns
+			$out .= 'KEY ' . $name . ' (';
+			if (is_array($value['column'])) {
+				if (isset($value['length'])) {
+					$vals = array();
+					foreach ($value['column'] as $column) {
+						$name = $this->name($column);
+						if (isset($value['length'])) {
+							$name .= $this->_buildIndexSubPart($value['length'], $column);
+						}
+						$vals[] = $name;
+					}
+					$out .= implode(', ', $vals);
+				} else {
+					$out .= implode(', ', array_map(array(&$this, 'name'), $value['column']));
+				}
+			} else {
+				$out .= $this->name($value['column']);
+				if (isset($value['length'])) {
+					$out .= $this->_buildIndexSubPart($value['length'], $value['column']);
+				}
+			}
+			$out .= ')';
+			$join[] = $out;
+		}
+		return $join;
+	}
+
 /**
  * Generate MySQL index alteration statements for a table.
  *
@@ -595,6 +650,23 @@ class Mysql extends DboSource {
 		return $alter;
 	}
 
+/**
+ * Format length for text indexes
+ *
+ * @param array $lengths An array of lengths for a single index
+ * @param string $column The column for which to generate the index length
+ * @return string Formatted length part of an index field
+ */
+	protected function _buildIndexSubPart($lengths, $column) {
+		if (is_null($lengths)) {
+			return '';
+		}
+		if (!isset($lengths[$column])) {
+			return '';
+		}
+		return '(' . $lengths[$column] . ')';
+	}
+
 /**
  * Returns an detailed array of sources (tables) in the database.
  *
diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php
index 51dd5f159..75a7c131f 100644
--- a/lib/Cake/Model/Model.php
+++ b/lib/Cake/Model/Model.php
@@ -1574,7 +1574,9 @@ class Model extends Object implements CakeEventListener {
  *
  * @param string $name Name of the table field
  * @param mixed $value Value of the field
- * @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed
+ * @param boolean|array $validate Either a boolean, or an array.
+ *   If a boolean, indicates whether or not to validate before saving.
+ *   If an array, allows control of 'validate' and 'callbacks' options.
  * @return boolean See Model::save()
  * @see Model::save()
  * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false
diff --git a/lib/Cake/Network/Email/MailTransport.php b/lib/Cake/Network/Email/MailTransport.php
index a037e3418..950459a75 100644
--- a/lib/Cake/Network/Email/MailTransport.php
+++ b/lib/Cake/Network/Email/MailTransport.php
@@ -42,11 +42,7 @@ class MailTransport extends AbstractTransport {
 		$headers = $this->_headersToString($headers, $eol);
 		$message = implode($eol, $email->message());
 
-		$params = null;
-		if (!ini_get('safe_mode')) {
-			$params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null;
-		}
-
+		$params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null;
 		$this->_mail($to, $email->subject(), $message, $headers, $params);
 		return array('headers' => $headers, 'message' => $message);
 	}
@@ -58,16 +54,20 @@ class MailTransport extends AbstractTransport {
  * @param string $subject email's subject
  * @param string $message email's body
  * @param string $headers email's custom headers
- * @param string $params additional params for sending email
+ * @param string $params additional params for sending email, will be ignored when in safe_mode
  * @throws SocketException if mail could not be sent
  * @return void
  */
 	protected function _mail($to, $subject, $message, $headers, $params = null) {
-		//@codingStandardsIgnoreStart
-		if (!@mail($to, $subject, $message, $headers, $params)) {
+		if (ini_get('safe_mode')) {
+			//@codingStandardsIgnoreStart
+			if (!@mail($to, $subject, $message, $headers)) {
+				throw new SocketException(__d('cake_dev', 'Could not send email.'));
+			}
+		} elseif (!@mail($to, $subject, $message, $headers, $params)) {
+			//@codingStandardsIgnoreEnd
 			throw new SocketException(__d('cake_dev', 'Could not send email.'));
 		}
-		//@codingStandardsIgnoreEnd
 	}
 
 }
diff --git a/lib/Cake/Routing/Route/CakeRoute.php b/lib/Cake/Routing/Route/CakeRoute.php
index 293aeb58c..83d822f7b 100644
--- a/lib/Cake/Routing/Route/CakeRoute.php
+++ b/lib/Cake/Routing/Route/CakeRoute.php
@@ -11,8 +11,7 @@
  * @since         CakePHP(tm) v 1.3
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-
-App::uses('Set', 'Utility');
+App::uses('Hash', 'Utility');
 
 /**
  * A single Route used by the Router to connect requests to
diff --git a/lib/Cake/Routing/Router.php b/lib/Cake/Routing/Router.php
index 801e88e7a..4b6dafe8a 100644
--- a/lib/Cake/Routing/Router.php
+++ b/lib/Cake/Routing/Router.php
@@ -266,18 +266,28 @@ class Router {
  * $options offers four 'special' keys. `pass`, `named`, `persist` and `routeClass`
  * have special meaning in the $options array.
  *
- * `pass` is used to define which of the routed parameters should be shifted into the pass array.  Adding a
- * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')`
+ * - `pass` is used to define which of the routed parameters should be shifted into the pass array.  Adding a
+ *   parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')`
+ * - `persist` is used to define which route parameters should be automatically included when generating
+ *   new urls. You can override persistent parameters by redefining them in a url or remove them by
+ *   setting the parameter to `false`.  Ex. `'persist' => array('lang')`
+ * - `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing,
+ *   via a custom routing class. Ex. `'routeClass' => 'SlugRoute'`
+ * - `named` is used to configure named parameters at the route level. This key uses the same options
+ *   as Router::connectNamed()
  *
- * `persist` is used to define which route parameters should be automatically included when generating
- * new urls. You can override persistent parameters by redefining them in a url or remove them by
- * setting the parameter to `false`.  Ex. `'persist' => array('lang')`
+ * You can also add additional conditions for matching routes to the $defaults array.
+ * The following conditions can be used:
  *
- * `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing,
- * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'`
+ * - `[type]` Only match requests for specific content types.
+ * - `[method]` Only match requests with specific HTTP verbs.
+ * - `[server]` Only match when $_SERVER['SERVER_NAME'] matches the given value.
  *
- * `named` is used to configure named parameters at the route level. This key uses the same options
- * as Router::connectNamed()
+ * Example of using the `[method]` condition:
+ *
+ * `Router::connect('/tasks', array('controller' => 'tasks', 'action' => 'index', '[method]' => 'GET'));`
+ *
+ * The above route will only be matched for GET requests. POST requests will fail to match this route.
  *
  * @param string $route A string describing the template of the route
  * @param array $defaults An array describing the default route parameters. These parameters will be used by default
diff --git a/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php b/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php
index 2cf265995..7d3a23ec1 100644
--- a/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php
+++ b/lib/Cake/Test/Case/Console/ConsoleOptionParserTest.php
@@ -78,6 +78,18 @@ class ConsoleOptionParserTest extends CakeTestCase {
 		$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
 	}
 
+/**
+ * test adding an option with a zero value
+ *
+ * @return void
+ */
+	public function testAddOptionZero() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('count', array());
+		$result = $parser->parse(array('--count', '0'));
+		$this->assertEquals(array('count' => '0', 'help' => false), $result[0], 'Zero parameter did not parse out');
+	}
+
 /**
  * test addOption with an object.
  *
diff --git a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php
index ea4dda39a..7c4f8dcef 100644
--- a/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php
+++ b/lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php
@@ -330,6 +330,26 @@ class MysqlTest extends CakeTestCase {
 		$result = $this->Dbo->index('with_fulltext', false);
 		$this->Dbo->rawQuery('DROP TABLE ' . $name);
 		$this->assertEquals($expected, $result);
+
+		$name = $this->Dbo->fullTableName('with_text_index');
+		$this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field text, primary key(id), KEY `text_index` ( `text_field`(20) ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'text_index' => array('column' => 'text_field', 'unique' => 0, 'length' => array('text_field' => 20)),
+		);
+		$result = $this->Dbo->index('with_text_index', false);
+		$this->Dbo->rawQuery('DROP TABLE ' . $name);
+		$this->assertEquals($expected, $result);
+
+		$name = $this->Dbo->fullTableName('with_compound_text_index');
+		$this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field1 text, text_field2 text, primary key(id), KEY `text_index` ( `text_field1`(20), `text_field2`(20) ));');
+		$expected = array(
+			'PRIMARY' => array('column' => 'id', 'unique' => 1),
+			'text_index' => array('column' => array('text_field1', 'text_field2'), 'unique' => 0, 'length' => array('text_field1' => 20, 'text_field2' => 20)),
+		);
+		$result = $this->Dbo->index('with_compound_text_index', false);
+		$this->Dbo->rawQuery('DROP TABLE ' . $name);
+		$this->assertEquals($expected, $result);
 	}
 
 /**
@@ -2963,6 +2983,19 @@ class MysqlTest extends CakeTestCase {
 		);
 		$result = $this->Dbo->buildIndex($data);
 		$expected = array('FULLTEXT KEY `MyFtIndex` (`name`, `description`)');
+
+		$data = array(
+			'MyTextIndex' => array('column' => 'text_field', 'length' => array('text_field' => 20))
+		);
+		$result = $this->Dbo->buildIndex($data);
+		$expected = array('KEY `MyTextIndex` (`text_field`(20))');
+		$this->assertEquals($expected, $result);
+
+		$data = array(
+			'MyMultiTextIndex' => array('column' => array('text_field1', 'text_field2'), 'length' => array('text_field1' => 20, 'text_field2' => 20))
+		);
+		$result = $this->Dbo->buildIndex($data);
+		$expected = array('KEY `MyMultiTextIndex` (`text_field1`(20), `text_field2`(20))');
 		$this->assertEquals($expected, $result);
 	}
 
diff --git a/lib/Cake/Test/Case/Utility/DebuggerTest.php b/lib/Cake/Test/Case/Utility/DebuggerTest.php
index dd07dc84c..c496c2c7e 100644
--- a/lib/Cake/Test/Case/Utility/DebuggerTest.php
+++ b/lib/Cake/Test/Case/Utility/DebuggerTest.php
@@ -84,7 +84,13 @@ class DebuggerTest extends CakeTestCase {
 		$this->assertTrue(is_array($result));
 		$this->assertEquals(4, count($result));
 
-		$pattern = '/<code><span style\="color\: \#\d+">.*?&lt;\?php/';
+		$pattern = '/<code>.*?<span style\="color\: \#\d+">.*?&lt;\?php/';
+		$this->assertRegExp($pattern, $result[0]);
+
+		$result = Debugger::excerpt(__FILE__, 10, 2);
+		$this->assertEquals(5, count($result));
+
+		$pattern = '/<span style\="color\: \#\d{6}">\*<\/span>/';
 		$this->assertRegExp($pattern, $result[0]);
 
 		$return = Debugger::excerpt('[internal]', 2, 2);
diff --git a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
index e8de415d1..060ea31de 100644
--- a/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
+++ b/lib/Cake/Test/Case/View/Helper/FormHelperTest.php
@@ -6164,6 +6164,10 @@ class FormHelperTest extends CakeTestCase {
 			'/div'
 		);
 		$this->assertTags($result, $expected);
+
+		$this->Form->request->data['Model']['upload'] = 'no data should be set in value';
+		$result = $this->Form->file('Model.upload');
+		$this->assertTags($result, array('input' => array('type' => 'file', 'name' => 'data[Model][upload]', 'id' => 'ModelUpload')));
 	}
 
 /**
diff --git a/lib/Cake/Utility/Debugger.php b/lib/Cake/Utility/Debugger.php
index 5abcee676..d507073cb 100644
--- a/lib/Cake/Utility/Debugger.php
+++ b/lib/Cake/Utility/Debugger.php
@@ -396,12 +396,14 @@ class Debugger {
 			return array();
 		}
 		$data = file_get_contents($file);
-		if (!empty($data) && strpos($data, "\n") !== false) {
+		if (empty($data)) {
+			return $lines;
+		}
+		if (strpos($data, "\n") !== false) {
 			$data = explode("\n", $data);
 		}
-
-		if (empty($data) || !isset($data[$line])) {
-			return;
+		if (!isset($data[$line])) {
+			return $lines;
 		}
 		for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
 			if (!isset($data[$i])) {
@@ -425,13 +427,23 @@ class Debugger {
  * @return string
  */
 	protected static function _highlight($str) {
-		static $supportHighlight = null;
-		if (!$supportHighlight && function_exists('hphp_log')) {
-			$supportHighlight = false;
+		if (function_exists('hphp_log')) {
 			return htmlentities($str);
 		}
-		$supportHighlight = true;
-		return highlight_string($str, true);
+		$added = false;
+		if (strpos($str, '<?php') === false) {
+			$added = true;
+			$str = "<?php \n" . $str;
+		}
+		$highlight = highlight_string($str, true);
+		if ($added) {
+			$highlight = str_replace(
+				'&lt;?php&nbsp;<br />',
+				'',
+				$highlight
+			);
+		}
+		return $highlight;
 	}
 
 /**
diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php
index a00bef997..77efa1bb5 100644
--- a/lib/Cake/View/Helper/FormHelper.php
+++ b/lib/Cake/View/Helper/FormHelper.php
@@ -1433,7 +1433,7 @@ class FormHelper extends AppHelper {
 
 		$showEmpty = $this->_extractOption('empty', $attributes);
 		if ($showEmpty) {
-			$showEmpty = ($showEmpty === true) ? __('empty') : $showEmpty;
+			$showEmpty = ($showEmpty === true) ? __d('cake', 'empty') : $showEmpty;
 			$options = array('' => $showEmpty) + $options;
 		}
 		unset($attributes['empty']);
@@ -1628,7 +1628,8 @@ class FormHelper extends AppHelper {
 			$this->_secure($secure, array_merge($field, array($suffix)));
 		}
 
-		return $this->Html->useTag('file', $options['name'], array_diff_key($options, array('name' => '')));
+		$exclude = array('name' => '', 'value' => '');
+		return $this->Html->useTag('file', $options['name'], array_diff_key($options, $exclude));
 	}
 
 /**
diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php
index 20736b950..0acc346a9 100644
--- a/lib/Cake/View/Helper/HtmlHelper.php
+++ b/lib/Cake/View/Helper/HtmlHelper.php
@@ -744,7 +744,7 @@ class HtmlHelper extends AppHelper {
 					'text' => $startText
 				);
 			}
-			$startText += array('url' => '/', 'text' => __('Home'));
+			$startText += array('url' => '/', 'text' => __d('cake', 'Home'));
 			list($url, $text) = array($startText['url'], $startText['text']);
 			unset($startText['url'], $startText['text']);
 			array_unshift($crumbs, array($text, $url, $startText));