Browse Source

Merge branch 'master' into i18n-icu

* master: (103 commits)
  fixed broken test after whitespace changes in view
  Removed the extra EOLs.
  I add new line in methods that render code in the head and for body
  GII update button style
  GII create object button style [skip ci]
  Fixes #980: Changed the default way of generating action URLs for ActionColumn.
  View default value for $params
  adjusted cubrid version in schema quote
  removed cubrid env from tavis.yml
  simplified cubrid db install on travis
  fixed cubrid schema test for pdo type
  fixed validator test break
  added cubrid specific pdo type casting
  Updated FileValidator tests
  Better AR connection init in tests
  Removed @codeCoverageIgnore
  no xss for attribute error messages that contain {value}
  Clientvalidation {value} was not what has been validated
  moved getPdoType() to Schema.
  optimized datepick js code.
  ...
tags/2.0.0-beta
Carsten Brandt 11 years ago
parent
commit
61fe888780
  1. 3
      .travis.yml
  2. 2
      README.md
  3. 2
      apps/basic/controllers/SiteController.php
  4. 4
      docs/guide/active-record.md
  5. 3
      docs/guide/caching.md
  6. 4
      docs/guide/configuration.md
  7. 4
      docs/guide/controller.md
  8. 15
      docs/guide/security.md
  9. 2
      docs/guide/validation.md
  10. 2
      extensions/composer/composer.json
  11. 208
      extensions/composer/yii/composer/Installer.php
  12. 30
      extensions/composer/yii/composer/InstallerPlugin.php
  13. 2
      extensions/jui/composer.json
  14. 13
      extensions/jui/yii/jui/DatePicker.php
  15. 2
      extensions/mutex/composer.json
  16. 4
      extensions/smarty/composer.json
  17. 2
      extensions/twig/composer.json
  18. 2
      extensions/twig/yii/twig/ViewRenderer.php
  19. 4
      framework/composer.json
  20. 2
      framework/yii/BaseYii.php
  21. 2
      framework/yii/assets/yii.activeForm.js
  22. 61
      framework/yii/assets/yii.gridView.js
  23. 37
      framework/yii/assets/yii.validation.js
  24. 8
      framework/yii/base/Application.php
  25. 11
      framework/yii/base/Exception.php
  26. 12
      framework/yii/base/Model.php
  27. 65
      framework/yii/base/View.php
  28. 2
      framework/yii/caching/RedisCache.php
  29. 27
      framework/yii/classes.php
  30. 2
      framework/yii/console/controllers/MigrateController.php
  31. 112
      framework/yii/data/ActiveDataProvider.php
  32. 81
      framework/yii/data/ArrayDataProvider.php
  33. 256
      framework/yii/data/BaseDataProvider.php
  34. 133
      framework/yii/data/DataProvider.php
  35. 12
      framework/yii/data/DataProviderInterface.php
  36. 15
      framework/yii/db/ActiveQuery.php
  37. 4
      framework/yii/db/ActiveRelation.php
  38. 39
      framework/yii/db/Command.php
  39. 38
      framework/yii/db/Query.php
  40. 20
      framework/yii/db/Schema.php
  41. 23
      framework/yii/db/cubrid/Schema.php
  42. 7
      framework/yii/debug/Module.php
  43. 4
      framework/yii/gii/components/ActiveField.php
  44. 5
      framework/yii/gii/generators/crud/templates/views/_search.php
  45. 7
      framework/yii/gii/generators/model/Generator.php
  46. 3
      framework/yii/gii/generators/module/Generator.php
  47. 3
      framework/yii/grid/ActionColumn.php
  48. 6
      framework/yii/grid/DataColumn.php
  49. 39
      framework/yii/grid/GridView.php
  50. 7
      framework/yii/helpers/BaseFileHelper.php
  51. 2
      framework/yii/helpers/BaseHtml.php
  52. 26
      framework/yii/helpers/BaseJson.php
  53. 84
      framework/yii/helpers/BaseSecurity.php
  54. 6
      framework/yii/i18n/PhpMessageSource.php
  55. 2
      framework/yii/log/FileTarget.php
  56. 2
      framework/yii/requirements/YiiRequirementChecker.php
  57. 1
      framework/yii/validators/BooleanValidator.php
  58. 3
      framework/yii/validators/CompareValidator.php
  59. 1
      framework/yii/validators/EmailValidator.php
  60. 4
      framework/yii/validators/NumberValidator.php
  61. 1
      framework/yii/validators/RangeValidator.php
  62. 1
      framework/yii/validators/RegularExpressionValidator.php
  63. 1
      framework/yii/validators/RequiredValidator.php
  64. 5
      framework/yii/validators/StringValidator.php
  65. 1
      framework/yii/validators/UrlValidator.php
  66. 6
      framework/yii/web/AccessControl.php
  67. 2
      framework/yii/web/Application.php
  68. 5
      framework/yii/web/AssetBundle.php
  69. 4
      framework/yii/web/AssetManager.php
  70. 13
      framework/yii/web/Controller.php
  71. 6
      framework/yii/web/HeaderCollection.php
  72. 3
      framework/yii/web/Request.php
  73. 27
      framework/yii/web/Response.php
  74. 1
      framework/yii/web/UrlRule.php
  75. 4
      framework/yii/web/User.php
  76. 36
      framework/yii/widgets/ActiveField.php
  77. 7
      framework/yii/widgets/BaseListView.php
  78. 2
      framework/yii/widgets/ListView.php
  79. 2
      tests/unit/data/ar/Customer.php
  80. 32
      tests/unit/data/mysql.sql
  81. 29
      tests/unit/data/postgres.sql
  82. 29
      tests/unit/data/sqlite.sql
  83. 31
      tests/unit/data/travis/cubrid-setup.sh
  84. 44
      tests/unit/data/validators/TestValidator.php
  85. 63
      tests/unit/data/validators/models/FakedValidationModel.php
  86. 21
      tests/unit/data/validators/models/ValidatorTestMainModel.php
  87. 23
      tests/unit/data/validators/models/ValidatorTestRefModel.php
  88. 22
      tests/unit/data/views/layout.php
  89. 5
      tests/unit/data/views/rawlayout.php
  90. 1
      tests/unit/data/views/simple.php
  91. 2
      tests/unit/data/web/assets/.gitignore
  92. 23
      tests/unit/framework/base/ExceptionTest.php
  93. 7
      tests/unit/framework/caching/CacheTestCase.php
  94. 3
      tests/unit/framework/data/ActiveDataProviderTest.php
  95. 23
      tests/unit/framework/db/CommandTest.php
  96. 2
      tests/unit/framework/db/DatabaseTestCase.php
  97. 23
      tests/unit/framework/db/SchemaTest.php
  98. 6
      tests/unit/framework/db/cubrid/CubridActiveRecordTest.php
  99. 23
      tests/unit/framework/db/cubrid/CubridSchemaTest.php
  100. 4
      tests/unit/framework/helpers/JsonTest.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.travis.yml

@ -5,9 +5,6 @@ php:
- 5.4
- 5.5
env:
- CUBRID_VERSION=9.1.0
services:
- redis-server
- memcached

2
README.md

@ -42,7 +42,7 @@ DOCUMENTATION
For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/upgrade-from-v1.md)
to have a general idea of what has changed in 2.0.
We are writing more documentation to get you started and learn more in depth.
[Definitive Guide draft](docs/guide/index.md) is available. It's not complete yet but main parts are already OK.
HOW TO PARTICIPATE

2
apps/basic/controllers/SiteController.php

