diff --git a/composer.json b/composer.json index 95d72a1..8babb23 100644 --- a/composer.json +++ b/composer.json @@ -97,6 +97,7 @@ }, "autoload": { "psr-4": { + "yii\\": "framework/", "yii\\apidoc\\": "extensions/apidoc/", "yii\\authclient\\": "extensions/authclient/", "yii\\bootstrap\\": "extensions/bootstrap/", @@ -113,9 +114,6 @@ "yii\\swiftmailer\\": "extensions/swiftmailer/", "yii\\sphinx\\": "extensions/sphinx/", "yii\\twig\\": "extensions/twig/" - }, - "psr-0": { - "yii\\": "framework/" } } } diff --git a/framework/yii/.gitignore b/framework/.gitignore similarity index 100% rename from framework/yii/.gitignore rename to framework/.gitignore diff --git a/framework/yii/.htaccess b/framework/.htaccess similarity index 100% rename from framework/yii/.htaccess rename to framework/.htaccess diff --git a/framework/yii/BaseYii.php b/framework/BaseYii.php similarity index 100% rename from framework/yii/BaseYii.php rename to framework/BaseYii.php diff --git a/framework/yii/Yii.php b/framework/Yii.php similarity index 100% rename from framework/yii/Yii.php rename to framework/Yii.php diff --git a/framework/yii/assets/jquery.maskedinput.js b/framework/assets/jquery.maskedinput.js similarity index 100% rename from framework/yii/assets/jquery.maskedinput.js rename to framework/assets/jquery.maskedinput.js diff --git a/framework/yii/assets/punycode/LICENSE-GPL.txt b/framework/assets/punycode/LICENSE-GPL.txt similarity index 100% rename from framework/yii/assets/punycode/LICENSE-GPL.txt rename to framework/assets/punycode/LICENSE-GPL.txt diff --git a/framework/yii/assets/punycode/LICENSE-MIT.txt b/framework/assets/punycode/LICENSE-MIT.txt similarity index 100% rename from framework/yii/assets/punycode/LICENSE-MIT.txt rename to framework/assets/punycode/LICENSE-MIT.txt diff --git a/framework/yii/assets/punycode/punycode.js b/framework/assets/punycode/punycode.js similarity index 100% rename from framework/yii/assets/punycode/punycode.js rename to framework/assets/punycode/punycode.js diff --git a/framework/yii/assets/punycode/punycode.min.js b/framework/assets/punycode/punycode.min.js similarity index 100% rename from framework/yii/assets/punycode/punycode.min.js rename to framework/assets/punycode/punycode.min.js diff --git a/framework/yii/assets/yii.activeForm.js b/framework/assets/yii.activeForm.js similarity index 100% rename from framework/yii/assets/yii.activeForm.js rename to framework/assets/yii.activeForm.js diff --git a/framework/yii/assets/yii.captcha.js b/framework/assets/yii.captcha.js similarity index 100% rename from framework/yii/assets/yii.captcha.js rename to framework/assets/yii.captcha.js diff --git a/framework/yii/assets/yii.gridView.js b/framework/assets/yii.gridView.js similarity index 100% rename from framework/yii/assets/yii.gridView.js rename to framework/assets/yii.gridView.js diff --git a/framework/yii/assets/yii.js b/framework/assets/yii.js similarity index 100% rename from framework/yii/assets/yii.js rename to framework/assets/yii.js diff --git a/framework/yii/assets/yii.validation.js b/framework/assets/yii.validation.js similarity index 100% rename from framework/yii/assets/yii.validation.js rename to framework/assets/yii.validation.js diff --git a/framework/yii/base/Action.php b/framework/base/Action.php similarity index 100% rename from framework/yii/base/Action.php rename to framework/base/Action.php diff --git a/framework/yii/base/ActionEvent.php b/framework/base/ActionEvent.php similarity index 100% rename from framework/yii/base/ActionEvent.php rename to framework/base/ActionEvent.php diff --git a/framework/yii/base/ActionFilter.php b/framework/base/ActionFilter.php similarity index 100% rename from framework/yii/base/ActionFilter.php rename to framework/base/ActionFilter.php diff --git a/framework/yii/base/Application.php b/framework/base/Application.php similarity index 100% rename from framework/yii/base/Application.php rename to framework/base/Application.php diff --git a/framework/yii/base/Arrayable.php b/framework/base/Arrayable.php similarity index 100% rename from framework/yii/base/Arrayable.php rename to framework/base/Arrayable.php diff --git a/framework/yii/base/Behavior.php b/framework/base/Behavior.php similarity index 100% rename from framework/yii/base/Behavior.php rename to framework/base/Behavior.php diff --git a/framework/yii/base/Component.php b/framework/base/Component.php similarity index 100% rename from framework/yii/base/Component.php rename to framework/base/Component.php diff --git a/framework/yii/base/Controller.php b/framework/base/Controller.php similarity index 100% rename from framework/yii/base/Controller.php rename to framework/base/Controller.php diff --git a/framework/yii/base/ErrorException.php b/framework/base/ErrorException.php similarity index 100% rename from framework/yii/base/ErrorException.php rename to framework/base/ErrorException.php diff --git a/framework/yii/base/ErrorHandler.php b/framework/base/ErrorHandler.php similarity index 100% rename from framework/yii/base/ErrorHandler.php rename to framework/base/ErrorHandler.php diff --git a/framework/yii/base/Event.php b/framework/base/Event.php similarity index 100% rename from framework/yii/base/Event.php rename to framework/base/Event.php diff --git a/framework/yii/base/Exception.php b/framework/base/Exception.php similarity index 100% rename from framework/yii/base/Exception.php rename to framework/base/Exception.php diff --git a/framework/yii/base/Extension.php b/framework/base/Extension.php similarity index 100% rename from framework/yii/base/Extension.php rename to framework/base/Extension.php diff --git a/framework/yii/base/Formatter.php b/framework/base/Formatter.php similarity index 100% rename from framework/yii/base/Formatter.php rename to framework/base/Formatter.php diff --git a/framework/yii/base/InlineAction.php b/framework/base/InlineAction.php similarity index 100% rename from framework/yii/base/InlineAction.php rename to framework/base/InlineAction.php diff --git a/framework/yii/base/InvalidCallException.php b/framework/base/InvalidCallException.php similarity index 100% rename from framework/yii/base/InvalidCallException.php rename to framework/base/InvalidCallException.php diff --git a/framework/yii/base/InvalidConfigException.php b/framework/base/InvalidConfigException.php similarity index 100% rename from framework/yii/base/InvalidConfigException.php rename to framework/base/InvalidConfigException.php diff --git a/framework/yii/base/InvalidParamException.php b/framework/base/InvalidParamException.php similarity index 100% rename from framework/yii/base/InvalidParamException.php rename to framework/base/InvalidParamException.php diff --git a/framework/yii/base/InvalidRouteException.php b/framework/base/InvalidRouteException.php similarity index 100% rename from framework/yii/base/InvalidRouteException.php rename to framework/base/InvalidRouteException.php diff --git a/framework/yii/base/MailEvent.php b/framework/base/MailEvent.php similarity index 100% rename from framework/yii/base/MailEvent.php rename to framework/base/MailEvent.php diff --git a/framework/yii/base/Model.php b/framework/base/Model.php similarity index 100% rename from framework/yii/base/Model.php rename to framework/base/Model.php diff --git a/framework/yii/base/ModelEvent.php b/framework/base/ModelEvent.php similarity index 100% rename from framework/yii/base/ModelEvent.php rename to framework/base/ModelEvent.php diff --git a/framework/yii/base/Module.php b/framework/base/Module.php similarity index 100% rename from framework/yii/base/Module.php rename to framework/base/Module.php diff --git a/framework/yii/base/NotSupportedException.php b/framework/base/NotSupportedException.php similarity index 100% rename from framework/yii/base/NotSupportedException.php rename to framework/base/NotSupportedException.php diff --git a/framework/yii/base/Object.php b/framework/base/Object.php similarity index 100% rename from framework/yii/base/Object.php rename to framework/base/Object.php diff --git a/framework/yii/base/Request.php b/framework/base/Request.php similarity index 100% rename from framework/yii/base/Request.php rename to framework/base/Request.php diff --git a/framework/yii/base/Response.php b/framework/base/Response.php similarity index 100% rename from framework/yii/base/Response.php rename to framework/base/Response.php diff --git a/framework/yii/base/Theme.php b/framework/base/Theme.php similarity index 100% rename from framework/yii/base/Theme.php rename to framework/base/Theme.php diff --git a/framework/yii/base/UnknownClassException.php b/framework/base/UnknownClassException.php similarity index 100% rename from framework/yii/base/UnknownClassException.php rename to framework/base/UnknownClassException.php diff --git a/framework/yii/base/UnknownMethodException.php b/framework/base/UnknownMethodException.php similarity index 100% rename from framework/yii/base/UnknownMethodException.php rename to framework/base/UnknownMethodException.php diff --git a/framework/yii/base/UnknownPropertyException.php b/framework/base/UnknownPropertyException.php similarity index 100% rename from framework/yii/base/UnknownPropertyException.php rename to framework/base/UnknownPropertyException.php diff --git a/framework/yii/base/UserException.php b/framework/base/UserException.php similarity index 100% rename from framework/yii/base/UserException.php rename to framework/base/UserException.php diff --git a/framework/yii/base/View.php b/framework/base/View.php similarity index 100% rename from framework/yii/base/View.php rename to framework/base/View.php diff --git a/framework/yii/base/ViewContextInterface.php b/framework/base/ViewContextInterface.php similarity index 100% rename from framework/yii/base/ViewContextInterface.php rename to framework/base/ViewContextInterface.php diff --git a/framework/yii/base/ViewEvent.php b/framework/base/ViewEvent.php similarity index 100% rename from framework/yii/base/ViewEvent.php rename to framework/base/ViewEvent.php diff --git a/framework/yii/base/ViewRenderer.php b/framework/base/ViewRenderer.php similarity index 100% rename from framework/yii/base/ViewRenderer.php rename to framework/base/ViewRenderer.php diff --git a/framework/yii/base/Widget.php b/framework/base/Widget.php similarity index 100% rename from framework/yii/base/Widget.php rename to framework/base/Widget.php diff --git a/framework/yii/behaviors/AutoTimestamp.php b/framework/behaviors/AutoTimestamp.php similarity index 100% rename from framework/yii/behaviors/AutoTimestamp.php rename to framework/behaviors/AutoTimestamp.php diff --git a/framework/yii/caching/ApcCache.php b/framework/caching/ApcCache.php similarity index 100% rename from framework/yii/caching/ApcCache.php rename to framework/caching/ApcCache.php diff --git a/framework/yii/caching/Cache.php b/framework/caching/Cache.php similarity index 100% rename from framework/yii/caching/Cache.php rename to framework/caching/Cache.php diff --git a/framework/yii/caching/ChainedDependency.php b/framework/caching/ChainedDependency.php similarity index 100% rename from framework/yii/caching/ChainedDependency.php rename to framework/caching/ChainedDependency.php diff --git a/framework/yii/caching/DbCache.php b/framework/caching/DbCache.php similarity index 100% rename from framework/yii/caching/DbCache.php rename to framework/caching/DbCache.php diff --git a/framework/yii/caching/DbDependency.php b/framework/caching/DbDependency.php similarity index 100% rename from framework/yii/caching/DbDependency.php rename to framework/caching/DbDependency.php diff --git a/framework/yii/caching/Dependency.php b/framework/caching/Dependency.php similarity index 100% rename from framework/yii/caching/Dependency.php rename to framework/caching/Dependency.php diff --git a/framework/yii/caching/DummyCache.php b/framework/caching/DummyCache.php similarity index 100% rename from framework/yii/caching/DummyCache.php rename to framework/caching/DummyCache.php diff --git a/framework/yii/caching/ExpressionDependency.php b/framework/caching/ExpressionDependency.php similarity index 100% rename from framework/yii/caching/ExpressionDependency.php rename to framework/caching/ExpressionDependency.php diff --git a/framework/yii/caching/FileCache.php b/framework/caching/FileCache.php similarity index 100% rename from framework/yii/caching/FileCache.php rename to framework/caching/FileCache.php diff --git a/framework/yii/caching/FileDependency.php b/framework/caching/FileDependency.php similarity index 100% rename from framework/yii/caching/FileDependency.php rename to framework/caching/FileDependency.php diff --git a/framework/yii/caching/GroupDependency.php b/framework/caching/GroupDependency.php similarity index 100% rename from framework/yii/caching/GroupDependency.php rename to framework/caching/GroupDependency.php diff --git a/framework/yii/caching/MemCache.php b/framework/caching/MemCache.php similarity index 100% rename from framework/yii/caching/MemCache.php rename to framework/caching/MemCache.php diff --git a/framework/yii/caching/MemCacheServer.php b/framework/caching/MemCacheServer.php similarity index 100% rename from framework/yii/caching/MemCacheServer.php rename to framework/caching/MemCacheServer.php diff --git a/framework/yii/caching/WinCache.php b/framework/caching/WinCache.php similarity index 100% rename from framework/yii/caching/WinCache.php rename to framework/caching/WinCache.php diff --git a/framework/yii/caching/XCache.php b/framework/caching/XCache.php similarity index 100% rename from framework/yii/caching/XCache.php rename to framework/caching/XCache.php diff --git a/framework/yii/caching/ZendDataCache.php b/framework/caching/ZendDataCache.php similarity index 100% rename from framework/yii/caching/ZendDataCache.php rename to framework/caching/ZendDataCache.php diff --git a/framework/yii/captcha/Captcha.php b/framework/captcha/Captcha.php similarity index 100% rename from framework/yii/captcha/Captcha.php rename to framework/captcha/Captcha.php diff --git a/framework/yii/captcha/CaptchaAction.php b/framework/captcha/CaptchaAction.php similarity index 100% rename from framework/yii/captcha/CaptchaAction.php rename to framework/captcha/CaptchaAction.php diff --git a/framework/yii/captcha/CaptchaAsset.php b/framework/captcha/CaptchaAsset.php similarity index 100% rename from framework/yii/captcha/CaptchaAsset.php rename to framework/captcha/CaptchaAsset.php diff --git a/framework/yii/captcha/CaptchaValidator.php b/framework/captcha/CaptchaValidator.php similarity index 100% rename from framework/yii/captcha/CaptchaValidator.php rename to framework/captcha/CaptchaValidator.php diff --git a/framework/yii/captcha/SpicyRice.md b/framework/captcha/SpicyRice.md similarity index 100% rename from framework/yii/captcha/SpicyRice.md rename to framework/captcha/SpicyRice.md diff --git a/framework/yii/captcha/SpicyRice.ttf b/framework/captcha/SpicyRice.ttf similarity index 100% rename from framework/yii/captcha/SpicyRice.ttf rename to framework/captcha/SpicyRice.ttf Binary files a/framework/yii/captcha/SpicyRice.ttf and b/framework/captcha/SpicyRice.ttf differ diff --git a/framework/yii/classes.php b/framework/classes.php similarity index 100% rename from framework/yii/classes.php rename to framework/classes.php diff --git a/framework/composer.json b/framework/composer.json index d3ed7e7..c96726c 100644 --- a/framework/composer.json +++ b/framework/composer.json @@ -59,6 +59,6 @@ "michelf/php-markdown": "1.3.*" }, "autoload": { - "psr-0": { "yii\\": "" } + "psr-4": { "yii\\": "" } } } diff --git a/framework/yii/console/Application.php b/framework/console/Application.php similarity index 100% rename from framework/yii/console/Application.php rename to framework/console/Application.php diff --git a/framework/yii/console/Controller.php b/framework/console/Controller.php similarity index 100% rename from framework/yii/console/Controller.php rename to framework/console/Controller.php diff --git a/framework/yii/console/Exception.php b/framework/console/Exception.php similarity index 100% rename from framework/yii/console/Exception.php rename to framework/console/Exception.php diff --git a/framework/yii/console/Request.php b/framework/console/Request.php similarity index 100% rename from framework/yii/console/Request.php rename to framework/console/Request.php diff --git a/framework/yii/console/Response.php b/framework/console/Response.php similarity index 100% rename from framework/yii/console/Response.php rename to framework/console/Response.php diff --git a/framework/yii/console/controllers/AssetController.php b/framework/console/controllers/AssetController.php similarity index 100% rename from framework/yii/console/controllers/AssetController.php rename to framework/console/controllers/AssetController.php diff --git a/framework/yii/console/controllers/CacheController.php b/framework/console/controllers/CacheController.php similarity index 100% rename from framework/yii/console/controllers/CacheController.php rename to framework/console/controllers/CacheController.php diff --git a/framework/yii/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php similarity index 100% rename from framework/yii/console/controllers/FixtureController.php rename to framework/console/controllers/FixtureController.php diff --git a/framework/yii/console/controllers/HelpController.php b/framework/console/controllers/HelpController.php similarity index 100% rename from framework/yii/console/controllers/HelpController.php rename to framework/console/controllers/HelpController.php diff --git a/framework/yii/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php similarity index 100% rename from framework/yii/console/controllers/MessageController.php rename to framework/console/controllers/MessageController.php diff --git a/framework/yii/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php similarity index 100% rename from framework/yii/console/controllers/MigrateController.php rename to framework/console/controllers/MigrateController.php diff --git a/framework/yii/console/runtime/.gitignore b/framework/console/runtime/.gitignore similarity index 100% rename from framework/yii/console/runtime/.gitignore rename to framework/console/runtime/.gitignore diff --git a/framework/yii/data/ActiveDataProvider.php b/framework/data/ActiveDataProvider.php similarity index 100% rename from framework/yii/data/ActiveDataProvider.php rename to framework/data/ActiveDataProvider.php diff --git a/framework/yii/data/ArrayDataProvider.php b/framework/data/ArrayDataProvider.php similarity index 100% rename from framework/yii/data/ArrayDataProvider.php rename to framework/data/ArrayDataProvider.php diff --git a/framework/yii/data/BaseDataProvider.php b/framework/data/BaseDataProvider.php similarity index 100% rename from framework/yii/data/BaseDataProvider.php rename to framework/data/BaseDataProvider.php diff --git a/framework/yii/data/DataProviderInterface.php b/framework/data/DataProviderInterface.php similarity index 100% rename from framework/yii/data/DataProviderInterface.php rename to framework/data/DataProviderInterface.php diff --git a/framework/yii/data/Pagination.php b/framework/data/Pagination.php similarity index 100% rename from framework/yii/data/Pagination.php rename to framework/data/Pagination.php diff --git a/framework/yii/data/Sort.php b/framework/data/Sort.php similarity index 100% rename from framework/yii/data/Sort.php rename to framework/data/Sort.php diff --git a/framework/yii/data/SqlDataProvider.php b/framework/data/SqlDataProvider.php similarity index 100% rename from framework/yii/data/SqlDataProvider.php rename to framework/data/SqlDataProvider.php diff --git a/framework/yii/db/ActiveQuery.php b/framework/db/ActiveQuery.php similarity index 100% rename from framework/yii/db/ActiveQuery.php rename to framework/db/ActiveQuery.php diff --git a/framework/yii/db/ActiveQueryInterface.php b/framework/db/ActiveQueryInterface.php similarity index 100% rename from framework/yii/db/ActiveQueryInterface.php rename to framework/db/ActiveQueryInterface.php diff --git a/framework/yii/db/ActiveQueryTrait.php b/framework/db/ActiveQueryTrait.php similarity index 100% rename from framework/yii/db/ActiveQueryTrait.php rename to framework/db/ActiveQueryTrait.php diff --git a/framework/yii/db/ActiveRecord.php b/framework/db/ActiveRecord.php similarity index 100% rename from framework/yii/db/ActiveRecord.php rename to framework/db/ActiveRecord.php diff --git a/framework/yii/db/ActiveRecordInterface.php b/framework/db/ActiveRecordInterface.php similarity index 100% rename from framework/yii/db/ActiveRecordInterface.php rename to framework/db/ActiveRecordInterface.php diff --git a/framework/yii/db/ActiveRelation.php b/framework/db/ActiveRelation.php similarity index 100% rename from framework/yii/db/ActiveRelation.php rename to framework/db/ActiveRelation.php diff --git a/framework/yii/db/ActiveRelationInterface.php b/framework/db/ActiveRelationInterface.php similarity index 100% rename from framework/yii/db/ActiveRelationInterface.php rename to framework/db/ActiveRelationInterface.php diff --git a/framework/yii/db/ActiveRelationTrait.php b/framework/db/ActiveRelationTrait.php similarity index 100% rename from framework/yii/db/ActiveRelationTrait.php rename to framework/db/ActiveRelationTrait.php diff --git a/framework/yii/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php similarity index 100% rename from framework/yii/db/BaseActiveRecord.php rename to framework/db/BaseActiveRecord.php diff --git a/framework/yii/db/ColumnSchema.php b/framework/db/ColumnSchema.php similarity index 100% rename from framework/yii/db/ColumnSchema.php rename to framework/db/ColumnSchema.php diff --git a/framework/yii/db/Command.php b/framework/db/Command.php similarity index 100% rename from framework/yii/db/Command.php rename to framework/db/Command.php diff --git a/framework/yii/db/Connection.php b/framework/db/Connection.php similarity index 100% rename from framework/yii/db/Connection.php rename to framework/db/Connection.php diff --git a/framework/yii/db/DataReader.php b/framework/db/DataReader.php similarity index 100% rename from framework/yii/db/DataReader.php rename to framework/db/DataReader.php diff --git a/framework/yii/db/Exception.php b/framework/db/Exception.php similarity index 100% rename from framework/yii/db/Exception.php rename to framework/db/Exception.php diff --git a/framework/yii/db/Expression.php b/framework/db/Expression.php similarity index 100% rename from framework/yii/db/Expression.php rename to framework/db/Expression.php diff --git a/framework/yii/db/Migration.php b/framework/db/Migration.php similarity index 100% rename from framework/yii/db/Migration.php rename to framework/db/Migration.php diff --git a/framework/yii/db/Query.php b/framework/db/Query.php similarity index 100% rename from framework/yii/db/Query.php rename to framework/db/Query.php diff --git a/framework/yii/db/QueryBuilder.php b/framework/db/QueryBuilder.php similarity index 100% rename from framework/yii/db/QueryBuilder.php rename to framework/db/QueryBuilder.php diff --git a/framework/yii/db/QueryInterface.php b/framework/db/QueryInterface.php similarity index 100% rename from framework/yii/db/QueryInterface.php rename to framework/db/QueryInterface.php diff --git a/framework/yii/db/QueryTrait.php b/framework/db/QueryTrait.php similarity index 100% rename from framework/yii/db/QueryTrait.php rename to framework/db/QueryTrait.php diff --git a/framework/yii/db/Schema.php b/framework/db/Schema.php similarity index 100% rename from framework/yii/db/Schema.php rename to framework/db/Schema.php diff --git a/framework/yii/db/StaleObjectException.php b/framework/db/StaleObjectException.php similarity index 100% rename from framework/yii/db/StaleObjectException.php rename to framework/db/StaleObjectException.php diff --git a/framework/yii/db/TableSchema.php b/framework/db/TableSchema.php similarity index 100% rename from framework/yii/db/TableSchema.php rename to framework/db/TableSchema.php diff --git a/framework/yii/db/Transaction.php b/framework/db/Transaction.php similarity index 100% rename from framework/yii/db/Transaction.php rename to framework/db/Transaction.php diff --git a/framework/yii/db/cubrid/QueryBuilder.php b/framework/db/cubrid/QueryBuilder.php similarity index 100% rename from framework/yii/db/cubrid/QueryBuilder.php rename to framework/db/cubrid/QueryBuilder.php diff --git a/framework/yii/db/cubrid/Schema.php b/framework/db/cubrid/Schema.php similarity index 100% rename from framework/yii/db/cubrid/Schema.php rename to framework/db/cubrid/Schema.php diff --git a/framework/yii/db/mssql/PDO.php b/framework/db/mssql/PDO.php similarity index 100% rename from framework/yii/db/mssql/PDO.php rename to framework/db/mssql/PDO.php diff --git a/framework/yii/db/mssql/QueryBuilder.php b/framework/db/mssql/QueryBuilder.php similarity index 100% rename from framework/yii/db/mssql/QueryBuilder.php rename to framework/db/mssql/QueryBuilder.php diff --git a/framework/yii/db/mssql/Schema.php b/framework/db/mssql/Schema.php similarity index 100% rename from framework/yii/db/mssql/Schema.php rename to framework/db/mssql/Schema.php diff --git a/framework/yii/db/mssql/SqlsrvPDO.php b/framework/db/mssql/SqlsrvPDO.php similarity index 100% rename from framework/yii/db/mssql/SqlsrvPDO.php rename to framework/db/mssql/SqlsrvPDO.php diff --git a/framework/yii/db/mssql/TableSchema.php b/framework/db/mssql/TableSchema.php similarity index 100% rename from framework/yii/db/mssql/TableSchema.php rename to framework/db/mssql/TableSchema.php diff --git a/framework/yii/db/mysql/QueryBuilder.php b/framework/db/mysql/QueryBuilder.php similarity index 100% rename from framework/yii/db/mysql/QueryBuilder.php rename to framework/db/mysql/QueryBuilder.php diff --git a/framework/yii/db/mysql/Schema.php b/framework/db/mysql/Schema.php similarity index 100% rename from framework/yii/db/mysql/Schema.php rename to framework/db/mysql/Schema.php diff --git a/framework/yii/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php similarity index 100% rename from framework/yii/db/oci/QueryBuilder.php rename to framework/db/oci/QueryBuilder.php diff --git a/framework/yii/db/oci/Schema.php b/framework/db/oci/Schema.php similarity index 100% rename from framework/yii/db/oci/Schema.php rename to framework/db/oci/Schema.php diff --git a/framework/yii/db/pgsql/QueryBuilder.php b/framework/db/pgsql/QueryBuilder.php similarity index 100% rename from framework/yii/db/pgsql/QueryBuilder.php rename to framework/db/pgsql/QueryBuilder.php diff --git a/framework/yii/db/pgsql/Schema.php b/framework/db/pgsql/Schema.php similarity index 100% rename from framework/yii/db/pgsql/Schema.php rename to framework/db/pgsql/Schema.php diff --git a/framework/yii/db/sqlite/QueryBuilder.php b/framework/db/sqlite/QueryBuilder.php similarity index 100% rename from framework/yii/db/sqlite/QueryBuilder.php rename to framework/db/sqlite/QueryBuilder.php diff --git a/framework/yii/db/sqlite/Schema.php b/framework/db/sqlite/Schema.php similarity index 100% rename from framework/yii/db/sqlite/Schema.php rename to framework/db/sqlite/Schema.php diff --git a/framework/yii/grid/ActionColumn.php b/framework/grid/ActionColumn.php similarity index 100% rename from framework/yii/grid/ActionColumn.php rename to framework/grid/ActionColumn.php diff --git a/framework/yii/grid/CheckboxColumn.php b/framework/grid/CheckboxColumn.php similarity index 100% rename from framework/yii/grid/CheckboxColumn.php rename to framework/grid/CheckboxColumn.php diff --git a/framework/yii/grid/Column.php b/framework/grid/Column.php similarity index 100% rename from framework/yii/grid/Column.php rename to framework/grid/Column.php diff --git a/framework/yii/grid/DataColumn.php b/framework/grid/DataColumn.php similarity index 100% rename from framework/yii/grid/DataColumn.php rename to framework/grid/DataColumn.php diff --git a/framework/yii/grid/GridView.php b/framework/grid/GridView.php similarity index 100% rename from framework/yii/grid/GridView.php rename to framework/grid/GridView.php diff --git a/framework/yii/grid/GridViewAsset.php b/framework/grid/GridViewAsset.php similarity index 100% rename from framework/yii/grid/GridViewAsset.php rename to framework/grid/GridViewAsset.php diff --git a/framework/yii/grid/SerialColumn.php b/framework/grid/SerialColumn.php similarity index 100% rename from framework/yii/grid/SerialColumn.php rename to framework/grid/SerialColumn.php diff --git a/framework/yii/helpers/ArrayHelper.php b/framework/helpers/ArrayHelper.php similarity index 100% rename from framework/yii/helpers/ArrayHelper.php rename to framework/helpers/ArrayHelper.php diff --git a/framework/yii/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php similarity index 100% rename from framework/yii/helpers/BaseArrayHelper.php rename to framework/helpers/BaseArrayHelper.php diff --git a/framework/yii/helpers/BaseConsole.php b/framework/helpers/BaseConsole.php similarity index 100% rename from framework/yii/helpers/BaseConsole.php rename to framework/helpers/BaseConsole.php diff --git a/framework/yii/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php similarity index 100% rename from framework/yii/helpers/BaseFileHelper.php rename to framework/helpers/BaseFileHelper.php diff --git a/framework/yii/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php similarity index 100% rename from framework/yii/helpers/BaseHtml.php rename to framework/helpers/BaseHtml.php diff --git a/framework/yii/helpers/BaseHtmlPurifier.php b/framework/helpers/BaseHtmlPurifier.php similarity index 100% rename from framework/yii/helpers/BaseHtmlPurifier.php rename to framework/helpers/BaseHtmlPurifier.php diff --git a/framework/yii/helpers/BaseInflector.php b/framework/helpers/BaseInflector.php similarity index 100% rename from framework/yii/helpers/BaseInflector.php rename to framework/helpers/BaseInflector.php diff --git a/framework/yii/helpers/BaseJson.php b/framework/helpers/BaseJson.php similarity index 100% rename from framework/yii/helpers/BaseJson.php rename to framework/helpers/BaseJson.php diff --git a/framework/yii/helpers/BaseMarkdown.php b/framework/helpers/BaseMarkdown.php similarity index 100% rename from framework/yii/helpers/BaseMarkdown.php rename to framework/helpers/BaseMarkdown.php diff --git a/framework/yii/helpers/BaseSecurity.php b/framework/helpers/BaseSecurity.php similarity index 100% rename from framework/yii/helpers/BaseSecurity.php rename to framework/helpers/BaseSecurity.php diff --git a/framework/yii/helpers/BaseStringHelper.php b/framework/helpers/BaseStringHelper.php similarity index 100% rename from framework/yii/helpers/BaseStringHelper.php rename to framework/helpers/BaseStringHelper.php diff --git a/framework/yii/helpers/BaseVarDumper.php b/framework/helpers/BaseVarDumper.php similarity index 100% rename from framework/yii/helpers/BaseVarDumper.php rename to framework/helpers/BaseVarDumper.php diff --git a/framework/yii/helpers/Console.php b/framework/helpers/Console.php similarity index 100% rename from framework/yii/helpers/Console.php rename to framework/helpers/Console.php diff --git a/framework/yii/helpers/FileHelper.php b/framework/helpers/FileHelper.php similarity index 100% rename from framework/yii/helpers/FileHelper.php rename to framework/helpers/FileHelper.php diff --git a/framework/yii/helpers/Html.php b/framework/helpers/Html.php similarity index 100% rename from framework/yii/helpers/Html.php rename to framework/helpers/Html.php diff --git a/framework/yii/helpers/HtmlPurifier.php b/framework/helpers/HtmlPurifier.php similarity index 100% rename from framework/yii/helpers/HtmlPurifier.php rename to framework/helpers/HtmlPurifier.php diff --git a/framework/yii/helpers/Inflector.php b/framework/helpers/Inflector.php similarity index 100% rename from framework/yii/helpers/Inflector.php rename to framework/helpers/Inflector.php diff --git a/framework/yii/helpers/Json.php b/framework/helpers/Json.php similarity index 100% rename from framework/yii/helpers/Json.php rename to framework/helpers/Json.php diff --git a/framework/yii/helpers/Markdown.php b/framework/helpers/Markdown.php similarity index 100% rename from framework/yii/helpers/Markdown.php rename to framework/helpers/Markdown.php diff --git a/framework/yii/helpers/Security.php b/framework/helpers/Security.php similarity index 100% rename from framework/yii/helpers/Security.php rename to framework/helpers/Security.php diff --git a/framework/yii/helpers/StringHelper.php b/framework/helpers/StringHelper.php similarity index 100% rename from framework/yii/helpers/StringHelper.php rename to framework/helpers/StringHelper.php diff --git a/framework/yii/helpers/VarDumper.php b/framework/helpers/VarDumper.php similarity index 100% rename from framework/yii/helpers/VarDumper.php rename to framework/helpers/VarDumper.php diff --git a/framework/yii/helpers/mimeTypes.php b/framework/helpers/mimeTypes.php similarity index 100% rename from framework/yii/helpers/mimeTypes.php rename to framework/helpers/mimeTypes.php diff --git a/framework/yii/i18n/DbMessageSource.php b/framework/i18n/DbMessageSource.php similarity index 100% rename from framework/yii/i18n/DbMessageSource.php rename to framework/i18n/DbMessageSource.php diff --git a/framework/yii/i18n/Formatter.php b/framework/i18n/Formatter.php similarity index 100% rename from framework/yii/i18n/Formatter.php rename to framework/i18n/Formatter.php diff --git a/framework/yii/i18n/GettextFile.php b/framework/i18n/GettextFile.php similarity index 100% rename from framework/yii/i18n/GettextFile.php rename to framework/i18n/GettextFile.php diff --git a/framework/yii/i18n/GettextMessageSource.php b/framework/i18n/GettextMessageSource.php similarity index 100% rename from framework/yii/i18n/GettextMessageSource.php rename to framework/i18n/GettextMessageSource.php diff --git a/framework/yii/i18n/GettextMoFile.php b/framework/i18n/GettextMoFile.php similarity index 100% rename from framework/yii/i18n/GettextMoFile.php rename to framework/i18n/GettextMoFile.php diff --git a/framework/yii/i18n/GettextPoFile.php b/framework/i18n/GettextPoFile.php similarity index 100% rename from framework/yii/i18n/GettextPoFile.php rename to framework/i18n/GettextPoFile.php diff --git a/framework/yii/i18n/I18N.php b/framework/i18n/I18N.php similarity index 100% rename from framework/yii/i18n/I18N.php rename to framework/i18n/I18N.php diff --git a/framework/yii/i18n/MessageFormatter.php b/framework/i18n/MessageFormatter.php similarity index 100% rename from framework/yii/i18n/MessageFormatter.php rename to framework/i18n/MessageFormatter.php diff --git a/framework/yii/i18n/MessageSource.php b/framework/i18n/MessageSource.php similarity index 100% rename from framework/yii/i18n/MessageSource.php rename to framework/i18n/MessageSource.php diff --git a/framework/yii/i18n/MissingTranslationEvent.php b/framework/i18n/MissingTranslationEvent.php similarity index 100% rename from framework/yii/i18n/MissingTranslationEvent.php rename to framework/i18n/MissingTranslationEvent.php diff --git a/framework/yii/i18n/PhpMessageSource.php b/framework/i18n/PhpMessageSource.php similarity index 100% rename from framework/yii/i18n/PhpMessageSource.php rename to framework/i18n/PhpMessageSource.php diff --git a/framework/yii/log/DbTarget.php b/framework/log/DbTarget.php similarity index 100% rename from framework/yii/log/DbTarget.php rename to framework/log/DbTarget.php diff --git a/framework/yii/log/EmailTarget.php b/framework/log/EmailTarget.php similarity index 100% rename from framework/yii/log/EmailTarget.php rename to framework/log/EmailTarget.php diff --git a/framework/yii/log/FileTarget.php b/framework/log/FileTarget.php similarity index 100% rename from framework/yii/log/FileTarget.php rename to framework/log/FileTarget.php diff --git a/framework/yii/log/Logger.php b/framework/log/Logger.php similarity index 100% rename from framework/yii/log/Logger.php rename to framework/log/Logger.php diff --git a/framework/yii/log/Target.php b/framework/log/Target.php similarity index 100% rename from framework/yii/log/Target.php rename to framework/log/Target.php diff --git a/framework/yii/mail/BaseMailer.php b/framework/mail/BaseMailer.php similarity index 100% rename from framework/yii/mail/BaseMailer.php rename to framework/mail/BaseMailer.php diff --git a/framework/yii/mail/BaseMessage.php b/framework/mail/BaseMessage.php similarity index 100% rename from framework/yii/mail/BaseMessage.php rename to framework/mail/BaseMessage.php diff --git a/framework/yii/mail/MailerInterface.php b/framework/mail/MailerInterface.php similarity index 100% rename from framework/yii/mail/MailerInterface.php rename to framework/mail/MailerInterface.php diff --git a/framework/yii/mail/MessageInterface.php b/framework/mail/MessageInterface.php similarity index 100% rename from framework/yii/mail/MessageInterface.php rename to framework/mail/MessageInterface.php diff --git a/framework/yii/messages/config.php b/framework/messages/config.php similarity index 100% rename from framework/yii/messages/config.php rename to framework/messages/config.php diff --git a/framework/yii/messages/da/yii.php b/framework/messages/da/yii.php similarity index 100% rename from framework/yii/messages/da/yii.php rename to framework/messages/da/yii.php diff --git a/framework/yii/messages/de/yii.php b/framework/messages/de/yii.php similarity index 100% rename from framework/yii/messages/de/yii.php rename to framework/messages/de/yii.php diff --git a/framework/yii/messages/es/yii.php b/framework/messages/es/yii.php similarity index 100% rename from framework/yii/messages/es/yii.php rename to framework/messages/es/yii.php diff --git a/framework/yii/messages/it/yii.php b/framework/messages/it/yii.php similarity index 100% rename from framework/yii/messages/it/yii.php rename to framework/messages/it/yii.php diff --git a/framework/yii/messages/ja/yii.php b/framework/messages/ja/yii.php similarity index 100% rename from framework/yii/messages/ja/yii.php rename to framework/messages/ja/yii.php diff --git a/framework/yii/messages/pl/yii.php b/framework/messages/pl/yii.php similarity index 100% rename from framework/yii/messages/pl/yii.php rename to framework/messages/pl/yii.php diff --git a/framework/yii/messages/pt-BR/yii.php b/framework/messages/pt-BR/yii.php similarity index 100% rename from framework/yii/messages/pt-BR/yii.php rename to framework/messages/pt-BR/yii.php diff --git a/framework/yii/messages/ro/yii.php b/framework/messages/ro/yii.php similarity index 100% rename from framework/yii/messages/ro/yii.php rename to framework/messages/ro/yii.php diff --git a/framework/yii/messages/ru/yii.php b/framework/messages/ru/yii.php similarity index 100% rename from framework/yii/messages/ru/yii.php rename to framework/messages/ru/yii.php diff --git a/framework/yii/messages/zh_cn/yii.php b/framework/messages/zh_cn/yii.php similarity index 100% rename from framework/yii/messages/zh_cn/yii.php rename to framework/messages/zh_cn/yii.php diff --git a/framework/yii/mutex/DbMutex.php b/framework/mutex/DbMutex.php similarity index 100% rename from framework/yii/mutex/DbMutex.php rename to framework/mutex/DbMutex.php diff --git a/framework/yii/mutex/FileMutex.php b/framework/mutex/FileMutex.php similarity index 100% rename from framework/yii/mutex/FileMutex.php rename to framework/mutex/FileMutex.php diff --git a/framework/yii/mutex/Mutex.php b/framework/mutex/Mutex.php similarity index 100% rename from framework/yii/mutex/Mutex.php rename to framework/mutex/Mutex.php diff --git a/framework/yii/mutex/MysqlMutex.php b/framework/mutex/MysqlMutex.php similarity index 100% rename from framework/yii/mutex/MysqlMutex.php rename to framework/mutex/MysqlMutex.php diff --git a/framework/yii/rbac/Assignment.php b/framework/rbac/Assignment.php similarity index 100% rename from framework/yii/rbac/Assignment.php rename to framework/rbac/Assignment.php diff --git a/framework/yii/rbac/DbManager.php b/framework/rbac/DbManager.php similarity index 100% rename from framework/yii/rbac/DbManager.php rename to framework/rbac/DbManager.php diff --git a/framework/yii/rbac/Item.php b/framework/rbac/Item.php similarity index 100% rename from framework/yii/rbac/Item.php rename to framework/rbac/Item.php diff --git a/framework/yii/rbac/Manager.php b/framework/rbac/Manager.php similarity index 100% rename from framework/yii/rbac/Manager.php rename to framework/rbac/Manager.php diff --git a/framework/yii/rbac/PhpManager.php b/framework/rbac/PhpManager.php similarity index 100% rename from framework/yii/rbac/PhpManager.php rename to framework/rbac/PhpManager.php diff --git a/framework/yii/rbac/schema-mssql.sql b/framework/rbac/schema-mssql.sql similarity index 100% rename from framework/yii/rbac/schema-mssql.sql rename to framework/rbac/schema-mssql.sql diff --git a/framework/yii/rbac/schema-mysql.sql b/framework/rbac/schema-mysql.sql similarity index 100% rename from framework/yii/rbac/schema-mysql.sql rename to framework/rbac/schema-mysql.sql diff --git a/framework/yii/rbac/schema-oci.sql b/framework/rbac/schema-oci.sql similarity index 100% rename from framework/yii/rbac/schema-oci.sql rename to framework/rbac/schema-oci.sql diff --git a/framework/yii/rbac/schema-pgsql.sql b/framework/rbac/schema-pgsql.sql similarity index 100% rename from framework/yii/rbac/schema-pgsql.sql rename to framework/rbac/schema-pgsql.sql diff --git a/framework/yii/rbac/schema-sqlite.sql b/framework/rbac/schema-sqlite.sql similarity index 100% rename from framework/yii/rbac/schema-sqlite.sql rename to framework/rbac/schema-sqlite.sql diff --git a/framework/yii/requirements/YiiRequirementChecker.php b/framework/requirements/YiiRequirementChecker.php similarity index 100% rename from framework/yii/requirements/YiiRequirementChecker.php rename to framework/requirements/YiiRequirementChecker.php diff --git a/framework/yii/requirements/requirements.php b/framework/requirements/requirements.php similarity index 100% rename from framework/yii/requirements/requirements.php rename to framework/requirements/requirements.php diff --git a/framework/yii/requirements/views/console/index.php b/framework/requirements/views/console/index.php similarity index 100% rename from framework/yii/requirements/views/console/index.php rename to framework/requirements/views/console/index.php diff --git a/framework/yii/requirements/views/web/css.php b/framework/requirements/views/web/css.php similarity index 100% rename from framework/yii/requirements/views/web/css.php rename to framework/requirements/views/web/css.php diff --git a/framework/requirements/views/web/index.php b/framework/requirements/views/web/index.php new file mode 100644 index 0000000..287d4bb --- /dev/null +++ b/framework/requirements/views/web/index.php @@ -0,0 +1,82 @@ +<?php +/* @var YiiRequirementChecker $this */ +/* @var array $summary */ +/* @var array[] $requirements */ +?> +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"/> + <title>Yii Application Requirement Checker</title> + <?php $this->renderViewFile(dirname(__FILE__) . '/css.php'); ?> +</head> +<body> +<div class="container"> + <div class="header"> + <h1>Yii Application Requirement Checker</h1> + </div> + <hr> + + <div class="content"> + <h3>Description</h3> + <p> + This script checks if your server configuration meets the requirements + for running Yii application. + It checks if the server is running the right version of PHP, + if appropriate PHP extensions have been loaded, and if php.ini file settings are correct. + </p> + <p> + There are two kinds of requirements being checked. Mandatory requirements are those that have to be met + to allow Yii to work as expected. There are also some optional requirements beeing checked which will + show you a warning when they do not meet. You can use Yii framework without them but some specific + functionality may be not available in this case. + </p> + + <h3>Conclusion</h3> + <?php if ($summary['errors'] > 0): ?> + <div class="alert alert-error"> + <strong>Unfortunately your server configuration does not satisfy the requirements by this application.<br>Please refer to the table below for detailed explanation.</strong> + </div> + <?php elseif ($summary['warnings'] > 0): ?> + <div class="alert alert-info"> + <strong>Your server configuration satisfies the minimum requirements by this application.<br>Please pay attention to the warnings listed below and check if your application will use the corresponding features.</strong> + </div> + <?php else: ?> + <div class="alert alert-success"> + <strong>Congratulations! Your server configuration satisfies all requirements.</strong> + </div> + <?php endif; ?> + + <h3>Details</h3> + + <table class="table table-bordered"> + <tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr> + <?php foreach($requirements as $requirement): ?> + <tr class="<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'error' : 'warning') ?>"> + <td> + <?php echo $requirement['name'] ?> + </td> + <td> + <span class="result"><?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning') ?></span> + </td> + <td> + <?php echo $requirement['by'] ?> + </td> + <td> + <?php echo $requirement['memo'] ?> + </td> + </tr> + <?php endforeach; ?> + </table> + + </div> + + <hr> + + <div class="footer"> + <p>Server: <?php echo $this->getServerInfo() . ' ' . $this->getNowDate() ?></p> + <p>Powered by <a href="http://www.yiiframework.com/" rel="external">Yii Framework</a></p> + </div> +</div> +</body> +</html> diff --git a/framework/yii/test/DbFixtureManager.php b/framework/test/DbFixtureManager.php similarity index 100% rename from framework/yii/test/DbFixtureManager.php rename to framework/test/DbFixtureManager.php diff --git a/framework/yii/test/DbTestTrait.php b/framework/test/DbTestTrait.php similarity index 100% rename from framework/yii/test/DbTestTrait.php rename to framework/test/DbTestTrait.php diff --git a/framework/yii/validators/BooleanValidator.php b/framework/validators/BooleanValidator.php similarity index 100% rename from framework/yii/validators/BooleanValidator.php rename to framework/validators/BooleanValidator.php diff --git a/framework/yii/validators/CompareValidator.php b/framework/validators/CompareValidator.php similarity index 100% rename from framework/yii/validators/CompareValidator.php rename to framework/validators/CompareValidator.php diff --git a/framework/yii/validators/DateValidator.php b/framework/validators/DateValidator.php similarity index 100% rename from framework/yii/validators/DateValidator.php rename to framework/validators/DateValidator.php diff --git a/framework/yii/validators/DefaultValueValidator.php b/framework/validators/DefaultValueValidator.php similarity index 100% rename from framework/yii/validators/DefaultValueValidator.php rename to framework/validators/DefaultValueValidator.php diff --git a/framework/validators/EmailValidator.php b/framework/validators/EmailValidator.php new file mode 100644 index 0000000..e5d9b75 --- /dev/null +++ b/framework/validators/EmailValidator.php @@ -0,0 +1,116 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +use Yii; +use yii\base\InvalidConfigException; +use yii\helpers\Html; +use yii\web\JsExpression; +use yii\helpers\Json; + +/** + * EmailValidator validates that the attribute value is a valid email address. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class EmailValidator extends Validator +{ + /** + * @var string the regular expression used to validate the attribute value. + * @see http://www.regular-expressions.info/email.html + */ + public $pattern = '/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/'; + /** + * @var string the regular expression used to validate email addresses with the name part. + * This property is used only when [[allowName]] is true. + * @see allowName + */ + public $fullPattern = '/^[^@]*<[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/'; + /** + * @var boolean whether to allow name in the email address (e.g. "John Smith <john.smith@example.com>"). Defaults to false. + * @see fullPattern + */ + public $allowName = false; + /** + * @var boolean whether to check whether the emails domain exists and has either an A or MX record. + * Be aware of the fact that this check can fail due to temporary DNS problems even if the email address is + * valid and an email would be deliverable. Defaults to false. + */ + public $checkDNS = false; + /** + * @var boolean whether validation process should take into account IDN (internationalized domain + * names). Defaults to false meaning that validation of emails containing IDN will always fail. + * Note that in order to use IDN validation you have to install and enable `intl` PHP extension, + * otherwise an exception would be thrown. + */ + public $enableIDN = false; + + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if ($this->enableIDN && !function_exists('idn_to_ascii')) { + throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.'); + } + if ($this->message === null) { + $this->message = Yii::t('yii', '{attribute} is not a valid email address.'); + } + } + + /** + * @inheritdoc + */ + protected function validateValue($value) + { + // make sure string length is limited to avoid DOS attacks + if (!is_string($value) || strlen($value) >= 320) { + $valid = false; + } elseif (!preg_match('/^(.*<?)(.*)@(.*)(>?)$/', $value, $matches)) { + $valid = false; + } else { + $domain = $matches[3]; + if ($this->enableIDN) { + $value = $matches[1] . idn_to_ascii($matches[2]) . '@' . idn_to_ascii($domain) . $matches[4]; + } + $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value); + if ($valid && $this->checkDNS) { + $valid = checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A'); + } + } + return $valid ? null : [$this->message, []]; + } + + /** + * @inheritdoc + */ + public function clientValidateAttribute($object, $attribute, $view) + { + $options = [ + 'pattern' => new JsExpression($this->pattern), + 'fullPattern' => new JsExpression($this->fullPattern), + 'allowName' => $this->allowName, + 'message' => strtr($this->message, [ + '{attribute}' => $object->getAttributeLabel($attribute), + ]), + 'enableIDN' => (boolean)$this->enableIDN, + ]; + if ($this->skipOnEmpty) { + $options['skipOnEmpty'] = 1; + } + + ValidationAsset::register($view); + if ($this->enableIDN) { + PunycodeAsset::register($view); + } + return 'yii.validation.email(value, messages, ' . Json::encode($options) . ');'; + } +} diff --git a/framework/validators/ExistValidator.php b/framework/validators/ExistValidator.php new file mode 100644 index 0000000..caefc49 --- /dev/null +++ b/framework/validators/ExistValidator.php @@ -0,0 +1,122 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +use Yii; +use yii\base\InvalidConfigException; + +/** + * ExistValidator validates that the attribute value exists in a table. + * + * ExistValidator checks if the value being validated can be found in the table column specified by + * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]]. + * + * This validator is often used to verify that a foreign key contains a value + * that can be found in the foreign table. + * + * The followings are examples of validation rules using this validator: + * + * ```php + * // a1 needs to exist + * ['a1', 'exist'] + * // a1 needs to exist, but its value will use a2 to check for the existence + * ['a1', 'exist', 'targetAttribute' => 'a2'] + * // a1 and a2 need to exist together, and they both will receive error message + * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']] + * // a1 and a2 need to exist together, only a1 will receive error message + * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']] + * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value) + * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']] + * ``` + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class ExistValidator extends Validator +{ + /** + * @var string the name of the ActiveRecord class that should be used to validate the existence + * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated. + * @see targetAttribute + */ + public $targetClass; + /** + * @var string|array the name of the ActiveRecord attribute that should be used to + * validate the existence of the current attribute value. If not set, it will use the name + * of the attribute currently being validated. You may use an array to validate the existence + * of multiple columns at the same time. The array values are the attributes that will be + * used to validate the existence, while the array keys are the attributes whose values are to be validated. + * If the key and the value are the same, you can just specify the value. + */ + public $targetAttribute; + + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if ($this->message === null) { + $this->message = Yii::t('yii', '{attribute} is invalid.'); + } + } + + /** + * @inheritdoc + */ + public function validateAttribute($object, $attribute) + { + /** @var \yii\db\ActiveRecordInterface $targetClass */ + $targetClass = $this->targetClass === null ? get_class($object) : $this->targetClass; + $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute; + + if (is_array($targetAttribute)) { + $params = []; + foreach ($targetAttribute as $k => $v) { + $params[$v] = is_integer($k) ? $object->$v : $object->$k; + } + } else { + $params = [$targetAttribute => $object->$attribute]; + } + + foreach ($params as $value) { + if (is_array($value)) { + $this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.')); + return; + } + } + + /** @var \yii\db\ActiveRecordInterface $className */ + if (!$targetClass::find()->where($params)->exists()) { + $this->addError($object, $attribute, $this->message); + } + } + + /** + * @inheritdoc + */ + protected function validateValue($value) + { + if (is_array($value)) { + return [$this->message, []]; + } + if ($this->targetClass === null) { + throw new InvalidConfigException('The "className" property must be set.'); + } + if (!is_string($this->targetAttribute)) { + throw new InvalidConfigException('The "attributeName" property must be configured as a string.'); + } + + /** @var \yii\db\ActiveRecordInterface $targetClass */ + $targetClass = $this->targetClass; + $query = $targetClass::find(); + $query->where([$this->targetAttribute => $value]); + return $query->exists() ? null : [$this->message, []]; + } +} diff --git a/framework/yii/validators/FileValidator.php b/framework/validators/FileValidator.php similarity index 100% rename from framework/yii/validators/FileValidator.php rename to framework/validators/FileValidator.php diff --git a/framework/yii/validators/FilterValidator.php b/framework/validators/FilterValidator.php similarity index 100% rename from framework/yii/validators/FilterValidator.php rename to framework/validators/FilterValidator.php diff --git a/framework/yii/validators/ImageValidator.php b/framework/validators/ImageValidator.php similarity index 100% rename from framework/yii/validators/ImageValidator.php rename to framework/validators/ImageValidator.php diff --git a/framework/validators/InlineValidator.php b/framework/validators/InlineValidator.php new file mode 100644 index 0000000..b769e4e --- /dev/null +++ b/framework/validators/InlineValidator.php @@ -0,0 +1,84 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +/** + * InlineValidator represents a validator which is defined as a method in the object being validated. + * + * The validation method must have the following signature: + * + * ~~~ + * function foo($attribute, $params) + * ~~~ + * + * where `$attribute` refers to the name of the attribute being validated, while `$params` + * is an array representing the additional parameters supplied in the validation rule. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +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. The signature of the method should be like the following: + * + * ~~~ + * function foo($attribute, $params) + * ~~~ + */ + public $method; + /** + * @var array additional parameters that are passed to the validation method + */ + public $params; + /** + * @var string|\Closure an anonymous function or the name of a model class method that returns the client validation code. + * The signature of the method should be like the following: + * + * ~~~ + * function foo($attribute, $params) + * { + * return "javascript"; + * } + * ~~~ + * + * where `$attribute` refers to the attribute name to be validated. + * + * Please refer to [[clientValidateAttribute()]] for details on how to return client validation code. + */ + public $clientValidate; + + /** + * @inheritdoc + */ + public function validateAttribute($object, $attribute) + { + $method = $this->method; + if (is_string($method)) { + $method = [$object, $method]; + } + call_user_func($method, $attribute, $this->params); + } + + /** + * @inheritdoc + */ + public function clientValidateAttribute($object, $attribute, $view) + { + if ($this->clientValidate !== null) { + $method = $this->clientValidate; + if (is_string($method)) { + $method = [$object, $method]; + } + return call_user_func($method, $attribute, $this->params); + } else { + return null; + } + } +} diff --git a/framework/yii/validators/NumberValidator.php b/framework/validators/NumberValidator.php similarity index 100% rename from framework/yii/validators/NumberValidator.php rename to framework/validators/NumberValidator.php diff --git a/framework/yii/validators/PunycodeAsset.php b/framework/validators/PunycodeAsset.php similarity index 100% rename from framework/yii/validators/PunycodeAsset.php rename to framework/validators/PunycodeAsset.php diff --git a/framework/validators/RangeValidator.php b/framework/validators/RangeValidator.php new file mode 100644 index 0000000..a4da139 --- /dev/null +++ b/framework/validators/RangeValidator.php @@ -0,0 +1,87 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +use Yii; +use yii\base\InvalidConfigException; +use yii\helpers\Html; + +/** + * RangeValidator validates that the attribute value is among a list of values. + * + * The range can be specified via the [[range]] property. + * If the [[not]] property is set true, the validator will ensure the attribute value + * is NOT among the specified range. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class RangeValidator extends Validator +{ + /** + * @var array list of valid values that the attribute value should be among + */ + public $range; + /** + * @var boolean whether the comparison is strict (both type and value must be the same) + */ + public $strict = false; + /** + * @var boolean whether to invert the validation logic. Defaults to false. If set to true, + * the attribute value should NOT be among the list of values defined via [[range]]. + **/ + public $not = false; + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if (!is_array($this->range)) { + throw new InvalidConfigException('The "range" property must be set.'); + } + if ($this->message === null) { + $this->message = Yii::t('yii', '{attribute} is invalid.'); + } + } + + /** + * @inheritdoc + */ + protected function validateValue($value) + { + $valid = !$this->not && in_array($value, $this->range, $this->strict) + || $this->not && !in_array($value, $this->range, $this->strict); + return $valid ? null : [$this->message, []]; + } + + /** + * @inheritdoc + */ + public function clientValidateAttribute($object, $attribute, $view) + { + $range = []; + foreach ($this->range as $value) { + $range[] = (string)$value; + } + $options = [ + 'range' => $range, + 'not' => $this->not, + 'message' => strtr($this->message, [ + '{attribute}' => $object->getAttributeLabel($attribute), + ]), + ]; + if ($this->skipOnEmpty) { + $options['skipOnEmpty'] = 1; + } + + ValidationAsset::register($view); + return 'yii.validation.range(value, messages, ' . json_encode($options) . ');'; + } +} diff --git a/framework/validators/RegularExpressionValidator.php b/framework/validators/RegularExpressionValidator.php new file mode 100644 index 0000000..28e9bdc --- /dev/null +++ b/framework/validators/RegularExpressionValidator.php @@ -0,0 +1,94 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +use Yii; +use yii\base\InvalidConfigException; +use yii\helpers\Html; +use yii\web\JsExpression; +use yii\helpers\Json; + +/** + * RegularExpressionValidator validates that the attribute value matches the specified [[pattern]]. + * + * If the [[not]] property is set true, the validator will ensure the attribute value do NOT match the [[pattern]]. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class RegularExpressionValidator extends Validator +{ + /** + * @var string the regular expression to be matched with + */ + public $pattern; + /** + * @var boolean whether to invert the validation logic. Defaults to false. If set to true, + * the regular expression defined via [[pattern]] should NOT match the attribute value. + **/ + public $not = false; + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if ($this->pattern === null) { + throw new InvalidConfigException('The "pattern" property must be set.'); + } + if ($this->message === null) { + $this->message = Yii::t('yii', '{attribute} is invalid.'); + } + } + + /** + * @inheritdoc + */ + protected function validateValue($value) + { + $valid = !is_array($value) && + (!$this->not && preg_match($this->pattern, $value) + || $this->not && !preg_match($this->pattern, $value)); + return $valid ? null : [$this->message, []]; + } + + /** + * @inheritdoc + */ + public function clientValidateAttribute($object, $attribute, $view) + { + $pattern = $this->pattern; + $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $pattern); + $deliminator = substr($pattern, 0, 1); + $pos = strrpos($pattern, $deliminator, 1); + $flag = substr($pattern, $pos + 1); + if ($deliminator !== '/') { + $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/'; + } else { + $pattern = substr($pattern, 0, $pos + 1); + } + if (!empty($flag)) { + $pattern .= preg_replace('/[^igm]/', '', $flag); + } + + $options = [ + 'pattern' => new JsExpression($pattern), + 'not' => $this->not, + 'message' => strtr($this->message, [ + '{attribute}' => $object->getAttributeLabel($attribute), + ]), + ]; + if ($this->skipOnEmpty) { + $options['skipOnEmpty'] = 1; + } + + ValidationAsset::register($view); + return 'yii.validation.regularExpression(value, messages, ' . Json::encode($options) . ');'; + } +} diff --git a/framework/validators/RequiredValidator.php b/framework/validators/RequiredValidator.php new file mode 100644 index 0000000..f291f39 --- /dev/null +++ b/framework/validators/RequiredValidator.php @@ -0,0 +1,111 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\validators; + +use Yii; +use yii\helpers\Html; + +/** + * RequiredValidator validates that the specified attribute does not have null or empty value. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class RequiredValidator extends Validator +{ + /** + * @var boolean whether to skip this validator if the value being validated is empty. + */ + public $skipOnEmpty = false; + /** + * @var mixed the desired value that the attribute must have. + * If this is null, the validator will validate that the specified attribute is not empty. + * If this is set as a value that is not null, the validator will validate that + * the attribute has a value that is the same as this property value. + * Defaults to null. + * @see strict + */ + public $requiredValue; + /** + * @var boolean whether the comparison between the attribute value and [[requiredValue]] is strict. + * When this is true, both the values and types must match. + * Defaults to false, meaning only the values need to match. + * Note that when [[requiredValue]] is null, if this property is true, the validator will check + * if the attribute value is null; If this property is false, the validator will call [[isEmpty]] + * to check if the attribute value is empty. + */ + public $strict = false; + /** + * @var string the user-defined error message. It may contain the following placeholders which + * will be replaced accordingly by the validator: + * + * - `{attribute}`: the label of the attribute being validated + * - `{value}`: the value of the attribute being validated + * - `{requiredValue}`: the value of [[requiredValue]] + */ + public $message; + + /** + * @inheritdoc + */ + public function init() + { + parent::init(); + if ($this->message === null) { + $this->message = $this->requiredValue === null ? Yii::t('yii', '{attribute} cannot be blank.') + : Yii::t('yii', '{attribute} must be "{requiredValue}".'); + } + } + + /** + * @inheritdoc + */ + protected function validateValue($value) + { + if ($this->requiredValue === null) { + if ($this->strict && $value !== null || !$this->strict && !$this->isEmpty($value, true)) { + return null; + } + } elseif (!$this->strict && $value == $this->requiredValue || $this->strict && $value === $this->requiredValue) { + return null; + } + if ($this->requiredValue === null) { + return [$this->message, []]; + } else { + return [$this->message, [ + 'requiredValue' => $this->requiredValue, + ]]; + } + } + + /** + * @inheritdoc + */ + public function clientValidateAttribute($object, $attribute, $view) + { + $options = []; + if ($this->requiredValue !== null) { + $options['message'] = strtr($this->message, [ + '{requiredValue}' => $this->requiredValue, + ]); + $options['requiredValue'] = $this->requiredValue; + } else { + $options['message'] = $this->message; + } + if ($this->strict) { + $options['strict'] = 1; + } + + $options['message'] = strtr($options['message'], [ + '{attribute}' => $object->getAttributeLabel($attribute), + ]); + + ValidationAsset::register($view); + return 'yii.validation.required(value, messages, ' . json_encode($options) . ');'; + } +} diff --git a/framework/yii/validators/SafeValidator.php b/framework/validators/SafeValidator.php similarity index 100% rename from framework/yii/validators/SafeValidator.php rename to framework/validators/SafeValidator.php diff --git a/framework/yii/validators/StringValidator.php b/framework/validators/StringValidator.php similarity index 100% rename from framework/yii/validators/StringValidator.php rename to framework/validators/StringValidator.php diff --git a/framework/yii/validators/ExistValidator.php b/framework/validators/UniqueValidator.php similarity index 60% rename from framework/yii/validators/ExistValidator.php rename to framework/validators/UniqueValidator.php index caefc49..51474b0 100644 --- a/framework/yii/validators/ExistValidator.php +++ b/framework/validators/UniqueValidator.php @@ -8,54 +8,50 @@ namespace yii\validators; use Yii; -use yii\base\InvalidConfigException; +use yii\db\ActiveRecordInterface; /** - * ExistValidator validates that the attribute value exists in a table. + * UniqueValidator validates that the attribute value is unique in the specified database table. * - * ExistValidator checks if the value being validated can be found in the table column specified by + * UniqueValidator checks if the value being validated is unique in the table column specified by * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]]. * - * This validator is often used to verify that a foreign key contains a value - * that can be found in the foreign table. - * * The followings are examples of validation rules using this validator: * * ```php - * // a1 needs to exist - * ['a1', 'exist'] - * // a1 needs to exist, but its value will use a2 to check for the existence - * ['a1', 'exist', 'targetAttribute' => 'a2'] - * // a1 and a2 need to exist together, and they both will receive error message - * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']] - * // a1 and a2 need to exist together, only a1 will receive error message - * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']] - * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value) - * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']] + * // a1 needs to be unique + * ['a1', 'unique'] + * // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value + * ['a1', 'unique', 'targetAttribute' => 'a2'] + * // a1 and a2 need to unique together, and they both will receive error message + * [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']] + * // a1 and a2 need to unique together, only a1 will receive error message + * ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']] + * // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value) + * ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']] * ``` * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ -class ExistValidator extends Validator +class UniqueValidator extends Validator { /** - * @var string the name of the ActiveRecord class that should be used to validate the existence + * @var string the name of the ActiveRecord class that should be used to validate the uniqueness * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated. * @see targetAttribute */ public $targetClass; /** * @var string|array the name of the ActiveRecord attribute that should be used to - * validate the existence of the current attribute value. If not set, it will use the name - * of the attribute currently being validated. You may use an array to validate the existence + * validate the uniqueness of the current attribute value. If not set, it will use the name + * of the attribute currently being validated. You may use an array to validate the uniqueness * of multiple columns at the same time. The array values are the attributes that will be - * used to validate the existence, while the array keys are the attributes whose values are to be validated. + * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated. * If the key and the value are the same, you can just specify the value. */ public $targetAttribute; - /** * @inheritdoc */ @@ -63,7 +59,7 @@ class ExistValidator extends Validator { parent::init(); if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} is invalid.'); + $this->message = Yii::t('yii', '{attribute} "{value}" has already been taken.'); } } @@ -72,7 +68,7 @@ class ExistValidator extends Validator */ public function validateAttribute($object, $attribute) { - /** @var \yii\db\ActiveRecordInterface $targetClass */ + /** @var ActiveRecordInterface $targetClass */ $targetClass = $this->targetClass === null ? get_class($object) : $this->targetClass; $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute; @@ -92,31 +88,36 @@ class ExistValidator extends Validator } } - /** @var \yii\db\ActiveRecordInterface $className */ - if (!$targetClass::find()->where($params)->exists()) { - $this->addError($object, $attribute, $this->message); - } - } + $query = $targetClass::find(); + $query->where($params); - /** - * @inheritdoc - */ - protected function validateValue($value) - { - if (is_array($value)) { - return [$this->message, []]; - } - if ($this->targetClass === null) { - throw new InvalidConfigException('The "className" property must be set.'); - } - if (!is_string($this->targetAttribute)) { - throw new InvalidConfigException('The "attributeName" property must be configured as a string.'); + if (!$object instanceof ActiveRecordInterface || $object->getIsNewRecord()) { + // if current $object isn't in the database yet then it's OK just to call exists() + $exists = $query->exists(); + } else { + // if current $object is in the database already we can't use exists() + /** @var ActiveRecordInterface[] $objects */ + $objects = $query->limit(2)->all(); + $n = count($objects); + if ($n === 1) { + $keys = array_keys($params); + $pks = $targetClass::primaryKey(); + sort($keys); + sort($pks); + if ($keys === $pks) { + // primary key is modified and not unique + $exists = $object->getOldPrimaryKey() != $object->getPrimaryKey(); + } else { + // non-primary key, need to exclude the current record based on PK + $exists = $objects[0]->getPrimaryKey() != $object->getOldPrimaryKey(); + } + } else { + $exists = $n > 1; + } } - /** @var \yii\db\ActiveRecordInterface $targetClass */ - $targetClass = $this->targetClass; - $query = $targetClass::find(); - $query->where([$this->targetAttribute => $value]); - return $query->exists() ? null : [$this->message, []]; + if ($exists) { + $this->addError($object, $attribute, $this->message); + } } } diff --git a/framework/yii/validators/EmailValidator.php b/framework/validators/UrlValidator.php similarity index 55% rename from framework/yii/validators/EmailValidator.php rename to framework/validators/UrlValidator.php index e5d9b75..4cb20f6 100644 --- a/framework/yii/validators/EmailValidator.php +++ b/framework/validators/UrlValidator.php @@ -14,40 +14,38 @@ use yii\web\JsExpression; use yii\helpers\Json; /** - * EmailValidator validates that the attribute value is a valid email address. + * UrlValidator validates that the attribute value is a valid http or https URL. + * + * Note that this validator only checks if the URL scheme and host part are correct. + * It does not check the rest part of a URL. * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ -class EmailValidator extends Validator +class UrlValidator extends Validator { /** * @var string the regular expression used to validate the attribute value. - * @see http://www.regular-expressions.info/email.html - */ - public $pattern = '/^[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/'; - /** - * @var string the regular expression used to validate email addresses with the name part. - * This property is used only when [[allowName]] is true. - * @see allowName + * The pattern may contain a `{schemes}` token that will be replaced + * by a regular expression which represents the [[validSchemes]]. */ - public $fullPattern = '/^[^@]*<[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&\'*+\\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?>$/'; + public $pattern = '/^{schemes}:\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)/i'; /** - * @var boolean whether to allow name in the email address (e.g. "John Smith <john.smith@example.com>"). Defaults to false. - * @see fullPattern - */ - public $allowName = false; + * @var array list of URI schemes which should be considered valid. By default, http and https + * are considered to be valid schemes. + **/ + public $validSchemes = ['http', 'https']; /** - * @var boolean whether to check whether the emails domain exists and has either an A or MX record. - * Be aware of the fact that this check can fail due to temporary DNS problems even if the email address is - * valid and an email would be deliverable. Defaults to false. - */ - public $checkDNS = false; + * @var string the default URI scheme. If the input doesn't contain the scheme part, the default + * scheme will be prepended to it (thus changing the input). Defaults to null, meaning a URL must + * contain the scheme part. + **/ + public $defaultScheme; /** - * @var boolean whether validation process should take into account IDN (internationalized domain - * names). Defaults to false meaning that validation of emails containing IDN will always fail. - * Note that in order to use IDN validation you have to install and enable `intl` PHP extension, - * otherwise an exception would be thrown. + * @var boolean whether validation process should take into account IDN (internationalized + * domain names). Defaults to false meaning that validation of URLs containing IDN will always + * fail. Note that in order to use IDN validation you have to install and enable `intl` PHP + * extension, otherwise an exception would be thrown. */ public $enableIDN = false; @@ -62,7 +60,21 @@ class EmailValidator extends Validator throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.'); } if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} is not a valid email address.'); + $this->message = Yii::t('yii', '{attribute} is not a valid URL.'); + } + } + + /** + * @inheritdoc + */ + public function validateAttribute($object, $attribute) + { + $value = $object->$attribute; + $result = $this->validateValue($value); + if (!empty($result)) { + $this->addError($object, $attribute, $result[0], $result[1]); + } elseif ($this->defaultScheme !== null && strpos($value, '://') === false) { + $object->$attribute = $this->defaultScheme . '://' . $value; } } @@ -71,22 +83,29 @@ class EmailValidator extends Validator */ protected function validateValue($value) { - // make sure string length is limited to avoid DOS attacks - if (!is_string($value) || strlen($value) >= 320) { - $valid = false; - } elseif (!preg_match('/^(.*<?)(.*)@(.*)(>?)$/', $value, $matches)) { - $valid = false; - } else { - $domain = $matches[3]; + // make sure the length is limited to avoid DOS attacks + if (is_string($value) && strlen($value) < 2000) { + if ($this->defaultScheme !== null && strpos($value, '://') === false) { + $value = $this->defaultScheme . '://' . $value; + } + + if (strpos($this->pattern, '{schemes}') !== false) { + $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern); + } else { + $pattern = $this->pattern; + } + if ($this->enableIDN) { - $value = $matches[1] . idn_to_ascii($matches[2]) . '@' . idn_to_ascii($domain) . $matches[4]; + $value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) { + return '://' . idn_to_ascii($matches[1]); + }, $value); } - $valid = preg_match($this->pattern, $value) || $this->allowName && preg_match($this->fullPattern, $value); - if ($valid && $this->checkDNS) { - $valid = checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A'); + + if (preg_match($pattern, $value)) { + return null; } } - return $valid ? null : [$this->message, []]; + return [$this->message, []]; } /** @@ -94,10 +113,14 @@ class EmailValidator extends Validator */ public function clientValidateAttribute($object, $attribute, $view) { + if (strpos($this->pattern, '{schemes}') !== false) { + $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern); + } else { + $pattern = $this->pattern; + } + $options = [ - 'pattern' => new JsExpression($this->pattern), - 'fullPattern' => new JsExpression($this->fullPattern), - 'allowName' => $this->allowName, + 'pattern' => new JsExpression($pattern), 'message' => strtr($this->message, [ '{attribute}' => $object->getAttributeLabel($attribute), ]), @@ -106,11 +129,14 @@ class EmailValidator extends Validator if ($this->skipOnEmpty) { $options['skipOnEmpty'] = 1; } + if ($this->defaultScheme !== null) { + $options['defaultScheme'] = $this->defaultScheme; + } ValidationAsset::register($view); if ($this->enableIDN) { PunycodeAsset::register($view); } - return 'yii.validation.email(value, messages, ' . Json::encode($options) . ');'; + return 'yii.validation.url(value, messages, ' . Json::encode($options) . ');'; } } diff --git a/framework/yii/validators/ValidationAsset.php b/framework/validators/ValidationAsset.php similarity index 100% rename from framework/yii/validators/ValidationAsset.php rename to framework/validators/ValidationAsset.php diff --git a/framework/yii/validators/Validator.php b/framework/validators/Validator.php similarity index 100% rename from framework/yii/validators/Validator.php rename to framework/validators/Validator.php diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/views/errorHandler/callStackItem.php similarity index 100% rename from framework/yii/views/errorHandler/callStackItem.php rename to framework/views/errorHandler/callStackItem.php diff --git a/framework/views/errorHandler/error.php b/framework/views/errorHandler/error.php new file mode 100644 index 0000000..066d7e4 --- /dev/null +++ b/framework/views/errorHandler/error.php @@ -0,0 +1,86 @@ +<?php +/** + * @var \Exception $exception + * @var \yii\base\ErrorHandler $handler + */ +if ($exception instanceof \yii\web\HttpException) { + $code = $exception->statusCode; +} else { + $code = $exception->getCode(); +} +if ($exception instanceof \yii\base\Exception) { + $name = $exception->getName(); +} else { + $name = 'Error'; +} +if ($code) { + $name .= " (#$code)"; +} + +if ($exception instanceof \yii\base\UserException) { + $message = $exception->getMessage(); +} else { + $message = 'An internal server error occurred.'; +} +?> +<?php if (method_exists($this, 'beginPage')) $this->beginPage(); ?> +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title><?= $handler->htmlEncode($name) ?></title> + + <style> + body { + font: normal 9pt "Verdana"; + color: #000; + background: #fff; + } + + h1 { + font: normal 18pt "Verdana"; + color: #f00; + margin-bottom: .5em; + } + + h2 { + font: normal 14pt "Verdana"; + color: #800000; + margin-bottom: .5em; + } + + h3 { + font: bold 11pt "Verdana"; + } + + p { + font: normal 9pt "Verdana"; + color: #000; + } + + .version { + color: gray; + font-size: 8pt; + border-top: 1px solid #aaa; + padding-top: 1em; + margin-bottom: 1em; + } + </style> +</head> + +<body> + <h1><?= $handler->htmlEncode($name) ?></h1> + <h2><?= nl2br($handler->htmlEncode($message)) ?></h2> + <p> + The above error occurred while the Web server was processing your request. + </p> + <p> + Please contact us if you think this is a server error. Thank you. + </p> + <div class="version"> + <?= date('Y-m-d H:i:s', time()) ?> + </div> + <?php if (method_exists($this, 'endBody')) $this->endBody(); // to allow injecting code into body (mostly by Yii Debug Toolbar) ?> +</body> +</html> +<?php if (method_exists($this, 'endPage')) $this->endPage(); ?> diff --git a/framework/yii/views/errorHandler/exception.php b/framework/views/errorHandler/exception.php similarity index 100% rename from framework/yii/views/errorHandler/exception.php rename to framework/views/errorHandler/exception.php diff --git a/framework/yii/views/errorHandler/previousException.php b/framework/views/errorHandler/previousException.php similarity index 100% rename from framework/yii/views/errorHandler/previousException.php rename to framework/views/errorHandler/previousException.php diff --git a/framework/views/messageConfig.php b/framework/views/messageConfig.php new file mode 100644 index 0000000..9babb0c --- /dev/null +++ b/framework/views/messageConfig.php @@ -0,0 +1,45 @@ +<?php + +return [ + // string, required, root directory of all source files + 'sourcePath' => __DIR__, + // string, required, root directory containing message translations. + 'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', + // array, required, list of language codes that the extracted messages + // should be translated to. For example, ['zh_cn', 'de']. + 'languages' => ['de'], + // string, the name of the function for translating messages. + // Defaults to 'Yii::t'. This is used as a mark to find the messages to be + // translated. You may use a string for single function name or an array for + // multiple function names. + 'translator' => 'Yii::t', + // boolean, whether to sort messages by keys when merging new messages + // with the existing ones. Defaults to false, which means the new (untranslated) + // messages will be separated from the old (translated) ones. + 'sort' => false, + // boolean, whether the message file should be overwritten with the merged messages + 'overwrite' => true, + // boolean, whether to remove messages that no longer appear in the source code. + // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks. + 'removeUnused' => false, + // array, list of patterns that specify which files/directories should be processed. + // If empty or not set, all files/directories will be processed. + // A path matches a pattern if it contains the pattern string at its end. For example, + // '/a/b' will match all files and directories ending with '/a/b'; + // and the '.svn' will match all files and directories whose name ends with '.svn'. + // Note, the '/' characters in a pattern matches both '/' and '\'. + // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. + 'only' => ['.php'], + // array, list of patterns that specify which files/directories should NOT be processed. + // If empty or not set, all files/directories will be processed. + // Please refer to "only" for details about the patterns. + 'except' => [ + '.svn', + '.git', + '.gitignore', + '.gitkeep', + '.hgignore', + '.hgkeep', + '/messages', + ], +]; diff --git a/framework/yii/views/migration.php b/framework/views/migration.php similarity index 100% rename from framework/yii/views/migration.php rename to framework/views/migration.php diff --git a/framework/web/AccessControl.php b/framework/web/AccessControl.php new file mode 100644 index 0000000..e755e80 --- /dev/null +++ b/framework/web/AccessControl.php @@ -0,0 +1,144 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Action; +use yii\base\ActionFilter; + +/** + * AccessControl provides simple access control based on a set of rules. + * + * AccessControl is an action filter. It will check its [[rules]] to find + * the first rule that matches the current context variables (such as user IP address, user role). + * The matching rule will dictate whether to allow or deny the access to the requested controller + * action. If no rule matches, the access will be denied. + * + * To use AccessControl, declare it in the `behaviors()` method of your controller class. + * For example, the following declarations will allow authenticated users to access the "create" + * and "update" actions and deny all other users from accessing these two actions. + * + * ~~~ + * public function behaviors() + * { + * return [ + * 'access' => [ + * 'class' => \yii\web\AccessControl::className(), + * 'only' => ['create', 'update'], + * 'rules' => [ + * // deny all POST requests + * [ + * 'allow' => false, + * 'verbs' => ['POST'] + * ], + * // allow authenticated users + * [ + * 'allow' => true, + * 'roles' => ['@'], + * ], + * // everything else is denied + * ], + * ], + * ]; + * } + * ~~~ + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class AccessControl extends ActionFilter +{ + /** + * @var callback a callback that will be called if the access should be denied + * to the current user. If not set, [[denyAccess()]] will be called. + * + * The signature of the callback should be as follows: + * + * ~~~ + * function ($rule, $action) + * ~~~ + * + * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. + */ + public $denyCallback; + /** + * @var array the default configuration of access rules. Individual rule configurations + * specified via [[rules]] will take precedence when the same property of the rule is configured. + */ + public $ruleConfig = ['class' => 'yii\web\AccessRule']; + /** + * @var array a list of access rule objects or configuration arrays for creating the rule objects. + * If a rule is specified via a configuration array, it will be merged with [[ruleConfig]] first + * before it is used for creating the rule object. + * @see ruleConfig + */ + public $rules = []; + + /** + * Initializes the [[rules]] array by instantiating rule objects from configurations. + */ + public function init() + { + parent::init(); + foreach ($this->rules as $i => $rule) { + if (is_array($rule)) { + $this->rules[$i] = Yii::createObject(array_merge($this->ruleConfig, $rule)); + } + } + } + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param Action $action the action to be executed. + * @return boolean whether the action should continue to be executed. + */ + public function beforeAction($action) + { + $user = Yii::$app->getUser(); + $request = Yii::$app->getRequest(); + /** @var AccessRule $rule */ + foreach ($this->rules as $rule) { + if ($allow = $rule->allows($action, $user, $request)) { + return true; + } elseif ($allow === false) { + if (isset($rule->denyCallback)) { + call_user_func($rule->denyCallback, $rule); + } elseif (isset($this->denyCallback)) { + call_user_func($this->denyCallback, $rule); + } else { + $this->denyAccess($user); + } + return false; + } + } + if (isset($this->denyCallback)) { + call_user_func($this->denyCallback, $rule); + } + else { + $this->denyAccess($user); + } + return false; + } + + /** + * Denies the access of the user. + * The default implementation will redirect the user to the login page if he is a guest; + * if the user is already logged, a 403 HTTP exception will be thrown. + * @param User $user the current user + * @throws AccessDeniedHttpException if the user is already logged in. + */ + protected function denyAccess($user) + { + if ($user->getIsGuest()) { + $user->loginRequired(); + } else { + throw new AccessDeniedHttpException(Yii::t('yii', 'You are not allowed to perform this action.')); + } + } +} diff --git a/framework/yii/web/AccessDeniedHttpException.php b/framework/web/AccessDeniedHttpException.php similarity index 100% rename from framework/yii/web/AccessDeniedHttpException.php rename to framework/web/AccessDeniedHttpException.php diff --git a/framework/web/AccessRule.php b/framework/web/AccessRule.php new file mode 100644 index 0000000..7aeaac1 --- /dev/null +++ b/framework/web/AccessRule.php @@ -0,0 +1,186 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\Component; +use yii\base\Action; + +/** + * This class represents an access rule defined by the [[AccessControl]] action filter + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class AccessRule extends Component +{ + /** + * @var boolean whether this is an 'allow' rule or 'deny' rule. + */ + public $allow; + /** + * @var array list of action IDs that this rule applies to. The comparison is case-sensitive. + * If not set or empty, it means this rule applies to all actions. + */ + public $actions; + /** + * @var array list of controller IDs that this rule applies to. The comparison is case-sensitive. + * If not set or empty, it means this rule applies to all controllers. + */ + public $controllers; + /** + * @var array list of roles that this rule applies to. Two special roles are recognized, and + * they are checked via [[User::isGuest]]: + * + * - `?`: matches a guest user (not authenticated yet) + * - `@`: matches an authenticated user + * + * Using additional role names requires RBAC (Role-Based Access Control), and + * [[User::checkAccess()]] will be called. + * + * If this property is not set or empty, it means this rule applies to all roles. + */ + public $roles; + /** + * @var array list of user IP addresses that this rule applies to. An IP address + * can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix. + * For example, '192.168.*' matches all IP addresses in the segment '192.168.'. + * If not set or empty, it means this rule applies to all IP addresses. + * @see Request::userIP + */ + public $ips; + /** + * @var array list of request methods (e.g. `GET`, `POST`) that this rule applies to. + * The request methods must be specified in uppercase. + * If not set or empty, it means this rule applies to all request methods. + * @see Request::requestMethod + */ + public $verbs; + /** + * @var callback a callback that will be called to determine if the rule should be applied. + * The signature of the callback should be as follows: + * + * ~~~ + * function ($rule, $action) + * ~~~ + * + * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. + * The callback should return a boolean value indicating whether this rule should be applied. + */ + public $matchCallback; + /** + * @var callback a callback that will be called if this rule determines the access to + * the current action should be denied. If not set, the behavior will be determined by + * [[AccessControl]]. + * + * The signature of the callback should be as follows: + * + * ~~~ + * function ($rule, $action) + * ~~~ + * + * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. + */ + public $denyCallback; + + + /** + * Checks whether the Web user is allowed to perform the specified action. + * @param Action $action the action to be performed + * @param User $user the user object + * @param Request $request + * @return boolean|null true if the user is allowed, false if the user is denied, null if the rule does not apply to the user + */ + public function allows($action, $user, $request) + { + if ($this->matchAction($action) + && $this->matchRole($user) + && $this->matchIP($request->getUserIP()) + && $this->matchVerb($request->getMethod()) + && $this->matchController($action->controller) + && $this->matchCustom($action) + ) { + return $this->allow ? true : false; + } else { + return null; + } + } + + /** + * @param Action $action the action + * @return boolean whether the rule applies to the action + */ + protected function matchAction($action) + { + return empty($this->actions) || in_array($action->id, $this->actions, true); + } + + /** + * @param Controller $controller the controller + * @return boolean whether the rule applies to the controller + */ + protected function matchController($controller) + { + return empty($this->controllers) || in_array($controller->uniqueId, $this->controllers, true); + } + + /** + * @param User $user the user object + * @return boolean whether the rule applies to the role + */ + protected function matchRole($user) + { + if (empty($this->roles)) { + return true; + } + foreach ($this->roles as $role) { + if ($role === '?' && $user->getIsGuest()) { + return true; + } elseif ($role === '@' && !$user->getIsGuest()) { + return true; + } elseif ($user->checkAccess($role)) { + return true; + } + } + return false; + } + + /** + * @param string $ip the IP address + * @return boolean whether the rule applies to the IP address + */ + protected function matchIP($ip) + { + if (empty($this->ips)) { + return true; + } + foreach ($this->ips as $rule) { + if ($rule === '*' || $rule === $ip || (($pos = strpos($rule, '*')) !== false && !strncmp($ip, $rule, $pos))) { + return true; + } + } + return false; + } + + /** + * @param string $verb the request method + * @return boolean whether the rule applies to the request + */ + protected function matchVerb($verb) + { + return empty($this->verbs) || in_array($verb, $this->verbs, true); + } + + /** + * @param Action $action the action to be performed + * @return boolean whether the rule should be applied + */ + protected function matchCustom($action) + { + return empty($this->matchCallback) || call_user_func($this->matchCallback, $this, $action); + } +} diff --git a/framework/web/Application.php b/framework/web/Application.php new file mode 100644 index 0000000..17c1411 --- /dev/null +++ b/framework/web/Application.php @@ -0,0 +1,178 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\InvalidRouteException; + +/** + * Application is the base class for all web application classes. + * + * @property AssetManager $assetManager The asset manager component. This property is read-only. + * @property string $homeUrl The homepage URL. + * @property Request $request The request component. This property is read-only. + * @property Response $response The response component. This property is read-only. + * @property Session $session The session component. This property is read-only. + * @property User $user The user component. This property is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class Application extends \yii\base\Application +{ + /** + * @var string the default route of this application. Defaults to 'site'. + */ + public $defaultRoute = 'site'; + /** + * @var array the configuration specifying a controller action which should handle + * all user requests. This is mainly used when the application is in maintenance mode + * and needs to handle all incoming requests via a single action. + * The configuration is an array whose first element specifies the route of the action. + * The rest of the array elements (key-value pairs) specify the parameters to be bound + * to the action. For example, + * + * ~~~ + * [ + * 'offline/notice', + * 'param1' => 'value1', + * 'param2' => 'value2', + * ] + * ~~~ + * + * Defaults to null, meaning catch-all is not used. + */ + public $catchAll; + /** + * @var Controller the currently active controller instance + */ + public $controller; + + + /** + * Handles the specified request. + * @param Request $request the request to be handled + * @return Response the resulting response + * @throws NotFoundHttpException if the requested route is invalid + */ + public function handleRequest($request) + { + Yii::setAlias('@webroot', dirname($request->getScriptFile())); + Yii::setAlias('@web', $request->getBaseUrl()); + + if (empty($this->catchAll)) { + list ($route, $params) = $request->resolve(); + } else { + $route = $this->catchAll[0]; + $params = array_splice($this->catchAll, 1); + } + try { + Yii::trace("Route requested: '$route'", __METHOD__); + $this->requestedRoute = $route; + $result = $this->runAction($route, $params); + if ($result instanceof Response) { + return $result; + } else { + $response = $this->getResponse(); + if ($result !== null) { + $response->data = $result; + } + return $response; + } + } catch (InvalidRouteException $e) { + throw new NotFoundHttpException($e->getMessage(), $e->getCode(), $e); + } + } + + private $_homeUrl; + + /** + * @return string the homepage URL + */ + public function getHomeUrl() + { + if ($this->_homeUrl === null) { + if ($this->getUrlManager()->showScriptName) { + return $this->getRequest()->getScriptUrl(); + } else { + return $this->getRequest()->getBaseUrl() . '/'; + } + } else { + return $this->_homeUrl; + } + } + + /** + * @param string $value the homepage URL + */ + public function setHomeUrl($value) + { + $this->_homeUrl = $value; + } + + /** + * Returns the request component. + * @return Request the request component + */ + public function getRequest() + { + return $this->getComponent('request'); + } + + /** + * Returns the response component. + * @return Response the response component + */ + public function getResponse() + { + return $this->getComponent('response'); + } + + /** + * Returns the session component. + * @return Session the session component + */ + public function getSession() + { + return $this->getComponent('session'); + } + + /** + * Returns the user component. + * @return User the user component + */ + public function getUser() + { + return $this->getComponent('user'); + } + + /** + * Returns the asset manager. + * @return AssetManager the asset manager component + */ + public function getAssetManager() + { + return $this->getComponent('assetManager'); + } + + /** + * Registers the core application components. + * @see setComponents + */ + public function registerCoreComponents() + { + parent::registerCoreComponents(); + $this->setComponents([ + 'request' => ['class' => 'yii\web\Request'], + 'response' => ['class' => 'yii\web\Response'], + 'session' => ['class' => 'yii\web\Session'], + 'user' => ['class' => 'yii\web\User'], + 'assetManager' => ['class' => 'yii\web\AssetManager'], + ]); + } +} diff --git a/framework/web/AssetBundle.php b/framework/web/AssetBundle.php new file mode 100644 index 0000000..da34848 --- /dev/null +++ b/framework/web/AssetBundle.php @@ -0,0 +1,194 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\InvalidConfigException; +use yii\base\Object; +use yii\web\View; + +/** + * AssetBundle represents a collection of asset files, such as CSS, JS, images. + * + * Each asset bundle has a unique name that globally identifies it among all asset bundles used in an application. + * The name is the [fully qualified class name](http://php.net/manual/en/language.namespaces.rules.php) + * of the class representing it. + * + * An asset bundle can depend on other asset bundles. When registering an asset bundle + * with a view, all its dependent asset bundles will be automatically registered. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class AssetBundle extends Object +{ + /** + * @var string the root directory of the source asset files. A source asset file + * is a file that is part of your source code repository of your Web application. + * + * You must set this property if the directory containing the source asset files + * is not Web accessible (this is usually the case for extensions). + * + * By setting this property, the asset manager will publish the source asset files + * to a Web-accessible directory [[basePath]]. + * + * You can use either a directory or an alias of the directory. + */ + public $sourcePath; + /** + * @var string the Web-accessible directory that contains the asset files in this bundle. + * + * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] + * when it publishes the asset files from [[sourcePath]]. + * + * If the bundle contains any assets that are specified in terms of relative file path, + * then this property must be set either manually or automatically (by [[AssetManager]] via + * asset publishing). + * + * You can use either a directory or an alias of the directory. + */ + public $basePath; + /** + * @var string the base URL that will be prefixed to the asset files for them to + * be accessed via Web server. + * + * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] + * when it publishes the asset files from [[sourcePath]]. + * + * If the bundle contains any assets that are specified in terms of relative file path, + * then this property must be set either manually or automatically (by asset manager via + * asset publishing). + * + * You can use either a URL or an alias of the URL. + */ + public $baseUrl; + /** + * @var array list of bundle class names that this bundle depends on. + * + * For example: + * + * ```php + * public $depends = [ + * 'yii\web\YiiAsset', + * 'yii\bootstrap\BootstrapAsset', + * ]; + * ``` + */ + public $depends = []; + /** + * @var array list of JavaScript files that this bundle contains. Each JavaScript file can + * be either a file path (without leading slash) relative to [[basePath]] or a URL representing + * an external JavaScript file. + * + * Note that only forward slash "/" can be used as directory separator. + */ + public $js = []; + /** + * @var array list of CSS files that this bundle contains. Each CSS file can + * be either a file path (without leading slash) relative to [[basePath]] or a URL representing + * an external CSS file. + * + * Note that only forward slash "/" can be used as directory separator. + */ + public $css = []; + /** + * @var array the options that will be passed to [[\yii\web\View::registerJsFile()]] + * when registering the JS files in this bundle. + */ + public $jsOptions = []; + /** + * @var array the options that will be passed to [[\yii\web\View::registerCssFile()]] + * when registering the CSS files in this bundle. + */ + public $cssOptions = []; + /** + * @var array the options to be passed to [[AssetManager::publish()]] when the asset bundle + * is being published. + */ + public $publishOptions = []; + + /** + * @param View $view + * @return AssetBundle the registered asset bundle instance + */ + public static function register($view) + { + return $view->registerAssetBundle(get_called_class()); + } + + /** + * Initializes the bundle. + * If you override this method, make sure you call the parent implementation in the last. + */ + public function init() + { + if ($this->sourcePath !== null) { + $this->sourcePath = rtrim(Yii::getAlias($this->sourcePath), '/\\'); + } + if ($this->basePath !== null) { + $this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\'); + } + if ($this->baseUrl !== null) { + $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/'); + } + } + + /** + * Registers the CSS and JS files with the given view. + * @param \yii\web\View $view the view that the asset files are to be registered with. + */ + public function registerAssetFiles($view) + { + foreach ($this->js as $js) { + if (strpos($js, '/') !== 0 && strpos($js, '://') === false) { + $view->registerJsFile($this->baseUrl . '/' . $js, [], $this->jsOptions); + } else { + $view->registerJsFile($js, [], $this->jsOptions); + } + } + foreach ($this->css as $css) { + if (strpos($css, '/') !== 0 && strpos($css, '://') === false) { + $view->registerCssFile($this->baseUrl . '/' . $css, [], $this->cssOptions); + } else { + $view->registerCssFile($css, [], $this->cssOptions); + } + } + } + + /** + * Publishes the asset bundle if its source code is not under Web-accessible directory. + * It will also try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding + * CSS or JS files using [[AssetManager::converter|asset converter]]. + * @param AssetManager $am the asset manager to perform the asset publishing + */ + public function publish($am) + { + if ($this->sourcePath !== null && !isset($this->basePath, $this->baseUrl)) { + list ($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions); + } + $converter = $am->getConverter(); + foreach ($this->js as $i => $js) { + if (strpos($js, '/') !== 0 && strpos($js, '://') === false) { + if (isset($this->basePath, $this->baseUrl)) { + $this->js[$i] = $converter->convert($js, $this->basePath, $this->baseUrl); + } else { + $this->js[$i] = '/' . $js; + } + } + } + foreach ($this->css as $i => $css) { + if (strpos($css, '/') !== 0 && strpos($css, '://') === false) { + if (isset($this->basePath, $this->baseUrl)) { + $this->css[$i] = $converter->convert($css, $this->basePath, $this->baseUrl); + } else { + $this->css[$i] = '/' . $css; + } + } + } + } +} diff --git a/framework/web/AssetConverter.php b/framework/web/AssetConverter.php new file mode 100644 index 0000000..1b7d1c8 --- /dev/null +++ b/framework/web/AssetConverter.php @@ -0,0 +1,99 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Component; +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 <qiang.xue@gmail.com> + * @since 2.0 + */ +class AssetConverter extends Component implements AssetConverterInterface +{ + /** + * @var array the commands that are used to perform the asset conversion. + * The keys are the asset file extension names, and the values are the corresponding + * target script types (either "css" or "js") and the commands used for the conversion. + */ + public $commands = [ + 'less' => ['css', 'lessc {from} {to} --no-color'], + 'scss' => ['css', 'sass {from} {to}'], + 'sass' => ['css', 'sass {from} {to}'], + 'styl' => ['js', 'stylus < {from} > {to}'], + 'coffee' => ['js', 'coffee -p {from} > {to}'], + 'ts' => ['js', 'tsc --out {to} {from}'], + ]; + + /** + * Converts a given asset file into a CSS or JS file. + * @param string $asset the asset file path, relative to $basePath + * @param string $basePath the directory the $asset is relative to. + * @return string the converted asset file path, relative to $basePath. + */ + public function convert($asset, $basePath) + { + $pos = strrpos($asset, '.'); + if ($pos !== false) { + $ext = substr($asset, $pos + 1); + if (isset($this->commands[$ext])) { + list ($ext, $command) = $this->commands[$ext]; + $result = substr($asset, 0, $pos + 1) . $ext; + if (@filemtime("$basePath/$result") < filemtime("$basePath/$asset")) { + $this->runCommand($command, $basePath, $asset, $result); + } + return $result; + } + } + return $asset; + } + + /** + * Runs a command to convert asset files. + * @param string $command the command to run + * @param string $basePath asset base path and command working directory + * @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) + { + $command = strtr($command, [ + '{from}' => escapeshellarg("$basePath/$asset"), + '{to}' => escapeshellarg("$basePath/$result"), + ]); + $descriptor = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $pipes = []; + $proc = proc_open($command, $descriptor, $pipes, $basePath); + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + foreach($pipes as $pipe) { + fclose($pipe); + } + $status = proc_close($proc); + + if ($status === 0) { + Yii::trace("Converted $asset into $result:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__); + } elseif (YII_DEBUG) { + throw new Exception("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr"); + } else { + Yii::error("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr"); + } + return $status === 0; + } +} diff --git a/framework/yii/web/AssetConverterInterface.php b/framework/web/AssetConverterInterface.php similarity index 100% rename from framework/yii/web/AssetConverterInterface.php rename to framework/web/AssetConverterInterface.php diff --git a/framework/yii/web/AssetManager.php b/framework/web/AssetManager.php similarity index 100% rename from framework/yii/web/AssetManager.php rename to framework/web/AssetManager.php diff --git a/framework/yii/web/BadRequestHttpException.php b/framework/web/BadRequestHttpException.php similarity index 100% rename from framework/yii/web/BadRequestHttpException.php rename to framework/web/BadRequestHttpException.php diff --git a/framework/web/CacheSession.php b/framework/web/CacheSession.php new file mode 100644 index 0000000..7b4a98d --- /dev/null +++ b/framework/web/CacheSession.php @@ -0,0 +1,118 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\caching\Cache; +use yii\base\InvalidConfigException; + +/** + * CacheSession implements a session component using cache as storage medium. + * + * The cache being used can be any cache application component. + * The ID of the cache application component is specified via [[cache]], which defaults to 'cache'. + * + * 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, [[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. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class CacheSession extends Session +{ + /** + * @var Cache|string the cache object or the application component ID of the cache object. + * The session data will be stored using this cache object. + * + * After the CacheSession object is created, if you want to change this property, + * you should only assign it with a cache object. + */ + public $cache = 'cache'; + + /** + * Initializes the application component. + */ + public function init() + { + if (is_string($this->cache)) { + $this->cache = Yii::$app->getComponent($this->cache); + } + if (!$this->cache instanceof Cache) { + throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.'); + } + parent::init(); + } + + /** + * Returns a value indicating whether to use custom session storage. + * This method overrides the parent implementation and always returns true. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return true; + } + + /** + * Session read handler. + * Do not call this method directly. + * @param string $id session ID + * @return string the session data + */ + public function readSession($id) + { + $data = $this->cache->get($this->calculateKey($id)); + return $data === false ? '' : $data; + } + + /** + * Session write handler. + * Do not call this method directly. + * @param string $id session ID + * @param string $data session data + * @return boolean whether session write is successful + */ + public function writeSession($id, $data) + { + return $this->cache->set($this->calculateKey($id), $data, $this->getTimeout()); + } + + /** + * Session destroy handler. + * Do not call this method directly. + * @param string $id session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + return $this->cache->delete($this->calculateKey($id)); + } + + /** + * Generates a unique key used for storing session data in cache. + * @param string $id session variable name + * @return mixed a safe cache key associated with the session variable name + */ + protected function calculateKey($id) + { + return [__CLASS__, $id]; + } +} diff --git a/framework/yii/web/Controller.php b/framework/web/Controller.php similarity index 100% rename from framework/yii/web/Controller.php rename to framework/web/Controller.php diff --git a/framework/web/Cookie.php b/framework/web/Cookie.php new file mode 100644 index 0000000..8cbb412 --- /dev/null +++ b/framework/web/Cookie.php @@ -0,0 +1,65 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +/** + * Cookie represents information related with a cookie, such as [[name]], [[value]], [[domain]], etc. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class Cookie extends \yii\base\Object +{ + /** + * @var string name of the cookie + */ + public $name; + /** + * @var string value of the cookie + */ + public $value = ''; + /** + * @var string domain of the cookie + */ + public $domain = ''; + /** + * @var integer the timestamp at which the cookie expires. This is the server timestamp. + * Defaults to 0, meaning "until the browser is closed". + */ + public $expire = 0; + /** + * @var string the path on the server in which the cookie will be available on. The default is '/'. + */ + public $path = '/'; + /** + * @var boolean whether cookie should be sent via secure connection + */ + public $secure = false; + /** + * @var boolean whether the cookie should be accessible only through the HTTP protocol. + * By setting this property to true, the cookie will not be accessible by scripting languages, + * such as JavaScript, which can effectively help to reduce identity theft through XSS attacks. + */ + public $httpOnly = false; + + /** + * Magic method to turn a cookie object into a string without having to explicitly access [[value]]. + * + * ~~~ + * if (isset($request->cookies['name'])) { + * $value = (string)$request->cookies['name']; + * } + * ~~~ + * + * @return string The value of the cookie. If the value property is null, an empty string will be returned. + */ + public function __toString() + { + return (string)$this->value; + } +} diff --git a/framework/web/CookieCollection.php b/framework/web/CookieCollection.php new file mode 100644 index 0000000..3cf80ff --- /dev/null +++ b/framework/web/CookieCollection.php @@ -0,0 +1,228 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use ArrayIterator; +use yii\base\InvalidCallException; +use yii\base\Object; + +/** + * CookieCollection maintains the cookies available in the current request. + * + * @property integer $count The number of cookies in the collection. This property is read-only. + * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. This property + * is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable +{ + /** + * @var boolean whether this collection is read only. + */ + public $readOnly = false; + + /** + * @var Cookie[] the cookies in this collection (indexed by the cookie names) + */ + private $_cookies = []; + + /** + * Constructor. + * @param array $cookies the cookies that this collection initially contains. This should be + * an array of name-value pairs.s + * @param array $config name-value pairs that will be used to initialize the object properties + */ + public function __construct($cookies = [], $config = []) + { + $this->_cookies = $cookies; + parent::__construct($config); + } + + /** + * Returns an iterator for traversing the cookies in the collection. + * This method is required by the SPL interface `IteratorAggregate`. + * It will be implicitly called when you use `foreach` to traverse the collection. + * @return ArrayIterator an iterator for traversing the cookies in the collection. + */ + public function getIterator() + { + return new ArrayIterator($this->_cookies); + } + + /** + * Returns the number of cookies in the collection. + * This method is required by the SPL `Countable` interface. + * It will be implicitly called when you use `count($collection)`. + * @return integer the number of cookies in the collection. + */ + public function count() + { + return $this->getCount(); + } + + /** + * Returns the number of cookies in the collection. + * @return integer the number of cookies in the collection. + */ + public function getCount() + { + return count($this->_cookies); + } + + /** + * Returns the cookie with the specified name. + * @param string $name the cookie name + * @return Cookie the cookie with the specified name. Null if the named cookie does not exist. + * @see getValue() + */ + public function get($name) + { + return isset($this->_cookies[$name]) ? $this->_cookies[$name] : null; + } + + /** + * Returns the value of the named cookie. + * @param string $name the cookie name + * @param mixed $defaultValue the value that should be returned when the named cookie does not exist. + * @return mixed the value of the named cookie. + * @see get() + */ + public function getValue($name, $defaultValue = null) + { + return isset($this->_cookies[$name]) ? $this->_cookies[$name]->value : $defaultValue; + } + + /** + * Returns whether there is a cookie with the specified name. + * @param string $name the cookie name + * @return boolean whether the named cookie exists + */ + public function has($name) + { + return isset($this->_cookies[$name]); + } + + /** + * Adds a cookie to the collection. + * If there is already a cookie with the same name in the collection, it will be removed first. + * @param Cookie $cookie the cookie to be added + * @throws InvalidCallException if the cookie collection is read only + */ + public function add($cookie) + { + if ($this->readOnly) { + throw new InvalidCallException('The cookie collection is read only.'); + } + $this->_cookies[$cookie->name] = $cookie; + } + + /** + * Removes a cookie. + * If `$removeFromBrowser` is true, the cookie will be removed from the browser. + * In this case, a cookie with outdated expiry will be added to the collection. + * @param Cookie|string $cookie the cookie object or the name of the cookie to be removed. + * @param boolean $removeFromBrowser whether to remove the cookie from browser + * @throws InvalidCallException if the cookie collection is read only + */ + public function remove($cookie, $removeFromBrowser = true) + { + if ($this->readOnly) { + throw new InvalidCallException('The cookie collection is read only.'); + } + if ($cookie instanceof Cookie) { + $cookie->expire = 1; + $cookie->value = ''; + } else { + $cookie = new Cookie([ + 'name' => $cookie, + 'expire' => 1, + ]); + } + if ($removeFromBrowser) { + $this->_cookies[$cookie->name] = $cookie; + } else { + unset($this->_cookies[$cookie->name]); + } + } + + /** + * Removes all cookies. + * @throws InvalidCallException if the cookie collection is read only + */ + public function removeAll() + { + if ($this->readOnly) { + throw new InvalidCallException('The cookie collection is read only.'); + } + $this->_cookies = []; + } + + /** + * Returns the collection as a PHP array. + * @return array the array representation of the collection. + * The array keys are cookie names, and the array values are the corresponding + * cookie objects. + */ + public function toArray() + { + return $this->_cookies; + } + + /** + * Returns whether there is a cookie with the specified name. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `isset($collection[$name])`. + * @param string $name the cookie name + * @return boolean whether the named cookie exists + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Returns the cookie with the specified name. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `$cookie = $collection[$name];`. + * This is equivalent to [[get()]]. + * @param string $name the cookie name + * @return Cookie the cookie with the specified name, null if the named cookie does not exist. + */ + public function offsetGet($name) + { + return $this->get($name); + } + + /** + * Adds the cookie to the collection. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `$collection[$name] = $cookie;`. + * This is equivalent to [[add()]]. + * @param string $name the cookie name + * @param Cookie $cookie the cookie to be added + */ + public function offsetSet($name, $cookie) + { + $this->add($cookie); + } + + /** + * Removes the named cookie. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `unset($collection[$name])`. + * This is equivalent to [[remove()]]. + * @param string $name the cookie name + */ + public function offsetUnset($name) + { + $this->remove($name); + } +} diff --git a/framework/web/DbSession.php b/framework/web/DbSession.php new file mode 100644 index 0000000..2b0b0e7 --- /dev/null +++ b/framework/web/DbSession.php @@ -0,0 +1,224 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\db\Connection; +use yii\db\Query; +use yii\base\InvalidConfigException; + +/** + * DbSession extends [[Session]] by using database as session data storage. + * + * By default, DbSession stores session data in a DB table named 'tbl_session'. This table + * must be pre-created. The table name can be changed by setting [[sessionTable]]. + * + * The following example shows how you can configure the application to use DbSession: + * Add the following to your application config under `components`: + * + * ~~~ + * 'session' => [ + * 'class' => 'yii\web\DbSession', + * // 'db' => 'mydb', + * // 'sessionTable' => 'my_session', + * ] + * ~~~ + * + * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class DbSession extends Session +{ + /** + * @var Connection|string the DB connection object or the application component ID of the DB connection. + * After the DbSession object is created, if you want to change this property, you should only assign it + * with a DB connection object. + */ + public $db = 'db'; + /** + * @var string the name of the DB table that stores the session data. + * The table should be pre-created as follows: + * + * ~~~ + * CREATE TABLE tbl_session + * ( + * id CHAR(40) NOT NULL PRIMARY KEY, + * expire INTEGER, + * data BLOB + * ) + * ~~~ + * + * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type + * that can be used for some popular DBMS: + * + * - MySQL: LONGBLOB + * - PostgreSQL: BYTEA + * - MSSQL: BLOB + * + * When using DbSession in a production server, we recommend you create a DB index for the 'expire' + * column in the session table to improve the performance. + */ + public $sessionTable = '{{%session}}'; + + /** + * Initializes the DbSession component. + * This method will initialize the [[db]] property to make sure it refers to a valid DB connection. + * @throws InvalidConfigException if [[db]] is invalid. + */ + public function init() + { + if (is_string($this->db)) { + $this->db = Yii::$app->getComponent($this->db); + } + if (!$this->db instanceof Connection) { + throw new InvalidConfigException("DbSession::db must be either a DB connection instance or the application component ID of a DB connection."); + } + parent::init(); + } + + /** + * Returns a value indicating whether to use custom session storage. + * This method overrides the parent implementation and always returns true. + * @return boolean whether to use custom storage. + */ + public function getUseCustomStorage() + { + return true; + } + + /** + * Updates the current session ID with a newly generated one . + * Please refer to <http://php.net/session_regenerate_id> for more details. + * @param boolean $deleteOldSession Whether to delete the old associated session file or not. + */ + public function regenerateID($deleteOldSession = false) + { + $oldID = session_id(); + + // if no session is started, there is nothing to regenerate + if (empty($oldID)) { + return; + } + + parent::regenerateID(false); + $newID = session_id(); + + $query = new Query; + $row = $query->from($this->sessionTable) + ->where(['id' => $oldID]) + ->createCommand($this->db) + ->queryOne(); + if ($row !== false) { + if ($deleteOldSession) { + $this->db->createCommand() + ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID]) + ->execute(); + } else { + $row['id'] = $newID; + $this->db->createCommand() + ->insert($this->sessionTable, $row) + ->execute(); + } + } else { + // shouldn't reach here normally + $this->db->createCommand() + ->insert($this->sessionTable, [ + 'id' => $newID, + 'expire' => time() + $this->getTimeout(), + ])->execute(); + } + } + + /** + * Session read handler. + * Do not call this method directly. + * @param string $id session ID + * @return string the session data + */ + public function readSession($id) + { + $query = new Query; + $data = $query->select(['data']) + ->from($this->sessionTable) + ->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id]) + ->createCommand($this->db) + ->queryScalar(); + return $data === false ? '' : $data; + } + + /** + * Session write handler. + * Do not call this method directly. + * @param string $id session ID + * @param string $data session data + * @return boolean whether session write is successful + */ + public function writeSession($id, $data) + { + // exception must be caught in session write handler + // http://us.php.net/manual/en/function.session-set-save-handler.php + try { + $expire = time() + $this->getTimeout(); + $query = new Query; + $exists = $query->select(['id']) + ->from($this->sessionTable) + ->where(['id' => $id]) + ->createCommand($this->db) + ->queryScalar(); + if ($exists === false) { + $this->db->createCommand() + ->insert($this->sessionTable, [ + 'id' => $id, + 'data' => $data, + 'expire' => $expire, + ])->execute(); + } else { + $this->db->createCommand() + ->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id]) + ->execute(); + } + } catch (\Exception $e) { + if (YII_DEBUG) { + echo $e->getMessage(); + } + // it is too late to log an error message here + return false; + } + return true; + } + + /** + * Session destroy handler. + * Do not call this method directly. + * @param string $id session ID + * @return boolean whether session is destroyed successfully + */ + public function destroySession($id) + { + $this->db->createCommand() + ->delete($this->sessionTable, ['id' => $id]) + ->execute(); + return true; + } + + /** + * Session GC (garbage collection) handler. + * Do not call this method directly. + * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. + * @return boolean whether session is GCed successfully + */ + public function gcSession($maxLifetime) + { + $this->db->createCommand() + ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()]) + ->execute(); + return true; + } +} diff --git a/framework/web/ErrorAction.php b/framework/web/ErrorAction.php new file mode 100644 index 0000000..95f17be --- /dev/null +++ b/framework/web/ErrorAction.php @@ -0,0 +1,106 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Action; +use yii\base\Exception; +use yii\base\UserException; + +/** + * ErrorAction displays application errors using a specified view. + * + * To use ErrorAction, you need to do the following steps: + * + * First, declare an action of ErrorAction type in the `actions()` method of your `SiteController` + * class (or whatever controller you prefer), like the following: + * + * ```php + * public function actions() + * { + * return [ + * 'error' => ['class' => 'yii\web\ErrorAction'], + * ]; + * } + * ``` + * + * Then, create a view file for this action. If the route of your error action is `site/error`, then + * the view file should be `views/site/error.php`. In this view file, the following variables are available: + * + * - `$name`: the error name + * - `$message`: the error message + * - `$exception`: the exception being handled + * + * Finally, configure the "errorHandler" application component as follows, + * + * ```php + * 'errorHandler' => [ + * 'errorAction' => 'site/error', + * ] + * ``` + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class ErrorAction extends Action +{ + /** + * @var string the view file to be rendered. If not set, it will take the value of [[id]]. + * That means, if you name the action as "error" in "SiteController", then the view name + * would be "error", and the corresponding view file would be "views/site/error.php". + */ + public $view; + /** + * @var string the name of the error when the exception name cannot be determined. + * Defaults to "Error". + */ + public $defaultName; + /** + * @var string the message to be displayed when the exception message contains sensitive information. + * Defaults to "An internal server error occurred.". + */ + public $defaultMessage; + + + public function run() + { + if (($exception = Yii::$app->exception) === null) { + return ''; + } + + if ($exception instanceof HttpException) { + $code = $exception->statusCode; + } else { + $code = $exception->getCode(); + } + if ($exception instanceof Exception) { + $name = $exception->getName(); + } else { + $name = $this->defaultName ?: Yii::t('yii', 'Error'); + } + if ($code) { + $name .= " (#$code)"; + } + + if ($exception instanceof UserException) { + $message = $exception->getMessage(); + } else { + $message = $this->defaultMessage ?: Yii::t('yii', 'An internal server error occurred.'); + } + + if (Yii::$app->getRequest()->getIsAjax()) { + return "$name: $message"; + } else { + return $this->controller->render($this->view ?: $this->id, [ + 'name' => $name, + 'message' => $message, + 'exception' => $exception, + ]); + } + } +} diff --git a/framework/web/HeaderCollection.php b/framework/web/HeaderCollection.php new file mode 100644 index 0000000..e8e4f9c --- /dev/null +++ b/framework/web/HeaderCollection.php @@ -0,0 +1,221 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Object; +use ArrayIterator; + +/** + * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. + * + * @property integer $count The number of headers in the collection. This property is read-only. + * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. This property + * is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable +{ + /** + * @var array the headers in this collection (indexed by the header names) + */ + private $_headers = []; + + /** + * Returns an iterator for traversing the headers in the collection. + * This method is required by the SPL interface `IteratorAggregate`. + * It will be implicitly called when you use `foreach` to traverse the collection. + * @return ArrayIterator an iterator for traversing the headers in the collection. + */ + public function getIterator() + { + return new ArrayIterator($this->_headers); + } + + /** + * Returns the number of headers in the collection. + * This method is required by the SPL `Countable` interface. + * It will be implicitly called when you use `count($collection)`. + * @return integer the number of headers in the collection. + */ + public function count() + { + return $this->getCount(); + } + + /** + * Returns the number of headers in the collection. + * @return integer the number of headers in the collection. + */ + public function getCount() + { + return count($this->_headers); + } + + /** + * Returns the named header(s). + * @param string $name the name of the header to return + * @param mixed $default the value to return in case the named header does not exist + * @param boolean $first whether to only return the first header of the specified name. + * If false, all headers of the specified name will be returned. + * @return string|array the named header(s). If `$first` is true, a string will be returned; + * If `$first` is false, an array will be returned. + */ + public function get($name, $default = null, $first = true) + { + $name = strtolower($name); + if (isset($this->_headers[$name])) { + return $first ? reset($this->_headers[$name]) : $this->_headers[$name]; + } else { + return $default; + } + } + + /** + * Adds a new header. + * If there is already a header with the same name, it will be replaced. + * @param string $name the name of the header + * @param string $value the value of the header + * @return static the collection object itself + */ + public function set($name, $value = '') + { + $name = strtolower($name); + $this->_headers[$name] = (array)$value; + return $this; + } + + /** + * Adds a new header. + * If there is already a header with the same name, the new one will + * be appended to it instead of replacing it. + * @param string $name the name of the header + * @param string $value the value of the header + * @return static the collection object itself + */ + public function add($name, $value) + { + $name = strtolower($name); + $this->_headers[$name][] = $value; + return $this; + } + + /** + * Sets a new header only if it does not exist yet. + * If there is already a header with the same name, the new one will be ignored. + * @param string $name the name of the header + * @param string $value the value of the header + * @return static the collection object itself + */ + public function setDefault($name, $value) + { + $name = strtolower($name); + if (empty($this->_headers[$name])) { + $this->_headers[$name][] = $value; + } + return $this; + } + + /** + * Returns a value indicating whether the named header exists. + * @param string $name the name of the header + * @return boolean whether the named header exists + */ + public function has($name) + { + $name = strtolower($name); + return isset($this->_headers[$name]); + } + + /** + * Removes a header. + * @param string $name the name of the header to be removed. + * @return string the value of the removed header. Null is returned if the header does not exist. + */ + public function remove($name) + { + $name = strtolower($name); + if (isset($this->_headers[$name])) { + $value = $this->_headers[$name]; + unset($this->_headers[$name]); + return $value; + } else { + return null; + } + } + + /** + * Removes all headers. + */ + public function removeAll() + { + $this->_headers = []; + } + + /** + * Returns the collection as a PHP array. + * @return array the array representation of the collection. + * The array keys are header names, and the array values are the corresponding header values. + */ + public function toArray() + { + return $this->_headers; + } + + /** + * Returns whether there is a header with the specified name. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `isset($collection[$name])`. + * @param string $name the header name + * @return boolean whether the named header exists + */ + public function offsetExists($name) + { + return $this->has($name); + } + + /** + * Returns the header with the specified name. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `$header = $collection[$name];`. + * This is equivalent to [[get()]]. + * @param string $name the header name + * @return string the header value with the specified name, null if the named header does not exist. + */ + public function offsetGet($name) + { + return $this->get($name); + } + + /** + * Adds the header to the collection. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `$collection[$name] = $header;`. + * This is equivalent to [[add()]]. + * @param string $name the header name + * @param string $value the header value to be added + */ + public function offsetSet($name, $value) + { + $this->set($name, $value); + } + + /** + * Removes the named header. + * This method is required by the SPL interface `ArrayAccess`. + * It is implicitly called when you use something like `unset($collection[$name])`. + * This is equivalent to [[remove()]]. + * @param string $name the header name + */ + public function offsetUnset($name) + { + $this->remove($name); + } +} diff --git a/framework/web/HttpCache.php b/framework/web/HttpCache.php new file mode 100644 index 0000000..134df71 --- /dev/null +++ b/framework/web/HttpCache.php @@ -0,0 +1,160 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\ActionFilter; +use yii\base\Action; + +/** + * 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 <webmaster@dasourcerer.net> + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class HttpCache extends ActionFilter +{ + /** + * @var callback a PHP callback that returns the UNIX timestamp of the last modification time. + * The callback's signature should be: + * + * ~~~ + * function ($action, $params) + * ~~~ + * + * where `$action` is the [[Action]] object that this filter is currently handling; + * `$params` takes the value of [[params]]. The callback should return a UNIX timestamp. + */ + public $lastModified; + /** + * @var callback a PHP callback that generates the Etag seed string. + * The callback's signature should be: + * + * ~~~ + * function ($action, $params) + * ~~~ + * + * where `$action` is the [[Action]] object that this filter is currently handling; + * `$params` takes the value of [[params]]. The callback should return a string serving + * as the seed for generating an Etag. + */ + public $etagSeed; + /** + * @var mixed additional parameters that should be passed to the [[lastModified]] and [[etagSeed]] callbacks. + */ + public $params; + /** + * @var string HTTP cache control header. If null, the header will not be sent. + */ + public $cacheControlHeader = 'max-age=3600, public'; + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param Action $action the action to be executed. + * @return boolean whether the action should continue to be executed. + */ + public function beforeAction($action) + { + $verb = Yii::$app->getRequest()->getMethod(); + if ($verb !== 'GET' && $verb !== 'HEAD' || $this->lastModified === null && $this->etagSeed === null) { + return true; + } + + $lastModified = $etag = null; + if ($this->lastModified !== null) { + $lastModified = call_user_func($this->lastModified, $action, $this->params); + } + if ($this->etagSeed !== null) { + $seed = call_user_func($this->etagSeed, $action, $this->params); + $etag = $this->generateEtag($seed); + } + + $this->sendCacheControlHeader(); + $response = Yii::$app->getResponse(); + if ($etag !== null) { + $response->getHeaders()->set('Etag', $etag); + } + + if ($this->validateCache($lastModified, $etag)) { + $response->setStatusCode(304); + return false; + } + + if ($lastModified !== null) { + $response->getHeaders()->set('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); + } + return true; + } + + /** + * Validates if the HTTP cache contains valid content. + * @param integer $lastModified the calculated Last-Modified value in terms of a UNIX timestamp. + * If null, the Last-Modified header will not be validated. + * @param string $etag the calculated ETag value. If null, the ETag header will not be validated. + * @return boolean whether the HTTP cache is still valid. + */ + protected function validateCache($lastModified, $etag) + { + if ($lastModified !== null && (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < $lastModified)) { + return false; + } else { + return $etag === null || isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag; + } + } + + /** + * Sends the cache control header to the client + * @see cacheControl + */ + protected function sendCacheControlHeader() + { + session_cache_limiter('public'); + $headers = Yii::$app->getResponse()->getHeaders(); + $headers->set('Pragma'); + if ($this->cacheControlHeader !== null) { + $headers->set('Cache-Control', $this->cacheControlHeader); + } + } + + /** + * Generates an Etag from the given seed string. + * @param string $seed Seed for the ETag + * @return string the generated Etag + */ + protected function generateEtag($seed) + { + return '"' . base64_encode(sha1($seed, true)) . '"'; + } +} diff --git a/framework/web/HttpException.php b/framework/web/HttpException.php new file mode 100644 index 0000000..2398437 --- /dev/null +++ b/framework/web/HttpException.php @@ -0,0 +1,72 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\UserException; + +/** + * HttpException represents an exception caused by an improper request of the end-user. + * + * HttpException can be differentiated via its [[statusCode]] property value which + * 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 <qiang.xue@gmail.com> + * @since 2.0 + */ +class HttpException extends UserException +{ + /** + * @var integer HTTP status code, such as 403, 404, 500, etc. + */ + public $statusCode; + + /** + * Constructor. + * @param integer $status HTTP status code, such as 404, 500, etc. + * @param string $message error message + * @param integer $code error code + * @param \Exception $previous The previous exception used for the exception chaining. + */ + public function __construct($status, $message = null, $code = 0, \Exception $previous = null) + { + $this->statusCode = $status; + parent::__construct($message, $code, $previous); + } + + /** + * @return string the user-friendly name of this exception + */ + public function getName() + { + if (isset(Response::$httpStatuses[$this->statusCode])) { + return Response::$httpStatuses[$this->statusCode]; + } else { + return 'Error'; + } + } + + /** + * Returns the array representation of this object. + * @return array the array representation of this object. + */ + public function toArray() + { + $array = parent::toArray(); + $array['status'] = $this->statusCode; + return $array; + } +} diff --git a/framework/web/IdentityInterface.php b/framework/web/IdentityInterface.php new file mode 100644 index 0000000..c796b50 --- /dev/null +++ b/framework/web/IdentityInterface.php @@ -0,0 +1,81 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +/** + * IdentityInterface is the interface that should be implemented by a class providing identity information. + * + * This interface can typically be implemented by a user model class. For example, the following + * code shows how to implement this interface by a User ActiveRecord class: + * + * ~~~ + * class User extends ActiveRecord implements IdentityInterface + * { + * public static function findIdentity($id) + * { + * return static::find($id); + * } + * + * public function getId() + * { + * return $this->id; + * } + * + * public function getAuthKey() + * { + * return $this->authKey; + * } + * + * public function validateAuthKey($authKey) + * { + * return $this->authKey === $authKey; + * } + * } + * ~~~ + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +interface IdentityInterface +{ + /** + * Finds an identity by the given ID. + * @param string|integer $id the ID to be looked for + * @return IdentityInterface the identity object that matches the given ID. + * Null should be returned if such an identity cannot be found + * or the identity is not in an active state (disabled, deleted, etc.) + */ + public static function findIdentity($id); + /** + * Returns an ID that can uniquely identify a user identity. + * @return string|integer an ID that uniquely identifies a user identity. + */ + public function getId(); + /** + * Returns a key that can be used to check the validity of a given identity ID. + * + * The key should be unique for each individual user, and should be persistent + * so that it can be used to check the validity of the user identity. + * + * The space of such keys should be big enough to defeat potential identity attacks. + * + * This is required if [[User::enableAutoLogin]] is enabled. + * @return string a key that is used to check the validity of a given identity ID. + * @see validateAuthKey() + */ + public function getAuthKey(); + /** + * Validates the given auth key. + * + * This is required if [[User::enableAutoLogin]] is enabled. + * @param string $authKey the given auth key + * @return boolean whether the given auth key is valid. + * @see getAuthKey() + */ + public function validateAuthKey($authKey); +} diff --git a/framework/yii/web/JqueryAsset.php b/framework/web/JqueryAsset.php similarity index 100% rename from framework/yii/web/JqueryAsset.php rename to framework/web/JqueryAsset.php diff --git a/framework/web/JsExpression.php b/framework/web/JsExpression.php new file mode 100644 index 0000000..1d05b57 --- /dev/null +++ b/framework/web/JsExpression.php @@ -0,0 +1,47 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\Object; + +/** + * JsExpression marks a string as a JavaScript expression. + * + * When using [[yii\helpers\Json::encode()]] to encode a value, JsonExpression objects + * will be specially handled and encoded as a JavaScript expression instead of a string. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class JsExpression extends Object +{ + /** + * @var string the JavaScript expression represented by this object + */ + public $expression; + + /** + * Constructor. + * @param string $expression the JavaScript expression represented by this object + * @param array $config additional configurations for this object + */ + public function __construct($expression, $config = []) + { + $this->expression = $expression; + parent::__construct($config); + } + + /** + * The PHP magic function converting an object into a string. + * @return string the JavaScript expression. + */ + public function __toString() + { + return $this->expression; + } +} diff --git a/framework/yii/web/MethodNotAllowedHttpException.php b/framework/web/MethodNotAllowedHttpException.php similarity index 100% rename from framework/yii/web/MethodNotAllowedHttpException.php rename to framework/web/MethodNotAllowedHttpException.php diff --git a/framework/yii/web/NotFoundHttpException.php b/framework/web/NotFoundHttpException.php similarity index 100% rename from framework/yii/web/NotFoundHttpException.php rename to framework/web/NotFoundHttpException.php diff --git a/framework/web/PageCache.php b/framework/web/PageCache.php new file mode 100644 index 0000000..4c8cc50 --- /dev/null +++ b/framework/web/PageCache.php @@ -0,0 +1,150 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\ActionFilter; +use yii\base\Action; +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 <qiang.xue@gmail.com> + * @since 2.0 + */ +class PageCache extends ActionFilter +{ + /** + * @var boolean whether the content being cached should be differentiated according to the route. + * A route consists of the requested controller ID and action ID. Defaults to true. + */ + public $varyByRoute = true; + /** + * @var string the application component ID of the [[\yii\caching\Cache|cache]] object. + */ + public $cache = 'cache'; + /** + * @var integer number of seconds that the data can remain valid in cache. + * Use 0 to indicate that the cached data will never expire. + */ + public $duration = 60; + /** + * @var array|Dependency the dependency that the cached content depends on. + * This can be either a [[Dependency]] object or a configuration array for creating the dependency object. + * For example, + * + * ~~~ + * [ + * 'class' => 'yii\caching\DbDependency', + * 'sql' => 'SELECT MAX(lastModified) FROM Post', + * ] + * ~~~ + * + * would make the output cache depends on the last modified time of all posts. + * If any post has its modification time changed, the cached content would be invalidated. + */ + public $dependency; + /** + * @var array list of factors that would cause the variation of the content being cached. + * Each factor is a string representing a variation (e.g. the language, a GET parameter). + * The following variation setting will cause the content to be cached in different versions + * according to the current application language: + * + * ~~~ + * [ + * Yii::$app->language, + * ] + * ~~~ + */ + public $variations; + /** + * @var boolean whether to enable the fragment cache. You may use this property to turn on and off + * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). + */ + public $enabled = true; + /** + * @var \yii\base\View the view component to use for caching. If not set, the default application view component + * [[Application::view]] will be used. + */ + public $view; + + + public function init() + { + parent::init(); + if ($this->view === null) { + $this->view = Yii::$app->getView(); + } + } + + /** + * This method is invoked right before an action is to be executed (after all possible filters.) + * You may override this method to do last-minute preparation for the action. + * @param Action $action the action to be executed. + * @return boolean whether the action should continue to be executed. + */ + public function beforeAction($action) + { + $properties = []; + foreach (['cache', 'duration', 'dependency', 'variations', 'enabled'] as $name) { + $properties[$name] = $this->$name; + } + $id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__; + ob_start(); + ob_implicit_flush(false); + if ($this->view->beginCache($id, $properties)) { + return true; + } else { + Yii::$app->getResponse()->content = ob_get_clean(); + return false; + } + } + + /** + * This method is invoked right after an action is executed. + * You may override this method to do some postprocessing for the action. + * @param Action $action the action just executed. + * @param mixed $result the action execution result + */ + public function afterAction($action, &$result) + { + echo $result; + $this->view->endCache(); + $result = ob_get_clean(); + } +} diff --git a/framework/yii/web/Request.php b/framework/web/Request.php similarity index 100% rename from framework/yii/web/Request.php rename to framework/web/Request.php diff --git a/framework/yii/web/Response.php b/framework/web/Response.php similarity index 100% rename from framework/yii/web/Response.php rename to framework/web/Response.php diff --git a/framework/yii/web/ResponseFormatterInterface.php b/framework/web/ResponseFormatterInterface.php similarity index 100% rename from framework/yii/web/ResponseFormatterInterface.php rename to framework/web/ResponseFormatterInterface.php diff --git a/framework/yii/web/Session.php b/framework/web/Session.php similarity index 100% rename from framework/yii/web/Session.php rename to framework/web/Session.php diff --git a/framework/web/SessionIterator.php b/framework/web/SessionIterator.php new file mode 100644 index 0000000..c960dd4 --- /dev/null +++ b/framework/web/SessionIterator.php @@ -0,0 +1,84 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +/** + * SessionIterator implements an iterator for traversing session variables managed by [[Session]]. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class SessionIterator implements \Iterator +{ + /** + * @var array list of keys in the map + */ + private $_keys; + /** + * @var mixed current key + */ + private $_key; + + /** + * Constructor. + */ + public function __construct() + { + $this->_keys = array_keys($_SESSION); + } + + /** + * Rewinds internal array pointer. + * This method is required by the interface Iterator. + */ + public function rewind() + { + $this->_key = reset($this->_keys); + } + + /** + * Returns the key of the current array element. + * This method is required by the interface Iterator. + * @return mixed the key of the current array element + */ + public function key() + { + return $this->_key; + } + + /** + * Returns the current array element. + * This method is required by the interface Iterator. + * @return mixed the current array element + */ + public function current() + { + return isset($_SESSION[$this->_key]) ? $_SESSION[$this->_key] : null; + } + + /** + * Moves the internal pointer to the next array element. + * This method is required by the interface Iterator. + */ + public function next() + { + do { + $this->_key = next($this->_keys); + } while (!isset($_SESSION[$this->_key]) && $this->_key !== false); + } + + /** + * Returns whether there is an element at current position. + * This method is required by the interface Iterator. + * @return boolean + */ + public function valid() + { + return $this->_key !== false; + } +} diff --git a/framework/web/UploadedFile.php b/framework/web/UploadedFile.php new file mode 100644 index 0000000..5e1e428 --- /dev/null +++ b/framework/web/UploadedFile.php @@ -0,0 +1,234 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\Object; +use yii\helpers\Html; + +/** + * UploadedFile represents the information for an uploaded file. + * + * You can call [[getInstance()]] to retrieve the instance of an uploaded file, + * and then use [[saveAs()]] to save it on the server. + * You may also query other information about the file, including [[name]], + * [[tempName]], [[type]], [[size]] and [[error]]. + * + * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed + * error code information. This property is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class UploadedFile extends Object +{ + private static $_files; + + /** + * @var string the original name of the file being uploaded + */ + public $name; + /** + * @var string the path of the uploaded file on the server. + * Note, this is a temporary file which will be automatically deleted by PHP + * after the current request is processed. + */ + public $tempName; + /** + * @var string the MIME-type of the uploaded file (such as "image/gif"). + * Since this MIME type is not checked on the server side, do not take this value for granted. + * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. + */ + public $type; + /** + * @var integer the actual size of the uploaded file in bytes + */ + public $size; + /** + * @var integer an error code describing the status of this file uploading. + * @see http://www.php.net/manual/en/features.file-upload.errors.php + */ + public $error; + + + /** + * String output. + * This is PHP magic method that returns string representation of an object. + * The implementation here returns the uploaded file's name. + * @return string the string representation of the object + */ + public function __toString() + { + return $this->name; + } + + /** + * Returns an uploaded file for the given model attribute. + * The file should be uploaded using [[ActiveForm::fileInput()]]. + * @param \yii\base\Model $model the data model + * @param string $attribute the attribute name. The attribute name may contain array indexes. + * 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() + */ + public static function getInstance($model, $attribute) + { + $name = Html::getInputName($model, $attribute); + return static::getInstanceByName($name); + } + + /** + * Returns all uploaded files for the given model attribute. + * @param \yii\base\Model $model the data model + * @param string $attribute the attribute name. The attribute name may contain array indexes + * for tabular file uploading, e.g. '[1]file'. + * @return UploadedFile[] array of UploadedFile objects. + * Empty array is returned if no available file was found for the given attribute. + */ + public static function getInstances($model, $attribute) + { + $name = Html::getInputName($model, $attribute); + return static::getInstancesByName($name); + } + + /** + * Returns an uploaded file according to the given file input name. + * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]'). + * @param string $name the name of the file input field. + * @return UploadedFile the instance of the uploaded file. + * Null is returned if no file is uploaded for the specified name. + */ + public static function getInstanceByName($name) + { + $files = static::loadFiles(); + return isset($files[$name]) ? $files[$name] : null; + } + + /** + * Returns an array of uploaded files corresponding to the specified file input name. + * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]', + * 'files[n]'..., and you can retrieve them all by passing 'files' as the name. + * @param string $name the name of the array of files + * @return UploadedFile[] the array of CUploadedFile objects. Empty array is returned + * if no adequate upload was found. Please note that this array will contain + * all files from all sub-arrays regardless how deeply nested they are. + */ + public static function getInstancesByName($name) + { + $files = static::loadFiles(); + if (isset($files[$name])) { + return [$files[$name]]; + } + $results = []; + foreach ($files as $key => $file) { + if (strpos($key, "{$name}[") === 0) { + $results[] = self::$_files[$key]; + } + } + return $results; + } + + /** + * Cleans up the loaded UploadedFile instances. + * This method is mainly used by test scripts to set up a fixture. + */ + public static function reset() + { + self::$_files = null; + } + + /** + * Saves the uploaded file. + * Note that this method uses php's move_uploaded_file() method. If the target file `$file` + * already exists, it will be overwritten. + * @param string $file the file path used to save the uploaded file + * @param boolean $deleteTempFile whether to delete the temporary file after saving. + * If true, you will not be able to save the uploaded file again in the current request. + * @return boolean true whether the file is saved successfully + * @see error + */ + public function saveAs($file, $deleteTempFile = true) + { + if ($this->error == UPLOAD_ERR_OK) { + if ($deleteTempFile) { + return move_uploaded_file($this->tempName, $file); + } elseif (is_uploaded_file($this->tempName)) { + return copy($this->tempName, $file); + } + } + return false; + } + + /** + * @return string original file base name + */ + public function getBaseName() + { + return pathinfo($this->name, PATHINFO_FILENAME); + } + + /** + * @return string file extension + */ + public function getExtension() + { + return strtolower(pathinfo($this->name, PATHINFO_EXTENSION)); + } + + /** + * @return boolean whether there is an error with the uploaded file. + * Check [[error]] for detailed error code information. + */ + public function getHasError() + { + return $this->error != UPLOAD_ERR_OK; + } + + /** + * Creates UploadedFile instances from $_FILE. + * @return array the UploadedFile instances + */ + private static function loadFiles() + { + if (self::$_files === null) { + self::$_files = []; + if (isset($_FILES) && is_array($_FILES)) { + foreach ($_FILES as $class => $info) { + self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']); + } + } + } + return self::$_files; + } + + /** + * Creates UploadedFile instances from $_FILE recursively. + * @param string $key key for identifying uploaded file: class name and sub-array indexes + * @param mixed $names file names provided by PHP + * @param mixed $tempNames temporary file names provided by PHP + * @param mixed $types file types provided by PHP + * @param mixed $sizes file sizes provided by PHP + * @param mixed $errors uploading issues provided by PHP + */ + private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors) + { + if (is_array($names)) { + foreach ($names as $i => $name) { + self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]); + } + } else { + self::$_files[$key] = new static([ + 'name' => $names, + 'tempName' => $tempNames, + 'type' => $types, + 'size' => $sizes, + 'error' => $errors, + ]); + } + } +} diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php new file mode 100644 index 0000000..a2044cb --- /dev/null +++ b/framework/web/UrlManager.php @@ -0,0 +1,338 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\Component; +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. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class UrlManager extends Component +{ + /** + * @var boolean whether to enable pretty URLs. Instead of putting all parameters in the query + * string part of a URL, pretty URLs allow using path info to represent some of the parameters + * and can thus produce more user-friendly URLs, such as "/news/Yii-is-released", instead of + * "/index.php?r=news/view&id=100". + */ + public $enablePrettyUrl = false; + /** + * @var boolean whether to enable strict parsing. If strict parsing is enabled, the incoming + * requested URL must match at least one of the [[rules]] in order to be treated as a valid request. + * Otherwise, the path info part of the request will be treated as the requested route. + * This property is used only when [[enablePrettyUrl]] is true. + */ + public $enableStrictParsing = false; + /** + * @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is true. + * This property is used only if [[enablePrettyUrl]] is true. Each element in the array + * is the configuration array for creating a single URL rule. The configuration will + * be merged with [[ruleConfig]] first before it is used for creating the rule object. + * + * A special shortcut format can be used if a rule only specifies [[UrlRule::pattern|pattern]] + * and [[UrlRule::route|route]]: `'pattern' => 'route'`. That is, instead of using a configuration + * array, one can use the key to represent the pattern and the value the corresponding route. + * For example, `'post/<id:\d+>' => 'post/view'`. + * + * For RESTful routing the mentioned shortcut format also allows you to specify the + * [[UrlRule::verb|HTTP verb]] that the rule should apply for. + * You can do that by prepending it to the pattern, separated by space. + * For example, `'PUT post/<id:\d+>' => 'post/update'`. + * You may specify multiple verbs by separating them with comma + * like this: `'POST,PUT post/index' => 'post/create'`. + * The supported verbs in the shortcut format are: GET, HEAD, POST, PUT, PATCH and DELETE. + * Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way + * so you normally would not specify a verb for normal GET request. + * + * Here is an example configuration for RESTful CRUD controller: + * + * ~~~php + * [ + * 'dashboard' => 'site/index', + * + * 'POST <controller:\w+>s' => '<controller>/create', + * '<controller:\w+>s' => '<controller>/index', + * + * 'PUT <controller:\w+>/<id:\d+>' => '<controller>/update', + * 'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete', + * '<controller:\w+>/<id:\d+>' => '<controller>/view', + * ]; + * ~~~ + * + * Note that if you modify this property after the UrlManager object is created, make sure + * you populate the array with rule objects instead of rule configurations. + */ + public $rules = []; + /** + * @var string the URL suffix used when in 'path' format. + * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. + * This property is used only if [[enablePrettyUrl]] is true. + */ + public $suffix; + /** + * @var boolean whether to show entry script name in the constructed URL. Defaults to true. + * This property is used only if [[enablePrettyUrl]] is true. + */ + public $showScriptName = true; + /** + * @var string the GET variable name for route. This property is used only if [[enablePrettyUrl]] is false. + */ + public $routeVar = 'r'; + /** + * @var Cache|string the cache object or the application component ID of the cache object. + * Compiled URL rules will be cached through this cache object, if it is available. + * + * After the UrlManager object is created, if you want to change this property, + * you should only assign it with a cache object. + * Set this property to null if you do not want to cache the URL rules. + */ + public $cache = 'cache'; + /** + * @var array the default configuration of URL rules. Individual rule configurations + * specified via [[rules]] will take precedence when the same property of the rule is configured. + */ + public $ruleConfig = ['class' => 'yii\web\UrlRule']; + + private $_baseUrl; + private $_hostInfo; + + /** + * Initializes UrlManager. + */ + public function init() + { + parent::init(); + $this->compileRules(); + } + + /** + * Parses the URL rules. + */ + protected function compileRules() + { + if (!$this->enablePrettyUrl || empty($this->rules)) { + return; + } + if (is_string($this->cache)) { + $this->cache = Yii::$app->getComponent($this->cache); + } + if ($this->cache instanceof Cache) { + $key = __CLASS__; + $hash = md5(json_encode($this->rules)); + if (($data = $this->cache->get($key)) !== false && isset($data[1]) && $data[1] === $hash) { + $this->rules = $data[0]; + return; + } + } + + $rules = []; + foreach ($this->rules as $key => $rule) { + if (!is_array($rule)) { + $rule = ['route' => $rule]; + if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) { + $rule['verb'] = explode(',', $matches[1]); + $rule['mode'] = UrlRule::PARSING_ONLY; + $key = $matches[4]; + } + $rule['pattern'] = $key; + } + $rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule)); + } + $this->rules = $rules; + + if (isset($key, $hash)) { + $this->cache->set($key, [$this->rules, $hash]); + } + } + + /** + * Parses the user request. + * @param Request $request the request component + * @return array|boolean the route and the associated parameters. The latter is always empty + * if [[enablePrettyUrl]] is false. False is returned if the current request cannot be successfully parsed. + */ + public function parseRequest($request) + { + if ($this->enablePrettyUrl) { + $pathInfo = $request->getPathInfo(); + /** @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__); + return $result; + } + } + + if ($this->enableStrictParsing) { + return false; + } + + Yii::trace('No matching URL rules. Using default URL parsing logic.', __METHOD__); + + $suffix = (string)$this->suffix; + if ($suffix !== '' && $pathInfo !== '') { + $n = strlen($this->suffix); + if (substr($pathInfo, -$n) === $this->suffix) { + $pathInfo = substr($pathInfo, 0, -$n); + if ($pathInfo === '') { + // suffix alone is not allowed + return false; + } + } else { + // suffix doesn't match + return false; + } + } + + return [$pathInfo, []]; + } else { + Yii::trace('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__); + $route = $request->get($this->routeVar); + if (is_array($route)) { + $route = ''; + } + return [(string)$route, []]; + } + } + + /** + * Creates a URL using the given route and parameters. + * The URL created is a relative one. Use [[createAbsoluteUrl()]] to create an absolute URL. + * @param string $route the route + * @param array $params the parameters (name-value pairs) + * @return string the created URL + */ + public function createUrl($route, $params = []) + { + $anchor = isset($params['#']) ? '#' . $params['#'] : ''; + unset($params['#'], $params[$this->routeVar]); + + $route = trim($route, '/'); + $baseUrl = $this->getBaseUrl(); + + if ($this->enablePrettyUrl) { + /** @var UrlRule $rule */ + foreach ($this->rules as $rule) { + if (($url = $rule->createUrl($this, $route, $params)) !== false) { + if ($rule->host !== null) { + if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) { + return substr($url, 0, $pos) . $baseUrl . substr($url, $pos); + } else { + return $url . $baseUrl . $anchor; + } + } else { + return "$baseUrl/{$url}{$anchor}"; + } + } + } + + if ($this->suffix !== null) { + $route .= $this->suffix; + } + if (!empty($params)) { + $route .= '?' . http_build_query($params); + } + return "$baseUrl/{$route}{$anchor}"; + } else { + $url = "$baseUrl?{$this->routeVar}=$route"; + if (!empty($params)) { + $url .= '&' . http_build_query($params); + } + return $url . $anchor; + } + } + + /** + * Creates an absolute URL using the given route and parameters. + * This method prepends the URL created by [[createUrl()]] with the [[hostInfo]]. + * @param string $route the route + * @param array $params the parameters (name-value pairs) + * @return string the created URL + * @see createUrl() + */ + public function createAbsoluteUrl($route, $params = []) + { + $url = $this->createUrl($route, $params); + if (strpos($url, '://') !== false) { + return $url; + } else { + return $this->getHostInfo() . $url; + } + } + + /** + * Returns the base URL that is used by [[createUrl()]] to prepend URLs it creates. + * It defaults to [[Request::scriptUrl]] if [[showScriptName]] is true or [[enablePrettyUrl]] is false; + * otherwise, it defaults to [[Request::baseUrl]]. + * @return string the base URL that is used by [[createUrl()]] to prepend URLs it creates. + */ + public function getBaseUrl() + { + if ($this->_baseUrl === null) { + /** @var \yii\web\Request $request */ + $request = Yii::$app->getRequest(); + $this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl(); + } + return $this->_baseUrl; + } + + /** + * Sets the base URL that is used by [[createUrl()]] to prepend URLs it creates. + * @param string $value the base URL that is used by [[createUrl()]] to prepend URLs it creates. + */ + public function setBaseUrl($value) + { + $this->_baseUrl = rtrim($value, '/'); + } + + /** + * Returns the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + * @return string the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + */ + public function getHostInfo() + { + if ($this->_hostInfo === null) { + $this->_hostInfo = Yii::$app->getRequest()->getHostInfo(); + } + return $this->_hostInfo; + } + + /** + * Sets the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + * @param string $value the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. + */ + public function setHostInfo($value) + { + $this->_hostInfo = rtrim($value, '/'); + } +} diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php new file mode 100644 index 0000000..2934b26 --- /dev/null +++ b/framework/web/UrlRule.php @@ -0,0 +1,326 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use yii\base\Object; +use yii\base\InvalidConfigException; + +/** + * 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 <qiang.xue@gmail.com> + * @since 2.0 + */ +class UrlRule extends Object +{ + /** + * Set [[mode]] with this value to mark that this rule is for URL parsing only + */ + const PARSING_ONLY = 1; + /** + * Set [[mode]] with this value to mark that this rule is for URL creation only + */ + const CREATION_ONLY = 2; + + /** + * @var string the name of this rule. If not set, it will use [[pattern]] as the name. + */ + public $name; + /** + * @var string the pattern used to parse and create the path info part of a URL. + * @see host + */ + public $pattern; + /** + * @var string the pattern used to parse and create the host info part of a URL. + * @see pattern + */ + public $host; + /** + * @var string the route to the controller action + */ + public $route; + /** + * @var array the default GET parameters (name => value) that this rule provides. + * When this rule is used to parse the incoming request, the values declared in this property + * will be injected into $_GET. + */ + public $defaults = []; + /** + * @var string the URL suffix used for this rule. + * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. + * If not, the value of [[UrlManager::suffix]] will be used. + */ + public $suffix; + /** + * @var string|array the HTTP verb (e.g. GET, POST, DELETE) that this rule should match. + * Use array to represent multiple verbs that this rule may match. + * If this property is not set, the rule can match any verb. + * Note that this property is only used when parsing a request. It is ignored for URL creation. + */ + public $verb; + /** + * @var integer a value indicating if this rule should be used for both request parsing and URL creation, + * parsing only, or creation only. + * If not set or 0, it means the rule is both request parsing and URL creation. + * If it is [[PARSING_ONLY]], the rule is for request parsing only. + * If it is [[CREATION_ONLY]], the rule is for URL creation only. + */ + public $mode; + + /** + * @var string the template for generating a new URL. This is derived from [[pattern]] and is used in generating URL. + */ + private $_template; + /** + * @var string the regex for matching the route part. This is used in generating URL. + */ + private $_routeRule; + /** + * @var array list of regex for matching parameters. This is used in generating URL. + */ + private $_paramRules = []; + /** + * @var array list of parameters used in the route. + */ + private $_routeParams = []; + + /** + * Initializes this rule. + */ + public function init() + { + if ($this->pattern === null) { + throw new InvalidConfigException('UrlRule::pattern must be set.'); + } + if ($this->route === null) { + throw new InvalidConfigException('UrlRule::route must be set.'); + } + if ($this->verb !== null) { + if (is_array($this->verb)) { + foreach ($this->verb as $i => $verb) { + $this->verb[$i] = strtoupper($verb); + } + } else { + $this->verb = [strtoupper($this->verb)]; + } + } + if ($this->name === null) { + $this->name = $this->pattern; + } + + $this->pattern = trim($this->pattern, '/'); + + if ($this->host !== null) { + $this->pattern = rtrim($this->host, '/') . rtrim('/' . $this->pattern, '/') . '/'; + } elseif ($this->pattern === '') { + $this->_template = ''; + $this->pattern = '#^$#u'; + return; + } else { + $this->pattern = '/' . $this->pattern . '/'; + } + + $this->route = trim($this->route, '/'); + if (strpos($this->route, '<') !== false && preg_match_all('/<(\w+)>/', $this->route, $matches)) { + foreach ($matches[1] as $name) { + $this->_routeParams[$name] = "<$name>"; + } + } + + $tr = [ + '.' => '\\.', + '*' => '\\*', + '$' => '\\$', + '[' => '\\[', + ']' => '\\]', + '(' => '\\(', + ')' => '\\)', + ]; + $tr2 = []; + if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + foreach ($matches as $match) { + $name = $match[1][0]; + $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+'; + if (isset($this->defaults[$name])) { + $length = strlen($match[0][0]); + $offset = $match[0][1]; + if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') { + $tr["/<$name>"] = "(/(?P<$name>$pattern))?"; + } else { + $tr["<$name>"] = "(?P<$name>$pattern)?"; + } + } else { + $tr["<$name>"] = "(?P<$name>$pattern)"; + } + if (isset($this->_routeParams[$name])) { + $tr2["<$name>"] = "(?P<$name>$pattern)"; + } else { + $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#"; + } + } + } + + $this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern); + $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u'; + + if (!empty($this->_routeParams)) { + $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u'; + } + } + + /** + * Parses the given request and returns the corresponding route and parameters. + * @param UrlManager $manager the URL manager + * @param Request $request the request component + * @return array|boolean the parsing result. The route and the parameters are returned as an array. + * If false, it means this rule cannot be used to parse this path info. + */ + public function parseRequest($manager, $request) + { + if ($this->mode === self::CREATION_ONLY) { + return false; + } + + if ($this->verb !== null && !in_array($request->getMethod(), $this->verb, true)) { + return false; + } + + $pathInfo = $request->getPathInfo(); + $suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix); + if ($suffix !== '' && $pathInfo !== '') { + $n = strlen($suffix); + if (substr($pathInfo, -$n) === $suffix) { + $pathInfo = substr($pathInfo, 0, -$n); + if ($pathInfo === '') { + // suffix alone is not allowed + return false; + } + } else { + return false; + } + } + + if ($this->host !== null) { + $pathInfo = strtolower($request->getHostInfo()) . '/' . $pathInfo; + } + + if (!preg_match($this->pattern, $pathInfo, $matches)) { + return false; + } + foreach ($this->defaults as $name => $value) { + if (!isset($matches[$name]) || $matches[$name] === '') { + $matches[$name] = $value; + } + } + $params = $this->defaults; + $tr = []; + foreach ($matches as $name => $value) { + if (isset($this->_routeParams[$name])) { + $tr[$this->_routeParams[$name]] = $value; + unset($params[$name]); + } elseif (isset($this->_paramRules[$name])) { + $params[$name] = $value; + } + } + if ($this->_routeRule !== null) { + $route = strtr($this->route, $tr); + } else { + $route = $this->route; + } + return [$route, $params]; + } + + /** + * Creates a URL according to the given route and parameters. + * @param UrlManager $manager the URL manager + * @param string $route the route. It should not have slashes at the beginning or the end. + * @param array $params the parameters + * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL. + */ + public function createUrl($manager, $route, $params) + { + if ($this->mode === self::PARSING_ONLY) { + return false; + } + + $tr = []; + + // match the route part first + if ($route !== $this->route) { + if ($this->_routeRule !== null && preg_match($this->_routeRule, $route, $matches)) { + foreach ($this->_routeParams as $name => $token) { + if (isset($this->defaults[$name]) && strcmp($this->defaults[$name], $matches[$name]) === 0) { + $tr[$token] = ''; + } else { + $tr[$token] = $matches[$name]; + } + } + } else { + return false; + } + } + + // match default params + // if a default param is not in the route pattern, its value must also be matched + foreach ($this->defaults as $name => $value) { + if (isset($this->_routeParams[$name])) { + continue; + } + if (!isset($params[$name])) { + return false; + } elseif (strcmp($params[$name], $value) === 0) { // strcmp will do string conversion automatically + unset($params[$name]); + if (isset($this->_paramRules[$name])) { + $tr["<$name>"] = ''; + } + } elseif (!isset($this->_paramRules[$name])) { + return false; + } + } + + // match params in the pattern + foreach ($this->_paramRules as $name => $rule) { + if (isset($params[$name]) && !is_array($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) { + $tr["<$name>"] = urlencode($params[$name]); + unset($params[$name]); + } elseif (!isset($this->defaults[$name]) || isset($params[$name])) { + return false; + } + } + + $url = trim(strtr($this->_template, $tr), '/'); + if ($this->host !== null) { + $pos = strpos($url, '/', 8); + if ($pos !== false) { + $url = substr($url, 0, $pos) . preg_replace('#/+#', '/', substr($url, $pos)); + } + } elseif (strpos($url, '//') !== false) { + $url = preg_replace('#/+#', '/', $url); + } + + if ($url !== '') { + $url .= ($this->suffix === null ? $manager->suffix : $this->suffix); + } + + if (!empty($params)) { + $url .= '?' . http_build_query($params); + } + return $url; + } +} diff --git a/framework/yii/web/User.php b/framework/web/User.php similarity index 100% rename from framework/yii/web/User.php rename to framework/web/User.php diff --git a/framework/yii/web/JsExpression.php b/framework/web/UserEvent.php similarity index 50% rename from framework/yii/web/JsExpression.php rename to framework/web/UserEvent.php index 1d05b57..8577ef5 100644 --- a/framework/yii/web/JsExpression.php +++ b/framework/web/UserEvent.php @@ -7,41 +7,29 @@ namespace yii\web; -use yii\base\Object; +use yii\base\Event; /** - * JsExpression marks a string as a JavaScript expression. - * - * When using [[yii\helpers\Json::encode()]] to encode a value, JsonExpression objects - * will be specially handled and encoded as a JavaScript expression instead of a string. + * This event class is used for Events triggered by the [[User]] class. * * @author Qiang Xue <qiang.xue@gmail.com> * @since 2.0 */ -class JsExpression extends Object +class UserEvent extends Event { /** - * @var string the JavaScript expression represented by this object + * @var IdentityInterface the identity object associated with this event */ - public $expression; - + public $identity; /** - * Constructor. - * @param string $expression the JavaScript expression represented by this object - * @param array $config additional configurations for this object + * @var boolean whether the login is cookie-based. This property is only meaningful + * for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_AFTER_LOGIN]] events. */ - public function __construct($expression, $config = []) - { - $this->expression = $expression; - parent::__construct($config); - } - + public $cookieBased; /** - * The PHP magic function converting an object into a string. - * @return string the JavaScript expression. + * @var boolean whether the login or logout should proceed. + * Event handlers may modify this property to determine whether the login or logout should proceed. + * This property is only meaningful for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_BEFORE_LOGOUT]] events. */ - public function __toString() - { - return $this->expression; - } + public $isValid = true; } diff --git a/framework/web/VerbFilter.php b/framework/web/VerbFilter.php new file mode 100644 index 0000000..1ca08c2 --- /dev/null +++ b/framework/web/VerbFilter.php @@ -0,0 +1,108 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use Yii; +use yii\base\ActionEvent; +use yii\base\Behavior; + +/** + * VerbFilter is an action filter that filters by HTTP request methods. + * + * It allows to define allowed HTTP request methods for each action and will throw + * an HTTP 405 error when the method is not allowed. + * + * To use VerbFilter, declare it in the `behaviors()` method of your controller class. + * For example, the following declarations will define a typical set of allowed + * request methods for REST CRUD actions. + * + * ~~~ + * public function behaviors() + * { + * return [ + * 'verbs' => [ + * 'class' => \yii\web\VerbFilter::className(), + * 'actions' => [ + * 'index' => ['get'], + * 'view' => ['get'], + * 'create' => ['get', 'post'], + * 'update' => ['get', 'put', 'post'], + * 'delete' => ['post', 'delete'], + * ], + * ], + * ]; + * } + * ~~~ + * + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 + * @author Carsten Brandt <mail@cebe.cc> + * @since 2.0 + */ +class VerbFilter extends Behavior +{ + /** + * @var array this property defines the allowed request methods for each action. + * For each action that should only support limited set of request methods + * you add an entry with the action id as array key and an array of + * allowed methods (e.g. GET, HEAD, PUT) as the value. + * If an action is not listed all request methods are considered allowed. + * + * You can use '*' to stand for all actions. When an action is explicitly + * specified, it takes precedence over the specification given by '*'. + * + * For example, + * + * ~~~ + * [ + * 'create' => ['get', 'post'], + * 'update' => ['get', 'put', 'post'], + * 'delete' => ['post', 'delete'], + * '*' => ['get'], + * ] + * ~~~ + */ + public $actions = []; + + + /** + * Declares event handlers for the [[owner]]'s events. + * @return array events (array keys) and the corresponding event handler methods (array values). + */ + public function events() + { + return [Controller::EVENT_BEFORE_ACTION => 'beforeAction']; + } + + /** + * @param ActionEvent $event + * @return boolean + * @throws HttpException when the request method is not allowed. + */ + public function beforeAction($event) + { + $action = $event->action->id; + if (isset($this->actions[$action])) { + $verbs = $this->actions[$action]; + } elseif (isset($this->actions['*'])) { + $verbs = $this->actions['*']; + } else { + return $event->isValid; + } + + $verb = Yii::$app->getRequest()->getMethod(); + $allowed = array_map('strtoupper', $verbs); + if (!in_array($verb, array_map('strtoupper', $verbs))) { + $event->isValid = false; + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 + Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed)); + throw new MethodNotAllowedHttpException('Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed) . '.'); + } + + return $event->isValid; + } +} diff --git a/framework/yii/web/View.php b/framework/web/View.php similarity index 100% rename from framework/yii/web/View.php rename to framework/web/View.php diff --git a/framework/web/XmlResponseFormatter.php b/framework/web/XmlResponseFormatter.php new file mode 100644 index 0000000..292424a --- /dev/null +++ b/framework/web/XmlResponseFormatter.php @@ -0,0 +1,98 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\web; + +use DOMDocument; +use DOMElement; +use DOMText; +use yii\base\Arrayable; +use yii\base\Component; +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 <qiang.xue@gmail.com> + * @since 2.0 + */ +class XmlResponseFormatter extends Component implements ResponseFormatterInterface +{ + /** + * @var string the Content-Type header for the response + */ + public $contentType = 'application/xml'; + /** + * @var string the XML version + */ + public $version = '1.0'; + /** + * @var string the XML encoding. If not set, it will use the value of [[Response::charset]]. + */ + public $encoding; + /** + * @var string the name of the root element. + */ + public $rootTag = 'response'; + /** + * @var string the name of the elements that represent the array elements with numeric keys. + */ + public $itemTag = 'item'; + + /** + * Formats the specified response. + * @param Response $response the response to be formatted. + */ + public function format($response) + { + $response->getHeaders()->set('Content-Type', $this->contentType); + $dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding); + $root = new DOMElement($this->rootTag); + $dom->appendChild($root); + $this->buildXml($root, $response->data); + $response->content = $dom->saveXML(); + } + + /** + * @param DOMElement $element + * @param mixed $data + */ + protected function buildXml($element, $data) + { + if (is_object($data)) { + $child = new DOMElement(StringHelper::basename(get_class($data))); + $element->appendChild($child); + if ($data instanceof Arrayable) { + $this->buildXml($child, $data->toArray()); + } else { + $array = []; + foreach ($data as $name => $value) { + $array[$name] = $value; + } + $this->buildXml($child, $array); + } + } elseif (is_array($data)) { + foreach ($data as $name => $value) { + if (is_int($name) && is_object($value)) { + $this->buildXml($element, $value); + } elseif (is_array($value) || is_object($value)) { + $child = new DOMElement(is_int($name) ? $this->itemTag : $name); + $element->appendChild($child); + $this->buildXml($child, $value); + } else { + $child = new DOMElement(is_int($name) ? $this->itemTag : $name); + $element->appendChild($child); + $child->appendChild(new DOMText((string)$value)); + } + } + } else { + $element->appendChild(new DOMText((string)$data)); + } + } +} diff --git a/framework/yii/web/YiiAsset.php b/framework/web/YiiAsset.php similarity index 100% rename from framework/yii/web/YiiAsset.php rename to framework/web/YiiAsset.php diff --git a/framework/yii/widgets/ActiveField.php b/framework/widgets/ActiveField.php similarity index 100% rename from framework/yii/widgets/ActiveField.php rename to framework/widgets/ActiveField.php diff --git a/framework/widgets/ActiveForm.php b/framework/widgets/ActiveForm.php new file mode 100644 index 0000000..b218a2e --- /dev/null +++ b/framework/widgets/ActiveForm.php @@ -0,0 +1,357 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Widget; +use yii\base\Model; +use yii\helpers\Html; +use yii\helpers\Json; +use yii\web\JsExpression; + +/** + * ActiveForm ... + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class ActiveForm extends Widget +{ + /** + * @param array|string $action the form action URL. This parameter will be processed by [[\yii\helpers\Html::url()]]. + */ + public $action = ''; + /** + * @var string the form submission method. This should be either 'post' or 'get'. + * Defaults to 'post'. + */ + public $method = 'post'; + /** + * @var array the HTML attributes (name-value pairs) for the form tag. + * The values will be HTML-encoded using [[Html::encode()]]. + * If a value is null, the corresponding attribute will not be rendered. + */ + public $options = []; + /** + * @var array the default configuration used by [[field()]] when creating a new field object. + */ + public $fieldConfig; + /** + * @var string the default CSS class for the error summary container. + * @see errorSummary() + */ + public $errorSummaryCssClass = 'error-summary'; + /** + * @var string the CSS class that is added to a field container when the associated attribute is required. + */ + public $requiredCssClass = 'required'; + /** + * @var string the CSS class that is added to a field container when the associated attribute has validation error. + */ + public $errorCssClass = 'has-error'; + /** + * @var string the CSS class that is added to a field container when the associated attribute is successfully validated. + */ + public $successCssClass = 'has-success'; + /** + * @var string the CSS class that is added to a field container when the associated attribute is being validated. + */ + public $validatingCssClass = 'validating'; + /** + * @var boolean whether to enable client-side data validation. + * If [[ActiveField::enableClientValidation]] is set, its value will take precedence for that input field. + */ + public $enableClientValidation = true; + /** + * @var boolean whether to enable AJAX-based data validation. + * If [[ActiveField::enableAjaxValidation]] is set, its value will take precedence for that input field. + */ + public $enableAjaxValidation = false; + /** + * @var array|string the URL for performing AJAX-based validation. This property will be processed by + * [[Html::url()]]. Please refer to [[Html::url()]] for more details on how to configure this property. + * If this property is not set, it will take the value of the form's action attribute. + */ + public $validationUrl; + /** + * @var boolean whether to perform validation when the form is submitted. + */ + public $validateOnSubmit = true; + /** + * @var boolean whether to perform validation when an input field loses focus and its value is found changed. + * If [[ActiveField::validateOnChange]] is set, its value will take precedence for that input field. + */ + public $validateOnChange = true; + /** + * @var boolean whether to perform validation while the user is typing in an input field. + * If [[ActiveField::validateOnType]] is set, its value will take precedence for that input field. + * @see validationDelay + */ + public $validateOnType = false; + /** + * @var integer number of milliseconds that the validation should be delayed when an input field + * is changed or the user types in the field. + * If [[ActiveField::validationDelay]] is set, its value will take precedence for that input field. + */ + public $validationDelay = 200; + /** + * @var string the name of the GET parameter indicating the validation request is an AJAX request. + */ + public $ajaxVar = 'ajax'; + /** + * @var string|JsExpression a JS callback that will be called when the form is being submitted. + * The signature of the callback should be: + * + * ~~~ + * function ($form) { + * ...return false to cancel submission... + * } + * ~~~ + */ + public $beforeSubmit; + /** + * @var string|JsExpression a JS callback that is called before validating an attribute. + * The signature of the callback should be: + * + * ~~~ + * function ($form, attribute, messages) { + * ...return false to cancel the validation... + * } + * ~~~ + */ + public $beforeValidate; + /** + * @var string|JsExpression a JS callback that is called after validating an attribute. + * The signature of the callback should be: + * + * ~~~ + * function ($form, attribute, messages) { + * } + * ~~~ + */ + public $afterValidate; + /** + * @var array the client validation options for individual attributes. Each element of the array + * represents the validation options for a particular attribute. + * @internal + */ + public $attributes = []; + + /** + * Initializes the widget. + * This renders the form open tag. + */ + public function init() + { + if (!isset($this->options['id'])) { + $this->options['id'] = $this->getId(); + } + if (!isset($this->fieldConfig['class'])) { + $this->fieldConfig['class'] = ActiveField::className(); + } + echo Html::beginForm($this->action, $this->method, $this->options); + } + + /** + * Runs the widget. + * This registers the necessary javascript code and renders the form close tag. + */ + public function run() + { + if (!empty($this->attributes)) { + $id = $this->options['id']; + $options = Json::encode($this->getClientOptions()); + $attributes = Json::encode($this->attributes); + $view = $this->getView(); + ActiveFormAsset::register($view); + $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); + } + echo Html::endForm(); + } + + /** + * Returns the options for the form JS widget. + * @return array the options + */ + protected function getClientOptions() + { + $options = [ + 'errorSummary' => '.' . $this->errorSummaryCssClass, + 'validateOnSubmit' => $this->validateOnSubmit, + 'errorCssClass' => $this->errorCssClass, + 'successCssClass' => $this->successCssClass, + 'validatingCssClass' => $this->validatingCssClass, + 'ajaxVar' => $this->ajaxVar, + ]; + if ($this->validationUrl !== null) { + $options['validationUrl'] = Html::url($this->validationUrl); + } + foreach (['beforeSubmit', 'beforeValidate', 'afterValidate'] as $name) { + if (($value = $this->$name) !== null) { + $options[$name] = $value instanceof JsExpression ? $value : new JsExpression($value); + } + } + return $options; + } + + /** + * Generates a summary of the validation errors. + * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden. + * @param Model|Model[] $models the model(s) associated with this form + * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used. + * - footer: string, the footer HTML for the error summary. + * + * The rest of the options will be rendered as the attributes of the container 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 error summary + */ + public function errorSummary($models, $options = []) + { + if (!is_array($models)) { + $models = [$models]; + } + + $lines = []; + foreach ($models as $model) { + /** @var Model $model */ + foreach ($model->getFirstErrors() as $error) { + $lines[] = Html::encode($error); + } + } + + $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>'; + $footer = isset($options['footer']) ? $options['footer'] : ''; + unset($options['header'], $options['footer']); + + if (!isset($options['class'])) { + $options['class'] = $this->errorSummaryCssClass; + } else { + $options['class'] .= ' ' . $this->errorSummaryCssClass; + } + + if (!empty($lines)) { + $content = "<ul><li>" . implode("</li>\n<li>", $lines) . "</li><ul>"; + return Html::tag('div', $header . $content . $footer, $options); + } else { + $content = "<ul></ul>"; + $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none'; + return Html::tag('div', $header . $content . $footer, $options); + } + } + + /** + * Generates a form field. + * A form field is associated with a model and an attribute. It contains a label, an input and an error message + * and use them to interact with end users to collect their inputs for the attribute. + * @param Model $model the data model + * @param string $attribute the attribute name or expression. See [[Html::getAttributeName()]] for the format + * about attribute expression. + * @param array $options the additional configurations for the field object + * @return ActiveField the created ActiveField object + * @see fieldConfig + */ + public function field($model, $attribute, $options = []) + { + return Yii::createObject(array_merge($this->fieldConfig, $options, [ + 'model' => $model, + 'attribute' => $attribute, + 'form' => $this, + ])); + } + + /** + * Validates one or several models and returns an error message array indexed by the attribute IDs. + * This is a helper method that simplifies the way of writing AJAX validation code. + * + * For example, you may use the following code in a controller action to respond + * to an AJAX validation request: + * + * ~~~ + * $model = new Post; + * $model->load($_POST); + * if (Yii::$app->request->isAjax) { + * Yii::$app->response->format = Response::FORMAT_JSON; + * return ActiveForm::validate($model); + * } + * // ... respond to non-AJAX request ... + * ~~~ + * + * To validate multiple models, simply pass each model as a parameter to this method, like + * the following: + * + * ~~~ + * ActiveForm::validate($model1, $model2, ...); + * ~~~ + * + * @param Model $model the model to be validated + * @param mixed $attributes list of attributes that should be validated. + * If this parameter is empty, it means any attribute listed in the applicable + * validation rules should be validated. + * + * When this method is used to validate multiple models, this parameter will be interpreted + * as a model. + * + * @return array the error message array indexed by the attribute IDs. + */ + public static function validate($model, $attributes = null) + { + $result = []; + if ($attributes instanceof Model) { + // validating multiple models + $models = func_get_args(); + $attributes = null; + } else { + $models = [$model]; + } + /** @var Model $model */ + foreach ($models as $model) { + $model->validate($attributes); + foreach ($model->getErrors() as $attribute => $errors) { + $result[Html::getInputId($model, $attribute)] = $errors; + } + } + return $result; + } + + /** + * Validates an array of model instances and returns an error message array indexed by the attribute IDs. + * This is a helper method that simplifies the way of writing AJAX validation code for tabular input. + * + * For example, you may use the following code in a controller action to respond + * to an AJAX validation request: + * + * ~~~ + * // ... load $models ... + * if (Yii::$app->request->isAjax) { + * Yii::$app->response->format = Response::FORMAT_JSON; + * return ActiveForm::validateMultiple($models); + * } + * // ... respond to non-AJAX request ... + * ~~~ + * + * @param array $models an array of models to be validated. + * @param mixed $attributes list of attributes that should be validated. + * If this parameter is empty, it means any attribute listed in the applicable + * validation rules should be validated. + * @return array the error message array indexed by the attribute IDs. + */ + public static function validateMultiple($models, $attributes = null) + { + $result = []; + /** @var Model $model */ + foreach ($models as $i => $model) { + $model->validate($attributes); + foreach ($model->getErrors() as $attribute => $errors) { + $result[Html::getInputId($model, "[$i]" . $attribute)] = $errors; + } + } + return $result; + } +} diff --git a/framework/yii/widgets/ActiveFormAsset.php b/framework/widgets/ActiveFormAsset.php similarity index 100% rename from framework/yii/widgets/ActiveFormAsset.php rename to framework/widgets/ActiveFormAsset.php diff --git a/framework/widgets/BaseListView.php b/framework/widgets/BaseListView.php new file mode 100644 index 0000000..4c4e5a4 --- /dev/null +++ b/framework/widgets/BaseListView.php @@ -0,0 +1,215 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\InvalidConfigException; +use yii\base\Widget; +use yii\helpers\ArrayHelper; +use yii\helpers\Html; + +/** + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +abstract class BaseListView extends Widget +{ + /** + * @var array the HTML attributes for the container tag of the list view. + * The "tag" element specifies the tag name of the container element and defaults to "div". + */ + public $options = []; + /** + * @var \yii\data\DataProviderInterface the data provider for the view. This property is required. + */ + public $dataProvider; + /** + * @var array the configuration for the pager widget. By default, [[LinkPager]] will be + * used to render the pager. You can use a different widget class by configuring the "class" element. + */ + public $pager = []; + /** + * @var array the configuration for the sorter widget. By default, [[LinkSorter]] will be + * used to render the sorter. You can use a different widget class by configuring the "class" element. + */ + public $sorter = []; + /** + * @var string the HTML content to be displayed as the summary of the list view. + * If you do not want to show the summary, you may set it with an empty string. + * + * The following tokens will be replaced with the corresponding values: + * + * - `{begin}`: the starting row number (1-based) currently being displayed + * - `{end}`: the ending row number (1-based) currently being displayed + * - `{count}`: the number of rows currently being displayed + * - `{totalCount}`: the total number of rows available + * - `{page}`: the page number (1-based) current being displayed + * - `{pageCount}`: the number of pages available + */ + public $summary; + /** + * @var boolean whether to show the list view if [[dataProvider]] returns no data. + */ + 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: + * + * - `{summary}`: the summary section. See [[renderSummary()]]. + * - `{items}`: the list items. See [[renderItems()]]. + * - `{sorter}`: the sorter. See [[renderSorter()]]. + * - `{pager}`: the pager. See [[renderPager()]]. + */ + public $layout = "{summary}\n{items}\n{pager}"; + + + /** + * Renders the data models. + * @return string the rendering result. + */ + abstract public function renderItems(); + + /** + * Initializes the view. + */ + public function init() + { + 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(); + } + + /** + * Runs the widget. + */ + public function run() + { + 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->renderEmpty(); + } + $tag = ArrayHelper::remove($this->options, 'tag', 'div'); + echo Html::tag($tag, $content, $this->options); + } + + /** + * Renders a section of the specified name. + * If the named section is not supported, false will be returned. + * @param string $name the section name, e.g., `{summary}`, `{items}`. + * @return string|boolean the rendering result of the section, or false if the named section is not supported. + */ + public function renderSection($name) + { + switch ($name) { + case '{summary}': + return $this->renderSummary(); + case '{items}': + return $this->renderItems(); + case '{pager}': + return $this->renderPager(); + case '{sorter}': + return $this->renderSorter(); + default: + return false; + } + } + + /** + * Renders the HTML content indicating that the list view has no data. + * @return string the rendering result + * @see emptyText + */ + public function renderEmpty() + { + return '<div class="empty">' . ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText) . '</div>'; + } + + /** + * Renders the summary text. + */ + 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; + $end = $begin + $count - 1; + if ($begin > $end) { + $begin = $end; + } + $page = $pagination->getPage() + 1; + $pageCount = $pagination->pageCount; + if (($summaryContent = $this->summary) === null) { + $summaryContent = '<div class="summary">' + . Yii::t('yii', 'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.') + . '</div>'; + } + } else { + $begin = $page = $pageCount = 1; + $end = $totalCount = $count; + if (($summaryContent = $this->summary) === null) { + $summaryContent = '<div class="summary">' . Yii::t('yii', 'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.') . '</div>'; + } + } + return Yii::$app->getI18n()->format($summaryContent, [ + 'begin' => $begin, + 'end' => $end, + 'count' => $count, + 'totalCount' => $totalCount, + 'page' => $page, + 'pageCount' => $pageCount, + ], Yii::$app->language); + } + + /** + * Renders the pager. + * @return string the rendering result + */ + public function renderPager() + { + $pagination = $this->dataProvider->getPagination(); + if ($pagination === false || $this->dataProvider->getCount() <= 0) { + return ''; + } + /** @var LinkPager $class */ + $class = ArrayHelper::remove($this->pager, 'class', LinkPager::className()); + $this->pager['pagination'] = $pagination; + return $class::widget($this->pager); + } + + /** + * Renders the sorter. + * @return string the rendering result + */ + public function renderSorter() + { + $sort = $this->dataProvider->getSort(); + if ($sort === false || empty($sort->attributes) || $this->dataProvider->getCount() <= 0) { + return ''; + } + /** @var LinkSorter $class */ + $class = ArrayHelper::remove($this->sorter, 'class', LinkSorter::className()); + $this->sorter['sort'] = $sort; + return $class::widget($this->sorter); + } +} diff --git a/framework/widgets/Block.php b/framework/widgets/Block.php new file mode 100644 index 0000000..fdd210f --- /dev/null +++ b/framework/widgets/Block.php @@ -0,0 +1,49 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use yii\base\Widget; + +/** + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class Block extends Widget +{ + /** + * @var string the ID of this block. + */ + public $id; + /** + * @var boolean whether to render the block content in place. Defaults to false, + * meaning the captured block content will not be displayed. + */ + public $renderInPlace = false; + + /** + * Starts recording a block. + */ + public function init() + { + ob_start(); + ob_implicit_flush(false); + } + + /** + * Ends recording a block. + * This method stops output buffering and saves the rendering result as a named block in the controller. + */ + public function run() + { + $block = ob_get_clean(); + if ($this->renderInPlace) { + echo $block; + } + $this->view->blocks[$this->id] = $block; + } +} diff --git a/framework/widgets/Breadcrumbs.php b/framework/widgets/Breadcrumbs.php new file mode 100644 index 0000000..2353845 --- /dev/null +++ b/framework/widgets/Breadcrumbs.php @@ -0,0 +1,141 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Widget; +use yii\base\InvalidConfigException; +use yii\helpers\Html; + +/** + * Breadcrumbs displays a list of links indicating the position of the current page in the whole site hierarchy. + * + * For example, breadcrumbs like "Home / Sample Post / Edit" means the user is viewing an edit page + * for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" + * to return to the homepage. + * + * To use Breadcrumbs, you need to configure its [[links]] property, which specifies the links to be displayed. For example, + * + * ~~~ + * // $this is the view object currently being used + * echo Breadcrumbs::widget([ + * 'links' => [ + * ['label' => 'Sample Post', 'url' => ['post/edit', 'id' => 1]], + * 'Edit', + * ], + * ]); + * ~~~ + * + * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view. + * You can use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different + * views. In the layout view, you assign this view parameter to the [[links]] property like the following: + * + * ~~~ + * // $this is the view object currently being used + * echo Breadcrumbs::widget([ + * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], + * ]); + * ~~~ + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class Breadcrumbs extends Widget +{ + /** + * @var string the name of the breadcrumb container tag. + */ + public $tag = 'ul'; + /** + * @var array the HTML attributes for the breadcrumb container tag. + */ + public $options = ['class' => 'breadcrumb']; + /** + * @var boolean whether to HTML-encode the link labels. + */ + public $encodeLabels = true; + /** + * @var string the first hyperlink in the breadcrumbs (called home link). + * If this property is not set, it will default to a link pointing to [[\yii\web\Application::homeUrl]] + * with the label 'Home'. If this property is false, the home link will not be rendered. + */ + public $homeLink; + /** + * @var array list of links to appear in the breadcrumbs. If this property is empty, + * the widget will not render anything. Each array element represents a single link in the breadcrumbs + * with the following structure: + * + * ~~~ + * [ + * 'label' => 'label of the link', // required + * 'url' => 'url of the link', // optional, will be processed by Html::url() + * ] + * ~~~ + * + * If a link is active, you only need to specify its "label", and instead of writing `['label' => $label]`, + * you should simply use `$label`. + */ + public $links = []; + /** + * @var string the template used to render each inactive item in the breadcrumbs. The token `{link}` + * will be replaced with the actual HTML link for each inactive item. + */ + public $itemTemplate = "<li>{link}</li>\n"; + /** + * @var string the template used to render each active item in the breadcrumbs. The token `{link}` + * will be replaced with the actual HTML link for each active item. + */ + public $activeItemTemplate = "<li class=\"active\">{link}</li>\n"; + + /** + * Renders the widget. + */ + public function run() + { + if (empty($this->links)) { + return; + } + $links = []; + if ($this->homeLink === null) { + $links[] = $this->renderItem([ + 'label' => Yii::t('yii', 'Home'), + 'url' => Yii::$app->homeUrl, + ], $this->itemTemplate); + } elseif ($this->homeLink !== false) { + $links[] = $this->renderItem($this->homeLink, $this->itemTemplate); + } + foreach ($this->links as $link) { + if (!is_array($link)) { + $link = ['label' => $link]; + } + $links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate); + } + echo Html::tag($this->tag, implode('', $links), $this->options); + } + + /** + * Renders a single breadcrumb item. + * @param array $link the link to be rendered. It must contain the "label" element. The "url" element is optional. + * @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the link. + * @return string the rendering result + * @throws InvalidConfigException if `$link` does not have "label" element. + */ + protected function renderItem($link, $template) + { + if (isset($link['label'])) { + $label = $this->encodeLabels ? Html::encode($link['label']) : $link['label']; + } else { + throw new InvalidConfigException('The "label" element is required for each link.'); + } + if (isset($link['url'])) { + return strtr($template, ['{link}' => Html::a($label, $link['url'])]); + } else { + return strtr($template, ['{link}' => $label]); + } + } +} diff --git a/framework/widgets/ContentDecorator.php b/framework/widgets/ContentDecorator.php new file mode 100644 index 0000000..9224f35 --- /dev/null +++ b/framework/widgets/ContentDecorator.php @@ -0,0 +1,52 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use yii\base\InvalidConfigException; +use yii\base\Widget; + +/** + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class ContentDecorator extends Widget +{ + /** + * @var string the view file that will be used to decorate the content enclosed by this widget. + * This can be specified as either the view file path or path alias. + */ + public $viewFile; + /** + * @var array the parameters (name => value) to be extracted and made available in the decorative view. + */ + public $params = []; + + /** + * Starts recording a clip. + */ + public function init() + { + if ($this->viewFile === null) { + throw new InvalidConfigException('ContentDecorator::viewFile must be set.'); + } + ob_start(); + ob_implicit_flush(false); + } + + /** + * Ends recording a clip. + * This method stops output buffering and saves the rendering result as a named clip in the controller. + */ + public function run() + { + $params = $this->params; + $params['content'] = ob_get_clean(); + // render under the existing context + echo $this->view->renderFile($this->viewFile, $params); + } +} diff --git a/framework/widgets/DetailView.php b/framework/widgets/DetailView.php new file mode 100644 index 0000000..3a45a4e --- /dev/null +++ b/framework/widgets/DetailView.php @@ -0,0 +1,211 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Arrayable; +use yii\base\Formatter; +use yii\base\InvalidConfigException; +use yii\base\Model; +use yii\base\Widget; +use yii\helpers\ArrayHelper; +use yii\helpers\Html; +use yii\helpers\Inflector; + +/** + * DetailView displays the detail of a single data [[model]]. + * + * DetailView is best used for displaying a model in a regular format (e.g. each model attribute + * is displayed as a row in a table.) The model can be either an instance of [[Model]] + * or an associative array. + * + * DetailView uses the [[attributes]] property to determines which model attributes + * should be displayed and how they should be formatted. + * + * A typical usage of DetailView is as follows: + * + * ~~~ + * echo DetailView::widget([ + * 'model' => $model, + * 'attributes' => [ + * 'title', // title attribute (in plain text) + * 'description:html', // description attribute in HTML + * [ // the owner name of the model + * 'label' => 'Owner', + * 'value' => $model->owner->name, + * ], + * ], + * ]); + * ~~~ + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class DetailView extends Widget +{ + /** + * @var array|object the data model whose details are to be displayed. This can be either a [[Model]] instance + * or an associative array. + */ + public $model; + /** + * @var array a list of attributes to be displayed in the detail view. Each array element + * represents the specification for displaying one particular attribute. + * + * An attribute can be specified as a string in the format of "Name" or "Name:Format", where "Name" refers to + * the attribute name, and "Format" represents the format of the attribute. The "Format" is passed to the [[Formatter::format()]] + * method to format an attribute value into a displayable text. Please refer to [[Formatter]] for the supported types. + * + * An attribute can also be specified in terms of an array with the following elements: + * + * - name: the attribute name. This is required if either "label" or "value" is not specified. + * - label: the label associated with the attribute. If this is not specified, it will be generated from the attribute name. + * - value: the value to be displayed. If this is not specified, it will be retrieved from [[model]] using the attribute name + * by calling [[ArrayHelper::getValue()]]. Note that this value will be formatted into a displayable text + * according to the "format" option. + * - format: the type of the value that determines how the value would be formatted into a displayable text. + * Please refer to [[Formatter]] for supported types. + * - visible: whether the attribute is visible. If set to `false`, the attribute will NOT be displayed. + */ + public $attributes; + /** + * @var string|callback the template used to render a single attribute. If a string, the token `{label}` + * and `{value}` will be replaced with the label and the value of the corresponding attribute. + * If a callback (e.g. an anonymous function), the signature must be as follows: + * + * ~~~ + * function ($attribute, $index, $widget) + * ~~~ + * + * where `$attribute` refer to the specification of the attribute being rendered, `$index` is the zero-based + * index of the attribute in the [[attributes]] array, and `$widget` refers to this widget instance. + */ + public $template = "<tr><th>{label}</th><td>{value}</td></tr>"; + /** + * @var array the HTML attributes for the container tag of this widget. The "tag" option specifies + * what container tag should be used. It defaults to "table" if not set. + */ + public $options = ['class' => 'table table-striped table-bordered detail-view']; + /** + * @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]] + * instance. If this property is not set, the "formatter" application component will be used. + */ + public $formatter; + + /** + * Initializes the detail view. + * This method will initialize required property values. + */ + public function init() + { + if ($this->model === null) { + throw new InvalidConfigException('Please specify the "model" property.'); + } + if ($this->formatter == null) { + $this->formatter = Yii::$app->getFormatter(); + } elseif (is_array($this->formatter)) { + $this->formatter = Yii::createObject($this->formatter); + } + if (!$this->formatter instanceof Formatter) { + throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); + } + $this->normalizeAttributes(); + } + + /** + * Renders the detail view. + * This is the main entry of the whole detail view rendering. + */ + public function run() + { + $rows = []; + $i = 0; + foreach ($this->attributes as $attribute) { + $rows[] = $this->renderAttribute($attribute, $i++); + } + + $tag = ArrayHelper::remove($this->options, 'tag', 'table'); + echo Html::tag($tag, implode("\n", $rows), $this->options); + } + + /** + * Renders a single attribute. + * @param array $attribute the specification of the attribute to be rendered. + * @param integer $index the zero-based index of the attribute in the [[attributes]] array + * @return string the rendering result + */ + protected function renderAttribute($attribute, $index) + { + if (is_string($this->template)) { + return strtr($this->template, [ + '{label}' => $attribute['label'], + '{value}' => $this->formatter->format($attribute['value'], $attribute['format']), + ]); + } else { + return call_user_func($this->template, $attribute, $index, $this); + } + } + + /** + * Normalizes the attribute specifications. + * @throws InvalidConfigException + */ + protected function normalizeAttributes() + { + if ($this->attributes === null) { + if ($this->model instanceof Model) { + $this->attributes = $this->model->attributes(); + } elseif (is_object($this->model)) { + $this->attributes = $this->model instanceof Arrayable ? $this->model->toArray() : array_keys(get_object_vars($this->model)); + } elseif (is_array($this->model)) { + $this->attributes = array_keys($this->model); + } else { + throw new InvalidConfigException('The "model" property must be either an array or an object.'); + } + sort($this->attributes); + } + + foreach ($this->attributes as $i => $attribute) { + if (is_string($attribute)) { + if (!preg_match('/^(\w+)(\s*:\s*(\w+))?$/', $attribute, $matches)) { + throw new InvalidConfigException('The attribute must be specified in the format of "Name" or "Name:Format"'); + } + $attribute = [ + 'name' => $matches[1], + 'format' => isset($matches[3]) ? $matches[3] : 'text', + ]; + } + + if (!is_array($attribute)) { + throw new InvalidConfigException('The attribute configuration must be an array.'); + } + + if (isset($attribute['visible']) && !$attribute['visible']) { + continue; + } + + if (!isset($attribute['format'])) { + $attribute['format'] = 'text'; + } + if (isset($attribute['name'])) { + $name = $attribute['name']; + if (!isset($attribute['label'])) { + $attribute['label'] = $this->model instanceof Model ? $this->model->getAttributeLabel($name) : Inflector::camel2words($name, true); + } + if (!array_key_exists('value', $attribute)) { + $attribute['value'] = ArrayHelper::getValue($this->model, $name); + } + } elseif (!isset($attribute['label']) || !array_key_exists('value', $attribute)) { + throw new InvalidConfigException('The attribute configuration requires the "name" element to determine the value and display label.'); + } + + $this->attributes[$i] = $attribute; + } + } +} diff --git a/framework/widgets/FragmentCache.php b/framework/widgets/FragmentCache.php new file mode 100644 index 0000000..57c4659 --- /dev/null +++ b/framework/widgets/FragmentCache.php @@ -0,0 +1,178 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Widget; +use yii\caching\Cache; +use yii\caching\Dependency; + +/** + * + * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found + * in the cache. This property is read-only. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class FragmentCache extends Widget +{ + /** + * @var Cache|string the cache object or the application component ID of the cache object. + * After the FragmentCache object is created, if you want to change this property, + * you should only assign it with a cache object. + */ + public $cache = 'cache'; + /** + * @var integer number of seconds that the data can remain valid in cache. + * Use 0 to indicate that the cached data will never expire. + */ + public $duration = 60; + /** + * @var array|Dependency the dependency that the cached content depends on. + * This can be either a [[Dependency]] object or a configuration array for creating the dependency object. + * For example, + * + * ~~~ + * [ + * 'class' => 'yii\caching\DbDependency', + * 'sql' => 'SELECT MAX(lastModified) FROM Post', + * ] + * ~~~ + * + * would make the output cache depends on the last modified time of all posts. + * If any post has its modification time changed, the cached content would be invalidated. + */ + public $dependency; + /** + * @var array list of factors that would cause the variation of the content being cached. + * Each factor is a string representing a variation (e.g. the language, a GET parameter). + * The following variation setting will cause the content to be cached in different versions + * according to the current application language: + * + * ~~~ + * [ + * Yii::$app->language, + * ] + */ + public $variations; + /** + * @var boolean whether to enable the fragment cache. You may use this property to turn on and off + * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). + */ + public $enabled = true; + /** + * @var array a list of placeholders for embedding dynamic contents. This property + * is used internally to implement the content caching feature. Do not modify it. + */ + public $dynamicPlaceholders; + + /** + * Initializes the FragmentCache object. + */ + public function init() + { + parent::init(); + + if (!$this->enabled) { + $this->cache = null; + } elseif (is_string($this->cache)) { + $this->cache = Yii::$app->getComponent($this->cache); + } + + if ($this->getCachedContent() === false) { + $this->getView()->cacheStack[] = $this; + ob_start(); + ob_implicit_flush(false); + } + } + + /** + * Marks the end of content to be cached. + * Content displayed before this method call and after [[init()]] + * will be captured and saved in cache. + * This method does nothing if valid content is already found in cache. + */ + public function run() + { + if (($content = $this->getCachedContent()) !== false) { + echo $content; + } elseif ($this->cache instanceof Cache) { + $content = ob_get_clean(); + array_pop($this->getView()->cacheStack); + if (is_array($this->dependency)) { + $this->dependency = Yii::createObject($this->dependency); + } + $data = [$content, $this->dynamicPlaceholders]; + $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency); + + if (empty($this->getView()->cacheStack) && !empty($this->dynamicPlaceholders)) { + $content = $this->updateDynamicContent($content, $this->dynamicPlaceholders); + } + echo $content; + } + } + + /** + * @var string|boolean the cached content. False if the content is not cached. + */ + private $_content; + + /** + * Returns the cached content if available. + * @return string|boolean the cached content. False is returned if valid content is not found in the cache. + */ + public function getCachedContent() + { + if ($this->_content === null) { + $this->_content = false; + if ($this->cache instanceof Cache) { + $key = $this->calculateKey(); + $data = $this->cache->get($key); + if (is_array($data) && count($data) === 2) { + list ($content, $placeholders) = $data; + if (is_array($placeholders) && count($placeholders) > 0) { + if (empty($this->getView()->cacheStack)) { + // outermost cache: replace placeholder with dynamic content + $content = $this->updateDynamicContent($content, $placeholders); + } + foreach ($placeholders as $name => $statements) { + $this->getView()->addDynamicPlaceholder($name, $statements); + } + } + $this->_content = $content; + } + } + } + return $this->_content; + } + + protected function updateDynamicContent($content, $placeholders) + { + foreach ($placeholders as $name => $statements) { + $placeholders[$name] = $this->getView()->evaluateDynamicContent($statements); + } + return strtr($content, $placeholders); + } + + /** + * Generates a unique key used for storing the content in cache. + * The key generated depends on both [[id]] and [[variations]]. + * @return mixed a valid cache key + */ + protected function calculateKey() + { + $factors = [__CLASS__, $this->getId()]; + if (is_array($this->variations)) { + foreach ($this->variations as $factor) { + $factors[] = $factor; + } + } + return $factors; + } +} diff --git a/framework/widgets/InputWidget.php b/framework/widgets/InputWidget.php new file mode 100644 index 0000000..0a4b5b7 --- /dev/null +++ b/framework/widgets/InputWidget.php @@ -0,0 +1,72 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Widget; +use yii\base\Model; +use yii\base\InvalidConfigException; +use yii\helpers\Html; + +/** + * InputWidget is the base class for widgets that collect user inputs. + * + * An input widget can be associated with a data model and an attribute, + * or a name and a value. If the former, the name and the value will + * be generated automatically. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class InputWidget extends Widget +{ + /** + * @var Model the data model that this widget is associated with. + */ + public $model; + /** + * @var string the model attribute that this widget is associated with. + */ + public $attribute; + /** + * @var string the input name. This must be set if [[model]] and [[attribute]] are not set. + */ + public $name; + /** + * @var string the input value. + */ + public $value; + /** + * @var array the HTML attributes for the input tag. + */ + public $options = []; + + + /** + * Initializes the widget. + * If you override this method, make sure you call the parent implementation first. + */ + public function init() + { + if (!$this->hasModel() && $this->name === null) { + throw new InvalidConfigException("Either 'name', or 'model' and 'attribute' properties must be specified."); + } + if (!isset($this->options['id'])) { + $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId(); + } + parent::init(); + } + + /** + * @return boolean whether this widget is associated with a data model. + */ + protected function hasModel() + { + return $this->model instanceof Model && $this->attribute !== null; + } +} diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php new file mode 100644 index 0000000..807a4b8 --- /dev/null +++ b/framework/widgets/LinkPager.php @@ -0,0 +1,193 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\InvalidConfigException; +use yii\helpers\Html; +use yii\base\Widget; +use yii\data\Pagination; + +/** + * LinkPager displays a list of hyperlinks that lead to different pages of target. + * + * LinkPager works with a [[Pagination]] object which specifies the totally number + * of pages and the current page number. + * + * Note that LinkPager only generates the necessary HTML markups. In order for it + * to look like a real pager, you should provide some CSS styles for it. + * With the default configuration, LinkPager should look good using Twitter Bootstrap CSS framework. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class LinkPager extends Widget +{ + /** + * @var Pagination the pagination object that this pager is associated with. + * You must set this property in order to make LinkPager work. + */ + public $pagination; + /** + * @var array HTML attributes for the pager container tag. + */ + public $options = ['class' => 'pagination']; + /** + * @var string the CSS class for the "first" page button. + */ + public $firstPageCssClass = 'first'; + /** + * @var string the CSS class for the "last" page button. + */ + public $lastPageCssClass = 'last'; + /** + * @var string the CSS class for the "previous" page button. + */ + public $prevPageCssClass = 'prev'; + /** + * @var string the CSS class for the "next" page button. + */ + public $nextPageCssClass = 'next'; + /** + * @var string the CSS class for the active (currently selected) page button. + */ + public $activePageCssClass = 'active'; + /** + * @var string the CSS class for the disabled page buttons. + */ + public $disabledPageCssClass = 'disabled'; + /** + * @var integer maximum number of page buttons that can be displayed. Defaults to 10. + */ + public $maxButtonCount = 10; + /** + * @var string the label for the "next" page button. Note that this will NOT be HTML-encoded. + * If this property is null, the "next" page button will not be displayed. + */ + public $nextPageLabel = '»'; + /** + * @var string the text label for the previous page button. Note that this will NOT be HTML-encoded. + * If this property is null, the "previous" page button will not be displayed. + */ + public $prevPageLabel = '«'; + /** + * @var string the text label for the "first" page button. Note that this will NOT be HTML-encoded. + * If this property is null, the "first" page button will not be displayed. + */ + public $firstPageLabel; + /** + * @var string the text label for the "last" page button. Note that this will NOT be HTML-encoded. + * If this property is null, the "last" page button will not be displayed. + */ + public $lastPageLabel; + + + /** + * Initializes the pager. + */ + public function init() + { + if ($this->pagination === null) { + throw new InvalidConfigException('The "pagination" property must be set.'); + } + } + + /** + * Executes the widget. + * This overrides the parent implementation by displaying the generated page buttons. + */ + public function run() + { + echo $this->renderPageButtons(); + } + + /** + * Renders the page buttons. + * @return string the rendering result + */ + protected function renderPageButtons() + { + $buttons = []; + + $pageCount = $this->pagination->getPageCount(); + $currentPage = $this->pagination->getPage(); + + // first page + if ($this->firstPageLabel !== null) { + $buttons[] = $this->renderPageButton($this->firstPageLabel, 0, $this->firstPageCssClass, $currentPage <= 0, false); + } + + // prev page + if ($this->prevPageLabel !== null) { + if (($page = $currentPage - 1) < 0) { + $page = 0; + } + $buttons[] = $this->renderPageButton($this->prevPageLabel, $page, $this->prevPageCssClass, $currentPage <= 0, false); + } + + // internal pages + list($beginPage, $endPage) = $this->getPageRange(); + for ($i = $beginPage; $i <= $endPage; ++$i) { + $buttons[] = $this->renderPageButton($i + 1, $i, null, false, $i == $currentPage); + } + + // next page + if ($this->nextPageLabel !== null) { + if (($page = $currentPage + 1) >= $pageCount - 1) { + $page = $pageCount - 1; + } + $buttons[] = $this->renderPageButton($this->nextPageLabel, $page, $this->nextPageCssClass, $currentPage >= $pageCount - 1, false); + } + + // last page + if ($this->lastPageLabel !== null) { + $buttons[] = $this->renderPageButton($this->lastPageLabel, $pageCount - 1, $this->lastPageCssClass, $currentPage >= $pageCount - 1, false); + } + + return Html::tag('ul', implode("\n", $buttons), $this->options); + } + + /** + * Renders a page button. + * You may override this method to customize the generation of page buttons. + * @param string $label the text label for the button + * @param integer $page the page number + * @param string $class the CSS class for the page button. + * @param boolean $disabled whether this page button is disabled + * @param boolean $active whether this page button is active + * @return string the rendering result + */ + protected function renderPageButton($label, $page, $class, $disabled, $active) + { + $options = ['class' => $class === '' ? null : $class]; + if ($active) { + Html::addCssClass($options, $this->activePageCssClass); + } + if ($disabled) { + Html::addCssClass($options, $this->disabledPageCssClass); + return Html::tag('li', Html::tag('span', $label), $options); + } + return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), ['data-page' => $page]), $options); + } + + /** + * @return array the begin and end pages that need to be displayed. + */ + protected function getPageRange() + { + $currentPage = $this->pagination->getPage(); + $pageCount = $this->pagination->getPageCount(); + + $beginPage = max(0, $currentPage - (int)($this->maxButtonCount / 2)); + if (($endPage = $beginPage + $this->maxButtonCount - 1) >= $pageCount) { + $endPage = $pageCount - 1; + $beginPage = max(0, $endPage - $this->maxButtonCount + 1); + } + return [$beginPage, $endPage]; + } +} diff --git a/framework/widgets/LinkSorter.php b/framework/widgets/LinkSorter.php new file mode 100644 index 0000000..bfafcea --- /dev/null +++ b/framework/widgets/LinkSorter.php @@ -0,0 +1,74 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\InvalidConfigException; +use yii\base\Widget; +use yii\data\Sort; +use yii\helpers\Html; + +/** + * LinkSorter renders a list of sort links for the given sort definition. + * + * LinkSorter will generate a hyperlink for every attribute declared in [[sort]]. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class LinkSorter extends Widget +{ + /** + * @var Sort the sort definition + */ + public $sort; + /** + * @var array list of the attributes that support sorting. If not set, it will be determined + * using [[Sort::attributes]]. + */ + public $attributes; + /** + * @var array HTML attributes for the sorter container tag. + * See [[yii\helpers\Html::ul()]] for special attributes. + */ + public $options = ['class' => 'sorter']; + + + /** + * Initializes the sorter. + */ + public function init() + { + if ($this->sort === null) { + throw new InvalidConfigException('The "sort" property must be set.'); + } + } + + /** + * Executes the widget. + * This method renders the sort links. + */ + public function run() + { + echo $this->renderSortLinks(); + } + + /** + * Renders the sort links. + * @return string the rendering result + */ + protected function renderSortLinks() + { + $attributes = empty($this->attributes) ? array_keys($this->sort->attributes) : $this->attributes; + $links = []; + foreach ($attributes as $name) { + $links[] = $this->sort->link($name); + } + return Html::ul($links, array_merge($this->options, ['encode' => false])); + } +} diff --git a/framework/widgets/ListView.php b/framework/widgets/ListView.php new file mode 100644 index 0000000..43eaab4 --- /dev/null +++ b/framework/widgets/ListView.php @@ -0,0 +1,107 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\helpers\ArrayHelper; +use yii\helpers\Html; + +/** + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class ListView extends BaseListView +{ + /** + * @var array the HTML attributes for the container of the rendering result of each data model. + * The "tag" element specifies the tag name of the container element and defaults to "div". + * If "tag" is false, it means no container element will be rendered. + */ + public $itemOptions = []; + /** + * @var string|callback the name of the view for rendering each data item, or a callback (e.g. an anonymous function) + * for rendering each data item. If it specifies a view name, the following variables will + * be available in the view: + * + * - `$model`: mixed, the data model + * - `$key`: mixed, the key value associated with the data item + * - `$index`: integer, the zero-based index of the data item in the items array returned by [[dataProvider]]. + * - `$widget`: ListView, this widget instance + * + * Note that the view name is resolved into the view file by the current context of the [[view]] object. + * + * If this property is specified as a callback, it should have the following signature: + * + * ~~~ + * function ($model, $key, $index, $widget) + * ~~~ + */ + public $itemView; + /** + * @var array additional parameters to be passed to [[itemView]] when it is being rendered. + * This property is used only when [[itemView]] is a string representing a view name. + */ + public $viewParams = []; + /** + * @var string the HTML code to be displayed between any two consecutive items. + */ + public $separator = "\n"; + /** + * @var array the HTML attributes for the container tag of the list view. + * The "tag" element specifies the tag name of the container element and defaults to "div". + */ + public $options = ['class' => 'list-view']; + + + /** + * Renders all data models. + * @return string the rendering result + */ + public function renderItems() + { + $models = $this->dataProvider->getModels(); + $keys = $this->dataProvider->getKeys(); + $rows = []; + foreach (array_values($models) as $index => $model) { + $rows[] = $this->renderItem($model, $keys[$index], $index); + } + return implode($this->separator, $rows); + } + + /** + * Renders a single data model. + * @param mixed $model the data model to be rendered + * @param mixed $key the key value associated with the data model + * @param integer $index the zero-based index of the data model in the model array returned by [[dataProvider]]. + * @return string the rendering result + */ + public function renderItem($model, $key, $index) + { + if ($this->itemView === null) { + $content = $key; + } elseif (is_string($this->itemView)) { + $content = $this->getView()->render($this->itemView, array_merge([ + 'model' => $model, + 'key' => $key, + 'index' => $index, + 'widget' => $this, + ], $this->viewParams)); + } else { + $content = call_user_func($this->itemView, $model, $key, $index, $this); + } + $options = $this->itemOptions; + $tag = ArrayHelper::remove($options, 'tag', 'div'); + if ($tag !== false) { + $options['data-key'] = is_array($key) ? json_encode($key) : $key; + return Html::tag($tag, $content, $options); + } else { + return $content; + } + } +} diff --git a/framework/widgets/MaskedInput.php b/framework/widgets/MaskedInput.php new file mode 100644 index 0000000..7eb42a7 --- /dev/null +++ b/framework/widgets/MaskedInput.php @@ -0,0 +1,129 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use yii\base\InvalidConfigException; +use yii\helpers\Html; +use yii\helpers\Json; +use yii\web\JsExpression; + +/** + * MaskedInput generates a masked text input. + * + * MaskedInput is similar to [[Html::textInput()]] except that + * an input mask will be used to force users to enter properly formatted data, + * such as phone numbers, social security numbers. + * + * To use MaskedInput, you must set the [[mask]] property. The following example + * shows how to use MaskedInput to collect phone numbers: + * + * ~~~ + * echo MaskedInput::widget([ + * 'name' => 'phone', + * 'mask' => '999-999-9999', + * ]); + * ~~~ + * + * The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin). + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class MaskedInput extends InputWidget +{ + /** + * @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined: + * + * - `a`: represents an alpha character (A-Z,a-z) + * - `9`: represents a numeric character (0-9) + * - `*`: represents an alphanumeric character (A-Z,a-z,0-9) + * - `?`: anything listed after '?' within the mask is considered optional user input + * + * Additional characters can be defined by specifying the [[charMap]] property. + */ + public $mask; + /** + * @var array the mapping between mask characters and the corresponding patterns. + * For example, `['~' => '[+-]']` specifies that the '~' character expects '+' or '-' input. + * Defaults to null, meaning using the map as described in [[mask]]. + */ + public $charMap; + /** + * @var string the character prompting for user input. Defaults to underscore '_'. + */ + public $placeholder; + /** + * @var string a JavaScript function callback that will be invoked when user finishes the input. + */ + public $completed; + + + /** + * Initializes the widget. + * @throws InvalidConfigException if the "mask" property is not set. + */ + public function init() + { + parent::init(); + if (empty($this->mask)) { + throw new InvalidConfigException('The "mask" property must be set.'); + } + } + + /** + * Runs the widget. + */ + public function run() + { + if ($this->hasModel()) { + echo Html::activeTextInput($this->model, $this->attribute, $this->options); + } else { + echo Html::textInput($this->name, $this->value, $this->options); + } + $this->registerClientScript(); + } + + /** + * Registers the needed JavaScript. + */ + public function registerClientScript() + { + $options = $this->getClientOptions(); + $options = empty($options) ? '' : ',' . Json::encode($options); + $js = ''; + if (is_array($this->charMap) && !empty($this->charMap)) { + $js .= 'jQuery.mask.definitions=' . Json::encode($this->charMap) . ";\n"; + } + $id = $this->options['id']; + $js .= "jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});"; + $view = $this->getView(); + MaskedInputAsset::register($view); + $view->registerJs($js); + } + + /** + * @return array the options for the text field + */ + protected function getClientOptions() + { + $options = []; + if ($this->placeholder !== null) { + $options['placeholder'] = $this->placeholder; + } + + if ($this->completed !== null) { + if ($this->completed instanceof JsExpression) { + $options['completed'] = $this->completed; + } else { + $options['completed'] = new JsExpression($this->completed); + } + } + + return $options; + } +} diff --git a/framework/yii/widgets/MaskedInputAsset.php b/framework/widgets/MaskedInputAsset.php similarity index 100% rename from framework/yii/widgets/MaskedInputAsset.php rename to framework/widgets/MaskedInputAsset.php diff --git a/framework/widgets/Menu.php b/framework/widgets/Menu.php new file mode 100644 index 0000000..d5ff8ef --- /dev/null +++ b/framework/widgets/Menu.php @@ -0,0 +1,307 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use Yii; +use yii\base\Widget; +use yii\helpers\ArrayHelper; +use yii\helpers\Html; + +/** + * Menu displays a multi-level menu using nested HTML lists. + * + * The main property of Menu is [[items]], which specifies the possible items in the menu. + * A menu item can contain sub-items which specify the sub-menu under that menu item. + * + * Menu checks the current route and request parameters to toggle certain menu items + * with active state. + * + * Note that Menu only renders the HTML tags about the menu. It does do any styling. + * You are responsible to provide CSS styles to make it look like a real menu. + * + * The following example shows how to use Menu: + * + * ~~~ + * echo Menu::widget([ + * 'items' => [ + * // Important: you need to specify url as 'controller/action', + * // not just as 'controller' even if default action is used. + * ['label' => 'Home', 'url' => ['site/index']], + * // 'Products' menu item will be selected as long as the route is 'product/index' + * ['label' => 'Products', 'url' => ['product/index'], 'items' => [ + * ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], + * ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], + * ]], + * ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], + * ], + * ]); + * ~~~ + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @since 2.0 + */ +class Menu extends Widget +{ + /** + * @var array list of menu items. Each menu item should be an array of the following structure: + * + * - label: string, optional, specifies the menu item label. When [[encodeLabels]] is true, the label + * will be HTML-encoded. If the label is not specified, an empty string will be used. + * - url: string or array, optional, specifies the URL of the menu item. It will be processed by [[Html::url]]. + * When this is set, the actual menu item content will be generated using [[linkTemplate]]; + * otherwise, [[labelTemplate]] will be used. + * - visible: boolean, optional, whether this menu item is visible. Defaults to true. + * - items: array, optional, specifies the sub-menu items. Its format is the same as the parent items. + * - active: boolean, optional, whether this menu item is in active state (currently selected). + * If a menu item is active, its CSS class will be appended with [[activeCssClass]]. + * If this option is not set, the menu item will be set active automatically when the current request + * is triggered by [[url]]. For more details, please refer to [[isItemActive()]]. + * - template: string, optional, the template used to render the content of this menu item. + * The token `{url}` will be replaced by the URL associated with this menu item, + * and the token `{label}` will be replaced by the label of the menu item. + * If this option is not set, [[linkTemplate]] or [[labelTemplate]] will be used instead. + * - options: array, optional, the HTML attributes for the menu container tag. + */ + public $items = []; + /** + * @var array list of HTML attributes for the menu container tag. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. + */ + public $itemOptions = []; + /** + * @var string the template used to render the body of a menu which is a link. + * In this template, the token `{url}` will be replaced with the corresponding link URL; + * while `{label}` will be replaced with the link text. + * This property will be overridden by the `template` option set in individual menu items via [[items]]. + */ + public $linkTemplate = '<a href="{url}">{label}</a>'; + /** + * @var string the template used to render the body of a menu which is NOT a link. + * In this template, the token `{label}` will be replaced with the label of the menu item. + * This property will be overridden by the `template` option set in individual menu items via [[items]]. + */ + public $labelTemplate = '{label}'; + /** + * @var string the template used to render a list of sub-menus. + * In this template, the token `{items}` will be replaced with the renderer sub-menu items. + */ + public $submenuTemplate = "\n<ul>\n{items}\n</ul>\n"; + /** + * @var boolean whether the labels for menu items should be HTML-encoded. + */ + public $encodeLabels = true; + /** + * @var string the CSS class to be appended to the active menu item. + */ + public $activeCssClass = 'active'; + /** + * @var boolean whether to automatically activate items according to whether their route setting + * matches the currently requested route. + * @see isItemActive() + */ + public $activateItems = true; + /** + * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active. + * The activated parent menu items will also have its CSS classes appended with [[activeCssClass]]. + */ + public $activateParents = false; + /** + * @var boolean whether to hide empty menu items. An empty menu item is one whose `url` option is not + * set and which has no visible child menu items. + */ + public $hideEmptyItems = true; + /** + * @var array the HTML attributes for the menu's container tag. The following special options are recognized: + * + * - tag: string, defaults to "ul", the tag name of the item container tags. + */ + public $options = []; + /** + * @var string the CSS class that will be assigned to the first item in the main menu or each submenu. + * Defaults to null, meaning no such CSS class will be assigned. + */ + public $firstItemCssClass; + /** + * @var string the CSS class that will be assigned to the last item in the main menu or each submenu. + * Defaults to null, meaning no such CSS class will be assigned. + */ + public $lastItemCssClass; + /** + * @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() + */ + 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() + */ + public $params; + + + /** + * Renders the menu. + */ + public function run() + { + if ($this->route === null && Yii::$app->controller !== null) { + $this->route = Yii::$app->controller->getRoute(); + } + if ($this->params === null) { + $this->params = $_GET; + } + $items = $this->normalizeItems($this->items, $hasActiveChild); + $options = $this->options; + $tag = ArrayHelper::remove($options, 'tag', 'ul'); + echo Html::tag($tag, $this->renderItems($items), $options); + } + + /** + * Recursively renders the menu items (without the container tag). + * @param array $items the menu items to be rendered recursively + * @return string the rendering result + */ + protected function renderItems($items) + { + $n = count($items); + $lines = []; + foreach ($items as $i => $item) { + $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', [])); + $tag = ArrayHelper::remove($options, 'tag', 'li'); + $class = []; + if ($item['active']) { + $class[] = $this->activeCssClass; + } + if ($i === 0 && $this->firstItemCssClass !== null) { + $class[] = $this->firstItemCssClass; + } + if ($i === $n - 1 && $this->lastItemCssClass !== null) { + $class[] = $this->lastItemCssClass; + } + if (!empty($class)) { + if (empty($options['class'])) { + $options['class'] = implode(' ', $class); + } else { + $options['class'] .= ' ' . implode(' ', $class); + } + } + + $menu = $this->renderItem($item); + if (!empty($item['items'])) { + $menu .= strtr($this->submenuTemplate, [ + '{items}' => $this->renderItems($item['items']), + ]); + } + $lines[] = Html::tag($tag, $menu, $options); + } + return implode("\n", $lines); + } + + /** + * Renders the content of a menu item. + * Note that the container and the sub-menus are not rendered here. + * @param array $item the menu item to be rendered. Please refer to [[items]] to see what data might be in the item. + * @return string the rendering result + */ + protected function renderItem($item) + { + if (isset($item['url'])) { + $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate); + return strtr($template, [ + '{url}' => Html::url($item['url']), + '{label}' => $item['label'], + ]); + } else { + $template = ArrayHelper::getValue($item, 'template', $this->labelTemplate); + return strtr($template, [ + '{label}' => $item['label'], + ]); + } + } + + /** + * Normalizes the [[items]] property to remove invisible items and activate certain items. + * @param array $items the items to be normalized. + * @param boolean $active whether there is an active child menu item. + * @return array the normalized menu items + */ + protected function normalizeItems($items, &$active) + { + foreach ($items as $i => $item) { + if (isset($item['visible']) && !$item['visible']) { + unset($items[$i]); + continue; + } + if (!isset($item['label'])) { + $item['label'] = ''; + } + if ($this->encodeLabels) { + $items[$i]['label'] = Html::encode($item['label']); + } + $hasActiveChild = false; + if (isset($item['items'])) { + $items[$i]['items'] = $this->normalizeItems($item['items'], $hasActiveChild); + if (empty($items[$i]['items']) && $this->hideEmptyItems) { + unset($items[$i]['items']); + if (!isset($item['url'])) { + unset($items[$i]); + continue; + } + } + } + if (!isset($item['active'])) { + if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item)) { + $active = $items[$i]['active'] = true; + } else { + $items[$i]['active'] = false; + } + } elseif ($item['active']) { + $active = true; + } + } + return array_values($items); + } + + /** + * Checks whether a menu item is active. + * This is done by checking if [[route]] and [[params]] match that specified in the `url` option of the menu item. + * When the `url` option of a menu item is specified in terms of an array, its first element is treated + * as the route for the item and the rest of the elements are the associated parameters. + * Only when its route and parameters match [[route]] and [[params]], respectively, will a menu item + * be considered active. + * @param array $item the menu item to be checked + * @return boolean whether the menu item is active + */ + protected function isItemActive($item) + { + if (isset($item['url']) && is_array($item['url']) && isset($item['url'][0])) { + $route = $item['url'][0]; + if ($route[0] !== '/' && Yii::$app->controller) { + $route = Yii::$app->controller->module->getUniqueId() . '/' . $route; + } + if (ltrim($route, '/') !== $this->route) { + return false; + } + unset($item['url']['#']); + if (count($item['url']) > 1) { + foreach (array_splice($item['url'], 1) as $name => $value) { + if (!isset($this->params[$name]) || $this->params[$name] != $value) { + return false; + } + } + } + return true; + } + return false; + } +} diff --git a/framework/widgets/Spaceless.php b/framework/widgets/Spaceless.php new file mode 100644 index 0000000..8115f85 --- /dev/null +++ b/framework/widgets/Spaceless.php @@ -0,0 +1,69 @@ +<?php +/** + * @link http://www.yiiframework.com/ + * @copyright Copyright (c) 2008 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +namespace yii\widgets; + +use yii\base\Widget; + +/** + * Spaceless widget removes whitespace characters between HTML tags. Whitespaces within HTML tags + * or in a plain text are always left untouched. + * + * Usage example: + * + * ```php + * <body> + * <?php Spaceless::begin(); ?> + * <div class="nav-bar"> + * <!-- tags --> + * </div> + * <div class="content"> + * <!-- tags --> + * </div> + * <?php Spaceless::end(); ?> + * </body> + * ``` + * + * This example will generate the following HTML: + * + * ```html + * <body> + * <div class="navbar"><!-- other tags --></div><div class="content"><!-- other tags --></div></body> + * ``` + * + * This method is not designed for content compression (you should use `gzip` output compression to + * achieve it). Main intention is to strip out extra whitespace characters between HTML tags in order + * to avoid browser rendering quirks in some circumstances (e.g. newlines between inline-block elements). + * + * Note, never use this method with `pre` or `textarea` tags. It's not that trivial to deal with such tags + * as it may seem at first sight. For this case you should consider using + * [HTML Tidy Project](http://tidy.sourceforge.net/) instead. + * + * @see http://tidy.sourceforge.net/ + * @author resurtm <resurtm@gmail.com> + * @since 2.0 + */ +class Spaceless extends Widget +{ + /** + * Starts capturing an output to be cleaned from whitespace characters between HTML tags. + */ + public function init() + { + ob_start(); + ob_implicit_flush(false); + } + + /** + * Marks the end of content to be cleaned from whitespace characters between HTML tags. + * Stops capturing an output and echoes cleaned result. + */ + public function run() + { + echo trim(preg_replace('/>\s+</', '><', ob_get_clean())); + } +} diff --git a/framework/yii/yii b/framework/yii similarity index 100% rename from framework/yii/yii rename to framework/yii diff --git a/framework/yii/yii.bat b/framework/yii.bat similarity index 100% rename from framework/yii/yii.bat rename to framework/yii.bat diff --git a/framework/yii/requirements/views/web/index.php b/framework/yii/requirements/views/web/index.php deleted file mode 100644 index 287d4bb..0000000 --- a/framework/yii/requirements/views/web/index.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -/* @var YiiRequirementChecker $this */ -/* @var array $summary */ -/* @var array[] $requirements */ -?> -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="utf-8"/> - <title>Yii Application Requirement Checker</title> - <?php $this->renderViewFile(dirname(__FILE__) . '/css.php'); ?> -</head> -<body> -<div class="container"> - <div class="header"> - <h1>Yii Application Requirement Checker</h1> - </div> - <hr> - - <div class="content"> - <h3>Description</h3> - <p> - This script checks if your server configuration meets the requirements - for running Yii application. - It checks if the server is running the right version of PHP, - if appropriate PHP extensions have been loaded, and if php.ini file settings are correct. - </p> - <p> - There are two kinds of requirements being checked. Mandatory requirements are those that have to be met - to allow Yii to work as expected. There are also some optional requirements beeing checked which will - show you a warning when they do not meet. You can use Yii framework without them but some specific - functionality may be not available in this case. - </p> - - <h3>Conclusion</h3> - <?php if ($summary['errors'] > 0): ?> - <div class="alert alert-error"> - <strong>Unfortunately your server configuration does not satisfy the requirements by this application.<br>Please refer to the table below for detailed explanation.</strong> - </div> - <?php elseif ($summary['warnings'] > 0): ?> - <div class="alert alert-info"> - <strong>Your server configuration satisfies the minimum requirements by this application.<br>Please pay attention to the warnings listed below and check if your application will use the corresponding features.</strong> - </div> - <?php else: ?> - <div class="alert alert-success"> - <strong>Congratulations! Your server configuration satisfies all requirements.</strong> - </div> - <?php endif; ?> - - <h3>Details</h3> - - <table class="table table-bordered"> - <tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr> - <?php foreach($requirements as $requirement): ?> - <tr class="<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'error' : 'warning') ?>"> - <td> - <?php echo $requirement['name'] ?> - </td> - <td> - <span class="result"><?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning') ?></span> - </td> - <td> - <?php echo $requirement['by'] ?> - </td> - <td> - <?php echo $requirement['memo'] ?> - </td> - </tr> - <?php endforeach; ?> - </table> - - </div> - - <hr> - - <div class="footer"> - <p>Server: <?php echo $this->getServerInfo() . ' ' . $this->getNowDate() ?></p> - <p>Powered by <a href="http://www.yiiframework.com/" rel="external">Yii Framework</a></p> - </div> -</div> -</body> -</html> diff --git a/framework/yii/validators/InlineValidator.php b/framework/yii/validators/InlineValidator.php deleted file mode 100644 index b769e4e..0000000 --- a/framework/yii/validators/InlineValidator.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -/** - * InlineValidator represents a validator which is defined as a method in the object being validated. - * - * The validation method must have the following signature: - * - * ~~~ - * function foo($attribute, $params) - * ~~~ - * - * where `$attribute` refers to the name of the attribute being validated, while `$params` - * is an array representing the additional parameters supplied in the validation rule. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -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. The signature of the method should be like the following: - * - * ~~~ - * function foo($attribute, $params) - * ~~~ - */ - public $method; - /** - * @var array additional parameters that are passed to the validation method - */ - public $params; - /** - * @var string|\Closure an anonymous function or the name of a model class method that returns the client validation code. - * The signature of the method should be like the following: - * - * ~~~ - * function foo($attribute, $params) - * { - * return "javascript"; - * } - * ~~~ - * - * where `$attribute` refers to the attribute name to be validated. - * - * Please refer to [[clientValidateAttribute()]] for details on how to return client validation code. - */ - public $clientValidate; - - /** - * @inheritdoc - */ - public function validateAttribute($object, $attribute) - { - $method = $this->method; - if (is_string($method)) { - $method = [$object, $method]; - } - call_user_func($method, $attribute, $this->params); - } - - /** - * @inheritdoc - */ - public function clientValidateAttribute($object, $attribute, $view) - { - if ($this->clientValidate !== null) { - $method = $this->clientValidate; - if (is_string($method)) { - $method = [$object, $method]; - } - return call_user_func($method, $attribute, $this->params); - } else { - return null; - } - } -} diff --git a/framework/yii/validators/RangeValidator.php b/framework/yii/validators/RangeValidator.php deleted file mode 100644 index a4da139..0000000 --- a/framework/yii/validators/RangeValidator.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -use Yii; -use yii\base\InvalidConfigException; -use yii\helpers\Html; - -/** - * RangeValidator validates that the attribute value is among a list of values. - * - * The range can be specified via the [[range]] property. - * If the [[not]] property is set true, the validator will ensure the attribute value - * is NOT among the specified range. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class RangeValidator extends Validator -{ - /** - * @var array list of valid values that the attribute value should be among - */ - public $range; - /** - * @var boolean whether the comparison is strict (both type and value must be the same) - */ - public $strict = false; - /** - * @var boolean whether to invert the validation logic. Defaults to false. If set to true, - * the attribute value should NOT be among the list of values defined via [[range]]. - **/ - public $not = false; - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - if (!is_array($this->range)) { - throw new InvalidConfigException('The "range" property must be set.'); - } - if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} is invalid.'); - } - } - - /** - * @inheritdoc - */ - protected function validateValue($value) - { - $valid = !$this->not && in_array($value, $this->range, $this->strict) - || $this->not && !in_array($value, $this->range, $this->strict); - return $valid ? null : [$this->message, []]; - } - - /** - * @inheritdoc - */ - public function clientValidateAttribute($object, $attribute, $view) - { - $range = []; - foreach ($this->range as $value) { - $range[] = (string)$value; - } - $options = [ - 'range' => $range, - 'not' => $this->not, - 'message' => strtr($this->message, [ - '{attribute}' => $object->getAttributeLabel($attribute), - ]), - ]; - if ($this->skipOnEmpty) { - $options['skipOnEmpty'] = 1; - } - - ValidationAsset::register($view); - return 'yii.validation.range(value, messages, ' . json_encode($options) . ');'; - } -} diff --git a/framework/yii/validators/RegularExpressionValidator.php b/framework/yii/validators/RegularExpressionValidator.php deleted file mode 100644 index 28e9bdc..0000000 --- a/framework/yii/validators/RegularExpressionValidator.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -use Yii; -use yii\base\InvalidConfigException; -use yii\helpers\Html; -use yii\web\JsExpression; -use yii\helpers\Json; - -/** - * RegularExpressionValidator validates that the attribute value matches the specified [[pattern]]. - * - * If the [[not]] property is set true, the validator will ensure the attribute value do NOT match the [[pattern]]. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class RegularExpressionValidator extends Validator -{ - /** - * @var string the regular expression to be matched with - */ - public $pattern; - /** - * @var boolean whether to invert the validation logic. Defaults to false. If set to true, - * the regular expression defined via [[pattern]] should NOT match the attribute value. - **/ - public $not = false; - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - if ($this->pattern === null) { - throw new InvalidConfigException('The "pattern" property must be set.'); - } - if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} is invalid.'); - } - } - - /** - * @inheritdoc - */ - protected function validateValue($value) - { - $valid = !is_array($value) && - (!$this->not && preg_match($this->pattern, $value) - || $this->not && !preg_match($this->pattern, $value)); - return $valid ? null : [$this->message, []]; - } - - /** - * @inheritdoc - */ - public function clientValidateAttribute($object, $attribute, $view) - { - $pattern = $this->pattern; - $pattern = preg_replace('/\\\\x\{?([0-9a-fA-F]+)\}?/', '\u$1', $pattern); - $deliminator = substr($pattern, 0, 1); - $pos = strrpos($pattern, $deliminator, 1); - $flag = substr($pattern, $pos + 1); - if ($deliminator !== '/') { - $pattern = '/' . str_replace('/', '\\/', substr($pattern, 1, $pos - 1)) . '/'; - } else { - $pattern = substr($pattern, 0, $pos + 1); - } - if (!empty($flag)) { - $pattern .= preg_replace('/[^igm]/', '', $flag); - } - - $options = [ - 'pattern' => new JsExpression($pattern), - 'not' => $this->not, - 'message' => strtr($this->message, [ - '{attribute}' => $object->getAttributeLabel($attribute), - ]), - ]; - if ($this->skipOnEmpty) { - $options['skipOnEmpty'] = 1; - } - - ValidationAsset::register($view); - return 'yii.validation.regularExpression(value, messages, ' . Json::encode($options) . ');'; - } -} diff --git a/framework/yii/validators/RequiredValidator.php b/framework/yii/validators/RequiredValidator.php deleted file mode 100644 index f291f39..0000000 --- a/framework/yii/validators/RequiredValidator.php +++ /dev/null @@ -1,111 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -use Yii; -use yii\helpers\Html; - -/** - * RequiredValidator validates that the specified attribute does not have null or empty value. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class RequiredValidator extends Validator -{ - /** - * @var boolean whether to skip this validator if the value being validated is empty. - */ - public $skipOnEmpty = false; - /** - * @var mixed the desired value that the attribute must have. - * If this is null, the validator will validate that the specified attribute is not empty. - * If this is set as a value that is not null, the validator will validate that - * the attribute has a value that is the same as this property value. - * Defaults to null. - * @see strict - */ - public $requiredValue; - /** - * @var boolean whether the comparison between the attribute value and [[requiredValue]] is strict. - * When this is true, both the values and types must match. - * Defaults to false, meaning only the values need to match. - * Note that when [[requiredValue]] is null, if this property is true, the validator will check - * if the attribute value is null; If this property is false, the validator will call [[isEmpty]] - * to check if the attribute value is empty. - */ - public $strict = false; - /** - * @var string the user-defined error message. It may contain the following placeholders which - * will be replaced accordingly by the validator: - * - * - `{attribute}`: the label of the attribute being validated - * - `{value}`: the value of the attribute being validated - * - `{requiredValue}`: the value of [[requiredValue]] - */ - public $message; - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - if ($this->message === null) { - $this->message = $this->requiredValue === null ? Yii::t('yii', '{attribute} cannot be blank.') - : Yii::t('yii', '{attribute} must be "{requiredValue}".'); - } - } - - /** - * @inheritdoc - */ - protected function validateValue($value) - { - if ($this->requiredValue === null) { - if ($this->strict && $value !== null || !$this->strict && !$this->isEmpty($value, true)) { - return null; - } - } elseif (!$this->strict && $value == $this->requiredValue || $this->strict && $value === $this->requiredValue) { - return null; - } - if ($this->requiredValue === null) { - return [$this->message, []]; - } else { - return [$this->message, [ - 'requiredValue' => $this->requiredValue, - ]]; - } - } - - /** - * @inheritdoc - */ - public function clientValidateAttribute($object, $attribute, $view) - { - $options = []; - if ($this->requiredValue !== null) { - $options['message'] = strtr($this->message, [ - '{requiredValue}' => $this->requiredValue, - ]); - $options['requiredValue'] = $this->requiredValue; - } else { - $options['message'] = $this->message; - } - if ($this->strict) { - $options['strict'] = 1; - } - - $options['message'] = strtr($options['message'], [ - '{attribute}' => $object->getAttributeLabel($attribute), - ]); - - ValidationAsset::register($view); - return 'yii.validation.required(value, messages, ' . json_encode($options) . ');'; - } -} diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php deleted file mode 100644 index 51474b0..0000000 --- a/framework/yii/validators/UniqueValidator.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -use Yii; -use yii\db\ActiveRecordInterface; - -/** - * UniqueValidator validates that the attribute value is unique in the specified database table. - * - * UniqueValidator checks if the value being validated is unique in the table column specified by - * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]]. - * - * The followings are examples of validation rules using this validator: - * - * ```php - * // a1 needs to be unique - * ['a1', 'unique'] - * // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value - * ['a1', 'unique', 'targetAttribute' => 'a2'] - * // a1 and a2 need to unique together, and they both will receive error message - * [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']] - * // a1 and a2 need to unique together, only a1 will receive error message - * ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']] - * // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value) - * ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']] - * ``` - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class UniqueValidator extends Validator -{ - /** - * @var string the name of the ActiveRecord class that should be used to validate the uniqueness - * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated. - * @see targetAttribute - */ - public $targetClass; - /** - * @var string|array the name of the ActiveRecord attribute that should be used to - * validate the uniqueness of the current attribute value. If not set, it will use the name - * of the attribute currently being validated. You may use an array to validate the uniqueness - * of multiple columns at the same time. The array values are the attributes that will be - * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated. - * If the key and the value are the same, you can just specify the value. - */ - public $targetAttribute; - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} "{value}" has already been taken.'); - } - } - - /** - * @inheritdoc - */ - public function validateAttribute($object, $attribute) - { - /** @var ActiveRecordInterface $targetClass */ - $targetClass = $this->targetClass === null ? get_class($object) : $this->targetClass; - $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute; - - if (is_array($targetAttribute)) { - $params = []; - foreach ($targetAttribute as $k => $v) { - $params[$v] = is_integer($k) ? $object->$v : $object->$k; - } - } else { - $params = [$targetAttribute => $object->$attribute]; - } - - foreach ($params as $value) { - if (is_array($value)) { - $this->addError($object, $attribute, Yii::t('yii', '{attribute} is invalid.')); - return; - } - } - - $query = $targetClass::find(); - $query->where($params); - - if (!$object instanceof ActiveRecordInterface || $object->getIsNewRecord()) { - // if current $object isn't in the database yet then it's OK just to call exists() - $exists = $query->exists(); - } else { - // if current $object is in the database already we can't use exists() - /** @var ActiveRecordInterface[] $objects */ - $objects = $query->limit(2)->all(); - $n = count($objects); - if ($n === 1) { - $keys = array_keys($params); - $pks = $targetClass::primaryKey(); - sort($keys); - sort($pks); - if ($keys === $pks) { - // primary key is modified and not unique - $exists = $object->getOldPrimaryKey() != $object->getPrimaryKey(); - } else { - // non-primary key, need to exclude the current record based on PK - $exists = $objects[0]->getPrimaryKey() != $object->getOldPrimaryKey(); - } - } else { - $exists = $n > 1; - } - } - - if ($exists) { - $this->addError($object, $attribute, $this->message); - } - } -} diff --git a/framework/yii/validators/UrlValidator.php b/framework/yii/validators/UrlValidator.php deleted file mode 100644 index 4cb20f6..0000000 --- a/framework/yii/validators/UrlValidator.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\validators; - -use Yii; -use yii\base\InvalidConfigException; -use yii\helpers\Html; -use yii\web\JsExpression; -use yii\helpers\Json; - -/** - * UrlValidator validates that the attribute value is a valid http or https URL. - * - * Note that this validator only checks if the URL scheme and host part are correct. - * It does not check the rest part of a URL. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class UrlValidator extends Validator -{ - /** - * @var string the regular expression used to validate the attribute value. - * The pattern may contain a `{schemes}` token that will be replaced - * by a regular expression which represents the [[validSchemes]]. - */ - public $pattern = '/^{schemes}:\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)/i'; - /** - * @var array list of URI schemes which should be considered valid. By default, http and https - * are considered to be valid schemes. - **/ - public $validSchemes = ['http', 'https']; - /** - * @var string the default URI scheme. If the input doesn't contain the scheme part, the default - * scheme will be prepended to it (thus changing the input). Defaults to null, meaning a URL must - * contain the scheme part. - **/ - public $defaultScheme; - /** - * @var boolean whether validation process should take into account IDN (internationalized - * domain names). Defaults to false meaning that validation of URLs containing IDN will always - * fail. Note that in order to use IDN validation you have to install and enable `intl` PHP - * extension, otherwise an exception would be thrown. - */ - public $enableIDN = false; - - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - if ($this->enableIDN && !function_exists('idn_to_ascii')) { - throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.'); - } - if ($this->message === null) { - $this->message = Yii::t('yii', '{attribute} is not a valid URL.'); - } - } - - /** - * @inheritdoc - */ - public function validateAttribute($object, $attribute) - { - $value = $object->$attribute; - $result = $this->validateValue($value); - if (!empty($result)) { - $this->addError($object, $attribute, $result[0], $result[1]); - } elseif ($this->defaultScheme !== null && strpos($value, '://') === false) { - $object->$attribute = $this->defaultScheme . '://' . $value; - } - } - - /** - * @inheritdoc - */ - protected function validateValue($value) - { - // make sure the length is limited to avoid DOS attacks - if (is_string($value) && strlen($value) < 2000) { - if ($this->defaultScheme !== null && strpos($value, '://') === false) { - $value = $this->defaultScheme . '://' . $value; - } - - if (strpos($this->pattern, '{schemes}') !== false) { - $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern); - } else { - $pattern = $this->pattern; - } - - if ($this->enableIDN) { - $value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) { - return '://' . idn_to_ascii($matches[1]); - }, $value); - } - - if (preg_match($pattern, $value)) { - return null; - } - } - return [$this->message, []]; - } - - /** - * @inheritdoc - */ - public function clientValidateAttribute($object, $attribute, $view) - { - if (strpos($this->pattern, '{schemes}') !== false) { - $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern); - } else { - $pattern = $this->pattern; - } - - $options = [ - 'pattern' => new JsExpression($pattern), - 'message' => strtr($this->message, [ - '{attribute}' => $object->getAttributeLabel($attribute), - ]), - 'enableIDN' => (boolean)$this->enableIDN, - ]; - if ($this->skipOnEmpty) { - $options['skipOnEmpty'] = 1; - } - if ($this->defaultScheme !== null) { - $options['defaultScheme'] = $this->defaultScheme; - } - - ValidationAsset::register($view); - if ($this->enableIDN) { - PunycodeAsset::register($view); - } - return 'yii.validation.url(value, messages, ' . Json::encode($options) . ');'; - } -} diff --git a/framework/yii/views/errorHandler/error.php b/framework/yii/views/errorHandler/error.php deleted file mode 100644 index 066d7e4..0000000 --- a/framework/yii/views/errorHandler/error.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * @var \Exception $exception - * @var \yii\base\ErrorHandler $handler - */ -if ($exception instanceof \yii\web\HttpException) { - $code = $exception->statusCode; -} else { - $code = $exception->getCode(); -} -if ($exception instanceof \yii\base\Exception) { - $name = $exception->getName(); -} else { - $name = 'Error'; -} -if ($code) { - $name .= " (#$code)"; -} - -if ($exception instanceof \yii\base\UserException) { - $message = $exception->getMessage(); -} else { - $message = 'An internal server error occurred.'; -} -?> -<?php if (method_exists($this, 'beginPage')) $this->beginPage(); ?> -<!DOCTYPE html> -<html> -<head> - <meta charset="utf-8" /> - <title><?= $handler->htmlEncode($name) ?></title> - - <style> - body { - font: normal 9pt "Verdana"; - color: #000; - background: #fff; - } - - h1 { - font: normal 18pt "Verdana"; - color: #f00; - margin-bottom: .5em; - } - - h2 { - font: normal 14pt "Verdana"; - color: #800000; - margin-bottom: .5em; - } - - h3 { - font: bold 11pt "Verdana"; - } - - p { - font: normal 9pt "Verdana"; - color: #000; - } - - .version { - color: gray; - font-size: 8pt; - border-top: 1px solid #aaa; - padding-top: 1em; - margin-bottom: 1em; - } - </style> -</head> - -<body> - <h1><?= $handler->htmlEncode($name) ?></h1> - <h2><?= nl2br($handler->htmlEncode($message)) ?></h2> - <p> - The above error occurred while the Web server was processing your request. - </p> - <p> - Please contact us if you think this is a server error. Thank you. - </p> - <div class="version"> - <?= date('Y-m-d H:i:s', time()) ?> - </div> - <?php if (method_exists($this, 'endBody')) $this->endBody(); // to allow injecting code into body (mostly by Yii Debug Toolbar) ?> -</body> -</html> -<?php if (method_exists($this, 'endPage')) $this->endPage(); ?> diff --git a/framework/yii/views/messageConfig.php b/framework/yii/views/messageConfig.php deleted file mode 100644 index 9babb0c..0000000 --- a/framework/yii/views/messageConfig.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -return [ - // string, required, root directory of all source files - 'sourcePath' => __DIR__, - // string, required, root directory containing message translations. - 'messagePath' => __DIR__ . DIRECTORY_SEPARATOR . 'messages', - // array, required, list of language codes that the extracted messages - // should be translated to. For example, ['zh_cn', 'de']. - 'languages' => ['de'], - // string, the name of the function for translating messages. - // Defaults to 'Yii::t'. This is used as a mark to find the messages to be - // translated. You may use a string for single function name or an array for - // multiple function names. - 'translator' => 'Yii::t', - // boolean, whether to sort messages by keys when merging new messages - // with the existing ones. Defaults to false, which means the new (untranslated) - // messages will be separated from the old (translated) ones. - 'sort' => false, - // boolean, whether the message file should be overwritten with the merged messages - 'overwrite' => true, - // boolean, whether to remove messages that no longer appear in the source code. - // Defaults to false, which means each of these messages will be enclosed with a pair of '@@' marks. - 'removeUnused' => false, - // array, list of patterns that specify which files/directories should be processed. - // If empty or not set, all files/directories will be processed. - // A path matches a pattern if it contains the pattern string at its end. For example, - // '/a/b' will match all files and directories ending with '/a/b'; - // and the '.svn' will match all files and directories whose name ends with '.svn'. - // Note, the '/' characters in a pattern matches both '/' and '\'. - // If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. - 'only' => ['.php'], - // array, list of patterns that specify which files/directories should NOT be processed. - // If empty or not set, all files/directories will be processed. - // Please refer to "only" for details about the patterns. - 'except' => [ - '.svn', - '.git', - '.gitignore', - '.gitkeep', - '.hgignore', - '.hgkeep', - '/messages', - ], -]; diff --git a/framework/yii/web/AccessControl.php b/framework/yii/web/AccessControl.php deleted file mode 100644 index e755e80..0000000 --- a/framework/yii/web/AccessControl.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\Action; -use yii\base\ActionFilter; - -/** - * AccessControl provides simple access control based on a set of rules. - * - * AccessControl is an action filter. It will check its [[rules]] to find - * the first rule that matches the current context variables (such as user IP address, user role). - * The matching rule will dictate whether to allow or deny the access to the requested controller - * action. If no rule matches, the access will be denied. - * - * To use AccessControl, declare it in the `behaviors()` method of your controller class. - * For example, the following declarations will allow authenticated users to access the "create" - * and "update" actions and deny all other users from accessing these two actions. - * - * ~~~ - * public function behaviors() - * { - * return [ - * 'access' => [ - * 'class' => \yii\web\AccessControl::className(), - * 'only' => ['create', 'update'], - * 'rules' => [ - * // deny all POST requests - * [ - * 'allow' => false, - * 'verbs' => ['POST'] - * ], - * // allow authenticated users - * [ - * 'allow' => true, - * 'roles' => ['@'], - * ], - * // everything else is denied - * ], - * ], - * ]; - * } - * ~~~ - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class AccessControl extends ActionFilter -{ - /** - * @var callback a callback that will be called if the access should be denied - * to the current user. If not set, [[denyAccess()]] will be called. - * - * The signature of the callback should be as follows: - * - * ~~~ - * function ($rule, $action) - * ~~~ - * - * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. - */ - public $denyCallback; - /** - * @var array the default configuration of access rules. Individual rule configurations - * specified via [[rules]] will take precedence when the same property of the rule is configured. - */ - public $ruleConfig = ['class' => 'yii\web\AccessRule']; - /** - * @var array a list of access rule objects or configuration arrays for creating the rule objects. - * If a rule is specified via a configuration array, it will be merged with [[ruleConfig]] first - * before it is used for creating the rule object. - * @see ruleConfig - */ - public $rules = []; - - /** - * Initializes the [[rules]] array by instantiating rule objects from configurations. - */ - public function init() - { - parent::init(); - foreach ($this->rules as $i => $rule) { - if (is_array($rule)) { - $this->rules[$i] = Yii::createObject(array_merge($this->ruleConfig, $rule)); - } - } - } - - /** - * This method is invoked right before an action is to be executed (after all possible filters.) - * You may override this method to do last-minute preparation for the action. - * @param Action $action the action to be executed. - * @return boolean whether the action should continue to be executed. - */ - public function beforeAction($action) - { - $user = Yii::$app->getUser(); - $request = Yii::$app->getRequest(); - /** @var AccessRule $rule */ - foreach ($this->rules as $rule) { - if ($allow = $rule->allows($action, $user, $request)) { - return true; - } elseif ($allow === false) { - if (isset($rule->denyCallback)) { - call_user_func($rule->denyCallback, $rule); - } elseif (isset($this->denyCallback)) { - call_user_func($this->denyCallback, $rule); - } else { - $this->denyAccess($user); - } - return false; - } - } - if (isset($this->denyCallback)) { - call_user_func($this->denyCallback, $rule); - } - else { - $this->denyAccess($user); - } - return false; - } - - /** - * Denies the access of the user. - * The default implementation will redirect the user to the login page if he is a guest; - * if the user is already logged, a 403 HTTP exception will be thrown. - * @param User $user the current user - * @throws AccessDeniedHttpException if the user is already logged in. - */ - protected function denyAccess($user) - { - if ($user->getIsGuest()) { - $user->loginRequired(); - } else { - throw new AccessDeniedHttpException(Yii::t('yii', 'You are not allowed to perform this action.')); - } - } -} diff --git a/framework/yii/web/AccessRule.php b/framework/yii/web/AccessRule.php deleted file mode 100644 index 7aeaac1..0000000 --- a/framework/yii/web/AccessRule.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use yii\base\Component; -use yii\base\Action; - -/** - * This class represents an access rule defined by the [[AccessControl]] action filter - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class AccessRule extends Component -{ - /** - * @var boolean whether this is an 'allow' rule or 'deny' rule. - */ - public $allow; - /** - * @var array list of action IDs that this rule applies to. The comparison is case-sensitive. - * If not set or empty, it means this rule applies to all actions. - */ - public $actions; - /** - * @var array list of controller IDs that this rule applies to. The comparison is case-sensitive. - * If not set or empty, it means this rule applies to all controllers. - */ - public $controllers; - /** - * @var array list of roles that this rule applies to. Two special roles are recognized, and - * they are checked via [[User::isGuest]]: - * - * - `?`: matches a guest user (not authenticated yet) - * - `@`: matches an authenticated user - * - * Using additional role names requires RBAC (Role-Based Access Control), and - * [[User::checkAccess()]] will be called. - * - * If this property is not set or empty, it means this rule applies to all roles. - */ - public $roles; - /** - * @var array list of user IP addresses that this rule applies to. An IP address - * can contain the wildcard `*` at the end so that it matches IP addresses with the same prefix. - * For example, '192.168.*' matches all IP addresses in the segment '192.168.'. - * If not set or empty, it means this rule applies to all IP addresses. - * @see Request::userIP - */ - public $ips; - /** - * @var array list of request methods (e.g. `GET`, `POST`) that this rule applies to. - * The request methods must be specified in uppercase. - * If not set or empty, it means this rule applies to all request methods. - * @see Request::requestMethod - */ - public $verbs; - /** - * @var callback a callback that will be called to determine if the rule should be applied. - * The signature of the callback should be as follows: - * - * ~~~ - * function ($rule, $action) - * ~~~ - * - * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. - * The callback should return a boolean value indicating whether this rule should be applied. - */ - public $matchCallback; - /** - * @var callback a callback that will be called if this rule determines the access to - * the current action should be denied. If not set, the behavior will be determined by - * [[AccessControl]]. - * - * The signature of the callback should be as follows: - * - * ~~~ - * function ($rule, $action) - * ~~~ - * - * where `$rule` is this rule, and `$action` is the current [[Action|action]] object. - */ - public $denyCallback; - - - /** - * Checks whether the Web user is allowed to perform the specified action. - * @param Action $action the action to be performed - * @param User $user the user object - * @param Request $request - * @return boolean|null true if the user is allowed, false if the user is denied, null if the rule does not apply to the user - */ - public function allows($action, $user, $request) - { - if ($this->matchAction($action) - && $this->matchRole($user) - && $this->matchIP($request->getUserIP()) - && $this->matchVerb($request->getMethod()) - && $this->matchController($action->controller) - && $this->matchCustom($action) - ) { - return $this->allow ? true : false; - } else { - return null; - } - } - - /** - * @param Action $action the action - * @return boolean whether the rule applies to the action - */ - protected function matchAction($action) - { - return empty($this->actions) || in_array($action->id, $this->actions, true); - } - - /** - * @param Controller $controller the controller - * @return boolean whether the rule applies to the controller - */ - protected function matchController($controller) - { - return empty($this->controllers) || in_array($controller->uniqueId, $this->controllers, true); - } - - /** - * @param User $user the user object - * @return boolean whether the rule applies to the role - */ - protected function matchRole($user) - { - if (empty($this->roles)) { - return true; - } - foreach ($this->roles as $role) { - if ($role === '?' && $user->getIsGuest()) { - return true; - } elseif ($role === '@' && !$user->getIsGuest()) { - return true; - } elseif ($user->checkAccess($role)) { - return true; - } - } - return false; - } - - /** - * @param string $ip the IP address - * @return boolean whether the rule applies to the IP address - */ - protected function matchIP($ip) - { - if (empty($this->ips)) { - return true; - } - foreach ($this->ips as $rule) { - if ($rule === '*' || $rule === $ip || (($pos = strpos($rule, '*')) !== false && !strncmp($ip, $rule, $pos))) { - return true; - } - } - return false; - } - - /** - * @param string $verb the request method - * @return boolean whether the rule applies to the request - */ - protected function matchVerb($verb) - { - return empty($this->verbs) || in_array($verb, $this->verbs, true); - } - - /** - * @param Action $action the action to be performed - * @return boolean whether the rule should be applied - */ - protected function matchCustom($action) - { - return empty($this->matchCallback) || call_user_func($this->matchCallback, $this, $action); - } -} diff --git a/framework/yii/web/Application.php b/framework/yii/web/Application.php deleted file mode 100644 index 17c1411..0000000 --- a/framework/yii/web/Application.php +++ /dev/null @@ -1,178 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\InvalidRouteException; - -/** - * Application is the base class for all web application classes. - * - * @property AssetManager $assetManager The asset manager component. This property is read-only. - * @property string $homeUrl The homepage URL. - * @property Request $request The request component. This property is read-only. - * @property Response $response The response component. This property is read-only. - * @property Session $session The session component. This property is read-only. - * @property User $user The user component. This property is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class Application extends \yii\base\Application -{ - /** - * @var string the default route of this application. Defaults to 'site'. - */ - public $defaultRoute = 'site'; - /** - * @var array the configuration specifying a controller action which should handle - * all user requests. This is mainly used when the application is in maintenance mode - * and needs to handle all incoming requests via a single action. - * The configuration is an array whose first element specifies the route of the action. - * The rest of the array elements (key-value pairs) specify the parameters to be bound - * to the action. For example, - * - * ~~~ - * [ - * 'offline/notice', - * 'param1' => 'value1', - * 'param2' => 'value2', - * ] - * ~~~ - * - * Defaults to null, meaning catch-all is not used. - */ - public $catchAll; - /** - * @var Controller the currently active controller instance - */ - public $controller; - - - /** - * Handles the specified request. - * @param Request $request the request to be handled - * @return Response the resulting response - * @throws NotFoundHttpException if the requested route is invalid - */ - public function handleRequest($request) - { - Yii::setAlias('@webroot', dirname($request->getScriptFile())); - Yii::setAlias('@web', $request->getBaseUrl()); - - if (empty($this->catchAll)) { - list ($route, $params) = $request->resolve(); - } else { - $route = $this->catchAll[0]; - $params = array_splice($this->catchAll, 1); - } - try { - Yii::trace("Route requested: '$route'", __METHOD__); - $this->requestedRoute = $route; - $result = $this->runAction($route, $params); - if ($result instanceof Response) { - return $result; - } else { - $response = $this->getResponse(); - if ($result !== null) { - $response->data = $result; - } - return $response; - } - } catch (InvalidRouteException $e) { - throw new NotFoundHttpException($e->getMessage(), $e->getCode(), $e); - } - } - - private $_homeUrl; - - /** - * @return string the homepage URL - */ - public function getHomeUrl() - { - if ($this->_homeUrl === null) { - if ($this->getUrlManager()->showScriptName) { - return $this->getRequest()->getScriptUrl(); - } else { - return $this->getRequest()->getBaseUrl() . '/'; - } - } else { - return $this->_homeUrl; - } - } - - /** - * @param string $value the homepage URL - */ - public function setHomeUrl($value) - { - $this->_homeUrl = $value; - } - - /** - * Returns the request component. - * @return Request the request component - */ - public function getRequest() - { - return $this->getComponent('request'); - } - - /** - * Returns the response component. - * @return Response the response component - */ - public function getResponse() - { - return $this->getComponent('response'); - } - - /** - * Returns the session component. - * @return Session the session component - */ - public function getSession() - { - return $this->getComponent('session'); - } - - /** - * Returns the user component. - * @return User the user component - */ - public function getUser() - { - return $this->getComponent('user'); - } - - /** - * Returns the asset manager. - * @return AssetManager the asset manager component - */ - public function getAssetManager() - { - return $this->getComponent('assetManager'); - } - - /** - * Registers the core application components. - * @see setComponents - */ - public function registerCoreComponents() - { - parent::registerCoreComponents(); - $this->setComponents([ - 'request' => ['class' => 'yii\web\Request'], - 'response' => ['class' => 'yii\web\Response'], - 'session' => ['class' => 'yii\web\Session'], - 'user' => ['class' => 'yii\web\User'], - 'assetManager' => ['class' => 'yii\web\AssetManager'], - ]); - } -} diff --git a/framework/yii/web/AssetBundle.php b/framework/yii/web/AssetBundle.php deleted file mode 100644 index da34848..0000000 --- a/framework/yii/web/AssetBundle.php +++ /dev/null @@ -1,194 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\InvalidConfigException; -use yii\base\Object; -use yii\web\View; - -/** - * AssetBundle represents a collection of asset files, such as CSS, JS, images. - * - * Each asset bundle has a unique name that globally identifies it among all asset bundles used in an application. - * The name is the [fully qualified class name](http://php.net/manual/en/language.namespaces.rules.php) - * of the class representing it. - * - * An asset bundle can depend on other asset bundles. When registering an asset bundle - * with a view, all its dependent asset bundles will be automatically registered. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class AssetBundle extends Object -{ - /** - * @var string the root directory of the source asset files. A source asset file - * is a file that is part of your source code repository of your Web application. - * - * You must set this property if the directory containing the source asset files - * is not Web accessible (this is usually the case for extensions). - * - * By setting this property, the asset manager will publish the source asset files - * to a Web-accessible directory [[basePath]]. - * - * You can use either a directory or an alias of the directory. - */ - public $sourcePath; - /** - * @var string the Web-accessible directory that contains the asset files in this bundle. - * - * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] - * when it publishes the asset files from [[sourcePath]]. - * - * If the bundle contains any assets that are specified in terms of relative file path, - * then this property must be set either manually or automatically (by [[AssetManager]] via - * asset publishing). - * - * You can use either a directory or an alias of the directory. - */ - public $basePath; - /** - * @var string the base URL that will be prefixed to the asset files for them to - * be accessed via Web server. - * - * If [[sourcePath]] is set, this property will be *overwritten* by [[AssetManager]] - * when it publishes the asset files from [[sourcePath]]. - * - * If the bundle contains any assets that are specified in terms of relative file path, - * then this property must be set either manually or automatically (by asset manager via - * asset publishing). - * - * You can use either a URL or an alias of the URL. - */ - public $baseUrl; - /** - * @var array list of bundle class names that this bundle depends on. - * - * For example: - * - * ```php - * public $depends = [ - * 'yii\web\YiiAsset', - * 'yii\bootstrap\BootstrapAsset', - * ]; - * ``` - */ - public $depends = []; - /** - * @var array list of JavaScript files that this bundle contains. Each JavaScript file can - * be either a file path (without leading slash) relative to [[basePath]] or a URL representing - * an external JavaScript file. - * - * Note that only forward slash "/" can be used as directory separator. - */ - public $js = []; - /** - * @var array list of CSS files that this bundle contains. Each CSS file can - * be either a file path (without leading slash) relative to [[basePath]] or a URL representing - * an external CSS file. - * - * Note that only forward slash "/" can be used as directory separator. - */ - public $css = []; - /** - * @var array the options that will be passed to [[\yii\web\View::registerJsFile()]] - * when registering the JS files in this bundle. - */ - public $jsOptions = []; - /** - * @var array the options that will be passed to [[\yii\web\View::registerCssFile()]] - * when registering the CSS files in this bundle. - */ - public $cssOptions = []; - /** - * @var array the options to be passed to [[AssetManager::publish()]] when the asset bundle - * is being published. - */ - public $publishOptions = []; - - /** - * @param View $view - * @return AssetBundle the registered asset bundle instance - */ - public static function register($view) - { - return $view->registerAssetBundle(get_called_class()); - } - - /** - * Initializes the bundle. - * If you override this method, make sure you call the parent implementation in the last. - */ - public function init() - { - if ($this->sourcePath !== null) { - $this->sourcePath = rtrim(Yii::getAlias($this->sourcePath), '/\\'); - } - if ($this->basePath !== null) { - $this->basePath = rtrim(Yii::getAlias($this->basePath), '/\\'); - } - if ($this->baseUrl !== null) { - $this->baseUrl = rtrim(Yii::getAlias($this->baseUrl), '/'); - } - } - - /** - * Registers the CSS and JS files with the given view. - * @param \yii\web\View $view the view that the asset files are to be registered with. - */ - public function registerAssetFiles($view) - { - foreach ($this->js as $js) { - if (strpos($js, '/') !== 0 && strpos($js, '://') === false) { - $view->registerJsFile($this->baseUrl . '/' . $js, [], $this->jsOptions); - } else { - $view->registerJsFile($js, [], $this->jsOptions); - } - } - foreach ($this->css as $css) { - if (strpos($css, '/') !== 0 && strpos($css, '://') === false) { - $view->registerCssFile($this->baseUrl . '/' . $css, [], $this->cssOptions); - } else { - $view->registerCssFile($css, [], $this->cssOptions); - } - } - } - - /** - * Publishes the asset bundle if its source code is not under Web-accessible directory. - * It will also try to convert non-CSS or JS files (e.g. LESS, Sass) into the corresponding - * CSS or JS files using [[AssetManager::converter|asset converter]]. - * @param AssetManager $am the asset manager to perform the asset publishing - */ - public function publish($am) - { - if ($this->sourcePath !== null && !isset($this->basePath, $this->baseUrl)) { - list ($this->basePath, $this->baseUrl) = $am->publish($this->sourcePath, $this->publishOptions); - } - $converter = $am->getConverter(); - foreach ($this->js as $i => $js) { - if (strpos($js, '/') !== 0 && strpos($js, '://') === false) { - if (isset($this->basePath, $this->baseUrl)) { - $this->js[$i] = $converter->convert($js, $this->basePath, $this->baseUrl); - } else { - $this->js[$i] = '/' . $js; - } - } - } - foreach ($this->css as $i => $css) { - if (strpos($css, '/') !== 0 && strpos($css, '://') === false) { - if (isset($this->basePath, $this->baseUrl)) { - $this->css[$i] = $converter->convert($css, $this->basePath, $this->baseUrl); - } else { - $this->css[$i] = '/' . $css; - } - } - } - } -} diff --git a/framework/yii/web/AssetConverter.php b/framework/yii/web/AssetConverter.php deleted file mode 100644 index 1b7d1c8..0000000 --- a/framework/yii/web/AssetConverter.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\Component; -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 <qiang.xue@gmail.com> - * @since 2.0 - */ -class AssetConverter extends Component implements AssetConverterInterface -{ - /** - * @var array the commands that are used to perform the asset conversion. - * The keys are the asset file extension names, and the values are the corresponding - * target script types (either "css" or "js") and the commands used for the conversion. - */ - public $commands = [ - 'less' => ['css', 'lessc {from} {to} --no-color'], - 'scss' => ['css', 'sass {from} {to}'], - 'sass' => ['css', 'sass {from} {to}'], - 'styl' => ['js', 'stylus < {from} > {to}'], - 'coffee' => ['js', 'coffee -p {from} > {to}'], - 'ts' => ['js', 'tsc --out {to} {from}'], - ]; - - /** - * Converts a given asset file into a CSS or JS file. - * @param string $asset the asset file path, relative to $basePath - * @param string $basePath the directory the $asset is relative to. - * @return string the converted asset file path, relative to $basePath. - */ - public function convert($asset, $basePath) - { - $pos = strrpos($asset, '.'); - if ($pos !== false) { - $ext = substr($asset, $pos + 1); - if (isset($this->commands[$ext])) { - list ($ext, $command) = $this->commands[$ext]; - $result = substr($asset, 0, $pos + 1) . $ext; - if (@filemtime("$basePath/$result") < filemtime("$basePath/$asset")) { - $this->runCommand($command, $basePath, $asset, $result); - } - return $result; - } - } - return $asset; - } - - /** - * Runs a command to convert asset files. - * @param string $command the command to run - * @param string $basePath asset base path and command working directory - * @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) - { - $command = strtr($command, [ - '{from}' => escapeshellarg("$basePath/$asset"), - '{to}' => escapeshellarg("$basePath/$result"), - ]); - $descriptor = [ - 1 => ['pipe', 'w'], - 2 => ['pipe', 'w'], - ]; - $pipes = []; - $proc = proc_open($command, $descriptor, $pipes, $basePath); - $stdout = stream_get_contents($pipes[1]); - $stderr = stream_get_contents($pipes[2]); - foreach($pipes as $pipe) { - fclose($pipe); - } - $status = proc_close($proc); - - if ($status === 0) { - Yii::trace("Converted $asset into $result:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr", __METHOD__); - } elseif (YII_DEBUG) { - throw new Exception("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr"); - } else { - Yii::error("AssetConverter command '$command' failed with exit code $status:\nSTDOUT:\n$stdout\nSTDERR:\n$stderr"); - } - return $status === 0; - } -} diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php deleted file mode 100644 index 7b4a98d..0000000 --- a/framework/yii/web/CacheSession.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\caching\Cache; -use yii\base\InvalidConfigException; - -/** - * CacheSession implements a session component using cache as storage medium. - * - * The cache being used can be any cache application component. - * The ID of the cache application component is specified via [[cache]], which defaults to 'cache'. - * - * 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, [[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. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class CacheSession extends Session -{ - /** - * @var Cache|string the cache object or the application component ID of the cache object. - * The session data will be stored using this cache object. - * - * After the CacheSession object is created, if you want to change this property, - * you should only assign it with a cache object. - */ - public $cache = 'cache'; - - /** - * Initializes the application component. - */ - public function init() - { - if (is_string($this->cache)) { - $this->cache = Yii::$app->getComponent($this->cache); - } - if (!$this->cache instanceof Cache) { - throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.'); - } - parent::init(); - } - - /** - * Returns a value indicating whether to use custom session storage. - * This method overrides the parent implementation and always returns true. - * @return boolean whether to use custom storage. - */ - public function getUseCustomStorage() - { - return true; - } - - /** - * Session read handler. - * Do not call this method directly. - * @param string $id session ID - * @return string the session data - */ - public function readSession($id) - { - $data = $this->cache->get($this->calculateKey($id)); - return $data === false ? '' : $data; - } - - /** - * Session write handler. - * Do not call this method directly. - * @param string $id session ID - * @param string $data session data - * @return boolean whether session write is successful - */ - public function writeSession($id, $data) - { - return $this->cache->set($this->calculateKey($id), $data, $this->getTimeout()); - } - - /** - * Session destroy handler. - * Do not call this method directly. - * @param string $id session ID - * @return boolean whether session is destroyed successfully - */ - public function destroySession($id) - { - return $this->cache->delete($this->calculateKey($id)); - } - - /** - * Generates a unique key used for storing session data in cache. - * @param string $id session variable name - * @return mixed a safe cache key associated with the session variable name - */ - protected function calculateKey($id) - { - return [__CLASS__, $id]; - } -} diff --git a/framework/yii/web/Cookie.php b/framework/yii/web/Cookie.php deleted file mode 100644 index 8cbb412..0000000 --- a/framework/yii/web/Cookie.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -/** - * Cookie represents information related with a cookie, such as [[name]], [[value]], [[domain]], etc. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class Cookie extends \yii\base\Object -{ - /** - * @var string name of the cookie - */ - public $name; - /** - * @var string value of the cookie - */ - public $value = ''; - /** - * @var string domain of the cookie - */ - public $domain = ''; - /** - * @var integer the timestamp at which the cookie expires. This is the server timestamp. - * Defaults to 0, meaning "until the browser is closed". - */ - public $expire = 0; - /** - * @var string the path on the server in which the cookie will be available on. The default is '/'. - */ - public $path = '/'; - /** - * @var boolean whether cookie should be sent via secure connection - */ - public $secure = false; - /** - * @var boolean whether the cookie should be accessible only through the HTTP protocol. - * By setting this property to true, the cookie will not be accessible by scripting languages, - * such as JavaScript, which can effectively help to reduce identity theft through XSS attacks. - */ - public $httpOnly = false; - - /** - * Magic method to turn a cookie object into a string without having to explicitly access [[value]]. - * - * ~~~ - * if (isset($request->cookies['name'])) { - * $value = (string)$request->cookies['name']; - * } - * ~~~ - * - * @return string The value of the cookie. If the value property is null, an empty string will be returned. - */ - public function __toString() - { - return (string)$this->value; - } -} diff --git a/framework/yii/web/CookieCollection.php b/framework/yii/web/CookieCollection.php deleted file mode 100644 index 3cf80ff..0000000 --- a/framework/yii/web/CookieCollection.php +++ /dev/null @@ -1,228 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use ArrayIterator; -use yii\base\InvalidCallException; -use yii\base\Object; - -/** - * CookieCollection maintains the cookies available in the current request. - * - * @property integer $count The number of cookies in the collection. This property is read-only. - * @property ArrayIterator $iterator An iterator for traversing the cookies in the collection. This property - * is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class CookieCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable -{ - /** - * @var boolean whether this collection is read only. - */ - public $readOnly = false; - - /** - * @var Cookie[] the cookies in this collection (indexed by the cookie names) - */ - private $_cookies = []; - - /** - * Constructor. - * @param array $cookies the cookies that this collection initially contains. This should be - * an array of name-value pairs.s - * @param array $config name-value pairs that will be used to initialize the object properties - */ - public function __construct($cookies = [], $config = []) - { - $this->_cookies = $cookies; - parent::__construct($config); - } - - /** - * Returns an iterator for traversing the cookies in the collection. - * This method is required by the SPL interface `IteratorAggregate`. - * It will be implicitly called when you use `foreach` to traverse the collection. - * @return ArrayIterator an iterator for traversing the cookies in the collection. - */ - public function getIterator() - { - return new ArrayIterator($this->_cookies); - } - - /** - * Returns the number of cookies in the collection. - * This method is required by the SPL `Countable` interface. - * It will be implicitly called when you use `count($collection)`. - * @return integer the number of cookies in the collection. - */ - public function count() - { - return $this->getCount(); - } - - /** - * Returns the number of cookies in the collection. - * @return integer the number of cookies in the collection. - */ - public function getCount() - { - return count($this->_cookies); - } - - /** - * Returns the cookie with the specified name. - * @param string $name the cookie name - * @return Cookie the cookie with the specified name. Null if the named cookie does not exist. - * @see getValue() - */ - public function get($name) - { - return isset($this->_cookies[$name]) ? $this->_cookies[$name] : null; - } - - /** - * Returns the value of the named cookie. - * @param string $name the cookie name - * @param mixed $defaultValue the value that should be returned when the named cookie does not exist. - * @return mixed the value of the named cookie. - * @see get() - */ - public function getValue($name, $defaultValue = null) - { - return isset($this->_cookies[$name]) ? $this->_cookies[$name]->value : $defaultValue; - } - - /** - * Returns whether there is a cookie with the specified name. - * @param string $name the cookie name - * @return boolean whether the named cookie exists - */ - public function has($name) - { - return isset($this->_cookies[$name]); - } - - /** - * Adds a cookie to the collection. - * If there is already a cookie with the same name in the collection, it will be removed first. - * @param Cookie $cookie the cookie to be added - * @throws InvalidCallException if the cookie collection is read only - */ - public function add($cookie) - { - if ($this->readOnly) { - throw new InvalidCallException('The cookie collection is read only.'); - } - $this->_cookies[$cookie->name] = $cookie; - } - - /** - * Removes a cookie. - * If `$removeFromBrowser` is true, the cookie will be removed from the browser. - * In this case, a cookie with outdated expiry will be added to the collection. - * @param Cookie|string $cookie the cookie object or the name of the cookie to be removed. - * @param boolean $removeFromBrowser whether to remove the cookie from browser - * @throws InvalidCallException if the cookie collection is read only - */ - public function remove($cookie, $removeFromBrowser = true) - { - if ($this->readOnly) { - throw new InvalidCallException('The cookie collection is read only.'); - } - if ($cookie instanceof Cookie) { - $cookie->expire = 1; - $cookie->value = ''; - } else { - $cookie = new Cookie([ - 'name' => $cookie, - 'expire' => 1, - ]); - } - if ($removeFromBrowser) { - $this->_cookies[$cookie->name] = $cookie; - } else { - unset($this->_cookies[$cookie->name]); - } - } - - /** - * Removes all cookies. - * @throws InvalidCallException if the cookie collection is read only - */ - public function removeAll() - { - if ($this->readOnly) { - throw new InvalidCallException('The cookie collection is read only.'); - } - $this->_cookies = []; - } - - /** - * Returns the collection as a PHP array. - * @return array the array representation of the collection. - * The array keys are cookie names, and the array values are the corresponding - * cookie objects. - */ - public function toArray() - { - return $this->_cookies; - } - - /** - * Returns whether there is a cookie with the specified name. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `isset($collection[$name])`. - * @param string $name the cookie name - * @return boolean whether the named cookie exists - */ - public function offsetExists($name) - { - return $this->has($name); - } - - /** - * Returns the cookie with the specified name. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$cookie = $collection[$name];`. - * This is equivalent to [[get()]]. - * @param string $name the cookie name - * @return Cookie the cookie with the specified name, null if the named cookie does not exist. - */ - public function offsetGet($name) - { - return $this->get($name); - } - - /** - * Adds the cookie to the collection. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$collection[$name] = $cookie;`. - * This is equivalent to [[add()]]. - * @param string $name the cookie name - * @param Cookie $cookie the cookie to be added - */ - public function offsetSet($name, $cookie) - { - $this->add($cookie); - } - - /** - * Removes the named cookie. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `unset($collection[$name])`. - * This is equivalent to [[remove()]]. - * @param string $name the cookie name - */ - public function offsetUnset($name) - { - $this->remove($name); - } -} diff --git a/framework/yii/web/DbSession.php b/framework/yii/web/DbSession.php deleted file mode 100644 index 2b0b0e7..0000000 --- a/framework/yii/web/DbSession.php +++ /dev/null @@ -1,224 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\db\Connection; -use yii\db\Query; -use yii\base\InvalidConfigException; - -/** - * DbSession extends [[Session]] by using database as session data storage. - * - * By default, DbSession stores session data in a DB table named 'tbl_session'. This table - * must be pre-created. The table name can be changed by setting [[sessionTable]]. - * - * The following example shows how you can configure the application to use DbSession: - * Add the following to your application config under `components`: - * - * ~~~ - * 'session' => [ - * 'class' => 'yii\web\DbSession', - * // 'db' => 'mydb', - * // 'sessionTable' => 'my_session', - * ] - * ~~~ - * - * @property boolean $useCustomStorage Whether to use custom storage. This property is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class DbSession extends Session -{ - /** - * @var Connection|string the DB connection object or the application component ID of the DB connection. - * After the DbSession object is created, if you want to change this property, you should only assign it - * with a DB connection object. - */ - public $db = 'db'; - /** - * @var string the name of the DB table that stores the session data. - * The table should be pre-created as follows: - * - * ~~~ - * CREATE TABLE tbl_session - * ( - * id CHAR(40) NOT NULL PRIMARY KEY, - * expire INTEGER, - * data BLOB - * ) - * ~~~ - * - * where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB type - * that can be used for some popular DBMS: - * - * - MySQL: LONGBLOB - * - PostgreSQL: BYTEA - * - MSSQL: BLOB - * - * When using DbSession in a production server, we recommend you create a DB index for the 'expire' - * column in the session table to improve the performance. - */ - public $sessionTable = '{{%session}}'; - - /** - * Initializes the DbSession component. - * This method will initialize the [[db]] property to make sure it refers to a valid DB connection. - * @throws InvalidConfigException if [[db]] is invalid. - */ - public function init() - { - if (is_string($this->db)) { - $this->db = Yii::$app->getComponent($this->db); - } - if (!$this->db instanceof Connection) { - throw new InvalidConfigException("DbSession::db must be either a DB connection instance or the application component ID of a DB connection."); - } - parent::init(); - } - - /** - * Returns a value indicating whether to use custom session storage. - * This method overrides the parent implementation and always returns true. - * @return boolean whether to use custom storage. - */ - public function getUseCustomStorage() - { - return true; - } - - /** - * Updates the current session ID with a newly generated one . - * Please refer to <http://php.net/session_regenerate_id> for more details. - * @param boolean $deleteOldSession Whether to delete the old associated session file or not. - */ - public function regenerateID($deleteOldSession = false) - { - $oldID = session_id(); - - // if no session is started, there is nothing to regenerate - if (empty($oldID)) { - return; - } - - parent::regenerateID(false); - $newID = session_id(); - - $query = new Query; - $row = $query->from($this->sessionTable) - ->where(['id' => $oldID]) - ->createCommand($this->db) - ->queryOne(); - if ($row !== false) { - if ($deleteOldSession) { - $this->db->createCommand() - ->update($this->sessionTable, ['id' => $newID], ['id' => $oldID]) - ->execute(); - } else { - $row['id'] = $newID; - $this->db->createCommand() - ->insert($this->sessionTable, $row) - ->execute(); - } - } else { - // shouldn't reach here normally - $this->db->createCommand() - ->insert($this->sessionTable, [ - 'id' => $newID, - 'expire' => time() + $this->getTimeout(), - ])->execute(); - } - } - - /** - * Session read handler. - * Do not call this method directly. - * @param string $id session ID - * @return string the session data - */ - public function readSession($id) - { - $query = new Query; - $data = $query->select(['data']) - ->from($this->sessionTable) - ->where('[[expire]]>:expire AND [[id]]=:id', [':expire' => time(), ':id' => $id]) - ->createCommand($this->db) - ->queryScalar(); - return $data === false ? '' : $data; - } - - /** - * Session write handler. - * Do not call this method directly. - * @param string $id session ID - * @param string $data session data - * @return boolean whether session write is successful - */ - public function writeSession($id, $data) - { - // exception must be caught in session write handler - // http://us.php.net/manual/en/function.session-set-save-handler.php - try { - $expire = time() + $this->getTimeout(); - $query = new Query; - $exists = $query->select(['id']) - ->from($this->sessionTable) - ->where(['id' => $id]) - ->createCommand($this->db) - ->queryScalar(); - if ($exists === false) { - $this->db->createCommand() - ->insert($this->sessionTable, [ - 'id' => $id, - 'data' => $data, - 'expire' => $expire, - ])->execute(); - } else { - $this->db->createCommand() - ->update($this->sessionTable, ['data' => $data, 'expire' => $expire], ['id' => $id]) - ->execute(); - } - } catch (\Exception $e) { - if (YII_DEBUG) { - echo $e->getMessage(); - } - // it is too late to log an error message here - return false; - } - return true; - } - - /** - * Session destroy handler. - * Do not call this method directly. - * @param string $id session ID - * @return boolean whether session is destroyed successfully - */ - public function destroySession($id) - { - $this->db->createCommand() - ->delete($this->sessionTable, ['id' => $id]) - ->execute(); - return true; - } - - /** - * Session GC (garbage collection) handler. - * Do not call this method directly. - * @param integer $maxLifetime the number of seconds after which data will be seen as 'garbage' and cleaned up. - * @return boolean whether session is GCed successfully - */ - public function gcSession($maxLifetime) - { - $this->db->createCommand() - ->delete($this->sessionTable, '[[expire]]<:expire', [':expire' => time()]) - ->execute(); - return true; - } -} diff --git a/framework/yii/web/ErrorAction.php b/framework/yii/web/ErrorAction.php deleted file mode 100644 index 95f17be..0000000 --- a/framework/yii/web/ErrorAction.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\Action; -use yii\base\Exception; -use yii\base\UserException; - -/** - * ErrorAction displays application errors using a specified view. - * - * To use ErrorAction, you need to do the following steps: - * - * First, declare an action of ErrorAction type in the `actions()` method of your `SiteController` - * class (or whatever controller you prefer), like the following: - * - * ```php - * public function actions() - * { - * return [ - * 'error' => ['class' => 'yii\web\ErrorAction'], - * ]; - * } - * ``` - * - * Then, create a view file for this action. If the route of your error action is `site/error`, then - * the view file should be `views/site/error.php`. In this view file, the following variables are available: - * - * - `$name`: the error name - * - `$message`: the error message - * - `$exception`: the exception being handled - * - * Finally, configure the "errorHandler" application component as follows, - * - * ```php - * 'errorHandler' => [ - * 'errorAction' => 'site/error', - * ] - * ``` - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class ErrorAction extends Action -{ - /** - * @var string the view file to be rendered. If not set, it will take the value of [[id]]. - * That means, if you name the action as "error" in "SiteController", then the view name - * would be "error", and the corresponding view file would be "views/site/error.php". - */ - public $view; - /** - * @var string the name of the error when the exception name cannot be determined. - * Defaults to "Error". - */ - public $defaultName; - /** - * @var string the message to be displayed when the exception message contains sensitive information. - * Defaults to "An internal server error occurred.". - */ - public $defaultMessage; - - - public function run() - { - if (($exception = Yii::$app->exception) === null) { - return ''; - } - - if ($exception instanceof HttpException) { - $code = $exception->statusCode; - } else { - $code = $exception->getCode(); - } - if ($exception instanceof Exception) { - $name = $exception->getName(); - } else { - $name = $this->defaultName ?: Yii::t('yii', 'Error'); - } - if ($code) { - $name .= " (#$code)"; - } - - if ($exception instanceof UserException) { - $message = $exception->getMessage(); - } else { - $message = $this->defaultMessage ?: Yii::t('yii', 'An internal server error occurred.'); - } - - if (Yii::$app->getRequest()->getIsAjax()) { - return "$name: $message"; - } else { - return $this->controller->render($this->view ?: $this->id, [ - 'name' => $name, - 'message' => $message, - 'exception' => $exception, - ]); - } - } -} diff --git a/framework/yii/web/HeaderCollection.php b/framework/yii/web/HeaderCollection.php deleted file mode 100644 index e8e4f9c..0000000 --- a/framework/yii/web/HeaderCollection.php +++ /dev/null @@ -1,221 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\Object; -use ArrayIterator; - -/** - * HeaderCollection is used by [[Response]] to maintain the currently registered HTTP headers. - * - * @property integer $count The number of headers in the collection. This property is read-only. - * @property ArrayIterator $iterator An iterator for traversing the headers in the collection. This property - * is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAccess, \Countable -{ - /** - * @var array the headers in this collection (indexed by the header names) - */ - private $_headers = []; - - /** - * Returns an iterator for traversing the headers in the collection. - * This method is required by the SPL interface `IteratorAggregate`. - * It will be implicitly called when you use `foreach` to traverse the collection. - * @return ArrayIterator an iterator for traversing the headers in the collection. - */ - public function getIterator() - { - return new ArrayIterator($this->_headers); - } - - /** - * Returns the number of headers in the collection. - * This method is required by the SPL `Countable` interface. - * It will be implicitly called when you use `count($collection)`. - * @return integer the number of headers in the collection. - */ - public function count() - { - return $this->getCount(); - } - - /** - * Returns the number of headers in the collection. - * @return integer the number of headers in the collection. - */ - public function getCount() - { - return count($this->_headers); - } - - /** - * Returns the named header(s). - * @param string $name the name of the header to return - * @param mixed $default the value to return in case the named header does not exist - * @param boolean $first whether to only return the first header of the specified name. - * If false, all headers of the specified name will be returned. - * @return string|array the named header(s). If `$first` is true, a string will be returned; - * If `$first` is false, an array will be returned. - */ - public function get($name, $default = null, $first = true) - { - $name = strtolower($name); - if (isset($this->_headers[$name])) { - return $first ? reset($this->_headers[$name]) : $this->_headers[$name]; - } else { - return $default; - } - } - - /** - * Adds a new header. - * If there is already a header with the same name, it will be replaced. - * @param string $name the name of the header - * @param string $value the value of the header - * @return static the collection object itself - */ - public function set($name, $value = '') - { - $name = strtolower($name); - $this->_headers[$name] = (array)$value; - return $this; - } - - /** - * Adds a new header. - * If there is already a header with the same name, the new one will - * be appended to it instead of replacing it. - * @param string $name the name of the header - * @param string $value the value of the header - * @return static the collection object itself - */ - public function add($name, $value) - { - $name = strtolower($name); - $this->_headers[$name][] = $value; - return $this; - } - - /** - * Sets a new header only if it does not exist yet. - * If there is already a header with the same name, the new one will be ignored. - * @param string $name the name of the header - * @param string $value the value of the header - * @return static the collection object itself - */ - public function setDefault($name, $value) - { - $name = strtolower($name); - if (empty($this->_headers[$name])) { - $this->_headers[$name][] = $value; - } - return $this; - } - - /** - * Returns a value indicating whether the named header exists. - * @param string $name the name of the header - * @return boolean whether the named header exists - */ - public function has($name) - { - $name = strtolower($name); - return isset($this->_headers[$name]); - } - - /** - * Removes a header. - * @param string $name the name of the header to be removed. - * @return string the value of the removed header. Null is returned if the header does not exist. - */ - public function remove($name) - { - $name = strtolower($name); - if (isset($this->_headers[$name])) { - $value = $this->_headers[$name]; - unset($this->_headers[$name]); - return $value; - } else { - return null; - } - } - - /** - * Removes all headers. - */ - public function removeAll() - { - $this->_headers = []; - } - - /** - * Returns the collection as a PHP array. - * @return array the array representation of the collection. - * The array keys are header names, and the array values are the corresponding header values. - */ - public function toArray() - { - return $this->_headers; - } - - /** - * Returns whether there is a header with the specified name. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `isset($collection[$name])`. - * @param string $name the header name - * @return boolean whether the named header exists - */ - public function offsetExists($name) - { - return $this->has($name); - } - - /** - * Returns the header with the specified name. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$header = $collection[$name];`. - * This is equivalent to [[get()]]. - * @param string $name the header name - * @return string the header value with the specified name, null if the named header does not exist. - */ - public function offsetGet($name) - { - return $this->get($name); - } - - /** - * Adds the header to the collection. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `$collection[$name] = $header;`. - * This is equivalent to [[add()]]. - * @param string $name the header name - * @param string $value the header value to be added - */ - public function offsetSet($name, $value) - { - $this->set($name, $value); - } - - /** - * Removes the named header. - * This method is required by the SPL interface `ArrayAccess`. - * It is implicitly called when you use something like `unset($collection[$name])`. - * This is equivalent to [[remove()]]. - * @param string $name the header name - */ - public function offsetUnset($name) - { - $this->remove($name); - } -} diff --git a/framework/yii/web/HttpCache.php b/framework/yii/web/HttpCache.php deleted file mode 100644 index 134df71..0000000 --- a/framework/yii/web/HttpCache.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\ActionFilter; -use yii\base\Action; - -/** - * 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 <webmaster@dasourcerer.net> - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class HttpCache extends ActionFilter -{ - /** - * @var callback a PHP callback that returns the UNIX timestamp of the last modification time. - * The callback's signature should be: - * - * ~~~ - * function ($action, $params) - * ~~~ - * - * where `$action` is the [[Action]] object that this filter is currently handling; - * `$params` takes the value of [[params]]. The callback should return a UNIX timestamp. - */ - public $lastModified; - /** - * @var callback a PHP callback that generates the Etag seed string. - * The callback's signature should be: - * - * ~~~ - * function ($action, $params) - * ~~~ - * - * where `$action` is the [[Action]] object that this filter is currently handling; - * `$params` takes the value of [[params]]. The callback should return a string serving - * as the seed for generating an Etag. - */ - public $etagSeed; - /** - * @var mixed additional parameters that should be passed to the [[lastModified]] and [[etagSeed]] callbacks. - */ - public $params; - /** - * @var string HTTP cache control header. If null, the header will not be sent. - */ - public $cacheControlHeader = 'max-age=3600, public'; - - /** - * This method is invoked right before an action is to be executed (after all possible filters.) - * You may override this method to do last-minute preparation for the action. - * @param Action $action the action to be executed. - * @return boolean whether the action should continue to be executed. - */ - public function beforeAction($action) - { - $verb = Yii::$app->getRequest()->getMethod(); - if ($verb !== 'GET' && $verb !== 'HEAD' || $this->lastModified === null && $this->etagSeed === null) { - return true; - } - - $lastModified = $etag = null; - if ($this->lastModified !== null) { - $lastModified = call_user_func($this->lastModified, $action, $this->params); - } - if ($this->etagSeed !== null) { - $seed = call_user_func($this->etagSeed, $action, $this->params); - $etag = $this->generateEtag($seed); - } - - $this->sendCacheControlHeader(); - $response = Yii::$app->getResponse(); - if ($etag !== null) { - $response->getHeaders()->set('Etag', $etag); - } - - if ($this->validateCache($lastModified, $etag)) { - $response->setStatusCode(304); - return false; - } - - if ($lastModified !== null) { - $response->getHeaders()->set('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); - } - return true; - } - - /** - * Validates if the HTTP cache contains valid content. - * @param integer $lastModified the calculated Last-Modified value in terms of a UNIX timestamp. - * If null, the Last-Modified header will not be validated. - * @param string $etag the calculated ETag value. If null, the ETag header will not be validated. - * @return boolean whether the HTTP cache is still valid. - */ - protected function validateCache($lastModified, $etag) - { - if ($lastModified !== null && (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) < $lastModified)) { - return false; - } else { - return $etag === null || isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag; - } - } - - /** - * Sends the cache control header to the client - * @see cacheControl - */ - protected function sendCacheControlHeader() - { - session_cache_limiter('public'); - $headers = Yii::$app->getResponse()->getHeaders(); - $headers->set('Pragma'); - if ($this->cacheControlHeader !== null) { - $headers->set('Cache-Control', $this->cacheControlHeader); - } - } - - /** - * Generates an Etag from the given seed string. - * @param string $seed Seed for the ETag - * @return string the generated Etag - */ - protected function generateEtag($seed) - { - return '"' . base64_encode(sha1($seed, true)) . '"'; - } -} diff --git a/framework/yii/web/HttpException.php b/framework/yii/web/HttpException.php deleted file mode 100644 index 2398437..0000000 --- a/framework/yii/web/HttpException.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use yii\base\UserException; - -/** - * HttpException represents an exception caused by an improper request of the end-user. - * - * HttpException can be differentiated via its [[statusCode]] property value which - * 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 <qiang.xue@gmail.com> - * @since 2.0 - */ -class HttpException extends UserException -{ - /** - * @var integer HTTP status code, such as 403, 404, 500, etc. - */ - public $statusCode; - - /** - * Constructor. - * @param integer $status HTTP status code, such as 404, 500, etc. - * @param string $message error message - * @param integer $code error code - * @param \Exception $previous The previous exception used for the exception chaining. - */ - public function __construct($status, $message = null, $code = 0, \Exception $previous = null) - { - $this->statusCode = $status; - parent::__construct($message, $code, $previous); - } - - /** - * @return string the user-friendly name of this exception - */ - public function getName() - { - if (isset(Response::$httpStatuses[$this->statusCode])) { - return Response::$httpStatuses[$this->statusCode]; - } else { - return 'Error'; - } - } - - /** - * Returns the array representation of this object. - * @return array the array representation of this object. - */ - public function toArray() - { - $array = parent::toArray(); - $array['status'] = $this->statusCode; - return $array; - } -} diff --git a/framework/yii/web/IdentityInterface.php b/framework/yii/web/IdentityInterface.php deleted file mode 100644 index c796b50..0000000 --- a/framework/yii/web/IdentityInterface.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -/** - * IdentityInterface is the interface that should be implemented by a class providing identity information. - * - * This interface can typically be implemented by a user model class. For example, the following - * code shows how to implement this interface by a User ActiveRecord class: - * - * ~~~ - * class User extends ActiveRecord implements IdentityInterface - * { - * public static function findIdentity($id) - * { - * return static::find($id); - * } - * - * public function getId() - * { - * return $this->id; - * } - * - * public function getAuthKey() - * { - * return $this->authKey; - * } - * - * public function validateAuthKey($authKey) - * { - * return $this->authKey === $authKey; - * } - * } - * ~~~ - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -interface IdentityInterface -{ - /** - * Finds an identity by the given ID. - * @param string|integer $id the ID to be looked for - * @return IdentityInterface the identity object that matches the given ID. - * Null should be returned if such an identity cannot be found - * or the identity is not in an active state (disabled, deleted, etc.) - */ - public static function findIdentity($id); - /** - * Returns an ID that can uniquely identify a user identity. - * @return string|integer an ID that uniquely identifies a user identity. - */ - public function getId(); - /** - * Returns a key that can be used to check the validity of a given identity ID. - * - * The key should be unique for each individual user, and should be persistent - * so that it can be used to check the validity of the user identity. - * - * The space of such keys should be big enough to defeat potential identity attacks. - * - * This is required if [[User::enableAutoLogin]] is enabled. - * @return string a key that is used to check the validity of a given identity ID. - * @see validateAuthKey() - */ - public function getAuthKey(); - /** - * Validates the given auth key. - * - * This is required if [[User::enableAutoLogin]] is enabled. - * @param string $authKey the given auth key - * @return boolean whether the given auth key is valid. - * @see getAuthKey() - */ - public function validateAuthKey($authKey); -} diff --git a/framework/yii/web/PageCache.php b/framework/yii/web/PageCache.php deleted file mode 100644 index 4c8cc50..0000000 --- a/framework/yii/web/PageCache.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\ActionFilter; -use yii\base\Action; -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 <qiang.xue@gmail.com> - * @since 2.0 - */ -class PageCache extends ActionFilter -{ - /** - * @var boolean whether the content being cached should be differentiated according to the route. - * A route consists of the requested controller ID and action ID. Defaults to true. - */ - public $varyByRoute = true; - /** - * @var string the application component ID of the [[\yii\caching\Cache|cache]] object. - */ - public $cache = 'cache'; - /** - * @var integer number of seconds that the data can remain valid in cache. - * Use 0 to indicate that the cached data will never expire. - */ - public $duration = 60; - /** - * @var array|Dependency the dependency that the cached content depends on. - * This can be either a [[Dependency]] object or a configuration array for creating the dependency object. - * For example, - * - * ~~~ - * [ - * 'class' => 'yii\caching\DbDependency', - * 'sql' => 'SELECT MAX(lastModified) FROM Post', - * ] - * ~~~ - * - * would make the output cache depends on the last modified time of all posts. - * If any post has its modification time changed, the cached content would be invalidated. - */ - public $dependency; - /** - * @var array list of factors that would cause the variation of the content being cached. - * Each factor is a string representing a variation (e.g. the language, a GET parameter). - * The following variation setting will cause the content to be cached in different versions - * according to the current application language: - * - * ~~~ - * [ - * Yii::$app->language, - * ] - * ~~~ - */ - public $variations; - /** - * @var boolean whether to enable the fragment cache. You may use this property to turn on and off - * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). - */ - public $enabled = true; - /** - * @var \yii\base\View the view component to use for caching. If not set, the default application view component - * [[Application::view]] will be used. - */ - public $view; - - - public function init() - { - parent::init(); - if ($this->view === null) { - $this->view = Yii::$app->getView(); - } - } - - /** - * This method is invoked right before an action is to be executed (after all possible filters.) - * You may override this method to do last-minute preparation for the action. - * @param Action $action the action to be executed. - * @return boolean whether the action should continue to be executed. - */ - public function beforeAction($action) - { - $properties = []; - foreach (['cache', 'duration', 'dependency', 'variations', 'enabled'] as $name) { - $properties[$name] = $this->$name; - } - $id = $this->varyByRoute ? $action->getUniqueId() : __CLASS__; - ob_start(); - ob_implicit_flush(false); - if ($this->view->beginCache($id, $properties)) { - return true; - } else { - Yii::$app->getResponse()->content = ob_get_clean(); - return false; - } - } - - /** - * This method is invoked right after an action is executed. - * You may override this method to do some postprocessing for the action. - * @param Action $action the action just executed. - * @param mixed $result the action execution result - */ - public function afterAction($action, &$result) - { - echo $result; - $this->view->endCache(); - $result = ob_get_clean(); - } -} diff --git a/framework/yii/web/SessionIterator.php b/framework/yii/web/SessionIterator.php deleted file mode 100644 index c960dd4..0000000 --- a/framework/yii/web/SessionIterator.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -/** - * SessionIterator implements an iterator for traversing session variables managed by [[Session]]. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class SessionIterator implements \Iterator -{ - /** - * @var array list of keys in the map - */ - private $_keys; - /** - * @var mixed current key - */ - private $_key; - - /** - * Constructor. - */ - public function __construct() - { - $this->_keys = array_keys($_SESSION); - } - - /** - * Rewinds internal array pointer. - * This method is required by the interface Iterator. - */ - public function rewind() - { - $this->_key = reset($this->_keys); - } - - /** - * Returns the key of the current array element. - * This method is required by the interface Iterator. - * @return mixed the key of the current array element - */ - public function key() - { - return $this->_key; - } - - /** - * Returns the current array element. - * This method is required by the interface Iterator. - * @return mixed the current array element - */ - public function current() - { - return isset($_SESSION[$this->_key]) ? $_SESSION[$this->_key] : null; - } - - /** - * Moves the internal pointer to the next array element. - * This method is required by the interface Iterator. - */ - public function next() - { - do { - $this->_key = next($this->_keys); - } while (!isset($_SESSION[$this->_key]) && $this->_key !== false); - } - - /** - * Returns whether there is an element at current position. - * This method is required by the interface Iterator. - * @return boolean - */ - public function valid() - { - return $this->_key !== false; - } -} diff --git a/framework/yii/web/UploadedFile.php b/framework/yii/web/UploadedFile.php deleted file mode 100644 index 5e1e428..0000000 --- a/framework/yii/web/UploadedFile.php +++ /dev/null @@ -1,234 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use yii\base\Object; -use yii\helpers\Html; - -/** - * UploadedFile represents the information for an uploaded file. - * - * You can call [[getInstance()]] to retrieve the instance of an uploaded file, - * and then use [[saveAs()]] to save it on the server. - * You may also query other information about the file, including [[name]], - * [[tempName]], [[type]], [[size]] and [[error]]. - * - * @property boolean $hasError Whether there is an error with the uploaded file. Check [[error]] for detailed - * error code information. This property is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class UploadedFile extends Object -{ - private static $_files; - - /** - * @var string the original name of the file being uploaded - */ - public $name; - /** - * @var string the path of the uploaded file on the server. - * Note, this is a temporary file which will be automatically deleted by PHP - * after the current request is processed. - */ - public $tempName; - /** - * @var string the MIME-type of the uploaded file (such as "image/gif"). - * Since this MIME type is not checked on the server side, do not take this value for granted. - * Instead, use [[FileHelper::getMimeType()]] to determine the exact MIME type. - */ - public $type; - /** - * @var integer the actual size of the uploaded file in bytes - */ - public $size; - /** - * @var integer an error code describing the status of this file uploading. - * @see http://www.php.net/manual/en/features.file-upload.errors.php - */ - public $error; - - - /** - * String output. - * This is PHP magic method that returns string representation of an object. - * The implementation here returns the uploaded file's name. - * @return string the string representation of the object - */ - public function __toString() - { - return $this->name; - } - - /** - * Returns an uploaded file for the given model attribute. - * The file should be uploaded using [[ActiveForm::fileInput()]]. - * @param \yii\base\Model $model the data model - * @param string $attribute the attribute name. The attribute name may contain array indexes. - * 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() - */ - public static function getInstance($model, $attribute) - { - $name = Html::getInputName($model, $attribute); - return static::getInstanceByName($name); - } - - /** - * Returns all uploaded files for the given model attribute. - * @param \yii\base\Model $model the data model - * @param string $attribute the attribute name. The attribute name may contain array indexes - * for tabular file uploading, e.g. '[1]file'. - * @return UploadedFile[] array of UploadedFile objects. - * Empty array is returned if no available file was found for the given attribute. - */ - public static function getInstances($model, $attribute) - { - $name = Html::getInputName($model, $attribute); - return static::getInstancesByName($name); - } - - /** - * Returns an uploaded file according to the given file input name. - * The name can be a plain string or a string like an array element (e.g. 'Post[imageFile]', or 'Post[0][imageFile]'). - * @param string $name the name of the file input field. - * @return UploadedFile the instance of the uploaded file. - * Null is returned if no file is uploaded for the specified name. - */ - public static function getInstanceByName($name) - { - $files = static::loadFiles(); - return isset($files[$name]) ? $files[$name] : null; - } - - /** - * Returns an array of uploaded files corresponding to the specified file input name. - * This is mainly used when multiple files were uploaded and saved as 'files[0]', 'files[1]', - * 'files[n]'..., and you can retrieve them all by passing 'files' as the name. - * @param string $name the name of the array of files - * @return UploadedFile[] the array of CUploadedFile objects. Empty array is returned - * if no adequate upload was found. Please note that this array will contain - * all files from all sub-arrays regardless how deeply nested they are. - */ - public static function getInstancesByName($name) - { - $files = static::loadFiles(); - if (isset($files[$name])) { - return [$files[$name]]; - } - $results = []; - foreach ($files as $key => $file) { - if (strpos($key, "{$name}[") === 0) { - $results[] = self::$_files[$key]; - } - } - return $results; - } - - /** - * Cleans up the loaded UploadedFile instances. - * This method is mainly used by test scripts to set up a fixture. - */ - public static function reset() - { - self::$_files = null; - } - - /** - * Saves the uploaded file. - * Note that this method uses php's move_uploaded_file() method. If the target file `$file` - * already exists, it will be overwritten. - * @param string $file the file path used to save the uploaded file - * @param boolean $deleteTempFile whether to delete the temporary file after saving. - * If true, you will not be able to save the uploaded file again in the current request. - * @return boolean true whether the file is saved successfully - * @see error - */ - public function saveAs($file, $deleteTempFile = true) - { - if ($this->error == UPLOAD_ERR_OK) { - if ($deleteTempFile) { - return move_uploaded_file($this->tempName, $file); - } elseif (is_uploaded_file($this->tempName)) { - return copy($this->tempName, $file); - } - } - return false; - } - - /** - * @return string original file base name - */ - public function getBaseName() - { - return pathinfo($this->name, PATHINFO_FILENAME); - } - - /** - * @return string file extension - */ - public function getExtension() - { - return strtolower(pathinfo($this->name, PATHINFO_EXTENSION)); - } - - /** - * @return boolean whether there is an error with the uploaded file. - * Check [[error]] for detailed error code information. - */ - public function getHasError() - { - return $this->error != UPLOAD_ERR_OK; - } - - /** - * Creates UploadedFile instances from $_FILE. - * @return array the UploadedFile instances - */ - private static function loadFiles() - { - if (self::$_files === null) { - self::$_files = []; - if (isset($_FILES) && is_array($_FILES)) { - foreach ($_FILES as $class => $info) { - self::loadFilesRecursive($class, $info['name'], $info['tmp_name'], $info['type'], $info['size'], $info['error']); - } - } - } - return self::$_files; - } - - /** - * Creates UploadedFile instances from $_FILE recursively. - * @param string $key key for identifying uploaded file: class name and sub-array indexes - * @param mixed $names file names provided by PHP - * @param mixed $tempNames temporary file names provided by PHP - * @param mixed $types file types provided by PHP - * @param mixed $sizes file sizes provided by PHP - * @param mixed $errors uploading issues provided by PHP - */ - private static function loadFilesRecursive($key, $names, $tempNames, $types, $sizes, $errors) - { - if (is_array($names)) { - foreach ($names as $i => $name) { - self::loadFilesRecursive($key . '[' . $i . ']', $name, $tempNames[$i], $types[$i], $sizes[$i], $errors[$i]); - } - } else { - self::$_files[$key] = new static([ - 'name' => $names, - 'tempName' => $tempNames, - 'type' => $types, - 'size' => $sizes, - 'error' => $errors, - ]); - } - } -} diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php deleted file mode 100644 index a2044cb..0000000 --- a/framework/yii/web/UrlManager.php +++ /dev/null @@ -1,338 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\Component; -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. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class UrlManager extends Component -{ - /** - * @var boolean whether to enable pretty URLs. Instead of putting all parameters in the query - * string part of a URL, pretty URLs allow using path info to represent some of the parameters - * and can thus produce more user-friendly URLs, such as "/news/Yii-is-released", instead of - * "/index.php?r=news/view&id=100". - */ - public $enablePrettyUrl = false; - /** - * @var boolean whether to enable strict parsing. If strict parsing is enabled, the incoming - * requested URL must match at least one of the [[rules]] in order to be treated as a valid request. - * Otherwise, the path info part of the request will be treated as the requested route. - * This property is used only when [[enablePrettyUrl]] is true. - */ - public $enableStrictParsing = false; - /** - * @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is true. - * This property is used only if [[enablePrettyUrl]] is true. Each element in the array - * is the configuration array for creating a single URL rule. The configuration will - * be merged with [[ruleConfig]] first before it is used for creating the rule object. - * - * A special shortcut format can be used if a rule only specifies [[UrlRule::pattern|pattern]] - * and [[UrlRule::route|route]]: `'pattern' => 'route'`. That is, instead of using a configuration - * array, one can use the key to represent the pattern and the value the corresponding route. - * For example, `'post/<id:\d+>' => 'post/view'`. - * - * For RESTful routing the mentioned shortcut format also allows you to specify the - * [[UrlRule::verb|HTTP verb]] that the rule should apply for. - * You can do that by prepending it to the pattern, separated by space. - * For example, `'PUT post/<id:\d+>' => 'post/update'`. - * You may specify multiple verbs by separating them with comma - * like this: `'POST,PUT post/index' => 'post/create'`. - * The supported verbs in the shortcut format are: GET, HEAD, POST, PUT, PATCH and DELETE. - * Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way - * so you normally would not specify a verb for normal GET request. - * - * Here is an example configuration for RESTful CRUD controller: - * - * ~~~php - * [ - * 'dashboard' => 'site/index', - * - * 'POST <controller:\w+>s' => '<controller>/create', - * '<controller:\w+>s' => '<controller>/index', - * - * 'PUT <controller:\w+>/<id:\d+>' => '<controller>/update', - * 'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete', - * '<controller:\w+>/<id:\d+>' => '<controller>/view', - * ]; - * ~~~ - * - * Note that if you modify this property after the UrlManager object is created, make sure - * you populate the array with rule objects instead of rule configurations. - */ - public $rules = []; - /** - * @var string the URL suffix used when in 'path' format. - * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. - * This property is used only if [[enablePrettyUrl]] is true. - */ - public $suffix; - /** - * @var boolean whether to show entry script name in the constructed URL. Defaults to true. - * This property is used only if [[enablePrettyUrl]] is true. - */ - public $showScriptName = true; - /** - * @var string the GET variable name for route. This property is used only if [[enablePrettyUrl]] is false. - */ - public $routeVar = 'r'; - /** - * @var Cache|string the cache object or the application component ID of the cache object. - * Compiled URL rules will be cached through this cache object, if it is available. - * - * After the UrlManager object is created, if you want to change this property, - * you should only assign it with a cache object. - * Set this property to null if you do not want to cache the URL rules. - */ - public $cache = 'cache'; - /** - * @var array the default configuration of URL rules. Individual rule configurations - * specified via [[rules]] will take precedence when the same property of the rule is configured. - */ - public $ruleConfig = ['class' => 'yii\web\UrlRule']; - - private $_baseUrl; - private $_hostInfo; - - /** - * Initializes UrlManager. - */ - public function init() - { - parent::init(); - $this->compileRules(); - } - - /** - * Parses the URL rules. - */ - protected function compileRules() - { - if (!$this->enablePrettyUrl || empty($this->rules)) { - return; - } - if (is_string($this->cache)) { - $this->cache = Yii::$app->getComponent($this->cache); - } - if ($this->cache instanceof Cache) { - $key = __CLASS__; - $hash = md5(json_encode($this->rules)); - if (($data = $this->cache->get($key)) !== false && isset($data[1]) && $data[1] === $hash) { - $this->rules = $data[0]; - return; - } - } - - $rules = []; - foreach ($this->rules as $key => $rule) { - if (!is_array($rule)) { - $rule = ['route' => $rule]; - if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) { - $rule['verb'] = explode(',', $matches[1]); - $rule['mode'] = UrlRule::PARSING_ONLY; - $key = $matches[4]; - } - $rule['pattern'] = $key; - } - $rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule)); - } - $this->rules = $rules; - - if (isset($key, $hash)) { - $this->cache->set($key, [$this->rules, $hash]); - } - } - - /** - * Parses the user request. - * @param Request $request the request component - * @return array|boolean the route and the associated parameters. The latter is always empty - * if [[enablePrettyUrl]] is false. False is returned if the current request cannot be successfully parsed. - */ - public function parseRequest($request) - { - if ($this->enablePrettyUrl) { - $pathInfo = $request->getPathInfo(); - /** @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__); - return $result; - } - } - - if ($this->enableStrictParsing) { - return false; - } - - Yii::trace('No matching URL rules. Using default URL parsing logic.', __METHOD__); - - $suffix = (string)$this->suffix; - if ($suffix !== '' && $pathInfo !== '') { - $n = strlen($this->suffix); - if (substr($pathInfo, -$n) === $this->suffix) { - $pathInfo = substr($pathInfo, 0, -$n); - if ($pathInfo === '') { - // suffix alone is not allowed - return false; - } - } else { - // suffix doesn't match - return false; - } - } - - return [$pathInfo, []]; - } else { - Yii::trace('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__); - $route = $request->get($this->routeVar); - if (is_array($route)) { - $route = ''; - } - return [(string)$route, []]; - } - } - - /** - * Creates a URL using the given route and parameters. - * The URL created is a relative one. Use [[createAbsoluteUrl()]] to create an absolute URL. - * @param string $route the route - * @param array $params the parameters (name-value pairs) - * @return string the created URL - */ - public function createUrl($route, $params = []) - { - $anchor = isset($params['#']) ? '#' . $params['#'] : ''; - unset($params['#'], $params[$this->routeVar]); - - $route = trim($route, '/'); - $baseUrl = $this->getBaseUrl(); - - if ($this->enablePrettyUrl) { - /** @var UrlRule $rule */ - foreach ($this->rules as $rule) { - if (($url = $rule->createUrl($this, $route, $params)) !== false) { - if ($rule->host !== null) { - if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) { - return substr($url, 0, $pos) . $baseUrl . substr($url, $pos); - } else { - return $url . $baseUrl . $anchor; - } - } else { - return "$baseUrl/{$url}{$anchor}"; - } - } - } - - if ($this->suffix !== null) { - $route .= $this->suffix; - } - if (!empty($params)) { - $route .= '?' . http_build_query($params); - } - return "$baseUrl/{$route}{$anchor}"; - } else { - $url = "$baseUrl?{$this->routeVar}=$route"; - if (!empty($params)) { - $url .= '&' . http_build_query($params); - } - return $url . $anchor; - } - } - - /** - * Creates an absolute URL using the given route and parameters. - * This method prepends the URL created by [[createUrl()]] with the [[hostInfo]]. - * @param string $route the route - * @param array $params the parameters (name-value pairs) - * @return string the created URL - * @see createUrl() - */ - public function createAbsoluteUrl($route, $params = []) - { - $url = $this->createUrl($route, $params); - if (strpos($url, '://') !== false) { - return $url; - } else { - return $this->getHostInfo() . $url; - } - } - - /** - * Returns the base URL that is used by [[createUrl()]] to prepend URLs it creates. - * It defaults to [[Request::scriptUrl]] if [[showScriptName]] is true or [[enablePrettyUrl]] is false; - * otherwise, it defaults to [[Request::baseUrl]]. - * @return string the base URL that is used by [[createUrl()]] to prepend URLs it creates. - */ - public function getBaseUrl() - { - if ($this->_baseUrl === null) { - /** @var \yii\web\Request $request */ - $request = Yii::$app->getRequest(); - $this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl(); - } - return $this->_baseUrl; - } - - /** - * Sets the base URL that is used by [[createUrl()]] to prepend URLs it creates. - * @param string $value the base URL that is used by [[createUrl()]] to prepend URLs it creates. - */ - public function setBaseUrl($value) - { - $this->_baseUrl = rtrim($value, '/'); - } - - /** - * Returns the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. - * @return string the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. - */ - public function getHostInfo() - { - if ($this->_hostInfo === null) { - $this->_hostInfo = Yii::$app->getRequest()->getHostInfo(); - } - return $this->_hostInfo; - } - - /** - * Sets the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. - * @param string $value the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. - */ - public function setHostInfo($value) - { - $this->_hostInfo = rtrim($value, '/'); - } -} diff --git a/framework/yii/web/UrlRule.php b/framework/yii/web/UrlRule.php deleted file mode 100644 index 2934b26..0000000 --- a/framework/yii/web/UrlRule.php +++ /dev/null @@ -1,326 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use yii\base\Object; -use yii\base\InvalidConfigException; - -/** - * 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 <qiang.xue@gmail.com> - * @since 2.0 - */ -class UrlRule extends Object -{ - /** - * Set [[mode]] with this value to mark that this rule is for URL parsing only - */ - const PARSING_ONLY = 1; - /** - * Set [[mode]] with this value to mark that this rule is for URL creation only - */ - const CREATION_ONLY = 2; - - /** - * @var string the name of this rule. If not set, it will use [[pattern]] as the name. - */ - public $name; - /** - * @var string the pattern used to parse and create the path info part of a URL. - * @see host - */ - public $pattern; - /** - * @var string the pattern used to parse and create the host info part of a URL. - * @see pattern - */ - public $host; - /** - * @var string the route to the controller action - */ - public $route; - /** - * @var array the default GET parameters (name => value) that this rule provides. - * When this rule is used to parse the incoming request, the values declared in this property - * will be injected into $_GET. - */ - public $defaults = []; - /** - * @var string the URL suffix used for this rule. - * For example, ".html" can be used so that the URL looks like pointing to a static HTML page. - * If not, the value of [[UrlManager::suffix]] will be used. - */ - public $suffix; - /** - * @var string|array the HTTP verb (e.g. GET, POST, DELETE) that this rule should match. - * Use array to represent multiple verbs that this rule may match. - * If this property is not set, the rule can match any verb. - * Note that this property is only used when parsing a request. It is ignored for URL creation. - */ - public $verb; - /** - * @var integer a value indicating if this rule should be used for both request parsing and URL creation, - * parsing only, or creation only. - * If not set or 0, it means the rule is both request parsing and URL creation. - * If it is [[PARSING_ONLY]], the rule is for request parsing only. - * If it is [[CREATION_ONLY]], the rule is for URL creation only. - */ - public $mode; - - /** - * @var string the template for generating a new URL. This is derived from [[pattern]] and is used in generating URL. - */ - private $_template; - /** - * @var string the regex for matching the route part. This is used in generating URL. - */ - private $_routeRule; - /** - * @var array list of regex for matching parameters. This is used in generating URL. - */ - private $_paramRules = []; - /** - * @var array list of parameters used in the route. - */ - private $_routeParams = []; - - /** - * Initializes this rule. - */ - public function init() - { - if ($this->pattern === null) { - throw new InvalidConfigException('UrlRule::pattern must be set.'); - } - if ($this->route === null) { - throw new InvalidConfigException('UrlRule::route must be set.'); - } - if ($this->verb !== null) { - if (is_array($this->verb)) { - foreach ($this->verb as $i => $verb) { - $this->verb[$i] = strtoupper($verb); - } - } else { - $this->verb = [strtoupper($this->verb)]; - } - } - if ($this->name === null) { - $this->name = $this->pattern; - } - - $this->pattern = trim($this->pattern, '/'); - - if ($this->host !== null) { - $this->pattern = rtrim($this->host, '/') . rtrim('/' . $this->pattern, '/') . '/'; - } elseif ($this->pattern === '') { - $this->_template = ''; - $this->pattern = '#^$#u'; - return; - } else { - $this->pattern = '/' . $this->pattern . '/'; - } - - $this->route = trim($this->route, '/'); - if (strpos($this->route, '<') !== false && preg_match_all('/<(\w+)>/', $this->route, $matches)) { - foreach ($matches[1] as $name) { - $this->_routeParams[$name] = "<$name>"; - } - } - - $tr = [ - '.' => '\\.', - '*' => '\\*', - '$' => '\\$', - '[' => '\\[', - ']' => '\\]', - '(' => '\\(', - ')' => '\\)', - ]; - $tr2 = []; - if (preg_match_all('/<(\w+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { - foreach ($matches as $match) { - $name = $match[1][0]; - $pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+'; - if (isset($this->defaults[$name])) { - $length = strlen($match[0][0]); - $offset = $match[0][1]; - if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') { - $tr["/<$name>"] = "(/(?P<$name>$pattern))?"; - } else { - $tr["<$name>"] = "(?P<$name>$pattern)?"; - } - } else { - $tr["<$name>"] = "(?P<$name>$pattern)"; - } - if (isset($this->_routeParams[$name])) { - $tr2["<$name>"] = "(?P<$name>$pattern)"; - } else { - $this->_paramRules[$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#"; - } - } - } - - $this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern); - $this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u'; - - if (!empty($this->_routeParams)) { - $this->_routeRule = '#^' . strtr($this->route, $tr2) . '$#u'; - } - } - - /** - * Parses the given request and returns the corresponding route and parameters. - * @param UrlManager $manager the URL manager - * @param Request $request the request component - * @return array|boolean the parsing result. The route and the parameters are returned as an array. - * If false, it means this rule cannot be used to parse this path info. - */ - public function parseRequest($manager, $request) - { - if ($this->mode === self::CREATION_ONLY) { - return false; - } - - if ($this->verb !== null && !in_array($request->getMethod(), $this->verb, true)) { - return false; - } - - $pathInfo = $request->getPathInfo(); - $suffix = (string)($this->suffix === null ? $manager->suffix : $this->suffix); - if ($suffix !== '' && $pathInfo !== '') { - $n = strlen($suffix); - if (substr($pathInfo, -$n) === $suffix) { - $pathInfo = substr($pathInfo, 0, -$n); - if ($pathInfo === '') { - // suffix alone is not allowed - return false; - } - } else { - return false; - } - } - - if ($this->host !== null) { - $pathInfo = strtolower($request->getHostInfo()) . '/' . $pathInfo; - } - - if (!preg_match($this->pattern, $pathInfo, $matches)) { - return false; - } - foreach ($this->defaults as $name => $value) { - if (!isset($matches[$name]) || $matches[$name] === '') { - $matches[$name] = $value; - } - } - $params = $this->defaults; - $tr = []; - foreach ($matches as $name => $value) { - if (isset($this->_routeParams[$name])) { - $tr[$this->_routeParams[$name]] = $value; - unset($params[$name]); - } elseif (isset($this->_paramRules[$name])) { - $params[$name] = $value; - } - } - if ($this->_routeRule !== null) { - $route = strtr($this->route, $tr); - } else { - $route = $this->route; - } - return [$route, $params]; - } - - /** - * Creates a URL according to the given route and parameters. - * @param UrlManager $manager the URL manager - * @param string $route the route. It should not have slashes at the beginning or the end. - * @param array $params the parameters - * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL. - */ - public function createUrl($manager, $route, $params) - { - if ($this->mode === self::PARSING_ONLY) { - return false; - } - - $tr = []; - - // match the route part first - if ($route !== $this->route) { - if ($this->_routeRule !== null && preg_match($this->_routeRule, $route, $matches)) { - foreach ($this->_routeParams as $name => $token) { - if (isset($this->defaults[$name]) && strcmp($this->defaults[$name], $matches[$name]) === 0) { - $tr[$token] = ''; - } else { - $tr[$token] = $matches[$name]; - } - } - } else { - return false; - } - } - - // match default params - // if a default param is not in the route pattern, its value must also be matched - foreach ($this->defaults as $name => $value) { - if (isset($this->_routeParams[$name])) { - continue; - } - if (!isset($params[$name])) { - return false; - } elseif (strcmp($params[$name], $value) === 0) { // strcmp will do string conversion automatically - unset($params[$name]); - if (isset($this->_paramRules[$name])) { - $tr["<$name>"] = ''; - } - } elseif (!isset($this->_paramRules[$name])) { - return false; - } - } - - // match params in the pattern - foreach ($this->_paramRules as $name => $rule) { - if (isset($params[$name]) && !is_array($params[$name]) && ($rule === '' || preg_match($rule, $params[$name]))) { - $tr["<$name>"] = urlencode($params[$name]); - unset($params[$name]); - } elseif (!isset($this->defaults[$name]) || isset($params[$name])) { - return false; - } - } - - $url = trim(strtr($this->_template, $tr), '/'); - if ($this->host !== null) { - $pos = strpos($url, '/', 8); - if ($pos !== false) { - $url = substr($url, 0, $pos) . preg_replace('#/+#', '/', substr($url, $pos)); - } - } elseif (strpos($url, '//') !== false) { - $url = preg_replace('#/+#', '/', $url); - } - - if ($url !== '') { - $url .= ($this->suffix === null ? $manager->suffix : $this->suffix); - } - - if (!empty($params)) { - $url .= '?' . http_build_query($params); - } - return $url; - } -} diff --git a/framework/yii/web/UserEvent.php b/framework/yii/web/UserEvent.php deleted file mode 100644 index 8577ef5..0000000 --- a/framework/yii/web/UserEvent.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use yii\base\Event; - -/** - * This event class is used for Events triggered by the [[User]] class. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class UserEvent extends Event -{ - /** - * @var IdentityInterface the identity object associated with this event - */ - public $identity; - /** - * @var boolean whether the login is cookie-based. This property is only meaningful - * for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_AFTER_LOGIN]] events. - */ - public $cookieBased; - /** - * @var boolean whether the login or logout should proceed. - * Event handlers may modify this property to determine whether the login or logout should proceed. - * This property is only meaningful for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_BEFORE_LOGOUT]] events. - */ - public $isValid = true; -} diff --git a/framework/yii/web/VerbFilter.php b/framework/yii/web/VerbFilter.php deleted file mode 100644 index 1ca08c2..0000000 --- a/framework/yii/web/VerbFilter.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use Yii; -use yii\base\ActionEvent; -use yii\base\Behavior; - -/** - * VerbFilter is an action filter that filters by HTTP request methods. - * - * It allows to define allowed HTTP request methods for each action and will throw - * an HTTP 405 error when the method is not allowed. - * - * To use VerbFilter, declare it in the `behaviors()` method of your controller class. - * For example, the following declarations will define a typical set of allowed - * request methods for REST CRUD actions. - * - * ~~~ - * public function behaviors() - * { - * return [ - * 'verbs' => [ - * 'class' => \yii\web\VerbFilter::className(), - * 'actions' => [ - * 'index' => ['get'], - * 'view' => ['get'], - * 'create' => ['get', 'post'], - * 'update' => ['get', 'put', 'post'], - * 'delete' => ['post', 'delete'], - * ], - * ], - * ]; - * } - * ~~~ - * - * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 - * @author Carsten Brandt <mail@cebe.cc> - * @since 2.0 - */ -class VerbFilter extends Behavior -{ - /** - * @var array this property defines the allowed request methods for each action. - * For each action that should only support limited set of request methods - * you add an entry with the action id as array key and an array of - * allowed methods (e.g. GET, HEAD, PUT) as the value. - * If an action is not listed all request methods are considered allowed. - * - * You can use '*' to stand for all actions. When an action is explicitly - * specified, it takes precedence over the specification given by '*'. - * - * For example, - * - * ~~~ - * [ - * 'create' => ['get', 'post'], - * 'update' => ['get', 'put', 'post'], - * 'delete' => ['post', 'delete'], - * '*' => ['get'], - * ] - * ~~~ - */ - public $actions = []; - - - /** - * Declares event handlers for the [[owner]]'s events. - * @return array events (array keys) and the corresponding event handler methods (array values). - */ - public function events() - { - return [Controller::EVENT_BEFORE_ACTION => 'beforeAction']; - } - - /** - * @param ActionEvent $event - * @return boolean - * @throws HttpException when the request method is not allowed. - */ - public function beforeAction($event) - { - $action = $event->action->id; - if (isset($this->actions[$action])) { - $verbs = $this->actions[$action]; - } elseif (isset($this->actions['*'])) { - $verbs = $this->actions['*']; - } else { - return $event->isValid; - } - - $verb = Yii::$app->getRequest()->getMethod(); - $allowed = array_map('strtoupper', $verbs); - if (!in_array($verb, array_map('strtoupper', $verbs))) { - $event->isValid = false; - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 - Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed)); - throw new MethodNotAllowedHttpException('Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed) . '.'); - } - - return $event->isValid; - } -} diff --git a/framework/yii/web/XmlResponseFormatter.php b/framework/yii/web/XmlResponseFormatter.php deleted file mode 100644 index 292424a..0000000 --- a/framework/yii/web/XmlResponseFormatter.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\web; - -use DOMDocument; -use DOMElement; -use DOMText; -use yii\base\Arrayable; -use yii\base\Component; -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 <qiang.xue@gmail.com> - * @since 2.0 - */ -class XmlResponseFormatter extends Component implements ResponseFormatterInterface -{ - /** - * @var string the Content-Type header for the response - */ - public $contentType = 'application/xml'; - /** - * @var string the XML version - */ - public $version = '1.0'; - /** - * @var string the XML encoding. If not set, it will use the value of [[Response::charset]]. - */ - public $encoding; - /** - * @var string the name of the root element. - */ - public $rootTag = 'response'; - /** - * @var string the name of the elements that represent the array elements with numeric keys. - */ - public $itemTag = 'item'; - - /** - * Formats the specified response. - * @param Response $response the response to be formatted. - */ - public function format($response) - { - $response->getHeaders()->set('Content-Type', $this->contentType); - $dom = new DOMDocument($this->version, $this->encoding === null ? $response->charset : $this->encoding); - $root = new DOMElement($this->rootTag); - $dom->appendChild($root); - $this->buildXml($root, $response->data); - $response->content = $dom->saveXML(); - } - - /** - * @param DOMElement $element - * @param mixed $data - */ - protected function buildXml($element, $data) - { - if (is_object($data)) { - $child = new DOMElement(StringHelper::basename(get_class($data))); - $element->appendChild($child); - if ($data instanceof Arrayable) { - $this->buildXml($child, $data->toArray()); - } else { - $array = []; - foreach ($data as $name => $value) { - $array[$name] = $value; - } - $this->buildXml($child, $array); - } - } elseif (is_array($data)) { - foreach ($data as $name => $value) { - if (is_int($name) && is_object($value)) { - $this->buildXml($element, $value); - } elseif (is_array($value) || is_object($value)) { - $child = new DOMElement(is_int($name) ? $this->itemTag : $name); - $element->appendChild($child); - $this->buildXml($child, $value); - } else { - $child = new DOMElement(is_int($name) ? $this->itemTag : $name); - $element->appendChild($child); - $child->appendChild(new DOMText((string)$value)); - } - } - } else { - $element->appendChild(new DOMText((string)$data)); - } - } -} diff --git a/framework/yii/widgets/ActiveForm.php b/framework/yii/widgets/ActiveForm.php deleted file mode 100644 index b218a2e..0000000 --- a/framework/yii/widgets/ActiveForm.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Widget; -use yii\base\Model; -use yii\helpers\Html; -use yii\helpers\Json; -use yii\web\JsExpression; - -/** - * ActiveForm ... - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class ActiveForm extends Widget -{ - /** - * @param array|string $action the form action URL. This parameter will be processed by [[\yii\helpers\Html::url()]]. - */ - public $action = ''; - /** - * @var string the form submission method. This should be either 'post' or 'get'. - * Defaults to 'post'. - */ - public $method = 'post'; - /** - * @var array the HTML attributes (name-value pairs) for the form tag. - * The values will be HTML-encoded using [[Html::encode()]]. - * If a value is null, the corresponding attribute will not be rendered. - */ - public $options = []; - /** - * @var array the default configuration used by [[field()]] when creating a new field object. - */ - public $fieldConfig; - /** - * @var string the default CSS class for the error summary container. - * @see errorSummary() - */ - public $errorSummaryCssClass = 'error-summary'; - /** - * @var string the CSS class that is added to a field container when the associated attribute is required. - */ - public $requiredCssClass = 'required'; - /** - * @var string the CSS class that is added to a field container when the associated attribute has validation error. - */ - public $errorCssClass = 'has-error'; - /** - * @var string the CSS class that is added to a field container when the associated attribute is successfully validated. - */ - public $successCssClass = 'has-success'; - /** - * @var string the CSS class that is added to a field container when the associated attribute is being validated. - */ - public $validatingCssClass = 'validating'; - /** - * @var boolean whether to enable client-side data validation. - * If [[ActiveField::enableClientValidation]] is set, its value will take precedence for that input field. - */ - public $enableClientValidation = true; - /** - * @var boolean whether to enable AJAX-based data validation. - * If [[ActiveField::enableAjaxValidation]] is set, its value will take precedence for that input field. - */ - public $enableAjaxValidation = false; - /** - * @var array|string the URL for performing AJAX-based validation. This property will be processed by - * [[Html::url()]]. Please refer to [[Html::url()]] for more details on how to configure this property. - * If this property is not set, it will take the value of the form's action attribute. - */ - public $validationUrl; - /** - * @var boolean whether to perform validation when the form is submitted. - */ - public $validateOnSubmit = true; - /** - * @var boolean whether to perform validation when an input field loses focus and its value is found changed. - * If [[ActiveField::validateOnChange]] is set, its value will take precedence for that input field. - */ - public $validateOnChange = true; - /** - * @var boolean whether to perform validation while the user is typing in an input field. - * If [[ActiveField::validateOnType]] is set, its value will take precedence for that input field. - * @see validationDelay - */ - public $validateOnType = false; - /** - * @var integer number of milliseconds that the validation should be delayed when an input field - * is changed or the user types in the field. - * If [[ActiveField::validationDelay]] is set, its value will take precedence for that input field. - */ - public $validationDelay = 200; - /** - * @var string the name of the GET parameter indicating the validation request is an AJAX request. - */ - public $ajaxVar = 'ajax'; - /** - * @var string|JsExpression a JS callback that will be called when the form is being submitted. - * The signature of the callback should be: - * - * ~~~ - * function ($form) { - * ...return false to cancel submission... - * } - * ~~~ - */ - public $beforeSubmit; - /** - * @var string|JsExpression a JS callback that is called before validating an attribute. - * The signature of the callback should be: - * - * ~~~ - * function ($form, attribute, messages) { - * ...return false to cancel the validation... - * } - * ~~~ - */ - public $beforeValidate; - /** - * @var string|JsExpression a JS callback that is called after validating an attribute. - * The signature of the callback should be: - * - * ~~~ - * function ($form, attribute, messages) { - * } - * ~~~ - */ - public $afterValidate; - /** - * @var array the client validation options for individual attributes. Each element of the array - * represents the validation options for a particular attribute. - * @internal - */ - public $attributes = []; - - /** - * Initializes the widget. - * This renders the form open tag. - */ - public function init() - { - if (!isset($this->options['id'])) { - $this->options['id'] = $this->getId(); - } - if (!isset($this->fieldConfig['class'])) { - $this->fieldConfig['class'] = ActiveField::className(); - } - echo Html::beginForm($this->action, $this->method, $this->options); - } - - /** - * Runs the widget. - * This registers the necessary javascript code and renders the form close tag. - */ - public function run() - { - if (!empty($this->attributes)) { - $id = $this->options['id']; - $options = Json::encode($this->getClientOptions()); - $attributes = Json::encode($this->attributes); - $view = $this->getView(); - ActiveFormAsset::register($view); - $view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); - } - echo Html::endForm(); - } - - /** - * Returns the options for the form JS widget. - * @return array the options - */ - protected function getClientOptions() - { - $options = [ - 'errorSummary' => '.' . $this->errorSummaryCssClass, - 'validateOnSubmit' => $this->validateOnSubmit, - 'errorCssClass' => $this->errorCssClass, - 'successCssClass' => $this->successCssClass, - 'validatingCssClass' => $this->validatingCssClass, - 'ajaxVar' => $this->ajaxVar, - ]; - if ($this->validationUrl !== null) { - $options['validationUrl'] = Html::url($this->validationUrl); - } - foreach (['beforeSubmit', 'beforeValidate', 'afterValidate'] as $name) { - if (($value = $this->$name) !== null) { - $options[$name] = $value instanceof JsExpression ? $value : new JsExpression($value); - } - } - return $options; - } - - /** - * Generates a summary of the validation errors. - * If there is no validation error, an empty error summary markup will still be generated, but it will be hidden. - * @param Model|Model[] $models the model(s) associated with this form - * @param array $options the tag options in terms of name-value pairs. The following options are specially handled: - * - * - header: string, the header HTML for the error summary. If not set, a default prompt string will be used. - * - footer: string, the footer HTML for the error summary. - * - * The rest of the options will be rendered as the attributes of the container 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 error summary - */ - public function errorSummary($models, $options = []) - { - if (!is_array($models)) { - $models = [$models]; - } - - $lines = []; - foreach ($models as $model) { - /** @var Model $model */ - foreach ($model->getFirstErrors() as $error) { - $lines[] = Html::encode($error); - } - } - - $header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>'; - $footer = isset($options['footer']) ? $options['footer'] : ''; - unset($options['header'], $options['footer']); - - if (!isset($options['class'])) { - $options['class'] = $this->errorSummaryCssClass; - } else { - $options['class'] .= ' ' . $this->errorSummaryCssClass; - } - - if (!empty($lines)) { - $content = "<ul><li>" . implode("</li>\n<li>", $lines) . "</li><ul>"; - return Html::tag('div', $header . $content . $footer, $options); - } else { - $content = "<ul></ul>"; - $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none'; - return Html::tag('div', $header . $content . $footer, $options); - } - } - - /** - * Generates a form field. - * A form field is associated with a model and an attribute. It contains a label, an input and an error message - * and use them to interact with end users to collect their inputs for the attribute. - * @param Model $model the data model - * @param string $attribute the attribute name or expression. See [[Html::getAttributeName()]] for the format - * about attribute expression. - * @param array $options the additional configurations for the field object - * @return ActiveField the created ActiveField object - * @see fieldConfig - */ - public function field($model, $attribute, $options = []) - { - return Yii::createObject(array_merge($this->fieldConfig, $options, [ - 'model' => $model, - 'attribute' => $attribute, - 'form' => $this, - ])); - } - - /** - * Validates one or several models and returns an error message array indexed by the attribute IDs. - * This is a helper method that simplifies the way of writing AJAX validation code. - * - * For example, you may use the following code in a controller action to respond - * to an AJAX validation request: - * - * ~~~ - * $model = new Post; - * $model->load($_POST); - * if (Yii::$app->request->isAjax) { - * Yii::$app->response->format = Response::FORMAT_JSON; - * return ActiveForm::validate($model); - * } - * // ... respond to non-AJAX request ... - * ~~~ - * - * To validate multiple models, simply pass each model as a parameter to this method, like - * the following: - * - * ~~~ - * ActiveForm::validate($model1, $model2, ...); - * ~~~ - * - * @param Model $model the model to be validated - * @param mixed $attributes list of attributes that should be validated. - * If this parameter is empty, it means any attribute listed in the applicable - * validation rules should be validated. - * - * When this method is used to validate multiple models, this parameter will be interpreted - * as a model. - * - * @return array the error message array indexed by the attribute IDs. - */ - public static function validate($model, $attributes = null) - { - $result = []; - if ($attributes instanceof Model) { - // validating multiple models - $models = func_get_args(); - $attributes = null; - } else { - $models = [$model]; - } - /** @var Model $model */ - foreach ($models as $model) { - $model->validate($attributes); - foreach ($model->getErrors() as $attribute => $errors) { - $result[Html::getInputId($model, $attribute)] = $errors; - } - } - return $result; - } - - /** - * Validates an array of model instances and returns an error message array indexed by the attribute IDs. - * This is a helper method that simplifies the way of writing AJAX validation code for tabular input. - * - * For example, you may use the following code in a controller action to respond - * to an AJAX validation request: - * - * ~~~ - * // ... load $models ... - * if (Yii::$app->request->isAjax) { - * Yii::$app->response->format = Response::FORMAT_JSON; - * return ActiveForm::validateMultiple($models); - * } - * // ... respond to non-AJAX request ... - * ~~~ - * - * @param array $models an array of models to be validated. - * @param mixed $attributes list of attributes that should be validated. - * If this parameter is empty, it means any attribute listed in the applicable - * validation rules should be validated. - * @return array the error message array indexed by the attribute IDs. - */ - public static function validateMultiple($models, $attributes = null) - { - $result = []; - /** @var Model $model */ - foreach ($models as $i => $model) { - $model->validate($attributes); - foreach ($model->getErrors() as $attribute => $errors) { - $result[Html::getInputId($model, "[$i]" . $attribute)] = $errors; - } - } - return $result; - } -} diff --git a/framework/yii/widgets/BaseListView.php b/framework/yii/widgets/BaseListView.php deleted file mode 100644 index 4c4e5a4..0000000 --- a/framework/yii/widgets/BaseListView.php +++ /dev/null @@ -1,215 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\InvalidConfigException; -use yii\base\Widget; -use yii\helpers\ArrayHelper; -use yii\helpers\Html; - -/** - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -abstract class BaseListView extends Widget -{ - /** - * @var array the HTML attributes for the container tag of the list view. - * The "tag" element specifies the tag name of the container element and defaults to "div". - */ - public $options = []; - /** - * @var \yii\data\DataProviderInterface the data provider for the view. This property is required. - */ - public $dataProvider; - /** - * @var array the configuration for the pager widget. By default, [[LinkPager]] will be - * used to render the pager. You can use a different widget class by configuring the "class" element. - */ - public $pager = []; - /** - * @var array the configuration for the sorter widget. By default, [[LinkSorter]] will be - * used to render the sorter. You can use a different widget class by configuring the "class" element. - */ - public $sorter = []; - /** - * @var string the HTML content to be displayed as the summary of the list view. - * If you do not want to show the summary, you may set it with an empty string. - * - * The following tokens will be replaced with the corresponding values: - * - * - `{begin}`: the starting row number (1-based) currently being displayed - * - `{end}`: the ending row number (1-based) currently being displayed - * - `{count}`: the number of rows currently being displayed - * - `{totalCount}`: the total number of rows available - * - `{page}`: the page number (1-based) current being displayed - * - `{pageCount}`: the number of pages available - */ - public $summary; - /** - * @var boolean whether to show the list view if [[dataProvider]] returns no data. - */ - 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: - * - * - `{summary}`: the summary section. See [[renderSummary()]]. - * - `{items}`: the list items. See [[renderItems()]]. - * - `{sorter}`: the sorter. See [[renderSorter()]]. - * - `{pager}`: the pager. See [[renderPager()]]. - */ - public $layout = "{summary}\n{items}\n{pager}"; - - - /** - * Renders the data models. - * @return string the rendering result. - */ - abstract public function renderItems(); - - /** - * Initializes the view. - */ - public function init() - { - 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(); - } - - /** - * Runs the widget. - */ - public function run() - { - 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->renderEmpty(); - } - $tag = ArrayHelper::remove($this->options, 'tag', 'div'); - echo Html::tag($tag, $content, $this->options); - } - - /** - * Renders a section of the specified name. - * If the named section is not supported, false will be returned. - * @param string $name the section name, e.g., `{summary}`, `{items}`. - * @return string|boolean the rendering result of the section, or false if the named section is not supported. - */ - public function renderSection($name) - { - switch ($name) { - case '{summary}': - return $this->renderSummary(); - case '{items}': - return $this->renderItems(); - case '{pager}': - return $this->renderPager(); - case '{sorter}': - return $this->renderSorter(); - default: - return false; - } - } - - /** - * Renders the HTML content indicating that the list view has no data. - * @return string the rendering result - * @see emptyText - */ - public function renderEmpty() - { - return '<div class="empty">' . ($this->emptyText === null ? Yii::t('yii', 'No results found.') : $this->emptyText) . '</div>'; - } - - /** - * Renders the summary text. - */ - 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; - $end = $begin + $count - 1; - if ($begin > $end) { - $begin = $end; - } - $page = $pagination->getPage() + 1; - $pageCount = $pagination->pageCount; - if (($summaryContent = $this->summary) === null) { - $summaryContent = '<div class="summary">' - . Yii::t('yii', 'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.') - . '</div>'; - } - } else { - $begin = $page = $pageCount = 1; - $end = $totalCount = $count; - if (($summaryContent = $this->summary) === null) { - $summaryContent = '<div class="summary">' . Yii::t('yii', 'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.') . '</div>'; - } - } - return Yii::$app->getI18n()->format($summaryContent, [ - 'begin' => $begin, - 'end' => $end, - 'count' => $count, - 'totalCount' => $totalCount, - 'page' => $page, - 'pageCount' => $pageCount, - ], Yii::$app->language); - } - - /** - * Renders the pager. - * @return string the rendering result - */ - public function renderPager() - { - $pagination = $this->dataProvider->getPagination(); - if ($pagination === false || $this->dataProvider->getCount() <= 0) { - return ''; - } - /** @var LinkPager $class */ - $class = ArrayHelper::remove($this->pager, 'class', LinkPager::className()); - $this->pager['pagination'] = $pagination; - return $class::widget($this->pager); - } - - /** - * Renders the sorter. - * @return string the rendering result - */ - public function renderSorter() - { - $sort = $this->dataProvider->getSort(); - if ($sort === false || empty($sort->attributes) || $this->dataProvider->getCount() <= 0) { - return ''; - } - /** @var LinkSorter $class */ - $class = ArrayHelper::remove($this->sorter, 'class', LinkSorter::className()); - $this->sorter['sort'] = $sort; - return $class::widget($this->sorter); - } -} diff --git a/framework/yii/widgets/Block.php b/framework/yii/widgets/Block.php deleted file mode 100644 index fdd210f..0000000 --- a/framework/yii/widgets/Block.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use yii\base\Widget; - -/** - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class Block extends Widget -{ - /** - * @var string the ID of this block. - */ - public $id; - /** - * @var boolean whether to render the block content in place. Defaults to false, - * meaning the captured block content will not be displayed. - */ - public $renderInPlace = false; - - /** - * Starts recording a block. - */ - public function init() - { - ob_start(); - ob_implicit_flush(false); - } - - /** - * Ends recording a block. - * This method stops output buffering and saves the rendering result as a named block in the controller. - */ - public function run() - { - $block = ob_get_clean(); - if ($this->renderInPlace) { - echo $block; - } - $this->view->blocks[$this->id] = $block; - } -} diff --git a/framework/yii/widgets/Breadcrumbs.php b/framework/yii/widgets/Breadcrumbs.php deleted file mode 100644 index 2353845..0000000 --- a/framework/yii/widgets/Breadcrumbs.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Widget; -use yii\base\InvalidConfigException; -use yii\helpers\Html; - -/** - * Breadcrumbs displays a list of links indicating the position of the current page in the whole site hierarchy. - * - * For example, breadcrumbs like "Home / Sample Post / Edit" means the user is viewing an edit page - * for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" - * to return to the homepage. - * - * To use Breadcrumbs, you need to configure its [[links]] property, which specifies the links to be displayed. For example, - * - * ~~~ - * // $this is the view object currently being used - * echo Breadcrumbs::widget([ - * 'links' => [ - * ['label' => 'Sample Post', 'url' => ['post/edit', 'id' => 1]], - * 'Edit', - * ], - * ]); - * ~~~ - * - * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view. - * You can use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different - * views. In the layout view, you assign this view parameter to the [[links]] property like the following: - * - * ~~~ - * // $this is the view object currently being used - * echo Breadcrumbs::widget([ - * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], - * ]); - * ~~~ - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class Breadcrumbs extends Widget -{ - /** - * @var string the name of the breadcrumb container tag. - */ - public $tag = 'ul'; - /** - * @var array the HTML attributes for the breadcrumb container tag. - */ - public $options = ['class' => 'breadcrumb']; - /** - * @var boolean whether to HTML-encode the link labels. - */ - public $encodeLabels = true; - /** - * @var string the first hyperlink in the breadcrumbs (called home link). - * If this property is not set, it will default to a link pointing to [[\yii\web\Application::homeUrl]] - * with the label 'Home'. If this property is false, the home link will not be rendered. - */ - public $homeLink; - /** - * @var array list of links to appear in the breadcrumbs. If this property is empty, - * the widget will not render anything. Each array element represents a single link in the breadcrumbs - * with the following structure: - * - * ~~~ - * [ - * 'label' => 'label of the link', // required - * 'url' => 'url of the link', // optional, will be processed by Html::url() - * ] - * ~~~ - * - * If a link is active, you only need to specify its "label", and instead of writing `['label' => $label]`, - * you should simply use `$label`. - */ - public $links = []; - /** - * @var string the template used to render each inactive item in the breadcrumbs. The token `{link}` - * will be replaced with the actual HTML link for each inactive item. - */ - public $itemTemplate = "<li>{link}</li>\n"; - /** - * @var string the template used to render each active item in the breadcrumbs. The token `{link}` - * will be replaced with the actual HTML link for each active item. - */ - public $activeItemTemplate = "<li class=\"active\">{link}</li>\n"; - - /** - * Renders the widget. - */ - public function run() - { - if (empty($this->links)) { - return; - } - $links = []; - if ($this->homeLink === null) { - $links[] = $this->renderItem([ - 'label' => Yii::t('yii', 'Home'), - 'url' => Yii::$app->homeUrl, - ], $this->itemTemplate); - } elseif ($this->homeLink !== false) { - $links[] = $this->renderItem($this->homeLink, $this->itemTemplate); - } - foreach ($this->links as $link) { - if (!is_array($link)) { - $link = ['label' => $link]; - } - $links[] = $this->renderItem($link, isset($link['url']) ? $this->itemTemplate : $this->activeItemTemplate); - } - echo Html::tag($this->tag, implode('', $links), $this->options); - } - - /** - * Renders a single breadcrumb item. - * @param array $link the link to be rendered. It must contain the "label" element. The "url" element is optional. - * @param string $template the template to be used to rendered the link. The token "{link}" will be replaced by the link. - * @return string the rendering result - * @throws InvalidConfigException if `$link` does not have "label" element. - */ - protected function renderItem($link, $template) - { - if (isset($link['label'])) { - $label = $this->encodeLabels ? Html::encode($link['label']) : $link['label']; - } else { - throw new InvalidConfigException('The "label" element is required for each link.'); - } - if (isset($link['url'])) { - return strtr($template, ['{link}' => Html::a($label, $link['url'])]); - } else { - return strtr($template, ['{link}' => $label]); - } - } -} diff --git a/framework/yii/widgets/ContentDecorator.php b/framework/yii/widgets/ContentDecorator.php deleted file mode 100644 index 9224f35..0000000 --- a/framework/yii/widgets/ContentDecorator.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use yii\base\InvalidConfigException; -use yii\base\Widget; - -/** - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class ContentDecorator extends Widget -{ - /** - * @var string the view file that will be used to decorate the content enclosed by this widget. - * This can be specified as either the view file path or path alias. - */ - public $viewFile; - /** - * @var array the parameters (name => value) to be extracted and made available in the decorative view. - */ - public $params = []; - - /** - * Starts recording a clip. - */ - public function init() - { - if ($this->viewFile === null) { - throw new InvalidConfigException('ContentDecorator::viewFile must be set.'); - } - ob_start(); - ob_implicit_flush(false); - } - - /** - * Ends recording a clip. - * This method stops output buffering and saves the rendering result as a named clip in the controller. - */ - public function run() - { - $params = $this->params; - $params['content'] = ob_get_clean(); - // render under the existing context - echo $this->view->renderFile($this->viewFile, $params); - } -} diff --git a/framework/yii/widgets/DetailView.php b/framework/yii/widgets/DetailView.php deleted file mode 100644 index 3a45a4e..0000000 --- a/framework/yii/widgets/DetailView.php +++ /dev/null @@ -1,211 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Arrayable; -use yii\base\Formatter; -use yii\base\InvalidConfigException; -use yii\base\Model; -use yii\base\Widget; -use yii\helpers\ArrayHelper; -use yii\helpers\Html; -use yii\helpers\Inflector; - -/** - * DetailView displays the detail of a single data [[model]]. - * - * DetailView is best used for displaying a model in a regular format (e.g. each model attribute - * is displayed as a row in a table.) The model can be either an instance of [[Model]] - * or an associative array. - * - * DetailView uses the [[attributes]] property to determines which model attributes - * should be displayed and how they should be formatted. - * - * A typical usage of DetailView is as follows: - * - * ~~~ - * echo DetailView::widget([ - * 'model' => $model, - * 'attributes' => [ - * 'title', // title attribute (in plain text) - * 'description:html', // description attribute in HTML - * [ // the owner name of the model - * 'label' => 'Owner', - * 'value' => $model->owner->name, - * ], - * ], - * ]); - * ~~~ - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class DetailView extends Widget -{ - /** - * @var array|object the data model whose details are to be displayed. This can be either a [[Model]] instance - * or an associative array. - */ - public $model; - /** - * @var array a list of attributes to be displayed in the detail view. Each array element - * represents the specification for displaying one particular attribute. - * - * An attribute can be specified as a string in the format of "Name" or "Name:Format", where "Name" refers to - * the attribute name, and "Format" represents the format of the attribute. The "Format" is passed to the [[Formatter::format()]] - * method to format an attribute value into a displayable text. Please refer to [[Formatter]] for the supported types. - * - * An attribute can also be specified in terms of an array with the following elements: - * - * - name: the attribute name. This is required if either "label" or "value" is not specified. - * - label: the label associated with the attribute. If this is not specified, it will be generated from the attribute name. - * - value: the value to be displayed. If this is not specified, it will be retrieved from [[model]] using the attribute name - * by calling [[ArrayHelper::getValue()]]. Note that this value will be formatted into a displayable text - * according to the "format" option. - * - format: the type of the value that determines how the value would be formatted into a displayable text. - * Please refer to [[Formatter]] for supported types. - * - visible: whether the attribute is visible. If set to `false`, the attribute will NOT be displayed. - */ - public $attributes; - /** - * @var string|callback the template used to render a single attribute. If a string, the token `{label}` - * and `{value}` will be replaced with the label and the value of the corresponding attribute. - * If a callback (e.g. an anonymous function), the signature must be as follows: - * - * ~~~ - * function ($attribute, $index, $widget) - * ~~~ - * - * where `$attribute` refer to the specification of the attribute being rendered, `$index` is the zero-based - * index of the attribute in the [[attributes]] array, and `$widget` refers to this widget instance. - */ - public $template = "<tr><th>{label}</th><td>{value}</td></tr>"; - /** - * @var array the HTML attributes for the container tag of this widget. The "tag" option specifies - * what container tag should be used. It defaults to "table" if not set. - */ - public $options = ['class' => 'table table-striped table-bordered detail-view']; - /** - * @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]] - * instance. If this property is not set, the "formatter" application component will be used. - */ - public $formatter; - - /** - * Initializes the detail view. - * This method will initialize required property values. - */ - public function init() - { - if ($this->model === null) { - throw new InvalidConfigException('Please specify the "model" property.'); - } - if ($this->formatter == null) { - $this->formatter = Yii::$app->getFormatter(); - } elseif (is_array($this->formatter)) { - $this->formatter = Yii::createObject($this->formatter); - } - if (!$this->formatter instanceof Formatter) { - throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); - } - $this->normalizeAttributes(); - } - - /** - * Renders the detail view. - * This is the main entry of the whole detail view rendering. - */ - public function run() - { - $rows = []; - $i = 0; - foreach ($this->attributes as $attribute) { - $rows[] = $this->renderAttribute($attribute, $i++); - } - - $tag = ArrayHelper::remove($this->options, 'tag', 'table'); - echo Html::tag($tag, implode("\n", $rows), $this->options); - } - - /** - * Renders a single attribute. - * @param array $attribute the specification of the attribute to be rendered. - * @param integer $index the zero-based index of the attribute in the [[attributes]] array - * @return string the rendering result - */ - protected function renderAttribute($attribute, $index) - { - if (is_string($this->template)) { - return strtr($this->template, [ - '{label}' => $attribute['label'], - '{value}' => $this->formatter->format($attribute['value'], $attribute['format']), - ]); - } else { - return call_user_func($this->template, $attribute, $index, $this); - } - } - - /** - * Normalizes the attribute specifications. - * @throws InvalidConfigException - */ - protected function normalizeAttributes() - { - if ($this->attributes === null) { - if ($this->model instanceof Model) { - $this->attributes = $this->model->attributes(); - } elseif (is_object($this->model)) { - $this->attributes = $this->model instanceof Arrayable ? $this->model->toArray() : array_keys(get_object_vars($this->model)); - } elseif (is_array($this->model)) { - $this->attributes = array_keys($this->model); - } else { - throw new InvalidConfigException('The "model" property must be either an array or an object.'); - } - sort($this->attributes); - } - - foreach ($this->attributes as $i => $attribute) { - if (is_string($attribute)) { - if (!preg_match('/^(\w+)(\s*:\s*(\w+))?$/', $attribute, $matches)) { - throw new InvalidConfigException('The attribute must be specified in the format of "Name" or "Name:Format"'); - } - $attribute = [ - 'name' => $matches[1], - 'format' => isset($matches[3]) ? $matches[3] : 'text', - ]; - } - - if (!is_array($attribute)) { - throw new InvalidConfigException('The attribute configuration must be an array.'); - } - - if (isset($attribute['visible']) && !$attribute['visible']) { - continue; - } - - if (!isset($attribute['format'])) { - $attribute['format'] = 'text'; - } - if (isset($attribute['name'])) { - $name = $attribute['name']; - if (!isset($attribute['label'])) { - $attribute['label'] = $this->model instanceof Model ? $this->model->getAttributeLabel($name) : Inflector::camel2words($name, true); - } - if (!array_key_exists('value', $attribute)) { - $attribute['value'] = ArrayHelper::getValue($this->model, $name); - } - } elseif (!isset($attribute['label']) || !array_key_exists('value', $attribute)) { - throw new InvalidConfigException('The attribute configuration requires the "name" element to determine the value and display label.'); - } - - $this->attributes[$i] = $attribute; - } - } -} diff --git a/framework/yii/widgets/FragmentCache.php b/framework/yii/widgets/FragmentCache.php deleted file mode 100644 index 57c4659..0000000 --- a/framework/yii/widgets/FragmentCache.php +++ /dev/null @@ -1,178 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Widget; -use yii\caching\Cache; -use yii\caching\Dependency; - -/** - * - * @property string|boolean $cachedContent The cached content. False is returned if valid content is not found - * in the cache. This property is read-only. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class FragmentCache extends Widget -{ - /** - * @var Cache|string the cache object or the application component ID of the cache object. - * After the FragmentCache object is created, if you want to change this property, - * you should only assign it with a cache object. - */ - public $cache = 'cache'; - /** - * @var integer number of seconds that the data can remain valid in cache. - * Use 0 to indicate that the cached data will never expire. - */ - public $duration = 60; - /** - * @var array|Dependency the dependency that the cached content depends on. - * This can be either a [[Dependency]] object or a configuration array for creating the dependency object. - * For example, - * - * ~~~ - * [ - * 'class' => 'yii\caching\DbDependency', - * 'sql' => 'SELECT MAX(lastModified) FROM Post', - * ] - * ~~~ - * - * would make the output cache depends on the last modified time of all posts. - * If any post has its modification time changed, the cached content would be invalidated. - */ - public $dependency; - /** - * @var array list of factors that would cause the variation of the content being cached. - * Each factor is a string representing a variation (e.g. the language, a GET parameter). - * The following variation setting will cause the content to be cached in different versions - * according to the current application language: - * - * ~~~ - * [ - * Yii::$app->language, - * ] - */ - public $variations; - /** - * @var boolean whether to enable the fragment cache. You may use this property to turn on and off - * the fragment cache according to specific setting (e.g. enable fragment cache only for GET requests). - */ - public $enabled = true; - /** - * @var array a list of placeholders for embedding dynamic contents. This property - * is used internally to implement the content caching feature. Do not modify it. - */ - public $dynamicPlaceholders; - - /** - * Initializes the FragmentCache object. - */ - public function init() - { - parent::init(); - - if (!$this->enabled) { - $this->cache = null; - } elseif (is_string($this->cache)) { - $this->cache = Yii::$app->getComponent($this->cache); - } - - if ($this->getCachedContent() === false) { - $this->getView()->cacheStack[] = $this; - ob_start(); - ob_implicit_flush(false); - } - } - - /** - * Marks the end of content to be cached. - * Content displayed before this method call and after [[init()]] - * will be captured and saved in cache. - * This method does nothing if valid content is already found in cache. - */ - public function run() - { - if (($content = $this->getCachedContent()) !== false) { - echo $content; - } elseif ($this->cache instanceof Cache) { - $content = ob_get_clean(); - array_pop($this->getView()->cacheStack); - if (is_array($this->dependency)) { - $this->dependency = Yii::createObject($this->dependency); - } - $data = [$content, $this->dynamicPlaceholders]; - $this->cache->set($this->calculateKey(), $data, $this->duration, $this->dependency); - - if (empty($this->getView()->cacheStack) && !empty($this->dynamicPlaceholders)) { - $content = $this->updateDynamicContent($content, $this->dynamicPlaceholders); - } - echo $content; - } - } - - /** - * @var string|boolean the cached content. False if the content is not cached. - */ - private $_content; - - /** - * Returns the cached content if available. - * @return string|boolean the cached content. False is returned if valid content is not found in the cache. - */ - public function getCachedContent() - { - if ($this->_content === null) { - $this->_content = false; - if ($this->cache instanceof Cache) { - $key = $this->calculateKey(); - $data = $this->cache->get($key); - if (is_array($data) && count($data) === 2) { - list ($content, $placeholders) = $data; - if (is_array($placeholders) && count($placeholders) > 0) { - if (empty($this->getView()->cacheStack)) { - // outermost cache: replace placeholder with dynamic content - $content = $this->updateDynamicContent($content, $placeholders); - } - foreach ($placeholders as $name => $statements) { - $this->getView()->addDynamicPlaceholder($name, $statements); - } - } - $this->_content = $content; - } - } - } - return $this->_content; - } - - protected function updateDynamicContent($content, $placeholders) - { - foreach ($placeholders as $name => $statements) { - $placeholders[$name] = $this->getView()->evaluateDynamicContent($statements); - } - return strtr($content, $placeholders); - } - - /** - * Generates a unique key used for storing the content in cache. - * The key generated depends on both [[id]] and [[variations]]. - * @return mixed a valid cache key - */ - protected function calculateKey() - { - $factors = [__CLASS__, $this->getId()]; - if (is_array($this->variations)) { - foreach ($this->variations as $factor) { - $factors[] = $factor; - } - } - return $factors; - } -} diff --git a/framework/yii/widgets/InputWidget.php b/framework/yii/widgets/InputWidget.php deleted file mode 100644 index 0a4b5b7..0000000 --- a/framework/yii/widgets/InputWidget.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Widget; -use yii\base\Model; -use yii\base\InvalidConfigException; -use yii\helpers\Html; - -/** - * InputWidget is the base class for widgets that collect user inputs. - * - * An input widget can be associated with a data model and an attribute, - * or a name and a value. If the former, the name and the value will - * be generated automatically. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class InputWidget extends Widget -{ - /** - * @var Model the data model that this widget is associated with. - */ - public $model; - /** - * @var string the model attribute that this widget is associated with. - */ - public $attribute; - /** - * @var string the input name. This must be set if [[model]] and [[attribute]] are not set. - */ - public $name; - /** - * @var string the input value. - */ - public $value; - /** - * @var array the HTML attributes for the input tag. - */ - public $options = []; - - - /** - * Initializes the widget. - * If you override this method, make sure you call the parent implementation first. - */ - public function init() - { - if (!$this->hasModel() && $this->name === null) { - throw new InvalidConfigException("Either 'name', or 'model' and 'attribute' properties must be specified."); - } - if (!isset($this->options['id'])) { - $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId(); - } - parent::init(); - } - - /** - * @return boolean whether this widget is associated with a data model. - */ - protected function hasModel() - { - return $this->model instanceof Model && $this->attribute !== null; - } -} diff --git a/framework/yii/widgets/LinkPager.php b/framework/yii/widgets/LinkPager.php deleted file mode 100644 index 807a4b8..0000000 --- a/framework/yii/widgets/LinkPager.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\InvalidConfigException; -use yii\helpers\Html; -use yii\base\Widget; -use yii\data\Pagination; - -/** - * LinkPager displays a list of hyperlinks that lead to different pages of target. - * - * LinkPager works with a [[Pagination]] object which specifies the totally number - * of pages and the current page number. - * - * Note that LinkPager only generates the necessary HTML markups. In order for it - * to look like a real pager, you should provide some CSS styles for it. - * With the default configuration, LinkPager should look good using Twitter Bootstrap CSS framework. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class LinkPager extends Widget -{ - /** - * @var Pagination the pagination object that this pager is associated with. - * You must set this property in order to make LinkPager work. - */ - public $pagination; - /** - * @var array HTML attributes for the pager container tag. - */ - public $options = ['class' => 'pagination']; - /** - * @var string the CSS class for the "first" page button. - */ - public $firstPageCssClass = 'first'; - /** - * @var string the CSS class for the "last" page button. - */ - public $lastPageCssClass = 'last'; - /** - * @var string the CSS class for the "previous" page button. - */ - public $prevPageCssClass = 'prev'; - /** - * @var string the CSS class for the "next" page button. - */ - public $nextPageCssClass = 'next'; - /** - * @var string the CSS class for the active (currently selected) page button. - */ - public $activePageCssClass = 'active'; - /** - * @var string the CSS class for the disabled page buttons. - */ - public $disabledPageCssClass = 'disabled'; - /** - * @var integer maximum number of page buttons that can be displayed. Defaults to 10. - */ - public $maxButtonCount = 10; - /** - * @var string the label for the "next" page button. Note that this will NOT be HTML-encoded. - * If this property is null, the "next" page button will not be displayed. - */ - public $nextPageLabel = '»'; - /** - * @var string the text label for the previous page button. Note that this will NOT be HTML-encoded. - * If this property is null, the "previous" page button will not be displayed. - */ - public $prevPageLabel = '«'; - /** - * @var string the text label for the "first" page button. Note that this will NOT be HTML-encoded. - * If this property is null, the "first" page button will not be displayed. - */ - public $firstPageLabel; - /** - * @var string the text label for the "last" page button. Note that this will NOT be HTML-encoded. - * If this property is null, the "last" page button will not be displayed. - */ - public $lastPageLabel; - - - /** - * Initializes the pager. - */ - public function init() - { - if ($this->pagination === null) { - throw new InvalidConfigException('The "pagination" property must be set.'); - } - } - - /** - * Executes the widget. - * This overrides the parent implementation by displaying the generated page buttons. - */ - public function run() - { - echo $this->renderPageButtons(); - } - - /** - * Renders the page buttons. - * @return string the rendering result - */ - protected function renderPageButtons() - { - $buttons = []; - - $pageCount = $this->pagination->getPageCount(); - $currentPage = $this->pagination->getPage(); - - // first page - if ($this->firstPageLabel !== null) { - $buttons[] = $this->renderPageButton($this->firstPageLabel, 0, $this->firstPageCssClass, $currentPage <= 0, false); - } - - // prev page - if ($this->prevPageLabel !== null) { - if (($page = $currentPage - 1) < 0) { - $page = 0; - } - $buttons[] = $this->renderPageButton($this->prevPageLabel, $page, $this->prevPageCssClass, $currentPage <= 0, false); - } - - // internal pages - list($beginPage, $endPage) = $this->getPageRange(); - for ($i = $beginPage; $i <= $endPage; ++$i) { - $buttons[] = $this->renderPageButton($i + 1, $i, null, false, $i == $currentPage); - } - - // next page - if ($this->nextPageLabel !== null) { - if (($page = $currentPage + 1) >= $pageCount - 1) { - $page = $pageCount - 1; - } - $buttons[] = $this->renderPageButton($this->nextPageLabel, $page, $this->nextPageCssClass, $currentPage >= $pageCount - 1, false); - } - - // last page - if ($this->lastPageLabel !== null) { - $buttons[] = $this->renderPageButton($this->lastPageLabel, $pageCount - 1, $this->lastPageCssClass, $currentPage >= $pageCount - 1, false); - } - - return Html::tag('ul', implode("\n", $buttons), $this->options); - } - - /** - * Renders a page button. - * You may override this method to customize the generation of page buttons. - * @param string $label the text label for the button - * @param integer $page the page number - * @param string $class the CSS class for the page button. - * @param boolean $disabled whether this page button is disabled - * @param boolean $active whether this page button is active - * @return string the rendering result - */ - protected function renderPageButton($label, $page, $class, $disabled, $active) - { - $options = ['class' => $class === '' ? null : $class]; - if ($active) { - Html::addCssClass($options, $this->activePageCssClass); - } - if ($disabled) { - Html::addCssClass($options, $this->disabledPageCssClass); - return Html::tag('li', Html::tag('span', $label), $options); - } - return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), ['data-page' => $page]), $options); - } - - /** - * @return array the begin and end pages that need to be displayed. - */ - protected function getPageRange() - { - $currentPage = $this->pagination->getPage(); - $pageCount = $this->pagination->getPageCount(); - - $beginPage = max(0, $currentPage - (int)($this->maxButtonCount / 2)); - if (($endPage = $beginPage + $this->maxButtonCount - 1) >= $pageCount) { - $endPage = $pageCount - 1; - $beginPage = max(0, $endPage - $this->maxButtonCount + 1); - } - return [$beginPage, $endPage]; - } -} diff --git a/framework/yii/widgets/LinkSorter.php b/framework/yii/widgets/LinkSorter.php deleted file mode 100644 index bfafcea..0000000 --- a/framework/yii/widgets/LinkSorter.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\InvalidConfigException; -use yii\base\Widget; -use yii\data\Sort; -use yii\helpers\Html; - -/** - * LinkSorter renders a list of sort links for the given sort definition. - * - * LinkSorter will generate a hyperlink for every attribute declared in [[sort]]. - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class LinkSorter extends Widget -{ - /** - * @var Sort the sort definition - */ - public $sort; - /** - * @var array list of the attributes that support sorting. If not set, it will be determined - * using [[Sort::attributes]]. - */ - public $attributes; - /** - * @var array HTML attributes for the sorter container tag. - * See [[yii\helpers\Html::ul()]] for special attributes. - */ - public $options = ['class' => 'sorter']; - - - /** - * Initializes the sorter. - */ - public function init() - { - if ($this->sort === null) { - throw new InvalidConfigException('The "sort" property must be set.'); - } - } - - /** - * Executes the widget. - * This method renders the sort links. - */ - public function run() - { - echo $this->renderSortLinks(); - } - - /** - * Renders the sort links. - * @return string the rendering result - */ - protected function renderSortLinks() - { - $attributes = empty($this->attributes) ? array_keys($this->sort->attributes) : $this->attributes; - $links = []; - foreach ($attributes as $name) { - $links[] = $this->sort->link($name); - } - return Html::ul($links, array_merge($this->options, ['encode' => false])); - } -} diff --git a/framework/yii/widgets/ListView.php b/framework/yii/widgets/ListView.php deleted file mode 100644 index 43eaab4..0000000 --- a/framework/yii/widgets/ListView.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\helpers\ArrayHelper; -use yii\helpers\Html; - -/** - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class ListView extends BaseListView -{ - /** - * @var array the HTML attributes for the container of the rendering result of each data model. - * The "tag" element specifies the tag name of the container element and defaults to "div". - * If "tag" is false, it means no container element will be rendered. - */ - public $itemOptions = []; - /** - * @var string|callback the name of the view for rendering each data item, or a callback (e.g. an anonymous function) - * for rendering each data item. If it specifies a view name, the following variables will - * be available in the view: - * - * - `$model`: mixed, the data model - * - `$key`: mixed, the key value associated with the data item - * - `$index`: integer, the zero-based index of the data item in the items array returned by [[dataProvider]]. - * - `$widget`: ListView, this widget instance - * - * Note that the view name is resolved into the view file by the current context of the [[view]] object. - * - * If this property is specified as a callback, it should have the following signature: - * - * ~~~ - * function ($model, $key, $index, $widget) - * ~~~ - */ - public $itemView; - /** - * @var array additional parameters to be passed to [[itemView]] when it is being rendered. - * This property is used only when [[itemView]] is a string representing a view name. - */ - public $viewParams = []; - /** - * @var string the HTML code to be displayed between any two consecutive items. - */ - public $separator = "\n"; - /** - * @var array the HTML attributes for the container tag of the list view. - * The "tag" element specifies the tag name of the container element and defaults to "div". - */ - public $options = ['class' => 'list-view']; - - - /** - * Renders all data models. - * @return string the rendering result - */ - public function renderItems() - { - $models = $this->dataProvider->getModels(); - $keys = $this->dataProvider->getKeys(); - $rows = []; - foreach (array_values($models) as $index => $model) { - $rows[] = $this->renderItem($model, $keys[$index], $index); - } - return implode($this->separator, $rows); - } - - /** - * Renders a single data model. - * @param mixed $model the data model to be rendered - * @param mixed $key the key value associated with the data model - * @param integer $index the zero-based index of the data model in the model array returned by [[dataProvider]]. - * @return string the rendering result - */ - public function renderItem($model, $key, $index) - { - if ($this->itemView === null) { - $content = $key; - } elseif (is_string($this->itemView)) { - $content = $this->getView()->render($this->itemView, array_merge([ - 'model' => $model, - 'key' => $key, - 'index' => $index, - 'widget' => $this, - ], $this->viewParams)); - } else { - $content = call_user_func($this->itemView, $model, $key, $index, $this); - } - $options = $this->itemOptions; - $tag = ArrayHelper::remove($options, 'tag', 'div'); - if ($tag !== false) { - $options['data-key'] = is_array($key) ? json_encode($key) : $key; - return Html::tag($tag, $content, $options); - } else { - return $content; - } - } -} diff --git a/framework/yii/widgets/MaskedInput.php b/framework/yii/widgets/MaskedInput.php deleted file mode 100644 index 7eb42a7..0000000 --- a/framework/yii/widgets/MaskedInput.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use yii\base\InvalidConfigException; -use yii\helpers\Html; -use yii\helpers\Json; -use yii\web\JsExpression; - -/** - * MaskedInput generates a masked text input. - * - * MaskedInput is similar to [[Html::textInput()]] except that - * an input mask will be used to force users to enter properly formatted data, - * such as phone numbers, social security numbers. - * - * To use MaskedInput, you must set the [[mask]] property. The following example - * shows how to use MaskedInput to collect phone numbers: - * - * ~~~ - * echo MaskedInput::widget([ - * 'name' => 'phone', - * 'mask' => '999-999-9999', - * ]); - * ~~~ - * - * The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin). - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class MaskedInput extends InputWidget -{ - /** - * @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined: - * - * - `a`: represents an alpha character (A-Z,a-z) - * - `9`: represents a numeric character (0-9) - * - `*`: represents an alphanumeric character (A-Z,a-z,0-9) - * - `?`: anything listed after '?' within the mask is considered optional user input - * - * Additional characters can be defined by specifying the [[charMap]] property. - */ - public $mask; - /** - * @var array the mapping between mask characters and the corresponding patterns. - * For example, `['~' => '[+-]']` specifies that the '~' character expects '+' or '-' input. - * Defaults to null, meaning using the map as described in [[mask]]. - */ - public $charMap; - /** - * @var string the character prompting for user input. Defaults to underscore '_'. - */ - public $placeholder; - /** - * @var string a JavaScript function callback that will be invoked when user finishes the input. - */ - public $completed; - - - /** - * Initializes the widget. - * @throws InvalidConfigException if the "mask" property is not set. - */ - public function init() - { - parent::init(); - if (empty($this->mask)) { - throw new InvalidConfigException('The "mask" property must be set.'); - } - } - - /** - * Runs the widget. - */ - public function run() - { - if ($this->hasModel()) { - echo Html::activeTextInput($this->model, $this->attribute, $this->options); - } else { - echo Html::textInput($this->name, $this->value, $this->options); - } - $this->registerClientScript(); - } - - /** - * Registers the needed JavaScript. - */ - public function registerClientScript() - { - $options = $this->getClientOptions(); - $options = empty($options) ? '' : ',' . Json::encode($options); - $js = ''; - if (is_array($this->charMap) && !empty($this->charMap)) { - $js .= 'jQuery.mask.definitions=' . Json::encode($this->charMap) . ";\n"; - } - $id = $this->options['id']; - $js .= "jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});"; - $view = $this->getView(); - MaskedInputAsset::register($view); - $view->registerJs($js); - } - - /** - * @return array the options for the text field - */ - protected function getClientOptions() - { - $options = []; - if ($this->placeholder !== null) { - $options['placeholder'] = $this->placeholder; - } - - if ($this->completed !== null) { - if ($this->completed instanceof JsExpression) { - $options['completed'] = $this->completed; - } else { - $options['completed'] = new JsExpression($this->completed); - } - } - - return $options; - } -} diff --git a/framework/yii/widgets/Menu.php b/framework/yii/widgets/Menu.php deleted file mode 100644 index d5ff8ef..0000000 --- a/framework/yii/widgets/Menu.php +++ /dev/null @@ -1,307 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use Yii; -use yii\base\Widget; -use yii\helpers\ArrayHelper; -use yii\helpers\Html; - -/** - * Menu displays a multi-level menu using nested HTML lists. - * - * The main property of Menu is [[items]], which specifies the possible items in the menu. - * A menu item can contain sub-items which specify the sub-menu under that menu item. - * - * Menu checks the current route and request parameters to toggle certain menu items - * with active state. - * - * Note that Menu only renders the HTML tags about the menu. It does do any styling. - * You are responsible to provide CSS styles to make it look like a real menu. - * - * The following example shows how to use Menu: - * - * ~~~ - * echo Menu::widget([ - * 'items' => [ - * // Important: you need to specify url as 'controller/action', - * // not just as 'controller' even if default action is used. - * ['label' => 'Home', 'url' => ['site/index']], - * // 'Products' menu item will be selected as long as the route is 'product/index' - * ['label' => 'Products', 'url' => ['product/index'], 'items' => [ - * ['label' => 'New Arrivals', 'url' => ['product/index', 'tag' => 'new']], - * ['label' => 'Most Popular', 'url' => ['product/index', 'tag' => 'popular']], - * ]], - * ['label' => 'Login', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], - * ], - * ]); - * ~~~ - * - * @author Qiang Xue <qiang.xue@gmail.com> - * @since 2.0 - */ -class Menu extends Widget -{ - /** - * @var array list of menu items. Each menu item should be an array of the following structure: - * - * - label: string, optional, specifies the menu item label. When [[encodeLabels]] is true, the label - * will be HTML-encoded. If the label is not specified, an empty string will be used. - * - url: string or array, optional, specifies the URL of the menu item. It will be processed by [[Html::url]]. - * When this is set, the actual menu item content will be generated using [[linkTemplate]]; - * otherwise, [[labelTemplate]] will be used. - * - visible: boolean, optional, whether this menu item is visible. Defaults to true. - * - items: array, optional, specifies the sub-menu items. Its format is the same as the parent items. - * - active: boolean, optional, whether this menu item is in active state (currently selected). - * If a menu item is active, its CSS class will be appended with [[activeCssClass]]. - * If this option is not set, the menu item will be set active automatically when the current request - * is triggered by [[url]]. For more details, please refer to [[isItemActive()]]. - * - template: string, optional, the template used to render the content of this menu item. - * The token `{url}` will be replaced by the URL associated with this menu item, - * and the token `{label}` will be replaced by the label of the menu item. - * If this option is not set, [[linkTemplate]] or [[labelTemplate]] will be used instead. - * - options: array, optional, the HTML attributes for the menu container tag. - */ - public $items = []; - /** - * @var array list of HTML attributes for the menu container tag. This will be overwritten - * by the "options" set in individual [[items]]. The following special options are recognized: - * - * - tag: string, defaults to "li", the tag name of the item container tags. - */ - public $itemOptions = []; - /** - * @var string the template used to render the body of a menu which is a link. - * In this template, the token `{url}` will be replaced with the corresponding link URL; - * while `{label}` will be replaced with the link text. - * This property will be overridden by the `template` option set in individual menu items via [[items]]. - */ - public $linkTemplate = '<a href="{url}">{label}</a>'; - /** - * @var string the template used to render the body of a menu which is NOT a link. - * In this template, the token `{label}` will be replaced with the label of the menu item. - * This property will be overridden by the `template` option set in individual menu items via [[items]]. - */ - public $labelTemplate = '{label}'; - /** - * @var string the template used to render a list of sub-menus. - * In this template, the token `{items}` will be replaced with the renderer sub-menu items. - */ - public $submenuTemplate = "\n<ul>\n{items}\n</ul>\n"; - /** - * @var boolean whether the labels for menu items should be HTML-encoded. - */ - public $encodeLabels = true; - /** - * @var string the CSS class to be appended to the active menu item. - */ - public $activeCssClass = 'active'; - /** - * @var boolean whether to automatically activate items according to whether their route setting - * matches the currently requested route. - * @see isItemActive() - */ - public $activateItems = true; - /** - * @var boolean whether to activate parent menu items when one of the corresponding child menu items is active. - * The activated parent menu items will also have its CSS classes appended with [[activeCssClass]]. - */ - public $activateParents = false; - /** - * @var boolean whether to hide empty menu items. An empty menu item is one whose `url` option is not - * set and which has no visible child menu items. - */ - public $hideEmptyItems = true; - /** - * @var array the HTML attributes for the menu's container tag. The following special options are recognized: - * - * - tag: string, defaults to "ul", the tag name of the item container tags. - */ - public $options = []; - /** - * @var string the CSS class that will be assigned to the first item in the main menu or each submenu. - * Defaults to null, meaning no such CSS class will be assigned. - */ - public $firstItemCssClass; - /** - * @var string the CSS class that will be assigned to the last item in the main menu or each submenu. - * Defaults to null, meaning no such CSS class will be assigned. - */ - public $lastItemCssClass; - /** - * @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() - */ - 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() - */ - public $params; - - - /** - * Renders the menu. - */ - public function run() - { - if ($this->route === null && Yii::$app->controller !== null) { - $this->route = Yii::$app->controller->getRoute(); - } - if ($this->params === null) { - $this->params = $_GET; - } - $items = $this->normalizeItems($this->items, $hasActiveChild); - $options = $this->options; - $tag = ArrayHelper::remove($options, 'tag', 'ul'); - echo Html::tag($tag, $this->renderItems($items), $options); - } - - /** - * Recursively renders the menu items (without the container tag). - * @param array $items the menu items to be rendered recursively - * @return string the rendering result - */ - protected function renderItems($items) - { - $n = count($items); - $lines = []; - foreach ($items as $i => $item) { - $options = array_merge($this->itemOptions, ArrayHelper::getValue($item, 'options', [])); - $tag = ArrayHelper::remove($options, 'tag', 'li'); - $class = []; - if ($item['active']) { - $class[] = $this->activeCssClass; - } - if ($i === 0 && $this->firstItemCssClass !== null) { - $class[] = $this->firstItemCssClass; - } - if ($i === $n - 1 && $this->lastItemCssClass !== null) { - $class[] = $this->lastItemCssClass; - } - if (!empty($class)) { - if (empty($options['class'])) { - $options['class'] = implode(' ', $class); - } else { - $options['class'] .= ' ' . implode(' ', $class); - } - } - - $menu = $this->renderItem($item); - if (!empty($item['items'])) { - $menu .= strtr($this->submenuTemplate, [ - '{items}' => $this->renderItems($item['items']), - ]); - } - $lines[] = Html::tag($tag, $menu, $options); - } - return implode("\n", $lines); - } - - /** - * Renders the content of a menu item. - * Note that the container and the sub-menus are not rendered here. - * @param array $item the menu item to be rendered. Please refer to [[items]] to see what data might be in the item. - * @return string the rendering result - */ - protected function renderItem($item) - { - if (isset($item['url'])) { - $template = ArrayHelper::getValue($item, 'template', $this->linkTemplate); - return strtr($template, [ - '{url}' => Html::url($item['url']), - '{label}' => $item['label'], - ]); - } else { - $template = ArrayHelper::getValue($item, 'template', $this->labelTemplate); - return strtr($template, [ - '{label}' => $item['label'], - ]); - } - } - - /** - * Normalizes the [[items]] property to remove invisible items and activate certain items. - * @param array $items the items to be normalized. - * @param boolean $active whether there is an active child menu item. - * @return array the normalized menu items - */ - protected function normalizeItems($items, &$active) - { - foreach ($items as $i => $item) { - if (isset($item['visible']) && !$item['visible']) { - unset($items[$i]); - continue; - } - if (!isset($item['label'])) { - $item['label'] = ''; - } - if ($this->encodeLabels) { - $items[$i]['label'] = Html::encode($item['label']); - } - $hasActiveChild = false; - if (isset($item['items'])) { - $items[$i]['items'] = $this->normalizeItems($item['items'], $hasActiveChild); - if (empty($items[$i]['items']) && $this->hideEmptyItems) { - unset($items[$i]['items']); - if (!isset($item['url'])) { - unset($items[$i]); - continue; - } - } - } - if (!isset($item['active'])) { - if ($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item)) { - $active = $items[$i]['active'] = true; - } else { - $items[$i]['active'] = false; - } - } elseif ($item['active']) { - $active = true; - } - } - return array_values($items); - } - - /** - * Checks whether a menu item is active. - * This is done by checking if [[route]] and [[params]] match that specified in the `url` option of the menu item. - * When the `url` option of a menu item is specified in terms of an array, its first element is treated - * as the route for the item and the rest of the elements are the associated parameters. - * Only when its route and parameters match [[route]] and [[params]], respectively, will a menu item - * be considered active. - * @param array $item the menu item to be checked - * @return boolean whether the menu item is active - */ - protected function isItemActive($item) - { - if (isset($item['url']) && is_array($item['url']) && isset($item['url'][0])) { - $route = $item['url'][0]; - if ($route[0] !== '/' && Yii::$app->controller) { - $route = Yii::$app->controller->module->getUniqueId() . '/' . $route; - } - if (ltrim($route, '/') !== $this->route) { - return false; - } - unset($item['url']['#']); - if (count($item['url']) > 1) { - foreach (array_splice($item['url'], 1) as $name => $value) { - if (!isset($this->params[$name]) || $this->params[$name] != $value) { - return false; - } - } - } - return true; - } - return false; - } -} diff --git a/framework/yii/widgets/Spaceless.php b/framework/yii/widgets/Spaceless.php deleted file mode 100644 index 8115f85..0000000 --- a/framework/yii/widgets/Spaceless.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/** - * @link http://www.yiiframework.com/ - * @copyright Copyright (c) 2008 Yii Software LLC - * @license http://www.yiiframework.com/license/ - */ - -namespace yii\widgets; - -use yii\base\Widget; - -/** - * Spaceless widget removes whitespace characters between HTML tags. Whitespaces within HTML tags - * or in a plain text are always left untouched. - * - * Usage example: - * - * ```php - * <body> - * <?php Spaceless::begin(); ?> - * <div class="nav-bar"> - * <!-- tags --> - * </div> - * <div class="content"> - * <!-- tags --> - * </div> - * <?php Spaceless::end(); ?> - * </body> - * ``` - * - * This example will generate the following HTML: - * - * ```html - * <body> - * <div class="navbar"><!-- other tags --></div><div class="content"><!-- other tags --></div></body> - * ``` - * - * This method is not designed for content compression (you should use `gzip` output compression to - * achieve it). Main intention is to strip out extra whitespace characters between HTML tags in order - * to avoid browser rendering quirks in some circumstances (e.g. newlines between inline-block elements). - * - * Note, never use this method with `pre` or `textarea` tags. It's not that trivial to deal with such tags - * as it may seem at first sight. For this case you should consider using - * [HTML Tidy Project](http://tidy.sourceforge.net/) instead. - * - * @see http://tidy.sourceforge.net/ - * @author resurtm <resurtm@gmail.com> - * @since 2.0 - */ -class Spaceless extends Widget -{ - /** - * Starts capturing an output to be cleaned from whitespace characters between HTML tags. - */ - public function init() - { - ob_start(); - ob_implicit_flush(false); - } - - /** - * Marks the end of content to be cleaned from whitespace characters between HTML tags. - * Stops capturing an output and echoes cleaned result. - */ - public function run() - { - echo trim(preg_replace('/>\s+</', '><', ob_get_clean())); - } -}