From 713a987a9d803ea9e676026321f7428bdd904dbd Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 25 Aug 2013 00:00:14 +0200 Subject: [PATCH 001/156] auto fill sorting colums in ActiveDataProvider if none are configured --- framework/yii/data/ActiveDataProvider.php | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 6dfc9ed..539d00a 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -9,6 +9,8 @@ namespace yii\data; use Yii; use yii\base\InvalidConfigException; +use yii\base\InvalidParamException; +use yii\base\Model; use yii\db\Query; use yii\db\ActiveQuery; use yii\db\Connection; @@ -214,4 +216,34 @@ class ActiveDataProvider extends DataProvider $this->_totalCount = null; $this->_keys = null; } + + /** + * Sets the sort definition for this data provider. + * @param array|Sort|boolean $value the sort definition to be used by this data provider. + * This can be one of the following: + * + * - a configuration array for creating the sort definition object. The "class" element defaults + * to 'yii\data\Sort' + * - an instance of [[Sort]] or its subclass + * - false, if sorting needs to be disabled. + * + * @throws InvalidParamException + */ + public function setSort($value) + { + parent::setSort($value); + if (($sort = $this->getSort()) !== false && empty($sort->attributes) && + $this->query instanceof ActiveQuery) { + + /** @var Model $model */ + $model = new $this->query->modelClass; + foreach($model->attributes() as $attribute) { + $sort->attributes[$attribute] = array( + 'asc' => array($attribute => Sort::ASC), + 'desc' => array($attribute => Sort::DESC), + 'label' => $model->getAttributeLabel($attribute), + ); + } + } + } } From f9fab5d40d50d8a300b5a5c83d01b4137db9212a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 25 Aug 2013 00:01:26 +0200 Subject: [PATCH 002/156] Lazy fallback of label creation for Sort class --- framework/yii/data/Sort.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index 73e6f1c..e638848 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -127,7 +127,7 @@ class Sort extends Object * a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label. * * Note that if the Sort object is already created, you can only use the full format - * to configure every attribute. Each attribute must include these elements: asc, desc and label. + * to configure every attribute. Each attribute must include these elements: asc and desc. */ public $attributes = array(); /** @@ -195,13 +195,11 @@ class Sort extends Object $attributes[$attribute] = array( 'asc' => array($attribute => self::ASC), 'desc' => array($attribute => self::DESC), - 'label' => Inflector::camel2words($attribute), ); - } elseif (!isset($attribute['asc'], $attribute['desc'], $attribute['label'])) { + } elseif (!isset($attribute['asc'], $attribute['desc'])) { $attributes[$name] = array_merge(array( 'asc' => array($name => self::ASC), 'desc' => array($name => self::DESC), - 'label' => Inflector::camel2words($name), ), $attribute); } else { $attributes[$name] = $attribute; @@ -304,7 +302,12 @@ class Sort extends Object $url = $this->createUrl($attribute); $options['data-sort'] = $this->createSortVar($attribute); - return Html::a($this->attributes[$attribute]['label'], $url, $options); + if (isset($this->attributes[$attribute]['label'])) { + $label = $this->attributes[$attribute]['label']; + } else { + $label = Inflector::camel2words($attribute); + } + return Html::a($label, $url, $options); } /** From c5d65e09afa0f59c20068d5b3683c56449e25be1 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 25 Aug 2013 00:16:12 +0200 Subject: [PATCH 003/156] Refactor DataColum to use model label for sorting link --- framework/yii/grid/DataColumn.php | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index d31507d..ed6e803 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -62,25 +62,33 @@ class DataColumn extends Column if ($this->attribute !== null && $this->header === null) { $provider = $this->grid->dataProvider; if ($this->enableSorting && ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { - return $sort->link($this->attribute); - } - $models = $provider->getModels(); - if (($model = reset($models)) instanceof Model) { - /** @var Model $model */ - return $model->getAttributeLabel($this->attribute); - } elseif ($provider instanceof ActiveDataProvider) { - if ($provider->query instanceof ActiveQuery) { - /** @var Model $model */ - $model = new $provider->query->modelClass; - return $model->getAttributeLabel($this->attribute); + if (!isset($sort->attributes[$this->attribute]['label'])) { + $sort->attributes[$this->attribute]['label'] = $this->getHeaderLabel(); } + return $sort->link($this->attribute); } - return Inflector::camel2words($this->attribute); + return $this->getHeaderLabel(); } else { return parent::renderHeaderCellContent(); } } + protected function getHeaderLabel() + { + $provider = $this->grid->dataProvider; + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { + /** @var Model $model */ + $model = new $provider->query->modelClass; + return $model->getAttributeLabel($this->attribute); + } + $models = $provider->getModels(); + if (($model = reset($models)) instanceof Model) { + /** @var Model $model */ + return $model->getAttributeLabel($this->attribute); + } + return Inflector::camel2words($this->attribute); + } + protected function renderFilterCellContent() { if (is_string($this->filter)) { From 381216595d9cd8caf6d6ff8979ceae8ec86a1758 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 25 Aug 2013 00:37:29 +0200 Subject: [PATCH 004/156] DataColumn: Allow setting label of sort link via short syntax attribute:type:Header should also affect sort link --- framework/yii/grid/DataColumn.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index ed6e803..e1913c3 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -59,14 +59,16 @@ class DataColumn extends Column protected function renderHeaderCellContent() { - if ($this->attribute !== null && $this->header === null) { - $provider = $this->grid->dataProvider; - if ($this->enableSorting && ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { - if (!isset($sort->attributes[$this->attribute]['label'])) { - $sort->attributes[$this->attribute]['label'] = $this->getHeaderLabel(); - } - return $sort->link($this->attribute); + $provider = $this->grid->dataProvider; + if ($this->attribute !== null && $this->enableSorting && + ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { + + $label = $this->getHeaderLabel(); + if (($this->header !== null || !isset($sort->attributes[$this->attribute]['label'])) && trim($label) !== '') { + $sort->attributes[$this->attribute]['label'] = $label; } + return $sort->link($this->attribute); + } elseif ($this->header === null) { return $this->getHeaderLabel(); } else { return parent::renderHeaderCellContent(); @@ -76,6 +78,9 @@ class DataColumn extends Column protected function getHeaderLabel() { $provider = $this->grid->dataProvider; + if ($this->header !== null) { + return $this->header; + } if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { /** @var Model $model */ $model = new $provider->query->modelClass; From a2eb49cd3e48df541c2cea893967e612e355ab66 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 25 Aug 2013 11:54:17 +0200 Subject: [PATCH 005/156] use `@inheritdoc` for ActiveDataProvider::setSort --- framework/yii/data/ActiveDataProvider.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 539d00a..0347db1 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -218,16 +218,7 @@ class ActiveDataProvider extends DataProvider } /** - * Sets the sort definition for this data provider. - * @param array|Sort|boolean $value the sort definition to be used by this data provider. - * This can be one of the following: - * - * - a configuration array for creating the sort definition object. The "class" element defaults - * to 'yii\data\Sort' - * - an instance of [[Sort]] or its subclass - * - false, if sorting needs to be disabled. - * - * @throws InvalidParamException + * @inheritdoc */ public function setSort($value) { From b147760c7bd4cecc2df3f52e94c6a409789f2241 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 26 Aug 2013 14:06:48 +0200 Subject: [PATCH 006/156] added label option to Sort::link() --- framework/yii/data/Sort.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index e638848..c2c8529 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -285,11 +285,14 @@ class Sort extends Object * Based on the sort direction, the CSS class of the generated hyperlink will be appended * with "asc" or "desc". * @param string $attribute the attribute name by which the data should be sorted by. + * @param string $label the label to be used for the generated link. + * If this is null, the label defined in [[attributes]] we be used. + * If there is no label defined, [[Inflector::camel2words()]] will be called to get a label. * @param array $options additional HTML attributes for the hyperlink tag * @return string the generated hyperlink * @throws InvalidConfigException if the attribute is unknown */ - public function link($attribute, $options = array()) + public function link($attribute, $label = null, $options = array()) { if (($direction = $this->getAttributeOrder($attribute)) !== null) { $class = $direction ? 'desc' : 'asc'; @@ -302,10 +305,13 @@ class Sort extends Object $url = $this->createUrl($attribute); $options['data-sort'] = $this->createSortVar($attribute); - if (isset($this->attributes[$attribute]['label'])) { - $label = $this->attributes[$attribute]['label']; - } else { - $label = Inflector::camel2words($attribute); + + if ($label === null) { + if (isset($this->attributes[$attribute]['label'])) { + $label = $this->attributes[$attribute]['label']; + } else { + $label = Inflector::camel2words($attribute); + } } return Html::a($label, $url, $options); } From 9847472144ee99e6a129ad6e53dfee419e6aafb8 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 26 Aug 2013 15:13:16 +0200 Subject: [PATCH 007/156] introduced DataColumn::label property --- framework/yii/grid/DataColumn.php | 67 +++++++++++++++++++++++---------------- framework/yii/grid/GridView.php | 12 +++---- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index e1913c3..2b9c0be 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -28,6 +28,14 @@ class DataColumn extends Column */ public $attribute; /** + * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting + * link label when sorting is enabled for this column. + * If it is not set and the models provided by the GridViews data provider are instances + * of [[ActiveRecord]], the label will be determined via [[ActiveRecord::getAttributeLabel()]]. + * Otherwise [[yii\helpers\Inflector::camel2words]] will be used to get a label. + */ + public $label; + /** * @var \Closure an anonymous function that returns the value to be displayed for every data model. * If this is not set, `$model[$attribute]` will be used to obtain the value. */ @@ -46,6 +54,11 @@ class DataColumn extends Column */ public $enableSorting = true; /** + * @var array the HTML attributes for the link tag in the header cell + * generated by [[Sort::link]] when sorting is enabled for this column. + */ + public $sortLinkOptions = array(); + /** * @var string|array|boolean the HTML code representing a filter input (e.g. a text field, a dropdown list) * that is used for this data column. This property is effective only when [[GridView::filterModel]] is set. * @@ -59,41 +72,39 @@ class DataColumn extends Column protected function renderHeaderCellContent() { - $provider = $this->grid->dataProvider; - if ($this->attribute !== null && $this->enableSorting && - ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { + if ($this->header === null) { + $provider = $this->grid->dataProvider; - $label = $this->getHeaderLabel(); - if (($this->header !== null || !isset($sort->attributes[$this->attribute]['label'])) && trim($label) !== '') { - $sort->attributes[$this->attribute]['label'] = $label; + if ($this->label === null) { + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { + /** @var Model $model */ + $model = new $provider->query->modelClass; + $label = $model->getAttributeLabel($this->attribute); + } else { + $models = $provider->getModels(); + if (($model = reset($models)) instanceof Model) { + /** @var Model $model */ + $label = $model->getAttributeLabel($this->attribute); + } else { + $label = Inflector::camel2words($this->attribute); + } + } + } else { + $label = $this->label; + } + + if ($this->attribute !== null && $this->enableSorting && + ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { + + return $sort->link($this->attribute, Html::encode($label), $this->sortLinkOptions); + } else { + return Html::encode($label); } - return $sort->link($this->attribute); - } elseif ($this->header === null) { - return $this->getHeaderLabel(); } else { return parent::renderHeaderCellContent(); } } - protected function getHeaderLabel() - { - $provider = $this->grid->dataProvider; - if ($this->header !== null) { - return $this->header; - } - if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { - /** @var Model $model */ - $model = new $provider->query->modelClass; - return $model->getAttributeLabel($this->attribute); - } - $models = $provider->getModels(); - if (($model = reset($models)) instanceof Model) { - /** @var Model $model */ - return $model->getAttributeLabel($this->attribute); - } - return Inflector::camel2words($this->attribute); - } - protected function renderFilterCellContent() { if (is_string($this->filter)) { diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php index 9490f27..60d325d 100644 --- a/framework/yii/grid/GridView.php +++ b/framework/yii/grid/GridView.php @@ -108,7 +108,7 @@ class GridView extends ListViewBase * 'class' => DataColumn::className(), * 'attribute' => 'name', * 'format' => 'text', - * 'header' => 'Name', + * 'label' => 'Name', * ), * array( * 'class' => CheckboxColumn::className(), @@ -119,9 +119,9 @@ class GridView extends ListViewBase * If a column is of class [[DataColumn]], the "class" element can be omitted. * * As a shortcut format, a string may be used to specify the configuration of a data column - * which only contains "attribute", "format", and/or "header" options: `"attribute:format:header"`. + * which only contains "attribute", "format", and/or "label" options: `"attribute:format:label"`. * For example, the above "name" column can also be specified as: `"name:text:Name"`. - * Both "format" and "header" are optional. They will take default values if absent. + * Both "format" and "label" are optional. They will take default values if absent. */ public $columns = array(); /** @@ -372,7 +372,7 @@ class GridView extends ListViewBase } /** - * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:header". + * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label". * @param string $text the column specification string * @return DataColumn the column instance * @throws InvalidConfigException if the column specification is invalid @@ -380,14 +380,14 @@ class GridView extends ListViewBase protected function createDataColumn($text) { if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { - throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:header'); + throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label'); } return Yii::createObject(array( 'class' => $this->dataColumnClass ?: DataColumn::className(), 'grid' => $this, 'attribute' => $matches[1], 'format' => isset($matches[3]) ? $matches[3] : 'text', - 'header' => isset($matches[5]) ? $matches[5] : null, + 'label' => isset($matches[5]) ? $matches[5] : null, )); } From 155749e18d1150b7371452a22243be278dbd6388 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 26 Aug 2013 15:23:22 +0200 Subject: [PATCH 008/156] typos in doc --- framework/yii/data/Sort.php | 4 ++-- framework/yii/grid/DataColumn.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index c2c8529..954996d 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -286,8 +286,8 @@ class Sort extends Object * with "asc" or "desc". * @param string $attribute the attribute name by which the data should be sorted by. * @param string $label the label to be used for the generated link. - * If this is null, the label defined in [[attributes]] we be used. - * If there is no label defined, [[Inflector::camel2words()]] will be called to get a label. + * If this is null, the label defined in [[attributes]] will be used. + * If no label is defined, [[yii\helpers\Inflector::camel2words()]] will be called to get a label. * @param array $options additional HTML attributes for the hyperlink tag * @return string the generated hyperlink * @throws InvalidConfigException if the attribute is unknown diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index 2b9c0be..afb2fba 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -31,8 +31,8 @@ class DataColumn extends Column * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting * link label when sorting is enabled for this column. * If it is not set and the models provided by the GridViews data provider are instances - * of [[ActiveRecord]], the label will be determined via [[ActiveRecord::getAttributeLabel()]]. - * Otherwise [[yii\helpers\Inflector::camel2words]] will be used to get a label. + * of [[yii\db\ActiveRecord]], the label will be determined using [[yii\db\ActiveRecord::getAttributeLabel()]]. + * Otherwise [[yii\helpers\Inflector::camel2words()]] will be used to get a label. */ public $label; /** From 39c002e83cc4f327577a303ba86faddd6c585144 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 26 Aug 2013 15:23:49 +0200 Subject: [PATCH 009/156] Refactored DataColumn::renderHeaderCellContent() --- framework/yii/grid/DataColumn.php | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index afb2fba..82b8641 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -72,36 +72,36 @@ class DataColumn extends Column protected function renderHeaderCellContent() { - if ($this->header === null) { - $provider = $this->grid->dataProvider; + if ($this->header !== null || $this->label === null && $this->attribute === null) { + return parent::renderHeaderCellContent(); + } + + $provider = $this->grid->dataProvider; - if ($this->label === null) { - if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { + if ($this->label === null) { + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQuery) { + /** @var Model $model */ + $model = new $provider->query->modelClass; + $label = $model->getAttributeLabel($this->attribute); + } else { + $models = $provider->getModels(); + if (($model = reset($models)) instanceof Model) { /** @var Model $model */ - $model = new $provider->query->modelClass; $label = $model->getAttributeLabel($this->attribute); } else { - $models = $provider->getModels(); - if (($model = reset($models)) instanceof Model) { - /** @var Model $model */ - $label = $model->getAttributeLabel($this->attribute); - } else { - $label = Inflector::camel2words($this->attribute); - } + $label = Inflector::camel2words($this->attribute); } - } else { - $label = $this->label; } + } else { + $label = $this->label; + } - if ($this->attribute !== null && $this->enableSorting && - ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { + if ($this->attribute !== null && $this->enableSorting && + ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { - return $sort->link($this->attribute, Html::encode($label), $this->sortLinkOptions); - } else { - return Html::encode($label); - } + return $sort->link($this->attribute, Html::encode($label), $this->sortLinkOptions); } else { - return parent::renderHeaderCellContent(); + return Html::encode($label); } } From 53b4ed3d7f9b2611ce2d572adccaf2a01d771a05 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 26 Aug 2013 18:02:44 +0200 Subject: [PATCH 010/156] Moved label for Sort::link() to html options --- framework/yii/data/Sort.php | 15 ++++++++++----- framework/yii/grid/DataColumn.php | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index 954996d..d489c44 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -125,6 +125,7 @@ class Sort extends Object * if it is not currently sorted (the default value is ascending order). * - The "label" element specifies what label should be used when calling [[link()]] to create * a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label. + * Note that it will not be HTML-encoded. * * Note that if the Sort object is already created, you can only use the full format * to configure every attribute. Each attribute must include these elements: asc and desc. @@ -285,14 +286,15 @@ class Sort extends Object * Based on the sort direction, the CSS class of the generated hyperlink will be appended * with "asc" or "desc". * @param string $attribute the attribute name by which the data should be sorted by. - * @param string $label the label to be used for the generated link. - * If this is null, the label defined in [[attributes]] will be used. + * @param array $options additional HTML attributes for the hyperlink tag. + * There is one special attribute `label` which will be used as the label of the hyperlink. + * If this is not set, the label defined in [[attributes]] will be used. * If no label is defined, [[yii\helpers\Inflector::camel2words()]] will be called to get a label. - * @param array $options additional HTML attributes for the hyperlink tag + * Note that it will not be HTML-encoded. * @return string the generated hyperlink * @throws InvalidConfigException if the attribute is unknown */ - public function link($attribute, $label = null, $options = array()) + public function link($attribute, $options = array()) { if (($direction = $this->getAttributeOrder($attribute)) !== null) { $class = $direction ? 'desc' : 'asc'; @@ -306,7 +308,10 @@ class Sort extends Object $url = $this->createUrl($attribute); $options['data-sort'] = $this->createSortVar($attribute); - if ($label === null) { + if (isset($options['label'])) { + $label = $options['label']; + unset($options['label']); + } else { if (isset($this->attributes[$attribute]['label'])) { $label = $this->attributes[$attribute]['label']; } else { diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index 82b8641..295dece 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -99,7 +99,7 @@ class DataColumn extends Column if ($this->attribute !== null && $this->enableSorting && ($sort = $provider->getSort()) !== false && $sort->hasAttribute($this->attribute)) { - return $sort->link($this->attribute, Html::encode($label), $this->sortLinkOptions); + return $sort->link($this->attribute, array_merge($this->sortLinkOptions, array('label' => Html::encode($label)))); } else { return Html::encode($label); } From 3b8aff5a44daa7598f3011266404839c89fd73d2 Mon Sep 17 00:00:00 2001 From: MaximAL Date: Mon, 26 Aug 2013 20:49:23 +0400 Subject: [PATCH 011/156] =?UTF-8?q?Blowfish=20cost=20must=20be=20in=20rang?= =?UTF-8?q?e=204=E2=80=A631,=20not=204=E2=80=A630?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `generateSalt` parameter validation fix. --- framework/yii/helpers/SecurityBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/helpers/SecurityBase.php b/framework/yii/helpers/SecurityBase.php index 05b2682..541b311 100644 --- a/framework/yii/helpers/SecurityBase.php +++ b/framework/yii/helpers/SecurityBase.php @@ -262,7 +262,7 @@ class SecurityBase protected static function generateSalt($cost = 13) { $cost = (int)$cost; - if ($cost < 4 || $cost > 30) { + if ($cost < 4 || $cost > 31) { throw new InvalidParamException('Cost must be between 4 and 31.'); } From 62b4a645f1c5fa52c29dad57f1baf84d9f67f708 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 27 Aug 2013 08:08:57 -0400 Subject: [PATCH 012/156] crud generator WIP --- framework/yii/gii/generators/crud/Generator.php | 83 +++++++++++++++++++++- framework/yii/gii/generators/crud/form.php | 10 +++ .../gii/generators/crud/templates/controller.php | 8 +++ .../yii/gii/generators/crud/templates/model.php | 8 +++ 4 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 framework/yii/gii/generators/crud/form.php create mode 100644 framework/yii/gii/generators/crud/templates/controller.php create mode 100644 framework/yii/gii/generators/crud/templates/model.php diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 15da2c2..2b6697d 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -7,6 +7,10 @@ namespace yii\gii\generators\crud; +use yii\db\ActiveRecord; +use yii\gii\CodeFile; +use yii\web\Controller; + /** * * @author Qiang Xue @@ -14,6 +18,10 @@ namespace yii\gii\generators\crud; */ class Generator extends \yii\gii\Generator { + public $modelClass; + public $controllerID; + public $baseControllerClass = 'yii\web\Controller'; + public function getName() { return 'CRUD Generator'; @@ -25,11 +33,84 @@ class Generator extends \yii\gii\Generator operations for the specified data model.'; } + public function rules() + { + return array_merge(parent::rules(), array( + array('modelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'), + array('modelClass, controllerID, baseControllerClass', 'required'), + array('modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), + array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())), + array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'), + array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), + array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())), + )); + } + + public function attributeLabels() + { + return array_merge(parent::attributeLabels(), array( + 'modelClass' => 'Model Class', + 'controllerID' => 'Controller ID', + 'baseControllerClass' => 'Base Controller Class', + )); + } + + /** + * @inheritdoc + */ + public function hints() + { + return array( + 'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon. + You should provide a fully qualified class name, e.g., app\models\Post.', + 'controllerID' => 'CRUD controllers are often named after the model class name that they are dealing with. + Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example: +
    +
  • order generates OrderController.php
  • +
  • order-item generates OrderItemController.php
  • +
  • admin/user generates UserController.php within the admin module.
  • +
', + 'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from. + You should provide a fully qualified class name, e.g., yii\web\Controller.', + ); + } + + public function requiredTemplates() + { + return array( + 'controller.php', + ); + } + + /** + * @inheritdoc + */ + public function stickyAttributes() + { + return array('baseControllerClass'); + } + /** * @inheritdoc */ public function generate() { - return array(); + $files = array(); + $files[] = new CodeFile( + $this->controllerFile, + $this->render('controller.php') + ); + + $files = scandir($this->getTemplatePath()); + foreach ($files as $file) { + if (is_file($templatePath . '/' . $file) && CFileHelper::getExtension($file) === 'php' && $file !== 'controller.php') { + $files[] = new CodeFile( + $this->viewPath . DIRECTORY_SEPARATOR . $file, + $this->render($templatePath . '/' . $file) + ); + } + } + + return $files; } } diff --git a/framework/yii/gii/generators/crud/form.php b/framework/yii/gii/generators/crud/form.php new file mode 100644 index 0000000..0951695 --- /dev/null +++ b/framework/yii/gii/generators/crud/form.php @@ -0,0 +1,10 @@ +field($generator, 'modelClass'); +echo $form->field($generator, 'controllerID'); +echo $form->field($generator, 'baseControllerClass'); diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php new file mode 100644 index 0000000..f372629 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/controller.php @@ -0,0 +1,8 @@ + Date: Tue, 27 Aug 2013 08:09:15 -0400 Subject: [PATCH 013/156] Fixes #820. --- framework/yii/db/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 5317e67..b55be3c 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -851,8 +851,8 @@ class QueryBuilder extends \yii\base\Object $column = $this->db->quoteColumnName($column); } $phName1 = self::PARAM_PREFIX . count($params); - $phName2 = self::PARAM_PREFIX . count($params); $params[$phName1] = $value1; + $phName2 = self::PARAM_PREFIX . count($params); $params[$phName2] = $value2; return "$column $operator $phName1 AND $phName2"; From d34f8486ab2b6d5e363ceeb005f0d84e470800b0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 27 Aug 2013 08:13:20 -0400 Subject: [PATCH 014/156] Fixes #811. --- framework/yii/caching/ApcCache.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/yii/caching/ApcCache.php b/framework/yii/caching/ApcCache.php index ff3acf5..60f2ec1 100644 --- a/framework/yii/caching/ApcCache.php +++ b/framework/yii/caching/ApcCache.php @@ -21,6 +21,16 @@ namespace yii\caching; class ApcCache extends Cache { /** + * Checks the existence of a key in APC cache. + * @param string $key the key to be checked. + * @return boolean if the key exists or not + */ + public function keyExists($key) + { + return apc_exists($key); + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value From 0e2ac86566a1cdf862f4e2c091e21eff0d2445b0 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 27 Aug 2013 17:24:09 +0200 Subject: [PATCH 015/156] added Cache::exists() to check key existance in cache --- framework/yii/caching/ApcCache.php | 14 ++++++++++---- framework/yii/caching/Cache.php | 19 +++++++++++++++++++ tests/unit/framework/caching/CacheTestCase.php | 12 ++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/framework/yii/caching/ApcCache.php b/framework/yii/caching/ApcCache.php index 60f2ec1..6c2754a 100644 --- a/framework/yii/caching/ApcCache.php +++ b/framework/yii/caching/ApcCache.php @@ -21,12 +21,18 @@ namespace yii\caching; class ApcCache extends Cache { /** - * Checks the existence of a key in APC cache. - * @param string $key the key to be checked. - * @return boolean if the key exists or not + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. */ - public function keyExists($key) + public function exists($key) { + $key = $this->buildKey($key); return apc_exists($key); } diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php index a6ee455..b129294 100644 --- a/framework/yii/caching/Cache.php +++ b/framework/yii/caching/Cache.php @@ -134,6 +134,25 @@ abstract class Cache extends Component implements \ArrayAccess } /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * In case a cache does not support this feature natively, this method will try to simulate it + * but has no performance improvement over getting it. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $key = $this->buildKey($key); + $value = $this->getValue($key); + return $value !== false; + } + + /** * Retrieves multiple values from cache with the specified keys. * Some caches (such as memcache, apc) allow retrieving multiple cached values at the same time, * which may improve the performance. In case a cache does not support this feature natively, diff --git a/tests/unit/framework/caching/CacheTestCase.php b/tests/unit/framework/caching/CacheTestCase.php index 491459b..94894a3 100644 --- a/tests/unit/framework/caching/CacheTestCase.php +++ b/tests/unit/framework/caching/CacheTestCase.php @@ -91,6 +91,18 @@ abstract class CacheTestCase extends TestCase $this->assertEquals('array_test', $array['array_test']); } + public function testExists() + { + $cache = $this->prepare(); + + $this->assertTrue($cache->exists('string_test')); + // check whether exists affects the value + $this->assertEquals('string_test', $cache->get('string_test')); + + $this->assertTrue($cache->exists('number_test')); + $this->assertFalse($cache->exists('not_exists')); + } + public function testArrayAccess() { $cache = $this->getCacheInstance(); From 3b91801a353d70dfa58a0a089e19ae19d3f42138 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 27 Aug 2013 17:29:38 +0200 Subject: [PATCH 016/156] cherry picked RedisCache and redis\Connection from redis WIP branch commit in redis branch was: 0cd65e7496befb3c4aed86e47257103fea63d329 --- framework/yii/caching/RedisCache.php | 198 +++++++++++ framework/yii/db/redis/Connection.php | 425 ++++++++++++++++++++++++ framework/yii/db/redis/Transaction.php | 91 +++++ tests/unit/framework/caching/RedisCacheTest.php | 83 +++++ 4 files changed, 797 insertions(+) create mode 100644 framework/yii/caching/RedisCache.php create mode 100644 framework/yii/db/redis/Connection.php create mode 100644 framework/yii/db/redis/Transaction.php create mode 100644 tests/unit/framework/caching/RedisCacheTest.php diff --git a/framework/yii/caching/RedisCache.php b/framework/yii/caching/RedisCache.php new file mode 100644 index 0000000..0c8bf15 --- /dev/null +++ b/framework/yii/caching/RedisCache.php @@ -0,0 +1,198 @@ +array( + * 'cache'=>array( + * 'class'=>'RedisCache', + * 'hostname'=>'localhost', + * 'port'=>6379, + * 'database'=>0, + * ), + * ), + * ) + * ~~~ + * + * @author Carsten Brandt + * @since 2.0 + */ +class RedisCache extends Cache +{ + /** + * @var string hostname to use for connecting to the redis server. Defaults to 'localhost'. + */ + public $hostname = 'localhost'; + /** + * @var int the port to use for connecting to the redis server. Default port is 6379. + */ + public $port = 6379; + /** + * @var string the password to use to authenticate with the redis server. If not set, no AUTH command will be sent. + */ + public $password; + /** + * @var int the redis database to use. This is an integer value starting from 0. Defaults to 0. + */ + public $database = 0; + /** + * @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout") + */ + public $connectionTimeout = null; + /** + * @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used. + */ + public $dataTimeout = null; + /** + * @var \yii\db\redis\Connection the redis connection + */ + private $_connection; + + + /** + * Initializes the cache component by establishing a connection to the redis server. + */ + public function init() + { + parent::init(); + $this->getConnection(); + } + + /** + * Returns the redis connection object. + * Establishes a connection to the redis server if it does not already exists. + * + * TODO throw exception on error + * @return \yii\db\redis\Connection + */ + public function getConnection() + { + if ($this->_connection === null) { + $this->_connection = new Connection(array( + 'dsn' => 'redis://' . $this->hostname . ':' . $this->port . '/' . $this->database, + 'password' => $this->password, + 'connectionTimeout' => $this->connectionTimeout, + 'dataTimeout' => $this->dataTimeout, + )); + } + return $this->_connection; + } + + /** + * Retrieves a value from cache with a specified key. + * This is the implementation of the method declared in the parent class. + * @param string $key a unique key identifying the cached value + * @return string|boolean the value stored in cache, false if the value is not in the cache or expired. + */ + protected function getValue($key) + { + return $this->_connection->executeCommand('GET', array($key)); + } + + /** + * Retrieves multiple values from cache with the specified keys. + * @param array $keys a list of keys identifying the cached values + * @return array a list of cached values indexed by the keys + */ + protected function getValues($keys) + { + $response = $this->_connection->executeCommand('MGET', $keys); + $result = array(); + $i = 0; + foreach($keys as $key) { + $result[$key] = $response[$i++]; + } + return $result; + } + + /** + * Stores a value identified by a key in cache. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param float $expire the number of seconds in which the cached value will expire. 0 means never expire. + * This can be a floating point number to specify the time in milliseconds. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function setValue($key,$value,$expire) + { + if ($expire == 0) { + return (bool) $this->_connection->executeCommand('SET', array($key, $value)); + } else { + $expire = (int) ($expire * 1000); + return (bool) $this->_connection->executeCommand('PSETEX', array($key, $expire, $value)); + } + } + + /** + * Stores a value identified by a key into cache if the cache does not contain this key. + * This is the implementation of the method declared in the parent class. + * + * @param string $key the key identifying the value to be cached + * @param string $value the value to be cached + * @param float $expire the number of seconds in which the cached value will expire. 0 means never expire. + * This can be a floating point number to specify the time in milliseconds. + * @return boolean true if the value is successfully stored into cache, false otherwise + */ + protected function addValue($key,$value,$expire) + { + if ($expire == 0) { + return (bool) $this->_connection->executeCommand('SETNX', array($key, $value)); + } else { + // TODO consider requiring redis version >= 2.6.12 that supports this in one command + $expire = (int) ($expire * 1000); + $this->_connection->executeCommand('MULTI'); + $this->_connection->executeCommand('SETNX', array($key, $value)); + $this->_connection->executeCommand('PEXPIRE', array($key, $expire)); + $response = $this->_connection->executeCommand('EXEC'); + return (bool) $response[0]; + } + } + + /** + * Deletes a value with the specified key from cache + * This is the implementation of the method declared in the parent class. + * @param string $key the key of the value to be deleted + * @return boolean if no error happens during deletion + */ + protected function deleteValue($key) + { + return (bool) $this->_connection->executeCommand('DEL', array($key)); + } + + /** + * Deletes all values from cache. + * This is the implementation of the method declared in the parent class. + * @return boolean whether the flush operation was successful. + */ + protected function flushValues() + { + return $this->_connection->executeCommand('FLUSHDB'); + } +} diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php new file mode 100644 index 0000000..46db575 --- /dev/null +++ b/framework/yii/db/redis/Connection.php @@ -0,0 +1,425 @@ +close(); + return array_keys(get_object_vars($this)); + } + + /** + * Returns a value indicating whether the DB connection is established. + * @return boolean whether the DB connection is established + */ + public function getIsActive() + { + return $this->_socket !== null; + } + + /** + * Establishes a DB connection. + * It does nothing if a DB connection has already been established. + * @throws Exception if connection fails + */ + public function open() + { + if ($this->_socket === null) { + if (empty($this->dsn)) { + throw new InvalidConfigException('Connection.dsn cannot be empty.'); + } + $dsn = explode('/', $this->dsn); + $host = $dsn[2]; + if (strpos($host, ':')===false) { + $host .= ':6379'; + } + $db = isset($dsn[3]) ? $dsn[3] : 0; + + \Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__); + $this->_socket = @stream_socket_client( + $host, + $errorNumber, + $errorDescription, + $this->connectionTimeout ? $this->connectionTimeout : ini_get("default_socket_timeout") + ); + if ($this->_socket) { + if ($this->dataTimeout !== null) { + stream_set_timeout($this->_socket, $timeout=(int)$this->dataTimeout, (int) (($this->dataTimeout - $timeout) * 1000000)); + } + if ($this->password !== null) { + $this->executeCommand('AUTH', array($this->password)); + } + $this->executeCommand('SELECT', array($db)); + $this->initConnection(); + } else { + \Yii::error("Failed to open DB connection ({$this->dsn}): " . $errorNumber . ' - ' . $errorDescription, __CLASS__); + $message = YII_DEBUG ? 'Failed to open DB connection: ' . $errorNumber . ' - ' . $errorDescription : 'Failed to open DB connection.'; + throw new Exception($message, $errorDescription, (int)$errorNumber); + } + } + } + + /** + * Closes the currently active DB connection. + * It does nothing if the connection is already closed. + */ + public function close() + { + if ($this->_socket !== null) { + \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); + $this->executeCommand('QUIT'); + stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR); + $this->_socket = null; + $this->_transaction = null; + } + } + + /** + * Initializes the DB connection. + * This method is invoked right after the DB connection is established. + * The default implementation triggers an [[EVENT_AFTER_OPEN]] event. + */ + protected function initConnection() + { + $this->trigger(self::EVENT_AFTER_OPEN); + } + + /** + * Returns the currently active transaction. + * @return Transaction the currently active transaction. Null if no active transaction. + */ + public function getTransaction() + { + return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null; + } + + /** + * Starts a transaction. + * @return Transaction the transaction initiated + */ + public function beginTransaction() + { + $this->open(); + $this->_transaction = new Transaction(array( + 'db' => $this, + )); + $this->_transaction->begin(); + return $this->_transaction; + } + + /** + * Returns the name of the DB driver for the current [[dsn]]. + * @return string name of the DB driver + */ + public function getDriverName() + { + if (($pos = strpos($this->dsn, ':')) !== false) { + return strtolower(substr($this->dsn, 0, $pos)); + } else { + return 'redis'; + } + } + + /** + * + * @param string $name + * @param array $params + * @return mixed + */ + public function __call($name, $params) + { + $redisCommand = strtoupper(Inflector::camel2words($name, false)); + if (in_array($redisCommand, $this->redisCommands)) { + return $this->executeCommand($name, $params); + } else { + return parent::__call($name, $params); + } + } + + /** + * Executes a redis command. + * For a list of available commands and their parameters see http://redis.io/commands. + * + * @param string $name the name of the command + * @param array $params list of parameters for the command + * @return array|bool|null|string Dependend on the executed command this method + * will return different data types: + * + * - `true` for commands that return "status reply". + * - `string` for commands that return "integer reply" + * as the value is in the range of a signed 64 bit integer. + * - `string` or `null` for commands that return "bulk reply". + * - `array` for commands that return "Multi-bulk replies". + * + * See [redis protocol description](http://redis.io/topics/protocol) + * for details on the mentioned reply types. + * @trows Exception for commands that return [error reply](http://redis.io/topics/protocol#error-reply). + */ + public function executeCommand($name, $params=array()) + { + $this->open(); + + array_unshift($params, $name); + $command = '*' . count($params) . "\r\n"; + foreach($params as $arg) { + $command .= '$' . mb_strlen($arg, '8bit') . "\r\n" . $arg . "\r\n"; + } + + \Yii::trace("Executing Redis Command: {$name}", __CLASS__); + fwrite($this->_socket, $command); + + return $this->parseResponse(implode(' ', $params)); + } + + private function parseResponse($command) + { + if(($line = fgets($this->_socket)) === false) { + throw new Exception("Failed to read from socket.\nRedis command was: " . $command); + } + $type = $line[0]; + $line = mb_substr($line, 1, -2, '8bit'); + switch($type) + { + case '+': // Status reply + return true; + case '-': // Error reply + throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command); + case ':': // Integer reply + // no cast to int as it is in the range of a signed 64 bit integer + return $line; + case '$': // Bulk replies + if ($line == '-1') { + return null; + } + $length = $line + 2; + $data = ''; + while ($length > 0) { + if(($block = fread($this->_socket, $line + 2)) === false) { + throw new Exception("Failed to read from socket.\nRedis command was: " . $command); + } + $data .= $block; + $length -= mb_strlen($block, '8bit'); + } + return mb_substr($data, 0, -2, '8bit'); + case '*': // Multi-bulk replies + $count = (int) $line; + $data = array(); + for($i = 0; $i < $count; $i++) { + $data[] = $this->parseResponse($command); + } + return $data; + default: + throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . $command); + } + } +} diff --git a/framework/yii/db/redis/Transaction.php b/framework/yii/db/redis/Transaction.php new file mode 100644 index 0000000..721a7be --- /dev/null +++ b/framework/yii/db/redis/Transaction.php @@ -0,0 +1,91 @@ +_active; + } + + /** + * Begins a transaction. + * @throws InvalidConfigException if [[connection]] is null + */ + public function begin() + { + if (!$this->_active) { + if ($this->db === null) { + throw new InvalidConfigException('Transaction::db must be set.'); + } + \Yii::trace('Starting transaction', __CLASS__); + $this->db->open(); + $this->db->createCommand('MULTI')->execute(); + $this->_active = true; + } + } + + /** + * Commits a transaction. + * @throws Exception if the transaction or the DB connection is not active. + */ + public function commit() + { + if ($this->_active && $this->db && $this->db->isActive) { + \Yii::trace('Committing transaction', __CLASS__); + $this->db->createCommand('EXEC')->execute(); + // TODO handle result of EXEC + $this->_active = false; + } else { + throw new Exception('Failed to commit transaction: transaction was inactive.'); + } + } + + /** + * Rolls back a transaction. + * @throws Exception if the transaction or the DB connection is not active. + */ + public function rollback() + { + if ($this->_active && $this->db && $this->db->isActive) { + \Yii::trace('Rolling back transaction', __CLASS__); + $this->db->pdo->commit(); + $this->_active = false; + } else { + throw new Exception('Failed to roll back transaction: transaction was inactive.'); + } + } +} diff --git a/tests/unit/framework/caching/RedisCacheTest.php b/tests/unit/framework/caching/RedisCacheTest.php new file mode 100644 index 0000000..0924d0f --- /dev/null +++ b/tests/unit/framework/caching/RedisCacheTest.php @@ -0,0 +1,83 @@ + 'localhost', + 'port' => 6379, + 'database' => 0, + 'dataTimeout' => 0.1, + ); + $dsn = $config['hostname'] . ':' .$config['port']; + if(!@stream_socket_client($dsn, $errorNumber, $errorDescription, 0.5)) { + $this->markTestSkipped('No redis server running at ' . $dsn .' : ' . $errorNumber . ' - ' . $errorDescription); + } + + if($this->_cacheInstance === null) { + $this->_cacheInstance = new RedisCache($config); + } + return $this->_cacheInstance; + } + + public function testExpireMilliseconds() + { + $cache = $this->getCacheInstance(); + + $this->assertTrue($cache->set('expire_test_ms', 'expire_test_ms', 0.2)); + usleep(100000); + $this->assertEquals('expire_test_ms', $cache->get('expire_test_ms')); + usleep(300000); + $this->assertFalse($cache->get('expire_test_ms')); + } + + /** + * Store a value that is 2 times buffer size big + * https://github.com/yiisoft/yii2/issues/743 + */ + public function testLargeData() + { + $cache = $this->getCacheInstance(); + + $data=str_repeat('XX',8192); // http://www.php.net/manual/en/function.fread.php + $key='bigdata1'; + + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + + // try with multibyte string + $data=str_repeat('ЖЫ',8192); // http://www.php.net/manual/en/function.fread.php + $key='bigdata2'; + + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + } + + public function testMultiByteGetAndSet() + { + $cache = $this->getCacheInstance(); + + $data=array('abc'=>'ежик',2=>'def'); + $key='data1'; + + $this->assertFalse($cache->get($key)); + $cache->set($key,$data); + $this->assertTrue($cache->get($key)===$data); + } + +} \ No newline at end of file From 427c373ba6a202072d25c8ee1d135f427351722d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 27 Aug 2013 17:54:38 +0200 Subject: [PATCH 017/156] implement Cache::exists() for various cache backends fixes #811 --- framework/yii/caching/DbCache.php | 29 +++++++++++++++++++++++++++++ framework/yii/caching/FileCache.php | 20 ++++++++++++++++++++ framework/yii/caching/RedisCache.php | 15 +++++++++++++++ framework/yii/caching/WinCache.php | 18 +++++++++++++++++- framework/yii/caching/XCache.php | 16 ++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) diff --git a/framework/yii/caching/DbCache.php b/framework/yii/caching/DbCache.php index 7571a42..b7b6692 100644 --- a/framework/yii/caching/DbCache.php +++ b/framework/yii/caching/DbCache.php @@ -89,6 +89,35 @@ class DbCache extends Cache } /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $key = $this->buildKey($key); + + $query = new Query; + $query->select(array('COUNT(*)')) + ->from($this->cacheTable) + ->where('[[id]] = :id AND ([[expire]] = 0 OR [[expire]] >' . time() . ')', array(':id' => $key)); + if ($this->db->enableQueryCache) { + // temporarily disable and re-enable query caching + $this->db->enableQueryCache = false; + $result = $query->createCommand($this->db)->queryScalar(); + $this->db->enableQueryCache = true; + } else { + $result = $query->createCommand($this->db)->queryScalar(); + } + return $result > 0; + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value diff --git a/framework/yii/caching/FileCache.php b/framework/yii/caching/FileCache.php index a15751e..54d6506 100644 --- a/framework/yii/caching/FileCache.php +++ b/framework/yii/caching/FileCache.php @@ -59,6 +59,26 @@ class FileCache extends Cache } /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $cacheFile = $this->getCacheFile($this->buildKey($key)); + if (($time = @filemtime($cacheFile)) > time()) { + return true; + } else { + return false; + } + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value diff --git a/framework/yii/caching/RedisCache.php b/framework/yii/caching/RedisCache.php index 0c8bf15..df56852 100644 --- a/framework/yii/caching/RedisCache.php +++ b/framework/yii/caching/RedisCache.php @@ -104,6 +104,21 @@ class RedisCache extends Cache } /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + return (bool) $this->_connection->executeCommand('EXISTS', array($this->buildKey($key))); + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value diff --git a/framework/yii/caching/WinCache.php b/framework/yii/caching/WinCache.php index eed580d..3679884 100644 --- a/framework/yii/caching/WinCache.php +++ b/framework/yii/caching/WinCache.php @@ -8,7 +8,7 @@ namespace yii\caching; /** - * WinCache provides XCache caching in terms of an application component. + * WinCache provides Windows Cache caching in terms of an application component. * * To use this application component, the [WinCache PHP extension](http://www.iis.net/expand/wincacheforphp) * must be loaded. Also note that "wincache.ucenabled" should be set to "On" in your php.ini file. @@ -21,6 +21,22 @@ namespace yii\caching; class WinCache extends Cache { /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $key = $this->buildKey($key); + return wincache_ucache_exists($key); + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value diff --git a/framework/yii/caching/XCache.php b/framework/yii/caching/XCache.php index 1f12f23..4221a39 100644 --- a/framework/yii/caching/XCache.php +++ b/framework/yii/caching/XCache.php @@ -22,6 +22,22 @@ namespace yii\caching; class XCache extends Cache { /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $key = $this->buildKey($key); + return xcache_isset($key); + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value From fa2a48c68485676dab640f85cc8b64a5ceb2a477 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 07:29:59 +0200 Subject: [PATCH 018/156] ported @property phpdoc generator from yii 1.1 fixes #822 --- build/controllers/PhpDocController.php | 149 +++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 build/controllers/PhpDocController.php diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php new file mode 100644 index 0000000..444d33e --- /dev/null +++ b/build/controllers/PhpDocController.php @@ -0,0 +1,149 @@ + + * @author Alexander Makarov + * @since 2.0 + */ +class PhpDocController extends Controller +{ + /** + * Generates @property annotations in class files from getters and setters + * + * @param string $directory the directory to parse files from + * @param boolean $updateFiles whether to update class docs directly + */ + public function actionProperty($directory=null, $updateFiles=false) + { + if ($directory === null) { + $directory = dirname(dirname(__DIR__)) . '/framework/yii'; + } + + $nFilesTotal = 0; + $files = new \RegexIterator( + new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory)), + '#^.+\.php$#i', + \RecursiveRegexIterator::GET_MATCH + ); + foreach ($files as $file) { + list($className, $phpdoc) = $this->generateClassPropertyDocs($file[0]); + if ($phpdoc !== false) { + if ($updateFiles) { + $this->updateClassPropertyDocs($file[0], $className, $phpdoc); + } else { + $this->stdout("\n[ " . $file[0] . " ]\n\n", Console::BOLD); + $this->stdout($phpdoc . "\n"); + } + } + $nFilesTotal++; + } + + $this->stdout("\n\nParsed $nFilesTotal files.\n"); + } + + protected function updateClassPropertyDocs($file, $className, $phpDoc) + { + // TODO implement + } + + protected function generateClassPropertyDocs($fileName) + { + $phpdoc = ""; + $file = str_replace("\r", "", str_replace("\t", " ", file_get_contents($fileName, true))); + $ns = $this->match('#\nnamespace (?[\w\\\\]+);\n#', $file); + $namespace = reset($ns); + $namespace = $namespace['name']; + $classes = $this->match('#\n(?:abstract )?class (?\w+) extends .+\{(?.+)\n\}(\n|$)#', $file); + + if (count($classes) > 1) { + $this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED); + return false; + } + if (count($classes) < 1) { + $this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED); + return false; + } + + $className = null; + foreach ($classes as &$class) { + + $className = $namespace . '\\' . $class['name']; + + $gets = $this->match( + '#\* @return (?\w+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', + $class['content']); + $sets = $this->match( + '#\* @param (?\w+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', + $class['content']); + $acrs = array_merge($gets, $sets); + //print_r($acrs); continue; + + $props = array(); + foreach ($acrs as &$acr) { + $acr['name'] = lcfirst($acr['name']); + $acr['comment'] = trim(preg_replace('#(^|\n)\s+\*\s?#', '$1 * ', $acr['comment'])); + $props[$acr['name']][$acr['kind']] = array( + 'type' => $acr['type'], + 'comment' => $this->fixSentence($acr['comment']), + ); + } + +// foreach ($props as $propName => &$prop) // I don't like write-only props... +// if (!isset($prop['get'])) +// unset($props[$propName]); + + if (count($props) > 0) { + $phpdoc .= " *\n"; + foreach ($props as $propName => &$prop) { + $phpdoc .= ' * @'; +// if (isset($prop['get']) && isset($prop['set'])) // Few IDEs support complex syntax + $phpdoc .= 'property'; +// elseif (isset($prop['get'])) +// $phpdoc .= 'property-read'; +// elseif (isset($prop['set'])) +// $phpdoc .= 'property-write'; + $phpdoc .= ' ' . $this->getPropParam($prop, 'type') . " $$propName " . $this->getPropParam($prop, 'comment') . "\n"; + } + $phpdoc .= " *\n"; + } + } + return array($className, $phpdoc); + } + + protected function match($pattern, $subject) + { + $sets = array(); + preg_match_all($pattern . 'suU', $subject, $sets, PREG_SET_ORDER); + foreach ($sets as &$set) + foreach ($set as $i => $match) + if (is_numeric($i) /*&& $i != 0*/) + unset($set[$i]); + return $sets; + } + + protected function fixSentence($str) + { + // TODO fix word wrap + if ($str == '') + return ''; + return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[strlen($str) - 1] != '.' ? '.' : ''); + } + + protected function getPropParam($prop, $param) + { + return isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]; + } +} \ No newline at end of file From 14181b2be0bffac55dd4123bb039e3a1abcabaef Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 11:36:25 +0200 Subject: [PATCH 019/156] Update PHPDoc command to update classfiles automatically --- build/controllers/PhpDocController.php | 172 +++++++++++++++++++++++++++++---- framework/yii/db/redis/Connection.php | 1 + framework/yii/db/redis/Transaction.php | 1 + 3 files changed, 153 insertions(+), 21 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 444d33e..7f1ba58 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -9,6 +9,7 @@ namespace yii\build\controllers; use yii\console\Controller; use yii\helpers\Console; +use yii\helpers\FileHelper; /** * PhpDocController is there to help maintaining PHPDoc annotation in class files @@ -21,40 +22,162 @@ class PhpDocController extends Controller /** * Generates @property annotations in class files from getters and setters * - * @param string $directory the directory to parse files from - * @param boolean $updateFiles whether to update class docs directly + * @param null $root the directory to parse files from + * @param bool $updateFiles whether to update class docs directly */ - public function actionProperty($directory=null, $updateFiles=false) + public function actionProperty($root=null, $updateFiles=true) { - if ($directory === null) { - $directory = dirname(dirname(__DIR__)) . '/framework/yii'; + if ($root === null) { + $root = YII_PATH; } - + $root = FileHelper::normalizePath($root); + $options = array( + 'filter' => function ($path) { + if (is_file($path)) { + $file = basename($path); + if ($file[0] < 'A' || $file[0] > 'Z') { + return false; + } + } + return null; + }, + 'only' => array('.php'), + 'except' => array( + 'YiiBase.php', + 'Yii.php', + '/debug/views/', + '/requirements/', + '/gii/views/', + '/gii/generators/', + ), + ); + $files = FileHelper::findFiles($root, $options); $nFilesTotal = 0; - $files = new \RegexIterator( - new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory)), - '#^.+\.php$#i', - \RecursiveRegexIterator::GET_MATCH - ); + $nFilesUpdated = 0; foreach ($files as $file) { - list($className, $phpdoc) = $this->generateClassPropertyDocs($file[0]); - if ($phpdoc !== false) { + $result = $this->generateClassPropertyDocs($file); + if ($result !== false) { + list($className, $phpdoc) = $result; if ($updateFiles) { - $this->updateClassPropertyDocs($file[0], $className, $phpdoc); - } else { - $this->stdout("\n[ " . $file[0] . " ]\n\n", Console::BOLD); - $this->stdout($phpdoc . "\n"); + if ($this->updateClassPropertyDocs($file, $className, $phpdoc)) { + $nFilesUpdated++; + } + } elseif (!empty($phpdoc)) { + $this->stdout("\n[ " . $file . " ]\n\n", Console::BOLD); + $this->stdout($phpdoc); } } $nFilesTotal++; } $this->stdout("\n\nParsed $nFilesTotal files.\n"); + $this->stdout("Updated $nFilesUpdated files.\n"); } - protected function updateClassPropertyDocs($file, $className, $phpDoc) + protected function updateClassPropertyDocs($file, $className, $propertyDoc) { - // TODO implement + $ref = new \ReflectionClass($className); + if ($ref->getFileName() != $file) { + $this->stderr("[ERR] Unable to create ReflectionClass for class: $className loaded class is not from file: $file\n", Console::FG_RED); + } + + $oldDoc = $ref->getDocComment(); + $newDoc = $this->cleanDocComment($this->updateDocComment($oldDoc, $propertyDoc)); + + $seenSince = false; + $seenAuthor = false; + + $lines = explode("\n", $newDoc); + foreach($lines as $line) { + if (substr(trim($line), 0, 9) == '* @since ') { + $seenSince = true; + } elseif (substr(trim($line), 0, 10) == '* @author ') { + $seenAuthor = true; + } + } + + if (!$seenSince) { + $this->stderr("[ERR] No @since found in class doc in file: $file\n", Console::FG_RED); + } + if (!$seenAuthor) { + $this->stderr("[ERR] No @author found in class doc in file: $file\n", Console::FG_RED); + } + + if ($oldDoc != $newDoc) { + + $fileContent = file($file); + $start = $ref->getStartLine(); + $docStart = $start - count(explode("\n", $oldDoc)); + + $newFileContent = array(); + foreach($fileContent as $i => $line) { + if ($i > $start || $i < $docStart) { + $newFileContent[] = $line; + } else { + $newFileContent[] = trim($newDoc); + } + } + + file_put_contents($file, implode("\n", $newFileContent)); + + return true; + } + return false; + } + + /** + * remove multi empty lines and trim trailing whitespace + * + * @param $doc + * @return string + */ + protected function cleanDocComment($doc) + { + $lines = explode("\n", $doc); + $n = count($lines); + for($i = 0; $i < $n; $i++) { + $lines[$i] = rtrim($lines[$i]); + if (trim($lines[$i]) == '*' && trim($lines[$i + 1]) == '*') { + unset($lines[$i]); + } + } + return implode("\n", $lines); + } + + /** + * replace property annotations in doc comment + * @param $doc + * @param $properties + * @return string + */ + protected function updateDocComment($doc, $properties) + { + $lines = explode("\n", $doc); + $propertyPart = false; + $propertyPosition = false; + foreach($lines as $i => $line) { + if (substr(trim($line), 0, 12) == '* @property ') { + $propertyPart = true; + } elseif ($propertyPart && trim($line) == '*') { + $propertyPosition = $i; + $propertyPart = false; + } + if (substr(trim($line), 0, 10) == '* @author ' && $propertyPosition === false) { + $propertyPosition = $i; + $propertyPart = false; + } + if ($propertyPart) { + unset($lines[$i]); + } + } + $finalDoc = ''; + foreach($lines as $i => $line) { + $finalDoc .= $line . "\n"; + if ($i == $propertyPosition) { + $finalDoc .= $properties; + } + } + return $finalDoc; } protected function generateClassPropertyDocs($fileName) @@ -64,14 +187,21 @@ class PhpDocController extends Controller $ns = $this->match('#\nnamespace (?[\w\\\\]+);\n#', $file); $namespace = reset($ns); $namespace = $namespace['name']; - $classes = $this->match('#\n(?:abstract )?class (?\w+) extends .+\{(?.+)\n\}(\n|$)#', $file); + $classes = $this->match('#\n(?:abstract )?class (?\w+)( |\n)(extends )?.+\{(?.*)\n\}(\n|$)#', $file); if (count($classes) > 1) { $this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED); return false; } if (count($classes) < 1) { - $this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED); + $interfaces = $this->match('#\ninterface (?\w+)\n\{(?.+)\n\}(\n|$)#', $file); + if (count($interfaces) == 1) { + return false; + } elseif (count($interfaces) > 1) { + $this->stderr("[ERR] There should be only one interface in a file: $fileName\n", Console::FG_RED); + } else { + $this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED); + } return false; } diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php index 46db575..4a297d7 100644 --- a/framework/yii/db/redis/Connection.php +++ b/framework/yii/db/redis/Connection.php @@ -23,6 +23,7 @@ use yii\helpers\StringHelper; * @method mixed get($key) Set the string value of a key * TODO document methods * + * @author Carsten Brandt * @since 2.0 */ class Connection extends Component diff --git a/framework/yii/db/redis/Transaction.php b/framework/yii/db/redis/Transaction.php index 721a7be..97c5c88 100644 --- a/framework/yii/db/redis/Transaction.php +++ b/framework/yii/db/redis/Transaction.php @@ -17,6 +17,7 @@ use yii\db\Exception; * * @property boolean $isActive Whether the transaction is active. This property is read-only. * + * @author Carsten Brandt * @since 2.0 */ class Transaction extends \yii\base\Object From df21e10c5d7047b890c428954e903494887f9e25 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 11:51:19 +0200 Subject: [PATCH 020/156] fixed postioning of property tags in php-doc command --- build/controllers/PhpDocController.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 7f1ba58..17dcca7 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -105,16 +105,18 @@ class PhpDocController extends Controller if ($oldDoc != $newDoc) { - $fileContent = file($file); - $start = $ref->getStartLine(); - $docStart = $start - count(explode("\n", $oldDoc)); + $fileContent = explode("\n", file_get_contents($file)); + $start = $ref->getStartLine() - 2; + $docStart = $start - count(explode("\n", $oldDoc)) + 1; $newFileContent = array(); - foreach($fileContent as $i => $line) { + $n = count($fileContent); + for($i = 0; $i < $n; $i++) { if ($i > $start || $i < $docStart) { - $newFileContent[] = $line; + $newFileContent[] = $fileContent[$i]; } else { $newFileContent[] = trim($newDoc); + $i = $start; } } @@ -163,7 +165,7 @@ class PhpDocController extends Controller $propertyPart = false; } if (substr(trim($line), 0, 10) == '* @author ' && $propertyPosition === false) { - $propertyPosition = $i; + $propertyPosition = $i - 1; $propertyPart = false; } if ($propertyPart) { From 1c9b9681a752587de1338c39cf7c09b613ffa401 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 11:59:34 +0200 Subject: [PATCH 021/156] phpdoc command skip classes that do not extend Object --- build/controllers/PhpDocController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 17dcca7..1249966 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -81,6 +81,11 @@ class PhpDocController extends Controller $this->stderr("[ERR] Unable to create ReflectionClass for class: $className loaded class is not from file: $file\n", Console::FG_RED); } + if (!$ref->isSubclassOf('yii\base\Object') && $className != 'yii\base\Object') { + $this->stderr("[INFO] Skipping class $className as it is not a subclass of yii\\base\\Object\n", Console::FG_BLUE, Console::BOLD); + return false; + } + $oldDoc = $ref->getDocComment(); $newDoc = $this->cleanDocComment($this->updateDocComment($oldDoc, $propertyDoc)); @@ -103,7 +108,7 @@ class PhpDocController extends Controller $this->stderr("[ERR] No @author found in class doc in file: $file\n", Console::FG_RED); } - if ($oldDoc != $newDoc) { + if (trim($oldDoc) != trim($newDoc)) { $fileContent = explode("\n", file_get_contents($file)); $start = $ref->getStartLine() - 2; From f87016253515d85d8ec43a514d42baa2725e709a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 12:09:04 +0200 Subject: [PATCH 022/156] fixed indentation of PhpDocController and sort properties alphabetically --- build/controllers/PhpDocController.php | 122 +++++++++++++++++---------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 1249966..ccb0b82 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -54,21 +54,21 @@ class PhpDocController extends Controller $files = FileHelper::findFiles($root, $options); $nFilesTotal = 0; $nFilesUpdated = 0; - foreach ($files as $file) { - $result = $this->generateClassPropertyDocs($file); - if ($result !== false) { - list($className, $phpdoc) = $result; - if ($updateFiles) { - if ($this->updateClassPropertyDocs($file, $className, $phpdoc)) { - $nFilesUpdated++; - } - } elseif (!empty($phpdoc)) { - $this->stdout("\n[ " . $file . " ]\n\n", Console::BOLD); - $this->stdout($phpdoc); - } - } - $nFilesTotal++; - } + foreach ($files as $file) { + $result = $this->generateClassPropertyDocs($file); + if ($result !== false) { + list($className, $phpdoc) = $result; + if ($updateFiles) { + if ($this->updateClassPropertyDocs($file, $className, $phpdoc)) { + $nFilesUpdated++; + } + } elseif (!empty($phpdoc)) { + $this->stdout("\n[ " . $file . " ]\n\n", Console::BOLD); + $this->stdout($phpdoc); + } + } + $nFilesTotal++; + } $this->stdout("\n\nParsed $nFilesTotal files.\n"); $this->stdout("Updated $nFilesUpdated files.\n"); @@ -189,12 +189,12 @@ class PhpDocController extends Controller protected function generateClassPropertyDocs($fileName) { - $phpdoc = ""; - $file = str_replace("\r", "", str_replace("\t", " ", file_get_contents($fileName, true))); + $phpdoc = ""; + $file = str_replace("\r", "", str_replace("\t", " ", file_get_contents($fileName, true))); $ns = $this->match('#\nnamespace (?[\w\\\\]+);\n#', $file); $namespace = reset($ns); $namespace = $namespace['name']; - $classes = $this->match('#\n(?:abstract )?class (?\w+)( |\n)(extends )?.+\{(?.*)\n\}(\n|$)#', $file); + $classes = $this->match('#\n(?:abstract )?class (?\w+)( |\n)(extends )?.+\{(?.*)\n\}(\n|$)#', $file); if (count($classes) > 1) { $this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED); @@ -213,74 +213,76 @@ class PhpDocController extends Controller } $className = null; - foreach ($classes as &$class) { + foreach ($classes as &$class) { - $className = $namespace . '\\' . $class['name']; + $className = $namespace . '\\' . $class['name']; - $gets = $this->match( - '#\* @return (?\w+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . - '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', - $class['content']); - $sets = $this->match( - '#\* @param (?\w+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . - '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', - $class['content']); - $acrs = array_merge($gets, $sets); - //print_r($acrs); continue; + $gets = $this->match( + '#\* @return (?\w+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', + $class['content']); + $sets = $this->match( + '#\* @param (?\w+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', + $class['content']); + $acrs = array_merge($gets, $sets); + //print_r($acrs); continue; - $props = array(); - foreach ($acrs as &$acr) { - $acr['name'] = lcfirst($acr['name']); - $acr['comment'] = trim(preg_replace('#(^|\n)\s+\*\s?#', '$1 * ', $acr['comment'])); - $props[$acr['name']][$acr['kind']] = array( - 'type' => $acr['type'], - 'comment' => $this->fixSentence($acr['comment']), - ); - } + $props = array(); + foreach ($acrs as &$acr) { + $acr['name'] = lcfirst($acr['name']); + $acr['comment'] = trim(preg_replace('#(^|\n)\s+\*\s?#', '$1 * ', $acr['comment'])); + $props[$acr['name']][$acr['kind']] = array( + 'type' => $acr['type'], + 'comment' => $this->fixSentence($acr['comment']), + ); + } + + ksort($props); // foreach ($props as $propName => &$prop) // I don't like write-only props... // if (!isset($prop['get'])) // unset($props[$propName]); - if (count($props) > 0) { - $phpdoc .= " *\n"; - foreach ($props as $propName => &$prop) { - $phpdoc .= ' * @'; + if (count($props) > 0) { + $phpdoc .= " *\n"; + foreach ($props as $propName => &$prop) { + $phpdoc .= ' * @'; // if (isset($prop['get']) && isset($prop['set'])) // Few IDEs support complex syntax $phpdoc .= 'property'; // elseif (isset($prop['get'])) // $phpdoc .= 'property-read'; // elseif (isset($prop['set'])) // $phpdoc .= 'property-write'; - $phpdoc .= ' ' . $this->getPropParam($prop, 'type') . " $$propName " . $this->getPropParam($prop, 'comment') . "\n"; - } - $phpdoc .= " *\n"; - } - } - return array($className, $phpdoc); + $phpdoc .= ' ' . $this->getPropParam($prop, 'type') . " $$propName " . $this->getPropParam($prop, 'comment') . "\n"; + } + $phpdoc .= " *\n"; + } + } + return array($className, $phpdoc); } protected function match($pattern, $subject) { - $sets = array(); - preg_match_all($pattern . 'suU', $subject, $sets, PREG_SET_ORDER); - foreach ($sets as &$set) - foreach ($set as $i => $match) - if (is_numeric($i) /*&& $i != 0*/) - unset($set[$i]); - return $sets; + $sets = array(); + preg_match_all($pattern . 'suU', $subject, $sets, PREG_SET_ORDER); + foreach ($sets as &$set) + foreach ($set as $i => $match) + if (is_numeric($i) /*&& $i != 0*/) + unset($set[$i]); + return $sets; } protected function fixSentence($str) { // TODO fix word wrap - if ($str == '') - return ''; - return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[strlen($str) - 1] != '.' ? '.' : ''); + if ($str == '') + return ''; + return strtoupper(substr($str, 0, 1)) . substr($str, 1) . ($str[strlen($str) - 1] != '.' ? '.' : ''); } protected function getPropParam($prop, $param) { - return isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]; + return isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]; } } \ No newline at end of file From 172d92958f590d3957747e222cb718e2c611cdcf Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 28 Aug 2013 06:46:43 -0400 Subject: [PATCH 023/156] refactored code. --- framework/yii/caching/FileCache.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/framework/yii/caching/FileCache.php b/framework/yii/caching/FileCache.php index 54d6506..437a7be 100644 --- a/framework/yii/caching/FileCache.php +++ b/framework/yii/caching/FileCache.php @@ -71,11 +71,7 @@ class FileCache extends Cache public function exists($key) { $cacheFile = $this->getCacheFile($this->buildKey($key)); - if (($time = @filemtime($cacheFile)) > time()) { - return true; - } else { - return false; - } + return @filemtime($cacheFile) > time(); } /** @@ -87,7 +83,7 @@ class FileCache extends Cache protected function getValue($key) { $cacheFile = $this->getCacheFile($key); - if (($time = @filemtime($cacheFile)) > time()) { + if (@filemtime($cacheFile) > time()) { return @file_get_contents($cacheFile); } else { return false; From bdb77f33bafe7564a230d14fe958ebe096f75523 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 14:16:13 +0200 Subject: [PATCH 024/156] updated @property annotations of many classes --- framework/yii/base/Action.php | 2 ++ framework/yii/base/Application.php | 17 +++++++++ framework/yii/base/Controller.php | 6 ++++ framework/yii/base/Request.php | 4 +++ framework/yii/base/Theme.php | 1 - framework/yii/base/View.php | 2 ++ framework/yii/base/Widget.php | 4 +++ framework/yii/caching/Cache.php | 1 - framework/yii/caching/ChainedDependency.php | 2 -- framework/yii/caching/Dependency.php | 2 -- framework/yii/caching/MemCache.php | 5 +-- framework/yii/captcha/CaptchaAction.php | 2 ++ framework/yii/captcha/CaptchaValidator.php | 2 ++ framework/yii/console/Application.php | 2 ++ framework/yii/console/Request.php | 3 ++ .../yii/console/controllers/AssetController.php | 2 +- .../yii/console/controllers/HelpController.php | 2 ++ framework/yii/data/ActiveDataProvider.php | 6 ++++ framework/yii/data/ArrayDataProvider.php | 5 +++ framework/yii/data/DataProvider.php | 4 +++ framework/yii/data/Pagination.php | 9 ++--- framework/yii/data/Sort.php | 6 ++++ framework/yii/db/Command.php | 3 +- framework/yii/db/DataReader.php | 8 ++--- framework/yii/db/Schema.php | 5 ++- framework/yii/db/TableSchema.php | 2 +- framework/yii/db/Transaction.php | 3 +- framework/yii/db/redis/Connection.php | 6 ++-- framework/yii/db/redis/Transaction.php | 3 +- framework/yii/debug/Panel.php | 5 +++ framework/yii/gii/CodeFile.php | 3 ++ framework/yii/gii/Generator.php | 4 +++ framework/yii/grid/SerialColumn.php | 2 +- framework/yii/log/Logger.php | 6 ++++ framework/yii/log/Target.php | 3 +- framework/yii/rbac/Item.php | 2 ++ framework/yii/validators/FileValidator.php | 2 ++ framework/yii/web/Application.php | 7 ++++ framework/yii/web/AssetManager.php | 2 ++ framework/yii/web/CacheSession.php | 2 ++ framework/yii/web/Controller.php | 1 - framework/yii/web/CookieCollection.php | 3 +- framework/yii/web/DbSession.php | 6 ++-- framework/yii/web/HeaderCollection.php | 3 ++ framework/yii/web/Request.php | 41 ++++++++++++++++++++++ framework/yii/web/Response.php | 15 ++++++++ framework/yii/web/Session.php | 23 +++++++----- framework/yii/web/UploadedFile.php | 13 +++++++ framework/yii/web/UrlManager.php | 3 ++ framework/yii/web/User.php | 6 ++++ framework/yii/widgets/FragmentCache.php | 3 ++ framework/yii/widgets/MaskedInput.php | 6 ++-- 52 files changed, 236 insertions(+), 44 deletions(-) diff --git a/framework/yii/base/Action.php b/framework/yii/base/Action.php index a4ae163..2ec73ce 100644 --- a/framework/yii/base/Action.php +++ b/framework/yii/base/Action.php @@ -28,6 +28,8 @@ use Yii; * And the parameters provided for the action are: `array('id' => 1)`. * Then the `run()` method will be invoked as `run(1)` automatically. * + * @property string $uniqueId The unique ID of this action among the whole application. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 0d0dd89..1ebc230 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -14,6 +14,23 @@ use yii\web\HttpException; /** * Application is the base class for all application classes. * + * @property \yii\rbac\Manager $authManager The auth manager for this application. + * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled. + * @property \yii\db\Connection $db The database connection. + * @property ErrorHandler $errorHandler The error handler application component. + * @property \yii\base\Formatter $formatter The formatter application component. + * @property \yii\i18n\I18N $i18N The internationalization component. + * @property \yii\log\Logger $log The log component. + * @property \yii\web\Request|\yii\console\Request $request The request component. + * @property string $runtimePath The directory that stores runtime files. + * Defaults to the "runtime" subdirectory under [[basePath]]. + * @property string $timeZone The time zone used by this application. + * @property string $uniqueId The unique ID of the module. + * @property \yii\web\UrlManager $urlManager The URL manager for this application. + * @property string $vendorPath The directory that stores vendor files. + * Defaults to "vendor" directory under [[basePath]]. + * @property View $view The view object that is used to render various view files. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index 5289b5b..18b7067 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -12,6 +12,12 @@ use Yii; /** * Controller is the base class for classes containing controller logic. * + * @property array $actionParams The request parameters (name-value pairs) to be used for action parameter binding. + * @property string $route The route (module ID, controller ID and action ID) of the current request. + * @property string $uniqueId The controller ID that is prefixed with the module ID (if any). + * @property View $view The view object that can be used to render views or view files. + * @property string $viewPath The directory containing the view files for this controller. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Request.php b/framework/yii/base/Request.php index 45556ab..0d660d6 100644 --- a/framework/yii/base/Request.php +++ b/framework/yii/base/Request.php @@ -8,6 +8,10 @@ namespace yii\base; /** + * + * @property boolean $isConsoleRequest The value indicating whether the current request is made via console. + * @property string $scriptFile Entry script file path (processed w/ realpath()). + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Theme.php b/framework/yii/base/Theme.php index ffd2732..658ada1 100644 --- a/framework/yii/base/Theme.php +++ b/framework/yii/base/Theme.php @@ -41,7 +41,6 @@ use yii\helpers\FileHelper; * that contains the entry script of the application. If your theme is designed to handle modules, * you may configure the [[pathMap]] property like described above. * - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index a5a3832..ead1e12 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -21,6 +21,8 @@ use yii\widgets\FragmentCache; * * View provides a set of methods (e.g. [[render()]]) for rendering purpose. * + * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application component. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Widget.php b/framework/yii/base/Widget.php index 0c948e9..3230101 100644 --- a/framework/yii/base/Widget.php +++ b/framework/yii/base/Widget.php @@ -12,6 +12,10 @@ use Yii; /** * Widget is the base class for widgets. * + * @property string $id ID of the widget. + * @property View $view The view object that can be used to render views or view files. + * @property string $viewPath The directory containing the view files for this widget. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php index b129294..56c6d96 100644 --- a/framework/yii/caching/Cache.php +++ b/framework/yii/caching/Cache.php @@ -47,7 +47,6 @@ use yii\helpers\StringHelper; * - [[deleteValue()]]: delete the value with the specified key from cache * - [[flushValues()]]: delete all values from cache * - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/caching/ChainedDependency.php b/framework/yii/caching/ChainedDependency.php index 87debfe..d3b9953 100644 --- a/framework/yii/caching/ChainedDependency.php +++ b/framework/yii/caching/ChainedDependency.php @@ -14,8 +14,6 @@ namespace yii\caching; * considered changed; When [[dependOnAll]] is false, if one of the dependencies has NOT changed, * this dependency is considered NOT changed. * - * @property boolean $hasChanged Whether the dependency is changed or not. - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/caching/Dependency.php b/framework/yii/caching/Dependency.php index 1a8770b..91c0568 100644 --- a/framework/yii/caching/Dependency.php +++ b/framework/yii/caching/Dependency.php @@ -13,8 +13,6 @@ namespace yii\caching; * Child classes should override its [[generateDependencyData()]] for generating * the actual dependency data. * - * @property boolean $hasChanged Whether the dependency has changed. - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/caching/MemCache.php b/framework/yii/caching/MemCache.php index aaea696..ee8f1e7 100644 --- a/framework/yii/caching/MemCache.php +++ b/framework/yii/caching/MemCache.php @@ -52,8 +52,9 @@ use yii\base\InvalidConfigException; * In the above, two memcache servers are used: server1 and server2. You can configure more properties of * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options. * - * @property \Memcache|\Memcached $memCache The memcache instance (or memcached if [[useMemcached]] is true) used by this component. - * @property MemCacheServer[] $servers List of memcache server configurations. + * @property \Memcache|\Memcached $memcache The memcache (or memcached) object used by this cache component. + * @property array $servers List of memcache server configurations. Each element must be an array + * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/captcha/CaptchaAction.php b/framework/yii/captcha/CaptchaAction.php index 771cc02..a30d289 100644 --- a/framework/yii/captcha/CaptchaAction.php +++ b/framework/yii/captcha/CaptchaAction.php @@ -29,6 +29,8 @@ use yii\base\InvalidConfigException; * to be validated by the 'captcha' validator. * 3. In the controller view, insert a [[Captcha]] widget in the form. * + * @property string $verifyCode The verification code. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/captcha/CaptchaValidator.php b/framework/yii/captcha/CaptchaValidator.php index e710573..11d171b 100644 --- a/framework/yii/captcha/CaptchaValidator.php +++ b/framework/yii/captcha/CaptchaValidator.php @@ -18,6 +18,8 @@ use yii\validators\Validator; * * CaptchaValidator should be used together with [[CaptchaAction]]. * + * @property \yii\captcha\CaptchaAction $captchaAction The action object. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/console/Application.php b/framework/yii/console/Application.php index a69abe0..9ae7263 100644 --- a/framework/yii/console/Application.php +++ b/framework/yii/console/Application.php @@ -45,6 +45,8 @@ use yii\base\InvalidRouteException; * yii help * ~~~ * + * @property Response $response The response component. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/console/Request.php b/framework/yii/console/Request.php index fe1e23b..dca0240 100644 --- a/framework/yii/console/Request.php +++ b/framework/yii/console/Request.php @@ -8,6 +8,9 @@ namespace yii\console; /** + * + * @property array $params The command line arguments. It does not include the entry script name. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index c4d4b5a..287beca 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -28,7 +28,7 @@ use yii\console\Controller; * Note: by default this command relies on an external tools to perform actual files compression, * check [[jsCompressor]] and [[cssCompressor]] for more details. * - * @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing. + * @property \yii\web\AssetManager $assetManager Asset manager instance. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/console/controllers/HelpController.php b/framework/yii/console/controllers/HelpController.php index bf97e90..cc9264b 100644 --- a/framework/yii/console/controllers/HelpController.php +++ b/framework/yii/console/controllers/HelpController.php @@ -31,6 +31,8 @@ use yii\helpers\Inflector; * In the above, if the command name is not provided, all * available commands will be displayed. * + * @property array $commands All available command names. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 0347db1..5b94b5a 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -49,6 +49,12 @@ use yii\db\Connection; * $posts = $provider->getModels(); * ~~~ * + * @property integer $count The number of data models in the current page. + * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] + * is uniquely identified by the corresponding key value in this array. + * @property array $models The list of data models in the current page. + * @property integer $totalCount Total number of possible data models. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/data/ArrayDataProvider.php b/framework/yii/data/ArrayDataProvider.php index e79a63e..9b92fdb 100644 --- a/framework/yii/data/ArrayDataProvider.php +++ b/framework/yii/data/ArrayDataProvider.php @@ -47,6 +47,11 @@ use yii\helpers\ArrayHelper; * Note: if you want to use the sorting feature, you must configure the [[sort]] property * so that the provider knows which columns can be sorted. * + * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] + * is uniquely identified by the corresponding key value in this array. + * @property array $models The list of data models in the current page. + * @property integer $totalCount Total number of possible data models. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/data/DataProvider.php b/framework/yii/data/DataProvider.php index 122e60a..677c7b4 100644 --- a/framework/yii/data/DataProvider.php +++ b/framework/yii/data/DataProvider.php @@ -16,6 +16,10 @@ use yii\base\InvalidParamException; * * It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[IDataProvider]] interface. * + * @property integer $count The number of data models in the current page. + * @property Pagination $pagination The pagination object. If this is false, it means the pagination is disabled. + * @property Sort $sort The sorting object. If this is false, it means the sorting is disabled. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/data/Pagination.php b/framework/yii/data/Pagination.php index 8d4c067..6383f8c 100644 --- a/framework/yii/data/Pagination.php +++ b/framework/yii/data/Pagination.php @@ -53,12 +53,13 @@ use yii\base\Object; * )); * ~~~ * - * @property integer $pageCount Number of pages. - * @property integer $page The zero-based index of the current page. - * @property integer $offset The offset of the data. This may be used to set the - * OFFSET value for a SQL statement for fetching the current page of data. * @property integer $limit The limit of the data. This may be used to set the * LIMIT value for a SQL statement for fetching the current page of data. + * Note that if the page size is infinite, a value -1 will be returned. + * @property integer $offset The offset of the data. This may be used to set the + * OFFSET value for a SQL statement for fetching the current page of data. + * @property integer $page The zero-based current page number. + * @property integer $pageCount Number of pages. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index d489c44..7422c7f 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -65,6 +65,12 @@ use yii\helpers\Inflector; * sorted by the orders specified by the Sort object. In the view, we show two hyperlinks * that can lead to pages with the data sorted by the corresponding attributes. * + * @property array $attributeOrders Sort directions indexed by attribute names. + * Sort direction can be either [[Sort::ASC]] for ascending order or + * [[Sort::DESC]] for descending order. + * @property array $orders The columns (keys) and their corresponding sort directions (values). + * This can be passed to [[\yii\db\Query::orderBy()]] to construct a DB query. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index feb6c5e..2af07bb 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -44,7 +44,8 @@ use yii\caching\Cache; * * To build SELECT SQL statements, please use [[QueryBuilder]] instead. * - * @property string $sql the SQL statement to be executed + * @property string $rawSql The raw SQL. + * @property string $sql The SQL statement to be executed. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/DataReader.php b/framework/yii/db/DataReader.php index ce776c0..9f6fac9 100644 --- a/framework/yii/db/DataReader.php +++ b/framework/yii/db/DataReader.php @@ -39,10 +39,10 @@ use yii\base\InvalidCallException; * [[fetchMode]]. See the [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) * for more details about possible fetch mode. * - * @property boolean $isClosed whether the reader is closed or not. - * @property integer $rowCount number of rows contained in the result. - * @property integer $columnCount the number of columns in the result set. - * @property mixed $fetchMode fetch mode used when retrieving the data. + * @property integer $columnCount The number of columns in the result set. + * @property mixed $fetchMode Fetch mode. + * @property boolean $isClosed Whether the reader is closed or not. + * @property integer $rowCount Number of rows contained in the result. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 8804f77..6795ad4 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -19,9 +19,8 @@ use yii\caching\GroupDependency; * * Schema represents the database schema information that is DBMS specific. * - * @property QueryBuilder $queryBuilder the query builder for the DBMS represented by this schema - * @property array $tableNames the names of all tables in this database. - * @property array $tableSchemas the schema information for all tables in this database. + * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object. + * @property QueryBuilder $queryBuilder The query builder for this connection. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/TableSchema.php b/framework/yii/db/TableSchema.php index cad4c0a..a0d2c15 100644 --- a/framework/yii/db/TableSchema.php +++ b/framework/yii/db/TableSchema.php @@ -13,7 +13,7 @@ use yii\base\InvalidParamException; /** * TableSchema represents the metadata of a database table. * - * @property array $columnNames list of column names + * @property array $columnNames List of column names. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/Transaction.php b/framework/yii/db/Transaction.php index 195a8c8..3be2d34 100644 --- a/framework/yii/db/Transaction.php +++ b/framework/yii/db/Transaction.php @@ -29,7 +29,8 @@ use yii\base\InvalidConfigException; * } * ~~~ * - * @property boolean $isActive Whether the transaction is active. This property is read-only. + * @property boolean $isActive Whether this transaction is active. Only an active transaction + * can [[commit()]] or [[rollback()]]. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php index 4a297d7..00ed80f 100644 --- a/framework/yii/db/redis/Connection.php +++ b/framework/yii/db/redis/Connection.php @@ -17,12 +17,14 @@ use yii\helpers\StringHelper; /** * - * - * * @method mixed set($key, $value) Set the string value of a key * @method mixed get($key) Set the string value of a key * TODO document methods * + * @property string $driverName Name of the DB driver. + * @property boolean $isActive Whether the DB connection is established. + * @property Transaction $transaction The currently active transaction. Null if no active transaction. + * * @author Carsten Brandt * @since 2.0 */ diff --git a/framework/yii/db/redis/Transaction.php b/framework/yii/db/redis/Transaction.php index 97c5c88..8458c85 100644 --- a/framework/yii/db/redis/Transaction.php +++ b/framework/yii/db/redis/Transaction.php @@ -15,7 +15,8 @@ use yii\db\Exception; /** * Transaction represents a DB transaction. * - * @property boolean $isActive Whether the transaction is active. This property is read-only. + * @property boolean $isActive Whether this transaction is active. Only an active transaction + * can [[commit()]] or [[rollBack()]]. * * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/debug/Panel.php b/framework/yii/debug/Panel.php index dc297f4..6c01362 100644 --- a/framework/yii/debug/Panel.php +++ b/framework/yii/debug/Panel.php @@ -14,6 +14,11 @@ use yii\base\Component; * Panel is a base class for debugger panel classes. It defines how data should be collected, * what should be displayed at debug toolbar and on debugger details view. * + * @property string $detail Content that is displayed in debugger detail view. + * @property string $name Name of the panel. + * @property string $summary Content that is displayed at debug toolbar. + * @property string $url URL pointing to panel detail view. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/gii/CodeFile.php b/framework/yii/gii/CodeFile.php index d3dcd43..6fcbdbd 100644 --- a/framework/yii/gii/CodeFile.php +++ b/framework/yii/gii/CodeFile.php @@ -16,6 +16,9 @@ use yii\helpers\StringHelper; /** * CodeFile represents a code file to be generated. * + * @property string $relativePath The code file path relative to the application base path. + * @property string $type The code file extension (e.g. php, txt). + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/gii/Generator.php b/framework/yii/gii/Generator.php index 68ca640..387f523 100644 --- a/framework/yii/gii/Generator.php +++ b/framework/yii/gii/Generator.php @@ -27,6 +27,10 @@ use yii\base\View; * - [[generate()]]: generates the code based on the current user input and the specified code template files. * This is the place where main code generation code resides. * + * @property string $description The detailed description of the generator. + * @property string $stickyDataFile The file path that stores the sticky attribute values. + * @property string $templatePath The root path of the template files that are currently being used. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/grid/SerialColumn.php b/framework/yii/grid/SerialColumn.php index 3a5e21b..2fdb770 100644 --- a/framework/yii/grid/SerialColumn.php +++ b/framework/yii/grid/SerialColumn.php @@ -9,7 +9,7 @@ namespace yii\grid; /** * SerialColumn displays a column of row numbers (1-based). - * + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php index bb7cea5..2d3f7c1 100644 --- a/framework/yii/log/Logger.php +++ b/framework/yii/log/Logger.php @@ -61,6 +61,12 @@ use yii\base\InvalidConfigException; * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]] * to send logged messages to different log targets, such as file, email, Web. * + * @property array $dbProfiling The first element indicates the number of SQL statements executed, + * and the second element the total time spent in SQL execution. + * @property float $elapsedTime The total elapsed time in seconds for current request. + * @property array $profiling The profiling results. Each array element has the following structure: + * `array($token, $category, $time)`. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php index 48c340f..12b5d15 100644 --- a/framework/yii/log/Target.php +++ b/framework/yii/log/Target.php @@ -22,7 +22,8 @@ use yii\base\InvalidConfigException; * satisfying both filter conditions will be handled. Additionally, you * may specify [[except]] to exclude messages of certain categories. * - * @property integer $levels the message levels that this target is interested in. + * @property integer $levels The message levels that this target is interested in. This is a bitmap of + * level values. Defaults to 0, meaning all available levels. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/rbac/Item.php b/framework/yii/rbac/Item.php index 2504ad5..4c09e28 100644 --- a/framework/yii/rbac/Item.php +++ b/framework/yii/rbac/Item.php @@ -18,6 +18,8 @@ use yii\base\Object; * A user may be assigned one or several authorization items (called [[Assignment]] assignments). * He can perform an operation only when it is among his assigned items. * + * @property string $name The item name. + * * @author Qiang Xue * @author Alexander Kochetov * @since 2.0 diff --git a/framework/yii/validators/FileValidator.php b/framework/yii/validators/FileValidator.php index 8a3ab9e..44e6cd2 100644 --- a/framework/yii/validators/FileValidator.php +++ b/framework/yii/validators/FileValidator.php @@ -13,6 +13,8 @@ use yii\web\UploadedFile; /** * FileValidator verifies if an attribute is receiving a valid uploaded file. * + * @property integer $sizeLimit The size limit for uploaded files. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php index 249b0b4..1a00846 100644 --- a/framework/yii/web/Application.php +++ b/framework/yii/web/Application.php @@ -13,6 +13,13 @@ use yii\base\InvalidRouteException; /** * Application is the base class for all application classes. * + * @property AssetManager $assetManager The asset manager component. + * @property string $homeUrl The homepage URL. + * @property Request $request The request component. + * @property Response $response The response component. + * @property Session $session The session component. + * @property User $user The user component. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php index 702fd30..fd7ed1d 100644 --- a/framework/yii/web/AssetManager.php +++ b/framework/yii/web/AssetManager.php @@ -16,6 +16,8 @@ use yii\helpers\FileHelper; /** * AssetManager manages asset bundles and asset publishing. * + * @property IAssetConverter $converter The asset converter. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php index 79acd74..bb04cff 100644 --- a/framework/yii/web/CacheSession.php +++ b/framework/yii/web/CacheSession.php @@ -21,6 +21,8 @@ use yii\base\InvalidConfigException; * may be swapped out and get lost. Therefore, you must make sure the cache used by this component * is NOT volatile. If you want to use database as storage medium, use [[DbSession]] is a better choice. * + * @property boolean $useCustomStorage Whether to use custom storage. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index b094981..b9aa68c 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -14,7 +14,6 @@ use yii\helpers\Html; /** * Controller is the base class of Web controllers. * - * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/CookieCollection.php b/framework/yii/web/CookieCollection.php index 3e22092..9384d31 100644 --- a/framework/yii/web/CookieCollection.php +++ b/framework/yii/web/CookieCollection.php @@ -15,7 +15,8 @@ use yii\base\Object; /** * CookieCollection maintains the cookies available in the current request. * - * @property integer $count the number of cookies in the collection + * @property integer $count The number of cookies in the collection. + * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/DbSession.php b/framework/yii/web/DbSession.php index 30f5ed0..3d6b204 100644 --- a/framework/yii/web/DbSession.php +++ b/framework/yii/web/DbSession.php @@ -17,9 +17,9 @@ use yii\base\InvalidConfigException; * * By default, DbSession stores session data in a DB table named 'tbl_session'. This table * must be pre-created. The table name can be changed by setting [[sessionTable]]. - * + * * The following example shows how you can configure the application to use DbSession: - * + * * ~~~ * 'session' => array( * 'class' => 'yii\web\DbSession', @@ -28,6 +28,8 @@ use yii\base\InvalidConfigException; * ) * ~~~ * + * @property boolean $useCustomStorage Whether to use custom storage. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/HeaderCollection.php b/framework/yii/web/HeaderCollection.php index 872a997..b0b422a 100644 --- a/framework/yii/web/HeaderCollection.php +++ b/framework/yii/web/HeaderCollection.php @@ -14,6 +14,9 @@ use ArrayIterator; /** * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. * + * @property integer $count The number of headers in the collection. + * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index c2f5c4b..3833dd7 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -12,6 +12,47 @@ use yii\base\InvalidConfigException; use yii\helpers\Security; /** + * + * @property string $absoluteUrl The currently requested absolute URL. + * @property string $acceptTypes User browser accept types, null if not present. + * @property array $acceptedContentTypes The content types ordered by the preference level. The first element + * represents the most preferred content type. + * @property array $acceptedLanguages The languages ordered by the preference level. The first element + * represents the most preferred language. + * @property string $baseUrl The relative URL for the application. + * @property string $cookieValidationKey The secret key used for cookie validation. If it was not set previously, + * a random key will be generated and used. + * @property CookieCollection $cookies The cookie collection. + * @property string $csrfToken The random token for CSRF validation. + * @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g. `http://www.yiiframework.com`). + * @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. + * @property boolean $isDelete Whether this is a DELETE request. + * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. + * @property boolean $isPatch Whether this is a PATCH request. + * @property boolean $isPost Whether this is a POST request. + * @property boolean $isPut Whether this is a PUT request. + * @property boolean $isSecureConnection If the request is sent via secure channel (https). + * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. + * The value returned is turned into upper case. + * @property string $pathInfo Part of the request URL that is after the entry script and before the question mark. + * Note, the returned path info is already URL-decoded. + * @property integer $port Port number for insecure requests. + * @property string $preferredLanguage The language that the application should use. Null is returned if both [[getAcceptedLanguages()]] + * and `$languages` are empty. + * @property string $queryString Part of the request URL that is after the question mark. + * @property string $rawBody The request body. + * @property string $referrer URL referrer, null if not present. + * @property array $restParams The RESTful request parameters. + * @property string $scriptFile The entry script file path. + * @property string $scriptUrl The relative URL of the entry script. + * @property integer $securePort Port number for secure requests. + * @property string $serverName Server name. + * @property integer $serverPort Server port number. + * @property string $url The currently requested relative URL. Note that the URI returned is URL-encoded. + * @property string $userAgent User agent, null if not present. + * @property string $userHost User host name, null if cannot be determined. + * @property string $userIP User IP address. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index 19a9ea2..fb7ee65 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -17,6 +17,21 @@ use yii\helpers\Security; use yii\helpers\StringHelper; /** + * + * @property CookieCollection $cookies The cookie collection. + * @property HeaderCollection $headers The header collection. + * @property boolean $isClientError Whether this response indicates a client error. + * @property boolean $isEmpty Whether this response is empty. + * @property boolean $isForbidden Whether this response indicates the current request is forbidden. + * @property boolean $isInformational Whether this response is informational. + * @property boolean $isInvalid Whether this response has a valid [[statusCode]]. + * @property boolean $isNotFound Whether this response indicates the currently requested resource is not found. + * @property boolean $isOk Whether this response is OK. + * @property boolean $isRedirection Whether this response is a redirection. + * @property boolean $isServerError Whether this response indicates a server error. + * @property boolean $isSuccessful Whether this response is successful. + * @property integer $statusCode The HTTP status code to send with the response. + * * @author Qiang Xue * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index cbed8fc..df98448 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -45,18 +45,23 @@ use yii\base\InvalidParamException; * useful for displaying confirmation messages. To use flash messages, simply * call methods such as [[setFlash()]], [[getFlash()]]. * + * @property array $allFlashes Flash messages (key => message). + * @property array $cookieParams The session cookie parameters. + * @property integer $count The number of session variables. + * @property string $flash The key identifying the flash message. Note that flash messages + * and normal session variables share the same name space. If you have a normal + * session variable using the same name, its value will be overwritten by this method. + * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. * @property string $id The current session ID. + * @property boolean $isActive Whether the session has started. + * @property SessionIterator $iterator An iterator for traversing the session variables. * @property string $name The current session name. - * @property boolean $isActive Whether the session has started - * @property string $savePath The current session save path, defaults to {@link http://php.net/manual/en/session.configuration.php#ini.session.save-path}. - * @property array $cookieParams The session cookie parameters. - * @property boolean $useCookies Whether cookies should be used to store session IDs. - * @property float $gCProbability The probability (percentage) that the gc (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. + * @property string $savePath The current session save path, defaults to '/tmp'. + * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up. + * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini). + * @property boolean|null $useCookies The value indicating whether cookies should be used to store session IDs. + * @property boolean $useCustomStorage Whether to use custom storage. * @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to false. - * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up, defaults to 1440 seconds. - * @property SessionIterator $iterator An iterator for traversing the session variables. - * @property integer $count The number of session variables. - * @property array $allFlashes The list of all flash messages. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php index ee3b1f0..82116bd 100644 --- a/framework/yii/web/UploadedFile.php +++ b/framework/yii/web/UploadedFile.php @@ -10,6 +10,19 @@ namespace yii\web; use yii\helpers\Html; /** + * + * @property integer $error The error code. + * @property boolean $hasError Whether there is an error with the uploaded file. + * Check [[error]] for detailed error code information. + * @property string $name The original name of the file being uploaded. + * @property integer $size The actual size of the uploaded file in bytes. + * @property string $tempName The path of the uploaded file on the server. + * Note, this is a temporary file which will be automatically deleted by PHP + * after the current request is processed. + * @property string $type The MIME-type of the uploaded file (such as "image/gif"). + * Since this MIME type is not checked on the server side, do not take this value for granted. + * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 6617947..071c5eb 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -14,6 +14,9 @@ use yii\caching\Cache; /** * UrlManager handles HTTP request parsing and creation of URLs based on a set of rules. * + * @property string $baseUrl The base URL that is used by [[createUrl()]] to prepend URLs it creates. + * @property string $hostInfo The host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index 0ad8f68..bae78ec 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -20,6 +20,12 @@ use yii\base\InvalidConfigException; * User works with a class implementing the [[Identity]] interface. This class implements * the actual user authentication logic and is often backed by a user database table. * + * @property string|integer $id The unique identifier for the user. If null, it means the user is a guest. + * @property Identity $identity The identity object associated with the currently logged user. + * Null is returned if the user is not logged in (not authenticated). + * @property boolean $isGuest Whether the current user is a guest. + * @property string $returnUrl The URL that the user should be redirected to after login. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/widgets/FragmentCache.php b/framework/yii/widgets/FragmentCache.php index 8632566..f67eff3 100644 --- a/framework/yii/widgets/FragmentCache.php +++ b/framework/yii/widgets/FragmentCache.php @@ -13,6 +13,9 @@ use yii\caching\Cache; use yii\caching\Dependency; /** + * + * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found in the cache. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/widgets/MaskedInput.php b/framework/yii/widgets/MaskedInput.php index 35794b8..8b8cbc6 100644 --- a/framework/yii/widgets/MaskedInput.php +++ b/framework/yii/widgets/MaskedInput.php @@ -18,17 +18,17 @@ use yii\web\JsExpression; * MaskedInput is similar to [[Html::textInput()]] except that * an input mask will be used to force users to enter properly formatted data, * such as phone numbers, social security numbers. - * + * * To use MaskedInput, you must set the [[mask]] property. The following example * shows how to use MaskedInput to collect phone numbers: - * + * * ~~~ * echo MaskedInput::widget(array( * 'name' => 'phone', * 'mask' => '999-999-9999', * )); * ~~~ - * + * * The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin). * * @author Qiang Xue From 6405c4f3495ec6c8a03202d0ef2fe4431552e546 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 14:36:11 +0200 Subject: [PATCH 025/156] added proper word wrapping and note about read/writeonly properties --- build/controllers/PhpDocController.php | 37 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index ccb0b82..93c7de6 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -82,7 +82,7 @@ class PhpDocController extends Controller } if (!$ref->isSubclassOf('yii\base\Object') && $className != 'yii\base\Object') { - $this->stderr("[INFO] Skipping class $className as it is not a subclass of yii\\base\\Object\n", Console::FG_BLUE, Console::BOLD); + $this->stderr("[INFO] Skipping class $className as it is not a subclass of yii\\base\\Object.\n", Console::FG_BLUE, Console::BOLD); return false; } @@ -92,7 +92,11 @@ class PhpDocController extends Controller $seenSince = false; $seenAuthor = false; + // TODO move these checks to different action $lines = explode("\n", $newDoc); + if (trim($lines[1]) == '*' || substr(trim($lines[1]), 0, 3) == '* @') { + $this->stderr("[WARN] Class $className has no short description.\n", Console::FG_YELLOW, Console::BOLD); + } foreach($lines as $line) { if (substr(trim($line), 0, 9) == '* @since ') { $seenSince = true; @@ -218,11 +222,11 @@ class PhpDocController extends Controller $className = $namespace . '\\' . $class['name']; $gets = $this->match( - '#\* @return (?\w+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '#\* @return (?[\w\\|\\\\]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', $class['content']); $sets = $this->match( - '#\* @param (?\w+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '#\* @param (?[\w\\|\\\\]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', $class['content']); $acrs = array_merge($gets, $sets); @@ -247,14 +251,25 @@ class PhpDocController extends Controller if (count($props) > 0) { $phpdoc .= " *\n"; foreach ($props as $propName => &$prop) { - $phpdoc .= ' * @'; -// if (isset($prop['get']) && isset($prop['set'])) // Few IDEs support complex syntax - $phpdoc .= 'property'; -// elseif (isset($prop['get'])) -// $phpdoc .= 'property-read'; -// elseif (isset($prop['set'])) -// $phpdoc .= 'property-write'; - $phpdoc .= ' ' . $this->getPropParam($prop, 'type') . " $$propName " . $this->getPropParam($prop, 'comment') . "\n"; + $docline = ' * @'; + $docline .= 'property'; // Do not use property-read and property-write as few IDEs support complex syntax. + if (isset($prop['get']) && isset($prop['set'])) { + $note = ''; + } elseif (isset($prop['get'])) { + $note = ' This property is read-only.'; +// $docline .= '-read'; + } elseif (isset($prop['set'])) { + $note = ' This property is write-only.'; +// $docline .= '-write'; + } + $docline .= ' ' . $this->getPropParam($prop, 'type') . " $$propName "; + $comment = explode("\n", $this->getPropParam($prop, 'comment') . $note); + foreach ($comment as &$cline) { + $cline = ltrim($cline, '* '); + } + $docline = wordwrap($docline . implode(' ', $comment), 110, "\n * ") . "\n"; + + $phpdoc .= $docline; } $phpdoc .= " *\n"; } From 8366cb6e671d29e7352150533ba85c59cf977bd4 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 14:54:00 +0200 Subject: [PATCH 026/156] allow [] in property doc type --- build/controllers/PhpDocController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 93c7de6..a96495b 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -222,11 +222,11 @@ class PhpDocController extends Controller $className = $namespace . '\\' . $class['name']; $gets = $this->match( - '#\* @return (?[\w\\|\\\\]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '#\* @return (?[\w\\|\\\\\\[\\]]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\s\n]{2,}public function (?get)(?\w+)\((?:,? ?\$\w+ ?= ?[^,]+)*\)#', $class['content']); $sets = $this->match( - '#\* @param (?[\w\\|\\\\]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '#\* @param (?[\w\\|\\\\\\[\\]]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', $class['content']); $acrs = array_merge($gets, $sets); From ca69ef09d4a7d64f27a0c9527d6e1fd4b4698d6b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 14:59:52 +0200 Subject: [PATCH 027/156] update of @property annotations --- framework/yii/base/Action.php | 3 +- framework/yii/base/Application.php | 29 ++++++----- framework/yii/base/Component.php | 3 ++ framework/yii/base/Controller.php | 12 +++-- framework/yii/base/View.php | 3 +- framework/yii/base/Widget.php | 3 +- framework/yii/caching/RedisCache.php | 2 + framework/yii/captcha/CaptchaAction.php | 2 +- framework/yii/captcha/CaptchaValidator.php | 2 +- framework/yii/console/Application.php | 2 +- .../yii/console/controllers/HelpController.php | 2 +- framework/yii/data/ActiveDataProvider.php | 8 +-- framework/yii/data/ArrayDataProvider.php | 4 +- framework/yii/data/DataProvider.php | 5 +- framework/yii/data/Pagination.php | 12 ++--- framework/yii/data/Sort.php | 9 ++-- framework/yii/db/ActiveRecord.php | 17 +++--- framework/yii/db/Command.php | 2 +- framework/yii/db/Connection.php | 15 +++--- framework/yii/db/DataReader.php | 8 +-- framework/yii/db/Schema.php | 8 ++- framework/yii/db/TableSchema.php | 2 +- framework/yii/db/Transaction.php | 4 +- framework/yii/db/redis/Connection.php | 7 +-- framework/yii/db/redis/Transaction.php | 4 +- framework/yii/debug/Panel.php | 8 +-- framework/yii/gii/CodeFile.php | 5 +- framework/yii/gii/Generator.php | 8 +-- framework/yii/log/Logger.php | 9 ++-- framework/yii/log/Target.php | 4 +- framework/yii/rbac/DbManager.php | 2 + framework/yii/rbac/Item.php | 1 + framework/yii/rbac/Manager.php | 6 +-- framework/yii/rbac/PhpManager.php | 2 +- framework/yii/validators/FileValidator.php | 2 +- framework/yii/web/Application.php | 10 ++-- framework/yii/web/CacheSession.php | 2 +- framework/yii/web/CookieCollection.php | 5 +- framework/yii/web/DbSession.php | 2 +- framework/yii/web/HeaderCollection.php | 5 +- framework/yii/web/Request.php | 60 ++++++++++++---------- framework/yii/web/Response.php | 28 +++++----- framework/yii/web/Session.php | 26 ++++++---- framework/yii/web/UploadedFile.php | 21 ++++---- framework/yii/web/UrlManager.php | 3 +- framework/yii/web/User.php | 7 +-- framework/yii/widgets/FragmentCache.php | 3 +- 47 files changed, 217 insertions(+), 170 deletions(-) diff --git a/framework/yii/base/Action.php b/framework/yii/base/Action.php index 2ec73ce..2693003 100644 --- a/framework/yii/base/Action.php +++ b/framework/yii/base/Action.php @@ -28,7 +28,8 @@ use Yii; * And the parameters provided for the action are: `array('id' => 1)`. * Then the `run()` method will be invoked as `run(1)` automatically. * - * @property string $uniqueId The unique ID of this action among the whole application. + * @property string $uniqueId The unique ID of this action among the whole application. This property is + * read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 1ebc230..9efb6ce 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -14,22 +14,23 @@ use yii\web\HttpException; /** * Application is the base class for all application classes. * - * @property \yii\rbac\Manager $authManager The auth manager for this application. + * @property \yii\rbac\Manager $authManager The auth manager for this application. This property is read-only. * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled. - * @property \yii\db\Connection $db The database connection. - * @property ErrorHandler $errorHandler The error handler application component. - * @property \yii\base\Formatter $formatter The formatter application component. - * @property \yii\i18n\I18N $i18N The internationalization component. - * @property \yii\log\Logger $log The log component. - * @property \yii\web\Request|\yii\console\Request $request The request component. - * @property string $runtimePath The directory that stores runtime files. - * Defaults to the "runtime" subdirectory under [[basePath]]. + * This property is read-only. + * @property \yii\db\Connection $db The database connection. This property is read-only. + * @property ErrorHandler $errorHandler The error handler application component. This property is read-only. + * @property \yii\base\Formatter $formatter The formatter application component. This property is read-only. + * @property \yii\i18n\I18N $i18N The internationalization component. This property is read-only. + * @property \yii\log\Logger $log The log component. This property is read-only. + * @property \yii\web\Request|\yii\console\Request $request The request component. This property is read-only. + * @property string $runtimePath The directory that stores runtime files. Defaults to the "runtime" + * subdirectory under [[basePath]]. * @property string $timeZone The time zone used by this application. - * @property string $uniqueId The unique ID of the module. - * @property \yii\web\UrlManager $urlManager The URL manager for this application. - * @property string $vendorPath The directory that stores vendor files. - * Defaults to "vendor" directory under [[basePath]]. - * @property View $view The view object that is used to render various view files. + * @property string $uniqueId The unique ID of the module. This property is read-only. + * @property \yii\web\UrlManager $urlManager The URL manager for this application. This property is read-only. + * @property string $vendorPath The directory that stores vendor files. Defaults to "vendor" directory under + * [[basePath]]. + * @property View $view The view object that is used to render various view files. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index 5ff8ae4..6aebc92 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -11,6 +11,9 @@ use Yii; /** * @include @yii/base/Component.md + * + * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index 18b7067..20f6e2b 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -12,11 +12,15 @@ use Yii; /** * Controller is the base class for classes containing controller logic. * - * @property array $actionParams The request parameters (name-value pairs) to be used for action parameter binding. - * @property string $route The route (module ID, controller ID and action ID) of the current request. - * @property string $uniqueId The controller ID that is prefixed with the module ID (if any). + * @property array $actionParams The request parameters (name-value pairs) to be used for action parameter + * binding. This property is read-only. + * @property string $route The route (module ID, controller ID and action ID) of the current request. This + * property is read-only. + * @property string $uniqueId The controller ID that is prefixed with the module ID (if any). This property is + * read-only. * @property View $view The view object that can be used to render views or view files. - * @property string $viewPath The directory containing the view files for this controller. + * @property string $viewPath The directory containing the view files for this controller. This property is + * read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index ead1e12..4d3d996 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -21,7 +21,8 @@ use yii\widgets\FragmentCache; * * View provides a set of methods (e.g. [[render()]]) for rendering purpose. * - * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application component. + * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application + * component. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/base/Widget.php b/framework/yii/base/Widget.php index 3230101..943d007 100644 --- a/framework/yii/base/Widget.php +++ b/framework/yii/base/Widget.php @@ -14,7 +14,8 @@ use Yii; * * @property string $id ID of the widget. * @property View $view The view object that can be used to render views or view files. - * @property string $viewPath The directory containing the view files for this widget. + * @property string $viewPath The directory containing the view files for this widget. This property is + * read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/caching/RedisCache.php b/framework/yii/caching/RedisCache.php index df56852..09ce599 100644 --- a/framework/yii/caching/RedisCache.php +++ b/framework/yii/caching/RedisCache.php @@ -39,6 +39,8 @@ use yii\db\redis\Connection; * ) * ~~~ * + * @property \yii\db\redis\Connection $connection This property is read-only. + * * @author Carsten Brandt * @since 2.0 */ diff --git a/framework/yii/captcha/CaptchaAction.php b/framework/yii/captcha/CaptchaAction.php index a30d289..e1761a1 100644 --- a/framework/yii/captcha/CaptchaAction.php +++ b/framework/yii/captcha/CaptchaAction.php @@ -29,7 +29,7 @@ use yii\base\InvalidConfigException; * to be validated by the 'captcha' validator. * 3. In the controller view, insert a [[Captcha]] widget in the form. * - * @property string $verifyCode The verification code. + * @property string $verifyCode The verification code. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/captcha/CaptchaValidator.php b/framework/yii/captcha/CaptchaValidator.php index 11d171b..97bfd1b 100644 --- a/framework/yii/captcha/CaptchaValidator.php +++ b/framework/yii/captcha/CaptchaValidator.php @@ -18,7 +18,7 @@ use yii\validators\Validator; * * CaptchaValidator should be used together with [[CaptchaAction]]. * - * @property \yii\captcha\CaptchaAction $captchaAction The action object. + * @property \yii\captcha\CaptchaAction $captchaAction The action object. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/console/Application.php b/framework/yii/console/Application.php index 9ae7263..bbdd4e1 100644 --- a/framework/yii/console/Application.php +++ b/framework/yii/console/Application.php @@ -45,7 +45,7 @@ use yii\base\InvalidRouteException; * yii help * ~~~ * - * @property Response $response The response component. + * @property Response $response The response component. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/console/controllers/HelpController.php b/framework/yii/console/controllers/HelpController.php index cc9264b..f0e72cd 100644 --- a/framework/yii/console/controllers/HelpController.php +++ b/framework/yii/console/controllers/HelpController.php @@ -31,7 +31,7 @@ use yii\helpers\Inflector; * In the above, if the command name is not provided, all * available commands will be displayed. * - * @property array $commands All available command names. + * @property array $commands All available command names. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 5b94b5a..e36ef90 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -49,10 +49,10 @@ use yii\db\Connection; * $posts = $provider->getModels(); * ~~~ * - * @property integer $count The number of data models in the current page. - * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] - * is uniquely identified by the corresponding key value in this array. - * @property array $models The list of data models in the current page. + * @property integer $count The number of data models in the current page. This property is read-only. + * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is + * uniquely identified by the corresponding key value in this array. This property is read-only. + * @property array $models The list of data models in the current page. This property is read-only. * @property integer $totalCount Total number of possible data models. * * @author Qiang Xue diff --git a/framework/yii/data/ArrayDataProvider.php b/framework/yii/data/ArrayDataProvider.php index 9b92fdb..d6eaca7 100644 --- a/framework/yii/data/ArrayDataProvider.php +++ b/framework/yii/data/ArrayDataProvider.php @@ -47,8 +47,8 @@ use yii\helpers\ArrayHelper; * Note: if you want to use the sorting feature, you must configure the [[sort]] property * so that the provider knows which columns can be sorted. * - * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] - * is uniquely identified by the corresponding key value in this array. + * @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is + * uniquely identified by the corresponding key value in this array. * @property array $models The list of data models in the current page. * @property integer $totalCount Total number of possible data models. * diff --git a/framework/yii/data/DataProvider.php b/framework/yii/data/DataProvider.php index 677c7b4..8e648cc 100644 --- a/framework/yii/data/DataProvider.php +++ b/framework/yii/data/DataProvider.php @@ -16,8 +16,9 @@ use yii\base\InvalidParamException; * * It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[IDataProvider]] interface. * - * @property integer $count The number of data models in the current page. - * @property Pagination $pagination The pagination object. If this is false, it means the pagination is disabled. + * @property integer $count The number of data models in the current page. This property is read-only. + * @property Pagination $pagination The pagination object. If this is false, it means the pagination is + * disabled. * @property Sort $sort The sorting object. If this is false, it means the sorting is disabled. * * @author Qiang Xue diff --git a/framework/yii/data/Pagination.php b/framework/yii/data/Pagination.php index 6383f8c..1625fde 100644 --- a/framework/yii/data/Pagination.php +++ b/framework/yii/data/Pagination.php @@ -53,13 +53,13 @@ use yii\base\Object; * )); * ~~~ * - * @property integer $limit The limit of the data. This may be used to set the - * LIMIT value for a SQL statement for fetching the current page of data. - * Note that if the page size is infinite, a value -1 will be returned. - * @property integer $offset The offset of the data. This may be used to set the - * OFFSET value for a SQL statement for fetching the current page of data. + * @property integer $limit The limit of the data. This may be used to set the LIMIT value for a SQL statement + * for fetching the current page of data. Note that if the page size is infinite, a value -1 will be returned. + * This property is read-only. + * @property integer $offset The offset of the data. This may be used to set the OFFSET value for a SQL + * statement for fetching the current page of data. This property is read-only. * @property integer $page The zero-based current page number. - * @property integer $pageCount Number of pages. + * @property integer $pageCount Number of pages. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/data/Sort.php b/framework/yii/data/Sort.php index 7422c7f..78fe2e0 100644 --- a/framework/yii/data/Sort.php +++ b/framework/yii/data/Sort.php @@ -65,11 +65,10 @@ use yii\helpers\Inflector; * sorted by the orders specified by the Sort object. In the view, we show two hyperlinks * that can lead to pages with the data sorted by the corresponding attributes. * - * @property array $attributeOrders Sort directions indexed by attribute names. - * Sort direction can be either [[Sort::ASC]] for ascending order or - * [[Sort::DESC]] for descending order. - * @property array $orders The columns (keys) and their corresponding sort directions (values). - * This can be passed to [[\yii\db\Query::orderBy()]] to construct a DB query. + * @property array $attributeOrders Sort directions indexed by attribute names. Sort direction can be either + * [[Sort::ASC]] for ascending order or [[Sort::DESC]] for descending order. This property is read-only. + * @property array $orders The columns (keys) and their corresponding sort directions (values). This can be + * passed to [[\yii\db\Query::orderBy()]] to construct a DB query. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 5aa9807..35d7305 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -22,13 +22,16 @@ use yii\helpers\Inflector; * * @include @yii/db/ActiveRecord.md * - * @property Connection $db the database connection used by this AR class. - * @property TableSchema $tableSchema the schema information of the DB table associated with this AR class. - * @property array $oldAttributes the old attribute values (name-value pairs). - * @property array $dirtyAttributes the changed attribute values (name-value pairs). - * @property boolean $isNewRecord whether the record is new and should be inserted when calling [[save()]]. - * @property mixed $primaryKey the primary key value. - * @property mixed $oldPrimaryKey the old primary key value. + * @property array $dirtyAttributes The changed attribute values (name-value pairs). This property is + * read-only. + * @property boolean $isNewRecord Whether the record is new and should be inserted when calling [[save()]]. + * @property array $oldAttributes The old attribute values (name-value pairs). + * @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is + * returned if the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be + * returned if the key value is null). This property is read-only. + * @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if + * the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be returned if + * the key value is null). This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 2af07bb..d7db35c 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -44,7 +44,7 @@ use yii\caching\Cache; * * To build SELECT SQL statements, please use [[QueryBuilder]] instead. * - * @property string $rawSql The raw SQL. + * @property string $rawSql The raw SQL. This property is read-only. * @property string $sql The SQL statement to be executed. * * @author Qiang Xue diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php index 26c4537..342fa15 100644 --- a/framework/yii/db/Connection.php +++ b/framework/yii/db/Connection.php @@ -89,13 +89,16 @@ use yii\caching\Cache; * ) * ~~~ * + * @property string $driverName Name of the DB driver. This property is read-only. * @property boolean $isActive Whether the DB connection is established. This property is read-only. - * @property Transaction $transaction The currently active transaction. Null if no active transaction. - * @property Schema $schema The database schema information for the current connection. - * @property QueryBuilder $queryBuilder The query builder. - * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object. - * @property string $driverName Name of the DB driver currently being used. - * @property array $querySummary The statistical results of SQL queries. + * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the + * sequence object. This property is read-only. + * @property QueryBuilder $queryBuilder The query builder for the current DB connection. This property is + * read-only. + * @property Schema $schema The schema information for the database opened by this connection. This property + * is read-only. + * @property Transaction $transaction The currently active transaction. Null if no active transaction. This + * property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/DataReader.php b/framework/yii/db/DataReader.php index 9f6fac9..ddfcf29 100644 --- a/framework/yii/db/DataReader.php +++ b/framework/yii/db/DataReader.php @@ -39,10 +39,10 @@ use yii\base\InvalidCallException; * [[fetchMode]]. See the [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) * for more details about possible fetch mode. * - * @property integer $columnCount The number of columns in the result set. - * @property mixed $fetchMode Fetch mode. - * @property boolean $isClosed Whether the reader is closed or not. - * @property integer $rowCount Number of rows contained in the result. + * @property integer $columnCount The number of columns in the result set. This property is read-only. + * @property mixed $fetchMode Fetch mode. This property is write-only. + * @property boolean $isClosed Whether the reader is closed or not. This property is read-only. + * @property integer $rowCount Number of rows contained in the result. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 6795ad4..4fd1cd1 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -19,8 +19,12 @@ use yii\caching\GroupDependency; * * Schema represents the database schema information that is DBMS specific. * - * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the sequence object. - * @property QueryBuilder $queryBuilder The query builder for this connection. + * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the + * sequence object. This property is read-only. + * @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only. + * @property string[] $tableNames All table names in the database. This property is read-only. + * @property TableSchema[] $tableSchemas The metadata for all tables in the database. Each array element is an + * instance of [[TableSchema]] or its child class. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/TableSchema.php b/framework/yii/db/TableSchema.php index a0d2c15..d599388 100644 --- a/framework/yii/db/TableSchema.php +++ b/framework/yii/db/TableSchema.php @@ -13,7 +13,7 @@ use yii\base\InvalidParamException; /** * TableSchema represents the metadata of a database table. * - * @property array $columnNames List of column names. + * @property array $columnNames List of column names. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/Transaction.php b/framework/yii/db/Transaction.php index 3be2d34..e0b90d9 100644 --- a/framework/yii/db/Transaction.php +++ b/framework/yii/db/Transaction.php @@ -29,8 +29,8 @@ use yii\base\InvalidConfigException; * } * ~~~ * - * @property boolean $isActive Whether this transaction is active. Only an active transaction - * can [[commit()]] or [[rollback()]]. + * @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]] + * or [[rollback()]]. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php index 00ed80f..ea59f22 100644 --- a/framework/yii/db/redis/Connection.php +++ b/framework/yii/db/redis/Connection.php @@ -21,9 +21,10 @@ use yii\helpers\StringHelper; * @method mixed get($key) Set the string value of a key * TODO document methods * - * @property string $driverName Name of the DB driver. - * @property boolean $isActive Whether the DB connection is established. - * @property Transaction $transaction The currently active transaction. Null if no active transaction. + * @property string $driverName Name of the DB driver. This property is read-only. + * @property boolean $isActive Whether the DB connection is established. This property is read-only. + * @property Transaction $transaction The currently active transaction. Null if no active transaction. This + * property is read-only. * * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/db/redis/Transaction.php b/framework/yii/db/redis/Transaction.php index 8458c85..024f821 100644 --- a/framework/yii/db/redis/Transaction.php +++ b/framework/yii/db/redis/Transaction.php @@ -15,8 +15,8 @@ use yii\db\Exception; /** * Transaction represents a DB transaction. * - * @property boolean $isActive Whether this transaction is active. Only an active transaction - * can [[commit()]] or [[rollBack()]]. + * @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]] + * or [[rollBack()]]. This property is read-only. * * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/debug/Panel.php b/framework/yii/debug/Panel.php index 6c01362..6782264 100644 --- a/framework/yii/debug/Panel.php +++ b/framework/yii/debug/Panel.php @@ -14,10 +14,10 @@ use yii\base\Component; * Panel is a base class for debugger panel classes. It defines how data should be collected, * what should be displayed at debug toolbar and on debugger details view. * - * @property string $detail Content that is displayed in debugger detail view. - * @property string $name Name of the panel. - * @property string $summary Content that is displayed at debug toolbar. - * @property string $url URL pointing to panel detail view. + * @property string $detail Content that is displayed in debugger detail view. This property is read-only. + * @property string $name Name of the panel. This property is read-only. + * @property string $summary Content that is displayed at debug toolbar. This property is read-only. + * @property string $url URL pointing to panel detail view. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/gii/CodeFile.php b/framework/yii/gii/CodeFile.php index 6fcbdbd..2f21b4c 100644 --- a/framework/yii/gii/CodeFile.php +++ b/framework/yii/gii/CodeFile.php @@ -16,8 +16,9 @@ use yii\helpers\StringHelper; /** * CodeFile represents a code file to be generated. * - * @property string $relativePath The code file path relative to the application base path. - * @property string $type The code file extension (e.g. php, txt). + * @property string $relativePath The code file path relative to the application base path. This property is + * read-only. + * @property string $type The code file extension (e.g. php, txt). This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/gii/Generator.php b/framework/yii/gii/Generator.php index 387f523..f79b428 100644 --- a/framework/yii/gii/Generator.php +++ b/framework/yii/gii/Generator.php @@ -27,9 +27,11 @@ use yii\base\View; * - [[generate()]]: generates the code based on the current user input and the specified code template files. * This is the place where main code generation code resides. * - * @property string $description The detailed description of the generator. - * @property string $stickyDataFile The file path that stores the sticky attribute values. - * @property string $templatePath The root path of the template files that are currently being used. + * @property string $description The detailed description of the generator. This property is read-only. + * @property string $stickyDataFile The file path that stores the sticky attribute values. This property is + * read-only. + * @property string $templatePath The root path of the template files that are currently being used. This + * property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php index 2d3f7c1..54f3a49 100644 --- a/framework/yii/log/Logger.php +++ b/framework/yii/log/Logger.php @@ -61,11 +61,12 @@ use yii\base\InvalidConfigException; * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]] * to send logged messages to different log targets, such as file, email, Web. * - * @property array $dbProfiling The first element indicates the number of SQL statements executed, - * and the second element the total time spent in SQL execution. - * @property float $elapsedTime The total elapsed time in seconds for current request. + * @property array $dbProfiling The first element indicates the number of SQL statements executed, and the + * second element the total time spent in SQL execution. This property is read-only. + * @property float $elapsedTime The total elapsed time in seconds for current request. This property is + * read-only. * @property array $profiling The profiling results. Each array element has the following structure: - * `array($token, $category, $time)`. + * `array($token, $category, $time)`. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php index 12b5d15..c858412 100644 --- a/framework/yii/log/Target.php +++ b/framework/yii/log/Target.php @@ -22,8 +22,8 @@ use yii\base\InvalidConfigException; * satisfying both filter conditions will be handled. Additionally, you * may specify [[except]] to exclude messages of certain categories. * - * @property integer $levels The message levels that this target is interested in. This is a bitmap of - * level values. Defaults to 0, meaning all available levels. + * @property integer $levels The message levels that this target is interested in. This is a bitmap of level + * values. Defaults to 0, meaning all available levels. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php index 90d301e..46c375f 100644 --- a/framework/yii/rbac/DbManager.php +++ b/framework/yii/rbac/DbManager.php @@ -24,6 +24,8 @@ use yii\base\InvalidParamException; * the three tables used to store the authorization data by setting [[itemTable]], * [[itemChildTable]] and [[assignmentTable]]. * + * @property Item[] $items The authorization items of the specific type. This property is read-only. + * * @author Qiang Xue * @author Alexander Kochetov * @since 2.0 diff --git a/framework/yii/rbac/Item.php b/framework/yii/rbac/Item.php index 4c09e28..f7930c1 100644 --- a/framework/yii/rbac/Item.php +++ b/framework/yii/rbac/Item.php @@ -18,6 +18,7 @@ use yii\base\Object; * A user may be assigned one or several authorization items (called [[Assignment]] assignments). * He can perform an operation only when it is among his assigned items. * + * @property Item[] $children All child items of this item. This property is read-only. * @property string $name The item name. * * @author Qiang Xue diff --git a/framework/yii/rbac/Manager.php b/framework/yii/rbac/Manager.php index 23ac1a8..3d3aafc 100644 --- a/framework/yii/rbac/Manager.php +++ b/framework/yii/rbac/Manager.php @@ -33,9 +33,9 @@ use yii\base\InvalidParamException; * at appropriate places in the application code to check if the current user * has the needed permission for an operation. * - * @property array $roles Roles (name => Item). - * @property array $tasks Tasks (name => Item). - * @property array $operations Operations (name => Item). + * @property Item[] $operations Operations (name => AuthItem). This property is read-only. + * @property Item[] $roles Roles (name => AuthItem). This property is read-only. + * @property Item[] $tasks Tasks (name => AuthItem). This property is read-only. * * @author Qiang Xue * @author Alexander Kochetov diff --git a/framework/yii/rbac/PhpManager.php b/framework/yii/rbac/PhpManager.php index 3808541..203b17e 100644 --- a/framework/yii/rbac/PhpManager.php +++ b/framework/yii/rbac/PhpManager.php @@ -23,7 +23,7 @@ use yii\base\InvalidParamException; * (for example, the authorization data for a personal blog system). * Use [[DbManager]] for more complex authorization data. * - * @property array $authItems The authorization items of the specific type. + * @property Item[] $items The authorization items of the specific type. This property is read-only. * * @author Qiang Xue * @author Alexander Kochetov diff --git a/framework/yii/validators/FileValidator.php b/framework/yii/validators/FileValidator.php index 44e6cd2..4487eee 100644 --- a/framework/yii/validators/FileValidator.php +++ b/framework/yii/validators/FileValidator.php @@ -13,7 +13,7 @@ use yii\web\UploadedFile; /** * FileValidator verifies if an attribute is receiving a valid uploaded file. * - * @property integer $sizeLimit The size limit for uploaded files. + * @property integer $sizeLimit The size limit for uploaded files. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php index 1a00846..c0b8793 100644 --- a/framework/yii/web/Application.php +++ b/framework/yii/web/Application.php @@ -13,12 +13,12 @@ use yii\base\InvalidRouteException; /** * Application is the base class for all application classes. * - * @property AssetManager $assetManager The asset manager component. + * @property AssetManager $assetManager The asset manager component. This property is read-only. * @property string $homeUrl The homepage URL. - * @property Request $request The request component. - * @property Response $response The response component. - * @property Session $session The session component. - * @property User $user The user component. + * @property Request $request The request component. This property is read-only. + * @property Response $response The response component. This property is read-only. + * @property Session $session The session component. This property is read-only. + * @property User $user The user component. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php index bb04cff..bb387e1 100644 --- a/framework/yii/web/CacheSession.php +++ b/framework/yii/web/CacheSession.php @@ -21,7 +21,7 @@ use yii\base\InvalidConfigException; * may be swapped out and get lost. Therefore, you must make sure the cache used by this component * is NOT volatile. If you want to use database as storage medium, use [[DbSession]] is a better choice. * - * @property boolean $useCustomStorage Whether to use custom storage. + * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/CookieCollection.php b/framework/yii/web/CookieCollection.php index 9384d31..6940493 100644 --- a/framework/yii/web/CookieCollection.php +++ b/framework/yii/web/CookieCollection.php @@ -15,8 +15,9 @@ use yii\base\Object; /** * CookieCollection maintains the cookies available in the current request. * - * @property integer $count The number of cookies in the collection. - * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. + * @property integer $count The number of cookies in the collection. This property is read-only. + * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. This property + * is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/DbSession.php b/framework/yii/web/DbSession.php index 3d6b204..c508bb8 100644 --- a/framework/yii/web/DbSession.php +++ b/framework/yii/web/DbSession.php @@ -28,7 +28,7 @@ use yii\base\InvalidConfigException; * ) * ~~~ * - * @property boolean $useCustomStorage Whether to use custom storage. + * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/HeaderCollection.php b/framework/yii/web/HeaderCollection.php index b0b422a..609058b 100644 --- a/framework/yii/web/HeaderCollection.php +++ b/framework/yii/web/HeaderCollection.php @@ -14,8 +14,9 @@ use ArrayIterator; /** * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. * - * @property integer $count The number of headers in the collection. - * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. + * @property integer $count The number of headers in the collection. This property is read-only. + * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. This property + * is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 3833dd7..55073a2 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -13,45 +13,49 @@ use yii\helpers\Security; /** * - * @property string $absoluteUrl The currently requested absolute URL. - * @property string $acceptTypes User browser accept types, null if not present. + * @property string $absoluteUrl The currently requested absolute URL. This property is read-only. + * @property string $acceptTypes User browser accept types, null if not present. This property is read-only. * @property array $acceptedContentTypes The content types ordered by the preference level. The first element * represents the most preferred content type. * @property array $acceptedLanguages The languages ordered by the preference level. The first element * represents the most preferred language. * @property string $baseUrl The relative URL for the application. - * @property string $cookieValidationKey The secret key used for cookie validation. If it was not set previously, - * a random key will be generated and used. - * @property CookieCollection $cookies The cookie collection. - * @property string $csrfToken The random token for CSRF validation. - * @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g. `http://www.yiiframework.com`). - * @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. - * @property boolean $isDelete Whether this is a DELETE request. - * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. - * @property boolean $isPatch Whether this is a PATCH request. - * @property boolean $isPost Whether this is a POST request. - * @property boolean $isPut Whether this is a PUT request. - * @property boolean $isSecureConnection If the request is sent via secure channel (https). - * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. - * The value returned is turned into upper case. - * @property string $pathInfo Part of the request URL that is after the entry script and before the question mark. - * Note, the returned path info is already URL-decoded. + * @property string $cookieValidationKey The secret key used for cookie validation. If it was not set + * previously, a random key will be generated and used. + * @property CookieCollection $cookies The cookie collection. This property is read-only. + * @property string $csrfToken The random token for CSRF validation. This property is read-only. + * @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g. + * `http://www.yiiframework.com`). + * @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only. + * @property boolean $isDelete Whether this is a DELETE request. This property is read-only. + * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is + * read-only. + * @property boolean $isPatch Whether this is a PATCH request. This property is read-only. + * @property boolean $isPost Whether this is a POST request. This property is read-only. + * @property boolean $isPut Whether this is a PUT request. This property is read-only. + * @property boolean $isSecureConnection If the request is sent via secure channel (https). This property is + * read-only. + * @property string $method Request method, such as GET, POST, HEAD, PUT, PATCH, DELETE. The value returned is + * turned into upper case. This property is read-only. + * @property string $pathInfo Part of the request URL that is after the entry script and before the question + * mark. Note, the returned path info is already URL-decoded. * @property integer $port Port number for insecure requests. - * @property string $preferredLanguage The language that the application should use. Null is returned if both [[getAcceptedLanguages()]] - * and `$languages` are empty. - * @property string $queryString Part of the request URL that is after the question mark. - * @property string $rawBody The request body. - * @property string $referrer URL referrer, null if not present. + * @property string $preferredLanguage The language that the application should use. Null is returned if both + * [[getAcceptedLanguages()]] and `$languages` are empty. This property is read-only. + * @property string $queryString Part of the request URL that is after the question mark. This property is + * read-only. + * @property string $rawBody The request body. This property is read-only. + * @property string $referrer URL referrer, null if not present. This property is read-only. * @property array $restParams The RESTful request parameters. * @property string $scriptFile The entry script file path. * @property string $scriptUrl The relative URL of the entry script. * @property integer $securePort Port number for secure requests. - * @property string $serverName Server name. - * @property integer $serverPort Server port number. + * @property string $serverName Server name. This property is read-only. + * @property integer $serverPort Server port number. This property is read-only. * @property string $url The currently requested relative URL. Note that the URI returned is URL-encoded. - * @property string $userAgent User agent, null if not present. - * @property string $userHost User host name, null if cannot be determined. - * @property string $userIP User IP address. + * @property string $userAgent User agent, null if not present. This property is read-only. + * @property string $userHost User host name, null if cannot be determined. This property is read-only. + * @property string $userIP User IP address. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index fb7ee65..286a0db 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -18,18 +18,22 @@ use yii\helpers\StringHelper; /** * - * @property CookieCollection $cookies The cookie collection. - * @property HeaderCollection $headers The header collection. - * @property boolean $isClientError Whether this response indicates a client error. - * @property boolean $isEmpty Whether this response is empty. - * @property boolean $isForbidden Whether this response indicates the current request is forbidden. - * @property boolean $isInformational Whether this response is informational. - * @property boolean $isInvalid Whether this response has a valid [[statusCode]]. - * @property boolean $isNotFound Whether this response indicates the currently requested resource is not found. - * @property boolean $isOk Whether this response is OK. - * @property boolean $isRedirection Whether this response is a redirection. - * @property boolean $isServerError Whether this response indicates a server error. - * @property boolean $isSuccessful Whether this response is successful. + * @property CookieCollection $cookies The cookie collection. This property is read-only. + * @property HeaderCollection $headers The header collection. This property is read-only. + * @property boolean $isClientError Whether this response indicates a client error. This property is + * read-only. + * @property boolean $isEmpty Whether this response is empty. This property is read-only. + * @property boolean $isForbidden Whether this response indicates the current request is forbidden. This + * property is read-only. + * @property boolean $isInformational Whether this response is informational. This property is read-only. + * @property boolean $isInvalid Whether this response has a valid [[statusCode]]. This property is read-only. + * @property boolean $isNotFound Whether this response indicates the currently requested resource is not + * found. This property is read-only. + * @property boolean $isOk Whether this response is OK. This property is read-only. + * @property boolean $isRedirection Whether this response is a redirection. This property is read-only. + * @property boolean $isServerError Whether this response indicates a server error. This property is + * read-only. + * @property boolean $isSuccessful Whether this response is successful. This property is read-only. * @property integer $statusCode The HTTP status code to send with the response. * * @author Qiang Xue diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index df98448..fc9fe16 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -45,23 +45,27 @@ use yii\base\InvalidParamException; * useful for displaying confirmation messages. To use flash messages, simply * call methods such as [[setFlash()]], [[getFlash()]]. * - * @property array $allFlashes Flash messages (key => message). + * @property array $allFlashes Flash messages (key => message). This property is read-only. * @property array $cookieParams The session cookie parameters. - * @property integer $count The number of session variables. - * @property string $flash The key identifying the flash message. Note that flash messages - * and normal session variables share the same name space. If you have a normal - * session variable using the same name, its value will be overwritten by this method. - * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is started on every session initialization, defaults to 1 meaning 1% chance. + * @property integer $count The number of session variables. This property is read-only. + * @property string $flash The key identifying the flash message. Note that flash messages and normal session + * variables share the same name space. If you have a normal session variable using the same name, its value will + * be overwritten by this method. This property is write-only. + * @property float $gCProbability The probability (percentage) that the GC (garbage collection) process is + * started on every session initialization, defaults to 1 meaning 1% chance. * @property string $id The current session ID. - * @property boolean $isActive Whether the session has started. - * @property SessionIterator $iterator An iterator for traversing the session variables. + * @property boolean $isActive Whether the session has started. This property is read-only. + * @property SessionIterator $iterator An iterator for traversing the session variables. This property is + * read-only. * @property string $name The current session name. * @property string $savePath The current session save path, defaults to '/tmp'. * @property integer $timeout The number of seconds after which data will be seen as 'garbage' and cleaned up. * The default value is 1440 seconds (or the value of "session.gc_maxlifetime" set in php.ini). - * @property boolean|null $useCookies The value indicating whether cookies should be used to store session IDs. - * @property boolean $useCustomStorage Whether to use custom storage. - * @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to false. + * @property boolean|null $useCookies The value indicating whether cookies should be used to store session + * IDs. + * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. + * @property boolean $useTransparentSessionID Whether transparent sid support is enabled or not, defaults to + * false. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php index 82116bd..609388f 100644 --- a/framework/yii/web/UploadedFile.php +++ b/framework/yii/web/UploadedFile.php @@ -11,17 +11,16 @@ use yii\helpers\Html; /** * - * @property integer $error The error code. - * @property boolean $hasError Whether there is an error with the uploaded file. - * Check [[error]] for detailed error code information. - * @property string $name The original name of the file being uploaded. - * @property integer $size The actual size of the uploaded file in bytes. - * @property string $tempName The path of the uploaded file on the server. - * Note, this is a temporary file which will be automatically deleted by PHP - * after the current request is processed. - * @property string $type The MIME-type of the uploaded file (such as "image/gif"). - * Since this MIME type is not checked on the server side, do not take this value for granted. - * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. + * @property integer $error The error code. This property is read-only. + * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed + * error code information. This property is read-only. + * @property string $name The original name of the file being uploaded. This property is read-only. + * @property integer $size The actual size of the uploaded file in bytes. This property is read-only. + * @property string $tempName The path of the uploaded file on the server. Note, this is a temporary file + * which will be automatically deleted by PHP after the current request is processed. This property is read-only. + * @property string $type The MIME-type of the uploaded file (such as "image/gif"). Since this MIME type is + * not checked on the server side, do not take this value for granted. Instead, use [[FileHelper::getMimeType()]] + * to determine the exact MIME type. This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 071c5eb..0d9547f 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -15,7 +15,8 @@ use yii\caching\Cache; * UrlManager handles HTTP request parsing and creation of URLs based on a set of rules. * * @property string $baseUrl The base URL that is used by [[createUrl()]] to prepend URLs it creates. - * @property string $hostInfo The host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + * @property string $hostInfo The host info (e.g. "http://www.example.com") that is used by + * [[createAbsoluteUrl()]] to prepend URLs it creates. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index bae78ec..cfdafd9 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -21,9 +21,10 @@ use yii\base\InvalidConfigException; * the actual user authentication logic and is often backed by a user database table. * * @property string|integer $id The unique identifier for the user. If null, it means the user is a guest. - * @property Identity $identity The identity object associated with the currently logged user. - * Null is returned if the user is not logged in (not authenticated). - * @property boolean $isGuest Whether the current user is a guest. + * This property is read-only. + * @property Identity $identity The identity object associated with the currently logged user. Null is + * returned if the user is not logged in (not authenticated). + * @property boolean $isGuest Whether the current user is a guest. This property is read-only. * @property string $returnUrl The URL that the user should be redirected to after login. * * @author Qiang Xue diff --git a/framework/yii/widgets/FragmentCache.php b/framework/yii/widgets/FragmentCache.php index f67eff3..3f4027b 100644 --- a/framework/yii/widgets/FragmentCache.php +++ b/framework/yii/widgets/FragmentCache.php @@ -14,7 +14,8 @@ use yii\caching\Dependency; /** * - * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found in the cache. + * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found + * in the cache. This property is read-only. * * @author Qiang Xue * @since 2.0 From c582e589d2ed3e046816b43f25d71cc06c690ce7 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 15:18:13 +0200 Subject: [PATCH 028/156] added note about differing property types --- build/controllers/PhpDocController.php | 6 +++++- framework/yii/caching/MemCache.php | 5 +++-- framework/yii/console/controllers/AssetController.php | 3 ++- framework/yii/data/DataProvider.php | 12 +++++++----- framework/yii/db/Command.php | 5 +++-- framework/yii/log/Target.php | 3 ++- framework/yii/web/AssetManager.php | 3 ++- framework/yii/web/User.php | 3 ++- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index a96495b..1011d19 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -253,8 +253,12 @@ class PhpDocController extends Controller foreach ($props as $propName => &$prop) { $docline = ' * @'; $docline .= 'property'; // Do not use property-read and property-write as few IDEs support complex syntax. + $note = ''; if (isset($prop['get']) && isset($prop['set'])) { - $note = ''; + if ($prop['get']['type'] != $prop['set']['type']) { + $note = ' Note that the type of this property differs in getter and setter.' + . ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.'; + } } elseif (isset($prop['get'])) { $note = ' This property is read-only.'; // $docline .= '-read'; diff --git a/framework/yii/caching/MemCache.php b/framework/yii/caching/MemCache.php index ee8f1e7..53202f0 100644 --- a/framework/yii/caching/MemCache.php +++ b/framework/yii/caching/MemCache.php @@ -53,8 +53,9 @@ use yii\base\InvalidConfigException; * each server, such as `persistent`, `weight`, `timeout`. Please see [[MemCacheServer]] for available options. * * @property \Memcache|\Memcached $memcache The memcache (or memcached) object used by this cache component. - * @property array $servers List of memcache server configurations. Each element must be an array - * with the following keys: host, port, persistent, weight, timeout, retryInterval, status. + * This property is read-only. + * @property MemCacheServer[] $servers List of memcache server configurations. Note that the type of this + * property differs in getter and setter. See [[getServers()]] and [[setServers()]] for details. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 287beca..1e95dd8 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -28,7 +28,8 @@ use yii\console\Controller; * Note: by default this command relies on an external tools to perform actual files compression, * check [[jsCompressor]] and [[cssCompressor]] for more details. * - * @property \yii\web\AssetManager $assetManager Asset manager instance. + * @property \yii\web\AssetManager $assetManager Asset manager instance. Note that the type of this property + * differs in getter and setter. See [[getAssetManager()]] and [[setAssetManager()]] for details. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/data/DataProvider.php b/framework/yii/data/DataProvider.php index 8e648cc..7d0244c 100644 --- a/framework/yii/data/DataProvider.php +++ b/framework/yii/data/DataProvider.php @@ -17,9 +17,11 @@ use yii\base\InvalidParamException; * It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[IDataProvider]] interface. * * @property integer $count The number of data models in the current page. This property is read-only. - * @property Pagination $pagination The pagination object. If this is false, it means the pagination is - * disabled. - * @property Sort $sort The sorting object. If this is false, it means the sorting is disabled. + * @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination + * is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and + * [[setPagination()]] for details. + * @property Sort|boolean $sort The sorting object. If this is false, it means that sorting is disabled. Note + * that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details. * * @author Qiang Xue * @since 2.0 @@ -37,7 +39,7 @@ abstract class DataProvider extends Component implements IDataProvider private $_pagination; /** - * @return Pagination the pagination object. If this is false, it means the pagination is disabled. + * @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled. */ public function getPagination() { @@ -80,7 +82,7 @@ abstract class DataProvider extends Component implements IDataProvider } /** - * @return Sort the sorting object. If this is false, it means the sorting is disabled. + * @return Sort|boolean the sorting object. If this is false, it means the sorting is disabled. */ public function getSort() { diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index d7db35c..f9376ee 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -44,7 +44,8 @@ use yii\caching\Cache; * * To build SELECT SQL statements, please use [[QueryBuilder]] instead. * - * @property string $rawSql The raw SQL. This property is read-only. + * @property string $rawSql The raw SQL with parameter values inserted into the corresponding placeholders in + * [[sql]]. This property is read-only. * @property string $sql The SQL statement to be executed. * * @author Qiang Xue @@ -103,7 +104,7 @@ class Command extends \yii\base\Component * Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]]. * Note that the return value of this method should mainly be used for logging purpose. * It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders. - * @return string the raw SQL + * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]]. */ public function getRawSql() { diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php index c858412..0cb72ef 100644 --- a/framework/yii/log/Target.php +++ b/framework/yii/log/Target.php @@ -23,7 +23,8 @@ use yii\base\InvalidConfigException; * may specify [[except]] to exclude messages of certain categories. * * @property integer $levels The message levels that this target is interested in. This is a bitmap of level - * values. Defaults to 0, meaning all available levels. + * values. Defaults to 0, meaning all available levels. Note that the type of this property differs in getter + * and setter. See [[getLevels()]] and [[setLevels()]] for details. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php index fd7ed1d..43e7cf9 100644 --- a/framework/yii/web/AssetManager.php +++ b/framework/yii/web/AssetManager.php @@ -16,7 +16,8 @@ use yii\helpers\FileHelper; /** * AssetManager manages asset bundles and asset publishing. * - * @property IAssetConverter $converter The asset converter. + * @property IAssetConverter $converter The asset converter. Note that the type of this property differs in + * getter and setter. See [[getConverter()]] and [[setConverter()]] for details. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index cfdafd9..b1ca8c2 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -25,7 +25,8 @@ use yii\base\InvalidConfigException; * @property Identity $identity The identity object associated with the currently logged user. Null is * returned if the user is not logged in (not authenticated). * @property boolean $isGuest Whether the current user is a guest. This property is read-only. - * @property string $returnUrl The URL that the user should be redirected to after login. + * @property string $returnUrl The URL that the user should be redirected to after login. Note that the type + * of this property differs in getter and setter. See [[getReturnUrl()]] and [[setReturnUrl()]] for details. * * @author Qiang Xue * @since 2.0 From 1d622b65c3219dfe4154d771a57f3b7456c5f5ce Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 15:26:00 +0200 Subject: [PATCH 029/156] fixed docs about error methods in Model --- framework/yii/base/Model.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 82083da..aca3e0b 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -244,8 +244,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * after the actual validation, respectively. If [[beforeValidate()]] returns false, * the validation will be cancelled and [[afterValidate()]] will not be called. * - * Errors found during the validation can be retrieved via [[getErrors()]] - * and [[getError()]]. + * Errors found during the validation can be retrieved via [[getErrors()]], + * [[getFirstErrors()]] and [[getFirstError()]]. * * @param array $attributes list of attributes that should be validated. * If this parameter is empty, it means any attribute listed in the applicable @@ -438,7 +438,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * ) * ~~~ * - * @see getError + * @see getFirstErrors + * @see getFirstError */ public function getErrors($attribute = null) { @@ -452,6 +453,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess /** * Returns the first error of every attribute in the model. * @return array the first errors. An empty array will be returned if there is no error. + * @see getErrors + * @see getFirstError */ public function getFirstErrors() { @@ -473,6 +476,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * @param string $attribute attribute name. * @return string the error message. Null is returned if no error. * @see getErrors + * @see getFirstErrors */ public function getFirstError($attribute) { From 91c16782ef3359c7e73cd27d767a1842c9f8b915 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 18:58:59 +0200 Subject: [PATCH 030/156] finalized PhpDocController - cleanup API and usage, add documetation link - introduced @property feature in getter or setter which has precedence over @return or @param tag if present --- build/controllers/PhpDocController.php | 43 +++++++++++++++++++++++++--------- framework/yii/base/Model.php | 16 +++++++++---- framework/yii/base/Module.php | 23 ++++++++++-------- framework/yii/data/DataProvider.php | 2 +- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 1011d19..5aac7e5 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -13,19 +13,32 @@ use yii\helpers\FileHelper; /** * PhpDocController is there to help maintaining PHPDoc annotation in class files + * * @author Carsten Brandt * @author Alexander Makarov * @since 2.0 */ class PhpDocController extends Controller { + public $defaultAction = 'property'; + + /** + * @var bool whether to update class docs directly. Setting this to false will just output docs + * for copy and paste. + */ + public $updateFiles = true; + /** * Generates @property annotations in class files from getters and setters * - * @param null $root the directory to parse files from - * @param bool $updateFiles whether to update class docs directly + * Property description will be taken from getter or setter or from an @property annotation + * in the getters docblock if there is one defined. + * + * See https://github.com/yiisoft/yii2/wiki/Core-framework-code-style#documentation for details. + * + * @param null $root the directory to parse files from. Defaults to YII_PATH. */ - public function actionProperty($root=null, $updateFiles=true) + public function actionProperty($root=null) { if ($root === null) { $root = YII_PATH; @@ -58,7 +71,7 @@ class PhpDocController extends Controller $result = $this->generateClassPropertyDocs($file); if ($result !== false) { list($className, $phpdoc) = $result; - if ($updateFiles) { + if ($this->updateFiles) { if ($this->updateClassPropertyDocs($file, $className, $phpdoc)) { $nFilesUpdated++; } @@ -70,10 +83,15 @@ class PhpDocController extends Controller $nFilesTotal++; } - $this->stdout("\n\nParsed $nFilesTotal files.\n"); + $this->stdout("\nParsed $nFilesTotal files.\n"); $this->stdout("Updated $nFilesUpdated files.\n"); } + public function globalOptions() + { + return array_merge(parent::globalOptions(), array('updateFiles')); + } + protected function updateClassPropertyDocs($file, $className, $propertyDoc) { $ref = new \ReflectionClass($className); @@ -229,7 +247,12 @@ class PhpDocController extends Controller '#\* @param (?[\w\\|\\\\\\[\\]]+) \$\w+(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . '[\s\n]{2,}public function (?set)(?\w+)\(\$\w+(?:, ?\$\w+ ?= ?[^,]+)*\)#', $class['content']); - $acrs = array_merge($gets, $sets); + // check for @property annotations in getter and setter + $properties = $this->match( + '#\* @(?property) (?[\w\\|\\\\\\[\\]]+)(?: (?(?:(?!\*/|\* @).)+?)(?:(?!\*/).)+|[\s\n]*)\*/' . + '[\s\n]{2,}public function [g|s]et(?\w+)\(((?:,? ?\$\w+ ?= ?[^,]+)*|\$\w+(?:, ?\$\w+ ?= ?[^,]+)*)\)#', + $class['content']); + $acrs = array_merge($properties, $gets, $sets); //print_r($acrs); continue; $props = array(); @@ -244,10 +267,6 @@ class PhpDocController extends Controller ksort($props); -// foreach ($props as $propName => &$prop) // I don't like write-only props... -// if (!isset($prop['get'])) -// unset($props[$propName]); - if (count($props) > 0) { $phpdoc .= " *\n"; foreach ($props as $propName => &$prop) { @@ -265,6 +284,8 @@ class PhpDocController extends Controller } elseif (isset($prop['set'])) { $note = ' This property is write-only.'; // $docline .= '-write'; + } else { + continue; } $docline .= ' ' . $this->getPropParam($prop, 'type') . " $$propName "; $comment = explode("\n", $this->getPropParam($prop, 'comment') . $note); @@ -302,6 +323,6 @@ class PhpDocController extends Controller protected function getPropParam($prop, $param) { - return isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]; + return isset($prop['property']) ? $prop['property'][$param] : (isset($prop['get']) ? $prop['get'][$param] : $prop['set'][$param]); } } \ No newline at end of file diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index aca3e0b..a195acd 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -36,11 +36,17 @@ use yii\validators\Validator; * You may directly use Model to store model data, or extend it with customization. * You may also customize Model by attaching [[ModelBehavior|model behaviors]]. * - * @property ArrayObject $validators All the validators declared in the model. - * @property array $activeValidators The validators applicable to the current [[scenario]]. - * @property array $errors Errors for all attributes or the specified attribute. Empty array is returned if no error. + * @property \yii\validators\Validator[] $activeValidators The validators applicable to the current + * [[scenario]]. This property is read-only. * @property array $attributes Attribute values (name => value). - * @property string $scenario The scenario that this model is in. + * @property array $errors An array of errors for all attributes. Empty array is returned if no error. The + * result is a two-dimensional array. See [[getErrors()]] for detailed description. This property is read-only. + * @property array $firstErrors The first errors. An empty array will be returned if there is no error. This + * property is read-only. + * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is + * read-only. + * @property string $scenario The scenario that this model is in. Defaults to 'default'. + * @property ArrayObject $validators All the validators declared in the model. This property is read-only. * * @author Qiang Xue * @since 2.0 @@ -423,6 +429,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess /** * Returns the errors for all attribute or a single attribute. * @param string $attribute attribute name. Use null to retrieve errors for all attributes. + * @property array An array of errors for all attributes. Empty array is returned if no error. + * The result is a two-dimensional array. See [[getErrors()]] for detailed description. * @return array errors for all attributes or the specified attribute. Empty array is returned if no error. * Note that when returning errors for all attributes, the result is a two-dimensional array, like the following: * diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php index a85385b..1b7bf5b 100644 --- a/framework/yii/base/Module.php +++ b/framework/yii/base/Module.php @@ -20,16 +20,16 @@ use Yii; * [[components|Components]] may be registered with the module so that they are globally * accessible within the module. * - * @property string $uniqueId An ID that uniquely identifies this module among all modules within - * the current application. - * @property string $basePath The root directory of the module. Defaults to the directory containing the module class. - * @property string $controllerPath The directory containing the controller classes. Defaults to "[[basePath]]/controllers". - * @property string $viewPath The directory containing the view files within this module. Defaults to "[[basePath]]/views". - * @property string $layoutPath The directory containing the layout view files within this module. Defaults to "[[viewPath]]/layouts". - * @property array $modules The configuration of the currently installed modules (module ID => configuration). - * @property array $components The components (indexed by their IDs) registered within this module. - * @property array $import List of aliases to be imported. This property is write-only. - * @property array $aliases List of aliases to be defined. This property is write-only. + * @property array $aliases List of path aliases to be defined. The array keys are alias names (must start + * with '@') and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example. + * This property is write-only. + * @property string $basePath The root directory of the module. + * @property array $components The components (indexed by their IDs). + * @property string $controllerPath The directory that contains the controller classes. + * @property string $layoutPath The root directory of layout files. Defaults to "[[viewPath]]/layouts". + * @property array $modules The modules (indexed by their IDs). + * @property string $uniqueId The unique ID of the module. This property is read-only. + * @property string $viewPath The root directory of view files. Defaults to "[[basePath]]/view". * * @author Qiang Xue * @since 2.0 @@ -304,6 +304,9 @@ abstract class Module extends Component * Defines path aliases. * This method calls [[Yii::setAlias()]] to register the path aliases. * This method is provided so that you can define path aliases when configuring a module. + * @property array list of path aliases to be defined. The array keys are alias names + * (must start with '@') and the array values are the corresponding paths or aliases. + * See [[setAliases()]] for an example. * @param array $aliases list of path aliases to be defined. The array keys are alias names * (must start with '@') and the array values are the corresponding paths or aliases. * For example, diff --git a/framework/yii/data/DataProvider.php b/framework/yii/data/DataProvider.php index 7d0244c..84491d6 100644 --- a/framework/yii/data/DataProvider.php +++ b/framework/yii/data/DataProvider.php @@ -20,7 +20,7 @@ use yii\base\InvalidParamException; * @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination * is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and * [[setPagination()]] for details. - * @property Sort|boolean $sort The sorting object. If this is false, it means that sorting is disabled. Note + * @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note * that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details. * * @author Qiang Xue From 147558ea198a668815ac4f864601b5f1beb2cd53 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 28 Aug 2013 19:33:06 +0200 Subject: [PATCH 031/156] added some class documentation --- framework/yii/debug/LogTarget.php | 2 ++ framework/yii/debug/Module.php | 2 ++ framework/yii/web/AccessRule.php | 1 + framework/yii/web/Application.php | 2 +- framework/yii/web/Controller.php | 2 +- framework/yii/web/HttpCache.php | 2 ++ framework/yii/web/JqueryAsset.php | 2 ++ framework/yii/web/JsExpression.php | 4 +++- framework/yii/web/PageCache.php | 2 ++ framework/yii/web/Request.php | 5 +++++ framework/yii/web/Response.php | 4 ++++ framework/yii/web/UploadedFile.php | 6 ++++++ framework/yii/web/UserEvent.php | 1 + 13 files changed, 32 insertions(+), 3 deletions(-) diff --git a/framework/yii/debug/LogTarget.php b/framework/yii/debug/LogTarget.php index 3e36cd5..b415b50 100644 --- a/framework/yii/debug/LogTarget.php +++ b/framework/yii/debug/LogTarget.php @@ -11,6 +11,8 @@ use Yii; use yii\log\Target; /** + * The debug LogTarget is used to store logs for later use in the debugger tool + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/debug/Module.php b/framework/yii/debug/Module.php index 2246520..dd027a7 100644 --- a/framework/yii/debug/Module.php +++ b/framework/yii/debug/Module.php @@ -12,6 +12,8 @@ use yii\base\View; use yii\web\HttpException; /** + * The Yii Debug Module provides the debug toolbar and debugger + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/AccessRule.php b/framework/yii/web/AccessRule.php index 0de791c..7aeaac1 100644 --- a/framework/yii/web/AccessRule.php +++ b/framework/yii/web/AccessRule.php @@ -11,6 +11,7 @@ use yii\base\Component; use yii\base\Action; /** + * This class represents an access rule defined by the [[AccessControl]] action filter * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php index c0b8793..5fd2310 100644 --- a/framework/yii/web/Application.php +++ b/framework/yii/web/Application.php @@ -11,7 +11,7 @@ use Yii; use yii\base\InvalidRouteException; /** - * Application is the base class for all application classes. + * Application is the base class for all web application classes. * * @property AssetManager $assetManager The asset manager component. This property is read-only. * @property string $homeUrl The homepage URL. diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index b9aa68c..adb1b4d 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -12,7 +12,7 @@ use yii\base\InlineAction; use yii\helpers\Html; /** - * Controller is the base class of Web controllers. + * Controller is the base class of web controllers. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/HttpCache.php b/framework/yii/web/HttpCache.php index cc9e6ed..d2f3923 100644 --- a/framework/yii/web/HttpCache.php +++ b/framework/yii/web/HttpCache.php @@ -12,6 +12,8 @@ use yii\base\ActionFilter; use yii\base\Action; /** + * The HttpCache provides functionality for caching via HTTP Last-Modified and Etag headers + * * @author Da:Sourcerer * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/JqueryAsset.php b/framework/yii/web/JqueryAsset.php index 4585acd..9991eb4 100644 --- a/framework/yii/web/JqueryAsset.php +++ b/framework/yii/web/JqueryAsset.php @@ -8,6 +8,8 @@ namespace yii\web; /** + * This asset bundle provides the [jquery javascript library](http://jquery.com/) + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/JsExpression.php b/framework/yii/web/JsExpression.php index 027c065..7daac08 100644 --- a/framework/yii/web/JsExpression.php +++ b/framework/yii/web/JsExpression.php @@ -11,8 +11,10 @@ use yii\base\Object; /** * JsExpression marks a string as a JavaScript expression. - * When using [[Json::encode()]] to encode a value, JsonExpression objects + * + * When using [[yii\helpers\Json::encode()]] to encode a value, JsonExpression objects * will be specially handled and encoded as a JavaScript expression instead of a string. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/PageCache.php b/framework/yii/web/PageCache.php index d28203e..9bc8981 100644 --- a/framework/yii/web/PageCache.php +++ b/framework/yii/web/PageCache.php @@ -14,6 +14,8 @@ use yii\base\View; use yii\caching\Dependency; /** + * The PageCache provides functionality for whole page caching + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 55073a2..c6e1ce3 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -12,6 +12,11 @@ use yii\base\InvalidConfigException; use yii\helpers\Security; /** + * The web Request class represents an HTTP request + * + * It encapsulates the $_SERVER variable and resolves its inconsistency among different Web servers. + * Also it provides an interface to retrieve request parameters from $_POST, $_GET, $_COOKIES and REST + * parameters sent via other HTTP methods like PUT or DELETE. * * @property string $absoluteUrl The currently requested absolute URL. This property is read-only. * @property string $acceptTypes User browser accept types, null if not present. This property is read-only. diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index 286a0db..cfbc537 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -17,6 +17,10 @@ use yii\helpers\Security; use yii\helpers\StringHelper; /** + * The web Response class represents an HTTP response + * + * It holds the [[headers]], [[cookies]] and [[content]] that is to be sent to the client. + * It also controls the HTTP [[statusCode|status code]]. * * @property CookieCollection $cookies The cookie collection. This property is read-only. * @property HeaderCollection $headers The header collection. This property is read-only. diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php index 609388f..21fc355 100644 --- a/framework/yii/web/UploadedFile.php +++ b/framework/yii/web/UploadedFile.php @@ -10,6 +10,12 @@ namespace yii\web; use yii\helpers\Html; /** + * UploadedFile represents the information for an uploaded file. + * + * You can call [[getInstance()]] to retrieve the instance of an uploaded file, + * and then use [[saveAs()]] to save it on the server. + * You may also query other information about the file, including [[name]], + * [[tempName]], [[type]], [[size]] and [[error]]. * * @property integer $error The error code. This property is read-only. * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed diff --git a/framework/yii/web/UserEvent.php b/framework/yii/web/UserEvent.php index 4e39380..3e403da 100644 --- a/framework/yii/web/UserEvent.php +++ b/framework/yii/web/UserEvent.php @@ -10,6 +10,7 @@ namespace yii\web; use yii\base\Event; /** + * This event class is used for Events triggered by the [[User]] class. * * @author Qiang Xue * @since 2.0 From 6dba4da3ed7521ac81b88ea5c7efece12f7a25d1 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 29 Aug 2013 16:47:48 -0400 Subject: [PATCH 032/156] Fixes #828: refactored QueryBuilder::build() --- framework/yii/db/ActiveQuery.php | 8 ++++---- framework/yii/db/ActiveRelation.php | 4 ++-- framework/yii/db/Query.php | 4 ++-- framework/yii/db/QueryBuilder.php | 20 ++++++++++++-------- tests/unit/framework/db/QueryBuilderTest.php | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 4d08659..12997ee 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -156,6 +156,8 @@ class ActiveQuery extends Query if ($db === null) { $db = $modelClass::getDb(); } + + $params = $this->params; if ($this->sql === null) { if ($this->from === null) { $tableName = $modelClass::tableName(); @@ -164,11 +166,9 @@ class ActiveQuery extends Query } $this->from = array($tableName); } - /** @var $qb QueryBuilder */ - $qb = $db->getQueryBuilder(); - $this->sql = $qb->build($this); + list ($this->sql, $params) = $db->getQueryBuilder()->build($this); } - return $db->createCommand($this->sql, $this->params); + return $db->createCommand($this->sql, $params); } /** diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index f10f287..0eee25b 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -297,7 +297,7 @@ class ActiveRelation extends ActiveQuery /** @var $primaryModel ActiveRecord */ $primaryModel = reset($primaryModels); $db = $primaryModel->getDb(); - $sql = $db->getQueryBuilder()->build($this); - return $db->createCommand($sql, $this->params)->queryAll(); + list ($sql, $params) = $db->getQueryBuilder()->build($this); + return $db->createCommand($sql, $params)->queryAll(); } } diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 19ea028..d1e7864 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -149,8 +149,8 @@ class Query extends Component if ($db === null) { $db = Yii::$app->getDb(); } - $sql = $db->getQueryBuilder()->build($this); - return $db->createCommand($sql, $this->params); + list ($sql, $params) = $db->getQueryBuilder()->build($this); + return $db->createCommand($sql, $params); } /** diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index b55be3c..9a9329d 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -55,22 +55,24 @@ class QueryBuilder extends \yii\base\Object /** * Generates a SELECT SQL statement from a [[Query]] object. * @param Query $query the [[Query]] object from which the SQL statement will be generated - * @return string the generated SQL statement + * @return array the generated SQL statement (the first array element) and the corresponding + * parameters to be bound to the SQL statement (the second array element). */ public function build($query) { + $params = $query->params; $clauses = array( $this->buildSelect($query->select, $query->distinct, $query->selectOption), $this->buildFrom($query->from), - $this->buildJoin($query->join, $query->params), - $this->buildWhere($query->where, $query->params), + $this->buildJoin($query->join, $params), + $this->buildWhere($query->where, $params), $this->buildGroupBy($query->groupBy), - $this->buildHaving($query->having, $query->params), - $this->buildUnion($query->union, $query->params), + $this->buildHaving($query->having, $params), + $this->buildUnion($query->union, $params), $this->buildOrderBy($query->orderBy), $this->buildLimit($query->limit, $query->offset), ); - return implode($this->separator, array_filter($clauses)); + return array(implode($this->separator, array_filter($clauses)), $params); } /** @@ -718,9 +720,11 @@ class QueryBuilder extends \yii\base\Object } foreach ($unions as $i => $union) { if ($union instanceof Query) { + // save the original parameters so that we can restore them later to prevent from modifying the query object + $originalParams = $union->params; $union->addParams($params); - $unions[$i] = $this->build($union); - $params = $union->params; + list ($unions[$i], $params) = $this->build($union); + $union->params = $originalParams; } } return "UNION (\n" . implode("\n) UNION (\n", $unions) . "\n)"; diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php index 146cfdb..e08ac87 100644 --- a/tests/unit/framework/db/QueryBuilderTest.php +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -112,7 +112,7 @@ class QueryBuilderTest extends DatabaseTestCase } } - public function testAddDropPrimayKey() + public function testAddDropPrimaryKey() { $tableName = 'tbl_constraints'; $pkeyName = $tableName . "_pkey"; From 30d622994369923f0a07c36fc449cf440ca92607 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 29 Aug 2013 16:53:15 -0400 Subject: [PATCH 033/156] Fixed test break. --- tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php index 2f6b164..3291187 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -73,9 +73,9 @@ class SqliteQueryBuilderTest extends QueryBuilderTest ); } - public function testAddDropPrimayKey() + public function testAddDropPrimaryKey() { $this->setExpectedException('yii\base\NotSupportedException'); - parent::testAddDropPrimayKey(); + parent::testAddDropPrimaryKey(); } } From cb60fa5ddd4b3b76d4c55cb80a227f98bc983913 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 29 Aug 2013 17:19:22 -0400 Subject: [PATCH 034/156] Make sure query cloning works as expected. --- framework/yii/data/ActiveDataProvider.php | 1 - framework/yii/db/ActiveRelation.php | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index e36ef90..aaf71b2 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -9,7 +9,6 @@ namespace yii\data; use Yii; use yii\base\InvalidConfigException; -use yii\base\InvalidParamException; use yii\base\Model; use yii\db\Query; use yii\db\ActiveQuery; diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 0eee25b..0be4feb 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -50,6 +50,16 @@ class ActiveRelation extends ActiveQuery */ public $via; + /** + * Clones internal objects. + */ + public function __clone() + { + if (is_object($this->via)) { + // make a clone of "via" object so that the same query object can be reused multiple times + $this->via = clone $this->via; + } + } /** * Specifies the relation associated with the pivot table. From 2ecdfd62252ebb682b2edadf1ed42635df9143e0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 29 Aug 2013 17:20:04 -0400 Subject: [PATCH 035/156] gii form width adjustment. --- framework/yii/gii/views/default/view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php index 821f6fc..1782c77 100644 --- a/framework/yii/gii/views/default/view.php +++ b/framework/yii/gii/views/default/view.php @@ -30,7 +30,7 @@ foreach ($generator->templates as $name => $path) { array('class' => ActiveField::className()))); ?>
-
+
renderFile($generator->formView(), array( 'generator' => $generator, 'form' => $form, From c2c12a9049ec321e77ee13bc7f8a9a8293596b21 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 30 Aug 2013 13:44:22 +0400 Subject: [PATCH 036/156] Fixes #829: loginRequired now responds with HTTP 403 in case of AJAX or loginUrl is not set --- framework/yii/web/User.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index b1ca8c2..4784063 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -287,10 +287,8 @@ class User extends Component public function loginRequired() { $request = Yii::$app->getRequest(); - if (!$request->getIsAjax()) { + if ($this->loginUrl !== null && !$request->getIsAjax()) { $this->setReturnUrl($request->getUrl()); - } - if ($this->loginUrl !== null) { Yii::$app->getResponse()->redirect($this->loginUrl)->send(); exit(); } else { From 70d263d66ebfc254d48296a0eed26a73722e163c Mon Sep 17 00:00:00 2001 From: Philipp Frenzel Date: Fri, 30 Aug 2013 12:23:10 +0200 Subject: [PATCH 037/156] Update module.php module should extend \yii\base\Module as \yii\web\Module doesn't exist... If this is outdated, then pls just ignore it! --- framework/yii/gii/generators/module/templates/module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/gii/generators/module/templates/module.php b/framework/yii/gii/generators/module/templates/module.php index 911cb29..40af635 100644 --- a/framework/yii/gii/generators/module/templates/module.php +++ b/framework/yii/gii/generators/module/templates/module.php @@ -16,7 +16,7 @@ echo "; -class extends \yii\web\Module +class extends \yii\base\Module { public $controllerNamespace = 'getControllerNamespace(); ?>'; From e94b4555355479913f96d475d8292dc02e72fdff Mon Sep 17 00:00:00 2001 From: Philipp Frenzel Date: Fri, 30 Aug 2013 15:05:00 +0200 Subject: [PATCH 038/156] add missing return to module template I think according to the framework it should return the render and otherwise the browser will stay blank... --- framework/yii/gii/generators/module/templates/controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/gii/generators/module/templates/controller.php b/framework/yii/gii/generators/module/templates/controller.php index 34dd961..4d3da93 100644 --- a/framework/yii/gii/generators/module/templates/controller.php +++ b/framework/yii/gii/generators/module/templates/controller.php @@ -16,6 +16,6 @@ class DefaultController extends Controller { public function actionIndex() { - $this->render('index'); + return $this->render('index'); } } From e1a6aacba8ac4b19c95d716a74f1da5db35d5ca7 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 30 Aug 2013 18:22:53 +0400 Subject: [PATCH 039/156] Fixes #829: loginRequired now responds with HTTP 403 in case of AJAX or loginUrl is not set (reverted from commit c2c12a9049ec321e77ee13bc7f8a9a8293596b21) --- framework/yii/web/User.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index 4784063..b1ca8c2 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -287,8 +287,10 @@ class User extends Component public function loginRequired() { $request = Yii::$app->getRequest(); - if ($this->loginUrl !== null && !$request->getIsAjax()) { + if (!$request->getIsAjax()) { $this->setReturnUrl($request->getUrl()); + } + if ($this->loginUrl !== null) { Yii::$app->getResponse()->redirect($this->loginUrl)->send(); exit(); } else { From fa07d313103f6f1858514ae419032e2ff1f0a302 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 30 Aug 2013 16:30:20 +0200 Subject: [PATCH 040/156] documentation fixed for mutex extension --- extensions/mutex/yii/mutex/FileMutex.php | 4 ++-- extensions/mutex/yii/mutex/Mutex.php | 1 + extensions/mutex/yii/mutex/MysqlMutex.php | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/mutex/yii/mutex/FileMutex.php b/extensions/mutex/yii/mutex/FileMutex.php index 4a949d0..ab418ba 100644 --- a/extensions/mutex/yii/mutex/FileMutex.php +++ b/extensions/mutex/yii/mutex/FileMutex.php @@ -18,7 +18,7 @@ class FileMutex extends Mutex { /** * @var string the directory to store mutex files. You may use path alias here. - * If not set, it will use the "mutex" subdirectory under the application runtime path. + * Defaults to the "mutex" subdirectory under the application runtime path. */ public $mutexPath = '@runtime/mutex'; /** @@ -69,7 +69,7 @@ class FileMutex extends Mutex } /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. + * Releases lock by given name. * @param string $name of the lock to be released. * @return boolean release result. */ diff --git a/extensions/mutex/yii/mutex/Mutex.php b/extensions/mutex/yii/mutex/Mutex.php index 297abaf..8f0a560 100644 --- a/extensions/mutex/yii/mutex/Mutex.php +++ b/extensions/mutex/yii/mutex/Mutex.php @@ -45,6 +45,7 @@ abstract class Mutex extends Component } /** + * Acquires lock by given name. * @param string $name of the lock to be acquired. Must be unique. * @param integer $timeout to wait for lock to be released. Defaults to zero meaning that method will return * false immediately in case lock was already acquired. diff --git a/extensions/mutex/yii/mutex/MysqlMutex.php b/extensions/mutex/yii/mutex/MysqlMutex.php index b04427a..f1c7cd1 100644 --- a/extensions/mutex/yii/mutex/MysqlMutex.php +++ b/extensions/mutex/yii/mutex/MysqlMutex.php @@ -29,7 +29,7 @@ class MysqlMutex extends Mutex } /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. + * Acquires lock by given name. * @param string $name of the lock to be acquired. * @param integer $timeout to wait for lock to become released. * @return boolean acquiring result. @@ -43,7 +43,7 @@ class MysqlMutex extends Mutex } /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. + * Releases lock by given name. * @param string $name of the lock to be released. * @return boolean release result. * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock From 4b7f5a728a6f44e08cd84c1d6333cf413b42361c Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 30 Aug 2013 16:52:33 +0200 Subject: [PATCH 041/156] improved control over and handling of file and dir permissions - rename FileHelper::mkdir for API consistency - changed default permission for directories to 775 instead of 777 - added some properties to classes that deal with files to allow control over directory permissions. --- extensions/mutex/yii/mutex/FileMutex.php | 24 +++++++++++++++++++++--- framework/yii/caching/FileCache.php | 23 ++++++++++++++++++++--- framework/yii/helpers/FileHelperBase.php | 14 +++++++------- framework/yii/log/FileTarget.php | 19 ++++++++++++++++++- framework/yii/web/AssetManager.php | 11 ++++++----- tests/unit/framework/helpers/FileHelperTest.php | 4 ++-- 6 files changed, 74 insertions(+), 21 deletions(-) diff --git a/extensions/mutex/yii/mutex/FileMutex.php b/extensions/mutex/yii/mutex/FileMutex.php index ab418ba..5ac9262 100644 --- a/extensions/mutex/yii/mutex/FileMutex.php +++ b/extensions/mutex/yii/mutex/FileMutex.php @@ -9,6 +9,7 @@ namespace yii\mutex; use Yii; use yii\base\InvalidConfigException; +use yii\helpers\FileHelper; /** * @author resurtm @@ -22,6 +23,19 @@ class FileMutex extends Mutex */ public $mutexPath = '@runtime/mutex'; /** + * @var integer the permission to be set for newly created mutex files. + * This value will be used by PHP chmod() function. No umask will be applied. + * If not set, the permission will be determined by the current environment. + */ + public $fileMode; + /** + * @var integer the permission to be set for newly created directories. + * This value will be used by PHP chmod() function. No umask will be applied. + * Defaults to 0775, meaning the directory is read-writable by owner and group, + * but read-only for other users. + */ + public $dirMode = 0775; + /** * @var resource[] stores all opened lock files. Keys are lock names and values are file handles. */ private $_files = array(); @@ -39,22 +53,26 @@ class FileMutex extends Mutex } $this->mutexPath = Yii::getAlias($this->mutexPath); if (!is_dir($this->mutexPath)) { - mkdir($this->mutexPath, 0777, true); + FileHelper::createDirectory($this->mutexPath, $this->dirMode, true); } } /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. + * Acquires lock by given name. * @param string $name of the lock to be acquired. * @param integer $timeout to wait for lock to become released. * @return boolean acquiring result. */ protected function acquireLock($name, $timeout = 0) { - $file = fopen($this->mutexPath . '/' . md5($name) . '.lock', 'w+'); + $fileName = $this->mutexPath . '/' . md5($name) . '.lock'; + $file = fopen($fileName, 'w+'); if ($file === false) { return false; } + if ($this->fileMode !== null) { + @chmod($fileName, $this->fileMode); + } $waitTime = 0; while (!flock($file, LOCK_EX | LOCK_NB)) { $waitTime++; diff --git a/framework/yii/caching/FileCache.php b/framework/yii/caching/FileCache.php index 437a7be..32cdf58 100644 --- a/framework/yii/caching/FileCache.php +++ b/framework/yii/caching/FileCache.php @@ -8,6 +8,7 @@ namespace yii\caching; use Yii; +use yii\helpers\FileHelper; /** * FileCache implements a cache component using files. @@ -45,6 +46,20 @@ class FileCache extends Cache * This number should be between 0 and 1000000. A value 0 means no GC will be performed at all. **/ public $gcProbability = 10; + /** + * @var integer the permission to be set for newly created cache files. + * This value will be used by PHP chmod() function. No umask will be applied. + * If not set, the permission will be determined by the current environment. + */ + public $fileMode; + /** + * @var integer the permission to be set for newly created directories. + * This value will be used by PHP chmod() function. No umask will be applied. + * Defaults to 0775, meaning the directory is read-writable by owner and group, + * but read-only for other users. + */ + public $dirMode = 0775; + /** * Initializes this component by ensuring the existence of the cache path. @@ -54,7 +69,7 @@ class FileCache extends Cache parent::init(); $this->cachePath = Yii::getAlias($this->cachePath); if (!is_dir($this->cachePath)) { - mkdir($this->cachePath, 0777, true); + FileHelper::createDirectory($this->cachePath, $this->dirMode, true); } } @@ -108,10 +123,12 @@ class FileCache extends Cache $cacheFile = $this->getCacheFile($key); if ($this->directoryLevel > 0) { - @mkdir(dirname($cacheFile), 0777, true); + @FileHelper::createDirectory(dirname($cacheFile), $this->dirMode, true); } if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) { - @chmod($cacheFile, 0777); + if ($this->fileMode !== null) { + @chmod($cacheFile, $this->fileMode); + } return @touch($cacheFile, $expire); } else { return false; diff --git a/framework/yii/helpers/FileHelperBase.php b/framework/yii/helpers/FileHelperBase.php index 6358ea1..0e480da 100644 --- a/framework/yii/helpers/FileHelperBase.php +++ b/framework/yii/helpers/FileHelperBase.php @@ -133,7 +133,7 @@ class FileHelperBase * @param string $dst the destination directory * @param array $options options for directory copy. Valid options are: * - * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777. + * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775. * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. * - filter: callback, a PHP callback that is called for each directory or file. * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. @@ -162,7 +162,7 @@ class FileHelperBase public static function copyDirectory($src, $dst, $options = array()) { if (!is_dir($dst)) { - static::mkdir($dst, isset($options['dirMode']) ? $options['dirMode'] : 0777, true); + static::createDirectory($dst, isset($options['dirMode']) ? $options['dirMode'] : 0775, true); } $handle = opendir($src); @@ -302,25 +302,25 @@ class FileHelperBase } /** - * Makes directory. + * Creates a new directory. * * This method is similar to the PHP `mkdir()` function except that * it uses `chmod()` to set the permission of the created directory * in order to avoid the impact of the `umask` setting. * - * @param string $path path to be created. - * @param integer $mode the permission to be set for created directory. + * @param string $path path of the directory to be created. + * @param integer $mode the permission to be set for the created directory. * @param boolean $recursive whether to create parent directories if they do not exist. * @return boolean whether the directory is created successfully */ - public static function mkdir($path, $mode = 0777, $recursive = true) + public static function createDirectory($path, $mode = 0775, $recursive = true) { if (is_dir($path)) { return true; } $parentDir = dirname($path); if ($recursive && !is_dir($parentDir)) { - static::mkdir($parentDir, $mode, true); + static::createDirectory($parentDir, $mode, true); } $result = mkdir($path, $mode); chmod($path, $mode); diff --git a/framework/yii/log/FileTarget.php b/framework/yii/log/FileTarget.php index 74a33be..970c71b 100644 --- a/framework/yii/log/FileTarget.php +++ b/framework/yii/log/FileTarget.php @@ -9,6 +9,7 @@ namespace yii\log; use Yii; use yii\base\InvalidConfigException; +use yii\helpers\FileHelper; /** * FileTarget records log messages in a file. @@ -37,6 +38,19 @@ class FileTarget extends Target * @var integer number of log files used for rotation. Defaults to 5. */ public $maxLogFiles = 5; + /** + * @var integer the permission to be set for newly created log files. + * This value will be used by PHP chmod() function. No umask will be applied. + * If not set, the permission will be determined by the current environment. + */ + public $fileMode; + /** + * @var integer the permission to be set for newly created directories. + * This value will be used by PHP chmod() function. No umask will be applied. + * Defaults to 0775, meaning the directory is read-writable by owner and group, + * but read-only for other users. + */ + public $dirMode = 0775; /** @@ -53,7 +67,7 @@ class FileTarget extends Target } $logPath = dirname($this->logFile); if (!is_dir($logPath)) { - @mkdir($logPath, 0777, true); + FileHelper::createDirectory($logPath, $this->dirMode, true); } if ($this->maxLogFiles < 1) { $this->maxLogFiles = 1; @@ -87,6 +101,9 @@ class FileTarget extends Target @flock($fp, LOCK_UN); @fclose($fp); } + if ($this->fileMode !== null) { + @chmod($this->logFile, $this->fileMode); + } } /** diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php index 43e7cf9..c6f7fea 100644 --- a/framework/yii/web/AssetManager.php +++ b/framework/yii/web/AssetManager.php @@ -58,16 +58,17 @@ class AssetManager extends Component public $linkAssets = false; /** * @var integer the permission to be set for newly published asset files. - * This value will be used by PHP chmod() function. + * This value will be used by PHP chmod() function. No umask will be applied. * If not set, the permission will be determined by the current environment. */ public $fileMode; /** * @var integer the permission to be set for newly generated asset directories. - * This value will be used by PHP chmod() function. - * Defaults to 0777, meaning the directory can be read, written and executed by all users. + * This value will be used by PHP chmod() function. No umask will be applied. + * Defaults to 0775, meaning the directory is read-writable by owner and group, + * but read-only for other users. */ - public $dirMode = 0777; + public $dirMode = 0775; /** * Initializes the component. @@ -205,7 +206,7 @@ class AssetManager extends Component $dstFile = $dstDir . DIRECTORY_SEPARATOR . $fileName; if (!is_dir($dstDir)) { - mkdir($dstDir, $this->dirMode, true); + FileHelper::createDirectory($dstDir, $this->dirMode, true); } if ($this->linkAssets) { diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 896218e..7c0f8c4 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -275,9 +275,9 @@ class FileHelperTest extends TestCase { $basePath = $this->testFilePath; $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_level_1' . DIRECTORY_SEPARATOR . 'test_dir_level_2'; - $this->assertTrue(FileHelper::mkdir($dirName), 'FileHelper::mkdir should return true if directory was created!'); + $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::mkdir should return true if directory was created!'); $this->assertTrue(file_exists($dirName), 'Unable to create directory recursively!'); - $this->assertTrue(FileHelper::mkdir($dirName), 'FileHelper::mkdir should return true for already existing directories!'); + $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::mkdir should return true for already existing directories!'); } public function testGetMimeTypeByExtension() From 4d84e0947999b6208cc12a312de13fcdbcb0aa01 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 30 Aug 2013 16:57:11 +0200 Subject: [PATCH 042/156] typo in query builder, fixes #832 --- framework/yii/db/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 9a9329d..dd59881 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -803,7 +803,7 @@ class QueryBuilder extends \yii\base\Object $parts = array(); foreach ($condition as $column => $value) { if (is_array($value)) { // IN condition - $parts[] = $this->buildInCondition('in', array($column, $value), $params); + $parts[] = $this->buildInCondition('IN', array($column, $value), $params); } else { if (strpos($column, '(') === false) { $column = $this->db->quoteColumnName($column); From b5a6dac3ae707ce6ac05fcb06406495c734ac9da Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 30 Aug 2013 17:03:09 +0200 Subject: [PATCH 043/156] renamed test method after implementation rename in FileHelper --- tests/unit/framework/helpers/FileHelperTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 7c0f8c4..2212072 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -271,13 +271,13 @@ class FileHelperTest extends TestCase $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); } - public function testMkdir() + public function testCreateDirectory() { $basePath = $this->testFilePath; $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_level_1' . DIRECTORY_SEPARATOR . 'test_dir_level_2'; - $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::mkdir should return true if directory was created!'); + $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::createDirectory should return true if directory was created!'); $this->assertTrue(file_exists($dirName), 'Unable to create directory recursively!'); - $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::mkdir should return true for already existing directories!'); + $this->assertTrue(FileHelper::createDirectory($dirName), 'FileHelper::createDirectory should return true for already existing directories!'); } public function testGetMimeTypeByExtension() From 595ac6d0beaed1ba95c9249262c003cc40a87802 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 30 Aug 2013 17:07:55 -0400 Subject: [PATCH 044/156] Fixes #833: added charset to json response. --- framework/yii/web/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index cfbc537..3599e63 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -781,7 +781,7 @@ class Response extends \yii\base\Response $this->content = $this->data; break; case self::FORMAT_JSON: - $this->getHeaders()->set('Content-Type', 'application/json'); + $this->getHeaders()->set('Content-Type', 'application/json; charset=' . $this->charset); $this->content = Json::encode($this->data); break; case self::FORMAT_JSONP: From 947a557240e6bf8eda4a1b41057d76874e7e0a49 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 31 Aug 2013 08:15:10 -0400 Subject: [PATCH 045/156] Reverted changeset 595ac6d0beaed1ba95c9249262c003cc40a87802 --- framework/yii/web/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index 3599e63..cfbc537 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -781,7 +781,7 @@ class Response extends \yii\base\Response $this->content = $this->data; break; case self::FORMAT_JSON: - $this->getHeaders()->set('Content-Type', 'application/json; charset=' . $this->charset); + $this->getHeaders()->set('Content-Type', 'application/json'); $this->content = Json::encode($this->data); break; case self::FORMAT_JSONP: From f83fcc553cb11f27f945d9cc2390ad13b5883345 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 31 Aug 2013 18:51:00 +0200 Subject: [PATCH 046/156] fixed return value of Component::can*Property return false, not null --- framework/yii/base/Component.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index 6aebc92..bd49bc8 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -264,8 +264,8 @@ class Component extends Object return true; } } - return false; } + return false; } /** @@ -294,8 +294,8 @@ class Component extends Object return true; } } - return false; } + return false; } /** From 7a5564067041cf06a45d94f71c3f50d5ec237fdc Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 31 Aug 2013 18:53:44 +0200 Subject: [PATCH 047/156] Added hasMethod() to Component and Object class this allows implementing __call() in behaviors See this forum topic for details: http://www.yiiframework.com/forum/index.php/topic/46629-invoke-behavior-method-through-php-magic-method-call-dosent-work/ --- framework/yii/base/Component.php | 26 ++++++++++++++++++++++++++ framework/yii/base/Object.php | 13 +++++++++++++ 2 files changed, 39 insertions(+) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index bd49bc8..544d7d4 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -299,6 +299,32 @@ class Component extends Object } /** + * Returns a value indicating whether a method is defined. + * A method is defined if: + * + * - the class has a method with the specified name + * - an attached behavior has a method with the given name (when `$checkBehaviors` is true). + * + * @param string $name the property name + * @param boolean $checkBehaviors whether to treat behaviors' methods as methods of this component + * @return boolean whether the property is defined + */ + public function hasMethod($name, $checkBehaviors = true) + { + if (method_exists($this, $name)) { + return true; + } elseif ($checkBehaviors) { + $this->ensureBehaviors(); + foreach ($this->_behaviors as $behavior) { + if ($behavior->hasMethod($name)) { + return true; + } + } + } + return false; + } + + /** * Returns a list of behaviors that this component should behave as. * * Child classes may override this method to specify the behaviors they want to behave as. diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index 50ad9e9..749ad63 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -220,6 +220,19 @@ class Object implements Arrayable } /** + * Returns a value indicating whether a method is defined. + * + * The default implementation is a call to php function `method_exists()`. + * You may override this method when you implemented the php magic method `__call()`. + * @param string $name the property name + * @return boolean whether the property is defined + */ + public function hasMethod($name) + { + return method_exists($this, $name); + } + + /** * Converts the object into an array. * The default implementation will return all public property values as an array. * @return array the array representation of the object From 15a3ec2764cb6d6a70fdc86d5b41e14a9fad1391 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 31 Aug 2013 23:27:52 +0200 Subject: [PATCH 048/156] added unit tests and call to $behavior->hasMethod in Component --- framework/yii/base/Component.php | 2 +- tests/unit/framework/base/BehaviorTest.php | 41 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index 544d7d4..f57280d 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -199,7 +199,7 @@ class Component extends Object $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { - if (method_exists($object, $name)) { + if (method_exists($object, $name) || $object->hasMethod($name)) { return call_user_func_array(array($object, $name), $params); } } diff --git a/tests/unit/framework/base/BehaviorTest.php b/tests/unit/framework/base/BehaviorTest.php index edb9a1e..e596ea8 100644 --- a/tests/unit/framework/base/BehaviorTest.php +++ b/tests/unit/framework/base/BehaviorTest.php @@ -28,6 +28,22 @@ class BarBehavior extends Behavior { return 'behavior method'; } + + public function __call($name, $params) + { + if ($name == 'magicBehaviorMethod') { + return 'Magic Behavior Method Result!'; + } + return parent::__call($name, $params); + } + + public function hasMethod($name) + { + if ($name == 'magicBehaviorMethod') { + return true; + } + return parent::hasMethod($name); + } } class BehaviorTest extends TestCase @@ -59,4 +75,29 @@ class BehaviorTest extends TestCase $this->assertEquals('behavior property', $foo->behaviorProperty); $this->assertEquals('behavior method', $foo->behaviorMethod()); } + + public function testMagicMethods() + { + $bar = new BarClass(); + $behavior = new BarBehavior(); + + $this->assertFalse($bar->hasMethod('magicBehaviorMethod')); + $bar->attachBehavior('bar', $behavior); + $this->assertFalse($bar->hasMethod('magicBehaviorMethod', false)); + $this->assertTrue($bar->hasMethod('magicBehaviorMethod')); + + $this->assertEquals('Magic Behavior Method Result!', $bar->magicBehaviorMethod()); + } + + public function testCallUnknownMethod() + { + $bar = new BarClass(); + $behavior = new BarBehavior(); + $this->setExpectedException('yii\base\UnknownMethodException'); + + $this->assertFalse($bar->hasMethod('nomagicBehaviorMethod')); + $bar->attachBehavior('bar', $behavior); + $bar->nomagicBehaviorMethod(); + } + } From f87e4ce6042338328b9d3bc3ddd516f4b6d5ea0a Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 31 Aug 2013 18:47:30 -0400 Subject: [PATCH 049/156] automatically hide model class input if needed. --- framework/yii/gii/assets/gii.js | 5 +++++ framework/yii/gii/controllers/DefaultController.php | 2 +- framework/yii/gii/views/default/view.php | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/framework/yii/gii/assets/gii.js b/framework/yii/gii/assets/gii.js index 284006e..b581d3b 100644 --- a/framework/yii/gii/assets/gii.js +++ b/framework/yii/gii/assets/gii.js @@ -76,6 +76,11 @@ yii.gii = (function ($) { initPreviewDiffLinks(); initConfirmationCheckboxes(); + // model generator: hide class name input when table name input contains * + $('#model-generator #generator-tablename').on('change', function () { + $('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1); + }).change(); + // hide Generate button if any input is changed $('.default-view .form-group input,select,textarea').change(function () { $('.default-view-results,.default-view-files').hide(); diff --git a/framework/yii/gii/controllers/DefaultController.php b/framework/yii/gii/controllers/DefaultController.php index 9bc444e..969096b 100644 --- a/framework/yii/gii/controllers/DefaultController.php +++ b/framework/yii/gii/controllers/DefaultController.php @@ -35,7 +35,7 @@ class DefaultController extends Controller public function actionView($id) { $generator = $this->loadGenerator($id); - $params = array('generator' => $generator); + $params = array('generator' => $generator, 'id' => $id); if (isset($_POST['preview']) || isset($_POST['generate'])) { if ($generator->validate()) { $generator->saveStickyAttributes(); diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php index 1782c77..c61d5d0 100644 --- a/framework/yii/gii/views/default/view.php +++ b/framework/yii/gii/views/default/view.php @@ -28,7 +28,10 @@ foreach ($generator->templates as $name => $path) {

getDescription(); ?>

- array('class' => ActiveField::className()))); ?> + "$id-generator", + 'fieldConfig' => array('class' => ActiveField::className()), + )); ?>
renderFile($generator->formView(), array( From afd04727846689e0f37b9fe18e39ab36751723b5 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 31 Aug 2013 21:29:46 -0400 Subject: [PATCH 050/156] Fixes #837: turn private variables into public ones. --- framework/yii/web/UploadedFile.php | 124 ++++++++++++------------------------- 1 file changed, 39 insertions(+), 85 deletions(-) diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php index 21fc355..c4e2f0f 100644 --- a/framework/yii/web/UploadedFile.php +++ b/framework/yii/web/UploadedFile.php @@ -7,6 +7,7 @@ namespace yii\web; +use yii\base\Object; use yii\helpers\Html; /** @@ -17,49 +18,42 @@ use yii\helpers\Html; * You may also query other information about the file, including [[name]], * [[tempName]], [[type]], [[size]] and [[error]]. * - * @property integer $error The error code. This property is read-only. * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed * error code information. This property is read-only. - * @property string $name The original name of the file being uploaded. This property is read-only. - * @property integer $size The actual size of the uploaded file in bytes. This property is read-only. - * @property string $tempName The path of the uploaded file on the server. Note, this is a temporary file - * which will be automatically deleted by PHP after the current request is processed. This property is read-only. - * @property string $type The MIME-type of the uploaded file (such as "image/gif"). Since this MIME type is - * not checked on the server side, do not take this value for granted. Instead, use [[FileHelper::getMimeType()]] - * to determine the exact MIME type. This property is read-only. * * @author Qiang Xue * @since 2.0 */ -class UploadedFile extends \yii\base\Object +class UploadedFile extends Object { private static $_files; - private $_name; - private $_tempName; - private $_type; - private $_size; - private $_error; - /** - * Constructor. - * Instead of using the constructor to create a new instance, - * you should normally call [[getInstance()]] or [[getInstances()]] - * to obtain new instances. - * @param string $name the original name of the file being uploaded - * @param string $tempName the path of the uploaded file on the server. - * @param string $type the MIME-type of the uploaded file (such as "image/gif"). - * @param integer $size the actual size of the uploaded file in bytes - * @param integer $error the error code + * @var string the original name of the file being uploaded */ - public function __construct($name, $tempName, $type, $size, $error) - { - $this->_name = $name; - $this->_tempName = $tempName; - $this->_type = $type; - $this->_size = $size; - $this->_error = $error; - } + public $name; + /** + * @var string the path of the uploaded file on the server. + * Note, this is a temporary file which will be automatically deleted by PHP + * after the current request is processed. + */ + public $tempName; + /** + * @var string the MIME-type of the uploaded file (such as "image/gif"). + * Since this MIME type is not checked on the server side, do not take this value for granted. + * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. + */ + public $type; + /** + * @var integer the actual size of the uploaded file in bytes + */ + public $size; + /** + * @var integer an error code describing the status of this file uploading. + * @see http://www.php.net/manual/en/features.file-upload.errors.php + */ + public $error; + /** * String output. @@ -69,7 +63,7 @@ class UploadedFile extends \yii\base\Object */ public function __toString() { - return $this->_name; + return $this->name; } /** @@ -160,69 +154,23 @@ class UploadedFile extends \yii\base\Object */ public function saveAs($file, $deleteTempFile = true) { - if ($this->_error == UPLOAD_ERR_OK) { + if ($this->error == UPLOAD_ERR_OK) { if ($deleteTempFile) { - return move_uploaded_file($this->_tempName, $file); - } elseif (is_uploaded_file($this->_tempName)) { - return copy($this->_tempName, $file); + return move_uploaded_file($this->tempName, $file); + } elseif (is_uploaded_file($this->tempName)) { + return copy($this->tempName, $file); } } return false; } /** - * @return string the original name of the file being uploaded - */ - public function getName() - { - return $this->_name; - } - - /** - * @return string the path of the uploaded file on the server. - * Note, this is a temporary file which will be automatically deleted by PHP - * after the current request is processed. - */ - public function getTempName() - { - return $this->_tempName; - } - - /** - * @return string the MIME-type of the uploaded file (such as "image/gif"). - * Since this MIME type is not checked on the server side, do not take this value for granted. - * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. - */ - public function getType() - { - return $this->_type; - } - - /** - * @return integer the actual size of the uploaded file in bytes - */ - public function getSize() - { - return $this->_size; - } - - /** - * Returns an error code describing the status of this file uploading. - * @return integer the error code - * @see http://www.php.net/manual/en/features.file-upload.errors.php - */ - public function getError() - { - return $this->_error; - } - - /** * @return boolean whether there is an error with the uploaded file. * Check [[error]] for detailed error code information. */ public function getHasError() { - return $this->_error != UPLOAD_ERR_OK; + return $this->error != UPLOAD_ERR_OK; } /** @@ -258,7 +206,13 @@ class UploadedFile extends \yii\base\Object self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]); } } else { - self::$_files[$key] = new static($names, $tempNames, $types, $sizes, $errors); + self::$_files[$key] = new static(array( + 'name' => $names, + 'tempName' => $tempNames, + 'type' => $types, + 'size' => $sizes, + 'error' => $errors, + )); } } } From 7a6ce8251a37f52f04f88174661f21a6643cd9de Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 1 Sep 2013 14:40:15 +0200 Subject: [PATCH 051/156] simplified method check in Component::__call() --- framework/yii/base/Component.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index f57280d..b5467a7 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -199,7 +199,7 @@ class Component extends Object $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { - if (method_exists($object, $name) || $object->hasMethod($name)) { + if ($object->hasMethod($name)) { return call_user_func_array(array($object, $name), $params); } } From ea10868824159533a3b1ece349d617f2c6c70153 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 1 Sep 2013 10:26:59 -0400 Subject: [PATCH 052/156] Removed Yii::import(). --- framework/yii/YiiBase.php | 39 ---------------------------- framework/yii/validators/ExistValidator.php | 2 +- framework/yii/validators/UniqueValidator.php | 2 +- framework/yii/web/User.php | 2 +- 4 files changed, 3 insertions(+), 42 deletions(-) diff --git a/framework/yii/YiiBase.php b/framework/yii/YiiBase.php index a5a3833..c96a969 100644 --- a/framework/yii/YiiBase.php +++ b/framework/yii/YiiBase.php @@ -100,7 +100,6 @@ class YiiBase */ public static $objectConfig = array(); - private static $_imported = array(); // alias => class name or directory /** * @return string the version of Yii framework @@ -111,40 +110,6 @@ class YiiBase } /** - * Imports a class by its alias. - * - * This method is provided to support autoloading of non-namespaced classes. - * Such a class can be specified in terms of an alias. For example, the alias `@old/code/Sample` - * may represent the `Sample` class under the directory `@old/code` (a path alias). - * - * By importing a class, the class is put in an internal storage such that when - * the class is used for the first time, the class autoloader will be able to - * find the corresponding class file and include it. For this reason, this method - * is much lighter than `include()`. - * - * You may import the same class multiple times. Only the first importing will count. - * - * @param string $alias the class to be imported. This may be either a class alias or a fully-qualified class name. - * If the latter, it will be returned back without change. - * @return string the actual class name that `$alias` refers to - * @throws Exception if the alias is invalid - */ - public static function import($alias) - { - if (strncmp($alias, '@', 1)) { - return $alias; - } else { - $alias = static::getAlias($alias); - if (!isset(self::$_imported[$alias])) { - $className = basename($alias); - self::$_imported[$alias] = $className; - self::$classMap[$className] = $alias . '.php'; - } - return self::$_imported[$alias]; - } - } - - /** * Imports a set of namespaces. * * By importing a namespace, the method will create an alias for the directory corresponding @@ -431,10 +396,6 @@ class YiiBase throw new InvalidConfigException('Object configuration must be an array containing a "class" element.'); } - if (!class_exists($class, false)) { - $class = static::import($class); - } - $class = ltrim($class, '\\'); if (isset(self::$objectConfig[$class])) { diff --git a/framework/yii/validators/ExistValidator.php b/framework/yii/validators/ExistValidator.php index 9c74890..8cbce5f 100644 --- a/framework/yii/validators/ExistValidator.php +++ b/framework/yii/validators/ExistValidator.php @@ -66,7 +66,7 @@ class ExistValidator extends Validator } /** @var $className \yii\db\ActiveRecord */ - $className = $this->className === null ? get_class($object) : Yii::import($this->className); + $className = $this->className === null ? get_class($object) : $this->className; $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; $query = $className::find(); $query->where(array($attributeName => $value)); diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php index b650693..c3876e8 100644 --- a/framework/yii/validators/UniqueValidator.php +++ b/framework/yii/validators/UniqueValidator.php @@ -61,7 +61,7 @@ class UniqueValidator extends Validator } /** @var $className \yii\db\ActiveRecord */ - $className = $this->className === null ? get_class($object) : Yii::import($this->className); + $className = $this->className === null ? get_class($object) : $this->className; $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; $table = $className::getTableSchema(); diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index b1ca8c2..e02559b 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -140,7 +140,7 @@ class User extends Component $this->_identity = null; } else { /** @var $class Identity */ - $class = Yii::import($this->identityClass); + $class = $this->identityClass; $this->_identity = $class::findIdentity($id); } } From 7fa81949bfbc3736cb35dd59acb76be2f5571167 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 1 Sep 2013 19:47:26 +0200 Subject: [PATCH 053/156] various documentation changes --- framework/yii/base/Application.php | 2 +- framework/yii/base/Component.php | 3 +-- framework/yii/base/Formatter.php | 2 +- framework/yii/base/Object.php | 3 +-- framework/yii/bootstrap/Nav.php | 2 +- framework/yii/db/Command.php | 12 ++++++------ framework/yii/db/DataReader.php | 6 +++--- framework/yii/db/Migration.php | 4 ++-- framework/yii/db/QueryBuilder.php | 6 +++--- framework/yii/helpers/ArrayHelperBase.php | 2 +- framework/yii/helpers/ConsoleBase.php | 8 ++++---- framework/yii/validators/FileValidator.php | 2 +- 12 files changed, 25 insertions(+), 27 deletions(-) diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 9efb6ce..e8c496c 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -86,7 +86,7 @@ abstract class Application extends Module */ public $controller; /** - * @var mixed the layout that should be applied for views in this application. Defaults to 'main'. + * @var string|boolean the layout that should be applied for views in this application. Defaults to 'main'. * If this is false, layout will be disabled. */ public $layout = 'main'; diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index b5467a7..c1a2e04 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -38,8 +38,7 @@ class Component extends Object * Do not call this method directly as it is a PHP magic method that * will be implicitly called when executing `$value = $component->property;`. * @param string $name the property name - * @return mixed the property value, event handlers attached to the event, - * the behavior, or the value of a behavior's property + * @return mixed the property value or the value of a behavior's property * @throws UnknownPropertyException if the property is not defined * @see __set */ diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php index 50b656a..84b4b8d 100644 --- a/framework/yii/base/Formatter.php +++ b/framework/yii/base/Formatter.php @@ -309,7 +309,7 @@ class Formatter extends Component /** * Normalizes the given datetime value as one that can be taken by various date/time formatting methods. * @param mixed $value the datetime value to be normalized. - * @return mixed the normalized datetime value + * @return integer the normalized datetime value */ protected function normalizeDatetimeValue($value) { diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index 749ad63..9f22aba 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -61,8 +61,7 @@ class Object implements Arrayable * Do not call this method directly as it is a PHP magic method that * will be implicitly called when executing `$value = $object->property;`. * @param string $name the property name - * @return mixed the property value, event handlers attached to the event, - * the named behavior, or the value of a behavior's property + * @return mixed the property value * @throws UnknownPropertyException if the property is not defined * @see __set */ diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index ee103d4..e19eae1 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -137,7 +137,7 @@ class Nav extends Widget /** * Renders a widget's item. - * @param mixed $item the item to render. + * @param string|array $item the item to render. * @return string the rendering result. * @throws InvalidConfigException */ diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index f9376ee..460cd46 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -62,7 +62,7 @@ class Command extends \yii\base\Component */ public $pdoStatement; /** - * @var mixed the default fetch mode for this command. + * @var integer the default fetch mode for this command. * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php */ public $fetchMode = \PDO::FETCH_ASSOC; @@ -314,7 +314,7 @@ class Command extends \yii\base\Component /** * Executes the SQL statement and returns ALL rows at once. - * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) + * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used. * @return array all rows of the query result. Each array element is an array representing a row of data. * An empty array is returned if the query results in nothing. @@ -328,7 +328,7 @@ class Command extends \yii\base\Component /** * Executes the SQL statement and returns the first row of the result. * This method is best used when only the first row of result is needed for a query. - * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) + * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used. * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query * results in nothing. @@ -371,7 +371,7 @@ class Command extends \yii\base\Component /** * Performs the actual DB query of a SQL statement. * @param string $method method of PDOStatement to be called - * @param mixed $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) + * @param integer $fetchMode the result fetch mode. Please refer to [PHP manual](http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php) * for valid fetch modes. If this parameter is null, the value set in [[fetchMode]] will be used. * @return mixed the method execution result * @throws Exception if the query causes any problem @@ -501,7 +501,7 @@ class Command extends \yii\base\Component * * @param string $table the table to be updated. * @param array $columns the column data (name => value) to be updated. - * @param mixed $condition the condition that will be put in the WHERE part. Please + * @param string|array $condition the condition that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify condition. * @param array $params the parameters to be bound to the command * @return Command the command object itself @@ -525,7 +525,7 @@ class Command extends \yii\base\Component * Note that the created command is not executed until [[execute()]] is called. * * @param string $table the table where the data will be deleted from. - * @param mixed $condition the condition that will be put in the WHERE part. Please + * @param string|array $condition the condition that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify condition. * @param array $params the parameters to be bound to the command * @return Command the command object itself diff --git a/framework/yii/db/DataReader.php b/framework/yii/db/DataReader.php index ddfcf29..d18de4c 100644 --- a/framework/yii/db/DataReader.php +++ b/framework/yii/db/DataReader.php @@ -73,7 +73,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable * Binds a column to a PHP variable. * When rows of data are being fetched, the corresponding column value * will be set in the variable. Note, the fetch mode must include PDO::FETCH_BOUND. - * @param mixed $column Number of the column (1-indexed) or name of the column + * @param integer|string $column Number of the column (1-indexed) or name of the column * in the result set. If using the column name, be aware that the name * should match the case of the column, as returned by the driver. * @param mixed $value Name of the PHP variable to which the column will be bound. @@ -91,7 +91,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable /** * Set the default fetch mode for this statement - * @param mixed $mode fetch mode + * @param integer $mode fetch mode * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php */ public function setFetchMode($mode) @@ -112,7 +112,7 @@ class DataReader extends \yii\base\Object implements \Iterator, \Countable /** * Returns a single column from the next row of a result set. * @param integer $columnIndex zero-based column index - * @return mixed the column of the current row, false if no more row available + * @return mixed the column of the current row, false if no more rows available */ public function readColumn($columnIndex) { diff --git a/framework/yii/db/Migration.php b/framework/yii/db/Migration.php index 7c6815b..7368788 100644 --- a/framework/yii/db/Migration.php +++ b/framework/yii/db/Migration.php @@ -162,7 +162,7 @@ class Migration extends \yii\base\Component * The method will properly escape the column names and bind the values to be updated. * @param string $table the table to be updated. * @param array $columns the column data (name => value) to be updated. - * @param mixed $condition the conditions that will be put in the WHERE part. Please + * @param array|string $condition the conditions that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify conditions. * @param array $params the parameters to be bound to the query. */ @@ -177,7 +177,7 @@ class Migration extends \yii\base\Component /** * Creates and executes a DELETE SQL statement. * @param string $table the table where the data will be deleted from. - * @param mixed $condition the conditions that will be put in the WHERE part. Please + * @param array|string $condition the conditions that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify conditions. * @param array $params the parameters to be bound to the query. */ diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index dd59881..f67038c 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -163,7 +163,7 @@ class QueryBuilder extends \yii\base\Object * * @param string $table the table to be updated. * @param array $columns the column data (name => value) to be updated. - * @param mixed $condition the condition that will be put in the WHERE part. Please + * @param array|string $condition the condition that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify condition. * @param array $params the binding parameters that will be modified by this method * so that they can be bound to the DB command later. @@ -207,7 +207,7 @@ class QueryBuilder extends \yii\base\Object * The method will properly escape the table and column names. * * @param string $table the table where the data will be deleted from. - * @param mixed $condition the condition that will be put in the WHERE part. Please + * @param array|string $condition the condition that will be put in the WHERE part. Please * refer to [[Query::where()]] on how to specify condition. * @param array $params the binding parameters that will be modified by this method * so that they can be bound to the DB command later. @@ -461,7 +461,7 @@ class QueryBuilder extends \yii\base\Object * The sequence will be reset such that the primary key of the next new row inserted * will have the specified value or 1. * @param string $table the name of the table whose primary key sequence will be reset - * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, + * @param array|string $value the value for the primary key of the next new row inserted. If this is not set, * the next new row's primary key will have a value 1. * @return string the SQL statement for resetting sequence * @throws NotSupportedException if this is not supported by the underlying DBMS diff --git a/framework/yii/helpers/ArrayHelperBase.php b/framework/yii/helpers/ArrayHelperBase.php index 2492246..59129de 100644 --- a/framework/yii/helpers/ArrayHelperBase.php +++ b/framework/yii/helpers/ArrayHelperBase.php @@ -143,7 +143,7 @@ class ArrayHelperBase * or an anonymous function returning the value. The anonymous function signature should be: * `function($array, $defaultValue)`. * @param mixed $default the default value to be returned if the specified key does not exist - * @return mixed the value of the + * @return mixed the value of the element if found, default value otherwise */ public static function getValue($array, $key, $default = null) { diff --git a/framework/yii/helpers/ConsoleBase.php b/framework/yii/helpers/ConsoleBase.php index 3593e74..a985291 100644 --- a/framework/yii/helpers/ConsoleBase.php +++ b/framework/yii/helpers/ConsoleBase.php @@ -665,8 +665,8 @@ class ConsoleBase /** * Prints text to STDOUT appended with a carriage return (PHP_EOL). * - * @param string $string - * @return mixed Number of bytes printed or bool false on error + * @param string $string the text to print + * @return integer|boolean number of bytes printed or false on error. */ public static function output($string = null) { @@ -676,8 +676,8 @@ class ConsoleBase /** * Prints text to STDERR appended with a carriage return (PHP_EOL). * - * @param string $string - * @return mixed Number of bytes printed or false on error + * @param string $string the text to print + * @return integer|boolean number of bytes printed or false on error. */ public static function error($string = null) { diff --git a/framework/yii/validators/FileValidator.php b/framework/yii/validators/FileValidator.php index 4487eee..fbd432d 100644 --- a/framework/yii/validators/FileValidator.php +++ b/framework/yii/validators/FileValidator.php @@ -21,7 +21,7 @@ use yii\web\UploadedFile; class FileValidator extends Validator { /** - * @var mixed a list of file name extensions that are allowed to be uploaded. + * @var array|string a list of file name extensions that are allowed to be uploaded. * This can be either an array or a string consisting of file extension names * separated by space or comma (e.g. "gif, jpg"). * Extension names are case-insensitive. Defaults to null, meaning all file name From 9a4d7ff15bf8bf44570a1c186141958bd1b86c74 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 1 Sep 2013 19:57:54 +0200 Subject: [PATCH 054/156] Exception message when getting write-only property --- framework/yii/base/Component.php | 6 +++++- framework/yii/base/Object.php | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index c1a2e04..cc3525e 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -57,7 +57,11 @@ class Component extends Object } } } - throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); + if (method_exists($this, 'set' . $name)) { + throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); + } else { + throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); + } } /** diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index 9f22aba..adbab9c 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -70,6 +70,8 @@ class Object implements Arrayable $getter = 'get' . $name; if (method_exists($this, $getter)) { return $this->$getter(); + } elseif (method_exists($this, 'set' . $name)) { + throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); } From 38dab3490a2cab5779e71c5508974a9a54dbb254 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 1 Sep 2013 20:00:03 +0200 Subject: [PATCH 055/156] Fixed type of db\Exception::$errorInfo type is array in PHP documentation --- framework/yii/db/Exception.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/yii/db/Exception.php b/framework/yii/db/Exception.php index 9339211..25ae39f 100644 --- a/framework/yii/db/Exception.php +++ b/framework/yii/db/Exception.php @@ -16,19 +16,19 @@ namespace yii\db; class Exception extends \yii\base\Exception { /** - * @var mixed the error info provided by a PDO exception. This is the same as returned + * @var array the error info provided by a PDO exception. This is the same as returned * by [PDO::errorInfo](http://www.php.net/manual/en/pdo.errorinfo.php). */ - public $errorInfo; + public $errorInfo = array(); /** * Constructor. * @param string $message PDO error message - * @param mixed $errorInfo PDO error info + * @param array $errorInfo PDO error info * @param integer $code PDO error code * @param \Exception $previous The previous exception used for the exception chaining. */ - public function __construct($message, $errorInfo = null, $code = 0, \Exception $previous = null) + public function __construct($message, $errorInfo = array(), $code = 0, \Exception $previous = null) { $this->errorInfo = $errorInfo; parent::__construct($message, $code, $previous); From 3f2e7fa604a1af8a852780d2e9cf06b3baf35b18 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 2 Sep 2013 20:25:39 -0400 Subject: [PATCH 056/156] crud wip --- framework/yii/gii/generators/crud/Generator.php | 22 ++- framework/yii/gii/generators/crud/form.php | 6 + .../gii/generators/crud/templates/controller.php | 182 ++++++++++++++++++++- .../yii/gii/generators/crud/templates/model.php | 8 - .../yii/gii/generators/crud/templates/search.php | 8 + .../gii/generators/crud/templates/views/_form.php | 49 ++++++ .../generators/crud/templates/views/_search.php | 38 +++++ .../gii/generators/crud/templates/views/_view.php | 31 ++++ .../gii/generators/crud/templates/views/create.php | 27 +++ .../generators/crud/templates/views/index-grid.php | 73 +++++++++ .../gii/generators/crud/templates/views/index.php | 29 ++++ .../gii/generators/crud/templates/views/update.php | 31 ++++ .../gii/generators/crud/templates/views/view.php | 39 +++++ framework/yii/gii/views/default/view.php | 1 + 14 files changed, 527 insertions(+), 17 deletions(-) delete mode 100644 framework/yii/gii/generators/crud/templates/model.php create mode 100644 framework/yii/gii/generators/crud/templates/search.php create mode 100644 framework/yii/gii/generators/crud/templates/views/_form.php create mode 100644 framework/yii/gii/generators/crud/templates/views/_search.php create mode 100644 framework/yii/gii/generators/crud/templates/views/_view.php create mode 100644 framework/yii/gii/generators/crud/templates/views/create.php create mode 100644 framework/yii/gii/generators/crud/templates/views/index-grid.php create mode 100644 framework/yii/gii/generators/crud/templates/views/index.php create mode 100644 framework/yii/gii/generators/crud/templates/views/update.php create mode 100644 framework/yii/gii/generators/crud/templates/views/view.php diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 2b6697d..7029e7c 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -7,6 +7,7 @@ namespace yii\gii\generators\crud; +use yii\base\Model; use yii\db\ActiveRecord; use yii\gii\CodeFile; use yii\web\Controller; @@ -21,6 +22,9 @@ class Generator extends \yii\gii\Generator public $modelClass; public $controllerID; public $baseControllerClass = 'yii\web\Controller'; + public $indexWidgetType = 'grid'; + public $enableSearch = true; + public $searchModelClass; public function getName() { @@ -36,9 +40,9 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), array( - array('modelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'), - array('modelClass, controllerID, baseControllerClass', 'required'), - array('modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), + array('modelClass, searchModelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'), + array('modelClass, searchModelClass, controllerID, baseControllerClass', 'required'), + array('modelClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())), array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'), array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), @@ -52,6 +56,9 @@ class Generator extends \yii\gii\Generator 'modelClass' => 'Model Class', 'controllerID' => 'Controller ID', 'baseControllerClass' => 'Base Controller Class', + 'indexWidgetType' => 'Widget Used in Index Page', + 'enableSearch' => 'Enable Search', + 'searchModelClass' => 'Search Model Class', )); } @@ -72,6 +79,13 @@ class Generator extends \yii\gii\Generator ', 'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from. You should provide a fully qualified class name, e.g., yii\web\Controller.', + 'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models. + You may choose either GridView or ListView', + 'enableSearch' => 'Whether to enable the search functionality on the index page. When search is enabled, + a search form will be displayed on the index page, and the index page will display the search results.', + 'searchModelClass' => 'This is the class representing the data being collecting in the search form. + A fully qualified namespaced class name is required, e.g., app\models\PostSearchForm. + This is only used when search is enabled.', ); } @@ -87,7 +101,7 @@ class Generator extends \yii\gii\Generator */ public function stickyAttributes() { - return array('baseControllerClass'); + return array('baseControllerClass', 'indexWidgetType', 'enableSearch'); } /** diff --git a/framework/yii/gii/generators/crud/form.php b/framework/yii/gii/generators/crud/form.php index 0951695..095791f 100644 --- a/framework/yii/gii/generators/crud/form.php +++ b/framework/yii/gii/generators/crud/form.php @@ -8,3 +8,9 @@ echo $form->field($generator, 'modelClass'); echo $form->field($generator, 'controllerID'); echo $form->field($generator, 'baseControllerClass'); +echo $form->field($generator, 'indexWidgetType')->dropDownList(array( + 'grid' => 'GridView', + 'list' => 'ListView', +)); +echo $form->field($generator, 'enableSearch')->checkbox(); +echo $form->field($generator, 'searchModelClass'); diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php index f372629..47c193f 100644 --- a/framework/yii/gii/generators/crud/templates/controller.php +++ b/framework/yii/gii/generators/crud/templates/controller.php @@ -1,8 +1,180 @@ + + +class controllerClass; ?> extends baseControllerClass."\n"; ?> +{ + /** + * @var string the default layout for the views. Defaults to '//layouts/column2', meaning + * using two-column layout. See 'protected/views/layouts/column2.php'. + */ + public $layout='//layouts/column2'; + + /** + * @return array action filters + */ + public function filters() + { + return array( + 'accessControl', // perform access control for CRUD operations + 'postOnly + delete', // we only allow deletion via POST request + ); + } + + /** + * Specifies the access control rules. + * This method is used by the 'accessControl' filter. + * @return array access control rules + */ + public function accessRules() + { + return array( + array('allow', // allow all users to perform 'index' and 'view' actions + 'actions'=>array('index','view'), + 'users'=>array('*'), + ), + array('allow', // allow authenticated user to perform 'create' and 'update' actions + 'actions'=>array('create','update'), + 'users'=>array('@'), + ), + array('allow', // allow admin user to perform 'admin' and 'delete' actions + 'actions'=>array('admin','delete'), + 'users'=>array('admin'), + ), + array('deny', // deny all users + 'users'=>array('*'), + ), + ); + } + + /** + * Displays a particular model. + * @param integer $id the ID of the model to be displayed + */ + public function actionView($id) + { + $this->render('view',array( + 'model'=>$this->loadModel($id), + )); + } + + /** + * Creates a new model. + * If creation is successful, the browser will be redirected to the 'view' page. + */ + public function actionCreate() + { + $model=new modelClass; ?>; + + // Uncomment the following line if AJAX validation is needed + // $this->performAjaxValidation($model); + + if(isset($_POST['modelClass; ?>'])) + { + $model->attributes=$_POST['modelClass; ?>']; + if($model->save()) + $this->redirect(array('view','id'=>$model->tableSchema->primaryKey; ?>)); + } + + $this->render('create',array( + 'model'=>$model, + )); + } + + /** + * Updates a particular model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id the ID of the model to be updated + */ + public function actionUpdate($id) + { + $model=$this->loadModel($id); + + // Uncomment the following line if AJAX validation is needed + // $this->performAjaxValidation($model); + + if(isset($_POST['modelClass; ?>'])) + { + $model->attributes=$_POST['modelClass; ?>']; + if($model->save()) + $this->redirect(array('view','id'=>$model->tableSchema->primaryKey; ?>)); + } + + $this->render('update',array( + 'model'=>$model, + )); + } + + /** + * Deletes a particular model. + * If deletion is successful, the browser will be redirected to the 'admin' page. + * @param integer $id the ID of the model to be deleted + */ + public function actionDelete($id) + { + $this->loadModel($id)->delete(); + + // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser + if(!isset($_GET['ajax'])) + $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin')); + } + + /** + * Lists all models. + */ + public function actionIndex() + { + $dataProvider=new CActiveDataProvider('modelClass; ?>'); + $this->render('index',array( + 'dataProvider'=>$dataProvider, + )); + } + + /** + * Manages all models. + */ + public function actionAdmin() + { + $model=new modelClass; ?>('search'); + $model->unsetAttributes(); // clear any default values + if(isset($_GET['modelClass; ?>'])) + $model->attributes=$_GET['modelClass; ?>']; + + $this->render('admin',array( + 'model'=>$model, + )); + } + + /** + * Returns the data model based on the primary key given in the GET variable. + * If the data model is not found, an HTTP exception will be raised. + * @param integer $id the ID of the model to be loaded + * @return modelClass; ?> the loaded model + * @throws CHttpException + */ + public function loadModel($id) + { + $model=modelClass; ?>::model()->findByPk($id); + if($model===null) + throw new CHttpException(404,'The requested page does not exist.'); + return $model; + } + + /** + * Performs the AJAX validation. + * @param modelClass; ?> $model the model to be validated + */ + protected function performAjaxValidation($model) + { + if(isset($_POST['ajax']) && $_POST['ajax']==='class2id($this->modelClass); ?>-form') + { + echo CActiveForm::validate($model); + Yii::app()->end(); + } + } +} diff --git a/framework/yii/gii/generators/crud/templates/model.php b/framework/yii/gii/generators/crud/templates/model.php deleted file mode 100644 index f372629..0000000 --- a/framework/yii/gii/generators/crud/templates/model.php +++ /dev/null @@ -1,8 +0,0 @@ - + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ +/* @var $form CActiveForm */ +?> + +
+ +beginWidget('CActiveForm', array( + 'id'=>'".$this->class2id($this->modelClass)."-form', + // Please note: When you enable ajax validation, make sure the corresponding + // controller action is handling ajax validation correctly. + // There is a call to performAjaxValidation() commented in generated controller code. + // See class documentation of CActiveForm for details on this. + 'enableAjaxValidation'=>false, +)); ?>\n"; ?> + +

Fields with * are required.

+ + errorSummary(\$model); ?>\n"; ?> + +tableSchema->columns as $column) +{ + if($column->autoIncrement) + continue; +?> +
+ generateActiveLabel($this->modelClass,$column)."; ?>\n"; ?> + generateActiveField($this->modelClass,$column)."; ?>\n"; ?> + error(\$model,'{$column->name}'); ?>\n"; ?> +
+ + +
+ isNewRecord ? 'Create' : 'Save'); ?>\n"; ?> +
+ +endWidget(); ?>\n"; ?> + +
\ No newline at end of file diff --git a/framework/yii/gii/generators/crud/templates/views/_search.php b/framework/yii/gii/generators/crud/templates/views/_search.php new file mode 100644 index 0000000..a9679f1 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/_search.php @@ -0,0 +1,38 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ +/* @var $form CActiveForm */ +?> + +
+ +beginWidget('CActiveForm', array( + 'action'=>Yii::app()->createUrl(\$this->route), + 'method'=>'get', +)); ?>\n"; ?> + +tableSchema->columns as $column): ?> +generateInputField($this->modelClass,$column); + if(strpos($field,'password')!==false) + continue; +?> +
+ label(\$model,'{$column->name}'); ?>\n"; ?> + generateActiveField($this->modelClass,$column)."; ?>\n"; ?> +
+ + +
+ \n"; ?> +
+ +endWidget(); ?>\n"; ?> + +
\ No newline at end of file diff --git a/framework/yii/gii/generators/crud/templates/views/_view.php b/framework/yii/gii/generators/crud/templates/views/_view.php new file mode 100644 index 0000000..0f11051 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/_view.php @@ -0,0 +1,31 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $data getModelClass(); ?> */ +?> + +
+ +getAttributeLabel('{$this->tableSchema->primaryKey}')); ?>:\n"; +echo "\t{$this->tableSchema->primaryKey}), array('view', 'id'=>\$data->{$this->tableSchema->primaryKey})); ?>\n\t
\n\n"; +$count=0; +foreach($this->tableSchema->columns as $column) +{ + if($column->isPrimaryKey) + continue; + if(++$count==7) + echo "\tgetAttributeLabel('{$column->name}')); ?>:\n"; + echo "\t{$column->name}); ?>\n\t
\n\n"; +} +if($count>=7) + echo "\t*/ ?>\n"; +?> + +
\ No newline at end of file diff --git a/framework/yii/gii/generators/crud/templates/views/create.php b/framework/yii/gii/generators/crud/templates/views/create.php new file mode 100644 index 0000000..9fd3ccf --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/create.php @@ -0,0 +1,27 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ + +pluralize($this->class2name($this->modelClass)); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + 'Create', +);\n"; +?> + +$this->menu=array( + array('label'=>'List modelClass; ?>', 'url'=>array('index')), + array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), +); +?> + +

Create modelClass; ?>

+ +renderPartial('_form', array('model'=>\$model)); ?>"; ?> diff --git a/framework/yii/gii/generators/crud/templates/views/index-grid.php b/framework/yii/gii/generators/crud/templates/views/index-grid.php new file mode 100644 index 0000000..1884515 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/index-grid.php @@ -0,0 +1,73 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ + +pluralize($this->class2name($this->modelClass)); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + 'Manage', +);\n"; +?> + +$this->menu=array( + array('label'=>'List modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create modelClass; ?>', 'url'=>array('create')), +); + +Yii::app()->clientScript->registerScript('search', " +$('.search-button').click(function(){ + $('.search-form').toggle(); + return false; +}); +$('.search-form form').submit(function(){ + $('#class2id($this->modelClass); ?>-grid').yiiGridView('update', { + data: $(this).serialize() + }); + return false; +}); +"); +?> + +

Manage pluralize($this->class2name($this->modelClass)); ?>

+ +

+You may optionally enter a comparison operator (<, <=, >, >=, <> +or =) at the beginning of each of your search values to specify how the comparison should be done. +

+ +'search-button')); ?>"; ?> + + + + $this->widget('zii.widgets.grid.CGridView', array( + 'id'=>'class2id($this->modelClass); ?>-grid', + 'dataProvider'=>$model->search(), + 'filter'=>$model, + 'columns'=>array( +tableSchema->columns as $column) +{ + if(++$count==7) + echo "\t\t/*\n"; + echo "\t\t'".$column->name."',\n"; +} +if($count>=7) + echo "\t\t*/\n"; +?> + array( + 'class'=>'CButtonColumn', + ), + ), +)); ?> diff --git a/framework/yii/gii/generators/crud/templates/views/index.php b/framework/yii/gii/generators/crud/templates/views/index.php new file mode 100644 index 0000000..a115251 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/index.php @@ -0,0 +1,29 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $dataProvider CActiveDataProvider */ + +pluralize($this->class2name($this->modelClass)); +echo "\$this->breadcrumbs=array( + '$label', +);\n"; +?> + +$this->menu=array( + array('label'=>'Create modelClass; ?>', 'url'=>array('create')), + array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), +); +?> + +

+ + $this->widget('zii.widgets.CListView', array( + 'dataProvider'=>$dataProvider, + 'itemView'=>'_view', +)); ?> diff --git a/framework/yii/gii/generators/crud/templates/views/update.php b/framework/yii/gii/generators/crud/templates/views/update.php new file mode 100644 index 0000000..4adc72d --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/update.php @@ -0,0 +1,31 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ + +guessNameColumn($this->tableSchema->columns); +$label=$this->pluralize($this->class2name($this->modelClass)); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + \$model->{$nameColumn}=>array('view','id'=>\$model->{$this->tableSchema->primaryKey}), + 'Update', +);\n"; +?> + +$this->menu=array( + array('label'=>'List modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create modelClass; ?>', 'url'=>array('create')), + array('label'=>'View modelClass; ?>', 'url'=>array('view', 'id'=>$model->tableSchema->primaryKey; ?>)), + array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), +); +?> + +

Update modelClass." {$this->tableSchema->primaryKey}; ?>"; ?>

+ +renderPartial('_form', array('model'=>\$model)); ?>"; ?> \ No newline at end of file diff --git a/framework/yii/gii/generators/crud/templates/views/view.php b/framework/yii/gii/generators/crud/templates/views/view.php new file mode 100644 index 0000000..3363130 --- /dev/null +++ b/framework/yii/gii/generators/crud/templates/views/view.php @@ -0,0 +1,39 @@ + + +/* @var $this getControllerClass(); ?> */ +/* @var $model getModelClass(); ?> */ + +guessNameColumn($this->tableSchema->columns); +$label=$this->pluralize($this->class2name($this->modelClass)); +echo "\$this->breadcrumbs=array( + '$label'=>array('index'), + \$model->{$nameColumn}, +);\n"; +?> + +$this->menu=array( + array('label'=>'List modelClass; ?>', 'url'=>array('index')), + array('label'=>'Create modelClass; ?>', 'url'=>array('create')), + array('label'=>'Update modelClass; ?>', 'url'=>array('update', 'id'=>$model->tableSchema->primaryKey; ?>)), + array('label'=>'Delete modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->tableSchema->primaryKey; ?>),'confirm'=>'Are you sure you want to delete this item?')), + array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), +); +?> + +

View modelClass." #{$this->tableSchema->primaryKey}; ?>"; ?>

+ + $this->widget('zii.widgets.CDetailView', array( + 'data'=>$model, + 'attributes'=>array( +tableSchema->columns as $column) + echo "\t\t'".$column->name."',\n"; +?> + ), +)); ?> diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php index c61d5d0..bf05e84 100644 --- a/framework/yii/gii/views/default/view.php +++ b/framework/yii/gii/views/default/view.php @@ -30,6 +30,7 @@ foreach ($generator->templates as $name => $path) { "$id-generator", + 'successCssClass' => '', 'fieldConfig' => array('class' => ActiveField::className()), )); ?>
From 7f4e02cb4599cfc17f4f3c3a64e74f452a3994f7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 3 Sep 2013 19:36:23 -0400 Subject: [PATCH 057/156] crud generator WIP --- .../yii/gii/generators/controller/Generator.php | 2 +- framework/yii/gii/generators/crud/Generator.php | 85 +++++++++++++++++++--- .../generators/crud/templates/views/index-grid.php | 73 ------------------- 3 files changed, 77 insertions(+), 83 deletions(-) delete mode 100644 framework/yii/gii/generators/crud/templates/views/index-grid.php diff --git a/framework/yii/gii/generators/controller/Generator.php b/framework/yii/gii/generators/controller/Generator.php index c57b2b2..9de9c17 100644 --- a/framework/yii/gii/generators/controller/Generator.php +++ b/framework/yii/gii/generators/controller/Generator.php @@ -78,7 +78,7 @@ class Generator extends \yii\gii\Generator 'baseClass' => 'Base Class', 'controller' => 'Controller ID', 'actions' => 'Action IDs', - 'ns' => 'Namespace', + 'ns' => 'Controller Namespace', ); } diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 7029e7c..983dbc7 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -7,9 +7,11 @@ namespace yii\gii\generators\crud; +use Yii; use yii\base\Model; use yii\db\ActiveRecord; use yii\gii\CodeFile; +use yii\helpers\Inflector; use yii\web\Controller; /** @@ -41,12 +43,15 @@ class Generator extends \yii\gii\Generator { return array_merge(parent::rules(), array( array('modelClass, searchModelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'), - array('modelClass, searchModelClass, controllerID, baseControllerClass', 'required'), + array('modelClass, controllerID, baseControllerClass', 'required'), array('modelClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())), array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'), array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())), + array('enableSearch', 'boolean'), + array('indexWidgetType', 'in', 'range' => array('grid', 'list')), + array('searchModelClass', 'validateSearchModelClass'), )); } @@ -104,6 +109,13 @@ class Generator extends \yii\gii\Generator return array('baseControllerClass', 'indexWidgetType', 'enableSearch'); } + public function validateSearchModelClass() + { + if ($this->enableSearch && empty($this->searchModelClass)) { + $this->addError('searchModelClass', 'Search Model Class cannot be empty.'); + } + } + /** * @inheritdoc */ @@ -111,20 +123,75 @@ class Generator extends \yii\gii\Generator { $files = array(); $files[] = new CodeFile( - $this->controllerFile, + $this->getControllerFile(), $this->render('controller.php') ); + $viewPath = $this->getViewPath(); - $files = scandir($this->getTemplatePath()); - foreach ($files as $file) { - if (is_file($templatePath . '/' . $file) && CFileHelper::getExtension($file) === 'php' && $file !== 'controller.php') { - $files[] = new CodeFile( - $this->viewPath . DIRECTORY_SEPARATOR . $file, - $this->render($templatePath . '/' . $file) - ); + $templatePath = $this->getTemplatePath() . '/views'; + foreach (scandir($templatePath) as $file) { + if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { + $files[] = new CodeFile("$viewPath/$file", $this->render("views/$file")); } } + if ($this->enableSearch) { + + } + return $files; } + + + /** + * @return string the controller class name without the namespace part. + */ + public function getControllerClass() + { + return Inflector::id2camel($this->getControllerID()) . 'Controller'; + } + + /** + * @return string the controller ID (without the module ID prefix) + */ + public function getControllerID() + { + if (($pos = strrpos($this->controllerID, '/')) !== false) { + return substr($this->controllerID, $pos + 1); + } else { + return $this->controllerID; + } + } + + /** + * @return \yii\base\Module the module that the new controller belongs to + */ + public function getModule() + { + if (($pos = strpos($this->controllerID, '/')) !== false) { + $id = substr($this->controllerID, 0, $pos); + if (($module = Yii::$app->getModule($id)) !== null) { + return $module; + } + } + return Yii::$app; + } + + /** + * @return string the controller class file path + */ + public function getControllerFile() + { + $module = $this->getModule(); + return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php'; + } + + /** + * @return string the action view file path + */ + public function getViewPath() + { + $module = $this->getModule(); + return $module->getViewPath() . '/' . $this->getControllerID() ; + } } diff --git a/framework/yii/gii/generators/crud/templates/views/index-grid.php b/framework/yii/gii/generators/crud/templates/views/index-grid.php deleted file mode 100644 index 1884515..0000000 --- a/framework/yii/gii/generators/crud/templates/views/index-grid.php +++ /dev/null @@ -1,73 +0,0 @@ - - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ - -pluralize($this->class2name($this->modelClass)); -echo "\$this->breadcrumbs=array( - '$label'=>array('index'), - 'Manage', -);\n"; -?> - -$this->menu=array( - array('label'=>'List modelClass; ?>', 'url'=>array('index')), - array('label'=>'Create modelClass; ?>', 'url'=>array('create')), -); - -Yii::app()->clientScript->registerScript('search', " -$('.search-button').click(function(){ - $('.search-form').toggle(); - return false; -}); -$('.search-form form').submit(function(){ - $('#class2id($this->modelClass); ?>-grid').yiiGridView('update', { - data: $(this).serialize() - }); - return false; -}); -"); -?> - -

Manage pluralize($this->class2name($this->modelClass)); ?>

- -

-You may optionally enter a comparison operator (<, <=, >, >=, <> -or =) at the beginning of each of your search values to specify how the comparison should be done. -

- -'search-button')); ?>"; ?> - - - - $this->widget('zii.widgets.grid.CGridView', array( - 'id'=>'class2id($this->modelClass); ?>-grid', - 'dataProvider'=>$model->search(), - 'filter'=>$model, - 'columns'=>array( -tableSchema->columns as $column) -{ - if(++$count==7) - echo "\t\t/*\n"; - echo "\t\t'".$column->name."',\n"; -} -if($count>=7) - echo "\t\t*/\n"; -?> - array( - 'class'=>'CButtonColumn', - ), - ), -)); ?> From 8c7d0aebe108c4bda29a31be6d1e578257c2b728 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 4 Sep 2013 15:44:31 +0400 Subject: [PATCH 058/156] Added note about the fact that PHP SSL extension is required to install all Composer dependencies --- apps/advanced/README.md | 2 ++ apps/basic/README.md | 2 ++ apps/benchmark/README.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/apps/advanced/README.md b/apps/advanced/README.md index 7055360..6860c11 100644 --- a/apps/advanced/README.md +++ b/apps/advanced/README.md @@ -74,6 +74,8 @@ You can then install the application using the following command: php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced ~~~ +Note that in order to install some dependencies you must have `php_openssl` extension enabled. + ### Install from an Archive File diff --git a/apps/basic/README.md b/apps/basic/README.md index b5e1ec2..aaa7fba 100644 --- a/apps/basic/README.md +++ b/apps/basic/README.md @@ -56,6 +56,8 @@ php composer.phar create-project --stability=dev yiisoft/yii2-app-basic yii-basi Now you should be able to access the application using the URL `http://localhost/yii-basic/web/`, assuming `yii-basic` is directly under the document root of your Web server. +Note that in order to install some dependencies you must have `php_openssl` extension enabled. + ### Install from an Archive File diff --git a/apps/benchmark/README.md b/apps/benchmark/README.md index 2aeb0ae..2d5871a 100644 --- a/apps/benchmark/README.md +++ b/apps/benchmark/README.md @@ -54,3 +54,5 @@ http://localhost/yii-benchmark/index.php/site/hello In the above, we assume `yii-benchmark` is directly under the document root of your Web server. +Note that in order to install some dependencies you must have `php_openssl` extension enabled. + From f877de144b273ca0f4278e8cfcda61b05feaf334 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 4 Sep 2013 15:55:32 +0400 Subject: [PATCH 059/156] Removed DebugTarget reference from advanced app template configs --- apps/advanced/environments/dev/backend/config/main-local.php | 9 --------- apps/advanced/environments/dev/frontend/config/main-local.php | 9 --------- apps/advanced/frontend/config/main.php | 1 + 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/apps/advanced/environments/dev/backend/config/main-local.php b/apps/advanced/environments/dev/backend/config/main-local.php index fdc131d..6ad664a 100644 --- a/apps/advanced/environments/dev/backend/config/main-local.php +++ b/apps/advanced/environments/dev/backend/config/main-local.php @@ -5,13 +5,4 @@ return array( // 'class' => 'yii\debug\Module', // ), ), - 'components' => array( - 'log' => array( - 'targets' => array( -// array( -// 'class' => 'yii\log\DebugTarget', -// ) - ), - ), - ), ); diff --git a/apps/advanced/environments/dev/frontend/config/main-local.php b/apps/advanced/environments/dev/frontend/config/main-local.php index f7d77e3..5ad1e61 100644 --- a/apps/advanced/environments/dev/frontend/config/main-local.php +++ b/apps/advanced/environments/dev/frontend/config/main-local.php @@ -5,13 +5,4 @@ return array( // 'class' => 'yii\debug\Module', // ), ), - 'components' => array( - 'log' => array( - 'targets' => array( -// array( -// 'class' => 'yii\log\DebugTarget', -// ) - ), - ), - ), ); diff --git a/apps/advanced/frontend/config/main.php b/apps/advanced/frontend/config/main.php index b9bfdae..ca4a734 100644 --- a/apps/advanced/frontend/config/main.php +++ b/apps/advanced/frontend/config/main.php @@ -14,6 +14,7 @@ return array( 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'controllerNamespace' => 'frontend\controllers', 'modules' => array( + 'gii' => 'yii\gii\Module' ), 'components' => array( 'db' => $params['components.db'], From df85be3622c64d887ff43f0f239eef1b13d465c7 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 4 Sep 2013 16:18:15 +0400 Subject: [PATCH 060/156] Added debug to preload in advanced app template --- apps/advanced/environments/dev/backend/config/main-local.php | 3 +++ apps/advanced/environments/dev/frontend/config/main-local.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/apps/advanced/environments/dev/backend/config/main-local.php b/apps/advanced/environments/dev/backend/config/main-local.php index 6ad664a..2689ed1 100644 --- a/apps/advanced/environments/dev/backend/config/main-local.php +++ b/apps/advanced/environments/dev/backend/config/main-local.php @@ -1,5 +1,8 @@ array( + //'debug', + ), 'modules' => array( // 'debug' => array( // 'class' => 'yii\debug\Module', diff --git a/apps/advanced/environments/dev/frontend/config/main-local.php b/apps/advanced/environments/dev/frontend/config/main-local.php index 5ad1e61..35d10ed 100644 --- a/apps/advanced/environments/dev/frontend/config/main-local.php +++ b/apps/advanced/environments/dev/frontend/config/main-local.php @@ -1,5 +1,8 @@ array( + //'debug', + ), 'modules' => array( // 'debug' => array( // 'class' => 'yii\debug\Module', From 8c753676db71adaa62ad552cd0b0a8a8ba2ff125 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 4 Sep 2013 14:27:20 +0200 Subject: [PATCH 061/156] Exception about missing class in class file only when YII_DEBUG See discussion about exceptions in autoloader here: https://groups.google.com/forum/?fromgroups#!topic/php-fig/kRTVRSIJ0qE --- framework/yii/YiiBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/YiiBase.php b/framework/yii/YiiBase.php index c96a969..f04903d 100644 --- a/framework/yii/YiiBase.php +++ b/framework/yii/YiiBase.php @@ -335,7 +335,7 @@ class YiiBase include($classFile); - if (!class_exists($className, false) && !interface_exists($className, false) && + if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) && (!function_exists('trait_exists') || !trait_exists($className, false))) { throw new UnknownClassException("Unable to find '$className' in file: $classFile"); } From b77ea4dcb0f9270ff353bfe2c8e0fcc67af757e9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 4 Sep 2013 21:03:10 -0400 Subject: [PATCH 062/156] Allow generator to define actions. --- .../yii/gii/controllers/DefaultController.php | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/framework/yii/gii/controllers/DefaultController.php b/framework/yii/gii/controllers/DefaultController.php index 969096b..305ef35 100644 --- a/framework/yii/gii/controllers/DefaultController.php +++ b/framework/yii/gii/controllers/DefaultController.php @@ -7,6 +7,7 @@ namespace yii\gii\controllers; +use Yii; use yii\web\Controller; use yii\web\HttpException; @@ -86,6 +87,26 @@ class DefaultController extends Controller throw new HttpException(404, "Code file not found: $file"); } + /** + * Runs an action defined in the generator. + * Given an action named "xyz", the method "actionXyz()" in the generator will be called. + * If the method does not exist, a 400 HTTP exception will be thrown. + * @param string $id the ID of the generator + * @param string $name the action name + * @return mixed the result of the action. + * @throws HttpException if the action method does not exist. + */ + public function actionAction($id, $name) + { + $generator = $this->loadGenerator($id); + $method = 'action' . $name; + if (method_exists($generator, $method)) { + return $generator->$method(); + } else { + throw new HttpException(400, "Unknown generator action: $name"); + } + } + public function createUrl($route, $params = array()) { if (!isset($params['id']) && $this->generator !== null) { @@ -99,6 +120,18 @@ class DefaultController extends Controller return parent::createUrl($route, $params); } + public function createActionUrl($name, $params = array()) + { + foreach ($this->module->generators as $id => $generator) { + if ($generator === $this->generator) { + $params['id'] = $id; + break; + } + } + $params['name'] = $name; + return parent::createUrl('action', $params); + } + /** * Loads the generator with the specified ID. * @param string $id the ID of the generator to be loaded. From c306fccd46d9f37dbcfeb1673f42ccdcf96ea146 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 12:36:38 +0200 Subject: [PATCH 063/156] fixed FileValidator after UploadedFile refactoring fixes #845 --- framework/yii/validators/FileValidator.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/framework/yii/validators/FileValidator.php b/framework/yii/validators/FileValidator.php index fbd432d..e2880af 100644 --- a/framework/yii/validators/FileValidator.php +++ b/framework/yii/validators/FileValidator.php @@ -135,7 +135,7 @@ class FileValidator extends Validator return; } foreach ($files as $i => $file) { - if (!$file instanceof UploadedFile || $file->getError() == UPLOAD_ERR_NO_FILE) { + if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) { unset($files[$i]); } } @@ -152,7 +152,7 @@ class FileValidator extends Validator } } else { $file = $object->$attribute; - if ($file instanceof UploadedFile && $file->getError() != UPLOAD_ERR_NO_FILE) { + if ($file instanceof UploadedFile && $file->error != UPLOAD_ERR_NO_FILE) { $this->validateFile($object, $attribute, $file); } else { $this->addError($object, $attribute, $this->uploadRequired); @@ -168,37 +168,37 @@ class FileValidator extends Validator */ protected function validateFile($object, $attribute, $file) { - switch ($file->getError()) { + switch ($file->error) { case UPLOAD_ERR_OK: - if ($this->maxSize !== null && $file->getSize() > $this->maxSize) { - $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->getName(), '{limit}' => $this->getSizeLimit())); + if ($this->maxSize !== null && $file->size > $this->maxSize) { + $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->name, '{limit}' => $this->getSizeLimit())); } - if ($this->minSize !== null && $file->getSize() < $this->minSize) { - $this->addError($object, $attribute, $this->tooSmall, array('{file}' => $file->getName(), '{limit}' => $this->minSize)); + if ($this->minSize !== null && $file->size < $this->minSize) { + $this->addError($object, $attribute, $this->tooSmall, array('{file}' => $file->name, '{limit}' => $this->minSize)); } - if (!empty($this->types) && !in_array(strtolower(pathinfo($file->getName(), PATHINFO_EXTENSION)), $this->types, true)) { - $this->addError($object, $attribute, $this->wrongType, array('{file}' => $file->getName(), '{extensions}' => implode(', ', $this->types))); + if (!empty($this->types) && !in_array(strtolower(pathinfo($file->name, PATHINFO_EXTENSION)), $this->types, true)) { + $this->addError($object, $attribute, $this->wrongType, array('{file}' => $file->name, '{extensions}' => implode(', ', $this->types))); } break; case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: - $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->getName(), '{limit}' => $this->getSizeLimit())); + $this->addError($object, $attribute, $this->tooBig, array('{file}' => $file->name, '{limit}' => $this->getSizeLimit())); break; case UPLOAD_ERR_PARTIAL: $this->addError($object, $attribute, $this->message); - Yii::warning('File was only partially uploaded: ' . $file->getName(), __METHOD__); + Yii::warning('File was only partially uploaded: ' . $file->name, __METHOD__); break; case UPLOAD_ERR_NO_TMP_DIR: $this->addError($object, $attribute, $this->message); - Yii::warning('Missing the temporary folder to store the uploaded file: ' . $file->getName(), __METHOD__); + Yii::warning('Missing the temporary folder to store the uploaded file: ' . $file->name, __METHOD__); break; case UPLOAD_ERR_CANT_WRITE: $this->addError($object, $attribute, $this->message); - Yii::warning('Failed to write the uploaded file to disk: ' . $file->getName(), __METHOD__); + Yii::warning('Failed to write the uploaded file to disk: ' . $file->name, __METHOD__); break; case UPLOAD_ERR_EXTENSION: $this->addError($object, $attribute, $this->message); - Yii::warning('File upload was stopped by some PHP extension: ' . $file->getName(), __METHOD__); + Yii::warning('File upload was stopped by some PHP extension: ' . $file->name, __METHOD__); break; default: break; From d4995ddc57554a836563cc1545263c1987384546 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 12:40:16 +0200 Subject: [PATCH 064/156] updated php doc --- framework/yii/db/DataReader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/DataReader.php b/framework/yii/db/DataReader.php index d18de4c..f2990c1 100644 --- a/framework/yii/db/DataReader.php +++ b/framework/yii/db/DataReader.php @@ -40,7 +40,7 @@ use yii\base\InvalidCallException; * for more details about possible fetch mode. * * @property integer $columnCount The number of columns in the result set. This property is read-only. - * @property mixed $fetchMode Fetch mode. This property is write-only. + * @property integer $fetchMode Fetch mode. This property is write-only. * @property boolean $isClosed Whether the reader is closed or not. This property is read-only. * @property integer $rowCount Number of rows contained in the result. This property is read-only. * From 8976b7cbd877473501455bf3aed4ffa40739817e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 12:48:52 +0200 Subject: [PATCH 065/156] moved MSSQL specific property to mssql TableSchema --- framework/yii/db/TableSchema.php | 6 ------ framework/yii/db/mssql/Schema.php | 2 +- framework/yii/db/mssql/TableSchema.php | 23 +++++++++++++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 framework/yii/db/mssql/TableSchema.php diff --git a/framework/yii/db/TableSchema.php b/framework/yii/db/TableSchema.php index d599388..910061d 100644 --- a/framework/yii/db/TableSchema.php +++ b/framework/yii/db/TableSchema.php @@ -21,12 +21,6 @@ use yii\base\InvalidParamException; class TableSchema extends Object { /** - * @var string name of the catalog (database) that this table belongs to. - * Defaults to null, meaning no catalog (or the current database). - * This property is only meaningful for MSSQL. - */ - public $catalogName; - /** * @var string name of the schema that this table belongs to. */ public $schemaName; diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php index ad0f7d4..4048fe2 100644 --- a/framework/yii/db/mssql/Schema.php +++ b/framework/yii/db/mssql/Schema.php @@ -7,7 +7,7 @@ namespace yii\db\mssql; -use yii\db\TableSchema; +use yii\db\mssql\TableSchema; use yii\db\ColumnSchema; /** diff --git a/framework/yii/db/mssql/TableSchema.php b/framework/yii/db/mssql/TableSchema.php new file mode 100644 index 0000000..67ad85c --- /dev/null +++ b/framework/yii/db/mssql/TableSchema.php @@ -0,0 +1,23 @@ + + * @since 2.0 + */ +class TableSchema extends \yii\db\TableSchema +{ + /** + * @var string name of the catalog (database) that this table belongs to. + * Defaults to null, meaning no catalog (or the current database). + */ + public $catalogName; +} From 326e98a41ca81edc902dc0a7919f507f4ad88e78 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 5 Sep 2013 08:31:04 -0400 Subject: [PATCH 066/156] doc fix. --- framework/yii/base/Component.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index cc3525e..a7ddd29 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -40,6 +40,7 @@ class Component extends Object * @param string $name the property name * @return mixed the property value or the value of a behavior's property * @throws UnknownPropertyException if the property is not defined + * @throws InvalidCallException if the property is write-only. * @see __set */ public function __get($name) From 267f2d784946230a275a8735fd2750aa6d3e0b0c Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 16:47:05 +0200 Subject: [PATCH 067/156] fixed docs in db Schema classes --- framework/yii/db/Schema.php | 2 +- framework/yii/db/mssql/Schema.php | 4 +--- framework/yii/db/mysql/Schema.php | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 4fd1cd1..7b19066 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -216,7 +216,7 @@ abstract class Schema extends Object * This method should be overridden by child classes in order to support this feature * because the default implementation simply throws an exception. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @return array all table names in the database. The names have NO the schema name prefix. + * @return array all table names in the database. The names have NO schema name prefix. * @throws NotSupportedException if this method is called */ protected function findTableNames($schema = '') diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php index 4048fe2..5f53d8e 100644 --- a/framework/yii/db/mssql/Schema.php +++ b/framework/yii/db/mssql/Schema.php @@ -332,10 +332,8 @@ SQL; /** * Returns all table names in the database. - * This method should be overridden by child classes in order to support this feature - * because the default implementation simply throws an exception. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @return array all table names in the database. The names have NO the schema name prefix. + * @return array all table names in the database. The names have NO schema name prefix. */ protected function findTableNames($schema = '') { diff --git a/framework/yii/db/mysql/Schema.php b/framework/yii/db/mysql/Schema.php index 225ef38..998f49a 100644 --- a/framework/yii/db/mysql/Schema.php +++ b/framework/yii/db/mysql/Schema.php @@ -236,10 +236,8 @@ class Schema extends \yii\db\Schema /** * Returns all table names in the database. - * This method should be overridden by child classes in order to support this feature - * because the default implementation simply throws an exception. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. - * @return array all table names in the database. The names have NO the schema name prefix. + * @return array all table names in the database. The names have NO schema name prefix. */ protected function findTableNames($schema = '') { From be67559fe155bedff60fdf194e066dac10de778b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 16:49:11 +0200 Subject: [PATCH 068/156] added CUBIRD DB Schema class --- framework/yii/db/Connection.php | 5 +- framework/yii/db/cubrid/Schema.php | 222 +++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 framework/yii/db/cubrid/Schema.php diff --git a/framework/yii/db/Connection.php b/framework/yii/db/Connection.php index 342fa15..69bf6a5 100644 --- a/framework/yii/db/Connection.php +++ b/framework/yii/db/Connection.php @@ -201,7 +201,7 @@ class Connection extends Component public $queryCache = 'cache'; /** * @var string the charset used for database connection. The property is only used - * for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset + * for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset * as specified by the database. * * Note that if you're using GBK or BIG5 then it's highly recommended to @@ -244,6 +244,7 @@ class Connection extends Component 'oci' => 'yii\db\oci\Schema', // Oracle driver 'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts 'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts + 'cubrid' => 'yii\db\cubrid\Schema', // CUBRID ); /** * @var Transaction the currently active transaction @@ -361,7 +362,7 @@ class Connection extends Component if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) { $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare); } - if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) { + if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli', 'cubrid'))) { $this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset)); } $this->trigger(self::EVENT_AFTER_OPEN); diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php new file mode 100644 index 0000000..3b60b64 --- /dev/null +++ b/framework/yii/db/cubrid/Schema.php @@ -0,0 +1,222 @@ + + * @since 2.0 + */ +class Schema extends \yii\db\Schema +{ + /** + * @var array mapping from physical column types (keys) to abstract column types (values) + * Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for + * details on data types. + */ + public $typeMap = array( + // Numeric data types + 'short' => self::TYPE_SMALLINT, + 'smallint' => self::TYPE_SMALLINT, + 'int' => self::TYPE_INTEGER, + 'integer' => self::TYPE_INTEGER, + 'bigint' => self::TYPE_BIGINT, + 'numeric' => self::TYPE_DECIMAL, + 'decimal' => self::TYPE_DECIMAL, + 'float' => self::TYPE_FLOAT, + 'real' => self::TYPE_FLOAT, + 'double' => self::TYPE_FLOAT, + 'double precision' => self::TYPE_FLOAT, + 'monetary' => self::TYPE_MONEY, + // Date/Time data types + 'date' => self::TYPE_DATE, + 'time' => self::TYPE_TIME, + 'timestamp' => self::TYPE_TIMESTAMP, + 'datetime' => self::TYPE_DATETIME, + // Bit string data types +// 'bit' => self::TYPE_BINARY, // TODO +// 'bit varying' => self::TYPE_BINARY, + // String data types + 'char' => self::TYPE_STRING, + 'varchar' => self::TYPE_STRING, + 'char varying' => self::TYPE_STRING, + 'nchar' => self::TYPE_STRING, + 'nchar varying' => self::TYPE_STRING, + 'string' => self::TYPE_STRING, + // BLOB/CLOB data types + 'blob' => self::TYPE_BINARY, + 'clob' => self::TYPE_BINARY, + // Collection data types (TODO are considered strings for now, naybe support conversion?) +// 'set' => self::TYPE_STRING, +// 'multiset' => self::TYPE_STRING, +// 'list' => self::TYPE_STRING, +// 'sequence' => self::TYPE_STRING, +// 'enum' => self::TYPE_STRING, + ); + + /** + * Quotes a table name for use in a query. + * A simple table name has no schema prefix. + * @param string $name table name + * @return string the properly quoted table name + */ + public function quoteSimpleTableName($name) + { + return strpos($name, "`") !== false ? $name : "`" . $name . "`"; + } + + /** + * Quotes a column name for use in a query. + * A simple column name has no prefix. + * @param string $name column name + * @return string the properly quoted column name + */ + public function quoteSimpleColumnName($name) + { + return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`'; + } + + /** + * Creates a query builder for the CUBRID database. + * @return QueryBuilder query builder instance + */ + public function createQueryBuilder() + { + return new QueryBuilder($this->db); + } + + /** + * Loads the metadata for the specified table. + * @param string $name table name + * @return TableSchema driver dependent table metadata. Null if the table does not exist. + */ + protected function loadTableSchema($name) + { + $this->db->open(); + $tableInfo = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name); + + if (isset($tableInfo[0]['NAME'])) { + $table = new TableSchema; + $table->name = $tableInfo[0]['NAME']; + + $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name); + $columns = $this->db->createCommand($sql)->queryAll(); + + foreach ($columns as $info) { + $column = $this->loadColumnSchema($info); + $table->columns[$column->name] = $column; + if ($column->isPrimaryKey) { + $table->primaryKey[] = $column->name; + if ($column->autoIncrement) { + $table->sequenceName = ''; + } + } + } + + $foreignKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name); + foreach($foreignKeys as $key) { + $table->foreignKeys[] = array( + $key['PKTABLE_NAME'], + $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'] + // TODO support composite foreign keys + ); + } + + return $table; + } else { + return null; + } + } + + /** + * Loads the column information into a [[ColumnSchema]] object. + * @param array $info column information + * @return ColumnSchema the column schema object + */ + protected function loadColumnSchema($info) + { + $column = new ColumnSchema; + + $column->name = $info['Field']; + $column->allowNull = $info['Null'] === 'YES'; + $column->isPrimaryKey = strpos($info['Key'], 'PRI') !== false; + $column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false; + + $column->dbType = strtolower($info['Type']); + $column->unsigned = strpos($column->dbType, 'unsigned') !== false; + + $column->type = self::TYPE_STRING; + if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) { + $type = $matches[1]; + if (isset($this->typeMap[$type])) { + $column->type = $this->typeMap[$type]; + } + if (!empty($matches[2])) { + if ($type === 'enum') { + $values = explode(',', $matches[2]); + foreach ($values as $i => $value) { + $values[$i] = trim($value, "'"); + } + $column->enumValues = $values; + } else { + $values = explode(',', $matches[2]); + $column->size = $column->precision = (int)$values[0]; + if (isset($values[1])) { + $column->scale = (int)$values[1]; + } + if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { + $column->type = 'boolean'; + } elseif ($type === 'bit' || $type === 'bit varying') { + if ($column->size > 32) { + $column->type = 'bigint'; + } elseif ($column->size === 32) { + $column->type = 'integer'; + } + } + } + } + } + + $column->phpType = $this->getColumnPhpType($column); + + if ($column->type === 'timestamp' && $info['Default'] === 'CURRENT_TIMESTAMP' || + $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' || + $column->type === 'date' && $info['Default'] === 'SYS_DATE' || + $column->type === 'time' && $info['Default'] === 'SYS_TIME') { + $column->defaultValue = new Expression($info['Default']); + } else { + $column->defaultValue = $column->typecast($info['Default']); + } + + return $column; + } + + /** + * Returns all table names in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * @return array all table names in the database. The names have NO schema name prefix. + */ + protected function findTableNames($schema = '') + { + $this->db->open(); + $tables = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE); + $tableNames = array(); + foreach($tables as $table) { + // do not list system tables + if ($table['TYPE'] !== 0) { + $tableNames[] = $table['NAME']; + } + } + return $tableNames; + } +} From f4fb2d94efef724c7aac1f098f13d8ee103eef2b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 17:29:47 +0200 Subject: [PATCH 069/156] finalizing cubrid Schema --- framework/yii/db/Command.php | 2 +- framework/yii/db/QueryBuilder.php | 2 +- framework/yii/db/cubrid/Schema.php | 22 +++++++++++----------- framework/yii/db/mysql/QueryBuilder.php | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 460cd46..c439775 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -472,7 +472,7 @@ class Command extends \yii\base\Component * ))->execute(); * ~~~ * - * Not that the values in each row must match the corresponding column names. + * Note that the values in each row must match the corresponding column names. * * @param string $table the table that new rows will be inserted into. * @param array $columns the column names diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index f67038c..00d21c0 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -134,7 +134,7 @@ class QueryBuilder extends \yii\base\Object * ))->execute(); * ~~~ * - * Not that the values in each row must match the corresponding column names. + * Note that the values in each row must match the corresponding column names. * * @param string $table the table that new rows will be inserted into. * @param array $columns the column names diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index 3b60b64..9242dc1 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -43,9 +43,6 @@ class Schema extends \yii\db\Schema 'time' => self::TYPE_TIME, 'timestamp' => self::TYPE_TIMESTAMP, 'datetime' => self::TYPE_DATETIME, - // Bit string data types -// 'bit' => self::TYPE_BINARY, // TODO -// 'bit varying' => self::TYPE_BINARY, // String data types 'char' => self::TYPE_STRING, 'varchar' => self::TYPE_STRING, @@ -56,6 +53,9 @@ class Schema extends \yii\db\Schema // BLOB/CLOB data types 'blob' => self::TYPE_BINARY, 'clob' => self::TYPE_BINARY, + // Bit string data types +// 'bit' => self::TYPE_STRING, +// 'bit varying' => self::TYPE_STRING, // Collection data types (TODO are considered strings for now, naybe support conversion?) // 'set' => self::TYPE_STRING, // 'multiset' => self::TYPE_STRING, @@ -106,7 +106,7 @@ class Schema extends \yii\db\Schema $tableInfo = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name); if (isset($tableInfo[0]['NAME'])) { - $table = new TableSchema; + $table = new TableSchema(); $table->name = $tableInfo[0]['NAME']; $sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name); @@ -145,7 +145,7 @@ class Schema extends \yii\db\Schema */ protected function loadColumnSchema($info) { - $column = new ColumnSchema; + $column = new ColumnSchema(); $column->name = $info['Field']; $column->allowNull = $info['Null'] === 'YES'; @@ -174,13 +174,13 @@ class Schema extends \yii\db\Schema if (isset($values[1])) { $column->scale = (int)$values[1]; } - if ($column->size === 1 && ($type === 'tinyint' || $type === 'bit')) { - $column->type = 'boolean'; - } elseif ($type === 'bit' || $type === 'bit varying') { - if ($column->size > 32) { - $column->type = 'bigint'; + if ($type === 'bit' || $type === 'bit varying') { + if ($column->size === 1) { + $column->type = self::TYPE_BOOLEAN; + } elseif ($column->size > 32) { + $column->type = self::TYPE_BIGINT; } elseif ($column->size === 32) { - $column->type = 'integer'; + $column->type = self::TYPE_INTEGER; } } } diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 0307abd..c7a4256 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -152,7 +152,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * ))->execute(); * ~~~ * - * Not that the values in each row must match the corresponding column names. + * Note that the values in each row must match the corresponding column names. * * @param string $table the table that new rows will be inserted into. * @param array $columns the column names From 57a91c842afe84f9651410cf7314f4c65f055b65 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 17:30:18 +0200 Subject: [PATCH 070/156] CUBRID Query builder --- framework/yii/db/cubrid/QueryBuilder.php | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 framework/yii/db/cubrid/QueryBuilder.php diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php new file mode 100644 index 0000000..28b6466 --- /dev/null +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -0,0 +1,116 @@ + + * @since 2.0 + */ +class QueryBuilder extends \yii\db\QueryBuilder +{ + /** + * @var array mapping from abstract column types (keys) to physical column types (values). + */ + public $typeMap = array( + Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_STRING => 'varchar(255)', + Schema::TYPE_TEXT => 'varchar', + Schema::TYPE_SMALLINT => 'smallint', + Schema::TYPE_INTEGER => 'int', + Schema::TYPE_BIGINT => 'bigint', + Schema::TYPE_FLOAT => 'float(7)', + Schema::TYPE_DECIMAL => 'decimal(10,0)', + Schema::TYPE_DATETIME => 'datetime', + Schema::TYPE_TIMESTAMP => 'timestamp', + Schema::TYPE_TIME => 'time', + Schema::TYPE_DATE => 'date', + Schema::TYPE_BINARY => 'blob', + Schema::TYPE_BOOLEAN => 'bit(1)', + Schema::TYPE_MONEY => 'decimal(19,4)', + ); + + /** + * Creates a SQL statement for resetting the sequence value of a table's primary key. + * The sequence will be reset such that the primary key of the next new row inserted + * will have the specified value or 1. + * @param string $tableName the name of the table whose primary key sequence will be reset + * @param mixed $value the value for the primary key of the next new row inserted. If this is not set, + * the next new row's primary key will have a value 1. + * @return string the SQL statement for resetting sequence + * @throws InvalidParamException if the table does not exist or there is no sequence associated with the table. + */ + public function resetSequence($tableName, $value = null) + { + $table = $this->db->getTableSchema($tableName); + if ($table !== null && $table->sequenceName !== null) { + $tableName = $this->db->quoteTableName($tableName); + if ($value === null) { + $key = reset($table->primaryKey); + $value = (int)$this->db->createCommand("SELECT MAX(`$key`) FROM " . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1; + } else { + $value = (int)$value; + } + return "ALTER TABLE " . $this->db->schema->quoteTableName($tableName) . " AUTO_INCREMENT=$value;"; + } elseif ($table === null) { + throw new InvalidParamException("Table not found: $tableName"); + } else { + throw new InvalidParamException("There is not sequence associated with table '$tableName'."); + } + } + + /** + * Generates a batch INSERT SQL statement. + * For example, + * + * ~~~ + * $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array( + * array('Tom', 30), + * array('Jane', 20), + * array('Linda', 25), + * ))->execute(); + * ~~~ + * + * Note that the values in each row must match the corresponding column names. + * + * @param string $table the table that new rows will be inserted into. + * @param array $columns the column names + * @param array $rows the rows to be batch inserted into the table + * @return string the batch INSERT SQL statement + */ + public function batchInsert($table, $columns, $rows) + { + if (($tableSchema = $this->db->getTableSchema($table)) !== null) { + $columnSchemas = $tableSchema->columns; + } else { + $columnSchemas = array(); + } + + foreach ($columns as $i => $name) { + $columns[$i] = $this->db->quoteColumnName($name); + } + + $values = array(); + foreach ($rows as $row) { + $vs = array(); + foreach ($row as $i => $value) { + if (!is_array($value) && isset($columnSchemas[$columns[$i]])) { + $value = $columnSchemas[$columns[$i]]->typecast($value); + } + $vs[] = is_string($value) ? $this->db->quoteValue($value) : $value; + } + $values[] = '(' . implode(', ', $vs) . ')'; + } + + return 'INSERT INTO ' . $this->db->quoteTableName($table) + . ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values); + } +} From 2387d003415647bcd20750c60b0ca7c93fcfb69e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 18:22:24 +0200 Subject: [PATCH 071/156] CUBRID added exception about wrong implementation of quoteValue --- framework/yii/db/cubrid/QueryBuilder.php | 2 +- framework/yii/db/cubrid/Schema.php | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 28b6466..2132b58 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'varchar', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index 9242dc1..f0b909b 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -7,6 +7,7 @@ namespace yii\db\cubrid; +use yii\base\NotSupportedException; use yii\db\Expression; use yii\db\TableSchema; use yii\db\ColumnSchema; @@ -87,6 +88,29 @@ class Schema extends \yii\db\Schema } /** + * Quotes a string value for use in a query. + * Note that if the parameter is not a string, it will be returned without change. + * @param string $str string to be quoted + * @return string the properly quoted string + * @see http://www.php.net/manual/en/function.PDO-quote.php + */ + public function quoteValue($str) + { + throw new NotSupportedException('quoteValue is currently broken in cubrid PDO'); + // TODO implement workaround +/* if (!is_string($str)) { + return $str; + } + + $this->db->open(); + if (($value = $this->db->pdo->quote($str)) !== false) { + return $value; + } else { // the driver doesn't support quote (e.g. oci) + return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'"; + }*/ + } + + /** * Creates a query builder for the CUBRID database. * @return QueryBuilder query builder instance */ From 825258efb865a864d51ad7a0a9f655c80f3aa470 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 5 Sep 2013 18:23:25 +0200 Subject: [PATCH 072/156] cubrid unit tests WIP --- tests/unit/data/config.php | 6 ++ tests/unit/data/cubrid.sql | 100 +++++++++++++++++++++ tests/unit/framework/db/QueryBuilderTest.php | 3 + .../framework/db/cubrid/CubridActiveRecordTest.php | 13 +++ .../unit/framework/db/cubrid/CubridCommandTest.php | 13 +++ .../framework/db/cubrid/CubridConnectionTest.php | 13 +++ .../framework/db/cubrid/CubridQueryBuilderTest.php | 80 +++++++++++++++++ tests/unit/framework/db/cubrid/CubridQueryTest.php | 13 +++ 8 files changed, 241 insertions(+) create mode 100644 tests/unit/data/cubrid.sql create mode 100644 tests/unit/framework/db/cubrid/CubridActiveRecordTest.php create mode 100644 tests/unit/framework/db/cubrid/CubridCommandTest.php create mode 100644 tests/unit/framework/db/cubrid/CubridConnectionTest.php create mode 100644 tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php create mode 100644 tests/unit/framework/db/cubrid/CubridQueryTest.php diff --git a/tests/unit/data/config.php b/tests/unit/data/config.php index 8ead605..fda2be1 100644 --- a/tests/unit/data/config.php +++ b/tests/unit/data/config.php @@ -1,6 +1,12 @@ array( + 'cubrid' => array( + 'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', + 'username' => 'dba', + 'password' => '', + 'fixture' => __DIR__ . '/cubrid.sql', + ), 'mysql' => array( 'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', 'username' => 'travis', diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql new file mode 100644 index 0000000..2cf7716 --- /dev/null +++ b/tests/unit/data/cubrid.sql @@ -0,0 +1,100 @@ +/** + * This is the database schema for testing CUBRID support of Yii DAO and Active Record. + * The database setup in config.php is required to perform then relevant tests: + */ + +DROP TABLE IF EXISTS tbl_order_item; +DROP TABLE IF EXISTS tbl_item; +DROP TABLE IF EXISTS tbl_order; +DROP TABLE IF EXISTS tbl_category; +DROP TABLE IF EXISTS tbl_customer; +DROP TABLE IF EXISTS tbl_type; +DROP TABLE IF EXISTS tbl_constraints; + +CREATE TABLE `tbl_constraints` +( + `id` integer not null, + `field1` varchar(255) +); + + +CREATE TABLE `tbl_customer` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `email` varchar(128) NOT NULL, + `name` varchar(128) NOT NULL, + `address` string, + `status` int (11) DEFAULT 0, + PRIMARY KEY (`id`) +); + +CREATE TABLE `tbl_category` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `tbl_item` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL, + `category_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_item_category_id` FOREIGN KEY (`category_id`) REFERENCES `tbl_category` (`id`) ON DELETE CASCADE +); + +CREATE TABLE `tbl_order` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `customer_id` int(11) NOT NULL, + `create_time` int(11) NOT NULL, + `total` decimal(10,0) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_order_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `tbl_customer` (`id`) ON DELETE CASCADE +); + +CREATE TABLE `tbl_order_item` ( + `order_id` int(11) NOT NULL, + `item_id` int(11) NOT NULL, + `quantity` int(11) NOT NULL, + `subtotal` decimal(10,0) NOT NULL, + PRIMARY KEY (`order_id`,`item_id`), + CONSTRAINT `FK_order_item_order_id` FOREIGN KEY (`order_id`) REFERENCES `tbl_order` (`id`) ON DELETE CASCADE, + CONSTRAINT `FK_order_item_item_id` FOREIGN KEY (`item_id`) REFERENCES `tbl_item` (`id`) ON DELETE CASCADE +); + +CREATE TABLE `tbl_type` ( + `int_col` int(11) NOT NULL, + `int_col2` int(11) DEFAULT '1', + `char_col` char(100) NOT NULL, + `char_col2` varchar(100) DEFAULT 'something', + `char_col3` string, + `float_col` double NOT NULL, + `float_col2` double DEFAULT '1.23', + `blob_col` blob, + `numeric_col` decimal(5,2) DEFAULT '33.22', + `time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00', + `bool_col` bit(1) NOT NULL, + `bool_col2` bit(1) DEFAULT B'1' +); + +INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1); +INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1); +INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2); + +INSERT INTO tbl_category (name) VALUES ('Books'); +INSERT INTO tbl_category (name) VALUES ('Movies'); + +INSERT INTO tbl_item (name, category_id) VALUES ('Agile Web Application Development with Yii1.1 and PHP5', 1); +INSERT INTO tbl_item (name, category_id) VALUES ('Yii 1.1 Application Development Cookbook', 1); +INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2); +INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2); +INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2); + +INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0); +INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0); +INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0); + +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0); +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0); +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4, 1, 10.0); +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0); +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0); +INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0); diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php index e08ac87..81a8156 100644 --- a/tests/unit/framework/db/QueryBuilderTest.php +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -8,6 +8,7 @@ use yii\db\mysql\QueryBuilder as MysqlQueryBuilder; use yii\db\sqlite\QueryBuilder as SqliteQueryBuilder; use yii\db\mssql\QueryBuilder as MssqlQueryBuilder; use yii\db\pgsql\QueryBuilder as PgsqlQueryBuilder; +use yii\db\cubrid\QueryBuilder as CubridQueryBuilder; class QueryBuilderTest extends DatabaseTestCase { @@ -32,6 +33,8 @@ class QueryBuilderTest extends DatabaseTestCase return new MssqlQueryBuilder($this->getConnection()); case 'pgsql': return new PgsqlQueryBuilder($this->getConnection()); + case 'cubrid': + return new CubridQueryBuilder($this->getConnection()); } throw new \Exception('Test is not implemented for ' . $this->driverName); } diff --git a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php new file mode 100644 index 0000000..b30880b --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php @@ -0,0 +1,13 @@ +driverName = 'cubrid'; + parent::setUp(); + } +} diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php new file mode 100644 index 0000000..60e422f --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -0,0 +1,13 @@ +driverName = 'cubrid'; + parent::setUp(); + } +} diff --git a/tests/unit/framework/db/cubrid/CubridConnectionTest.php b/tests/unit/framework/db/cubrid/CubridConnectionTest.php new file mode 100644 index 0000000..2937d22 --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridConnectionTest.php @@ -0,0 +1,13 @@ +driverName = 'cubrid'; + parent::setUp(); + } +} diff --git a/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php new file mode 100644 index 0000000..36967d7 --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php @@ -0,0 +1,80 @@ + 5)', 'int NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'), + array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'int NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'), + array(Schema::TYPE_STRING, 'varchar(255)'), + array(Schema::TYPE_STRING . '(32)', 'varchar(32)'), + array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'), + array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'), + array(Schema::TYPE_TEXT, 'varchar'), + array(Schema::TYPE_TEXT . '(255)', 'varchar'), + array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'varchar CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'varchar CHECK (value LIKE "test%")'), + array(Schema::TYPE_TEXT . ' NOT NULL', 'varchar NOT NULL'), + array(Schema::TYPE_TEXT . '(255) NOT NULL', 'varchar NOT NULL'), + array(Schema::TYPE_SMALLINT, 'smallint'), + array(Schema::TYPE_SMALLINT . '(8)', 'smallint'), + array(Schema::TYPE_INTEGER, 'int'), + array(Schema::TYPE_INTEGER . '(8)', 'int'), + array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'int CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'int CHECK (value > 5)'), + array(Schema::TYPE_INTEGER . ' NOT NULL', 'int NOT NULL'), + array(Schema::TYPE_BIGINT, 'bigint'), + array(Schema::TYPE_BIGINT . '(8)', 'bigint'), + array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint CHECK (value > 5)'), + array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint NOT NULL'), + array(Schema::TYPE_FLOAT, 'float(7)'), + array(Schema::TYPE_FLOAT . '(16)', 'float(16)'), + array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float(7) CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . '(16) CHECK (value > 5.6)', 'float(16) CHECK (value > 5.6)'), + array(Schema::TYPE_FLOAT . ' NOT NULL', 'float(7) NOT NULL'), + array(Schema::TYPE_DECIMAL, 'decimal(10,0)'), + array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'), + array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'), + array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'), + array(Schema::TYPE_DATETIME, 'datetime'), + array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'), + array(Schema::TYPE_TIMESTAMP, 'timestamp'), + array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'), + array(Schema::TYPE_TIME, 'time'), + array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"), + array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'), + array(Schema::TYPE_DATE, 'date'), + array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), + array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'), + array(Schema::TYPE_BINARY, 'blob'), + array(Schema::TYPE_BOOLEAN, 'bit(1)'), + array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'bit(1) NOT NULL DEFAULT 1'), + array(Schema::TYPE_MONEY, 'decimal(19,4)'), + array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'), + array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'), + array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'), + ); + } + +} diff --git a/tests/unit/framework/db/cubrid/CubridQueryTest.php b/tests/unit/framework/db/cubrid/CubridQueryTest.php new file mode 100644 index 0000000..0503fbc --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridQueryTest.php @@ -0,0 +1,13 @@ +driverName = 'cubrid'; + parent::setUp(); + } +} From fccd8185b5ad32c3932a118635aea97e6f557888 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 5 Sep 2013 22:39:40 -0400 Subject: [PATCH 073/156] Enhanced the default implementation of Model::scenarios() to return all scenarios found in rules(). --- framework/yii/base/Model.php | 47 ++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index a195acd..8cfc4d4 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -45,7 +45,7 @@ use yii\validators\Validator; * property is read-only. * @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is * read-only. - * @property string $scenario The scenario that this model is in. Defaults to 'default'. + * @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]]. * @property ArrayObject $validators All the validators declared in the model. This property is read-only. * * @author Qiang Xue @@ -54,6 +54,11 @@ use yii\validators\Validator; class Model extends Component implements IteratorAggregate, ArrayAccess { /** + * The name of the default scenario. + */ + const DEFAULT_SCENARIO = 'default'; + + /** * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set * [[ModelEvent::isValid]] to be false to stop the validation. */ @@ -74,7 +79,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess /** * @var string current scenario */ - private $_scenario = 'default'; + private $_scenario = self::DEFAULT_SCENARIO; /** * Returns the validation rules for attributes. @@ -159,23 +164,39 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * If an attribute should NOT be massively assigned (thus considered unsafe), * please prefix the attribute with an exclamation character (e.g. '!rank'). * - * The default implementation of this method will return a 'default' scenario - * which corresponds to all attributes listed in the validation rules applicable - * to the 'default' scenario. + * The default implementation of this method will all scenarios found in the [[rules()]] + * declaration. A special scenario named [[DEFAULT_SCENARIO]] will contain all attributes + * found in the [[rules()]]. Each scenario will be associated with the attributes that + * are being validated by the validation rules that apply to the scenario. * * @return array a list of scenarios and the corresponding active attributes. */ public function scenarios() { - $attributes = array(); - foreach ($this->getActiveValidators() as $validator) { - foreach ($validator->attributes as $name) { - $attributes[$name] = true; + $scenarios = array(); + $defaults = array(); + /** @var $validator Validator */ + foreach ($this->getValidators() as $validator) { + if (empty($validator->on)) { + foreach ($validator->attributes as $attribute) { + $defaults[$attribute] = true; + } + } else { + foreach ($validator->on as $scenario) { + foreach ($validator->attributes as $attribute) { + $scenarios[$scenario][$attribute] = true; + } + } + } + } + foreach ($scenarios as $scenario => $attributes) { + foreach (array_keys($defaults) as $attribute) { + $attributes[$attribute] = true; } + $scenarios[$scenario] = array_keys($attributes); } - return array( - 'default' => array_keys($attributes), - ); + $scenarios[self::DEFAULT_SCENARIO] = array_keys($defaults); + return $scenarios; } /** @@ -593,7 +614,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * Scenario affects how validation is performed and which attributes can * be massively assigned. * - * @return string the scenario that this model is in. Defaults to 'default'. + * @return string the scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]]. */ public function getScenario() { From b16bf0691ace16c1d83864b11292a0a7ce63419e Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 5 Sep 2013 23:37:15 -0400 Subject: [PATCH 074/156] fixed typo --- framework/yii/base/Model.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 8cfc4d4..9d81d83 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -164,7 +164,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * If an attribute should NOT be massively assigned (thus considered unsafe), * please prefix the attribute with an exclamation character (e.g. '!rank'). * - * The default implementation of this method will all scenarios found in the [[rules()]] + * The default implementation of this method will return all scenarios found in the [[rules()]] * declaration. A special scenario named [[DEFAULT_SCENARIO]] will contain all attributes * found in the [[rules()]]. Each scenario will be associated with the attributes that * are being validated by the validation rules that apply to the scenario. From d6ff097cbbc952d643fdf16777bba72d04adf755 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 10:28:10 +0200 Subject: [PATCH 075/156] avoid hanging test on bindValue problems (close db connection in tearDown) --- tests/unit/framework/db/DatabaseTestCase.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/framework/db/DatabaseTestCase.php b/tests/unit/framework/db/DatabaseTestCase.php index 1fd2d56..1dd8cfc 100644 --- a/tests/unit/framework/db/DatabaseTestCase.php +++ b/tests/unit/framework/db/DatabaseTestCase.php @@ -1,12 +1,16 @@ db) { + $this->db->close(); + } + } + /** * @param bool $reset whether to clean up the test database * @param bool $open whether to open and populate test database From e446a118640fae9ecad414022ad5d2799d4dab0b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 11:38:54 +0200 Subject: [PATCH 076/156] added CURBID to the docs --- docs/guide/database-basics.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/guide/database-basics.md b/docs/guide/database-basics.md index 71510f4..310e3f5 100644 --- a/docs/guide/database-basics.md +++ b/docs/guide/database-basics.md @@ -2,8 +2,14 @@ Database basics =============== Yii has a database access layer built on top of PHP's [PDO](http://www.php.net/manual/en/ref.pdo.php). It provides -uniform API and solves some inconsistencies between different DBMS. By default Yii supports MySQL, SQLite, PostgreSQL, -Oracle and MSSQL. +uniform API and solves some inconsistencies between different DBMS. By default Yii supports the following DBMS: + +- [MySQL](http://www.mysql.com/) +- [SQLite](http://sqlite.org/) +- [PostgreSQL](http://www.postgresql.org/) +- [CUBRID](http://www.cubrid.org/) (version 9.1.0 and higher). +- Oracle +- MSSQL Configuration @@ -22,6 +28,7 @@ return array( 'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB //'dsn' => 'sqlite:/path/to/database/file', // SQLite //'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL + //'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID //'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver //'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver //'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver @@ -34,8 +41,10 @@ return array( // ... ); ``` +Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) for more details +on the format of the DSN string. -After the component is configured you can access it using the following syntax: +After the connection component is configured you can access it using the following syntax: ```php $connection = \Yii::$app->db; From c6ef7ec9d5acd00720cb306018960244dcd08938 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 11:40:29 +0200 Subject: [PATCH 077/156] moved Command::getPdoType() to Schema this allows different implementation in different DBMS CUBRID does not supprt PDO::TYPE_BOOL, so we use STRING here which will be casted by the DBMS --- framework/yii/db/Command.php | 25 +++---------------------- framework/yii/db/Schema.php | 19 +++++++++++++++++++ framework/yii/db/cubrid/QueryBuilder.php | 2 +- framework/yii/db/cubrid/Schema.php | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index c439775..7f2d81d 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -181,7 +181,7 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) { - $this->pdoStatement->bindParam($name, $value, $this->getPdoType($value)); + $this->pdoStatement->bindParam($name, $value, $this->db->schema->getPdoType($value)); } elseif ($length === null) { $this->pdoStatement->bindParam($name, $value, $dataType); } elseif ($driverOptions === null) { @@ -208,7 +208,7 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) { - $this->pdoStatement->bindValue($name, $value, $this->getPdoType($value)); + $this->pdoStatement->bindValue($name, $value, $this->db->schema->getPdoType($value)); } else { $this->pdoStatement->bindValue($name, $value, $dataType); } @@ -236,7 +236,7 @@ class Command extends \yii\base\Component $type = $value[1]; $value = $value[0]; } else { - $type = $this->getPdoType($value); + $type = $this->db->schema->getPdoType($value); } $this->pdoStatement->bindValue($name, $value, $type); $this->_params[$name] = $value; @@ -246,25 +246,6 @@ class Command extends \yii\base\Component } /** - * Determines the PDO type for the give PHP data value. - * @param mixed $data the data whose PDO type is to be determined - * @return integer the PDO type - * @see http://www.php.net/manual/en/pdo.constants.php - */ - private function getPdoType($data) - { - static $typeMap = array( - 'boolean' => \PDO::PARAM_BOOL, - 'integer' => \PDO::PARAM_INT, - 'string' => \PDO::PARAM_STR, - 'resource' => \PDO::PARAM_LOB, - 'NULL' => \PDO::PARAM_NULL, - ); - $type = gettype($data); - return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; - } - - /** * Executes the SQL statement. * This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs. * No result set will be returned. diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 4fd1cd1..9839144 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -376,4 +376,23 @@ abstract class Schema extends Object return 'string'; } } + + /** + * Determines the PDO type for the give PHP data value. + * @param mixed $data the data whose PDO type is to be determined + * @return integer the PDO type + * @see http://www.php.net/manual/en/pdo.constants.php + */ + public function getPdoType($data) + { + static $typeMap = array( // php type => PDO type + 'boolean' => \PDO::PARAM_BOOL, + 'integer' => \PDO::PARAM_INT, + 'string' => \PDO::PARAM_STR, + 'resource' => \PDO::PARAM_LOB, + 'NULL' => \PDO::PARAM_NULL, + ); + $type = gettype($data); + return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; + } } diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 2132b58..4e5121a 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -10,7 +10,7 @@ namespace yii\db\cubrid; use yii\base\InvalidParamException; /** - * QueryBuilder is the query builder for CUBRID databases. + * QueryBuilder is the query builder for CUBRID databases (version 9.1.x and higher). * * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index f0b909b..a423296 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -243,4 +243,23 @@ class Schema extends \yii\db\Schema } return $tableNames; } + + /** + * Determines the PDO type for the give PHP data value. + * @param mixed $data the data whose PDO type is to be determined + * @return integer the PDO type + * @see http://www.php.net/manual/en/pdo.constants.php + */ + public function getPdoType($data) + { + static $typeMap = array( + 'boolean' => \PDO::PARAM_STR, // CUBRID PDO does not support PARAM_BOOL + 'integer' => \PDO::PARAM_INT, + 'string' => \PDO::PARAM_STR, + 'resource' => \PDO::PARAM_LOB, + 'NULL' => \PDO::PARAM_NULL, + ); + $type = gettype($data); + return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; + } } From 791f9d3f4e815244cc67c6cc2e928d57d884c76b Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 13:21:35 +0200 Subject: [PATCH 078/156] better use int for boolean representation bit has special syntax for storing and retreiving so we'd need a converter for that. Storing 1 bit will result in one byte left padded on the disc so the result of a query for boolean 0 will be 0x00 and for boolean 1 will be 0x80. --- framework/yii/db/cubrid/QueryBuilder.php | 2 +- framework/yii/db/cubrid/Schema.php | 11 +---------- tests/unit/data/cubrid.sql | 4 ++-- tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php | 4 ++-- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 4e5121a..935b3d3 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -34,7 +34,7 @@ class QueryBuilder extends \yii\db\QueryBuilder Schema::TYPE_TIME => 'time', Schema::TYPE_DATE => 'date', Schema::TYPE_BINARY => 'blob', - Schema::TYPE_BOOLEAN => 'bit(1)', + Schema::TYPE_BOOLEAN => 'smallint', Schema::TYPE_MONEY => 'decimal(19,4)', ); diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index a423296..c07602f 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -198,15 +198,6 @@ class Schema extends \yii\db\Schema if (isset($values[1])) { $column->scale = (int)$values[1]; } - if ($type === 'bit' || $type === 'bit varying') { - if ($column->size === 1) { - $column->type = self::TYPE_BOOLEAN; - } elseif ($column->size > 32) { - $column->type = self::TYPE_BIGINT; - } elseif ($column->size === 32) { - $column->type = self::TYPE_INTEGER; - } - } } } } @@ -253,7 +244,7 @@ class Schema extends \yii\db\Schema public function getPdoType($data) { static $typeMap = array( - 'boolean' => \PDO::PARAM_STR, // CUBRID PDO does not support PARAM_BOOL + 'boolean' => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL 'integer' => \PDO::PARAM_INT, 'string' => \PDO::PARAM_STR, 'resource' => \PDO::PARAM_LOB, diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql index 2cf7716..5525418 100644 --- a/tests/unit/data/cubrid.sql +++ b/tests/unit/data/cubrid.sql @@ -71,8 +71,8 @@ CREATE TABLE `tbl_type` ( `blob_col` blob, `numeric_col` decimal(5,2) DEFAULT '33.22', `time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00', - `bool_col` bit(1) NOT NULL, - `bool_col2` bit(1) DEFAULT B'1' + `bool_col` smallint NOT NULL, + `bool_col2` smallint DEFAULT 1 ); INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1); diff --git a/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php index 36967d7..94a002e 100644 --- a/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php +++ b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php @@ -67,8 +67,8 @@ class CubridQueryBuilderTest extends QueryBuilderTest array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"), array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'), array(Schema::TYPE_BINARY, 'blob'), - array(Schema::TYPE_BOOLEAN, 'bit(1)'), - array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'bit(1) NOT NULL DEFAULT 1'), + array(Schema::TYPE_BOOLEAN, 'smallint'), + array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'smallint NOT NULL DEFAULT 1'), array(Schema::TYPE_MONEY, 'decimal(19,4)'), array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'), array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'), From 58f8293b8455d242d47c3b1894351371624ad2cd Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 13:24:36 +0200 Subject: [PATCH 079/156] custom bindValue test for cubrid --- tests/unit/data/cubrid.sql | 1 + .../unit/framework/db/cubrid/CubridCommandTest.php | 63 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql index 5525418..8ea20a8 100644 --- a/tests/unit/data/cubrid.sql +++ b/tests/unit/data/cubrid.sql @@ -66,6 +66,7 @@ CREATE TABLE `tbl_type` ( `char_col` char(100) NOT NULL, `char_col2` varchar(100) DEFAULT 'something', `char_col3` string, + `enum_col` enum('a', 'b'), `float_col` double NOT NULL, `float_col2` double DEFAULT '1.23', `blob_col` blob, diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php index 60e422f..6f10113 100644 --- a/tests/unit/framework/db/cubrid/CubridCommandTest.php +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -10,4 +10,67 @@ class CubridCommandTest extends CommandTest $this->driverName = 'cubrid'; parent::setUp(); } + + public function testBindParamValue() + { + $db = $this->getConnection(); + + // bindParam + $sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, :name, :address)'; + $command = $db->createCommand($sql); + $email = 'user4@example.com'; + $name = 'user4'; + $address = 'address4'; + $command->bindParam(':email', $email); + $command->bindParam(':name', $name); + $command->bindParam(':address', $address); + $command->execute(); + + $sql = 'SELECT name FROM tbl_customer WHERE email=:email'; + $command = $db->createCommand($sql); + $command->bindParam(':email', $email); + $this->assertEquals($name, $command->queryScalar()); + + $sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col, bool_col, bool_col2) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col, :bool_col, :bool_col2)"; + $command = $db->createCommand($sql); + $intCol = 123; + $charCol = 'abc'; + $enumCol = 'a'; + $floatCol = 1.23; + $blobCol = "\x10\x11\x12"; + $numericCol = '1.23'; + $boolCol = false; + $boolCol2 = true; + $command->bindParam(':int_col', $intCol); + $command->bindParam(':char_col', $charCol); + $command->bindParam(':enum_col', $enumCol); + $command->bindParam(':float_col', $floatCol); + $command->bindParam(':blob_col', $blobCol); + $command->bindParam(':numeric_col', $numericCol); + $command->bindParam(':bool_col', $boolCol); + $command->bindParam(':bool_col2', $boolCol2); + $this->assertEquals(1, $command->execute()); + + $sql = 'SELECT * FROM tbl_type'; + $row = $db->createCommand($sql)->queryOne(); + $this->assertEquals($intCol, $row['int_col']); + $this->assertEquals($enumCol, $row['enum_col']); + $this->assertEquals($charCol, $row['char_col2']); + $this->assertEquals($floatCol, $row['float_col']); + $this->assertEquals($blobCol, fread($row['blob_col'], 3)); + $this->assertEquals($numericCol, $row['numeric_col']); + $this->assertEquals($boolCol, $row['bool_col']); + $this->assertEquals($boolCol2, $row['bool_col2']); + + // bindValue + $sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, \'user5\', \'address5\')'; + $command = $db->createCommand($sql); + $command->bindValue(':email', 'user5@example.com'); + $command->execute(); + + $sql = 'SELECT email FROM tbl_customer WHERE name=:name'; + $command = $db->createCommand($sql); + $command->bindValue(':name', 'user5'); + $this->assertEquals('user5@example.com', $command->queryScalar()); + } } From e996f3dfd579eed51f24eaa6415b367f7cb1b1f7 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 13:53:33 +0200 Subject: [PATCH 080/156] Workaround for broken PDO::quote() in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658 --- framework/yii/db/cubrid/Schema.php | 13 ++++++------- tests/unit/framework/db/cubrid/CubridConnectionTest.php | 8 ++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index c07602f..d1fdc10 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -96,18 +96,17 @@ class Schema extends \yii\db\Schema */ public function quoteValue($str) { - throw new NotSupportedException('quoteValue is currently broken in cubrid PDO'); - // TODO implement workaround -/* if (!is_string($str)) { + if (!is_string($str)) { return $str; } $this->db->open(); - if (($value = $this->db->pdo->quote($str)) !== false) { - return $value; - } else { // the driver doesn't support quote (e.g. oci) + // workaround for broken PDO::quote() implementation in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658 + if (version_compare($this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), '9.1.0', '<=')) { return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'"; - }*/ + } else { + return $this->db->pdo->quote($str); + } } /** diff --git a/tests/unit/framework/db/cubrid/CubridConnectionTest.php b/tests/unit/framework/db/cubrid/CubridConnectionTest.php index 2937d22..bfd2c90 100644 --- a/tests/unit/framework/db/cubrid/CubridConnectionTest.php +++ b/tests/unit/framework/db/cubrid/CubridConnectionTest.php @@ -10,4 +10,12 @@ class CubridConnectionTest extends ConnectionTest $this->driverName = 'cubrid'; parent::setUp(); } + + public function testQuoteValue() + { + $connection = $this->getConnection(false); + $this->assertEquals(123, $connection->quoteValue(123)); + $this->assertEquals("'string'", $connection->quoteValue('string')); + $this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting")); + } } From 4e1ab6e5a5f7e80b4ae25d7b6f5ccbf682cf9e6b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 6 Sep 2013 08:40:16 -0400 Subject: [PATCH 081/156] Fixes #848: Added hidden field for Html::activeFileInput(). --- framework/yii/helpers/HtmlBase.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/yii/helpers/HtmlBase.php b/framework/yii/helpers/HtmlBase.php index cfff8f5..a93c93e 100644 --- a/framework/yii/helpers/HtmlBase.php +++ b/framework/yii/helpers/HtmlBase.php @@ -1049,7 +1049,10 @@ class HtmlBase */ public static function activeFileInput($model, $attribute, $options = array()) { - return static::activeInput('file', $model, $attribute, $options); + // add a hidden field so that if a model only has a file field, we can + // still use isset($_POST[$modelClass]) to detect if the input is submitted + return static::activeHiddenInput($model, $attribute, array('id' => null, 'value' => '')) + . static::activeInput('file', $model, $attribute, $options); } /** From 95c2674609c2d61c9dfe0c56c4f1d8a96278b34f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 6 Sep 2013 08:40:44 -0400 Subject: [PATCH 082/156] hint fix. --- framework/yii/gii/generators/model/Generator.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/framework/yii/gii/generators/model/Generator.php b/framework/yii/gii/generators/model/Generator.php index 7aa601a..b9c8f23 100644 --- a/framework/yii/gii/generators/model/Generator.php +++ b/framework/yii/gii/generators/model/Generator.php @@ -93,8 +93,11 @@ class Generator extends \yii\gii\Generator 'db' => 'This is the ID of the DB application component.', 'tableName' => 'This is the name of the DB table that the new ActiveRecord class is associated with, e.g. tbl_post. The table name may consist of the DB schema part if needed, e.g. public.tbl_post. - The table name may contain an asterisk at the end to match multiple table names, e.g. tbl_*. - In this case, multiple ActiveRecord classes will be generated, one for each matching table name.', + The table name may contain an asterisk to match multiple table names, e.g. tbl_* + will match tables who name starts with tbl_. In this case, multiple ActiveRecord classes + will be generated, one for each matching table name; and the class names will be generated from + the matching characters. For example, table tbl_post will generate Post + class.', 'modelClass' => 'This is the name of the ActiveRecord class to be generated. The class name should not contain the namespace part as it is specified in "Namespace". You do not need to specify the class name if "Table Name" contains an asterisk at the end, in which case multiple ActiveRecord classes will be generated.', From 544e412af824487bba1911b509a25f1d7ee99108 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 15:03:25 +0200 Subject: [PATCH 083/156] unit test cleanup --- tests/unit/framework/db/CommandTest.php | 6 ------ tests/unit/framework/db/ConnectionTest.php | 6 ------ tests/unit/framework/db/DatabaseTestCase.php | 3 ++- tests/unit/framework/db/QueryBuilderTest.php | 6 ------ tests/unit/framework/db/QueryTest.php | 6 ------ tests/unit/framework/db/cubrid/CubridActiveRecordTest.php | 6 +----- tests/unit/framework/db/cubrid/CubridCommandTest.php | 6 +----- tests/unit/framework/db/cubrid/CubridConnectionTest.php | 6 +----- tests/unit/framework/db/cubrid/CubridQueryTest.php | 6 +----- tests/unit/framework/db/mssql/MssqlActiveRecordTest.php | 6 +----- tests/unit/framework/db/mssql/MssqlCommandTest.php | 6 +----- tests/unit/framework/db/mssql/MssqlConnectionTest.php | 6 +----- tests/unit/framework/db/mssql/MssqlQueryTest.php | 6 +----- tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php | 6 +----- tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php | 6 +----- tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php | 2 -- tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php | 6 +----- tests/unit/framework/db/sqlite/SqliteCommandTest.php | 6 +----- tests/unit/framework/db/sqlite/SqliteConnectionTest.php | 6 +----- tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php | 2 +- tests/unit/framework/db/sqlite/SqliteQueryTest.php | 6 +----- 21 files changed, 17 insertions(+), 98 deletions(-) diff --git a/tests/unit/framework/db/CommandTest.php b/tests/unit/framework/db/CommandTest.php index 52fd046..229545d 100644 --- a/tests/unit/framework/db/CommandTest.php +++ b/tests/unit/framework/db/CommandTest.php @@ -9,12 +9,6 @@ use yii\db\DataReader; class CommandTest extends DatabaseTestCase { - protected function setUp() - { - parent::setUp(); - $this->mockApplication(); - } - public function testConstruct() { $db = $this->getConnection(false); diff --git a/tests/unit/framework/db/ConnectionTest.php b/tests/unit/framework/db/ConnectionTest.php index 42b470b..f2895f1 100644 --- a/tests/unit/framework/db/ConnectionTest.php +++ b/tests/unit/framework/db/ConnectionTest.php @@ -6,12 +6,6 @@ use yii\db\Connection; class ConnectionTest extends DatabaseTestCase { - protected function setUp() - { - parent::setUp(); - $this->mockApplication(); - } - public function testConstruct() { $connection = $this->getConnection(false); diff --git a/tests/unit/framework/db/DatabaseTestCase.php b/tests/unit/framework/db/DatabaseTestCase.php index 1dd8cfc..d8d2916 100644 --- a/tests/unit/framework/db/DatabaseTestCase.php +++ b/tests/unit/framework/db/DatabaseTestCase.php @@ -16,7 +16,6 @@ abstract class DatabaseTestCase extends TestCase protected function setUp() { parent::setUp(); - $this->mockApplication(); $databases = $this->getParam('databases'); $this->database = $databases[$this->driverName]; $pdo_database = 'pdo_'.$this->driverName; @@ -24,6 +23,7 @@ abstract class DatabaseTestCase extends TestCase if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) { $this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.'); } + $this->mockApplication(); } protected function tearDown() @@ -31,6 +31,7 @@ abstract class DatabaseTestCase extends TestCase if ($this->db) { $this->db->close(); } + $this->destroyApplication(); } /** diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php index 81a8156..a2815e8 100644 --- a/tests/unit/framework/db/QueryBuilderTest.php +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -12,12 +12,6 @@ use yii\db\cubrid\QueryBuilder as CubridQueryBuilder; class QueryBuilderTest extends DatabaseTestCase { - protected function setUp() - { - parent::setUp(); - $this->mockApplication(); - } - /** * @throws \Exception * @return QueryBuilder diff --git a/tests/unit/framework/db/QueryTest.php b/tests/unit/framework/db/QueryTest.php index 5d1f1a9..dfb6c9f 100644 --- a/tests/unit/framework/db/QueryTest.php +++ b/tests/unit/framework/db/QueryTest.php @@ -9,12 +9,6 @@ use yii\db\DataReader; class QueryTest extends DatabaseTestCase { - protected function setUp() - { - parent::setUp(); - $this->mockApplication(); - } - public function testSelect() { // default diff --git a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php index b30880b..6633e11 100644 --- a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php +++ b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php @@ -5,9 +5,5 @@ use yiiunit\framework\db\ActiveRecordTest; class CubridActiveRecordTest extends ActiveRecordTest { - protected function setUp() - { - $this->driverName = 'cubrid'; - parent::setUp(); - } + public $driverName = 'cubrid'; } diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php index 6f10113..5fe8848 100644 --- a/tests/unit/framework/db/cubrid/CubridCommandTest.php +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -5,11 +5,7 @@ use yiiunit\framework\db\CommandTest; class CubridCommandTest extends CommandTest { - protected function setUp() - { - $this->driverName = 'cubrid'; - parent::setUp(); - } + public $driverName = 'cubrid'; public function testBindParamValue() { diff --git a/tests/unit/framework/db/cubrid/CubridConnectionTest.php b/tests/unit/framework/db/cubrid/CubridConnectionTest.php index bfd2c90..6924883 100644 --- a/tests/unit/framework/db/cubrid/CubridConnectionTest.php +++ b/tests/unit/framework/db/cubrid/CubridConnectionTest.php @@ -5,11 +5,7 @@ use yiiunit\framework\db\ConnectionTest; class CubridConnectionTest extends ConnectionTest { - protected function setUp() - { - $this->driverName = 'cubrid'; - parent::setUp(); - } + public $driverName = 'cubrid'; public function testQuoteValue() { diff --git a/tests/unit/framework/db/cubrid/CubridQueryTest.php b/tests/unit/framework/db/cubrid/CubridQueryTest.php index 0503fbc..2c68a35 100644 --- a/tests/unit/framework/db/cubrid/CubridQueryTest.php +++ b/tests/unit/framework/db/cubrid/CubridQueryTest.php @@ -5,9 +5,5 @@ use yiiunit\framework\db\QueryTest; class CubridQueryTest extends QueryTest { - protected function setUp() - { - $this->driverName = 'cubrid'; - parent::setUp(); - } + public $driverName = 'cubrid'; } diff --git a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php index 5647411..92e4da2 100644 --- a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php +++ b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php @@ -6,9 +6,5 @@ use yiiunit\framework\db\ActiveRecordTest; class MssqlActiveRecordTest extends ActiveRecordTest { - protected function setUp() - { - $this->driverName = 'sqlsrv'; - parent::setUp(); - } + protected $driverName = 'sqlsrv'; } diff --git a/tests/unit/framework/db/mssql/MssqlCommandTest.php b/tests/unit/framework/db/mssql/MssqlCommandTest.php index e27fcee..1908f65 100644 --- a/tests/unit/framework/db/mssql/MssqlCommandTest.php +++ b/tests/unit/framework/db/mssql/MssqlCommandTest.php @@ -6,11 +6,7 @@ use yiiunit\framework\db\CommandTest; class MssqlCommandTest extends CommandTest { - public function setUp() - { - $this->driverName = 'sqlsrv'; - parent::setUp(); - } + protected $driverName = 'sqlsrv'; public function testAutoQuoting() { diff --git a/tests/unit/framework/db/mssql/MssqlConnectionTest.php b/tests/unit/framework/db/mssql/MssqlConnectionTest.php index 7bbaae7..854c81f 100644 --- a/tests/unit/framework/db/mssql/MssqlConnectionTest.php +++ b/tests/unit/framework/db/mssql/MssqlConnectionTest.php @@ -6,11 +6,7 @@ use yiiunit\framework\db\ConnectionTest; class MssqlConnectionTest extends ConnectionTest { - public function setUp() - { - $this->driverName = 'sqlsrv'; - parent::setUp(); - } + protected $driverName = 'sqlsrv'; public function testQuoteValue() { diff --git a/tests/unit/framework/db/mssql/MssqlQueryTest.php b/tests/unit/framework/db/mssql/MssqlQueryTest.php index 4f37c14..e07ac1d 100644 --- a/tests/unit/framework/db/mssql/MssqlQueryTest.php +++ b/tests/unit/framework/db/mssql/MssqlQueryTest.php @@ -6,9 +6,5 @@ use yiiunit\framework\db\QueryTest; class MssqlQueryTest extends QueryTest { - public function setUp() - { - $this->driverName = 'sqlsrv'; - parent::setUp(); - } + protected $driverName = 'sqlsrv'; } diff --git a/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php b/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php index 2836223..aef09fd 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php @@ -6,9 +6,5 @@ use yiiunit\framework\db\ActiveRecordTest; class PostgreSQLActiveRecordTest extends ActiveRecordTest { - protected function setUp() - { - $this->driverName = 'pgsql'; - parent::setUp(); - } + protected $driverName = 'pgsql'; } diff --git a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php index 7ac65c5..cd76a97 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php @@ -5,11 +5,7 @@ use yiiunit\framework\db\ConnectionTest; class PostgreSQLConnectionTest extends ConnectionTest { - public function setUp() - { - $this->driverName = 'pgsql'; - parent::setUp(); - } + protected $driverName = 'pgsql'; public function testConnection() { diff --git a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php index 7c31190..bf2dd73 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php @@ -2,13 +2,11 @@ namespace yiiunit\framework\db\pgsql; -use yii\base\NotSupportedException; use yii\db\pgsql\Schema; use yiiunit\framework\db\QueryBuilderTest; class PostgreSQLQueryBuilderTest extends QueryBuilderTest { - public $driverName = 'pgsql'; public function columnTypes() diff --git a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php index 2e76162..1088280 100644 --- a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php +++ b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php @@ -5,9 +5,5 @@ use yiiunit\framework\db\ActiveRecordTest; class SqliteActiveRecordTest extends ActiveRecordTest { - protected function setUp() - { - $this->driverName = 'sqlite'; - parent::setUp(); - } + protected $driverName = 'sqlite'; } diff --git a/tests/unit/framework/db/sqlite/SqliteCommandTest.php b/tests/unit/framework/db/sqlite/SqliteCommandTest.php index e330c7e..af9a323 100644 --- a/tests/unit/framework/db/sqlite/SqliteCommandTest.php +++ b/tests/unit/framework/db/sqlite/SqliteCommandTest.php @@ -5,11 +5,7 @@ use yiiunit\framework\db\CommandTest; class SqliteCommandTest extends CommandTest { - protected function setUp() - { - $this->driverName = 'sqlite'; - parent::setUp(); - } + protected $driverName = 'sqlite'; public function testAutoQuoting() { diff --git a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php index 2065200..f1d5b94 100644 --- a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php +++ b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php @@ -5,11 +5,7 @@ use yiiunit\framework\db\ConnectionTest; class SqliteConnectionTest extends ConnectionTest { - protected function setUp() - { - $this->driverName = 'sqlite'; - parent::setUp(); - } + protected $driverName = 'sqlite'; public function testConstruct() { diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php index 3291187..2e3a615 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -8,7 +8,7 @@ use yiiunit\framework\db\QueryBuilderTest; class SqliteQueryBuilderTest extends QueryBuilderTest { - public $driverName = 'sqlite'; + protected $driverName = 'sqlite'; public function columnTypes() { diff --git a/tests/unit/framework/db/sqlite/SqliteQueryTest.php b/tests/unit/framework/db/sqlite/SqliteQueryTest.php index ebbb1bb..3f112dc 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryTest.php @@ -5,9 +5,5 @@ use yiiunit\framework\db\QueryTest; class SqliteQueryTest extends QueryTest { - protected function setUp() - { - $this->driverName = 'sqlite'; - parent::setUp(); - } + protected $driverName = 'sqlite'; } From 8abeed03bd827a3684350f4775a23f5e92e7c317 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 15:03:57 +0200 Subject: [PATCH 084/156] added SchemaTest --- framework/yii/db/cubrid/Schema.php | 2 +- tests/unit/framework/db/SchemaTest.php | 70 ++++++++++++++++++++++ .../unit/framework/db/cubrid/CubridSchemaTest.php | 31 ++++++++++ .../unit/framework/db/sqlite/SqliteSchemaTest.php | 9 +++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/unit/framework/db/SchemaTest.php create mode 100644 tests/unit/framework/db/cubrid/CubridSchemaTest.php create mode 100644 tests/unit/framework/db/sqlite/SqliteSchemaTest.php diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index d1fdc10..d221073 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -227,7 +227,7 @@ class Schema extends \yii\db\Schema $tableNames = array(); foreach($tables as $table) { // do not list system tables - if ($table['TYPE'] !== 0) { + if ($table['TYPE'] != 0) { $tableNames[] = $table['NAME']; } } diff --git a/tests/unit/framework/db/SchemaTest.php b/tests/unit/framework/db/SchemaTest.php new file mode 100644 index 0000000..66aa410 --- /dev/null +++ b/tests/unit/framework/db/SchemaTest.php @@ -0,0 +1,70 @@ +getConnection()->schema; + + foreach($values as $value) { + $this->assertEquals($value[1], $schema->getPdoType($value[0])); + } + fclose($fp); + } + + public function testFindTableNames() + { + /** @var Schema $schema */ + $schema = $this->getConnection()->schema; + + $tables = $schema->getTableNames(); + $this->assertTrue(in_array('tbl_customer', $tables)); + $this->assertTrue(in_array('tbl_category', $tables)); + $this->assertTrue(in_array('tbl_item', $tables)); + $this->assertTrue(in_array('tbl_order', $tables)); + $this->assertTrue(in_array('tbl_order_item', $tables)); + $this->assertTrue(in_array('tbl_type', $tables)); + } + + public function testGetTableSchemas() + { + /** @var Schema $schema */ + $schema = $this->getConnection()->schema; + + $tables = $schema->getTableSchemas(); + $this->assertEquals(count($schema->getTableNames()), count($tables)); + foreach($tables as $table) { + $this->assertInstanceOf('yii\db\TableSchema', $table); + } + } + + public function testSchemaCache() + { + /** @var Schema $schema */ + $schema = $this->getConnection()->schema; + + $schema->db->enableSchemaCache = true; + $schema->db->schemaCache = new FileCache(); + $noCacheTable = $schema->getTableSchema('tbl_type', true); + $cachedTable = $schema->getTableSchema('tbl_type', true); + $this->assertEquals($noCacheTable, $cachedTable); + } +} diff --git a/tests/unit/framework/db/cubrid/CubridSchemaTest.php b/tests/unit/framework/db/cubrid/CubridSchemaTest.php new file mode 100644 index 0000000..d235b40 --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridSchemaTest.php @@ -0,0 +1,31 @@ + \PDO::PARAM_NULL, + '' => \PDO::PARAM_STR, + 'hello' => \PDO::PARAM_STR, + 0 => \PDO::PARAM_INT, + 1 => \PDO::PARAM_INT, + 1337 => \PDO::PARAM_INT, + true => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL + false => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL + ); + + $schema = $this->getConnection()->schema; + + foreach($values as $value => $type) { + $this->assertEquals($type, $schema->getPdoType($value)); + } + $this->assertEquals(\PDO::PARAM_LOB, $schema->getPdoType($fp=fopen(__FILE__, 'rb'))); + fclose($fp); + } +} diff --git a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php new file mode 100644 index 0000000..f3f6b60 --- /dev/null +++ b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php @@ -0,0 +1,9 @@ + Date: Fri, 6 Sep 2013 15:32:33 +0200 Subject: [PATCH 085/156] Add support for composite FK to cubrid --- framework/yii/db/cubrid/Schema.php | 14 +++++++++----- tests/unit/data/cubrid.sql | 9 +++++++++ tests/unit/data/mysql.sql | 9 +++++++++ tests/unit/data/sqlite.sql | 9 +++++++++ tests/unit/framework/db/SchemaTest.php | 19 +++++++++++++++++++ tests/unit/framework/db/sqlite/SqliteSchemaTest.php | 5 +++++ 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index d221073..e3b125b 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -148,12 +148,16 @@ class Schema extends \yii\db\Schema $foreignKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name); foreach($foreignKeys as $key) { - $table->foreignKeys[] = array( - $key['PKTABLE_NAME'], - $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'] - // TODO support composite foreign keys - ); + if (isset($table->foreignKeys[$key['FK_NAME']])) { + $table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME']; + } else { + $table->foreignKeys[$key['FK_NAME']] = array( + $key['PKTABLE_NAME'], + $key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME'] + ); + } } + $table->foreignKeys = array_values($table->foreignKeys); return $table; } else { diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql index 8ea20a8..bfaf85c 100644 --- a/tests/unit/data/cubrid.sql +++ b/tests/unit/data/cubrid.sql @@ -3,6 +3,7 @@ * The database setup in config.php is required to perform then relevant tests: */ +DROP TABLE IF EXISTS tbl_composite_fk; DROP TABLE IF EXISTS tbl_order_item; DROP TABLE IF EXISTS tbl_item; DROP TABLE IF EXISTS tbl_order; @@ -76,6 +77,14 @@ CREATE TABLE `tbl_type` ( `bool_col2` smallint DEFAULT 1 ); +CREATE TABLE `tbl_composite_fk` ( + `id` int(11) NOT NULL, + `order_id` int(11) NOT NULL, + `item_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE +); + INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1); INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1); INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2); diff --git a/tests/unit/data/mysql.sql b/tests/unit/data/mysql.sql index 2e9458e..133348d 100644 --- a/tests/unit/data/mysql.sql +++ b/tests/unit/data/mysql.sql @@ -3,6 +3,7 @@ * The database setup in config.php is required to perform then relevant tests: */ +DROP TABLE IF EXISTS tbl_composite_fk CASCADE; DROP TABLE IF EXISTS tbl_order_item CASCADE; DROP TABLE IF EXISTS tbl_item CASCADE; DROP TABLE IF EXISTS tbl_order CASCADE; @@ -62,6 +63,14 @@ CREATE TABLE `tbl_order_item` ( CONSTRAINT `FK_order_item_item_id` FOREIGN KEY (`item_id`) REFERENCES `tbl_item` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `tbl_composite_fk` ( + `id` int(11) NOT NULL, + `order_id` int(11) NOT NULL, + `item_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE +); + CREATE TABLE `tbl_type` ( `int_col` int(11) NOT NULL, `int_col2` int(11) DEFAULT '1', diff --git a/tests/unit/data/sqlite.sql b/tests/unit/data/sqlite.sql index f75bfa6..f031ac3 100644 --- a/tests/unit/data/sqlite.sql +++ b/tests/unit/data/sqlite.sql @@ -3,6 +3,7 @@ * The database setup in config.php is required to perform then relevant tests: */ +DROP TABLE IF EXISTS tbl_composite_fk; DROP TABLE IF EXISTS tbl_order_item; DROP TABLE IF EXISTS tbl_item; DROP TABLE IF EXISTS tbl_order; @@ -48,6 +49,14 @@ CREATE TABLE tbl_order_item ( PRIMARY KEY (order_id, item_id) ); +CREATE TABLE `tbl_composite_fk` ( + `id` int(11) NOT NULL, + `order_id` int(11) NOT NULL, + `item_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE +); + CREATE TABLE tbl_type ( int_col INTEGER NOT NULL, int_col2 INTEGER DEFAULT '1', diff --git a/tests/unit/framework/db/SchemaTest.php b/tests/unit/framework/db/SchemaTest.php index 66aa410..5f504e1 100644 --- a/tests/unit/framework/db/SchemaTest.php +++ b/tests/unit/framework/db/SchemaTest.php @@ -56,6 +56,11 @@ class SchemaTest extends DatabaseTestCase } } + public function testGetNonExistingTableSchema() + { + $this->assertNull($this->getConnection()->schema->getTableSchema('nonexisting_table')); + } + public function testSchemaCache() { /** @var Schema $schema */ @@ -67,4 +72,18 @@ class SchemaTest extends DatabaseTestCase $cachedTable = $schema->getTableSchema('tbl_type', true); $this->assertEquals($noCacheTable, $cachedTable); } + + public function testCompositeFk() + { + /** @var Schema $schema */ + $schema = $this->getConnection()->schema; + + $table = $schema->getTableSchema('tbl_composite_fk'); + + $this->assertCount(1, $table->foreignKeys); + $this->assertTrue(isset($table->foreignKeys[0])); + $this->assertEquals('tbl_order_item', $table->foreignKeys[0][0]); + $this->assertEquals('order_id', $table->foreignKeys[0]['order_id']); + $this->assertEquals('item_id', $table->foreignKeys[0]['item_id']); + } } diff --git a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php index f3f6b60..9b17a1d 100644 --- a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php +++ b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php @@ -6,4 +6,9 @@ use yiiunit\framework\db\SchemaTest; class SqliteSchemaTest extends SchemaTest { protected $driverName = 'sqlite'; + + public function testCompositeFk() + { + $this->markTestSkipped('sqlite does not allow getting enough information about composite FK.'); + } } From 23b858a2ccd0e3eb7ad468ba011aeaf7eab98c86 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 6 Sep 2013 15:51:30 +0200 Subject: [PATCH 086/156] removed comments --- framework/yii/db/cubrid/Schema.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index e3b125b..fae932f 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -55,14 +55,14 @@ class Schema extends \yii\db\Schema 'blob' => self::TYPE_BINARY, 'clob' => self::TYPE_BINARY, // Bit string data types -// 'bit' => self::TYPE_STRING, -// 'bit varying' => self::TYPE_STRING, - // Collection data types (TODO are considered strings for now, naybe support conversion?) -// 'set' => self::TYPE_STRING, -// 'multiset' => self::TYPE_STRING, -// 'list' => self::TYPE_STRING, -// 'sequence' => self::TYPE_STRING, -// 'enum' => self::TYPE_STRING, + 'bit' => self::TYPE_STRING, + 'bit varying' => self::TYPE_STRING, + // Collection data types (considered strings for now, may add support for them later) + 'set' => self::TYPE_STRING, + 'multiset' => self::TYPE_STRING, + 'list' => self::TYPE_STRING, + 'sequence' => self::TYPE_STRING, + 'enum' => self::TYPE_STRING, ); /** From 6d25d3aef6dab835294f4147c64bdd0eaa3a82e2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 6 Sep 2013 18:44:02 +0400 Subject: [PATCH 087/156] Added info about custom validator method signature to Model::rules phpdoc --- framework/yii/base/Model.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 9d81d83..93b5a6b 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -116,6 +116,10 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * function validatorName($attribute, $params) * ~~~ * + * In the above `$attribute` refers to currently validated attribute name while `$params` contains an array of + * validator configuration options such as `max` in case of `length` validator. Currently validate attribute value + * can be accessed as `$this->[$attribute]`. + * * Yii also provides a set of [[Validator::builtInValidators|built-in validators]]. * They each has an alias name which can be used when specifying a validation rule. * From 3766e1b22f5144800dbd5efe15dae5e26df584b9 Mon Sep 17 00:00:00 2001 From: Larry Ullman Date: Mon, 26 Aug 2013 13:50:55 -0400 Subject: [PATCH 088/156] Edited the first half --- docs/guide/active-record.md | 52 +++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/docs/guide/active-record.md b/docs/guide/active-record.md index bb39115..0c93fad 100644 --- a/docs/guide/active-record.md +++ b/docs/guide/active-record.md @@ -1,17 +1,15 @@ Active Record ============= -ActiveRecord implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record). -The idea is that an [[ActiveRecord]] object is associated with a row in a database table and its attributes are mapped -to the columns of the corresponding table columns. Reading an ActiveRecord attribute is equivalent to accessing -the corresponding table column. For example, a `Customer` object is associated with a row in the -`tbl_customer` table, and its `name` attribute is mapped to the `name` column in the `tbl_customer` table. -To get the value of the `name` column in the table row, you can simply use the expression `$customer->name`, -just like reading an object property. - -Instead of writing raw SQL statements to perform database queries, you can call intuitive methods provided -by ActiveRecord to achieve the same goals. For example, calling [[ActiveRecord::save()|save()]] would -insert or update a row in the associated table of the ActiveRecord class: +Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record). +The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Therefore, referencing an Active Record attribute is equivalent to accessing +the corresponding table column for that record. + +For example, say that the `Customer` ActiveRecord class is associated with the +`tbl_customer` table. This would mean that the class's `name` attribute is automatically mapped to the `name` column in `tbl_customer`. +Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of the `name` column for the table row, you can simply use the expression `$customer->name`. In this example, Active Record is providing an object-oriented way to access data stored in the database, just as you would access any object property. But Active Record provides much more functionality than this. + +With Active Record, instead of writing raw SQL statements to perform database queries, you can call intuitive methods to achieve the same goals. For example, calling [[ActiveRecord::save()|save()]] would perform an INSERT or UPDATE query, creating or updating a row in the associated table of the ActiveRecord class: ```php $customer = new Customer(); @@ -24,7 +22,7 @@ Declaring ActiveRecord Classes ------------------------------ To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and -implement the `tableName` method like the following: +implement the `tableName` method: ```php use yii\db\ActiveRecord; @@ -41,13 +39,12 @@ class Customer extends ActiveRecord } ``` -Connecting to Database +Connecting to the Database ---------------------- ActiveRecord relies on a [[Connection|DB connection]] to perform the underlying DB operations. -By default, it assumes that there is an application component named `db` which gives the needed -[[Connection]] instance. Usually this component is configured via application configuration -like the following: +By default, ActiveRecord assumes that there is an application component named `db` which provides the needed +[[Connection]] instance. Usually this component is configured in application configuration file: ```php return array( @@ -65,13 +62,10 @@ return array( Please read the [Database basics](database-basics.md) section to learn more on how to configure and use database connections. -> Tip: To use a different database connection, you may override the [[ActiveRecord::getDb()]] method. -You may create a base ActiveRecord class and override its [[ActiveRecord::getDb()]] method. You -then extend from this base class for all those ActiveRecord classes that need to use the same -DB connection. +> Tip: To use a different database connection, you need to override the [[ActiveRecord::getDb()]] method. To do that, create a base class that extends ActiveRecord. In the base class, override the [[ActiveRecord::getDb()]] method. Then extend your new base class for all of your ActiveRecord classes that need to use the same alternative database connection. -Querying Data from Database +Querying Data from the Database --------------------------- There are two ActiveRecord methods for querying data from database: @@ -79,8 +73,8 @@ There are two ActiveRecord methods for querying data from database: - [[ActiveRecord::find()]] - [[ActiveRecord::findBySql()]] -They both return an [[ActiveQuery]] instance which extends from [[Query]] and thus supports -the same set of flexible and powerful DB query methods. The followings are some examples, +Both methods return an [[ActiveQuery]] instance, which extends from [[Query]] thus supporting +the same set of flexible and powerful DB query methods. The followings examples demonstrate some of the possibilities. ```php // to retrieve all *active* customers and order them by their ID: @@ -122,10 +116,10 @@ Accessing Column Data --------------------- ActiveRecord maps each column of the corresponding database table row to an *attribute* in the ActiveRecord -object. An attribute is like a regular object property whose name is the same as the corresponding column -name and is case-sensitive. +object. The attribute behaves like any regular object public property. The attribute's name will be the same as the corresponding column +name, and is case-sensitive. -To read the value of a column, you can use the following expression: +To read the value of a column, you can use the following syntax: ```php // "id" is the name of a column in the table associated with $customer ActiveRecord object @@ -141,7 +135,7 @@ $values = $customer->attributes; ``` -Manipulating Data in Database +Manipulating Data in the Database ----------------------------- ActiveRecord provides the following methods to insert, update and delete data in the database: @@ -157,9 +151,7 @@ ActiveRecord provides the following methods to insert, update and delete data in Note that [[ActiveRecord::updateAll()|updateAll()]], [[ActiveRecord::updateAllCounters()|updateAllCounters()]] and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods and apply to the whole database -table, while the rest of the methods only apply to the row associated with the ActiveRecord object. - -The followings are some examples: +table, while the other methods only apply to the row associated with the ActiveRecord object through which the method is being called. ```php // to insert a new customer record From 746a250e3f85ffe42600d8bf1ea18973e3ce556a Mon Sep 17 00:00:00 2001 From: Larry Ullman Date: Mon, 26 Aug 2013 13:51:40 -0400 Subject: [PATCH 089/156] Ignoring .DS_Store --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 13fcf4a..f2915a9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,6 @@ Thumbs.db # composer itself is not needed composer.phar + +# Mac DS_Store Files +.DS_Store From a9991d4e916e23360195b1a091c33d4cf0ab7e96 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 7 Sep 2013 08:18:41 -0400 Subject: [PATCH 090/156] CRUD WIP --- framework/yii/base/Module.php | 5 +- framework/yii/gii/Generator.php | 22 ++ framework/yii/gii/assets/gii.js | 5 + framework/yii/gii/generators/crud/Generator.php | 103 +++++---- framework/yii/gii/generators/crud/form.php | 7 +- .../gii/generators/crud/templates/controller.php | 236 +++++++++------------ .../gii/generators/crud/templates/views/create.php | 40 ++-- .../gii/generators/crud/templates/views/update.php | 44 ++-- .../gii/generators/crud/templates/views/view.php | 48 ++--- 9 files changed, 256 insertions(+), 254 deletions(-) diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php index 1b7bf5b..de75ce7 100644 --- a/framework/yii/base/Module.php +++ b/framework/yii/base/Module.php @@ -338,10 +338,9 @@ abstract class Module extends Component /** * Retrieves the named module. - * @param string $id module ID (case-sensitive) + * @param string $id module ID (case-sensitive). * @param boolean $load whether to load the module if it is not yet loaded. - * @return Module|null the module instance, null if the module - * does not exist. + * @return Module|null the module instance, null if the module does not exist. * @see hasModule() */ public function getModule($id, $load = true) diff --git a/framework/yii/gii/Generator.php b/framework/yii/gii/Generator.php index f79b428..eb5b8b4 100644 --- a/framework/yii/gii/Generator.php +++ b/framework/yii/gii/Generator.php @@ -325,6 +325,28 @@ abstract class Generator extends Model } /** + * An inline validator that checks if the attribute value refers to a valid namespaced class name. + * The validator will check if the directory containing the new class file exist or not. + * @param string $attribute the attribute being validated + * @param array $params the validation options + */ + public function validateNewClass($attribute, $params) + { + $class = ltrim($this->$attribute, '\\'); + if (($pos = strrpos($class, '\\')) === false) { + $this->addError($attribute, "The class name must contain fully qualified namespace name."); + } else { + $ns = substr($class, 0, $pos); + $path = Yii::getAlias('@' . str_replace('\\', '/', $ns), false); + if ($path === false) { + $this->addError($attribute, "The class namespace is invalid: $ns"); + } elseif (!is_dir($path)) { + $this->addError($attribute, "Please make sure the directory containing this class exists: $path"); + } + } + } + + /** * @param string $value the attribute to be validated * @return boolean whether the value is a reserved PHP keyword. */ diff --git a/framework/yii/gii/assets/gii.js b/framework/yii/gii/assets/gii.js index b581d3b..45951a1 100644 --- a/framework/yii/gii/assets/gii.js +++ b/framework/yii/gii/assets/gii.js @@ -81,6 +81,11 @@ yii.gii = (function ($) { $('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1); }).change(); + // crud generator: hide Search Model Class input if search is not enabled + $('#crud-generator #generator-enablesearch').on('change', function () { + $('#crud-generator .field-generator-searchmodelclass').toggle(this.checked); + }).change(); + // hide Generate button if any input is changed $('.default-view .form-group input,select,textarea').change(function () { $('.default-view-results,.default-view-files').hide(); diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 983dbc7..3ed51c1 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -22,7 +22,8 @@ use yii\web\Controller; class Generator extends \yii\gii\Generator { public $modelClass; - public $controllerID; + public $moduleID; + public $controllerClass; public $baseControllerClass = 'yii\web\Controller'; public $indexWidgetType = 'grid'; public $enableSearch = true; @@ -42,16 +43,18 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), array( - array('modelClass, searchModelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'), - array('modelClass, controllerID, baseControllerClass', 'required'), - array('modelClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), + array('moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'), + array('modelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'), + array('modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())), - array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'), - array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())), + array('controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'), + array('controllerClass, searchModelClass', 'validateNewClass'), array('enableSearch', 'boolean'), array('indexWidgetType', 'in', 'range' => array('grid', 'list')), + array('modelClass', 'validateModelClass'), array('searchModelClass', 'validateSearchModelClass'), + array('moduleID', 'validateModuleID'), )); } @@ -59,7 +62,8 @@ class Generator extends \yii\gii\Generator { return array_merge(parent::attributeLabels(), array( 'modelClass' => 'Model Class', - 'controllerID' => 'Controller ID', + 'moduleID' => 'Module ID', + 'controllerClass' => 'Controller Class', 'baseControllerClass' => 'Base Controller Class', 'indexWidgetType' => 'Widget Used in Index Page', 'enableSearch' => 'Enable Search', @@ -75,15 +79,12 @@ class Generator extends \yii\gii\Generator return array( 'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon. You should provide a fully qualified class name, e.g., app\models\Post.', - 'controllerID' => 'CRUD controllers are often named after the model class name that they are dealing with. - Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example: -
    -
  • order generates OrderController.php
  • -
  • order-item generates OrderItemController.php
  • -
  • admin/user generates UserController.php within the admin module.
  • -
', + 'controllerClass' => 'This is the name of the controller class to be generated. You should + provide a fully qualified namespaced class, .e.g, app\controllers\PostController.', 'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from. You should provide a fully qualified class name, e.g., yii\web\Controller.', + 'moduleID' => 'This is the ID of the module that the generated controller will belong to. + If not set, it means the controller will belong to the application.', 'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models. You may choose either GridView or ListView', 'enableSearch' => 'Whether to enable the search functionality on the index page. When search is enabled, @@ -106,7 +107,17 @@ class Generator extends \yii\gii\Generator */ public function stickyAttributes() { - return array('baseControllerClass', 'indexWidgetType', 'enableSearch'); + return array('baseControllerClass', 'moduleID', 'indexWidgetType', 'enableSearch'); + } + + public function validateModelClass() + { + /** @var ActiveRecord $class */ + $class = $this->modelClass; + $pk = $class::primaryKey(); + if (empty($pk)) { + $this->addError('modelClass', "The table associated with $class must have primary key(s)."); + } } public function validateSearchModelClass() @@ -116,6 +127,16 @@ class Generator extends \yii\gii\Generator } } + public function validateModuleID() + { + if (!empty($this->moduleID)) { + $module = Yii::$app->getModule($this->moduleID); + if ($module === null) { + $this->addError('moduleID', "Module '{$this->moduleID}' does not exist."); + } + } + } + /** * @inheritdoc */ @@ -130,6 +151,9 @@ class Generator extends \yii\gii\Generator $templatePath = $this->getTemplatePath() . '/views'; foreach (scandir($templatePath) as $file) { + if (!in_array($file, array('create.php', 'update.php', 'view.php'))) { + continue; + } if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { $files[] = new CodeFile("$viewPath/$file", $this->render("views/$file")); } @@ -142,39 +166,14 @@ class Generator extends \yii\gii\Generator return $files; } - - /** - * @return string the controller class name without the namespace part. - */ - public function getControllerClass() - { - return Inflector::id2camel($this->getControllerID()) . 'Controller'; - } - /** * @return string the controller ID (without the module ID prefix) */ public function getControllerID() { - if (($pos = strrpos($this->controllerID, '/')) !== false) { - return substr($this->controllerID, $pos + 1); - } else { - return $this->controllerID; - } - } - - /** - * @return \yii\base\Module the module that the new controller belongs to - */ - public function getModule() - { - if (($pos = strpos($this->controllerID, '/')) !== false) { - $id = substr($this->controllerID, 0, $pos); - if (($module = Yii::$app->getModule($id)) !== null) { - return $module; - } - } - return Yii::$app; + $pos = strrpos($this->controllerClass, '\\'); + $class = substr(substr($this->controllerClass, $pos + 1), 0, -10); + return Inflector::camel2id($class); } /** @@ -182,8 +181,7 @@ class Generator extends \yii\gii\Generator */ public function getControllerFile() { - $module = $this->getModule(); - return $module->getControllerPath() . '/' . $this->getControllerClass() . '.php'; + return Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php'); } /** @@ -191,7 +189,20 @@ class Generator extends \yii\gii\Generator */ public function getViewPath() { - $module = $this->getModule(); + $module = empty($this->moduleID) ? Yii::$app : Yii::$app->getModule($this->moduleID); return $module->getViewPath() . '/' . $this->getControllerID() ; } + + public function getNameAttribute() + { + /** @var \yii\db\ActiveRecord $class */ + $class = $this->modelClass; + foreach ($class::getTableSchema()->columnNames as $name) { + if (!strcasecmp($name, 'name') || !strcasecmp($name, 'title')) { + return $name; + } + } + $pk = $class::primaryKey(); + return $pk[0]; + } } diff --git a/framework/yii/gii/generators/crud/form.php b/framework/yii/gii/generators/crud/form.php index 095791f..ee4771b 100644 --- a/framework/yii/gii/generators/crud/form.php +++ b/framework/yii/gii/generators/crud/form.php @@ -6,11 +6,12 @@ */ echo $form->field($generator, 'modelClass'); -echo $form->field($generator, 'controllerID'); +echo $form->field($generator, 'controllerClass'); echo $form->field($generator, 'baseControllerClass'); +echo $form->field($generator, 'moduleID'); +echo $form->field($generator, 'enableSearch')->checkbox(); +echo $form->field($generator, 'searchModelClass'); echo $form->field($generator, 'indexWidgetType')->dropDownList(array( 'grid' => 'GridView', 'list' => 'ListView', )); -echo $form->field($generator, 'enableSearch')->checkbox(); -echo $form->field($generator, 'searchModelClass'); diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php index 47c193f..7b3cce8 100644 --- a/framework/yii/gii/generators/crud/templates/controller.php +++ b/framework/yii/gii/generators/crud/templates/controller.php @@ -1,180 +1,152 @@ - - -class controllerClass; ?> extends baseControllerClass."\n"; ?> -{ - /** - * @var string the default layout for the views. Defaults to '//layouts/column2', meaning - * using two-column layout. See 'protected/views/layouts/column2.php'. - */ - public $layout='//layouts/column2'; - /** - * @return array action filters - */ - public function filters() - { - return array( - 'accessControl', // perform access control for CRUD operations - 'postOnly + delete', // we only allow deletion via POST request - ); +$pos = strrpos($generator->controllerClass, '\\'); +$ns = ltrim(substr($generator->controllerClass, 0, $pos), '\\'); +$controllerClass = substr($generator->controllerClass, $pos + 1); +$pos = strrpos($generator->modelClass, '\\'); +$modelClass = $pos === false ? $generator->modelClass : substr($generator->modelClass, $pos + 1); + +/** @var \yii\db\ActiveRecord $class */ +$class = $generator->modelClass; +$pks = $class::primaryKey(); +$schema = $class::getTableSchema(); +if (count($pks) === 1) { + $ids = '$id'; + $params = "array('id' => \$model->{$pks[0]})"; + $paramComments = '@param ' . $schema->columns[$pks[0]]->phpType . ' $id'; +} else { + $ids = '$' . implode(', $', $pks); + $params = array(); + $paramComments = array(); + foreach ($pks as $pk) { + $paramComments[] = '@param ' . $schema->columns[$pk]->phpType . ' $' . $pk; + $params[] = "'$pk' => \$model->$pk"; } + $params = implode(', ', $params); + $paramComments = implode("\n\t * ", $paramComments); +} - /** - * Specifies the access control rules. - * This method is used by the 'accessControl' filter. - * @return array access control rules - */ - public function accessRules() - { - return array( - array('allow', // allow all users to perform 'index' and 'view' actions - 'actions'=>array('index','view'), - 'users'=>array('*'), - ), - array('allow', // allow authenticated user to perform 'create' and 'update' actions - 'actions'=>array('create','update'), - 'users'=>array('@'), - ), - array('allow', // allow admin user to perform 'admin' and 'delete' actions - 'actions'=>array('admin','delete'), - 'users'=>array('admin'), - ), - array('deny', // deny all users - 'users'=>array('*'), - ), - ); - } +echo " + +namespace ; + +use modelClass, '\\'); ?>; +use yii\data\ActiveDataProvider; +use baseControllerClass, '\\'); ?>; +use yii\web\HttpException; +/** + * implements the CRUD actions for model. + */ +class extends baseControllerClass) . "\n"; ?> +{ /** - * Displays a particular model. - * @param integer $id the ID of the model to be displayed + * Displays a single model. + * + * @return mixed */ - public function actionView($id) + public function actionView() { - $this->render('view',array( - 'model'=>$this->loadModel($id), + return $this->render('view', array( + 'model' => $this->findModel(), )); } /** - * Creates a new model. + * Creates a new model. * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed */ public function actionCreate() { - $model=new modelClass; ?>; - - // Uncomment the following line if AJAX validation is needed - // $this->performAjaxValidation($model); - - if(isset($_POST['modelClass; ?>'])) - { - $model->attributes=$_POST['modelClass; ?>']; - if($model->save()) - $this->redirect(array('view','id'=>$model->tableSchema->primaryKey; ?>)); + $model = new ; + + if ($model->load($_POST) && $model->save()) { + return $this->redirect(array('view', )); + } else { + return $this->render('create', array( + 'model' => $model, + )); } - - $this->render('create',array( - 'model'=>$model, - )); } /** - * Updates a particular model. + * Updates an existing model. * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id the ID of the model to be updated + * + * @return mixed */ - public function actionUpdate($id) + public function actionUpdate() { - $model=$this->loadModel($id); - - // Uncomment the following line if AJAX validation is needed - // $this->performAjaxValidation($model); - - if(isset($_POST['modelClass; ?>'])) - { - $model->attributes=$_POST['modelClass; ?>']; - if($model->save()) - $this->redirect(array('view','id'=>$model->tableSchema->primaryKey; ?>)); + $model = $this->findModel(); + + if ($model->load($_POST) && $model->save()) { + return $this->redirect(array('view', )); + } else { + return $this->render('update', array( + 'model' => $model, + )); } - - $this->render('update',array( - 'model'=>$model, - )); } /** - * Deletes a particular model. - * If deletion is successful, the browser will be redirected to the 'admin' page. - * @param integer $id the ID of the model to be deleted + * Deletes an existing model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @return mixed */ - public function actionDelete($id) + public function actionDelete() { - $this->loadModel($id)->delete(); - - // if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser - if(!isset($_GET['ajax'])) - $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin')); + $this->findModel()->delete(); + return $this->redirect(array('index')); } /** * Lists all models. + * @return mixed */ public function actionIndex() { - $dataProvider=new CActiveDataProvider('modelClass; ?>'); - $this->render('index',array( - 'dataProvider'=>$dataProvider, + $dataProvider = new ActiveDataProvider(''); + return $this->render('index', array( + 'dataProvider' => $dataProvider, )); } /** - * Manages all models. + * Returns the data model based on its primary key value. + * If the data model is not found, a 404 HTTP exception will be thrown. + * + * @return the loaded model + * @throws HttpException if the model cannot be found */ - public function actionAdmin() + protected function findModel() { - $model=new modelClass; ?>('search'); - $model->unsetAttributes(); // clear any default values - if(isset($_GET['modelClass; ?>'])) - $model->attributes=$_GET['modelClass; ?>']; - - $this->render('admin',array( - 'model'=>$model, - )); - } - - /** - * Returns the data model based on the primary key given in the GET variable. - * If the data model is not found, an HTTP exception will be raised. - * @param integer $id the ID of the model to be loaded - * @return modelClass; ?> the loaded model - * @throws CHttpException - */ - public function loadModel($id) - { - $model=modelClass; ?>::model()->findByPk($id); - if($model===null) - throw new CHttpException(404,'The requested page does not exist.'); - return $model; + \$$pk"; } - - /** - * Performs the AJAX validation. - * @param modelClass; ?> $model the model to be validated - */ - protected function performAjaxValidation($model) - { - if(isset($_POST['ajax']) && $_POST['ajax']==='class2id($this->modelClass); ?>-form') - { - echo CActiveForm::validate($model); - Yii::app()->end(); + $condition = 'array(' . implode(', ', $condition) . ')'; +} +?> + $model = ::find(); + if ($model === null) { + throw new HttpException(404, 'The requested page does not exist.'); } + return $model; } } diff --git a/framework/yii/gii/generators/crud/templates/views/create.php b/framework/yii/gii/generators/crud/templates/views/create.php index 9fd3ccf..48a2318 100644 --- a/framework/yii/gii/generators/crud/templates/views/create.php +++ b/framework/yii/gii/generators/crud/templates/views/create.php @@ -1,27 +1,31 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ -pluralize($this->class2name($this->modelClass)); -echo "\$this->breadcrumbs=array( - '$label'=>array('index'), - 'Create', -);\n"; +echo " -$this->menu=array( - array('label'=>'List modelClass; ?>', 'url'=>array('index')), - array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), -); +use yii\helpers\Html; + +/** + * @var yii\base\View $this + * @var modelClass, '\\'); ?> $model + */ + +$this->title = 'Create modelClass)); ?>'; ?> +
+ +

echo Html::encode($this->title); ?>

-

Create modelClass; ?>

+ echo $this->render('_form', array( + 'model' => $model, + )); ?> -renderPartial('_form', array('model'=>\$model)); ?>"; ?> +
diff --git a/framework/yii/gii/generators/crud/templates/views/update.php b/framework/yii/gii/generators/crud/templates/views/update.php index 4adc72d..8dc3c73 100644 --- a/framework/yii/gii/generators/crud/templates/views/update.php +++ b/framework/yii/gii/generators/crud/templates/views/update.php @@ -1,31 +1,31 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ -guessNameColumn($this->tableSchema->columns); -$label=$this->pluralize($this->class2name($this->modelClass)); -echo "\$this->breadcrumbs=array( - '$label'=>array('index'), - \$model->{$nameColumn}=>array('view','id'=>\$model->{$this->tableSchema->primaryKey}), - 'Update', -);\n"; +echo " -$this->menu=array( - array('label'=>'List modelClass; ?>', 'url'=>array('index')), - array('label'=>'Create modelClass; ?>', 'url'=>array('create')), - array('label'=>'View modelClass; ?>', 'url'=>array('view', 'id'=>$model->tableSchema->primaryKey; ?>)), - array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), -); +use yii\helpers\Html; + +/** +* @var yii\base\View $this +* @var modelClass, '\\'); ?> $model +*/ + +$this->title = 'Modify modelClass)); ?>: ' . $model->getNameAttribute(); ?>; ?> +
+ +

echo Html::encode($this->title); ?>

-

Update modelClass." {$this->tableSchema->primaryKey}; ?>"; ?>

+ echo $this->render('_form', array( + 'model' => $model, + )); ?> -renderPartial('_form', array('model'=>\$model)); ?>"; ?> \ No newline at end of file +
diff --git a/framework/yii/gii/generators/crud/templates/views/view.php b/framework/yii/gii/generators/crud/templates/views/view.php index 3363130..baca46f 100644 --- a/framework/yii/gii/generators/crud/templates/views/view.php +++ b/framework/yii/gii/generators/crud/templates/views/view.php @@ -1,39 +1,27 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ -guessNameColumn($this->tableSchema->columns); -$label=$this->pluralize($this->class2name($this->modelClass)); -echo "\$this->breadcrumbs=array( - '$label'=>array('index'), - \$model->{$nameColumn}, -);\n"; +echo " -$this->menu=array( - array('label'=>'List modelClass; ?>', 'url'=>array('index')), - array('label'=>'Create modelClass; ?>', 'url'=>array('create')), - array('label'=>'Update modelClass; ?>', 'url'=>array('update', 'id'=>$model->tableSchema->primaryKey; ?>)), - array('label'=>'Delete modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->tableSchema->primaryKey; ?>),'confirm'=>'Are you sure you want to delete this item?')), - array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), -); -?> +use yii\helpers\Html; -

View modelClass." #{$this->tableSchema->primaryKey}; ?>"; ?>

+/** +* @var yii\base\View $this +* @var modelClass, '\\'); ?> $model +*/ - $this->widget('zii.widgets.CDetailView', array( - 'data'=>$model, - 'attributes'=>array( -tableSchema->columns as $column) - echo "\t\t'".$column->name."',\n"; +$this->title = $model->getNameAttribute(); ?>; ?> - ), -)); ?> +
+ +

echo Html::encode($this->title); ?>

+ +
From 409500000a65d8fc970e56bc41286aa3c246b6c2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 8 Sep 2013 02:26:35 +0400 Subject: [PATCH 091/156] Validation reference, validators phpdoc fixes --- docs/guide/validation.md | 188 +++++++++++++++++++-- .../yii/validators/RegularExpressionValidator.php | 2 - framework/yii/validators/StringValidator.php | 2 +- 3 files changed, 172 insertions(+), 20 deletions(-) diff --git a/docs/guide/validation.md b/docs/guide/validation.md index 7bfeb96..0322573 100644 --- a/docs/guide/validation.md +++ b/docs/guide/validation.md @@ -7,22 +7,176 @@ In order to learn model validation basics please refer to [Model, Validation sub Standard Yii validators ----------------------- -- `boolean`: [[BooleanValidator]] -- `captcha`: [[CaptchaValidator]] -- `compare`: [[CompareValidator]] -- `date`: [[DateValidator]] -- `default`: [[DefaultValueValidator]] -- `double`: [[NumberValidator]] -- `email`: [[EmailValidator]] -- `exist`: [[ExistValidator]] -- `file`: [[FileValidator]] -- `filter`: [[FilterValidator]] -- `in`: [[RangeValidator]] -- `integer`: [[NumberValidator]] -- `match`: [[RegularExpressionValidator]] -- `required`: [[RequiredValidator]] -- `string`: [[StringValidator]] -- `unique`: [[UniqueValidator]] -- `url`: [[UrlValidator]] +Standard Yii validators could be specified using aliases instead of referring to class names. Here's the list of all +validators budled with Yii with their most useful properties: + +### `boolean`: [[BooleanValidator]] + +Checks if the attribute value is a boolean value. + +- `trueValue`, the value representing true status. _(1)_ +- `falseValue`, the value representing false status. _(0)_ +- `strict`, whether to compare the type of the value and `trueValue`/`falseValue`. _(false)_ + +### `captcha`: [[CaptchaValidator]] + +Validates that the attribute value is the same as the verification code displayed in the CAPTCHA. Should be used together +with [[CaptchaAction]]. + +- `caseSensitive` whether the comparison is case sensitive. _(false)_ +- `captchaAction` the route of the controller action that renders the CAPTCHA image. _('site/captcha')_ + +### `compare`: [[CompareValidator]] + +Compares the specified attribute value with another value and validates if they are equal. + +- `compareAttribute` the name of the attribute to be compared with. _(currentAttribute_repeat)_ +- `compareValue` the constant value to be compared with. +- `operator` the operator for comparison. _('==')_ + +### `date`: [[DateValidator]] + +Verifies if the attribute represents a date, time or datetime in a proper format. + +- `format` the date format that the value being validated should follow accodring to [[http://www.php.net/manual/en/datetime.createfromformat.php]]. _('Y-m-d')_ +- `timestampAttribute` the name of the attribute to receive the parsing result. + +### `default`: [[DefaultValueValidator]] + +Sets the attribute to be the specified default value. + +- `value` the default value to be set to the specified attributes. + +### `double`: [[NumberValidator]] + +Validates that the attribute value is a number. + +- `max` limit of the number. _(null)_ +- `min` lower limit of the number. _(null)_ + +### `email`: [[EmailValidator]] + +Validates that the attribute value is a valid email address. + +- `allowName` whether to allow name in the email address (e.g. `John Smith `). _(false)_. +- `checkMX` whether to check the MX record for the email address. _(false)_ +- `checkPort` whether to check port 25 for the email address. _(false)_ +- `enableIDN` whether validation process should take into account IDN (internationalized domain names). _(false)_ + +### `exist`: [[ExistValidator]] + +Validates that the attribute value exists in a table. + +- `className` the ActiveRecord class name or alias of the class that should be used to look for the attribute value being + validated. _(ActiveRecord class of the attribute being validated)_ +- `attributeName` the ActiveRecord attribute name that should be used to look for the attribute value being validated. + _(name of the attribute being validated)_ + +### `file`: [[FileValidator]] + +Verifies if an attribute is receiving a valid uploaded file. + +- `types` a list of file name extensions that are allowed to be uploaded. _(any)_ +- `minSize` the minimum number of bytes required for the uploaded file. +- `maxSize` the maximum number of bytes required for the uploaded file. +- `maxFiles` the maximum file count the given attribute can hold. _(1)_ + +### `filter`: [[FilterValidator]] + +Converts the attribute value according to a filter. + +- `filter` PHP callback that defines a filter. + +Typically a callback is either the name of PHP function: + +```php +array('password', 'filter', 'filter' => 'trim'), +``` + +Or an anonymous function: + +```php +array('text', 'filter', 'filter' => function ($value) { + // here we are removing all swear words from text + return $newValue; +}), +``` + +### `in`: [[RangeValidator]] + +Validates that the attribute value is among a list of values. + +- `range` list of valid values that the attribute value should be among. +- `strict` whether the comparison is strict (both type and value must be the same). _(false)_ +- `not` whether to invert the validation logic. _(false)_ + +### `integer`: [[NumberValidator]] + +Validates that the attribute value is an integer number. + +- `max` limit of the number. _(null)_ +- `min` lower limit of the number. _(null)_ + +### `match`: [[RegularExpressionValidator]] + +Validates that the attribute value matches the specified pattern defined by regular expression. + +- `pattern` the regular expression to be matched with. +- `not` whether to invert the validation logic. _(false)_ + +### `required`: [[RequiredValidator]] + +Validates that the specified attribute does not have null or empty value. + +- `requiredValue` the desired value that the attribute must have. _(any)_ +- `strict` whether the comparison between the attribute value and [[requiredValue]] is strict. _(false)_ + +### `safe`: [[SafeValidator]] + +Serves as a dummy validator whose main purpose is to mark the attributes to be safe for massive assignment. + +### `string`: [[StringValidator]] + +Validates that the attribute value is of certain length. + +- `length` specifies the length limit of the value to be validated. Can be `exactly X`, `array(min X)`, `array(min X, max Y)`. +- `max` maximum length. If not set, it means no maximum length limit. +- `min` minimum length. If not set, it means no minimum length limit. +- `encoding` the encoding of the string value to be validated. _([[\yii\base\Application::charset]])_ + +### `unique`: [[UniqueValidator]] + +Validates that the attribute value is unique in the corresponding database table. + +- `className` the ActiveRecord class name or alias of the class that should be used to look for the attribute value being + validated. _(ActiveRecord class of the attribute being validated)_ +- `attributeName` the ActiveRecord attribute name that should be used to look for the attribute value being validated. + _(name of the attribute being validated)_ + +### `url`: [[UrlValidator]] + +Validates that the attribute value is a valid http or https URL. + +- `validSchemes` list of URI schemes which should be considered valid. _array('http', 'https')_ +- `defaultScheme` the default URI scheme. If the input doesn't contain the scheme part, the default scheme will be + prepended to it. _(null)_ +- `enableIDN` whether validation process should take into account IDN (internationalized domain names). _(false)_ + +Validating values out of model context +-------------------------------------- + +Sometimes you need to validate a value that is not bound to any model such as email. In Yii `Validator` class has +`validateValue` method that can help you with it. Not all validator classes have it implemented but the ones that can +operate without model do. In our case to validate an email we can do the following: + +```php +$email = 'test@example.com'; +$validator = new yii\validators\EmailValidator(); +if ($validator->validateValue($email)) { + echo 'Email is valid.'; +} else { + echo 'Email is not valid.' +} +``` TBD: refer to http://www.yiiframework.com/wiki/56/ for the format \ No newline at end of file diff --git a/framework/yii/validators/RegularExpressionValidator.php b/framework/yii/validators/RegularExpressionValidator.php index 4ae2099..df57e7d 100644 --- a/framework/yii/validators/RegularExpressionValidator.php +++ b/framework/yii/validators/RegularExpressionValidator.php @@ -30,7 +30,6 @@ class RegularExpressionValidator extends Validator /** * @var boolean whether to invert the validation logic. Defaults to false. If set to true, * the regular expression defined via [[pattern]] should NOT match the attribute value. - * @throws InvalidConfigException if the "pattern" is not a valid regular expression **/ public $not = false; @@ -82,7 +81,6 @@ class RegularExpressionValidator extends Validator * @param \yii\base\View $view the view object that is going to be used to render views or view files * containing a model form with this validator applied. * @return string the client-side validation script. - * @throws InvalidConfigException if the "pattern" is not a valid regular expression */ public function clientValidateAttribute($object, $attribute, $view) { diff --git a/framework/yii/validators/StringValidator.php b/framework/yii/validators/StringValidator.php index 2cbab6c..946ca6e 100644 --- a/framework/yii/validators/StringValidator.php +++ b/framework/yii/validators/StringValidator.php @@ -52,7 +52,7 @@ class StringValidator extends Validator */ public $tooLong; /** - * @var string user-defined error message used when the length of the value is not equal to [[is]]. + * @var string user-defined error message used when the length of the value is not equal to [[length]]. */ public $notEqual; /** From a12f8da1ff960b84d4eabdb9aa17e517fefbc0a3 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 7 Sep 2013 23:37:15 -0400 Subject: [PATCH 092/156] Fixed debugger style. --- framework/yii/debug/assets/main.css | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/yii/debug/assets/main.css b/framework/yii/debug/assets/main.css index 6cb65dd..7953873 100644 --- a/framework/yii/debug/assets/main.css +++ b/framework/yii/debug/assets/main.css @@ -131,6 +131,7 @@ ul.trace { margin: 2px 0 0 0; padding: 0; list-style: none; + white-space: normal; } .callout-danger { From 263dbd264de3fcc8d788ef00b31e17e8eed6dcea Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 7 Sep 2013 23:37:36 -0400 Subject: [PATCH 093/156] Changed default pagination size to 20. --- framework/yii/data/Pagination.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/data/Pagination.php b/framework/yii/data/Pagination.php index 1625fde..04af828 100644 --- a/framework/yii/data/Pagination.php +++ b/framework/yii/data/Pagination.php @@ -98,10 +98,10 @@ class Pagination extends Object */ public $validatePage = true; /** - * @var integer number of items on each page. Defaults to 10. + * @var integer number of items on each page. Defaults to 20. * If it is less than 1, it means the page size is infinite, and thus a single page contains all items. */ - public $pageSize = 10; + public $pageSize = 20; /** * @var integer total number of items. */ From 02e3b451beb07fb3d30ac79168f48f8f85f2c783 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 7 Sep 2013 23:43:26 -0400 Subject: [PATCH 094/156] Removed sorter from the default display of ListView. --- framework/yii/grid/GridView.php | 10 ---------- framework/yii/widgets/ListViewBase.php | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php index 60d325d..a783a75 100644 --- a/framework/yii/grid/GridView.php +++ b/framework/yii/grid/GridView.php @@ -124,16 +124,6 @@ class GridView extends ListViewBase * Both "format" and "label" are optional. They will take default values if absent. */ public $columns = array(); - /** - * @var string the layout that determines how different sections of the list view should be organized. - * The following tokens will be replaced with the corresponding section contents: - * - * - `{summary}`: the summary section. See [[renderSummary()]]. - * - `{items}`: the list items. See [[renderItems()]]. - * - `{sorter}`: the sorter. See [[renderSorter()]]. - * - `{pager}`: the pager. See [[renderPager()]]. - */ - public $layout = "{items}\n{summary}\n{pager}"; public $emptyCell = ' '; /** * @var \yii\base\Model the model that keeps the user-entered filter data. When this property is set, diff --git a/framework/yii/widgets/ListViewBase.php b/framework/yii/widgets/ListViewBase.php index 6be704d..8c2f8f4 100644 --- a/framework/yii/widgets/ListViewBase.php +++ b/framework/yii/widgets/ListViewBase.php @@ -66,7 +66,7 @@ abstract class ListViewBase extends Widget * - `{sorter}`: the sorter. See [[renderSorter()]]. * - `{pager}`: the pager. See [[renderPager()]]. */ - public $layout = "{summary}\n{sorter}\n{items}\n{pager}"; + public $layout = "{summary}\n{items}\n{pager}"; /** From 4edd842558745018d603df568451b8a892f2ab16 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 7 Sep 2013 23:43:47 -0400 Subject: [PATCH 095/156] Added LinkSorter::attributes. --- framework/yii/widgets/LinkSorter.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/yii/widgets/LinkSorter.php b/framework/yii/widgets/LinkSorter.php index b555475..c8b30e5 100644 --- a/framework/yii/widgets/LinkSorter.php +++ b/framework/yii/widgets/LinkSorter.php @@ -28,6 +28,11 @@ class LinkSorter extends Widget */ public $sort; /** + * @var array list of the attributes that support sorting. If not set, it will be determined + * using [[Sort::attributes]]. + */ + public $attributes; + /** * @var array HTML attributes for the sorter container tag. */ public $options = array('class' => 'sorter'); @@ -58,8 +63,9 @@ class LinkSorter extends Widget */ protected function renderSortLinks() { + $attributes = empty($this->atttributes) ? array_keys($this->sort->attributes) : $this->attributes; $links = array(); - foreach (array_keys($this->sort->attributes) as $name) { + foreach ($attributes as $name) { $links[] = $this->sort->link($name); } return Html::ul($links, array('encode' => false)); From e683bd3ddb38ef24d427302d80ed23580aea2d3d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 8 Sep 2013 08:05:49 -0400 Subject: [PATCH 096/156] CRUD WIP --- framework/yii/gii/generators/crud/Generator.php | 53 +++++++++++++++- .../gii/generators/crud/templates/controller.php | 36 ++++++----- .../gii/generators/crud/templates/views/_form.php | 72 ++++++++++------------ .../gii/generators/crud/templates/views/index.php | 65 +++++++++++++------ .../gii/generators/crud/templates/views/update.php | 8 +-- .../gii/generators/crud/templates/views/view.php | 23 ++++++- 6 files changed, 174 insertions(+), 83 deletions(-) diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 3ed51c1..7f1bff0 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -151,7 +151,7 @@ class Generator extends \yii\gii\Generator $templatePath = $this->getTemplatePath() . '/views'; foreach (scandir($templatePath) as $file) { - if (!in_array($file, array('create.php', 'update.php', 'view.php'))) { + if (!in_array($file, array('index.php', 'create.php', 'update.php', 'view.php', '_form.php'))) { continue; } if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { @@ -205,4 +205,55 @@ class Generator extends \yii\gii\Generator $pk = $class::primaryKey(); return $pk[0]; } + + /** + * @param ActiveRecord $model + * @param string $attribute + * @return string + */ + public function generateActiveField($model, $attribute) + { + $tableSchema = $model->getTableSchema(); + if (!isset($tableSchema->columns[$attribute])) { + return "\$form->field(\$model, '$attribute');"; + } + $column = $tableSchema->columns[$attribute]; + if ($column->phpType === 'boolean') { + return "\$form->field(\$model, '$attribute')->checkbox();"; + } elseif ($column->type === 'text') { + return "\$form->field(\$model, '$attribute')->textarea(array('rows' => 6));"; + } else { + if (preg_match('/^(password|pass|passwd|passcode)$/i', $column->name)) { + $input = 'passwordInput'; + } else { + $input = 'textInput'; + } + if ($column->phpType !== 'string' || $column->size === null) { + return "\$form->field(\$model, '$attribute')->$input();"; + } else { + return "\$form->field(\$model, '$attribute')->$input(array('maxlength' => $column->size));"; + } + } + } + + /** + * @param \yii\db\ColumnSchema $column + * @return string + */ + public function generateColumnFormat($column) + { + if ($column->phpType === 'boolean') { + return 'boolean'; + } elseif ($column->type === 'text') { + return 'ntext'; + } elseif (stripos($column->name, 'time') !== false && $column->phpType === 'integer') { + return 'datetime'; + } elseif (stripos($column->name, 'email') !== false) { + return 'email'; + } elseif (stripos($column->name, 'url') !== false) { + return 'url'; + } else { + return 'text'; + } + } } diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php index 7b3cce8..da915c8 100644 --- a/framework/yii/gii/generators/crud/templates/controller.php +++ b/framework/yii/gii/generators/crud/templates/controller.php @@ -51,6 +51,20 @@ use yii\web\HttpException; class extends baseControllerClass) . "\n"; ?> { /** + * Lists all models. + * @return mixed + */ + public function actionIndex() + { + $dataProvider = new ActiveDataProvider(array( + 'query' => ::find(), + )); + return $this->render('index', array( + 'dataProvider' => $dataProvider, + )); + } + + /** * Displays a single model. * * @return mixed @@ -112,20 +126,8 @@ class extends '); - return $this->render('index', array( - 'dataProvider' => $dataProvider, - )); - } - - /** - * Returns the data model based on its primary key value. - * If the data model is not found, a 404 HTTP exception will be thrown. + * Finds the model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. * * @return the loaded model * @throws HttpException if the model cannot be found @@ -143,10 +145,10 @@ if (count($pks) === 1) { $condition = 'array(' . implode(', ', $condition) . ')'; } ?> - $model = ::find(); - if ($model === null) { + if (($model = ::find()) !== null) { + return $model; + } else { throw new HttpException(404, 'The requested page does not exist.'); } - return $model; } } diff --git a/framework/yii/gii/generators/crud/templates/views/_form.php b/framework/yii/gii/generators/crud/templates/views/_form.php index caf6f2a..6160e72 100644 --- a/framework/yii/gii/generators/crud/templates/views/_form.php +++ b/framework/yii/gii/generators/crud/templates/views/_form.php @@ -1,49 +1,45 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ -/* @var $form CActiveForm */ -?> -
- -beginWidget('CActiveForm', array( - 'id'=>'".$this->class2id($this->modelClass)."-form', - // Please note: When you enable ajax validation, make sure the corresponding - // controller action is handling ajax validation correctly. - // There is a call to performAjaxValidation() commented in generated controller code. - // See class documentation of CActiveForm for details on this. - 'enableAjaxValidation'=>false, -)); ?>\n"; ?> +/** @var \yii\db\ActiveRecord $model */ +$class = $generator->modelClass; +$model = new $class; +$safeAttributes = $model->safeAttributes(); +if (empty($safeAttributes)) { + $safeAttributes = $model->getTableSchema()->columnNames; +} -

Fields with * are required.

+echo " - errorSummary(\$model); ?>\n"; ?> +use yii\helpers\Html; +use yii\widgets\ActiveForm; -tableSchema->columns as $column) -{ - if($column->autoIncrement) - continue; +/** + * @var yii\base\View $this + * @var modelClass, '\\'); ?> $model + * @var yii\widgets\ActiveForm $form + */ ?> -
- generateActiveLabel($this->modelClass,$column)."; ?>\n"; ?> - generateActiveField($this->modelClass,$column)."; ?>\n"; ?> - error(\$model,'{$column->name}'); ?>\n"; ?> -
- -
- isNewRecord ? 'Create' : 'Save'); ?>\n"; ?> -
+
+ + $form = ActiveForm::begin(); ?> + +generateActiveField($model, $attribute) . " ?>\n\n"; +} ?> +
+ echo Html::submitButton($model->isNewRecord ? 'Create' : 'Update', array('class' => 'btn btn-primary')); ?> +
-endWidget(); ?>\n"; ?> + ActiveForm::end(); ?> -
\ No newline at end of file +
diff --git a/framework/yii/gii/generators/crud/templates/views/index.php b/framework/yii/gii/generators/crud/templates/views/index.php index a115251..fbac7c3 100644 --- a/framework/yii/gii/generators/crud/templates/views/index.php +++ b/framework/yii/gii/generators/crud/templates/views/index.php @@ -1,29 +1,54 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $dataProvider CActiveDataProvider */ -pluralize($this->class2name($this->modelClass)); -echo "\$this->breadcrumbs=array( - '$label', -);\n"; +/** @var \yii\db\ActiveRecord $model */ +$class = $generator->modelClass; +$pks = $class::primaryKey(); +if (count($pks) === 1) { + $viewUrl = "array('view', 'id' => \$model->{$pks[0]})"; +} else { + $params = array(); + foreach ($pks as $pk) { + $params[] = "'$pk' => \$model->$pk"; + } + $viewUrl = "array('view', " . implode(', ', $params) . ')'; +} + +$nameAttribute = $generator->getNameAttribute(); + +echo " -$this->menu=array( - array('label'=>'Create modelClass; ?>', 'url'=>array('create')), - array('label'=>'Manage modelClass; ?>', 'url'=>array('admin')), -); +use yii\helpers\Html; +use indexWidgetType === 'grid' ? 'yii\grid\GridView' : 'yii\widgets\ListView'; ?>; + +/** + * @var yii\base\View $this + * @var yii\data\ActiveDataProvider $dataProvider + */ + +$this->title = 'modelClass))); ?>'; ?> +
+ +

echo Html::encode($this->title); ?>

+ +indexWidgetType === 'grid'): ?> -

+ + echo ListView::widget(array( + 'dataProvider' => $dataProvider, + 'itemView' => function ($model, $key, $index, $widget) { + return Html::a(Html::encode($model->), ); + }, + )); ?> + - $this->widget('zii.widgets.CListView', array( - 'dataProvider'=>$dataProvider, - 'itemView'=>'_view', -)); ?> +
diff --git a/framework/yii/gii/generators/crud/templates/views/update.php b/framework/yii/gii/generators/crud/templates/views/update.php index 8dc3c73..bacc1ec 100644 --- a/framework/yii/gii/generators/crud/templates/views/update.php +++ b/framework/yii/gii/generators/crud/templates/views/update.php @@ -14,11 +14,11 @@ echo "modelClass, '\\'); ?> $model -*/ + * @var yii\base\View $this + * @var modelClass, '\\'); ?> $model + */ -$this->title = 'Modify modelClass)); ?>: ' . $model->getNameAttribute(); ?>; +$this->title = 'Update modelClass)); ?>: ' . $model->getNameAttribute(); ?>; ?>
diff --git a/framework/yii/gii/generators/crud/templates/views/view.php b/framework/yii/gii/generators/crud/templates/views/view.php index baca46f..214f10b 100644 --- a/framework/yii/gii/generators/crud/templates/views/view.php +++ b/framework/yii/gii/generators/crud/templates/views/view.php @@ -8,15 +8,20 @@ use yii\helpers\StringHelper; * @var yii\gii\generators\crud\Generator $generator */ +/** @var \yii\db\ActiveRecord $model */ +$class = $generator->modelClass; +$model = new $class; + echo " use yii\helpers\Html; +use yii\widgets\DetailView; /** -* @var yii\base\View $this -* @var modelClass, '\\'); ?> $model -*/ + * @var yii\base\View $this + * @var modelClass, '\\'); ?> $model + */ $this->title = $model->getNameAttribute(); ?>; ?> @@ -24,4 +29,16 @@ $this->title = $model->getNameAttribute(); ?>;

echo Html::encode($this->title); ?>

+ echo DetailView::widget(array( + 'model' => $model, + 'attributes' => array( +getTableSchema()->columns as $column) { + $format = $generator->generateColumnFormat($column); + echo "\t\t\t'" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n"; +} +?> + ), + )); ?> +
From b6f07859c1c28954eee41d6931c04e6cdec7a143 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 8 Sep 2013 08:47:52 -0400 Subject: [PATCH 097/156] Added support for getting all tables for pgsql. --- framework/yii/db/pgsql/Schema.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php index 8acb7bd..9693dd6 100644 --- a/framework/yii/db/pgsql/Schema.php +++ b/framework/yii/db/pgsql/Schema.php @@ -129,6 +129,35 @@ class Schema extends \yii\db\Schema } /** + * Returns all table names in the database. + * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema. + * If not empty, the returned table names will be prefixed with the schema name. + * @return array all table names in the database. + */ + protected function findTableNames($schema = '') + { + if ($schema === '') { + $schema = $this->defaultSchema; + } + $sql = <<db->createCommand($sql); + $command->bindParam(':schema', $schema); + $rows = $command->queryAll(); + $names = array(); + foreach ($rows as $row) { + if ($schema === $this->defaultSchema) { + $names[] = $row['table_name']; + } else { + $names[] = $row['table_schema'] . '.' . $row['table_name']; + } + } + return $names; + } + + /** * Collects the foreign key column details for the given table. * @param TableSchema $table the table metadata */ From 851dfe6333daa069ebfa4c67d90d73611f4a6342 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 8 Sep 2013 09:58:28 -0400 Subject: [PATCH 098/156] Use horizontal form for login page. --- apps/basic/views/site/login.php | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/apps/basic/views/site/login.php b/apps/basic/views/site/login.php index 524b0cc..f1a02b5 100644 --- a/apps/basic/views/site/login.php +++ b/apps/basic/views/site/login.php @@ -15,20 +15,32 @@ $this->params['breadcrumbs'][] = $this->title;

Please fill out the following fields to login:

-
-
- 'login-form')); ?> - field($model, 'username'); ?> - field($model, 'password')->passwordInput(); ?> - field($model, 'rememberMe')->checkbox(); ?> -
- 'btn btn-primary')); ?> -
- -
-
- You may login with admin/admin or demo/demo.
- To modify the username/password, please check out the code app\models\User::$users. + 'login-form', + 'options' => array('class' => 'form-horizontal'), + 'fieldConfig' => array( + 'template' => "
{label}
\n
{input}
\n
{error}
", + ), + )); ?> + + field($model, 'username'); ?> + + field($model, 'password')->passwordInput(); ?> + + field($model, 'rememberMe', array( + 'template' => "
{input}
\n
{error}
", + ))->checkbox(); ?> + +
+
+ 'btn btn-primary')); ?> +
+ + + +
+ You may login with admin/admin or demo/demo.
+ To modify the username/password, please check out the code app\models\User::$users.
From ea561ed3e03d09d9b5e217db85f516782cbb7e28 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 07:05:35 -0400 Subject: [PATCH 099/156] Fixes #861. --- framework/yii/widgets/ActiveField.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php index bba1ead..6c06483 100644 --- a/framework/yii/widgets/ActiveField.php +++ b/framework/yii/widgets/ActiveField.php @@ -410,6 +410,7 @@ class ActiveField extends Component */ public function checkbox($options = array(), $enclosedByLabel = true) { + $options = array_merge($this->inputOptions, $options); if ($enclosedByLabel) { if (!isset($options['label'])) { $options['label'] = Html::encode($this->model->getAttributeLabel($this->attribute)); From 791905e8bbf925d7cd149f4473e863bfc1ec2ed5 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 08:44:40 -0400 Subject: [PATCH 100/156] Reverted previous change as it breaks layout. --- framework/yii/widgets/ActiveField.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php index 6c06483..51f27ab 100644 --- a/framework/yii/widgets/ActiveField.php +++ b/framework/yii/widgets/ActiveField.php @@ -374,7 +374,6 @@ class ActiveField extends Component */ public function radio($options = array(), $enclosedByLabel = true) { - $options = array_merge($this->inputOptions, $options); if ($enclosedByLabel) { if (!isset($options['label'])) { $options['label'] = Html::encode($this->model->getAttributeLabel($this->attribute)); @@ -410,7 +409,6 @@ class ActiveField extends Component */ public function checkbox($options = array(), $enclosedByLabel = true) { - $options = array_merge($this->inputOptions, $options); if ($enclosedByLabel) { if (!isset($options['label'])) { $options['label'] = Html::encode($this->model->getAttributeLabel($this->attribute)); From 347e79a4674e655fd88161c6bd5c69467b8e2d65 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 21:27:58 -0400 Subject: [PATCH 101/156] Fixes #863: adjusted horizontal form layout --- apps/basic/views/layouts/main.php | 2 +- apps/basic/views/site/login.php | 21 ++++++++------- .../gii/generators/crud/templates/views/_view.php | 31 ---------------------- 3 files changed, 12 insertions(+), 42 deletions(-) delete mode 100644 framework/yii/gii/generators/crud/templates/views/_view.php diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php index 04a2f33..240c2a3 100644 --- a/apps/basic/views/layouts/main.php +++ b/apps/basic/views/layouts/main.php @@ -36,7 +36,7 @@ app\config\AppAsset::register($this); array('label' => 'Contact', 'url' => array('/site/contact')), Yii::$app->user->isGuest ? array('label' => 'Login', 'url' => array('/site/login')) : - array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')), + array('label' => 'Logout (' . Html::encode(Yii::$app->user->identity->username) .')' , 'url' => array('/site/logout')), ), )); NavBar::end(); diff --git a/apps/basic/views/site/login.php b/apps/basic/views/site/login.php index f1a02b5..f61d9d7 100644 --- a/apps/basic/views/site/login.php +++ b/apps/basic/views/site/login.php @@ -19,23 +19,24 @@ $this->params['breadcrumbs'][] = $this->title; 'id' => 'login-form', 'options' => array('class' => 'form-horizontal'), 'fieldConfig' => array( - 'template' => "
{label}
\n
{input}
\n
{error}
", + 'template' => "{label}\n
{input}
\n
{error}
", + 'labelOptions' => array('class' => 'col-lg-1 control-label'), ), )); ?> - field($model, 'username'); ?> + field($model, 'username'); ?> - field($model, 'password')->passwordInput(); ?> + field($model, 'password')->passwordInput(); ?> - field($model, 'rememberMe', array( - 'template' => "
{input}
\n
{error}
", - ))->checkbox(); ?> + field($model, 'rememberMe', array( + 'template' => "
{input}
\n
{error}
", + ))->checkbox(); ?> -
-
- 'btn btn-primary')); ?> -
+
+
+ 'btn btn-primary')); ?>
+
diff --git a/framework/yii/gii/generators/crud/templates/views/_view.php b/framework/yii/gii/generators/crud/templates/views/_view.php deleted file mode 100644 index 0f11051..0000000 --- a/framework/yii/gii/generators/crud/templates/views/_view.php +++ /dev/null @@ -1,31 +0,0 @@ - - -/* @var $this getControllerClass(); ?> */ -/* @var $data getModelClass(); ?> */ -?> - -
- -getAttributeLabel('{$this->tableSchema->primaryKey}')); ?>:\n"; -echo "\t{$this->tableSchema->primaryKey}), array('view', 'id'=>\$data->{$this->tableSchema->primaryKey})); ?>\n\t
\n\n"; -$count=0; -foreach($this->tableSchema->columns as $column) -{ - if($column->isPrimaryKey) - continue; - if(++$count==7) - echo "\tgetAttributeLabel('{$column->name}')); ?>:\n"; - echo "\t{$column->name}); ?>\n\t
\n\n"; -} -if($count>=7) - echo "\t*/ ?>\n"; -?> - -
\ No newline at end of file From e9a5b92dd9a3f584b20322fdca8d0c3cb995a376 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 21:59:50 -0400 Subject: [PATCH 102/156] Added StringHelper::dirname() --- framework/yii/helpers/StringHelperBase.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/framework/yii/helpers/StringHelperBase.php b/framework/yii/helpers/StringHelperBase.php index 54dabda..cbb696e 100644 --- a/framework/yii/helpers/StringHelperBase.php +++ b/framework/yii/helpers/StringHelperBase.php @@ -47,8 +47,8 @@ class StringHelperBase /** * Returns the trailing name component of a path. - * This method does the same as the php function `basename()` except that it will - * always use \ and / as directory separators, independent of the operating system. + * This method is similar to the php function `basename()` except that it will + * treat both \ and / as directory separators, independent of the operating system. * This method was mainly created to work on php namespaces. When working with real * file paths, php's `basename()` should work fine for you. * Note: this method is not aware of the actual filesystem, or path components such as "..". @@ -70,6 +70,24 @@ class StringHelperBase } /** + * Returns parent directory's path. + * This method is similar to `dirname()` except that it will treat + * both \ and / as directory separators, independent of the operating system. + * @param string $path A path string. + * @return string the parent directory's path. + * @see http://www.php.net/manual/en/function.basename.php + */ + public static function dirname($path) + { + $pos = mb_strrpos(str_replace('\\', '/', $path), '/'); + if ($pos !== false) { + return mb_substr($path, 0, $pos); + } else { + return $path; + } + } + + /** * Compares two strings or string arrays, and return their differences. * This is a wrapper of the [phpspec/php-diff](https://packagist.org/packages/phpspec/php-diff) package. * @param string|array $lines1 the first string or string array to be compared. If it is a string, From db212f0586a873b208615443db7d3444037096e3 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 22:49:01 -0400 Subject: [PATCH 103/156] Added $label parameter to ActiveField::label(). --- framework/yii/widgets/ActiveField.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php index 51f27ab..ea8aa1b 100644 --- a/framework/yii/widgets/ActiveField.php +++ b/framework/yii/widgets/ActiveField.php @@ -216,22 +216,19 @@ class ActiveField extends Component /** * Generates a label tag for [[attribute]]. - * The label text is the label associated with the attribute, obtained via [[Model::getAttributeLabel()]]. + * @param string $label the label to use. If null, it will be generated via [[Model::getAttributeLabel()]]. + * Note that this will NOT be [[Html::encode()|encoded]]. * @param array $options the tag options in terms of name-value pairs. It will be merged with [[labelOptions]]. * The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded * using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered. - * - * The following options are specially handled: - * - * - label: this specifies the label to be displayed. Note that this will NOT be [[encoded()]]. - * If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display - * (after encoding). - * * @return ActiveField the field object itself */ - public function label($options = array()) + public function label($label = null, $options = array()) { $options = array_merge($this->labelOptions, $options); + if ($label !== null) { + $options['label'] = $label; + } $this->parts['{label}'] = Html::activeLabel($this->model, $this->attribute, $options); return $this; } From 4cbdd7a6dd0d6e186d13e453257865506f5899c7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 9 Sep 2013 23:36:44 -0400 Subject: [PATCH 104/156] crud WIP --- framework/yii/gii/assets/gii.js | 5 - framework/yii/gii/generators/crud/Generator.php | 208 +++++++++++++++++---- framework/yii/gii/generators/crud/form.php | 3 +- .../gii/generators/crud/templates/controller.php | 69 +++---- .../yii/gii/generators/crud/templates/search.php | 85 ++++++++- .../gii/generators/crud/templates/views/_form.php | 5 +- .../generators/crud/templates/views/_search.php | 59 +++--- .../gii/generators/crud/templates/views/create.php | 2 + .../gii/generators/crud/templates/views/index.php | 49 +++-- .../gii/generators/crud/templates/views/update.php | 5 + .../gii/generators/crud/templates/views/view.php | 13 +- framework/yii/gii/views/default/view.php | 2 +- 12 files changed, 362 insertions(+), 143 deletions(-) diff --git a/framework/yii/gii/assets/gii.js b/framework/yii/gii/assets/gii.js index 45951a1..b581d3b 100644 --- a/framework/yii/gii/assets/gii.js +++ b/framework/yii/gii/assets/gii.js @@ -81,11 +81,6 @@ yii.gii = (function ($) { $('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1); }).change(); - // crud generator: hide Search Model Class input if search is not enabled - $('#crud-generator #generator-enablesearch').on('change', function () { - $('#crud-generator .field-generator-searchmodelclass').toggle(this.checked); - }).change(); - // hide Generate button if any input is changed $('.default-view .form-group input,select,textarea').change(function () { $('.default-view-results,.default-view-files').hide(); diff --git a/framework/yii/gii/generators/crud/Generator.php b/framework/yii/gii/generators/crud/Generator.php index 7f1bff0..9bce7ff 100644 --- a/framework/yii/gii/generators/crud/Generator.php +++ b/framework/yii/gii/generators/crud/Generator.php @@ -10,6 +10,7 @@ namespace yii\gii\generators\crud; use Yii; use yii\base\Model; use yii\db\ActiveRecord; +use yii\db\Schema; use yii\gii\CodeFile; use yii\helpers\Inflector; use yii\web\Controller; @@ -26,7 +27,6 @@ class Generator extends \yii\gii\Generator public $controllerClass; public $baseControllerClass = 'yii\web\Controller'; public $indexWidgetType = 'grid'; - public $enableSearch = true; public $searchModelClass; public function getName() @@ -44,16 +44,14 @@ class Generator extends \yii\gii\Generator { return array_merge(parent::rules(), array( array('moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'), - array('modelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'), + array('modelClass, searchModelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'), array('modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'), array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())), array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())), array('controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'), array('controllerClass, searchModelClass', 'validateNewClass'), - array('enableSearch', 'boolean'), array('indexWidgetType', 'in', 'range' => array('grid', 'list')), array('modelClass', 'validateModelClass'), - array('searchModelClass', 'validateSearchModelClass'), array('moduleID', 'validateModuleID'), )); } @@ -66,7 +64,6 @@ class Generator extends \yii\gii\Generator 'controllerClass' => 'Controller Class', 'baseControllerClass' => 'Base Controller Class', 'indexWidgetType' => 'Widget Used in Index Page', - 'enableSearch' => 'Enable Search', 'searchModelClass' => 'Search Model Class', )); } @@ -87,11 +84,8 @@ class Generator extends \yii\gii\Generator If not set, it means the controller will belong to the application.', 'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models. You may choose either GridView or ListView', - 'enableSearch' => 'Whether to enable the search functionality on the index page. When search is enabled, - a search form will be displayed on the index page, and the index page will display the search results.', 'searchModelClass' => 'This is the class representing the data being collecting in the search form. - A fully qualified namespaced class name is required, e.g., app\models\PostSearchForm. - This is only used when search is enabled.', + A fully qualified namespaced class name is required, e.g., app\models\search\PostSearch.', ); } @@ -107,7 +101,7 @@ class Generator extends \yii\gii\Generator */ public function stickyAttributes() { - return array('baseControllerClass', 'moduleID', 'indexWidgetType', 'enableSearch'); + return array('baseControllerClass', 'moduleID', 'indexWidgetType'); } public function validateModelClass() @@ -120,13 +114,6 @@ class Generator extends \yii\gii\Generator } } - public function validateSearchModelClass() - { - if ($this->enableSearch && empty($this->searchModelClass)) { - $this->addError('searchModelClass', 'Search Model Class cannot be empty.'); - } - } - public function validateModuleID() { if (!empty($this->moduleID)) { @@ -142,26 +129,21 @@ class Generator extends \yii\gii\Generator */ public function generate() { - $files = array(); - $files[] = new CodeFile( - $this->getControllerFile(), - $this->render('controller.php') + $controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php'); + $searchModel = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->searchModelClass, '\\') . '.php')); + $files = array( + new CodeFile($controllerFile, $this->render('controller.php')), + new CodeFile($searchModel, $this->render('search.php')), ); - $viewPath = $this->getViewPath(); + $viewPath = $this->getViewPath(); $templatePath = $this->getTemplatePath() . '/views'; foreach (scandir($templatePath) as $file) { - if (!in_array($file, array('index.php', 'create.php', 'update.php', 'view.php', '_form.php'))) { - continue; - } if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') { $files[] = new CodeFile("$viewPath/$file", $this->render("views/$file")); } } - if ($this->enableSearch) { - - } return $files; } @@ -177,14 +159,6 @@ class Generator extends \yii\gii\Generator } /** - * @return string the controller class file path - */ - public function getControllerFile() - { - return Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php'); - } - - /** * @return string the action view file path */ public function getViewPath() @@ -207,13 +181,12 @@ class Generator extends \yii\gii\Generator } /** - * @param ActiveRecord $model * @param string $attribute * @return string */ - public function generateActiveField($model, $attribute) + public function generateActiveField($attribute) { - $tableSchema = $model->getTableSchema(); + $tableSchema = $this->getTableSchema(); if (!isset($tableSchema->columns[$attribute])) { return "\$form->field(\$model, '$attribute');"; } @@ -237,6 +210,21 @@ class Generator extends \yii\gii\Generator } /** + * @param string $attribute + * @return string + */ + public function generateActiveSearchField($attribute) + { + $tableSchema = $this->getTableSchema(); + $column = $tableSchema->columns[$attribute]; + if ($column->phpType === 'boolean') { + return "\$form->field(\$model, '$attribute')->checkbox();"; + } else { + return "\$form->field(\$model, '$attribute');"; + } + } + + /** * @param \yii\db\ColumnSchema $column * @return string */ @@ -256,4 +244,146 @@ class Generator extends \yii\gii\Generator return 'text'; } } + + /** + * Generates validation rules for the search model. + * @return array the generated validation rules + */ + public function generateSearchRules() + { + $table = $this->getTableSchema(); + $types = array(); + foreach ($table->columns as $column) { + switch ($column->type) { + case Schema::TYPE_SMALLINT: + case Schema::TYPE_INTEGER: + case Schema::TYPE_BIGINT: + $types['integer'][] = $column->name; + break; + case Schema::TYPE_BOOLEAN: + $types['boolean'][] = $column->name; + break; + case Schema::TYPE_FLOAT: + case Schema::TYPE_DECIMAL: + case Schema::TYPE_MONEY: + $types['number'][] = $column->name; + break; + case Schema::TYPE_DATE: + case Schema::TYPE_TIME: + case Schema::TYPE_DATETIME: + case Schema::TYPE_TIMESTAMP: + default: + $types['safe'][] = $column->name; + break; + } + } + + $rules = array(); + foreach ($types as $type => $columns) { + $rules[] = "array('" . implode(', ', $columns) . "', '$type')"; + } + + return $rules; + } + + public function getSearchAttributes() + { + return $this->getTableSchema()->getColumnNames(); + } + + /** + * Generates the attribute labels for the search model. + * @return array the generated attribute labels (name => label) + */ + public function generateSearchLabels() + { + $table = $this->getTableSchema(); + $labels = array(); + foreach ($table->columns as $column) { + if (!strcasecmp($column->name, 'id')) { + $labels[$column->name] = 'ID'; + } else { + $label = Inflector::camel2words($column->name); + if (strcasecmp(substr($label, -3), ' id') === 0) { + $label = substr($label, 0, -3) . ' ID'; + } + $labels[$column->name] = $label; + } + } + return $labels; + } + + public function generateSearchConditions() + { + $table = $this->getTableSchema(); + $conditions = array(); + foreach ($table->columns as $column) { + switch ($column->type) { + case Schema::TYPE_SMALLINT: + case Schema::TYPE_INTEGER: + case Schema::TYPE_BIGINT: + case Schema::TYPE_BOOLEAN: + case Schema::TYPE_FLOAT: + case Schema::TYPE_DECIMAL: + case Schema::TYPE_MONEY: + case Schema::TYPE_DATE: + case Schema::TYPE_TIME: + case Schema::TYPE_DATETIME: + case Schema::TYPE_TIMESTAMP: + $conditions[] = "\$this->addCondition(\$query, '{$column->name}');"; + break; + default: + $conditions[] = "\$this->addCondition(\$query, '{$column->name}', true);"; + break; + } + } + + return $conditions; + } + + public function generateUrlParams() + { + $pks = $this->getTableSchema()->primaryKey; + if (count($pks) === 1) { + return "'id' => \$model->{$pks[0]}"; + } else { + $params = array(); + foreach ($pks as $pk) { + $params[] = "'$pk' => \$model->$pk"; + } + return implode(', ', $params); + } + } + + public function generateActionParams() + { + $pks = $this->getTableSchema()->primaryKey; + if (count($pks) === 1) { + return '$id'; + } else { + return '$' . implode(', $', $pks); + } + } + + public function generateActionParamComments() + { + $table = $this->getTableSchema(); + $pks = $table->primaryKey; + if (count($pks) === 1) { + return array('@param ' . $table->columns[$pks[0]]->phpType . ' $id'); + } else { + $params = array(); + foreach ($pks as $pk) { + $params[] = '@param ' . $table->columns[$pk]->phpType . ' $' . $pk; + } + return $params; + } + } + + public function getTableSchema() + { + /** @var ActiveRecord $class */ + $class = $this->modelClass; + return $class::getTableSchema(); + } } diff --git a/framework/yii/gii/generators/crud/form.php b/framework/yii/gii/generators/crud/form.php index ee4771b..829b8a3 100644 --- a/framework/yii/gii/generators/crud/form.php +++ b/framework/yii/gii/generators/crud/form.php @@ -6,11 +6,10 @@ */ echo $form->field($generator, 'modelClass'); +echo $form->field($generator, 'searchModelClass'); echo $form->field($generator, 'controllerClass'); echo $form->field($generator, 'baseControllerClass'); echo $form->field($generator, 'moduleID'); -echo $form->field($generator, 'enableSearch')->checkbox(); -echo $form->field($generator, 'searchModelClass'); echo $form->field($generator, 'indexWidgetType')->dropDownList(array( 'grid' => 'GridView', 'list' => 'ListView', diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php index da915c8..fd33eda 100644 --- a/framework/yii/gii/generators/crud/templates/controller.php +++ b/framework/yii/gii/generators/crud/templates/controller.php @@ -9,38 +9,22 @@ use yii\helpers\StringHelper; * @var yii\gii\generators\crud\Generator $generator */ -$pos = strrpos($generator->controllerClass, '\\'); -$ns = ltrim(substr($generator->controllerClass, 0, $pos), '\\'); -$controllerClass = substr($generator->controllerClass, $pos + 1); -$pos = strrpos($generator->modelClass, '\\'); -$modelClass = $pos === false ? $generator->modelClass : substr($generator->modelClass, $pos + 1); - -/** @var \yii\db\ActiveRecord $class */ -$class = $generator->modelClass; -$pks = $class::primaryKey(); -$schema = $class::getTableSchema(); -if (count($pks) === 1) { - $ids = '$id'; - $params = "array('id' => \$model->{$pks[0]})"; - $paramComments = '@param ' . $schema->columns[$pks[0]]->phpType . ' $id'; -} else { - $ids = '$' . implode(', $', $pks); - $params = array(); - $paramComments = array(); - foreach ($pks as $pk) { - $paramComments[] = '@param ' . $schema->columns[$pk]->phpType . ' $' . $pk; - $params[] = "'$pk' => \$model->$pk"; - } - $params = implode(', ', $params); - $paramComments = implode("\n\t * ", $paramComments); -} +$controllerClass = StringHelper::basename($generator->controllerClass); +$modelClass = StringHelper::basename($generator->modelClass); +$searchModelClass = StringHelper::basename($generator->searchModelClass); + +$pks = $generator->getTableSchema()->primaryKey; +$urlParams = $generator->generateUrlParams(); +$actionParams = $generator->generateActionParams(); +$actionParamComments = $generator->generateActionParamComments(); echo " -namespace ; +namespace controllerClass, '\\')); ?>; use modelClass, '\\'); ?>; +use searchModelClass, '\\'); ?>; use yii\data\ActiveDataProvider; use baseControllerClass, '\\'); ?>; use yii\web\HttpException; @@ -56,23 +40,24 @@ class extends ::find(), - )); + $searchModel = new ; + $dataProvider = $searchModel->search($_GET); + return $this->render('index', array( 'dataProvider' => $dataProvider, + 'searchModel' => $searchModel, )); } /** * Displays a single model. - * + * * @return mixed */ - public function actionView() + public function actionView() { return $this->render('view', array( - 'model' => $this->findModel(), + 'model' => $this->findModel(), )); } @@ -86,7 +71,7 @@ class extends ; if ($model->load($_POST) && $model->save()) { - return $this->redirect(array('view', )); + return $this->redirect(array('view', )); } else { return $this->render('create', array( 'model' => $model, @@ -97,15 +82,15 @@ class extends model. * If update is successful, the browser will be redirected to the 'view' page. - * + * * @return mixed */ - public function actionUpdate() + public function actionUpdate() { - $model = $this->findModel(); + $model = $this->findModel(); if ($model->load($_POST) && $model->save()) { - return $this->redirect(array('view', )); + return $this->redirect(array('view', )); } else { return $this->render('update', array( 'model' => $model, @@ -116,23 +101,23 @@ class extends model. * If deletion is successful, the browser will be redirected to the 'index' page. - * + * * @return mixed */ - public function actionDelete() + public function actionDelete() { - $this->findModel()->delete(); + $this->findModel()->delete(); return $this->redirect(array('index')); } /** * Finds the model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. - * + * * @return the loaded model * @throws HttpException if the model cannot be found */ - protected function findModel() + protected function findModel() { modelClass); +$searchModelClass = StringHelper::basename($generator->searchModelClass); +$rules = $generator->generateSearchRules(); +$labels = $generator->generateSearchLabels(); +$searchAttributes = $generator->getSearchAttributes(); +$searchConditions = $generator->generateSearchConditions(); + +echo " + +namespace searchModelClass, '\\')); ?>; + +use yii\base\Model; +use yii\data\ActiveDataProvider; +use modelClass, '\\'); ?>; + +/** + * represents the model behind the search form about . + */ +class extends Model +{ + public $; + + public function rules() + { + return array( + , + ); + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return array( + $label): ?> + '" . addslashes($label) . "',\n"; ?> + + ); + } + + public function search($params) + { + $query = ::find(); + $dataProvider = new ActiveDataProvider(array( + 'query' => $query, + )); + + if (!($this->load($params) && $this->validate())) { + return $dataProvider; + } + + + + return $dataProvider; + } + + protected function addCondition($query, $attribute, $partialMatch = false) + { + $value = $this->$attribute; + if (trim($value) === '') { + return; + } + if ($partialMatch) { + $value = '%' . strtr($value, array('%'=>'\%', '_'=>'\_', '\\'=>'\\\\')) . '%'; + $query->andWhere(array('like', $attribute, $value)); + } else { + $query->andWhere(array($attribute => $value)); + } + } +} diff --git a/framework/yii/gii/generators/crud/templates/views/_form.php b/framework/yii/gii/generators/crud/templates/views/_form.php index 6160e72..2d9d5dc 100644 --- a/framework/yii/gii/generators/crud/templates/views/_form.php +++ b/framework/yii/gii/generators/crud/templates/views/_form.php @@ -9,8 +9,7 @@ use yii\helpers\StringHelper; */ /** @var \yii\db\ActiveRecord $model */ -$class = $generator->modelClass; -$model = new $class; +$model = new $generator->modelClass; $safeAttributes = $model->safeAttributes(); if (empty($safeAttributes)) { $safeAttributes = $model->getTableSchema()->columnNames; @@ -34,7 +33,7 @@ use yii\widgets\ActiveForm; $form = ActiveForm::begin(); ?> generateActiveField($model, $attribute) . " ?>\n\n"; + echo "\t\tgenerateActiveField($attribute) . " ?>\n\n"; } ?>
echo Html::submitButton($model->isNewRecord ? 'Create' : 'Update', array('class' => 'btn btn-primary')); ?> diff --git a/framework/yii/gii/generators/crud/templates/views/_search.php b/framework/yii/gii/generators/crud/templates/views/_search.php index a9679f1..a649589 100644 --- a/framework/yii/gii/generators/crud/templates/views/_search.php +++ b/framework/yii/gii/generators/crud/templates/views/_search.php @@ -1,38 +1,45 @@ - -/* @var $this getControllerClass(); ?> */ -/* @var $model getModelClass(); ?> */ -/* @var $form CActiveForm */ + +use yii\helpers\Html; +use yii\widgets\ActiveForm; + +/** + * @var yii\base\View $this + * @var searchModelClass, '\\'); ?> $model + * @var yii\widgets\ActiveForm $form + */ ?> -
+ \ No newline at end of file +
diff --git a/framework/yii/gii/generators/crud/templates/views/create.php b/framework/yii/gii/generators/crud/templates/views/create.php index 48a2318..669b99a 100644 --- a/framework/yii/gii/generators/crud/templates/views/create.php +++ b/framework/yii/gii/generators/crud/templates/views/create.php @@ -19,6 +19,8 @@ use yii\helpers\Html; */ $this->title = 'Create modelClass)); ?>'; +$this->params['breadcrumbs'][] = array('label' => 'modelClass))); ?>', 'url' => array('index')); +$this->params['breadcrumbs'][] = $this->title; ?>
diff --git a/framework/yii/gii/generators/crud/templates/views/index.php b/framework/yii/gii/generators/crud/templates/views/index.php index fbac7c3..8efa53a 100644 --- a/framework/yii/gii/generators/crud/templates/views/index.php +++ b/framework/yii/gii/generators/crud/templates/views/index.php @@ -8,19 +8,7 @@ use yii\helpers\StringHelper; * @var yii\gii\generators\crud\Generator $generator */ -/** @var \yii\db\ActiveRecord $model */ -$class = $generator->modelClass; -$pks = $class::primaryKey(); -if (count($pks) === 1) { - $viewUrl = "array('view', 'id' => \$model->{$pks[0]})"; -} else { - $params = array(); - foreach ($pks as $pk) { - $params[] = "'$pk' => \$model->$pk"; - } - $viewUrl = "array('view', " . implode(', ', $params) . ')'; -} - +$urlParams = $generator->generateUrlParams(); $nameAttribute = $generator->getNameAttribute(); echo "indexWidgetType === 'grid' ? 'yii\grid\GridView' : 'y /** * @var yii\base\View $this * @var yii\data\ActiveDataProvider $dataProvider + * @var searchModelClass, '\\'); ?> $searchModel */ $this->title = 'modelClass))); ?>'; +$this->params['breadcrumbs'][] = $this->title; ?>

echo Html::encode($this->title); ?>

-indexWidgetType === 'grid'): ?> + echo $this->render('_search', array('model' => $searchModel)); ?> +
+ +
+ echo Html::a('Create modelClass); ?>', array('create'), array('class' => 'btn btn-danger')); ?> +
+ +indexWidgetType === 'grid'): ?> + echo GridView::widget(array( + 'dataProvider' => $dataProvider, + 'filterModel' => $searchModel, + 'columns' => array( +getTableSchema()->columns as $column) { + $format = $generator->generateColumnFormat($column); + if (++$count < 6) { + echo "\t\t\t'" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n"; + } else { + echo "\t\t\t// '" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n"; + } +} +?> + ), + )); ?> - echo ListView::widget(array( + echo ListView::widget(array( 'dataProvider' => $dataProvider, + 'itemOptions' => array( + 'class' => 'item', + ), 'itemView' => function ($model, $key, $index, $widget) { - return Html::a(Html::encode($model->), ); + return Html::a(Html::encode($model->), array('view', ); }, )); ?> diff --git a/framework/yii/gii/generators/crud/templates/views/update.php b/framework/yii/gii/generators/crud/templates/views/update.php index bacc1ec..cb892c2 100644 --- a/framework/yii/gii/generators/crud/templates/views/update.php +++ b/framework/yii/gii/generators/crud/templates/views/update.php @@ -8,6 +8,8 @@ use yii\helpers\StringHelper; * @var yii\gii\generators\crud\Generator $generator */ +$urlParams = $generator->generateUrlParams(); + echo " @@ -19,6 +21,9 @@ use yii\helpers\Html; */ $this->title = 'Update modelClass)); ?>: ' . $model->getNameAttribute(); ?>; +$this->params['breadcrumbs'][] = array('label' => 'modelClass))); ?>', 'url' => array('index')); +$this->params['breadcrumbs'][] = array('label' => $model->getNameAttribute(); ?>, 'url' => array('view', )); +$this->params['breadcrumbs'][] = 'Update'; ?>
diff --git a/framework/yii/gii/generators/crud/templates/views/view.php b/framework/yii/gii/generators/crud/templates/views/view.php index 214f10b..d08ec23 100644 --- a/framework/yii/gii/generators/crud/templates/views/view.php +++ b/framework/yii/gii/generators/crud/templates/views/view.php @@ -8,9 +8,7 @@ use yii\helpers\StringHelper; * @var yii\gii\generators\crud\Generator $generator */ -/** @var \yii\db\ActiveRecord $model */ -$class = $generator->modelClass; -$model = new $class; +$urlParams = $generator->generateUrlParams(); echo " @@ -24,16 +22,23 @@ use yii\widgets\DetailView; */ $this->title = $model->getNameAttribute(); ?>; +$this->params['breadcrumbs'][] = array('label' => 'modelClass))); ?>', 'url' => array('index')); +$this->params['breadcrumbs'][] = $this->title; ?>

echo Html::encode($this->title); ?>

+
+ echo Html::a('Update', array('update', ), array('class' => 'btn btn-danger')); ?> + echo Html::a('Delete', array('delete', ), array('class' => 'btn btn-danger')); ?> +
+ echo DetailView::widget(array( 'model' => $model, 'attributes' => array( getTableSchema()->columns as $column) { +foreach ($generator->getTableSchema()->columns as $column) { $format = $generator->generateColumnFormat($column); echo "\t\t\t'" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n"; } diff --git a/framework/yii/gii/views/default/view.php b/framework/yii/gii/views/default/view.php index bf05e84..9754918 100644 --- a/framework/yii/gii/views/default/view.php +++ b/framework/yii/gii/views/default/view.php @@ -40,7 +40,7 @@ foreach ($generator->templates as $name => $path) { 'form' => $form, )); ?> field($generator, 'template')->sticky() - ->label(array('label' => 'Code Template')) + ->label('Code Template') ->dropDownList($templates)->hint(' Please select which set of the templates should be used to generated the code. '); ?> From 590121c4ff76b548b2bdb74f72ce247a806af415 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 10 Sep 2013 14:42:22 +0400 Subject: [PATCH 105/156] Zend Data Cache returns null when record doesn't exist --- framework/yii/caching/ZendDataCache.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/framework/yii/caching/ZendDataCache.php b/framework/yii/caching/ZendDataCache.php index 9ff2fd0..7e040f0 100644 --- a/framework/yii/caching/ZendDataCache.php +++ b/framework/yii/caching/ZendDataCache.php @@ -21,6 +21,25 @@ namespace yii\caching; class ZendDataCache extends Cache { /** + * Checks whether a specified key exists in the cache. + * This can be faster than getting the value from the cache if the data is big. + * In case a cache does not support this feature natively, this method will try to simulate it + * but has no performance improvement over getting it. + * Note that this method does not check whether the dependency associated + * with the cached data, if there is any, has changed. So a call to [[get]] + * may return false while exists returns true. + * @param mixed $key a key identifying the cached value. This can be a simple string or + * a complex data structure consisting of factors representing the key. + * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. + */ + public function exists($key) + { + $key = $this->buildKey($key); + $value = $this->getValue($key); + return $value !== null; + } + + /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value From 2e01c06a4b26662d578d563ad97186c87edf4e34 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 10 Sep 2013 08:15:46 -0400 Subject: [PATCH 106/156] Fixes #865. --- framework/yii/db/QueryBuilder.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 00d21c0..7ae5369 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -912,11 +912,6 @@ class QueryBuilder extends \yii\base\Object protected function buildCompositeInCondition($operator, $columns, $values, &$params) { - foreach ($columns as $i => $column) { - if (strpos($column, '(') === false) { - $columns[$i] = $this->db->quoteColumnName($column); - } - } $vss = array(); foreach ($values as $value) { $vs = array(); @@ -931,6 +926,11 @@ class QueryBuilder extends \yii\base\Object } $vss[] = '(' . implode(', ', $vs) . ')'; } + foreach ($columns as $i => $column) { + if (strpos($column, '(') === false) { + $columns[$i] = $this->db->quoteColumnName($column); + } + } return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')'; } From 7303bae38212ed2e1c8a4080e2f465172795431b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 10 Sep 2013 08:24:14 -0400 Subject: [PATCH 107/156] Fixed foreign key generation bug for pgsql. --- framework/yii/db/pgsql/Schema.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php index 9693dd6..969b47a 100644 --- a/framework/yii/db/pgsql/Schema.php +++ b/framework/yii/db/pgsql/Schema.php @@ -200,7 +200,7 @@ SQL; } $citem = array($foreignTable); foreach ($columns as $idx => $column) { - $citem[] = array($fcolumns[$idx] => $column); + $citem[$fcolumns[$idx]] = $column; } $table->foreignKeys[] = $citem; } From d02e7d4004a3730950cf46f741d4e05d5b86fa6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20W=C3=B6ster?= Date: Tue, 10 Sep 2013 14:27:22 +0200 Subject: [PATCH 108/156] add checks for GET and OPTIONS requests --- framework/yii/web/Request.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index c6e1ce3..2b9da0a 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -137,6 +137,24 @@ class Request extends \yii\base\Request } /** + * Returns whether this is a GET request. + * @return boolean whether this is a GET request. + */ + public function getIsGet() + { + return $this->getMethod() === 'GET'; + } + + /** + * Returns whether this is an OPTIONS request. + * @return boolean whether this is a OPTIONS request. + */ + public function getIsOptions() + { + return $this->getMethod() === 'OPTIONS'; + } + + /** * Returns whether this is a POST request. * @return boolean whether this is a POST request. */ From 64d57b397ac38080ef7148811b71b6931b7e0ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20W=C3=B6ster?= Date: Tue, 10 Sep 2013 14:33:50 +0200 Subject: [PATCH 109/156] add checks for HEAD request --- framework/yii/web/Request.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 2b9da0a..1482633 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -155,6 +155,15 @@ class Request extends \yii\base\Request } /** + * Returns whether this is a HEAD request. + * @return boolean whether this is a HEAD request. + */ + public function getIsHead() + { + return $this->getMethod() === 'HEAD'; + } + + /** * Returns whether this is a POST request. * @return boolean whether this is a POST request. */ From c6f4dac2497d78269061e26077cba4af7bc4ffb6 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 11 Sep 2013 13:41:47 -0400 Subject: [PATCH 110/156] Refactored AR code. --- framework/yii/db/ActiveRecord.php | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 35d7305..a09c60a 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -749,21 +749,21 @@ class ActiveRecord extends Model return false; } $db = static::getDb(); - $transaction = $this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null ? $db->beginTransaction() : null; - try { - $result = $this->insertInternal($attributes); - if ($transaction !== null) { + if ($this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null) { + $transaction = $db->beginTransaction(); + try { + $result = $this->insertInternal($attributes); if ($result === false) { $transaction->rollback(); } else { $transaction->commit(); } - } - } catch (\Exception $e) { - if ($transaction !== null) { + } catch (\Exception $e) { $transaction->rollback(); + throw $e; } - throw $e; + } else { + $result = $this->insertInternal($attributes); } return $result; } @@ -859,21 +859,21 @@ class ActiveRecord extends Model return false; } $db = static::getDb(); - $transaction = $this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null ? $db->beginTransaction() : null; - try { - $result = $this->updateInternal($attributes); - if ($transaction !== null) { + if ($this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null) { + $transaction = $db->beginTransaction(); + try { + $result = $this->updateInternal($attributes); if ($result === false) { $transaction->rollback(); } else { $transaction->commit(); } - } - } catch (\Exception $e) { - if ($transaction !== null) { + } catch (\Exception $e) { $transaction->rollback(); + throw $e; } - throw $e; + } else { + $result = $this->updateInternal($attributes); } return $result; } @@ -1010,6 +1010,16 @@ class ActiveRecord extends Model } /** + * Sets the value indicating whether the record is new. + * @param boolean $value whether the record is new and should be inserted when calling [[save()]]. + * @see getIsNewRecord + */ + public function setIsNewRecord($value) + { + $this->_oldAttributes = $value ? null : $this->_attributes; + } + + /** * Initializes the object. * This method is called at the end of the constructor. * The default implementation will trigger an [[EVENT_INIT]] event. @@ -1034,16 +1044,6 @@ class ActiveRecord extends Model } /** - * Sets the value indicating whether the record is new. - * @param boolean $value whether the record is new and should be inserted when calling [[save()]]. - * @see getIsNewRecord - */ - public function setIsNewRecord($value) - { - $this->_oldAttributes = $value ? null : $this->_attributes; - } - - /** * This method is called at the beginning of inserting or updating a record. * The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is true, * or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is false. From 84dd19d76f729e82f4d609fad2313a35dc15f45d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 11 Sep 2013 13:42:34 -0400 Subject: [PATCH 111/156] Fixed the issue that Object/Component doesn't support using anonymous function as normal property values. --- framework/yii/base/Component.php | 5 ++--- framework/yii/base/Object.php | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index a7ddd29..f497a29 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -193,9 +193,8 @@ class Component extends Object */ public function __call($name, $params) { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - $func = $this->$getter(); + if ($this->canGetProperty($name)) { + $func = $this->$name; if ($func instanceof \Closure) { return call_user_func_array($func, $params); } diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index adbab9c..0af3131 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -155,9 +155,8 @@ class Object implements Arrayable */ public function __call($name, $params) { - $getter = 'get' . $name; - if (method_exists($this, $getter)) { - $func = $this->$getter(); + if ($this->canGetProperty($name)) { + $func = $this->$name; if ($func instanceof \Closure) { return call_user_func_array($func, $params); } From 62f5b47f1d5332654e67ba2477ac383c1e6e68a6 Mon Sep 17 00:00:00 2001 From: ninbopa Date: Wed, 11 Sep 2013 20:59:25 +0300 Subject: [PATCH 112/156] Update single row query function --- docs/guide/database-basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/database-basics.md b/docs/guide/database-basics.md index 310e3f5..85bc042 100644 --- a/docs/guide/database-basics.md +++ b/docs/guide/database-basics.md @@ -88,7 +88,7 @@ When only a single row is returned: ```php $command = $connection->createCommand('SELECT * FROM tbl_post WHERE id=1'); -$post = $command->query(); +$post = $command->queryOne(); ``` When there are multiple values from the same column: From a2b946e4d3acc7c37a8e3f91f7a1405be24a1073 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Fri, 13 Sep 2013 00:49:06 +0400 Subject: [PATCH 113/156] Zend Data Cache returns null when record doesn't exist (reverted from commit 590121c4ff76b548b2bdb74f72ce247a806af415) --- framework/yii/caching/ZendDataCache.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/framework/yii/caching/ZendDataCache.php b/framework/yii/caching/ZendDataCache.php index 7e040f0..9ff2fd0 100644 --- a/framework/yii/caching/ZendDataCache.php +++ b/framework/yii/caching/ZendDataCache.php @@ -21,25 +21,6 @@ namespace yii\caching; class ZendDataCache extends Cache { /** - * Checks whether a specified key exists in the cache. - * This can be faster than getting the value from the cache if the data is big. - * In case a cache does not support this feature natively, this method will try to simulate it - * but has no performance improvement over getting it. - * Note that this method does not check whether the dependency associated - * with the cached data, if there is any, has changed. So a call to [[get]] - * may return false while exists returns true. - * @param mixed $key a key identifying the cached value. This can be a simple string or - * a complex data structure consisting of factors representing the key. - * @return boolean true if a value exists in cache, false if the value is not in the cache or expired. - */ - public function exists($key) - { - $key = $this->buildKey($key); - $value = $this->getValue($key); - return $value !== null; - } - - /** * Retrieves a value from cache with a specified key. * This is the implementation of the method declared in the parent class. * @param string $key a unique key identifying the cached value From 91b6e2945aef0bd4d4e23bea42dbab53b82e5b4c Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 12 Sep 2013 20:10:06 -0400 Subject: [PATCH 114/156] Removed the support for calling anonymous function returned as a property value. --- framework/yii/base/Component.php | 12 ++---------- framework/yii/base/Object.php | 8 -------- tests/unit/framework/base/ObjectTest.php | 5 ----- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index f497a29..2ad2c94 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -179,9 +179,8 @@ class Component extends Object /** * Calls the named method which is not a class method. - * If the name refers to a component property whose value is - * an anonymous function, the method will execute the function. - * Otherwise, it will check if any attached behavior has + * + * This method will check if any attached behavior has * the named method and will execute it if available. * * Do not call this method directly as it is a PHP magic method that @@ -193,13 +192,6 @@ class Component extends Object */ public function __call($name, $params) { - if ($this->canGetProperty($name)) { - $func = $this->$name; - if ($func instanceof \Closure) { - return call_user_func_array($func, $params); - } - } - $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { if ($object->hasMethod($name)) { diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index 0af3131..55754de 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -143,8 +143,6 @@ class Object implements Arrayable /** * Calls the named method which is not a class method. - * If the name refers to a component property whose value is - * an anonymous function, the method will execute the function. * * Do not call this method directly as it is a PHP magic method that * will be implicitly called when an unknown method is being invoked. @@ -155,12 +153,6 @@ class Object implements Arrayable */ public function __call($name, $params) { - if ($this->canGetProperty($name)) { - $func = $this->$name; - if ($func instanceof \Closure) { - return call_user_func_array($func, $params); - } - } throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()"); } diff --git a/tests/unit/framework/base/ObjectTest.php b/tests/unit/framework/base/ObjectTest.php index 933b721..d6b95e5 100644 --- a/tests/unit/framework/base/ObjectTest.php +++ b/tests/unit/framework/base/ObjectTest.php @@ -134,11 +134,6 @@ class ObjectTest extends TestCase $this->assertEquals('new text', $this->object->object->text); } - public function testAnonymousFunctionProperty() - { - $this->assertEquals(2, $this->object->execute(1)); - } - public function testConstruct() { $object = new NewObject(array('text' => 'test text')); From b05442d368e08c999d018fec538eaf1f63f1ba76 Mon Sep 17 00:00:00 2001 From: Larry Ullman Date: Fri, 13 Sep 2013 11:23:56 -0400 Subject: [PATCH 115/156] Doing that editing thing --- docs/guide/active-record.md | 64 +++++++++++++++++++++-------------------- docs/guide/bootstrap-widgets.md | 9 +++--- docs/guide/overview.md | 18 ++++++------ docs/guide/template.md | 3 +- 4 files changed, 46 insertions(+), 48 deletions(-) diff --git a/docs/guide/active-record.md b/docs/guide/active-record.md index 0c93fad..fc98f98 100644 --- a/docs/guide/active-record.md +++ b/docs/guide/active-record.md @@ -2,12 +2,12 @@ Active Record ============= Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record). -The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Therefore, referencing an Active Record attribute is equivalent to accessing +The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Referencing an Active Record attribute is equivalent to accessing the corresponding table column for that record. -For example, say that the `Customer` ActiveRecord class is associated with the +As an example, say that the `Customer` ActiveRecord class is associated with the `tbl_customer` table. This would mean that the class's `name` attribute is automatically mapped to the `name` column in `tbl_customer`. -Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of the `name` column for the table row, you can simply use the expression `$customer->name`. In this example, Active Record is providing an object-oriented way to access data stored in the database, just as you would access any object property. But Active Record provides much more functionality than this. +Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of the `name` column for the table row, you can use the expression `$customer->name`. In this example, Active Record is providing an object-oriented interface for accessing data stored in the database. But Active Record provides much more functionality than this. With Active Record, instead of writing raw SQL statements to perform database queries, you can call intuitive methods to achieve the same goals. For example, calling [[ActiveRecord::save()|save()]] would perform an INSERT or UPDATE query, creating or updating a row in the associated table of the ActiveRecord class: @@ -39,6 +39,13 @@ class Customer extends ActiveRecord } ``` +The `tableName` method only has to return the name of the database table associated with the class. + +Class instances are obtained in one of two ways: + +* Using the `new` operator to create a new, empty object +* Using a method to fetch an existing record (or records) from the database + Connecting to the Database ---------------------- @@ -59,11 +66,7 @@ return array( ); ``` -Please read the [Database basics](database-basics.md) section to learn more on how to configure -and use database connections. - -> Tip: To use a different database connection, you need to override the [[ActiveRecord::getDb()]] method. To do that, create a base class that extends ActiveRecord. In the base class, override the [[ActiveRecord::getDb()]] method. Then extend your new base class for all of your ActiveRecord classes that need to use the same alternative database connection. - +Please read the [Database basics](database-basics.md) section to learn more on how to configure and use database connections. Querying Data from the Database --------------------------- @@ -73,8 +76,8 @@ There are two ActiveRecord methods for querying data from database: - [[ActiveRecord::find()]] - [[ActiveRecord::findBySql()]] -Both methods return an [[ActiveQuery]] instance, which extends from [[Query]] thus supporting -the same set of flexible and powerful DB query methods. The followings examples demonstrate some of the possibilities. +Both methods return an [[ActiveQuery]] instance, which extends [[Query]], and thus supports +the same set of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities. ```php // to retrieve all *active* customers and order them by their ID: @@ -115,25 +118,24 @@ $customers = Customer::find()->indexBy('id')->all(); Accessing Column Data --------------------- -ActiveRecord maps each column of the corresponding database table row to an *attribute* in the ActiveRecord +ActiveRecord maps each column of the corresponding database table row to an attribute in the ActiveRecord object. The attribute behaves like any regular object public property. The attribute's name will be the same as the corresponding column name, and is case-sensitive. To read the value of a column, you can use the following syntax: ```php -// "id" is the name of a column in the table associated with $customer ActiveRecord object +// "id" and "email" are the names of columns in the table associated with $customer ActiveRecord object $id = $customer->id; -// or alternatively, -$id = $customer->getAttribute('id'); +$email = $customer->email; ``` -You can get all column values through the [[ActiveRecord::attributes]] property: +To change the value of a column, assign a new value to the associated property and save the object: -```php -$values = $customer->attributes; ``` - +$customer->email = 'jane@example.com'; +$customer->save(); +``` Manipulating Data in the Database ----------------------------- @@ -150,8 +152,8 @@ ActiveRecord provides the following methods to insert, update and delete data in - [[ActiveRecord::deleteAll()|deleteAll()]] Note that [[ActiveRecord::updateAll()|updateAll()]], [[ActiveRecord::updateAllCounters()|updateAllCounters()]] -and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods and apply to the whole database -table, while the other methods only apply to the row associated with the ActiveRecord object through which the method is being called. +and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods that apply to the whole database +table. The other methods only apply to the row associated with the ActiveRecord object through which the method is being called. ```php // to insert a new customer record @@ -173,12 +175,21 @@ $customer->delete(); Customer::updateAllCounters(array('age' => 1)); ``` +Notice that you can always use the `save` method, and ActiveRecord will automatically perform an INSERT for new records and an UPDATE for existing ones. + +Data Input and Validation +------------------------- + +ActiveRecord inherits data validation and data input features from [[\yii\base\Model]]. Data validation is called +automatically when `save()` is performed. If data validation fails, the saving operation will be cancelled. + +For more details refer to the [Model](model.md) section of this guide. Querying Relational Data ------------------------ -You can use ActiveRecord to query the relational data of a table. The relational data returned can -be accessed like a property of the ActiveRecord object associated with the primary table. +You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property of the ActiveRecord object associated with the primary table. + For example, with an appropriate relation declaration, by accessing `$customer->orders` you may obtain an array of `Order` objects which represent the orders placed by the specified customer. @@ -397,15 +408,6 @@ The [[link()]] call above will set the `customer_id` of the order to be the prim value of `$customer` and then call [[save()]] to save the order into database. -Data Input and Validation -------------------------- - -ActiveRecord inherits data validation and data input features from [[\yii\base\Model]]. Data validation is called -automatically when `save()` is performed. If data validation fails, the saving operation will be cancelled. - -For more details refer to the [Model](model.md) section of this guide. - - Life Cycles of an ActiveRecord Object ------------------------------------- diff --git a/docs/guide/bootstrap-widgets.md b/docs/guide/bootstrap-widgets.md index 0739847..3f56839 100644 --- a/docs/guide/bootstrap-widgets.md +++ b/docs/guide/bootstrap-widgets.md @@ -1,13 +1,12 @@ Bootstrap widgets ================= -Yii includes support of [Bootstrap 3](http://getbootstrap.com/) markup and components framework out of the box. It is an -excellent framework that allows you to speed up development a lot. +Yii includes support for the [Bootstrap 3](http://getbootstrap.com/) markup and components framework out of the box. Bootstrap is an excellent, responsive framework that can greatly speed up your development process. -Bootstrap is generally about two parts: +The core of Bootstrap is represented by two parts: -- Basics such as grid system, typography, helper classes and responsive utilities. -- Ready to use components such as menus, pagination, modal boxes, tabs etc. +- CSS basics, such as grid layout system, typography, helper classes, and responsive utilities. +- Ready to use components, such as menus, pagination, modal boxes, tabs etc. Basics ------ diff --git a/docs/guide/overview.md b/docs/guide/overview.md index d2ccb19..ef71aa0 100644 --- a/docs/guide/overview.md +++ b/docs/guide/overview.md @@ -1,8 +1,7 @@ What is Yii =========== -Yii is a high-performance, component-based PHP framework for developing -large-scale Web applications rapidly. It enables maximum reusability in Web +Yii is a high-performance, component-based PHP framework for rapidly developing large-scale Web applications. Yii enables maximum reusability in Web programming and can significantly accelerate your Web application development process. The name Yii (pronounced `Yee` or `[ji:]`) is an acronym for **Yes It Is!**. @@ -12,7 +11,7 @@ Requirements ------------ To run a Yii-powered Web application, you need a Web server that supports -PHP 5.3.?. +PHP 5.3.? or greater. For developers who want to use Yii, understanding object-oriented programming (OOP) is very helpful, because Yii is a pure OOP framework. @@ -31,10 +30,9 @@ management systems (CMS), e-commerce systems, etc. How does Yii Compare with Other Frameworks? ------------------------------------------- -- Like most PHP frameworks, Yii is an MVC (Model-View-Controller) framework. -- It is a fullstack framework providing many solutions and components such as logging, session management, caching etc. -- It has a good balance of simplicity and features. -- Syntax and overall development usability are taken seriously. -- Performance is one of the key goals. -- We are constantly watching other web frameworks out there and getting the best ideas in. Initial Yii release was heavily - influenced by Ruby on Rails. Still, we aren't blindly copying anything. +- Like most PHP frameworks, Yii is uses the MVC (Model-View-Controller) design approach. +- Yii is a fullstack framework providing many solutions and components, such as logging, session management, caching etc. +- Yii strikes a good balance between simplicity and features. +- Syntax and overall development usability are taken seriously by the Yii development team. +- Performance is one of the key goals for the Yii framework. +- The Yii development team is constantly watching what other Web frameworks are doing to see what best practices and features should be incorporated into Yii. The initial Yii release was heavily influenced by Ruby on Rails. Still, no framework or feature is being blindly copied into Yii; all decisions are based upon what's best for Web developers and in keeping with Yii's philosophy. diff --git a/docs/guide/template.md b/docs/guide/template.md index f2a6fc4..6d2db88 100644 --- a/docs/guide/template.md +++ b/docs/guide/template.md @@ -1,8 +1,7 @@ Using template engines ====================== -By default Yii uses PHP as template language but you can configure it to be able -to render templates with special engines such as Twig or Smarty. +By default Yii uses PHP as template language, but you can configure it to support other rendering engines, such as [Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/). The component responsible for rendering a view is called `view`. You can add a custom template engines as follows: From 3e5491e0896d80fd3ac797be5a1e1d021e3749c7 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 13 Sep 2013 17:45:55 +0200 Subject: [PATCH 116/156] changed cubrid table and column name quoting ` is a MySQL thing supported by cubrid. " is more common. --- framework/yii/db/cubrid/Schema.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index fae932f..e192c9f 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -57,7 +57,7 @@ class Schema extends \yii\db\Schema // Bit string data types 'bit' => self::TYPE_STRING, 'bit varying' => self::TYPE_STRING, - // Collection data types (considered strings for now, may add support for them later) + // Collection data types (considered strings for now) 'set' => self::TYPE_STRING, 'multiset' => self::TYPE_STRING, 'list' => self::TYPE_STRING, @@ -73,7 +73,7 @@ class Schema extends \yii\db\Schema */ public function quoteSimpleTableName($name) { - return strpos($name, "`") !== false ? $name : "`" . $name . "`"; + return strpos($name, '"') !== false ? $name : '"' . $name . '"'; } /** @@ -84,7 +84,7 @@ class Schema extends \yii\db\Schema */ public function quoteSimpleColumnName($name) { - return strpos($name, '`') !== false || $name === '*' ? $name : '`' . $name . '`'; + return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"'; } /** From 0bf2daddd5d4987aebbc6c268ade6cfa5d968a1e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 13 Sep 2013 17:49:29 +0200 Subject: [PATCH 117/156] changed querybuilder typemap to lower case --- framework/yii/db/cubrid/QueryBuilder.php | 2 +- framework/yii/db/mssql/QueryBuilder.php | 2 +- framework/yii/db/mysql/QueryBuilder.php | 2 +- framework/yii/db/sqlite/QueryBuilder.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 935b3d3..0f9cd33 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_PK => 'int not null auto_increment primary key', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'varchar', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/yii/db/mssql/QueryBuilder.php index e7f8f80..26e8c28 100644 --- a/framework/yii/db/mssql/QueryBuilder.php +++ b/framework/yii/db/mssql/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY', + Schema::TYPE_PK => 'int identity primary key', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index c7a4256..9c81c99 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -22,7 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_PK => 'int(11) not null auto_increment primary key', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index be0275a..5094442 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -23,7 +23,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', + Schema::TYPE_PK => 'integer primary key autoincrement not null', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint', From 6015312548a87cc6cd4eac4b65d5ff93e4e5c7ee Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 13 Sep 2013 17:55:01 +0200 Subject: [PATCH 118/156] code style --- framework/yii/db/cubrid/Schema.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index e192c9f..c7b6ad2 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -210,7 +210,8 @@ class Schema extends \yii\db\Schema if ($column->type === 'timestamp' && $info['Default'] === 'CURRENT_TIMESTAMP' || $column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' || $column->type === 'date' && $info['Default'] === 'SYS_DATE' || - $column->type === 'time' && $info['Default'] === 'SYS_TIME') { + $column->type === 'time' && $info['Default'] === 'SYS_TIME' + ) { $column->defaultValue = new Expression($info['Default']); } else { $column->defaultValue = $column->typecast($info['Default']); From 6fe152da7e81a2a599426a28d42a930400de32c9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 13 Sep 2013 20:13:46 -0400 Subject: [PATCH 119/156] Reverted 0bf2dad: it's very common to use lower case for column types and upper case for other DB keywords. --- framework/yii/db/cubrid/QueryBuilder.php | 2 +- framework/yii/db/mssql/QueryBuilder.php | 2 +- framework/yii/db/mysql/QueryBuilder.php | 2 +- framework/yii/db/pgsql/QueryBuilder.php | 2 +- framework/yii/db/sqlite/QueryBuilder.php | 2 +- tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php | 1 - 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 0f9cd33..935b3d3 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int not null auto_increment primary key', + Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'varchar', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/yii/db/mssql/QueryBuilder.php index 26e8c28..e7f8f80 100644 --- a/framework/yii/db/mssql/QueryBuilder.php +++ b/framework/yii/db/mssql/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int identity primary key', + Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 9c81c99..c7a4256 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -22,7 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'int(11) not null auto_increment primary key', + Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/pgsql/QueryBuilder.php b/framework/yii/db/pgsql/QueryBuilder.php index 9701fd6..22d615e 100644 --- a/framework/yii/db/pgsql/QueryBuilder.php +++ b/framework/yii/db/pgsql/QueryBuilder.php @@ -21,7 +21,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'serial not null primary key', + Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index 5094442..be0275a 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -23,7 +23,7 @@ class QueryBuilder extends \yii\db\QueryBuilder * @var array mapping from abstract column types (keys) to physical column types (values). */ public $typeMap = array( - Schema::TYPE_PK => 'integer primary key autoincrement not null', + Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint', diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php index 2e3a615..1f99503 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -2,7 +2,6 @@ namespace yiiunit\framework\db\sqlite; -use yii\base\NotSupportedException; use yii\db\sqlite\Schema; use yiiunit\framework\db\QueryBuilderTest; From 02fd82c42a9623b28f970af7696bccd0540fa4cd Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 13 Sep 2013 20:19:50 -0400 Subject: [PATCH 120/156] Fixed build break. --- framework/yii/db/redis/Connection.php | 1 - tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php index ea59f22..68b40d3 100644 --- a/framework/yii/db/redis/Connection.php +++ b/framework/yii/db/redis/Connection.php @@ -13,7 +13,6 @@ use \yii\base\Component; use yii\base\InvalidConfigException; use \yii\db\Exception; use yii\helpers\Inflector; -use yii\helpers\StringHelper; /** * diff --git a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php index bf2dd73..c7bfeed 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php @@ -12,10 +12,10 @@ class PostgreSQLQueryBuilderTest extends QueryBuilderTest public function columnTypes() { return array( - array(Schema::TYPE_PK, 'serial not null primary key'), - array(Schema::TYPE_PK . '(8)', 'serial not null primary key'), - array(Schema::TYPE_PK . ' CHECK (value > 5)', 'serial not null primary key CHECK (value > 5)'), - array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'serial not null primary key CHECK (value > 5)'), + array(Schema::TYPE_PK, 'serial NOT NULL PRIMARY KEY'), + array(Schema::TYPE_PK . '(8)', 'serial NOT NULL PRIMARY KEY'), + array(Schema::TYPE_PK . ' CHECK (value > 5)', 'serial NOT NULL PRIMARY KEY CHECK (value > 5)'), + array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'serial NOT NULL PRIMARY KEY CHECK (value > 5)'), array(Schema::TYPE_STRING, 'varchar(255)'), array(Schema::TYPE_STRING . '(32)', 'varchar(32)'), array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'), From 6c6cb3cd90b19a23ad202fcefc9617bfb5e59a76 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 13 Sep 2013 20:41:40 -0400 Subject: [PATCH 121/156] Fixes #853: Added composite FK support for SQLite. --- framework/yii/db/sqlite/Schema.php | 8 +++++++- tests/unit/framework/db/sqlite/SqliteSchemaTest.php | 5 ----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/yii/db/sqlite/Schema.php b/framework/yii/db/sqlite/Schema.php index d4fb245..bca26c1 100644 --- a/framework/yii/db/sqlite/Schema.php +++ b/framework/yii/db/sqlite/Schema.php @@ -126,7 +126,13 @@ class Schema extends \yii\db\Schema $sql = "PRAGMA foreign_key_list(" . $this->quoteSimpleTableName($table->name) . ')'; $keys = $this->db->createCommand($sql)->queryAll(); foreach ($keys as $key) { - $table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']); + $id = (int)$key['id']; + if (!isset($table->foreignKeys[$id])) { + $table->foreignKeys[$id] = array($key['table'], $key['from'] => $key['to']); + } else { + // composite FK + $table->foreignKeys[$id][$key['from']] = $key['to']; + } } } diff --git a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php index 9b17a1d..f3f6b60 100644 --- a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php +++ b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php @@ -6,9 +6,4 @@ use yiiunit\framework\db\SchemaTest; class SqliteSchemaTest extends SchemaTest { protected $driverName = 'sqlite'; - - public function testCompositeFk() - { - $this->markTestSkipped('sqlite does not allow getting enough information about composite FK.'); - } } From 30907b6134f17f287d5b182ae006cda4cdf63580 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 13 Sep 2013 20:54:16 -0400 Subject: [PATCH 122/156] Fixes #826: cleaned up User::getReturnUrl(). --- framework/yii/web/User.php | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index e02559b..22b85e5 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -10,6 +10,7 @@ namespace yii\web; use Yii; use yii\base\Component; use yii\base\InvalidConfigException; +use yii\base\InvalidParamException; /** * User is the class for the "user" application component that manages the user authentication status. @@ -255,20 +256,34 @@ class User extends Component * This property is usually used by the login action. If the login is successful, * the action should read this property and use it to redirect the user browser. * @param string|array $defaultUrl the default return URL in case it was not set previously. - * If this is null, it means [[Application::homeUrl]] will be redirected to. - * Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats. + * If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to. + * Please refer to [[setReturnUrl()]] on accepted format of the URL. * @return string the URL that the user should be redirected to after login. * @see loginRequired */ public function getReturnUrl($defaultUrl = null) { $url = Yii::$app->getSession()->get($this->returnUrlVar, $defaultUrl); + if (is_array($url)) { + if (isset($url[0])) { + $route = array_shift($url); + return Yii::$app->getUrlManager()->createUrl($route, $url); + } else { + $url = null; + } + } return $url === null ? Yii::$app->getHomeUrl() : $url; } /** * @param string|array $url the URL that the user should be redirected to after login. - * Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats. + * If an array is given, [[UrlManager::createUrl()]] will be called to create the corresponding URL. + * The first element of the array should be the route, and the rest of + * the name-value pairs are GET parameters used to construct the URL. For example, + * + * ~~~ + * array('admin/index', 'ref' => 1) + * ~~~ */ public function setReturnUrl($url) { From d3740932a4a64774a6a62d856378ced43af79ddc Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 11:12:48 +0200 Subject: [PATCH 123/156] fixed broken cubrid tests fixed expected quoting --- .../unit/framework/db/cubrid/CubridCommandTest.php | 9 +++++++++ .../framework/db/cubrid/CubridConnectionTest.php | 23 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php index 5fe8848..4151f2b 100644 --- a/tests/unit/framework/db/cubrid/CubridCommandTest.php +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -69,4 +69,13 @@ class CubridCommandTest extends CommandTest $command->bindValue(':name', 'user5'); $this->assertEquals('user5@example.com', $command->queryScalar()); } + + public function testAutoQuoting() + { + $db = $this->getConnection(false); + + $sql = 'SELECT [[id]], [[t.name]] FROM {{tbl_customer}} t'; + $command = $db->createCommand($sql); + $this->assertEquals('SELECT "id", "t"."name" FROM "tbl_customer" t', $command->sql); + } } diff --git a/tests/unit/framework/db/cubrid/CubridConnectionTest.php b/tests/unit/framework/db/cubrid/CubridConnectionTest.php index 6924883..2ce1c7b 100644 --- a/tests/unit/framework/db/cubrid/CubridConnectionTest.php +++ b/tests/unit/framework/db/cubrid/CubridConnectionTest.php @@ -14,4 +14,27 @@ class CubridConnectionTest extends ConnectionTest $this->assertEquals("'string'", $connection->quoteValue('string')); $this->assertEquals("'It''s interesting'", $connection->quoteValue("It's interesting")); } + + public function testQuoteTableName() + { + $connection = $this->getConnection(false); + $this->assertEquals('"table"', $connection->quoteTableName('table')); + $this->assertEquals('"table"', $connection->quoteTableName('"table"')); + $this->assertEquals('"schema"."table"', $connection->quoteTableName('schema.table')); + $this->assertEquals('"schema"."table"', $connection->quoteTableName('schema."table"')); + $this->assertEquals('{{table}}', $connection->quoteTableName('{{table}}')); + $this->assertEquals('(table)', $connection->quoteTableName('(table)')); + } + + public function testQuoteColumnName() + { + $connection = $this->getConnection(false); + $this->assertEquals('"column"', $connection->quoteColumnName('column')); + $this->assertEquals('"column"', $connection->quoteColumnName('"column"')); + $this->assertEquals('"table"."column"', $connection->quoteColumnName('table.column')); + $this->assertEquals('"table"."column"', $connection->quoteColumnName('table."column"')); + $this->assertEquals('[[column]]', $connection->quoteColumnName('[[column]]')); + $this->assertEquals('{{column}}', $connection->quoteColumnName('{{column}}')); + $this->assertEquals('(column)', $connection->quoteColumnName('(column)')); + } } From eb5bf2dabedd4b00b4d78a35d2468f1807837c2f Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 11:33:07 +0200 Subject: [PATCH 124/156] added cubrid dbms to travis-ci copied from https://github.com/CUBRID/node-cubrid/blob/056e734ce36bb3fd25f100c983beb3947e899c1c/.travis.yml --- .travis.yml | 21 +++++++++++++++++++++ tests/unit/data/cubrid-solo.rb | 5 +++++ 2 files changed, 26 insertions(+) create mode 100644 tests/unit/data/cubrid-solo.rb diff --git a/.travis.yml b/.travis.yml index c24c0b3..6078518 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,32 @@ php: - 5.4 - 5.5 +env: + - CUBRID_VERSION=9.1.0 + before_script: - composer self-update && composer --version - composer require satooshi/php-coveralls 0.6.* - mysql -e 'CREATE DATABASE yiitest;'; - psql -U postgres -c 'CREATE DATABASE yiitest;'; + # + # install CUBRID DBMS https://github.com/CUBRID/node-cubrid/blob/056e734ce36bb3fd25f100c983beb3947e899c1c/.travis.yml + - sudo hostname localhost + # Update OS before installing prerequisites. + - sudo apt-get update + # Install Chef Solo prerequisites. + - sudo apt-get install ruby ruby-dev libopenssl-ruby rdoc ri irb build-essential ssl-cert + # Install Chef Solo. + # Chef Solo 11.4.4 is broken, so install a previous version. + # The bug is planned to be fixed in 11.4.5 which haven't been released yet. + - sudo gem install --version '<11.4.4' chef --no-rdoc --no-ri + # Make sure the target directory for cookbooks exists. + - mkdir -p /tmp/chef-solo + # Prepare a file with runlist for Chef Solo. + - echo '{"cubrid":{"version":"'$CUBRID_VERSION'"},"run_list":["cubrid::demodb"]}' > cubrid_chef.json + # Install CUBRID via Chef Solo. Download all cookbooks from a remote URL. + - sudo chef-solo -c tests/unit/data/cubrid-solo.rb -j cubrid_chef.json -r http://sourceforge.net/projects/cubrid/files/CUBRID-Demo-Virtual-Machines/Vagrant/chef-cookbooks.tar.gz/download + script: - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml diff --git a/tests/unit/data/cubrid-solo.rb b/tests/unit/data/cubrid-solo.rb new file mode 100644 index 0000000..f5f0004 --- /dev/null +++ b/tests/unit/data/cubrid-solo.rb @@ -0,0 +1,5 @@ +file_cache_path "/tmp/chef-solo" +data_bag_path "/tmp/chef-solo/data_bags" +encrypted_data_bag_secret "/tmp/chef-solo/data_bag_key" +cookbook_path [ "/tmp/chef-solo/cookbooks" ] +role_path "/tmp/chef-solo/roles" \ No newline at end of file From 1179cb2a1c5b65e93755f4d02fefc505e83ea5b3 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 11:41:23 +0200 Subject: [PATCH 125/156] made travis phpunit verbose to see why cubrid tests are skipped --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6078518..6aeb868 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ before_script: script: - - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml + - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose after_script: - php vendor/bin/coveralls \ No newline at end of file From 281e602850139afb6a79a82f34963d96429c15e2 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 11:53:46 +0200 Subject: [PATCH 126/156] added pecl install pdo_cubrid to travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6aeb868..8b0153e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ before_script: - echo '{"cubrid":{"version":"'$CUBRID_VERSION'"},"run_list":["cubrid::demodb"]}' > cubrid_chef.json # Install CUBRID via Chef Solo. Download all cookbooks from a remote URL. - sudo chef-solo -c tests/unit/data/cubrid-solo.rb -j cubrid_chef.json -r http://sourceforge.net/projects/cubrid/files/CUBRID-Demo-Virtual-Machines/Vagrant/chef-cookbooks.tar.gz/download - + - sudo pecl install pdo_cubrid script: - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose From 69a38d189545841161234a5f62313f45f40932c8 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:07:04 +0200 Subject: [PATCH 127/156] added cubrid_pdo to travis --- .travis.yml | 19 +------------------ tests/unit/data/cubrid-solo.rb | 5 ----- tests/unit/data/travis/cubrid-setup.sh | 32 ++++++++++++++++++++++++++++++++ tests/unit/data/travis/cubrid-solo.rb | 5 +++++ 4 files changed, 38 insertions(+), 23 deletions(-) delete mode 100644 tests/unit/data/cubrid-solo.rb create mode 100644 tests/unit/data/travis/cubrid-setup.sh create mode 100644 tests/unit/data/travis/cubrid-solo.rb diff --git a/.travis.yml b/.travis.yml index 8b0153e..3a38d05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,24 +13,7 @@ before_script: - composer require satooshi/php-coveralls 0.6.* - mysql -e 'CREATE DATABASE yiitest;'; - psql -U postgres -c 'CREATE DATABASE yiitest;'; - # - # install CUBRID DBMS https://github.com/CUBRID/node-cubrid/blob/056e734ce36bb3fd25f100c983beb3947e899c1c/.travis.yml - - sudo hostname localhost - # Update OS before installing prerequisites. - - sudo apt-get update - # Install Chef Solo prerequisites. - - sudo apt-get install ruby ruby-dev libopenssl-ruby rdoc ri irb build-essential ssl-cert - # Install Chef Solo. - # Chef Solo 11.4.4 is broken, so install a previous version. - # The bug is planned to be fixed in 11.4.5 which haven't been released yet. - - sudo gem install --version '<11.4.4' chef --no-rdoc --no-ri - # Make sure the target directory for cookbooks exists. - - mkdir -p /tmp/chef-solo - # Prepare a file with runlist for Chef Solo. - - echo '{"cubrid":{"version":"'$CUBRID_VERSION'"},"run_list":["cubrid::demodb"]}' > cubrid_chef.json - # Install CUBRID via Chef Solo. Download all cookbooks from a remote URL. - - sudo chef-solo -c tests/unit/data/cubrid-solo.rb -j cubrid_chef.json -r http://sourceforge.net/projects/cubrid/files/CUBRID-Demo-Virtual-Machines/Vagrant/chef-cookbooks.tar.gz/download - - sudo pecl install pdo_cubrid + - tests/unit/data/travis/cubrid-setup.sh script: - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose diff --git a/tests/unit/data/cubrid-solo.rb b/tests/unit/data/cubrid-solo.rb deleted file mode 100644 index f5f0004..0000000 --- a/tests/unit/data/cubrid-solo.rb +++ /dev/null @@ -1,5 +0,0 @@ -file_cache_path "/tmp/chef-solo" -data_bag_path "/tmp/chef-solo/data_bags" -encrypted_data_bag_secret "/tmp/chef-solo/data_bag_key" -cookbook_path [ "/tmp/chef-solo/cookbooks" ] -role_path "/tmp/chef-solo/roles" \ No newline at end of file diff --git a/tests/unit/data/travis/cubrid-setup.sh b/tests/unit/data/travis/cubrid-setup.sh new file mode 100644 index 0000000..af007ff --- /dev/null +++ b/tests/unit/data/travis/cubrid-setup.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# install CUBRID DBMS https://github.com/CUBRID/node-cubrid/blob/056e734ce36bb3fd25f100c983beb3947e899c1c/.travis.yml + +sudo hostname localhost +# Update OS before installing prerequisites. +sudo apt-get update +# Install Chef Solo prerequisites. +sudo apt-get install ruby ruby-dev libopenssl-ruby rdoc ri irb build-essential ssl-cert +# Install Chef Solo. +# Chef Solo 11.4.4 is broken, so install a previous version. +# The bug is planned to be fixed in 11.4.5 which haven't been released yet. +sudo gem install --version '<11.4.4' chef --no-rdoc --no-ri +# Make sure the target directory for cookbooks exists. +mkdir -p /tmp/chef-solo +# Prepare a file with runlist for Chef Solo. +echo '{"cubrid":{"version":"'$CUBRID_VERSION'"},"run_list":["cubrid::demodb"]}' > cubrid_chef.json +# Install CUBRID via Chef Solo. Download all cookbooks from a remote URL. +sudo chef-solo -c tests/unit/data/travis/cubrid-solo.rb -j cubrid_chef.json -r http://sourceforge.net/projects/cubrid/files/CUBRID-Demo-Virtual-Machines/Vagrant/chef-cookbooks.tar.gz/download + + +install_pdo_cubrid() { + wget "http://pecl.php.net/get/PDO_CUBRID-9.1.0.0003.tgz" && + tar -zxf "PDO_CUBRID-9.1.0.0003.tgz" && + sh -c "cd PDO_CUBRID-9.1.0.0003 && phpize && ./configure && make && sudo make install" + + echo "extension=pdo_cubrid.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + + return $? +} + +install_pdo_cubrid > ~/pdo_cubrid.log || ( echo "=== PDO CUBRID BUILD FAILED ==="; cat ~/pdo_cubrid.log ) \ No newline at end of file diff --git a/tests/unit/data/travis/cubrid-solo.rb b/tests/unit/data/travis/cubrid-solo.rb new file mode 100644 index 0000000..f5f0004 --- /dev/null +++ b/tests/unit/data/travis/cubrid-solo.rb @@ -0,0 +1,5 @@ +file_cache_path "/tmp/chef-solo" +data_bag_path "/tmp/chef-solo/data_bags" +encrypted_data_bag_secret "/tmp/chef-solo/data_bag_key" +cookbook_path [ "/tmp/chef-solo/cookbooks" ] +role_path "/tmp/chef-solo/roles" \ No newline at end of file From f6f2522ad664b2a5d227979ccec0f274355a987a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:12:20 +0200 Subject: [PATCH 128/156] added travis README from yii 1.1 --- tests/unit/data/travis/README.md | 10 ++++++++++ tests/unit/data/travis/cubrid-setup.sh | 0 2 files changed, 10 insertions(+) create mode 100644 tests/unit/data/travis/README.md mode change 100644 => 100755 tests/unit/data/travis/cubrid-setup.sh diff --git a/tests/unit/data/travis/README.md b/tests/unit/data/travis/README.md new file mode 100644 index 0000000..e87ebe4 --- /dev/null +++ b/tests/unit/data/travis/README.md @@ -0,0 +1,10 @@ +This directory contains scripts for automated test runs via the [Travis CI](http://travis-ci.org) build service. They are used for the preparation of worker instances by setting up needed extensions and configuring database access. + +These scripts might be used to configure your own system for test runs. But since their primary purpose remains to support Travis in running the test cases, you would be best advised to stick to the setup notes in the tests themselves. + +The scripts are: + + - [`memcache-setup.sh`](memcache-setup.sh) + Compiles and installs the [memcache pecl extension](http://pecl.php.net/package/memcache) + - [`cubrid-setup.sh`](cubrid-setup.sh) + Prepares the [CUBRID](http://www.cubrid.org/) server instance by installing the server and PHP PDO driver diff --git a/tests/unit/data/travis/cubrid-setup.sh b/tests/unit/data/travis/cubrid-setup.sh old mode 100644 new mode 100755 From 41bd9ab7a8d0b096076c267c8a680e6d1a6233f4 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:23:06 +0200 Subject: [PATCH 129/156] added memcache and apc to travis --- tests/unit/data/travis/README.md | 2 ++ tests/unit/data/travis/apc-setup.sh | 2 ++ tests/unit/data/travis/memcache-setup.sh | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100755 tests/unit/data/travis/apc-setup.sh create mode 100755 tests/unit/data/travis/memcache-setup.sh diff --git a/tests/unit/data/travis/README.md b/tests/unit/data/travis/README.md index e87ebe4..c86497e 100644 --- a/tests/unit/data/travis/README.md +++ b/tests/unit/data/travis/README.md @@ -4,6 +4,8 @@ These scripts might be used to configure your own system for test runs. But sinc The scripts are: + - [`apc-setup.sh`](apc-setup.sh) + Installs and configures the [apc pecl extension](http://pecl.php.net/package/apc) - [`memcache-setup.sh`](memcache-setup.sh) Compiles and installs the [memcache pecl extension](http://pecl.php.net/package/memcache) - [`cubrid-setup.sh`](cubrid-setup.sh) diff --git a/tests/unit/data/travis/apc-setup.sh b/tests/unit/data/travis/apc-setup.sh new file mode 100755 index 0000000..3355f8f --- /dev/null +++ b/tests/unit/data/travis/apc-setup.sh @@ -0,0 +1,2 @@ +echo "extension = .so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini +echo "apc.enable_cli = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini \ No newline at end of file diff --git a/tests/unit/data/travis/memcache-setup.sh b/tests/unit/data/travis/memcache-setup.sh new file mode 100755 index 0000000..d0a9888 --- /dev/null +++ b/tests/unit/data/travis/memcache-setup.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +install_memcache() { + if [ "$(expr "$TRAVIS_PHP_VERSION" ">=" "5.5")" -eq 1 ]; then + MEMCACHE_VERSION="2.2.7" + wget "http://pecl.php.net/get/memcache-$MEMCACHE_VERSION.tgz" && + tar -zxf "memcache-$MEMCACHE_VERSION.tgz" && + sh -c "cd memcache-$MEMCACHE_VERSION && phpize && ./configure --enable-memcache && make && sudo make install" + fi + + echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + + return $? +} + +install_memcache > ~/memcache.log || ( echo "=== MEMCACHE BUILD FAILED ==="; cat ~/memcache.log ) \ No newline at end of file From 949dda5fdb2e584101bf438dfbcf58359e8c197e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:49:47 +0200 Subject: [PATCH 130/156] tagged tests with @group tags --- .travis.yml | 2 +- tests/unit/framework/YiiBaseTest.php | 1 + tests/unit/framework/base/BehaviorTest.php | 3 +++ tests/unit/framework/base/ComponentTest.php | 3 +++ tests/unit/framework/base/FormatterTest.php | 4 +--- tests/unit/framework/base/ModelTest.php | 2 +- tests/unit/framework/base/ObjectTest.php | 2 +- tests/unit/framework/behaviors/AutoTimestampTest.php | 2 ++ tests/unit/framework/caching/ApcCacheTest.php | 2 ++ tests/unit/framework/caching/DbCacheTest.php | 2 ++ tests/unit/framework/caching/FileCacheTest.php | 1 + tests/unit/framework/caching/MemCacheTest.php | 2 ++ tests/unit/framework/caching/MemCachedTest.php | 2 ++ tests/unit/framework/caching/RedisCacheTest.php | 2 ++ tests/unit/framework/caching/WinCacheTest.php | 2 ++ tests/unit/framework/caching/XCacheTest.php | 2 ++ tests/unit/framework/caching/ZendDataCacheTest.php | 2 ++ tests/unit/framework/console/controllers/AssetControllerTest.php | 2 ++ tests/unit/framework/console/controllers/MessageControllerTest.php | 2 ++ tests/unit/framework/data/ActiveDataProviderTest.php | 2 ++ tests/unit/framework/data/SortTest.php | 2 ++ tests/unit/framework/db/ActiveRecordTest.php | 4 ++++ tests/unit/framework/db/CommandTest.php | 4 ++++ tests/unit/framework/db/ConnectionTest.php | 4 ++++ tests/unit/framework/db/QueryBuilderTest.php | 4 ++++ tests/unit/framework/db/QueryTest.php | 4 ++++ tests/unit/framework/db/SchemaTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridActiveRecordTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridCommandTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridConnectionTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridQueryTest.php | 4 ++++ tests/unit/framework/db/cubrid/CubridSchemaTest.php | 4 ++++ tests/unit/framework/db/mssql/MssqlActiveRecordTest.php | 4 ++++ tests/unit/framework/db/mssql/MssqlCommandTest.php | 4 ++++ tests/unit/framework/db/mssql/MssqlConnectionTest.php | 4 ++++ tests/unit/framework/db/mssql/MssqlQueryTest.php | 4 ++++ tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php | 4 ++++ tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php | 4 ++++ tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteCommandTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteConnectionTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteQueryTest.php | 4 ++++ tests/unit/framework/db/sqlite/SqliteSchemaTest.php | 4 ++++ tests/unit/framework/helpers/ArrayHelperTest.php | 3 +++ tests/unit/framework/helpers/ConsoleTest.php | 4 ++++ tests/unit/framework/helpers/FileHelperTest.php | 1 + tests/unit/framework/helpers/HtmlTest.php | 3 +++ tests/unit/framework/helpers/InflectorTest.php | 3 +++ tests/unit/framework/helpers/JsonTest.php | 3 +++ tests/unit/framework/helpers/StringHelperTest.php | 1 + tests/unit/framework/helpers/VarDumperTest.php | 3 +++ tests/unit/framework/i18n/FormatterTest.php | 1 + tests/unit/framework/i18n/GettextMessageSourceTest.php | 3 +++ tests/unit/framework/i18n/GettextMoFileTest.php | 3 +++ tests/unit/framework/i18n/GettextPoFileTest.php | 3 +++ tests/unit/framework/rbac/PhpManagerTest.php | 3 +++ tests/unit/framework/requirements/YiiRequirementCheckerTest.php | 1 + tests/unit/framework/validators/EmailValidatorTest.php | 1 + tests/unit/framework/web/ResponseTest.php | 3 +++ tests/unit/framework/web/UrlManagerTest.php | 3 +++ tests/unit/framework/web/UrlRuleTest.php | 3 +++ tests/unit/framework/web/XmlResponseFormatterTest.php | 2 ++ tests/unit/framework/widgets/SpacelessTest.php | 3 +++ 66 files changed, 188 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3a38d05..d8200f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_script: - tests/unit/data/travis/cubrid-setup.sh script: - - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose + - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata after_script: - php vendor/bin/coveralls \ No newline at end of file diff --git a/tests/unit/framework/YiiBaseTest.php b/tests/unit/framework/YiiBaseTest.php index e256b2b..72b5f24 100644 --- a/tests/unit/framework/YiiBaseTest.php +++ b/tests/unit/framework/YiiBaseTest.php @@ -6,6 +6,7 @@ use yiiunit\TestCase; /** * YiiBaseTest + * @group base */ class YiiBaseTest extends TestCase { diff --git a/tests/unit/framework/base/BehaviorTest.php b/tests/unit/framework/base/BehaviorTest.php index e596ea8..b6eda09 100644 --- a/tests/unit/framework/base/BehaviorTest.php +++ b/tests/unit/framework/base/BehaviorTest.php @@ -46,6 +46,9 @@ class BarBehavior extends Behavior } } +/** + * @group base + */ class BehaviorTest extends TestCase { protected function setUp() diff --git a/tests/unit/framework/base/ComponentTest.php b/tests/unit/framework/base/ComponentTest.php index 79fb7db..98786e2 100644 --- a/tests/unit/framework/base/ComponentTest.php +++ b/tests/unit/framework/base/ComponentTest.php @@ -17,6 +17,9 @@ function globalEventHandler2($event) $event->handled = true; } +/** + * @group base + */ class ComponentTest extends TestCase { /** diff --git a/tests/unit/framework/base/FormatterTest.php b/tests/unit/framework/base/FormatterTest.php index 01dd682..ae71a5c 100644 --- a/tests/unit/framework/base/FormatterTest.php +++ b/tests/unit/framework/base/FormatterTest.php @@ -10,9 +10,7 @@ use yii\base\Formatter; use yiiunit\TestCase; /** - * - * @author Qiang Xue - * @since 2.0 + * @group base */ class FormatterTest extends TestCase { diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php index e4d8976..b338c17 100644 --- a/tests/unit/framework/base/ModelTest.php +++ b/tests/unit/framework/base/ModelTest.php @@ -9,7 +9,7 @@ use yiiunit\data\base\Singer; use yiiunit\data\base\InvalidRulesModel; /** - * ModelTest + * @group base */ class ModelTest extends TestCase { diff --git a/tests/unit/framework/base/ObjectTest.php b/tests/unit/framework/base/ObjectTest.php index d6b95e5..0bd9b1d 100644 --- a/tests/unit/framework/base/ObjectTest.php +++ b/tests/unit/framework/base/ObjectTest.php @@ -5,7 +5,7 @@ use yii\base\Object; use yiiunit\TestCase; /** - * ObjectTest + * @group base */ class ObjectTest extends TestCase { diff --git a/tests/unit/framework/behaviors/AutoTimestampTest.php b/tests/unit/framework/behaviors/AutoTimestampTest.php index 0e17a39..c26d912 100644 --- a/tests/unit/framework/behaviors/AutoTimestampTest.php +++ b/tests/unit/framework/behaviors/AutoTimestampTest.php @@ -11,6 +11,8 @@ use yii\behaviors\AutoTimestamp; /** * Unit test for [[\yii\behaviors\AutoTimestamp]]. * @see AutoTimestamp + * + * @group behaviors */ class AutoTimestampTest extends TestCase { diff --git a/tests/unit/framework/caching/ApcCacheTest.php b/tests/unit/framework/caching/ApcCacheTest.php index 1d07498..adda151 100644 --- a/tests/unit/framework/caching/ApcCacheTest.php +++ b/tests/unit/framework/caching/ApcCacheTest.php @@ -5,6 +5,8 @@ use yii\caching\ApcCache; /** * Class for testing APC cache backend + * @group apc + * @group caching */ class ApcCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/DbCacheTest.php b/tests/unit/framework/caching/DbCacheTest.php index 969b034..1e94d58 100644 --- a/tests/unit/framework/caching/DbCacheTest.php +++ b/tests/unit/framework/caching/DbCacheTest.php @@ -6,6 +6,8 @@ use yii\caching\DbCache; /** * Class for testing file cache backend + * @group db + * @group caching */ class DbCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/FileCacheTest.php b/tests/unit/framework/caching/FileCacheTest.php index 0bdbc86..263ecb4 100644 --- a/tests/unit/framework/caching/FileCacheTest.php +++ b/tests/unit/framework/caching/FileCacheTest.php @@ -5,6 +5,7 @@ use yii\caching\FileCache; /** * Class for testing file cache backend + * @group caching */ class FileCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/MemCacheTest.php b/tests/unit/framework/caching/MemCacheTest.php index d54d807..32374b5 100644 --- a/tests/unit/framework/caching/MemCacheTest.php +++ b/tests/unit/framework/caching/MemCacheTest.php @@ -5,6 +5,8 @@ use yii\caching\MemCache; /** * Class for testing memcache cache backend + * @group memcache + * @group caching */ class MemCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/MemCachedTest.php b/tests/unit/framework/caching/MemCachedTest.php index faf0d56..f39ed17 100644 --- a/tests/unit/framework/caching/MemCachedTest.php +++ b/tests/unit/framework/caching/MemCachedTest.php @@ -5,6 +5,8 @@ use yii\caching\MemCache; /** * Class for testing memcached cache backend + * @group memcached + * @group caching */ class MemCachedTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/RedisCacheTest.php b/tests/unit/framework/caching/RedisCacheTest.php index 0924d0f..d02773d 100644 --- a/tests/unit/framework/caching/RedisCacheTest.php +++ b/tests/unit/framework/caching/RedisCacheTest.php @@ -6,6 +6,8 @@ use yiiunit\TestCase; /** * Class for testing redis cache backend + * @group redis + * @group caching */ class RedisCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/WinCacheTest.php b/tests/unit/framework/caching/WinCacheTest.php index b6f2425..1bce102 100644 --- a/tests/unit/framework/caching/WinCacheTest.php +++ b/tests/unit/framework/caching/WinCacheTest.php @@ -5,6 +5,8 @@ use yii\caching\WinCache; /** * Class for testing wincache backend + * @group wincache + * @group caching */ class WinCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/XCacheTest.php b/tests/unit/framework/caching/XCacheTest.php index 7ee0a0e..989765d 100644 --- a/tests/unit/framework/caching/XCacheTest.php +++ b/tests/unit/framework/caching/XCacheTest.php @@ -5,6 +5,8 @@ use yii\caching\XCache; /** * Class for testing xcache backend + * @group xcache + * @group caching */ class XCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/caching/ZendDataCacheTest.php b/tests/unit/framework/caching/ZendDataCacheTest.php index 2d68bfd..96354cd 100644 --- a/tests/unit/framework/caching/ZendDataCacheTest.php +++ b/tests/unit/framework/caching/ZendDataCacheTest.php @@ -6,6 +6,8 @@ use yii\caching\ZendDataCache; /** * Class for testing Zend cache backend + * @group zenddata + * @group caching */ class ZendDataCacheTest extends CacheTestCase { diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index aaf5ca9..67dbdaa 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -6,6 +6,8 @@ use yii\console\controllers\AssetController; /** * Unit test for [[\yii\console\controllers\AssetController]]. * @see AssetController + * + * @group console */ class AssetControllerTest extends TestCase { diff --git a/tests/unit/framework/console/controllers/MessageControllerTest.php b/tests/unit/framework/console/controllers/MessageControllerTest.php index cdd38b8..b0c697c 100644 --- a/tests/unit/framework/console/controllers/MessageControllerTest.php +++ b/tests/unit/framework/console/controllers/MessageControllerTest.php @@ -6,6 +6,8 @@ use yii\console\controllers\MessageController; /** * Unit test for [[\yii\console\controllers\MessageController]]. * @see MessageController + * + * @group console */ class MessageControllerTest extends TestCase { diff --git a/tests/unit/framework/data/ActiveDataProviderTest.php b/tests/unit/framework/data/ActiveDataProviderTest.php index 2699a52..3f65ebb 100644 --- a/tests/unit/framework/data/ActiveDataProviderTest.php +++ b/tests/unit/framework/data/ActiveDataProviderTest.php @@ -16,6 +16,8 @@ use yiiunit\data\ar\Order; /** * @author Qiang Xue * @since 2.0 + * + * @group data */ class ActiveDataProviderTest extends DatabaseTestCase { diff --git a/tests/unit/framework/data/SortTest.php b/tests/unit/framework/data/SortTest.php index 9891ad1..c4cc6aa 100644 --- a/tests/unit/framework/data/SortTest.php +++ b/tests/unit/framework/data/SortTest.php @@ -14,6 +14,8 @@ use yii\data\Sort; /** * @author Qiang Xue * @since 2.0 + * + * @group data */ class SortTest extends TestCase { diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index 7df4159..2f9b345 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -9,6 +9,10 @@ use yiiunit\data\ar\OrderItem; use yiiunit\data\ar\Order; use yiiunit\data\ar\Item; +/** + * @group db + * @group mysql + */ class ActiveRecordTest extends DatabaseTestCase { protected function setUp() diff --git a/tests/unit/framework/db/CommandTest.php b/tests/unit/framework/db/CommandTest.php index 229545d..7b16c76 100644 --- a/tests/unit/framework/db/CommandTest.php +++ b/tests/unit/framework/db/CommandTest.php @@ -7,6 +7,10 @@ use yii\db\Command; use yii\db\Query; use yii\db\DataReader; +/** + * @group db + * @group mysql + */ class CommandTest extends DatabaseTestCase { public function testConstruct() diff --git a/tests/unit/framework/db/ConnectionTest.php b/tests/unit/framework/db/ConnectionTest.php index f2895f1..04c5d53 100644 --- a/tests/unit/framework/db/ConnectionTest.php +++ b/tests/unit/framework/db/ConnectionTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db; use yii\db\Connection; +/** + * @group db + * @group mysql + */ class ConnectionTest extends DatabaseTestCase { public function testConstruct() diff --git a/tests/unit/framework/db/QueryBuilderTest.php b/tests/unit/framework/db/QueryBuilderTest.php index a2815e8..d43c901 100644 --- a/tests/unit/framework/db/QueryBuilderTest.php +++ b/tests/unit/framework/db/QueryBuilderTest.php @@ -10,6 +10,10 @@ use yii\db\mssql\QueryBuilder as MssqlQueryBuilder; use yii\db\pgsql\QueryBuilder as PgsqlQueryBuilder; use yii\db\cubrid\QueryBuilder as CubridQueryBuilder; +/** + * @group db + * @group mysql + */ class QueryBuilderTest extends DatabaseTestCase { /** diff --git a/tests/unit/framework/db/QueryTest.php b/tests/unit/framework/db/QueryTest.php index dfb6c9f..a275afd 100644 --- a/tests/unit/framework/db/QueryTest.php +++ b/tests/unit/framework/db/QueryTest.php @@ -7,6 +7,10 @@ use yii\db\Command; use yii\db\Query; use yii\db\DataReader; +/** + * @group db + * @group mysql + */ class QueryTest extends DatabaseTestCase { public function testSelect() diff --git a/tests/unit/framework/db/SchemaTest.php b/tests/unit/framework/db/SchemaTest.php index 5f504e1..dce6e20 100644 --- a/tests/unit/framework/db/SchemaTest.php +++ b/tests/unit/framework/db/SchemaTest.php @@ -5,6 +5,10 @@ namespace yiiunit\framework\db; use yii\caching\FileCache; use yii\db\Schema; +/** + * @group db + * @group mysql + */ class SchemaTest extends DatabaseTestCase { diff --git a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php index 6633e11..dd48f44 100644 --- a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php +++ b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\cubrid; use yiiunit\framework\db\ActiveRecordTest; +/** + * @group db + * @group cubrid + */ class CubridActiveRecordTest extends ActiveRecordTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php index 4151f2b..895f548 100644 --- a/tests/unit/framework/db/cubrid/CubridCommandTest.php +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\cubrid; use yiiunit\framework\db\CommandTest; +/** + * @group db + * @group cubrid + */ class CubridCommandTest extends CommandTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/cubrid/CubridConnectionTest.php b/tests/unit/framework/db/cubrid/CubridConnectionTest.php index 2ce1c7b..4cd6e20 100644 --- a/tests/unit/framework/db/cubrid/CubridConnectionTest.php +++ b/tests/unit/framework/db/cubrid/CubridConnectionTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\cubrid; use yiiunit\framework\db\ConnectionTest; +/** + * @group db + * @group cubrid + */ class CubridConnectionTest extends ConnectionTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php index 94a002e..107b73b 100644 --- a/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php +++ b/tests/unit/framework/db/cubrid/CubridQueryBuilderTest.php @@ -6,6 +6,10 @@ use yii\base\NotSupportedException; use yii\db\sqlite\Schema; use yiiunit\framework\db\QueryBuilderTest; +/** + * @group db + * @group cubrid + */ class CubridQueryBuilderTest extends QueryBuilderTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/cubrid/CubridQueryTest.php b/tests/unit/framework/db/cubrid/CubridQueryTest.php index 2c68a35..b7c9009 100644 --- a/tests/unit/framework/db/cubrid/CubridQueryTest.php +++ b/tests/unit/framework/db/cubrid/CubridQueryTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\cubrid; use yiiunit\framework\db\QueryTest; +/** + * @group db + * @group cubrid + */ class CubridQueryTest extends QueryTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/cubrid/CubridSchemaTest.php b/tests/unit/framework/db/cubrid/CubridSchemaTest.php index d235b40..6a1f6a2 100644 --- a/tests/unit/framework/db/cubrid/CubridSchemaTest.php +++ b/tests/unit/framework/db/cubrid/CubridSchemaTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\cubrid; use yiiunit\framework\db\SchemaTest; +/** + * @group db + * @group cubrid + */ class CubridSchemaTest extends SchemaTest { public $driverName = 'cubrid'; diff --git a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php index 92e4da2..c21efc6 100644 --- a/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php +++ b/tests/unit/framework/db/mssql/MssqlActiveRecordTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db\mssql; use yiiunit\framework\db\ActiveRecordTest; +/** + * @group db + * @group mssql + */ class MssqlActiveRecordTest extends ActiveRecordTest { protected $driverName = 'sqlsrv'; diff --git a/tests/unit/framework/db/mssql/MssqlCommandTest.php b/tests/unit/framework/db/mssql/MssqlCommandTest.php index 1908f65..86f7f45 100644 --- a/tests/unit/framework/db/mssql/MssqlCommandTest.php +++ b/tests/unit/framework/db/mssql/MssqlCommandTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db\mssql; use yiiunit\framework\db\CommandTest; +/** + * @group db + * @group mssql + */ class MssqlCommandTest extends CommandTest { protected $driverName = 'sqlsrv'; diff --git a/tests/unit/framework/db/mssql/MssqlConnectionTest.php b/tests/unit/framework/db/mssql/MssqlConnectionTest.php index 854c81f..6531f83 100644 --- a/tests/unit/framework/db/mssql/MssqlConnectionTest.php +++ b/tests/unit/framework/db/mssql/MssqlConnectionTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db\mssql; use yiiunit\framework\db\ConnectionTest; +/** + * @group db + * @group mssql + */ class MssqlConnectionTest extends ConnectionTest { protected $driverName = 'sqlsrv'; diff --git a/tests/unit/framework/db/mssql/MssqlQueryTest.php b/tests/unit/framework/db/mssql/MssqlQueryTest.php index e07ac1d..a2cb019 100644 --- a/tests/unit/framework/db/mssql/MssqlQueryTest.php +++ b/tests/unit/framework/db/mssql/MssqlQueryTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db\mssql; use yiiunit\framework\db\QueryTest; +/** + * @group db + * @group mssql + */ class MssqlQueryTest extends QueryTest { protected $driverName = 'sqlsrv'; diff --git a/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php b/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php index aef09fd..1fffad7 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLActiveRecordTest.php @@ -4,6 +4,10 @@ namespace yiiunit\framework\db\pgsql; use yiiunit\framework\db\ActiveRecordTest; +/** + * @group db + * @group pgsql + */ class PostgreSQLActiveRecordTest extends ActiveRecordTest { protected $driverName = 'pgsql'; diff --git a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php index cd76a97..26ac0e0 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLConnectionTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\pgsql; use yiiunit\framework\db\ConnectionTest; +/** + * @group db + * @group pgsql + */ class PostgreSQLConnectionTest extends ConnectionTest { protected $driverName = 'pgsql'; diff --git a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php index c7bfeed..3ef329e 100644 --- a/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php +++ b/tests/unit/framework/db/pgsql/PostgreSQLQueryBuilderTest.php @@ -5,6 +5,10 @@ namespace yiiunit\framework\db\pgsql; use yii\db\pgsql\Schema; use yiiunit\framework\db\QueryBuilderTest; +/** + * @group db + * @group pgsql + */ class PostgreSQLQueryBuilderTest extends QueryBuilderTest { public $driverName = 'pgsql'; diff --git a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php index 1088280..a689e5d 100644 --- a/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php +++ b/tests/unit/framework/db/sqlite/SqliteActiveRecordTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\sqlite; use yiiunit\framework\db\ActiveRecordTest; +/** + * @group db + * @group sqlite + */ class SqliteActiveRecordTest extends ActiveRecordTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/db/sqlite/SqliteCommandTest.php b/tests/unit/framework/db/sqlite/SqliteCommandTest.php index af9a323..1f9ddc2 100644 --- a/tests/unit/framework/db/sqlite/SqliteCommandTest.php +++ b/tests/unit/framework/db/sqlite/SqliteCommandTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\sqlite; use yiiunit\framework\db\CommandTest; +/** + * @group db + * @group sqlite + */ class SqliteCommandTest extends CommandTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php index f1d5b94..e1a2961 100644 --- a/tests/unit/framework/db/sqlite/SqliteConnectionTest.php +++ b/tests/unit/framework/db/sqlite/SqliteConnectionTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\sqlite; use yiiunit\framework\db\ConnectionTest; +/** + * @group db + * @group sqlite + */ class SqliteConnectionTest extends ConnectionTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php index 1f99503..b20acad 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php @@ -5,6 +5,10 @@ namespace yiiunit\framework\db\sqlite; use yii\db\sqlite\Schema; use yiiunit\framework\db\QueryBuilderTest; +/** + * @group db + * @group sqlite + */ class SqliteQueryBuilderTest extends QueryBuilderTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/db/sqlite/SqliteQueryTest.php b/tests/unit/framework/db/sqlite/SqliteQueryTest.php index 3f112dc..f1db36b 100644 --- a/tests/unit/framework/db/sqlite/SqliteQueryTest.php +++ b/tests/unit/framework/db/sqlite/SqliteQueryTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\sqlite; use yiiunit\framework\db\QueryTest; +/** + * @group db + * @group sqlite + */ class SqliteQueryTest extends QueryTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php index f3f6b60..260bb4c 100644 --- a/tests/unit/framework/db/sqlite/SqliteSchemaTest.php +++ b/tests/unit/framework/db/sqlite/SqliteSchemaTest.php @@ -3,6 +3,10 @@ namespace yiiunit\framework\db\sqlite; use yiiunit\framework\db\SchemaTest; +/** + * @group db + * @group sqlite + */ class SqliteSchemaTest extends SchemaTest { protected $driverName = 'sqlite'; diff --git a/tests/unit/framework/helpers/ArrayHelperTest.php b/tests/unit/framework/helpers/ArrayHelperTest.php index 92a3971..ca8c95a 100644 --- a/tests/unit/framework/helpers/ArrayHelperTest.php +++ b/tests/unit/framework/helpers/ArrayHelperTest.php @@ -35,6 +35,9 @@ class Post3 extends Object } } +/** + * @group helpers + */ class ArrayHelperTest extends TestCase { public function testToArray() diff --git a/tests/unit/framework/helpers/ConsoleTest.php b/tests/unit/framework/helpers/ConsoleTest.php index f1d3b3a..4b983f8 100644 --- a/tests/unit/framework/helpers/ConsoleTest.php +++ b/tests/unit/framework/helpers/ConsoleTest.php @@ -6,6 +6,10 @@ use Yii; use yii\helpers\Console; use yiiunit\TestCase; +/** + * @group helpers + * @group console + */ class ConsoleTest extends TestCase { public function testStripAnsiFormat() diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 2212072..05bd7af 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -6,6 +6,7 @@ use yii\test\TestCase; /** * Unit test for [[yii\helpers\FileHelper]] * @see FileHelper + * @group helpers */ class FileHelperTest extends TestCase { diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php index 6d725de..857f9c2 100644 --- a/tests/unit/framework/helpers/HtmlTest.php +++ b/tests/unit/framework/helpers/HtmlTest.php @@ -6,6 +6,9 @@ use Yii; use yii\helpers\Html; use yiiunit\TestCase; +/** + * @group helpers + */ class HtmlTest extends TestCase { protected function setUp() diff --git a/tests/unit/framework/helpers/InflectorTest.php b/tests/unit/framework/helpers/InflectorTest.php index 732c10d..de7fe01 100644 --- a/tests/unit/framework/helpers/InflectorTest.php +++ b/tests/unit/framework/helpers/InflectorTest.php @@ -6,6 +6,9 @@ use Yii; use yii\helpers\Inflector; use yiiunit\TestCase; +/** + * @group helpers + */ class InflectorTest extends TestCase { public function testPluralize() diff --git a/tests/unit/framework/helpers/JsonTest.php b/tests/unit/framework/helpers/JsonTest.php index 3734744..df2ca5f 100644 --- a/tests/unit/framework/helpers/JsonTest.php +++ b/tests/unit/framework/helpers/JsonTest.php @@ -7,6 +7,9 @@ use yii\helpers\Json; use yii\test\TestCase; use yii\web\JsExpression; +/** + * @group helpers + */ class JsonTest extends TestCase { public function testEncode() diff --git a/tests/unit/framework/helpers/StringHelperTest.php b/tests/unit/framework/helpers/StringHelperTest.php index fe6a96c..8af731d 100644 --- a/tests/unit/framework/helpers/StringHelperTest.php +++ b/tests/unit/framework/helpers/StringHelperTest.php @@ -6,6 +6,7 @@ use yii\test\TestCase; /** * StringHelperTest + * @group helpers */ class StringHelperTest extends TestCase { diff --git a/tests/unit/framework/helpers/VarDumperTest.php b/tests/unit/framework/helpers/VarDumperTest.php index 2b40b63..d41a69d 100644 --- a/tests/unit/framework/helpers/VarDumperTest.php +++ b/tests/unit/framework/helpers/VarDumperTest.php @@ -4,6 +4,9 @@ namespace yiiunit\framework\helpers; use \yii\helpers\VarDumper; use yii\test\TestCase; +/** + * @group helpers + */ class VarDumperTest extends TestCase { public function testDumpObject() diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index c13fff3..6966853 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -13,6 +13,7 @@ use yiiunit\TestCase; /** * @author Qiang Xue * @since 2.0 + * @group i18n */ class FormatterTest extends TestCase { diff --git a/tests/unit/framework/i18n/GettextMessageSourceTest.php b/tests/unit/framework/i18n/GettextMessageSourceTest.php index 7b499f4..d039629 100644 --- a/tests/unit/framework/i18n/GettextMessageSourceTest.php +++ b/tests/unit/framework/i18n/GettextMessageSourceTest.php @@ -5,6 +5,9 @@ namespace yiiunit\framework\i18n; use yii\i18n\GettextMessageSource; use yiiunit\TestCase; +/** + * @group i18n + */ class GettextMessageSourceTest extends TestCase { public function testLoadMessages() diff --git a/tests/unit/framework/i18n/GettextMoFileTest.php b/tests/unit/framework/i18n/GettextMoFileTest.php index 0aa22da..9b61145 100644 --- a/tests/unit/framework/i18n/GettextMoFileTest.php +++ b/tests/unit/framework/i18n/GettextMoFileTest.php @@ -5,6 +5,9 @@ namespace yiiunit\framework\i18n; use yii\i18n\GettextMoFile; use yiiunit\TestCase; +/** + * @group i18n + */ class GettextMoFileTest extends TestCase { public function testLoad() diff --git a/tests/unit/framework/i18n/GettextPoFileTest.php b/tests/unit/framework/i18n/GettextPoFileTest.php index 8dddb40..4165b81 100644 --- a/tests/unit/framework/i18n/GettextPoFileTest.php +++ b/tests/unit/framework/i18n/GettextPoFileTest.php @@ -5,6 +5,9 @@ namespace yiiunit\framework\i18n; use yii\i18n\GettextPoFile; use yiiunit\TestCase; +/** + * @group i18n + */ class GettextPoFileTest extends TestCase { public function testLoad() diff --git a/tests/unit/framework/rbac/PhpManagerTest.php b/tests/unit/framework/rbac/PhpManagerTest.php index b3b7c4f..8c5d366 100644 --- a/tests/unit/framework/rbac/PhpManagerTest.php +++ b/tests/unit/framework/rbac/PhpManagerTest.php @@ -5,6 +5,9 @@ namespace yiiunit\framework\rbac; use Yii; use yii\rbac\PhpManager; +/** + * @group rbac + */ class PhpManagerTest extends ManagerTestCase { protected function setUp() diff --git a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php index 7554729..652d003 100644 --- a/tests/unit/framework/requirements/YiiRequirementCheckerTest.php +++ b/tests/unit/framework/requirements/YiiRequirementCheckerTest.php @@ -7,6 +7,7 @@ use yiiunit\TestCase; /** * Test case for [[YiiRequirementChecker]]. * @see YiiRequirementChecker + * @group requirements */ class YiiRequirementCheckerTest extends TestCase { diff --git a/tests/unit/framework/validators/EmailValidatorTest.php b/tests/unit/framework/validators/EmailValidatorTest.php index 5807aed..b33a809 100644 --- a/tests/unit/framework/validators/EmailValidatorTest.php +++ b/tests/unit/framework/validators/EmailValidatorTest.php @@ -6,6 +6,7 @@ use yiiunit\TestCase; /** * EmailValidatorTest + * @group validators */ class EmailValidatorTest extends TestCase { diff --git a/tests/unit/framework/web/ResponseTest.php b/tests/unit/framework/web/ResponseTest.php index 41ed939..2a9b4bf 100644 --- a/tests/unit/framework/web/ResponseTest.php +++ b/tests/unit/framework/web/ResponseTest.php @@ -13,6 +13,9 @@ class MockResponse extends \yii\web\Response } } +/** + * @group web + */ class ResponseTest extends \yiiunit\TestCase { /** diff --git a/tests/unit/framework/web/UrlManagerTest.php b/tests/unit/framework/web/UrlManagerTest.php index efa6695..a77a66d 100644 --- a/tests/unit/framework/web/UrlManagerTest.php +++ b/tests/unit/framework/web/UrlManagerTest.php @@ -5,6 +5,9 @@ use yii\web\Request; use yii\web\UrlManager; use yiiunit\TestCase; +/** + * @group web + */ class UrlManagerTest extends TestCase { protected function setUp() diff --git a/tests/unit/framework/web/UrlRuleTest.php b/tests/unit/framework/web/UrlRuleTest.php index d67dc58..0a0def4 100644 --- a/tests/unit/framework/web/UrlRuleTest.php +++ b/tests/unit/framework/web/UrlRuleTest.php @@ -7,6 +7,9 @@ use yii\web\UrlRule; use yii\web\Request; use yiiunit\TestCase; +/** + * @group web + */ class UrlRuleTest extends TestCase { public function testCreateUrl() diff --git a/tests/unit/framework/web/XmlResponseFormatterTest.php b/tests/unit/framework/web/XmlResponseFormatterTest.php index 590caef..e97962a 100644 --- a/tests/unit/framework/web/XmlResponseFormatterTest.php +++ b/tests/unit/framework/web/XmlResponseFormatterTest.php @@ -26,6 +26,8 @@ class Post extends Object /** * @author Qiang Xue * @since 2.0 + * + * @group web */ class XmlResponseFormatterTest extends \yiiunit\TestCase { diff --git a/tests/unit/framework/widgets/SpacelessTest.php b/tests/unit/framework/widgets/SpacelessTest.php index 6b2cf45..00f5a96 100644 --- a/tests/unit/framework/widgets/SpacelessTest.php +++ b/tests/unit/framework/widgets/SpacelessTest.php @@ -4,6 +4,9 @@ namespace yiiunit\framework\widgets; use yii\widgets\Spaceless; +/** + * @group widgets + */ class SpacelessTest extends \yiiunit\TestCase { public function testWidget() From 34ebe65cf5fffa8e36098cf2ee765e91873a130a Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:50:16 +0200 Subject: [PATCH 131/156] added apc,redis and memcache(d) to travis --- .travis.yml | 5 +++++ tests/unit/data/travis/memcache-setup.sh | 16 +++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index d8200f0..d5aa6c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,16 @@ php: env: - CUBRID_VERSION=9.1.0 +services: + - redis-server + before_script: - composer self-update && composer --version - composer require satooshi/php-coveralls 0.6.* - mysql -e 'CREATE DATABASE yiitest;'; - psql -U postgres -c 'CREATE DATABASE yiitest;'; + - tests/unit/data/travis/apc-setup.sh + - tests/unit/data/travis/memcache-setup.sh - tests/unit/data/travis/cubrid-setup.sh script: diff --git a/tests/unit/data/travis/memcache-setup.sh b/tests/unit/data/travis/memcache-setup.sh index d0a9888..4a0a311 100755 --- a/tests/unit/data/travis/memcache-setup.sh +++ b/tests/unit/data/travis/memcache-setup.sh @@ -1,16 +1,18 @@ #!/bin/sh install_memcache() { - if [ "$(expr "$TRAVIS_PHP_VERSION" ">=" "5.5")" -eq 1 ]; then - MEMCACHE_VERSION="2.2.7" - wget "http://pecl.php.net/get/memcache-$MEMCACHE_VERSION.tgz" && - tar -zxf "memcache-$MEMCACHE_VERSION.tgz" && - sh -c "cd memcache-$MEMCACHE_VERSION && phpize && ./configure --enable-memcache && make && sudo make install" - fi +# if [ "$(expr "$TRAVIS_PHP_VERSION" ">=" "5.5")" -eq 1 ]; then +# MEMCACHE_VERSION="2.2.7" +# wget "http://pecl.php.net/get/memcache-$MEMCACHE_VERSION.tgz" && +# tar -zxf "memcache-$MEMCACHE_VERSION.tgz" && +# sh -c "cd memcache-$MEMCACHE_VERSION && phpize && ./configure --enable-memcache && make && sudo make install" +# fi echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini return $? } -install_memcache > ~/memcache.log || ( echo "=== MEMCACHE BUILD FAILED ==="; cat ~/memcache.log ) \ No newline at end of file +install_memcache > ~/memcache.log || ( echo "=== MEMCACHE BUILD FAILED ==="; cat ~/memcache.log ) + +echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini From a1af321d38d3df38e6068b64674e4477c32bdea2 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:56:10 +0200 Subject: [PATCH 132/156] fixed typo in apc install script --- tests/unit/data/travis/apc-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/data/travis/apc-setup.sh b/tests/unit/data/travis/apc-setup.sh index 3355f8f..e5e8734 100755 --- a/tests/unit/data/travis/apc-setup.sh +++ b/tests/unit/data/travis/apc-setup.sh @@ -1,2 +1,2 @@ -echo "extension = .so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini +echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini echo "apc.enable_cli = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini \ No newline at end of file From 0a5b1a935ea6d5bb677bcc828f14a4e97ba50cfb Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sat, 14 Sep 2013 12:57:45 +0200 Subject: [PATCH 133/156] simplified memcache installation on travis --- tests/unit/data/travis/memcache-setup.sh | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/tests/unit/data/travis/memcache-setup.sh b/tests/unit/data/travis/memcache-setup.sh index 4a0a311..6b623d6 100755 --- a/tests/unit/data/travis/memcache-setup.sh +++ b/tests/unit/data/travis/memcache-setup.sh @@ -1,18 +1,4 @@ #!/bin/sh -install_memcache() { -# if [ "$(expr "$TRAVIS_PHP_VERSION" ">=" "5.5")" -eq 1 ]; then -# MEMCACHE_VERSION="2.2.7" -# wget "http://pecl.php.net/get/memcache-$MEMCACHE_VERSION.tgz" && -# tar -zxf "memcache-$MEMCACHE_VERSION.tgz" && -# sh -c "cd memcache-$MEMCACHE_VERSION && phpize && ./configure --enable-memcache && make && sudo make install" -# fi - - echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - return $? -} - -install_memcache > ~/memcache.log || ( echo "=== MEMCACHE BUILD FAILED ==="; cat ~/memcache.log ) - +echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini From a2b4ef0f8245d2bcca2880a8b01314d5a5749ab9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 14 Sep 2013 08:35:37 -0400 Subject: [PATCH 134/156] Fixes #876. --- framework/yii/db/ActiveRecord.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index a09c60a..e1c4b4f 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -889,6 +889,7 @@ class ActiveRecord extends Model } $values = $this->getDirtyAttributes($attributes); if (empty($values)) { + $this->afterSave(false); return 0; } $condition = $this->getOldPrimaryKey(true); From 523a63f5763c05c341c481fd473c1e54c63d2f14 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 15 Sep 2013 15:08:43 +0400 Subject: [PATCH 135/156] Added TYPE_BIGPK to Schema --- framework/yii/db/QueryBuilder.php | 1 + framework/yii/db/Schema.php | 1 + framework/yii/db/cubrid/QueryBuilder.php | 1 + framework/yii/db/mssql/QueryBuilder.php | 1 + framework/yii/db/mysql/QueryBuilder.php | 2 ++ framework/yii/db/pgsql/QueryBuilder.php | 1 + framework/yii/db/pgsql/Schema.php | 2 +- framework/yii/db/sqlite/QueryBuilder.php | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 7ae5369..3cc7971 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -491,6 +491,7 @@ class QueryBuilder extends \yii\base\Object * physical types): * * - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY" + * - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY" * - `string`: string type, will be converted into "varchar(255)" * - `text`: a long string type, will be converted into "text" * - `smallint`: a small integer type, will be converted into "smallint(6)" diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index fad91fa..396e944 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -35,6 +35,7 @@ abstract class Schema extends Object * The followings are the supported abstract column data types. */ const TYPE_PK = 'pk'; + const TYPE_BIGPK = 'bigpk'; const TYPE_STRING = 'string'; const TYPE_TEXT = 'text'; const TYPE_SMALLINT = 'smallint'; diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/yii/db/cubrid/QueryBuilder.php index 935b3d3..4b7ef43 100644 --- a/framework/yii/db/cubrid/QueryBuilder.php +++ b/framework/yii/db/cubrid/QueryBuilder.php @@ -22,6 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public $typeMap = array( Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_BIGPK => 'bigint NOT NULL AUTO_INCREMENT PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'varchar', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/yii/db/mssql/QueryBuilder.php index e7f8f80..aeb5be8 100644 --- a/framework/yii/db/mssql/QueryBuilder.php +++ b/framework/yii/db/mssql/QueryBuilder.php @@ -22,6 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public $typeMap = array( Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY', + Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index c7a4256..d4f4497 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -9,6 +9,7 @@ namespace yii\db\mysql; use yii\db\Exception; use yii\base\InvalidParamException; +use yii\db\sqlite\Schema; /** * QueryBuilder is the query builder for MySQL databases. @@ -23,6 +24,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public $typeMap = array( Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', + Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint(6)', diff --git a/framework/yii/db/pgsql/QueryBuilder.php b/framework/yii/db/pgsql/QueryBuilder.php index 22d615e..33c7bf6 100644 --- a/framework/yii/db/pgsql/QueryBuilder.php +++ b/framework/yii/db/pgsql/QueryBuilder.php @@ -22,6 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public $typeMap = array( Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY', + Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint', diff --git a/framework/yii/db/pgsql/Schema.php b/framework/yii/db/pgsql/Schema.php index 969b47a..d131342 100644 --- a/framework/yii/db/pgsql/Schema.php +++ b/framework/yii/db/pgsql/Schema.php @@ -255,7 +255,7 @@ SELECT information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t)) AS numeric ) AS size, - a.attnum = any (ct.conkey) as is_pkey + a.attnum = any (ct.conkey) as is_pkey FROM pg_class c LEFT JOIN pg_attribute a ON a.attrelid = c.oid diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/yii/db/sqlite/QueryBuilder.php index be0275a..4e210f8 100644 --- a/framework/yii/db/sqlite/QueryBuilder.php +++ b/framework/yii/db/sqlite/QueryBuilder.php @@ -24,6 +24,7 @@ class QueryBuilder extends \yii\db\QueryBuilder */ public $typeMap = array( Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', + Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_TEXT => 'text', Schema::TYPE_SMALLINT => 'smallint', From 4a9efc9e756f443319b9da013cdbb4336dd7c84e Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 15 Sep 2013 15:18:38 +0400 Subject: [PATCH 136/156] Corrected schema used for MySQL --- framework/yii/db/mysql/QueryBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index d4f4497..1b67cda 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -9,7 +9,7 @@ namespace yii\db\mysql; use yii\db\Exception; use yii\base\InvalidParamException; -use yii\db\sqlite\Schema; +use yii\db\mysql\Schema; /** * QueryBuilder is the query builder for MySQL databases. From 0284bc4a457a5dedb20ac13eff3034d406826f20 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Sun, 15 Sep 2013 16:34:19 +0400 Subject: [PATCH 137/156] Fixes #875: Security::generateRandomKey() can now be safely used in URLs --- framework/yii/helpers/SecurityBase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/helpers/SecurityBase.php b/framework/yii/helpers/SecurityBase.php index 541b311..1cd9403 100644 --- a/framework/yii/helpers/SecurityBase.php +++ b/framework/yii/helpers/SecurityBase.php @@ -140,12 +140,12 @@ class SecurityBase public static function generateRandomKey($length = 32) { if (function_exists('openssl_random_pseudo_bytes')) { - $key = base64_encode(openssl_random_pseudo_bytes($length, $strong)); + $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), array('+' => '_', '/' => '~')); if ($strong) { return substr($key, 0, $length); } } - $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_~'; return substr(str_shuffle(str_repeat($chars, 5)), 0, $length); } From a8d21805f5b0535805c62e22d83b0d84a996fd05 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 16 Sep 2013 01:49:54 +0400 Subject: [PATCH 138/156] Security::generateRandomKey enhancements: - Equals sign is now replaced with dot. - Slash is now replaced with dash. - Better phpdoc. --- framework/yii/helpers/SecurityBase.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/yii/helpers/SecurityBase.php b/framework/yii/helpers/SecurityBase.php index 1cd9403..0ecad0f 100644 --- a/framework/yii/helpers/SecurityBase.php +++ b/framework/yii/helpers/SecurityBase.php @@ -133,19 +133,19 @@ class SecurityBase } /** - * Generates a random key. + * Generates a random key. The key may contain uppercase and lowercase latin letters, digits, underscore, dash and dot. * @param integer $length the length of the key that should be generated * @return string the generated random key */ public static function generateRandomKey($length = 32) { if (function_exists('openssl_random_pseudo_bytes')) { - $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), array('+' => '_', '/' => '~')); + $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), array('+' => '_', '/' => '-', '=' => '.')); if ($strong) { return substr($key, 0, $length); } } - $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_~'; + $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.'; return substr(str_shuffle(str_repeat($chars, 5)), 0, $length); } From f34d7064ead978973e03649dea0e5b9345764aa7 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 16 Sep 2013 02:08:29 +0400 Subject: [PATCH 139/156] Better phpdoc for AccessControl --- framework/yii/web/AccessControl.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/framework/yii/web/AccessControl.php b/framework/yii/web/AccessControl.php index 35d6cae..7f791b8 100644 --- a/framework/yii/web/AccessControl.php +++ b/framework/yii/web/AccessControl.php @@ -31,15 +31,17 @@ use yii\base\ActionFilter; * 'class' => \yii\web\AccessControl::className(), * 'only' => array('create', 'update'), * 'rules' => array( + * // deny all POST requests + * array( + * 'allow' => false, + * 'verbs' => array('POST') + * ), * // allow authenticated users * array( * 'allow' => true, * 'roles' => array('@'), * ), - * // deny all - * array( - * 'allow' => false, - * ), + * // everything else is denied * ), * ), * ); From 325f83f66b801605638ebbd0d7635d863ab29c3f Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 16 Sep 2013 02:41:19 +0400 Subject: [PATCH 140/156] Basic application enhancements. - Turned on CSRF validation by default. - Application params are now readed before config is defined to be able to use values from params when configuring. - Added access control for login and logout. --- apps/basic/config/web.php | 7 +++++-- apps/basic/controllers/SiteController.php | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/basic/config/web.php b/apps/basic/config/web.php index 1433a64..e7d9420 100644 --- a/apps/basic/config/web.php +++ b/apps/basic/config/web.php @@ -1,9 +1,12 @@ 'bootstrap', 'basePath' => dirname(__DIR__), 'components' => array( + 'request' => array( + 'enableCsrfValidation' => true, + ), 'cache' => array( 'class' => 'yii\caching\FileCache', ), @@ -23,7 +26,7 @@ $config = array( ), ), ), - 'params' => require(__DIR__ . '/params.php'), + 'params' => $params, ); if (YII_ENV_DEV) { diff --git a/apps/basic/controllers/SiteController.php b/apps/basic/controllers/SiteController.php index cd0b3fb..785eddf 100644 --- a/apps/basic/controllers/SiteController.php +++ b/apps/basic/controllers/SiteController.php @@ -9,6 +9,28 @@ use app\models\ContactForm; class SiteController extends Controller { + public function behaviors() + { + return array( + 'access' => array( + 'class' => \yii\web\AccessControl::className(), + 'only' => array('login', 'logout'), + 'rules' => array( + array( + 'actions' => array('login'), + 'allow' => true, + 'roles' => array('?'), + ), + array( + 'actions' => array('logout'), + 'allow' => true, + 'roles' => array('@'), + ), + ), + ), + ); + } + public function actions() { return array( From f5778b6bf0e9a39a65f3ce6a9788e2d3ff9676b1 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 16 Sep 2013 02:46:29 +0400 Subject: [PATCH 141/156] Advanced application enhancements. - Turned on CSRF validation by default. - Added access control for login, signup and logout for frontend application. - Added access control for login, logout and index for backend application. - YII_ENV is now defined for all applications. - No trace is writted to logs if debug is turned off. - Added default error view for frontend and backend. - In frontend application captcha will always ask for "testme" if YII_ENV is defined as "test". --- apps/advanced/backend/config/main.php | 8 +++++- .../backend/controllers/SiteController.php | 30 ++++++++++++++++++++++ apps/advanced/backend/views/site/error.php | 29 +++++++++++++++++++++ .../environments/dev/backend/web/index.php | 2 +- .../environments/dev/frontend/web/index.php | 3 +-- apps/advanced/environments/dev/yii | 1 + .../environments/prod/backend/web/index.php | 2 +- .../environments/prod/frontend/web/index.php | 3 +-- apps/advanced/environments/prod/yii | 1 + apps/advanced/frontend/config/main.php | 8 +++++- .../frontend/controllers/SiteController.php | 26 +++++++++++++++++++ apps/advanced/frontend/views/site/error.php | 29 +++++++++++++++++++++ 12 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 apps/advanced/backend/views/site/error.php create mode 100644 apps/advanced/frontend/views/site/error.php diff --git a/apps/advanced/backend/config/main.php b/apps/advanced/backend/config/main.php index 377d34c..30c1825 100644 --- a/apps/advanced/backend/config/main.php +++ b/apps/advanced/backend/config/main.php @@ -17,13 +17,16 @@ return array( 'modules' => array( ), 'components' => array( + 'request' => array( + 'enableCsrfValidation' => true, + ), 'db' => $params['components.db'], 'cache' => $params['components.cache'], 'user' => array( - 'class' => 'yii\web\User', 'identityClass' => 'common\models\User', ), 'log' => array( + 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => array( array( 'class' => 'yii\log\FileTarget', @@ -31,6 +34,9 @@ return array( ), ), ), + 'errorHandler' => array( + 'errorAction' => 'site/error', + ), ), 'params' => $params, ); diff --git a/apps/advanced/backend/controllers/SiteController.php b/apps/advanced/backend/controllers/SiteController.php index 480406a..28f2310 100644 --- a/apps/advanced/backend/controllers/SiteController.php +++ b/apps/advanced/backend/controllers/SiteController.php @@ -8,6 +8,36 @@ use common\models\LoginForm; class SiteController extends Controller { + public function behaviors() + { + return array( + 'access' => array( + 'class' => \yii\web\AccessControl::className(), + 'rules' => array( + array( + 'actions' => array('login'), + 'allow' => true, + 'roles' => array('?'), + ), + array( + 'actions' => array('logout', 'index'), + 'allow' => true, + 'roles' => array('@'), + ), + ), + ), + ); + } + + public function actions() + { + return array( + 'error' => array( + 'class' => 'yii\web\ErrorAction', + ), + ); + } + public function actionIndex() { return $this->render('index'); diff --git a/apps/advanced/backend/views/site/error.php b/apps/advanced/backend/views/site/error.php new file mode 100644 index 0000000..024e27d --- /dev/null +++ b/apps/advanced/backend/views/site/error.php @@ -0,0 +1,29 @@ +title = $name; +?> +
+ +

title); ?>

+ +
+ +
+ +

+ The above error occurred while the Web server was processing your request. +

+

+ Please contact us if you think this is a server error. Thank you. +

+ +
diff --git a/apps/advanced/environments/dev/backend/web/index.php b/apps/advanced/environments/dev/backend/web/index.php index 7d47419..2113419 100644 --- a/apps/advanced/environments/dev/backend/web/index.php +++ b/apps/advanced/environments/dev/backend/web/index.php @@ -1,6 +1,6 @@ 'yii\gii\Module' ), 'components' => array( + 'request' => array( + 'enableCsrfValidation' => true, + ), 'db' => $params['components.db'], 'cache' => $params['components.cache'], 'user' => array( - 'class' => 'yii\web\User', 'identityClass' => 'common\models\User', ), 'log' => array( + 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => array( array( 'class' => 'yii\log\FileTarget', @@ -31,6 +34,9 @@ return array( ), ), ), + 'errorHandler' => array( + 'errorAction' => 'site/error', + ), ), 'params' => $params, ); diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php index 0c1b2f5..be9a634 100644 --- a/apps/advanced/frontend/controllers/SiteController.php +++ b/apps/advanced/frontend/controllers/SiteController.php @@ -12,11 +12,37 @@ use yii\helpers\Security; class SiteController extends Controller { + public function behaviors() + { + return array( + 'access' => array( + 'class' => \yii\web\AccessControl::className(), + 'only' => array('login', 'logout', 'signup'), + 'rules' => array( + array( + 'actions' => array('login', 'signup'), + 'allow' => true, + 'roles' => array('?'), + ), + array( + 'actions' => array('logout'), + 'allow' => true, + 'roles' => array('@'), + ), + ), + ), + ); + } + public function actions() { return array( + 'error' => array( + 'class' => 'yii\web\ErrorAction', + ), 'captcha' => array( 'class' => 'yii\captcha\CaptchaAction', + 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, ), ); } diff --git a/apps/advanced/frontend/views/site/error.php b/apps/advanced/frontend/views/site/error.php new file mode 100644 index 0000000..024e27d --- /dev/null +++ b/apps/advanced/frontend/views/site/error.php @@ -0,0 +1,29 @@ +title = $name; +?> +
+ +

title); ?>

+ +
+ +
+ +

+ The above error occurred while the Web server was processing your request. +

+

+ Please contact us if you think this is a server error. Thank you. +

+ +
From 51c29e444dc8bbd1ce620f80320c13f998e99f7d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 15 Sep 2013 18:54:26 -0400 Subject: [PATCH 142/156] renamed Request::csrfTokenName to csrfVar. added version, csrfVar and csrfToken to yii js module. --- framework/yii/assets/yii.js | 6 ++++++ framework/yii/helpers/HtmlBase.php | 2 +- framework/yii/web/Request.php | 22 +++++++++++----------- framework/yii/web/YiiAsset.php | 17 +++++++++++++++++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js index 31a57d5..3859a55 100644 --- a/framework/yii/assets/yii.js +++ b/framework/yii/assets/yii.js @@ -43,7 +43,13 @@ */ yii = (function ($) { var pub = { + // version of Yii framework version: '2.0', + // CSRF token name and value. If this is set and a form is created and submitted using JavaScript + // via POST, the CSRF token should be submitted too to pass CSRF validation. + csrfVar: undefined, + csrfToken: undefined, + initModule: function (module) { if (module.isActive === undefined || module.isActive) { if ($.isFunction(module.init)) { diff --git a/framework/yii/helpers/HtmlBase.php b/framework/yii/helpers/HtmlBase.php index a93c93e..a5786cb 100644 --- a/framework/yii/helpers/HtmlBase.php +++ b/framework/yii/helpers/HtmlBase.php @@ -238,7 +238,7 @@ class HtmlBase $method = 'post'; } if ($request->enableCsrfValidation) { - $hiddenInputs[] = static::hiddenInput($request->csrfTokenName, $request->getCsrfToken()); + $hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken()); } } diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 1482633..37aa4a8 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -73,16 +73,16 @@ class Request extends \yii\base\Request * from the same application. If not, a 400 HTTP exception will be raised. * * Note, this feature requires that the user client accepts cookie. Also, to use this feature, - * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfTokenName]]. + * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfVar]]. * You may use [[\yii\web\Html::beginForm()]] to generate his hidden input. * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery */ public $enableCsrfValidation = false; /** - * @var string the name of the token used to prevent CSRF. Defaults to 'YII_CSRF_TOKEN'. - * This property is effectively only when {@link enableCsrfValidation} is true. + * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'. + * This property is effectively only when [[enableCsrfValidation]] is true. */ - public $csrfTokenName = '_csrf'; + public $csrfVar = '_csrf'; /** * @var array the configuration of the CSRF cookie. This property is used only when [[enableCsrfValidation]] is true. * @see Cookie @@ -975,7 +975,7 @@ class Request extends \yii\base\Request public function getCsrfToken() { if ($this->_csrfCookie === null) { - $this->_csrfCookie = $this->getCookies()->get($this->csrfTokenName); + $this->_csrfCookie = $this->getCookies()->get($this->csrfVar); if ($this->_csrfCookie === null) { $this->_csrfCookie = $this->createCsrfCookie(); Yii::$app->getResponse()->getCookies()->add($this->_csrfCookie); @@ -994,7 +994,7 @@ class Request extends \yii\base\Request protected function createCsrfCookie() { $options = $this->csrfCookie; - $options['name'] = $this->csrfTokenName; + $options['name'] = $this->csrfVar; $options['value'] = sha1(uniqid(mt_rand(), true)); return new Cookie($options); } @@ -1015,19 +1015,19 @@ class Request extends \yii\base\Request $cookies = $this->getCookies(); switch ($method) { case 'POST': - $token = $this->getPost($this->csrfTokenName); + $token = $this->getPost($this->csrfVar); break; case 'PUT': - $token = $this->getPut($this->csrfTokenName); + $token = $this->getPut($this->csrfVar); break; case 'PATCH': - $token = $this->getPatch($this->csrfTokenName); + $token = $this->getPatch($this->csrfVar); break; case 'DELETE': - $token = $this->getDelete($this->csrfTokenName); + $token = $this->getDelete($this->csrfVar); } - if (empty($token) || $cookies->getValue($this->csrfTokenName) !== $token) { + if (empty($token) || $cookies->getValue($this->csrfVar) !== $token) { throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.')); } } diff --git a/framework/yii/web/YiiAsset.php b/framework/yii/web/YiiAsset.php index 8a4d77a..3c843a1 100644 --- a/framework/yii/web/YiiAsset.php +++ b/framework/yii/web/YiiAsset.php @@ -7,6 +7,8 @@ namespace yii\web; +use Yii; + /** * @author Qiang Xue * @since 2.0 @@ -20,4 +22,19 @@ class YiiAsset extends AssetBundle public $depends = array( 'yii\web\JqueryAsset', ); + + /** + * @inheritdoc + */ + public function registerAssets($view) + { + parent::registerAssets($view); + $js[] = "yii.version = '" . Yii::getVersion() . "';"; + $request = Yii::$app->getRequest(); + if ($request instanceof Request && $request->enableCsrfValidation) { + $js[] = "yii.csrfVar = '{$request->csrfVar}';"; + $js[] = "yii.csrfToken = '{$request->csrfToken}';"; + } + $view->registerJs(implode("\n", $js)); + } } From ad479dd7f65d7701848968c9ba2169042b29623f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 15 Sep 2013 18:57:11 -0400 Subject: [PATCH 143/156] Modified js registration position. --- framework/yii/web/YiiAsset.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/yii/web/YiiAsset.php b/framework/yii/web/YiiAsset.php index 3c843a1..7d82027 100644 --- a/framework/yii/web/YiiAsset.php +++ b/framework/yii/web/YiiAsset.php @@ -8,6 +8,7 @@ namespace yii\web; use Yii; +use yii\base\View; /** * @author Qiang Xue @@ -29,12 +30,12 @@ class YiiAsset extends AssetBundle public function registerAssets($view) { parent::registerAssets($view); - $js[] = "yii.version = '" . Yii::getVersion() . "';"; + $js[] = "yii.version='" . Yii::getVersion() . "';"; $request = Yii::$app->getRequest(); if ($request instanceof Request && $request->enableCsrfValidation) { - $js[] = "yii.csrfVar = '{$request->csrfVar}';"; - $js[] = "yii.csrfToken = '{$request->csrfToken}';"; + $js[] = "yii.csrfVar='{$request->csrfVar}';"; + $js[] = "yii.csrfToken='{$request->csrfToken}';"; } - $view->registerJs(implode("\n", $js)); + $view->registerJs(implode("\n", $js), View::POS_END); } } From bc9a6f3e16e304c5608eb336dbd8b68253a7fef2 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 16 Sep 2013 23:20:17 +0400 Subject: [PATCH 144/156] Better strtr arguments format --- framework/yii/helpers/SecurityBase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/helpers/SecurityBase.php b/framework/yii/helpers/SecurityBase.php index 0ecad0f..5b192de 100644 --- a/framework/yii/helpers/SecurityBase.php +++ b/framework/yii/helpers/SecurityBase.php @@ -140,7 +140,7 @@ class SecurityBase public static function generateRandomKey($length = 32) { if (function_exists('openssl_random_pseudo_bytes')) { - $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), array('+' => '_', '/' => '-', '=' => '.')); + $key = strtr(base64_encode(openssl_random_pseudo_bytes($length, $strong)), '+/=', '_-.'); if ($strong) { return substr($key, 0, $length); } From dd59dd1db7f3c634ecf334526bae599b36267d29 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 16 Sep 2013 22:13:32 -0400 Subject: [PATCH 145/156] doc fix. --- framework/yii/bootstrap/Nav.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index e19eae1..8d62ec5 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -56,7 +56,7 @@ class Nav extends Widget { /** * @var array list of items in the nav widget. Each array element represents a single - * menu item with the following structure: + * menu item which can be either a string or an array with the following structure: * * - label: string, required, the nav item label. * - url: optional, the item's URL. Defaults to "#". @@ -66,6 +66,8 @@ class Nav extends Widget * - active: boolean, optional, whether the item should be on active state or not. * - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget, * or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus. + * + * It a menu item is a string, it will be rendered directly without HTML encoding. */ public $items = array(); /** From 43d15155c60fc18d4137062ff5b5d0216527602e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 17 Sep 2013 10:01:01 +0200 Subject: [PATCH 146/156] cleanup db `use` statements --- framework/yii/db/cubrid/Schema.php | 1 - framework/yii/db/mssql/Schema.php | 1 - framework/yii/db/mysql/QueryBuilder.php | 1 - 3 files changed, 3 deletions(-) diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index c7b6ad2..99624f6 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -7,7 +7,6 @@ namespace yii\db\cubrid; -use yii\base\NotSupportedException; use yii\db\Expression; use yii\db\TableSchema; use yii\db\ColumnSchema; diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php index 5f53d8e..9def3b4 100644 --- a/framework/yii/db/mssql/Schema.php +++ b/framework/yii/db/mssql/Schema.php @@ -7,7 +7,6 @@ namespace yii\db\mssql; -use yii\db\mssql\TableSchema; use yii\db\ColumnSchema; /** diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/yii/db/mysql/QueryBuilder.php index 1b67cda..386de2f 100644 --- a/framework/yii/db/mysql/QueryBuilder.php +++ b/framework/yii/db/mysql/QueryBuilder.php @@ -9,7 +9,6 @@ namespace yii\db\mysql; use yii\db\Exception; use yii\base\InvalidParamException; -use yii\db\mysql\Schema; /** * QueryBuilder is the query builder for MySQL databases. From 240b42aa0c81928252b3802f7a016436f3367473 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 17 Sep 2013 20:48:20 +0400 Subject: [PATCH 147/156] fixed typo --- framework/yii/bootstrap/Nav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index 8d62ec5..f24f729 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -67,7 +67,7 @@ class Nav extends Widget * - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget, * or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus. * - * It a menu item is a string, it will be rendered directly without HTML encoding. + * If a menu item is a string, it will be rendered directly without HTML encoding. */ public $items = array(); /** From f38c516ac5c2ffee681d0a4bfe5ae3c7b8f9c09f Mon Sep 17 00:00:00 2001 From: Panagiotis Moustafellos Date: Tue, 17 Sep 2013 20:20:18 +0300 Subject: [PATCH 148/156] Added memcached service in Travis YAML --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d5aa6c3..2add223 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: services: - redis-server + - memcached before_script: - composer self-update && composer --version @@ -24,4 +25,4 @@ script: - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata after_script: - - php vendor/bin/coveralls \ No newline at end of file + - php vendor/bin/coveralls From 1aa836ffc74ba2788786fb32b2a939a7b54ae1b7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 17 Sep 2013 21:00:19 -0400 Subject: [PATCH 149/156] use meta tags to pass CSRF token. --- framework/yii/assets/yii.js | 19 +++++++++++++------ framework/yii/base/View.php | 8 ++++++++ framework/yii/web/YiiAsset.php | 15 --------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js index 3859a55..f2ac379 100644 --- a/framework/yii/assets/yii.js +++ b/framework/yii/assets/yii.js @@ -43,12 +43,19 @@ */ yii = (function ($) { var pub = { - // version of Yii framework - version: '2.0', - // CSRF token name and value. If this is set and a form is created and submitted using JavaScript - // via POST, the CSRF token should be submitted too to pass CSRF validation. - csrfVar: undefined, - csrfToken: undefined, + /** + * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled. + */ + getCsrfVar: function() { + return $('meta[name=csrf-var]').attr('content'); + }, + + /** + * @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled. + */ + getCsrfToken: function() { + return $('meta[name=csrf-token]').attr('content'); + }, initModule: function (module) { if (module.isActive === undefined || module.isActive) { diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index 4d3d996..77d0e5c 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -12,6 +12,7 @@ use yii\helpers\FileHelper; use yii\helpers\Html; use yii\web\JqueryAsset; use yii\web\AssetBundle; +use yii\web\Request; use yii\widgets\Block; use yii\widgets\ContentDecorator; use yii\widgets\FragmentCache; @@ -708,6 +709,13 @@ class View extends Component if (!empty($this->metaTags)) { $lines[] = implode("\n", $this->metaTags); } + + $request = Yii::$app->getRequest(); + if ($request instanceof Request && $request->enableCsrfValidation) { + $lines[] = Html::tag('meta', '', array('name' => 'csrf-var', 'content' => $request->csrfVar)); + $lines[] = Html::tag('meta', '', array('name' => 'csrf-token', 'content' => $request->getCsrfToken())); + } + if (!empty($this->linkTags)) { $lines[] = implode("\n", $this->linkTags); } diff --git a/framework/yii/web/YiiAsset.php b/framework/yii/web/YiiAsset.php index 7d82027..2ad5384 100644 --- a/framework/yii/web/YiiAsset.php +++ b/framework/yii/web/YiiAsset.php @@ -23,19 +23,4 @@ class YiiAsset extends AssetBundle public $depends = array( 'yii\web\JqueryAsset', ); - - /** - * @inheritdoc - */ - public function registerAssets($view) - { - parent::registerAssets($view); - $js[] = "yii.version='" . Yii::getVersion() . "';"; - $request = Yii::$app->getRequest(); - if ($request instanceof Request && $request->enableCsrfValidation) { - $js[] = "yii.csrfVar='{$request->csrfVar}';"; - $js[] = "yii.csrfToken='{$request->csrfToken}';"; - } - $view->registerJs(implode("\n", $js), View::POS_END); - } } From 2db91187db2033eec943b44b402a1fbe8838a888 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 17 Sep 2013 21:02:48 -0400 Subject: [PATCH 150/156] Use .prop() instead .attr(). --- framework/yii/assets/yii.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js index f2ac379..eb5ecf6 100644 --- a/framework/yii/assets/yii.js +++ b/framework/yii/assets/yii.js @@ -47,14 +47,14 @@ yii = (function ($) { * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled. */ getCsrfVar: function() { - return $('meta[name=csrf-var]').attr('content'); + return $('meta[name=csrf-var]').prop('content'); }, /** * @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled. */ getCsrfToken: function() { - return $('meta[name=csrf-token]').attr('content'); + return $('meta[name=csrf-token]').prop('content'); }, initModule: function (module) { From 2deff126cfdba665b9ab8654d963298ee0616f96 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 17 Sep 2013 21:43:03 -0400 Subject: [PATCH 151/156] Supports sending CSRF token via HTTP header. --- framework/yii/web/Request.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 37aa4a8..eb1652c 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -68,19 +68,28 @@ use yii\helpers\Security; class Request extends \yii\base\Request { /** + * The name of the HTTP header for sending CSRF token. + */ + const CSRF_HEADER = 'X-CSRF-TOKEN'; + + /** * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false. - * By setting this property to true, forms submitted to an Yii Web application must be originated + * When CSRF validation is enabled, forms submitted to an Yii Web application must be originated * from the same application. If not, a 400 HTTP exception will be raised. * * Note, this feature requires that the user client accepts cookie. Also, to use this feature, * forms submitted via POST method must contain a hidden input whose name is specified by [[csrfVar]]. * You may use [[\yii\web\Html::beginForm()]] to generate his hidden input. + * + * In JavaScript, you may get the values of [[csrfVar]] and [[csrfToken]] via `yii.getCsrfVar()` and + * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered. + * * @see http://en.wikipedia.org/wiki/Cross-site_request_forgery */ public $enableCsrfValidation = false; /** * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'. - * This property is effectively only when [[enableCsrfValidation]] is true. + * This property is used only when [[enableCsrfValidation]] is true. */ public $csrfVar = '_csrf'; /** @@ -986,6 +995,14 @@ class Request extends \yii\base\Request } /** + * @return string the CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned if no such header is sent. + */ + public function getCsrfTokenFromHeader() + { + return isset($_SERVER[self::CSRF_HEADER]) ? $_SERVER[self::CSRF_HEADER] : null; + } + + /** * Creates a cookie with a randomly generated CSRF token. * Initial values specified in [[csrfCookie]] will be applied to the generated cookie. * @return Cookie the generated cookie @@ -1012,7 +1029,7 @@ class Request extends \yii\base\Request } $method = $this->getMethod(); if ($method === 'POST' || $method === 'PUT' || $method === 'PATCH' || $method === 'DELETE') { - $cookies = $this->getCookies(); + $trueToken = $this->getCookies()->getValue($this->csrfVar); switch ($method) { case 'POST': $token = $this->getPost($this->csrfVar); @@ -1027,7 +1044,8 @@ class Request extends \yii\base\Request $token = $this->getDelete($this->csrfVar); } - if (empty($token) || $cookies->getValue($this->csrfVar) !== $token) { + $valid = !empty($token) && $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken; + if (!$valid) { throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.')); } } From 9836b28ed38428e9f7aa84e95fff3d05638550b8 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 18 Sep 2013 00:35:31 -0400 Subject: [PATCH 152/156] Fixes #884. --- framework/yii/base/View.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index 77d0e5c..df0b2b2 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -12,7 +12,6 @@ use yii\helpers\FileHelper; use yii\helpers\Html; use yii\web\JqueryAsset; use yii\web\AssetBundle; -use yii\web\Request; use yii\widgets\Block; use yii\widgets\ContentDecorator; use yii\widgets\FragmentCache; @@ -711,7 +710,7 @@ class View extends Component } $request = Yii::$app->getRequest(); - if ($request instanceof Request && $request->enableCsrfValidation) { + if ($request instanceof \yii\web\Request && $request->enableCsrfValidation) { $lines[] = Html::tag('meta', '', array('name' => 'csrf-var', 'content' => $request->csrfVar)); $lines[] = Html::tag('meta', '', array('name' => 'csrf-token', 'content' => $request->getCsrfToken())); } From 0e261fba5ad7ebb020485f803939b99ac115b49d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 18 Sep 2013 16:00:55 +0200 Subject: [PATCH 153/156] moved redis out of yii\db namespace --- framework/yii/caching/RedisCache.php | 10 +- framework/yii/db/redis/Connection.php | 428 --------------------------------- framework/yii/db/redis/Transaction.php | 93 ------- framework/yii/redis/Connection.php | 428 +++++++++++++++++++++++++++++++++ framework/yii/redis/Transaction.php | 93 +++++++ 5 files changed, 525 insertions(+), 527 deletions(-) delete mode 100644 framework/yii/db/redis/Connection.php delete mode 100644 framework/yii/db/redis/Transaction.php create mode 100644 framework/yii/redis/Connection.php create mode 100644 framework/yii/redis/Transaction.php diff --git a/framework/yii/caching/RedisCache.php b/framework/yii/caching/RedisCache.php index 09ce599..5c778fc 100644 --- a/framework/yii/caching/RedisCache.php +++ b/framework/yii/caching/RedisCache.php @@ -7,7 +7,7 @@ namespace yii\caching; -use yii\db\redis\Connection; +use yii\redis\Connection; /** * RedisCache implements a cache application component based on [redis](http://redis.io/). @@ -39,7 +39,7 @@ use yii\db\redis\Connection; * ) * ~~~ * - * @property \yii\db\redis\Connection $connection This property is read-only. + * @property Connection $connection The redis connection object. This property is read-only. * * @author Carsten Brandt * @since 2.0 @@ -71,7 +71,7 @@ class RedisCache extends Cache */ public $dataTimeout = null; /** - * @var \yii\db\redis\Connection the redis connection + * @var Connection the redis connection */ private $_connection; @@ -88,9 +88,7 @@ class RedisCache extends Cache /** * Returns the redis connection object. * Establishes a connection to the redis server if it does not already exists. - * - * TODO throw exception on error - * @return \yii\db\redis\Connection + * @return Connection the redis connection object. */ public function getConnection() { diff --git a/framework/yii/db/redis/Connection.php b/framework/yii/db/redis/Connection.php deleted file mode 100644 index 68b40d3..0000000 --- a/framework/yii/db/redis/Connection.php +++ /dev/null @@ -1,428 +0,0 @@ - - * @since 2.0 - */ -class Connection extends Component -{ - /** - * @event Event an event that is triggered after a DB connection is established - */ - const EVENT_AFTER_OPEN = 'afterOpen'; - - /** - * @var string the Data Source Name, or DSN, contains the information required to connect to the database. - * DSN format: redis://server:port[/db] - * Where db is a zero based integer which refers to the DB to use. - * If no DB is given, ID 0 is used. - * - * Example: redis://localhost:6379/2 - */ - public $dsn; - /** - * @var string the password for establishing DB connection. Defaults to null meaning no AUTH command is send. - * See http://redis.io/commands/auth - */ - public $password; - /** - * @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout") - */ - public $connectionTimeout = null; - /** - * @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used. - */ - public $dataTimeout = null; - - /** - * @var array List of available redis commands http://redis.io/commands - */ - public $redisCommands = array( - 'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available - 'BRPOPLPUSH', // source destination timeout Pop a value from a list, push it to another list and return it; or block until one is available - 'CLIENT KILL', // ip:port Kill the connection of a client - 'CLIENT LIST', // Get the list of client connections - 'CLIENT GETNAME', // Get the current connection name - 'CLIENT SETNAME', // connection-name Set the current connection name - 'CONFIG GET', // parameter Get the value of a configuration parameter - 'CONFIG SET', // parameter value Set a configuration parameter to the given value - 'CONFIG RESETSTAT', // Reset the stats returned by INFO - 'DBSIZE', // Return the number of keys in the selected database - 'DEBUG OBJECT', // key Get debugging information about a key - 'DEBUG SEGFAULT', // Make the server crash - 'DECR', // key Decrement the integer value of a key by one - 'DECRBY', // key decrement Decrement the integer value of a key by the given number - 'DEL', // key [key ...] Delete a key - 'DISCARD', // Discard all commands issued after MULTI - 'DUMP', // key Return a serialized version of the value stored at the specified key. - 'ECHO', // message Echo the given string - 'EVAL', // script numkeys key [key ...] arg [arg ...] Execute a Lua script server side - 'EVALSHA', // sha1 numkeys key [key ...] arg [arg ...] Execute a Lua script server side - 'EXEC', // Execute all commands issued after MULTI - 'EXISTS', // key Determine if a key exists - 'EXPIRE', // key seconds Set a key's time to live in seconds - 'EXPIREAT', // key timestamp Set the expiration for a key as a UNIX timestamp - 'FLUSHALL', // Remove all keys from all databases - 'FLUSHDB', // Remove all keys from the current database - 'GET', // key Get the value of a key - 'GETBIT', // key offset Returns the bit value at offset in the string value stored at key - 'GETRANGE', // key start end Get a substring of the string stored at a key - 'GETSET', // key value Set the string value of a key and return its old value - 'HDEL', // key field [field ...] Delete one or more hash fields - 'HEXISTS', // key field Determine if a hash field exists - 'HGET', // key field Get the value of a hash field - 'HGETALL', // key Get all the fields and values in a hash - 'HINCRBY', // key field increment Increment the integer value of a hash field by the given number - 'HINCRBYFLOAT', // key field increment Increment the float value of a hash field by the given amount - 'HKEYS', // key Get all the fields in a hash - 'HLEN', // key Get the number of fields in a hash - 'HMGET', // key field [field ...] Get the values of all the given hash fields - 'HMSET', // key field value [field value ...] Set multiple hash fields to multiple values - 'HSET', // key field value Set the string value of a hash field - 'HSETNX', // key field value Set the value of a hash field, only if the field does not exist - 'HVALS', // key Get all the values in a hash - 'INCR', // key Increment the integer value of a key by one - 'INCRBY', // key increment Increment the integer value of a key by the given amount - 'INCRBYFLOAT', // key increment Increment the float value of a key by the given amount - 'INFO', // [section] Get information and statistics about the server - 'KEYS', // pattern Find all keys matching the given pattern - 'LASTSAVE', // Get the UNIX time stamp of the last successful save to disk - 'LINDEX', // key index Get an element from a list by its index - 'LINSERT', // key BEFORE|AFTER pivot value Insert an element before or after another element in a list - 'LLEN', // key Get the length of a list - 'LPOP', // key Remove and get the first element in a list - 'LPUSH', // key value [value ...] Prepend one or multiple values to a list - 'LPUSHX', // key value Prepend a value to a list, only if the list exists - 'LRANGE', // key start stop Get a range of elements from a list - 'LREM', // key count value Remove elements from a list - 'LSET', // key index value Set the value of an element in a list by its index - 'LTRIM', // key start stop Trim a list to the specified range - 'MGET', // key [key ...] Get the values of all the given keys - 'MIGRATE', // host port key destination-db timeout Atomically transfer a key from a Redis instance to another one. - 'MONITOR', // Listen for all requests received by the server in real time - 'MOVE', // key db Move a key to another database - 'MSET', // key value [key value ...] Set multiple keys to multiple values - 'MSETNX', // key value [key value ...] Set multiple keys to multiple values, only if none of the keys exist - 'MULTI', // Mark the start of a transaction block - 'OBJECT', // subcommand [arguments [arguments ...]] Inspect the internals of Redis objects - 'PERSIST', // key Remove the expiration from a key - 'PEXPIRE', // key milliseconds Set a key's time to live in milliseconds - 'PEXPIREAT', // key milliseconds-timestamp Set the expiration for a key as a UNIX timestamp specified in milliseconds - 'PING', // Ping the server - 'PSETEX', // key milliseconds value Set the value and expiration in milliseconds of a key - 'PSUBSCRIBE', // pattern [pattern ...] Listen for messages published to channels matching the given patterns - 'PTTL', // key Get the time to live for a key in milliseconds - 'PUBLISH', // channel message Post a message to a channel - 'PUNSUBSCRIBE', // [pattern [pattern ...]] Stop listening for messages posted to channels matching the given patterns - 'QUIT', // Close the connection - 'RANDOMKEY', // Return a random key from the keyspace - 'RENAME', // key newkey Rename a key - 'RENAMENX', // key newkey Rename a key, only if the new key does not exist - 'RESTORE', // key ttl serialized-value Create a key using the provided serialized value, previously obtained using DUMP. - 'RPOP', // key Remove and get the last element in a list - 'RPOPLPUSH', // source destination Remove the last element in a list, append it to another list and return it - 'RPUSH', // key value [value ...] Append one or multiple values to a list - 'RPUSHX', // key value Append a value to a list, only if the list exists - 'SADD', // key member [member ...] Add one or more members to a set - 'SAVE', // Synchronously save the dataset to disk - 'SCARD', // key Get the number of members in a set - 'SCRIPT EXISTS', // script [script ...] Check existence of scripts in the script cache. - 'SCRIPT FLUSH', // Remove all the scripts from the script cache. - 'SCRIPT KILL', // Kill the script currently in execution. - 'SCRIPT LOAD', // script Load the specified Lua script into the script cache. - 'SDIFF', // key [key ...] Subtract multiple sets - 'SDIFFSTORE', // destination key [key ...] Subtract multiple sets and store the resulting set in a key - 'SELECT', // index Change the selected database for the current connection - 'SET', // key value Set the string value of a key - 'SETBIT', // key offset value Sets or clears the bit at offset in the string value stored at key - 'SETEX', // key seconds value Set the value and expiration of a key - 'SETNX', // key value Set the value of a key, only if the key does not exist - 'SETRANGE', // key offset value Overwrite part of a string at key starting at the specified offset - 'SHUTDOWN', // [NOSAVE] [SAVE] Synchronously save the dataset to disk and then shut down the server - 'SINTER', // key [key ...] Intersect multiple sets - 'SINTERSTORE', // destination key [key ...] Intersect multiple sets and store the resulting set in a key - 'SISMEMBER', // key member Determine if a given value is a member of a set - 'SLAVEOF', // host port Make the server a slave of another instance, or promote it as master - 'SLOWLOG', // subcommand [argument] Manages the Redis slow queries log - 'SMEMBERS', // key Get all the members in a set - 'SMOVE', // source destination member Move a member from one set to another - 'SORT', // key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination] Sort the elements in a list, set or sorted set - 'SPOP', // key Remove and return a random member from a set - 'SRANDMEMBER', // key [count] Get one or multiple random members from a set - 'SREM', // key member [member ...] Remove one or more members from a set - 'STRLEN', // key Get the length of the value stored in a key - 'SUBSCRIBE', // channel [channel ...] Listen for messages published to the given channels - 'SUNION', // key [key ...] Add multiple sets - 'SUNIONSTORE', // destination key [key ...] Add multiple sets and store the resulting set in a key - 'SYNC', // Internal command used for replication - 'TIME', // Return the current server time - 'TTL', // key Get the time to live for a key - 'TYPE', // key Determine the type stored at key - 'UNSUBSCRIBE', // [channel [channel ...]] Stop listening for messages posted to the given channels - 'UNWATCH', // Forget about all watched keys - 'WATCH', // key [key ...] Watch the given keys to determine execution of the MULTI/EXEC block - 'ZADD', // key score member [score member ...] Add one or more members to a sorted set, or update its score if it already exists - 'ZCARD', // key Get the number of members in a sorted set - 'ZCOUNT', // key min max Count the members in a sorted set with scores within the given values - 'ZINCRBY', // key increment member Increment the score of a member in a sorted set - 'ZINTERSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Intersect multiple sorted sets and store the resulting sorted set in a new key - 'ZRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index - 'ZRANGEBYSCORE', // key min max [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score - 'ZRANK', // key member Determine the index of a member in a sorted set - 'ZREM', // key member [member ...] Remove one or more members from a sorted set - 'ZREMRANGEBYRANK', // key start stop Remove all members in a sorted set within the given indexes - 'ZREMRANGEBYSCORE', // key min max Remove all members in a sorted set within the given scores - 'ZREVRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index, with scores ordered from high to low - 'ZREVRANGEBYSCORE', // key max min [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score, with scores ordered from high to low - 'ZREVRANK', // key member Determine the index of a member in a sorted set, with scores ordered from high to low - 'ZSCORE', // key member Get the score associated with the given member in a sorted set - 'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key - ); - /** - * @var Transaction the currently active transaction - */ - private $_transaction; - /** - * @var resource redis socket connection - */ - private $_socket; - - /** - * Closes the connection when this component is being serialized. - * @return array - */ - public function __sleep() - { - $this->close(); - return array_keys(get_object_vars($this)); - } - - /** - * Returns a value indicating whether the DB connection is established. - * @return boolean whether the DB connection is established - */ - public function getIsActive() - { - return $this->_socket !== null; - } - - /** - * Establishes a DB connection. - * It does nothing if a DB connection has already been established. - * @throws Exception if connection fails - */ - public function open() - { - if ($this->_socket === null) { - if (empty($this->dsn)) { - throw new InvalidConfigException('Connection.dsn cannot be empty.'); - } - $dsn = explode('/', $this->dsn); - $host = $dsn[2]; - if (strpos($host, ':')===false) { - $host .= ':6379'; - } - $db = isset($dsn[3]) ? $dsn[3] : 0; - - \Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__); - $this->_socket = @stream_socket_client( - $host, - $errorNumber, - $errorDescription, - $this->connectionTimeout ? $this->connectionTimeout : ini_get("default_socket_timeout") - ); - if ($this->_socket) { - if ($this->dataTimeout !== null) { - stream_set_timeout($this->_socket, $timeout=(int)$this->dataTimeout, (int) (($this->dataTimeout - $timeout) * 1000000)); - } - if ($this->password !== null) { - $this->executeCommand('AUTH', array($this->password)); - } - $this->executeCommand('SELECT', array($db)); - $this->initConnection(); - } else { - \Yii::error("Failed to open DB connection ({$this->dsn}): " . $errorNumber . ' - ' . $errorDescription, __CLASS__); - $message = YII_DEBUG ? 'Failed to open DB connection: ' . $errorNumber . ' - ' . $errorDescription : 'Failed to open DB connection.'; - throw new Exception($message, $errorDescription, (int)$errorNumber); - } - } - } - - /** - * Closes the currently active DB connection. - * It does nothing if the connection is already closed. - */ - public function close() - { - if ($this->_socket !== null) { - \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); - $this->executeCommand('QUIT'); - stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR); - $this->_socket = null; - $this->_transaction = null; - } - } - - /** - * Initializes the DB connection. - * This method is invoked right after the DB connection is established. - * The default implementation triggers an [[EVENT_AFTER_OPEN]] event. - */ - protected function initConnection() - { - $this->trigger(self::EVENT_AFTER_OPEN); - } - - /** - * Returns the currently active transaction. - * @return Transaction the currently active transaction. Null if no active transaction. - */ - public function getTransaction() - { - return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null; - } - - /** - * Starts a transaction. - * @return Transaction the transaction initiated - */ - public function beginTransaction() - { - $this->open(); - $this->_transaction = new Transaction(array( - 'db' => $this, - )); - $this->_transaction->begin(); - return $this->_transaction; - } - - /** - * Returns the name of the DB driver for the current [[dsn]]. - * @return string name of the DB driver - */ - public function getDriverName() - { - if (($pos = strpos($this->dsn, ':')) !== false) { - return strtolower(substr($this->dsn, 0, $pos)); - } else { - return 'redis'; - } - } - - /** - * - * @param string $name - * @param array $params - * @return mixed - */ - public function __call($name, $params) - { - $redisCommand = strtoupper(Inflector::camel2words($name, false)); - if (in_array($redisCommand, $this->redisCommands)) { - return $this->executeCommand($name, $params); - } else { - return parent::__call($name, $params); - } - } - - /** - * Executes a redis command. - * For a list of available commands and their parameters see http://redis.io/commands. - * - * @param string $name the name of the command - * @param array $params list of parameters for the command - * @return array|bool|null|string Dependend on the executed command this method - * will return different data types: - * - * - `true` for commands that return "status reply". - * - `string` for commands that return "integer reply" - * as the value is in the range of a signed 64 bit integer. - * - `string` or `null` for commands that return "bulk reply". - * - `array` for commands that return "Multi-bulk replies". - * - * See [redis protocol description](http://redis.io/topics/protocol) - * for details on the mentioned reply types. - * @trows Exception for commands that return [error reply](http://redis.io/topics/protocol#error-reply). - */ - public function executeCommand($name, $params=array()) - { - $this->open(); - - array_unshift($params, $name); - $command = '*' . count($params) . "\r\n"; - foreach($params as $arg) { - $command .= '$' . mb_strlen($arg, '8bit') . "\r\n" . $arg . "\r\n"; - } - - \Yii::trace("Executing Redis Command: {$name}", __CLASS__); - fwrite($this->_socket, $command); - - return $this->parseResponse(implode(' ', $params)); - } - - private function parseResponse($command) - { - if(($line = fgets($this->_socket)) === false) { - throw new Exception("Failed to read from socket.\nRedis command was: " . $command); - } - $type = $line[0]; - $line = mb_substr($line, 1, -2, '8bit'); - switch($type) - { - case '+': // Status reply - return true; - case '-': // Error reply - throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command); - case ':': // Integer reply - // no cast to int as it is in the range of a signed 64 bit integer - return $line; - case '$': // Bulk replies - if ($line == '-1') { - return null; - } - $length = $line + 2; - $data = ''; - while ($length > 0) { - if(($block = fread($this->_socket, $line + 2)) === false) { - throw new Exception("Failed to read from socket.\nRedis command was: " . $command); - } - $data .= $block; - $length -= mb_strlen($block, '8bit'); - } - return mb_substr($data, 0, -2, '8bit'); - case '*': // Multi-bulk replies - $count = (int) $line; - $data = array(); - for($i = 0; $i < $count; $i++) { - $data[] = $this->parseResponse($command); - } - return $data; - default: - throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . $command); - } - } -} diff --git a/framework/yii/db/redis/Transaction.php b/framework/yii/db/redis/Transaction.php deleted file mode 100644 index 024f821..0000000 --- a/framework/yii/db/redis/Transaction.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @since 2.0 - */ -class Transaction extends \yii\base\Object -{ - /** - * @var Connection the database connection that this transaction is associated with. - */ - public $db; - /** - * @var boolean whether this transaction is active. Only an active transaction - * can [[commit()]] or [[rollBack()]]. This property is set true when the transaction is started. - */ - private $_active = false; - - /** - * Returns a value indicating whether this transaction is active. - * @return boolean whether this transaction is active. Only an active transaction - * can [[commit()]] or [[rollBack()]]. - */ - public function getIsActive() - { - return $this->_active; - } - - /** - * Begins a transaction. - * @throws InvalidConfigException if [[connection]] is null - */ - public function begin() - { - if (!$this->_active) { - if ($this->db === null) { - throw new InvalidConfigException('Transaction::db must be set.'); - } - \Yii::trace('Starting transaction', __CLASS__); - $this->db->open(); - $this->db->createCommand('MULTI')->execute(); - $this->_active = true; - } - } - - /** - * Commits a transaction. - * @throws Exception if the transaction or the DB connection is not active. - */ - public function commit() - { - if ($this->_active && $this->db && $this->db->isActive) { - \Yii::trace('Committing transaction', __CLASS__); - $this->db->createCommand('EXEC')->execute(); - // TODO handle result of EXEC - $this->_active = false; - } else { - throw new Exception('Failed to commit transaction: transaction was inactive.'); - } - } - - /** - * Rolls back a transaction. - * @throws Exception if the transaction or the DB connection is not active. - */ - public function rollback() - { - if ($this->_active && $this->db && $this->db->isActive) { - \Yii::trace('Rolling back transaction', __CLASS__); - $this->db->pdo->commit(); - $this->_active = false; - } else { - throw new Exception('Failed to roll back transaction: transaction was inactive.'); - } - } -} diff --git a/framework/yii/redis/Connection.php b/framework/yii/redis/Connection.php new file mode 100644 index 0000000..848b408 --- /dev/null +++ b/framework/yii/redis/Connection.php @@ -0,0 +1,428 @@ + + * @since 2.0 + */ +class Connection extends Component +{ + /** + * @event Event an event that is triggered after a DB connection is established + */ + const EVENT_AFTER_OPEN = 'afterOpen'; + + /** + * @var string the Data Source Name, or DSN, contains the information required to connect to the database. + * DSN format: redis://server:port[/db] + * Where db is a zero based integer which refers to the DB to use. + * If no DB is given, ID 0 is used. + * + * Example: redis://localhost:6379/2 + */ + public $dsn; + /** + * @var string the password for establishing DB connection. Defaults to null meaning no AUTH command is send. + * See http://redis.io/commands/auth + */ + public $password; + /** + * @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout") + */ + public $connectionTimeout = null; + /** + * @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used. + */ + public $dataTimeout = null; + + /** + * @var array List of available redis commands http://redis.io/commands + */ + public $redisCommands = array( + 'BRPOP', // key [key ...] timeout Remove and get the last element in a list, or block until one is available + 'BRPOPLPUSH', // source destination timeout Pop a value from a list, push it to another list and return it; or block until one is available + 'CLIENT KILL', // ip:port Kill the connection of a client + 'CLIENT LIST', // Get the list of client connections + 'CLIENT GETNAME', // Get the current connection name + 'CLIENT SETNAME', // connection-name Set the current connection name + 'CONFIG GET', // parameter Get the value of a configuration parameter + 'CONFIG SET', // parameter value Set a configuration parameter to the given value + 'CONFIG RESETSTAT', // Reset the stats returned by INFO + 'DBSIZE', // Return the number of keys in the selected database + 'DEBUG OBJECT', // key Get debugging information about a key + 'DEBUG SEGFAULT', // Make the server crash + 'DECR', // key Decrement the integer value of a key by one + 'DECRBY', // key decrement Decrement the integer value of a key by the given number + 'DEL', // key [key ...] Delete a key + 'DISCARD', // Discard all commands issued after MULTI + 'DUMP', // key Return a serialized version of the value stored at the specified key. + 'ECHO', // message Echo the given string + 'EVAL', // script numkeys key [key ...] arg [arg ...] Execute a Lua script server side + 'EVALSHA', // sha1 numkeys key [key ...] arg [arg ...] Execute a Lua script server side + 'EXEC', // Execute all commands issued after MULTI + 'EXISTS', // key Determine if a key exists + 'EXPIRE', // key seconds Set a key's time to live in seconds + 'EXPIREAT', // key timestamp Set the expiration for a key as a UNIX timestamp + 'FLUSHALL', // Remove all keys from all databases + 'FLUSHDB', // Remove all keys from the current database + 'GET', // key Get the value of a key + 'GETBIT', // key offset Returns the bit value at offset in the string value stored at key + 'GETRANGE', // key start end Get a substring of the string stored at a key + 'GETSET', // key value Set the string value of a key and return its old value + 'HDEL', // key field [field ...] Delete one or more hash fields + 'HEXISTS', // key field Determine if a hash field exists + 'HGET', // key field Get the value of a hash field + 'HGETALL', // key Get all the fields and values in a hash + 'HINCRBY', // key field increment Increment the integer value of a hash field by the given number + 'HINCRBYFLOAT', // key field increment Increment the float value of a hash field by the given amount + 'HKEYS', // key Get all the fields in a hash + 'HLEN', // key Get the number of fields in a hash + 'HMGET', // key field [field ...] Get the values of all the given hash fields + 'HMSET', // key field value [field value ...] Set multiple hash fields to multiple values + 'HSET', // key field value Set the string value of a hash field + 'HSETNX', // key field value Set the value of a hash field, only if the field does not exist + 'HVALS', // key Get all the values in a hash + 'INCR', // key Increment the integer value of a key by one + 'INCRBY', // key increment Increment the integer value of a key by the given amount + 'INCRBYFLOAT', // key increment Increment the float value of a key by the given amount + 'INFO', // [section] Get information and statistics about the server + 'KEYS', // pattern Find all keys matching the given pattern + 'LASTSAVE', // Get the UNIX time stamp of the last successful save to disk + 'LINDEX', // key index Get an element from a list by its index + 'LINSERT', // key BEFORE|AFTER pivot value Insert an element before or after another element in a list + 'LLEN', // key Get the length of a list + 'LPOP', // key Remove and get the first element in a list + 'LPUSH', // key value [value ...] Prepend one or multiple values to a list + 'LPUSHX', // key value Prepend a value to a list, only if the list exists + 'LRANGE', // key start stop Get a range of elements from a list + 'LREM', // key count value Remove elements from a list + 'LSET', // key index value Set the value of an element in a list by its index + 'LTRIM', // key start stop Trim a list to the specified range + 'MGET', // key [key ...] Get the values of all the given keys + 'MIGRATE', // host port key destination-db timeout Atomically transfer a key from a Redis instance to another one. + 'MONITOR', // Listen for all requests received by the server in real time + 'MOVE', // key db Move a key to another database + 'MSET', // key value [key value ...] Set multiple keys to multiple values + 'MSETNX', // key value [key value ...] Set multiple keys to multiple values, only if none of the keys exist + 'MULTI', // Mark the start of a transaction block + 'OBJECT', // subcommand [arguments [arguments ...]] Inspect the internals of Redis objects + 'PERSIST', // key Remove the expiration from a key + 'PEXPIRE', // key milliseconds Set a key's time to live in milliseconds + 'PEXPIREAT', // key milliseconds-timestamp Set the expiration for a key as a UNIX timestamp specified in milliseconds + 'PING', // Ping the server + 'PSETEX', // key milliseconds value Set the value and expiration in milliseconds of a key + 'PSUBSCRIBE', // pattern [pattern ...] Listen for messages published to channels matching the given patterns + 'PTTL', // key Get the time to live for a key in milliseconds + 'PUBLISH', // channel message Post a message to a channel + 'PUNSUBSCRIBE', // [pattern [pattern ...]] Stop listening for messages posted to channels matching the given patterns + 'QUIT', // Close the connection + 'RANDOMKEY', // Return a random key from the keyspace + 'RENAME', // key newkey Rename a key + 'RENAMENX', // key newkey Rename a key, only if the new key does not exist + 'RESTORE', // key ttl serialized-value Create a key using the provided serialized value, previously obtained using DUMP. + 'RPOP', // key Remove and get the last element in a list + 'RPOPLPUSH', // source destination Remove the last element in a list, append it to another list and return it + 'RPUSH', // key value [value ...] Append one or multiple values to a list + 'RPUSHX', // key value Append a value to a list, only if the list exists + 'SADD', // key member [member ...] Add one or more members to a set + 'SAVE', // Synchronously save the dataset to disk + 'SCARD', // key Get the number of members in a set + 'SCRIPT EXISTS', // script [script ...] Check existence of scripts in the script cache. + 'SCRIPT FLUSH', // Remove all the scripts from the script cache. + 'SCRIPT KILL', // Kill the script currently in execution. + 'SCRIPT LOAD', // script Load the specified Lua script into the script cache. + 'SDIFF', // key [key ...] Subtract multiple sets + 'SDIFFSTORE', // destination key [key ...] Subtract multiple sets and store the resulting set in a key + 'SELECT', // index Change the selected database for the current connection + 'SET', // key value Set the string value of a key + 'SETBIT', // key offset value Sets or clears the bit at offset in the string value stored at key + 'SETEX', // key seconds value Set the value and expiration of a key + 'SETNX', // key value Set the value of a key, only if the key does not exist + 'SETRANGE', // key offset value Overwrite part of a string at key starting at the specified offset + 'SHUTDOWN', // [NOSAVE] [SAVE] Synchronously save the dataset to disk and then shut down the server + 'SINTER', // key [key ...] Intersect multiple sets + 'SINTERSTORE', // destination key [key ...] Intersect multiple sets and store the resulting set in a key + 'SISMEMBER', // key member Determine if a given value is a member of a set + 'SLAVEOF', // host port Make the server a slave of another instance, or promote it as master + 'SLOWLOG', // subcommand [argument] Manages the Redis slow queries log + 'SMEMBERS', // key Get all the members in a set + 'SMOVE', // source destination member Move a member from one set to another + 'SORT', // key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination] Sort the elements in a list, set or sorted set + 'SPOP', // key Remove and return a random member from a set + 'SRANDMEMBER', // key [count] Get one or multiple random members from a set + 'SREM', // key member [member ...] Remove one or more members from a set + 'STRLEN', // key Get the length of the value stored in a key + 'SUBSCRIBE', // channel [channel ...] Listen for messages published to the given channels + 'SUNION', // key [key ...] Add multiple sets + 'SUNIONSTORE', // destination key [key ...] Add multiple sets and store the resulting set in a key + 'SYNC', // Internal command used for replication + 'TIME', // Return the current server time + 'TTL', // key Get the time to live for a key + 'TYPE', // key Determine the type stored at key + 'UNSUBSCRIBE', // [channel [channel ...]] Stop listening for messages posted to the given channels + 'UNWATCH', // Forget about all watched keys + 'WATCH', // key [key ...] Watch the given keys to determine execution of the MULTI/EXEC block + 'ZADD', // key score member [score member ...] Add one or more members to a sorted set, or update its score if it already exists + 'ZCARD', // key Get the number of members in a sorted set + 'ZCOUNT', // key min max Count the members in a sorted set with scores within the given values + 'ZINCRBY', // key increment member Increment the score of a member in a sorted set + 'ZINTERSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Intersect multiple sorted sets and store the resulting sorted set in a new key + 'ZRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index + 'ZRANGEBYSCORE', // key min max [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score + 'ZRANK', // key member Determine the index of a member in a sorted set + 'ZREM', // key member [member ...] Remove one or more members from a sorted set + 'ZREMRANGEBYRANK', // key start stop Remove all members in a sorted set within the given indexes + 'ZREMRANGEBYSCORE', // key min max Remove all members in a sorted set within the given scores + 'ZREVRANGE', // key start stop [WITHSCORES] Return a range of members in a sorted set, by index, with scores ordered from high to low + 'ZREVRANGEBYSCORE', // key max min [WITHSCORES] [LIMIT offset count] Return a range of members in a sorted set, by score, with scores ordered from high to low + 'ZREVRANK', // key member Determine the index of a member in a sorted set, with scores ordered from high to low + 'ZSCORE', // key member Get the score associated with the given member in a sorted set + 'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key + ); + /** + * @var Transaction the currently active transaction + */ + private $_transaction; + /** + * @var resource redis socket connection + */ + private $_socket; + + /** + * Closes the connection when this component is being serialized. + * @return array + */ + public function __sleep() + { + $this->close(); + return array_keys(get_object_vars($this)); + } + + /** + * Returns a value indicating whether the DB connection is established. + * @return boolean whether the DB connection is established + */ + public function getIsActive() + { + return $this->_socket !== null; + } + + /** + * Establishes a DB connection. + * It does nothing if a DB connection has already been established. + * @throws Exception if connection fails + */ + public function open() + { + if ($this->_socket === null) { + if (empty($this->dsn)) { + throw new InvalidConfigException('Connection.dsn cannot be empty.'); + } + $dsn = explode('/', $this->dsn); + $host = $dsn[2]; + if (strpos($host, ':')===false) { + $host .= ':6379'; + } + $db = isset($dsn[3]) ? $dsn[3] : 0; + + \Yii::trace('Opening DB connection: ' . $this->dsn, __CLASS__); + $this->_socket = @stream_socket_client( + $host, + $errorNumber, + $errorDescription, + $this->connectionTimeout ? $this->connectionTimeout : ini_get("default_socket_timeout") + ); + if ($this->_socket) { + if ($this->dataTimeout !== null) { + stream_set_timeout($this->_socket, $timeout=(int)$this->dataTimeout, (int) (($this->dataTimeout - $timeout) * 1000000)); + } + if ($this->password !== null) { + $this->executeCommand('AUTH', array($this->password)); + } + $this->executeCommand('SELECT', array($db)); + $this->initConnection(); + } else { + \Yii::error("Failed to open DB connection ({$this->dsn}): " . $errorNumber . ' - ' . $errorDescription, __CLASS__); + $message = YII_DEBUG ? 'Failed to open DB connection: ' . $errorNumber . ' - ' . $errorDescription : 'Failed to open DB connection.'; + throw new Exception($message, $errorDescription, (int)$errorNumber); + } + } + } + + /** + * Closes the currently active DB connection. + * It does nothing if the connection is already closed. + */ + public function close() + { + if ($this->_socket !== null) { + \Yii::trace('Closing DB connection: ' . $this->dsn, __CLASS__); + $this->executeCommand('QUIT'); + stream_socket_shutdown($this->_socket, STREAM_SHUT_RDWR); + $this->_socket = null; + $this->_transaction = null; + } + } + + /** + * Initializes the DB connection. + * This method is invoked right after the DB connection is established. + * The default implementation triggers an [[EVENT_AFTER_OPEN]] event. + */ + protected function initConnection() + { + $this->trigger(self::EVENT_AFTER_OPEN); + } + + /** + * Returns the currently active transaction. + * @return Transaction the currently active transaction. Null if no active transaction. + */ + public function getTransaction() + { + return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null; + } + + /** + * Starts a transaction. + * @return Transaction the transaction initiated + */ + public function beginTransaction() + { + $this->open(); + $this->_transaction = new Transaction(array( + 'db' => $this, + )); + $this->_transaction->begin(); + return $this->_transaction; + } + + /** + * Returns the name of the DB driver for the current [[dsn]]. + * @return string name of the DB driver + */ + public function getDriverName() + { + if (($pos = strpos($this->dsn, ':')) !== false) { + return strtolower(substr($this->dsn, 0, $pos)); + } else { + return 'redis'; + } + } + + /** + * + * @param string $name + * @param array $params + * @return mixed + */ + public function __call($name, $params) + { + $redisCommand = strtoupper(Inflector::camel2words($name, false)); + if (in_array($redisCommand, $this->redisCommands)) { + return $this->executeCommand($name, $params); + } else { + return parent::__call($name, $params); + } + } + + /** + * Executes a redis command. + * For a list of available commands and their parameters see http://redis.io/commands. + * + * @param string $name the name of the command + * @param array $params list of parameters for the command + * @return array|bool|null|string Dependend on the executed command this method + * will return different data types: + * + * - `true` for commands that return "status reply". + * - `string` for commands that return "integer reply" + * as the value is in the range of a signed 64 bit integer. + * - `string` or `null` for commands that return "bulk reply". + * - `array` for commands that return "Multi-bulk replies". + * + * See [redis protocol description](http://redis.io/topics/protocol) + * for details on the mentioned reply types. + * @trows Exception for commands that return [error reply](http://redis.io/topics/protocol#error-reply). + */ + public function executeCommand($name, $params=array()) + { + $this->open(); + + array_unshift($params, $name); + $command = '*' . count($params) . "\r\n"; + foreach($params as $arg) { + $command .= '$' . mb_strlen($arg, '8bit') . "\r\n" . $arg . "\r\n"; + } + + \Yii::trace("Executing Redis Command: {$name}", __CLASS__); + fwrite($this->_socket, $command); + + return $this->parseResponse(implode(' ', $params)); + } + + private function parseResponse($command) + { + if(($line = fgets($this->_socket)) === false) { + throw new Exception("Failed to read from socket.\nRedis command was: " . $command); + } + $type = $line[0]; + $line = mb_substr($line, 1, -2, '8bit'); + switch($type) + { + case '+': // Status reply + return true; + case '-': // Error reply + throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command); + case ':': // Integer reply + // no cast to int as it is in the range of a signed 64 bit integer + return $line; + case '$': // Bulk replies + if ($line == '-1') { + return null; + } + $length = $line + 2; + $data = ''; + while ($length > 0) { + if(($block = fread($this->_socket, $line + 2)) === false) { + throw new Exception("Failed to read from socket.\nRedis command was: " . $command); + } + $data .= $block; + $length -= mb_strlen($block, '8bit'); + } + return mb_substr($data, 0, -2, '8bit'); + case '*': // Multi-bulk replies + $count = (int) $line; + $data = array(); + for($i = 0; $i < $count; $i++) { + $data[] = $this->parseResponse($command); + } + return $data; + default: + throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . $command); + } + } +} diff --git a/framework/yii/redis/Transaction.php b/framework/yii/redis/Transaction.php new file mode 100644 index 0000000..94cff7a --- /dev/null +++ b/framework/yii/redis/Transaction.php @@ -0,0 +1,93 @@ + + * @since 2.0 + */ +class Transaction extends \yii\base\Object +{ + /** + * @var Connection the database connection that this transaction is associated with. + */ + public $db; + /** + * @var boolean whether this transaction is active. Only an active transaction + * can [[commit()]] or [[rollBack()]]. This property is set true when the transaction is started. + */ + private $_active = false; + + /** + * Returns a value indicating whether this transaction is active. + * @return boolean whether this transaction is active. Only an active transaction + * can [[commit()]] or [[rollBack()]]. + */ + public function getIsActive() + { + return $this->_active; + } + + /** + * Begins a transaction. + * @throws InvalidConfigException if [[connection]] is null + */ + public function begin() + { + if (!$this->_active) { + if ($this->db === null) { + throw new InvalidConfigException('Transaction::db must be set.'); + } + \Yii::trace('Starting transaction', __CLASS__); + $this->db->open(); + $this->db->createCommand('MULTI')->execute(); + $this->_active = true; + } + } + + /** + * Commits a transaction. + * @throws Exception if the transaction or the DB connection is not active. + */ + public function commit() + { + if ($this->_active && $this->db && $this->db->isActive) { + \Yii::trace('Committing transaction', __CLASS__); + $this->db->createCommand('EXEC')->execute(); + // TODO handle result of EXEC + $this->_active = false; + } else { + throw new Exception('Failed to commit transaction: transaction was inactive.'); + } + } + + /** + * Rolls back a transaction. + * @throws Exception if the transaction or the DB connection is not active. + */ + public function rollback() + { + if ($this->_active && $this->db && $this->db->isActive) { + \Yii::trace('Rolling back transaction', __CLASS__); + $this->db->pdo->commit(); + $this->_active = false; + } else { + throw new Exception('Failed to roll back transaction: transaction was inactive.'); + } + } +} From cc09ef56b9f9a4b6409e98a15dfb2784df07f1df Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 18 Sep 2013 16:01:29 +0200 Subject: [PATCH 154/156] updated @property annotations of web\Request --- framework/yii/web/Request.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index eb1652c..9e625f7 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -35,6 +35,9 @@ use yii\helpers\Security; * @property boolean $isDelete Whether this is a DELETE request. This property is read-only. * @property boolean $isFlash Whether this is an Adobe Flash or Adobe Flex request. This property is * read-only. + * @property boolean $isGet Whether this is a GET request. This property is read-only. + * @property boolean $isHead Whether this is a HEAD request. This property is read-only. + * @property boolean $isOptions Whether this is a OPTIONS request. This property is read-only. * @property boolean $isPatch Whether this is a PATCH request. This property is read-only. * @property boolean $isPost Whether this is a POST request. This property is read-only. * @property boolean $isPut Whether this is a PUT request. This property is read-only. From ef13a11f66094e53301910094ea8292cd9879226 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 18 Sep 2013 16:29:16 +0200 Subject: [PATCH 155/156] moved getPDOType() back to Command to avoid dependency on Schema fixes #854 --- framework/yii/db/Command.php | 25 +++++++++++++++++++--- framework/yii/db/Schema.php | 19 ---------------- framework/yii/db/cubrid/Schema.php | 19 ---------------- tests/unit/data/cubrid.sql | 4 +--- tests/unit/framework/db/CommandTest.php | 23 ++++++++++++++++++++ tests/unit/framework/db/SchemaTest.php | 23 -------------------- .../framework/db/cubrid/CubridActiveRecordTest.php | 23 ++++++++++++++++++++ .../unit/framework/db/cubrid/CubridCommandTest.php | 8 +------ .../unit/framework/db/cubrid/CubridSchemaTest.php | 22 ------------------- 9 files changed, 70 insertions(+), 96 deletions(-) diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 7f2d81d..bfb8a26 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -181,7 +181,7 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) { - $this->pdoStatement->bindParam($name, $value, $this->db->schema->getPdoType($value)); + $this->pdoStatement->bindParam($name, $value, $this->getPdoType($value)); } elseif ($length === null) { $this->pdoStatement->bindParam($name, $value, $dataType); } elseif ($driverOptions === null) { @@ -208,7 +208,7 @@ class Command extends \yii\base\Component { $this->prepare(); if ($dataType === null) { - $this->pdoStatement->bindValue($name, $value, $this->db->schema->getPdoType($value)); + $this->pdoStatement->bindValue($name, $value, $this->getPdoType($value)); } else { $this->pdoStatement->bindValue($name, $value, $dataType); } @@ -236,7 +236,7 @@ class Command extends \yii\base\Component $type = $value[1]; $value = $value[0]; } else { - $type = $this->db->schema->getPdoType($value); + $type = $this->getPdoType($value); } $this->pdoStatement->bindValue($name, $value, $type); $this->_params[$name] = $value; @@ -246,6 +246,25 @@ class Command extends \yii\base\Component } /** + * Determines the PDO type for the given PHP data value. + * @param mixed $data the data whose PDO type is to be determined + * @return integer the PDO type + * @see http://www.php.net/manual/en/pdo.constants.php + */ + private function getPdoType($data) + { + static $typeMap = array( // php type => PDO type + 'boolean' => \PDO::PARAM_BOOL, + 'integer' => \PDO::PARAM_INT, + 'string' => \PDO::PARAM_STR, + 'resource' => \PDO::PARAM_LOB, + 'NULL' => \PDO::PARAM_NULL, + ); + $type = gettype($data); + return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; + } + + /** * Executes the SQL statement. * This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs. * No result set will be returned. diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 396e944..1d86616 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -377,23 +377,4 @@ abstract class Schema extends Object return 'string'; } } - - /** - * Determines the PDO type for the give PHP data value. - * @param mixed $data the data whose PDO type is to be determined - * @return integer the PDO type - * @see http://www.php.net/manual/en/pdo.constants.php - */ - public function getPdoType($data) - { - static $typeMap = array( // php type => PDO type - 'boolean' => \PDO::PARAM_BOOL, - 'integer' => \PDO::PARAM_INT, - 'string' => \PDO::PARAM_STR, - 'resource' => \PDO::PARAM_LOB, - 'NULL' => \PDO::PARAM_NULL, - ); - $type = gettype($data); - return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; - } } diff --git a/framework/yii/db/cubrid/Schema.php b/framework/yii/db/cubrid/Schema.php index 99624f6..ba7fcae 100644 --- a/framework/yii/db/cubrid/Schema.php +++ b/framework/yii/db/cubrid/Schema.php @@ -237,23 +237,4 @@ class Schema extends \yii\db\Schema } return $tableNames; } - - /** - * Determines the PDO type for the give PHP data value. - * @param mixed $data the data whose PDO type is to be determined - * @return integer the PDO type - * @see http://www.php.net/manual/en/pdo.constants.php - */ - public function getPdoType($data) - { - static $typeMap = array( - 'boolean' => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL - 'integer' => \PDO::PARAM_INT, - 'string' => \PDO::PARAM_STR, - 'resource' => \PDO::PARAM_LOB, - 'NULL' => \PDO::PARAM_NULL, - ); - $type = gettype($data); - return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR; - } } diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql index bfaf85c..3dcfa37 100644 --- a/tests/unit/data/cubrid.sql +++ b/tests/unit/data/cubrid.sql @@ -72,9 +72,7 @@ CREATE TABLE `tbl_type` ( `float_col2` double DEFAULT '1.23', `blob_col` blob, `numeric_col` decimal(5,2) DEFAULT '33.22', - `time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00', - `bool_col` smallint NOT NULL, - `bool_col2` smallint DEFAULT 1 + `time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00' ); CREATE TABLE `tbl_composite_fk` ( diff --git a/tests/unit/framework/db/CommandTest.php b/tests/unit/framework/db/CommandTest.php index 7b16c76..d9eb0e7 100644 --- a/tests/unit/framework/db/CommandTest.php +++ b/tests/unit/framework/db/CommandTest.php @@ -219,6 +219,29 @@ class CommandTest extends DatabaseTestCase $this->assertTrue(is_array($result) && isset($result[0])); } + // getPDOType is currently private +// public function testGetPDOType() +// { +// $values = array( +// array(null, \PDO::PARAM_NULL), +// array('', \PDO::PARAM_STR), +// array('hello', \PDO::PARAM_STR), +// array(0, \PDO::PARAM_INT), +// array(1, \PDO::PARAM_INT), +// array(1337, \PDO::PARAM_INT), +// array(true, \PDO::PARAM_BOOL), +// array(false, \PDO::PARAM_BOOL), +// array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB), +// ); +// +// $command = $this->getConnection()->createCommand(); +// +// foreach($values as $value) { +// $this->assertEquals($value[1], $command->getPdoType($value[0])); +// } +// fclose($fp); +// } + public function testInsert() { } diff --git a/tests/unit/framework/db/SchemaTest.php b/tests/unit/framework/db/SchemaTest.php index dce6e20..2a3015d 100644 --- a/tests/unit/framework/db/SchemaTest.php +++ b/tests/unit/framework/db/SchemaTest.php @@ -11,29 +11,6 @@ use yii\db\Schema; */ class SchemaTest extends DatabaseTestCase { - - public function testGetPDOType() - { - $values = array( - array(null, \PDO::PARAM_NULL), - array('', \PDO::PARAM_STR), - array('hello', \PDO::PARAM_STR), - array(0, \PDO::PARAM_INT), - array(1, \PDO::PARAM_INT), - array(1337, \PDO::PARAM_INT), - array(true, \PDO::PARAM_BOOL), - array(false, \PDO::PARAM_BOOL), - array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB), - ); - - $schema = $this->getConnection()->schema; - - foreach($values as $value) { - $this->assertEquals($value[1], $schema->getPdoType($value[0])); - } - fclose($fp); - } - public function testFindTableNames() { /** @var Schema $schema */ diff --git a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php index dd48f44..2d2db15 100644 --- a/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php +++ b/tests/unit/framework/db/cubrid/CubridActiveRecordTest.php @@ -1,6 +1,7 @@ name = 'boolean customer'; + $customer->email = 'mail@example.com'; + $customer->status = true; + $customer->save(false); + + $customer->refresh(); + $this->assertEquals(1, $customer->status); + + $customer->status = false; + $customer->save(false); + + $customer->refresh(); + $this->assertEquals(0, $customer->status); + } } diff --git a/tests/unit/framework/db/cubrid/CubridCommandTest.php b/tests/unit/framework/db/cubrid/CubridCommandTest.php index 895f548..45d3c1c 100644 --- a/tests/unit/framework/db/cubrid/CubridCommandTest.php +++ b/tests/unit/framework/db/cubrid/CubridCommandTest.php @@ -31,7 +31,7 @@ class CubridCommandTest extends CommandTest $command->bindParam(':email', $email); $this->assertEquals($name, $command->queryScalar()); - $sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col, bool_col, bool_col2) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col, :bool_col, :bool_col2)"; + $sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col)"; $command = $db->createCommand($sql); $intCol = 123; $charCol = 'abc'; @@ -39,16 +39,12 @@ class CubridCommandTest extends CommandTest $floatCol = 1.23; $blobCol = "\x10\x11\x12"; $numericCol = '1.23'; - $boolCol = false; - $boolCol2 = true; $command->bindParam(':int_col', $intCol); $command->bindParam(':char_col', $charCol); $command->bindParam(':enum_col', $enumCol); $command->bindParam(':float_col', $floatCol); $command->bindParam(':blob_col', $blobCol); $command->bindParam(':numeric_col', $numericCol); - $command->bindParam(':bool_col', $boolCol); - $command->bindParam(':bool_col2', $boolCol2); $this->assertEquals(1, $command->execute()); $sql = 'SELECT * FROM tbl_type'; @@ -59,8 +55,6 @@ class CubridCommandTest extends CommandTest $this->assertEquals($floatCol, $row['float_col']); $this->assertEquals($blobCol, fread($row['blob_col'], 3)); $this->assertEquals($numericCol, $row['numeric_col']); - $this->assertEquals($boolCol, $row['bool_col']); - $this->assertEquals($boolCol2, $row['bool_col2']); // bindValue $sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, \'user5\', \'address5\')'; diff --git a/tests/unit/framework/db/cubrid/CubridSchemaTest.php b/tests/unit/framework/db/cubrid/CubridSchemaTest.php index 6a1f6a2..9a0c139 100644 --- a/tests/unit/framework/db/cubrid/CubridSchemaTest.php +++ b/tests/unit/framework/db/cubrid/CubridSchemaTest.php @@ -10,26 +10,4 @@ use yiiunit\framework\db\SchemaTest; class CubridSchemaTest extends SchemaTest { public $driverName = 'cubrid'; - - public function testGetPDOType() - { - $values = array( - null => \PDO::PARAM_NULL, - '' => \PDO::PARAM_STR, - 'hello' => \PDO::PARAM_STR, - 0 => \PDO::PARAM_INT, - 1 => \PDO::PARAM_INT, - 1337 => \PDO::PARAM_INT, - true => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL - false => \PDO::PARAM_INT, // CUBRID PDO does not support PARAM_BOOL - ); - - $schema = $this->getConnection()->schema; - - foreach($values as $value => $type) { - $this->assertEquals($type, $schema->getPdoType($value)); - } - $this->assertEquals(\PDO::PARAM_LOB, $schema->getPdoType($fp=fopen(__FILE__, 'rb'))); - fclose($fp); - } } From 3a347c3587d510e1e434ff169211bb72812ff239 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 18 Sep 2013 16:30:00 +0200 Subject: [PATCH 156/156] added property docs to AR test classes --- tests/unit/data/ar/Customer.php | 9 +++++++++ tests/unit/data/ar/Item.php | 7 +++++++ tests/unit/data/ar/Order.php | 8 ++++++++ tests/unit/data/ar/OrderItem.php | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php index 561d1ae..bbc0182 100644 --- a/tests/unit/data/ar/Customer.php +++ b/tests/unit/data/ar/Customer.php @@ -3,6 +3,15 @@ namespace yiiunit\data\ar; use yii\db\ActiveQuery; +/** + * Class Customer + * + * @property integer $id + * @property string $name + * @property string $email + * @property string $address + * @property integer $status + */ class Customer extends ActiveRecord { const STATUS_ACTIVE = 1; diff --git a/tests/unit/data/ar/Item.php b/tests/unit/data/ar/Item.php index 5d23378..e725be9 100644 --- a/tests/unit/data/ar/Item.php +++ b/tests/unit/data/ar/Item.php @@ -2,6 +2,13 @@ namespace yiiunit\data\ar; +/** + * Class Item + * + * @property integer $id + * @property string $name + * @property integer $category_id + */ class Item extends ActiveRecord { public static function tableName() diff --git a/tests/unit/data/ar/Order.php b/tests/unit/data/ar/Order.php index 119f332..063bb67 100644 --- a/tests/unit/data/ar/Order.php +++ b/tests/unit/data/ar/Order.php @@ -2,6 +2,14 @@ namespace yiiunit\data\ar; +/** + * Class Order + * + * @property integer $id + * @property integer $customer_id + * @property integer $create_time + * @property string $total + */ class Order extends ActiveRecord { public static function tableName() diff --git a/tests/unit/data/ar/OrderItem.php b/tests/unit/data/ar/OrderItem.php index 607133e..297432b 100644 --- a/tests/unit/data/ar/OrderItem.php +++ b/tests/unit/data/ar/OrderItem.php @@ -2,6 +2,14 @@ namespace yiiunit\data\ar; +/** + * Class OrderItem + * + * @property integer $order_id + * @property integer $item_id + * @property integer $quantity + * @property string $subtotal + */ class OrderItem extends ActiveRecord { public static function tableName()