@ -61,7 +61,7 @@ class SiteController extends Controller
{
$model = new LoginForm();
if ($model->load($_POST) && $model->login()) {
return $this->goHome();
return $this->goBack();
} else {
return $this->render('login', array(
'model' => $model,

4
docs/guide/active-record.md

@ -175,7 +175,9 @@ $customer->delete();
Customer::updateAllCounters(array('age' => 1));
```
Notice that you can always use the `save` method, and ActiveRecord will automatically perform an INSERT for new records and an UPDATE for existing ones.
> Info: The `save()` method will either perform an `INSERT` or `UPDATE` SQL statement, depending
on whether the ActiveRecord being saved is new or not by checking `ActiveRecord::isNewRecord`.
Data Input and Validation
-------------------------

3
docs/guide/caching.md

@ -60,7 +60,8 @@ is a summary of the available cache components:
the fastest one when dealing with cache in a distributed applications (e.g. with several servers, load
balancers, etc.)
* [[\yii\caching\RedisCache]]: implements a cache component based on [Redis](http://redis.io/) NoSQL database.
* [[\yii\caching\RedisCache]]: implements a cache component based on [Redis](http://redis.io/) key-value store
(redis version 2.6 or higher is required).
* [[\yii\caching\WinCache]]: uses PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
([see also](http://php.net/manual/en/book.wincache.php)) extension.

4
docs/guide/configuration.md

@ -1,8 +1,8 @@
Configuration
=============
In Yii application and majority of components have sensible defaults so it's unlikely you spend lots of time configuring
it. Still there are some mandatory options such as database connection you should set up.
Yii applications rely upon components to perform most of the common tasks, such as connecting to a database, routing browser requests, and handling sessions. How these stock components behave can be adjusted by *configuring* your Yii application. The majority of components have sensible defaults, so it's unlikely that you'll spend a lot of time configuring
them. Still there are some mandatory settings, such as the database connection, that you will have to establish.
How application is configured depends on application template but there are some general principles applying in any case.

4
docs/guide/controller.md

@ -35,6 +35,9 @@ class SiteController extends Controller
```
As you can see, typical controller contains actions that are public class methods named as `actionSomething`.
The output of an action is what the method returns. The return value will be handled by the `response` application
component which can convert the output to differnet formats such as JSON for example. The default behavior
is to output the value unchanged though.
Routes
------
@ -183,7 +186,6 @@ Filters
Catching all incoming requests
------------------------------
See also
--------

15
docs/guide/security.md

@ -6,24 +6,23 @@ Hashing and verifying passwords
Most developers know that you cannot store passwords in plain text, but many believe it's safe to hash passwords using `md5` or `sha1`. There was a time when those hashing algorithms were sufficient, but modern hardware makes it possible to break those hashes very quickly using a brute force attack.
In order to truly secure user passwords, even in the worst case scenario (your database is broken into), you need to use a hashing algorithm that is resistant to brute force attacks. The best current choice is bcrypt. In PHP, you can create a bcrypt hash by using [crypt function](http://php.net/manual/en/function.crypt.php). However, this function is not easy to use properly, so Yii provides two helper functions for generating hash from
password and verifying existing hash.
In order to truly secure user passwords, even in the worst case scenario (your database is broken into), you need to use a hashing algorithm that is resistant to brute force attacks. The best current choice is `bcrypt`. In PHP, you can create a `bcrypt` hash by using the [crypt function](http://php.net/manual/en/function.crypt.php). However, this function is not easy to use properly, so Yii provides two helper functions to make securely generating and verifying hashes easier.
When user sets his password we're taking password string from POST and then getting a hash:
When a user provides a password for the first time (e.g., upon registration), the password needs to be hashed:
```php
$hash = \yii\helpers\Security::generatePasswordHash($password);
```
The hash we've got is persisted to database to be used later.
The hash would then be associated with the model, so that it will be stored in the database for later use.
Then when user is trying to log in we're verifying the password he entered against a hash that we've previously persisted:
When user attempts to log in, the submitted log in password must be verified against the previously hashed and stored password:
```php
if(Security::validatePassword($password, $hash)) {
use \yii\helpers;
if (Security::validatePassword($password, $hash)) {
// all good, logging user in
}
else {
} else {
// wrong password
}
```

2
docs/guide/validation.md

@ -38,7 +38,7 @@ Compares the specified attribute value with another value and validates if they
Verifies if the attribute represents a date, time or datetime in a proper format.
- `format` the date format that the value being validated should follow accodring to [[http://www.php.net/manual/en/datetime.createfromformat.php]]. _('Y-m-d')_
- `format` the date format that the value being validated should follow according to [[http://www.php.net/manual/en/datetime.createfromformat.php]]. _('Y-m-d')_
- `timestampAttribute` the name of the attribute to receive the parsing result.
### `default`: [[DefaultValueValidator]]

2
extensions/composer/composer.json

@ -22,6 +22,6 @@
"yiisoft/yii2": "*"
},
"autoload": {
"psr-0": { "yii\\composer": "" }
"psr-0": { "yii\\composer\\": "" }
}
}

208
extensions/composer/yii/composer/Installer.php

@ -0,0 +1,208 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\composer;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;
use Composer\Repository\InstalledRepositoryInterface;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Installer extends LibraryInstaller
{
const EXTRA_WRITABLES = 'yii-writables';
const EXTRA_EXECUTABLES = 'yii-executables';
const EXTRA_CONFIG = 'yii-config';
const EXTRA_COMMANDS = 'yii-commands';
const EXTRA_ALIASES = 'yii-aliases';
const EXTRA_PREINIT = 'yii-preinit';
const EXTRA_INIT = 'yii-init';
/**
* @inheritdoc
*/
public function supports($packageType)
{
return $packageType === 'yii2-extension';
}
/**
* @inheritdoc
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{
parent::install($repo, $package);
$this->addPackage($package);
}
/**
* @inheritdoc
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{
parent::update($repo, $initial, $target);
$this->removePackage($initial);
$this->addPackage($target);
}
/**
* @inheritdoc
*/
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{
parent::uninstall($repo, $package);
$this->removePackage($package);
}
protected function addPackage(PackageInterface $package)
{
$extension = array('name' => $package->getPrettyName());
$root = $package->getPrettyName();
if ($targetDir = $package->getTargetDir()) {
$root .= '/' . trim($targetDir, '/');
}
$root = trim($root, '/');
$extra = $package->getExtra();
if (isset($extra[self::EXTRA_PREINIT]) && is_string($extra[self::EXTRA_PREINIT])) {
$extension[self::EXTRA_PREINIT] = "<vendor-dir>/$root/" . ltrim(str_replace('\\', '/', $extra[self::EXTRA_PREINIT]), '/');
}
if (isset($extra[self::EXTRA_INIT]) && is_string($extra[self::EXTRA_INIT])) {
$extension[self::EXTRA_INIT] = "<vendor-dir>/$root/" . ltrim(str_replace('\\', '/', $extra[self::EXTRA_INIT]), '/');
}
if (isset($extra['aliases']) && is_array($extra['aliases'])) {
foreach ($extra['aliases'] as $alias => $path) {
$extension['aliases']['@' . ltrim($alias, '@')] = "<vendor-dir>/$root/" . ltrim(str_replace('\\', '/', $path), '/');
}
}
if (!empty($aliases)) {
foreach ($aliases as $alias => $path) {
if (strncmp($alias, '@', 1) !== 0) {
$alias = '@' . $alias;
}
$path = trim(str_replace('\\', '/', $path), '/');
$extension['aliases'][$alias] = $root . '/' . $path;
}
}
$extensions = $this->loadExtensions();
$extensions[$package->getId()] = $extension;
$this->saveExtensions($extensions);
}
protected function removePackage(PackageInterface $package)
{
$packages = $this->loadExtensions();
unset($packages[$package->getId()]);
$this->saveExtensions($packages);
}
protected function loadExtensions()
{
$file = $this->vendorDir . '/yii-extensions.php';
if (!is_file($file)) {
return array();
}
$extensions = require($file);
/** @var string $vendorDir defined in yii-extensions.php */
$n = strlen($vendorDir);
foreach ($extensions as &$extension) {
if (isset($extension['aliases'])) {
foreach ($extension['aliases'] as $alias => $path) {
$extension['aliases'][$alias] = '<vendor-dir>' . substr($path, $n);
}
}
if (isset($extension[self::EXTRA_PREINIT])) {
$extension[self::EXTRA_PREINIT] = '<vendor-dir>' . substr($extension[self::EXTRA_PREINIT], $n);
}
if (isset($extension[self::EXTRA_INIT])) {
$extension[self::EXTRA_INIT] = '<vendor-dir>' . substr($extension[self::EXTRA_INIT], $n);
}
}
return $extensions;
}
protected function saveExtensions(array $extensions)
{
$file = $this->vendorDir . '/yii-extensions.php';
$array = str_replace("'<vendor-dir>", '$vendorDir . \'', var_export($extensions, true));
file_put_contents($file, "<?php\n\$vendorDir = __DIR__;\n\nreturn $array;\n");
}
/**
* Sets the correct permissions of files and directories.
* @param CommandEvent $event
*/
public static function setPermissions($event)
{
$options = array_merge(array(
self::EXTRA_WRITABLES => array(),
self::EXTRA_EXECUTABLES => array(),
), $event->getComposer()->getPackage()->getExtra());
foreach ((array)$options[self::EXTRA_WRITABLES] as $path) {
echo "Setting writable: $path ...";
if (is_dir($path)) {
chmod($path, 0777);
echo "done\n";
} else {
echo "The directory was not found: " . getcwd() . DIRECTORY_SEPARATOR . $path;
return;
}
}
foreach ((array)$options[self::EXTRA_EXECUTABLES] as $path) {
echo "Setting executable: $path ...";
if (is_file($path)) {
chmod($path, 0755);
echo "done\n";
} else {
echo "\n\tThe file was not found: " . getcwd() . DIRECTORY_SEPARATOR . $path . "\n";
return;
}
}
}
/**
* Executes a yii command.
* @param CommandEvent $event
*/
public static function run($event)
{
$options = array_merge(array(
self::EXTRA_COMMANDS => array(),
), $event->getComposer()->getPackage()->getExtra());
if (!isset($options[self::EXTRA_CONFIG])) {
throw new Exception('Please specify the "' . self::EXTRA_CONFIG . '" parameter in composer.json.');
}
$configFile = getcwd() . '/' . $options[self::EXTRA_CONFIG];
if (!is_file($configFile)) {
throw new Exception("Config file does not exist: $configFile");
}
require_once(__DIR__ . '/../../../yii2/yii/Yii.php');
$application = new Application(require($configFile));
$request = $application->getRequest();
foreach ((array)$options[self::EXTRA_COMMANDS] as $command) {
$params = str_getcsv($command, ' '); // see http://stackoverflow.com/a/6609509/291573
$request->setParams($params);
list($route, $params) = $request->resolve();
echo "Running command: yii {$command}\n";
$application->runAction($route, $params);
}
}
}

30
extensions/composer/yii/composer/InstallerPlugin.php

@ -0,0 +1,30 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\composer;
use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
/**
* InstallerPlugin is the composer plugin that registers the Yii composer installer.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InstallerPlugin implements PluginInterface
{
/**
* @inheritdoc
*/
public function activate(Composer $composer, IOInterface $io)
{
$installer = new Installer($io, $composer);
$composer->getInstallationManager()->addInstaller($installer);
}
}

2
extensions/jui/composer.json

@ -16,6 +16,6 @@
"yiisoft/yii2": "*"
},
"autoload": {
"psr-0": { "yii\\jui": "" }
"psr-0": { "yii\\jui\\": "" }
}
}

13
extensions/jui/yii/jui/DatePicker.php

@ -9,6 +9,7 @@ namespace yii\jui;
use Yii;
use yii\helpers\Html;
use yii\helpers\Json;
/**
* DatePicker renders an datepicker jQuery UI widget.
@ -61,11 +62,19 @@ class DatePicker extends InputWidget
public function run()
{
echo $this->renderWidget() . "\n";
$this->registerWidget('datepicker', DatePickerAsset::className());
if ($this->language !== false) {
$view = $this->getView();
DatePickerRegionalAsset::register($view);
$view->registerJs("$('#{$this->options['id']}').datepicker('option', $.datepicker.regional['{$this->language}']);");
$options = Json::encode($this->clientOptions);
$view->registerJs("$('#{$this->options['id']}').datepicker($.extend({}, $.datepicker.regional['{$this->language}'], $options));");
$options = $this->clientOptions;
$this->clientOptions = false; // the datepicker js widget is already registered
$this->registerWidget('datepicker', DatePickerAsset::className());
$this->clientOptions = $options;
} else {
$this->registerWidget('datepicker', DatePickerAsset::className());
}
}

2
extensions/mutex/composer.json

@ -22,6 +22,6 @@
"yiisoft/yii2": "*"
},
"autoload": {
"psr-0": { "yii\\mutex": "" }
"psr-0": { "yii\\mutex\\": "" }
}
}

4
extensions/smarty/composer.json

@ -20,9 +20,9 @@
"minimum-stability": "dev",
"require": {
"yiisoft/yii2": "*",
"smarty/smarty": "v3.1.13"
"smarty/smarty": ">=v3.1.13"
},
"autoload": {
"psr-0": { "yii\\smarty": "" }
"psr-0": { "yii\\smarty\\": "" }
}
}

2
extensions/twig/composer.json

@ -23,6 +23,6 @@
"twig/twig": "1.13.*"
},
"autoload": {
"psr-0": { "yii\\twig": "" }
"psr-0": { "yii\\twig\\": "" }
}
}

2
extensions/twig/yii/twig/ViewRenderer.php

@ -7,7 +7,7 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\renderers;
namespace yii\twig;
use Yii;
use yii\base\View;

4
framework/composer.json

@ -74,8 +74,6 @@
"psr-0": { "yii\\": "/" }
},
"suggest": {
"michelf/php-markdown": "Required by Markdown.",
"twig/twig": "Required by TwigViewRenderer.",
"smarty/smarty": "Required by SmartyViewRenderer."
"michelf/php-markdown": "Required by Markdown."
}
}

2
framework/yii/BaseYii.php

@ -541,7 +541,7 @@ class BaseYii
public static function t($category, $message, $params = array(), $language = null)
{
if (self::$app !== null) {
return self::$app->getI18N()->translate($category, $message, $params, $language ?: self::$app->language);
return self::$app->getI18n()->translate($category, $message, $params, $language ?: self::$app->language);
} else {
return is_array($params) ? strtr($message, $params) : $message;
}

2
framework/yii/assets/yii.activeForm.js

@ -345,7 +345,7 @@
var $container = $form.find(attribute.container);
var $error = $container.find(attribute.error);
if (hasError) {
$error.html(messages[attribute.name][0]);
$error.text(messages[attribute.name][0]);
$container.removeClass(data.settings.validatingCssClass + ' ' + data.settings.successCssClass)
.addClass(data.settings.errorCssClass);
} else {

61
framework/yii/assets/yii.gridView.js

@ -22,6 +22,8 @@
};
var defaults = {
filterUrl: undefined,
filterSelector: undefined
};
var methods = {
@ -32,6 +34,32 @@
$e.data('yiiGridView', {
settings: settings
});
var enterPressed = false;
$(document).on('change.yiiGridView keydown.yiiGridView', settings.filterSelector, function (event) {
if (event.type === 'keydown') {
if (event.keyCode !== 13) {
return; // only react to enter key
} else {
enterPressed = true;
}
} else {
// prevent processing for both keydown and change events
if (enterPressed) {
enterPressed = false;
return;
}
}
var data = $(settings.filterSelector).serialize();
var url = settings.filterUrl;
if (url.indexOf('?') >= 0) {
url += '&' + data;
} else {
url += '?' + data;
}
window.location.href = url;
return false;
});
});
},
@ -74,5 +102,38 @@
return this.data('yiiGridView');
}
};
var enterPressed = false;
var filterChanged = function (event) {
if (event.type === 'keydown') {
if (event.keyCode !== 13) {
return; // only react to enter key
} else {
enterPressed = true;
}
} else {
// prevent processing for both keydown and change events
if (enterPressed) {
enterPressed = false;
return;
}
}
var data = $(settings.filterSelector).serialize();
if (settings.pageVar !== undefined) {
data += '&' + settings.pageVar + '=1';
}
if (settings.enableHistory && settings.ajaxUpdate !== false && window.History.enabled) {
// Ajaxify this link
var url = $('#' + id).yiiGridView('getUrl'),
params = $.deparam.querystring($.param.querystring(url, data));
delete params[settings.ajaxVar];
window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url.substr(0, url.indexOf('?')), params)));
} else {
$('#' + id).yiiGridView('update', {data: data});
}
return false;
};
})(window.jQuery);

37
framework/yii/assets/yii.validation.js

@ -16,6 +16,10 @@ yii.validation = (function ($) {
|| value === '' || trim && $.trim(value) === '';
};
var addMessage = function (messages, message, value) {
messages.push(message.replace(/\{value\}/g, value));
};
return {
required: function (value, messages, options) {
var valid = false;
@ -28,7 +32,7 @@ yii.validation = (function ($) {
}
if (!valid) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -40,7 +44,7 @@ yii.validation = (function ($) {
|| options.strict && (value === options.trueValue || value === options.falseValue);
if (!valid) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -50,18 +54,18 @@ yii.validation = (function ($) {
}
if (typeof value !== 'string') {
messages.push(options.message);
addMessage(messages, options.message, value);
return;
}
if (options.min !== undefined && value.length < options.min) {
messages.push(options.tooShort);
addMessage(messages, options.tooShort, value);
}
if (options.max !== undefined && value.length > options.max) {
messages.push(options.tooLong);
addMessage(messages, options.tooLong, value);
}
if (options.is !== undefined && value.length != options.is) {
messages.push(options.is);
addMessage(messages, options.is, value);
}
},
@ -71,15 +75,15 @@ yii.validation = (function ($) {
}
if (typeof value === 'string' && !value.match(options.pattern)) {
messages.push(options.message);
addMessage(messages, options.message, value);
return;
}
if (options.min !== undefined && value < options.min) {
messages.push(options.tooSmall);
addMessage(messages, options.tooSmall, value);
}
if (options.max !== undefined && value > options.max) {
messages.push(options.tooBig);
addMessage(messages, options.tooBig, value);
}
},
@ -91,7 +95,7 @@ yii.validation = (function ($) {
|| options.not && $.inArray(value, options.range) == -1;
if (!valid) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -101,7 +105,7 @@ yii.validation = (function ($) {
}
if (!options.not && !value.match(options.pattern) || options.not && value.match(options.pattern)) {
messages.push(options.message)
addMessage(messages, options.message, value);
}
},
@ -123,7 +127,7 @@ yii.validation = (function ($) {
}
if (!valid || !(value.match(options.pattern) && (!options.allowName || value.match(options.fullPattern)))) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -149,7 +153,7 @@ yii.validation = (function ($) {
}
if (!valid || !value.match(options.pattern)) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -170,7 +174,7 @@ yii.validation = (function ($) {
h += v.charCodeAt(i);
}
if (h != hash) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
},
@ -210,10 +214,13 @@ yii.validation = (function ($) {
case '<=':
valid = value <= compareValue;
break;
default:
valid = false;
break;
}
if (!valid) {
messages.push(options.message);
addMessage(messages, options.message, value);
}
}
};

8
framework/yii/base/Application.php

@ -383,7 +383,7 @@ abstract class Application extends Module
* Returns the internationalization (i18n) component
* @return \yii\i18n\I18N the internationalization component
*/
public function getI18N()
public function getI18n()
{
return $this->getComponent('i18n');
}
@ -450,7 +450,11 @@ abstract class Application extends Module
$msg .= "\nPrevious exception:\n";
$msg .= (string)$exception;
if (YII_DEBUG) {
echo $msg;
if (PHP_SAPI === 'cli') {
echo $msg . "\n";
} else {
echo '<pre>' . htmlspecialchars($msg, ENT_QUOTES, $this->charset) . '</pre>';
}
}
$msg .= "\n\$_SERVER = " . var_export($_SERVER, true);
error_log($msg);

11
framework/yii/base/Exception.php

@ -39,21 +39,12 @@ class Exception extends \Exception implements Arrayable
*/
protected function toArrayRecursive($exception)
{
if ($exception instanceof self) {
$array = array(
'type' => get_class($this),
'name' => $this->getName(),
'message' => $this->getMessage(),
'code' => $this->getCode(),
);
} else {
$array = array(
'type' => get_class($exception),
'name' => 'Exception',
'name' => $exception instanceof self ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
);
}
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->toArrayRecursive($prev);
}

12
framework/yii/base/Model.php

@ -422,6 +422,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* Returns a value indicating whether the attribute is safe for massive assignments.
* @param string $attribute attribute name
* @return boolean whether the attribute is safe for massive assignments
* @see safeAttributes()
*/
public function isAttributeSafe($attribute)
{
@ -429,6 +430,17 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
}
/**
* Returns a value indicating whether the attribute is active in the current scenario.
* @param string $attribute attribute name
* @return boolean whether the attribute is active in the current scenario
* @see activeAttributes()
*/
public function isAttributeActive($attribute)
{
return in_array($attribute, $this->activeAttributes(), true);
}
/**
* Returns the text label for the specified attribute.
* @param string $attribute the attribute name
* @return string the attribute label

65
framework/yii/base/View.php

@ -96,7 +96,7 @@ class View extends Component
/**
* @var mixed custom parameters that are shared among view templates.
*/
public $params;
public $params = array();
/**
* @var array a list of available renderers indexed by their corresponding supported file extensions.
* Each renderer may be a view renderer object or the configuration for creating the renderer object.
@ -142,11 +142,11 @@ class View extends Component
*/
public $dynamicPlaceholders = array();
/**
* @var array list of the registered asset bundles. The keys are the bundle names, and the values
* are booleans indicating whether the bundles have been registered.
* @var AssetBundle[] list of the registered asset bundles. The keys are the bundle names, and the values
* are the registered [[AssetBundle]] objects.
* @see registerAssetBundle
*/
public $assetBundles;
public $assetBundles = array();
/**
* @var string the page title
*/
@ -523,6 +523,9 @@ class View extends Component
$this->trigger(self::EVENT_END_PAGE);
$content = ob_get_clean();
foreach(array_keys($this->assetBundles) as $bundle) {
$this->registerAssetFiles($bundle);
}
echo strtr($content, array(
self::PH_HEAD => $this->renderHeadHtml(),
self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),
@ -530,7 +533,6 @@ class View extends Component
));
unset(
$this->assetBundles,
$this->metaTags,
$this->linkTags,
$this->css,
@ -541,6 +543,24 @@ class View extends Component
}
/**
* Registers all files provided by an asset bundle including depending bundles files.
* Removes a bundle from [[assetBundles]] once registered.
* @param string $name name of the bundle to register
*/
private function registerAssetFiles($name)
{
if (!isset($this->assetBundles[$name])) {
return;
}
$bundle = $this->assetBundles[$name];
foreach($bundle->depends as $dep) {
$this->registerAssetFiles($dep);
}
$bundle->registerAssets($this);
unset($this->assetBundles[$name]);
}
/**
* Marks the beginning of an HTML body section.
*/
public function beginBody()
@ -570,21 +590,44 @@ class View extends Component
* Registers the named asset bundle.
* All dependent asset bundles will be registered.
* @param string $name the name of the asset bundle.
* @param integer|null $position if set, this forces a minimum position for javascript files.
* This will adjust depending assets javascript file position or fail if requirement can not be met.
* If this is null, asset bundles position settings will not be changed.
* See [[registerJsFile]] for more details on javascript position.
* @return AssetBundle the registered asset bundle instance
* @throws InvalidConfigException if the asset bundle does not exist or a circular dependency is detected
*/
public function registerAssetBundle($name)
public function registerAssetBundle($name, $position = null)
{
if (!isset($this->assetBundles[$name])) {
$am = $this->getAssetManager();
$bundle = $am->getBundle($name);
$this->assetBundles[$name] = false;
$bundle->registerAssets($this);
// register dependencies
$pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
foreach ($bundle->depends as $dep) {
$this->registerAssetBundle($dep, $pos);
}
$this->assetBundles[$name] = $bundle;
} elseif ($this->assetBundles[$name] === false) {
throw new InvalidConfigException("A circular dependency is detected for bundle '$name'.");
} else {
$bundle = $this->assetBundles[$name];
}
if ($position !== null) {
$pos = isset($bundle->jsOptions['position']) ? $bundle->jsOptions['position'] : null;
if ($pos === null) {
$bundle->jsOptions['position'] = $pos = $position;
} elseif ($pos > $position) {
throw new InvalidConfigException("An asset bundle that depends on '$name' has a higher javascript file position configured than '$name'.");
}
// update position for all dependencies
foreach ($bundle->depends as $dep) {
$this->registerAssetBundle($dep, $pos);
}
}
return $this->assetBundles[$name];
return $bundle;
}
/**
@ -730,7 +773,7 @@ class View extends Component
if (!empty($this->js[self::POS_HEAD])) {
$lines[] = Html::script(implode("\n", $this->js[self::POS_HEAD]), array('type' => 'text/javascript'));
}
return empty($lines) ? '' : implode("\n", $lines) . "\n";
return empty($lines) ? '' : implode("\n", $lines);
}
/**
@ -747,7 +790,7 @@ class View extends Component
if (!empty($this->js[self::POS_BEGIN])) {
$lines[] = Html::script(implode("\n", $this->js[self::POS_BEGIN]), array('type' => 'text/javascript'));
}
return empty($lines) ? '' : implode("\n", $lines) . "\n";
return empty($lines) ? '' : implode("\n", $lines);
}
/**
@ -768,6 +811,6 @@ class View extends Component
$js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
$lines[] = Html::script($js, array('type' => 'text/javascript'));
}
return empty($lines) ? '' : implode("\n", $lines) . "\n";
return empty($lines) ? '' : implode("\n", $lines);
}
}

2
framework/yii/caching/RedisCache.php

@ -10,7 +10,7 @@ namespace yii\caching;
use yii\redis\Connection;
/**
* RedisCache implements a cache application component based on [redis](http://redis.io/).
* RedisCache implements a cache application component based on [redis](http://redis.io/) version 2.6 or higher.
*
* RedisCache needs to be configured with [[hostname]], [[port]] and [[database]] of the server
* to connect to. By default RedisCache assumes there is a redis server running on localhost at

27
framework/yii/classes.php

@ -85,7 +85,7 @@ return array(
'yii\captcha\CaptchaValidator' => YII_PATH . '/captcha/CaptchaValidator.php',
'yii\data\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
'yii\data\DataProvider' => YII_PATH . '/data/DataProvider.php',
'yii\data\BaseDataProvider' => YII_PATH . '/data/BaseDataProvider.php',
'yii\data\DataProviderInterface' => YII_PATH . '/data/DataProviderInterface.php',
'yii\data\Pagination' => YII_PATH . '/data/Pagination.php',
'yii\data\Sort' => YII_PATH . '/data/Sort.php',
@ -118,6 +118,7 @@ return array(
'yii\db\pgsql\Schema' => YII_PATH . '/db/pgsql/Schema.php',
'yii\db\sqlite\QueryBuilder' => YII_PATH . '/db/sqlite/QueryBuilder.php',
'yii\db\sqlite\Schema' => YII_PATH . '/db/sqlite/Schema.php',
'yii\grid\ActionColumn' => YII_PATH . '/grid/ActionColumn.php',
'yii\grid\CheckboxColumn' => YII_PATH . '/grid/CheckboxColumn.php',
'yii\grid\Column' => YII_PATH . '/grid/Column.php',
'yii\grid\DataColumn' => YII_PATH . '/grid/DataColumn.php',
@ -126,26 +127,26 @@ return array(
'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php',
'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php',
'yii\helpers\BaseArrayHelper' => YII_PATH . '/helpers/BaseArrayHelper.php',
'yii\helpers\Console' => YII_PATH . '/helpers/Console.php',
'yii\helpers\BaseConsole' => YII_PATH . '/helpers/BaseConsole.php',
'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php',
'yii\helpers\BaseFileHelper' => YII_PATH . '/helpers/BaseFileHelper.php',
'yii\helpers\Html' => YII_PATH . '/helpers/Html.php',
'yii\helpers\BaseHtml' => YII_PATH . '/helpers/BaseHtml.php',
'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php',
'yii\helpers\BaseHtmlPurifier' => YII_PATH . '/helpers/BaseHtmlPurifier.php',
'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php',
'yii\helpers\BaseInflector' => YII_PATH . '/helpers/BaseInflector.php',
'yii\helpers\Json' => YII_PATH . '/helpers/Json.php',
'yii\helpers\BaseJson' => YII_PATH . '/helpers/BaseJson.php',
'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php',
'yii\helpers\BaseMarkdown' => YII_PATH . '/helpers/BaseMarkdown.php',
'yii\helpers\Security' => YII_PATH . '/helpers/Security.php',
'yii\helpers\BaseSecurity' => YII_PATH . '/helpers/BaseSecurity.php',
'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php',
'yii\helpers\BaseStringHelper' => YII_PATH . '/helpers/BaseStringHelper.php',
'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php',
'yii\helpers\BaseVarDumper' => YII_PATH . '/helpers/BaseVarDumper.php',
'yii\helpers\Console' => YII_PATH . '/helpers/Console.php',
'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php',
'yii\helpers\Html' => YII_PATH . '/helpers/Html.php',
'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php',
'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php',
'yii\helpers\Json' => YII_PATH . '/helpers/Json.php',
'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php',
'yii\helpers\Security' => YII_PATH . '/helpers/Security.php',
'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php',
'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php',
'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php',
'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php',
'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php',
@ -194,6 +195,7 @@ return array(
'yii\web\Application' => YII_PATH . '/web/Application.php',
'yii\web\AssetBundle' => YII_PATH . '/web/AssetBundle.php',
'yii\web\AssetConverter' => YII_PATH . '/web/AssetConverter.php',
'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
'yii\web\AssetManager' => YII_PATH . '/web/AssetManager.php',
'yii\web\CacheSession' => YII_PATH . '/web/CacheSession.php',
'yii\web\Controller' => YII_PATH . '/web/Controller.php',
@ -204,7 +206,6 @@ return array(
'yii\web\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
@ -226,6 +227,7 @@ return array(
'yii\widgets\ActiveField' => YII_PATH . '/widgets/ActiveField.php',
'yii\widgets\ActiveForm' => YII_PATH . '/widgets/ActiveForm.php',
'yii\widgets\ActiveFormAsset' => YII_PATH . '/widgets/ActiveFormAsset.php',
'yii\widgets\BaseListView' => YII_PATH . '/widgets/BaseListView.php',
'yii\widgets\Block' => YII_PATH . '/widgets/Block.php',
'yii\widgets\Breadcrumbs' => YII_PATH . '/widgets/Breadcrumbs.php',
'yii\widgets\ContentDecorator' => YII_PATH . '/widgets/ContentDecorator.php',
@ -235,7 +237,6 @@ return array(
'yii\widgets\LinkPager' => YII_PATH . '/widgets/LinkPager.php',
'yii\widgets\LinkSorter' => YII_PATH . '/widgets/LinkSorter.php',
'yii\widgets\ListView' => YII_PATH . '/widgets/ListView.php',
'yii\widgets\ListViewBase' => YII_PATH . '/widgets/ListViewBase.php',
'yii\widgets\MaskedInput' => YII_PATH . '/widgets/MaskedInput.php',
'yii\widgets\MaskedInputAsset' => YII_PATH . '/widgets/MaskedInputAsset.php',
'yii\widgets\Menu' => YII_PATH . '/widgets/Menu.php',

2
framework/yii/console/controllers/MigrateController.php

@ -584,7 +584,7 @@ class MigrateController extends Controller
->from($this->migrationTable)
->orderBy('version DESC')
->limit($limit)
->createCommand()
->createCommand($this->db)
->queryAll();
$history = ArrayHelper::map($rows, 'version', 'apply_time');
unset($history[self::BASE_MIGRATION]);

112
framework/yii/data/ActiveDataProvider.php

@ -48,16 +48,10 @@ use yii\db\Connection;
* $posts = $provider->getModels();
* ~~~
*
* @property integer $count The number of data models in the current page. This property is read-only.
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
* uniquely identified by the corresponding key value in this array. This property is read-only.
* @property array $models The list of data models in the current page. This property is read-only.
* @property integer $totalCount Total number of possible data models.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActiveDataProvider extends DataProvider
class ActiveDataProvider extends BaseDataProvider
{
/**
* @var Query the query that is used to fetch data models and [[totalCount]]
@ -82,10 +76,6 @@ class ActiveDataProvider extends DataProvider
*/
public $db;
private $_models;
private $_keys;
private $_totalCount;
/**
* Initializes the DbCache component.
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
@ -103,87 +93,38 @@ class ActiveDataProvider extends DataProvider
}
/**
* Returns the number of data models in the current page.
* This is equivalent to `count($provider->models)`.
* When [[pagination]] is false, this is the same as [[totalCount]].
* @return integer the number of data models in the current page.
*/
public function getCount()
{
return count($this->getModels());
}
/**
* Returns the total number of data models.
* When [[pagination]] is false, this returns the same value as [[count]].
* If [[totalCount]] is not explicitly set, it will be calculated
* using [[query]] with a COUNT query.
* @return integer total number of possible data models.
* @throws InvalidConfigException
*/
public function getTotalCount()
{
if ($this->getPagination() === false) {
return $this->getCount();
} elseif ($this->_totalCount === null) {
if (!$this->query instanceof Query) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
}
$query = clone $this->query;
$this->_totalCount = $query->limit(-1)->offset(-1)->count('*', $this->db);
}
return $this->_totalCount;
}
/**
* Sets the total number of data models.
* @param integer $value the total number of data models.
*/
public function setTotalCount($value)
{
$this->_totalCount = $value;
}
/**
* Returns the data models in the current page.
* @return array the list of data models in the current page.
* @throws InvalidConfigException if [[query]] is not set or invalid.
* @inheritdoc
*/
public function getModels()
protected function prepareModels()
{
if ($this->_models === null) {
if (!$this->query instanceof Query) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
}
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$this->query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
if (($sort = $this->getSort()) !== false) {
$this->query->addOrderBy($sort->getOrders());
}
$this->_models = $this->query->all($this->db);
}
return $this->_models;
return $this->query->all($this->db);
}
/**
* Returns the key values associated with the data models.
* @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
* is uniquely identified by the corresponding key value in this array.
* @inheritdoc
*/
public function getKeys()
protected function prepareKeys($models)
{
if ($this->_keys === null) {
$this->_keys = array();
$models = $this->getModels();
$keys = array();
if ($this->key !== null) {
foreach ($models as $model) {
if (is_string($this->key)) {
$this->_keys[] = $model[$this->key];
$keys[] = $model[$this->key];
} else {
$this->_keys[] = call_user_func($this->key, $model);
$keys[] = call_user_func($this->key, $model);
}
}
return $keys;
} elseif ($this->query instanceof ActiveQuery) {
/** @var \yii\db\ActiveRecord $class */
$class = $this->query->modelClass;
@ -191,34 +132,33 @@ class ActiveDataProvider extends DataProvider
if (count($pks) === 1) {
$pk = $pks[0];
foreach ($models as $model) {
$this->_keys[] = $model[$pk];
$keys[] = $model[$pk];
}
} else {
foreach ($models as $model) {
$keys = array();
$kk = array();
foreach ($pks as $pk) {
$keys[] = $model[$pk];
$kk[] = $model[$pk];
}
$this->_keys[] = json_encode($keys);
$keys[] = json_encode($kk);
}
}
return $keys;
} else {
$this->_keys = array_keys($models);
}
return array_keys($models);
}
return $this->_keys;
}
/**
* Refreshes the data provider.
* After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,
* they will re-execute the query and return the latest data available.
* @inheritdoc
*/
public function refresh()
protected function prepareTotalCount()
{
$this->_models = null;
$this->_totalCount = null;
$this->_keys = null;
if (!$this->query instanceof Query) {
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
}
$query = clone $this->query;
return $query->limit(-1)->offset(-1)->count('*', $this->db);
}
/**
@ -227,9 +167,7 @@ class ActiveDataProvider extends DataProvider
public function setSort($value)
{
parent::setSort($value);
if (($sort = $this->getSort()) !== false && empty($sort->attributes) &&
$this->query instanceof ActiveQuery) {
if (($sort = $this->getSort()) !== false && empty($sort->attributes) && $this->query instanceof ActiveQuery) {
/** @var Model $model */
$model = new $this->query->modelClass;
foreach($model->attributes() as $attribute) {

81
framework/yii/data/ArrayDataProvider.php

@ -47,15 +47,10 @@ use yii\helpers\ArrayHelper;
* Note: if you want to use the sorting feature, you must configure the [[sort]] property
* so that the provider knows which columns can be sorted.
*
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
* uniquely identified by the corresponding key value in this array.
* @property array $models The list of data models in the current page.
* @property integer $totalCount Total number of possible data models.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ArrayDataProvider extends DataProvider
class ArrayDataProvider extends BaseDataProvider
{
/**
* @var string|callable the column that is used as the key of the data models.
@ -71,40 +66,12 @@ class ArrayDataProvider extends DataProvider
*/
public $allModels;
private $_totalCount;
/**
* Returns the total number of data models.
* @return integer total number of possible data models.
*/
public function getTotalCount()
{
if ($this->getPagination() === false) {
return $this->getCount();
} elseif ($this->_totalCount === null) {
$this->_totalCount = count($this->allModels);
}
return $this->_totalCount;
}
/**
* Sets the total number of data models.
* @param integer $value the total number of data models.
*/
public function setTotalCount($value)
{
$this->_totalCount = $value;
}
private $_models;
/**
* Returns the data models in the current page.
* @return array the list of data models in the current page.
* @inheritdoc
*/
public function getModels()
protected function prepareModels()
{
if ($this->_models === null) {
if (($models = $this->allModels) === null) {
return array();
}
@ -114,57 +81,39 @@ class ArrayDataProvider extends DataProvider
}
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());
}
$this->_models = $models;
}
return $this->_models;
}
/**
* Sets the data models in the current page.
* @param array $models the models in the current page
*/
public function setModels($models)
{
$this->_models = $models;
return $models;
}
private $_keys;
/**
* Returns the key values associated with the data models.
* @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
* is uniquely identified by the corresponding key value in this array.
* @inheritdoc
*/
public function getKeys()
protected function prepareKeys($models)
{
if ($this->_keys === null) {
$this->_keys = array();
$models = $this->getModels();
if ($this->key !== null) {
$keys = array();
foreach ($models as $model) {
if (is_string($this->key)) {
$this->_keys[] = $model[$this->key];
$keys[] = $model[$this->key];
} else {
$this->_keys[] = call_user_func($this->key, $model);
$keys[] = call_user_func($this->key, $model);
}
}
return $keys;
} else {
$this->_keys = array_keys($models);
}
return array_keys($models);
}
return $this->_keys;
}
/**
* Sets the key values associated with the data models.
* @param array $keys the list of key values corresponding to [[models]].
* @inheritdoc
*/
public function setKeys($keys)
protected function prepareTotalCount()
{
$this->_keys = $keys;
return count($this->allModels);
}
/**

256
framework/yii/data/BaseDataProvider.php

@ -0,0 +1,256 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\data;
use Yii;
use yii\base\Component;
use yii\base\InvalidParamException;
/**
* BaseDataProvider provides a base class that implements the [[DataProviderInterface]].
*
* @property integer $count The number of data models in the current page. This property is read-only.
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is
* uniquely identified by the corresponding key value in this array.
* @property array $models The list of data models in the current page.
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
* is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and
* [[setPagination()]] for details.
* @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note
* that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.
* @property integer $totalCount Total number of possible data models.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class BaseDataProvider extends Component implements DataProviderInterface
{
/**
* @var string an ID that uniquely identifies the data provider among all data providers.
* You should set this property if the same page contains two or more different data providers.
* Otherwise, the [[pagination]] and [[sort]] mainly not work properly.
*/
public $id;
private $_sort;
private $_pagination;
private $_keys;
private $_models;
private $_totalCount;
/**
* Prepares the data models that will be made available in the current page.
* @return array the available data models
*/
abstract protected function prepareModels();
/**
* Prepares the keys associated with the currently available data models.
* @param array $models the available data models
* @return array the keys
*/
abstract protected function prepareKeys($models);
/**
* Returns a value indicating the total number of data models in this data provider.
* @return integer total number of data models in this data provider.
*/
abstract protected function prepareTotalCount();
/**
* Prepares the data models and keys.
*
* This method will prepare the data models and keys that can be retrieved via
* [[getModels()]] and [[getKeys()]].
*
* This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
*
* @param boolean $forcePrepare whether to force data preparation even if it has been done before.
*/
public function prepare($forcePrepare = false)
{
if ($forcePrepare || $this->_models === null) {
$this->_models = $this->prepareModels();
}
if ($forcePrepare || $this->_keys === null) {
$this->_keys = $this->prepareKeys($this->_models);
}
}
/**
* Returns the data models in the current page.
* @return array the list of data models in the current page.
*/
public function getModels()
{
$this->prepare();
return $this->_models;
}
/**
* Sets the data models in the current page.
* @param array $models the models in the current page
*/
public function setModels($models)
{
$this->_models = $models;
}
/**
* Returns the key values associated with the data models.
* @return array the list of key values corresponding to [[models]]. Each data model in [[models]]
* is uniquely identified by the corresponding key value in this array.
*/
public function getKeys()
{
$this->prepare();
return $this->_keys;
}
/**
* Sets the key values associated with the data models.
* @param array $keys the list of key values corresponding to [[models]].
*/
public function setKeys($keys)
{
$this->_keys = $keys;
}
/**
* Returns the number of data models in the current page.
* @return integer the number of data models in the current page.
*/
public function getCount()
{
return count($this->getModels());
}
/**
* Returns the total number of data models.
* When [[pagination]] is false, this returns the same value as [[count]].
* Otherwise, it will call [[prepareTotalCount()]] to get the count.
* @return integer total number of possible data models.
*/
public function getTotalCount()
{
if ($this->getPagination() === false) {
return $this->getCount();
} elseif ($this->_totalCount === null) {
$this->_totalCount = $this->prepareTotalCount();
}
return $this->_totalCount;
}
/**
* Sets the total number of data models.
* @param integer $value the total number of data models.
*/
public function setTotalCount($value)
{
$this->_totalCount = $value;
}
/**
* Returns the pagination object used by this data provider.
* Note that you should call [[prepare()]] or [[getModels()]] first to get correct values
* of [[Pagination::totalCount]] and [[Pagination::pageCount]].
* @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled.
*/
public function getPagination()
{
if ($this->_pagination === null) {
$this->_pagination = new Pagination;
if ($this->id !== null) {
$this->_pagination->pageVar = $this->id . '-page';
}
}
return $this->_pagination;
}
/**
* Sets the pagination for this data provider.
* @param array|Pagination|boolean $value the pagination to be used by this data provider.
* This can be one of the following:
*
* - a configuration array for creating the pagination object. The "class" element defaults
* to 'yii\data\Pagination'
* - an instance of [[Pagination]] or its subclass
* - false, if pagination needs to be disabled.
*
* @throws InvalidParamException
*/
public function setPagination($value)
{
if (is_array($value)) {
$config = array(
'class' => Pagination::className(),
);
if ($this->id !== null) {
$config['pageVar'] = $this->id . '-page';
}
$this->_pagination = Yii::createObject(array_merge($config, $value));
} elseif ($value instanceof Pagination || $value === false) {
$this->_pagination = $value;
} else {
throw new InvalidParamException('Only Pagination instance, configuration array or false is allowed.');
}
}
/**
* @return Sort|boolean the sorting object. If this is false, it means the sorting is disabled.
*/
public function getSort()
{
if ($this->_sort === null) {
$this->setSort(array());
}
return $this->_sort;
}
/**
* Sets the sort definition for this data provider.
* @param array|Sort|boolean $value the sort definition to be used by this data provider.
* This can be one of the following:
*
* - a configuration array for creating the sort definition object. The "class" element defaults
* to 'yii\data\Sort'
* - an instance of [[Sort]] or its subclass
* - false, if sorting needs to be disabled.
*
* @throws InvalidParamException
*/
public function setSort($value)
{
if (is_array($value)) {
$config = array(
'class' => Sort::className(),
);
if ($this->id !== null) {
$config['sortVar'] = $this->id . '-sort';
}
$this->_sort = Yii::createObject(array_merge($config, $value));
} elseif ($value instanceof Sort || $value === false) {
$this->_sort = $value;
} else {
throw new InvalidParamException('Only Sort instance, configuration array or false is allowed.');
}
}
/**
* Refreshes the data provider.
* After calling this method, if [[getModels()]], [[getKeys()]] or [[getTotalCount()]] is called again,
* they will re-execute the query and return the latest data available.
*/
public function refresh()
{
$this->_totalCount = null;
$this->_models = null;
$this->_keys = null;
}
}

