diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
index cc0de732be..b3d441137a 100644
--- a/docs/guide/upgrade-from-v1.md
+++ b/docs/guide/upgrade-from-v1.md
@@ -216,12 +216,14 @@ Using a widget is more straightforward in 2.0. You mainly use the `begin()`, `en
methods of the `Widget` class. For example,
```php
-// $this refers to the View object
// Note that you have to "echo" the result to display it
echo \yii\widgets\Menu::widget(array('items' => $items));
-// $this refers to the View object
-$form = \yii\widgets\ActiveForm::begin($this);
+// Passing an array to initialize the object properties
+$form = \yii\widgets\ActiveForm::begin(array(
+ 'options' => array('class' => 'form-horizontal'),
+ 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
+));
... form inputs here ...
\yii\widgets\ActiveForm::end();
```
diff --git a/framework/composer.json b/framework/composer.json
index 4ef6f891da..2f0e85f537 100644
--- a/framework/composer.json
+++ b/framework/composer.json
@@ -64,7 +64,7 @@
"source": "https://github.com/yiisoft/yii2"
},
"require": {
- "php": ">=5.3.11",
+ "php": ">=5.3.7",
"ext-mbstring": "*",
"lib-pcre": "*"
},
diff --git a/framework/yii/YiiBase.php b/framework/yii/YiiBase.php
index df5f631f64..4fe0e77ae3 100644
--- a/framework/yii/YiiBase.php
+++ b/framework/yii/YiiBase.php
@@ -616,6 +616,7 @@ class YiiBase
YiiBase::$aliases = array(
'@yii' => array(
'@yii/bootstrap' => __DIR__ . '/bootstrap',
+ '@yii/jui' => __DIR__ . '/jui',
'@yii' => __DIR__,
),
);
diff --git a/framework/yii/bootstrap/Carousel.php b/framework/yii/bootstrap/Carousel.php
index 21fc5ffba0..3d38b547ab 100644
--- a/framework/yii/bootstrap/Carousel.php
+++ b/framework/yii/bootstrap/Carousel.php
@@ -7,28 +7,29 @@
namespace yii\bootstrap;
-use Yii;
use yii\base\InvalidConfigException;
-use yii\base\Model;
use yii\helpers\base\ArrayHelper;
use yii\helpers\Html;
/**
* Carousel renders a carousel bootstrap javascript component.
*
- * For example,
+ * For example:
*
* ```php
* echo Carousel::widget(array(
* 'items' => array(
+ * // the item contains only the image
* '
',
+ * // equivalent to the above
* array(
* 'content' => '
',
* ),
+ * // the item contains both the image and the caption
* array(
* 'content' => '
',
- * 'options' => array(...)
- * 'caption' => '
This is title
This is the caption text
'
+ * 'caption' => 'This is title
This is the caption text
',
+ * 'options' => array(...),
* ),
* )
* ));
@@ -41,20 +42,22 @@ use yii\helpers\Html;
class Carousel extends Widget
{
/**
- * @var array indicates what labels should be displayed on next and previous carousel controls. If [[controls]] is
- * set to `false` or they do not hold `left` and `right` keys, the controls will not be displayed.
+ * @var array|boolean the labels for the previous and the next control buttons.
+ * If false, it means the previous and the next control buttons should not be displayed.
*/
- public $controls = array('left' => '‹', 'right' => '›');
+ public $controls = array('‹', '›');
/**
- * @var array list of images to appear in the carousel. If this property is empty,
- * the widget will not render anything. Each array element represents a single image in the carousel
- * with the following structure:
+ * @var array list of slides in the carousel. Each array element represents a single
+ * slide with the following structure:
*
* ```php
* array(
- * 'content' => 'src of the image', // required
- * 'options' => ['html attributes of the item'], // optional
- * 'caption'=> ['html attributes of the image'] // optional
+ * // required, slide content (HTML), such as an image tag
+ * 'content' => '
',
+ * // optional, the caption (HTML) of the slide
+ * 'caption'=> 'This is title
This is the caption text
',
+ * // optional the HTML attributes of the slide container
+ * 'options' => array(),
* )
* ```
*/
@@ -75,97 +78,95 @@ class Carousel extends Widget
*/
public function run()
{
- if (empty($this->items)) {
- return;
- }
-
echo Html::beginTag('div', $this->options) . "\n";
echo $this->renderIndicators() . "\n";
echo $this->renderItems() . "\n";
- echo $this->renderPreviousAndNext() . "\n";
+ echo $this->renderControls() . "\n";
echo Html::endTag('div') . "\n";
-
$this->registerPlugin('carousel');
}
/**
- * Renders carousel indicators
+ * Renders carousel indicators.
+ * @return string the rendering result
*/
public function renderIndicators()
{
- ob_start();
- echo Html::beginTag('ol', array('class' => 'carousel-indicators')) . "\n";
- for ($i = 0, $ln = count($this->items); $i < $ln; $i++) {
+ $indicators = array();
+ for ($i = 0, $count = count($this->items); $i < $count; $i++) {
$options = array('data-target' => '#' . $this->options['id'], 'data-slide-to' => $i);
if ($i === 0) {
$this->addCssClass($options, 'active');
}
- echo Html::tag('li', '', $options) . "\n";
+ $indicators[] = Html::tag('li', '', $options);
}
- echo Html::endTag('ol') . "\n";
- return ob_get_clean();
+ return Html::tag('ol', implode("\n", $indicators), array('class' => 'carousel-indicators'));
}
/**
- * Renders carousel items as specified on [[items]]
+ * Renders carousel items as specified on [[items]].
+ * @return string the rendering result
*/
public function renderItems()
{
- ob_start();
- echo Html::beginTag('div', array('class' => 'carousel-inner')) . "\n";
- for ($i = 0, $ln = count($this->items); $i < $ln; $i++) {
- $this->renderItem($this->items[$i], $i);
+ $items = array();
+ for ($i = 0, $count = count($this->items); $i < $count; $i++) {
+ $items[] = $this->renderItem($this->items[$i], $i);
}
- echo Html::endTag('div') . "\n";
- return ob_get_clean();
+ return Html::tag('div', implode("\n", $items), array('class' => 'carousel-inner'));
}
/**
* Renders a single carousel item
- * @param mixed $item a single item from [[items]]
+ * @param string|array $item a single item from [[items]]
* @param integer $index the item index as the first item should be set to `active`
+ * @return string the rendering result
+ * @throws InvalidConfigException if the item is invalid
*/
public function renderItem($item, $index)
{
if (is_string($item)) {
- $itemOptions = array();
- $itemContent = $item;
- $itemCaption = '';
- } else {
- $itemOptions = ArrayHelper::getValue($item, 'options', array());
- $itemContent = $item['content']; // if not string, must be array, force required key
- $itemCaption = ArrayHelper::getValue($item, 'caption');
- if ($itemCaption) {
- $itemCaption = Html::tag('div', $itemCaption, array('class' => 'carousel-caption'));
+ $content = $item;
+ $caption = null;
+ $options = array();
+ } elseif (isset($item['content'])) {
+ $content = $item['content'];
+ $caption = ArrayHelper::getValue($item, 'caption');
+ if ($caption !== null) {
+ $caption = Html::tag('div', $caption, array('class' => 'carousel-caption'));
}
+ $options = ArrayHelper::getValue($item, 'options', array());
+ } else {
+ throw new InvalidConfigException('The "content" option is required.');
}
- $this->addCssClass($itemOptions, 'item');
+ $this->addCssClass($options, 'item');
if ($index === 0) {
- $this->addCssClass($itemOptions, 'active');
+ $this->addCssClass($options, 'active');
}
- echo Html::beginTag('div', $itemOptions) . "\n";
- echo $itemContent . "\n";
- echo $itemCaption . "\n";
- echo Html::endTag('div') . "\n";
+
+ return Html::tag('div', $content . "\n" . $caption, $options);
}
/**
- * Renders previous and next button if [[displayPreviousAndNext]] is set to `true`
+ * Renders previous and next control buttons.
+ * @throws InvalidConfigException if [[controls]] is invalid.
*/
- public function renderPreviousAndNext()
+ public function renderControls()
{
- if ($this->controls === false || !(isset($this->controls['left']) && isset($this->controls['left']))) {
- return;
- }
- echo Html::a($this->controls['left'], '#' . $this->options['id'], array(
+ if (isset($this->controls[0], $this->controls[1])) {
+ return Html::a($this->controls[0], '#' . $this->options['id'], array(
'class' => 'left carousel-control',
- 'data-slide' => 'prev'
- )) .
- "\n" .
- Html::a($this->controls['right'], '#' . $this->options['id'], array(
+ 'data-slide' => 'prev',
+ )) . "\n"
+ . Html::a($this->controls[1], '#' . $this->options['id'], array(
'class' => 'right carousel-control',
- 'data-slide' => 'next'
+ 'data-slide' => 'next',
));
+ } elseif ($this->controls === false) {
+ return '';
+ } else {
+ throw new InvalidConfigException('The "controls" property must be either false or an array of two elements.');
+ }
}
-}
\ No newline at end of file
+}
diff --git a/framework/yii/bootstrap/Collapse.php b/framework/yii/bootstrap/Collapse.php
new file mode 100644
index 0000000000..d83df3c000
--- /dev/null
+++ b/framework/yii/bootstrap/Collapse.php
@@ -0,0 +1,133 @@
+ array(
+ * // equivalent to the above
+ * 'Collapsible Group Item #1' => array(
+ * 'content' => 'Anim pariatur cliche...',
+ * // open its content by default
+ * 'contentOptions' => array('class'=>'in')
+ * ),
+ * // another group item
+ * 'Collapsible Group Item #2' => array(
+ * 'content' => 'Anim pariatur cliche...',
+ * 'contentOptions' => array(...),
+ * 'options' => array(...),
+ * ),
+ * )
+ * ));
+ * ```
+ *
+ * @see http://twitter.github.io/bootstrap/javascript.html#collapse
+ * @author Antonio Ramirez
+ * @since 2.0
+ */
+class Collapse extends Widget
+{
+ /**
+ * @var array list of groups in the collapse widget. Each array element represents a single
+ * group with the following structure:
+ *
+ * ```php
+ * // item key is the actual group header
+ * 'Collapsible Group Item #1' => array(
+ * // required, the content (HTML) of the group
+ * 'content' => 'Anim pariatur cliche...',
+ * // optional the HTML attributes of the content group
+ * 'contentOptions'=> array(),
+ * // optional the HTML attributes of the group
+ * 'options'=> array(),
+ * )
+ * ```
+ */
+ public $items = array();
+
+
+ /**
+ * Initializes the widget.
+ */
+ public function init()
+ {
+ parent::init();
+ $this->addCssClass($this->options, 'accordion');
+ }
+
+ /**
+ * Renders the widget.
+ */
+ public function run()
+ {
+ echo Html::beginTag('div', $this->options) . "\n";
+ echo $this->renderItems() . "\n";
+ echo Html::endTag('div') . "\n";
+ $this->registerPlugin('collapse');
+ }
+
+ /**
+ * Renders collapsible items as specified on [[items]].
+ * @return string the rendering result
+ */
+ public function renderItems()
+ {
+ $items = array();
+ $index = 0;
+ foreach ($this->items as $header => $item) {
+ $options = ArrayHelper::getValue($item, 'options', array());
+ $this->addCssClass($options, 'accordion-group');
+ $items[] = Html::tag('div', $this->renderItem($header, $item, ++$index), $options);
+ }
+
+ return implode("\n", $items);
+ }
+
+ /**
+ * Renders a single collapsible item group
+ * @param string $header a label of the item group [[items]]
+ * @param array $item a single item from [[items]]
+ * @param integer $index the item index as each item group content must have an id
+ * @return string the rendering result
+ * @throws InvalidConfigException
+ */
+ public function renderItem($header, $item, $index)
+ {
+ if (isset($item['content'])) {
+ $id = $this->options['id'] . '-collapse' . $index;
+ $options = ArrayHelper::getValue($item, 'contentOptions', array());
+ $options['id'] = $id;
+ $this->addCssClass($options, 'accordion-body collapse');
+
+ $header = Html::a($header, '#' . $id, array(
+ 'class' => 'accordion-toggle',
+ 'data-toggle' => 'collapse',
+ 'data-parent' => '#' . $this->options['id']
+ )) . "\n";
+
+ $content = Html::tag('div', $item['content'], array('class' => 'accordion-inner')) . "\n";
+ } else {
+ throw new InvalidConfigException('The "content" option is required.');
+ }
+ $group = array();
+
+ $group[] = Html::tag('div', $header, array('class' => 'accordion-heading'));
+ $group[] = Html::tag('div', $content, $options);
+
+ return implode("\n", $group);
+ }
+}
\ No newline at end of file
diff --git a/framework/yii/console/Request.php b/framework/yii/console/Request.php
index d1a6aa6714..a9a4b036dd 100644
--- a/framework/yii/console/Request.php
+++ b/framework/yii/console/Request.php
@@ -15,9 +15,32 @@ class Request extends \yii\base\Request
{
const ANONYMOUS_PARAMS = '-args';
- public function getRawParams()
+ private $_params;
+
+ /**
+ * Returns the command line arguments.
+ * @return array the command line arguments. It does not include the entry script name.
+ */
+ public function getParams()
{
- return isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
+ if (!isset($this->_params)) {
+ if (isset($_SERVER['argv'])) {
+ $this->_params = $_SERVER['argv'];
+ array_shift($this->_params);
+ } else {
+ $this->_params = array();
+ }
+ }
+ return $this->_params;
+ }
+
+ /**
+ * Sets the command line arguments.
+ * @param array $params the command line arguments
+ */
+ public function setParams($params)
+ {
+ $this->_params = $params;
}
/**
@@ -26,9 +49,7 @@ class Request extends \yii\base\Request
*/
public function resolve()
{
- $rawParams = $this->getRawParams();
- array_shift($rawParams); // the 1st argument is the yii script name
-
+ $rawParams = $this->getParams();
if (isset($rawParams[0])) {
$route = $rawParams[0];
array_shift($rawParams);
diff --git a/framework/yii/helpers/base/Inflector.php b/framework/yii/helpers/base/Inflector.php
index 3c1927c4b7..cc5d33f824 100644
--- a/framework/yii/helpers/base/Inflector.php
+++ b/framework/yii/helpers/base/Inflector.php
@@ -17,233 +17,206 @@ use Yii;
*/
class Inflector
{
-
/**
- * @var array rules of plural words
+ * @var array the rules for converting a word into its plural form.
+ * The keys are the regular expressions and the values are the corresponding replacements.
*/
- protected static $plural = array(
- 'rules' => array(
- '/(m)ove$/i' => '\1oves',
- '/(f)oot$/i' => '\1eet',
- '/(h)uman$/i' => '\1umans',
- '/(s)tatus$/i' => '\1\2tatuses',
- '/(s)taff$/i' => '\1taff',
- '/(t)ooth$/i' => '\1eeth',
- '/(quiz)$/i' => '\1zes',
- '/^(ox)$/i' => '\1\2en',
- '/([m|l])ouse$/i' => '\1ice',
- '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
- '/(x|ch|ss|sh)$/i' => '\1es',
- '/([^aeiouy]|qu)y$/i' => '\1ies',
- '/(hive)$/i' => '\1s',
- '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
- '/sis$/i' => 'ses',
- '/([ti])um$/i' => '\1a',
- '/(p)erson$/i' => '\1eople',
- '/(m)an$/i' => '\1en',
- '/(c)hild$/i' => '\1hildren',
- '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
- '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
- '/us$/i' => 'uses',
- '/(alias)$/i' => '\1es',
- '/(ax|cris|test)is$/i' => '\1es',
- '/s$/' => 's',
- '/^$/' => '',
- '/$/' => 's',
- ),
- 'uninflected' => array(
- '.*[nrlm]ese',
- '.*deer',
- '.*fish',
- '.*measles',
- '.*ois',
- '.*pox',
- '.*sheep',
- 'people'
- ),
- 'irregular' => array(
- 'atlas' => 'atlases',
- 'beef' => 'beefs',
- 'brother' => 'brothers',
- 'cafe' => 'cafes',
- 'child' => 'children',
- 'cookie' => 'cookies',
- 'corpus' => 'corpuses',
- 'cow' => 'cows',
- 'ganglion' => 'ganglions',
- 'genie' => 'genies',
- 'genus' => 'genera',
- 'graffito' => 'graffiti',
- 'hoof' => 'hoofs',
- 'loaf' => 'loaves',
- 'man' => 'men',
- 'money' => 'monies',
- 'mongoose' => 'mongooses',
- 'move' => 'moves',
- 'mythos' => 'mythoi',
- 'niche' => 'niches',
- 'numen' => 'numina',
- 'occiput' => 'occiputs',
- 'octopus' => 'octopuses',
- 'opus' => 'opuses',
- 'ox' => 'oxen',
- 'penis' => 'penises',
- 'person' => 'people',
- 'sex' => 'sexes',
- 'soliloquy' => 'soliloquies',
- 'testis' => 'testes',
- 'trilby' => 'trilbys',
- 'turf' => 'turfs'
- )
+ public static $plurals = array(
+ '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1',
+ '/^(sea[- ]bass)$/i' => '\1',
+ '/(m)ove$/i' => '\1oves',
+ '/(f)oot$/i' => '\1eet',
+ '/(h)uman$/i' => '\1umans',
+ '/(s)tatus$/i' => '\1tatuses',
+ '/(s)taff$/i' => '\1taff',
+ '/(t)ooth$/i' => '\1eeth',
+ '/(quiz)$/i' => '\1zes',
+ '/^(ox)$/i' => '\1\2en',
+ '/([m|l])ouse$/i' => '\1ice',
+ '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
+ '/(x|ch|ss|sh)$/i' => '\1es',
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
+ '/(hive)$/i' => '\1s',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/sis$/i' => 'ses',
+ '/([ti])um$/i' => '\1a',
+ '/(p)erson$/i' => '\1eople',
+ '/(m)an$/i' => '\1en',
+ '/(c)hild$/i' => '\1hildren',
+ '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
+ '/us$/i' => 'uses',
+ '/(alias)$/i' => '\1es',
+ '/(ax|cris|test)is$/i' => '\1es',
+ '/s$/' => 's',
+ '/^$/' => '',
+ '/$/' => 's',
);
/**
- * @var array the rules to singular inflector
+ * @var array the rules for converting a word into its singular form.
+ * The keys are the regular expressions and the values are the corresponding replacements.
*/
- protected static $singular = array(
- 'rules' => array(
- '/(s)tatuses$/i' => '\1\2tatus',
- '/(f)eet$/i' => '\1oot',
- '/(t)eeth$/i' => '\1ooth',
- '/^(.*)(menu)s$/i' => '\1\2',
- '/(quiz)zes$/i' => '\\1',
- '/(matr)ices$/i' => '\1ix',
- '/(vert|ind)ices$/i' => '\1ex',
- '/^(ox)en/i' => '\1',
- '/(alias)(es)*$/i' => '\1',
- '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
- '/([ftw]ax)es/i' => '\1',
- '/(cris|ax|test)es$/i' => '\1is',
- '/(shoe|slave)s$/i' => '\1',
- '/(o)es$/i' => '\1',
- '/ouses$/' => 'ouse',
- '/([^a])uses$/' => '\1us',
- '/([m|l])ice$/i' => '\1ouse',
- '/(x|ch|ss|sh)es$/i' => '\1',
- '/(m)ovies$/i' => '\1\2ovie',
- '/(s)eries$/i' => '\1\2eries',
- '/([^aeiouy]|qu)ies$/i' => '\1y',
- '/([lr])ves$/i' => '\1f',
- '/(tive)s$/i' => '\1',
- '/(hive)s$/i' => '\1',
- '/(drive)s$/i' => '\1',
- '/([^fo])ves$/i' => '\1fe',
- '/(^analy)ses$/i' => '\1sis',
- '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
- '/([ti])a$/i' => '\1um',
- '/(p)eople$/i' => '\1\2erson',
- '/(m)en$/i' => '\1an',
- '/(c)hildren$/i' => '\1\2hild',
- '/(n)ews$/i' => '\1\2ews',
- '/eaus$/' => 'eau',
- '/^(.*us)$/' => '\\1',
- '/s$/i' => ''
- ),
- 'uninflected' => array(
- '.*[nrlm]ese',
- '.*deer',
- '.*fish',
- '.*measles',
- '.*ois',
- '.*pox',
- '.*sheep',
- '.*ss'
- ),
- 'irregular' => array(
- 'foes' => 'foe',
- 'waves' => 'wave',
- 'curves' => 'curve'
- )
+ public static $singulars = array(
+ '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media|ss)$/i' => '\1',
+ '/^(sea[- ]bass)$/i' => '\1',
+ '/(s)tatuses$/i' => '\1tatus',
+ '/(f)eet$/i' => '\1oot',
+ '/(t)eeth$/i' => '\1ooth',
+ '/^(.*)(menu)s$/i' => '\1\2',
+ '/(quiz)zes$/i' => '\\1',
+ '/(matr)ices$/i' => '\1ix',
+ '/(vert|ind)ices$/i' => '\1ex',
+ '/^(ox)en/i' => '\1',
+ '/(alias)(es)*$/i' => '\1',
+ '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us',
+ '/([ftw]ax)es/i' => '\1',
+ '/(cris|ax|test)es$/i' => '\1is',
+ '/(shoe|slave)s$/i' => '\1',
+ '/(o)es$/i' => '\1',
+ '/ouses$/' => 'ouse',
+ '/([^a])uses$/' => '\1us',
+ '/([m|l])ice$/i' => '\1ouse',
+ '/(x|ch|ss|sh)es$/i' => '\1',
+ '/(m)ovies$/i' => '\1\2ovie',
+ '/(s)eries$/i' => '\1\2eries',
+ '/([^aeiouy]|qu)ies$/i' => '\1y',
+ '/([lr])ves$/i' => '\1f',
+ '/(tive)s$/i' => '\1',
+ '/(hive)s$/i' => '\1',
+ '/(drive)s$/i' => '\1',
+ '/([^fo])ves$/i' => '\1fe',
+ '/(^analy)ses$/i' => '\1sis',
+ '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis',
+ '/([ti])a$/i' => '\1um',
+ '/(p)eople$/i' => '\1\2erson',
+ '/(m)en$/i' => '\1an',
+ '/(c)hildren$/i' => '\1\2hild',
+ '/(n)ews$/i' => '\1\2ews',
+ '/eaus$/' => 'eau',
+ '/^(.*us)$/' => '\\1',
+ '/s$/i' => '',
);
-
/**
- * @var array list of words that should not be inflected
+ * @var array the special rules for converting a word between its plural form and singular form.
+ * The keys are the special words in singular form, and the values are the corresponding plural form.
*/
- protected static $uninflected = array(
- 'Amoyese',
- 'bison',
- 'Borghese',
- 'bream',
- 'breeches',
- 'britches',
- 'buffalo',
- 'cantus',
- 'carp',
- 'chassis',
- 'clippers',
- 'cod',
- 'coitus',
- 'Congoese',
- 'contretemps',
- 'corps',
- 'debris',
- 'diabetes',
- 'djinn',
- 'eland',
- 'elk',
- 'equipment',
- 'Faroese',
- 'flounder',
- 'Foochowese',
- 'gallows',
- 'Genevese',
- 'Genoese',
- 'Gilbertese',
- 'graffiti',
- 'headquarters',
- 'herpes',
- 'hijinks',
- 'Hottentotese',
- 'information',
- 'innings',
- 'jackanapes',
- 'Kiplingese',
- 'Kongoese',
- 'Lucchese',
- 'mackerel',
- 'Maltese',
- '.*?media',
- 'mews',
- 'moose',
- 'mumps',
- 'Nankingese',
- 'news',
- 'nexus',
- 'Niasese',
- 'Pekingese',
- 'Piedmontese',
- 'pincers',
- 'Pistoiese',
- 'pliers',
- 'Portuguese',
- 'proceedings',
- 'rabies',
- 'rice',
- 'rhinoceros',
- 'salmon',
- 'Sarawakese',
- 'scissors',
- 'sea[- ]bass',
- 'series',
- 'Shavese',
- 'shears',
- 'siemens',
- 'species',
- 'swine',
- 'testes',
- 'trousers',
- 'trout',
- 'tuna',
- 'Vermontese',
- 'Wenchowese',
- 'whiting',
- 'wildebeest',
- 'Yengeese'
+ public static $specials = array(
+ 'atlas' => 'atlases',
+ 'beef' => 'beefs',
+ 'brother' => 'brothers',
+ 'cafe' => 'cafes',
+ 'child' => 'children',
+ 'cookie' => 'cookies',
+ 'corpus' => 'corpuses',
+ 'cow' => 'cows',
+ 'curve' => 'curves',
+ 'foe' => 'foes',
+ 'ganglion' => 'ganglions',
+ 'genie' => 'genies',
+ 'genus' => 'genera',
+ 'graffito' => 'graffiti',
+ 'hoof' => 'hoofs',
+ 'loaf' => 'loaves',
+ 'man' => 'men',
+ 'money' => 'monies',
+ 'mongoose' => 'mongooses',
+ 'move' => 'moves',
+ 'mythos' => 'mythoi',
+ 'niche' => 'niches',
+ 'numen' => 'numina',
+ 'occiput' => 'occiputs',
+ 'octopus' => 'octopuses',
+ 'opus' => 'opuses',
+ 'ox' => 'oxen',
+ 'penis' => 'penises',
+ 'sex' => 'sexes',
+ 'soliloquy' => 'soliloquies',
+ 'testis' => 'testes',
+ 'trilby' => 'trilbys',
+ 'turf' => 'turfs',
+ 'wave' => 'waves',
+ 'Amoyese' => 'Amoyese',
+ 'bison' => 'bison',
+ 'Borghese' => 'Borghese',
+ 'bream' => 'bream',
+ 'breeches' => 'breeches',
+ 'britches' => 'britches',
+ 'buffalo' => 'buffalo',
+ 'cantus' => 'cantus',
+ 'carp' => 'carp',
+ 'chassis' => 'chassis',
+ 'clippers' => 'clippers',
+ 'cod' => 'cod',
+ 'coitus' => 'coitus',
+ 'Congoese' => 'Congoese',
+ 'contretemps' => 'contretemps',
+ 'corps' => 'corps',
+ 'debris' => 'debris',
+ 'diabetes' => 'diabetes',
+ 'djinn' => 'djinn',
+ 'eland' => 'eland',
+ 'elk' => 'elk',
+ 'equipment' => 'equipment',
+ 'Faroese' => 'Faroese',
+ 'flounder' => 'flounder',
+ 'Foochowese' => 'Foochowese',
+ 'gallows' => 'gallows',
+ 'Genevese' => 'Genevese',
+ 'Genoese' => 'Genoese',
+ 'Gilbertese' => 'Gilbertese',
+ 'graffiti' => 'graffiti',
+ 'headquarters' => 'headquarters',
+ 'herpes' => 'herpes',
+ 'hijinks' => 'hijinks',
+ 'Hottentotese' => 'Hottentotese',
+ 'information' => 'information',
+ 'innings' => 'innings',
+ 'jackanapes' => 'jackanapes',
+ 'Kiplingese' => 'Kiplingese',
+ 'Kongoese' => 'Kongoese',
+ 'Lucchese' => 'Lucchese',
+ 'mackerel' => 'mackerel',
+ 'Maltese' => 'Maltese',
+ 'mews' => 'mews',
+ 'moose' => 'moose',
+ 'mumps' => 'mumps',
+ 'Nankingese' => 'Nankingese',
+ 'news' => 'news',
+ 'nexus' => 'nexus',
+ 'Niasese' => 'Niasese',
+ 'Pekingese' => 'Pekingese',
+ 'Piedmontese' => 'Piedmontese',
+ 'pincers' => 'pincers',
+ 'Pistoiese' => 'Pistoiese',
+ 'pliers' => 'pliers',
+ 'Portuguese' => 'Portuguese',
+ 'proceedings' => 'proceedings',
+ 'rabies' => 'rabies',
+ 'rice' => 'rice',
+ 'rhinoceros' => 'rhinoceros',
+ 'salmon' => 'salmon',
+ 'Sarawakese' => 'Sarawakese',
+ 'scissors' => 'scissors',
+ 'series' => 'series',
+ 'Shavese' => 'Shavese',
+ 'shears' => 'shears',
+ 'siemens' => 'siemens',
+ 'species' => 'species',
+ 'swine' => 'swine',
+ 'testes' => 'testes',
+ 'trousers' => 'trousers',
+ 'trout' => 'trout',
+ 'tuna' => 'tuna',
+ 'Vermontese' => 'Vermontese',
+ 'Wenchowese' => 'Wenchowese',
+ 'whiting' => 'whiting',
+ 'wildebeest' => 'wildebeest',
+ 'Yengeese' => 'Yengeese',
);
-
/**
- * @var array map of special chars and its translation
+ * @var array map of special chars and its translation. This is used by [[slug()]].
*/
- protected static $transliteration = array(
+ public static $transliteration = array(
'/ä|æ|ǽ/' => 'ae',
'/ö|œ/' => 'oe',
'/ü/' => 'ue',
@@ -305,19 +278,10 @@ class Inflector
*/
public static function pluralize($word)
{
- $unInflected = ArrayHelper::merge(static::$plural['uninflected'], static::$uninflected);
- $irregular = array_keys(static::$plural['irregular']);
-
- $unInflectedRegex = '(?:' . implode('|', $unInflected) . ')';
- $irregularRegex = '(?:' . implode('|', $irregular) . ')';
-
- if (preg_match('/(.*)\\b(' . $irregularRegex . ')$/i', $word, $regs))
- return $regs[1] . substr($word, 0, 1) . substr(static::$plural['irregular'][strtolower($regs[2])], 1);
-
- if (preg_match('/^(' . $unInflectedRegex . ')$/i', $word, $regs))
- return $word;
-
- foreach (static::$plural['rules'] as $rule => $replacement) {
+ if (isset(self::$specials[$word])) {
+ return self::$specials[$word];
+ }
+ foreach (static::$plurals as $rule => $replacement) {
if (preg_match($rule, $word)) {
return preg_replace($rule, $replacement, $word);
}
@@ -332,27 +296,11 @@ class Inflector
*/
public static function singularize($word)
{
-
- $unInflected = ArrayHelper::merge(static::$singular['uninflected'], static::$uninflected);
-
- $irregular = array_merge(
- static::$singular['irregular'],
- array_flip(static::$plural['irregular'])
- );
-
- $unInflectedRegex = '(?:' . implode('|', $unInflected) . ')';
- $irregularRegex = '(?:' . implode('|', array_keys($irregular)) . ')';
-
-
- if (preg_match('/(.*)\\b(' . $irregularRegex . ')$/i', $word, $regs))
- return $regs[1] . substr($word, 0, 1) . substr($irregular[strtolower($regs[2])], 1);
-
-
- if (preg_match('/^(' . $unInflectedRegex . ')$/i', $word, $regs))
- return $word;
-
-
- foreach (static::$singular['rules'] as $rule => $replacement) {
+ $result = array_search($word, self::$specials, true);
+ if ($result !== false) {
+ return $result;
+ }
+ foreach (static::$singulars as $rule => $replacement) {
if (preg_match($rule, $word)) {
return preg_replace($rule, $replacement, $word);
}
@@ -369,7 +317,6 @@ class Inflector
*/
public static function titleize($words, $ucAll = false)
{
-
$words = static::humanize(static::underscore($words), $ucAll);
return $ucAll ? ucwords($words) : ucfirst($words);
}
@@ -492,7 +439,6 @@ class Inflector
*/
public static function slug($string, $replacement = '-')
{
-
$map = static::$transliteration + array(
'/[^\w\s]/' => ' ',
'/\\s+/' => $replacement,
@@ -521,20 +467,12 @@ class Inflector
{
if (in_array(($number % 100), range(11, 13))) {
return $number . 'th';
- } else {
- switch (($number % 10)) {
- case 1:
- return $number . 'st';
- break;
- case 2:
- return $number . 'nd';
- break;
- case 3:
- return $number . 'rd';
- default:
- return $number . 'th';
- break;
- }
+ }
+ switch (($number % 10)) {
+ case 1: return $number . 'st';
+ case 2: return $number . 'nd';
+ case 3: return $number . 'rd';
+ default: return $number . 'th';
}
}
}
diff --git a/framework/yii/jui/assets.php b/framework/yii/jui/assets.php
index 1d27ceec52..c292050a38 100644
--- a/framework/yii/jui/assets.php
+++ b/framework/yii/jui/assets.php
@@ -775,7 +775,7 @@ return array(
'css' => array(
'themes/base/jquery.ui.autocomplete.css',
),
- 'depends' => array('yii/jui/theme/base/core'),
+ 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/menu'),
),
'yii/jui/theme/base/button' => array(
'sourcePath' => __DIR__ . '/assets',
@@ -796,7 +796,7 @@ return array(
'css' => array(
'themes/base/jquery.ui.dialog.css',
),
- 'depends' => array('yii/jui/theme/base/core'),
+ 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button', 'yii/jui/theme/base/resizeable'),
),
'yii/jui/theme/base/menu' => array(
'sourcePath' => __DIR__ . '/assets',
@@ -838,7 +838,7 @@ return array(
'css' => array(
'themes/base/jquery.ui.spinner.css',
),
- 'depends' => array('yii/jui/theme/base/core'),
+ 'depends' => array('yii/jui/theme/base/core', 'yii/jui/theme/base/button'),
),
'yii/jui/theme/base/tabs' => array(
'sourcePath' => __DIR__ . '/assets',
diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php
index bc53c03e80..0dbc1fcba4 100644
--- a/framework/yii/requirements/requirements.php
+++ b/framework/yii/requirements/requirements.php
@@ -9,9 +9,9 @@ return array(
array(
'name' => 'PHP version',
'mandatory' => true,
- 'condition' => version_compare(PHP_VERSION, '5.3.11', '>='),
+ 'condition' => version_compare(PHP_VERSION, '5.3.7', '>='),
'by' => 'Yii Framework',
- 'memo' => 'PHP 5.3.11 or higher is required.',
+ 'memo' => 'PHP 5.3.7 or higher is required.',
),
array(
'name' => 'Reflection extension',
diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php
index dde10ee0ba..5a3c391074 100644
--- a/framework/yii/web/UrlManager.php
+++ b/framework/yii/web/UrlManager.php
@@ -26,6 +26,12 @@ class UrlManager extends Component
* "/index.php?r=news/view&id=100".
*/
public $enablePrettyUrl = false;
+ /**
+ * @var boolean whether to enable strict parsing. If strict parsing is enabled, the incoming
+ * requested URL must match at least one of the [[rules]] in order to be treated as a valid request.
+ * This property is used only when [[enablePrettyUrl]] is true.
+ */
+ public $enableStrictParsing = false;
/**
* @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is true.
* This property is used only if [[enablePrettyUrl]] is true. Each element in the array
@@ -139,6 +145,10 @@ class UrlManager extends Component
}
}
+ if ($this->enableStrictParsing) {
+ return false;
+ }
+
$suffix = (string)$this->suffix;
if ($suffix !== '' && $suffix !== '/' && $pathInfo !== '') {
$n = strlen($this->suffix);
diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php
index e76f11fa5a..d63f202fcc 100644
--- a/framework/yii/widgets/Menu.php
+++ b/framework/yii/widgets/Menu.php
@@ -26,7 +26,6 @@ use yii\helpers\Html;
* The following example shows how to use Menu:
*
* ~~~
- * // $this is the view object currently being used
* echo Menu::widget(array(
* 'items' => array(
* // Important: you need to specify url as 'controller/action',
diff --git a/tests/unit/framework/helpers/InflectorTest.php b/tests/unit/framework/helpers/InflectorTest.php
index 9f9626dd34..f1a98ba635 100644
--- a/tests/unit/framework/helpers/InflectorTest.php
+++ b/tests/unit/framework/helpers/InflectorTest.php
@@ -8,8 +8,6 @@ use yiiunit\TestCase;
class InflectorTest extends TestCase
{
-
-
public function testPluralize()
{
$testData = array(
diff --git a/tests/unit/framework/web/UrlManagerTest.php b/tests/unit/framework/web/UrlManagerTest.php
index b6246c697e..553b163f90 100644
--- a/tests/unit/framework/web/UrlManagerTest.php
+++ b/tests/unit/framework/web/UrlManagerTest.php
@@ -224,5 +224,27 @@ class UrlManagerTest extends \yiiunit\TestCase
$request->pathInfo = 'site/index';
$result = $manager->parseRequest($request);
$this->assertFalse($result);
+
+ // strict parsing
+ $manager = new UrlManager(array(
+ 'enablePrettyUrl' => true,
+ 'enableStrictParsing' => true,
+ 'suffix' => '.html',
+ 'cache' => null,
+ 'rules' => array(
+ array(
+ 'pattern' => 'post//',
+ 'route' => 'post/view',
+ ),
+ ),
+ ));
+ // matching pathinfo
+ $request->pathInfo = 'post/123/this+is+sample.html';
+ $result = $manager->parseRequest($request);
+ $this->assertEquals(array('post/view', array('id' => '123', 'title' => 'this+is+sample')), $result);
+ // unmatching pathinfo
+ $request->pathInfo = 'site/index.html';
+ $result = $manager->parseRequest($request);
+ $this->assertFalse($result);
}
}