From 4653526262030cf75799212440861b902e00d761 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 29 Oct 2013 12:20:52 +0100 Subject: [PATCH 01/79] moved common parts of db ActiveRelation and Query to traits these parts can be reused in other ActiveRecord implementations --- framework/yii/ar/ActiveQuery.php | 234 ++++++++++++++++++++++++++++ framework/yii/ar/ActiveRelation.php | 253 ++++++++++++++++++++++++++++++ framework/yii/db/ActiveQuery.php | 188 +---------------------- framework/yii/db/ActiveRelation.php | 221 +------------------------- framework/yii/db/BaseQuery.php | 299 ++++++++++++++++++++++++++++++++++++ framework/yii/db/Query.php | 128 +-------------- 6 files changed, 792 insertions(+), 531 deletions(-) create mode 100644 framework/yii/ar/ActiveQuery.php create mode 100644 framework/yii/ar/ActiveRelation.php create mode 100644 framework/yii/db/BaseQuery.php diff --git a/framework/yii/ar/ActiveQuery.php b/framework/yii/ar/ActiveQuery.php new file mode 100644 index 0000000..729b7b4 --- /dev/null +++ b/framework/yii/ar/ActiveQuery.php @@ -0,0 +1,234 @@ +with('orders')->asArray()->all(); + * ~~~ + * + * @author Qiang Xue + * @author Carsten Brandt + * @since 2.0 + */ +trait ActiveQuery +{ + /** + * @var string the name of the ActiveRecord class. + */ + public $modelClass; + /** + * @var array list of relations that this query should be performed with + */ + public $with; + /** + * @var boolean whether to return each record as an array. If false (default), an object + * of [[modelClass]] will be created to represent each record. + */ + public $asArray; + + + /** + * PHP magic method. + * This method allows calling static method defined in [[modelClass]] via this query object. + * It is mainly implemented for supporting the feature of scope. + * @param string $name the method name to be called + * @param array $params the parameters passed to the method + * @return mixed the method return result + */ + public function __call($name, $params) + { + if (method_exists($this->modelClass, $name)) { + array_unshift($params, $this); + call_user_func_array([$this->modelClass, $name], $params); + return $this; + } else { + return parent::__call($name, $params); + } + } + + /** + * Sets the [[asArray]] property. + * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. + * @return static the query object itself + */ + public function asArray($value = true) + { + $this->asArray = $value; + return $this; + } + + /** + * Specifies the relations with which this query should be performed. + * + * The parameters to this method can be either one or multiple strings, or a single array + * of relation names and the optional callbacks to customize the relations. + * + * The followings are some usage examples: + * + * ~~~ + * // find customers together with their orders and country + * Customer::find()->with('orders', 'country')->all(); + * // find customers together with their country and orders of status 1 + * Customer::find()->with([ + * 'orders' => function($query) { + * $query->andWhere('status = 1'); + * }, + * 'country', + * ])->all(); + * ~~~ + * + * @return static the query object itself + */ + public function with() + { + $this->with = func_get_args(); + if (isset($this->with[0]) && is_array($this->with[0])) { + // the parameter is given as an array + $this->with = $this->with[0]; + } + return $this; + } + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row or model data. The signature of the callable should be: + * + * ~~~ + * // $model is an AR instance when `asArray` is false, + * // or an array of column values when `asArray` is true. + * function ($model) + * { + * // return the index value corresponding to $model + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column) + { + return parent::indexBy($column); + } + + private function createModels($rows) + { + $models = []; + if ($this->asArray) { + if ($this->indexBy === null) { + return $rows; + } + foreach ($rows as $row) { + if (is_string($this->indexBy)) { + $key = $row[$this->indexBy]; + } else { + $key = call_user_func($this->indexBy, $row); + } + $models[$key] = $row; + } + } else { + /** @var $class ActiveRecord */ + $class = $this->modelClass; + if ($this->indexBy === null) { + foreach ($rows as $row) { + $models[] = $class::create($row); + } + } else { + foreach ($rows as $row) { + $model = $class::create($row); + if (is_string($this->indexBy)) { + $key = $model->{$this->indexBy}; + } else { + $key = call_user_func($this->indexBy, $model); + } + $models[$key] = $model; + } + } + } + return $models; + } + + private function populateRelations(&$models, $with) + { + $primaryModel = new $this->modelClass; + $relations = $this->normalizeRelations($primaryModel, $with); + foreach ($relations as $name => $relation) { + if ($relation->asArray === null) { + // inherit asArray from primary query + $relation->asArray = $this->asArray; + } + $relation->findWith($name, $models); + } + } + + /** + * @param ActiveRecord $model + * @param array $with + * @return ActiveRelation[] + */ + private function normalizeRelations($model, $with) + { + $relations = []; + foreach ($with as $name => $callback) { + if (is_integer($name)) { + $name = $callback; + $callback = null; + } + if (($pos = strpos($name, '.')) !== false) { + // with sub-relations + $childName = substr($name, $pos + 1); + $name = substr($name, 0, $pos); + } else { + $childName = null; + } + + $t = strtolower($name); + if (!isset($relations[$t])) { + $relation = $model->getRelation($name); + $relation->primaryModel = null; + $relations[$t] = $relation; + } else { + $relation = $relations[$t]; + } + + if (isset($childName)) { + $relation->with[$childName] = $callback; + } elseif ($callback !== null) { + call_user_func($callback, $relation); + } + } + return $relations; + } +} diff --git a/framework/yii/ar/ActiveRelation.php b/framework/yii/ar/ActiveRelation.php new file mode 100644 index 0000000..17bba67 --- /dev/null +++ b/framework/yii/ar/ActiveRelation.php @@ -0,0 +1,253 @@ + + * @since 2.0 + */ +trait ActiveRelation +{ + /** + * @var boolean whether this relation should populate all query results into AR instances. + * If false, only the first row of the results will be retrieved. + */ + public $multiple; + /** + * @var ActiveRecord the primary model that this relation is associated with. + * This is used only in lazy loading with dynamic query options. + */ + public $primaryModel; + /** + * @var array the columns of the primary and foreign tables that establish the relation. + * The array keys must be columns of the table for this relation, and the array values + * must be the corresponding columns from the primary table. + * Do not prefix or quote the column names as they will be done automatically by Yii. + */ + public $link; + /** + * @var array the query associated with the pivot table. Please call [[via()]] + * to set this property instead of directly setting it. + */ + 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. + * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. + * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. + * Its signature should be `function($query)`, where `$query` is the query to be customized. + * @return static the relation object itself. + */ + public function via($relationName, $callable = null) + { + $relation = $this->primaryModel->getRelation($relationName); + $this->via = [$relationName, $relation]; + if ($callable !== null) { + call_user_func($callable, $relation); + } + return $this; + } + + /** + * Finds the related records and populates them into the primary models. + * This method is internally used by [[ActiveQuery]]. Do not call it directly. + * @param string $name the relation name + * @param array $primaryModels primary models + * @return array the related models + * @throws InvalidConfigException + */ + public function findWith($name, &$primaryModels) + { + if (!is_array($this->link)) { + throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); + } + + if ($this->via instanceof self) { + // via pivot table + /** @var $viaQuery ActiveRelation */ + $viaQuery = $this->via; + $viaModels = $viaQuery->findPivotRows($primaryModels); + $this->filterByModels($viaModels); + } elseif (is_array($this->via)) { + // via relation + /** @var $viaQuery ActiveRelation */ + list($viaName, $viaQuery) = $this->via; + $viaQuery->primaryModel = null; + $viaModels = $viaQuery->findWith($viaName, $primaryModels); + $this->filterByModels($viaModels); + } else { + $this->filterByModels($primaryModels); + } + + if (count($primaryModels) === 1 && !$this->multiple) { + $model = $this->one(); + foreach ($primaryModels as $i => $primaryModel) { + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $model); + } else { + $primaryModels[$i][$name] = $model; + } + } + return [$model]; + } else { + $models = $this->all(); + if (isset($viaModels, $viaQuery)) { + $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); + } else { + $buckets = $this->buildBuckets($models, $this->link); + } + + $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); + foreach ($primaryModels as $i => $primaryModel) { + $key = $this->getModelKey($primaryModel, $link); + $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $value); + } else { + $primaryModels[$i][$name] = $value; + } + } + return $models; + } + } + + /** + * @param array $models + * @param array $link + * @param array $viaModels + * @param array $viaLink + * @return array + */ + private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) + { + $buckets = []; + $linkKeys = array_keys($link); + foreach ($models as $i => $model) { + $key = $this->getModelKey($model, $linkKeys); + if ($this->indexBy !== null) { + $buckets[$key][$i] = $model; + } else { + $buckets[$key][] = $model; + } + } + + if ($viaModels !== null) { + $viaBuckets = []; + $viaLinkKeys = array_keys($viaLink); + $linkValues = array_values($link); + foreach ($viaModels as $viaModel) { + $key1 = $this->getModelKey($viaModel, $viaLinkKeys); + $key2 = $this->getModelKey($viaModel, $linkValues); + if (isset($buckets[$key2])) { + foreach ($buckets[$key2] as $i => $bucket) { + if ($this->indexBy !== null) { + $viaBuckets[$key1][$i] = $bucket; + } else { + $viaBuckets[$key1][] = $bucket; + } + } + } + } + $buckets = $viaBuckets; + } + + if (!$this->multiple) { + foreach ($buckets as $i => $bucket) { + $buckets[$i] = reset($bucket); + } + } + return $buckets; + } + + /** + * @param ActiveRecord|array $model + * @param array $attributes + * @return string + */ + private function getModelKey($model, $attributes) + { + if (count($attributes) > 1) { + $key = []; + foreach ($attributes as $attribute) { + $key[] = $model[$attribute]; + } + return serialize($key); + } else { + $attribute = reset($attributes); + return $model[$attribute]; + } + } + + /** + * @param array $models + */ + private function filterByModels($models) + { + $attributes = array_keys($this->link); + $values = []; + if (count($attributes) === 1) { + // single key + $attribute = reset($this->link); + foreach ($models as $model) { + if (($value = $model[$attribute]) !== null) { + $values[] = $value; + } + } + } else { + // composite keys + foreach ($models as $model) { + $v = []; + foreach ($this->link as $attribute => $link) { + $v[$attribute] = $model[$link]; + } + $values[] = $v; + } + } + $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); + } + + /** + * @param ActiveRecord[] $primaryModels + * @return array + */ + private function findPivotRows($primaryModels) + { + if (empty($primaryModels)) { + return []; + } + $this->filterByModels($primaryModels); + /** @var $primaryModel ActiveRecord */ + $primaryModel = reset($primaryModels); + return $this->asArray()->all($primaryModel->getDb()); + } +} diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index b7fcdcd..5ec3d7a 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -46,19 +46,8 @@ namespace yii\db; */ class ActiveQuery extends Query { - /** - * @var string the name of the ActiveRecord class. - */ - public $modelClass; - /** - * @var array list of relations that this query should be performed with - */ - public $with; - /** - * @var boolean whether to return each record as an array. If false (default), an object - * of [[modelClass]] will be created to represent each record. - */ - public $asArray; + use \yii\ar\ActiveQuery; + /** * @var string the SQL statement to be executed for retrieving AR records. * This is set by [[ActiveRecord::findBySql()]]. @@ -67,25 +56,6 @@ class ActiveQuery extends Query /** - * PHP magic method. - * This method allows calling static method defined in [[modelClass]] via this query object. - * It is mainly implemented for supporting the feature of scope. - * @param string $name the method name to be called - * @param array $params the parameters passed to the method - * @return mixed the method return result - */ - public function __call($name, $params) - { - if (method_exists($this->modelClass, $name)) { - array_unshift($params, $this); - call_user_func_array([$this->modelClass, $name], $params); - return $this; - } else { - return parent::__call($name, $params); - } - } - - /** * Executes query and returns all results as an array. * @param Connection $db the DB connection used to create the DB command. * If null, the DB connection returned by [[modelClass]] will be used. @@ -164,158 +134,4 @@ class ActiveQuery extends Query } return $db->createCommand($this->sql, $params); } - - /** - * Sets the [[asArray]] property. - * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. - * @return static the query object itself - */ - public function asArray($value = true) - { - $this->asArray = $value; - return $this; - } - - /** - * Specifies the relations with which this query should be performed. - * - * The parameters to this method can be either one or multiple strings, or a single array - * of relation names and the optional callbacks to customize the relations. - * - * The followings are some usage examples: - * - * ~~~ - * // find customers together with their orders and country - * Customer::find()->with('orders', 'country')->all(); - * // find customers together with their country and orders of status 1 - * Customer::find()->with([ - * 'orders' => function($query) { - * $query->andWhere('status = 1'); - * }, - * 'country', - * ])->all(); - * ~~~ - * - * @return static the query object itself - */ - public function with() - { - $this->with = func_get_args(); - if (isset($this->with[0]) && is_array($this->with[0])) { - // the parameter is given as an array - $this->with = $this->with[0]; - } - return $this; - } - - /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row or model data. The signature of the callable should be: - * - * ~~~ - * // $model is an AR instance when `asArray` is false, - * // or an array of column values when `asArray` is true. - * function ($model) - * { - * // return the index value corresponding to $model - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column) - { - return parent::indexBy($column); - } - - private function createModels($rows) - { - $models = []; - if ($this->asArray) { - if ($this->indexBy === null) { - return $rows; - } - foreach ($rows as $row) { - if (is_string($this->indexBy)) { - $key = $row[$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); - } - $models[$key] = $row; - } - } else { - /** @var $class ActiveRecord */ - $class = $this->modelClass; - if ($this->indexBy === null) { - foreach ($rows as $row) { - $models[] = $class::create($row); - } - } else { - foreach ($rows as $row) { - $model = $class::create($row); - if (is_string($this->indexBy)) { - $key = $model->{$this->indexBy}; - } else { - $key = call_user_func($this->indexBy, $model); - } - $models[$key] = $model; - } - } - } - return $models; - } - - private function populateRelations(&$models, $with) - { - $primaryModel = new $this->modelClass; - $relations = $this->normalizeRelations($primaryModel, $with); - foreach ($relations as $name => $relation) { - if ($relation->asArray === null) { - // inherit asArray from primary query - $relation->asArray = $this->asArray; - } - $relation->findWith($name, $models); - } - } - - /** - * @param ActiveRecord $model - * @param array $with - * @return ActiveRelation[] - */ - private function normalizeRelations($model, $with) - { - $relations = []; - foreach ($with as $name => $callback) { - if (is_integer($name)) { - $name = $callback; - $callback = null; - } - if (($pos = strpos($name, '.')) !== false) { - // with sub-relations - $childName = substr($name, $pos + 1); - $name = substr($name, 0, $pos); - } else { - $childName = null; - } - - $t = strtolower($name); - if (!isset($relations[$t])) { - $relation = $model->getRelation($name); - $relation->primaryModel = null; - $relations[$t] = $relation; - } else { - $relation = $relations[$t]; - } - - if (isset($childName)) { - $relation->with[$childName] = $callback; - } elseif ($callback !== null) { - call_user_func($callback, $relation); - } - } - return $relations; - } } diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 1a7541a..3714c6a 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -27,56 +27,14 @@ use yii\base\InvalidConfigException; */ class ActiveRelation extends ActiveQuery { - /** - * @var boolean whether this relation should populate all query results into AR instances. - * If false, only the first row of the results will be retrieved. - */ - public $multiple; - /** - * @var ActiveRecord the primary model that this relation is associated with. - * This is used only in lazy loading with dynamic query options. - */ - public $primaryModel; - /** - * @var array the columns of the primary and foreign tables that establish the relation. - * The array keys must be columns of the table for this relation, and the array values - * must be the corresponding columns from the primary table. - * Do not prefix or quote the column names as they will be done automatically by Yii. - */ - public $link; + use \yii\ar\ActiveRelation; + /** * @var array|ActiveRelation the query associated with the pivot table. Please call [[via()]] * or [[viaTable()]] to set this property instead of directly setting it. */ 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. - * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. - * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. - * Its signature should be `function($query)`, where `$query` is the query to be customized. - * @return static the relation object itself. - */ - public function via($relationName, $callable = null) - { - $relation = $this->primaryModel->getRelation($relationName); - $this->via = [$relationName, $relation]; - if ($callable !== null) { - call_user_func($callable, $relation); - } - return $this; - } /** * Specifies the pivot table. @@ -137,179 +95,4 @@ class ActiveRelation extends ActiveQuery } return parent::createCommand($db); } - - /** - * Finds the related records and populates them into the primary models. - * This method is internally used by [[ActiveQuery]]. Do not call it directly. - * @param string $name the relation name - * @param array $primaryModels primary models - * @return array the related models - * @throws InvalidConfigException - */ - public function findWith($name, &$primaryModels) - { - if (!is_array($this->link)) { - throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); - } - - if ($this->via instanceof self) { - // via pivot table - /** @var $viaQuery ActiveRelation */ - $viaQuery = $this->via; - $viaModels = $viaQuery->findPivotRows($primaryModels); - $this->filterByModels($viaModels); - } elseif (is_array($this->via)) { - // via relation - /** @var $viaQuery ActiveRelation */ - list($viaName, $viaQuery) = $this->via; - $viaQuery->primaryModel = null; - $viaModels = $viaQuery->findWith($viaName, $primaryModels); - $this->filterByModels($viaModels); - } else { - $this->filterByModels($primaryModels); - } - - if (count($primaryModels) === 1 && !$this->multiple) { - $model = $this->one(); - foreach ($primaryModels as $i => $primaryModel) { - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $model); - } else { - $primaryModels[$i][$name] = $model; - } - } - return [$model]; - } else { - $models = $this->all(); - if (isset($viaModels, $viaQuery)) { - $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); - } else { - $buckets = $this->buildBuckets($models, $this->link); - } - - $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); - foreach ($primaryModels as $i => $primaryModel) { - $key = $this->getModelKey($primaryModel, $link); - $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $value); - } else { - $primaryModels[$i][$name] = $value; - } - } - return $models; - } - } - - /** - * @param array $models - * @param array $link - * @param array $viaModels - * @param array $viaLink - * @return array - */ - private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) - { - $buckets = []; - $linkKeys = array_keys($link); - foreach ($models as $i => $model) { - $key = $this->getModelKey($model, $linkKeys); - if ($this->indexBy !== null) { - $buckets[$key][$i] = $model; - } else { - $buckets[$key][] = $model; - } - } - - if ($viaModels !== null) { - $viaBuckets = []; - $viaLinkKeys = array_keys($viaLink); - $linkValues = array_values($link); - foreach ($viaModels as $viaModel) { - $key1 = $this->getModelKey($viaModel, $viaLinkKeys); - $key2 = $this->getModelKey($viaModel, $linkValues); - if (isset($buckets[$key2])) { - foreach ($buckets[$key2] as $i => $bucket) { - if ($this->indexBy !== null) { - $viaBuckets[$key1][$i] = $bucket; - } else { - $viaBuckets[$key1][] = $bucket; - } - } - } - } - $buckets = $viaBuckets; - } - - if (!$this->multiple) { - foreach ($buckets as $i => $bucket) { - $buckets[$i] = reset($bucket); - } - } - return $buckets; - } - - /** - * @param ActiveRecord|array $model - * @param array $attributes - * @return string - */ - private function getModelKey($model, $attributes) - { - if (count($attributes) > 1) { - $key = []; - foreach ($attributes as $attribute) { - $key[] = $model[$attribute]; - } - return serialize($key); - } else { - $attribute = reset($attributes); - return $model[$attribute]; - } - } - - /** - * @param array $models - */ - private function filterByModels($models) - { - $attributes = array_keys($this->link); - $values = []; - if (count($attributes) === 1) { - // single key - $attribute = reset($this->link); - foreach ($models as $model) { - if (($value = $model[$attribute]) !== null) { - $values[] = $value; - } - } - } else { - // composite keys - foreach ($models as $model) { - $v = []; - foreach ($this->link as $attribute => $link) { - $v[$attribute] = $model[$link]; - } - $values[] = $v; - } - } - $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); - } - - /** - * @param ActiveRecord[] $primaryModels - * @return array - */ - private function findPivotRows($primaryModels) - { - if (empty($primaryModels)) { - return []; - } - $this->filterByModels($primaryModels); - /** @var $primaryModel ActiveRecord */ - $primaryModel = reset($primaryModels); - $db = $primaryModel->getDb(); - list ($sql, $params) = $db->getQueryBuilder()->build($this); - return $db->createCommand($sql, $params)->queryAll(); - } } diff --git a/framework/yii/db/BaseQuery.php b/framework/yii/db/BaseQuery.php new file mode 100644 index 0000000..98fcdab --- /dev/null +++ b/framework/yii/db/BaseQuery.php @@ -0,0 +1,299 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +trait BaseQuery +{ + /** + * @var string|array query condition. This refers to the WHERE clause in a SQL statement. + * For example, `age > 31 AND team = 1`. + * @see where() + */ + public $where; + /** + * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit. + */ + public $limit; + /** + * @var integer zero-based offset from where the records are to be returned. If not set or + * less than 0, it means starting from the beginning. + */ + public $offset; + /** + * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement. + * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which + * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects. + * If that is the case, the expressions will be converted into strings without any change. + */ + public $orderBy; + /** + * @var string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row data. For more details, see [[indexBy()]]. This property is only used by [[all()]]. + */ + public $indexBy; + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row data. The signature of the callable should be: + * + * ~~~ + * function ($row) + * { + * // return the index value corresponding to $row + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column) + { + $this->indexBy = $column; + return $this; + } + + /** + * Executes the query and returns all results as an array. + * @return array the query results. If the query results in nothing, an empty array will be returned. + */ + abstract public function all(); + + /** + * Executes the query and returns a single row of result. + * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query + * results in nothing. + */ + abstract public function one(); + + /** + * Returns the number of records. + * @return integer number of records + */ + abstract public function count(); + + /** + * Returns a value indicating whether the query result contains any row of data. + * @return boolean whether the query result contains any row of data. + */ + abstract public function exists(); + + /** + * Sets the WHERE part of the query. + * + * The method requires a $condition parameter. + * + * The $condition parameter should be an array in one of the following two formats: + * + * - hash format: `['column1' => value1, 'column2' => value2, ...]` + * - operator format: `[operator, operand1, operand2, ...]` + * + * A condition in hash format represents the following SQL expression in general: + * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array, + * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used + * in the generated expression. Below are some examples: + * + * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`. + * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`. + * - `['status' => null] generates `status IS NULL`. + * + * A condition in operator format generates the SQL expression according to the specified operator, which + * can be one of the followings: + * + * - `and`: the operands should be concatenated together using `AND`. For example, + * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array, + * it will be converted into a string using the rules described here. For example, + * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`. + * The method will NOT do any quoting or escaping. + * + * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`. + * + * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the + * starting and ending values of the range that the column is in. + * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`. + * + * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` + * in the generated condition. + * + * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing + * the range of the values that the column or DB expression should be in. For example, + * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`. + * The method will properly quote the column name and escape values in the range. + * + * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition. + * + * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing + * the values that the column or DB expression should be like. + * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`. + * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated + * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate + * `name LIKE '%test%' AND name LIKE '%sample%'`. + * The method will properly quote the column name and escape values in the range. + * + * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE` + * predicates when operand 2 is an array. + * + * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE` + * in the generated condition. + * + * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate + * the `NOT LIKE` predicates. + * + * @param array $condition the conditions that should be put in the WHERE part. + * @return static the query object itself + * @see andWhere() + * @see orWhere() + */ + public function where($condition) + { + $this->where = $condition; + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'AND' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see orWhere() + */ + public function andWhere($condition) + { + if ($this->where === null) { + $this->where = $condition; + } else { + $this->where = ['and', $this->where, $condition]; + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'OR' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see andWhere() + */ + public function orWhere($condition) + { + if ($this->where === null) { + $this->where = $condition; + } else { + $this->where = ['or', $this->where, $condition]; + } + return $this; + } + + /** + * Sets the ORDER BY part of the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see addOrderBy() + */ + public function orderBy($columns) + { + $this->orderBy = $this->normalizeOrderBy($columns); + return $this; + } + + /** + * Adds additional ORDER BY columns to the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see orderBy() + */ + public function addOrderBy($columns) + { + $columns = $this->normalizeOrderBy($columns); + if ($this->orderBy === null) { + $this->orderBy = $columns; + } else { + $this->orderBy = array_merge($this->orderBy, $columns); + } + return $this; + } + + protected function normalizeOrderBy($columns) + { + if (is_array($columns)) { + return $columns; + } else { + $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); + $result = []; + foreach ($columns as $column) { + if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { + $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC; + } else { + $result[$column] = self::SORT_ASC; + } + } + return $result; + } + } + + /** + * Sets the LIMIT part of the query. + * @param integer $limit the limit. Use null or negative value to disable limit. + * @return static the query object itself + */ + public function limit($limit) + { + $this->limit = $limit; + return $this; + } + + /** + * Sets the OFFSET part of the query. + * @param integer $offset the offset. Use null or negative value to disable offset. + * @return static the query object itself + */ + public function offset($offset) + { + $this->offset = $offset; + return $this; + } +} \ No newline at end of file diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 0839849..73f8860 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -37,6 +37,8 @@ use yii\base\Component; */ class Query extends Component { + use BaseQuery; + /** * Sort ascending * @see orderBy @@ -71,28 +73,6 @@ class Query extends Component */ public $from; /** - * @var string|array query condition. This refers to the WHERE clause in a SQL statement. - * For example, `age > 31 AND team = 1`. - * @see where() - */ - public $where; - /** - * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit. - */ - public $limit; - /** - * @var integer zero-based offset from where the records are to be returned. If not set or - * less than 0, it means starting from the beginning. - */ - public $offset; - /** - * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement. - * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which - * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects. - * If that is the case, the expressions will be converted into strings without any change. - */ - public $orderBy; - /** * @var array how to group the query results. For example, `['company', 'department']`. * This is used to construct the GROUP BY clause in a SQL statement. */ @@ -130,12 +110,6 @@ class Query extends Component * For example, `[':name' => 'Dan', ':age' => 31]`. */ public $params; - /** - * @var string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row data. For more details, see [[indexBy()]]. This property is only used by [[all()]]. - */ - public $indexBy; /** @@ -154,27 +128,6 @@ class Query extends Component } /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row data. The signature of the callable should be: - * - * ~~~ - * function ($row) - * { - * // return the index value corresponding to $row - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column) - { - $this->indexBy = $column; - return $this; - } - - /** * Executes the query and returns all results as an array. * @param Connection $db the database connection used to generate the SQL statement. * If this parameter is not given, the `db` application component will be used. @@ -653,83 +606,6 @@ class Query extends Component } /** - * Sets the ORDER BY part of the query. - * @param string|array $columns the columns (and the directions) to be ordered by. - * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). - * The method will automatically quote the column names unless a column contains some parenthesis - * (which means the column contains a DB expression). - * @return static the query object itself - * @see addOrderBy() - */ - public function orderBy($columns) - { - $this->orderBy = $this->normalizeOrderBy($columns); - return $this; - } - - /** - * Adds additional ORDER BY columns to the query. - * @param string|array $columns the columns (and the directions) to be ordered by. - * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). - * The method will automatically quote the column names unless a column contains some parenthesis - * (which means the column contains a DB expression). - * @return static the query object itself - * @see orderBy() - */ - public function addOrderBy($columns) - { - $columns = $this->normalizeOrderBy($columns); - if ($this->orderBy === null) { - $this->orderBy = $columns; - } else { - $this->orderBy = array_merge($this->orderBy, $columns); - } - return $this; - } - - protected function normalizeOrderBy($columns) - { - if (is_array($columns)) { - return $columns; - } else { - $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); - $result = []; - foreach ($columns as $column) { - if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { - $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC; - } else { - $result[$column] = self::SORT_ASC; - } - } - return $result; - } - } - - /** - * Sets the LIMIT part of the query. - * @param integer $limit the limit. Use null or negative value to disable limit. - * @return static the query object itself - */ - public function limit($limit) - { - $this->limit = $limit; - return $this; - } - - /** - * Sets the OFFSET part of the query. - * @param integer $offset the offset. Use null or negative value to disable offset. - * @return static the query object itself - */ - public function offset($offset) - { - $this->offset = $offset; - return $this; - } - - /** * Appends a SQL statement using UNION operator. * @param string|Query $sql the SQL statement to be appended using UNION * @return static the query object itself From 0146596647b359d5f9afafbd9cd58e1b955bc0ac Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 29 Oct 2013 12:41:24 +0100 Subject: [PATCH 02/79] add relationClassName to AR to allow different relation classes ... for different dbms --- framework/yii/db/ActiveRecord.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 79d5146..be44a07 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -93,6 +93,10 @@ class ActiveRecord extends Model const OP_ALL = 0x07; /** + * @var string the name of the class to use for relations. + */ + protected $relationClassName = 'yii\\db\\ActiveRelation'; + /** * @var array attribute values indexed by attribute names */ private $_attributes = []; @@ -384,7 +388,7 @@ class ActiveRecord extends Model return $this->_related[$t]; } $value = parent::__get($name); - if ($value instanceof ActiveRelation) { + if ($value instanceof $this->relationClassName) { return $this->_related[$t] = $value->multiple ? $value->all() : $value->one(); } else { return $value; @@ -474,7 +478,7 @@ class ActiveRecord extends Model */ public function hasOne($class, $link) { - return new ActiveRelation([ + return new $this->relationClassName([ 'modelClass' => $class, 'primaryModel' => $this, 'link' => $link, @@ -512,7 +516,7 @@ class ActiveRecord extends Model */ public function hasMany($class, $link) { - return new ActiveRelation([ + return new $this->relationClassName([ 'modelClass' => $class, 'primaryModel' => $this, 'link' => $link, @@ -1264,7 +1268,7 @@ class ActiveRecord extends Model $getter = 'get' . $name; try { $relation = $this->$getter(); - if ($relation instanceof ActiveRelation) { + if ($relation instanceof $this->relationClassName) { return $relation; } } catch (UnknownMethodException $e) { From 3130aadb33652cae6326cd653f05b5a74da169dd Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 29 Oct 2013 12:52:52 +0100 Subject: [PATCH 03/79] fixed conflicting property declaration --- framework/yii/db/ActiveRelation.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 3714c6a..8ca1baf 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -8,8 +8,6 @@ namespace yii\db; -use yii\base\InvalidConfigException; - /** * ActiveRelation represents a relation between two Active Record classes. * @@ -22,6 +20,9 @@ use yii\base\InvalidConfigException; * * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method. * + * @property array|ActiveRelation $via the query associated with the pivot table. Please call [[via()]] + * or [[viaTable()]] to set this property instead of directly setting it. + * * @author Qiang Xue * @since 2.0 */ @@ -30,13 +31,6 @@ class ActiveRelation extends ActiveQuery use \yii\ar\ActiveRelation; /** - * @var array|ActiveRelation the query associated with the pivot table. Please call [[via()]] - * or [[viaTable()]] to set this property instead of directly setting it. - */ - public $via; - - - /** * Specifies the pivot table. * @param string $tableName the name of the pivot table. * @param array $link the link between the pivot table and the table associated with [[primaryModel]]. From a38c280a2948c0af8c2dc73f6b268c1fe05646a6 Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Fri, 8 Nov 2013 13:18:19 +0200 Subject: [PATCH 04/79] Unit test for 'yii\swiftmailer\Message' fixed. --- tests/unit/extensions/swiftmailer/MessageTest.php | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tests/unit/extensions/swiftmailer/MessageTest.php b/tests/unit/extensions/swiftmailer/MessageTest.php index fdecd88..c46b43d 100644 --- a/tests/unit/extensions/swiftmailer/MessageTest.php +++ b/tests/unit/extensions/swiftmailer/MessageTest.php @@ -119,6 +119,8 @@ class MessageTest extends VendorTestCase { $charset = 'utf-16'; $subject = 'Test Subject'; + $from = 'from@somedomain.com'; + $replyTo = 'reply-to@somedomain.com'; $to = 'someuser@somedomain.com'; $cc = 'ccuser@somedomain.com'; $bcc = 'bccuser@somedomain.com'; @@ -126,6 +128,8 @@ class MessageTest extends VendorTestCase $messageString = $this->createTestMessage() ->setCharset($charset) ->setSubject($subject) + ->setFrom($from) + ->setReplyTo($replyTo) ->setTo($to) ->setCc($cc) ->setBcc($bcc) @@ -133,6 +137,8 @@ class MessageTest extends VendorTestCase $this->assertContains('charset=' . $charset, $messageString, 'Incorrect charset!'); $this->assertContains('Subject: ' . $subject, $messageString, 'Incorrect "Subject" header!'); + $this->assertContains('From: ' . $from, $messageString, 'Incorrect "From" header!'); + $this->assertContains('Reply-To: ' . $replyTo, $messageString, 'Incorrect "Reply-To" header!'); $this->assertContains('To: ' . $to, $messageString, 'Incorrect "To" header!'); $this->assertContains('Cc: ' . $cc, $messageString, 'Incorrect "Cc" header!'); $this->assertContains('Bcc: ' . $bcc, $messageString, 'Incorrect "Bcc" header!'); @@ -141,19 +147,6 @@ class MessageTest extends VendorTestCase /** * @depends testGetSwiftMessage */ - public function testSetupFrom() - { - $from = 'someuser@somedomain.com'; - $messageString = $this->createTestMessage() - ->setFrom($from) - ->toString(); - $this->assertContains('From: ' . $from, $messageString, 'Incorrect "From" header!'); - $this->assertContains('Reply-To: ' . $from, $messageString, 'Incorrect "Reply-To" header!'); - } - - /** - * @depends testGetSwiftMessage - */ public function testSend() { $message = $this->createTestMessage(); From 5a584c0bda4dd483e0a5d029735995c83be57879 Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Fri, 8 Nov 2013 13:23:14 +0200 Subject: [PATCH 05/79] Creating transport at 'yii\swiftmailer\Mailer' fixed to track possible "__call". --- extensions/swiftmailer/Mailer.php | 2 +- tests/unit/extensions/swiftmailer/MailerTest.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php index 58f0f23..c00fad6 100644 --- a/extensions/swiftmailer/Mailer.php +++ b/extensions/swiftmailer/Mailer.php @@ -145,7 +145,7 @@ class Mailer extends BaseMailer $transport->$name = $value; } else { $setter = 'set' . $name; - if (method_exists($transport, $setter)) { + if (method_exists($transport, $setter) || method_exists($transport, '__call')) { $transport->$setter($value); } else { throw new InvalidConfigException('Setting unknown property: ' . get_class($transport) . '::' . $name); diff --git a/tests/unit/extensions/swiftmailer/MailerTest.php b/tests/unit/extensions/swiftmailer/MailerTest.php index 306e128..24602b2 100644 --- a/tests/unit/extensions/swiftmailer/MailerTest.php +++ b/tests/unit/extensions/swiftmailer/MailerTest.php @@ -53,6 +53,8 @@ class MailerTest extends VendorTestCase $transportConfig = [ 'class' => 'Swift_SmtpTransport', 'host' => 'localhost', + 'username' => 'username', + 'password' => 'password', ]; $mailer->setTransport($transportConfig); $transport = $mailer->getTransport(); From 913eb62294f084b3d085127d31b25505b84c2c67 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 8 Nov 2013 12:24:03 +0100 Subject: [PATCH 06/79] updated phpdoc and fixed generator read/write-only annotation --- build/controllers/PhpDocController.php | 32 ++++++++++++++++++++++++++++---- framework/yii/base/Application.php | 2 ++ framework/yii/db/ActiveRecord.php | 2 ++ framework/yii/mail/BaseMailer.php | 3 ++- framework/yii/mail/BaseMessage.php | 2 +- framework/yii/web/Controller.php | 2 ++ framework/yii/web/Request.php | 20 +++++++++++++++++++- 7 files changed, 56 insertions(+), 7 deletions(-) diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 760cb05..c7a2fa7 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -278,11 +278,35 @@ class PhpDocController extends Controller . ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.'; } } elseif (isset($prop['get'])) { - $note = ' This property is read-only.'; -// $docline .= '-read'; + // check if parent class has setter defined + $c = $className; + $parentSetter = false; + while($parent = get_parent_class($c)) { + if (method_exists($parent, 'set' . ucfirst($propName))) { + $parentSetter = true; + break; + } + $c = $parent; + } + if (!$parentSetter) { + $note = ' This property is read-only.'; +// $docline .= '-read'; + } } elseif (isset($prop['set'])) { - $note = ' This property is write-only.'; -// $docline .= '-write'; + // check if parent class has getter defined + $c = $className; + $parentGetter = false; + while($parent = get_parent_class($c)) { + if (method_exists($parent, 'set' . ucfirst($propName))) { + $parentGetter = true; + break; + } + $c = $parent; + } + if (!$parentGetter) { + $note = ' This property is write-only.'; +// $docline .= '-write'; + } } else { continue; } diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 8826d5a..70eaca3 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -15,6 +15,7 @@ use yii\web\HttpException; * Application is the base class for all application classes. * * @property \yii\rbac\Manager $authManager The auth manager for this application. This property is read-only. + * @property string $basePath The root directory of the application. * @property \yii\caching\Cache $cache The cache application component. Null if the component is not enabled. * This property is read-only. * @property \yii\db\Connection $db The database connection. This property is read-only. @@ -261,6 +262,7 @@ abstract class Application extends Module * Sets the root directory of the applicaition and the @app alias. * This method can only be invoked at the beginning of the constructor. * @param string $path the root directory of the application. + * @property string the root directory of the application. * @throws InvalidParamException if the directory does not exist. */ public function setBasePath($path) diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 830bf40..380eb03 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -29,6 +29,8 @@ use yii\helpers\Inflector; * @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 array $populatedRelations An array of relation data indexed by relation names. 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. diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index 2b6918a..52f31ce 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -20,7 +20,8 @@ use yii\web\View; * * @see BaseMessage * - * @property View|array $view view instance or its array configuration. + * @property View $view View instance. Note that the type of this property differs in getter and setter. See + * [[getView()]] and [[setView()]] for details. * * @author Paul Klimov * @since 2.0 diff --git a/framework/yii/mail/BaseMessage.php b/framework/yii/mail/BaseMessage.php index 04b7200..01b671c 100644 --- a/framework/yii/mail/BaseMessage.php +++ b/framework/yii/mail/BaseMessage.php @@ -18,7 +18,7 @@ use Yii; * * @see BaseMailer * - * @property BaseMailer $mailer mailer component instance. This property is read-only. + * @property MailerInterface $mailer The mailer component. This property is read-only. * * @author Paul Klimov * @since 2.0 diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index 6927893..01794ed 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -14,6 +14,8 @@ use yii\helpers\Html; /** * Controller is the base class of web controllers. * + * @property string $canonicalUrl 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 a6a92fa..6bede5e 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -31,6 +31,8 @@ use yii\helpers\Security; * @property string $csrfToken The random token for CSRF validation. This property is read-only. * @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned * if no such header is sent. This property is read-only. + * @property array $delete The DELETE request parameter values. This property is read-only. + * @property array $get The GET request parameter values. 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. @@ -47,11 +49,14 @@ use yii\helpers\Security; * 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 array $patch The PATCH request parameter values. 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 array $post The POST request parameter values. This property is read-only. * @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 array $put The PUT request parameter values. 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. @@ -304,12 +309,22 @@ class Request extends \yii\base\Request } /** + * Returns the GET request parameter values. + * @return array the GET request parameter values + */ + public function getGet() + { + return $_GET; + } + + /** * Returns the named POST parameter value. * If the POST parameter does not exist, the second parameter to this method will be returned. * @param string $name the POST parameter name. If not specified, whole $_POST is returned. * @param mixed $defaultValue the default parameter value if the POST parameter does not exist. + * @property array the POST request parameter values * @return mixed the POST parameter value - * @see getParam + * @see get */ public function getPost($name = null, $defaultValue = null) { @@ -323,6 +338,7 @@ class Request extends \yii\base\Request * Returns the named DELETE parameter value. * @param string $name the DELETE parameter name. If not specified, an array of DELETE parameters is returned. * @param mixed $defaultValue the default parameter value if the DELETE parameter does not exist. + * @property array the DELETE request parameter values * @return mixed the DELETE parameter value */ public function getDelete($name = null, $defaultValue = null) @@ -337,6 +353,7 @@ class Request extends \yii\base\Request * Returns the named PUT parameter value. * @param string $name the PUT parameter name. If not specified, an array of PUT parameters is returned. * @param mixed $defaultValue the default parameter value if the PUT parameter does not exist. + * @property array the PUT request parameter values * @return mixed the PUT parameter value */ public function getPut($name = null, $defaultValue = null) @@ -351,6 +368,7 @@ class Request extends \yii\base\Request * Returns the named PATCH parameter value. * @param string $name the PATCH parameter name. If not specified, an array of PATCH parameters is returned. * @param mixed $defaultValue the default parameter value if the PATCH parameter does not exist. + * @property array the PATCH request parameter values * @return mixed the PATCH parameter value */ public function getPatch($name = null, $defaultValue = null) From 5ddb0e2cbfc7f440c7b4e80cf04bb73a9071203f Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Fri, 8 Nov 2013 12:34:13 +0100 Subject: [PATCH 07/79] controller php-doc --- framework/yii/web/Controller.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index 01794ed..3319f3b 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -14,7 +14,8 @@ use yii\helpers\Html; /** * Controller is the base class of web controllers. * - * @property string $canonicalUrl This property is read-only. + * @property string $canonicalUrl The canonical URL of the currently requested page. This property is + * read-only. * * @author Qiang Xue * @since 2.0 @@ -130,7 +131,7 @@ class Controller extends \yii\base\Controller * $this->registerLinkTag(['rel' => 'canonical', 'href' => Yii::$app->controller->canonicalUrl]); * ~~~ * - * @return string + * @return string the canonical URL of the currently requested page */ public function getCanonicalUrl() { From b9fb97d68d561f03fcd1133d3147f856958097c9 Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Fri, 8 Nov 2013 16:34:35 +0200 Subject: [PATCH 08/79] Getters for 'yii\swiftmailer\Message' fixed. --- extensions/swiftmailer/Mailer.php | 2 +- extensions/swiftmailer/Message.php | 14 ++++----- tests/unit/extensions/swiftmailer/MessageTest.php | 36 +++++++++++++++++++++++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php index c00fad6..7c37a3f 100644 --- a/extensions/swiftmailer/Mailer.php +++ b/extensions/swiftmailer/Mailer.php @@ -108,7 +108,7 @@ class Mailer extends BaseMailer { $address = $message->getTo(); if (is_array($address)) { - $address = implode(', ', $address); + $address = implode(', ', array_keys($address)); } Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); return $this->getSwiftMailer()->send($message->getSwiftMessage()) > 0; diff --git a/extensions/swiftmailer/Message.php b/extensions/swiftmailer/Message.php index 067b50e..bc9c14a 100644 --- a/extensions/swiftmailer/Message.php +++ b/extensions/swiftmailer/Message.php @@ -44,7 +44,7 @@ class Message extends BaseMessage */ public function getCharset() { - $this->getSwiftMessage()->getCharset(); + return $this->getSwiftMessage()->getCharset(); } /** @@ -61,7 +61,7 @@ class Message extends BaseMessage */ public function getFrom() { - $this->getSwiftMessage()->getFrom(); + return $this->getSwiftMessage()->getFrom(); } /** @@ -78,7 +78,7 @@ class Message extends BaseMessage */ public function getReplyTo() { - $this->getSwiftMessage()->getReplyTo(); + return $this->getSwiftMessage()->getReplyTo(); } /** @@ -95,7 +95,7 @@ class Message extends BaseMessage */ public function getTo() { - $this->getSwiftMessage()->getTo(); + return $this->getSwiftMessage()->getTo(); } /** @@ -112,7 +112,7 @@ class Message extends BaseMessage */ public function getCc() { - $this->getSwiftMessage()->getCc(); + return $this->getSwiftMessage()->getCc(); } /** @@ -129,7 +129,7 @@ class Message extends BaseMessage */ public function getBcc() { - $this->getSwiftMessage()->getBcc(); + return $this->getSwiftMessage()->getBcc(); } /** @@ -146,7 +146,7 @@ class Message extends BaseMessage */ public function getSubject() { - $this->getSwiftMessage()->getSubject(); + return $this->getSwiftMessage()->getSubject(); } /** diff --git a/tests/unit/extensions/swiftmailer/MessageTest.php b/tests/unit/extensions/swiftmailer/MessageTest.php index c46b43d..bbdb476 100644 --- a/tests/unit/extensions/swiftmailer/MessageTest.php +++ b/tests/unit/extensions/swiftmailer/MessageTest.php @@ -115,6 +115,42 @@ class MessageTest extends VendorTestCase /** * @depends testGetSwiftMessage */ + public function testSetGet() + { + $message = new Message(); + + $charset = 'utf-16'; + $message->setCharset($charset); + $this->assertEquals($charset, $message->getCharset(), 'Unable to set charset!'); + + $subject = 'Test Subject'; + $message->setSubject($subject); + $this->assertEquals($subject, $message->getSubject(), 'Unable to set subject!'); + + $from = 'from@somedomain.com'; + $message->setFrom($from); + $this->assertContains($from, array_keys($message->getFrom()), 'Unable to set from!'); + + $replyTo = 'reply-to@somedomain.com'; + $message->setReplyTo($replyTo); + $this->assertContains($replyTo, array_keys($message->getReplyTo()), 'Unable to set replyTo!'); + + $to = 'someuser@somedomain.com'; + $message->setTo($to); + $this->assertContains($to, array_keys($message->getTo()), 'Unable to set to!'); + + $cc = 'ccuser@somedomain.com'; + $message->setCc($cc); + $this->assertContains($cc, array_keys($message->getCc()), 'Unable to set cc!'); + + $bcc = 'bccuser@somedomain.com'; + $message->setBcc($bcc); + $this->assertContains($bcc, array_keys($message->getBcc()), 'Unable to set bcc!'); + } + + /** + * @depends testGetSwiftMessage + */ public function testSetupHeaders() { $charset = 'utf-16'; From 5cbd5722c0b8bf9d0222a6cd4d16f21d082a66b7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 16:44:24 -0500 Subject: [PATCH 09/79] Fixes #1162: removed constructors of cache dependency classes so that they can be easily created from configuration. --- framework/yii/caching/ChainedDependency.php | 14 +------------- framework/yii/caching/DbDependency.php | 18 ++++-------------- framework/yii/caching/ExpressionDependency.php | 15 +-------------- framework/yii/caching/FileDependency.php | 16 +++++----------- framework/yii/caching/GroupDependency.php | 22 ++++++++++------------ 5 files changed, 21 insertions(+), 64 deletions(-) diff --git a/framework/yii/caching/ChainedDependency.php b/framework/yii/caching/ChainedDependency.php index 20cb632..f8bfee3 100644 --- a/framework/yii/caching/ChainedDependency.php +++ b/framework/yii/caching/ChainedDependency.php @@ -23,7 +23,7 @@ class ChainedDependency extends Dependency * @var Dependency[] list of dependencies that this dependency is composed of. * Each array element must be a dependency object. */ - public $dependencies; + public $dependencies = []; /** * @var boolean whether this dependency is depending on every dependency in [[dependencies]]. * Defaults to true, meaning if any of the dependencies has changed, this dependency is considered changed. @@ -33,18 +33,6 @@ class ChainedDependency extends Dependency public $dependOnAll = true; /** - * Constructor. - * @param Dependency[] $dependencies list of dependencies that this dependency is composed of. - * Each array element should be a dependency object. - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($dependencies = [], $config = []) - { - $this->dependencies = $dependencies; - parent::__construct($config); - } - - /** * Evaluates the dependency by generating and saving the data related with dependency. * @param Cache $cache the cache component that is currently evaluating this dependency */ diff --git a/framework/yii/caching/DbDependency.php b/framework/yii/caching/DbDependency.php index e8e1e68..ac6f2e7 100644 --- a/framework/yii/caching/DbDependency.php +++ b/framework/yii/caching/DbDependency.php @@ -34,20 +34,7 @@ class DbDependency extends Dependency /** * @var array the parameters (name => value) to be bound to the SQL statement specified by [[sql]]. */ - public $params; - - /** - * Constructor. - * @param string $sql the SQL query whose result is used to determine if the dependency has been changed. - * @param array $params the parameters (name => value) to be bound to the SQL statement specified by [[sql]]. - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($sql, $params = [], $config = []) - { - $this->sql = $sql; - $this->params = $params; - parent::__construct($config); - } + public $params = []; /** * Generates the data needed to determine if dependency has been changed. @@ -62,6 +49,9 @@ class DbDependency extends Dependency if (!$db instanceof Connection) { throw new InvalidConfigException("DbDependency::db must be the application component ID of a DB connection."); } + if ($this->sql === null) { + throw new InvalidConfigException("DbDependency::sql must be set."); + } if ($db->enableQueryCache) { // temporarily disable and re-enable query caching diff --git a/framework/yii/caching/ExpressionDependency.php b/framework/yii/caching/ExpressionDependency.php index 36250d2..f6642b4 100644 --- a/framework/yii/caching/ExpressionDependency.php +++ b/framework/yii/caching/ExpressionDependency.php @@ -27,7 +27,7 @@ class ExpressionDependency extends Dependency * A PHP expression can be any PHP code that evaluates to a value. To learn more about what an expression is, * please refer to the [php manual](http://www.php.net/manual/en/language.expressions.php). */ - public $expression; + public $expression = 'true'; /** * @var mixed custom parameters associated with this dependency. You may get the value * of this property in [[expression]] using `$this->params`. @@ -35,19 +35,6 @@ class ExpressionDependency extends Dependency public $params; /** - * Constructor. - * @param string $expression the PHP expression whose result is used to determine the dependency. - * @param mixed $params the custom parameters associated with this dependency - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($expression = 'true', $params = null, $config = []) - { - $this->expression = $expression; - $this->params = $params; - parent::__construct($config); - } - - /** * Generates the data needed to determine if dependency has been changed. * This method returns the result of the PHP expression. * @param Cache $cache the cache component that is currently evaluating this dependency diff --git a/framework/yii/caching/FileDependency.php b/framework/yii/caching/FileDependency.php index fa8b147..11afde3 100644 --- a/framework/yii/caching/FileDependency.php +++ b/framework/yii/caching/FileDependency.php @@ -6,6 +6,7 @@ */ namespace yii\caching; +use yii\base\InvalidConfigException; /** * FileDependency represents a dependency based on a file's last modification time. @@ -25,24 +26,17 @@ class FileDependency extends Dependency public $fileName; /** - * Constructor. - * @param string $fileName name of the file whose change is to be checked. - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($fileName = null, $config = []) - { - $this->fileName = $fileName; - parent::__construct($config); - } - - /** * Generates the data needed to determine if dependency has been changed. * This method returns the file's last modification time. * @param Cache $cache the cache component that is currently evaluating this dependency * @return mixed the data needed to determine if dependency has been changed. + * @throws InvalidConfigException if [[fileName]] is not set */ protected function generateDependencyData($cache) { + if ($this->fileName === null) { + throw new InvalidConfigException('FileDependency::fileName must be set'); + } return @filemtime($this->fileName); } } diff --git a/framework/yii/caching/GroupDependency.php b/framework/yii/caching/GroupDependency.php index 48673f6..1cf7869 100644 --- a/framework/yii/caching/GroupDependency.php +++ b/framework/yii/caching/GroupDependency.php @@ -6,6 +6,7 @@ */ namespace yii\caching; +use yii\base\InvalidConfigException; /** * GroupDependency marks a cached data item with a group name. @@ -19,29 +20,22 @@ namespace yii\caching; class GroupDependency extends Dependency { /** - * @var string the group name + * @var string the group name. This property must be set. */ public $group; /** - * Constructor. - * @param string $group the group name - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($group, $config = []) - { - $this->group = $group; - parent::__construct($config); - } - - /** * Generates the data needed to determine if dependency has been changed. * This method does nothing in this class. * @param Cache $cache the cache component that is currently evaluating this dependency * @return mixed the data needed to determine if dependency has been changed. + * @throws InvalidConfigException if [[group]] is not set. */ protected function generateDependencyData($cache) { + if ($this->group === null) { + throw new InvalidConfigException('GroupDependency::group must be set'); + } $version = $cache->get([__CLASS__, $this->group]); if ($version === false) { $version = $this->invalidate($cache, $this->group); @@ -53,9 +47,13 @@ class GroupDependency extends Dependency * Performs the actual dependency checking. * @param Cache $cache the cache component that is currently evaluating this dependency * @return boolean whether the dependency is changed or not. + * @throws InvalidConfigException if [[group]] is not set. */ public function getHasChanged($cache) { + if ($this->group === null) { + throw new InvalidConfigException('GroupDependency::group must be set'); + } $version = $cache->get([__CLASS__, $this->group]); return $version === false || $version !== $this->data; } From 49081f05668ab183369b4e806e8be22a56585863 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 16:58:50 -0500 Subject: [PATCH 10/79] Fixes test break. --- framework/yii/db/Schema.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 2b3a187..fbcec2c 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -99,7 +99,9 @@ abstract class Schema extends Object if ($refresh || ($table = $cache->get($key)) === false) { $table = $this->loadTableSchema($realName); if ($table !== null) { - $cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency($this->getCacheGroup())); + $cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency([ + 'group' => $this->getCacheGroup(), + ])); } } return $this->_tables[$name] = $table; From 4b49a31f574c336ffe516b2d55cf1aec59376bf9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 21:14:03 -0500 Subject: [PATCH 11/79] minor fix to gii --- extensions/gii/generators/crud/templates/controller.php | 1 - extensions/gii/generators/model/Generator.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/gii/generators/crud/templates/controller.php b/extensions/gii/generators/crud/templates/controller.php index 918f840..f975fd1 100644 --- a/extensions/gii/generators/crud/templates/controller.php +++ b/extensions/gii/generators/crud/templates/controller.php @@ -28,7 +28,6 @@ namespace controllerClass, '\\')) ?> use modelClass, '\\') ?>; use searchModelClass, '\\') ?> as ; -use yii\data\ActiveDataProvider; use baseControllerClass, '\\') ?>; use yii\web\HttpException; use yii\web\VerbFilter; diff --git a/extensions/gii/generators/model/Generator.php b/extensions/gii/generators/model/Generator.php index 4f628d7..78c5130 100644 --- a/extensions/gii/generators/model/Generator.php +++ b/extensions/gii/generators/model/Generator.php @@ -104,7 +104,7 @@ class Generator extends \yii\gii\Generator 'baseClass' => 'This is the base class of the new ActiveRecord class. It should be a fully qualified namespaced class name.', 'generateRelations' => 'This indicates whether the generator should generate relations based on foreign key constraints it detects in the database. Note that if your database contains too many tables, - you may want to uncheck this option to accelerate the code generation proc ess.', + you may want to uncheck this option to accelerate the code generation process.', 'generateLabelsFromComments' => 'This indicates whether the generator should generate attribute labels by using the comments of the corresponding DB columns.', ]; From f09c78aad993b9eee5c96a6a3c75e1c1de0eaee9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 21:35:51 -0500 Subject: [PATCH 12/79] save security keys as a serialized string instead of exported variable. --- apps/basic/config/console.php | 2 +- apps/basic/config/web.php | 2 +- framework/yii/helpers/BaseSecurity.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/basic/config/console.php b/apps/basic/config/console.php index c70993e..6f3f9a8 100644 --- a/apps/basic/config/console.php +++ b/apps/basic/config/console.php @@ -1,7 +1,7 @@ 'bootstrap-console', + 'id' => 'basic-console', 'basePath' => dirname(__DIR__), 'preload' => ['log'], 'controllerPath' => dirname(__DIR__) . '/commands', diff --git a/apps/basic/config/web.php b/apps/basic/config/web.php index 1f6c51f..cf921b0 100644 --- a/apps/basic/config/web.php +++ b/apps/basic/config/web.php @@ -1,7 +1,7 @@ 'bootstrap', + 'id' => 'basic', 'basePath' => dirname(__DIR__), 'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'), 'components' => [ diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php index 6b7f1cf..db226ea 100644 --- a/framework/yii/helpers/BaseSecurity.php +++ b/framework/yii/helpers/BaseSecurity.php @@ -175,7 +175,7 @@ class BaseSecurity /** * Returns a secret key associated with the specified name. * If the secret key does not exist, a random key will be generated - * and saved in the file "keys.php" under the application's runtime directory + * and saved in the file "keys.data" under the application's runtime directory * so that the same secret key can be returned in future requests. * @param string $name the name that is associated with the secret key * @param integer $length the length of the key that should be generated if not exists @@ -184,16 +184,16 @@ class BaseSecurity public static function getSecretKey($name, $length = 32) { static $keys; - $keyFile = Yii::$app->getRuntimePath() . '/keys.php'; + $keyFile = Yii::$app->getRuntimePath() . '/keys.data'; if ($keys === null) { $keys = []; if (is_file($keyFile)) { - $keys = require($keyFile); + $keys = unserialize(file_get_contents($keyFile)); } } if (!isset($keys[$name])) { $keys[$name] = static::generateRandomKey($length); - file_put_contents($keyFile, " Date: Fri, 8 Nov 2013 21:40:54 -0500 Subject: [PATCH 13/79] replaced exported value with serialized one. --- extensions/gii/Generator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/gii/Generator.php b/extensions/gii/Generator.php index 608f685..88fd25c 100644 --- a/extensions/gii/Generator.php +++ b/extensions/gii/Generator.php @@ -193,7 +193,7 @@ abstract class Generator extends Model $attributes[] = 'template'; $path = $this->getStickyDataFile(); if (is_file($path)) { - $result = @include($path); + $result = unserialize(file_get_contents($path)); if (is_array($result)) { foreach ($stickyAttributes as $name) { if (isset($result[$name])) { @@ -218,7 +218,7 @@ abstract class Generator extends Model } $path = $this->getStickyDataFile(); @mkdir(dirname($path), 0755, true); - file_put_contents($path, "getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.php'; + return Yii::$app->getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.data'; } /** From abb349361b1a4b0bab50aa50d640d2594953c30b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 21:47:54 -0500 Subject: [PATCH 14/79] use json format. --- extensions/gii/Generator.php | 6 +++--- framework/yii/helpers/BaseSecurity.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/gii/Generator.php b/extensions/gii/Generator.php index 88fd25c..fb856d5 100644 --- a/extensions/gii/Generator.php +++ b/extensions/gii/Generator.php @@ -193,7 +193,7 @@ abstract class Generator extends Model $attributes[] = 'template'; $path = $this->getStickyDataFile(); if (is_file($path)) { - $result = unserialize(file_get_contents($path)); + $result = json_decode(file_get_contents($path), true); if (is_array($result)) { foreach ($stickyAttributes as $name) { if (isset($result[$name])) { @@ -218,7 +218,7 @@ abstract class Generator extends Model } $path = $this->getStickyDataFile(); @mkdir(dirname($path), 0755, true); - file_put_contents($path, serialize($values)); + file_put_contents($path, json_encode($values)); } /** @@ -227,7 +227,7 @@ abstract class Generator extends Model */ public function getStickyDataFile() { - return Yii::$app->getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.data'; + return Yii::$app->getRuntimePath() . '/gii-' . Yii::getVersion() . '/' . str_replace('\\', '-', get_class($this)) . '.json'; } /** diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/yii/helpers/BaseSecurity.php index db226ea..8e86654 100644 --- a/framework/yii/helpers/BaseSecurity.php +++ b/framework/yii/helpers/BaseSecurity.php @@ -184,16 +184,16 @@ class BaseSecurity public static function getSecretKey($name, $length = 32) { static $keys; - $keyFile = Yii::$app->getRuntimePath() . '/keys.data'; + $keyFile = Yii::$app->getRuntimePath() . '/keys.json'; if ($keys === null) { $keys = []; if (is_file($keyFile)) { - $keys = unserialize(file_get_contents($keyFile)); + $keys = json_decode(file_get_contents($keyFile), true); } } if (!isset($keys[$name])) { $keys[$name] = static::generateRandomKey($length); - file_put_contents($keyFile, serialize($keys)); + file_put_contents($keyFile, json_encode($keys)); } return $keys[$name]; } From dbecf848f47a38f82b8bcb5a6b5b874ee90f10c5 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 23:21:41 -0500 Subject: [PATCH 15/79] Fixes #1156: action parameter binding now checks for array type. --- framework/yii/web/Controller.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index 3319f3b..2ecd9e4 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -41,7 +41,7 @@ class Controller extends \yii\base\Controller * @param \yii\base\Action $action the action to be bound with parameters * @param array $params the parameters to be bound to the action * @return array the valid parameters that the action can run with. - * @throws HttpException if there are missing parameters. + * @throws HttpException if there are missing or invalid parameters. */ public function bindActionParams($action, $params) { @@ -57,7 +57,15 @@ class Controller extends \yii\base\Controller foreach ($method->getParameters() as $param) { $name = $param->getName(); if (array_key_exists($name, $params)) { - $args[] = $actionParams[$name] = $params[$name]; + if ($param->isArray()) { + $args[] = $actionParams[$name] = is_array($params[$name]) ? $params[$name] : [$params[$name]]; + } elseif (!is_array($params[$name])) { + $args[] = $actionParams[$name] = $params[$name]; + } else { + throw new HttpException(400, Yii::t('yii', 'Invalid data received for parameter "{param}".', [ + 'param' => $name, + ])); + } unset($params[$name]); } elseif ($param->isDefaultValueAvailable()) { $args[] = $actionParams[$name] = $param->getDefaultValue(); From c8be05665ffdb0d1fde79b13c972682133cc80cf Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 23:58:58 -0500 Subject: [PATCH 16/79] Fixes #1122: removed init from post-install-cmd-script of composer. --- apps/advanced/README.md | 7 +++++++ apps/advanced/composer.json | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/advanced/README.md b/apps/advanced/README.md index 1afef67..00beb56 100644 --- a/apps/advanced/README.md +++ b/apps/advanced/README.md @@ -76,6 +76,13 @@ php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-a Note that in order to install some dependencies you must have `php_openssl` extension enabled. +After the application is installed, switch to the project folder and run the following command +to initialize the application: + +~~~ +./init (init on Windows) +~~~ + ### Install from an Archive File diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json index 17b5106..9fd15d2 100644 --- a/apps/advanced/composer.json +++ b/apps/advanced/composer.json @@ -23,8 +23,7 @@ }, "scripts": { "post-create-project-cmd": [ - "yii\\composer\\Installer::setPermission", - "./init" + "yii\\composer\\Installer::setPermission" ] }, "extra": { From 3bda94eeebba2fde6b10d6abd551e2f28317ab8a Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 8 Nov 2013 23:59:17 -0500 Subject: [PATCH 17/79] Fixed php-diff version. --- framework/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/composer.json b/framework/composer.json index 9e6e3c4..c25ca6c 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -69,7 +69,7 @@ "yiisoft/jquery": "1.10.*", "ext-mbstring": "*", "lib-pcre": "*", - "phpspec/php-diff": "dev-master", + "phpspec/php-diff": ">=1.0.2", "ezyang/htmlpurifier": "4.5.*" }, "autoload": { From 7d6d01b4bc9d7a23a7f5ec2a145b60f4898c80d6 Mon Sep 17 00:00:00 2001 From: Dmitry Erofeev Date: Sat, 9 Nov 2013 21:45:46 +0400 Subject: [PATCH 18/79] added FileMailer implementation of mailer interface --- extensions/swiftmailer/FileMailer.php | 135 +++++++++++++++++++++ extensions/swiftmailer/README.md | 16 ++- extensions/swiftmailer/composer.json | 6 +- .../unit/extensions/swiftmailer/FileMailerTest.php | 62 ++++++++++ 4 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 extensions/swiftmailer/FileMailer.php create mode 100644 tests/unit/extensions/swiftmailer/FileMailerTest.php diff --git a/extensions/swiftmailer/FileMailer.php b/extensions/swiftmailer/FileMailer.php new file mode 100644 index 0000000..966c033 --- /dev/null +++ b/extensions/swiftmailer/FileMailer.php @@ -0,0 +1,135 @@ + array( + * ... + * 'email' => array( + * 'class' => 'yii\swiftmailer\FileMailer' + * ), + * ... + * ), + * ~~~ + * + * To send an email, you may use the following code: + * + * ~~~ + * Yii::$app->mail->compose('contact/html', ['contactForm' => $form]) + * ->setFrom('from@domain.com') + * ->setTo($form->email) + * ->setSubject($form->subject) + * ->send(); + * ~~~ + * + * @author Dmitry Erofeev + * @since 2.0 + */ +class FileMailer extends BaseMailer +{ + /** + * @var string message default class name. + */ + public $messageClass = 'yii\swiftmailer\Message'; + + /** + * @var string The path under which mail files will be written. Defaults to "@app/runtime/mail". + */ + protected $path; + + /** + * @var callable Callback that will be used to generate name of the message to save. + */ + protected $callback; + + /** + * Sets path under which mail files will be written. + * Any trailing '/' and '\' characters in the given path will be trimmed. + * + * @param $path + * @throws \InvalidArgumentException + */ + public function setPath($path) + { + $path = Yii::getAlias($path); + if (!is_dir($path) || !is_writable($path)) { + throw new \InvalidArgumentException('Filemailer::setPath expects a valid path in which to write mail files'); + } + + $this->path = rtrim($path, '\\/'); + } + + /** + * Gets path under which mail files will be written. + * + * @return string + */ + public function getPath() + { + if ($this->path == null) { + $path = Yii::getAlias('@app/runtime/mail'); + if (!is_dir($path)) { + FileHelper::createDirectory($path); + } + $this->setPath($path); + } + + return $this->path; + } + + /** + * Sets callback that will be used to generate name of the message to save. + * + * @param callable $callback + */ + public function setCallback(callable $callback) + { + $this->callback = $callback; + } + + /** + * Gets callback that will be used to generate name of the message to save. + * + * @return callable + */ + public function getCallback() + { + if ($this->callback == null) { + $this->setCallback(function () { + return uniqid('Message_') . '.txt'; + }); + } + + return $this->callback; + } + + /** + * @inheritdoc + */ + public function send($message) + { + $address = $message->getTo(); + if (is_array($address)) { + $address = implode(', ', array_keys($address)); + } + Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); + + $filename = $this->getPath() . DIRECTORY_SEPARATOR . call_user_func($this->getCallback()); + + return file_put_contents($filename, $message->toString()) !== false; + } +} \ No newline at end of file diff --git a/extensions/swiftmailer/README.md b/extensions/swiftmailer/README.md index 3edd4a4..515dd49 100644 --- a/extensions/swiftmailer/README.md +++ b/extensions/swiftmailer/README.md @@ -3,7 +3,7 @@ SwiftMailer Extension for Yii 2 This extension provides a `SwiftMailer` mail solution for Yii 2. -To use this extension, simply add the following code in your application configuration: +To use this extension, simply add the following code in your application configuration: ```php return [ @@ -16,6 +16,20 @@ return [ ]; ``` +Extension also includes `FileMailer` that saves mails as files under runtime directory. +It can be useful during development. To use it add the following code in your application configuration: + +```php +return [ + //.... + 'components' => [ + 'mail' => [ + 'class' => 'yii\swiftmailer\FileMailer', + ], + ], +]; +``` + You can then send an email as follows: ```php diff --git a/extensions/swiftmailer/composer.json b/extensions/swiftmailer/composer.json index e995b2f..928230e 100644 --- a/extensions/swiftmailer/composer.json +++ b/extensions/swiftmailer/composer.json @@ -15,7 +15,11 @@ { "name": "Paul Klimov", "email": "klimov.paul@gmail.com" - } + }, + { + "name": "Dmitry Erofeev", + "email": "dmeroff@gmail.com" + } ], "minimum-stability": "dev", "require": { diff --git a/tests/unit/extensions/swiftmailer/FileMailerTest.php b/tests/unit/extensions/swiftmailer/FileMailerTest.php new file mode 100644 index 0000000..10b523d --- /dev/null +++ b/tests/unit/extensions/swiftmailer/FileMailerTest.php @@ -0,0 +1,62 @@ +mockApplication([ + 'components' => [ + 'mail' => $this->createTestEmailComponent() + ] + ]); + } + + /** + * @return FileMailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new FileMailer([ + 'callback' => function () { + return 'Message_test.txt'; + } + ]); + + return $component; + } + + public function testConfigurePath() + { + $mailer = new FileMailer(); + $this->assertEquals(\Yii::getAlias('@app/runtime/mail'), $mailer->getPath()); + $mailer->setPath('@yiiunit/runtime/'); + $this->assertEquals(\Yii::getAlias('@yiiunit/runtime'), $mailer->getPath()); + } + + public function testSend() + { + $message = \Yii::$app->mail->compose() + ->setTo('tester@example.com') + ->setFrom('admin@example.com') + ->setSubject('Just a test') + ->setHtmlBody('This is html body'); + $message->send(); + $this->assertEquals( + $message->toString(), + file_get_contents(\Yii::$app->getRuntimePath() . '/mail/Message_test.txt') + ); + } +} + \ No newline at end of file From c298b543fb52e7c2eed01a23498b6d72a1677f1d Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 20:51:16 -0500 Subject: [PATCH 19/79] Revert "added FileMailer implementation of mailer interface" This reverts commit 7d6d01b4bc9d7a23a7f5ec2a145b60f4898c80d6. --- extensions/swiftmailer/FileMailer.php | 135 --------------------- extensions/swiftmailer/README.md | 16 +-- extensions/swiftmailer/composer.json | 6 +- .../unit/extensions/swiftmailer/FileMailerTest.php | 62 ---------- 4 files changed, 2 insertions(+), 217 deletions(-) delete mode 100644 extensions/swiftmailer/FileMailer.php delete mode 100644 tests/unit/extensions/swiftmailer/FileMailerTest.php diff --git a/extensions/swiftmailer/FileMailer.php b/extensions/swiftmailer/FileMailer.php deleted file mode 100644 index 966c033..0000000 --- a/extensions/swiftmailer/FileMailer.php +++ /dev/null @@ -1,135 +0,0 @@ - array( - * ... - * 'email' => array( - * 'class' => 'yii\swiftmailer\FileMailer' - * ), - * ... - * ), - * ~~~ - * - * To send an email, you may use the following code: - * - * ~~~ - * Yii::$app->mail->compose('contact/html', ['contactForm' => $form]) - * ->setFrom('from@domain.com') - * ->setTo($form->email) - * ->setSubject($form->subject) - * ->send(); - * ~~~ - * - * @author Dmitry Erofeev - * @since 2.0 - */ -class FileMailer extends BaseMailer -{ - /** - * @var string message default class name. - */ - public $messageClass = 'yii\swiftmailer\Message'; - - /** - * @var string The path under which mail files will be written. Defaults to "@app/runtime/mail". - */ - protected $path; - - /** - * @var callable Callback that will be used to generate name of the message to save. - */ - protected $callback; - - /** - * Sets path under which mail files will be written. - * Any trailing '/' and '\' characters in the given path will be trimmed. - * - * @param $path - * @throws \InvalidArgumentException - */ - public function setPath($path) - { - $path = Yii::getAlias($path); - if (!is_dir($path) || !is_writable($path)) { - throw new \InvalidArgumentException('Filemailer::setPath expects a valid path in which to write mail files'); - } - - $this->path = rtrim($path, '\\/'); - } - - /** - * Gets path under which mail files will be written. - * - * @return string - */ - public function getPath() - { - if ($this->path == null) { - $path = Yii::getAlias('@app/runtime/mail'); - if (!is_dir($path)) { - FileHelper::createDirectory($path); - } - $this->setPath($path); - } - - return $this->path; - } - - /** - * Sets callback that will be used to generate name of the message to save. - * - * @param callable $callback - */ - public function setCallback(callable $callback) - { - $this->callback = $callback; - } - - /** - * Gets callback that will be used to generate name of the message to save. - * - * @return callable - */ - public function getCallback() - { - if ($this->callback == null) { - $this->setCallback(function () { - return uniqid('Message_') . '.txt'; - }); - } - - return $this->callback; - } - - /** - * @inheritdoc - */ - public function send($message) - { - $address = $message->getTo(); - if (is_array($address)) { - $address = implode(', ', array_keys($address)); - } - Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); - - $filename = $this->getPath() . DIRECTORY_SEPARATOR . call_user_func($this->getCallback()); - - return file_put_contents($filename, $message->toString()) !== false; - } -} \ No newline at end of file diff --git a/extensions/swiftmailer/README.md b/extensions/swiftmailer/README.md index 515dd49..3edd4a4 100644 --- a/extensions/swiftmailer/README.md +++ b/extensions/swiftmailer/README.md @@ -3,7 +3,7 @@ SwiftMailer Extension for Yii 2 This extension provides a `SwiftMailer` mail solution for Yii 2. -To use this extension, simply add the following code in your application configuration: +To use this extension, simply add the following code in your application configuration: ```php return [ @@ -16,20 +16,6 @@ return [ ]; ``` -Extension also includes `FileMailer` that saves mails as files under runtime directory. -It can be useful during development. To use it add the following code in your application configuration: - -```php -return [ - //.... - 'components' => [ - 'mail' => [ - 'class' => 'yii\swiftmailer\FileMailer', - ], - ], -]; -``` - You can then send an email as follows: ```php diff --git a/extensions/swiftmailer/composer.json b/extensions/swiftmailer/composer.json index 928230e..e995b2f 100644 --- a/extensions/swiftmailer/composer.json +++ b/extensions/swiftmailer/composer.json @@ -15,11 +15,7 @@ { "name": "Paul Klimov", "email": "klimov.paul@gmail.com" - }, - { - "name": "Dmitry Erofeev", - "email": "dmeroff@gmail.com" - } + } ], "minimum-stability": "dev", "require": { diff --git a/tests/unit/extensions/swiftmailer/FileMailerTest.php b/tests/unit/extensions/swiftmailer/FileMailerTest.php deleted file mode 100644 index 10b523d..0000000 --- a/tests/unit/extensions/swiftmailer/FileMailerTest.php +++ /dev/null @@ -1,62 +0,0 @@ -mockApplication([ - 'components' => [ - 'mail' => $this->createTestEmailComponent() - ] - ]); - } - - /** - * @return FileMailer test email component instance. - */ - protected function createTestEmailComponent() - { - $component = new FileMailer([ - 'callback' => function () { - return 'Message_test.txt'; - } - ]); - - return $component; - } - - public function testConfigurePath() - { - $mailer = new FileMailer(); - $this->assertEquals(\Yii::getAlias('@app/runtime/mail'), $mailer->getPath()); - $mailer->setPath('@yiiunit/runtime/'); - $this->assertEquals(\Yii::getAlias('@yiiunit/runtime'), $mailer->getPath()); - } - - public function testSend() - { - $message = \Yii::$app->mail->compose() - ->setTo('tester@example.com') - ->setFrom('admin@example.com') - ->setSubject('Just a test') - ->setHtmlBody('This is html body'); - $message->send(); - $this->assertEquals( - $message->toString(), - file_get_contents(\Yii::$app->getRuntimePath() . '/mail/Message_test.txt') - ); - } -} - \ No newline at end of file From b965d4eec75948cc663df7205b2db8669edd15bc Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 22:24:27 -0500 Subject: [PATCH 20/79] Implemented file transport for mails. --- extensions/swiftmailer/Mailer.php | 2 +- framework/yii/mail/BaseMailer.php | 80 +++++++++++++++++++++++++-- tests/unit/framework/mail/BaseMailerTest.php | 26 ++++++++- tests/unit/framework/mail/BaseMessageTest.php | 2 +- 4 files changed, 101 insertions(+), 9 deletions(-) diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php index 7c37a3f..35b3b7c 100644 --- a/extensions/swiftmailer/Mailer.php +++ b/extensions/swiftmailer/Mailer.php @@ -104,7 +104,7 @@ class Mailer extends BaseMailer /** * @inheritdoc */ - public function send($message) + protected function sendMessage($message) { $address = $message->getTo(); if (is_array($address)) { diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index 52f31ce..4cc5279 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -16,7 +16,7 @@ use yii\web\View; /** * BaseMailer serves as a base class that implements the basic functions required by [[MailerInterface]]. * - * Concrete child classes should may focus on implementing the [[send()]] method. + * Concrete child classes should may focus on implementing the [[sendMessage()]] method. * * @see BaseMessage * @@ -29,10 +29,6 @@ use yii\web\View; abstract class BaseMailer extends Component implements MailerInterface, ViewContextInterface { /** - * @var \yii\base\View|array view instance or its array configuration. - */ - private $_view = []; - /** * @var string directory containing view files for this email messages. * This can be specified as an absolute path or path alias. */ @@ -71,6 +67,27 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont * @var string the default class name of the new message instances created by [[createMessage()]] */ public $messageClass = 'yii\mail\BaseMessage'; + /** + * @var boolean whether to save email messages as files under [[fileTransportPath]] instead of sending them + * to the actual recipients. This is usually used during development for debugging purpose. + * @see fileTransportPath + */ + public $useFileTransport = false; + /** + * @var string the directory where the email messages are saved when [[useFileTransport]] is true. + */ + public $fileTransportPath = '@runtime/mail'; + /** + * @var callback a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true. + * The callback should return a file name which will be used to save the email message. + * If not set, the file name will be generated based on the current timestamp. + */ + public $fileTransportCallback; + + /** + * @var \yii\base\View|array view instance or its array configuration. + */ + private $_view = []; /** * @param array|View $view view instance or its array configuration that will be used to @@ -173,6 +190,30 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont } /** + * Sends the given email message. + * This method will log a message about the email being sent. + * If [[useFileTransport]] is true, it will save the email as a file under [[fileTransportPath]]. + * Otherwise, it will call [[sendMessage()]] to send the email to its recipient(s). + * Child classes should implement [[sendMessage()]] with the actual email sending logic. + * @param MessageInterface $message email message instance to be sent + * @return boolean whether the message has been sent successfully + */ + public function send($message) + { + $address = $message->getTo(); + if (is_array($address)) { + $address = implode(', ', array_keys($address)); + } + Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); + + if ($this->useFileTransport) { + return $this->saveMessage($message); + } else { + return $this->sendMessage($message); + } + } + + /** * Sends multiple messages at once. * * The default implementation simply calls [[send()]] multiple times. @@ -212,6 +253,35 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont } /** + * Sends the specified message. + * This method should be implemented by child classes with the actual email sending logic. + * @param MessageInterface $message the message to be sent + * @return boolean whether the message is sent successfully + */ + abstract protected function sendMessage($message); + + /** + * Saves the message as a file under [[fileTransportPath]]. + * @param MessageInterface $message + * @return boolean whether the message is saved successfully + */ + protected function saveMessage($message) + { + $path = Yii::getAlias($this->fileTransportPath); + if (!is_dir(($path))) { + mkdir($path, 0777, true); + } + if ($this->fileTransportCallback !== null) { + $file = $path . '/' . call_user_func($this->fileTransportCallback); + } else { + $time = microtime(true); + $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.txt'; + } + file_put_contents($file, $message->toString()); + return true; + } + + /** * Finds the view file corresponding to the specified relative view name. * This method will return the view file by prefixing the view name with [[viewPath]]. * @param string $view a relative view name. The name does NOT start with a slash. diff --git a/tests/unit/framework/mail/BaseMailerTest.php b/tests/unit/framework/mail/BaseMailerTest.php index 58e4c02..1c3ee22 100644 --- a/tests/unit/framework/mail/BaseMailerTest.php +++ b/tests/unit/framework/mail/BaseMailerTest.php @@ -208,6 +208,28 @@ class BaseMailerTest extends TestCase $this->assertEquals($htmlViewFileContent, $message->_htmlBody, 'Unable to render html by direct view!'); $this->assertEquals(strip_tags($htmlViewFileContent), $message->_textBody, 'Unable to render text by direct view!'); } + + public function testUseFileTransport() + { + $mailer = new Mailer(); + $this->assertFalse($mailer->useFileTransport); + $this->assertEquals('@runtime/mail', $mailer->fileTransportPath); + + $mailer->fileTransportPath = '@yiiunit/runtime/mail'; + $mailer->useFileTransport = true; + $mailer->fileTransportCallback = function () { + return 'message.txt'; + }; + $message = $mailer->compose() + ->setTo('to@example.com') + ->setFrom('from@example.com') + ->setSubject('test subject') + ->setTextBody('text body' . microtime(true)); + $this->assertTrue($mailer->send($message)); + $file = Yii::getAlias($mailer->fileTransportPath) . '/message.txt'; + $this->assertTrue(is_file($file)); + $this->assertEquals($message->toString(), file_get_contents($file)); + } } /** @@ -218,7 +240,7 @@ class Mailer extends BaseMailer public $messageClass = 'yiiunit\framework\mail\Message'; public $sentMessages = []; - public function send($message) + protected function sendMessage($message) { $this->sentMessages[] = $message; } @@ -340,6 +362,6 @@ class Message extends BaseMessage public function toString() { - return get_class($this); + return var_export($this, true); } } diff --git a/tests/unit/framework/mail/BaseMessageTest.php b/tests/unit/framework/mail/BaseMessageTest.php index 0ffedf3..d80d4f7 100644 --- a/tests/unit/framework/mail/BaseMessageTest.php +++ b/tests/unit/framework/mail/BaseMessageTest.php @@ -71,7 +71,7 @@ class TestMailer extends BaseMailer public $messageClass = 'yiiunit\framework\mail\TestMessage'; public $sentMessages = array(); - public function send($message) + protected function sendMessage($message) { $this->sentMessages[] = $message; } From 9867ffb6f73c182421f5d98f7f71c5083b83ebde Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 22:36:56 -0500 Subject: [PATCH 21/79] Added display of installed extension to debug panel. --- extensions/debug/panels/ConfigPanel.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/extensions/debug/panels/ConfigPanel.php b/extensions/debug/panels/ConfigPanel.php index 3ed5b06..6ea6570 100644 --- a/extensions/debug/panels/ConfigPanel.php +++ b/extensions/debug/panels/ConfigPanel.php @@ -64,6 +64,7 @@ EOD; return "

Configuration

\n" . $this->renderData('Application Configuration', $app) . "\n" . $this->renderData('PHP Configuration', $php) . "\n" + . $this->renderExtensions() . $this->getPhpInfo(); } @@ -93,6 +94,18 @@ $rows EOD; } + protected function renderExtensions() + { + if (empty($this->data['extensions'])) { + return ''; + } + $data = []; + foreach ($this->data['extensions'] as $extension) { + $data[$extension['name']] = $data['version']; + } + return $this->renderData('Extensions', $data) . "\n"; + } + public function save() { return [ @@ -110,6 +123,7 @@ EOD; 'apc' => extension_loaded('apc'), 'memcache' => extension_loaded('memcache'), ], + 'extensions' => Yii::$app->extensions, ]; } } From 8333a14eb51c04a772367781de28b567b68f6d32 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 23:02:56 -0500 Subject: [PATCH 22/79] Added parameters to fileTransportCallback. --- framework/yii/mail/BaseMailer.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index 4cc5279..6311549 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -81,6 +81,12 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont * @var callback a PHP callback that will be called by [[send()]] when [[useFileTransport]] is true. * The callback should return a file name which will be used to save the email message. * If not set, the file name will be generated based on the current timestamp. + * + * The signature of the callback is: + * + * ~~~ + * function ($mailer, $message) + * ~~~ */ public $fileTransportCallback; @@ -272,7 +278,7 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont mkdir($path, 0777, true); } if ($this->fileTransportCallback !== null) { - $file = $path . '/' . call_user_func($this->fileTransportCallback); + $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message); } else { $time = microtime(true); $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.txt'; From 896b7d1528a0f016d8cbdf9563200749da93f454 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 23:13:20 -0500 Subject: [PATCH 23/79] typo fix. --- extensions/debug/panels/ConfigPanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/debug/panels/ConfigPanel.php b/extensions/debug/panels/ConfigPanel.php index 6ea6570..99e39aa 100644 --- a/extensions/debug/panels/ConfigPanel.php +++ b/extensions/debug/panels/ConfigPanel.php @@ -101,7 +101,7 @@ EOD; } $data = []; foreach ($this->data['extensions'] as $extension) { - $data[$extension['name']] = $data['version']; + $data[$extension['name']] = $extension['version']; } return $this->renderData('Extensions', $data) . "\n"; } From f6b9337766dbe5276df9de4822edd921eaa858f9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 9 Nov 2013 23:18:01 -0500 Subject: [PATCH 24/79] Adjusted extension list. --- extensions/debug/panels/ConfigPanel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/debug/panels/ConfigPanel.php b/extensions/debug/panels/ConfigPanel.php index 99e39aa..59cabf3 100644 --- a/extensions/debug/panels/ConfigPanel.php +++ b/extensions/debug/panels/ConfigPanel.php @@ -63,8 +63,8 @@ EOD; ]; return "

Configuration

\n" . $this->renderData('Application Configuration', $app) . "\n" - . $this->renderData('PHP Configuration', $php) . "\n" . $this->renderExtensions() + . $this->renderData('PHP Configuration', $php) . "\n" . $this->getPhpInfo(); } @@ -103,7 +103,7 @@ EOD; foreach ($this->data['extensions'] as $extension) { $data[$extension['name']] = $extension['version']; } - return $this->renderData('Extensions', $data) . "\n"; + return $this->renderData('Installed Extensions', $data) . "\n"; } public function save() From de062c26a45e4e976080389dddc92d982eaffdf7 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 10 Nov 2013 18:17:17 -0500 Subject: [PATCH 25/79] moved markdown dependency to required. --- framework/composer.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/framework/composer.json b/framework/composer.json index c25ca6c..0d8a407 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -70,12 +70,10 @@ "ext-mbstring": "*", "lib-pcre": "*", "phpspec/php-diff": ">=1.0.2", - "ezyang/htmlpurifier": "4.5.*" + "ezyang/htmlpurifier": "4.5.*", + "michelf/php-markdown": "1.3.*" }, "autoload": { "psr-0": { "yii\\": "/" } - }, - "suggest": { - "michelf/php-markdown": "Required by Markdown." } } From 743d31a8e65457e333a4f6cdcb72df9ea6f4b6cf Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 10 Nov 2013 21:07:13 -0500 Subject: [PATCH 26/79] Fixes #1075: separated empty switch and empty display for ListView and GridView. --- framework/yii/grid/GridView.php | 13 +++++++++---- framework/yii/widgets/BaseListView.php | 26 +++++++++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php index 2981c82..8ba8c0a 100644 --- a/framework/yii/grid/GridView.php +++ b/framework/yii/grid/GridView.php @@ -89,10 +89,9 @@ class GridView extends BaseListView */ public $showFooter = false; /** - * @var string|boolean the HTML content to be displayed when [[dataProvider]] does not have any data. - * If false, the grid view will still be displayed (without body content though). + * @var boolean whether to show the grid view if [[dataProvider]] returns no data. */ - public $empty = false; + public $showOnEmpty = true; /** * @var array|Formatter the formatter used to format model attribute values into displayable texts. * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]] @@ -342,7 +341,13 @@ class GridView extends BaseListView } } } - return "\n" . implode("\n", $rows) . "\n"; + + if (empty($rows)) { + return "\n" . implode("\n", $rows) . "\n"; + } else { + $colspan = count($this->columns); + return "\n" . $this->renderEmpty() . "\n"; + } } /** diff --git a/framework/yii/widgets/BaseListView.php b/framework/yii/widgets/BaseListView.php index 310201a..ec9c1cb 100644 --- a/framework/yii/widgets/BaseListView.php +++ b/framework/yii/widgets/BaseListView.php @@ -53,10 +53,13 @@ abstract class BaseListView extends Widget */ public $summary; /** - * @var string|boolean the HTML content to be displayed when [[dataProvider]] does not have any data. - * If false, the list view will still be displayed (without body content though). + * @var boolean whether to show the list view if [[dataProvider]] returns no data. */ - public $empty; + public $showOnEmpty = false; + /** + * @var string the HTML content to be displayed when [[dataProvider]] does not have any data. + */ + public $emptyText; /** * @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: @@ -83,6 +86,9 @@ abstract class BaseListView extends Widget if ($this->dataProvider === null) { throw new InvalidConfigException('The "dataProvider" property must be set.'); } + if ($this->emptyText === null) { + $this->emptyText = Yii::t('yii', 'No results found.'); + } $this->dataProvider->prepare(); } @@ -91,13 +97,13 @@ abstract class BaseListView extends Widget */ public function run() { - if ($this->dataProvider->getCount() > 0 || $this->empty === false) { + if ($this->dataProvider->getCount() > 0 || $this->showOnEmpty) { $content = preg_replace_callback("/{\\w+}/", function ($matches) { $content = $this->renderSection($matches[0]); return $content === false ? $matches[0] : $content; }, $this->layout); } else { - $content = '
' . ($this->empty === null ? Yii::t('yii', 'No results found.') : $this->empty) . '
'; + $content = $this->renderEmpty(); } $tag = ArrayHelper::remove($this->options, 'tag', 'div'); echo Html::tag($tag, $content, $this->options); @@ -126,6 +132,16 @@ abstract class BaseListView extends Widget } /** + * Renders the HTML content indicating that the list view has no data. + * @return string the rendering result + * @see emptyText + */ + public function renderEmpty() + { + return '
' . ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText) . '
'; + } + + /** * Renders the summary text. */ public function renderSummary() From a8d189676f5d84559397fa268253b09c19850070 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 10 Nov 2013 21:18:19 -0500 Subject: [PATCH 27/79] typo fix. --- framework/yii/grid/GridView.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php index 8ba8c0a..6cef03a 100644 --- a/framework/yii/grid/GridView.php +++ b/framework/yii/grid/GridView.php @@ -343,10 +343,10 @@ class GridView extends BaseListView } if (empty($rows)) { - return "\n" . implode("\n", $rows) . "\n"; - } else { $colspan = count($this->columns); return "\n" . $this->renderEmpty() . "\n"; + } else { + return "\n" . implode("\n", $rows) . "\n"; } } From 183a0bfaef4cb48d70cfe9f9540c4aa0a58dc443 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 10 Nov 2013 21:20:15 -0500 Subject: [PATCH 28/79] hide summary when empty. --- framework/yii/widgets/BaseListView.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/yii/widgets/BaseListView.php b/framework/yii/widgets/BaseListView.php index ec9c1cb..4c4e5a4 100644 --- a/framework/yii/widgets/BaseListView.php +++ b/framework/yii/widgets/BaseListView.php @@ -147,6 +147,9 @@ abstract class BaseListView extends Widget public function renderSummary() { $count = $this->dataProvider->getCount(); + if ($count <= 0) { + return ''; + } if (($pagination = $this->dataProvider->getPagination()) !== false) { $totalCount = $this->dataProvider->getTotalCount(); $begin = $pagination->getPage() * $pagination->pageSize + 1; From faed08e6203246eed9387b7bc1412d5fb0008006 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 11 Nov 2013 12:39:38 +0100 Subject: [PATCH 29/79] fixed crud to work with table that has no PK but AR defines one --- extensions/gii/generators/crud/Generator.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php index 3fd5e41..ea347e1 100644 --- a/extensions/gii/generators/crud/Generator.php +++ b/extensions/gii/generators/crud/Generator.php @@ -341,7 +341,9 @@ class Generator extends \yii\gii\Generator public function generateUrlParams() { - $pks = $this->getTableSchema()->primaryKey; + /** @var ActiveRecord $class */ + $class = $this->modelClass; + $pks = $class::primaryKey(); if (count($pks) === 1) { return "'id' => \$model->{$pks[0]}"; } else { @@ -355,7 +357,9 @@ class Generator extends \yii\gii\Generator public function generateActionParams() { - $pks = $this->getTableSchema()->primaryKey; + /** @var ActiveRecord $class */ + $class = $this->modelClass; + $pks = $class::primaryKey(); if (count($pks) === 1) { return '$id'; } else { @@ -366,7 +370,9 @@ class Generator extends \yii\gii\Generator public function generateActionParamComments() { $table = $this->getTableSchema(); - $pks = $table->primaryKey; + /** @var ActiveRecord $class */ + $class = $this->modelClass; + $pks = $class::primaryKey(); if (count($pks) === 1) { return ['@param ' . $table->columns[$pks[0]]->phpType . ' $id']; } else { From a408b75318f19859df13b867b747c54d3b12b42b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 07:28:55 -0500 Subject: [PATCH 30/79] Fixes #1169: anchor doesn't work when enablePrettyUrl is false. --- framework/yii/web/UrlManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index c5f4c28..42b12ff 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -251,7 +251,7 @@ class UrlManager extends Component if (!empty($params)) { $url .= '&' . http_build_query($params); } - return $url; + return $url . $anchor; } } From baaab4e09f264b7ca3399131af75fae06f8e3bc1 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 08:31:52 -0500 Subject: [PATCH 31/79] Fixes #1170 --- framework/yii/caching/MemCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/caching/MemCache.php b/framework/yii/caching/MemCache.php index fc7efb4..6f7a760 100644 --- a/framework/yii/caching/MemCache.php +++ b/framework/yii/caching/MemCache.php @@ -31,7 +31,7 @@ use yii\base\InvalidConfigException; * [ * 'components' => [ * 'cache' => [ - * 'class' => 'MemCache', + * 'class' => 'yii\caching\MemCache', * 'servers' => [ * [ * 'host' => 'server1', From 365bdcf68aa5b31f8d79dfadbddc5ac508f19292 Mon Sep 17 00:00:00 2001 From: mcd-php Date: Mon, 11 Nov 2013 16:43:50 +0300 Subject: [PATCH 32/79] FIX: Relation names not mangled in \yii\db\ActiveQuery::normalizeRelations --- framework/yii/db/ActiveQuery.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 99846db..015ce93 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -308,13 +308,12 @@ class ActiveQuery extends Query $childName = null; } - $t = strtolower($name); - if (!isset($relations[$t])) { + if (!isset($relations[$name])) { $relation = $model->getRelation($name); $relation->primaryModel = null; - $relations[$t] = $relation; + $relations[$name] = $relation; } else { - $relation = $relations[$t]; + $relation = $relations[$name]; } if (isset($childName)) { From 7565dec37da1dfccfe275ff7041709bb5c478c7d Mon Sep 17 00:00:00 2001 From: egorpromo Date: Mon, 11 Nov 2013 21:35:34 +0700 Subject: [PATCH 33/79] More precise descripton fo Session::cookieParam --- framework/yii/web/Session.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index 92ec3ad..104d35d 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -82,7 +82,9 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co public $flashVar = '__flash'; /** - * @var array parameter-value pairs to override default session cookie parameters + * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function + * @see http://www.php.net/manual/en/function.session-set-cookie-params.php + * @see setCookieParams() */ public $cookieParams = array( 'httpOnly' => true From 23573968a6bff7c4e9c3fcd8ed6c2041a6515334 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 20:19:25 -0500 Subject: [PATCH 34/79] Changed important log messages to info level --- extensions/swiftmailer/Mailer.php | 2 +- framework/yii/db/Command.php | 4 ++-- framework/yii/mail/BaseMailer.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php index 35b3b7c..3418d8d 100644 --- a/extensions/swiftmailer/Mailer.php +++ b/extensions/swiftmailer/Mailer.php @@ -110,7 +110,7 @@ class Mailer extends BaseMailer if (is_array($address)) { $address = implode(', ', array_keys($address)); } - Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); + Yii::info('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); return $this->getSwiftMailer()->send($message->getSwiftMessage()) > 0; } diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 671558a..4dffb01 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -260,7 +260,7 @@ class Command extends \yii\base\Component $rawSql = $this->getRawSql(); - Yii::trace($rawSql, __METHOD__); + Yii::info($rawSql, __METHOD__); if ($sql == '') { return 0; @@ -364,7 +364,7 @@ class Command extends \yii\base\Component $db = $this->db; $rawSql = $this->getRawSql(); - Yii::trace($rawSql, __METHOD__); + Yii::info($rawSql, __METHOD__); /** @var $cache \yii\caching\Cache */ if ($db->enableQueryCache && $method !== '') { diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index 6311549..d907918 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -210,7 +210,7 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont if (is_array($address)) { $address = implode(', ', array_keys($address)); } - Yii::trace('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); + Yii::info('Sending email "' . $message->getSubject() . '" to "' . $address . '"', __METHOD__); if ($this->useFileTransport) { return $this->saveMessage($message); From 342a9a2e1a1e5d7468470e1ae6fc3b430276ecd8 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 20:19:46 -0500 Subject: [PATCH 35/79] Added log messages to user login and logout actions. --- framework/yii/web/User.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index eca2ed6..b77af2b 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -180,6 +180,9 @@ class User extends Component { if ($this->beforeLogin($identity, false)) { $this->switchIdentity($identity, $duration); + $id = $identity->getId(); + $ip = Yii::$app->getRequest()->getUserIP(); + Yii::info("User '$id' logged in from $ip.", __METHOD__); $this->afterLogin($identity, false); } return !$this->getIsGuest(); @@ -205,6 +208,8 @@ class User extends Component if ($identity !== null && $identity->validateAuthKey($authKey)) { if ($this->beforeLogin($identity, true)) { $this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0); + $ip = Yii::$app->getRequest()->getUserIP(); + Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__); $this->afterLogin($identity, true); } } elseif ($identity !== null) { @@ -225,6 +230,9 @@ class User extends Component $identity = $this->getIdentity(); if ($identity !== null && $this->beforeLogout($identity)) { $this->switchIdentity(null); + $id = $identity->getId(); + $ip = Yii::$app->getRequest()->getUserIP(); + Yii::info("User '$id' logged out from $ip.", __METHOD__); if ($destroySession) { Yii::$app->getSession()->destroy(); } From 202664f3a47fa8d650b924d1a2b4de7726860c51 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 20:33:05 -0500 Subject: [PATCH 36/79] Fixes #1176. --- framework/yii/validators/InlineValidator.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/framework/yii/validators/InlineValidator.php b/framework/yii/validators/InlineValidator.php index dabdb4a..febf8dc 100644 --- a/framework/yii/validators/InlineValidator.php +++ b/framework/yii/validators/InlineValidator.php @@ -26,8 +26,11 @@ class InlineValidator extends Validator { /** * @var string|\Closure an anonymous function or the name of a model class method that will be - * called to perform the actual validation. Note that if you use anonymous function, you cannot - * use `$this` in it unless you are using PHP 5.4 or above. + * called to perform the actual validation. The signature of the method should be like the following: + * + * ~~~ + * function foo($attribute, $params) + * ~~~ */ public $method; /** @@ -39,7 +42,7 @@ class InlineValidator extends Validator * The signature of the method should be like the following: * * ~~~ - * function foo($attribute) + * function foo($attribute, $params) * { * return "javascript"; * } @@ -93,7 +96,7 @@ class InlineValidator extends Validator if (is_string($method)) { $method = [$object, $method]; } - return call_user_func($method, $attribute); + return call_user_func($method, $attribute, $this->params); } else { return null; } From a7175bae31b25f58c50ba498f7539fb3b3a36779 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Mon, 11 Nov 2013 20:52:27 -0500 Subject: [PATCH 37/79] Refactored Session as suggested in #1172 --- framework/yii/web/Session.php | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index 48420bb..b3be392 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -80,13 +80,11 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co * @var string the name of the session variable that stores the flash message data. */ public $flashVar = '__flash'; - /** * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function * @see http://www.php.net/manual/en/function.session-set-cookie-params.php - * @see setCookieParams() */ - public $cookieParams = ['httpOnly' => true]; + private $_cookieParams = ['httpOnly' => true]; /** * Initializes the application component. @@ -137,7 +135,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co ); } - $this->setCookieParams($this->cookieParams); + $this->setCookieParamsInternal(); @session_start(); @@ -265,26 +263,36 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co $params['httpOnly'] = $params['httponly']; unset($params['httponly']); } - return $params; + return array_merge($params, $this->_cookieParams); } /** * Sets the session cookie parameters. - * The effect of this method only lasts for the duration of the script. - * Call this method before the session starts. + * The cookie parameters passed to this method will be merged with the result + * of `session_get_cookie_params()`. * @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httpOnly`. * @throws InvalidParamException if the parameters are incomplete. * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php */ - public function setCookieParams($value) + public function setCookieParams(array $value) + { + $this->_cookieParams = $value; + } + + /** + * Sets the session cookie parameters. + * This method is called by [[open()]] when it is about to open the session. + * @throws InvalidParamException if the parameters are incomplete. + * @see http://us2.php.net/manual/en/function.session-set-cookie-params.php + */ + private function setCookieParamsInternal() { $data = $this->getCookieParams(); extract($data); - extract($value); if (isset($lifetime, $path, $domain, $secure, $httpOnly)) { session_set_cookie_params($lifetime, $path, $domain, $secure, $httpOnly); } else { - throw new InvalidParamException('Please make sure these parameters are provided: lifetime, path, domain, secure and httpOnly.'); + throw new InvalidParamException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httpOnly.'); } } From f9db4fecb967001575f34ba79023217dd12c0926 Mon Sep 17 00:00:00 2001 From: Taras Gudz Date: Tue, 12 Nov 2013 02:29:21 +0000 Subject: [PATCH 38/79] Fixed phpDocs [skip ci] --- framework/yii/web/Request.php | 18 +++++++++--------- framework/yii/web/UploadedFile.php | 2 +- framework/yii/web/User.php | 8 ++++---- framework/yii/web/View.php | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 6bede5e..46b2d2d 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -116,8 +116,8 @@ class Request extends \yii\base\Request /** * @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE * request tunneled through POST. Default to '_method'. - * @see getMethod - * @see getRestParams + * @see getMethod() + * @see getRestParams() */ public $restVar = '_method'; @@ -242,7 +242,7 @@ class Request extends \yii\base\Request /** * Returns the request parameters for the RESTful request. * @return array the RESTful request parameters - * @see getMethod + * @see getMethod() */ public function getRestParams() { @@ -298,7 +298,7 @@ class Request extends \yii\base\Request * @param string $name the GET parameter name. If not specified, whole $_GET is returned. * @param mixed $defaultValue the default parameter value if the GET parameter does not exist. * @return mixed the GET parameter value - * @see getPost + * @see getPost() */ public function get($name = null, $defaultValue = null) { @@ -324,7 +324,7 @@ class Request extends \yii\base\Request * @param mixed $defaultValue the default parameter value if the POST parameter does not exist. * @property array the POST request parameter values * @return mixed the POST parameter value - * @see get + * @see get() */ public function getPost($name = null, $defaultValue = null) { @@ -387,7 +387,7 @@ class Request extends \yii\base\Request * By default this is determined based on the user request information. * You may explicitly specify it by setting the [[setHostInfo()|hostInfo]] property. * @return string schema and hostname part (with port number if needed) of the request URL (e.g. `http://www.yiiframework.com`) - * @see setHostInfo + * @see setHostInfo() */ public function getHostInfo() { @@ -426,7 +426,7 @@ class Request extends \yii\base\Request * This is similar to [[scriptUrl]] except that it does not include the script file name, * and the ending slashes are removed. * @return string the relative URL for the application - * @see setScriptUrl + * @see setScriptUrl() */ public function getBaseUrl() { @@ -743,7 +743,7 @@ class Request extends \yii\base\Request * Defaults to 80, or the port specified by the server if the current * request is insecure. * @return integer port number for insecure requests. - * @see setPort + * @see setPort() */ public function getPort() { @@ -774,7 +774,7 @@ class Request extends \yii\base\Request * Defaults to 443, or the port specified by the server if the current * request is secure. * @return integer port number for secure requests. - * @see setSecurePort + * @see setSecurePort() */ public function getSecurePort() { diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php index 3cb6813..1de4d46 100644 --- a/framework/yii/web/UploadedFile.php +++ b/framework/yii/web/UploadedFile.php @@ -74,7 +74,7 @@ class UploadedFile extends Object * For example, '[1]file' for tabular file uploading; and 'file[1]' for an element in a file array. * @return UploadedFile the instance of the uploaded file. * Null is returned if no file is uploaded for the specified model attribute. - * @see getInstanceByName + * @see getInstanceByName() */ public static function getInstance($model, $attribute) { diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index b77af2b..0b1cd94 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -129,8 +129,8 @@ class User extends Component * Returns the identity object associated with the currently logged user. * @return IdentityInterface the identity object associated with the currently logged user. * Null is returned if the user is not logged in (not authenticated). - * @see login - * @see logout + * @see login() + * @see logout() */ public function getIdentity() { @@ -266,7 +266,7 @@ class User extends Component * 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 + * @see loginRequired() */ public function getReturnUrl($defaultUrl = null) { @@ -413,7 +413,7 @@ class User extends Component * information in the cookie. * @param IdentityInterface $identity * @param integer $duration number of seconds that the user can remain in logged-in status. - * @see loginByCookie + * @see loginByCookie() */ protected function sendIdentityCookie($identity, $duration) { diff --git a/framework/yii/web/View.php b/framework/yii/web/View.php index ab78fc5..0c6d4d4 100644 --- a/framework/yii/web/View.php +++ b/framework/yii/web/View.php @@ -72,7 +72,7 @@ class View extends \yii\base\View /** * @var AssetBundle[] list of the registered asset bundles. The keys are the bundle names, and the values * are the registered [[AssetBundle]] objects. - * @see registerAssetBundle + * @see registerAssetBundle() */ public $assetBundles = []; /** @@ -81,32 +81,32 @@ class View extends \yii\base\View public $title; /** * @var array the registered meta tags. - * @see registerMetaTag + * @see registerMetaTag() */ public $metaTags; /** * @var array the registered link tags. - * @see registerLinkTag + * @see registerLinkTag() */ public $linkTags; /** * @var array the registered CSS code blocks. - * @see registerCss + * @see registerCss() */ public $css; /** * @var array the registered CSS files. - * @see registerCssFile + * @see registerCssFile() */ public $cssFiles; /** * @var array the registered JS code blocks - * @see registerJs + * @see registerJs() */ public $js; /** * @var array the registered JS files. - * @see registerJsFile + * @see registerJsFile() */ public $jsFiles; From 9b95a81b0c55c03eaf7854ef460249ea48a6f550 Mon Sep 17 00:00:00 2001 From: Taras Gudz Date: Tue, 12 Nov 2013 03:13:37 +0000 Subject: [PATCH 39/79] Fixed the rest phpDocs [skip ci] --- framework/yii/BaseYii.php | 16 ++++++++-------- framework/yii/base/Component.php | 16 ++++++++-------- framework/yii/base/Controller.php | 4 ++-- framework/yii/base/Model.php | 20 ++++++++++---------- framework/yii/base/Object.php | 12 ++++++------ framework/yii/base/View.php | 2 +- framework/yii/db/ActiveRecord.php | 12 ++++++------ framework/yii/db/Query.php | 4 ++-- framework/yii/db/Schema.php | 4 ++-- framework/yii/helpers/BaseHtml.php | 26 +++++++++++++------------- framework/yii/helpers/BaseInflector.php | 2 +- framework/yii/rbac/PhpManager.php | 8 ++++---- framework/yii/widgets/Menu.php | 6 +++--- 13 files changed, 66 insertions(+), 66 deletions(-) diff --git a/framework/yii/BaseYii.php b/framework/yii/BaseYii.php index 011a9c7..b32e8b2 100644 --- a/framework/yii/BaseYii.php +++ b/framework/yii/BaseYii.php @@ -64,7 +64,7 @@ class BaseYii * are the corresponding class file paths (or path aliases). This property mainly affects * how [[autoload()]] works. * @see import - * @see autoload + * @see autoload() */ public static $classMap = []; /** @@ -73,8 +73,8 @@ class BaseYii public static $app; /** * @var array registered path aliases - * @see getAlias - * @see setAlias + * @see getAlias() + * @see setAlias() */ public static $aliases = ['@yii' => __DIR__]; /** @@ -95,7 +95,7 @@ class BaseYii * ] * ~~~ * - * @see createObject + * @see createObject() */ public static $objectConfig = []; @@ -136,7 +136,7 @@ class BaseYii * If this is false and an invalid alias is given, false will be returned by this method. * @return string|boolean the path corresponding to the alias, false if the root alias is not previously registered. * @throws InvalidParamException if the alias is invalid while $throwException is true. - * @see setAlias + * @see setAlias() */ public static function getAlias($alias, $throwException = true) { @@ -219,7 +219,7 @@ class BaseYii * actual path first by calling [[getAlias()]]. * * @throws InvalidParamException if $path is an invalid alias. - * @see getAlias + * @see getAlias() */ public static function setAlias($alias, $path) { @@ -450,7 +450,7 @@ class BaseYii * ~~~ * @param string $token token for the code block * @param string $category the category of this log message - * @see endProfile + * @see endProfile() */ public static function beginProfile($token, $category = 'application') { @@ -462,7 +462,7 @@ class BaseYii * This has to be matched with a previous call to [[beginProfile]] with the same category name. * @param string $token token for the code block * @param string $category the category of this log message - * @see beginProfile + * @see beginProfile() */ public static function endProfile($token, $category = 'application') { diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index b67a09f..108f0f1 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -41,7 +41,7 @@ class Component extends Object * @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 + * @see __set() */ public function __get($name) { @@ -80,7 +80,7 @@ class Component extends Object * @param mixed $value the property value * @throws UnknownPropertyException if the property is not defined * @throws InvalidCallException if the property is read-only. - * @see __get + * @see __get() */ public function __set($name, $value) { @@ -225,8 +225,8 @@ class Component extends Object * @param boolean $checkVars whether to treat member variables as properties * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @return boolean whether the property is defined - * @see canGetProperty - * @see canSetProperty + * @see canGetProperty() + * @see canSetProperty() */ public function hasProperty($name, $checkVars = true, $checkBehaviors = true) { @@ -246,7 +246,7 @@ class Component extends Object * @param boolean $checkVars whether to treat member variables as properties * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @return boolean whether the property can be read - * @see canSetProperty + * @see canSetProperty() */ public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) { @@ -276,7 +276,7 @@ class Component extends Object * @param boolean $checkVars whether to treat member variables as properties * @param boolean $checkBehaviors whether to treat behaviors' properties as properties of this component * @return boolean whether the property can be written - * @see canGetProperty + * @see canGetProperty() */ public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) { @@ -492,7 +492,7 @@ class Component extends Object * - an object configuration array that will be passed to [[Yii::createObject()]] to create the behavior object. * * @return Behavior the behavior object - * @see detachBehavior + * @see detachBehavior() */ public function attachBehavior($name, $behavior) { @@ -505,7 +505,7 @@ class Component extends Object * Each behavior is indexed by its name and should be a [[Behavior]] object, * a string specifying the behavior class, or an configuration array for creating the behavior. * @param array $behaviors list of behaviors to be attached to the component - * @see attachBehavior + * @see attachBehavior() */ public function attachBehaviors($behaviors) { diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index d2f491c..b9840b4 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -111,7 +111,7 @@ class Controller extends Component implements ViewContextInterface * @param array $params the parameters (name-value pairs) to be passed to the action. * @return mixed the result of the action * @throws InvalidRouteException if the requested action ID cannot be resolved into an action successfully. - * @see createAction + * @see createAction() */ public function runAction($id, $params = []) { @@ -149,7 +149,7 @@ class Controller extends Component implements ViewContextInterface * @param string $route the route to be handled, e.g., 'view', 'comment/view', '/admin/comment/view'. * @param array $params the parameters to be passed to the action. * @return mixed the result of the action - * @see runAction + * @see runAction() * @see forward */ public function run($route, $params = []) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index 99c1df9..bb98a01 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -144,7 +144,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * merge the parent rules with child rules using functions such as `array_merge()`. * * @return array validation rules - * @see scenarios + * @see scenarios() */ public function rules() { @@ -255,7 +255,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * merge the parent labels with child labels using functions such as `array_merge()`. * * @return array attribute labels (name => label) - * @see generateAttributeLabel + * @see generateAttributeLabel() */ public function attributeLabels() { @@ -444,8 +444,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * Returns the text label for the specified attribute. * @param string $attribute the attribute name * @return string the attribute label - * @see generateAttributeLabel - * @see attributeLabels + * @see generateAttributeLabel() + * @see attributeLabels() */ public function getAttributeLabel($attribute) { @@ -483,8 +483,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * ] * ~~~ * - * @see getFirstErrors - * @see getFirstError + * @see getFirstErrors() + * @see getFirstError() */ public function getErrors($attribute = null) { @@ -498,8 +498,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 + * @see getErrors() + * @see getFirstError() */ public function getFirstErrors() { @@ -520,8 +520,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * Returns the first error of the specified attribute. * @param string $attribute attribute name. * @return string the error message. Null is returned if no error. - * @see getErrors - * @see getFirstErrors + * @see getErrors() + * @see getFirstErrors() */ public function getFirstError($attribute) { diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index f0bd92b..47c34e0 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -64,7 +64,7 @@ class Object implements Arrayable * @return mixed the property value * @throws UnknownPropertyException if the property is not defined * @throws InvalidCallException if the property is write-only - * @see __set + * @see __set() */ public function __get($name) { @@ -87,7 +87,7 @@ class Object implements Arrayable * @param mixed $value the property value * @throws UnknownPropertyException if the property is not defined * @throws InvalidCallException if the property is read-only - * @see __get + * @see __get() */ public function __set($name, $value) { @@ -168,8 +168,8 @@ class Object implements Arrayable * @param string $name the property name * @param boolean $checkVars whether to treat member variables as properties * @return boolean whether the property is defined - * @see canGetProperty - * @see canSetProperty + * @see canGetProperty() + * @see canSetProperty() */ public function hasProperty($name, $checkVars = true) { @@ -187,7 +187,7 @@ class Object implements Arrayable * @param string $name the property name * @param boolean $checkVars whether to treat member variables as properties * @return boolean whether the property can be read - * @see canSetProperty + * @see canSetProperty() */ public function canGetProperty($name, $checkVars = true) { @@ -205,7 +205,7 @@ class Object implements Arrayable * @param string $name the property name * @param boolean $checkVars whether to treat member variables as properties * @return boolean whether the property can be written - * @see canGetProperty + * @see canGetProperty() */ public function canSetProperty($name, $checkVars = true) { diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index c22b3c0..7b789d1 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -123,7 +123,7 @@ class View extends Component * existing [[context]] will be used. * @return string the rendering result * @throws InvalidParamException if the view cannot be resolved or the view file does not exist. - * @see renderFile + * @see renderFile() */ public function render($view, $params = [], $context = null) { diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 380eb03..74bc637 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -372,7 +372,7 @@ class ActiveRecord extends Model * This method is overridden so that attributes and related objects can be accessed like properties. * @param string $name property name * @return mixed property value - * @see getAttribute + * @see getAttribute() */ public function __get($name) { @@ -576,7 +576,7 @@ class ActiveRecord extends Model * null will be returned. * @param string $name the attribute name * @return mixed the attribute value. Null if the attribute is not set or does not exist. - * @see hasAttribute + * @see hasAttribute() */ public function getAttribute($name) { @@ -588,7 +588,7 @@ class ActiveRecord extends Model * @param string $name the attribute name * @param mixed $value the attribute value. * @throws InvalidParamException if the named attribute does not exist. - * @see hasAttribute + * @see hasAttribute() */ public function setAttribute($name, $value) { @@ -625,7 +625,7 @@ class ActiveRecord extends Model * @param string $name the attribute name * @return mixed the old attribute value. Null if the attribute is not loaded before * or does not exist. - * @see hasAttribute + * @see hasAttribute() */ public function getOldAttribute($name) { @@ -637,7 +637,7 @@ class ActiveRecord extends Model * @param string $name the attribute name * @param mixed $value the old attribute value. * @throws InvalidParamException if the named attribute does not exist. - * @see hasAttribute + * @see hasAttribute() */ public function setOldAttribute($name, $value) { @@ -1030,7 +1030,7 @@ 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 + * @see getIsNewRecord() */ public function setIsNewRecord($value) { diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 0839849..142a11d 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -39,12 +39,12 @@ class Query extends Component { /** * Sort ascending - * @see orderBy + * @see orderBy() */ const SORT_ASC = false; /** * Sort descending - * @see orderBy + * @see orderBy() */ const SORT_DESC = true; diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index fbcec2c..41ededa 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -291,7 +291,7 @@ abstract class Schema extends Object * then this method will do nothing. * @param string $name table name * @return string the properly quoted table name - * @see quoteSimpleTableName + * @see quoteSimpleTableName() */ public function quoteTableName($name) { @@ -316,7 +316,7 @@ abstract class Schema extends Object * then this method will do nothing. * @param string $name column name * @return string the properly quoted column name - * @see quoteSimpleColumnName + * @see quoteSimpleColumnName() */ public function quoteColumnName($name) { diff --git a/framework/yii/helpers/BaseHtml.php b/framework/yii/helpers/BaseHtml.php index 9f3df0e..71ad9ea 100644 --- a/framework/yii/helpers/BaseHtml.php +++ b/framework/yii/helpers/BaseHtml.php @@ -86,7 +86,7 @@ class BaseHtml * @param boolean $doubleEncode whether to encode HTML entities in `$content`. If false, * HTML entities in `$content` will not be further encoded. * @return string the encoded content - * @see decode + * @see decode() * @see http://www.php.net/manual/en/function.htmlspecialchars.php */ public static function encode($content, $doubleEncode = true) @@ -99,7 +99,7 @@ class BaseHtml * This is the opposite of [[encode()]]. * @param string $content the content to be decoded * @return string the decoded content - * @see encode + * @see encode() * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php */ public static function decode($content) @@ -116,8 +116,8 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated HTML tag - * @see beginTag - * @see endTag + * @see beginTag() + * @see endTag() */ public static function tag($name, $content = '', $options = []) { @@ -132,8 +132,8 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated start tag - * @see endTag - * @see tag + * @see endTag() + * @see tag() */ public static function beginTag($name, $options = []) { @@ -144,8 +144,8 @@ class BaseHtml * Generates an end tag. * @param string $name the tag name * @return string the generated end tag - * @see beginTag - * @see tag + * @see beginTag() + * @see tag() */ public static function endTag($name) { @@ -187,7 +187,7 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated link tag - * @see url + * @see url() */ public static function cssFile($url, $options = []) { @@ -203,7 +203,7 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated script tag - * @see url + * @see url() */ public static function jsFile($url, $options = []) { @@ -222,7 +222,7 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated form start tag. - * @see endForm + * @see endForm() */ public static function beginForm($action = '', $method = 'post', $options = []) { @@ -271,7 +271,7 @@ class BaseHtml /** * Generates a form end tag. * @return string the generated tag - * @see beginForm + * @see beginForm() */ public static function endForm() { @@ -290,7 +290,7 @@ class BaseHtml * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]]. * If a value is null, the corresponding attribute will not be rendered. * @return string the generated hyperlink - * @see url + * @see url() */ public static function a($text, $url = null, $options = []) { diff --git a/framework/yii/helpers/BaseInflector.php b/framework/yii/helpers/BaseInflector.php index 8d5fe28..deb8239 100644 --- a/framework/yii/helpers/BaseInflector.php +++ b/framework/yii/helpers/BaseInflector.php @@ -328,7 +328,7 @@ class BaseInflector * Converts a word like "send_email" to "SendEmail". It * will remove non alphanumeric character from the word, so * "who's online" will be converted to "WhoSOnline" - * @see variablize + * @see variablize() * @param string $word the word to CamelCase * @return string */ diff --git a/framework/yii/rbac/PhpManager.php b/framework/yii/rbac/PhpManager.php index a91d9bd..7332da7 100644 --- a/framework/yii/rbac/PhpManager.php +++ b/framework/yii/rbac/PhpManager.php @@ -36,8 +36,8 @@ class PhpManager extends Manager * If not set, it will be using 'protected/data/rbac.php' as the data file. * Make sure this file is writable by the Web server process if the authorization * needs to be changed. - * @see loadFromFile - * @see saveToFile + * @see loadFromFile() + * @see saveToFile() */ public $authFile; @@ -517,7 +517,7 @@ class PhpManager extends Manager * Loads the authorization data from a PHP script file. * @param string $file the file path. * @return array the authorization data - * @see saveToFile + * @see saveToFile() */ protected function loadFromFile($file) { @@ -532,7 +532,7 @@ class PhpManager extends Manager * Saves the authorization data to a PHP script file. * @param array $data the authorization data * @param string $file the file path. - * @see loadFromFile + * @see loadFromFile() */ protected function saveToFile($data, $file) { diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php index 7ea7717..d5ff8ef 100644 --- a/framework/yii/widgets/Menu.php +++ b/framework/yii/widgets/Menu.php @@ -104,7 +104,7 @@ class Menu extends Widget /** * @var boolean whether to automatically activate items according to whether their route setting * matches the currently requested route. - * @see isItemActive + * @see isItemActive() */ public $activateItems = true; /** @@ -137,14 +137,14 @@ class Menu extends Widget * @var string the route used to determine if a menu item is active or not. * If not set, it will use the route of the current request. * @see params - * @see isItemActive + * @see isItemActive() */ public $route; /** * @var array the parameters used to determine if a menu item is active or not. * If not set, it will use `$_GET`. * @see route - * @see isItemActive + * @see isItemActive() */ public $params; From 2617dfbaf365d30282f942e8180d41cf912ad93d Mon Sep 17 00:00:00 2001 From: Taras Gudz Date: Tue, 12 Nov 2013 03:18:01 +0000 Subject: [PATCH 40/79] Removed links to non-existent things phpDocs [skip ci] --- framework/yii/BaseYii.php | 1 - framework/yii/base/Controller.php | 1 - 2 files changed, 2 deletions(-) diff --git a/framework/yii/BaseYii.php b/framework/yii/BaseYii.php index b32e8b2..427afff 100644 --- a/framework/yii/BaseYii.php +++ b/framework/yii/BaseYii.php @@ -63,7 +63,6 @@ class BaseYii * The array keys are the class names (without leading backslashes), and the array values * are the corresponding class file paths (or path aliases). This property mainly affects * how [[autoload()]] works. - * @see import * @see autoload() */ public static $classMap = []; diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index b9840b4..c8f2d48 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -150,7 +150,6 @@ class Controller extends Component implements ViewContextInterface * @param array $params the parameters to be passed to the action. * @return mixed the result of the action * @see runAction() - * @see forward */ public function run($route, $params = []) { From 03d70b6e7ed8d406f7eee5285654c453d0123ab1 Mon Sep 17 00:00:00 2001 From: Veaceslav Medvedev Date: Tue, 12 Nov 2013 05:21:06 +0200 Subject: [PATCH 41/79] Update main.php --- apps/advanced/backend/config/main.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/advanced/backend/config/main.php b/apps/advanced/backend/config/main.php index 07658dd..f2745e2 100644 --- a/apps/advanced/backend/config/main.php +++ b/apps/advanced/backend/config/main.php @@ -1,5 +1,5 @@ 'app-backend', 'basePath' => dirname(__DIR__), - 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', + 'vendorPath' => $rootDir . '/vendor', 'preload' => ['log'], 'controllerNamespace' => 'backend\controllers', 'modules' => [], - 'extensions' => require(__DIR__ . '/../../vendor/yiisoft/extensions.php'), + 'extensions' => require($rootDir . '/vendor/yiisoft/extensions.php'), 'components' => [ 'request' => [ 'enableCsrfValidation' => true, From b0b1c27c859d2c69ffa755cd97b5a39fecde8202 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 06:21:04 +0200 Subject: [PATCH 42/79] Fix phpDoc var tag format --- extensions/gii/views/default/index.php | 8 ++++---- extensions/gii/views/default/view/files.php | 4 ++-- extensions/gii/views/layouts/generator.php | 8 ++++---- extensions/gii/views/layouts/main.php | 4 ++-- extensions/swiftmailer/Message.php | 2 +- framework/yii/BaseYii.php | 2 +- framework/yii/base/Application.php | 2 -- framework/yii/base/Model.php | 3 +-- framework/yii/base/Module.php | 2 +- framework/yii/base/View.php | 2 +- .../yii/console/controllers/CacheController.php | 2 +- framework/yii/db/ActiveQuery.php | 6 +++--- framework/yii/db/ActiveRecord.php | 10 +++++----- framework/yii/db/ActiveRelation.php | 8 ++++---- framework/yii/db/Command.php | 2 +- framework/yii/db/Schema.php | 4 ++-- framework/yii/log/Target.php | 2 +- framework/yii/rbac/PhpManager.php | 20 ++++++++++---------- framework/yii/requirements/requirements.php | 2 +- framework/yii/requirements/views/console/index.php | 6 +++--- framework/yii/requirements/views/web/index.php | 6 +++--- framework/yii/validators/ExistValidator.php | 4 ++-- framework/yii/validators/UniqueValidator.php | 2 +- framework/yii/web/AccessControl.php | 2 +- framework/yii/web/UrlManager.php | 6 +++--- framework/yii/web/User.php | 4 ++-- framework/yii/widgets/ActiveForm.php | 2 +- tests/unit/data/views/layout.php | 6 +++--- tests/unit/data/views/rawlayout.php | 4 ++-- tests/unit/extensions/swiftmailer/MessageTest.php | 2 +- tests/unit/framework/db/ActiveRecordTest.php | 6 +++--- tests/unit/framework/validators/ValidatorTest.php | 4 ++-- 32 files changed, 72 insertions(+), 75 deletions(-) diff --git a/extensions/gii/views/default/index.php b/extensions/gii/views/default/index.php index f6d6855..2e48816 100644 --- a/extensions/gii/views/default/index.php +++ b/extensions/gii/views/default/index.php @@ -2,10 +2,10 @@ use yii\helpers\Html; /** - * @var $this \yii\web\View - * @var $content string - * @var yii\gii\Generator[] $generators - * @var yii\gii\Generator $activeGenerator + * @var \yii\web\View $this + * @var \yii\gii\Generator[] $generators + * @var \yii\gii\Generator $activeGenerator + * @var string $content */ $generators = Yii::$app->controller->module->generators; $activeGenerator = Yii::$app->controller->generator; diff --git a/extensions/gii/views/default/view/files.php b/extensions/gii/views/default/view/files.php index 3da2d97..d8d5f0f 100644 --- a/extensions/gii/views/default/view/files.php +++ b/extensions/gii/views/default/view/files.php @@ -4,8 +4,8 @@ use yii\helpers\Html; use yii\gii\CodeFile; /** - * @var $this \yii\web\View - * @var $generator \yii\gii\Generator + * @var \yii\web\View $this + * @var \yii\gii\Generator $generator * @var CodeFile[] $files * @var array $answers */ diff --git a/extensions/gii/views/layouts/generator.php b/extensions/gii/views/layouts/generator.php index c03f09a..245cd29 100644 --- a/extensions/gii/views/layouts/generator.php +++ b/extensions/gii/views/layouts/generator.php @@ -2,10 +2,10 @@ use yii\helpers\Html; /** - * @var $this \yii\web\View - * @var $content string - * @var yii\gii\Generator[] $generators - * @var yii\gii\Generator $activeGenerator + * @var \yii\web\View $this + * @var \yii\gii\Generator[] $generators + * @var \yii\gii\Generator $activeGenerator + * @var string $content */ $generators = Yii::$app->controller->module->generators; $activeGenerator = Yii::$app->controller->generator; diff --git a/extensions/gii/views/layouts/main.php b/extensions/gii/views/layouts/main.php index 69afeb2..baea45e 100644 --- a/extensions/gii/views/layouts/main.php +++ b/extensions/gii/views/layouts/main.php @@ -4,8 +4,8 @@ use yii\bootstrap\Nav; use yii\helpers\Html; /** - * @var $this \yii\web\View - * @var $content string + * @var \yii\web\View $this + * @var string $content */ $asset = yii\gii\GiiAsset::register($this); ?> diff --git a/extensions/swiftmailer/Message.php b/extensions/swiftmailer/Message.php index bc9c14a..b0ebd63 100644 --- a/extensions/swiftmailer/Message.php +++ b/extensions/swiftmailer/Message.php @@ -192,7 +192,7 @@ class Message extends BaseMessage $partFound = false; foreach ($parts as $key => $part) { if (!($part instanceof \Swift_Mime_Attachment)) { - /* @var $part \Swift_Mime_MimePart */ + /* @var \Swift_Mime_MimePart $part */ if ($part->getContentType() == $contentType) { unset($parts[$key]); $partFound = true; diff --git a/framework/yii/BaseYii.php b/framework/yii/BaseYii.php index 427afff..357a1e7 100644 --- a/framework/yii/BaseYii.php +++ b/framework/yii/BaseYii.php @@ -367,7 +367,7 @@ class BaseYii } if (($n = func_num_args()) > 1) { - /** @var $reflection \ReflectionClass */ + /** @var \ReflectionClass $reflection */ if (isset($reflections[$class])) { $reflection = $reflections[$class]; } else { diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 70eaca3..497cb2f 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -616,10 +616,8 @@ abstract class Application extends Module { $category = get_class($exception); if ($exception instanceof HttpException) { - /** @var $exception HttpException */ $category .= '\\' . $exception->statusCode; } elseif ($exception instanceof \ErrorException) { - /** @var $exception \ErrorException */ $category .= '\\' . $exception->getSeverity(); } Yii::error((string)$exception, $category); diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index bb98a01..fa05576 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -349,7 +349,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * $model->validators[] = $newValidator; * ~~~ * - * @return ArrayObject all the validators declared in the model. + * @return ArrayObject|\yii\validators\Validator[] all the validators declared in the model. */ public function getValidators() { @@ -369,7 +369,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess { $validators = []; $scenario = $this->getScenario(); - /** @var $validator Validator */ foreach ($this->getValidators() as $validator) { if ($validator->isActive($scenario) && ($attribute === null || in_array($attribute, $validator->attributes, true))) { $validators[] = $validator; diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php index b3e28f6..1e5302c 100644 --- a/framework/yii/base/Module.php +++ b/framework/yii/base/Module.php @@ -578,7 +578,7 @@ abstract class Module extends Component { $parts = $this->createController($route); if (is_array($parts)) { - /** @var $controller Controller */ + /** @var Controller $controller */ list($controller, $actionID) = $parts; $oldController = Yii::$app->controller; Yii::$app->controller = $controller; diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php index 7b789d1..61d9373 100644 --- a/framework/yii/base/View.php +++ b/framework/yii/base/View.php @@ -410,7 +410,7 @@ class View extends Component { $properties['id'] = $id; $properties['view'] = $this; - /** @var $cache FragmentCache */ + /** @var FragmentCache $cache */ $cache = FragmentCache::begin($properties); if ($cache->getCachedContent() !== false) { $this->endCache(); diff --git a/framework/yii/console/controllers/CacheController.php b/framework/yii/console/controllers/CacheController.php index 1822b73..43932d1 100644 --- a/framework/yii/console/controllers/CacheController.php +++ b/framework/yii/console/controllers/CacheController.php @@ -52,7 +52,7 @@ class CacheController extends Controller */ public function actionFlush($component = 'cache') { - /** @var $cache Cache */ + /** @var Cache $cache */ $cache = Yii::$app->getComponent($component); if (!$cache || !$cache instanceof Cache) { throw new Exception('Application component "'.$component.'" is not defined or not a cache.'); diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 015ce93..608ff5c 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -122,7 +122,7 @@ class ActiveQuery extends Query if ($this->asArray) { $model = $row; } else { - /** @var $class ActiveRecord */ + /** @var ActiveRecord $class */ $class = $this->modelClass; $model = $class::create($row); } @@ -145,7 +145,7 @@ class ActiveQuery extends Query */ public function createCommand($db = null) { - /** @var $modelClass ActiveRecord */ + /** @var ActiveRecord $modelClass */ $modelClass = $this->modelClass; if ($db === null) { $db = $modelClass::getDb(); @@ -253,7 +253,7 @@ class ActiveQuery extends Query $models[$key] = $row; } } else { - /** @var $class ActiveRecord */ + /** @var ActiveRecord $class */ $class = $this->modelClass; if ($this->indexBy === null) { foreach ($rows as $row) { diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 74bc637..00a99c1 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -1321,9 +1321,9 @@ class ActiveRecord extends Model throw new InvalidCallException('Unable to link models: both models must NOT be newly created.'); } if (is_array($relation->via)) { - /** @var $viaRelation ActiveRelation */ + /** @var ActiveRelation $viaRelation */ list($viaName, $viaRelation) = $relation->via; - /** @var $viaClass ActiveRecord */ + /** @var ActiveRecord $viaClass */ $viaClass = $viaRelation->modelClass; $viaTable = $viaClass::tableName(); // unset $viaName so that it can be reloaded to reflect the change @@ -1396,9 +1396,9 @@ class ActiveRecord extends Model if ($relation->via !== null) { if (is_array($relation->via)) { - /** @var $viaRelation ActiveRelation */ + /** @var ActiveRelation $viaRelation */ list($viaName, $viaRelation) = $relation->via; - /** @var $viaClass ActiveRecord */ + /** @var ActiveRecord $viaClass */ $viaClass = $viaRelation->modelClass; $viaTable = $viaClass::tableName(); unset($this->_related[$viaName]); @@ -1444,7 +1444,7 @@ class ActiveRecord extends Model if (!$relation->multiple) { unset($this->_related[$name]); } elseif (isset($this->_related[$name])) { - /** @var $b ActiveRecord */ + /** @var ActiveRecord $b */ foreach ($this->_related[$name] as $a => $b) { if ($model->getPrimaryKey() == $b->getPrimaryKey()) { unset($this->_related[$name][$a]); diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 1a7541a..ee4fb96 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -120,7 +120,7 @@ class ActiveRelation extends ActiveQuery $this->filterByModels($viaModels); } elseif (is_array($this->via)) { // via relation - /** @var $viaQuery ActiveRelation */ + /** @var ActiveRelation $viaQuery */ list($viaName, $viaQuery) = $this->via; if ($viaQuery->multiple) { $viaModels = $viaQuery->all(); @@ -154,13 +154,13 @@ class ActiveRelation extends ActiveQuery if ($this->via instanceof self) { // via pivot table - /** @var $viaQuery ActiveRelation */ + /** @var ActiveRelation $viaQuery */ $viaQuery = $this->via; $viaModels = $viaQuery->findPivotRows($primaryModels); $this->filterByModels($viaModels); } elseif (is_array($this->via)) { // via relation - /** @var $viaQuery ActiveRelation */ + /** @var ActiveRelation $viaQuery */ list($viaName, $viaQuery) = $this->via; $viaQuery->primaryModel = null; $viaModels = $viaQuery->findWith($viaName, $primaryModels); @@ -306,7 +306,7 @@ class ActiveRelation extends ActiveQuery return []; } $this->filterByModels($primaryModels); - /** @var $primaryModel ActiveRecord */ + /** @var ActiveRecord $primaryModel */ $primaryModel = reset($primaryModels); $db = $primaryModel->getDb(); list ($sql, $params) = $db->getQueryBuilder()->build($this); diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 4dffb01..6ed0d9c 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -366,7 +366,7 @@ class Command extends \yii\base\Component Yii::info($rawSql, __METHOD__); - /** @var $cache \yii\caching\Cache */ + /** @var \yii\caching\Cache $cache */ if ($db->enableQueryCache && $method !== '') { $cache = is_string($db->queryCache) ? Yii::$app->getComponent($db->queryCache) : $db->queryCache; } diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 41ededa..f2ae94c 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -92,7 +92,7 @@ abstract class Schema extends Object $realName = $this->getRawTableName($name); if ($db->enableSchemaCache && !in_array($name, $db->schemaCacheExclude, true)) { - /** @var $cache Cache */ + /** @var Cache $cache */ $cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache; if ($cache instanceof Cache) { $key = $this->getCacheKey($name); @@ -215,7 +215,7 @@ abstract class Schema extends Object */ public function refresh() { - /** @var $cache Cache */ + /** @var Cache $cache */ $cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache; if ($this->db->enableSchemaCache && $cache instanceof Cache) { GroupDependency::invalidate($cache, $this->getCacheGroup()); diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php index cd1256a..c9d5d97 100644 --- a/framework/yii/log/Target.php +++ b/framework/yii/log/Target.php @@ -112,7 +112,7 @@ abstract class Target extends Component { $context = []; if ($this->logUser && ($user = Yii::$app->getComponent('user', false)) !== null) { - /** @var $user \yii\web\User */ + /** @var \yii\web\User $user */ $context[] = 'User: ' . $user->getId(); } diff --git a/framework/yii/rbac/PhpManager.php b/framework/yii/rbac/PhpManager.php index 7332da7..57ede09 100644 --- a/framework/yii/rbac/PhpManager.php +++ b/framework/yii/rbac/PhpManager.php @@ -74,7 +74,7 @@ class PhpManager extends Manager if (!isset($this->_items[$itemName])) { return false; } - /** @var $item Item */ + /** @var Item $item */ $item = $this->_items[$itemName]; Yii::trace('Checking permission: ' . $item->getName(), __METHOD__); if (!isset($params['userId'])) { @@ -85,7 +85,7 @@ class PhpManager extends Manager return true; } if (isset($this->_assignments[$userId][$itemName])) { - /** @var $assignment Assignment */ + /** @var Assignment $assignment */ $assignment = $this->_assignments[$userId][$itemName]; if ($this->executeBizRule($assignment->bizRule, $params, $assignment->data)) { return true; @@ -113,9 +113,9 @@ class PhpManager extends Manager if (!isset($this->_items[$childName], $this->_items[$itemName])) { throw new Exception("Either '$itemName' or '$childName' does not exist."); } - /** @var $child Item */ + /** @var Item $child */ $child = $this->_items[$childName]; - /** @var $item Item */ + /** @var Item $item */ $item = $this->_items[$itemName]; $this->checkItemChildType($item->type, $child->type); if ($this->detectLoop($itemName, $childName)) { @@ -270,14 +270,14 @@ class PhpManager extends Manager $items = []; if ($userId === null) { foreach ($this->_items as $name => $item) { - /** @var $item Item */ + /** @var Item $item */ if ($item->type == $type) { $items[$name] = $item; } } } elseif (isset($this->_assignments[$userId])) { foreach ($this->_assignments[$userId] as $assignment) { - /** @var $assignment Assignment */ + /** @var Assignment $assignment */ $name = $assignment->itemName; if (isset($this->_items[$name]) && ($type === null || $this->_items[$name]->type == $type)) { $items[$name] = $this->_items[$name]; @@ -400,7 +400,7 @@ class PhpManager extends Manager { $items = []; foreach ($this->_items as $name => $item) { - /** @var $item Item */ + /** @var Item $item */ $items[$name] = [ 'type' => $item->type, 'description' => $item->description, @@ -409,7 +409,7 @@ class PhpManager extends Manager ]; if (isset($this->_children[$name])) { foreach ($this->_children[$name] as $child) { - /** @var $child Item */ + /** @var Item $child */ $items[$name]['children'][] = $child->getName(); } } @@ -417,7 +417,7 @@ class PhpManager extends Manager foreach ($this->_assignments as $userId => $assignments) { foreach ($assignments as $name => $assignment) { - /** @var $assignment Assignment */ + /** @var Assignment $assignment */ if (isset($items[$name])) { $items[$name]['assignments'][$userId] = [ 'bizRule' => $assignment->bizRule, @@ -505,7 +505,7 @@ class PhpManager extends Manager return false; } foreach ($this->_children[$childName] as $child) { - /** @var $child Item */ + /** @var Item $child */ if ($this->detectLoop($itemName, $child->getName())) { return true; } diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php index f70f414..34b556e 100644 --- a/framework/yii/requirements/requirements.php +++ b/framework/yii/requirements/requirements.php @@ -3,7 +3,7 @@ * These are the Yii core requirements for the [[YiiRequirementChecker]] instance. * These requirements are mandatory for any Yii application. * - * @var $this YiiRequirementChecker + * @var YiiRequirementChecker $this */ return array( array( diff --git a/framework/yii/requirements/views/console/index.php b/framework/yii/requirements/views/console/index.php index 6935107..1d87fe9 100644 --- a/framework/yii/requirements/views/console/index.php +++ b/framework/yii/requirements/views/console/index.php @@ -1,7 +1,7 @@ diff --git a/framework/yii/validators/ExistValidator.php b/framework/yii/validators/ExistValidator.php index 2746b06..ba3f332 100644 --- a/framework/yii/validators/ExistValidator.php +++ b/framework/yii/validators/ExistValidator.php @@ -65,7 +65,7 @@ class ExistValidator extends Validator return; } - /** @var $className \yii\db\ActiveRecord */ + /** @var \yii\db\ActiveRecord $className */ $className = $this->className === null ? get_class($object) : $this->className; $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; $query = $className::find(); @@ -92,7 +92,7 @@ class ExistValidator extends Validator if ($this->attributeName === null) { throw new InvalidConfigException('The "attributeName" property must be set.'); } - /** @var $className \yii\db\ActiveRecord */ + /** @var \yii\db\ActiveRecord $className */ $className = $this->className; $query = $className::find(); $query->where([$this->attributeName => $value]); diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php index 334d057..7006cc4 100644 --- a/framework/yii/validators/UniqueValidator.php +++ b/framework/yii/validators/UniqueValidator.php @@ -60,7 +60,7 @@ class UniqueValidator extends Validator return; } - /** @var $className \yii\db\ActiveRecord */ + /** @var \yii\db\ActiveRecord $className */ $className = $this->className === null ? get_class($object) : $this->className; $attributeName = $this->attributeName === null ? $attribute : $this->attributeName; diff --git a/framework/yii/web/AccessControl.php b/framework/yii/web/AccessControl.php index d11f59c..549f087 100644 --- a/framework/yii/web/AccessControl.php +++ b/framework/yii/web/AccessControl.php @@ -102,7 +102,7 @@ class AccessControl extends ActionFilter { $user = Yii::$app->getUser(); $request = Yii::$app->getRequest(); - /** @var $rule AccessRule */ + /** @var AccessRule $rule */ foreach ($this->rules as $rule) { if ($allow = $rule->allows($action, $user, $request)) { return true; diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 42b12ff..0626fff 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -169,7 +169,7 @@ class UrlManager extends Component { if ($this->enablePrettyUrl) { $pathInfo = $request->getPathInfo(); - /** @var $rule UrlRule */ + /** @var UrlRule $rule */ foreach ($this->rules as $rule) { if (($result = $rule->parseRequest($this, $request)) !== false) { Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__); @@ -224,7 +224,7 @@ class UrlManager extends Component $baseUrl = $this->getBaseUrl(); if ($this->enablePrettyUrl) { - /** @var $rule UrlRule */ + /** @var UrlRule $rule */ foreach ($this->rules as $rule) { if (($url = $rule->createUrl($this, $route, $params)) !== false) { if ($rule->host !== null) { @@ -282,7 +282,7 @@ class UrlManager extends Component public function getBaseUrl() { if ($this->_baseUrl === null) { - /** @var $request \yii\web\Request */ + /** @var \yii\web\Request $request */ $request = Yii::$app->getRequest(); $this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl(); } diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index 0b1cd94..b43d038 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -139,7 +139,7 @@ class User extends Component if ($id === null) { $this->_identity = null; } else { - /** @var $class IdentityInterface */ + /** @var IdentityInterface $class */ $class = $this->identityClass; $this->_identity = $class::findIdentity($id); } @@ -202,7 +202,7 @@ class User extends Component $data = json_decode($value, true); if (count($data) === 3 && isset($data[0], $data[1], $data[2])) { list ($id, $authKey, $duration) = $data; - /** @var $class IdentityInterface */ + /** @var IdentityInterface $class */ $class = $this->identityClass; $identity = $class::findIdentity($id); if ($identity !== null && $identity->validateAuthKey($authKey)) { diff --git a/framework/yii/widgets/ActiveForm.php b/framework/yii/widgets/ActiveForm.php index c018011..b218a2e 100644 --- a/framework/yii/widgets/ActiveForm.php +++ b/framework/yii/widgets/ActiveForm.php @@ -220,7 +220,7 @@ class ActiveForm extends Widget $lines = []; foreach ($models as $model) { - /** @var $model Model */ + /** @var Model $model */ foreach ($model->getFirstErrors() as $error) { $lines[] = Html::encode($error); } diff --git a/tests/unit/data/views/layout.php b/tests/unit/data/views/layout.php index ed7dc27..97a0888 100644 --- a/tests/unit/data/views/layout.php +++ b/tests/unit/data/views/layout.php @@ -1,7 +1,7 @@ beginPage(); ?> @@ -19,4 +19,4 @@ endBody(); ?> -endPage(); ?> \ No newline at end of file +endPage(); ?> diff --git a/tests/unit/data/views/rawlayout.php b/tests/unit/data/views/rawlayout.php index 68cea54..aaa489f 100644 --- a/tests/unit/data/views/rawlayout.php +++ b/tests/unit/data/views/rawlayout.php @@ -1,5 +1,5 @@ beginPage(); ?>1head(); ?>2beginBody(); ?>3endBody(); ?>4endPage(); ?> \ No newline at end of file +?>beginPage(); ?>1head(); ?>2beginBody(); ?>3endBody(); ?>4endPage(); ?> diff --git a/tests/unit/extensions/swiftmailer/MessageTest.php b/tests/unit/extensions/swiftmailer/MessageTest.php index bbdb476..6309f15 100644 --- a/tests/unit/extensions/swiftmailer/MessageTest.php +++ b/tests/unit/extensions/swiftmailer/MessageTest.php @@ -306,7 +306,7 @@ class MessageTest extends VendorTestCase $htmlPresent = false; foreach ($messageParts as $part) { if (!($part instanceof \Swift_Mime_Attachment)) { - /* @var $part \Swift_Mime_MimePart */ + /* @var \Swift_Mime_MimePart $part */ if ($part->getContentType() == 'text/plain') { $textPresent = true; } diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index a86c084..d0d2f12 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -117,7 +117,7 @@ class ActiveRecordTest extends DatabaseTestCase public function testFindLazy() { - /** @var $customer Customer */ + /** @var Customer $customer */ $customer = Customer::find(2); $orders = $customer->orders; $this->assertEquals(2, count($orders)); @@ -137,7 +137,7 @@ class ActiveRecordTest extends DatabaseTestCase public function testFindLazyVia() { - /** @var $order Order */ + /** @var Order $order */ $order = Order::find(1); $this->assertEquals(1, $order->id); $this->assertEquals(2, count($order->items)); @@ -162,7 +162,7 @@ class ActiveRecordTest extends DatabaseTestCase public function testFindLazyViaTable() { - /** @var $order Order */ + /** @var Order $order */ $order = Order::find(1); $this->assertEquals(1, $order->id); $this->assertEquals(2, count($order->books)); diff --git a/tests/unit/framework/validators/ValidatorTest.php b/tests/unit/framework/validators/ValidatorTest.php index fc69c2f..b248a9b 100644 --- a/tests/unit/framework/validators/ValidatorTest.php +++ b/tests/unit/framework/validators/ValidatorTest.php @@ -30,7 +30,7 @@ class ValidatorTest extends TestCase public function testCreateValidator() { $model = FakedValidationModel::createWithAttributes(['attr_test1' => 'abc', 'attr_test2' => '2013']); - /** @var $numberVal NumberValidator */ + /** @var NumberValidator $numberVal */ $numberVal = TestValidator::createValidator('number', $model, ['attr_test1']); $this->assertInstanceOf(NumberValidator::className(), $numberVal); $numberVal = TestValidator::createValidator('integer', $model, ['attr_test2']); @@ -229,4 +229,4 @@ class ValidatorTest extends TestCase $errors = $m->getErrors('attr_msg_val'); $this->assertEquals('attr_msg_val::abc::param_value', $errors[0]); } -} \ No newline at end of file +} From 93dd629411fb725cb95bc29d04fc3288f6a7f061 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 06:24:07 +0200 Subject: [PATCH 43/79] Fix apps phpDoc and short tags --- apps/advanced/backend/views/layouts/main.php | 8 ++++---- apps/advanced/frontend/views/emails/passwordResetToken.php | 4 ++-- apps/advanced/frontend/views/layouts/main.php | 12 ++++++------ apps/advanced/frontend/views/site/login.php | 2 +- apps/basic/views/layouts/main.php | 13 +++++++------ 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/advanced/backend/views/layouts/main.php b/apps/advanced/backend/views/layouts/main.php index 0e9d501..0ef33d0 100644 --- a/apps/advanced/backend/views/layouts/main.php +++ b/apps/advanced/backend/views/layouts/main.php @@ -6,8 +6,8 @@ use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; /** - * @var $this \yii\web\View - * @var $content string + * @var \yii\web\View $this + * @var string $content */ AppAsset::register($this); ?> @@ -45,9 +45,9 @@ AppAsset::register($this); ?>
- isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], - ]); ?> + ]) ?>
diff --git a/apps/advanced/frontend/views/emails/passwordResetToken.php b/apps/advanced/frontend/views/emails/passwordResetToken.php index ac2155c..b617bd9 100644 --- a/apps/advanced/frontend/views/emails/passwordResetToken.php +++ b/apps/advanced/frontend/views/emails/passwordResetToken.php @@ -9,8 +9,8 @@ use yii\helpers\Html; $resetLink = Yii::$app->urlManager->createAbsoluteUrl('site/reset-password', ['token' => $user->password_reset_token]); ?> -Hello username)?>, +Hello username) ?>, Follow the link below to reset your password: - + diff --git a/apps/advanced/frontend/views/layouts/main.php b/apps/advanced/frontend/views/layouts/main.php index 5da179c..d00b837 100644 --- a/apps/advanced/frontend/views/layouts/main.php +++ b/apps/advanced/frontend/views/layouts/main.php @@ -1,14 +1,14 @@ @@ -49,10 +49,10 @@ AppAsset::register($this); ?>
- isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], - ]); ?> - + ]) ?> +
diff --git a/apps/advanced/frontend/views/site/login.php b/apps/advanced/frontend/views/site/login.php index 74c5ffa..75dd4ca 100644 --- a/apps/advanced/frontend/views/site/login.php +++ b/apps/advanced/frontend/views/site/login.php @@ -22,7 +22,7 @@ $this->params['breadcrumbs'][] = $this->title; field($model, 'password')->passwordInput() ?> field($model, 'rememberMe')->checkbox() ?>
- If you forgot your password you can . + If you forgot your password you can .
'btn btn-primary']) ?> diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php index 278aa1d..3bc4d37 100644 --- a/apps/basic/views/layouts/main.php +++ b/apps/basic/views/layouts/main.php @@ -3,12 +3,13 @@ use yii\helpers\Html; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; +use app\config\AppAsset; /** - * @var $this \yii\web\View - * @var $content string + * @var \yii\web\View $this + * @var string $content */ -app\config\AppAsset::register($this); +AppAsset::register($this); ?> beginPage(); ?> @@ -36,7 +37,7 @@ app\config\AppAsset::register($this); ['label' => 'Contact', 'url' => ['/site/contact']], Yii::$app->user->isGuest ? ['label' => 'Login', 'url' => ['/site/login']] : - ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , + ['label' => 'Logout (' . Yii::$app->user->identity->username . ')' , 'url' => ['/site/logout'], 'linkOptions' => ['data-method' => 'post']], ], @@ -45,9 +46,9 @@ app\config\AppAsset::register($this); ?>
- isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], - ]); ?> + ]) ?>
From 1c2489a3dc03bda5180a99de61e74e1bf6fb96d2 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 08:02:48 +0200 Subject: [PATCH 44/79] Using only arrays in rules instead comma-separated string --- framework/yii/base/Model.php | 18 +++++++++--------- framework/yii/validators/Validator.php | 13 +++++-------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index fa05576..fd1bea0 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -91,19 +91,19 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * * ~~~ * [ - * 'attribute list', + * ['attribute1', 'attribute2'], * 'validator type', - * 'on' => 'scenario name', + * 'on' => ['scenario1', 'scenario2'], * ...other parameters... * ] * ~~~ * * where * - * - attribute list: required, specifies the attributes (separated by commas) to be validated; + * - attribute list: required, specifies the attributes array to be validated; * - validator type: required, specifies the validator to be used. It can be the name of a model * class method, the name of a built-in validator, or a validator class name (or its path alias). - * - on: optional, specifies the [[scenario|scenarios]] (separated by commas) when the validation + * - on: optional, specifies the [[scenario|scenarios]] array when the validation * rule can be applied. If this option is not set, the rule will apply to all scenarios. * - additional name-value pairs can be specified to initialize the corresponding validator properties. * Please refer to individual validator class API for possible properties. @@ -128,15 +128,15 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * ~~~ * [ * // built-in "required" validator - * ['username', 'required'], + * [['username'], 'required'], * // built-in "string" validator customized with "min" and "max" properties - * ['username', 'string', 'min' => 3, 'max' => 12], + * [['username'], 'string', 'min' => 3, 'max' => 12], * // built-in "compare" validator that is used in "register" scenario only - * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], + * [['password'], 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], * // an inline validator defined via the "authenticate()" method in the model class - * ['password', 'authenticate', 'on' => 'login'], + * [['password'], 'authenticate', 'on' => 'login'], * // a validator of class "DateRangeValidator" - * ['dateRange', 'DateRangeValidator'], + * [['dateRange'], 'DateRangeValidator'], * ]; * ~~~ * diff --git a/framework/yii/validators/Validator.php b/framework/yii/validators/Validator.php index 012f392..8503983 100644 --- a/framework/yii/validators/Validator.php +++ b/framework/yii/validators/Validator.php @@ -133,17 +133,14 @@ abstract class Validator extends Component */ public static function createValidator($type, $object, $attributes, $params = []) { - if (!is_array($attributes)) { - $attributes = preg_split('/[\s,]+/', $attributes, -1, PREG_SPLIT_NO_EMPTY); - } - $params['attributes'] = $attributes; + $params['attributes'] = (array) $attributes; - if (isset($params['on']) && !is_array($params['on'])) { - $params['on'] = preg_split('/[\s,]+/', $params['on'], -1, PREG_SPLIT_NO_EMPTY); + if (isset($params['on'])) { + $params['on'] = (array) $params['on']; } - if (isset($params['except']) && !is_array($params['except'])) { - $params['except'] = preg_split('/[\s,]+/', $params['except'], -1, PREG_SPLIT_NO_EMPTY); + if (isset($params['except'])) { + $params['except'] = (array) $params['except']; } if (method_exists($object, $type)) { From 016d796e95fb71773ef9b9db0ab39a4f3965e03d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 12 Nov 2013 11:07:14 +0100 Subject: [PATCH 45/79] advanced app: go back after login, not home --- apps/advanced/backend/controllers/SiteController.php | 2 +- apps/advanced/frontend/controllers/SiteController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/advanced/backend/controllers/SiteController.php b/apps/advanced/backend/controllers/SiteController.php index 6850fe9..ecf684c 100644 --- a/apps/advanced/backend/controllers/SiteController.php +++ b/apps/advanced/backend/controllers/SiteController.php @@ -50,7 +50,7 @@ class SiteController extends Controller $model = new LoginForm(); if ($model->load($_POST) && $model->login()) { - return $this->goHome(); + return $this->goBack(); } else { return $this->render('login', [ 'model' => $model, diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php index 02b9bca..184d16c 100644 --- a/apps/advanced/frontend/controllers/SiteController.php +++ b/apps/advanced/frontend/controllers/SiteController.php @@ -60,7 +60,7 @@ class SiteController extends Controller $model = new LoginForm(); if ($model->load($_POST) && $model->login()) { - return $this->goHome(); + return $this->goBack(); } else { return $this->render('login', [ 'model' => $model, From 5b489aaa497657d9d3ca48a72ca171335e0eaeb2 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 12 Nov 2013 12:45:05 +0100 Subject: [PATCH 46/79] let base mailer store files as .eml instead of .txt allows opening email for view in mail program directly --- framework/yii/mail/BaseMailer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php index d907918..90565c9 100644 --- a/framework/yii/mail/BaseMailer.php +++ b/framework/yii/mail/BaseMailer.php @@ -281,7 +281,7 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont $file = $path . '/' . call_user_func($this->fileTransportCallback, $this, $message); } else { $time = microtime(true); - $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.txt'; + $file = $path . '/' . date('Ymd-His-', $time) . sprintf('%04d', (int)(($time - (int)$time) * 10000)) . '-' . sprintf('%04d', mt_rand(0, 10000)) . '.eml'; } file_put_contents($file, $message->toString()); return true; From 8a4dffed9d2daf1878a3ffca7eb5c813a1ccd720 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 12 Nov 2013 14:22:07 +0100 Subject: [PATCH 47/79] fixed ActiveRelation::__clone() to work with via relation --- framework/yii/db/ActiveRelation.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index ee4fb96..91e21d8 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -55,9 +55,11 @@ class ActiveRelation extends ActiveQuery */ public function __clone() { + // make a clone of "via" object so that the same query object can be reused multiple times 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; + } elseif (is_array($this->via)) { + $this->via = [$this->via[0], clone $this->via[1]]; } } From b6424946e4ea51b146e532bbcb102468a1caa271 Mon Sep 17 00:00:00 2001 From: egorpromo Date: Tue, 12 Nov 2013 21:12:43 +0700 Subject: [PATCH 48/79] Little helpful description for Session::cookieParam --- framework/yii/web/Session.php | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index b3be392..86f3506 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -82,6 +82,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co public $flashVar = '__flash'; /** * @var array parameter-value pairs to override default session cookie parameters that are used for session_set_cookie_params() function + * Array may have the following possible keys: 'lifetime', 'path', 'domain', 'secure', 'httpOnly' * @see http://www.php.net/manual/en/function.session-set-cookie-params.php */ private $_cookieParams = ['httpOnly' => true]; From 3cdd78cafe89300b248b60fe3fdf6b4b8afadcc8 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 16:27:31 +0200 Subject: [PATCH 49/79] Fix rules generator --- extensions/gii/generators/model/Generator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/gii/generators/model/Generator.php b/extensions/gii/generators/model/Generator.php index 78c5130..5804d30 100644 --- a/extensions/gii/generators/model/Generator.php +++ b/extensions/gii/generators/model/Generator.php @@ -237,10 +237,10 @@ class Generator extends \yii\gii\Generator $rules = []; foreach ($types as $type => $columns) { - $rules[] = "['" . implode(', ', $columns) . "', '$type']"; + $rules[] = "[['" . implode(', ', $columns) . "'], '$type']"; } foreach ($lengths as $length => $columns) { - $rules[] = "['" . implode(', ', $columns) . "', 'string', 'max' => $length]"; + $rules[] = "[['" . implode(', ', $columns) . "'], 'string', 'max' => $length]"; } return $rules; From ecc557ef92f62d0200113ea6dd833550abf8a702 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 16:39:23 +0200 Subject: [PATCH 50/79] Refactor all models to new rules format --- apps/advanced/common/models/LoginForm.php | 6 +++--- apps/advanced/common/models/User.php | 24 +++++++++++----------- apps/advanced/frontend/models/ContactForm.php | 6 +++--- apps/basic/models/ContactForm.php | 6 +++--- apps/basic/models/LoginForm.php | 6 +++--- extensions/gii/Generator.php | 4 ++-- extensions/gii/generators/controller/Generator.php | 12 +++++------ extensions/gii/generators/crud/Generator.php | 22 ++++++++++---------- extensions/gii/generators/form/Generator.php | 16 +++++++-------- extensions/gii/generators/model/Generator.php | 22 ++++++++++---------- extensions/gii/generators/module/Generator.php | 10 ++++----- tests/unit/data/base/Singer.php | 6 +++--- .../validators/models/FakedValidationModel.php | 6 +++--- 13 files changed, 73 insertions(+), 73 deletions(-) diff --git a/apps/advanced/common/models/LoginForm.php b/apps/advanced/common/models/LoginForm.php index 339005b..795c378 100644 --- a/apps/advanced/common/models/LoginForm.php +++ b/apps/advanced/common/models/LoginForm.php @@ -21,11 +21,11 @@ class LoginForm extends Model { return [ // username and password are both required - ['username, password', 'required'], + [['username', 'password'], 'required'], // password is validated by validatePassword() - ['password', 'validatePassword'], + [['password'], 'validatePassword'], // rememberMe must be a boolean value - ['rememberMe', 'boolean'], + [['rememberMe'], 'boolean'], ]; } diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php index 17bd630..85e5539 100644 --- a/apps/advanced/common/models/User.php +++ b/apps/advanced/common/models/User.php @@ -104,18 +104,18 @@ class User extends ActiveRecord implements IdentityInterface public function rules() { return [ - ['username', 'filter', 'filter' => 'trim'], - ['username', 'required'], - ['username', 'string', 'min' => 2, 'max' => 255], - - ['email', 'filter', 'filter' => 'trim'], - ['email', 'required'], - ['email', 'email'], - ['email', 'unique', 'message' => 'This email address has already been taken.', 'on' => 'signup'], - ['email', 'exist', 'message' => 'There is no user with such email.', 'on' => 'requestPasswordResetToken'], - - ['password', 'required'], - ['password', 'string', 'min' => 6], + [['username'], 'filter', 'filter' => 'trim'], + [['username'], 'required'], + [['username'], 'string', 'min' => 2, 'max' => 255], + + [['email'], 'filter', 'filter' => 'trim'], + [['email'], 'required'], + [['email'], 'email'], + [['email'], 'unique', 'message' => 'This email address has already been taken.', 'on' => 'signup'], + [['email'], 'exist', 'message' => 'There is no user with such email.', 'on' => 'requestPasswordResetToken'], + + [['password'], 'required'], + [['password'], 'string', 'min' => 6], ]; } diff --git a/apps/advanced/frontend/models/ContactForm.php b/apps/advanced/frontend/models/ContactForm.php index a3c56b8..45b0da0 100644 --- a/apps/advanced/frontend/models/ContactForm.php +++ b/apps/advanced/frontend/models/ContactForm.php @@ -23,11 +23,11 @@ class ContactForm extends Model { return [ // name, email, subject and body are required - ['name, email, subject, body', 'required'], + [['name', 'email', 'subject', 'body'], 'required'], // email has to be a valid email address - ['email', 'email'], + [['email'], 'email'], // verifyCode needs to be entered correctly - ['verifyCode', 'captcha'], + [['verifyCode'], 'captcha'], ]; } diff --git a/apps/basic/models/ContactForm.php b/apps/basic/models/ContactForm.php index 58f8d26..dbdde8b 100644 --- a/apps/basic/models/ContactForm.php +++ b/apps/basic/models/ContactForm.php @@ -23,11 +23,11 @@ class ContactForm extends Model { return [ // name, email, subject and body are required - ['name, email, subject, body', 'required'], + [['name', 'email', 'subject', 'body'], 'required'], // email has to be a valid email address - ['email', 'email'], + [['email'], 'email'], // verifyCode needs to be entered correctly - ['verifyCode', 'captcha'], + [['verifyCode'], 'captcha'], ]; } diff --git a/apps/basic/models/LoginForm.php b/apps/basic/models/LoginForm.php index 339cf31..cdff17b 100644 --- a/apps/basic/models/LoginForm.php +++ b/apps/basic/models/LoginForm.php @@ -21,11 +21,11 @@ class LoginForm extends Model { return [ // username and password are both required - ['username, password', 'required'], + [['username', 'password'], 'required'], // password is validated by validatePassword() - ['password', 'validatePassword'], + [['password'], 'validatePassword'], // rememberMe must be a boolean value - ['rememberMe', 'boolean'], + [['rememberMe'], 'boolean'], ]; } diff --git a/extensions/gii/Generator.php b/extensions/gii/Generator.php index fb856d5..05c45a7 100644 --- a/extensions/gii/Generator.php +++ b/extensions/gii/Generator.php @@ -178,8 +178,8 @@ abstract class Generator extends Model public function rules() { return [ - ['template', 'required', 'message' => 'A code template must be selected.'], - ['template', 'validateTemplate'], + [['template'], 'required', 'message' => 'A code template must be selected.'], + [['template'], 'validateTemplate'], ]; } diff --git a/extensions/gii/generators/controller/Generator.php b/extensions/gii/generators/controller/Generator.php index f23bcd4..08b29d5 100644 --- a/extensions/gii/generators/controller/Generator.php +++ b/extensions/gii/generators/controller/Generator.php @@ -69,12 +69,12 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), [ - ['controller, actions, baseClass, ns', 'filter', 'filter' => 'trim'], - ['controller, baseClass', 'required'], - ['controller', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'], - ['actions', 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'], - ['baseClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], - ['ns', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['controller', 'actions', 'baseClass', 'ns'], 'filter', 'filter' => 'trim'], + [['controller', 'baseClass'], 'required'], + [['controller'], 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'], + [['actions'], 'match', 'pattern' => '/^[a-z\\-,\\s]*$/', 'message' => 'Only a-z, dashes (-), spaces and commas are allowed.'], + [['baseClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['ns'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], ]); } diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php index ea347e1..8ea1ff6 100644 --- a/extensions/gii/generators/crud/Generator.php +++ b/extensions/gii/generators/crud/Generator.php @@ -42,17 +42,17 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), [ - ['moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'], - ['modelClass, searchModelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'], - ['searchModelClass', 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'], - ['modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], - ['modelClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], - ['baseControllerClass', 'validateClass', 'params' => ['extends' => Controller::className()]], - ['controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'], - ['controllerClass, searchModelClass', 'validateNewClass'], - ['indexWidgetType', 'in', 'range' => ['grid', 'list']], - ['modelClass', 'validateModelClass'], - ['moduleID', 'validateModuleID'], + [['moduleID', 'controllerClass', 'modelClass', 'searchModelClass', 'baseControllerClass'], 'filter', 'filter' => 'trim'], + [['modelClass', 'searchModelClass', 'controllerClass', 'baseControllerClass', 'indexWidgetType'], 'required'], + [['searchModelClass'], 'compare', 'compareAttribute' => 'modelClass', 'operator' => '!==', 'message' => 'Search Model Class must not be equal to Model Class.'], + [['modelClass', 'controllerClass', 'baseControllerClass', 'searchModelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['modelClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], + [['baseControllerClass'], 'validateClass', 'params' => ['extends' => Controller::className()]], + [['controllerClass'], 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'], + [['controllerClass', 'searchModelClass'], 'validateNewClass'], + [['indexWidgetType'], 'in', 'range' => ['grid', 'list']], + [['modelClass'], 'validateModelClass'], + [['moduleID'], 'validateModuleID'], ]); } diff --git a/extensions/gii/generators/form/Generator.php b/extensions/gii/generators/form/Generator.php index 749f9d8..3bc0be6 100644 --- a/extensions/gii/generators/form/Generator.php +++ b/extensions/gii/generators/form/Generator.php @@ -60,14 +60,14 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), [ - ['modelClass, viewName, scenarioName, viewPath', 'filter', 'filter' => 'trim'], - ['modelClass, viewName, viewPath', 'required'], - ['modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], - ['modelClass', 'validateClass', 'params' => ['extends' => Model::className()]], - ['viewName', 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'], - ['viewPath', 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'], - ['viewPath', 'validateViewPath'], - ['scenarioName', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'], + [['modelClass', 'viewName', 'scenarioName', 'viewPath'], 'filter', 'filter' => 'trim'], + [['modelClass', 'viewName', 'viewPath'], 'required'], + [['modelClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['modelClass'], 'validateClass', 'params' => ['extends' => Model::className()]], + [['viewName'], 'match', 'pattern' => '/^\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes and slashes are allowed.'], + [['viewPath'], 'match', 'pattern' => '/^@?\w+[\\-\\/\w]*$/', 'message' => 'Only word characters, dashes, slashes and @ are allowed.'], + [['viewPath'], 'validateViewPath'], + [['scenarioName'], 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'], ]); } diff --git a/extensions/gii/generators/model/Generator.php b/extensions/gii/generators/model/Generator.php index 5804d30..976fde8 100644 --- a/extensions/gii/generators/model/Generator.php +++ b/extensions/gii/generators/model/Generator.php @@ -53,17 +53,17 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), [ - ['db, ns, tableName, modelClass, baseClass', 'filter', 'filter' => 'trim'], - ['db, ns, tableName, baseClass', 'required'], - ['db, modelClass', 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'], - ['ns, baseClass', 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'], - ['tableName', 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'], - ['db', 'validateDb'], - ['ns', 'validateNamespace'], - ['tableName', 'validateTableName'], - ['modelClass', 'validateModelClass', 'skipOnEmpty' => false], - ['baseClass', 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], - ['generateRelations, generateLabelsFromComments', 'boolean'], + [['db', 'ns', 'tableName', 'modelClass', 'baseClass'], 'filter', 'filter' => 'trim'], + [['db', 'ns', 'tableName', 'baseClass'], 'required'], + [['db', 'modelClass'], 'match', 'pattern' => '/^\w+$/', 'message' => 'Only word characters are allowed.'], + [['ns', 'baseClass'], 'match', 'pattern' => '/^[\w\\\\]+$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['tableName'], 'match', 'pattern' => '/^(\w+\.)?([\w\*]+)$/', 'message' => 'Only word characters, and optionally an asterisk and/or a dot are allowed.'], + [['db'], 'validateDb'], + [['ns'], 'validateNamespace'], + [['tableName'], 'validateTableName'], + [['modelClass'], 'validateModelClass', 'skipOnEmpty' => false], + [['baseClass'], 'validateClass', 'params' => ['extends' => ActiveRecord::className()]], + [['generateRelations', 'generateLabelsFromComments'], 'boolean'], ]); } diff --git a/extensions/gii/generators/module/Generator.php b/extensions/gii/generators/module/Generator.php index fcb385d..23e9b45 100644 --- a/extensions/gii/generators/module/Generator.php +++ b/extensions/gii/generators/module/Generator.php @@ -45,11 +45,11 @@ class Generator extends \yii\gii\Generator public function rules() { return array_merge(parent::rules(), [ - ['moduleID, moduleClass', 'filter', 'filter' => 'trim'], - ['moduleID, moduleClass', 'required'], - ['moduleID', 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'], - ['moduleClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], - ['moduleClass', 'validateModuleClass'], + [['moduleID', 'moduleClass'], 'filter', 'filter' => 'trim'], + [['moduleID', 'moduleClass'], 'required'], + [['moduleID'], 'match', 'pattern' => '/^[\w\\-]+$/', 'message' => 'Only word characters and dashes are allowed.'], + [['moduleClass'], 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'], + [['moduleClass'], 'validateModuleClass'], ]); } diff --git a/tests/unit/data/base/Singer.php b/tests/unit/data/base/Singer.php index 5c0f0fe..547cee8 100644 --- a/tests/unit/data/base/Singer.php +++ b/tests/unit/data/base/Singer.php @@ -14,9 +14,9 @@ class Singer extends Model public function rules() { return [ - ['lastName', 'default', 'value' => 'Lennon'], - ['lastName', 'required'], - ['underscore_style', 'yii\captcha\CaptchaValidator'], + [['lastName'], 'default', 'value' => 'Lennon'], + [['lastName'], 'required'], + [['underscore_style'], 'yii\captcha\CaptchaValidator'], ]; } } diff --git a/tests/unit/data/validators/models/FakedValidationModel.php b/tests/unit/data/validators/models/FakedValidationModel.php index 6e9c8b1..73f8192 100644 --- a/tests/unit/data/validators/models/FakedValidationModel.php +++ b/tests/unit/data/validators/models/FakedValidationModel.php @@ -28,8 +28,8 @@ class FakedValidationModel extends Model public function rules() { return [ - ['val_attr_a, val_attr_b', 'required', 'on' => 'reqTest'], - ['val_attr_c', 'integer'], + [['val_attr_a', 'val_attr_b'], 'required', 'on' => 'reqTest'], + [['val_attr_c'], 'integer'], ]; } @@ -60,4 +60,4 @@ class FakedValidationModel extends Model { return $attr; } -} \ No newline at end of file +} From 35681c618bd09d6a53021e9e537a7fe46b7bf824 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 17:24:35 +0200 Subject: [PATCH 51/79] Fix test --- tests/unit/framework/validators/ValidatorTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/framework/validators/ValidatorTest.php b/tests/unit/framework/validators/ValidatorTest.php index b248a9b..7a0258d 100644 --- a/tests/unit/framework/validators/ValidatorTest.php +++ b/tests/unit/framework/validators/ValidatorTest.php @@ -39,7 +39,7 @@ class ValidatorTest extends TestCase $val = TestValidator::createValidator( 'boolean', $model, - 'attr_test1, attr_test2', + ['attr_test1', 'attr_test2'], ['on' => ['a', 'b']] ); $this->assertInstanceOf(BooleanValidator::className(), $val); @@ -48,8 +48,8 @@ class ValidatorTest extends TestCase $val = TestValidator::createValidator( 'boolean', $model, - 'attr_test1, attr_test2', - ['on' => 'a, b', 'except' => 'c,d,e'] + ['attr_test1', 'attr_test2'], + ['on' => ['a', 'b'], 'except' => ['c', 'd', 'e']] ); $this->assertInstanceOf(BooleanValidator::className(), $val); $this->assertSame(['a', 'b'], $val->on); From cbca145e97cb083dcb854c3b0cfe4dda626c5de8 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 17:24:52 +0200 Subject: [PATCH 52/79] Add type hinting --- framework/yii/validators/Validator.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/framework/yii/validators/Validator.php b/framework/yii/validators/Validator.php index 8503983..7ea166d 100644 --- a/framework/yii/validators/Validator.php +++ b/framework/yii/validators/Validator.php @@ -131,17 +131,9 @@ abstract class Validator extends Component * @param array $params initial values to be applied to the validator properties * @return Validator the validator */ - public static function createValidator($type, $object, $attributes, $params = []) + public static function createValidator($type, $object, array $attributes, $params = []) { - $params['attributes'] = (array) $attributes; - - if (isset($params['on'])) { - $params['on'] = (array) $params['on']; - } - - if (isset($params['except'])) { - $params['except'] = (array) $params['except']; - } + $params['attributes'] = $attributes; if (method_exists($object, $type)) { // method-based validator From 9bf7f791b8d9be86eb0774b9ed8f1dc9ce64f83b Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 17:53:24 +0200 Subject: [PATCH 53/79] Fix test --- tests/unit/framework/validators/ValidatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/framework/validators/ValidatorTest.php b/tests/unit/framework/validators/ValidatorTest.php index 7a0258d..5e5385b 100644 --- a/tests/unit/framework/validators/ValidatorTest.php +++ b/tests/unit/framework/validators/ValidatorTest.php @@ -54,7 +54,7 @@ class ValidatorTest extends TestCase $this->assertInstanceOf(BooleanValidator::className(), $val); $this->assertSame(['a', 'b'], $val->on); $this->assertSame(['c', 'd', 'e'], $val->except); - $val = TestValidator::createValidator('inlineVal', $model, 'val_attr_a'); + $val = TestValidator::createValidator('inlineVal', $model, ['val_attr_a']); $this->assertInstanceOf(InlineValidator::className(), $val); $this->assertSame('inlineVal', $val->method); } From 3ff8f10d1127e744669d840b11e8d16e6f06fa1e Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 19:26:49 +0200 Subject: [PATCH 54/79] Add array conversion on creating validator --- framework/yii/base/Model.php | 2 +- tests/unit/data/validators/models/FakedValidationModel.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index fd1bea0..bfd7249 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -390,7 +390,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess if ($rule instanceof Validator) { $validators->append($rule); } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type - $validator = Validator::createValidator($rule[1], $this, $rule[0], array_slice($rule, 2)); + $validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2)); $validators->append($validator); } else { throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.'); diff --git a/tests/unit/data/validators/models/FakedValidationModel.php b/tests/unit/data/validators/models/FakedValidationModel.php index 73f8192..e4de44b 100644 --- a/tests/unit/data/validators/models/FakedValidationModel.php +++ b/tests/unit/data/validators/models/FakedValidationModel.php @@ -29,7 +29,7 @@ class FakedValidationModel extends Model { return [ [['val_attr_a', 'val_attr_b'], 'required', 'on' => 'reqTest'], - [['val_attr_c'], 'integer'], + ['val_attr_c', 'integer'], ]; } From 2adcd16e0dc58ca4b8113c904f5cd34746e87fd3 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Tue, 12 Nov 2013 19:32:11 +0200 Subject: [PATCH 55/79] Update phpDoc --- framework/yii/base/Model.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index bfd7249..5ee66cf 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -100,7 +100,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * * where * - * - attribute list: required, specifies the attributes array to be validated; + * - attribute list: required, specifies the attributes array to be validated, for single attribute you can pass string; * - validator type: required, specifies the validator to be used. It can be the name of a model * class method, the name of a built-in validator, or a validator class name (or its path alias). * - on: optional, specifies the [[scenario|scenarios]] array when the validation @@ -128,15 +128,15 @@ class Model extends Component implements IteratorAggregate, ArrayAccess * ~~~ * [ * // built-in "required" validator - * [['username'], 'required'], + * [['username', 'password'], 'required'], * // built-in "string" validator customized with "min" and "max" properties - * [['username'], 'string', 'min' => 3, 'max' => 12], + * ['username', 'string', 'min' => 3, 'max' => 12], * // built-in "compare" validator that is used in "register" scenario only - * [['password'], 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], + * ['password', 'compare', 'compareAttribute' => 'password2', 'on' => 'register'], * // an inline validator defined via the "authenticate()" method in the model class - * [['password'], 'authenticate', 'on' => 'login'], + * ['password', 'authenticate', 'on' => 'login'], * // a validator of class "DateRangeValidator" - * [['dateRange'], 'DateRangeValidator'], + * ['dateRange', 'DateRangeValidator'], * ]; * ~~~ * From 539e233d9c1afed0a5fc15efcd79c28657ff972d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 01:16:21 +0100 Subject: [PATCH 56/79] massive improvement on class documentation - added example code for application components - added missing short description [ci skip] --- framework/yii/base/ActionFilter.php | 5 +++++ framework/yii/base/Component.php | 2 ++ framework/yii/base/ErrorHandler.php | 3 +++ framework/yii/base/Formatter.php | 3 +++ framework/yii/base/Model.php | 3 ++- framework/yii/base/Object.php | 3 +++ framework/yii/base/Request.php | 1 + framework/yii/base/Response.php | 2 ++ framework/yii/base/ViewEvent.php | 2 ++ framework/yii/captcha/CaptchaAsset.php | 2 ++ framework/yii/console/Request.php | 4 ++++ framework/yii/console/Response.php | 2 ++ framework/yii/grid/ActionColumn.php | 2 ++ framework/yii/grid/DataColumn.php | 4 ++++ framework/yii/grid/GridView.php | 4 ++++ framework/yii/grid/GridViewAsset.php | 1 + framework/yii/i18n/Formatter.php | 9 ++++++++ framework/yii/i18n/I18N.php | 3 +++ framework/yii/validators/PunycodeAsset.php | 2 ++ framework/yii/validators/ValidationAsset.php | 2 ++ framework/yii/web/AssetConverter.php | 4 ++++ framework/yii/web/AssetManager.php | 16 ++++++++++++++ framework/yii/web/CacheSession.php | 12 ++++++++++- framework/yii/web/Controller.php | 31 ++++++++++++++++++++++++++++ framework/yii/web/DbSession.php | 1 + framework/yii/web/HttpCache.php | 27 +++++++++++++++++++++++- framework/yii/web/HttpException.php | 8 +++++++ framework/yii/web/PageCache.php | 30 +++++++++++++++++++++++++++ framework/yii/web/Request.php | 3 +++ framework/yii/web/Response.php | 14 +++++++++++++ framework/yii/web/Session.php | 2 +- framework/yii/web/UrlManager.php | 16 ++++++++++++++ framework/yii/web/UrlRule.php | 12 ++++++++++- framework/yii/web/User.php | 15 ++++++++++++++ framework/yii/web/View.php | 16 ++++++++++++++ framework/yii/web/XmlResponseFormatter.php | 2 ++ framework/yii/web/YiiAsset.php | 2 ++ 37 files changed, 265 insertions(+), 5 deletions(-) diff --git a/framework/yii/base/ActionFilter.php b/framework/yii/base/ActionFilter.php index 60be177..648211c 100644 --- a/framework/yii/base/ActionFilter.php +++ b/framework/yii/base/ActionFilter.php @@ -8,6 +8,11 @@ namespace yii\base; /** + * ActionFilter provides a base implementation for action filters that can be added to a controller + * to handle the `beforeAction` event. + * + * Check implementation of [[AccessControl]], [[PageCache]] and [[HttpCache]] as examples on how to use it. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Component.php b/framework/yii/base/Component.php index 108f0f1..2ef4ead 100644 --- a/framework/yii/base/Component.php +++ b/framework/yii/base/Component.php @@ -10,6 +10,8 @@ namespace yii\base; use Yii; /** + * Component is the base class that implements the *property*, *event* and *behavior* features. + * * @include @yii/base/Component.md * * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only. diff --git a/framework/yii/base/ErrorHandler.php b/framework/yii/base/ErrorHandler.php index ead9646..c96ca5e 100644 --- a/framework/yii/base/ErrorHandler.php +++ b/framework/yii/base/ErrorHandler.php @@ -16,6 +16,9 @@ use yii\web\HttpException; * ErrorHandler displays these errors using appropriate views based on the * nature of the errors and the mode the application runs at. * + * ErrorHandler is configured as an application component in [[yii\base\Application]] by default. + * You can access that instance via `Yii::$app->errorHandler`. + * * @author Qiang Xue * @author Timur Ruziev * @since 2.0 diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php index 18faaff..30df3c3 100644 --- a/framework/yii/base/Formatter.php +++ b/framework/yii/base/Formatter.php @@ -19,6 +19,9 @@ use yii\helpers\Html; * The behavior of some of them may be configured via the properties of Formatter. For example, * by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string. * + * Formatter is configured as an application component in [[yii\base\Application]] by default. + * You can access that instance via `Yii::$app->formatter`. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Model.php b/framework/yii/base/Model.php index fa05576..8090ebc 100644 --- a/framework/yii/base/Model.php +++ b/framework/yii/base/Model.php @@ -46,7 +46,8 @@ use yii\validators\Validator; * @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_SCENARIO]]. - * @property ArrayObject $validators All the validators declared in the model. This property is read-only. + * @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model. + * This property is read-only. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/base/Object.php b/framework/yii/base/Object.php index 47c34e0..06fca50 100644 --- a/framework/yii/base/Object.php +++ b/framework/yii/base/Object.php @@ -10,7 +10,10 @@ namespace yii\base; use Yii; /** + * Object is the base class that implements the *property* feature. + * * @include @yii/base/Object.md + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/Request.php b/framework/yii/base/Request.php index 0d660d6..cd3ffdc 100644 --- a/framework/yii/base/Request.php +++ b/framework/yii/base/Request.php @@ -8,6 +8,7 @@ namespace yii\base; /** + * Request represents a request that is handled by an [[Application]]. * * @property boolean $isConsoleRequest The value indicating whether the current request is made via console. * @property string $scriptFile Entry script file path (processed w/ realpath()). diff --git a/framework/yii/base/Response.php b/framework/yii/base/Response.php index 467de9e..1403b69 100644 --- a/framework/yii/base/Response.php +++ b/framework/yii/base/Response.php @@ -8,6 +8,8 @@ namespace yii\base; /** + * Response represents the response of an [[Application]] to a [[Request]]. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/base/ViewEvent.php b/framework/yii/base/ViewEvent.php index b5734f4..d02e180 100644 --- a/framework/yii/base/ViewEvent.php +++ b/framework/yii/base/ViewEvent.php @@ -8,6 +8,8 @@ namespace yii\base; /** + * ViewEvent represents events triggered by the [[View]] component. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/captcha/CaptchaAsset.php b/framework/yii/captcha/CaptchaAsset.php index adece3c..4fc722f 100644 --- a/framework/yii/captcha/CaptchaAsset.php +++ b/framework/yii/captcha/CaptchaAsset.php @@ -10,6 +10,8 @@ namespace yii\captcha; use yii\web\AssetBundle; /** + * This asset bundle provides the javascript files needed for the [[Captcha]] widget. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/console/Request.php b/framework/yii/console/Request.php index d99c321..d4d3af0 100644 --- a/framework/yii/console/Request.php +++ b/framework/yii/console/Request.php @@ -8,6 +8,10 @@ namespace yii\console; /** + * The console Request represents the environment information for a console application. + * + * It is a wrapper for the PHP `$_SERVER` variable which holds information about the + * currently running PHP script and the command line arguments given to it. * * @property array $params The command line arguments. It does not include the entry script name. * diff --git a/framework/yii/console/Response.php b/framework/yii/console/Response.php index 9c23e83..f6e6dd0 100644 --- a/framework/yii/console/Response.php +++ b/framework/yii/console/Response.php @@ -8,6 +8,8 @@ namespace yii\console; /** + * The console Response represents the result of a console application by holding the [[exitCode]]. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/grid/ActionColumn.php b/framework/yii/grid/ActionColumn.php index 794198e..2ee1db2 100644 --- a/framework/yii/grid/ActionColumn.php +++ b/framework/yii/grid/ActionColumn.php @@ -12,6 +12,8 @@ use Closure; use yii\helpers\Html; /** + * ActionColumn is a column for the [[GridView]] widget that displays buttons for viewing and manipulating the items. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/grid/DataColumn.php b/framework/yii/grid/DataColumn.php index e8a25a7..bd6eacb 100644 --- a/framework/yii/grid/DataColumn.php +++ b/framework/yii/grid/DataColumn.php @@ -15,6 +15,10 @@ use yii\helpers\Html; use yii\helpers\Inflector; /** + * DataColumn is the default column type for the [[GridView]] widget. + * + * It is used to show data columns and allows sorting them. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/grid/GridView.php b/framework/yii/grid/GridView.php index 6cef03a..de99a18 100644 --- a/framework/yii/grid/GridView.php +++ b/framework/yii/grid/GridView.php @@ -16,6 +16,10 @@ use yii\helpers\Json; use yii\widgets\BaseListView; /** + * The GridView widget is used to display data in a grid. + * + * It provides features like sorting, paging and also filtering the data. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/grid/GridViewAsset.php b/framework/yii/grid/GridViewAsset.php index ae49070..a67999d 100644 --- a/framework/yii/grid/GridViewAsset.php +++ b/framework/yii/grid/GridViewAsset.php @@ -10,6 +10,7 @@ namespace yii\grid; use yii\web\AssetBundle; /** + * This asset bundle provides the javascript files for the [[GridView]] widget. * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php index 1388dc1..2eeb056 100644 --- a/framework/yii/i18n/Formatter.php +++ b/framework/yii/i18n/Formatter.php @@ -19,6 +19,15 @@ use yii\base\InvalidConfigException; * Formatter requires the PHP "intl" extension to be installed. Formatter supports localized * formatting of date, time and numbers, based on the current [[locale]]. * + * This Formatter can replace the `formatter` application component that is configured by default. + * To do so, add the following to your application config under `components`: + * + * ```php + * 'formatter' => [ + * 'class' => 'yii\i18n\Formatter', + * ] + * ``` + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/i18n/I18N.php b/framework/yii/i18n/I18N.php index fe0b533..5575621 100644 --- a/framework/yii/i18n/I18N.php +++ b/framework/yii/i18n/I18N.php @@ -14,6 +14,9 @@ use yii\base\InvalidConfigException; /** * I18N provides features related with internationalization (I18N) and localization (L10N). * + * I18N is configured as an application component in [[yii\base\Application]] by default. + * You can access that instance via `Yii::$app->i18n`. + * * @property MessageFormatter $messageFormatter The message formatter to be used to format message via ICU * message format. Note that the type of this property differs in getter and setter. See * [[getMessageFormatter()]] and [[setMessageFormatter()]] for details. diff --git a/framework/yii/validators/PunycodeAsset.php b/framework/yii/validators/PunycodeAsset.php index 08439bf..c0c1e2b 100644 --- a/framework/yii/validators/PunycodeAsset.php +++ b/framework/yii/validators/PunycodeAsset.php @@ -9,6 +9,8 @@ namespace yii\validators; use yii\web\AssetBundle; /** + * This asset bundle provides the javascript files needed for the [[EmailValidator]]s client validation. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/validators/ValidationAsset.php b/framework/yii/validators/ValidationAsset.php index 8ff1b2d..14d7ad0 100644 --- a/framework/yii/validators/ValidationAsset.php +++ b/framework/yii/validators/ValidationAsset.php @@ -9,6 +9,8 @@ namespace yii\validators; use yii\web\AssetBundle; /** + * This asset bundle provides the javascript files for client validation. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/AssetConverter.php b/framework/yii/web/AssetConverter.php index ba64aa9..a93b915 100644 --- a/framework/yii/web/AssetConverter.php +++ b/framework/yii/web/AssetConverter.php @@ -14,6 +14,8 @@ use yii\base\Exception; /** * AssetConverter supports conversion of several popular script formats into JS or CSS scripts. * + * It is used by [[AssetManager]] to convert files after they have been published. + * * @author Qiang Xue * @since 2.0 */ @@ -63,6 +65,8 @@ class AssetConverter extends Component implements AssetConverterInterface * @param string $asset the name of the asset file * @param string $result the name of the file to be generated by the converter command * @return bool true on success, false on failure. Failures will be logged. + * @throws \yii\base\Exception when the command fails and YII_DEBUG is true. + * In production mode the error will be logged. */ protected function runCommand($command, $basePath, $asset, $result) { diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php index 49374f0..b562cf6 100644 --- a/framework/yii/web/AssetManager.php +++ b/framework/yii/web/AssetManager.php @@ -16,6 +16,22 @@ use yii\helpers\FileHelper; /** * AssetManager manages asset bundles and asset publishing. * + * AssetManager is configured as an application component in [[yii\web\Application]] by default. + * You can access that instance via `Yii::$app->assetManager`. + * + * You can modify its configuration by adding an array to your application config under `components` + * as it is shown in the following example: + * + * ~~~ + * 'assetManager' => [ + * 'bundles' => [ + * // you can override AssetBundle configs here + * ], + * //'linkAssets' => true, + * // ... + * ] + * ~~~ + * * @property AssetConverterInterface $converter The asset converter. Note that the type of this property * differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details. * diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php index 84033b7..7b4a98d 100644 --- a/framework/yii/web/CacheSession.php +++ b/framework/yii/web/CacheSession.php @@ -19,7 +19,17 @@ use yii\base\InvalidConfigException; * * Beware, by definition cache storage are volatile, which means the data stored on them * 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. + * is NOT volatile. If you want to use database as storage medium, [[DbSession]] is a better choice. + * + * The following example shows how you can configure the application to use CacheSession: + * Add the following to your application config under `components`: + * + * ~~~ + * 'session' => [ + * 'class' => 'yii\web\CacheSession', + * // 'cache' => 'mycache', + * ] + * ~~~ * * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. * diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index 2ecd9e4..3b08b7e 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -150,6 +150,13 @@ class Controller extends \yii\base\Controller * Redirects the browser to the specified URL. * This method is a shortcut to [[Response::redirect()]]. * + * You can use it in an action by returning the [[Response]] directly: + * + * ```php + * // stop executing this action and redirect to login page + * return $this->redirect(['login']); + * ``` + * * @param string|array $url the URL to be redirected to. This can be in one of the following formats: * * - a string representing a URL (e.g. "http://example.com") @@ -172,6 +179,14 @@ class Controller extends \yii\base\Controller /** * Redirects the browser to the home page. + * + * You can use this method in an action by returning the [[Response]] directly: + * + * ```php + * // stop executing this action and redirect to home page + * return $this->goHome(); + * ``` + * * @return Response the current response object */ public function goHome() @@ -181,6 +196,14 @@ class Controller extends \yii\base\Controller /** * Redirects the browser to the last visited page. + * + * You can use this method in an action by returning the [[Response]] directly: + * + * ```php + * // stop executing this action and redirect to last visited page + * return $this->goBack(); + * ``` + * * @param string|array $defaultUrl the default return URL in case it was not set previously. * If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to. * Please refer to [[User::setReturnUrl()]] on accepted format of the URL. @@ -195,6 +218,14 @@ class Controller extends \yii\base\Controller /** * Refreshes the current page. * This method is a shortcut to [[Response::refresh()]]. + * + * You can use it in an action by returning the [[Response]] directly: + * + * ```php + * // stop executing this action and refresh the current page + * return $this->refresh(); + * ``` + * * @param string $anchor the anchor that should be appended to the redirection URL. * Defaults to empty. Make sure the anchor starts with '#' if you want to specify it. * @return Response the response object itself diff --git a/framework/yii/web/DbSession.php b/framework/yii/web/DbSession.php index 410439b..d5d1742 100644 --- a/framework/yii/web/DbSession.php +++ b/framework/yii/web/DbSession.php @@ -19,6 +19,7 @@ use yii\base\InvalidConfigException; * 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: + * Add the following to your application config under `components`: * * ~~~ * 'session' => [ diff --git a/framework/yii/web/HttpCache.php b/framework/yii/web/HttpCache.php index d2f3923..134df71 100644 --- a/framework/yii/web/HttpCache.php +++ b/framework/yii/web/HttpCache.php @@ -12,7 +12,32 @@ use yii\base\ActionFilter; use yii\base\Action; /** - * The HttpCache provides functionality for caching via HTTP Last-Modified and Etag headers + * The HttpCache provides functionality for caching via HTTP Last-Modified and Etag headers. + * + * It is an action filter that can be added to a controller and handles the `beforeAction` event. + * + * To use AccessControl, declare it in the `behaviors()` method of your controller class. + * In the following example the filter will be applied to the `list`-action and + * the Last-Modified header will contain the date of the last update to the user table in the database. + * + * ~~~ + * public function behaviors() + * { + * return [ + * 'httpCache' => [ + * 'class' => \yii\web\HttpCache::className(), + * 'only' => ['list'], + * 'lastModified' => function ($action, $params) { + * $q = new Query(); + * return strtotime($q->from('users')->max('updated_timestamp')); + * }, + * // 'etagSeed' => function ($action, $params) { + * // return // generate etag seed here + * // } + * ], + * ]; + * } + * ~~~ * * @author Da:Sourcerer * @author Qiang Xue diff --git a/framework/yii/web/HttpException.php b/framework/yii/web/HttpException.php index 2e677d5..2398437 100644 --- a/framework/yii/web/HttpException.php +++ b/framework/yii/web/HttpException.php @@ -16,6 +16,14 @@ use yii\base\UserException; * keeps a standard HTTP status code (e.g. 404, 500). Error handlers may use this status code * to decide how to format the error page. * + * Throwing an HttpException like in the following example will result in the 404 page to be displayed. + * + * ```php + * if ($item === null) { // item does not exist + * throw new \yii\web\HttpException(404, 'The requested Item could not be found.'); + * } + * ``` + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/PageCache.php b/framework/yii/web/PageCache.php index 2a3187b..4c8cc50 100644 --- a/framework/yii/web/PageCache.php +++ b/framework/yii/web/PageCache.php @@ -15,6 +15,35 @@ use yii\caching\Dependency; /** * The PageCache provides functionality for whole page caching * + * It is an action filter that can be added to a controller and handles the `beforeAction` event. + * + * To use PageCache, declare it in the `behaviors()` method of your controller class. + * In the following example the filter will be applied to the `list`-action and + * cache the whole page for maximum 60 seconds or until the count of entries in the post table changes. + * It also stores different versions of the page depended on the route ([[varyByRoute]] is true by default), + * the application language and user id. + * + * ~~~ + * public function behaviors() + * { + * return [ + * 'pageCache' => [ + * 'class' => \yii\web\PageCache::className(), + * 'only' => ['list'], + * 'duration' => 60, + * 'dependecy' => [ + * 'class' => 'yii\caching\DbDependency', + * 'sql' => 'SELECT COUNT(*) FROM post', + * ], + * 'variations' => [ + * Yii::$app->language, + * Yii::$app->user->id + * ] + * ], + * ]; + * } + * ~~~ + * * @author Qiang Xue * @since 2.0 */ @@ -60,6 +89,7 @@ class PageCache extends ActionFilter * [ * Yii::$app->language, * ] + * ~~~ */ public $variations; /** diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php index 46b2d2d..2071afa 100644 --- a/framework/yii/web/Request.php +++ b/framework/yii/web/Request.php @@ -18,6 +18,9 @@ use yii\helpers\Security; * 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. * + * Request is configured as an application component in [[yii\web\Application]] by default. + * You can access that instance via `Yii::$app->request`. + * * @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 diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php index ea1f0d9..8934fa1 100644 --- a/framework/yii/web/Response.php +++ b/framework/yii/web/Response.php @@ -22,6 +22,20 @@ use yii\helpers\StringHelper; * It holds the [[headers]], [[cookies]] and [[content]] that is to be sent to the client. * It also controls the HTTP [[statusCode|status code]]. * + * Response is configured as an application component in [[yii\web\Application]] by default. + * You can access that instance via `Yii::$app->response`. + * + * You can modify its configuration by adding an array to your application config under `components` + * as it is shown in the following example: + * + * ~~~ + * 'response' => [ + * 'format' => yii\web\Response::FORMAT_JSON, + * 'charset' => 'UTF-8', + * // ... + * ] + * ~~~ + * * @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 diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php index 86f3506..9fba49a 100644 --- a/framework/yii/web/Session.php +++ b/framework/yii/web/Session.php @@ -15,7 +15,7 @@ use yii\base\InvalidParamException; * Session provides session data management and the related configurations. * * Session is a Web application component that can be accessed via `Yii::$app->session`. - + * * To start the session, call [[open()]]; To complete and send out session data, call [[close()]]; * To destroy the session, call [[destroy()]]. * diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 0626fff..540e8d5 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -14,6 +14,22 @@ use yii\caching\Cache; /** * UrlManager handles HTTP request parsing and creation of URLs based on a set of rules. * + * UrlManager is configured as an application component in [[yii\base\Application]] by default. + * You can access that instance via `Yii::$app->urlManager`. + * + * You can modify its configuration by adding an array to your application config under `components` + * as it is shown in the following example: + * + * ~~~ + * 'urlManager' => [ + * 'enablePrettyUrl' => true, + * 'rules' => [ + * // your rules go here + * ], + * // ... + * ] + * ~~~ + * * @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. diff --git a/framework/yii/web/UrlRule.php b/framework/yii/web/UrlRule.php index 6ebc615..af227cd 100644 --- a/framework/yii/web/UrlRule.php +++ b/framework/yii/web/UrlRule.php @@ -11,7 +11,17 @@ use yii\base\Object; use yii\base\InvalidConfigException; /** - * UrlRule represents a rule used for parsing and generating URLs. + * UrlRule represents a rule used by [[UrlManager]] for parsing and generating URLs. + * + * To define your own URL parsing and creation logic you can extend from this class + * and add it to [[UrlManager::rules]] like this: + * + * ~~~ + * 'rules' => [ + * ['class' => 'MyUrlRule', 'pattern' => '...', 'route' => 'site/index', ...], + * // ... + * ] + * ~~~ * * @author Qiang Xue * @since 2.0 diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php index b43d038..682d78e 100644 --- a/framework/yii/web/User.php +++ b/framework/yii/web/User.php @@ -20,6 +20,21 @@ use yii\base\InvalidConfigException; * User works with a class implementing the [[IdentityInterface]]. This class implements * the actual user authentication logic and is often backed by a user database table. * + * User is configured as an application component in [[yii\web\Application]] by default. + * You can access that instance via `Yii::$app->user`. + * + * You can modify its configuration by adding an array to your application config under `components` + * as it is shown in the following example: + * + * ~~~ + * 'user' => [ + * 'identityClass' => 'app\models\User', // User must implement the IdentityInterface + * 'enableAutoLogin' => true, + * // 'loginUrl' => ['user/login'], + * // ... + * ] + * ~~~ + * * @property string|integer $id The unique identifier for the user. If null, it means the user is a guest. * This property is read-only. * @property IdentityInterface $identity The identity object associated with the currently logged user. Null diff --git a/framework/yii/web/View.php b/framework/yii/web/View.php index 0c6d4d4..db0c500 100644 --- a/framework/yii/web/View.php +++ b/framework/yii/web/View.php @@ -22,6 +22,22 @@ use yii\base\InvalidConfigException; * * View provides a set of methods (e.g. [[render()]]) for rendering purpose. * + * View is configured as an application component in [[yii\base\Application]] by default. + * You can access that instance via `Yii::$app->view`. + * + * You can modify its configuration by adding an array to your application config under `components` + * as it is shown in the following example: + * + * ~~~ + * 'view' => [ + * 'theme' => 'app\themes\MyTheme', + * 'renderers' => [ + * // you may add Smarty or Twig renderer here + * ] + * // ... + * ] + * ~~~ + * * @property \yii\web\AssetManager $assetManager The asset manager. Defaults to the "assetManager" application * component. * diff --git a/framework/yii/web/XmlResponseFormatter.php b/framework/yii/web/XmlResponseFormatter.php index 05c2762..292424a 100644 --- a/framework/yii/web/XmlResponseFormatter.php +++ b/framework/yii/web/XmlResponseFormatter.php @@ -17,6 +17,8 @@ use yii\helpers\StringHelper; /** * XmlResponseFormatter formats the given data into an XML response content. * + * It is used by [[Response]] to format response data. + * * @author Qiang Xue * @since 2.0 */ diff --git a/framework/yii/web/YiiAsset.php b/framework/yii/web/YiiAsset.php index e49082d..d38b711 100644 --- a/framework/yii/web/YiiAsset.php +++ b/framework/yii/web/YiiAsset.php @@ -8,6 +8,8 @@ namespace yii\web; /** + * This asset bundle provides the base javascript files for the Yii Framework. + * * @author Qiang Xue * @since 2.0 */ From 0eaafd74bade9477d011313308965b705a4334d0 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 01:34:41 +0100 Subject: [PATCH 57/79] updated composer.json to be less restrictive also removed superflous minimum-stability definition --- extensions/bootstrap/composer.json | 7 ++++++- extensions/composer/composer.json | 1 - extensions/debug/composer.json | 7 ++++++- extensions/gii/composer.json | 7 ++++++- extensions/jui/composer.json | 7 ++++++- extensions/smarty/composer.json | 3 +-- extensions/swiftmailer/composer.json | 3 +-- extensions/twig/composer.json | 3 +-- framework/composer.json | 4 ++-- 9 files changed, 29 insertions(+), 13 deletions(-) diff --git a/extensions/bootstrap/composer.json b/extensions/bootstrap/composer.json index b8eaacf..e80de80 100644 --- a/extensions/bootstrap/composer.json +++ b/extensions/bootstrap/composer.json @@ -11,7 +11,12 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, - "minimum-stability": "dev", + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], "require": { "yiisoft/yii2": "*", "twbs/bootstrap": "3.0.*" diff --git a/extensions/composer/composer.json b/extensions/composer/composer.json index ff0785c..652ee14 100644 --- a/extensions/composer/composer.json +++ b/extensions/composer/composer.json @@ -17,7 +17,6 @@ "email": "qiang.xue@gmail.com" } ], - "minimum-stability": "dev", "autoload": { "psr-0": { "yii\\composer\\": "" } }, diff --git a/extensions/debug/composer.json b/extensions/debug/composer.json index e1e57b2..f60d1df 100644 --- a/extensions/debug/composer.json +++ b/extensions/debug/composer.json @@ -11,7 +11,12 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, - "minimum-stability": "dev", + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], "require": { "yiisoft/yii2": "*", "yiisoft/yii2-bootstrap": "*" diff --git a/extensions/gii/composer.json b/extensions/gii/composer.json index fc74a7e..8654621 100644 --- a/extensions/gii/composer.json +++ b/extensions/gii/composer.json @@ -11,7 +11,12 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, - "minimum-stability": "dev", + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], "require": { "yiisoft/yii2": "*", "yiisoft/yii2-bootstrap": "*" diff --git a/extensions/jui/composer.json b/extensions/jui/composer.json index 0888ab1..ff54422 100644 --- a/extensions/jui/composer.json +++ b/extensions/jui/composer.json @@ -11,7 +11,12 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, - "minimum-stability": "dev", + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com" + } + ], "require": { "yiisoft/yii2": "*" }, diff --git a/extensions/smarty/composer.json b/extensions/smarty/composer.json index 566187a..88b75a3 100644 --- a/extensions/smarty/composer.json +++ b/extensions/smarty/composer.json @@ -17,10 +17,9 @@ "email": "sam@rmcreative.ru" } ], - "minimum-stability": "dev", "require": { "yiisoft/yii2": "*", - "smarty/smarty": ">=v3.1.13" + "smarty/smarty": "*" }, "autoload": { "psr-0": { "yii\\smarty\\": "" } diff --git a/extensions/swiftmailer/composer.json b/extensions/swiftmailer/composer.json index e995b2f..5a47397 100644 --- a/extensions/swiftmailer/composer.json +++ b/extensions/swiftmailer/composer.json @@ -17,10 +17,9 @@ "email": "klimov.paul@gmail.com" } ], - "minimum-stability": "dev", "require": { "yiisoft/yii2": "*", - "swiftmailer/swiftmailer": "@stable" + "swiftmailer/swiftmailer": "*" }, "autoload": { "psr-0": { "yii\\swiftmailer\\": "" } diff --git a/extensions/twig/composer.json b/extensions/twig/composer.json index c57c65d..8fe6431 100644 --- a/extensions/twig/composer.json +++ b/extensions/twig/composer.json @@ -17,10 +17,9 @@ "email": "sam@rmcreative.ru" } ], - "minimum-stability": "dev", "require": { "yiisoft/yii2": "*", - "twig/twig": "1.13.*" + "twig/twig": "*" }, "autoload": { "psr-0": { "yii\\twig\\": "" } diff --git a/framework/composer.json b/framework/composer.json index 0d8a407..637471c 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -65,10 +65,10 @@ }, "require": { "php": ">=5.4.0", - "yiisoft/yii2-composer": "*", - "yiisoft/jquery": "1.10.*", "ext-mbstring": "*", "lib-pcre": "*", + "yiisoft/yii2-composer": "*", + "yiisoft/jquery": "1.10.*", "phpspec/php-diff": ">=1.0.2", "ezyang/htmlpurifier": "4.5.*", "michelf/php-markdown": "1.3.*" From f57b3536a20ec71f2ac8a5315fda91f7fe92f3d7 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Wed, 13 Nov 2013 11:41:25 +0200 Subject: [PATCH 58/79] Fixed apps rules --- apps/advanced/common/models/LoginForm.php | 4 ++-- apps/advanced/common/models/User.php | 24 ++++++++++++------------ apps/advanced/frontend/models/ContactForm.php | 4 ++-- apps/basic/models/ContactForm.php | 4 ++-- apps/basic/models/LoginForm.php | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/advanced/common/models/LoginForm.php b/apps/advanced/common/models/LoginForm.php index 795c378..30fb39b 100644 --- a/apps/advanced/common/models/LoginForm.php +++ b/apps/advanced/common/models/LoginForm.php @@ -23,9 +23,9 @@ class LoginForm extends Model // username and password are both required [['username', 'password'], 'required'], // password is validated by validatePassword() - [['password'], 'validatePassword'], + ['password', 'validatePassword'], // rememberMe must be a boolean value - [['rememberMe'], 'boolean'], + ['rememberMe', 'boolean'], ]; } diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php index 85e5539..17bd630 100644 --- a/apps/advanced/common/models/User.php +++ b/apps/advanced/common/models/User.php @@ -104,18 +104,18 @@ class User extends ActiveRecord implements IdentityInterface public function rules() { return [ - [['username'], 'filter', 'filter' => 'trim'], - [['username'], 'required'], - [['username'], 'string', 'min' => 2, 'max' => 255], - - [['email'], 'filter', 'filter' => 'trim'], - [['email'], 'required'], - [['email'], 'email'], - [['email'], 'unique', 'message' => 'This email address has already been taken.', 'on' => 'signup'], - [['email'], 'exist', 'message' => 'There is no user with such email.', 'on' => 'requestPasswordResetToken'], - - [['password'], 'required'], - [['password'], 'string', 'min' => 6], + ['username', 'filter', 'filter' => 'trim'], + ['username', 'required'], + ['username', 'string', 'min' => 2, 'max' => 255], + + ['email', 'filter', 'filter' => 'trim'], + ['email', 'required'], + ['email', 'email'], + ['email', 'unique', 'message' => 'This email address has already been taken.', 'on' => 'signup'], + ['email', 'exist', 'message' => 'There is no user with such email.', 'on' => 'requestPasswordResetToken'], + + ['password', 'required'], + ['password', 'string', 'min' => 6], ]; } diff --git a/apps/advanced/frontend/models/ContactForm.php b/apps/advanced/frontend/models/ContactForm.php index 45b0da0..0a664ad 100644 --- a/apps/advanced/frontend/models/ContactForm.php +++ b/apps/advanced/frontend/models/ContactForm.php @@ -25,9 +25,9 @@ class ContactForm extends Model // name, email, subject and body are required [['name', 'email', 'subject', 'body'], 'required'], // email has to be a valid email address - [['email'], 'email'], + ['email', 'email'], // verifyCode needs to be entered correctly - [['verifyCode'], 'captcha'], + ['verifyCode', 'captcha'], ]; } diff --git a/apps/basic/models/ContactForm.php b/apps/basic/models/ContactForm.php index dbdde8b..1344562 100644 --- a/apps/basic/models/ContactForm.php +++ b/apps/basic/models/ContactForm.php @@ -25,9 +25,9 @@ class ContactForm extends Model // name, email, subject and body are required [['name', 'email', 'subject', 'body'], 'required'], // email has to be a valid email address - [['email'], 'email'], + ['email', 'email'], // verifyCode needs to be entered correctly - [['verifyCode'], 'captcha'], + ['verifyCode', 'captcha'], ]; } diff --git a/apps/basic/models/LoginForm.php b/apps/basic/models/LoginForm.php index cdff17b..ad854a2 100644 --- a/apps/basic/models/LoginForm.php +++ b/apps/basic/models/LoginForm.php @@ -23,9 +23,9 @@ class LoginForm extends Model // username and password are both required [['username', 'password'], 'required'], // password is validated by validatePassword() - [['password'], 'validatePassword'], + ['password', 'validatePassword'], // rememberMe must be a boolean value - [['rememberMe'], 'boolean'], + ['rememberMe', 'boolean'], ]; } From 2f8ee2a8b0e212e8c11ac9d0bed6a661e4d377ee Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 08:22:55 -0500 Subject: [PATCH 59/79] typo fix. --- framework/yii/base/Application.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index 70eaca3..1dbe8e7 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -259,7 +259,7 @@ abstract class Application extends Module } /** - * Sets the root directory of the applicaition and the @app alias. + * Sets the root directory of the application and the @app alias. * This method can only be invoked at the beginning of the constructor. * @param string $path the root directory of the application. * @property string the root directory of the application. @@ -427,7 +427,7 @@ abstract class Application extends Module /** * Returns the view object. - * @return View the view object that is used to render various view files. + * @return View|\yii\web\View the view object that is used to render various view files. */ public function getView() { From da93d9b4baf64914749e01c05d5c71404c2d59b8 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 08:25:07 -0500 Subject: [PATCH 60/79] Fixes #1195. --- extensions/gii/generators/module/Generator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/gii/generators/module/Generator.php b/extensions/gii/generators/module/Generator.php index fcb385d..76a3e5d 100644 --- a/extensions/gii/generators/module/Generator.php +++ b/extensions/gii/generators/module/Generator.php @@ -139,7 +139,7 @@ EOD; */ public function validateModuleClass() { - if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass)) === false) { + if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass), false) === false) { $this->addError('moduleClass', 'Module class must be properly namespaced.'); } if (substr($this->moduleClass, -1, 1) == '\\') { From 36c28bee4f5ff4add6545ffa7823d8efe994147f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 08:28:55 -0500 Subject: [PATCH 61/79] Fixes #1191 --- docs/guide/upgrade-from-v1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md index 2bf080a..e79259c 100644 --- a/docs/guide/upgrade-from-v1.md +++ b/docs/guide/upgrade-from-v1.md @@ -38,7 +38,7 @@ of `Object` should declare its constructor (if needed) in the following way so t it can be properly configured: ```php -class MyClass extends \yii\Object +class MyClass extends \yii\base\Object { public function __construct($param1, $param2, $config = []) { @@ -109,7 +109,7 @@ Yii::$app->trigger($eventName); If you need to handle all instances of a class instead of the object you can attach a handler like the following: ```php -Event::on([ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT], function ($event) { +Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) { Yii::trace(get_class($event->sender) . ' is inserted.'); }); ``` From 75dc290a24c59e5e79a10fdac78a01b9c805c426 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 08:31:49 -0500 Subject: [PATCH 62/79] Fixes #1193 --- apps/advanced/backend/views/layouts/main.php | 2 +- apps/advanced/frontend/views/layouts/main.php | 2 +- apps/basic/views/layouts/main.php | 2 +- extensions/gii/views/layouts/main.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/advanced/backend/views/layouts/main.php b/apps/advanced/backend/views/layouts/main.php index 0ef33d0..0093326 100644 --- a/apps/advanced/backend/views/layouts/main.php +++ b/apps/advanced/backend/views/layouts/main.php @@ -38,7 +38,7 @@ AppAsset::register($this); $menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']]; } echo Nav::widget([ - 'options' => ['class' => 'navbar-nav pull-right'], + 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => $menuItems, ]); NavBar::end(); diff --git a/apps/advanced/frontend/views/layouts/main.php b/apps/advanced/frontend/views/layouts/main.php index d00b837..ec78ac0 100644 --- a/apps/advanced/frontend/views/layouts/main.php +++ b/apps/advanced/frontend/views/layouts/main.php @@ -42,7 +42,7 @@ AppAsset::register($this); $menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']]; } echo Nav::widget([ - 'options' => ['class' => 'navbar-nav pull-right'], + 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => $menuItems, ]); NavBar::end(); diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php index 3bc4d37..49b0f4c 100644 --- a/apps/basic/views/layouts/main.php +++ b/apps/basic/views/layouts/main.php @@ -30,7 +30,7 @@ AppAsset::register($this); ], ]); echo Nav::widget([ - 'options' => ['class' => 'navbar-nav pull-right'], + 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => [ ['label' => 'Home', 'url' => ['/site/index']], ['label' => 'About', 'url' => ['/site/about']], diff --git a/extensions/gii/views/layouts/main.php b/extensions/gii/views/layouts/main.php index baea45e..983475b 100644 --- a/extensions/gii/views/layouts/main.php +++ b/extensions/gii/views/layouts/main.php @@ -26,7 +26,7 @@ NavBar::begin([ 'options' => ['class' => 'navbar-inverse navbar-fixed-top'], ]); echo Nav::widget([ - 'options' => ['class' => 'nav navbar-nav pull-right'], + 'options' => ['class' => 'nav navbar-nav navbar-right'], 'items' => [ ['label' => 'Home', 'url' => ['default/index']], ['label' => 'Help', 'url' => 'http://www.yiiframework.com/doc/guide/topics.gii'], From 5b0886f10b21fb9ab24cecd9e8d7fe687c9bfe97 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 08:45:12 -0500 Subject: [PATCH 63/79] Allow "on" and "attributes" to take either array or string for validators. --- framework/yii/validators/Validator.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/framework/yii/validators/Validator.php b/framework/yii/validators/Validator.php index 7ea166d..36a1af4 100644 --- a/framework/yii/validators/Validator.php +++ b/framework/yii/validators/Validator.php @@ -76,7 +76,8 @@ abstract class Validator extends Component ]; /** - * @var array list of attributes to be validated. + * @var array|string attributes to be validated by this validator. For multiple attributes, + * please specify them as an array; for single attribute, you may use either a string or an array. */ public $attributes = []; /** @@ -88,7 +89,8 @@ abstract class Validator extends Component */ public $message; /** - * @var array list of scenarios that the validator can be applied to. + * @var array|string scenarios that the validator can be applied to. For multiple scenarios, + * please specify them as an array; for single scenario, you may use either a string or an array. */ public $on = []; /** @@ -131,7 +133,7 @@ abstract class Validator extends Component * @param array $params initial values to be applied to the validator properties * @return Validator the validator */ - public static function createValidator($type, $object, array $attributes, $params = []) + public static function createValidator($type, $object, $attributes, $params = []) { $params['attributes'] = $attributes; @@ -156,6 +158,20 @@ abstract class Validator extends Component } /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if (!is_array($this->attributes)) { + $this->attributes = (array)$this->attributes; + } + if (!is_array($this->on)) { + $this->on = (array)$this->on; + } + } + + /** * Validates the specified object. * @param \yii\base\Model $object the data object being validated * @param array|null $attributes the list of attributes to be validated. From b8e31d50bb8839f6613e643c99b8cddf494908ac Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 16:50:30 +0100 Subject: [PATCH 64/79] renamed AR trait classes --- framework/yii/ar/ActiveQuery.php | 234 ------------------------ framework/yii/ar/ActiveQueryTrait.php | 234 ++++++++++++++++++++++++ framework/yii/ar/ActiveRelation.php | 253 -------------------------- framework/yii/ar/ActiveRelationTrait.php | 253 ++++++++++++++++++++++++++ framework/yii/db/ActiveQuery.php | 2 +- framework/yii/db/ActiveRelation.php | 2 +- framework/yii/db/BaseQuery.php | 299 ------------------------------- framework/yii/db/Query.php | 2 +- framework/yii/db/QueryTrait.php | 299 +++++++++++++++++++++++++++++++ 9 files changed, 789 insertions(+), 789 deletions(-) delete mode 100644 framework/yii/ar/ActiveQuery.php create mode 100644 framework/yii/ar/ActiveQueryTrait.php delete mode 100644 framework/yii/ar/ActiveRelation.php create mode 100644 framework/yii/ar/ActiveRelationTrait.php delete mode 100644 framework/yii/db/BaseQuery.php create mode 100644 framework/yii/db/QueryTrait.php diff --git a/framework/yii/ar/ActiveQuery.php b/framework/yii/ar/ActiveQuery.php deleted file mode 100644 index 729b7b4..0000000 --- a/framework/yii/ar/ActiveQuery.php +++ /dev/null @@ -1,234 +0,0 @@ -with('orders')->asArray()->all(); - * ~~~ - * - * @author Qiang Xue - * @author Carsten Brandt - * @since 2.0 - */ -trait ActiveQuery -{ - /** - * @var string the name of the ActiveRecord class. - */ - public $modelClass; - /** - * @var array list of relations that this query should be performed with - */ - public $with; - /** - * @var boolean whether to return each record as an array. If false (default), an object - * of [[modelClass]] will be created to represent each record. - */ - public $asArray; - - - /** - * PHP magic method. - * This method allows calling static method defined in [[modelClass]] via this query object. - * It is mainly implemented for supporting the feature of scope. - * @param string $name the method name to be called - * @param array $params the parameters passed to the method - * @return mixed the method return result - */ - public function __call($name, $params) - { - if (method_exists($this->modelClass, $name)) { - array_unshift($params, $this); - call_user_func_array([$this->modelClass, $name], $params); - return $this; - } else { - return parent::__call($name, $params); - } - } - - /** - * Sets the [[asArray]] property. - * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. - * @return static the query object itself - */ - public function asArray($value = true) - { - $this->asArray = $value; - return $this; - } - - /** - * Specifies the relations with which this query should be performed. - * - * The parameters to this method can be either one or multiple strings, or a single array - * of relation names and the optional callbacks to customize the relations. - * - * The followings are some usage examples: - * - * ~~~ - * // find customers together with their orders and country - * Customer::find()->with('orders', 'country')->all(); - * // find customers together with their country and orders of status 1 - * Customer::find()->with([ - * 'orders' => function($query) { - * $query->andWhere('status = 1'); - * }, - * 'country', - * ])->all(); - * ~~~ - * - * @return static the query object itself - */ - public function with() - { - $this->with = func_get_args(); - if (isset($this->with[0]) && is_array($this->with[0])) { - // the parameter is given as an array - $this->with = $this->with[0]; - } - return $this; - } - - /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row or model data. The signature of the callable should be: - * - * ~~~ - * // $model is an AR instance when `asArray` is false, - * // or an array of column values when `asArray` is true. - * function ($model) - * { - * // return the index value corresponding to $model - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column) - { - return parent::indexBy($column); - } - - private function createModels($rows) - { - $models = []; - if ($this->asArray) { - if ($this->indexBy === null) { - return $rows; - } - foreach ($rows as $row) { - if (is_string($this->indexBy)) { - $key = $row[$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); - } - $models[$key] = $row; - } - } else { - /** @var $class ActiveRecord */ - $class = $this->modelClass; - if ($this->indexBy === null) { - foreach ($rows as $row) { - $models[] = $class::create($row); - } - } else { - foreach ($rows as $row) { - $model = $class::create($row); - if (is_string($this->indexBy)) { - $key = $model->{$this->indexBy}; - } else { - $key = call_user_func($this->indexBy, $model); - } - $models[$key] = $model; - } - } - } - return $models; - } - - private function populateRelations(&$models, $with) - { - $primaryModel = new $this->modelClass; - $relations = $this->normalizeRelations($primaryModel, $with); - foreach ($relations as $name => $relation) { - if ($relation->asArray === null) { - // inherit asArray from primary query - $relation->asArray = $this->asArray; - } - $relation->findWith($name, $models); - } - } - - /** - * @param ActiveRecord $model - * @param array $with - * @return ActiveRelation[] - */ - private function normalizeRelations($model, $with) - { - $relations = []; - foreach ($with as $name => $callback) { - if (is_integer($name)) { - $name = $callback; - $callback = null; - } - if (($pos = strpos($name, '.')) !== false) { - // with sub-relations - $childName = substr($name, $pos + 1); - $name = substr($name, 0, $pos); - } else { - $childName = null; - } - - $t = strtolower($name); - if (!isset($relations[$t])) { - $relation = $model->getRelation($name); - $relation->primaryModel = null; - $relations[$t] = $relation; - } else { - $relation = $relations[$t]; - } - - if (isset($childName)) { - $relation->with[$childName] = $callback; - } elseif ($callback !== null) { - call_user_func($callback, $relation); - } - } - return $relations; - } -} diff --git a/framework/yii/ar/ActiveQueryTrait.php b/framework/yii/ar/ActiveQueryTrait.php new file mode 100644 index 0000000..2100076 --- /dev/null +++ b/framework/yii/ar/ActiveQueryTrait.php @@ -0,0 +1,234 @@ +with('orders')->asArray()->all(); + * ~~~ + * + * @author Qiang Xue + * @author Carsten Brandt + * @since 2.0 + */ +trait ActiveQueryTrait +{ + /** + * @var string the name of the ActiveRecord class. + */ + public $modelClass; + /** + * @var array list of relations that this query should be performed with + */ + public $with; + /** + * @var boolean whether to return each record as an array. If false (default), an object + * of [[modelClass]] will be created to represent each record. + */ + public $asArray; + + + /** + * PHP magic method. + * This method allows calling static method defined in [[modelClass]] via this query object. + * It is mainly implemented for supporting the feature of scope. + * @param string $name the method name to be called + * @param array $params the parameters passed to the method + * @return mixed the method return result + */ + public function __call($name, $params) + { + if (method_exists($this->modelClass, $name)) { + array_unshift($params, $this); + call_user_func_array([$this->modelClass, $name], $params); + return $this; + } else { + return parent::__call($name, $params); + } + } + + /** + * Sets the [[asArray]] property. + * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. + * @return static the query object itself + */ + public function asArray($value = true) + { + $this->asArray = $value; + return $this; + } + + /** + * Specifies the relations with which this query should be performed. + * + * The parameters to this method can be either one or multiple strings, or a single array + * of relation names and the optional callbacks to customize the relations. + * + * The followings are some usage examples: + * + * ~~~ + * // find customers together with their orders and country + * Customer::find()->with('orders', 'country')->all(); + * // find customers together with their country and orders of status 1 + * Customer::find()->with([ + * 'orders' => function($query) { + * $query->andWhere('status = 1'); + * }, + * 'country', + * ])->all(); + * ~~~ + * + * @return static the query object itself + */ + public function with() + { + $this->with = func_get_args(); + if (isset($this->with[0]) && is_array($this->with[0])) { + // the parameter is given as an array + $this->with = $this->with[0]; + } + return $this; + } + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row or model data. The signature of the callable should be: + * + * ~~~ + * // $model is an AR instance when `asArray` is false, + * // or an array of column values when `asArray` is true. + * function ($model) + * { + * // return the index value corresponding to $model + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column) + { + return parent::indexBy($column); + } + + private function createModels($rows) + { + $models = []; + if ($this->asArray) { + if ($this->indexBy === null) { + return $rows; + } + foreach ($rows as $row) { + if (is_string($this->indexBy)) { + $key = $row[$this->indexBy]; + } else { + $key = call_user_func($this->indexBy, $row); + } + $models[$key] = $row; + } + } else { + /** @var $class ActiveRecord */ + $class = $this->modelClass; + if ($this->indexBy === null) { + foreach ($rows as $row) { + $models[] = $class::create($row); + } + } else { + foreach ($rows as $row) { + $model = $class::create($row); + if (is_string($this->indexBy)) { + $key = $model->{$this->indexBy}; + } else { + $key = call_user_func($this->indexBy, $model); + } + $models[$key] = $model; + } + } + } + return $models; + } + + private function populateRelations(&$models, $with) + { + $primaryModel = new $this->modelClass; + $relations = $this->normalizeRelations($primaryModel, $with); + foreach ($relations as $name => $relation) { + if ($relation->asArray === null) { + // inherit asArray from primary query + $relation->asArray = $this->asArray; + } + $relation->findWith($name, $models); + } + } + + /** + * @param ActiveRecord $model + * @param array $with + * @return ActiveRelation[] + */ + private function normalizeRelations($model, $with) + { + $relations = []; + foreach ($with as $name => $callback) { + if (is_integer($name)) { + $name = $callback; + $callback = null; + } + if (($pos = strpos($name, '.')) !== false) { + // with sub-relations + $childName = substr($name, $pos + 1); + $name = substr($name, 0, $pos); + } else { + $childName = null; + } + + $t = strtolower($name); + if (!isset($relations[$t])) { + $relation = $model->getRelation($name); + $relation->primaryModel = null; + $relations[$t] = $relation; + } else { + $relation = $relations[$t]; + } + + if (isset($childName)) { + $relation->with[$childName] = $callback; + } elseif ($callback !== null) { + call_user_func($callback, $relation); + } + } + return $relations; + } +} diff --git a/framework/yii/ar/ActiveRelation.php b/framework/yii/ar/ActiveRelation.php deleted file mode 100644 index 17bba67..0000000 --- a/framework/yii/ar/ActiveRelation.php +++ /dev/null @@ -1,253 +0,0 @@ - - * @since 2.0 - */ -trait ActiveRelation -{ - /** - * @var boolean whether this relation should populate all query results into AR instances. - * If false, only the first row of the results will be retrieved. - */ - public $multiple; - /** - * @var ActiveRecord the primary model that this relation is associated with. - * This is used only in lazy loading with dynamic query options. - */ - public $primaryModel; - /** - * @var array the columns of the primary and foreign tables that establish the relation. - * The array keys must be columns of the table for this relation, and the array values - * must be the corresponding columns from the primary table. - * Do not prefix or quote the column names as they will be done automatically by Yii. - */ - public $link; - /** - * @var array the query associated with the pivot table. Please call [[via()]] - * to set this property instead of directly setting it. - */ - 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. - * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. - * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. - * Its signature should be `function($query)`, where `$query` is the query to be customized. - * @return static the relation object itself. - */ - public function via($relationName, $callable = null) - { - $relation = $this->primaryModel->getRelation($relationName); - $this->via = [$relationName, $relation]; - if ($callable !== null) { - call_user_func($callable, $relation); - } - return $this; - } - - /** - * Finds the related records and populates them into the primary models. - * This method is internally used by [[ActiveQuery]]. Do not call it directly. - * @param string $name the relation name - * @param array $primaryModels primary models - * @return array the related models - * @throws InvalidConfigException - */ - public function findWith($name, &$primaryModels) - { - if (!is_array($this->link)) { - throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); - } - - if ($this->via instanceof self) { - // via pivot table - /** @var $viaQuery ActiveRelation */ - $viaQuery = $this->via; - $viaModels = $viaQuery->findPivotRows($primaryModels); - $this->filterByModels($viaModels); - } elseif (is_array($this->via)) { - // via relation - /** @var $viaQuery ActiveRelation */ - list($viaName, $viaQuery) = $this->via; - $viaQuery->primaryModel = null; - $viaModels = $viaQuery->findWith($viaName, $primaryModels); - $this->filterByModels($viaModels); - } else { - $this->filterByModels($primaryModels); - } - - if (count($primaryModels) === 1 && !$this->multiple) { - $model = $this->one(); - foreach ($primaryModels as $i => $primaryModel) { - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $model); - } else { - $primaryModels[$i][$name] = $model; - } - } - return [$model]; - } else { - $models = $this->all(); - if (isset($viaModels, $viaQuery)) { - $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); - } else { - $buckets = $this->buildBuckets($models, $this->link); - } - - $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); - foreach ($primaryModels as $i => $primaryModel) { - $key = $this->getModelKey($primaryModel, $link); - $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $value); - } else { - $primaryModels[$i][$name] = $value; - } - } - return $models; - } - } - - /** - * @param array $models - * @param array $link - * @param array $viaModels - * @param array $viaLink - * @return array - */ - private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) - { - $buckets = []; - $linkKeys = array_keys($link); - foreach ($models as $i => $model) { - $key = $this->getModelKey($model, $linkKeys); - if ($this->indexBy !== null) { - $buckets[$key][$i] = $model; - } else { - $buckets[$key][] = $model; - } - } - - if ($viaModels !== null) { - $viaBuckets = []; - $viaLinkKeys = array_keys($viaLink); - $linkValues = array_values($link); - foreach ($viaModels as $viaModel) { - $key1 = $this->getModelKey($viaModel, $viaLinkKeys); - $key2 = $this->getModelKey($viaModel, $linkValues); - if (isset($buckets[$key2])) { - foreach ($buckets[$key2] as $i => $bucket) { - if ($this->indexBy !== null) { - $viaBuckets[$key1][$i] = $bucket; - } else { - $viaBuckets[$key1][] = $bucket; - } - } - } - } - $buckets = $viaBuckets; - } - - if (!$this->multiple) { - foreach ($buckets as $i => $bucket) { - $buckets[$i] = reset($bucket); - } - } - return $buckets; - } - - /** - * @param ActiveRecord|array $model - * @param array $attributes - * @return string - */ - private function getModelKey($model, $attributes) - { - if (count($attributes) > 1) { - $key = []; - foreach ($attributes as $attribute) { - $key[] = $model[$attribute]; - } - return serialize($key); - } else { - $attribute = reset($attributes); - return $model[$attribute]; - } - } - - /** - * @param array $models - */ - private function filterByModels($models) - { - $attributes = array_keys($this->link); - $values = []; - if (count($attributes) === 1) { - // single key - $attribute = reset($this->link); - foreach ($models as $model) { - if (($value = $model[$attribute]) !== null) { - $values[] = $value; - } - } - } else { - // composite keys - foreach ($models as $model) { - $v = []; - foreach ($this->link as $attribute => $link) { - $v[$attribute] = $model[$link]; - } - $values[] = $v; - } - } - $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); - } - - /** - * @param ActiveRecord[] $primaryModels - * @return array - */ - private function findPivotRows($primaryModels) - { - if (empty($primaryModels)) { - return []; - } - $this->filterByModels($primaryModels); - /** @var $primaryModel ActiveRecord */ - $primaryModel = reset($primaryModels); - return $this->asArray()->all($primaryModel->getDb()); - } -} diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php new file mode 100644 index 0000000..ab80558 --- /dev/null +++ b/framework/yii/ar/ActiveRelationTrait.php @@ -0,0 +1,253 @@ + + * @since 2.0 + */ +trait ActiveRelationTrait +{ + /** + * @var boolean whether this relation should populate all query results into AR instances. + * If false, only the first row of the results will be retrieved. + */ + public $multiple; + /** + * @var ActiveRecord the primary model that this relation is associated with. + * This is used only in lazy loading with dynamic query options. + */ + public $primaryModel; + /** + * @var array the columns of the primary and foreign tables that establish the relation. + * The array keys must be columns of the table for this relation, and the array values + * must be the corresponding columns from the primary table. + * Do not prefix or quote the column names as they will be done automatically by Yii. + */ + public $link; + /** + * @var array the query associated with the pivot table. Please call [[via()]] + * to set this property instead of directly setting it. + */ + 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. + * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. + * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. + * Its signature should be `function($query)`, where `$query` is the query to be customized. + * @return static the relation object itself. + */ + public function via($relationName, $callable = null) + { + $relation = $this->primaryModel->getRelation($relationName); + $this->via = [$relationName, $relation]; + if ($callable !== null) { + call_user_func($callable, $relation); + } + return $this; + } + + /** + * Finds the related records and populates them into the primary models. + * This method is internally used by [[ActiveQuery]]. Do not call it directly. + * @param string $name the relation name + * @param array $primaryModels primary models + * @return array the related models + * @throws InvalidConfigException + */ + public function findWith($name, &$primaryModels) + { + if (!is_array($this->link)) { + throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); + } + + if ($this->via instanceof self) { + // via pivot table + /** @var $viaQuery ActiveRelation */ + $viaQuery = $this->via; + $viaModels = $viaQuery->findPivotRows($primaryModels); + $this->filterByModels($viaModels); + } elseif (is_array($this->via)) { + // via relation + /** @var $viaQuery ActiveRelation */ + list($viaName, $viaQuery) = $this->via; + $viaQuery->primaryModel = null; + $viaModels = $viaQuery->findWith($viaName, $primaryModels); + $this->filterByModels($viaModels); + } else { + $this->filterByModels($primaryModels); + } + + if (count($primaryModels) === 1 && !$this->multiple) { + $model = $this->one(); + foreach ($primaryModels as $i => $primaryModel) { + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $model); + } else { + $primaryModels[$i][$name] = $model; + } + } + return [$model]; + } else { + $models = $this->all(); + if (isset($viaModels, $viaQuery)) { + $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); + } else { + $buckets = $this->buildBuckets($models, $this->link); + } + + $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); + foreach ($primaryModels as $i => $primaryModel) { + $key = $this->getModelKey($primaryModel, $link); + $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $value); + } else { + $primaryModels[$i][$name] = $value; + } + } + return $models; + } + } + + /** + * @param array $models + * @param array $link + * @param array $viaModels + * @param array $viaLink + * @return array + */ + private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) + { + $buckets = []; + $linkKeys = array_keys($link); + foreach ($models as $i => $model) { + $key = $this->getModelKey($model, $linkKeys); + if ($this->indexBy !== null) { + $buckets[$key][$i] = $model; + } else { + $buckets[$key][] = $model; + } + } + + if ($viaModels !== null) { + $viaBuckets = []; + $viaLinkKeys = array_keys($viaLink); + $linkValues = array_values($link); + foreach ($viaModels as $viaModel) { + $key1 = $this->getModelKey($viaModel, $viaLinkKeys); + $key2 = $this->getModelKey($viaModel, $linkValues); + if (isset($buckets[$key2])) { + foreach ($buckets[$key2] as $i => $bucket) { + if ($this->indexBy !== null) { + $viaBuckets[$key1][$i] = $bucket; + } else { + $viaBuckets[$key1][] = $bucket; + } + } + } + } + $buckets = $viaBuckets; + } + + if (!$this->multiple) { + foreach ($buckets as $i => $bucket) { + $buckets[$i] = reset($bucket); + } + } + return $buckets; + } + + /** + * @param ActiveRecord|array $model + * @param array $attributes + * @return string + */ + private function getModelKey($model, $attributes) + { + if (count($attributes) > 1) { + $key = []; + foreach ($attributes as $attribute) { + $key[] = $model[$attribute]; + } + return serialize($key); + } else { + $attribute = reset($attributes); + return $model[$attribute]; + } + } + + /** + * @param array $models + */ + private function filterByModels($models) + { + $attributes = array_keys($this->link); + $values = []; + if (count($attributes) === 1) { + // single key + $attribute = reset($this->link); + foreach ($models as $model) { + if (($value = $model[$attribute]) !== null) { + $values[] = $value; + } + } + } else { + // composite keys + foreach ($models as $model) { + $v = []; + foreach ($this->link as $attribute => $link) { + $v[$attribute] = $model[$link]; + } + $values[] = $v; + } + } + $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); + } + + /** + * @param ActiveRecord[] $primaryModels + * @return array + */ + private function findPivotRows($primaryModels) + { + if (empty($primaryModels)) { + return []; + } + $this->filterByModels($primaryModels); + /** @var $primaryModel ActiveRecord */ + $primaryModel = reset($primaryModels); + return $this->asArray()->all($primaryModel->getDb()); + } +} diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 5ec3d7a..3b245fe 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -46,7 +46,7 @@ namespace yii\db; */ class ActiveQuery extends Query { - use \yii\ar\ActiveQuery; + use \yii\ar\ActiveQueryTrait; /** * @var string the SQL statement to be executed for retrieving AR records. diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 8ca1baf..72b5667 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -28,7 +28,7 @@ namespace yii\db; */ class ActiveRelation extends ActiveQuery { - use \yii\ar\ActiveRelation; + use \yii\ar\ActiveRelationTrait; /** * Specifies the pivot table. diff --git a/framework/yii/db/BaseQuery.php b/framework/yii/db/BaseQuery.php deleted file mode 100644 index 98fcdab..0000000 --- a/framework/yii/db/BaseQuery.php +++ /dev/null @@ -1,299 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -trait BaseQuery -{ - /** - * @var string|array query condition. This refers to the WHERE clause in a SQL statement. - * For example, `age > 31 AND team = 1`. - * @see where() - */ - public $where; - /** - * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit. - */ - public $limit; - /** - * @var integer zero-based offset from where the records are to be returned. If not set or - * less than 0, it means starting from the beginning. - */ - public $offset; - /** - * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement. - * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which - * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects. - * If that is the case, the expressions will be converted into strings without any change. - */ - public $orderBy; - /** - * @var string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row data. For more details, see [[indexBy()]]. This property is only used by [[all()]]. - */ - public $indexBy; - - /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row data. The signature of the callable should be: - * - * ~~~ - * function ($row) - * { - * // return the index value corresponding to $row - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column) - { - $this->indexBy = $column; - return $this; - } - - /** - * Executes the query and returns all results as an array. - * @return array the query results. If the query results in nothing, an empty array will be returned. - */ - abstract public function all(); - - /** - * Executes the query and returns a single row of result. - * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query - * results in nothing. - */ - abstract public function one(); - - /** - * Returns the number of records. - * @return integer number of records - */ - abstract public function count(); - - /** - * Returns a value indicating whether the query result contains any row of data. - * @return boolean whether the query result contains any row of data. - */ - abstract public function exists(); - - /** - * Sets the WHERE part of the query. - * - * The method requires a $condition parameter. - * - * The $condition parameter should be an array in one of the following two formats: - * - * - hash format: `['column1' => value1, 'column2' => value2, ...]` - * - operator format: `[operator, operand1, operand2, ...]` - * - * A condition in hash format represents the following SQL expression in general: - * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array, - * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used - * in the generated expression. Below are some examples: - * - * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`. - * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`. - * - `['status' => null] generates `status IS NULL`. - * - * A condition in operator format generates the SQL expression according to the specified operator, which - * can be one of the followings: - * - * - `and`: the operands should be concatenated together using `AND`. For example, - * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array, - * it will be converted into a string using the rules described here. For example, - * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`. - * The method will NOT do any quoting or escaping. - * - * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`. - * - * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the - * starting and ending values of the range that the column is in. - * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`. - * - * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` - * in the generated condition. - * - * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing - * the range of the values that the column or DB expression should be in. For example, - * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`. - * The method will properly quote the column name and escape values in the range. - * - * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition. - * - * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing - * the values that the column or DB expression should be like. - * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`. - * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated - * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate - * `name LIKE '%test%' AND name LIKE '%sample%'`. - * The method will properly quote the column name and escape values in the range. - * - * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE` - * predicates when operand 2 is an array. - * - * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE` - * in the generated condition. - * - * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate - * the `NOT LIKE` predicates. - * - * @param array $condition the conditions that should be put in the WHERE part. - * @return static the query object itself - * @see andWhere() - * @see orWhere() - */ - public function where($condition) - { - $this->where = $condition; - return $this; - } - - /** - * Adds an additional WHERE condition to the existing one. - * The new condition and the existing one will be joined using the 'AND' operator. - * @param string|array $condition the new WHERE condition. Please refer to [[where()]] - * on how to specify this parameter. - * @return static the query object itself - * @see where() - * @see orWhere() - */ - public function andWhere($condition) - { - if ($this->where === null) { - $this->where = $condition; - } else { - $this->where = ['and', $this->where, $condition]; - } - return $this; - } - - /** - * Adds an additional WHERE condition to the existing one. - * The new condition and the existing one will be joined using the 'OR' operator. - * @param string|array $condition the new WHERE condition. Please refer to [[where()]] - * on how to specify this parameter. - * @return static the query object itself - * @see where() - * @see andWhere() - */ - public function orWhere($condition) - { - if ($this->where === null) { - $this->where = $condition; - } else { - $this->where = ['or', $this->where, $condition]; - } - return $this; - } - - /** - * Sets the ORDER BY part of the query. - * @param string|array $columns the columns (and the directions) to be ordered by. - * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). - * The method will automatically quote the column names unless a column contains some parenthesis - * (which means the column contains a DB expression). - * @return static the query object itself - * @see addOrderBy() - */ - public function orderBy($columns) - { - $this->orderBy = $this->normalizeOrderBy($columns); - return $this; - } - - /** - * Adds additional ORDER BY columns to the query. - * @param string|array $columns the columns (and the directions) to be ordered by. - * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). - * The method will automatically quote the column names unless a column contains some parenthesis - * (which means the column contains a DB expression). - * @return static the query object itself - * @see orderBy() - */ - public function addOrderBy($columns) - { - $columns = $this->normalizeOrderBy($columns); - if ($this->orderBy === null) { - $this->orderBy = $columns; - } else { - $this->orderBy = array_merge($this->orderBy, $columns); - } - return $this; - } - - protected function normalizeOrderBy($columns) - { - if (is_array($columns)) { - return $columns; - } else { - $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); - $result = []; - foreach ($columns as $column) { - if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { - $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC; - } else { - $result[$column] = self::SORT_ASC; - } - } - return $result; - } - } - - /** - * Sets the LIMIT part of the query. - * @param integer $limit the limit. Use null or negative value to disable limit. - * @return static the query object itself - */ - public function limit($limit) - { - $this->limit = $limit; - return $this; - } - - /** - * Sets the OFFSET part of the query. - * @param integer $offset the offset. Use null or negative value to disable offset. - * @return static the query object itself - */ - public function offset($offset) - { - $this->offset = $offset; - return $this; - } -} \ No newline at end of file diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 73f8860..103cb67 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -37,7 +37,7 @@ use yii\base\Component; */ class Query extends Component { - use BaseQuery; + use QueryTrait; /** * Sort ascending diff --git a/framework/yii/db/QueryTrait.php b/framework/yii/db/QueryTrait.php new file mode 100644 index 0000000..92051ab --- /dev/null +++ b/framework/yii/db/QueryTrait.php @@ -0,0 +1,299 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +trait QueryTrait +{ + /** + * @var string|array query condition. This refers to the WHERE clause in a SQL statement. + * For example, `age > 31 AND team = 1`. + * @see where() + */ + public $where; + /** + * @var integer maximum number of records to be returned. If not set or less than 0, it means no limit. + */ + public $limit; + /** + * @var integer zero-based offset from where the records are to be returned. If not set or + * less than 0, it means starting from the beginning. + */ + public $offset; + /** + * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement. + * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which + * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects. + * If that is the case, the expressions will be converted into strings without any change. + */ + public $orderBy; + /** + * @var string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row data. For more details, see [[indexBy()]]. This property is only used by [[all()]]. + */ + public $indexBy; + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row data. The signature of the callable should be: + * + * ~~~ + * function ($row) + * { + * // return the index value corresponding to $row + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column) + { + $this->indexBy = $column; + return $this; + } + + /** + * Executes the query and returns all results as an array. + * @return array the query results. If the query results in nothing, an empty array will be returned. + */ + abstract public function all(); + + /** + * Executes the query and returns a single row of result. + * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query + * results in nothing. + */ + abstract public function one(); + + /** + * Returns the number of records. + * @return integer number of records + */ + abstract public function count(); + + /** + * Returns a value indicating whether the query result contains any row of data. + * @return boolean whether the query result contains any row of data. + */ + abstract public function exists(); + + /** + * Sets the WHERE part of the query. + * + * The method requires a $condition parameter. + * + * The $condition parameter should be an array in one of the following two formats: + * + * - hash format: `['column1' => value1, 'column2' => value2, ...]` + * - operator format: `[operator, operand1, operand2, ...]` + * + * A condition in hash format represents the following SQL expression in general: + * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array, + * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used + * in the generated expression. Below are some examples: + * + * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`. + * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`. + * - `['status' => null] generates `status IS NULL`. + * + * A condition in operator format generates the SQL expression according to the specified operator, which + * can be one of the followings: + * + * - `and`: the operands should be concatenated together using `AND`. For example, + * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array, + * it will be converted into a string using the rules described here. For example, + * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`. + * The method will NOT do any quoting or escaping. + * + * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`. + * + * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the + * starting and ending values of the range that the column is in. + * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`. + * + * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` + * in the generated condition. + * + * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing + * the range of the values that the column or DB expression should be in. For example, + * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`. + * The method will properly quote the column name and escape values in the range. + * + * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition. + * + * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing + * the values that the column or DB expression should be like. + * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`. + * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated + * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate + * `name LIKE '%test%' AND name LIKE '%sample%'`. + * The method will properly quote the column name and escape values in the range. + * + * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE` + * predicates when operand 2 is an array. + * + * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE` + * in the generated condition. + * + * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate + * the `NOT LIKE` predicates. + * + * @param array $condition the conditions that should be put in the WHERE part. + * @return static the query object itself + * @see andWhere() + * @see orWhere() + */ + public function where($condition) + { + $this->where = $condition; + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'AND' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see orWhere() + */ + public function andWhere($condition) + { + if ($this->where === null) { + $this->where = $condition; + } else { + $this->where = ['and', $this->where, $condition]; + } + return $this; + } + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'OR' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see andWhere() + */ + public function orWhere($condition) + { + if ($this->where === null) { + $this->where = $condition; + } else { + $this->where = ['or', $this->where, $condition]; + } + return $this; + } + + /** + * Sets the ORDER BY part of the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see addOrderBy() + */ + public function orderBy($columns) + { + $this->orderBy = $this->normalizeOrderBy($columns); + return $this; + } + + /** + * Adds additional ORDER BY columns to the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see orderBy() + */ + public function addOrderBy($columns) + { + $columns = $this->normalizeOrderBy($columns); + if ($this->orderBy === null) { + $this->orderBy = $columns; + } else { + $this->orderBy = array_merge($this->orderBy, $columns); + } + return $this; + } + + protected function normalizeOrderBy($columns) + { + if (is_array($columns)) { + return $columns; + } else { + $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); + $result = []; + foreach ($columns as $column) { + if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { + $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC; + } else { + $result[$column] = self::SORT_ASC; + } + } + return $result; + } + } + + /** + * Sets the LIMIT part of the query. + * @param integer $limit the limit. Use null or negative value to disable limit. + * @return static the query object itself + */ + public function limit($limit) + { + $this->limit = $limit; + return $this; + } + + /** + * Sets the OFFSET part of the query. + * @param integer $offset the offset. Use null or negative value to disable offset. + * @return static the query object itself + */ + public function offset($offset) + { + $this->offset = $offset; + return $this; + } +} \ No newline at end of file From 4c4f7bd70869b39ae57118d5ffb798c4472715f1 Mon Sep 17 00:00:00 2001 From: slavcodev Date: Wed, 13 Nov 2013 18:17:05 +0200 Subject: [PATCH 65/79] Move AppAsset from config to assets directory --- apps/advanced/backend/assets/.gitkeep | 1 - apps/advanced/backend/assets/AppAsset.php | 26 ++++++++++++++++++++++++ apps/advanced/backend/config/AppAsset.php | 26 ------------------------ apps/advanced/backend/views/layouts/main.php | 2 +- apps/advanced/frontend/assets/.gitkeep | 2 -- apps/advanced/frontend/assets/AppAsset.php | 29 +++++++++++++++++++++++++++ apps/advanced/frontend/config/AppAsset.php | 29 --------------------------- apps/advanced/frontend/views/layouts/main.php | 2 +- apps/basic/assets/AppAsset.php | 29 +++++++++++++++++++++++++++ apps/basic/config/AppAsset.php | 29 --------------------------- apps/basic/views/layouts/main.php | 2 +- docs/guide/view.md | 2 +- 12 files changed, 88 insertions(+), 91 deletions(-) delete mode 100644 apps/advanced/backend/assets/.gitkeep create mode 100644 apps/advanced/backend/assets/AppAsset.php delete mode 100644 apps/advanced/backend/config/AppAsset.php delete mode 100644 apps/advanced/frontend/assets/.gitkeep create mode 100644 apps/advanced/frontend/assets/AppAsset.php delete mode 100644 apps/advanced/frontend/config/AppAsset.php create mode 100644 apps/basic/assets/AppAsset.php delete mode 100644 apps/basic/config/AppAsset.php diff --git a/apps/advanced/backend/assets/.gitkeep b/apps/advanced/backend/assets/.gitkeep deleted file mode 100644 index 72e8ffc..0000000 --- a/apps/advanced/backend/assets/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/apps/advanced/backend/assets/AppAsset.php b/apps/advanced/backend/assets/AppAsset.php new file mode 100644 index 0000000..bd5c3a0 --- /dev/null +++ b/apps/advanced/backend/assets/AppAsset.php @@ -0,0 +1,26 @@ + + * @since 2.0 + */ +class AppAsset extends AssetBundle +{ + public $basePath = '@webroot'; + public $baseUrl = '@web'; + public $css = ['css/site.css']; + public $js = []; + public $depends = [ + 'yii\web\YiiAsset', + 'yii\bootstrap\BootstrapAsset', + ]; +} diff --git a/apps/advanced/backend/config/AppAsset.php b/apps/advanced/backend/config/AppAsset.php deleted file mode 100644 index 2fd15ca..0000000 --- a/apps/advanced/backend/config/AppAsset.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @since 2.0 - */ -class AppAsset extends AssetBundle -{ - public $basePath = '@webroot'; - public $baseUrl = '@web'; - public $css = ['css/site.css']; - public $js = []; - public $depends = [ - 'yii\web\YiiAsset', - 'yii\bootstrap\BootstrapAsset', - ]; -} diff --git a/apps/advanced/backend/views/layouts/main.php b/apps/advanced/backend/views/layouts/main.php index 0093326..9f0280d 100644 --- a/apps/advanced/backend/views/layouts/main.php +++ b/apps/advanced/backend/views/layouts/main.php @@ -1,5 +1,5 @@ + * @since 2.0 + */ +class AppAsset extends AssetBundle +{ + public $basePath = '@webroot'; + public $baseUrl = '@web'; + public $css = [ + 'css/site.css', + ]; + public $js = [ + ]; + public $depends = [ + 'yii\web\YiiAsset', + 'yii\bootstrap\BootstrapAsset', + ]; +} diff --git a/apps/advanced/frontend/config/AppAsset.php b/apps/advanced/frontend/config/AppAsset.php deleted file mode 100644 index 98306e3..0000000 --- a/apps/advanced/frontend/config/AppAsset.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @since 2.0 - */ -class AppAsset extends AssetBundle -{ - public $basePath = '@webroot'; - public $baseUrl = '@web'; - public $css = [ - 'css/site.css', - ]; - public $js = [ - ]; - public $depends = [ - 'yii\web\YiiAsset', - 'yii\bootstrap\BootstrapAsset', - ]; -} diff --git a/apps/advanced/frontend/views/layouts/main.php b/apps/advanced/frontend/views/layouts/main.php index ec78ac0..7b2ce6f 100644 --- a/apps/advanced/frontend/views/layouts/main.php +++ b/apps/advanced/frontend/views/layouts/main.php @@ -3,7 +3,7 @@ use yii\helpers\Html; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; -use frontend\config\AppAsset; +use frontend\assets\AppAsset; use frontend\widgets\Alert; /** diff --git a/apps/basic/assets/AppAsset.php b/apps/basic/assets/AppAsset.php new file mode 100644 index 0000000..c964d36 --- /dev/null +++ b/apps/basic/assets/AppAsset.php @@ -0,0 +1,29 @@ + + * @since 2.0 + */ +class AppAsset extends AssetBundle +{ + public $basePath = '@webroot'; + public $baseUrl = '@web'; + public $css = [ + 'css/site.css', + ]; + public $js = [ + ]; + public $depends = [ + 'yii\web\YiiAsset', + 'yii\bootstrap\BootstrapAsset', + ]; +} diff --git a/apps/basic/config/AppAsset.php b/apps/basic/config/AppAsset.php deleted file mode 100644 index 87f6f8b..0000000 --- a/apps/basic/config/AppAsset.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @since 2.0 - */ -class AppAsset extends AssetBundle -{ - public $basePath = '@webroot'; - public $baseUrl = '@web'; - public $css = [ - 'css/site.css', - ]; - public $js = [ - ]; - public $depends = [ - 'yii\web\YiiAsset', - 'yii\bootstrap\BootstrapAsset', - ]; -} diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php index 49b0f4c..8489e9c 100644 --- a/apps/basic/views/layouts/main.php +++ b/apps/basic/views/layouts/main.php @@ -3,7 +3,7 @@ use yii\helpers\Html; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; -use app\config\AppAsset; +use app\assets\AppAsset; /** * @var \yii\web\View $this diff --git a/docs/guide/view.md b/docs/guide/view.md index 1b366a6..5951a30 100644 --- a/docs/guide/view.md +++ b/docs/guide/view.md @@ -240,7 +240,7 @@ details on how to define asset bundles in [asset manager](assets.md) section of asset bundle, it's very straightforward: ```php -frontend\config\AppAsset::register($this); +frontend\assets\AppAsset::register($this); ``` ### Layout From 65338972c7f7e6fee0e86bb300cc2502f42edcc1 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 18:42:50 +0100 Subject: [PATCH 66/79] refactored ActiveRecord classes to use Interfaces and traits this allows us to implement other activerecord implementations based on NoSQL dbms --- docs/guide/query-builder.md | 4 +- framework/yii/ar/ActiveQueryInterface.php | 59 ++++++++ framework/yii/ar/ActiveQueryTrait.php | 16 ++- framework/yii/ar/ActiveRelationInterface.php | 36 +++++ framework/yii/ar/ActiveRelationTrait.php | 1 + framework/yii/data/ActiveDataProvider.php | 20 +-- framework/yii/db/ActiveQuery.php | 10 +- framework/yii/db/ActiveRecord.php | 28 ++-- framework/yii/db/ActiveRelation.php | 13 +- framework/yii/db/Query.php | 20 +-- framework/yii/db/QueryBuilder.php | 2 +- framework/yii/db/QueryInterface.php | 205 +++++++++++++++++++++++++++ framework/yii/db/QueryTrait.php | 109 ++------------ 13 files changed, 369 insertions(+), 154 deletions(-) create mode 100644 framework/yii/ar/ActiveQueryInterface.php create mode 100644 framework/yii/ar/ActiveRelationInterface.php create mode 100644 framework/yii/db/QueryInterface.php diff --git a/docs/guide/query-builder.md b/docs/guide/query-builder.md index 7625c0b..ac79f1d 100644 --- a/docs/guide/query-builder.md +++ b/docs/guide/query-builder.md @@ -167,8 +167,8 @@ For ordering results `orderBy` and `addOrderBy` could be used: ```php $query->orderBy([ - 'id' => Query::SORT_ASC, - 'name' => Query::SORT_DESC, + 'id' => SORT_ASC, + 'name' => SORT_DESC, ]); ``` diff --git a/framework/yii/ar/ActiveQueryInterface.php b/framework/yii/ar/ActiveQueryInterface.php new file mode 100644 index 0000000..e2bb780 --- /dev/null +++ b/framework/yii/ar/ActiveQueryInterface.php @@ -0,0 +1,59 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +interface ActiveQueryInterface extends QueryInterface +{ + /** + * Sets the [[asArray]] property. + * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. + * @return static the query object itself + */ + public function asArray($value = true); + + /** + * Specifies the relations with which this query should be performed. + * + * The parameters to this method can be either one or multiple strings, or a single array + * of relation names and the optional callbacks to customize the relations. + * + * A relation name can refer to a relation defined in [[modelClass]] + * or a sub-relation that stands for a relation of a related record. + * For example, `orders.address` means the `address` relation defined + * in the model class corresponding to the `orders` relation. + * + * The followings are some usage examples: + * + * ~~~ + * // find customers together with their orders and country + * Customer::find()->with('orders', 'country')->all(); + * // find customers together with their orders and the orders' shipping address + * Customer::find()->with('orders.address')->all(); + * // find customers together with their country and orders of status 1 + * Customer::find()->with([ + * 'orders' => function($query) { + * $query->andWhere('status = 1'); + * }, + * 'country', + * ])->all(); + * ~~~ + * + * @return static the query object itself + */ + public function with(); +} diff --git a/framework/yii/ar/ActiveQueryTrait.php b/framework/yii/ar/ActiveQueryTrait.php index 1a4336f..8c8d6c0 100644 --- a/framework/yii/ar/ActiveQueryTrait.php +++ b/framework/yii/ar/ActiveQueryTrait.php @@ -10,10 +10,7 @@ namespace yii\ar; use yii\db\ActiveRecord; /** - * ActiveQuery represents a DB query associated with an Active Record class. - * - * ActiveQuery instances are usually created by [[ActiveRecord::find()]], [[ActiveRecord::findBySql()]] - * and [[ActiveRecord::count()]]. + * ActiveQueryTrait implements the common set of methods that are used by DB queries associated with an Active Record class. * * ActiveQuery mainly provides the following methods to retrieve the query results: * @@ -150,6 +147,11 @@ trait ActiveQueryTrait return parent::indexBy($column); } + /** + * Converts found rows into model instances + * @param array $rows + * @return array|ActiveRecord[] + */ private function createModels($rows) { $models = []; @@ -187,6 +189,10 @@ trait ActiveQueryTrait return $models; } + /** + * @param ActiveRecord[] $models + * @param array $with + */ private function populateRelations(&$models, $with) { $primaryModel = new $this->modelClass; @@ -203,7 +209,7 @@ trait ActiveQueryTrait /** * @param ActiveRecord $model * @param array $with - * @return ActiveRelation[] + * @return ActiveRelationInterface[] */ private function normalizeRelations($model, $with) { diff --git a/framework/yii/ar/ActiveRelationInterface.php b/framework/yii/ar/ActiveRelationInterface.php new file mode 100644 index 0000000..70b723a --- /dev/null +++ b/framework/yii/ar/ActiveRelationInterface.php @@ -0,0 +1,36 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +interface ActiveRelationInterface extends ActiveQueryInterface +{ + /** + * Specifies the relation associated with the pivot table. + * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. + * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. + * Its signature should be `function($query)`, where `$query` is the query to be customized. + * @return static the relation object itself. + */ + public function via($relationName, $callable = null); + + /** + * Finds the related records and populates them into the primary models. + * This method is internally used by [[ActiveQuery]]. Do not call it directly. + * @param string $name the relation name + * @param array $primaryModels primary models + * @return array the related models + */ + public function findWith($name, &$primaryModels); +} diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php index 6d30ebc..350718d 100644 --- a/framework/yii/ar/ActiveRelationTrait.php +++ b/framework/yii/ar/ActiveRelationTrait.php @@ -23,6 +23,7 @@ use yii\db\ActiveRecord; * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method. * * @author Qiang Xue + * @author Carsten Brandt * @since 2.0 */ trait ActiveRelationTrait diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index ca7696f..3766692 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -8,11 +8,11 @@ namespace yii\data; use Yii; +use yii\ar\ActiveQueryInterface; use yii\base\InvalidConfigException; use yii\base\Model; -use yii\db\Query; -use yii\db\ActiveQuery; use yii\db\Connection; +use yii\db\QueryInterface; /** * ActiveDataProvider implements a data provider based on [[Query]] and [[ActiveQuery]]. @@ -54,7 +54,7 @@ use yii\db\Connection; class ActiveDataProvider extends BaseDataProvider { /** - * @var Query the query that is used to fetch data models and [[totalCount]] + * @var QueryInterface the query that is used to fetch data models and [[totalCount]] * if it is not explicitly set. */ public $query; @@ -97,8 +97,8 @@ class ActiveDataProvider extends BaseDataProvider */ protected function prepareModels() { - if (!$this->query instanceof Query) { - throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.'); + if (!$this->query instanceof QueryInterface) { + throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.'); } if (($pagination = $this->getPagination()) !== false) { $pagination->totalCount = $this->getTotalCount(); @@ -125,7 +125,7 @@ class ActiveDataProvider extends BaseDataProvider } } return $keys; - } elseif ($this->query instanceof ActiveQuery) { + } elseif ($this->query instanceof ActiveQueryInterface) { /** @var \yii\db\ActiveRecord $class */ $class = $this->query->modelClass; $pks = $class::primaryKey(); @@ -154,11 +154,11 @@ class ActiveDataProvider extends BaseDataProvider */ protected function prepareTotalCount() { - if (!$this->query instanceof Query) { - throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.'); + if (!$this->query instanceof QueryInterface) { + throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.'); } $query = clone $this->query; - return (int) $query->limit(-1)->offset(-1)->count('*', $this->db); + return (int) $query->limit(-1)->offset(-1)->count($this->db); } /** @@ -167,7 +167,7 @@ class ActiveDataProvider extends BaseDataProvider public function setSort($value) { parent::setSort($value); - if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQuery) { + if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQueryInterface) { /** @var Model $model */ $model = new $this->query->modelClass; foreach ($model->attributes() as $attribute) { diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 94494bc..3995b1b 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -7,12 +7,13 @@ */ namespace yii\db; +use yii\ar\ActiveQueryInterface; +use yii\ar\ActiveQueryTrait; /** * ActiveQuery represents a DB query associated with an Active Record class. * - * ActiveQuery instances are usually created by [[ActiveRecord::find()]], [[ActiveRecord::findBySql()]] - * and [[ActiveRecord::count()]]. + * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]]. * * ActiveQuery mainly provides the following methods to retrieve the query results: * @@ -42,11 +43,12 @@ namespace yii\db; * ~~~ * * @author Qiang Xue + * @author Carsten Brandt * @since 2.0 */ -class ActiveQuery extends Query +class ActiveQuery extends Query implements ActiveQueryInterface { - use \yii\ar\ActiveQueryTrait; + use ActiveQueryTrait; /** * @var string the SQL statement to be executed for retrieving AR records. diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 57c5c8b..852d6f5 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -8,6 +8,7 @@ namespace yii\db; +use yii\ar\ActiveRelationInterface; use yii\base\InvalidConfigException; use yii\base\Model; use yii\base\InvalidParamException; @@ -36,6 +37,7 @@ use yii\helpers\Inflector; * the key value is null). This property is read-only. * * @author Qiang Xue + * @author Carsten Brandt * @since 2.0 */ class ActiveRecord extends Model @@ -95,10 +97,6 @@ class ActiveRecord extends Model const OP_ALL = 0x07; /** - * @var string the name of the class to use for relations. - */ - protected $relationClassName = 'yii\\db\\ActiveRelation'; - /** * @var array attribute values indexed by attribute names */ private $_attributes = []; @@ -256,7 +254,7 @@ class ActiveRecord extends Model /** * Creates an [[ActiveQuery]] instance. - * This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query. + * This method is called by [[find()]], [[findBySql()]] to start a SELECT query. * You may override this method to return a customized query (e.g. `CustomerQuery` specified * written for querying `Customer` purpose.) * @return ActiveQuery the newly created [[ActiveQuery]] instance. @@ -389,7 +387,7 @@ class ActiveRecord extends Model return $this->_related[$name]; } $value = parent::__get($name); - if ($value instanceof $this->relationClassName) { + if ($value instanceof ActiveRelationInterface) { return $this->_related[$name] = $value->multiple ? $value->all() : $value->one(); } else { return $value; @@ -478,7 +476,7 @@ class ActiveRecord extends Model */ public function hasOne($class, $link) { - return new $this->relationClassName([ + return $this->createActiveRelation([ 'modelClass' => $class, 'primaryModel' => $this, 'link' => $link, @@ -516,7 +514,7 @@ class ActiveRecord extends Model */ public function hasMany($class, $link) { - return new $this->relationClassName([ + return $this->createActiveRelation([ 'modelClass' => $class, 'primaryModel' => $this, 'link' => $link, @@ -525,6 +523,18 @@ class ActiveRecord extends Model } /** + * Creates an [[ActiveRelation]] instance. + * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance. + * You may override this method to return a customized relation. + * @param array $config the configuration passed to the ActiveRelation class. + * @return ActiveRelation the newly created [[ActiveRelation]] instance. + */ + protected function createActiveRelation($config = []) + { + return new ActiveRelation($config); + } + + /** * Populates the named relation with the related records. * Note that this method does not check if the relation exists or not. * @param string $name the relation name (case-sensitive) @@ -1287,7 +1297,7 @@ class ActiveRecord extends Model $getter = 'get' . $name; try { $relation = $this->$getter(); - if ($relation instanceof $this->relationClassName) { + if ($relation instanceof ActiveRelationInterface) { return $relation; } else { return null; diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 13ceb14..7bc449a 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -7,6 +7,8 @@ */ namespace yii\db; +use yii\ar\ActiveRelationInterface; +use yii\ar\ActiveRelationTrait; /** * ActiveRelation represents a relation between two Active Record classes. @@ -24,17 +26,12 @@ namespace yii\db; * or [[viaTable()]] to set this property instead of directly setting it. * * @author Qiang Xue + * @author Carsten Brandt * @since 2.0 */ -class ActiveRelation extends ActiveQuery +class ActiveRelation extends ActiveQuery implements ActiveRelationInterface { - use \yii\ar\ActiveRelationTrait; - - /** - * @var array|ActiveRelation the query associated with the pivot table. Please call [[via()]] - * or [[viaTable()]] to set this property instead of directly setting it. - */ - public $via; + use ActiveRelationTrait; /** * Specifies the pivot table. diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 0a26fd9..0ae5f44 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -33,24 +33,14 @@ use yii\base\Component; * ~~~ * * @author Qiang Xue + * @author Carsten Brandt * @since 2.0 */ -class Query extends Component +class Query extends Component implements QueryInterface { use QueryTrait; /** - * Sort ascending - * @see orderBy() - */ - const SORT_ASC = false; - /** - * Sort descending - * @see orderBy() - */ - const SORT_DESC = true; - - /** * @var array the columns being selected. For example, `['id', 'name']`. * This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns. * @see select() @@ -189,13 +179,13 @@ class Query extends Component /** * Returns the number of records. + * @param Connection $db the database connection used to generate the SQL statement. + * If this parameter is not given (or null), the `db` application component will be used. * @param string $q the COUNT expression. Defaults to '*'. * Make sure you properly quote column names in the expression. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. * @return integer number of records */ - public function count($q = '*', $db = null) + public function count($db = null, $q = '*') { $this->select = ["COUNT($q)"]; return $this->createCommand($db)->queryScalar(); diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php index 09948c8..0a547ae 100644 --- a/framework/yii/db/QueryBuilder.php +++ b/framework/yii/db/QueryBuilder.php @@ -683,7 +683,7 @@ class QueryBuilder extends \yii\base\Object if (is_object($direction)) { $orders[] = (string)$direction; } else { - $orders[] = $this->db->quoteColumnName($name) . ($direction === Query::SORT_DESC ? ' DESC' : ''); + $orders[] = $this->db->quoteColumnName($name) . ($direction === SORT_DESC ? ' DESC' : ''); } } diff --git a/framework/yii/db/QueryInterface.php b/framework/yii/db/QueryInterface.php new file mode 100644 index 0000000..d13fa2b --- /dev/null +++ b/framework/yii/db/QueryInterface.php @@ -0,0 +1,205 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +interface QueryInterface +{ + /** + * Executes the query and returns all results as an array. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `db` application component will be used. + * @return array the query results. If the query results in nothing, an empty array will be returned. + */ + public function all($db = null); + + /** + * Executes the query and returns a single row of result. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `db` application component 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. + */ + public function one($db = null); + + /** + * Returns the number of records. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `db` application component will be used. + * @return integer number of records + */ + public function count($db = null); + + /** + * Returns a value indicating whether the query result contains any row of data. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `db` application component will be used. + * @return boolean whether the query result contains any row of data. + */ + public function exists($db = null); + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row data. The signature of the callable should be: + * + * ~~~ + * function ($row) + * { + * // return the index value corresponding to $row + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column); + + /** + * Sets the WHERE part of the query. + * + * The method requires a $condition parameter. + * + * The $condition parameter should be an array in one of the following two formats: + * + * - hash format: `['column1' => value1, 'column2' => value2, ...]` + * - operator format: `[operator, operand1, operand2, ...]` + * + * A condition in hash format represents the following SQL expression in general: + * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array, + * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used + * in the generated expression. Below are some examples: + * + * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`. + * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`. + * - `['status' => null] generates `status IS NULL`. + * + * A condition in operator format generates the SQL expression according to the specified operator, which + * can be one of the followings: + * + * - `and`: the operands should be concatenated together using `AND`. For example, + * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array, + * it will be converted into a string using the rules described here. For example, + * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`. + * The method will NOT do any quoting or escaping. + * + * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`. + * + * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the + * starting and ending values of the range that the column is in. + * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`. + * + * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` + * in the generated condition. + * + * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing + * the range of the values that the column or DB expression should be in. For example, + * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`. + * The method will properly quote the column name and escape values in the range. + * + * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition. + * + * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing + * the values that the column or DB expression should be like. + * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`. + * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated + * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate + * `name LIKE '%test%' AND name LIKE '%sample%'`. + * The method will properly quote the column name and escape values in the range. + * + * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE` + * predicates when operand 2 is an array. + * + * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE` + * in the generated condition. + * + * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate + * the `NOT LIKE` predicates. + * + * @param array $condition the conditions that should be put in the WHERE part. + * @return static the query object itself + * @see andWhere() + * @see orWhere() + */ + public function where($condition); + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'AND' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see orWhere() + */ + public function andWhere($condition); + + /** + * Adds an additional WHERE condition to the existing one. + * The new condition and the existing one will be joined using the 'OR' operator. + * @param string|array $condition the new WHERE condition. Please refer to [[where()]] + * on how to specify this parameter. + * @return static the query object itself + * @see where() + * @see andWhere() + */ + public function orWhere($condition); + + /** + * Sets the ORDER BY part of the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see addOrderBy() + */ + public function orderBy($columns); + + /** + * Adds additional ORDER BY columns to the query. + * @param string|array $columns the columns (and the directions) to be ordered by. + * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array + * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). + * The method will automatically quote the column names unless a column contains some parenthesis + * (which means the column contains a DB expression). + * @return static the query object itself + * @see orderBy() + */ + public function addOrderBy($columns); + + /** + * Sets the LIMIT part of the query. + * @param integer $limit the limit. Use null or negative value to disable limit. + * @return static the query object itself + */ + public function limit($limit); + + /** + * Sets the OFFSET part of the query. + * @param integer $offset the offset. Use null or negative value to disable offset. + * @return static the query object itself + */ + public function offset($offset); +} \ No newline at end of file diff --git a/framework/yii/db/QueryTrait.php b/framework/yii/db/QueryTrait.php index 92051ab..a963869 100644 --- a/framework/yii/db/QueryTrait.php +++ b/framework/yii/db/QueryTrait.php @@ -7,18 +7,6 @@ namespace yii\db; -// TODO where to put these constants? -/** - * Sort ascending - * @see orderBy - */ -const SORT_ASC = false; -/** - * Sort descending - * @see orderBy - */ -const SORT_DESC = true; - /** * The BaseQuery trait represents the minimum method set of a database Query. * @@ -53,8 +41,10 @@ trait QueryTrait /** * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement. * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which - * can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects. - * If that is the case, the expressions will be converted into strings without any change. + * can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc) + * or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc). + * The array may also contain [[Expression]] objects. If that is the case, the expressions + * will be converted into strings without any change. */ public $orderBy; /** @@ -86,90 +76,9 @@ trait QueryTrait } /** - * Executes the query and returns all results as an array. - * @return array the query results. If the query results in nothing, an empty array will be returned. - */ - abstract public function all(); - - /** - * Executes the query and returns a single row of result. - * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query - * results in nothing. - */ - abstract public function one(); - - /** - * Returns the number of records. - * @return integer number of records - */ - abstract public function count(); - - /** - * Returns a value indicating whether the query result contains any row of data. - * @return boolean whether the query result contains any row of data. - */ - abstract public function exists(); - - /** * Sets the WHERE part of the query. * - * The method requires a $condition parameter. - * - * The $condition parameter should be an array in one of the following two formats: - * - * - hash format: `['column1' => value1, 'column2' => value2, ...]` - * - operator format: `[operator, operand1, operand2, ...]` - * - * A condition in hash format represents the following SQL expression in general: - * `column1=value1 AND column2=value2 AND ...`. In case when a value is an array, - * an `IN` expression will be generated. And if a value is null, `IS NULL` will be used - * in the generated expression. Below are some examples: - * - * - `['type' => 1, 'status' => 2]` generates `(type = 1) AND (status = 2)`. - * - `['id' => [1, 2, 3], 'status' => 2]` generates `(id IN (1, 2, 3)) AND (status = 2)`. - * - `['status' => null] generates `status IS NULL`. - * - * A condition in operator format generates the SQL expression according to the specified operator, which - * can be one of the followings: - * - * - `and`: the operands should be concatenated together using `AND`. For example, - * `['and', 'id=1', 'id=2']` will generate `id=1 AND id=2`. If an operand is an array, - * it will be converted into a string using the rules described here. For example, - * `['and', 'type=1', ['or', 'id=1', 'id=2']]` will generate `type=1 AND (id=1 OR id=2)`. - * The method will NOT do any quoting or escaping. - * - * - `or`: similar to the `and` operator except that the operands are concatenated using `OR`. - * - * - `between`: operand 1 should be the column name, and operand 2 and 3 should be the - * starting and ending values of the range that the column is in. - * For example, `['between', 'id', 1, 10]` will generate `id BETWEEN 1 AND 10`. - * - * - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN` - * in the generated condition. - * - * - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing - * the range of the values that the column or DB expression should be in. For example, - * `['in', 'id', [1, 2, 3]]` will generate `id IN (1, 2, 3)`. - * The method will properly quote the column name and escape values in the range. - * - * - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition. - * - * - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing - * the values that the column or DB expression should be like. - * For example, `['like', 'name', '%tester%']` will generate `name LIKE '%tester%'`. - * When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated - * using `AND`. For example, `['like', 'name', ['%test%', '%sample%']]` will generate - * `name LIKE '%test%' AND name LIKE '%sample%'`. - * The method will properly quote the column name and escape values in the range. - * - * - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE` - * predicates when operand 2 is an array. - * - * - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE` - * in the generated condition. - * - * - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate - * the `NOT LIKE` predicates. + * See [[QueryInterface::where()]] for detailed documentation. * * @param array $condition the conditions that should be put in the WHERE part. * @return static the query object itself @@ -224,7 +133,7 @@ trait QueryTrait * Sets the ORDER BY part of the query. * @param string|array $columns the columns (and the directions) to be ordered by. * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). * The method will automatically quote the column names unless a column contains some parenthesis * (which means the column contains a DB expression). * @return static the query object itself @@ -240,7 +149,7 @@ trait QueryTrait * Adds additional ORDER BY columns to the query. * @param string|array $columns the columns (and the directions) to be ordered by. * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array - * (e.g. `['id' => Query::SORT_ASC, 'name' => Query::SORT_DESC]`). + * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`). * The method will automatically quote the column names unless a column contains some parenthesis * (which means the column contains a DB expression). * @return static the query object itself @@ -266,9 +175,9 @@ trait QueryTrait $result = []; foreach ($columns as $column) { if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) { - $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? self::SORT_ASC : self::SORT_DESC; + $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC; } else { - $result[$column] = self::SORT_ASC; + $result[$column] = SORT_ASC; } } return $result; From 9448c3d471de2e83f3db112e8d60ebc617bec12d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 19:35:44 +0100 Subject: [PATCH 67/79] added unit tests for active dataprovider and fixed query tests --- framework/yii/ar/ActiveRelationTrait.php | 4 +- .../unit/framework/data/ActiveDataProviderTest.php | 74 ++++++++++++++++++++++ tests/unit/framework/db/ActiveRecordTest.php | 16 +++++ tests/unit/framework/db/QueryTest.php | 12 ++-- .../db/cubrid/CubridActiveDataProviderTest.php | 14 ++++ .../db/mssql/MssqlActiveDataProviderTest.php | 14 ++++ .../db/pgsql/PostgreSQLActiveDataProviderTest.php | 14 ++++ .../db/sqlite/SqliteActiveDataProviderTest.php | 14 ++++ 8 files changed, 154 insertions(+), 8 deletions(-) create mode 100644 tests/unit/framework/db/cubrid/CubridActiveDataProviderTest.php create mode 100644 tests/unit/framework/db/mssql/MssqlActiveDataProviderTest.php create mode 100644 tests/unit/framework/db/pgsql/PostgreSQLActiveDataProviderTest.php create mode 100644 tests/unit/framework/db/sqlite/SqliteActiveDataProviderTest.php diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php index 350718d..5d49641 100644 --- a/framework/yii/ar/ActiveRelationTrait.php +++ b/framework/yii/ar/ActiveRelationTrait.php @@ -97,13 +97,13 @@ trait ActiveRelationTrait if ($this->via instanceof self) { // via pivot table - /** @var ActiveRelation $viaQuery */ + /** @var ActiveRelationTrait $viaQuery */ $viaQuery = $this->via; $viaModels = $viaQuery->findPivotRows($primaryModels); $this->filterByModels($viaModels); } elseif (is_array($this->via)) { // via relation - /** @var ActiveRelation $viaQuery */ + /** @var ActiveRelationTrait $viaQuery */ list($viaName, $viaQuery) = $this->via; $viaQuery->primaryModel = null; $viaModels = $viaQuery->findWith($viaName, $primaryModels); diff --git a/tests/unit/framework/data/ActiveDataProviderTest.php b/tests/unit/framework/data/ActiveDataProviderTest.php index 3b445e0..4e4ca77 100644 --- a/tests/unit/framework/data/ActiveDataProviderTest.php +++ b/tests/unit/framework/data/ActiveDataProviderTest.php @@ -10,6 +10,8 @@ namespace yiiunit\framework\data; use yii\data\ActiveDataProvider; use yii\db\Query; use yiiunit\data\ar\ActiveRecord; +use yiiunit\data\ar\Customer; +use yiiunit\data\ar\Item; use yiiunit\framework\db\DatabaseTestCase; use yiiunit\data\ar\Order; @@ -18,6 +20,7 @@ use yiiunit\data\ar\Order; * @since 2.0 * * @group data + * @group db */ class ActiveDataProviderTest extends DatabaseTestCase { @@ -35,6 +38,8 @@ class ActiveDataProviderTest extends DatabaseTestCase $orders = $provider->getModels(); $this->assertEquals(3, count($orders)); $this->assertTrue($orders[0] instanceof Order); + $this->assertTrue($orders[1] instanceof Order); + $this->assertTrue($orders[2] instanceof Order); $this->assertEquals([1, 2, 3], $provider->getKeys()); $provider = new ActiveDataProvider([ @@ -47,6 +52,75 @@ class ActiveDataProviderTest extends DatabaseTestCase $this->assertEquals(2, count($orders)); } + public function testActiveRelation() + { + /** @var Customer $customer */ + $customer = Customer::find(2); + $provider = new ActiveDataProvider([ + 'query' => $customer->getOrders(), + ]); + $orders = $provider->getModels(); + $this->assertEquals(2, count($orders)); + $this->assertTrue($orders[0] instanceof Order); + $this->assertTrue($orders[1] instanceof Order); + $this->assertEquals([2, 3], $provider->getKeys()); + + $provider = new ActiveDataProvider([ + 'query' => $customer->getOrders(), + 'pagination' => [ + 'pageSize' => 1, + ] + ]); + $orders = $provider->getModels(); + $this->assertEquals(1, count($orders)); + } + + public function testActiveRelationVia() + { + /** @var Order $order */ + $order = Order::find(2); + $provider = new ActiveDataProvider([ + 'query' => $order->getItems(), + ]); + $items = $provider->getModels(); + $this->assertEquals(3, count($items)); + $this->assertTrue($items[0] instanceof Item); + $this->assertTrue($items[1] instanceof Item); + $this->assertTrue($items[2] instanceof Item); + $this->assertEquals([3, 4, 5], $provider->getKeys()); + + $provider = new ActiveDataProvider([ + 'query' => $order->getItems(), + 'pagination' => [ + 'pageSize' => 2, + ] + ]); + $items = $provider->getModels(); + $this->assertEquals(2, count($items)); + } + + public function testActiveRelationViaTable() + { + /** @var Order $order */ + $order = Order::find(1); + $provider = new ActiveDataProvider([ + 'query' => $order->getBooks(), + ]); + $items = $provider->getModels(); + $this->assertEquals(2, count($items)); + $this->assertTrue($items[0] instanceof Item); + $this->assertTrue($items[1] instanceof Item); + + $provider = new ActiveDataProvider([ + 'query' => $order->getBooks(), + 'pagination' => [ + 'pageSize' => 1, + ] + ]); + $items = $provider->getModels(); + $this->assertEquals(1, count($items)); + } + public function testQuery() { $query = new Query; diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index d0d2f12..f96f2d3 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -119,10 +119,19 @@ class ActiveRecordTest extends DatabaseTestCase { /** @var Customer $customer */ $customer = Customer::find(2); + $this->assertFalse($customer->isRelationPopulated('orders')); $orders = $customer->orders; + $this->assertTrue($customer->isRelationPopulated('orders')); $this->assertEquals(2, count($orders)); + $this->assertEquals(1, count($customer->populatedRelations)); + /** @var Customer $customer */ + $customer = Customer::find(2); + $this->assertFalse($customer->isRelationPopulated('orders')); $orders = $customer->getOrders()->where('id=3')->all(); + $this->assertFalse($customer->isRelationPopulated('orders')); + $this->assertEquals(0, count($customer->populatedRelations)); + $this->assertEquals(1, count($orders)); $this->assertEquals(3, $orders[0]->id); } @@ -131,8 +140,15 @@ class ActiveRecordTest extends DatabaseTestCase { $customers = Customer::find()->with('orders')->all(); $this->assertEquals(3, count($customers)); + $this->assertTrue($customers[0]->isRelationPopulated('orders')); + $this->assertTrue($customers[1]->isRelationPopulated('orders')); $this->assertEquals(1, count($customers[0]->orders)); $this->assertEquals(2, count($customers[1]->orders)); + + $customer = Customer::find()->with('orders')->one(); + $this->assertTrue($customer->isRelationPopulated('orders')); + $this->assertEquals(1, count($customer->orders)); + $this->assertEquals(1, count($customer->populatedRelations)); } public function testFindLazyVia() diff --git a/tests/unit/framework/db/QueryTest.php b/tests/unit/framework/db/QueryTest.php index a48d719..77c1ac0 100644 --- a/tests/unit/framework/db/QueryTest.php +++ b/tests/unit/framework/db/QueryTest.php @@ -86,19 +86,19 @@ class QueryTest extends DatabaseTestCase { $query = new Query; $query->orderBy('team'); - $this->assertEquals(['team' => false], $query->orderBy); + $this->assertEquals(['team' => SORT_ASC], $query->orderBy); $query->addOrderBy('company'); - $this->assertEquals(['team' => false, 'company' => false], $query->orderBy); + $this->assertEquals(['team' => SORT_ASC, 'company' => SORT_ASC], $query->orderBy); $query->addOrderBy('age'); - $this->assertEquals(['team' => false, 'company' => false, 'age' => false], $query->orderBy); + $this->assertEquals(['team' => SORT_ASC, 'company' => SORT_ASC, 'age' => SORT_ASC], $query->orderBy); - $query->addOrderBy(['age' => true]); - $this->assertEquals(['team' => false, 'company' => false, 'age' => true], $query->orderBy); + $query->addOrderBy(['age' => SORT_DESC]); + $this->assertEquals(['team' => SORT_ASC, 'company' => SORT_ASC, 'age' => SORT_DESC], $query->orderBy); $query->addOrderBy('age ASC, company DESC'); - $this->assertEquals(['team' => false, 'company' => true, 'age' => false], $query->orderBy); + $this->assertEquals(['team' => SORT_ASC, 'company' => SORT_DESC, 'age' => SORT_ASC], $query->orderBy); } public function testLimitOffset() diff --git a/tests/unit/framework/db/cubrid/CubridActiveDataProviderTest.php b/tests/unit/framework/db/cubrid/CubridActiveDataProviderTest.php new file mode 100644 index 0000000..191442a --- /dev/null +++ b/tests/unit/framework/db/cubrid/CubridActiveDataProviderTest.php @@ -0,0 +1,14 @@ + Date: Wed, 13 Nov 2013 19:47:35 +0100 Subject: [PATCH 68/79] dataprovider: reset orderBy when counting strict sql dbms like postgres would fail otherwise --- framework/yii/data/ActiveDataProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 3766692..a8bb18f 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -158,7 +158,7 @@ class ActiveDataProvider extends BaseDataProvider throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.'); } $query = clone $this->query; - return (int) $query->limit(-1)->offset(-1)->count($this->db); + return (int) $query->limit(-1)->offset(-1)->orderBy([])->count($this->db); } /** From 51faa62ecab71cfb5458e45ead7cb0fa3051efc4 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 19:52:14 +0100 Subject: [PATCH 69/79] updated interface and trait php-doc in yii\ar [ci skip] --- framework/yii/ar/ActiveQueryInterface.php | 2 +- framework/yii/ar/ActiveQueryTrait.php | 25 +------------------------ framework/yii/ar/ActiveRelationInterface.php | 2 ++ framework/yii/ar/ActiveRelationTrait.php | 11 +---------- 4 files changed, 5 insertions(+), 35 deletions(-) diff --git a/framework/yii/ar/ActiveQueryInterface.php b/framework/yii/ar/ActiveQueryInterface.php index e2bb780..ddcb027 100644 --- a/framework/yii/ar/ActiveQueryInterface.php +++ b/framework/yii/ar/ActiveQueryInterface.php @@ -9,7 +9,7 @@ namespace yii\ar; use yii\db\QueryInterface; /** - * ActiveQueryInterface defines the common interface to be implemented by active record relation classes. + * ActiveQueryInterface defines the common interface to be implemented by active record query classes. * * A class implementing this interface should also use [[ActiveQueryTrait]]. * diff --git a/framework/yii/ar/ActiveQueryTrait.php b/framework/yii/ar/ActiveQueryTrait.php index 8c8d6c0..e45f72a 100644 --- a/framework/yii/ar/ActiveQueryTrait.php +++ b/framework/yii/ar/ActiveQueryTrait.php @@ -10,30 +10,7 @@ namespace yii\ar; use yii\db\ActiveRecord; /** - * ActiveQueryTrait implements the common set of methods that are used by DB queries associated with an Active Record class. - * - * ActiveQuery mainly provides the following methods to retrieve the query results: - * - * - [[one()]]: returns a single record populated with the first row of data. - * - [[all()]]: returns all records based on the query results. - * - [[count()]]: returns the number of records. - * - [[scalar()]]: returns the value of the first column in the first row of the query result. - * - [[exists()]]: returns a value indicating whether the query result has data or not. - * - * Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]], - * [[orderBy()]] to customize the query options. - * - * ActiveQuery also provides the following additional query options: - * - * - [[with()]]: list of relations that this query should be performed with. - * - [[indexBy()]]: the name of the column by which the query result should be indexed. - * - [[asArray()]]: whether to return each record as an array. - * - * These options can be configured using methods of the same name. For example: - * - * ~~~ - * $customers = Customer::find()->with('orders')->asArray()->all(); - * ~~~ + * ActiveQueryTrait implements the common methods and properties for active record query classes. * * @author Qiang Xue * @author Carsten Brandt diff --git a/framework/yii/ar/ActiveRelationInterface.php b/framework/yii/ar/ActiveRelationInterface.php index 70b723a..1d58a7d 100644 --- a/framework/yii/ar/ActiveRelationInterface.php +++ b/framework/yii/ar/ActiveRelationInterface.php @@ -10,6 +10,8 @@ namespace yii\ar; /** * ActiveRelationInterface defines the common interface to be implemented by active record relation classes. * + * A class implementing this interface should also use [[ActiveRelationTrait]]. + * * @author Qiang Xue * @author Carsten Brandt * @since 2.0 diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php index 5d49641..6b617bc 100644 --- a/framework/yii/ar/ActiveRelationTrait.php +++ b/framework/yii/ar/ActiveRelationTrait.php @@ -11,16 +11,7 @@ use yii\base\InvalidConfigException; use yii\db\ActiveRecord; /** - * ActiveRelation represents a relation between two Active Record classes. - * - * ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and - * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining - * a getter method which calls one of the above methods and returns the created ActiveRelation object. - * - * A relation is specified by [[link]] which represents the association between columns - * of different tables; and the multiplicity of the relation is indicated by [[multiple]]. - * - * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method. + * ActiveRelationTrait implements the common methods and properties for active record relation classes. * * @author Qiang Xue * @author Carsten Brandt From 1acdbb741c1f2b6a0a3c9329c85512d3e62c41e8 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 13 Nov 2013 19:57:32 +0100 Subject: [PATCH 70/79] moved indexBy() trait -> interface + removed findWith from interface --- framework/yii/ar/ActiveQueryInterface.php | 19 +++++++++++++++++++ framework/yii/ar/ActiveQueryTrait.php | 22 ---------------------- framework/yii/ar/ActiveRelationInterface.php | 9 --------- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/framework/yii/ar/ActiveQueryInterface.php b/framework/yii/ar/ActiveQueryInterface.php index ddcb027..7a1efa1 100644 --- a/framework/yii/ar/ActiveQueryInterface.php +++ b/framework/yii/ar/ActiveQueryInterface.php @@ -27,6 +27,25 @@ interface ActiveQueryInterface extends QueryInterface public function asArray($value = true); /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row or model data. The signature of the callable should be: + * + * ~~~ + * // $model is an AR instance when `asArray` is false, + * // or an array of column values when `asArray` is true. + * function ($model) + * { + * // return the index value corresponding to $model + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column); + + /** * Specifies the relations with which this query should be performed. * * The parameters to this method can be either one or multiple strings, or a single array diff --git a/framework/yii/ar/ActiveQueryTrait.php b/framework/yii/ar/ActiveQueryTrait.php index e45f72a..98e7870 100644 --- a/framework/yii/ar/ActiveQueryTrait.php +++ b/framework/yii/ar/ActiveQueryTrait.php @@ -103,28 +103,6 @@ trait ActiveQueryTrait } /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row or model data. The signature of the callable should be: - * - * ~~~ - * // $model is an AR instance when `asArray` is false, - * // or an array of column values when `asArray` is true. - * function ($model) - * { - * // return the index value corresponding to $model - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column) - { - return parent::indexBy($column); - } - - /** * Converts found rows into model instances * @param array $rows * @return array|ActiveRecord[] diff --git a/framework/yii/ar/ActiveRelationInterface.php b/framework/yii/ar/ActiveRelationInterface.php index 1d58a7d..706d394 100644 --- a/framework/yii/ar/ActiveRelationInterface.php +++ b/framework/yii/ar/ActiveRelationInterface.php @@ -26,13 +26,4 @@ interface ActiveRelationInterface extends ActiveQueryInterface * @return static the relation object itself. */ public function via($relationName, $callable = null); - - /** - * Finds the related records and populates them into the primary models. - * This method is internally used by [[ActiveQuery]]. Do not call it directly. - * @param string $name the relation name - * @param array $primaryModels primary models - * @return array the related models - */ - public function findWith($name, &$primaryModels); } From c5ee83bffb054d44e3b6866900ee2d396c888076 Mon Sep 17 00:00:00 2001 From: KiTE Date: Wed, 13 Nov 2013 23:54:27 +0200 Subject: [PATCH 71/79] Fixed gii after merge #1184 --- extensions/gii/generators/crud/Generator.php | 2 +- extensions/gii/generators/model/Generator.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php index 8ea1ff6..1925de2 100644 --- a/extensions/gii/generators/crud/Generator.php +++ b/extensions/gii/generators/crud/Generator.php @@ -278,7 +278,7 @@ class Generator extends \yii\gii\Generator $rules = []; foreach ($types as $type => $columns) { - $rules[] = "['" . implode(', ', $columns) . "', '$type']"; + $rules[] = "[['" . implode("', '", $columns) . "'], '$type']"; } return $rules; diff --git a/extensions/gii/generators/model/Generator.php b/extensions/gii/generators/model/Generator.php index 976fde8..32ba2e5 100644 --- a/extensions/gii/generators/model/Generator.php +++ b/extensions/gii/generators/model/Generator.php @@ -237,10 +237,10 @@ class Generator extends \yii\gii\Generator $rules = []; foreach ($types as $type => $columns) { - $rules[] = "[['" . implode(', ', $columns) . "'], '$type']"; + $rules[] = "[['" . implode("', '", $columns) . "'], '$type']"; } foreach ($lengths as $length => $columns) { - $rules[] = "[['" . implode(', ', $columns) . "'], 'string', 'max' => $length]"; + $rules[] = "[['" . implode("', '", $columns) . "'], 'string', 'max' => $length]"; } return $rules; From 368dd4b1d4f1e4ebac4abe8b8954b44ed460db00 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 13 Nov 2013 19:50:19 -0500 Subject: [PATCH 72/79] Allow Validator::except to be either a string or an array. --- framework/yii/validators/Validator.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/framework/yii/validators/Validator.php b/framework/yii/validators/Validator.php index 36a1af4..2cd611b 100644 --- a/framework/yii/validators/Validator.php +++ b/framework/yii/validators/Validator.php @@ -94,7 +94,8 @@ abstract class Validator extends Component */ public $on = []; /** - * @var array list of scenarios that the validator should not be applied to. + * @var array|string scenarios that the validator should not be applied to. For multiple scenarios, + * please specify them as an array; for single scenario, you may use either a string or an array. */ public $except = []; /** @@ -163,12 +164,9 @@ abstract class Validator extends Component public function init() { parent::init(); - if (!is_array($this->attributes)) { - $this->attributes = (array)$this->attributes; - } - if (!is_array($this->on)) { - $this->on = (array)$this->on; - } + $this->attributes = (array)$this->attributes; + $this->on = (array)$this->on; + $this->except = (array)$this->except; } /** From 8250cfbb0f1b3b87ba1ed901c8d38bf1b92bdcfb Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 14 Nov 2013 08:06:57 +0100 Subject: [PATCH 73/79] reverted change of count() signature --- framework/yii/ar/ActiveRelationTrait.php | 2 +- framework/yii/data/ActiveDataProvider.php | 2 +- framework/yii/db/Query.php | 6 +++--- framework/yii/db/QueryInterface.php | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php index 6b617bc..d87b22d 100644 --- a/framework/yii/ar/ActiveRelationTrait.php +++ b/framework/yii/ar/ActiveRelationTrait.php @@ -33,7 +33,7 @@ trait ActiveRelationTrait * @var array the columns of the primary and foreign tables that establish the relation. * The array keys must be columns of the table for this relation, and the array values * must be the corresponding columns from the primary table. - * Do not prefix or quote the column names as they will be done automatically by Yii. + * Do not prefix or quote the column names as this will be done automatically by Yii. */ public $link; /** diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index a8bb18f..0943be4 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -158,7 +158,7 @@ class ActiveDataProvider extends BaseDataProvider throw new InvalidConfigException('The "query" property must be an instance of a class that implements the QueryInterface e.g. yii\db\Query or its subclasses.'); } $query = clone $this->query; - return (int) $query->limit(-1)->offset(-1)->orderBy([])->count($this->db); + return (int) $query->limit(-1)->offset(-1)->orderBy([])->count('*', $this->db); } /** diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 0ae5f44..50ed105 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -179,13 +179,13 @@ class Query extends Component implements QueryInterface /** * Returns the number of records. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given (or null), the `db` application component will be used. * @param string $q the COUNT expression. Defaults to '*'. * Make sure you properly quote column names in the expression. + * @param Connection $db the database connection used to generate the SQL statement. + * If this parameter is not given (or null), the `db` application component will be used. * @return integer number of records */ - public function count($db = null, $q = '*') + public function count($q = '*', $db = null) { $this->select = ["COUNT($q)"]; return $this->createCommand($db)->queryScalar(); diff --git a/framework/yii/db/QueryInterface.php b/framework/yii/db/QueryInterface.php index d13fa2b..f3cc312 100644 --- a/framework/yii/db/QueryInterface.php +++ b/framework/yii/db/QueryInterface.php @@ -44,11 +44,12 @@ interface QueryInterface /** * Returns the number of records. + * @param string $q the COUNT expression. Defaults to '*'. * @param Connection $db the database connection used to execute the query. * If this parameter is not given, the `db` application component will be used. * @return integer number of records */ - public function count($db = null); + public function count($q = '*', $db = null); /** * Returns a value indicating whether the query result contains any row of data. From cd7a4a6745c29721bfad364de40becd53f915f0b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 14 Nov 2013 10:36:35 -0500 Subject: [PATCH 74/79] fixed functional test. --- apps/basic/web/index-test.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/basic/web/index-test.php b/apps/basic/web/index-test.php index 1593164..bd61e0b 100644 --- a/apps/basic/web/index-test.php +++ b/apps/basic/web/index-test.php @@ -13,5 +13,12 @@ require_once(__DIR__ . '/../vendor/yiisoft/yii2/yii/Yii.php'); $config = require(__DIR__ . '/../config/web-test.php'); -$application = new yii\web\Application($config); -$application->run(); +if (isset($this)) { + // run in functional tests + $config['class'] = 'yii\web\Application'; + return $config; +} else { + // run in acceptance tests + $application = new yii\web\Application($config); + $application->run(); +} From 220e6dc42da35e96f13148a439364f2c92596557 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 14 Nov 2013 11:05:09 -0500 Subject: [PATCH 75/79] Added readme about tests. --- apps/basic/tests/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 apps/basic/tests/README.md diff --git a/apps/basic/tests/README.md b/apps/basic/tests/README.md new file mode 100644 index 0000000..bb4f22e --- /dev/null +++ b/apps/basic/tests/README.md @@ -0,0 +1,15 @@ +This folder contains various tests for the basic application. +These tests are developed with [Codeception PHP Testing Framework](http://codeception.com/). + +To run the tests, follow these steps: + +1. [Install Codeception](http://codeception.com/quickstart) if you do not have it yet. +2. Create test configuration files based on your environment: + - Copy `acceptance.suite.dist.yml` to `acceptance.suite.yml` and customize it; + - Copy `functional.suite.dist.yml` to `functional.suite.yml` and customize it; + - Copy `unit.suite.dist.yml` to `unit.suite.yml` and customize it. +3. Switch to the parent folder and run tests: + +``` +php codecept.phar run +``` From 8775e25ca6248011a155415f3d084bb7cf9a83a4 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 14 Nov 2013 11:11:51 -0500 Subject: [PATCH 76/79] Added readme and updated test helper classes. --- apps/basic/tests/README.md | 8 +- apps/basic/tests/acceptance/WebGuy.php | 1367 ++++++++++++++++++++++++------- apps/basic/tests/functional/TestGuy.php | 1367 +++++++++++++++++++++++++------ apps/basic/tests/unit/CodeGuy.php | 15 +- 4 files changed, 2226 insertions(+), 531 deletions(-) diff --git a/apps/basic/tests/README.md b/apps/basic/tests/README.md index bb4f22e..eb9a0fe 100644 --- a/apps/basic/tests/README.md +++ b/apps/basic/tests/README.md @@ -4,6 +4,7 @@ These tests are developed with [Codeception PHP Testing Framework](http://codece To run the tests, follow these steps: 1. [Install Codeception](http://codeception.com/quickstart) if you do not have it yet. +2. Update tests 2. Create test configuration files based on your environment: - Copy `acceptance.suite.dist.yml` to `acceptance.suite.yml` and customize it; - Copy `functional.suite.dist.yml` to `functional.suite.yml` and customize it; @@ -11,5 +12,10 @@ To run the tests, follow these steps: 3. Switch to the parent folder and run tests: ``` -php codecept.phar run +cd .. +php codecept.phar build // rebuild test scripts, only need to be run once +php codecept.phar run // run all available tests ``` + +Please refer to [Codeception tutorial](http://codeception.com/docs/01-Introduction) for +more details about writing acceptance, functional and unit tests. diff --git a/apps/basic/tests/acceptance/WebGuy.php b/apps/basic/tests/acceptance/WebGuy.php index e6cc370..f08ed9c 100644 --- a/apps/basic/tests/acceptance/WebGuy.php +++ b/apps/basic/tests/acceptance/WebGuy.php @@ -17,13 +17,21 @@ use Codeception\Module\WebHelper; * @method void expect($prediction) * @method void amGoingTo($argumentation) * @method void am($role) - * @method void lookForwardTo($role) + * @method void lookForwardTo($achieveValue) + * @method void offsetGet($offset) + * @method void offsetSet($offset, $value) + * @method void offsetExists($offset) + * @method void offsetUnset($offset) */ class WebGuy extends \Codeception\AbstractGuy { /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Submits a form located on page. * Specify the form by it's css or xpath selector. * Fill the form fields values as array. @@ -36,7 +44,7 @@ class WebGuy extends \Codeception\AbstractGuy * * ``` php * submitForm('#login', ['login' => 'davert', 'password' => '123456']); + * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456')); * * ``` * @@ -55,20 +63,18 @@ class WebGuy extends \Codeception\AbstractGuy * * ``` php * submitForm('#userForm', ['user' => ['login' => 'Davert', 'password' => '123456', 'agree' => true]]); + * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true))); * * ``` * Note, that pricing plan will be set to Paid, as it's selected on page. * * @param $selector * @param $params - * @see PhpBrowser::submitForm() + * @see Codeception\Module\PhpBrowser::submitForm() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function submitForm($selector, $params) { - $this->scenario->action('submitForm', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('submitForm', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -78,6 +84,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * If your page triggers an ajax request, you can perform it manually. * This action sends a POST ajax request with specified params. * Additional params can be passed as array. @@ -89,20 +99,18 @@ class WebGuy extends \Codeception\AbstractGuy * * ``` php * sendAjaxPostRequest('/updateSettings', ['notifications' => true]; // POST - * $I->sendAjaxGetRequest('/updateSettings', ['notifications' => true]; // GET + * $I->sendAjaxPostRequest('/updateSettings', array('notifications' => true); // POST + * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true); // GET * * ``` * * @param $uri * @param $params - * @see PhpBrowser::sendAjaxPostRequest() + * @see Codeception\Module\PhpBrowser::sendAjaxPostRequest() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function sendAjaxPostRequest($uri, $params = null) { - $this->scenario->action('sendAjaxPostRequest', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -112,6 +120,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * If your page triggers an ajax request, you can perform it manually. * This action sends a GET ajax request with specified params. * @@ -119,13 +131,11 @@ class WebGuy extends \Codeception\AbstractGuy * * @param $uri * @param $params - * @see PhpBrowser::sendAjaxGetRequest() + * @see Codeception\Module\PhpBrowser::sendAjaxGetRequest() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function sendAjaxGetRequest($uri, $params = null) { - $this->scenario->action('sendAjaxGetRequest', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -135,14 +145,34 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Asserts that current page has 404 response status code. + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\PhpBrowser::seePageNotFound() + * @return \Codeception\Maybe + */ + public function canSeePageNotFound() { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Asserts that current page has 404 response status code. - * @see PhpBrowser::seePageNotFound() + * @see Codeception\Module\PhpBrowser::seePageNotFound() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seePageNotFound() { - $this->scenario->assertion('seePageNotFound', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -152,17 +182,40 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that response code is equal to value provided. + * + * @param $code + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\PhpBrowser::seeResponseCodeIs() + * @return \Codeception\Maybe + */ + public function canSeeResponseCodeIs($code) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that response code is equal to value provided. * * @param $code * @return mixed - * @see PhpBrowser::seeResponseCodeIs() + * @see Codeception\Module\PhpBrowser::seeResponseCodeIs() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeResponseCodeIs($code) { - $this->scenario->assertion('seeResponseCodeIs', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -172,17 +225,19 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Adds HTTP authentication via username/password. * * @param $username * @param $password - * @see PhpBrowser::amHttpAuthenticated() + * @see Codeception\Module\PhpBrowser::amHttpAuthenticated() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amHttpAuthenticated($username, $password) { - $this->scenario->condition('amHttpAuthenticated', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -192,6 +247,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Low-level API method. * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly * @@ -202,12 +261,12 @@ class WebGuy extends \Codeception\AbstractGuy * // from the official Guzzle manual * $I->amGoingTo('Sign all requests with OAuth'); * $I->executeInGuzzle(function (\Guzzle\Http\Client $client) { - * $client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin([ + * $client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array( * 'consumer_key' => '***', * 'consumer_secret' => '***', * 'token' => '***', * 'token_secret' => '***' - * ])); + * ))); * }); * ?> * ``` @@ -216,13 +275,135 @@ class WebGuy extends \Codeception\AbstractGuy * If Codeception lacks important Guzzle Client methods implement then and submit patches. * * @param callable $function - * @see PhpBrowser::executeInGuzzle() + * @see Codeception\Module\PhpBrowser::executeInGuzzle() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function executeInGuzzle($function) { - $this->scenario->action('executeInGuzzle', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is checked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. + * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); + * ?> + * ``` + * + * @param $checkbox + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\PhpBrowser::seeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function canSeeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is checked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. + * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); + * ?> + * ``` + * + * @param $checkbox + * @see Codeception\Module\PhpBrowser::seeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function seeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is unchecked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. + * ?> + * ``` + * + * @param $checkbox + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\PhpBrowser::dontSeeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function cantSeeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is unchecked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. + * ?> + * ``` + * + * @param $checkbox + * @see Codeception\Module\PhpBrowser::dontSeeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function dontSeeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -232,16 +413,18 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Opens the page. * * @param $page - * @see Mink::amOnPage() + * @see Codeception\Util\Mink::amOnPage() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amOnPage($page) { - $this->scenario->condition('amOnPage', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -251,6 +434,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Sets 'url' configuration parameter to hosts subdomain. * It does not open a page on subdomain. Use `amOnPage` for that * @@ -267,13 +454,54 @@ class WebGuy extends \Codeception\AbstractGuy * ``` * @param $subdomain * @return mixed - * @see Mink::amOnSubdomain() + * @see Codeception\Util\Mink::amOnSubdomain() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amOnSubdomain($subdomain) { - $this->scenario->condition('amOnSubdomain', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * @param string $text + * @param string $selector + * + * @return void + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSee() + * @return \Codeception\Maybe + */ + public function cantSee($text, $selector = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * @param string $text + * @param string $selector + * + * @return void + * @see Codeception\Util\Mink::dontSee() + * @return \Codeception\Maybe + */ + public function dontSee($text, $selector = null) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -283,36 +511,42 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Check if current page doesn't contain the text specified. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Check if current page contains the text specified. * Specify the css selector to match only specific region. * * Examples: * - * ```php + * ``` php * dontSee('Login'); // I can suppose user is already logged in - * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page - * $I->dontSee('Sign Up','//body/h1'); // with XPath + * $I->see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up','h1'); // I can suppose it's a signup page + * $I->see('Sign Up','//body/h1'); // with XPath + * ?> * ``` * * @param $text * @param null $selector - * @see Mink::dontSee() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::see() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSee($text, $selector = null) { - $this->scenario->action('dontSee', func_get_args()); + public function canSee($text, $selector = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Check if current page contains the text specified. * Specify the css selector to match only specific region. * @@ -323,18 +557,16 @@ class WebGuy extends \Codeception\AbstractGuy * $I->see('Logout'); // I can suppose user is logged in * $I->see('Sign Up','h1'); // I can suppose it's a signup page * $I->see('Sign Up','//body/h1'); // with XPath - * + * ?> * ``` * * @param $text * @param null $selector - * @see Mink::see() + * @see Codeception\Util\Mink::see() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function see($text, $selector = null) { - $this->scenario->assertion('see', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('see', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -344,6 +576,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if there is a link with text specified. * Specify url to match link with exact this url. * @@ -353,18 +589,47 @@ class WebGuy extends \Codeception\AbstractGuy * seeLink('Logout'); // matches Logout * $I->seeLink('Logout','/logout'); // matches Logout + * ?> + * ``` * + * @param $text + * @param null $url + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeLink() + * @return \Codeception\Maybe + */ + public function canSeeLink($text, $url = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if there is a link with text specified. + * Specify url to match link with exact this url. + * + * Examples: + * + * ``` php + * seeLink('Logout'); // matches Logout + * $I->seeLink('Logout','/logout'); // matches Logout + * ?> * ``` * * @param $text * @param null $url - * @see Mink::seeLink() + * @see Codeception\Util\Mink::seeLink() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeLink($text, $url = null) { - $this->scenario->assertion('seeLink', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -374,6 +639,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if page doesn't contain the link with text specified. * Specify url to narrow the results. * @@ -382,18 +651,46 @@ class WebGuy extends \Codeception\AbstractGuy * ``` php * dontSeeLink('Logout'); // I suppose user is not logged in + * ?> + * ``` * + * @param $text + * @param null $url + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeLink() + * @return \Codeception\Maybe + */ + public function cantSeeLink($text, $url = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if page doesn't contain the link with text specified. + * Specify url to narrow the results. + * + * Examples: + * + * ``` php + * dontSeeLink('Logout'); // I suppose user is not logged in + * ?> * ``` * * @param $text * @param null $url - * @see Mink::dontSeeLink() + * @see Codeception\Util\Mink::dontSeeLink() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeLink($text, $url = null) { - $this->scenario->action('dontSeeLink', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -403,6 +700,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Perform a click on link or button. * Link or button are found by their names or CSS selector. * Submits a form if button is a submit type. @@ -431,13 +732,11 @@ class WebGuy extends \Codeception\AbstractGuy * ``` * @param $link * @param $context - * @see Mink::click() + * @see Codeception\Util\Mink::click() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function click($link, $context = null) { - $this->scenario->action('click', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('click', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -447,22 +746,50 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if element exists on a page, matching it by CSS or XPath + * + * ``` php + * seeElement('.error'); + * $I->seeElement('//form/input[1]'); + * ?> + * ``` + * @param $selector + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeElement() + * @return \Codeception\Maybe + */ + public function canSeeElement($selector) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if element exists on a page, matching it by CSS or XPath * * ``` php * seeElement('.error'); - * $I->seeElement(//form/input[1]); + * $I->seeElement('//form/input[1]'); * ?> * ``` * @param $selector - * @see Mink::seeElement() + * @see Codeception\Util\Mink::seeElement() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeElement($selector) { - $this->scenario->assertion('seeElement', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -472,22 +799,54 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath + * + * Example: + * + * ``` php + * dontSeeElement('.error'); + * $I->dontSeeElement('//form/input[1]'); + * ?> + * ``` + * @param $selector + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeElement() + * @return \Codeception\Maybe + */ + public function cantSeeElement($selector) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath * + * Example: + * * ``` php * dontSeeElement('.error'); - * $I->dontSeeElement(//form/input[1]); + * $I->dontSeeElement('//form/input[1]'); * ?> * ``` * @param $selector - * @see Mink::dontSeeElement() + * @see Codeception\Util\Mink::dontSeeElement() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeElement($selector) { - $this->scenario->action('dontSeeElement', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -497,14 +856,16 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Reloads current page - * @see Mink::reloadPage() + * @see Codeception\Util\Mink::reloadPage() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function reloadPage() { - $this->scenario->action('reloadPage', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('reloadPage', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -514,14 +875,16 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Moves back in history - * @see Mink::moveBack() + * @see Codeception\Util\Mink::moveBack() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function moveBack() { - $this->scenario->action('moveBack', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('moveBack', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -531,14 +894,16 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Moves forward in history - * @see Mink::moveForward() + * @see Codeception\Util\Mink::moveForward() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function moveForward() { - $this->scenario->action('moveForward', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('moveForward', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -548,17 +913,27 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Fills a text field or textarea with value. + * + * Example: + * + * ``` php + * fillField("//input[@type='text']", "Hello World!"); + * ?> + * ``` * * @param $field * @param $value - * @see Mink::fillField() + * @see Codeception\Util\Mink::fillField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function fillField($field, $value) { - $this->scenario->action('fillField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('fillField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -568,27 +943,265 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Selects an option in select tag or in radio button group. * * Example: * * ``` php * selectOption('form select[name=account]', 'Premium'); - * $I->selectOption('form input[name=payment]', 'Monthly'); - * $I->selectOption('//form/select[@name=account]', 'Monthly'); + * $I->selectOption('form select[name=account]', 'Premium'); + * $I->selectOption('form input[name=payment]', 'Monthly'); + * $I->selectOption('//form/select[@name=account]', 'Monthly'); + * ?> + * ``` + * + * Can select multiple options if second argument is array: + * + * ``` php + * selectOption('Which OS do you use?', array('Windows','Linux')); + * ?> + * ``` + * + * @param $select + * @param $option + * @see Codeception\Util\Mink::selectOption() + * @return \Codeception\Maybe + */ + public function selectOption($select, $option) { + $this->scenario->addStep(new \Codeception\Step\Action('selectOption', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Ticks a checkbox. + * For radio buttons use `selectOption` method. + * + * Example: + * + * ``` php + * checkOption('#agree'); + * ?> + * ``` + * + * @param $option + * @see Codeception\Util\Mink::checkOption() + * @return \Codeception\Maybe + */ + public function checkOption($option) { + $this->scenario->addStep(new \Codeception\Step\Action('checkOption', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Unticks a checkbox. + * + * Example: + * + * ``` php + * uncheckOption('#notify'); + * ?> + * ``` + * + * @param $option + * @see Codeception\Util\Mink::uncheckOption() + * @return \Codeception\Maybe + */ + public function uncheckOption($option) { + $this->scenario->addStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri contains a value + * + * ``` php + * seeInCurrentUrl('home'); + * // to match: /users/1 + * $I->seeInCurrentUrl('/users/'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeInCurrentUrl() + * @return \Codeception\Maybe + */ + public function canSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri contains a value + * + * ``` php + * seeInCurrentUrl('home'); + * // to match: /users/1 + * $I->seeInCurrentUrl('/users/'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Mink::seeInCurrentUrl() + * @return \Codeception\Maybe + */ + public function seeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri does not contain a value + * + * ``` php + * dontSeeInCurrentUrl('/users/'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeInCurrentUrl() + * @return \Codeception\Maybe + */ + public function cantSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri does not contain a value + * + * ``` php + * dontSeeInCurrentUrl('/users/'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Mink::dontSeeInCurrentUrl() + * @return \Codeception\Maybe + */ + public function dontSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is equal to value. + * Unlike `seeInCurrentUrl` performs a strict check. + * + * ``` php + * seeCurrentUrlEquals('/'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeCurrentUrlEquals() + * @return \Codeception\Maybe + */ + public function canSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is equal to value. + * Unlike `seeInCurrentUrl` performs a strict check. + * + * ``` php + * seeCurrentUrlEquals('/'); * ?> * ``` * - * @param $select - * @param $option - * @see Mink::selectOption() + * @param $uri + * @see Codeception\Util\Mink::seeCurrentUrlEquals() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function selectOption($select, $option) { - $this->scenario->action('selectOption', func_get_args()); + public function seeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -598,52 +1211,54 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Ticks a checkbox. - * For radio buttons use `selectOption` method. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * Example: + * Checks that current url is not equal to value. + * Unlike `dontSeeInCurrentUrl` performs a strict check. * * ``` php * checkOption('#agree'); + * // current url is not root + * $I->dontSeeCurrentUrlEquals('/'); * ?> * ``` * - * @param $option - * @see Mink::checkOption() + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeCurrentUrlEquals() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function checkOption($option) { - $this->scenario->action('checkOption', func_get_args()); + public function cantSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Unticks a checkbox. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * Example: + * Checks that current url is not equal to value. + * Unlike `dontSeeInCurrentUrl` performs a strict check. * * ``` php * uncheckOption('#notify'); + * // current url is not root + * $I->dontSeeCurrentUrlEquals('/'); * ?> * ``` * - * @param $option - * @see Mink::uncheckOption() + * @param $uri + * @see Codeception\Util\Mink::dontSeeCurrentUrlEquals() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function uncheckOption($option) { - $this->scenario->action('uncheckOption', func_get_args()); + public function dontSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -653,50 +1268,52 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Checks that current uri contains a value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is matches a RegEx value * * ``` php * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); + * // to match root url + * $I->seeCurrentUrlMatches('~$/users/(\d+)~'); * ?> * ``` * * @param $uri - * @see Mink::seeInCurrentUrl() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeCurrentUrlMatches() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeInCurrentUrl($uri) { - $this->scenario->assertion('seeInCurrentUrl', func_get_args()); + public function canSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current uri does not contain a value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is matches a RegEx value * * ``` php * dontSeeInCurrentUrl('/users/'); + * // to match root url + * $I->seeCurrentUrlMatches('~$/users/(\d+)~'); * ?> * ``` * * @param $uri - * @see Mink::dontSeeInCurrentUrl() + * @see Codeception\Util\Mink::seeCurrentUrlMatches() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeInCurrentUrl($uri) { - $this->scenario->action('dontSeeInCurrentUrl', func_get_args()); + public function seeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -706,47 +1323,52 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Checks that current url is equal to value. - * Unlike `seeInCurrentUrl` performs a strict check. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url does not match a RegEx value * + * ``` php * seeCurrentUrlEquals('/'); + * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~'); * ?> + * ``` * * @param $uri - * @see Mink::seeCurrentUrlEquals() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeCurrentUrlMatches() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCurrentUrlEquals($uri) { - $this->scenario->assertion('seeCurrentUrlEquals', func_get_args()); + public function cantSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current url is not equal to value. - * Unlike `dontSeeInCurrentUrl` performs a strict check. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url does not match a RegEx value * + * ``` php * dontSeeCurrentUrlEquals('/'); + * // to match root url + * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~'); * ?> + * ``` * * @param $uri - * @see Mink::dontSeeCurrentUrlEquals() + * @see Codeception\Util\Mink::dontSeeCurrentUrlMatches() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeCurrentUrlEquals($uri) { - $this->scenario->action('dontSeeCurrentUrlEquals', func_get_args()); + public function dontSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -756,45 +1378,40 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Checks that current url is matches a RegEx value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * seeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> + * Checks that cookie is set. * - * @param $uri - * @see Mink::seeCurrentUrlMatches() + * @param $cookie + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCurrentUrlMatches($uri) { - $this->scenario->assertion('seeCurrentUrlMatches', func_get_args()); + public function canSeeCookie($cookie) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current url does not match a RegEx value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); - * ?> + * Checks that cookie is set. * - * @param $uri - * @see Mink::dontSeeCurrentUrlMatches() + * @param $cookie + * @return mixed + * @see Codeception\Util\Mink::seeCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeCurrentUrlMatches($uri) { - $this->scenario->action('dontSeeCurrentUrlMatches', func_get_args()); + public function seeCookie($cookie) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -804,31 +1421,40 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * @see Mink::seeCookie() + * Checks that cookie doesn't exist + * + * @param $cookie + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCookie($cookie) { - $this->scenario->assertion('seeCookie', func_get_args()); + public function cantSeeCookie($cookie) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that cookie doesn't exist * - * @see Mink::dontSeeCookie() + * @param $cookie + * @return mixed + * @see Codeception\Util\Mink::dontSeeCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeCookie($cookie) { - $this->scenario->action('dontSeeCookie', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -838,14 +1464,20 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Sets a cookie. * - * @see Mink::setCookie() + * @param $cookie + * @param $value + * @return mixed + * @see Codeception\Util\Mink::setCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function setCookie($cookie, $value) { - $this->scenario->action('setCookie', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('setCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -855,14 +1487,19 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * @see Mink::resetCookie() + * Unsets cookie + * + * @param $cookie + * @return mixed + * @see Codeception\Util\Mink::resetCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function resetCookie($cookie) { - $this->scenario->action('resetCookie', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('resetCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -872,14 +1509,19 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * @see Mink::grabCookie() + * Grabs a cookie value. + * + * @param $cookie + * @return mixed + * @see Codeception\Util\Mink::grabCookie() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabCookie($cookie) { - $this->scenario->action('grabCookie', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabCookie', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -889,6 +1531,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Takes a parameters from current URI by RegEx. * If no url provided returns full URI. * @@ -902,13 +1548,11 @@ class WebGuy extends \Codeception\AbstractGuy * @param null $uri * @internal param $url * @return mixed - * @see Mink::grabFromCurrentUrl() + * @see Codeception\Util\Mink::grabFromCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabFromCurrentUrl($uri = null) { - $this->scenario->action('grabFromCurrentUrl', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -918,26 +1562,28 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Attaches file from Codeception data directory to upload field. * * Example: * * ``` php * attachFile('prices.xls'); + * // file is stored in 'tests/_data/prices.xls' + * $I->attachFile('input[@type="file"]', 'prices.xls'); * ?> * ``` * * @param $field * @param $filename - * @see Mink::attachFile() + * @see Codeception\Util\Mink::attachFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function attachFile($field, $filename) { - $this->scenario->action('attachFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('attachFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -947,6 +1593,38 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if option is selected in select field. + * + * ``` php + * seeOptionIsSelected('#form input[name=payment]', 'Visa'); + * ?> + * ``` + * + * @param $selector + * @param $optionText + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeOptionIsSelected() + * @return \Codeception\Maybe + */ + public function canSeeOptionIsSelected($select, $text) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if option is selected in select field. * * ``` php @@ -958,13 +1636,11 @@ class WebGuy extends \Codeception\AbstractGuy * @param $selector * @param $optionText * @return mixed - * @see Mink::seeOptionIsSelected() + * @see Codeception\Util\Mink::seeOptionIsSelected() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeOptionIsSelected($select, $text) { - $this->scenario->assertion('seeOptionIsSelected', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -974,6 +1650,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if option is not selected in select field. * * ``` php @@ -985,43 +1665,39 @@ class WebGuy extends \Codeception\AbstractGuy * @param $selector * @param $optionText * @return mixed - * @see Mink::dontSeeOptionIsSelected() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeOptionIsSelected() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeOptionIsSelected($select, $text) { - $this->scenario->action('dontSeeOptionIsSelected', func_get_args()); + public function cantSeeOptionIsSelected($select, $text) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Assert if the specified checkbox is checked. - * Use css selector or xpath to match. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * Example: + * Checks if option is not selected in select field. * * ``` php * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. - * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); - * + * $I->dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); + * ?> * ``` * - * @param $checkbox - * @see Mink::seeCheckboxIsChecked() + * @param $selector + * @param $optionText + * @return mixed + * @see Codeception\Util\Mink::dontSeeOptionIsSelected() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCheckboxIsChecked($checkbox) { - $this->scenario->assertion('seeCheckboxIsChecked', func_get_args()); + public function dontSeeOptionIsSelected($select, $text) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1031,35 +1707,44 @@ class WebGuy extends \Codeception\AbstractGuy /** - * Assert if the specified checkbox is unchecked. - * Use css selector or xpath to match. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that an input field or textarea contains value. + * Field is matched either by label or CSS or Xpath * * Example: * * ``` php * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms - * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. - * + * $I->seeInField('Body','Type your comment here'); + * $I->seeInField('form textarea[name=body]','Type your comment here'); + * $I->seeInField('form input[type=hidden]','hidden_value'); + * $I->seeInField('#searchform input','Search'); + * $I->seeInField('//form/*[@name=search]','Search'); + * ?> * ``` * - * @param $checkbox - * @see Mink::dontSeeCheckboxIsChecked() + * @param $field + * @param $value + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeInField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeCheckboxIsChecked($checkbox) { - $this->scenario->action('dontSeeCheckboxIsChecked', func_get_args()); + public function canSeeInField($field, $value) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that an input field or textarea contains value. * Field is matched either by label or CSS or Xpath * @@ -1077,13 +1762,11 @@ class WebGuy extends \Codeception\AbstractGuy * * @param $field * @param $value - * @see Mink::seeInField() + * @see Codeception\Util\Mink::seeInField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeInField($field, $value) { - $this->scenario->assertion('seeInField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1093,6 +1776,43 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that an input field or textarea doesn't contain value. + * Field is matched either by label or CSS or Xpath + * Example: + * + * ``` php + * dontSeeInField('Body','Type your comment here'); + * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); + * $I->dontSeeInField('form input[type=hidden]','hidden_value'); + * $I->dontSeeInField('#searchform input','Search'); + * $I->dontSeeInField('//form/*[@name=search]','Search'); + * ?> + * ``` + * + * @param $field + * @param $value + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeInField() + * @return \Codeception\Maybe + */ + public function cantSeeInField($field, $value) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that an input field or textarea doesn't contain value. * Field is matched either by label or CSS or Xpath * Example: @@ -1109,13 +1829,11 @@ class WebGuy extends \Codeception\AbstractGuy * * @param $field * @param $value - * @see Mink::dontSeeInField() + * @see Codeception\Util\Mink::dontSeeInField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeInField($field, $value) { - $this->scenario->action('dontSeeInField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1125,6 +1843,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Finds and returns text contents of element. * Element is searched by CSS selector, XPath or matcher by regex. * @@ -1140,13 +1862,11 @@ class WebGuy extends \Codeception\AbstractGuy * * @param $cssOrXPathOrRegex * @return mixed - * @see Mink::grabTextFrom() + * @see Codeception\Util\Mink::grabTextFrom() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabTextFrom($cssOrXPathOrRegex) { - $this->scenario->action('grabTextFrom', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1156,6 +1876,10 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Finds and returns field and returns it's value. * Searches by field name, then by CSS, then by XPath * @@ -1171,13 +1895,66 @@ class WebGuy extends \Codeception\AbstractGuy * * @param $field * @return mixed - * @see Mink::grabValueFrom() + * @see Codeception\Util\Mink::grabValueFrom() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabValueFrom($field) { - $this->scenario->action('grabValueFrom', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title contains text. + * + * ``` php + * seeInTitle('Blog - Post #1'); + * ?> + * ``` + * + * @param $title + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::seeInTitle() + * @return \Codeception\Maybe + */ + public function canSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title contains text. + * + * ``` php + * seeInTitle('Blog - Post #1'); + * ?> + * ``` + * + * @param $title + * @return mixed + * @see Codeception\Util\Mink::seeInTitle() + * @return \Codeception\Maybe + */ + public function seeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1187,14 +1964,40 @@ class WebGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title does not contain text. + * + * @param $title + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Mink::dontSeeInTitle() + * @return \Codeception\Maybe + */ + public function cantSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * @see Mink::grabAttribute() + * Checks that page title does not contain text. + * + * @param $title + * @return mixed + * @see Codeception\Util\Mink::dontSeeInTitle() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function grabAttribute() { - $this->scenario->action('grabAttribute', func_get_args()); + public function dontSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); diff --git a/apps/basic/tests/functional/TestGuy.php b/apps/basic/tests/functional/TestGuy.php index 58baf56..553a44f 100644 --- a/apps/basic/tests/functional/TestGuy.php +++ b/apps/basic/tests/functional/TestGuy.php @@ -18,24 +18,30 @@ use Codeception\Module\Yii2; * @method void expect($prediction) * @method void amGoingTo($argumentation) * @method void am($role) - * @method void lookForwardTo($role) + * @method void lookForwardTo($achieveValue) + * @method void offsetGet($offset) + * @method void offsetSet($offset, $value) + * @method void offsetExists($offset) + * @method void offsetUnset($offset) */ class TestGuy extends \Codeception\AbstractGuy { /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Enters a directory In local filesystem. * Project root directory is used by default * * @param $path - * @see Filesystem::amInPath() + * @see Codeception\Module\Filesystem::amInPath() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amInPath($path) { - $this->scenario->condition('amInPath', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amInPath', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -45,6 +51,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Opens a file and stores it's content. * * Usage: @@ -57,13 +67,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $filename - * @see Filesystem::openFile() + * @see Codeception\Module\Filesystem::openFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function openFile($filename) { - $this->scenario->action('openFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('openFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -73,6 +81,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Deletes a file * * ``` php @@ -82,13 +94,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $filename - * @see Filesystem::deleteFile() + * @see Codeception\Module\Filesystem::deleteFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function deleteFile($filename) { - $this->scenario->action('deleteFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('deleteFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -98,6 +108,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Deletes directory with all subdirectories * * ``` php @@ -107,13 +121,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $dirname - * @see Filesystem::deleteDir() + * @see Codeception\Module\Filesystem::deleteDir() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function deleteDir($dirname) { - $this->scenario->action('deleteDir', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('deleteDir', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -123,6 +135,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Copies directory with all contents * * ``` php @@ -133,13 +149,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $src * @param $dst - * @see Filesystem::copyDir() + * @see Codeception\Module\Filesystem::copyDir() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function copyDir($src, $dst) { - $this->scenario->action('copyDir', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('copyDir', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -149,6 +163,39 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks If opened file has `text` in it. + * + * Usage: + * + * ``` php + * openFile('composer.json'); + * $I->seeInThisFile('codeception/codeception'); + * ?> + * ``` + * + * @param $text + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\Filesystem::seeInThisFile() + * @return \Codeception\Maybe + */ + public function canSeeInThisFile($text) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks If opened file has `text` in it. * * Usage: @@ -161,13 +208,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $text - * @see Filesystem::seeInThisFile() + * @see Codeception\Module\Filesystem::seeInThisFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeInThisFile($text) { - $this->scenario->assertion('seeInThisFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -177,6 +222,40 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks the strict matching of file contents. + * Unlike `seeInThisFile` will fail if file has something more then expected lines. + * Better to use with HEREDOC strings. + * Matching is done after removing "\r" chars from file content. + * + * ``` php + * openFile('process.pid'); + * $I->seeFileContentsEqual('3192'); + * ?> + * ``` + * + * @param $text + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\Filesystem::seeFileContentsEqual() + * @return \Codeception\Maybe + */ + public function canSeeFileContentsEqual($text) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks the strict matching of file contents. * Unlike `seeInThisFile` will fail if file has something more then expected lines. * Better to use with HEREDOC strings. @@ -190,13 +269,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $text - * @see Filesystem::seeFileContentsEqual() + * @see Codeception\Module\Filesystem::seeFileContentsEqual() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeFileContentsEqual($text) { - $this->scenario->assertion('seeFileContentsEqual', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -206,23 +283,52 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks If opened file doesn't contain `text` in it * * ``` php * openFile('composer.json'); - * $I->seeInThisFile('codeception/codeception'); + * $I->dontSeeInThisFile('codeception/codeception'); + * ?> + * ``` + * + * @param $text + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\Filesystem::dontSeeInThisFile() + * @return \Codeception\Maybe + */ + public function cantSeeInThisFile($text) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks If opened file doesn't contain `text` in it + * + * ``` php + * openFile('composer.json'); + * $I->dontSeeInThisFile('codeception/codeception'); * ?> * ``` * * @param $text - * @see Filesystem::dontSeeInThisFile() + * @see Codeception\Module\Filesystem::dontSeeInThisFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeInThisFile($text) { - $this->scenario->action('dontSeeInThisFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInThisFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -232,14 +338,16 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Deletes a file - * @see Filesystem::deleteThisFile() + * @see Codeception\Module\Filesystem::deleteThisFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function deleteThisFile() { - $this->scenario->action('deleteThisFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('deleteThisFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -249,6 +357,38 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if file exists in path. + * Opens a file when it's exists + * + * ``` php + * seeFileFound('UserModel.php','app/models'); + * ?> + * ``` + * + * @param $filename + * @param string $path + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Module\Filesystem::seeFileFound() + * @return \Codeception\Maybe + */ + public function canSeeFileFound($filename, $path = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if file exists in path. * Opens a file when it's exists * @@ -260,13 +400,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $filename * @param string $path - * @see Filesystem::seeFileFound() + * @see Codeception\Module\Filesystem::seeFileFound() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeFileFound($filename, $path = null) { - $this->scenario->assertion('seeFileFound', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -276,6 +414,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Erases directory contents * * ``` php @@ -285,13 +427,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $dirname - * @see Filesystem::cleanDir() + * @see Codeception\Module\Filesystem::cleanDir() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function cleanDir($dirname) { - $this->scenario->action('cleanDir', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('cleanDir', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -301,17 +441,19 @@ class TestGuy extends \Codeception\AbstractGuy /** - * Adds HTTP authentication via username/password. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Authenticates user for HTTP_AUTH * * @param $username * @param $password - * @see Framework::amHttpAuthenticated() + * @see Codeception\Util\Framework::amHttpAuthenticated() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amHttpAuthenticated($username, $password) { - $this->scenario->condition('amHttpAuthenticated', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -321,6 +463,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Opens the page. * Requires relative uri as parameter * @@ -336,13 +482,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $page - * @see Framework::amOnPage() + * @see Codeception\Util\Framework::amOnPage() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function amOnPage($page) { - $this->scenario->condition('amOnPage', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Condition('amOnPage', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -352,6 +496,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Perform a click on link or button. * Link or button are found by their names or CSS selector. * Submits a form if button is a submit type. @@ -380,13 +528,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * @param $link * @param $context - * @see Framework::click() + * @see Codeception\Util\Framework::click() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function click($link, $context = null) { - $this->scenario->action('click', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('click', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -396,6 +542,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Check if current page contains the text specified. * Specify the css selector to match only specific region. * @@ -406,18 +556,48 @@ class TestGuy extends \Codeception\AbstractGuy * $I->see('Logout'); // I can suppose user is logged in * $I->see('Sign Up','h1'); // I can suppose it's a signup page * $I->see('Sign Up','//body/h1'); // with XPath + * ?> + * ``` + * + * @param $text + * @param null $selector + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::see() + * @return \Codeception\Maybe + */ + public function canSee($text, $selector = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Check if current page contains the text specified. + * Specify the css selector to match only specific region. + * + * Examples: * + * ``` php + * see('Logout'); // I can suppose user is logged in + * $I->see('Sign Up','h1'); // I can suppose it's a signup page + * $I->see('Sign Up','//body/h1'); // with XPath + * ?> * ``` * * @param $text * @param null $selector - * @see Framework::see() + * @see Codeception\Util\Framework::see() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function see($text, $selector = null) { - $this->scenario->assertion('see', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('see', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -427,6 +607,42 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Check if current page doesn't contain the text specified. + * Specify the css selector to match only specific region. + * + * Examples: + * + * ```php + * dontSee('Login'); // I can suppose user is already logged in + * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page + * $I->dontSee('Sign Up','//body/h1'); // with XPath + * ?> + * ``` + * + * @param $text + * @param null $selector + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSee() + * @return \Codeception\Maybe + */ + public function cantSee($text, $selector = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Check if current page doesn't contain the text specified. * Specify the css selector to match only specific region. * @@ -437,17 +653,16 @@ class TestGuy extends \Codeception\AbstractGuy * $I->dontSee('Login'); // I can suppose user is already logged in * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page * $I->dontSee('Sign Up','//body/h1'); // with XPath + * ?> * ``` * * @param $text * @param null $selector - * @see Framework::dontSee() + * @see Codeception\Util\Framework::dontSee() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSee($text, $selector = null) { - $this->scenario->action('dontSee', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSee', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -457,6 +672,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if there is a link with text specified. * Specify url to match link with exact this url. * @@ -466,18 +685,47 @@ class TestGuy extends \Codeception\AbstractGuy * seeLink('Logout'); // matches Logout * $I->seeLink('Logout','/logout'); // matches Logout + * ?> + * ``` + * + * @param $text + * @param null $url + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeLink() + * @return \Codeception\Maybe + */ + public function canSeeLink($text, $url = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * + * Checks if there is a link with text specified. + * Specify url to match link with exact this url. + * + * Examples: + * + * ``` php + * seeLink('Logout'); // matches Logout + * $I->seeLink('Logout','/logout'); // matches Logout + * ?> * ``` * * @param $text * @param null $url - * @see Framework::seeLink() + * @see Codeception\Util\Framework::seeLink() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeLink($text, $url = null) { - $this->scenario->assertion('seeLink', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeLink', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -487,6 +735,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if page doesn't contain the link with text specified. * Specify url to narrow the results. * @@ -495,46 +747,46 @@ class TestGuy extends \Codeception\AbstractGuy * ``` php * dontSeeLink('Logout'); // I suppose user is not logged in - * + * ?> * ``` * * @param $text * @param null $url - * @see Framework::dontSeeLink() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeLink() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeLink($text, $url = null) { - $this->scenario->action('dontSeeLink', func_get_args()); + public function cantSeeLink($text, $url = null) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current uri contains a value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if page doesn't contain the link with text specified. + * Specify url to narrow the results. + * + * Examples: * * ``` php * seeInCurrentUrl('home'); - * // to match: /users/1 - * $I->seeInCurrentUrl('/users/'); + * $I->dontSeeLink('Logout'); // I suppose user is not logged in * ?> * ``` * - * @param $uri - * @see Framework::seeInCurrentUrl() + * @param $text + * @param null $url + * @see Codeception\Util\Framework::dontSeeLink() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeInCurrentUrl($uri) { - $this->scenario->assertion('seeInCurrentUrl', func_get_args()); + public function dontSeeLink($text, $url = null) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -544,47 +796,56 @@ class TestGuy extends \Codeception\AbstractGuy /** - * Checks that current uri does not contain a value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri contains a value * * ``` php * dontSeeInCurrentUrl('/users/'); + * // to match: /home/dashboard + * $I->seeInCurrentUrl('home'); + * // to match: /users/1 + * $I->seeInCurrentUrl('/users/'); * ?> * ``` * * @param $uri - * @see Framework::dontSeeInCurrentUrl() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeInCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeInCurrentUrl($uri) { - $this->scenario->action('dontSeeInCurrentUrl', func_get_args()); + public function canSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current url is equal to value. - * Unlike `seeInCurrentUrl` performs a strict check. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri contains a value * + * ``` php * seeCurrentUrlEquals('/'); + * // to match: /home/dashboard + * $I->seeInCurrentUrl('home'); + * // to match: /users/1 + * $I->seeInCurrentUrl('/users/'); * ?> + * ``` * * @param $uri - * @see Framework::seeCurrentUrlEquals() + * @see Codeception\Util\Framework::seeInCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCurrentUrlEquals($uri) { - $this->scenario->assertion('seeCurrentUrlEquals', func_get_args()); + public function seeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -594,46 +855,50 @@ class TestGuy extends \Codeception\AbstractGuy /** - * Checks that current url is not equal to value. - * Unlike `dontSeeInCurrentUrl` performs a strict check. + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri does not contain a value * + * ``` php * dontSeeCurrentUrlEquals('/'); + * $I->dontSeeInCurrentUrl('/users/'); * ?> + * ``` * * @param $uri - * @see Framework::dontSeeCurrentUrlEquals() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeInCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeCurrentUrlEquals($uri) { - $this->scenario->action('dontSeeCurrentUrlEquals', func_get_args()); + public function cantSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); } return new Maybe(); } - - /** - * Checks that current url is matches a RegEx value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current uri does not contain a value * + * ``` php * seeCurrentUrlMatches('~$/users/(\d+)~'); + * $I->dontSeeInCurrentUrl('/users/'); * ?> + * ``` * * @param $uri - * @see Framework::seeCurrentUrlMatches() + * @see Codeception\Util\Framework::dontSeeInCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function seeCurrentUrlMatches($uri) { - $this->scenario->assertion('seeCurrentUrlMatches', func_get_args()); + public function dontSeeInCurrentUrl($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -643,21 +908,166 @@ class TestGuy extends \Codeception\AbstractGuy /** - * Checks that current url does not match a RegEx value + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * + * Checks that current url is equal to value. + * Unlike `seeInCurrentUrl` performs a strict check. + * + * ``` php * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); + * $I->seeCurrentUrlEquals('/'); * ?> + * ``` * * @param $uri - * @see Framework::dontSeeCurrentUrlMatches() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeCurrentUrlEquals() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ - public function dontSeeCurrentUrlMatches($uri) { - $this->scenario->action('dontSeeCurrentUrlMatches', func_get_args()); + public function canSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is equal to value. + * Unlike `seeInCurrentUrl` performs a strict check. + * + * ``` php + * seeCurrentUrlEquals('/'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Framework::seeCurrentUrlEquals() + * @return \Codeception\Maybe + */ + public function seeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is not equal to value. + * Unlike `dontSeeInCurrentUrl` performs a strict check. + * + * ``` php + * dontSeeCurrentUrlEquals('/'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeCurrentUrlEquals() + * @return \Codeception\Maybe + */ + public function cantSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is not equal to value. + * Unlike `dontSeeInCurrentUrl` performs a strict check. + * + * ``` php + * dontSeeCurrentUrlEquals('/'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Framework::dontSeeCurrentUrlEquals() + * @return \Codeception\Maybe + */ + public function dontSeeCurrentUrlEquals($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is matches a RegEx value + * + * ``` php + * seeCurrentUrlMatches('~$/users/(\d+)~'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeCurrentUrlMatches() + * @return \Codeception\Maybe + */ + public function canSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url is matches a RegEx value + * + * ``` php + * seeCurrentUrlMatches('~$/users/(\d+)~'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Framework::seeCurrentUrlMatches() + * @return \Codeception\Maybe + */ + public function seeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -667,6 +1077,65 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url does not match a RegEx value + * + * ``` php + * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); + * ?> + * ``` + * + * @param $uri + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeCurrentUrlMatches() + * @return \Codeception\Maybe + */ + public function cantSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that current url does not match a RegEx value + * + * ``` php + * dontSeeCurrentUrlMatches('~$/users/(\d+)~'); + * ?> + * ``` + * + * @param $uri + * @see Codeception\Util\Framework::dontSeeCurrentUrlMatches() + * @return \Codeception\Maybe + */ + public function dontSeeCurrentUrlMatches($uri) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Takes a parameters from current URI by RegEx. * If no url provided returns full URI. * @@ -680,13 +1149,11 @@ class TestGuy extends \Codeception\AbstractGuy * @param null $uri * @internal param $url * @return mixed - * @see Framework::grabFromCurrentUrl() + * @see Codeception\Util\Framework::grabFromCurrentUrl() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabFromCurrentUrl($uri = null) { - $this->scenario->action('grabFromCurrentUrl', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -696,6 +1163,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Assert if the specified checkbox is checked. * Use css selector or xpath to match. * @@ -706,17 +1177,46 @@ class TestGuy extends \Codeception\AbstractGuy * $I->seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); + * ?> + * ``` * + * @param $checkbox + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function canSeeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is checked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form. + * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]'); + * ?> * ``` * * @param $checkbox - * @see Framework::seeCheckboxIsChecked() + * @see Codeception\Util\Framework::seeCheckboxIsChecked() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeCheckboxIsChecked($checkbox) { - $this->scenario->assertion('seeCheckboxIsChecked', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -726,6 +1226,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Assert if the specified checkbox is unchecked. * Use css selector or xpath to match. * @@ -735,17 +1239,45 @@ class TestGuy extends \Codeception\AbstractGuy * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. + * ?> + * ``` * + * @param $checkbox + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeCheckboxIsChecked() + * @return \Codeception\Maybe + */ + public function cantSeeCheckboxIsChecked($checkbox) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Assert if the specified checkbox is unchecked. + * Use css selector or xpath to match. + * + * Example: + * + * ``` php + * dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms + * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form. + * ?> * ``` * * @param $checkbox - * @see Framework::dontSeeCheckboxIsChecked() + * @see Codeception\Util\Framework::dontSeeCheckboxIsChecked() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeCheckboxIsChecked($checkbox) { - $this->scenario->action('dontSeeCheckboxIsChecked', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -755,6 +1287,44 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that an input field or textarea contains value. + * Field is matched either by label or CSS or Xpath + * + * Example: + * + * ``` php + * seeInField('Body','Type your comment here'); + * $I->seeInField('form textarea[name=body]','Type your comment here'); + * $I->seeInField('form input[type=hidden]','hidden_value'); + * $I->seeInField('#searchform input','Search'); + * $I->seeInField('//form/*[@name=search]','Search'); + * ?> + * ``` + * + * @param $field + * @param $value + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeInField() + * @return \Codeception\Maybe + */ + public function canSeeInField($field, $value) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that an input field or textarea contains value. * Field is matched either by label or CSS or Xpath * @@ -772,13 +1342,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $field * @param $value - * @see Framework::seeInField() + * @see Codeception\Util\Framework::seeInField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeInField($field, $value) { - $this->scenario->assertion('seeInField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -788,6 +1356,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that an input field or textarea doesn't contain value. * Field is matched either by label or CSS or Xpath * Example: @@ -804,13 +1376,44 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $field * @param $value - * @see Framework::dontSeeInField() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeInField() + * @return \Codeception\Maybe + */ + public function cantSeeInField($field, $value) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that an input field or textarea doesn't contain value. + * Field is matched either by label or CSS or Xpath + * Example: + * + * ``` php + * dontSeeInField('Body','Type your comment here'); + * $I->dontSeeInField('form textarea[name=body]','Type your comment here'); + * $I->dontSeeInField('form input[type=hidden]','hidden_value'); + * $I->dontSeeInField('#searchform input','Search'); + * $I->dontSeeInField('//form/*[@name=search]','Search'); + * ?> + * ``` + * + * @param $field + * @param $value + * @see Codeception\Util\Framework::dontSeeInField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeInField($field, $value) { - $this->scenario->action('dontSeeInField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -820,6 +1423,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Submits a form located on page. * Specify the form by it's css or xpath selector. * Fill the form fields values as array. @@ -832,7 +1439,7 @@ class TestGuy extends \Codeception\AbstractGuy * * ``` php * submitForm('#login', ['login' => 'davert', 'password' => '123456']); + * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456')); * * ``` * @@ -851,20 +1458,18 @@ class TestGuy extends \Codeception\AbstractGuy * * ``` php * submitForm('#userForm', ['user' => ['login' => 'Davert', 'password' => '123456', 'agree' => true]]); + * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true))); * * ``` * Note, that pricing plan will be set to Paid, as it's selected on page. * * @param $selector * @param $params - * @see Framework::submitForm() + * @see Codeception\Util\Framework::submitForm() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function submitForm($selector, $params) { - $this->scenario->action('submitForm', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('submitForm', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -874,17 +1479,27 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Fills a text field or textarea with value. + * + * Example: + * + * ``` php + * fillField("//input[@type='text']", "Hello World!"); + * ?> + * ``` * * @param $field * @param $value - * @see Framework::fillField() + * @see Codeception\Util\Framework::fillField() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function fillField($field, $value) { - $this->scenario->action('fillField', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('fillField', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -894,6 +1509,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Selects an option in select tag or in radio button group. * * Example: @@ -906,15 +1525,21 @@ class TestGuy extends \Codeception\AbstractGuy * ?> * ``` * + * Can select multiple options if second argument is array: + * + * ``` php + * selectOption('Which OS do you use?', array('Windows','Linux')); + * ?> + * ``` + * * @param $select * @param $option - * @see Framework::selectOption() + * @see Codeception\Util\Framework::selectOption() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function selectOption($select, $option) { - $this->scenario->action('selectOption', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('selectOption', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -924,6 +1549,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Ticks a checkbox. * For radio buttons use `selectOption` method. * @@ -936,13 +1565,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $option - * @see Framework::checkOption() + * @see Codeception\Util\Framework::checkOption() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function checkOption($option) { - $this->scenario->action('checkOption', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('checkOption', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -952,6 +1579,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Unticks a checkbox. * * Example: @@ -963,13 +1594,11 @@ class TestGuy extends \Codeception\AbstractGuy * ``` * * @param $option - * @see Framework::uncheckOption() + * @see Codeception\Util\Framework::uncheckOption() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function uncheckOption($option) { - $this->scenario->action('uncheckOption', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('uncheckOption', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -979,26 +1608,28 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Attaches file from Codeception data directory to upload field. * * Example: * * ``` php * attachFile('prices.xls'); + * // file is stored in 'tests/_data/prices.xls' + * $I->attachFile('input[@type="file"]', 'prices.xls'); * ?> * ``` * * @param $field * @param $filename - * @see Framework::attachFile() + * @see Codeception\Util\Framework::attachFile() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function attachFile($field, $filename) { - $this->scenario->action('attachFile', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('attachFile', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1008,6 +1639,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * If your page triggers an ajax request, you can perform it manually. * This action sends a GET ajax request with specified params. * @@ -1015,13 +1650,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $uri * @param $params - * @see Framework::sendAjaxGetRequest() + * @see Codeception\Util\Framework::sendAjaxGetRequest() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function sendAjaxGetRequest($uri, $params = null) { - $this->scenario->action('sendAjaxGetRequest', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1031,6 +1664,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * If your page triggers an ajax request, you can perform it manually. * This action sends a POST ajax request with specified params. * Additional params can be passed as array. @@ -1042,20 +1679,18 @@ class TestGuy extends \Codeception\AbstractGuy * * ``` php * sendAjaxPostRequest('/updateSettings', ['notifications' => true]; // POST - * $I->sendAjaxGetRequest('/updateSettings', ['notifications' => true]; // GET + * $I->sendAjaxPostRequest('/updateSettings', array('notifications' => true); // POST + * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true); // GET * * ``` * * @param $uri * @param $params - * @see Framework::sendAjaxPostRequest() + * @see Codeception\Util\Framework::sendAjaxPostRequest() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function sendAjaxPostRequest($uri, $params = null) { - $this->scenario->action('sendAjaxPostRequest', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1065,23 +1700,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- * - * @see Framework::formatResponse() - * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! - */ - public function formatResponse($response) { - $this->scenario->action('formatResponse', func_get_args()); - if ($this->scenario->running()) { - $result = $this->scenario->runStep(); - return new Maybe($result); - } - return new Maybe(); - } - - - /** * Finds and returns text contents of element. * Element is searched by CSS selector, XPath or matcher by regex. * @@ -1097,13 +1719,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $cssOrXPathOrRegex * @return mixed - * @see Framework::grabTextFrom() + * @see Codeception\Util\Framework::grabTextFrom() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabTextFrom($cssOrXPathOrRegex) { - $this->scenario->action('grabTextFrom', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabTextFrom', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1113,6 +1733,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Finds and returns field and returns it's value. * Searches by field name, then by CSS, then by XPath * @@ -1128,13 +1752,11 @@ class TestGuy extends \Codeception\AbstractGuy * * @param $field * @return mixed - * @see Framework::grabValueFrom() + * @see Codeception\Util\Framework::grabValueFrom() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function grabValueFrom($field) { - $this->scenario->action('grabValueFrom', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Action('grabValueFrom', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1144,22 +1766,50 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if element exists on a page, matching it by CSS or XPath * * ``` php * seeElement('.error'); - * $I->seeElement(//form/input[1]); + * $I->seeElement('//form/input[1]'); * ?> * ``` * @param $selector - * @see Framework::seeElement() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeElement() + * @return \Codeception\Maybe + */ + public function canSeeElement($selector) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if element exists on a page, matching it by CSS or XPath + * + * ``` php + * seeElement('.error'); + * $I->seeElement('//form/input[1]'); + * ?> + * ``` + * @param $selector + * @see Codeception\Util\Framework::seeElement() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeElement($selector) { - $this->scenario->assertion('seeElement', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeElement', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1169,22 +1819,54 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath + * + * Example: + * + * ``` php + * dontSeeElement('.error'); + * $I->dontSeeElement('//form/input[1]'); + * ?> + * ``` + * @param $selector + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeElement() + * @return \Codeception\Maybe + */ + public function cantSeeElement($selector) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if element does not exist (or is visible) on a page, matching it by CSS or XPath * + * Example: + * * ``` php * dontSeeElement('.error'); - * $I->dontSeeElement(//form/input[1]); + * $I->dontSeeElement('//form/input[1]'); * ?> * ``` * @param $selector - * @see Framework::dontSeeElement() + * @see Codeception\Util\Framework::dontSeeElement() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeElement($selector) { - $this->scenario->action('dontSeeElement', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1194,6 +1876,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if option is selected in select field. * * ``` php @@ -1205,13 +1891,39 @@ class TestGuy extends \Codeception\AbstractGuy * @param $selector * @param $optionText * @return mixed - * @see Framework::seeOptionIsSelected() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeOptionIsSelected() + * @return \Codeception\Maybe + */ + public function canSeeOptionIsSelected($select, $optionText) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if option is selected in select field. + * + * ``` php + * seeOptionIsSelected('#form input[name=payment]', 'Visa'); + * ?> + * ``` + * + * @param $selector + * @param $optionText + * @return mixed + * @see Codeception\Util\Framework::seeOptionIsSelected() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeOptionIsSelected($select, $optionText) { - $this->scenario->assertion('seeOptionIsSelected', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1221,6 +1933,10 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks if option is not selected in select field. * * ``` php @@ -1232,13 +1948,39 @@ class TestGuy extends \Codeception\AbstractGuy * @param $selector * @param $optionText * @return mixed - * @see Framework::dontSeeOptionIsSelected() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeOptionIsSelected() + * @return \Codeception\Maybe + */ + public function cantSeeOptionIsSelected($select, $optionText) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks if option is not selected in select field. + * + * ``` php + * dontSeeOptionIsSelected('#form input[name=payment]', 'Visa'); + * ?> + * ``` + * + * @param $selector + * @param $optionText + * @return mixed + * @see Codeception\Util\Framework::dontSeeOptionIsSelected() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function dontSeeOptionIsSelected($select, $optionText) { - $this->scenario->action('dontSeeOptionIsSelected', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1248,14 +1990,34 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Asserts that current page has 404 response status code. + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seePageNotFound() + * @return \Codeception\Maybe + */ + public function canSeePageNotFound() { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Asserts that current page has 404 response status code. - * @see Framework::seePageNotFound() + * @see Codeception\Util\Framework::seePageNotFound() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seePageNotFound() { - $this->scenario->assertion('seePageNotFound', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); @@ -1265,17 +2027,138 @@ class TestGuy extends \Codeception\AbstractGuy /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * * Checks that response code is equal to value provided. * * @param $code * @return mixed - * @see Framework::seeResponseCodeIs() + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeResponseCodeIs() + * @return \Codeception\Maybe + */ + public function canSeeResponseCodeIs($code) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that response code is equal to value provided. + * + * @param $code + * @return mixed + * @see Codeception\Util\Framework::seeResponseCodeIs() * @return \Codeception\Maybe - * ! This method is generated. DO NOT EDIT. ! - * ! Documentation taken from corresponding module ! */ public function seeResponseCodeIs($code) { - $this->scenario->assertion('seeResponseCodeIs', func_get_args()); + $this->scenario->addStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title contains text. + * + * ``` php + * seeInTitle('Blog - Post #1'); + * ?> + * ``` + * + * @param $title + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::seeInTitle() + * @return \Codeception\Maybe + */ + public function canSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title contains text. + * + * ``` php + * seeInTitle('Blog - Post #1'); + * ?> + * ``` + * + * @param $title + * @return mixed + * @see Codeception\Util\Framework::seeInTitle() + * @return \Codeception\Maybe + */ + public function seeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + + + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title does not contain text. + * + * @param $title + * @return mixed + * Conditional Assertion: Test won't be stopped on fail + * @see Codeception\Util\Framework::dontSeeInTitle() + * @return \Codeception\Maybe + */ + public function cantSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args())); + if ($this->scenario->running()) { + $result = $this->scenario->runStep(); + return new Maybe($result); + } + return new Maybe(); + } + /** + * This method is generated. + * Documentation taken from corresponding module. + * ---------------------------------------------- + * + * Checks that page title does not contain text. + * + * @param $title + * @return mixed + * @see Codeception\Util\Framework::dontSeeInTitle() + * @return \Codeception\Maybe + */ + public function dontSeeInTitle($title) { + $this->scenario->addStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args())); if ($this->scenario->running()) { $result = $this->scenario->runStep(); return new Maybe($result); diff --git a/apps/basic/tests/unit/CodeGuy.php b/apps/basic/tests/unit/CodeGuy.php index adcd618..613c754 100644 --- a/apps/basic/tests/unit/CodeGuy.php +++ b/apps/basic/tests/unit/CodeGuy.php @@ -1,23 +1,26 @@ Date: Thu, 14 Nov 2013 12:42:27 -0500 Subject: [PATCH 77/79] removed extra line. --- apps/basic/tests/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/basic/tests/README.md b/apps/basic/tests/README.md index eb9a0fe..c87f762 100644 --- a/apps/basic/tests/README.md +++ b/apps/basic/tests/README.md @@ -4,7 +4,6 @@ These tests are developed with [Codeception PHP Testing Framework](http://codece To run the tests, follow these steps: 1. [Install Codeception](http://codeception.com/quickstart) if you do not have it yet. -2. Update tests 2. Create test configuration files based on your environment: - Copy `acceptance.suite.dist.yml` to `acceptance.suite.yml` and customize it; - Copy `functional.suite.dist.yml` to `functional.suite.yml` and customize it; From 3106105ed070e0a6bfc0849bb0abd41032fe41aa Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 14 Nov 2013 15:36:38 -0500 Subject: [PATCH 78/79] changed color setting. --- apps/basic/codeception.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/basic/codeception.yml b/apps/basic/codeception.yml index 5b1f441..b6adeb5 100644 --- a/apps/basic/codeception.yml +++ b/apps/basic/codeception.yml @@ -6,7 +6,6 @@ paths: settings: bootstrap: _bootstrap.php suite_class: \PHPUnit_Framework_TestSuite - colors: true memory_limit: 1024M log: true modules: From 42a58870fe5d81d970c9009f06dd523a8f76d129 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 14 Nov 2013 22:01:26 -0500 Subject: [PATCH 79/79] moved db interfaces and traits back to yii/db. --- framework/yii/ar/ActiveQueryInterface.php | 78 --------- framework/yii/ar/ActiveQueryTrait.php | 201 ---------------------- framework/yii/ar/ActiveRelationInterface.php | 29 ---- framework/yii/ar/ActiveRelationTrait.php | 247 --------------------------- framework/yii/data/ActiveDataProvider.php | 2 +- framework/yii/db/ActiveQuery.php | 2 - framework/yii/db/ActiveQueryInterface.php | 77 +++++++++ framework/yii/db/ActiveQueryTrait.php | 199 +++++++++++++++++++++ framework/yii/db/ActiveRecord.php | 1 - framework/yii/db/ActiveRelation.php | 2 - framework/yii/db/ActiveRelationInterface.php | 29 ++++ framework/yii/db/ActiveRelationTrait.php | 246 ++++++++++++++++++++++++++ 12 files changed, 552 insertions(+), 561 deletions(-) delete mode 100644 framework/yii/ar/ActiveQueryInterface.php delete mode 100644 framework/yii/ar/ActiveQueryTrait.php delete mode 100644 framework/yii/ar/ActiveRelationInterface.php delete mode 100644 framework/yii/ar/ActiveRelationTrait.php create mode 100644 framework/yii/db/ActiveQueryInterface.php create mode 100644 framework/yii/db/ActiveQueryTrait.php create mode 100644 framework/yii/db/ActiveRelationInterface.php create mode 100644 framework/yii/db/ActiveRelationTrait.php diff --git a/framework/yii/ar/ActiveQueryInterface.php b/framework/yii/ar/ActiveQueryInterface.php deleted file mode 100644 index 7a1efa1..0000000 --- a/framework/yii/ar/ActiveQueryInterface.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -interface ActiveQueryInterface extends QueryInterface -{ - /** - * Sets the [[asArray]] property. - * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. - * @return static the query object itself - */ - public function asArray($value = true); - - /** - * Sets the [[indexBy]] property. - * @param string|callable $column the name of the column by which the query results should be indexed by. - * This can also be a callable (e.g. anonymous function) that returns the index value based on the given - * row or model data. The signature of the callable should be: - * - * ~~~ - * // $model is an AR instance when `asArray` is false, - * // or an array of column values when `asArray` is true. - * function ($model) - * { - * // return the index value corresponding to $model - * } - * ~~~ - * - * @return static the query object itself - */ - public function indexBy($column); - - /** - * Specifies the relations with which this query should be performed. - * - * The parameters to this method can be either one or multiple strings, or a single array - * of relation names and the optional callbacks to customize the relations. - * - * A relation name can refer to a relation defined in [[modelClass]] - * or a sub-relation that stands for a relation of a related record. - * For example, `orders.address` means the `address` relation defined - * in the model class corresponding to the `orders` relation. - * - * The followings are some usage examples: - * - * ~~~ - * // find customers together with their orders and country - * Customer::find()->with('orders', 'country')->all(); - * // find customers together with their orders and the orders' shipping address - * Customer::find()->with('orders.address')->all(); - * // find customers together with their country and orders of status 1 - * Customer::find()->with([ - * 'orders' => function($query) { - * $query->andWhere('status = 1'); - * }, - * 'country', - * ])->all(); - * ~~~ - * - * @return static the query object itself - */ - public function with(); -} diff --git a/framework/yii/ar/ActiveQueryTrait.php b/framework/yii/ar/ActiveQueryTrait.php deleted file mode 100644 index 98e7870..0000000 --- a/framework/yii/ar/ActiveQueryTrait.php +++ /dev/null @@ -1,201 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -trait ActiveQueryTrait -{ - /** - * @var string the name of the ActiveRecord class. - */ - public $modelClass; - /** - * @var array list of relations that this query should be performed with - */ - public $with; - /** - * @var boolean whether to return each record as an array. If false (default), an object - * of [[modelClass]] will be created to represent each record. - */ - public $asArray; - - - /** - * PHP magic method. - * This method allows calling static method defined in [[modelClass]] via this query object. - * It is mainly implemented for supporting the feature of scope. - * @param string $name the method name to be called - * @param array $params the parameters passed to the method - * @return mixed the method return result - */ - public function __call($name, $params) - { - if (method_exists($this->modelClass, $name)) { - array_unshift($params, $this); - call_user_func_array([$this->modelClass, $name], $params); - return $this; - } else { - return parent::__call($name, $params); - } - } - - /** - * Sets the [[asArray]] property. - * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. - * @return static the query object itself - */ - public function asArray($value = true) - { - $this->asArray = $value; - return $this; - } - - /** - * Specifies the relations with which this query should be performed. - * - * The parameters to this method can be either one or multiple strings, or a single array - * of relation names and the optional callbacks to customize the relations. - * - * A relation name can refer to a relation defined in [[modelClass]] - * or a sub-relation that stands for a relation of a related record. - * For example, `orders.address` means the `address` relation defined - * in the model class corresponding to the `orders` relation. - * - * The followings are some usage examples: - * - * ~~~ - * // find customers together with their orders and country - * Customer::find()->with('orders', 'country')->all(); - * // find customers together with their orders and the orders' shipping address - * Customer::find()->with('orders.address')->all(); - * // find customers together with their country and orders of status 1 - * Customer::find()->with([ - * 'orders' => function($query) { - * $query->andWhere('status = 1'); - * }, - * 'country', - * ])->all(); - * ~~~ - * - * @return static the query object itself - */ - public function with() - { - $this->with = func_get_args(); - if (isset($this->with[0]) && is_array($this->with[0])) { - // the parameter is given as an array - $this->with = $this->with[0]; - } - return $this; - } - - /** - * Converts found rows into model instances - * @param array $rows - * @return array|ActiveRecord[] - */ - private function createModels($rows) - { - $models = []; - if ($this->asArray) { - if ($this->indexBy === null) { - return $rows; - } - foreach ($rows as $row) { - if (is_string($this->indexBy)) { - $key = $row[$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); - } - $models[$key] = $row; - } - } else { - /** @var ActiveRecord $class */ - $class = $this->modelClass; - if ($this->indexBy === null) { - foreach ($rows as $row) { - $models[] = $class::create($row); - } - } else { - foreach ($rows as $row) { - $model = $class::create($row); - if (is_string($this->indexBy)) { - $key = $model->{$this->indexBy}; - } else { - $key = call_user_func($this->indexBy, $model); - } - $models[$key] = $model; - } - } - } - return $models; - } - - /** - * @param ActiveRecord[] $models - * @param array $with - */ - private function populateRelations(&$models, $with) - { - $primaryModel = new $this->modelClass; - $relations = $this->normalizeRelations($primaryModel, $with); - foreach ($relations as $name => $relation) { - if ($relation->asArray === null) { - // inherit asArray from primary query - $relation->asArray = $this->asArray; - } - $relation->findWith($name, $models); - } - } - - /** - * @param ActiveRecord $model - * @param array $with - * @return ActiveRelationInterface[] - */ - private function normalizeRelations($model, $with) - { - $relations = []; - foreach ($with as $name => $callback) { - if (is_integer($name)) { - $name = $callback; - $callback = null; - } - if (($pos = strpos($name, '.')) !== false) { - // with sub-relations - $childName = substr($name, $pos + 1); - $name = substr($name, 0, $pos); - } else { - $childName = null; - } - - if (!isset($relations[$name])) { - $relation = $model->getRelation($name); - $relation->primaryModel = null; - $relations[$name] = $relation; - } else { - $relation = $relations[$name]; - } - - if (isset($childName)) { - $relation->with[$childName] = $callback; - } elseif ($callback !== null) { - call_user_func($callback, $relation); - } - } - return $relations; - } -} diff --git a/framework/yii/ar/ActiveRelationInterface.php b/framework/yii/ar/ActiveRelationInterface.php deleted file mode 100644 index 706d394..0000000 --- a/framework/yii/ar/ActiveRelationInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -interface ActiveRelationInterface extends ActiveQueryInterface -{ - /** - * Specifies the relation associated with the pivot table. - * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. - * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. - * Its signature should be `function($query)`, where `$query` is the query to be customized. - * @return static the relation object itself. - */ - public function via($relationName, $callable = null); -} diff --git a/framework/yii/ar/ActiveRelationTrait.php b/framework/yii/ar/ActiveRelationTrait.php deleted file mode 100644 index d87b22d..0000000 --- a/framework/yii/ar/ActiveRelationTrait.php +++ /dev/null @@ -1,247 +0,0 @@ - - * @author Carsten Brandt - * @since 2.0 - */ -trait ActiveRelationTrait -{ - /** - * @var boolean whether this relation should populate all query results into AR instances. - * If false, only the first row of the results will be retrieved. - */ - public $multiple; - /** - * @var ActiveRecord the primary model that this relation is associated with. - * This is used only in lazy loading with dynamic query options. - */ - public $primaryModel; - /** - * @var array the columns of the primary and foreign tables that establish the relation. - * The array keys must be columns of the table for this relation, and the array values - * must be the corresponding columns from the primary table. - * Do not prefix or quote the column names as this will be done automatically by Yii. - */ - public $link; - /** - * @var array the query associated with the pivot table. Please call [[via()]] - * to set this property instead of directly setting it. - */ - public $via; - - /** - * Clones internal objects. - */ - public function __clone() - { - // make a clone of "via" object so that the same query object can be reused multiple times - if (is_object($this->via)) { - $this->via = clone $this->via; - } elseif (is_array($this->via)) { - $this->via = [$this->via[0], clone $this->via[1]]; - } - } - - /** - * Specifies the relation associated with the pivot table. - * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. - * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. - * Its signature should be `function($query)`, where `$query` is the query to be customized. - * @return static the relation object itself. - */ - public function via($relationName, $callable = null) - { - $relation = $this->primaryModel->getRelation($relationName); - $this->via = [$relationName, $relation]; - if ($callable !== null) { - call_user_func($callable, $relation); - } - return $this; - } - - /** - * Finds the related records and populates them into the primary models. - * This method is internally used by [[ActiveQuery]]. Do not call it directly. - * @param string $name the relation name - * @param array $primaryModels primary models - * @return array the related models - * @throws InvalidConfigException - */ - public function findWith($name, &$primaryModels) - { - if (!is_array($this->link)) { - throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); - } - - if ($this->via instanceof self) { - // via pivot table - /** @var ActiveRelationTrait $viaQuery */ - $viaQuery = $this->via; - $viaModels = $viaQuery->findPivotRows($primaryModels); - $this->filterByModels($viaModels); - } elseif (is_array($this->via)) { - // via relation - /** @var ActiveRelationTrait $viaQuery */ - list($viaName, $viaQuery) = $this->via; - $viaQuery->primaryModel = null; - $viaModels = $viaQuery->findWith($viaName, $primaryModels); - $this->filterByModels($viaModels); - } else { - $this->filterByModels($primaryModels); - } - - if (count($primaryModels) === 1 && !$this->multiple) { - $model = $this->one(); - foreach ($primaryModels as $i => $primaryModel) { - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $model); - } else { - $primaryModels[$i][$name] = $model; - } - } - return [$model]; - } else { - $models = $this->all(); - if (isset($viaModels, $viaQuery)) { - $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); - } else { - $buckets = $this->buildBuckets($models, $this->link); - } - - $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); - foreach ($primaryModels as $i => $primaryModel) { - $key = $this->getModelKey($primaryModel, $link); - $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); - if ($primaryModel instanceof ActiveRecord) { - $primaryModel->populateRelation($name, $value); - } else { - $primaryModels[$i][$name] = $value; - } - } - return $models; - } - } - - /** - * @param array $models - * @param array $link - * @param array $viaModels - * @param array $viaLink - * @return array - */ - private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) - { - $buckets = []; - $linkKeys = array_keys($link); - foreach ($models as $i => $model) { - $key = $this->getModelKey($model, $linkKeys); - if ($this->indexBy !== null) { - $buckets[$key][$i] = $model; - } else { - $buckets[$key][] = $model; - } - } - - if ($viaModels !== null) { - $viaBuckets = []; - $viaLinkKeys = array_keys($viaLink); - $linkValues = array_values($link); - foreach ($viaModels as $viaModel) { - $key1 = $this->getModelKey($viaModel, $viaLinkKeys); - $key2 = $this->getModelKey($viaModel, $linkValues); - if (isset($buckets[$key2])) { - foreach ($buckets[$key2] as $i => $bucket) { - if ($this->indexBy !== null) { - $viaBuckets[$key1][$i] = $bucket; - } else { - $viaBuckets[$key1][] = $bucket; - } - } - } - } - $buckets = $viaBuckets; - } - - if (!$this->multiple) { - foreach ($buckets as $i => $bucket) { - $buckets[$i] = reset($bucket); - } - } - return $buckets; - } - - /** - * @param ActiveRecord|array $model - * @param array $attributes - * @return string - */ - private function getModelKey($model, $attributes) - { - if (count($attributes) > 1) { - $key = []; - foreach ($attributes as $attribute) { - $key[] = $model[$attribute]; - } - return serialize($key); - } else { - $attribute = reset($attributes); - return $model[$attribute]; - } - } - - /** - * @param array $models - */ - private function filterByModels($models) - { - $attributes = array_keys($this->link); - $values = []; - if (count($attributes) === 1) { - // single key - $attribute = reset($this->link); - foreach ($models as $model) { - if (($value = $model[$attribute]) !== null) { - $values[] = $value; - } - } - } else { - // composite keys - foreach ($models as $model) { - $v = []; - foreach ($this->link as $attribute => $link) { - $v[$attribute] = $model[$link]; - } - $values[] = $v; - } - } - $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); - } - - /** - * @param ActiveRecord[] $primaryModels - * @return array - */ - private function findPivotRows($primaryModels) - { - if (empty($primaryModels)) { - return []; - } - $this->filterByModels($primaryModels); - /** @var ActiveRecord $primaryModel */ - $primaryModel = reset($primaryModels); - return $this->asArray()->all($primaryModel->getDb()); - } -} diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/yii/data/ActiveDataProvider.php index 0943be4..89a7cf6 100644 --- a/framework/yii/data/ActiveDataProvider.php +++ b/framework/yii/data/ActiveDataProvider.php @@ -8,7 +8,7 @@ namespace yii\data; use Yii; -use yii\ar\ActiveQueryInterface; +use yii\db\ActiveQueryInterface; use yii\base\InvalidConfigException; use yii\base\Model; use yii\db\Connection; diff --git a/framework/yii/db/ActiveQuery.php b/framework/yii/db/ActiveQuery.php index 3995b1b..4d21fbe 100644 --- a/framework/yii/db/ActiveQuery.php +++ b/framework/yii/db/ActiveQuery.php @@ -7,8 +7,6 @@ */ namespace yii\db; -use yii\ar\ActiveQueryInterface; -use yii\ar\ActiveQueryTrait; /** * ActiveQuery represents a DB query associated with an Active Record class. diff --git a/framework/yii/db/ActiveQueryInterface.php b/framework/yii/db/ActiveQueryInterface.php new file mode 100644 index 0000000..a2e132f --- /dev/null +++ b/framework/yii/db/ActiveQueryInterface.php @@ -0,0 +1,77 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +interface ActiveQueryInterface extends QueryInterface +{ + /** + * Sets the [[asArray]] property. + * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. + * @return static the query object itself + */ + public function asArray($value = true); + + /** + * Sets the [[indexBy]] property. + * @param string|callable $column the name of the column by which the query results should be indexed by. + * This can also be a callable (e.g. anonymous function) that returns the index value based on the given + * row or model data. The signature of the callable should be: + * + * ~~~ + * // $model is an AR instance when `asArray` is false, + * // or an array of column values when `asArray` is true. + * function ($model) + * { + * // return the index value corresponding to $model + * } + * ~~~ + * + * @return static the query object itself + */ + public function indexBy($column); + + /** + * Specifies the relations with which this query should be performed. + * + * The parameters to this method can be either one or multiple strings, or a single array + * of relation names and the optional callbacks to customize the relations. + * + * A relation name can refer to a relation defined in [[modelClass]] + * or a sub-relation that stands for a relation of a related record. + * For example, `orders.address` means the `address` relation defined + * in the model class corresponding to the `orders` relation. + * + * The followings are some usage examples: + * + * ~~~ + * // find customers together with their orders and country + * Customer::find()->with('orders', 'country')->all(); + * // find customers together with their orders and the orders' shipping address + * Customer::find()->with('orders.address')->all(); + * // find customers together with their country and orders of status 1 + * Customer::find()->with([ + * 'orders' => function($query) { + * $query->andWhere('status = 1'); + * }, + * 'country', + * ])->all(); + * ~~~ + * + * @return static the query object itself + */ + public function with(); +} diff --git a/framework/yii/db/ActiveQueryTrait.php b/framework/yii/db/ActiveQueryTrait.php new file mode 100644 index 0000000..51135d8 --- /dev/null +++ b/framework/yii/db/ActiveQueryTrait.php @@ -0,0 +1,199 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +trait ActiveQueryTrait +{ + /** + * @var string the name of the ActiveRecord class. + */ + public $modelClass; + /** + * @var array list of relations that this query should be performed with + */ + public $with; + /** + * @var boolean whether to return each record as an array. If false (default), an object + * of [[modelClass]] will be created to represent each record. + */ + public $asArray; + + + /** + * PHP magic method. + * This method allows calling static method defined in [[modelClass]] via this query object. + * It is mainly implemented for supporting the feature of scope. + * @param string $name the method name to be called + * @param array $params the parameters passed to the method + * @return mixed the method return result + */ + public function __call($name, $params) + { + if (method_exists($this->modelClass, $name)) { + array_unshift($params, $this); + call_user_func_array([$this->modelClass, $name], $params); + return $this; + } else { + return parent::__call($name, $params); + } + } + + /** + * Sets the [[asArray]] property. + * @param boolean $value whether to return the query results in terms of arrays instead of Active Records. + * @return static the query object itself + */ + public function asArray($value = true) + { + $this->asArray = $value; + return $this; + } + + /** + * Specifies the relations with which this query should be performed. + * + * The parameters to this method can be either one or multiple strings, or a single array + * of relation names and the optional callbacks to customize the relations. + * + * A relation name can refer to a relation defined in [[modelClass]] + * or a sub-relation that stands for a relation of a related record. + * For example, `orders.address` means the `address` relation defined + * in the model class corresponding to the `orders` relation. + * + * The followings are some usage examples: + * + * ~~~ + * // find customers together with their orders and country + * Customer::find()->with('orders', 'country')->all(); + * // find customers together with their orders and the orders' shipping address + * Customer::find()->with('orders.address')->all(); + * // find customers together with their country and orders of status 1 + * Customer::find()->with([ + * 'orders' => function($query) { + * $query->andWhere('status = 1'); + * }, + * 'country', + * ])->all(); + * ~~~ + * + * @return static the query object itself + */ + public function with() + { + $this->with = func_get_args(); + if (isset($this->with[0]) && is_array($this->with[0])) { + // the parameter is given as an array + $this->with = $this->with[0]; + } + return $this; + } + + /** + * Converts found rows into model instances + * @param array $rows + * @return array|ActiveRecord[] + */ + private function createModels($rows) + { + $models = []; + if ($this->asArray) { + if ($this->indexBy === null) { + return $rows; + } + foreach ($rows as $row) { + if (is_string($this->indexBy)) { + $key = $row[$this->indexBy]; + } else { + $key = call_user_func($this->indexBy, $row); + } + $models[$key] = $row; + } + } else { + /** @var ActiveRecord $class */ + $class = $this->modelClass; + if ($this->indexBy === null) { + foreach ($rows as $row) { + $models[] = $class::create($row); + } + } else { + foreach ($rows as $row) { + $model = $class::create($row); + if (is_string($this->indexBy)) { + $key = $model->{$this->indexBy}; + } else { + $key = call_user_func($this->indexBy, $model); + } + $models[$key] = $model; + } + } + } + return $models; + } + + /** + * @param ActiveRecord[] $models + * @param array $with + */ + private function populateRelations(&$models, $with) + { + $primaryModel = new $this->modelClass; + $relations = $this->normalizeRelations($primaryModel, $with); + foreach ($relations as $name => $relation) { + if ($relation->asArray === null) { + // inherit asArray from primary query + $relation->asArray = $this->asArray; + } + $relation->findWith($name, $models); + } + } + + /** + * @param ActiveRecord $model + * @param array $with + * @return ActiveRelationInterface[] + */ + private function normalizeRelations($model, $with) + { + $relations = []; + foreach ($with as $name => $callback) { + if (is_integer($name)) { + $name = $callback; + $callback = null; + } + if (($pos = strpos($name, '.')) !== false) { + // with sub-relations + $childName = substr($name, $pos + 1); + $name = substr($name, 0, $pos); + } else { + $childName = null; + } + + if (!isset($relations[$name])) { + $relation = $model->getRelation($name); + $relation->primaryModel = null; + $relations[$name] = $relation; + } else { + $relation = $relations[$name]; + } + + if (isset($childName)) { + $relation->with[$childName] = $callback; + } elseif ($callback !== null) { + call_user_func($callback, $relation); + } + } + return $relations; + } +} diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php index 852d6f5..9edc824 100644 --- a/framework/yii/db/ActiveRecord.php +++ b/framework/yii/db/ActiveRecord.php @@ -8,7 +8,6 @@ namespace yii\db; -use yii\ar\ActiveRelationInterface; use yii\base\InvalidConfigException; use yii\base\Model; use yii\base\InvalidParamException; diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php index 7bc449a..ea9b87a 100644 --- a/framework/yii/db/ActiveRelation.php +++ b/framework/yii/db/ActiveRelation.php @@ -7,8 +7,6 @@ */ namespace yii\db; -use yii\ar\ActiveRelationInterface; -use yii\ar\ActiveRelationTrait; /** * ActiveRelation represents a relation between two Active Record classes. diff --git a/framework/yii/db/ActiveRelationInterface.php b/framework/yii/db/ActiveRelationInterface.php new file mode 100644 index 0000000..84e0648 --- /dev/null +++ b/framework/yii/db/ActiveRelationInterface.php @@ -0,0 +1,29 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +interface ActiveRelationInterface extends ActiveQueryInterface +{ + /** + * Specifies the relation associated with the pivot table. + * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. + * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. + * Its signature should be `function($query)`, where `$query` is the query to be customized. + * @return static the relation object itself. + */ + public function via($relationName, $callable = null); +} diff --git a/framework/yii/db/ActiveRelationTrait.php b/framework/yii/db/ActiveRelationTrait.php new file mode 100644 index 0000000..27963d0 --- /dev/null +++ b/framework/yii/db/ActiveRelationTrait.php @@ -0,0 +1,246 @@ + + * @author Carsten Brandt + * @since 2.0 + */ +trait ActiveRelationTrait +{ + /** + * @var boolean whether this relation should populate all query results into AR instances. + * If false, only the first row of the results will be retrieved. + */ + public $multiple; + /** + * @var ActiveRecord the primary model that this relation is associated with. + * This is used only in lazy loading with dynamic query options. + */ + public $primaryModel; + /** + * @var array the columns of the primary and foreign tables that establish the relation. + * The array keys must be columns of the table for this relation, and the array values + * must be the corresponding columns from the primary table. + * Do not prefix or quote the column names as this will be done automatically by Yii. + */ + public $link; + /** + * @var array the query associated with the pivot table. Please call [[via()]] + * to set this property instead of directly setting it. + */ + public $via; + + /** + * Clones internal objects. + */ + public function __clone() + { + // make a clone of "via" object so that the same query object can be reused multiple times + if (is_object($this->via)) { + $this->via = clone $this->via; + } elseif (is_array($this->via)) { + $this->via = [$this->via[0], clone $this->via[1]]; + } + } + + /** + * Specifies the relation associated with the pivot table. + * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]]. + * @param callable $callable a PHP callback for customizing the relation associated with the pivot table. + * Its signature should be `function($query)`, where `$query` is the query to be customized. + * @return static the relation object itself. + */ + public function via($relationName, $callable = null) + { + $relation = $this->primaryModel->getRelation($relationName); + $this->via = [$relationName, $relation]; + if ($callable !== null) { + call_user_func($callable, $relation); + } + return $this; + } + + /** + * Finds the related records and populates them into the primary models. + * This method is internally used by [[ActiveQuery]]. Do not call it directly. + * @param string $name the relation name + * @param array $primaryModels primary models + * @return array the related models + * @throws InvalidConfigException + */ + public function findWith($name, &$primaryModels) + { + if (!is_array($this->link)) { + throw new InvalidConfigException('Invalid link: it must be an array of key-value pairs.'); + } + + if ($this->via instanceof self) { + // via pivot table + /** @var ActiveRelationTrait $viaQuery */ + $viaQuery = $this->via; + $viaModels = $viaQuery->findPivotRows($primaryModels); + $this->filterByModels($viaModels); + } elseif (is_array($this->via)) { + // via relation + /** @var ActiveRelationTrait $viaQuery */ + list($viaName, $viaQuery) = $this->via; + $viaQuery->primaryModel = null; + $viaModels = $viaQuery->findWith($viaName, $primaryModels); + $this->filterByModels($viaModels); + } else { + $this->filterByModels($primaryModels); + } + + if (count($primaryModels) === 1 && !$this->multiple) { + $model = $this->one(); + foreach ($primaryModels as $i => $primaryModel) { + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $model); + } else { + $primaryModels[$i][$name] = $model; + } + } + return [$model]; + } else { + $models = $this->all(); + if (isset($viaModels, $viaQuery)) { + $buckets = $this->buildBuckets($models, $this->link, $viaModels, $viaQuery->link); + } else { + $buckets = $this->buildBuckets($models, $this->link); + } + + $link = array_values(isset($viaQuery) ? $viaQuery->link : $this->link); + foreach ($primaryModels as $i => $primaryModel) { + $key = $this->getModelKey($primaryModel, $link); + $value = isset($buckets[$key]) ? $buckets[$key] : ($this->multiple ? [] : null); + if ($primaryModel instanceof ActiveRecord) { + $primaryModel->populateRelation($name, $value); + } else { + $primaryModels[$i][$name] = $value; + } + } + return $models; + } + } + + /** + * @param array $models + * @param array $link + * @param array $viaModels + * @param array $viaLink + * @return array + */ + private function buildBuckets($models, $link, $viaModels = null, $viaLink = null) + { + $buckets = []; + $linkKeys = array_keys($link); + foreach ($models as $i => $model) { + $key = $this->getModelKey($model, $linkKeys); + if ($this->indexBy !== null) { + $buckets[$key][$i] = $model; + } else { + $buckets[$key][] = $model; + } + } + + if ($viaModels !== null) { + $viaBuckets = []; + $viaLinkKeys = array_keys($viaLink); + $linkValues = array_values($link); + foreach ($viaModels as $viaModel) { + $key1 = $this->getModelKey($viaModel, $viaLinkKeys); + $key2 = $this->getModelKey($viaModel, $linkValues); + if (isset($buckets[$key2])) { + foreach ($buckets[$key2] as $i => $bucket) { + if ($this->indexBy !== null) { + $viaBuckets[$key1][$i] = $bucket; + } else { + $viaBuckets[$key1][] = $bucket; + } + } + } + } + $buckets = $viaBuckets; + } + + if (!$this->multiple) { + foreach ($buckets as $i => $bucket) { + $buckets[$i] = reset($bucket); + } + } + return $buckets; + } + + /** + * @param ActiveRecord|array $model + * @param array $attributes + * @return string + */ + private function getModelKey($model, $attributes) + { + if (count($attributes) > 1) { + $key = []; + foreach ($attributes as $attribute) { + $key[] = $model[$attribute]; + } + return serialize($key); + } else { + $attribute = reset($attributes); + return $model[$attribute]; + } + } + + /** + * @param array $models + */ + private function filterByModels($models) + { + $attributes = array_keys($this->link); + $values = []; + if (count($attributes) === 1) { + // single key + $attribute = reset($this->link); + foreach ($models as $model) { + if (($value = $model[$attribute]) !== null) { + $values[] = $value; + } + } + } else { + // composite keys + foreach ($models as $model) { + $v = []; + foreach ($this->link as $attribute => $link) { + $v[$attribute] = $model[$link]; + } + $values[] = $v; + } + } + $this->andWhere(['in', $attributes, array_unique($values, SORT_REGULAR)]); + } + + /** + * @param ActiveRecord[] $primaryModels + * @return array + */ + private function findPivotRows($primaryModels) + { + if (empty($primaryModels)) { + return []; + } + $this->filterByModels($primaryModels); + /** @var ActiveRecord $primaryModel */ + $primaryModel = reset($primaryModels); + return $this->asArray()->all($primaryModel->getDb()); + } +}