133
framework/yii/data/DataProvider.php

@ -1,133 +0,0 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\data;
use Yii;
use yii\base\Component;
use yii\base\InvalidParamException;
/**
* DataProvider is the base class of data provider classes.
*
* It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[DataProviderInterface]].
*
* @property integer $count The number of data models in the current page. This property is read-only.
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
* is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and
* [[setPagination()]] for details.
* @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note
* that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class DataProvider extends Component implements DataProviderInterface
{
/**
* @var string an ID that uniquely identifies the data provider among all data providers.
* You should set this property if the same page contains two or more different data providers.
* Otherwise, the [[pagination]] and [[sort]] mainly not work properly.
*/
public $id;
private $_sort;
private $_pagination;
/**
* @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled.
*/
public function getPagination()
{
if ($this->_pagination === null) {
$this->_pagination = new Pagination;
if ($this->id !== null) {
$this->_pagination->pageVar = $this->id . '-page';
}
$this->_pagination->totalCount = $this->getTotalCount();
}
return $this->_pagination;
}
/**
* Sets the pagination for this data provider.
* @param array|Pagination|boolean $value the pagination to be used by this data provider.
* This can be one of the following:
*
* - a configuration array for creating the pagination object. The "class" element defaults
* to 'yii\data\Pagination'
* - an instance of [[Pagination]] or its subclass
* - false, if pagination needs to be disabled.
*
* @throws InvalidParamException
*/
public function setPagination($value)
{
if (is_array($value)) {
$config = array(
'class' => Pagination::className(),
);
if ($this->id !== null) {
$config['pageVar'] = $this->id . '-page';
}
$this->_pagination = Yii::createObject(array_merge($config, $value));
} elseif ($value instanceof Pagination || $value === false) {
$this->_pagination = $value;
} else {
throw new InvalidParamException('Only Pagination instance, configuration array or false is allowed.');
}
}
/**
* @return Sort|boolean the sorting object. If this is false, it means the sorting is disabled.
*/
public function getSort()
{
if ($this->_sort === null) {
$this->setSort(array());
}
return $this->_sort;
}
/**
* Sets the sort definition for this data provider.
* @param array|Sort|boolean $value the sort definition to be used by this data provider.
* This can be one of the following:
*
* - a configuration array for creating the sort definition object. The "class" element defaults
* to 'yii\data\Sort'
* - an instance of [[Sort]] or its subclass
* - false, if sorting needs to be disabled.
*
* @throws InvalidParamException
*/
public function setSort($value)
{
if (is_array($value)) {
$config = array(
'class' => Sort::className(),
);
if ($this->id !== null) {
$config['sortVar'] = $this->id . '-sort';
}
$this->_sort = Yii::createObject(array_merge($config, $value));
} elseif ($value instanceof Sort || $value === false) {
$this->_sort = $value;
} else {
throw new InvalidParamException('Only Sort instance, configuration array or false is allowed.');
}
}
/**
* Returns the number of data models in the current page.
* @return integer the number of data models in the current page.
*/
public function getCount()
{
return count($this->getModels());
}
}

