diff --git a/build/controllers/PhpDocController.php b/build/controllers/PhpDocController.php index 32c4e62..f584390 100644 --- a/build/controllers/PhpDocController.php +++ b/build/controllers/PhpDocController.php @@ -115,9 +115,9 @@ class PhpDocController extends Controller /** * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), ['updateFiles']); + return array_merge(parent::options($actionId), ['updateFiles']); } protected function updateClassPropertyDocs($file, $className, $propertyDoc) diff --git a/extensions/apidoc/commands/ApiController.php b/extensions/apidoc/commands/ApiController.php index fb16078..9c9e286 100644 --- a/extensions/apidoc/commands/ApiController.php +++ b/extensions/apidoc/commands/ApiController.php @@ -159,8 +159,8 @@ class ApiController extends BaseController /** * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), ['template', 'guide']); + return array_merge(parent::options($actionId), ['template', 'guide']); } } diff --git a/extensions/apidoc/commands/GuideController.php b/extensions/apidoc/commands/GuideController.php index 933e76b..754d49d 100644 --- a/extensions/apidoc/commands/GuideController.php +++ b/extensions/apidoc/commands/GuideController.php @@ -114,8 +114,8 @@ class GuideController extends BaseController /** * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), ['apiDocs']); + return array_merge(parent::options($actionId), ['apiDocs']); } } diff --git a/extensions/apidoc/components/BaseController.php b/extensions/apidoc/components/BaseController.php index 9d700b2..e62ef37 100644 --- a/extensions/apidoc/components/BaseController.php +++ b/extensions/apidoc/components/BaseController.php @@ -126,8 +126,8 @@ abstract class BaseController extends Controller /** * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), ['template', 'exclude']); + return array_merge(parent::options($actionId), ['template', 'exclude']); } } diff --git a/extensions/apidoc/models/TypeDoc.php b/extensions/apidoc/models/TypeDoc.php index 77a56de..c5be79c 100644 --- a/extensions/apidoc/models/TypeDoc.php +++ b/extensions/apidoc/models/TypeDoc.php @@ -86,8 +86,8 @@ class TypeDoc extends BaseDoc } /** - * @param null $visibility - * @param null $definedBy + * @param string|null $visibility + * @param string|null $definedBy * @return MethodDoc[] */ private function getFilteredMethods($visibility = null, $definedBy = null) diff --git a/extensions/apidoc/renderers/BaseRenderer.php b/extensions/apidoc/renderers/BaseRenderer.php index 90ea56a..eb0ceb7 100644 --- a/extensions/apidoc/renderers/BaseRenderer.php +++ b/extensions/apidoc/renderers/BaseRenderer.php @@ -53,10 +53,10 @@ abstract class BaseRenderer extends Component /** * creates a link to a type (class, interface or trait) - * @param ClassDoc|InterfaceDoc|TraitDoc|ClassDoc[]|InterfaceDoc[]|TraitDoc[] $types - * @param string $title a title to be used for the link TODO check whether [[yii\...|Class]] is supported - * @param BaseDoc $context - * @param array $options additional HTML attributes for the link. + * @param ClassDoc|InterfaceDoc|TraitDoc|ClassDoc[]|InterfaceDoc[]|TraitDoc[]|string|string[] $types + * @param string $title a title to be used for the link TODO check whether [[yii\...|Class]] is supported + * @param BaseDoc $context + * @param array $options additional HTML attributes for the link. * @return string */ public function createTypeLink($types, $context = null, $title = null, $options = []) diff --git a/extensions/apidoc/templates/bootstrap/layouts/api.php b/extensions/apidoc/templates/bootstrap/layouts/api.php index b76d4ab..b5242d3 100644 --- a/extensions/apidoc/templates/bootstrap/layouts/api.php +++ b/extensions/apidoc/templates/bootstrap/layouts/api.php @@ -6,6 +6,7 @@ use yii\helpers\StringHelper; /** * @var yii\web\View $this + * @var array $types * @var string $content */ diff --git a/extensions/debug/panels/DbPanel.php b/extensions/debug/panels/DbPanel.php index db20ae2..ba68391 100644 --- a/extensions/debug/panels/DbPanel.php +++ b/extensions/debug/panels/DbPanel.php @@ -86,7 +86,7 @@ class DbPanel extends Panel protected function calculateTimings() { if ($this->_timings === null) { - $this->_timings = Yii::$app->getLog()->calculateTimings($this->data['messages']); + $this->_timings = Yii::getLogger()->calculateTimings($this->data['messages']); } return $this->_timings; diff --git a/extensions/debug/panels/ProfilingPanel.php b/extensions/debug/panels/ProfilingPanel.php index 33f6984..635f4f8 100644 --- a/extensions/debug/panels/ProfilingPanel.php +++ b/extensions/debug/panels/ProfilingPanel.php @@ -85,7 +85,7 @@ class ProfilingPanel extends Panel { if ($this->_models === null) { $this->_models = []; - $timings = Yii::$app->getLog()->calculateTimings($this->data['messages']); + $timings = Yii::getLogger()->calculateTimings($this->data['messages']); foreach ($timings as $seq => $profileTiming) { $this->_models[] = [ diff --git a/extensions/elasticsearch/Command.php b/extensions/elasticsearch/Command.php index db2b62b..fa4defb 100644 --- a/extensions/elasticsearch/Command.php +++ b/extensions/elasticsearch/Command.php @@ -95,7 +95,7 @@ class Command extends Component */ public function get($index, $type, $id, $options = []) { - return $this->db->get([$index, $type, $id], $options, null); + return $this->db->get([$index, $type, $id], $options); } /** @@ -184,7 +184,7 @@ class Command extends Component { $body = $configuration !== null ? Json::encode($configuration) : null; - return $this->db->put([$index], $body); + return $this->db->put([$index], [], $body); } /** @@ -381,7 +381,7 @@ class Command extends Component 'mappings' => (object) $mappings, ]); - return $this->db->put(['_template', $name], $body); + return $this->db->put(['_template', $name], [], $body); } diff --git a/extensions/elasticsearch/QueryBuilder.php b/extensions/elasticsearch/QueryBuilder.php index 30607ed..65523d4 100644 --- a/extensions/elasticsearch/QueryBuilder.php +++ b/extensions/elasticsearch/QueryBuilder.php @@ -203,6 +203,8 @@ class QueryBuilder extends \yii\base\Object return count($parts) === 1 ? $parts[0] : ['and' => $parts]; } + + // TODO implement these methods correctly private function buildNotCondition($operator, $operands, &$params) { if (count($operands) != 1) { diff --git a/extensions/faker/FixtureController.php b/extensions/faker/FixtureController.php index b7134a6..fbc3a2b 100644 --- a/extensions/faker/FixtureController.php +++ b/extensions/faker/FixtureController.php @@ -171,12 +171,11 @@ class FixtureController extends \yii\console\controllers\FixtureController /** - * Returns the names of the global options for this command. - * @return array the names of the global options for this command. + * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), [ + return array_merge(parent::options($actionId), [ 'templatePath', 'language', 'fixtureDataPath' ]); } diff --git a/extensions/redis/ActiveQuery.php b/extensions/redis/ActiveQuery.php index 7fc0a32..cdbb974 100644 --- a/extensions/redis/ActiveQuery.php +++ b/extensions/redis/ActiveQuery.php @@ -270,7 +270,7 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface /** * Executes a script created by [[LuaScriptBuilder]] - * @param Connection $db the database connection used to execute the query. + * @param Connection|null $db the database connection used to execute the query. * If this parameter is not given, the `db` application component will be used. * @param string $type the type of the script to generate * @param string $columnName diff --git a/extensions/redis/ActiveRecord.php b/extensions/redis/ActiveRecord.php index 3244d6b..ae9a3d3 100644 --- a/extensions/redis/ActiveRecord.php +++ b/extensions/redis/ActiveRecord.php @@ -175,7 +175,7 @@ class ActiveRecord extends BaseActiveRecord } $db = static::getDb(); $n = 0; - foreach (static::fetchPks($condition) as $pk) { + foreach (self::fetchPks($condition) as $pk) { $newPk = $pk; $pk = static::buildKey($pk); $key = static::keyPrefix() . ':a:' . $pk; @@ -228,7 +228,7 @@ class ActiveRecord extends BaseActiveRecord } $db = static::getDb(); $n = 0; - foreach (static::fetchPks($condition) as $pk) { + foreach (self::fetchPks($condition) as $pk) { $key = static::keyPrefix() . ':a:' . static::buildKey($pk); foreach ($counters as $attribute => $value) { $db->executeCommand('HINCRBY', [$key, $attribute, $value]); @@ -257,7 +257,7 @@ class ActiveRecord extends BaseActiveRecord { $db = static::getDb(); $attributeKeys = []; - $pks = static::fetchPks($condition); + $pks = self::fetchPks($condition); $db->executeCommand('MULTI'); foreach ($pks as $pk) { $pk = static::buildKey($pk); diff --git a/extensions/redis/Connection.php b/extensions/redis/Connection.php index 44bbe58..0622262 100644 --- a/extensions/redis/Connection.php +++ b/extensions/redis/Connection.php @@ -65,7 +65,6 @@ class Connection extends Component * @var float timeout to use for redis socket when reading and writing data. If not set the php default value will be used. */ public $dataTimeout = null; - /** * @var array List of available redis commands http://redis.io/commands */ @@ -215,6 +214,7 @@ class Connection extends Component */ private $_socket; + /** * Closes the connection when this component is being serialized. * @return array diff --git a/framework/BaseYii.php b/framework/BaseYii.php index 5a1e817..bfe0240 100644 --- a/framework/BaseYii.php +++ b/framework/BaseYii.php @@ -348,6 +348,29 @@ class BaseYii } } + private static $_logger; + + /** + * @return Logger message logger + */ + public static function getLogger() + { + if (self::$_logger !== null) { + return self::$_logger; + } else { + return self::$_logger = static::createObject('yii\log\Logger'); + } + } + + /** + * Sets the logger object. + * @param Logger $logger the logger object. + */ + public static function setLogger($logger) + { + self::$_logger = $logger; + } + /** * Logs a trace message. * Trace messages are logged mainly for development purpose to see @@ -358,7 +381,7 @@ class BaseYii public static function trace($message, $category = 'application') { if (YII_DEBUG) { - static::$app->getLog()->log($message, Logger::LEVEL_TRACE, $category); + static::getLogger()->log($message, Logger::LEVEL_TRACE, $category); } } @@ -371,7 +394,7 @@ class BaseYii */ public static function error($message, $category = 'application') { - static::$app->getLog()->log($message, Logger::LEVEL_ERROR, $category); + static::getLogger()->log($message, Logger::LEVEL_ERROR, $category); } /** @@ -383,7 +406,7 @@ class BaseYii */ public static function warning($message, $category = 'application') { - static::$app->getLog()->log($message, Logger::LEVEL_WARNING, $category); + static::getLogger()->log($message, Logger::LEVEL_WARNING, $category); } /** @@ -395,7 +418,7 @@ class BaseYii */ public static function info($message, $category = 'application') { - static::$app->getLog()->log($message, Logger::LEVEL_INFO, $category); + static::getLogger()->log($message, Logger::LEVEL_INFO, $category); } /** @@ -417,7 +440,7 @@ class BaseYii */ public static function beginProfile($token, $category = 'application') { - static::$app->getLog()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); + static::getLogger()->log($token, Logger::LEVEL_PROFILE_BEGIN, $category); } /** @@ -429,7 +452,7 @@ class BaseYii */ public static function endProfile($token, $category = 'application') { - static::$app->getLog()->log($token, Logger::LEVEL_PROFILE_END, $category); + static::getLogger()->log($token, Logger::LEVEL_PROFILE_END, $category); } /** diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index b97863f..f8df6ae 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -62,6 +62,7 @@ Yii Framework 2 Change Log - Bug #2760: Fixed GridView `filterUrl` parameters (qiangxue, AlexGx) - Bug #2834: When overriding i18n translation sources from config using `app*` or `yii*` default `app` and `yii` sources were not removed (samdark) - Bug #2848: Individual queries should be enclosed within parenthesis in a UNION query (qiangxue) +- Bug #2862: Using `DbCache` while enabling schema caching may cause infinite loops (qiangxue) - Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark) - Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark) - Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe) @@ -73,6 +74,7 @@ Yii Framework 2 Change Log - Bug: Fixed `$model->load($data)` returned `true` if `$data` and `formName` were empty (samdark) - Bug: Fixed issue with `ActiveRelationTrait` preventing `ActiveQuery` from clearing events and behaviors on clone (jom) - Bug: `Query::queryScalar` wasn't making `SELECT DISTINCT` queries subqueries (jom) +- Bug: Fixed use `$files` instead of `self::$_files[$key]` to allow inheritance (pgaultier) - Enh #46: Added Image extension based on [Imagine library](http://imagine.readthedocs.org) (tonydspaniard) - Enh #364: Improve Inflector::slug with `intl` transliteration. Improved transliteration char map. (tonydspaniard) - Enh #497: Removed `\yii\log\Target::logUser` and added `\yii\log\Target::prefix` to support customizing message prefix (qiangxue) @@ -177,6 +179,7 @@ Yii Framework 2 Change Log - Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe) - Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue) - Enh: Added `yii\web\Request::getAuthUser()` and `getAuthPassword()` (qiangxue) +- Enh: Added summaryOptions and emptyTextOptions to BaseListView (johonunu) - Chg #47: Changed Markdown library to cebe/markdown and adjusted Markdown helper API (cebe) - Chg #735: Added back `ActiveField::hiddenInput()` (qiangxue) - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue) @@ -255,6 +258,8 @@ Yii Framework 2 Change Log - Chg: `getComponent()` and `setComponent()` in `Application` and `Module` are renamed to `get()` and `set()` respectively. (qiangxue) - Chg: The signature of `Yii::createObject()` is changed. Constructor parameters must be passed as the second parameter. (qiangxue) - Chg: `Yii::$objectConfig` is removed. You should use `Yii::$container->set()` to configure default settings of classes. (qiangxue) +- Chg: Removed `yii\grid\Column::getDataCellContent()` and renamed `yii\grid\DataColumn::getDataCellContent()` to `yii\grid\DataColumn::getDataCellValue()` (cebe) +- Chg: `yii\log\Logger` is split into `yii\log\Logger` and `yii\log\Dispatcher`. (qiangxue) - New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul) - New #503: Added `yii\di\Container` and `yii\di\ServiceLocator` (qiangxue) - New #706: Added `yii\widgets\Pjax` and enhanced `GridView` to work with `Pjax` to support AJAX-update (qiangxue) diff --git a/framework/base/Application.php b/framework/base/Application.php index 11a08b2..506e19c 100644 --- a/framework/base/Application.php +++ b/framework/base/Application.php @@ -23,7 +23,7 @@ use yii\web\HttpException; * @property ErrorHandler $errorHandler The error handler application component. This property is read-only. * @property \yii\base\Formatter $formatter The formatter application component. This property is read-only. * @property \yii\i18n\I18N $i18n The internationalization component. This property is read-only. - * @property \yii\log\Logger $log The log component. This property is read-only. + * @property \yii\log\Dispatcher $log The log dispatcher component. This property is read-only. * @property \yii\mail\MailerInterface $mail The mailer interface. This property is read-only. * @property \yii\web\Request|\yii\console\Request $request The request component. This property is read-only. * @property string $runtimePath The directory that stores runtime files. Defaults to the "runtime" @@ -415,8 +415,8 @@ abstract class Application extends Module } /** - * Returns the log component. - * @return \yii\log\Logger the log component + * Returns the log dispatcher component. + * @return \yii\log\Dispatcher the log dispatcher component */ public function getLog() { @@ -512,7 +512,7 @@ abstract class Application extends Module public function coreComponents() { return [ - 'log' => ['class' => 'yii\log\Logger'], + 'log' => ['class' => 'yii\log\Dispatcher'], 'errorHandler' => ['class' => 'yii\base\ErrorHandler'], 'formatter' => ['class' => 'yii\base\Formatter'], 'i18n' => ['class' => 'yii\i18n\I18N'], diff --git a/framework/base/DynamicModel.php b/framework/base/DynamicModel.php index ed2bb22..eb739a2 100644 --- a/framework/base/DynamicModel.php +++ b/framework/base/DynamicModel.php @@ -71,6 +71,7 @@ class DynamicModel extends Model $this->_attributes[$name] = $value; } } + parent::__construct($config); } /** diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php index 619d547..8de99d1 100644 --- a/framework/base/ErrorHandler.php +++ b/framework/base/ErrorHandler.php @@ -89,7 +89,6 @@ class ErrorHandler extends Component if (!YII_ENV_TEST) { exit(1); } - return; } @@ -145,6 +144,9 @@ class ErrorHandler extends Component 'message' => $exception->getMessage(), 'code' => $exception->getCode(), ]; + if (YII_DEBUG) { + $array['stack-trace'] = explode("\n", $exception->getTraceAsString()); + } if ($exception instanceof HttpException) { $array['status'] = $exception->statusCode; } diff --git a/framework/base/View.php b/framework/base/View.php index c52da26..8693455 100644 --- a/framework/base/View.php +++ b/framework/base/View.php @@ -141,7 +141,6 @@ class View extends Component public function render($view, $params = [], $context = null) { $viewFile = $this->findViewFile($view, $context); - return $this->renderFile($viewFile, $params, $context); } diff --git a/framework/caching/MemCache.php b/framework/caching/MemCache.php index 8dd5755..bf8c8d8 100644 --- a/framework/caching/MemCache.php +++ b/framework/caching/MemCache.php @@ -78,6 +78,7 @@ class MemCache extends Cache */ private $_servers = []; + /** * Initializes this application component. * It creates the memcache instance and adds memcache servers. diff --git a/framework/console/Controller.php b/framework/console/Controller.php index aa54a62..cf39ed1 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -264,10 +264,10 @@ class Controller extends \yii\base\Controller * Note that the values setting via options are not available * until [[beforeAction()]] is being called. * - * @param string $id action name + * @param string $actionId the action id of the current request * @return array the names of the options valid for the action */ - public function options($id) + public function options($actionId) { // $id might be used in subclass to provide options specific to action id return ['color', 'interactive']; diff --git a/framework/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php index f727bbd..f1c72b6 100644 --- a/framework/console/controllers/FixtureController.php +++ b/framework/console/controllers/FixtureController.php @@ -33,7 +33,6 @@ use yii\test\FixtureTrait; */ class FixtureController extends Controller { - use FixtureTrait; /** @@ -57,13 +56,13 @@ class FixtureController extends Controller 'yii\test\InitDb', ]; + /** - * Returns the names of the global options for this command. - * @return array the names of the global options for this command. + * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), [ + return array_merge(parent::options($actionId), [ 'namespace', 'globalFixtures' ]); } diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php index 4ed1435..e649b66 100644 --- a/framework/console/controllers/MigrateController.php +++ b/framework/console/controllers/MigrateController.php @@ -92,14 +92,13 @@ class MigrateController extends Controller public $db = 'db'; /** - * Returns the names of the global options for this command. - * @return array the names of the global options for this command. + * @inheritdoc */ - public function options($id) + public function options($actionId) { - return array_merge(parent::options($id), + return array_merge(parent::options($actionId), ['migrationPath', 'migrationTable', 'db'], // global for all actions - ($id == 'create') ? ['templateFile'] : [] // action create + ($actionId == 'create') ? ['templateFile'] : [] // action create ); } diff --git a/framework/data/ArrayDataProvider.php b/framework/data/ArrayDataProvider.php index bc5a5bc..a9e1d5a 100644 --- a/framework/data/ArrayDataProvider.php +++ b/framework/data/ArrayDataProvider.php @@ -64,6 +64,7 @@ class ArrayDataProvider extends BaseDataProvider */ public $allModels; + /** * @inheritdoc */ diff --git a/framework/db/ActiveQuery.php b/framework/db/ActiveQuery.php index 5805ed1..8ca51d3 100644 --- a/framework/db/ActiveQuery.php +++ b/framework/db/ActiveQuery.php @@ -91,6 +91,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface */ public $joinWith; + /** * Executes query and returns all results as an array. * @param Connection $db the DB connection used to create the DB command. @@ -232,7 +233,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface /** * Creates a DB command that can be used to execute this query. - * @param Connection $db the DB connection used to create the DB command. + * @param Connection|null $db the DB connection used to create the DB command. * If null, the DB connection returned by [[modelClass]] will be used. * @return Command the created DB command instance. */ @@ -260,7 +261,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface /** * Creates a command for lazy loading of a relation. - * @param Connection $db the DB connection used to create the DB command. + * @param Connection|null $db the DB connection used to create the DB command. * @return Command the created DB command instance. */ private function createRelationalCommand($db = null) @@ -348,6 +349,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface private function buildJoinWith() { + $join = $this->join; + $this->join = []; + foreach ($this->joinWith as $config) { list ($with, $eagerLoading, $joinType) = $config; $this->joinWithRelations(new $this->modelClass, $with, $joinType); @@ -368,6 +372,12 @@ class ActiveQuery extends Query implements ActiveQueryInterface $this->with($with); } + + if (!empty($join)) { + // append explicit join to joinWith() + // https://github.com/yiisoft/yii2/issues/2880 + $this->join = empty($this->join) ? $join : array_merge($this->join, $join); + } } /** @@ -524,7 +534,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface } else { $on = $child->on; } - $this->join($joinType, $childTable, $on); + $this->join($joinType, empty($child->from) ? $childTable : $child->from, $on); if (!empty($child->where)) { $this->andWhere($child->where); diff --git a/framework/db/ActiveRelationTrait.php b/framework/db/ActiveRelationTrait.php index 2c95ddf..6cc1bbb 100644 --- a/framework/db/ActiveRelationTrait.php +++ b/framework/db/ActiveRelationTrait.php @@ -356,12 +356,39 @@ trait ActiveRelationTrait return $buckets; } + private function prefixKeyColumns($attributes) + { + if ($this instanceof ActiveQuery && (!empty($this->join) || !empty($this->joinWith))) { + if (empty($this->from)) { + /** @var ActiveRecord $modelClass */ + $modelClass = $this->modelClass; + $alias = $modelClass::tableName(); + } else { + foreach ($this->from as $alias => $table) { + if (!is_string($alias)) { + $alias = $table; + } + break; + } + } + if (isset($alias)) { + foreach ($attributes as $i => $attribute) { + $attributes[$i] = "$alias.$attribute"; + } + } + } + return $attributes; + } + /** * @param array $models */ private function filterByModels($models) { $attributes = array_keys($this->link); + + $attributes = $this->prefixKeyColumns($attributes); + $values = []; if (count($attributes) === 1) { // single key diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php index 3ac93e3..a62308c 100644 --- a/framework/db/BaseActiveRecord.php +++ b/framework/db/BaseActiveRecord.php @@ -81,7 +81,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface */ private $_attributes = []; /** - * @var array old attribute values indexed by attribute names. + * @var array|null old attribute values indexed by attribute names. + * This is `null` if the record [[isNewRecord|is new]]. */ private $_oldAttributes; /** @@ -475,7 +476,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface /** * Sets the old attribute values. * All existing old attribute values will be discarded. - * @param array $values old attribute values to be set. + * @param array|null $values old attribute values to be set. */ public function setOldAttributes($values) { @@ -1304,8 +1305,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface /** * @param array $link - * @param BaseActiveRecord $foreignModel - * @param BaseActiveRecord $primaryModel + * @param ActiveRecordInterface $foreignModel + * @param ActiveRecordInterface $primaryModel * @throws InvalidCallException */ private function bindModels($link, $foreignModel, $primaryModel) diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php index 1c94b34..b9b5e4c 100644 --- a/framework/db/QueryBuilder.php +++ b/framework/db/QueryBuilder.php @@ -65,18 +65,7 @@ class QueryBuilder extends \yii\base\Object public function build($query, $params = []) { $params = empty($params) ? $query->params : array_merge($params, $query->params); - - $select = $query->select; - $from = $query->from; - if ($from === null && $query instanceof ActiveQuery) { - /** @var ActiveRecord $modelClass */ - $modelClass = $query->modelClass; - $tableName = $modelClass::tableName(); - $from = [$tableName]; - if ($select === null && !empty($query->join)) { - $select = ["$tableName.*"]; - } - } + list ($select, $from) = $this->adjustSelectFrom($query); $clauses = [ $this->buildSelect($select, $params, $query->distinct, $query->selectOption), @@ -100,6 +89,44 @@ class QueryBuilder extends \yii\base\Object } /** + * Adjusts the select and from parts of the query when it is an ActiveQuery. + * When ActiveQuery does not specify "from", or if it is a join query without explicit "select", + * certain adjustments need to be made. This method is put here so that QueryBuilder can + * support sub-queries. + * @param Query $query + * @return array the select and from parts. + */ + protected function adjustSelectFrom($query) + { + $select = $query->select; + $from = $query->from; + if ($query instanceof ActiveQuery && (empty($select) || empty($from))) { + /** @var ActiveRecord $modelClass */ + $modelClass = $query->modelClass; + $tableName = $modelClass::tableName(); + if (empty($from)) { + $from = [$tableName]; + } + if (empty($select) && !empty($query->join)) { + foreach ((array)$from as $alias => $table) { + if (is_string($alias)) { + $select = ["$alias.*"]; + } elseif (is_string($table)) { + if (preg_match('/^(.*?)\s+({{\w+}}|\w+)$/', $table, $matches)) { + $alias = $matches[2]; + } else { + $alias = $tableName; + } + $select = ["$alias.*"]; + } + break; + } + } + } + return [$select, $from]; + } + + /** * Creates an INSERT SQL statement. * For example, * @@ -642,23 +669,7 @@ class QueryBuilder extends \yii\base\Object return ''; } - foreach ($tables as $i => $table) { - if ($table instanceof Query) { - list($sql, $params) = $this->build($table, $params); - $tables[$i] = "($sql) " . $this->db->quoteTableName($i); - } elseif (is_string($i)) { - if (strpos($table, '(') === false) { - $table = $this->db->quoteTableName($table); - } - $tables[$i] = "$table " . $this->db->quoteTableName($i); - } elseif (strpos($table, '(') === false) { - if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias - $tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]); - } else { - $tables[$i] = $this->db->quoteTableName($table); - } - } - } + $tables = $this->quoteTableNames($tables, $params); return 'FROM ' . implode(', ', $tables); } @@ -681,21 +692,8 @@ class QueryBuilder extends \yii\base\Object } // 0:join type, 1:join table, 2:on-condition (optional) list ($joinType, $table) = $join; - if (is_array($table)) { - $query = reset($table); - if (!$query instanceof Query) { - throw new Exception('The sub-query for join must be an instance of yii\db\Query.'); - } - $alias = $this->db->quoteTableName(key($table)); - list ($sql, $params) = $this->build($query, $params); - $table = "($sql) $alias"; - } elseif (strpos($table, '(') === false) { - if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias - $table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]); - } else { - $table = $this->db->quoteTableName($table); - } - } + $tables = $this->quoteTableNames((array)$table, $params); + $table = reset($tables); $joins[$i] = "$joinType $table"; if (isset($join[2])) { $condition = $this->buildCondition($join[2], $params); @@ -708,6 +706,28 @@ class QueryBuilder extends \yii\base\Object return implode($this->separator, $joins); } + private function quoteTableNames($tables, &$params) + { + foreach ($tables as $i => $table) { + if ($table instanceof Query) { + list($sql, $params) = $this->build($table, $params); + $tables[$i] = "($sql) " . $this->db->quoteTableName($i); + } elseif (is_string($i)) { + if (strpos($table, '(') === false) { + $table = $this->db->quoteTableName($table); + } + $tables[$i] = "$table " . $this->db->quoteTableName($i); + } elseif (strpos($table, '(') === false) { + if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias + $tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]); + } else { + $tables[$i] = $this->db->quoteTableName($table); + } + } + } + return $tables; + } + /** * @param string|array $condition * @param array $params the binding parameters to be populated diff --git a/framework/db/Schema.php b/framework/db/Schema.php index 7cdb46c..fdfcf2d 100644 --- a/framework/db/Schema.php +++ b/framework/db/Schema.php @@ -87,7 +87,7 @@ abstract class Schema extends Object */ public function getTableSchema($name, $refresh = false) { - if (isset($this->_tables[$name]) && !$refresh) { + if (array_key_exists($name, $this->_tables) && !$refresh) { return $this->_tables[$name]; } @@ -100,15 +100,17 @@ abstract class Schema extends Object if ($cache instanceof Cache) { $key = $this->getCacheKey($name); if ($refresh || ($table = $cache->get($key)) === false) { - $table = $this->loadTableSchema($realName); + $this->_tables[$name] = $table = $this->loadTableSchema($realName); if ($table !== null) { $cache->set($key, $table, $db->schemaCacheDuration, new GroupDependency([ 'group' => $this->getCacheGroup(), ])); } + } else { + $this->_tables[$name] = $table; } - return $this->_tables[$name] = $table; + return $this->_tables[$name]; } } diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php index f9b14fd..a27d919 100644 --- a/framework/db/oci/QueryBuilder.php +++ b/framework/db/oci/QueryBuilder.php @@ -8,6 +8,8 @@ namespace yii\db\oci; use yii\base\InvalidParamException; +use yii\db\ActiveQuery; +use yii\db\ActiveRecord; /** * QueryBuilder is the query builder for Oracle databases. @@ -26,10 +28,11 @@ class QueryBuilder extends \yii\db\QueryBuilder public function build($query, $params = []) { $params = empty($params) ? $query->params : array_merge($params, $query->params); + list ($select, $from) = $this->adjustSelectFrom($query); $clauses = [ - $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption), - $this->buildFrom($query->from, $params), + $this->buildSelect($select, $params, $query->distinct, $query->selectOption), + $this->buildFrom($from, $params), $this->buildJoin($query->join, $params), $this->buildWhere($query->where, $params), $this->buildGroupBy($query->groupBy), diff --git a/framework/grid/Column.php b/framework/grid/Column.php index 945d6c4..44395d6 100644 --- a/framework/grid/Column.php +++ b/framework/grid/Column.php @@ -99,7 +99,6 @@ class Column extends Object } else { $options = $this->contentOptions; } - return Html::tag('td', $this->renderDataCellContent($model, $key, $index), $options); } @@ -134,35 +133,22 @@ class Column extends Object } /** - * Returns the raw data cell content. - * This method is called by [[renderDataCellContent()]] when rendering the content of a data cell. + * Renders the data cell content. * @param mixed $model the data model * @param mixed $key the key associated with the data model * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. * @return string the rendering result */ - protected function getDataCellContent($model, $key, $index) + protected function renderDataCellContent($model, $key, $index) { if ($this->content !== null) { return call_user_func($this->content, $model, $key, $index, $this); } else { - return null; + return $this->grid->emptyCell; } } /** - * Renders the data cell content. - * @param mixed $model the data model - * @param mixed $key the key associated with the data model - * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. - * @return string the rendering result - */ - protected function renderDataCellContent($model, $key, $index) - { - return $this->content !== null ? $this->getDataCellContent($model, $key, $index) : $this->grid->emptyCell; - } - - /** * Renders the filter cell content. * The default implementation simply renders a space. * This method may be overridden to customize the rendering of the filter cell (if any). diff --git a/framework/grid/DataColumn.php b/framework/grid/DataColumn.php index 2facd2a..30005dd 100644 --- a/framework/grid/DataColumn.php +++ b/framework/grid/DataColumn.php @@ -17,7 +17,17 @@ 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. + * It is used to show data columns and allows [[enableSorting|sorting]] and [[filter|filtering]] them. + * + * A simple data column definition refers to an attribute in the data model of the + * GridView's data provider. The name of the attribute is specified by [[attribute]]. + * + * By setting [[value]] and [[label]], the header and cell content can be customized. + * + * A data column differentiates between the [[getDataCellValue|data cell value]] and the + * [[renderDataCellContent|data cell content]]. The cell value is an un-formatted value that + * may be used for calculation, while the actual cell content is a [[format|formatted]] version of that + * value which may contain HTML markup. * * @author Qiang Xue * @since 2.0 @@ -40,10 +50,13 @@ class DataColumn extends Column */ public $label; /** - * @var string|\Closure the attribute name to be displayed in this column or an anonymous function that returns - * the value to be displayed for every data model. + * @var string|\Closure an anonymous function that returns the value to be displayed for every data model. * The signature of this function is `function ($model, $index, $widget)`. * If this is not set, `$model[$attribute]` will be used to obtain the value. + * + * You may also set this property to a string representing the attribute name to be displayed in this column. + * This can be used when the attribute to be displayed is different from the [[attribute]] that is used for + * sorting and filtering. */ public $value; /** @@ -133,7 +146,6 @@ class DataColumn extends Column { if (is_array($this->filter)) { $options = array_merge(['prompt' => ''], $this->filterInputOptions); - return Html::activeDropDownList($this->grid->filterModel, $this->attribute, $this->filter, $options); } else { return Html::activeTextInput($this->grid->filterModel, $this->attribute, $this->filterInputOptions); @@ -146,21 +158,18 @@ class DataColumn extends Column /** * @inheritdoc */ - protected function getDataCellContent($model, $key, $index) + protected function getDataCellValue($model, $key, $index) { if ($this->value !== null) { if (is_string($this->value)) { - $value = ArrayHelper::getValue($model, $this->value); + return ArrayHelper::getValue($model, $this->value); } else { - $value = call_user_func($this->value, $model, $index, $this); + return call_user_func($this->value, $model, $index, $this); } - } elseif ($this->content === null && $this->attribute !== null) { - $value = ArrayHelper::getValue($model, $this->attribute); - } else { - return parent::getDataCellContent($model, $key, $index); + } elseif ($this->attribute !== null) { + return ArrayHelper::getValue($model, $this->attribute); } - - return $value; + return null; } /** @@ -168,6 +177,10 @@ class DataColumn extends Column */ protected function renderDataCellContent($model, $key, $index) { - return $this->grid->formatter->format($this->getDataCellContent($model, $key, $index), $this->format); + if ($this->content === null) { + return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format); + } else { + return parent::renderDataCellContent($model, $key, $index); + } } } diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php index 8ead84f..4172688 100644 --- a/framework/helpers/BaseArrayHelper.php +++ b/framework/helpers/BaseArrayHelper.php @@ -62,7 +62,7 @@ class BaseArrayHelper if ($recursive) { foreach ($object as $key => $value) { if (is_array($value) || is_object($value)) { - $object[$key] = static::toArray($value, true); + $object[$key] = static::toArray($value, $properties, true); } } } diff --git a/framework/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php index 74b04ab..29f05ef 100644 --- a/framework/helpers/BaseFileHelper.php +++ b/framework/helpers/BaseFileHelper.php @@ -285,14 +285,14 @@ class BaseFileHelper if (isset($options['except'])) { foreach ($options['except'] as $key => $value) { if (is_string($value)) { - $options['except'][$key] = static::parseExcludePattern($value); + $options['except'][$key] = self::parseExcludePattern($value); } } } if (isset($options['only'])) { foreach ($options['only'] as $key => $value) { if (is_string($value)) { - $options['only'][$key] = static::parseExcludePattern($value); + $options['only'][$key] = self::parseExcludePattern($value); } } } diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php index ca12d27..07f4ed5 100644 --- a/framework/helpers/BaseHtml.php +++ b/framework/helpers/BaseHtml.php @@ -342,7 +342,7 @@ class BaseHtml /** * Generates an image tag. - * @param string $src the image URL. This parameter will be processed by [[yii\helpers\Url::to()]]. + * @param array|string $src the image URL. This parameter will be processed by [[yii\helpers\Url::to()]]. * @param array $options the tag options in terms of name-value pairs. These will be rendered as * 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. diff --git a/framework/helpers/BaseMarkdown.php b/framework/helpers/BaseMarkdown.php index 59bb3ec..6d8ee39 100644 --- a/framework/helpers/BaseMarkdown.php +++ b/framework/helpers/BaseMarkdown.php @@ -82,7 +82,7 @@ class BaseMarkdown * @return \cebe\markdown\Parser * @throws \yii\base\InvalidParamException when an undefined flavor is given. */ - private static function getParser($flavor) + protected static function getParser($flavor) { /** @var \cebe\markdown\Markdown $parser */ if (!isset(static::$flavors[$flavor])) { diff --git a/framework/helpers/BaseUrl.php b/framework/helpers/BaseUrl.php index d43882f..eb97526 100644 --- a/framework/helpers/BaseUrl.php +++ b/framework/helpers/BaseUrl.php @@ -108,7 +108,7 @@ class BaseUrl * @return string normalized route suitable for UrlManager * @throws InvalidParamException a relative route is given while there is no active controller */ - private static function normalizeRoute($route) + protected static function normalizeRoute($route) { $route = (string) $route; if (strncmp($route, '/', 1) === 0) { diff --git a/framework/i18n/MessageFormatter.php b/framework/i18n/MessageFormatter.php index 0708f69..893322f 100644 --- a/framework/i18n/MessageFormatter.php +++ b/framework/i18n/MessageFormatter.php @@ -220,7 +220,9 @@ class MessageFormatter extends Component if (!isset($token[2])) { return false; } - $subtokens = self::tokenizePattern($token[2]); + if (($subtokens = self::tokenizePattern($token[2])) === false) { + return false; + } $c = count($subtokens); for ($k = 0; $k + 1 < $c; $k++) { if (is_array($subtokens[$k]) || !is_array($subtokens[++$k])) { diff --git a/framework/log/Dispatcher.php b/framework/log/Dispatcher.php new file mode 100644 index 0000000..7a090b9 --- /dev/null +++ b/framework/log/Dispatcher.php @@ -0,0 +1,142 @@ +log`. + * + * You may configure the targets in application configuration, like the following: + * + * ~~~ + * [ + * 'components' => [ + * 'log' => [ + * 'targets' => [ + * 'file' => [ + * 'class' => 'yii\log\FileTarget', + * 'levels' => ['trace', 'info'], + * 'categories' => ['yii\*'], + * ], + * 'email' => [ + * 'class' => 'yii\log\EmailTarget', + * 'levels' => ['error', 'warning'], + * 'message' => [ + * 'to' => 'admin@example.com', + * ], + * ], + * ], + * ], + * ], + * ] + * ~~~ + * + * Each log target can have a name and can be referenced via the [[targets]] property + * as follows: + * + * ~~~ + * Yii::$app->log->targets['file']->enabled = false; + * ~~~ + * + * @author Qiang Xue + * @since 2.0 + */ +class Dispatcher extends Component +{ + /** + * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance + * or the configuration for creating the log target instance. + */ + public $targets = []; + /** + * @var Logger the logger. If not set, [[\Yii::getLogger()]] will be used. + */ + public $logger; + + /** + * Initializes the logger by registering [[flush()]] as a shutdown function. + */ + public function init() + { + parent::init(); + + foreach ($this->targets as $name => $target) { + if (!$target instanceof Target) { + $this->targets[$name] = Yii::createObject($target); + } + } + + if ($this->logger === null) { + $this->logger = Yii::getLogger(); + } + $this->logger->dispatcher = $this; + } + + /** + * @return integer how many application call stacks should be logged together with each message. + * This method returns the value of [[Logger::traceLevel]]. Defaults to 0. + */ + public function getTraceLevel() + { + return $this->logger->traceLevel; + } + + /** + * @param integer $value how many application call stacks should be logged together with each message. + * This method will set the value of [[Logger::traceLevel]]. If the value is greater than 0, + * at most that number of call stacks will be logged. Note that only application call stacks are counted. + * Defaults to 0. + */ + public function setTraceLevel($value) + { + $this->logger->traceLevel = $value; + } + + /** + * @return integer how many messages should be logged before they are sent to targets. + * This method returns the value of [[Logger::flushInterval]]. + */ + public function getFlushInterval() + { + return $this->logger->flushInterval; + } + + /** + * @param integer $value how many messages should be logged before they are sent to targets. + * This method will set the value of [[Logger::flushInterval]]. + * Defaults to 1000, meaning the [[Logger::flush()]] method will be invoked once every 1000 messages logged. + * Set this property to be 0 if you don't want to flush messages until the application terminates. + * This property mainly affects how much memory will be taken by the logged messages. + * A smaller value means less memory, but will increase the execution time due to the overhead of [[Logger::flush()]]. + */ + public function setFlushInterval($value) + { + $this->logger->flushInterval = $value; + } + + /** + * Dispatches the logged messages to [[targets]]. + * @param array $messages the logged messages + * @param boolean $final whether this method is called at the end of the current application + */ + public function dispatch($messages, $final) + { + foreach ($this->targets as $target) { + if ($target->enabled) { + $target->collect($messages, $final); + } + } + } +} diff --git a/framework/log/Logger.php b/framework/log/Logger.php index b067e39..029f474 100644 --- a/framework/log/Logger.php +++ b/framework/log/Logger.php @@ -11,11 +11,11 @@ use Yii; use yii\base\Component; /** - * Logger records logged messages in memory and sends them to different targets as needed. + * Logger records logged messages in memory and sends them to different targets if [[dispatcher]] is set. * - * Logger is registered as a core application component and can be accessed using `Yii::$app->log`. - * You can call the method [[log()]] to record a single log message. For convenience, a set of shortcut - * methods are provided for logging messages of various severity levels via the [[Yii]] class: + * Logger can be accessed via `Yii::getLogger()`. You can call the method [[log()]] to record a single log message. + * For convenience, a set of shortcut methods are provided for logging messages of various severity levels + * via the [[Yii]] class: * * - [[Yii::trace()]] * - [[Yii::error()]] @@ -24,43 +24,8 @@ use yii\base\Component; * - [[Yii::beginProfile()]] * - [[Yii::endProfile()]] * - * When enough messages are accumulated in the logger, or when the current request finishes, - * the logged messages will be sent to different [[targets]], such as log files, emails. - * - * You may configure the targets in application configuration, like the following: - * - * ~~~ - * [ - * 'components' => [ - * 'log' => [ - * 'targets' => [ - * 'file' => [ - * 'class' => 'yii\log\FileTarget', - * 'levels' => ['trace', 'info'], - * 'categories' => ['yii\*'], - * ], - * 'email' => [ - * 'class' => 'yii\log\EmailTarget', - * 'levels' => ['error', 'warning'], - * 'message' => [ - * 'to' => 'admin@example.com', - * ], - * ], - * ], - * ], - * ], - * ] - * ~~~ - * - * Each log target can have a name and can be referenced via the [[targets]] property - * as follows: - * - * ~~~ - * Yii::$app->log->targets['file']->enabled = false; - * ~~~ - * * When the application ends or [[flushInterval]] is reached, Logger will call [[flush()]] - * to send logged messages to different log targets, such as file, email, Web. + * to send logged messages to different log targets, such as file, email, Web, with the help of [[dispatcher]]. * * @property array $dbProfiling The first element indicates the number of SQL statements executed, and the * second element the total time spent in SQL execution. This property is read-only. @@ -125,16 +90,6 @@ class Logger extends Component */ public $messages = []; /** - * @var array debug data. This property stores various types of debug data reported at - * different instrument places. - */ - public $data = []; - /** - * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance - * or the configuration for creating the log target instance. - */ - public $targets = []; - /** * @var integer how many messages should be logged before they are flushed from memory and sent to targets. * Defaults to 1000, meaning the [[flush]] method will be invoked once every 1000 messages logged. * Set this property to be 0 if you don't want to flush messages until the application terminates. @@ -146,10 +101,13 @@ class Logger extends Component * @var integer how much call stack information (file name and line number) should be logged for each message. * If it is greater than 0, at most that number of call stacks will be logged. Note that only application * call stacks are counted. - * - * If not set, it will default to 3 when `YII_ENV` is set as "dev", and 0 otherwise. */ - public $traceLevel; + public $traceLevel = 0; + /** + * @var Dispatcher the message dispatcher + */ + public $dispatcher; + /** * Initializes the logger by registering [[flush()]] as a shutdown function. @@ -157,14 +115,6 @@ class Logger extends Component public function init() { parent::init(); - if ($this->traceLevel === null) { - $this->traceLevel = YII_ENV_DEV ? 3 : 0; - } - foreach ($this->targets as $name => $target) { - if (!$target instanceof Target) { - $this->targets[$name] = Yii::createObject($target); - } - } register_shutdown_function([$this, 'flush'], true); } @@ -208,11 +158,8 @@ class Logger extends Component */ public function flush($final = false) { - /** @var Target $target */ - foreach ($this->targets as $target) { - if ($target->enabled) { - $target->collect($this->messages, $final); - } + if ($this->dispatcher instanceof Dispatcher) { + $this->dispatcher->dispatch($this->messages, $final); } $this->messages = []; } diff --git a/framework/rbac/PhpManager.php b/framework/rbac/PhpManager.php index 92678e2..b3a1b7a 100644 --- a/framework/rbac/PhpManager.php +++ b/framework/rbac/PhpManager.php @@ -44,6 +44,7 @@ class PhpManager extends Manager private $_children = []; // itemName, childName => child private $_assignments = []; // userId, itemName => assignment + /** * Initializes the application component. * This method overrides parent implementation by loading the authorization data @@ -158,7 +159,7 @@ class PhpManager extends Manager /** * Returns the children of the specified item. - * @param mixed $names the parent item name. This can be either a string or an array. + * @param string|array $names the parent item name. This can be either a string or an array. * The latter represents a list of item names. * @return Item[] all child items of the parent */ diff --git a/framework/requirements/views/console/index.php b/framework/requirements/views/console/index.php index 1f92c68..dbd1c12 100644 --- a/framework/requirements/views/console/index.php +++ b/framework/requirements/views/console/index.php @@ -1,7 +1,9 @@ - +/** + * @var YiiRequirementChecker $this + * @var array $summary + * @var array[] $requirements + */ +?> diff --git a/framework/rest/Controller.php b/framework/rest/Controller.php index d17a14c..3239aa8 100644 --- a/framework/rest/Controller.php +++ b/framework/rest/Controller.php @@ -105,10 +105,9 @@ class Controller extends \yii\web\Controller */ public function beforeAction($action) { + $this->authenticate($action); if (parent::beforeAction($action)) { - $this->authenticate($action); $this->checkRateLimit($action); - return true; } else { return false; @@ -121,7 +120,6 @@ class Controller extends \yii\web\Controller public function afterAction($action, $result) { $result = parent::afterAction($action, $result); - return $this->serializeData($result); } diff --git a/framework/rest/RateLimiter.php b/framework/rest/RateLimiter.php index d668e6d..8226f59 100644 --- a/framework/rest/RateLimiter.php +++ b/framework/rest/RateLimiter.php @@ -8,7 +8,6 @@ namespace yii\rest; use yii\base\Component; -use yii\base\Action; use yii\web\Request; use yii\web\Response; use yii\web\TooManyRequestsHttpException; @@ -37,7 +36,7 @@ class RateLimiter extends Component * @param RateLimitInterface $user the current user * @param Request $request * @param Response $response - * @param Action $action the action to be executed + * @param \yii\base\Action $action the action to be executed * @throws TooManyRequestsHttpException if rate limit exceeds */ public function check($user, $request, $response, $action) diff --git a/framework/validators/FileValidator.php b/framework/validators/FileValidator.php index 20756eb..0990910 100644 --- a/framework/validators/FileValidator.php +++ b/framework/validators/FileValidator.php @@ -232,9 +232,8 @@ class FileValidator extends Validator */ public function isEmpty($value, $trim = false) { - $value = is_array($value) && !empty($value) ? $value[0] : $value; - - return !$value instanceof UploadedFile || $value->error == UPLOAD_ERR_NO_FILE; + $value = is_array($value) && !empty($value) ? $value[0] : $value; + return !($value instanceof UploadedFile) || $value->error == UPLOAD_ERR_NO_FILE; } /** diff --git a/framework/web/AccessControl.php b/framework/web/AccessControl.php index 077f9bd..da3d63b 100644 --- a/framework/web/AccessControl.php +++ b/framework/web/AccessControl.php @@ -64,6 +64,7 @@ class AccessControl extends ActionFilter * ~~~ * * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. + * `$rule` will be `null` if access is denied because none of the rules matched. */ public $denyCallback; /** @@ -79,6 +80,7 @@ class AccessControl extends ActionFilter */ public $rules = []; + /** * Initializes the [[rules]] array by instantiating rule objects from configurations. */ @@ -114,16 +116,14 @@ class AccessControl extends ActionFilter } else { $this->denyAccess($user); } - return false; } } if (isset($this->denyCallback)) { - call_user_func($this->denyCallback, $rule, $action); + call_user_func($this->denyCallback, null, $action); } else { $this->denyAccess($user); } - return false; } diff --git a/framework/web/Request.php b/framework/web/Request.php index 87a425b..75c7407 100644 --- a/framework/web/Request.php +++ b/framework/web/Request.php @@ -124,8 +124,8 @@ class Request extends \yii\base\Request */ public $enableCookieValidation = true; /** - * @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'. + * @var string the name of the POST parameter that is used to indicate if a request is a PUT, PATCH or DELETE + * request tunneled through POST. Defaults to '_method'. * @see getMethod() * @see getBodyParams() */ diff --git a/framework/web/Response.php b/framework/web/Response.php index c360582..9b4232d 100644 --- a/framework/web/Response.php +++ b/framework/web/Response.php @@ -738,7 +738,6 @@ class Response extends \yii\base\Response if ($this->_cookies === null) { $this->_cookies = new CookieCollection; } - return $this->_cookies; } diff --git a/framework/web/UploadedFile.php b/framework/web/UploadedFile.php index c7648c1..66fb37c 100644 --- a/framework/web/UploadedFile.php +++ b/framework/web/UploadedFile.php @@ -56,6 +56,7 @@ class UploadedFile extends Object */ public $error; + /** * String output. * This is PHP magic method that returns string representation of an object. @@ -80,7 +81,6 @@ class UploadedFile extends Object public static function getInstance($model, $attribute) { $name = Html::getInputName($model, $attribute); - return static::getInstanceByName($name); } @@ -95,7 +95,6 @@ class UploadedFile extends Object public static function getInstances($model, $attribute) { $name = Html::getInputName($model, $attribute); - return static::getInstancesByName($name); } @@ -108,8 +107,7 @@ class UploadedFile extends Object */ public static function getInstanceByName($name) { - $files = static::loadFiles(); - + $files = self::loadFiles(); return isset($files[$name]) ? $files[$name] : null; } @@ -124,17 +122,16 @@ class UploadedFile extends Object */ public static function getInstancesByName($name) { - $files = static::loadFiles(); + $files = self::loadFiles(); if (isset($files[$name])) { return [$files[$name]]; } $results = []; foreach ($files as $key => $file) { if (strpos($key, "{$name}[") === 0) { - $results[] = self::$_files[$key]; + $results[] = $file; } } - return $results; } @@ -166,7 +163,6 @@ class UploadedFile extends Object return copy($this->tempName, $file); } } - return false; } @@ -209,7 +205,6 @@ class UploadedFile extends Object } } } - return self::$_files; } diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php index cb73e70..fdb0ddf 100644 --- a/framework/web/UrlRule.php +++ b/framework/web/UrlRule.php @@ -101,6 +101,7 @@ class UrlRule extends Object implements UrlRuleInterface */ private $_routeParams = []; + /** * Initializes this rule. */ diff --git a/framework/web/User.php b/framework/web/User.php index 937f561..2d41474 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -141,8 +141,8 @@ class User extends Component * @param boolean $checkSession whether to check the session if the identity has never been determined before. * If the identity is already determined (e.g., by calling [[setIdentity()]] or [[login()]]), * then this parameter has no effect. - * @return IdentityInterface the identity object associated with the currently logged-in user. - * Null is returned if the user is not logged in (not authenticated). + * @return IdentityInterface|null the identity object associated with the currently logged-in user. + * `null` is returned if the user is not logged in (not authenticated). * @see login() * @see logout() */ @@ -170,7 +170,7 @@ class User extends Component * [[switchIdentity()]]. Those methods will try to use session and cookie to maintain the user authentication * status. * - * @param IdentityInterface $identity the identity object associated with the currently logged user. + * @param IdentityInterface|null $identity the identity object associated with the currently logged user. */ public function setIdentity($identity) { diff --git a/framework/widgets/BaseListView.php b/framework/widgets/BaseListView.php index e31f003..e26519a 100644 --- a/framework/widgets/BaseListView.php +++ b/framework/widgets/BaseListView.php @@ -54,6 +54,12 @@ abstract class BaseListView extends Widget */ public $summary; /** + * @var array the HTML attributes for the summary of the list view. + * The "tag" element specifies the tag name of the summary element and defaults to "div". + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $summaryOptions = ['class' => 'summary']; + /** * @var boolean whether to show the list view if [[dataProvider]] returns no data. */ public $showOnEmpty = false; @@ -62,6 +68,12 @@ abstract class BaseListView extends Widget */ public $emptyText; /** + * @var array the HTML attributes for the emptyText of the list view. + * The "tag" element specifies the tag name of the emptyText element and defaults to "div". + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $emptyTextOptions = ['class' => 'empty']; + /** * @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: * @@ -139,7 +151,8 @@ abstract class BaseListView extends Widget */ public function renderEmpty() { - return '
' . ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText) . '
'; + $tag = ArrayHelper::remove($this->emptyTextOptions, 'tag', 'div'); + return Html::tag($tag, ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText), $this->emptyTextOptions); } /** @@ -151,6 +164,7 @@ abstract class BaseListView extends Widget if ($count <= 0) { return ''; } + $tag = ArrayHelper::remove($this->summaryOptions, 'tag', 'div'); if (($pagination = $this->dataProvider->getPagination()) !== false) { $totalCount = $this->dataProvider->getTotalCount(); $begin = $pagination->getPage() * $pagination->pageSize + 1; @@ -161,29 +175,27 @@ abstract class BaseListView extends Widget $page = $pagination->getPage() + 1; $pageCount = $pagination->pageCount; if (($summaryContent = $this->summary) === null) { - return '
' - . Yii::t('yii', 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.', [ + return Html::tag($tag, Yii::t('yii', 'Showing {begin, number}-{end, number} of {totalCount, number} {totalCount, plural, one{item} other{items}}.', [ 'begin' => $begin, 'end' => $end, 'count' => $count, 'totalCount' => $totalCount, 'page' => $page, 'pageCount' => $pageCount, - ]) - . '
'; + ]), $this->summaryOptions); } } else { $begin = $page = $pageCount = 1; $end = $totalCount = $count; if (($summaryContent = $this->summary) === null) { - return '
' . Yii::t('yii', 'Total {count, number} {count, plural, one{item} other{items}}.', [ + return Html::tag($tag, Yii::t('yii', 'Total {count, number} {count, plural, one{item} other{items}}.', [ 'begin' => $begin, 'end' => $end, 'count' => $count, 'totalCount' => $totalCount, 'page' => $page, 'pageCount' => $pageCount, - ]) . '
'; + ]), $this->summaryOptions); } } diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php index 7be793a..3777464 100644 --- a/tests/unit/framework/db/ActiveRecordTest.php +++ b/tests/unit/framework/db/ActiveRecordTest.php @@ -402,6 +402,19 @@ class ActiveRecordTest extends DatabaseTestCase $this->assertEquals(3, $count); $orders = $query->all(); $this->assertEquals(3, count($orders)); + + // https://github.com/yiisoft/yii2/issues/2880 + $query = Order::find(1); + $customer = $query->getCustomer()->joinWith([ + 'orders' => function ($q) { $q->orderBy([]); } + ])->one(); + $this->assertEquals(1, $customer->id); + $order = Order::find()->joinWith([ + 'items' => function ($q) { + $q->from(['items' => 'tbl_item']) + ->orderBy('items.id'); + }, + ])->orderBy('tbl_order.id')->one(); } public function testJoinWithAndScope() diff --git a/tests/unit/framework/log/TargetTest.php b/tests/unit/framework/log/TargetTest.php index 035d639..1f2d694 100644 --- a/tests/unit/framework/log/TargetTest.php +++ b/tests/unit/framework/log/TargetTest.php @@ -5,6 +5,7 @@ namespace yiiunit\framework\log; +use yii\log\Dispatcher; use yii\log\Logger; use yii\log\Target; use yiiunit\TestCase; @@ -50,7 +51,9 @@ class TargetTest extends TestCase { static::$messages = []; - $logger = new Logger([ + $logger = new Logger; + $dispatcher = new Dispatcher([ + 'logger' => $logger, 'targets' => [new TestTarget(array_merge($filter, ['logVars' => []]))], 'flushInterval' => 1, ]);