12
framework/yii/data/DataProviderInterface.php

@ -19,6 +19,18 @@ namespace yii\data;
interface DataProviderInterface
{
/**
* Prepares the data models and keys.
*
* This method will prepare the data models and keys that can be retrieved via
* [[getModels()]] and [[getKeys()]].
*
* This method will be implicitly called by [[getModels()]] and [[getKeys()]] if it has not been called before.
*
* @param boolean $forcePrepare whether to force data preparation even if it has been done before.
*/
public function prepare($forcePrepare = false);
/**
* Returns the number of data models in the current page.
* This is equivalent to `count($provider->getModels())`.
* When [[pagination]] is false, this is the same as [[totalCount]].

15
framework/yii/db/ActiveQuery.php

@ -55,12 +55,6 @@ class ActiveQuery extends Query
*/
public $with;
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row or model data. For more details, see [[indexBy()]].
*/
public $indexBy;
/**
* @var boolean whether to return each record as an array. If false (default), an object
* of [[modelClass]] will be created to represent each record.
*/
@ -174,7 +168,7 @@ class ActiveQuery extends Query
/**
* Sets the [[asArray]] property.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
* @return ActiveQuery the query object itself
* @return static the query object itself
*/
public function asArray($value = true)
{
@ -202,7 +196,7 @@ class ActiveQuery extends Query
* ))->all();
* ~~~
*
* @return ActiveQuery the query object itself
* @return static the query object itself
*/
public function with()
{
@ -229,12 +223,11 @@ class ActiveQuery extends Query
* }
* ~~~
*
* @return ActiveQuery the query object itself
* @return static the query object itself
*/
public function indexBy($column)
{
$this->indexBy = $column;
return $this;
return parent::indexBy($column);
}
private function createModels($rows)

4
framework/yii/db/ActiveRelation.php

@ -66,7 +66,7 @@ class ActiveRelation extends ActiveQuery
* @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation the relation object itself.
* @return static the relation object itself.
*/
public function via($relationName, $callable = null)
{
@ -86,7 +86,7 @@ class ActiveRelation extends ActiveQuery
* in the [[primaryModel]] table.
* @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
* Its signature should be `function($query)`, where `$query` is the query to be customized.
* @return ActiveRelation
* @return static
*/
public function viaTable($tableName, $link, $callable = null)
{

39
framework/yii/db/Command.php

@ -88,7 +88,7 @@ class Command extends \yii\base\Component
* Specifies the SQL statement to be executed.
* The previous SQL execution (if any) will be cancelled, and [[params]] will be cleared as well.
* @param string $sql the SQL statement to be set.
* @return Command this command instance
* @return static this command instance
*/
public function setSql($sql)
{
@ -174,15 +174,16 @@ class Command extends \yii\base\Component
* @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.
* @param integer $length length of the data type
* @param mixed $driverOptions the driver-specific options
* @return Command the current command being executed
* @return static the current command being executed
* @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php
*/
public function bindParam($name, &$value, $dataType = null, $length = null, $driverOptions = null)
{
$this->prepare();
if ($dataType === null) {
$this->pdoStatement->bindParam($name, $value, $this->getPdoType($value));
} elseif ($length === null) {
$dataType = $this->db->getSchema()->getPdoType($value);
}
if ($length === null) {
$this->pdoStatement->bindParam($name, $value, $dataType);
} elseif ($driverOptions === null) {
$this->pdoStatement->bindParam($name, $value, $dataType, $length);
@ -201,17 +202,16 @@ class Command extends \yii\base\Component
* placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $value The value to bind to the parameter
* @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value.
* @return Command the current command being executed
* @return static the current command being executed
* @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php
*/
public function bindValue($name, $value, $dataType = null)
{
$this->prepare();
if ($dataType === null) {
$this->pdoStatement->bindValue($name, $value, $this->getPdoType($value));
} else {
$this->pdoStatement->bindValue($name, $value, $dataType);
$dataType = $this->db->getSchema()->getPdoType($value);
}
$this->pdoStatement->bindValue($name, $value, $dataType);
$this->_params[$name] = $value;
return $this;
}
@ -225,7 +225,7 @@ class Command extends \yii\base\Component
* e.g. `array(':name' => 'John', ':age' => 25)`. By default, the PDO type of each value is determined
* by its PHP type. You may explicitly specify the PDO type by using an array: `array(value, type)`,
* e.g. `array(':name' => 'John', ':profile' => array($profile, \PDO::PARAM_LOB))`.
* @return Command the current command being executed
* @return static the current command being executed
*/
public function bindValues($values)
{
@ -236,7 +236,7 @@ class Command extends \yii\base\Component
$type = $value[1];
$value = $value[0];
} else {
$type = $this->getPdoType($value);
$type = $this->db->getSchema()->getPdoType($value);
}
$this->pdoStatement->bindValue($name, $value, $type);
$this->_params[$name] = $value;
@ -246,25 +246,6 @@ class Command extends \yii\base\Component
}
/**
* Determines the PDO type for the given PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
private function getPdoType($data)
{
static $typeMap = array( // php type => PDO type
'boolean' => \PDO::PARAM_BOOL,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
/**
* Executes the SQL statement.
* This method should only be used for executing non-query SQL statement, such as `INSERT`, `DELETE`, `UPDATE` SQLs.
* No result set will be returned.

38
framework/yii/db/Query.php

@ -166,7 +166,7 @@ class Query extends Component
* }
* ~~~
*
* @return Query the query object itself
* @return static the query object itself
*/
public function indexBy($column)
{
@ -325,7 +325,7 @@ class Query extends Component
* (which means the column contains a DB expression).
* @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
* in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
* @return Query the query object itself
* @return static the query object itself
*/
public function select($columns, $option = null)
{
@ -340,7 +340,7 @@ class Query extends Component
/**
* Sets the value indicating whether to SELECT DISTINCT or not.
* @param bool $value whether to SELECT DISTINCT or not.
* @return Query the query object itself
* @return static the query object itself
*/
public function distinct($value = true)
{
@ -355,7 +355,7 @@ class Query extends Component
* Table names can contain schema prefixes (e.g. `'public.tbl_user'`) and/or table aliases (e.g. `'tbl_user u'`).
* The method will automatically quote the table names unless it contains some parenthesis
* (which means the table is given as a sub-query or DB expression).
* @return Query the query object itself
* @return static the query object itself
*/
public function from($tables)
{
@ -431,7 +431,7 @@ class Query extends Component
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see andWhere()
* @see orWhere()
*/
@ -448,7 +448,7 @@ class Query extends Component
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see where()
* @see orWhere()
*/
@ -469,7 +469,7 @@ class Query extends Component
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see where()
* @see andWhere()
*/
@ -560,7 +560,7 @@ class Query extends Component
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @return static the query object itself
* @see addGroupBy()
*/
public function groupBy($columns)
@ -578,7 +578,7 @@ class Query extends Component
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @return static the query object itself
* @see groupBy()
*/
public function addGroupBy($columns)
@ -599,7 +599,7 @@ class Query extends Component
* @param string|array $condition the conditions to be put after HAVING.
* Please refer to [[where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see andHaving()
* @see orHaving()
*/
@ -616,7 +616,7 @@ class Query extends Component
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see having()
* @see orHaving()
*/
@ -637,7 +637,7 @@ class Query extends Component
* @param string|array $condition the new HAVING condition. Please refer to [[where()]]
* on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return Query the query object itself
* @return static the query object itself
* @see having()
* @see andHaving()
*/
@ -659,7 +659,7 @@ class Query extends Component
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @return static the query object itself
* @see addOrderBy()
*/
public function orderBy($columns)
@ -675,7 +675,7 @@ class Query extends Component
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @return static the query object itself
* @see orderBy()
*/
public function addOrderBy($columns)
@ -710,7 +710,7 @@ class Query extends Component
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return Query the query object itself
* @return static the query object itself
*/
public function limit($limit)
{
@ -721,7 +721,7 @@ class Query extends Component
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return Query the query object itself
* @return static the query object itself
*/
public function offset($offset)
{
@ -732,7 +732,7 @@ class Query extends Component
/**
* Appends a SQL statement using UNION operator.
* @param string|Query $sql the SQL statement to be appended using UNION
* @return Query the query object itself
* @return static the query object itself
*/
public function union($sql)
{
@ -744,7 +744,7 @@ class Query extends Component
* Sets the parameters to be bound to the query.
* @param array $params list of query parameter values indexed by parameter placeholders.
* For example, `array(':name' => 'Dan', ':age' => 31)`.
* @return Query the query object itself
* @return static the query object itself
* @see addParams()
*/
public function params($params)
@ -757,7 +757,7 @@ class Query extends Component
* Adds additional parameters to be bound to the query.
* @param array $params list of query parameter values indexed by parameter placeholders.
* For example, `array(':name' => 'Dan', ':age' => 31)`.
* @return Query the query object itself
* @return static the query object itself
* @see params()
*/
public function addParams($params)

20
framework/yii/db/Schema.php

@ -187,6 +187,26 @@ abstract class Schema extends Object
}
/**
* Determines the PDO type for the given PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
public function getPdoType($data)
{
static $typeMap = array(
// php type => PDO type
'boolean' => \PDO::PARAM_BOOL,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
/**
* Refreshes the schema.
* This method cleans up all cached table schemas so that they can be re-created later
* to reflect the database schema change.

23
framework/yii/db/cubrid/Schema.php

@ -101,7 +101,8 @@ class Schema extends \yii\db\Schema
$this->db->open();
// workaround for broken PDO::quote() implementation in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658
if (version_compare($this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), '9.1.0', '<=')) {
$version = $this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION);
if (version_compare($version, '8.4.4.0002', '<') || $version[0] == '9' && version_compare($version, '9.2.0.0002', '<=')) {
return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
} else {
return $this->db->pdo->quote($str);
@ -237,4 +238,24 @@ class Schema extends \yii\db\Schema
}
return $tableNames;
}
/**
* Determines the PDO type for the given PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
public function getPdoType($data)
{
static $typeMap = array(
// php type => PDO type
'boolean' => \PDO::PARAM_INT, // PARAM_BOOL is not supported by CUBRID PDO
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
'resource' => \PDO::PARAM_LOB,
'NULL' => \PDO::PARAM_NULL,
);
$type = gettype($data);
return isset($typeMap[$type]) ? $typeMap[$type] : \PDO::PARAM_STR;
}
}

7
framework/yii/debug/Module.php

@ -8,6 +8,7 @@
namespace yii\debug;
use Yii;
use yii\base\Application;
use yii\base\View;
use yii\web\HttpException;
@ -55,7 +56,11 @@ class Module extends \yii\base\Module
parent::init();
$this->dataPath = Yii::getAlias($this->dataPath);
$this->logTarget = Yii::$app->getLog()->targets['debug'] = new LogTarget($this);
Yii::$app->getView()->on(View::EVENT_END_BODY, array($this, 'renderToolbar'));
// do not initialize view component before application is ready (needed when debug in preload)
$module = $this;
Yii::$app->on(Application::EVENT_BEFORE_ACTION, function() use ($module) {
Yii::$app->getView()->on(View::EVENT_END_BODY, array($module, 'renderToolbar'));
});
foreach (array_merge($this->corePanels(), $this->panels) as $id => $config) {
$config['module'] = $this;

4
framework/yii/gii/components/ActiveField.php

@ -32,6 +32,10 @@ class ActiveField extends \yii\widgets\ActiveField
}
}
/**
* Makes filed remember its value between page reloads
* @return static the field object itself
*/
public function sticky()
{
$this->options['class'] .= ' sticky';

5
framework/yii/gii/generators/crud/templates/views/_search.php

@ -23,7 +23,10 @@ use yii\widgets\ActiveForm;
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-search">
<?php echo '<?php'; ?> $form = ActiveForm::begin(array('method' => 'get')); ?>
<?php echo '<?php'; ?> $form = ActiveForm::begin(array(
'action' => array('index'),
'method' => 'get',
)); ?>
<?php
$count = 0;

7
framework/yii/gii/generators/model/Generator.php

@ -464,6 +464,9 @@ class Generator extends \yii\gii\Generator
return $this->_tableNames;
}
$db = $this->getDbConnection();
if ($db === null) {
return array();
}
$tableNames = array();
if (strpos($this->tableName, '*') !== false) {
if (($pos = strrpos($this->tableName, '.')) !== false) {
@ -511,7 +514,8 @@ class Generator extends \yii\gii\Generator
$patterns[] = '/^' . str_replace('*', '(\w+)', $pattern) . '$/';
}
if (!empty($db->tablePrefix)) {
$patterns[] = "/^{$db->tablePrefix}(.*?)|(.*?){$db->tablePrefix}$/";
$patterns[] = "/^{$db->tablePrefix}(.*?)$/";
$patterns[] = "/^(.*?){$db->tablePrefix}$/";
} else {
$patterns[] = "/^tbl_(.*?)$/";
}
@ -520,6 +524,7 @@ class Generator extends \yii\gii\Generator
foreach ($patterns as $pattern) {
if (preg_match($pattern, $tableName, $matches)) {
$className = $matches[1];
break;
}
}
return $this->_classNames[$tableName] = Inflector::id2camel($className, '_');

3
framework/yii/gii/generators/module/Generator.php

@ -147,6 +147,9 @@ EOD;
if (strpos($this->moduleClass, '\\') === false || Yii::getAlias('@' . str_replace('\\', '/', $this->moduleClass)) === false) {
$this->addError('moduleClass', 'Module class must be properly namespaced.');
}
if (substr($this->moduleClass, -1, 1) == '\\') {
$this->addError('moduleClass', 'Module class name must not be empty. Please enter a fully qualified class name. e.g. "app\\modules\\admin\\Module".');
}
}
/**

3
framework/yii/grid/ActionColumn.php

@ -72,12 +72,11 @@ class ActionColumn extends Column
if ($this->urlCreator instanceof Closure) {
return call_user_func($this->urlCreator, $model, $action);
} else {
$route = Inflector::camel2id(StringHelper::basename(get_class($model))) . '/' . $action;
$params = $model->getPrimaryKey(true);
if (count($params) === 1) {
$params = array('id' => reset($params));
}
return Yii::$app->getUrlManager()->createUrl($route, $params);
return Yii::$app->controller->createUrl($action, $params);
}
}

6
framework/yii/grid/DataColumn.php

@ -73,7 +73,7 @@ class DataColumn extends Column
* the [[filter]] property. When [[filter]] is not set or is an array, this property will be used to
* render the HTML attributes for the generated filter input fields.
*/
public $filterInputOptions = array('class' => 'form-control');
public $filterInputOptions = array('class' => 'form-control', 'id' => null);
protected function renderHeaderCellContent()
@ -115,7 +115,9 @@ class DataColumn extends Column
{
if (is_string($this->filter)) {
return $this->filter;
} elseif ($this->filter !== false && $this->grid->filterModel instanceof Model && $this->attribute !== null) {
} elseif ($this->filter !== false && $this->grid->filterModel instanceof Model &&
$this->attribute !== null && $this->grid->filterModel->isAttributeActive($this->attribute))
{
if (is_array($this->filter)) {
$options = array_merge(array('prompt' => ''), $this->filterInputOptions);
return Html::activeDropDownList($this->grid->filterModel, $this->attribute, $this->filter, $options);

39
framework/yii/grid/GridView.php

@ -14,13 +14,14 @@ use yii\base\InvalidConfigException;
use yii\base\Widget;
use yii\db\ActiveRecord;
use yii\helpers\Html;
use yii\widgets\ListViewBase;
use yii\helpers\Json;
use yii\widgets\BaseListView;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class GridView extends ListViewBase
class GridView extends BaseListView
{
const FILTER_POS_HEADER = 'header';
const FILTER_POS_FOOTER = 'footer';
@ -137,6 +138,14 @@ class GridView extends ListViewBase
*/
public $filterModel;
/**
* @var string|array the URL for returning the filtering result. [[Html::url()]] will be called to
* normalize the URL. If not set, the current controller action will be used.
* When the user makes change to any filter input, the current filtering inputs will be appended
* as GET parameters to this URL.
*/
public $filterUrl;
public $filterSelector;
/**
* @var string whether the filters should be displayed in the grid view. Valid values include:
*
* - [[FILTER_POS_HEADER]]: the filters will be displayed on top of each column's header cell.
@ -167,6 +176,9 @@ class GridView extends ListViewBase
if (!isset($this->options['id'])) {
$this->options['id'] = $this->getId();
}
if (!isset($this->filterRowOptions['id'])) {
$this->filterRowOptions['id'] = $this->options['id'] . '-filters';
}
$this->initColumns();
}
@ -177,12 +189,33 @@ class GridView extends ListViewBase
public function run()
{
$id = $this->options['id'];
$options = Json::encode($this->getClientOptions());
$view = $this->getView();
GridViewAsset::register($view);
$view->registerJs("jQuery('#$id').yiiGridView();");
$view->registerJs("jQuery('#$id').yiiGridView($options);");
parent::run();
}
/**
* Returns the options for the grid view JS widget.
* @return array the options
*/
protected function getClientOptions()
{
$filterUrl = isset($this->filterUrl) ? $this->filterUrl : array(Yii::$app->controller->action->id);
$id = $this->filterRowOptions['id'];
$filterSelector = "#$id input, #$id select";
if (isset($this->filterSelector)) {
$filterSelector .= ', ' . $this->filterSelector;
}
return array(
'filterUrl' => Html::url($filterUrl),
'filterSelector' => $filterSelector,
);
}
/**
* Renders the data models for the grid view.
*/

7
framework/yii/helpers/BaseFileHelper.php

@ -155,6 +155,10 @@ class BaseFileHelper
* and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
* both '/' and '\' in the paths.
* - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file to be copied from, while `$to` is the copy target.
* - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied.
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file copied from, while `$to` is the copy target.
@ -173,6 +177,9 @@ class BaseFileHelper
$from = $src . DIRECTORY_SEPARATOR . $file;
$to = $dst . DIRECTORY_SEPARATOR . $file;
if (static::filterPath($from, $options)) {
if (isset($options['beforeCopy']) && !call_user_func($options['beforeCopy'], $from, $to)) {
continue;
}
if (is_file($from)) {
copy($from, $to);
if (isset($options['fileMode'])) {

2
framework/yii/helpers/BaseHtml.php

@ -237,7 +237,7 @@ class BaseHtml
$hiddenInputs[] = static::hiddenInput($request->restVar, $method);
$method = 'post';
}
if ($request->enableCsrfValidation) {
if ($request->enableCsrfValidation && !strcasecmp($method, 'post')) {
$hiddenInputs[] = static::hiddenInput($request->csrfVar, $request->getCsrfToken());
}
}

26
framework/yii/helpers/BaseJson.php

@ -81,32 +81,26 @@ class BaseJson
*/
protected static function processData($data, &$expressions, $expPrefix)
{
if (is_array($data)) {
foreach ($data as $key => $value) {
if (is_array($value) || is_object($value)) {
$data[$key] = static::processData($value, $expressions, $expPrefix);
}
}
return $data;
} elseif (is_object($data)) {
if (is_object($data)) {
if ($data instanceof JsExpression) {
$token = "!{[$expPrefix=" . count($expressions) . ']}!';
$expressions['"' . $token . '"'] = $data->expression;
return $token;
} else {
}
$data = $data instanceof Arrayable ? $data->toArray() : get_object_vars($data);
$result = array();
if ($data === array() && !$data instanceof Arrayable) {
return new \stdClass();
}
}
if (is_array($data)) {
foreach ($data as $key => $value) {
if (is_array($value) || is_object($value)) {
$result[$key] = static::processData($value, $expressions, $expPrefix);
} else {
$result[$key] = $value;
$data[$key] = static::processData($value, $expressions, $expPrefix);
}
}
return $result;
}
} else {
return $data;
}
}
}

84
framework/yii/helpers/BaseSecurity.php

@ -24,20 +24,40 @@ use yii\base\InvalidParamException;
class BaseSecurity
{
/**
* Uses AES, block size is 128-bit (16 bytes).
*/
const CRYPT_BLOCK_SIZE = 16;
/**
* Uses AES-192, key size is 192-bit (24 bytes).
*/
const CRYPT_KEY_SIZE = 24;
/**
* Uses SHA-256.
*/
const DERIVATION_HASH = 'sha256';
/**
* Uses 1000 iterations.
*/
const DERIVATION_ITERATIONS = 1000;
/**
* Encrypts data.
* @param string $data data to be encrypted.
* @param string $key the encryption secret key
* @param string $password the encryption password
* @return string the encrypted data
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
* @see decrypt()
*/
public static function encrypt($data, $key)
public static function encrypt($data, $password)
{
$module = static::openCryptModule();
// 192-bit (24 bytes) key size
$key = StringHelper::substr($key, 0, 24);
$data = static::addPadding($data);
srand();
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
$key = static::deriveKey($password, $iv);
mcrypt_generic_init($module, $key, $iv);
$encrypted = $iv . mcrypt_generic($module, $data);
mcrypt_generic_deinit($module);
@ -48,23 +68,69 @@ class BaseSecurity
/**
* Decrypts data
* @param string $data data to be decrypted.
* @param string $key the decryption secret key
* @param string $password the decryption password
* @return string the decrypted data
* @throws Exception if PHP Mcrypt extension is not loaded or failed to be initialized
* @see encrypt()
*/
public static function decrypt($data, $key)
public static function decrypt($data, $password)
{
$module = static::openCryptModule();
// 192-bit (24 bytes) key size
$key = StringHelper::substr($key, 0, 24);
$ivSize = mcrypt_enc_get_iv_size($module);
$iv = StringHelper::substr($data, 0, $ivSize);
$key = static::deriveKey($password, $iv);
mcrypt_generic_init($module, $key, $iv);
$decrypted = mdecrypt_generic($module, StringHelper::substr($data, $ivSize, StringHelper::strlen($data)));
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return rtrim($decrypted, "\0");
return static::stripPadding($decrypted);
}
/**
* Adds a padding to the given data (PKCS #7).
* @param string $data the data to pad
* @return string the padded data
*/
protected static function addPadding($data)
{
$pad = self::CRYPT_BLOCK_SIZE - (StringHelper::strlen($data) % self::CRYPT_BLOCK_SIZE);
return $data . str_repeat(chr($pad), $pad);
}
/**
* Strips the padding from the given data.
* @param string $data the data to trim
* @return string the trimmed data
*/
protected static function stripPadding($data)
{
$end = StringHelper::substr($data, -1, NULL);
$last = ord($end);
$n = StringHelper::strlen($data) - $last;
if (StringHelper::substr($data, $n, NULL) == str_repeat($end, $last)) {
return StringHelper::substr($data, 0, $n);
}
return false;
}
/**
* Derives a key from the given password (PBKDF2).
* @param string $password the source password
* @param string $salt the random salt
* @return string the derived key
*/
protected static function deriveKey($password, $salt)
{
if (function_exists('hash_pbkdf2')) {
return hash_pbkdf2(self::DERIVATION_HASH, $password, $salt, self::DERIVATION_ITERATIONS, self::CRYPT_KEY_SIZE, true);
}
$hmac = hash_hmac(self::DERIVATION_HASH, $salt . pack('N', 1), $password, true);
$xorsum = $hmac;
for ($i = 1; $i < self::DERIVATION_ITERATIONS; $i++) {
$hmac = hash_hmac(self::DERIVATION_HASH, $hmac, $password, true);
$xorsum ^= $hmac;
}
return substr($xorsum, 0, self::CRYPT_KEY_SIZE);
}
/**

6
framework/yii/i18n/PhpMessageSource.php

@ -26,6 +26,8 @@ use Yii;
* );
* ~~~
*
* You may use [[fileMap]] to customize the association between category names and the file names.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
@ -60,10 +62,8 @@ class PhpMessageSource extends MessageSource
$messageFile = Yii::getAlias($this->basePath) . "/$language/";
if (isset($this->fileMap[$category])) {
$messageFile .= $this->fileMap[$category];
} elseif (($pos = strrpos($category, '\\')) !== false) {
$messageFile .= (substr($category, $pos) . '.php');
} else {
$messageFile .= "$category.php";
$messageFile .= str_replace('\\', '/', $category) . '.php';
}
if (is_file($messageFile)) {
$messages = include($messageFile);

2
framework/yii/log/FileTarget.php

@ -78,7 +78,7 @@ class FileTarget extends Target
}
/**
* Sends log messages to specified email addresses.
* Writes log messages to a file.
* @throws InvalidConfigException if unable to open the log file for writing
*/
public function export()

2
framework/yii/requirements/YiiRequirementChecker.php

@ -63,7 +63,7 @@ class YiiRequirementChecker
* @param array|string $requirements requirements to be checked.
* If an array, it is treated as the set of requirements;
* If a string, it is treated as the path of the file, which contains the requirements;
* @return YiiRequirementChecker self instance.
* @return static self instance.
*/
function check($requirements)
{

1
framework/yii/validators/BooleanValidator.php

@ -90,7 +90,6 @@ class BooleanValidator extends Validator
'falseValue' => $this->falseValue,
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
'{true}' => $this->trueValue,
'{false}' => $this->falseValue,
))),

3
framework/yii/validators/CompareValidator.php

@ -170,6 +170,7 @@ class CompareValidator extends Validator
case '>=': return $value >= $this->compareValue;
case '<': return $value < $this->compareValue;
case '<=': return $value <= $this->compareValue;
default: return false;
}
}
@ -201,7 +202,7 @@ class CompareValidator extends Validator
$options['message'] = Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
'{compareAttribute}' => $compareValue,
'{compareValue}' => $compareValue,
)));

1
framework/yii/validators/EmailValidator.php

@ -131,7 +131,6 @@ class EmailValidator extends Validator
'allowName' => $this->allowName,
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
))),
'enableIDN' => (boolean)$this->enableIDN,
);

4
framework/yii/validators/NumberValidator.php

@ -121,13 +121,11 @@ class NumberValidator extends Validator
public function clientValidateAttribute($object, $attribute, $view)
{
$label = $object->getAttributeLabel($attribute);
$value = $object->$attribute;
$options = array(
'pattern' => new JsExpression($this->integerOnly ? $this->integerPattern : $this->numberPattern),
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $label,
'{value}' => $value,
))),
);
@ -135,7 +133,6 @@ class NumberValidator extends Validator
$options['min'] = $this->min;
$options['tooSmall'] = Html::encode(strtr($this->tooSmall, array(
'{attribute}' => $label,
'{value}' => $value,
'{min}' => $this->min,
)));
}
@ -143,7 +140,6 @@ class NumberValidator extends Validator
$options['max'] = $this->max;
$options['tooBig'] = Html::encode(strtr($this->tooBig, array(
'{attribute}' => $label,
'{value}' => $value,
'{max}' => $this->max,
)));
}

1
framework/yii/validators/RangeValidator.php

@ -96,7 +96,6 @@ class RangeValidator extends Validator
'not' => $this->not,
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
))),
);
if ($this->skipOnEmpty) {

1
framework/yii/validators/RegularExpressionValidator.php

@ -103,7 +103,6 @@ class RegularExpressionValidator extends Validator
'not' => $this->not,
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
))),
);
if ($this->skipOnEmpty) {

1
framework/yii/validators/RequiredValidator.php

@ -123,7 +123,6 @@ class RequiredValidator extends Validator
$options['message'] = Html::encode(strtr($options['message'], array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
)));
ValidationAsset::register($view);

5
framework/yii/validators/StringValidator.php

@ -149,12 +149,10 @@ class StringValidator extends Validator
public function clientValidateAttribute($object, $attribute, $view)
{
$label = $object->getAttributeLabel($attribute);
$value = $object->$attribute;
$options = array(
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $label,
'{value}' => $value,
))),
);
@ -162,7 +160,6 @@ class StringValidator extends Validator
$options['min'] = $this->min;
$options['tooShort'] = Html::encode(strtr($this->tooShort, array(
'{attribute}' => $label,
'{value}' => $value,
'{min}' => $this->min,
)));
}
@ -170,7 +167,6 @@ class StringValidator extends Validator
$options['max'] = $this->max;
$options['tooLong'] = Html::encode(strtr($this->tooLong, array(
'{attribute}' => $label,
'{value}' => $value,
'{max}' => $this->max,
)));
}
@ -178,7 +174,6 @@ class StringValidator extends Validator
$options['is'] = $this->length;
$options['notEqual'] = Html::encode(strtr($this->notEqual, array(
'{attribute}' => $label,
'{value}' => $value,
'{length}' => $this->is,
)));
}

1
framework/yii/validators/UrlValidator.php

@ -132,7 +132,6 @@ class UrlValidator extends Validator
'pattern' => new JsExpression($pattern),
'message' => Html::encode(strtr($this->message, array(
'{attribute}' => $object->getAttributeLabel($attribute),
'{value}' => $object->$attribute,
))),
'enableIDN' => (boolean)$this->enableIDN,
);

6
framework/yii/web/AccessControl.php

@ -119,6 +119,12 @@ class AccessControl extends ActionFilter
return false;
}
}
if (isset($this->denyCallback)) {
call_user_func($this->denyCallback, $rule);
}
else {
$this->denyAccess($user);
}
return false;
}

2
framework/yii/web/Application.php

@ -45,7 +45,7 @@ class Application extends \yii\base\Application
* )
* ~~~
*
* Defaults to null, meaning catch-all is not effective.
* Defaults to null, meaning catch-all is not used.
*/
public $catchAll;
/**

5
framework/yii/web/AssetBundle.php

@ -130,7 +130,6 @@ class AssetBundle extends Object
/**
* Registers the CSS and JS files with the given view.
* This method will first register all dependent asset bundles.
* It will then 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 \yii\base\View $view the view that the asset files to be registered with.
@ -139,10 +138,6 @@ class AssetBundle extends Object
*/
public function registerAssets($view)
{
foreach ($this->depends as $name) {
$view->registerAssetBundle($name);
}
$this->publish($view->getAssetManager());
foreach ($this->js as $js) {

4
framework/yii/web/AssetManager.php

@ -16,8 +16,8 @@ use yii\helpers\FileHelper;
/**
* AssetManager manages asset bundles and asset publishing.
*
* @property AssetConverterInterface $converter The asset converter. Note that the type of this property differs in
* getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
* @property AssetConverterInterface $converter The asset converter. Note that the type of this property
* differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0

13
framework/yii/web/Controller.php

@ -146,6 +146,19 @@ class Controller extends \yii\base\Controller
}
/**
* Redirects the browser to the last visited page.
* @param string|array $defaultUrl the default return URL in case it was not set previously.
* If this is null and the return URL was not set previously, [[Application::homeUrl]] will be redirected to.
* Please refer to [[User::setReturnUrl()]] on accepted format of the URL.
* @return Response the current response object
* @see User::getReturnUrl()
*/
public function goBack($defaultUrl = null)
{
return Yii::$app->getResponse()->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl));
}
/**
* Refreshes the current page.
* This method is a shortcut to [[Response::refresh()]].
* @param string $anchor the anchor that should be appended to the redirection URL.

6
framework/yii/web/HeaderCollection.php

@ -83,7 +83,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
* 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 HeaderCollection the collection object itself
* @return static the collection object itself
*/
public function set($name, $value = '')
{
@ -98,7 +98,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
* 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 HeaderCollection the collection object itself
* @return static the collection object itself
*/
public function add($name, $value)
{
@ -112,7 +112,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
* 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 HeaderCollection the collection object itself
* @return static the collection object itself
*/
public function setDefault($name, $value)
{

3
framework/yii/web/Request.php

@ -29,6 +29,8 @@ use yii\helpers\Security;
* previously, a random key will be generated and used.
* @property CookieCollection $cookies The cookie collection. This property is read-only.
* @property string $csrfToken The random token for CSRF validation. This property is read-only.
* @property string $csrfTokenFromHeader The CSRF token sent via [[CSRF_HEADER]] by browser. Null is returned
* if no such header is sent. This property is read-only.
* @property string $hostInfo Schema and hostname part (with port number if needed) of the request URL (e.g.
* `http://www.yiiframework.com`).
* @property boolean $isAjax Whether this is an AJAX (XMLHttpRequest) request. This property is read-only.
@ -75,6 +77,7 @@ class Request extends \yii\base\Request
*/
const CSRF_HEADER = 'X-CSRF-Token';
/**
* @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
* When CSRF validation is enabled, forms submitted to an Yii Web application must be originated

27
framework/yii/web/Response.php

@ -559,7 +559,20 @@ class Response extends \yii\base\Response
/**
* Redirects the browser to the specified URL.
*
* This method will send out a "Location" header to achieve the redirection.
* This method adds a "Location" header to the current response. Note that it does not send out
* the header until [[send()]] is called. In a controller action you may use this method as follows:
*
* ~~~
* return Yii::$app->getResponse()->redirect($url);
* ~~~
*
* In other places, if you want to send out the "Location" header immediately, you should use
* the following code:
*
* ~~~
* Yii::$app->getResponse()->redirect($url)->send();
* return;
* ~~~
*
* In AJAX mode, this normally will not work as expected unless there are some
* client-side JavaScript code handling the redirection. To help achieve this goal,
@ -578,12 +591,6 @@ class Response extends \yii\base\Response
* });
* ~~~
*
* In a controller action you may use this method like this:
*
* ~~~
* return Yii::$app->getResponse()->redirect($url);
* ~~~
*
* @param string|array $url the URL to be redirected to. This can be in one of the following formats:
*
* - a string representing a URL (e.g. "http://example.com")
@ -595,12 +602,12 @@ class Response extends \yii\base\Response
* Any relative URL will be converted into an absolute one by prepending it with the host info
* of the current request.
*
* @param integer $statusCode the HTTP status code. If null, it will use 302.
* @param integer $statusCode the HTTP status code. Defaults to 302.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code
* @return Response the response object itself
* @return static the response object itself
*/
public function redirect($url, $statusCode = null)
public function redirect($url, $statusCode = 302)
{
if (is_array($url) && isset($url[0])) {
// ensure the route is absolute

1
framework/yii/web/UrlRule.php

@ -156,6 +156,7 @@ class UrlRule extends Object
}
}
}
$tr['.'] = '\\.';
$this->_template = preg_replace('/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern);
$this->pattern = '#^' . trim(strtr($this->_template, $tr), '/') . '$#u';

4
framework/yii/web/User.php

@ -23,8 +23,8 @@ use yii\base\InvalidParamException;
*
* @property string|integer $id The unique identifier for the user. If null, it means the user is a guest.
* This property is read-only.
* @property IdentityInterface $identity The identity object associated with the currently logged user. Null is
* returned if the user is not logged in (not authenticated).
* @property IdentityInterface $identity The identity object associated with the currently logged user. Null
* is returned if the user is not logged in (not authenticated).
* @property boolean $isGuest Whether the current user is a guest. This property is read-only.
* @property string $returnUrl The URL that the user should be redirected to after login. Note that the type
* of this property differs in getter and setter. See [[getReturnUrl()]] and [[setReturnUrl()]] for details.

36
framework/yii/widgets/ActiveField.php

@ -221,7 +221,7 @@ class ActiveField extends Component
* @param array $options the tag options in terms of name-value pairs. It will be merged with [[labelOptions]].
* The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
* using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function label($label = null, $options = array())
{
@ -244,7 +244,7 @@ class ActiveField extends Component
*
* - tag: this specifies the tag name. If not set, "div" will be used.
*
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function error($options = array())
{
@ -263,7 +263,7 @@ class ActiveField extends Component
*
* - tag: this specifies the tag name. If not set, "div" will be used.
*
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function hint($content, $options = array())
{
@ -278,7 +278,7 @@ class ActiveField extends Component
* @param string $type the input type (e.g. 'text', 'password')
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function input($type, $options = array())
{
@ -293,7 +293,7 @@ class ActiveField extends Component
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function textInput($options = array())
{
@ -308,7 +308,7 @@ class ActiveField extends Component
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function passwordInput($options = array())
{
@ -323,7 +323,7 @@ class ActiveField extends Component
* unless they are explicitly specified in `$options`.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function fileInput($options = array())
{
@ -339,7 +339,7 @@ class ActiveField extends Component
* The model attribute value will be used as the content in the textarea.
* @param array $options the tag options in terms of name-value pairs. These will be rendered as
* the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function textarea($options = array())
{
@ -367,7 +367,7 @@ class ActiveField extends Component
* @param boolean $enclosedByLabel whether to enclose the radio within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the radio is enclosed by the label tag.
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function radio($options = array(), $enclosedByLabel = true)
{
@ -402,7 +402,7 @@ class ActiveField extends Component
* @param boolean $enclosedByLabel whether to enclose the checkbox within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the checkbox is enclosed by the label tag.
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function checkbox($options = array(), $enclosedByLabel = true)
{
@ -448,7 +448,7 @@ class ActiveField extends Component
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function dropDownList($items, $options = array())
{
@ -490,7 +490,7 @@ class ActiveField extends Component
* The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
*
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function listBox($items, $options = array())
{
@ -522,7 +522,7 @@ class ActiveField extends Component
* where $index is the zero-based index of the checkbox in the whole list; $label
* is the label for the checkbox; and $name, $value and $checked represent the name,
* value and the checked status of the checkbox input.
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function checkboxList($items, $options = array())
{
@ -552,7 +552,7 @@ class ActiveField extends Component
* where $index is the zero-based index of the radio button in the whole list; $label
* is the label for the radio button; and $name, $value and $checked represent the name,
* value and the checked status of the radio button input.
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function radioList($items, $options = array())
{
@ -571,7 +571,7 @@ class ActiveField extends Component
*
* @param string $class the widget class name
* @param array $config name-value pairs that will be used to initialize the widget
* @return ActiveField the field object itself
* @return static the field object itself
*/
public function widget($class, $config = array())
{
@ -589,9 +589,13 @@ class ActiveField extends Component
*/
protected function getClientOptions()
{
$attribute = Html::getAttributeName($this->attribute);
if (!in_array($attribute, $this->model->activeAttributes(), true)) {
return array();
}
$enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
if ($enableClientValidation) {
$attribute = Html::getAttributeName($this->attribute);
$validators = array();
foreach ($this->model->getActiveValidators($attribute) as $validator) {
/** @var \yii\validators\Validator $validator */

7
framework/yii/widgets/ListViewBase.php → framework/yii/widgets/BaseListView.php

@ -17,7 +17,7 @@ use yii\helpers\Html;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class ListViewBase extends Widget
abstract class BaseListView extends Widget
{
/**
* @var array the HTML attributes for the container tag of the list view.
@ -83,6 +83,7 @@ abstract class ListViewBase extends Widget
if ($this->dataProvider === null) {
throw new InvalidConfigException('The "dataProvider" property must be set.');
}
$this->dataProvider->prepare();
}
/**
@ -131,14 +132,14 @@ abstract class ListViewBase extends Widget
public function renderSummary()
{
$count = $this->dataProvider->getCount();
if (($pagination = $this->dataProvider->getPagination()) !== false) {
if (($pagination = $this->dataProvider->getPagination()) !== false && $count > 0) {
$totalCount = $this->dataProvider->getTotalCount();
$begin = $pagination->getPage() * $pagination->pageSize + 1;
$end = $begin + $count - 1;
$page = $pagination->getPage() + 1;
$pageCount = $pagination->pageCount;
if (($summaryContent = $this->summary) === null) {
$summaryContent = '<div class="summary">' . Yii::t('yii', 'Total <b>1</b> item.|Showing <b>{begin}-{end}</b> of <b>{totalCount}</b> items.', $totalCount) . '</div>';
$summaryContent = '<div class="summary">' . Yii::t('yii', 'Showing <b>{begin}-{end}</b> of <b>{totalCount}</b> item.|Showing <b>{begin}-{end}</b> of <b>{totalCount}</b> items.', $totalCount) . '</div>';
}
} else {
$begin = $page = $pageCount = 1;

2
framework/yii/widgets/ListView.php

@ -16,7 +16,7 @@ use yii\helpers\Html;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ListView extends ListViewBase
class ListView extends BaseListView
{
/**
* @var array the HTML attributes for the container of the rendering result of each data model.

2
tests/unit/data/ar/Customer.php

@ -1,8 +1,6 @@
<?php
namespace yiiunit\data\ar;
use yii\db\ActiveQuery;
/**
* Class Customer
*

32
tests/unit/data/mysql.sql

@ -109,3 +109,35 @@ INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4,
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0);
/**
* (MySQL-)Database Schema for validator tests
*/
DROP TABLE IF EXISTS tbl_validator_main CASCADE;
DROP TABLE IF EXISTS tbl_validator_ref CASCADE;
CREATE TABLE tbl_validator_main (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`field1` VARCHAR(255),
PRIMARY KEY (`id`)
) ENGINE =InnoDB DEFAULT CHARSET =utf8;
CREATE TABLE tbl_validator_ref (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`a_field` VARCHAR(255),
`ref` INT(11),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO tbl_validator_main (id, field1) VALUES (1, 'just a string1');
INSERT INTO tbl_validator_main (id, field1) VALUES (2, 'just a string2');
INSERT INTO tbl_validator_main (id, field1) VALUES (3, 'just a string3');
INSERT INTO tbl_validator_main (id, field1) VALUES (4, 'just a string4');
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_2', 2);
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_2', 2);
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_3', 3);
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_4', 4);
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_4', 4);
INSERT INTO tbl_validator_ref (a_field, ref) VALUES ('ref_to_5', 5);

29
tests/unit/data/postgres.sql

@ -92,3 +92,32 @@ INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4,
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0);
/**
* (Postgres-)Database Schema for validator tests
*/
DROP TABLE IF EXISTS tbl_validator_main CASCADE;
DROP TABLE IF EXISTS tbl_validator_ref CASCADE;
CREATE TABLE tbl_validator_main (
id integer not null primary key,
field1 VARCHAR(255)
);
CREATE TABLE tbl_validator_ref (
id integer not null primary key,
a_field VARCHAR(255),
ref integer
);
INSERT INTO tbl_validator_main (id, field1) VALUES (1, 'just a string1');
INSERT INTO tbl_validator_main (id, field1) VALUES (2, 'just a string2');
INSERT INTO tbl_validator_main (id, field1) VALUES (3, 'just a string3');
INSERT INTO tbl_validator_main (id, field1) VALUES (4, 'just a string4');
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (1, 'ref_to_2', 2);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (2, 'ref_to_2', 2);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (3, 'ref_to_3', 3);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (4, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (5, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (6, 'ref_to_5', 5);

29
tests/unit/data/sqlite.sql

@ -95,3 +95,32 @@ INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 4,
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 5, 1, 15.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (2, 3, 1, 8.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (3, 2, 1, 40.0);
/**
* (SqLite-)Database Schema for validator tests
*/
DROP TABLE IF EXISTS tbl_validator_main;
DROP TABLE IF EXISTS tbl_validator_ref;
CREATE TABLE tbl_validator_main (
id INTEGER PRIMARY KEY ,
field1 VARCHAR(255)
);
CREATE TABLE tbl_validator_ref (
id INTEGER PRIMARY KEY ,
a_field VARCHAR(255),
ref INT(11)
);
INSERT INTO tbl_validator_main (id, field1) VALUES (1, 'just a string1');
INSERT INTO tbl_validator_main (id, field1) VALUES (2, 'just a string2');
INSERT INTO tbl_validator_main (id, field1) VALUES (3, 'just a string3');
INSERT INTO tbl_validator_main (id, field1) VALUES (4, 'just a string4');
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (1, 'ref_to_2', 2);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (2, 'ref_to_2', 2);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (3, 'ref_to_3', 3);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (4, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (5, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (6, 'ref_to_5', 5);

31
tests/unit/data/travis/cubrid-setup.sh

@ -1,28 +1,19 @@
#!/bin/sh
#
# install CUBRID DBMS https://github.com/CUBRID/node-cubrid/blob/056e734ce36bb3fd25f100c983beb3947e899c1c/.travis.yml
# install CUBRID DBMS
sudo hostname localhost
# Update OS before installing prerequisites.
# cubrid dbms
echo 'yes' | sudo add-apt-repository ppa:cubrid/cubrid
sudo apt-get update
# Install Chef Solo prerequisites.
sudo apt-get install ruby ruby-dev libopenssl-ruby rdoc ri irb build-essential ssl-cert
# Install Chef Solo.
# Chef Solo 11.4.4 is broken, so install a previous version.
# The bug is planned to be fixed in 11.4.5 which haven't been released yet.
sudo gem install --version '<11.4.4' chef --no-rdoc --no-ri
# Make sure the target directory for cookbooks exists.
mkdir -p /tmp/chef-solo
# Prepare a file with runlist for Chef Solo.
echo '{"cubrid":{"version":"'$CUBRID_VERSION'"},"run_list":["cubrid::demodb"]}' > cubrid_chef.json
# Install CUBRID via Chef Solo. Download all cookbooks from a remote URL.
sudo chef-solo -c tests/unit/data/travis/cubrid-solo.rb -j cubrid_chef.json -r http://sourceforge.net/projects/cubrid/files/CUBRID-Demo-Virtual-Machines/Vagrant/chef-cookbooks.tar.gz/download
sudo apt-get install cubrid
/etc/profile.d/cubrid.sh
sudo apt-get install cubrid-demodb
# cubrid pdo
install_pdo_cubrid() {
wget "http://pecl.php.net/get/PDO_CUBRID-9.1.0.0003.tgz" &&
tar -zxf "PDO_CUBRID-9.1.0.0003.tgz" &&
sh -c "cd PDO_CUBRID-9.1.0.0003 && phpize && ./configure && make && sudo make install"
wget "http://pecl.php.net/get/PDO_CUBRID-9.2.0.0001.tgz" &&
tar -zxf "PDO_CUBRID-9.2.0.0001.tgz" &&
sh -c "cd PDO_CUBRID-9.2.0.0001 && phpize && ./configure && make && sudo make install"
echo "extension=pdo_cubrid.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
@ -30,3 +21,5 @@ install_pdo_cubrid() {
}
install_pdo_cubrid > ~/pdo_cubrid.log || ( echo "=== PDO CUBRID BUILD FAILED ==="; cat ~/pdo_cubrid.log )
echo "Installed CUBRID `dpkg -s cubrid |grep Version`"

44
tests/unit/data/validators/TestValidator.php

@ -0,0 +1,44 @@
<?php
namespace yiiunit\data\validators;
use yii\validators\Validator;
class TestValidator extends Validator
{
private $_validatedAttributes = array();
private $_setErrorOnValidateAttribute = false;
public function validateAttribute($object, $attribute)
{
$this->markAttributeValidated($attribute);
if ($this->_setErrorOnValidateAttribute == true) {
$this->addError($object, $attribute, sprintf('%s##%s', $attribute, get_class($object)));
}
}
protected function markAttributeValidated($attr, $increaseBy = 1)
{
if (!isset($this->_validatedAttributes[$attr])) {
$this->_validatedAttributes[$attr] = 1;
} else {
$this->_validatedAttributes[$attr] = $this->_validatedAttributes[$attr] + $increaseBy;
}
}
public function countAttributeValidations($attr)
{
return isset($this->_validatedAttributes[$attr]) ? $this->_validatedAttributes[$attr] : 0;
}
public function isAttributeValidated($attr)
{
return isset($this->_validatedAttributes[$attr]);
}
public function enableErrorOnValidateAttribute()
{
$this->_setErrorOnValidateAttribute = true;
}
}

63
tests/unit/data/validators/models/FakedValidationModel.php

@ -0,0 +1,63 @@
<?php
namespace yiiunit\data\validators\models;
use yii\base\Model;
class FakedValidationModel extends Model
{
public $val_attr_a;
public $val_attr_b;
public $val_attr_c;
public $val_attr_d;
private $attr = array();
/**
* @param array $attributes
* @return self
*/
public static function createWithAttributes($attributes = array())
{
$m = new static();
foreach ($attributes as $attribute => $value) {
$m->$attribute = $value;
}
return $m;
}
public function rules()
{
return array(
array('val_attr_a, val_attr_b', 'required', 'on' => 'reqTest'),
array('val_attr_c', 'integer'),
);
}
public function inlineVal($attribute, $params = array())
{
return true;
}
public function __get($name)
{
if (stripos($name, 'attr') === 0) {
return isset($this->attr[$name]) ? $this->attr[$name] : null;
}
return parent::__get($name);
}
public function __set($name, $value)
{
if (stripos($name, 'attr') === 0) {
$this->attr[$name] = $value;
} else {
parent::__set($name, $value);
}
}
public function getAttributeLabel($attr)
{
return $attr;
}
}

21
tests/unit/data/validators/models/ValidatorTestMainModel.php

@ -0,0 +1,21 @@
<?php
namespace yiiunit\data\validators\models;
use yiiunit\data\ar\ActiveRecord;
class ValidatorTestMainModel extends ActiveRecord
{
public $testMainVal = 1;
public static function tableName()
{
return 'tbl_validator_main';
}
public function getReferences()
{
return $this->hasMany(ValidatorTestRefModel::className(), array('ref' => 'id'));
}
}

23
tests/unit/data/validators/models/ValidatorTestRefModel.php

@ -0,0 +1,23 @@
<?php
namespace yiiunit\data\validators\models;
use yiiunit\data\ar\ActiveRecord;
class ValidatorTestRefModel extends ActiveRecord
{
public $test_val = 2;
public $test_val_fail = 99;
public static function tableName()
{
return 'tbl_validator_ref';
}
public function getMain()
{
return $this->hasOne(ValidatorTestMainModel::className(), array('id' => 'ref'));
}
}

22
tests/unit/data/views/layout.php

@ -0,0 +1,22 @@
<?php
/**
* @var $this \yii\base\View
* @var $content string
*/
?>
<?php $this->beginPage(); ?>
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<?php $this->head(); ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php echo $content; ?>
<?php $this->endBody(); ?>
</body>
</html>
<?php $this->endPage(); ?>

5
tests/unit/data/views/rawlayout.php

@ -0,0 +1,5 @@
<?php
/**
* @var $this \yii\base\View
*/
?><?php $this->beginPage(); ?>1<?php $this->head(); ?>2<?php $this->beginBody(); ?>3<?php $this->endBody(); ?>4<?php $this->endPage(); ?>

1
tests/unit/data/views/simple.php

@ -0,0 +1 @@
This is a damn simple view file.

2
tests/unit/data/web/assets/.gitignore vendored

@ -0,0 +1,2 @@
*
!.gitignore

23
tests/unit/framework/base/ExceptionTest.php

@ -0,0 +1,23 @@
<?php
namespace yiiunit\framework\base;
use yii\test\TestCase;
use yii\base\UserException;
use yii\base\InvalidCallException;
class ExceptionTest extends TestCase
{
public function testToArrayWithPrevious()
{
$e = new InvalidCallException('bar', 0 ,new InvalidCallException('foo'));
$array = $e->toArray();
$this->assertEquals('bar', $array['message']);
$this->assertEquals('foo', $array['previous']['message']);
$e = new InvalidCallException('bar', 0 ,new UserException('foo'));
$array = $e->toArray();
$this->assertEquals('bar', $array['message']);
$this->assertEquals('foo', $array['previous']['message']);
}
}

7
tests/unit/framework/caching/CacheTestCase.php

@ -145,14 +145,9 @@ abstract class CacheTestCase extends TestCase
$cache = $this->getCacheInstance();
$this->assertTrue($cache->set('expire_test', 'expire_test', 2));
sleep(1);
usleep(500000);
$this->assertEquals('expire_test', $cache->get('expire_test'));
// wait a bit more than 2 sec to avoid random test failure
if (isset($_ENV['TRAVIS']) && substr(StringHelper::basename(get_class($this)), 0, 8) == 'MemCache') {
sleep(3); // usleep with 2,5 seconds does not work well on travis and memcache
} else {
usleep(2500000);
}
$this->assertFalse($cache->get('expire_test'));
}

3
tests/unit/framework/data/ActiveDataProviderTest.php

@ -94,8 +94,9 @@ class ActiveDataProviderTest extends DatabaseTestCase
'query' => $query->from('tbl_order')->orderBy('id'),
));
$pagination = $provider->getPagination();
$this->assertEquals(1, $pagination->getPageCount());
$this->assertEquals(0, $pagination->getPageCount());
$this->assertCount(3, $provider->getModels());
$this->assertEquals(1, $pagination->getPageCount());
$provider->getPagination()->pageSize = 2;
$this->assertEquals(3, count($provider->getModels()));

23
tests/unit/framework/db/CommandTest.php

@ -219,29 +219,6 @@ class CommandTest extends DatabaseTestCase
$this->assertTrue(is_array($result) && isset($result[0]));
}
// getPDOType is currently private
// public function testGetPDOType()
// {
// $values = array(
// array(null, \PDO::PARAM_NULL),
// array('', \PDO::PARAM_STR),
// array('hello', \PDO::PARAM_STR),
// array(0, \PDO::PARAM_INT),
// array(1, \PDO::PARAM_INT),
// array(1337, \PDO::PARAM_INT),
// array(true, \PDO::PARAM_BOOL),
// array(false, \PDO::PARAM_BOOL),
// array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB),
// );
//
// $command = $this->getConnection()->createCommand();
//
// foreach($values as $value) {
// $this->assertEquals($value[1], $command->getPdoType($value[0]));
// }
// fclose($fp);
// }
public function testInsert()
{
}

2
tests/unit/framework/db/DatabaseTestCase.php

@ -21,7 +21,7 @@ abstract class DatabaseTestCase extends TestCase
$pdo_database = 'pdo_'.$this->driverName;
if (!extension_loaded('pdo') || !extension_loaded($pdo_database)) {
$this->markTestSkipped('pdo and pdo_'.$pdo_database.' extension are required.');
$this->markTestSkipped('pdo and '.$pdo_database.' extension are required.');
}
$this->mockApplication();
}

23
tests/unit/framework/db/SchemaTest.php

@ -67,4 +67,27 @@ class SchemaTest extends DatabaseTestCase
$this->assertEquals('order_id', $table->foreignKeys[0]['order_id']);
$this->assertEquals('item_id', $table->foreignKeys[0]['item_id']);
}
public function testGetPDOType()
{
$values = array(
array(null, \PDO::PARAM_NULL),
array('', \PDO::PARAM_STR),
array('hello', \PDO::PARAM_STR),
array(0, \PDO::PARAM_INT),
array(1, \PDO::PARAM_INT),
array(1337, \PDO::PARAM_INT),
array(true, \PDO::PARAM_BOOL),
array(false, \PDO::PARAM_BOOL),
array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB),
);
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
foreach($values as $value) {
$this->assertEquals($value[1], $schema->getPdoType($value[0]));
}
fclose($fp);
}
}

6
tests/unit/framework/db/cubrid/CubridActiveRecordTest.php

@ -32,5 +32,11 @@ class CubridActiveRecordTest extends ActiveRecordTest
$customer->refresh();
$this->assertEquals(0, $customer->status);
$customers = Customer::find()->where(array('status' => true))->all();
$this->assertEquals(2, count($customers));
$customers = Customer::find()->where(array('status' => false))->all();
$this->assertEquals(1, count($customers));
}
}

23
tests/unit/framework/db/cubrid/CubridSchemaTest.php

@ -10,4 +10,27 @@ use yiiunit\framework\db\SchemaTest;
class CubridSchemaTest extends SchemaTest
{
public $driverName = 'cubrid';
public function testGetPDOType()
{
$values = array(
array(null, \PDO::PARAM_NULL),
array('', \PDO::PARAM_STR),
array('hello', \PDO::PARAM_STR),
array(0, \PDO::PARAM_INT),
array(1, \PDO::PARAM_INT),
array(1337, \PDO::PARAM_INT),
array(true, \PDO::PARAM_INT),
array(false, \PDO::PARAM_INT),
array($fp=fopen(__FILE__, 'rb'), \PDO::PARAM_LOB),
);
/** @var Schema $schema */
$schema = $this->getConnection()->schema;
foreach($values as $value) {
$this->assertEquals($value[1], $schema->getPdoType($value[0]));
}
fclose($fp);
}
}

4
tests/unit/framework/helpers/JsonTest.php

@ -45,6 +45,10 @@ class JsonTest extends TestCase
'b' => new JsExpression($expression2),
);
$this->assertSame("{\"a\":[1,$expression1],\"b\":$expression2}", Json::encode($data));
// https://github.com/yiisoft/yii2/issues/957
$data = (object)null;
$this->assertSame('{}', Json::encode($data));
}
public function testDecode()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save