Browse Source

Merge branch 'pgsql-driver' of github.com:gevik/yii2 into pgsql-driver

tags/2.0.0-beta
Gevik Babakhani 12 years ago
parent
commit
e4448beccc
  1. 15
      apps/advanced/README.md
  2. 5
      apps/advanced/composer.json
  3. 10
      apps/advanced/composer.lock
  4. 15
      apps/basic/README.md
  5. 5
      apps/basic/composer.json
  6. 10
      apps/basic/composer.lock
  7. 1
      apps/basic/config/main.php
  8. 4
      extensions/smarty/yii/smarty/ViewRenderer.php
  9. 2
      extensions/twig/yii/twig/ViewRenderer.php
  10. 52
      framework/yii/base/Application.php
  11. 16
      framework/yii/base/Formatter.php
  12. 23
      framework/yii/base/Module.php
  13. 8
      framework/yii/bootstrap/Nav.php
  14. 3
      framework/yii/caching/FileCache.php
  15. 69
      framework/yii/console/controllers/AssetController.php
  16. 8
      framework/yii/db/ActiveRecord.php
  17. 10
      framework/yii/db/QueryBuilder.php
  18. 2
      framework/yii/db/mssql/QueryBuilder.php
  19. 2
      framework/yii/db/mysql/QueryBuilder.php
  20. 6
      framework/yii/db/mysql/Schema.php
  21. 9
      framework/yii/db/pgsql/PDO.php
  22. 13
      framework/yii/db/pgsql/QueryBuilder.php
  23. 102
      framework/yii/db/pgsql/Schema.php
  24. 4
      framework/yii/db/sqlite/QueryBuilder.php
  25. 21
      framework/yii/helpers/base/SecurityHelper.php
  26. 58
      framework/yii/i18n/Formatter.php
  27. 2
      framework/yii/logging/Target.php
  28. 2
      framework/yii/validators/DateValidator.php
  29. 2
      framework/yii/web/AccessRule.php
  30. 2
      framework/yii/web/HttpCache.php
  31. 14
      framework/yii/web/Request.php
  32. 32
      framework/yii/web/UrlManager.php
  33. 2
      framework/yii/web/UrlRule.php
  34. 2
      framework/yii/web/VerbFilter.php
  35. 3
      tests/unit/TestCase.php
  36. 2
      tests/unit/data/config.php
  37. 1
      tests/unit/framework/console/controllers/AssetControllerTest.php
  38. 109
      tests/unit/framework/db/QueryBuilderTest.php
  39. 74
      tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php
  40. 54
      tests/unit/framework/web/UrlManagerTest.php

15
apps/advanced/README.md

@ -79,6 +79,21 @@ php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-a
This is not currently available. We will provide it when Yii 2 is formally released. This is not currently available. We will provide it when Yii 2 is formally released.
### Install from development repository
If you've cloned the [Yii 2 framework main development repository](https://github.com/yiisoft/yii2) you
can bootstrap your application with:
~~~
cd yii2/apps/advanced
php composer.phar create-project
~~~
*Note: If the above command fails with `[RuntimeException] Not enough arguments.` run
`php composer.phar self-update` to obtain an updated version of composer which supports creating projects
from local packages.*
GETTING STARTED GETTING STARTED
--------------- ---------------

5
apps/advanced/composer.json

@ -19,10 +19,7 @@
"yiisoft/yii2-composer": "dev-master" "yiisoft/yii2-composer": "dev-master"
}, },
"scripts": { "scripts": {
"post-install-cmd": [ "post-create-project-cmd": [
"yii\\composer\\InstallHandler::setPermissions"
],
"post-update-cmd": [
"yii\\composer\\InstallHandler::setPermissions" "yii\\composer\\InstallHandler::setPermissions"
] ]
}, },

10
apps/advanced/composer.lock generated

@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "05f7bcd0e99931b52415eaeb62d54daf", "hash": "2d1053fbaaf2044054f273a71d0ccde0",
"packages": [ "packages": [
{ {
"name": "yiisoft/yii2", "name": "yiisoft/yii2",
@ -11,12 +11,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/yiisoft/yii2-framework.git", "url": "https://github.com/yiisoft/yii2-framework.git",
"reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d" "reference": "3ad6334be076a80df3b2ea0b57f38bd0c6901989"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/3ad6334be076a80df3b2ea0b57f38bd0c6901989",
"reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "reference": "3ad6334be076a80df3b2ea0b57f38bd0c6901989",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -97,7 +97,7 @@
"framework", "framework",
"yii" "yii"
], ],
"time": "2013-05-29 02:55:14" "time": "2013-06-02 19:19:29"
}, },
{ {
"name": "yiisoft/yii2-composer", "name": "yiisoft/yii2-composer",

15
apps/basic/README.md

@ -59,3 +59,18 @@ assuming `yii-basic` is directly under the document root of your Web server.
### Install from an Archive File ### Install from an Archive File
This is not currently available. We will provide it when Yii 2 is formally released. This is not currently available. We will provide it when Yii 2 is formally released.
### Install from development repository
If you've cloned the [Yii 2 framework main development repository](https://github.com/yiisoft/yii2) you
can bootstrap your application with:
~~~
cd yii2/apps/basic
php composer.phar create-project
~~~
*Note: If the above command fails with `[RuntimeException] Not enough arguments.` run
`php composer.phar self-update` to obtain an updated version of composer which supports creating projects
from local packages.*

5
apps/basic/composer.json

@ -19,10 +19,7 @@
"yiisoft/yii2-composer": "dev-master" "yiisoft/yii2-composer": "dev-master"
}, },
"scripts": { "scripts": {
"post-install-cmd": [ "post-create-project-cmd": [
"yii\\composer\\InstallHandler::setPermissions"
],
"post-update-cmd": [
"yii\\composer\\InstallHandler::setPermissions" "yii\\composer\\InstallHandler::setPermissions"
] ]
}, },

10
apps/basic/composer.lock generated

@ -3,7 +3,7 @@
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "0411dbbd774aa1c89256c77c68023940", "hash": "91ba258de768b93025f86071f3bb4b84",
"packages": [ "packages": [
{ {
"name": "yiisoft/yii2", "name": "yiisoft/yii2",
@ -11,12 +11,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/yiisoft/yii2-framework.git", "url": "https://github.com/yiisoft/yii2-framework.git",
"reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d" "reference": "3ad6334be076a80df3b2ea0b57f38bd0c6901989"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/3ad6334be076a80df3b2ea0b57f38bd0c6901989",
"reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "reference": "3ad6334be076a80df3b2ea0b57f38bd0c6901989",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -97,7 +97,7 @@
"framework", "framework",
"yii" "yii"
], ],
"time": "2013-05-29 02:55:14" "time": "2013-06-02 19:19:29"
}, },
{ {
"name": "yiisoft/yii2-composer", "name": "yiisoft/yii2-composer",

1
apps/basic/config/main.php

@ -4,7 +4,6 @@ return array(
'id' => 'bootstrap', 'id' => 'bootstrap',
'basePath' => dirname(__DIR__), 'basePath' => dirname(__DIR__),
'preload' => array('log'), 'preload' => array('log'),
'controllerNamespace' => 'app\controllers',
'modules' => array( 'modules' => array(
// 'debug' => array( // 'debug' => array(
// 'class' => 'yii\debug\Module', // 'class' => 'yii\debug\Module',

4
extensions/smarty/yii/smarty/ViewRenderer.php

@ -26,12 +26,12 @@ class ViewRenderer extends BaseViewRenderer
/** /**
* @var string the directory or path alias pointing to where Smarty cache will be stored. * @var string the directory or path alias pointing to where Smarty cache will be stored.
*/ */
public $cachePath = '@app/runtime/Smarty/cache'; public $cachePath = '@runtime/Smarty/cache';
/** /**
* @var string the directory or path alias pointing to where Smarty compiled templates will be stored. * @var string the directory or path alias pointing to where Smarty compiled templates will be stored.
*/ */
public $compilePath = '@app/runtime/Smarty/compile'; public $compilePath = '@runtime/Smarty/compile';
/** /**
* @var Smarty * @var Smarty

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

@ -25,7 +25,7 @@ class ViewRenderer extends BaseViewRenderer
/** /**
* @var string the directory or path alias pointing to where Twig cache will be stored. * @var string the directory or path alias pointing to where Twig cache will be stored.
*/ */
public $cachePath = '@app/runtime/Twig/cache'; public $cachePath = '@runtime/Twig/cache';
/** /**
* @var array Twig options * @var array Twig options

52
framework/yii/base/Application.php

@ -72,30 +72,51 @@ class Application extends Module
public function __construct($config = array()) public function __construct($config = array())
{ {
Yii::$app = $this; Yii::$app = $this;
if (!isset($config['id'])) { if (!isset($config['id'])) {
throw new InvalidConfigException('The "id" configuration is required.'); throw new InvalidConfigException('The "id" configuration is required.');
} }
if (isset($config['basePath'])) { if (isset($config['basePath'])) {
$this->setBasePath($config['basePath']); $this->setBasePath($config['basePath']);
Yii::setAlias('@app', $this->getBasePath());
unset($config['basePath']); unset($config['basePath']);
} else { } else {
throw new InvalidConfigException('The "basePath" configuration is required.'); throw new InvalidConfigException('The "basePath" configuration is required.');
} }
$this->preInit($config);
$this->registerErrorHandlers();
$this->registerCoreComponents();
Component::__construct($config);
}
/**
* Pre-initializes the application.
* This method is called at the beginning of the application constructor.
* @param array $config the application configuration
*/
public function preInit(&$config)
{
if (isset($config['vendorPath'])) {
$this->setVendorPath($config['vendorPath']);
unset($config['vendorPath']);
} else {
// set "@vendor"
$this->getVendorPath();
}
if (isset($config['runtimePath'])) {
$this->setRuntimePath($config['runtimePath']);
unset($config['runtimePath']);
} else {
// set "@runtime"
$this->getRuntimePath();
}
if (isset($config['timeZone'])) { if (isset($config['timeZone'])) {
$this->setTimeZone($config['timeZone']); $this->setTimeZone($config['timeZone']);
unset($config['timeZone']); unset($config['timeZone']);
} elseif (!ini_get('date.timezone')) { } elseif (!ini_get('date.timezone')) {
$this->setTimeZone('UTC'); $this->setTimeZone('UTC');
} }
$this->registerErrorHandlers();
$this->registerCoreComponents();
Component::__construct($config);
} }
/** /**
@ -178,7 +199,8 @@ class Application extends Module
/** /**
* Returns the directory that stores runtime files. * Returns the directory that stores runtime files.
* @return string the directory that stores runtime files. Defaults to 'protected/runtime'. * @return string the directory that stores runtime files.
* Defaults to the "runtime" subdirectory under [[basePath]].
*/ */
public function getRuntimePath() public function getRuntimePath()
{ {
@ -191,16 +213,11 @@ class Application extends Module
/** /**
* Sets the directory that stores runtime files. * Sets the directory that stores runtime files.
* @param string $path the directory that stores runtime files. * @param string $path the directory that stores runtime files.
* @throws InvalidConfigException if the directory does not exist or is not writable
*/ */
public function setRuntimePath($path) public function setRuntimePath($path)
{ {
$path = Yii::getAlias($path); $this->_runtimePath = Yii::getAlias($path);
if (is_dir($path) && is_writable($path)) { Yii::setAlias('@runtime', $this->_runtimePath);
$this->_runtimePath = $path;
} else {
throw new InvalidConfigException("Runtime path must be a directory writable by the Web server process: $path");
}
} }
private $_vendorPath; private $_vendorPath;
@ -208,7 +225,7 @@ class Application extends Module
/** /**
* Returns the directory that stores vendor files. * Returns the directory that stores vendor files.
* @return string the directory that stores vendor files. * @return string the directory that stores vendor files.
* Defaults to 'vendor' directory under applications [[basePath]]. * Defaults to "vendor" directory under [[basePath]].
*/ */
public function getVendorPath() public function getVendorPath()
{ {
@ -225,6 +242,7 @@ class Application extends Module
public function setVendorPath($path) public function setVendorPath($path)
{ {
$this->_vendorPath = Yii::getAlias($path); $this->_vendorPath = Yii::getAlias($path);
Yii::setAlias('@vendor', $this->_vendorPath);
} }
/** /**

16
framework/yii/base/Formatter.php

@ -39,17 +39,19 @@ class Formatter extends Component
public $datetimeFormat = 'Y/m/d h:i:s A'; public $datetimeFormat = 'Y/m/d h:i:s A';
/** /**
* @var array the text to be displayed when formatting a boolean value. The first element corresponds * @var array the text to be displayed when formatting a boolean value. The first element corresponds
* to the text display for false, the second element for true. Defaults to <code>array('No', 'Yes')</code>. * to the text display for false, the second element for true. Defaults to `array('No', 'Yes')`.
*/ */
public $booleanFormat; public $booleanFormat;
/** /**
* @var string the character displayed as the decimal point when formatting a number. * @var string the character displayed as the decimal point when formatting a number.
* If not set, "." will be used.
*/ */
public $decimalSeparator = '.'; public $decimalSeparator;
/** /**
* @var string the character displayed as the thousands separator character when formatting a number. * @var string the character displayed as the thousands separator character when formatting a number.
* If not set, "," will be used.
*/ */
public $thousandSeparator = ','; public $thousandSeparator;
/** /**
@ -273,8 +275,12 @@ class Formatter extends Component
*/ */
public function asDouble($value, $decimals = 2) public function asDouble($value, $decimals = 2)
{ {
if ($this->decimalSeparator === null) {
return sprintf("%.{$decimals}f", $value);
} else {
return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value)); return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value));
} }
}
/** /**
* Formats the value as a number with decimal and thousand separators. * Formats the value as a number with decimal and thousand separators.
@ -287,6 +293,8 @@ class Formatter extends Component
*/ */
public function asNumber($value, $decimals = 0) public function asNumber($value, $decimals = 0)
{ {
return number_format($value, $decimals, $this->decimalSeparator, $this->thousandSeparator); $ds = isset($this->decimalSeparator) ? $this->decimalSeparator: '.';
$ts = isset($this->thousandSeparator) ? $this->thousandSeparator: ',';
return number_format($value, $decimals, $ds, $ts);
} }
} }

23
framework/yii/base/Module.php

@ -88,7 +88,11 @@ abstract class Module extends Component
*/ */
public $controllerMap = array(); public $controllerMap = array();
/** /**
* @var string the namespace that controller classes are in. Default is to use global namespace. * @var string the namespace that controller classes are in. If not set,
* it will use the "controllers" sub-namespace under the namespace of this module.
* For example, if the namespace of this module is "foo\bar", then the default
* controller namespace would be "foo\bar\controllers".
* If the module is an application, it will default to "app\controllers".
*/ */
public $controllerNamespace; public $controllerNamespace;
/** /**
@ -178,6 +182,16 @@ abstract class Module extends Component
public function init() public function init()
{ {
$this->preloadComponents(); $this->preloadComponents();
if ($this->controllerNamespace === null) {
if ($this instanceof Application) {
$this->controllerNamespace = 'app\\controllers';
} else {
$class = get_class($this);
if (($pos = strrpos($class, '\\')) !== false) {
$this->controllerNamespace = substr($class, 0, $pos) . '\\controllers';
}
}
}
} }
/** /**
@ -222,6 +236,9 @@ abstract class Module extends Component
$p = realpath($path); $p = realpath($path);
if ($p !== false && is_dir($p)) { if ($p !== false && is_dir($p)) {
$this->_basePath = $p; $this->_basePath = $p;
if ($this instanceof Application) {
Yii::setAlias('@app', $p);
}
} else { } else {
throw new InvalidParamException("The directory does not exist: $path"); throw new InvalidParamException("The directory does not exist: $path");
} }
@ -409,11 +426,11 @@ abstract class Module extends Component
* ~~~ * ~~~
* array( * array(
* 'comment' => array( * 'comment' => array(
* 'class' => 'app\modules\CommentModule', * 'class' => 'app\modules\comment\CommentModule',
* 'db' => 'db', * 'db' => 'db',
* ), * ),
* 'booking' => array( * 'booking' => array(
* 'class' => 'app\modules\BookingModule', * 'class' => 'app\modules\booking\BookingModule',
* ), * ),
* ) * )
* ~~~ * ~~~

8
framework/yii/bootstrap/Nav.php

@ -27,7 +27,7 @@ use yii\helpers\Html;
* ), * ),
* array( * array(
* 'label' => 'Dropdown', * 'label' => 'Dropdown',
* 'items' => array( * 'dropdown' => array(
* array( * array(
* 'label' => 'DropdownA', * 'label' => 'DropdownA',
* 'url' => '#', * 'url' => '#',
@ -128,8 +128,10 @@ class Nav extends Widget
$this->addCssClass($urlOptions, 'dropdown-toggle'); $this->addCssClass($urlOptions, 'dropdown-toggle');
$label .= ' ' . Html::tag('b', '', array('class' => 'caret')); $label .= ' ' . Html::tag('b', '', array('class' => 'caret'));
if (is_array($dropdown)) { if (is_array($dropdown)) {
$dropdown['clientOptions'] = false; $dropdown = Dropdown::widget(array(
$dropdown = Dropdown::widget($dropdown); 'items' => $dropdown,
'clientOptions' => false,
));
} }
} }

3
framework/yii/caching/FileCache.php

@ -25,8 +25,9 @@ class FileCache extends Cache
{ {
/** /**
* @var string the directory to store cache files. You may use path alias here. * @var string the directory to store cache files. You may use path alias here.
* If not set, it will use the "cache" subdirectory under the application runtime path.
*/ */
public $cachePath = '@app/runtime/cache'; public $cachePath = '@runtime/cache';
/** /**
* @var string cache file suffix. Defaults to '.bin'. * @var string cache file suffix. Defaults to '.bin'.
*/ */

69
framework/yii/console/controllers/AssetController.php

@ -14,6 +14,20 @@ use yii\console\Controller;
/** /**
* This command allows you to combine and compress your JavaScript and CSS files. * This command allows you to combine and compress your JavaScript and CSS files.
* *
* Usage:
* 1. Create a configuration file using 'template' action:
* yii asset/template /path/to/myapp/config.php
* 2. Edit the created config file, adjusting it for your web application needs.
* 3. Run the 'compress' action, using created config:
* yii asset /path/to/myapp/config.php /path/to/myapp/config/assets_compressed.php
* 4. Adjust your web application config to use compressed assets.
*
* Note: in the console environment some path aliases like '@wwwroot' and '@www' may not exist,
* so corresponding paths inside the configuration should be specified directly.
*
* Note: by default this command relies on an external tools to perform actual files compression,
* check [[jsCompressor]] and [[cssCompressor]] for more details.
*
* @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing. * @property array|\yii\web\AssetManager $assetManager asset manager, which will be used for assets processing.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -43,7 +57,7 @@ class AssetController extends Controller
* ~~~ * ~~~
* 'all' => array( * 'all' => array(
* 'css' => 'all.css', * 'css' => 'all.css',
* 'js' => 'js.css', * 'js' => 'all.js',
* 'depends' => array( ... ), * 'depends' => array( ... ),
* ) * )
* ~~~ * ~~~
@ -57,7 +71,7 @@ class AssetController extends Controller
*/ */
private $_assetManager = array(); private $_assetManager = array();
/** /**
* @var string|callback Java Script file compressor. * @var string|callback JavaScript file compressor.
* If a string, it is treated as shell command template, which should contain * If a string, it is treated as shell command template, which should contain
* placeholders {from} - source file name - and {to} - output file name. * placeholders {from} - source file name - and {to} - output file name.
* Otherwise, it is treated as PHP callback, which should perform the compression. * Otherwise, it is treated as PHP callback, which should perform the compression.
@ -159,7 +173,7 @@ class AssetController extends Controller
} }
} }
$this->getAssetManager(); // check asset manager configuration $this->getAssetManager(); // check if asset manager configuration is correct
} }
/** /**
@ -308,7 +322,7 @@ class AssetController extends Controller
/** /**
* Builds output asset bundle. * Builds output asset bundle.
* @param \yii\web\AssetBundle $target output asset bundle * @param \yii\web\AssetBundle $target output asset bundle
* @param string $type either "js" or "css". * @param string $type either 'js' or 'css'.
* @param \yii\web\AssetBundle[] $bundles source asset bundles. * @param \yii\web\AssetBundle[] $bundles source asset bundles.
* @param integer $timestamp current timestamp. * @param integer $timestamp current timestamp.
* @throws Exception on failure. * @throws Exception on failure.
@ -420,24 +434,23 @@ class AssetController extends Controller
} }
$array = var_export($array, true); $array = var_export($array, true);
$version = date('Y-m-d H:i:s', time()); $version = date('Y-m-d H:i:s', time());
$bytesWritten = file_put_contents($bundleFile, <<<EOD $bundleFileContent = <<<EOD
<?php <?php
/** /**
* This file is generated by the "yii script" command. * This file is generated by the "yii {$this->id}" command.
* DO NOT MODIFY THIS FILE DIRECTLY. * DO NOT MODIFY THIS FILE DIRECTLY.
* @version $version * @version {$version}
*/ */
return $array; return {$array};
EOD EOD;
); if (!file_put_contents($bundleFile, $bundleFileContent)) {
if ($bytesWritten <= 0) {
throw new Exception("Unable to write output bundle configuration at '{$bundleFile}'."); throw new Exception("Unable to write output bundle configuration at '{$bundleFile}'.");
} }
echo "Output bundle configuration created at '{$bundleFile}'.\n"; echo "Output bundle configuration created at '{$bundleFile}'.\n";
} }
/** /**
* Compresses given Java Script files and combines them into the single one. * Compresses given JavaScript files and combines them into the single one.
* @param array $inputFiles list of source file names. * @param array $inputFiles list of source file names.
* @param string $outputFile output file name. * @param string $outputFile output file name.
* @throws \yii\console\Exception on failure * @throws \yii\console\Exception on failure
@ -495,9 +508,10 @@ EOD
} }
/** /**
* Combines Java Script files into a single one. * Combines JavaScript files into a single one.
* @param array $inputFiles source file names. * @param array $inputFiles source file names.
* @param string $outputFile output file name. * @param string $outputFile output file name.
* @throws \yii\console\Exception on failure.
*/ */
public function combineJsFiles($inputFiles, $outputFile) public function combineJsFiles($inputFiles, $outputFile)
{ {
@ -507,13 +521,16 @@ EOD
. file_get_contents($file) . file_get_contents($file)
. "/*** END FILE: $file ***/\n"; . "/*** END FILE: $file ***/\n";
} }
file_put_contents($outputFile, $content); if (!file_put_contents($outputFile, $content)) {
throw new Exception("Unable to write output JavaScript file '{$outputFile}'.");
}
} }
/** /**
* Combines CSS files into a single one. * Combines CSS files into a single one.
* @param array $inputFiles source file names. * @param array $inputFiles source file names.
* @param string $outputFile output file name. * @param string $outputFile output file name.
* @throws \yii\console\Exception on failure.
*/ */
public function combineCssFiles($inputFiles, $outputFile) public function combineCssFiles($inputFiles, $outputFile)
{ {
@ -523,7 +540,9 @@ EOD
. $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile)) . $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile))
. "/*** END FILE: $file ***/\n"; . "/*** END FILE: $file ***/\n";
} }
file_put_contents($outputFile, $content); if (!file_put_contents($outputFile, $content)) {
throw new Exception("Unable to write output CSS file '{$outputFile}'.");
}
} }
/** /**
@ -590,18 +609,23 @@ EOD
/** /**
* Creates template of configuration file for [[actionCompress]]. * Creates template of configuration file for [[actionCompress]].
* @param string $configFile output file name. * @param string $configFile output file name.
* @throws \yii\console\Exception on failure.
*/ */
public function actionTemplate($configFile) public function actionTemplate($configFile)
{ {
$template = <<<EOD $template = <<<EOD
<?php <?php
/**
* Configuration file for the "yii asset" console command.
* Note: in the console environment some path aliases like '@wwwroot' and '@www' may not exist,
* so corresponding paths should be specified directly.
*/
return array( return array(
// // The list of asset bundles to compress:
'bundles' => require('path/to/bundles.php'), 'bundles' => require('path/to/bundles.php'),
// // The list of extensions to compress:
'extensions' => require('path/to/namespaces.php'), 'extensions' => require('path/to/namespaces.php'),
// // Asset bundle for compression output:
'targets' => array( 'targets' => array(
'all' => array( 'all' => array(
'basePath' => __DIR__, 'basePath' => __DIR__,
@ -610,7 +634,7 @@ return array(
'css' => 'all-{ts}.css', 'css' => 'all-{ts}.css',
), ),
), ),
// Asset manager configuration:
'assetManager' => array( 'assetManager' => array(
'basePath' => __DIR__, 'basePath' => __DIR__,
'baseUrl' => '/test', 'baseUrl' => '/test',
@ -622,9 +646,8 @@ EOD;
return; return;
} }
} }
$bytesWritten = file_put_contents($configFile, $template); if (!file_put_contents($configFile, $template)) {
if ($bytesWritten<=0) { throw new Exception("Unable to write template file '{$configFile}'.");
echo "Error: unable to write file '{$configFile}'!\n\n";
} else { } else {
echo "Configuration file template created at '{$configFile}'.\n\n"; echo "Configuration file template created at '{$configFile}'.\n\n";
} }

8
framework/yii/db/ActiveRecord.php

@ -275,10 +275,16 @@ class ActiveRecord extends Model
/** /**
* Returns the schema information of the DB table associated with this AR class. * Returns the schema information of the DB table associated with this AR class.
* @return TableSchema the schema information of the DB table associated with this AR class. * @return TableSchema the schema information of the DB table associated with this AR class.
* @throws InvalidConfigException if the table for the AR class does not exist.
*/ */
public static function getTableSchema() public static function getTableSchema()
{ {
return static::getDb()->getTableSchema(static::tableName()); $schema = static::getDb()->getTableSchema(static::tableName());
if ($schema !== null) {
return $schema;
} else {
throw new InvalidConfigException("The table does not exist: " . static::tableName());
}
} }
/** /**

10
framework/yii/db/QueryBuilder.php

@ -464,6 +464,12 @@ class QueryBuilder extends \yii\base\Object
* the first part will be converted, and the rest of the parts will be appended to the converted result. * the first part will be converted, and the rest of the parts will be appended to the converted result.
* For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'. * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
* *
* For some of the abstract types you can also specify a length or precision constraint
* by prepending it in round brackets directly to the type.
* For example `string(32)` will be converted into "varchar(32)" on a MySQL database.
* If the underlying DBMS does not support these kind of constraints for a type it will
* be ignored.
*
* If a type cannot be found in [[typeMap]], it will be returned without any change. * If a type cannot be found in [[typeMap]], it will be returned without any change.
* @param string $type abstract column type * @param string $type abstract column type
* @return string physical column type. * @return string physical column type.
@ -472,6 +478,10 @@ class QueryBuilder extends \yii\base\Object
{ {
if (isset($this->typeMap[$type])) { if (isset($this->typeMap[$type])) {
return $this->typeMap[$type]; return $this->typeMap[$type];
} elseif (preg_match('/^(\w+)\((.+?)\)(.*)$/', $type, $matches)) {
if (isset($this->typeMap[$matches[1]])) {
return preg_replace('/\(.+\)/', '(' . $matches[2] . ')', $this->typeMap[$matches[1]]) . $matches[3];
}
} elseif (preg_match('/^(\w+)\s+/', $type, $matches)) { } elseif (preg_match('/^(\w+)\s+/', $type, $matches)) {
if (isset($this->typeMap[$matches[1]])) { if (isset($this->typeMap[$matches[1]])) {
return preg_replace('/^\w+/', $this->typeMap[$matches[1]], $type); return preg_replace('/^\w+/', $this->typeMap[$matches[1]], $type);

2
framework/yii/db/mssql/QueryBuilder.php

@ -28,7 +28,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_INTEGER => 'int(11)', Schema::TYPE_INTEGER => 'int(11)',
Schema::TYPE_BIGINT => 'bigint(20)', Schema::TYPE_BIGINT => 'bigint(20)',
Schema::TYPE_FLOAT => 'float', Schema::TYPE_FLOAT => 'float',
Schema::TYPE_DECIMAL => 'decimal', Schema::TYPE_DECIMAL => 'decimal(10,0)',
Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time', Schema::TYPE_TIME => 'time',

2
framework/yii/db/mysql/QueryBuilder.php

@ -29,7 +29,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_INTEGER => 'int(11)', Schema::TYPE_INTEGER => 'int(11)',
Schema::TYPE_BIGINT => 'bigint(20)', Schema::TYPE_BIGINT => 'bigint(20)',
Schema::TYPE_FLOAT => 'float', Schema::TYPE_FLOAT => 'float',
Schema::TYPE_DECIMAL => 'decimal', Schema::TYPE_DECIMAL => 'decimal(10,0)',
Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time', Schema::TYPE_TIME => 'time',

6
framework/yii/db/mysql/Schema.php

@ -178,6 +178,7 @@ class Schema extends \yii\db\Schema
* Collects the metadata of table columns. * Collects the metadata of table columns.
* @param TableSchema $table the table metadata * @param TableSchema $table the table metadata
* @return boolean whether the table exists in the database * @return boolean whether the table exists in the database
* @throws \Exception if DB query fails
*/ */
protected function findColumns($table) protected function findColumns($table)
{ {
@ -185,8 +186,13 @@ class Schema extends \yii\db\Schema
try { try {
$columns = $this->db->createCommand($sql)->queryAll(); $columns = $this->db->createCommand($sql)->queryAll();
} catch (\Exception $e) { } catch (\Exception $e) {
$previous = $e->getPrevious();
if ($previous instanceof \PDOException && $previous->getCode() == '42S02') {
// table does not exist
return false; return false;
} }
throw $e;
}
foreach ($columns as $info) { foreach ($columns as $info) {
$column = $this->loadColumnSchema($info); $column = $this->loadColumnSchema($info);
$table->columns[$column->name] = $column; $table->columns[$column->name] = $column;

9
framework/yii/db/pgsql/PDO.php

@ -16,13 +16,14 @@ namespace yii\db\pgsql;
* @author Gevik babakhani <gevikb@gmail.com> * @author Gevik babakhani <gevikb@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class PDO extends \PDO { class PDO extends \PDO
{
const OPT_SEARCH_PATH = 'search_path'; const OPT_SEARCH_PATH = 'search_path';
const OPT_DEFAULT_SCHEMA = 'default_schema'; const OPT_DEFAULT_SCHEMA = 'default_schema';
const DEFAULT_SCHEMA = 'public'; const DEFAULT_SCHEMA = 'public';
private $_currentDatabase = null; private $_currentDatabase;
/** /**
* Returns value of the last inserted ID. * Returns value of the last inserted ID.
@ -53,11 +54,11 @@ class PDO extends \PDO {
} }
if (isset($options[self::OPT_DEFAULT_SCHEMA])) { if (isset($options[self::OPT_DEFAULT_SCHEMA])) {
$schema = trim($options[self::OPT_DEFAULT_SCHEMA]); $schema = trim($options[self::OPT_DEFAULT_SCHEMA]);
if ($schema !== '') { if (!empty($schema)) {
Schema::$DEFAULT_SCHEMA = $schema; Schema::$DEFAULT_SCHEMA = $schema;
} }
} }
if (Schema::$DEFAULT_SCHEMA === null || Schema::$DEFAULT_SCHEMA === '') { if (is_null(Schema::$DEFAULT_SCHEMA) || empty(Schema::$DEFAULT_SCHEMA)) {
Schema::$DEFAULT_SCHEMA = self::DEFAULT_SCHEMA; Schema::$DEFAULT_SCHEMA = self::DEFAULT_SCHEMA;
} }
} }

13
framework/yii/db/pgsql/QueryBuilder.php

@ -8,23 +8,21 @@
namespace yii\db\pgsql; namespace yii\db\pgsql;
use yii\db\Exception;
use yii\base\InvalidParamException;
/** /**
* QueryBuilder is the query builder for PostgreSQL databases. * QueryBuilder is the query builder for PostgreSQL databases.
* *
* @author Gevik Babakhani <gevikb@gmail.com> * @author Gevik Babakhani <gevikb@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class QueryBuilder extends \yii\db\QueryBuilder { class QueryBuilder extends \yii\db\QueryBuilder
{
/** /**
* @var array mapping from abstract column types (keys) to physical column types (values). * @var array mapping from abstract column types (keys) to physical column types (values).
*/ */
public $typeMap = array( public $typeMap = array(
Schema::TYPE_PK => 'bigserial not null primary key', Schema::TYPE_PK => 'bigserial not null primary key',
Schema::TYPE_STRING => 'varchar(255)', Schema::TYPE_STRING => 'varchar',
Schema::TYPE_TEXT => 'text', Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint', Schema::TYPE_SMALLINT => 'smallint',
Schema::TYPE_INTEGER => 'integer', Schema::TYPE_INTEGER => 'integer',
@ -40,9 +38,4 @@ class QueryBuilder extends \yii\db\QueryBuilder {
Schema::TYPE_MONEY => 'numeric(19,4)', Schema::TYPE_MONEY => 'numeric(19,4)',
); );
public function insert($table, $columns, &$params) {
$sql = parent::insert($table, $columns, $params);
return $sql . ' RETURNING *';
}
} }

102
framework/yii/db/pgsql/Schema.php

@ -18,7 +18,8 @@ use yii\db\ColumnSchema;
* @author Gevik Babakhani <gevikb@gmail.com> * @author Gevik Babakhani <gevikb@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Schema extends \yii\db\Schema { class Schema extends \yii\db\Schema
{
/** /**
* The default schema used for the current session. This value is * The default schema used for the current session. This value is
@ -33,31 +34,24 @@ class Schema extends \yii\db\Schema {
*/ */
public $typeMap = array( public $typeMap = array(
'abstime' => self::TYPE_TIMESTAMP, 'abstime' => self::TYPE_TIMESTAMP,
//'aclitem' => self::TYPE_STRING,
'bit' => self::TYPE_STRING, 'bit' => self::TYPE_STRING,
'boolean' => self::TYPE_BOOLEAN, 'boolean' => self::TYPE_BOOLEAN,
'box' => self::TYPE_STRING, 'box' => self::TYPE_STRING,
'character' => self::TYPE_STRING, 'character' => self::TYPE_STRING,
'bytea' => self::TYPE_BINARY, 'bytea' => self::TYPE_BINARY,
'char' => self::TYPE_STRING, 'char' => self::TYPE_STRING,
//'cid' => self::TYPE_STRING,
'cidr' => self::TYPE_STRING, 'cidr' => self::TYPE_STRING,
'circle' => self::TYPE_STRING, 'circle' => self::TYPE_STRING,
'date' => self::TYPE_DATE, 'date' => self::TYPE_DATE,
//'daterange' => self::TYPE_STRING,
'real' => self::TYPE_FLOAT, 'real' => self::TYPE_FLOAT,
'double precision' => self::TYPE_DECIMAL, 'double precision' => self::TYPE_DECIMAL,
//'gtsvector' => self::TYPE_STRING,
'inet' => self::TYPE_STRING, 'inet' => self::TYPE_STRING,
'smallint' => self::TYPE_SMALLINT, 'smallint' => self::TYPE_SMALLINT,
'integer' => self::TYPE_INTEGER, 'integer' => self::TYPE_INTEGER,
//'int4range' => self::TYPE_STRING, //unknown
'bigint' => self::TYPE_BIGINT, 'bigint' => self::TYPE_BIGINT,
//'int8range' => self::TYPE_STRING, // unknown
'interval' => self::TYPE_STRING, 'interval' => self::TYPE_STRING,
'json' => self::TYPE_STRING, 'json' => self::TYPE_STRING,
'line' => self::TYPE_STRING, 'line' => self::TYPE_STRING,
//'lseg' => self::TYPE_STRING,
'macaddr' => self::TYPE_STRING, 'macaddr' => self::TYPE_STRING,
'money' => self::TYPE_MONEY, 'money' => self::TYPE_MONEY,
'name' => self::TYPE_STRING, 'name' => self::TYPE_STRING,
@ -65,38 +59,49 @@ class Schema extends \yii\db\Schema {
'numrange' => self::TYPE_DECIMAL, 'numrange' => self::TYPE_DECIMAL,
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal! 'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal!
'path' => self::TYPE_STRING, 'path' => self::TYPE_STRING,
//'pg_node_tree' => self::TYPE_STRING,
'point' => self::TYPE_STRING, 'point' => self::TYPE_STRING,
'polygon' => self::TYPE_STRING, 'polygon' => self::TYPE_STRING,
//'refcursor' => self::TYPE_STRING,
//'regclass' => self::TYPE_STRING,
//'regconfig' => self::TYPE_STRING,
//'regdictionary' => self::TYPE_STRING,
//'regoper' => self::TYPE_STRING,
//'regoperator' => self::TYPE_STRING,
//'regproc' => self::TYPE_STRING,
//'regprocedure' => self::TYPE_STRING,
//'regtype' => self::TYPE_STRING,
//'reltime' => self::TYPE_STRING,
//'smgr' => self::TYPE_STRING,
'text' => self::TYPE_TEXT, 'text' => self::TYPE_TEXT,
//'tid' => self::TYPE_STRING,
'time without time zone' => self::TYPE_TIME, 'time without time zone' => self::TYPE_TIME,
'timestamp without time zone' => self::TYPE_TIMESTAMP, 'timestamp without time zone' => self::TYPE_TIMESTAMP,
'timestamp with time zone' => self::TYPE_TIMESTAMP, 'timestamp with time zone' => self::TYPE_TIMESTAMP,
'time with time zone' => self::TYPE_TIMESTAMP, 'time with time zone' => self::TYPE_TIMESTAMP,
//'tinterval' => self::TYPE_STRING,
//'tsquery' => self::TYPE_STRING,
//'tsrange' => self::TYPE_STRING,
//'tstzrange' => self::TYPE_STRING,
//'tsvector' => self::TYPE_STRING,
//'txid_snapshot' => self::TYPE_STRING,
'unknown' => self::TYPE_STRING, 'unknown' => self::TYPE_STRING,
'uuid' => self::TYPE_STRING, 'uuid' => self::TYPE_STRING,
'bit varying' => self::TYPE_STRING, 'bit varying' => self::TYPE_STRING,
'character varying' => self::TYPE_STRING, 'character varying' => self::TYPE_STRING,
//'xid' => self::TYPE_STRING,
'xml' => self::TYPE_STRING 'xml' => self::TYPE_STRING
/*
* internal PG types
* 'aclitem' => self::TYPE_STRING,
* 'cid' => self::TYPE_STRING,
* 'daterange' => self::TYPE_STRING,
* 'gtsvector' => self::TYPE_STRING,
* 'int4range' => self::TYPE_STRING, //unknown
* 'lseg' => self::TYPE_STRING,
* 'int8range' => self::TYPE_STRING, // unknown
* 'pg_node_tree' => self::TYPE_STRING,
* 'refcursor' => self::TYPE_STRING,
* 'regclass' => self::TYPE_STRING,
* 'regconfig' => self::TYPE_STRING,
* 'regdictionary' => self::TYPE_STRING,
* 'regoper' => self::TYPE_STRING,
* 'regoperator' => self::TYPE_STRING,
* 'regproc' => self::TYPE_STRING,
* 'regprocedure' => self::TYPE_STRING,
* 'regtype' => self::TYPE_STRING,
* 'reltime' => self::TYPE_STRING,
* 'smgr' => self::TYPE_STRING,
* 'tid' => self::TYPE_STRING,
* 'xid' => self::TYPE_STRING,
* 'tinterval' => self::TYPE_STRING,
* 'tsquery' => self::TYPE_STRING,
* 'tsrange' => self::TYPE_STRING,
* 'tstzrange' => self::TYPE_STRING,
* 'tsvector' => self::TYPE_STRING,
* 'txid_snapshot' => self::TYPE_STRING
*/
); );
/** /**
@ -155,14 +160,51 @@ class Schema extends \yii\db\Schema {
*/ */
protected function findConstraints($table) { protected function findConstraints($table) {
$tableName = $this->quoteValue($table->name);
$tableSchema = $this->quoteValue($table->schemaName);
$database = $this->quoteValue($this->db->pdo->getCurrentDatabase());
//We need to extract the constraints de hard way since:
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us
$sql = <<<SQL
select
ct.conname as containst,
c.relname as table_name,
ns.nspname as table_schema,
current_database() as table_catalog,
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.conrelid and attnum = any(ct.conkey)) as columns,
fc.relname as foreign_table_name,
fns.nspname as foreign_table_schema,
current_database() as foreign_table_catalog,
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.confrelid and attnum = any(ct.confkey)) as foreign_columns
from
pg_constraint ct
inner join pg_class c on c.oid=ct.conrelid
inner join pg_namespace ns on c.relnamespace=ns.oid
left join pg_class fc on fc.oid=ct.confrelid
left join pg_namespace fns on fc.relnamespace=fns.oid
where
ct.contype='f'
and c.relname={$tableName}
and ns.nspname={$tableSchema}
and current_database() = {$database}
SQL;
try { try {
$constraints = $this->db->createCommand($sql)->queryAll(); $constraints = $this->db->createCommand($sql)->queryAll();
} catch (\Exception $e) { } catch (\Exception $e) {
return false; return false;
} }
foreach ($constraints as $constraint) { foreach ($constraints as $constraint) {
$column = $this->loadColumnSchema($column); $columns = explode(',', $constraint['columns']);
$table->columns[$column->name] = $column; $fcolumns = explode(',', $constraint['foreign_columns']);
$citem = array($constraint['foreign_table_name']);
foreach ($columns as $idx => $column) {
$citem[] = array($fcolumns[$idx] => $column);
}
$table->foreignKeys[] = $citem;
} }
return true; return true;
} }

4
framework/yii/db/sqlite/QueryBuilder.php

@ -30,13 +30,13 @@ class QueryBuilder extends \yii\db\QueryBuilder
Schema::TYPE_INTEGER => 'integer', Schema::TYPE_INTEGER => 'integer',
Schema::TYPE_BIGINT => 'bigint', Schema::TYPE_BIGINT => 'bigint',
Schema::TYPE_FLOAT => 'float', Schema::TYPE_FLOAT => 'float',
Schema::TYPE_DECIMAL => 'decimal', Schema::TYPE_DECIMAL => 'decimal(10,0)',
Schema::TYPE_DATETIME => 'datetime', Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp', Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time', Schema::TYPE_TIME => 'time',
Schema::TYPE_DATE => 'date', Schema::TYPE_DATE => 'date',
Schema::TYPE_BINARY => 'blob', Schema::TYPE_BINARY => 'blob',
Schema::TYPE_BOOLEAN => 'tinyint(1)', Schema::TYPE_BOOLEAN => 'boolean',
Schema::TYPE_MONEY => 'decimal(19,4)', Schema::TYPE_MONEY => 'decimal(19,4)',
); );

21
framework/yii/helpers/base/SecurityHelper.php

@ -131,15 +131,30 @@ class SecurityHelper
$keys = is_file($keyFile) ? require($keyFile) : array(); $keys = is_file($keyFile) ? require($keyFile) : array();
} }
if (!isset($keys[$name])) { if (!isset($keys[$name])) {
// generate a 32-char random key $keys[$name] = static::generateRandomKey($length);
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$keys[$name] = substr(str_shuffle(str_repeat($chars, 5)), 0, $length);
file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n"); file_put_contents($keyFile, "<?php\nreturn " . var_export($keys, true) . ";\n");
} }
return $keys[$name]; return $keys[$name];
} }
/** /**
* Generates a random key.
* @param integer $length the length of the key that should be generated
* @return string the generated random key
*/
public static function generateRandomKey($length = 32)
{
if (function_exists('openssl_random_pseudo_bytes')) {
$key = base64_encode(openssl_random_pseudo_bytes($length, $strong));
if ($strong) {
return substr($key, 0, $length);
}
}
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return substr(str_shuffle(str_repeat($chars, 5)), 0, $length);
}
/**
* Opens the mcrypt module. * Opens the mcrypt module.
* @return resource the mcrypt module handle. * @return resource the mcrypt module handle.
* @throws InvalidConfigException if mcrypt extension is not installed * @throws InvalidConfigException if mcrypt extension is not installed

58
framework/yii/i18n/Formatter.php

@ -30,21 +30,40 @@ class Formatter extends \yii\base\Formatter
*/ */
public $locale; public $locale;
/** /**
* @var string the default format string to be used to format a date using PHP date() function. * @var string the default format string to be used to format a date.
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*/ */
public $dateFormat = 'short'; public $dateFormat = 'short';
/** /**
* @var string the default format string to be used to format a time using PHP date() function. * @var string the default format string to be used to format a time.
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*/ */
public $timeFormat = 'short'; public $timeFormat = 'short';
/** /**
* @var string the default format string to be used to format a date and time using PHP date() function. * @var string the default format string to be used to format a date and time.
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*/ */
public $datetimeFormat = 'short'; public $datetimeFormat = 'short';
/** /**
* @var array the options to be set for the NumberFormatter objects. Please refer to * @var array the options to be set for the NumberFormatter objects. Please refer to
* [PHP manual](http://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants.unumberformatattribute)
* for the possible options. This property is used by [[createNumberFormatter]] when
* creating a new number formatter to format decimals, currencies, etc.
*/ */
public $numberFormatOptions = array(); public $numberFormatOptions = array();
/**
* @var string the character displayed as the decimal point when formatting a number.
* If not set, the decimal separator corresponding to [[locale]] will be used.
*/
public $decimalSeparator;
/**
* @var string the character displayed as the thousands separator character when formatting a number.
* If not set, the thousand separator corresponding to [[locale]] will be used.
*/
public $thousandSeparator;
/** /**
@ -61,6 +80,16 @@ class Formatter extends \yii\base\Formatter
if ($this->locale === null) { if ($this->locale === null) {
$this->locale = Yii::$app->language; $this->locale = Yii::$app->language;
} }
if ($this->decimalSeparator === null || $this->thousandSeparator === null) {
$formatter = new NumberFormatter($this->locale, NumberFormatter::DECIMAL);
if ($this->decimalSeparator === null) {
$this->decimalSeparator = $formatter->getSymbol(NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
}
if ($this->thousandSeparator === null) {
$this->thousandSeparator = $formatter->getSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
}
}
parent::init(); parent::init();
} }
@ -81,8 +110,11 @@ class Formatter extends \yii\base\Formatter
* - a PHP DateTime object * - a PHP DateTime object
* *
* @param string $format the format used to convert the value into a date string. * @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one * If null, [[dateFormat]] will be used.
* that can be recognized by the PHP `date()` function. *
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result * @return string the formatted result
* @see dateFormat * @see dateFormat
*/ */
@ -111,8 +143,11 @@ class Formatter extends \yii\base\Formatter
* - a PHP DateTime object * - a PHP DateTime object
* *
* @param string $format the format used to convert the value into a date string. * @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one * If null, [[dateFormat]] will be used.
* that can be recognized by the PHP `date()` function. *
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result * @return string the formatted result
* @see timeFormat * @see timeFormat
*/ */
@ -141,8 +176,11 @@ class Formatter extends \yii\base\Formatter
* - a PHP DateTime object * - a PHP DateTime object
* *
* @param string $format the format used to convert the value into a date string. * @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used. The format string should be the one * If null, [[dateFormat]] will be used.
* that can be recognized by the PHP `date()` function. *
* This can be "short", "medium", "long", or "full", which represents a preset format of different lengths.
* It can also be a custom format as specified in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime).
*
* @return string the formatted result * @return string the formatted result
* @see datetimeFormat * @see datetimeFormat
*/ */
@ -213,7 +251,7 @@ class Formatter extends \yii\base\Formatter
/** /**
* Creates a number formatter based on the given type and format. * Creates a number formatter based on the given type and format.
* @param integer $type the type of the number formatter * @param integer $type the type of the number formatter
* @param string $format the format to be used * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details)
* @return NumberFormatter the created formatter instance * @return NumberFormatter the created formatter instance
*/ */
protected function createNumberFormatter($type, $format) protected function createNumberFormatter($type, $format)

2
framework/yii/logging/Target.php

@ -204,14 +204,12 @@ abstract class Target extends Component
if ($matched) { if ($matched) {
foreach ($this->except as $category) { foreach ($this->except as $category) {
$prefix = rtrim($category, '*'); $prefix = rtrim($category, '*');
foreach ($messages as $i => $message) {
if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) { if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
$matched = false; $matched = false;
break; break;
} }
} }
} }
}
if (!$matched) { if (!$matched) {
unset($messages[$i]); unset($messages[$i]);

2
framework/yii/validators/DateValidator.php

@ -58,7 +58,7 @@ class DateValidator extends Validator
$date = DateTime::createFromFormat($this->format, $value); $date = DateTime::createFromFormat($this->format, $value);
if ($date === false) { if ($date === false) {
$this->addError($object, $attribute, $this->message); $this->addError($object, $attribute, $this->message);
} elseif ($this->timestampAttribute !== false) { } elseif ($this->timestampAttribute !== null) {
$object->{$this->timestampAttribute} = $date->getTimestamp(); $object->{$this->timestampAttribute} = $date->getTimestamp();
} }
} }

2
framework/yii/web/AccessRule.php

@ -99,7 +99,7 @@ class AccessRule extends Component
if ($this->matchAction($action) if ($this->matchAction($action)
&& $this->matchRole($user) && $this->matchRole($user)
&& $this->matchIP($request->getUserIP()) && $this->matchIP($request->getUserIP())
&& $this->matchVerb($request->getRequestMethod()) && $this->matchVerb($request->getMethod())
&& $this->matchController($action->controller) && $this->matchController($action->controller)
&& $this->matchCustom($action) && $this->matchCustom($action)
) { ) {

2
framework/yii/web/HttpCache.php

@ -60,7 +60,7 @@ class HttpCache extends ActionFilter
*/ */
public function beforeAction($action) public function beforeAction($action)
{ {
$verb = Yii::$app->request->getRequestMethod(); $verb = Yii::$app->request->getMethod();
if ($verb !== 'GET' && $verb !== 'HEAD' || $this->lastModified === null && $this->etagSeed === null) { if ($verb !== 'GET' && $verb !== 'HEAD' || $this->lastModified === null && $this->etagSeed === null) {
return true; return true;
} }

14
framework/yii/web/Request.php

@ -49,7 +49,7 @@ class Request extends \yii\base\Request
/** /**
* @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT or DELETE * @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT or DELETE
* request tunneled through POST. Default to '_method'. * request tunneled through POST. Default to '_method'.
* @see getRequestMethod * @see getMethod
* @see getRestParams * @see getRestParams
*/ */
public $restVar = '_method'; public $restVar = '_method';
@ -81,7 +81,7 @@ class Request extends \yii\base\Request
* @return string request method, such as GET, POST, HEAD, PUT, DELETE. * @return string request method, such as GET, POST, HEAD, PUT, DELETE.
* The value returned is turned into upper case. * The value returned is turned into upper case.
*/ */
public function getRequestMethod() public function getMethod()
{ {
if (isset($_POST[$this->restVar])) { if (isset($_POST[$this->restVar])) {
return strtoupper($_POST[$this->restVar]); return strtoupper($_POST[$this->restVar]);
@ -96,7 +96,7 @@ class Request extends \yii\base\Request
*/ */
public function getIsPostRequest() public function getIsPostRequest()
{ {
return $this->getRequestMethod() === 'POST'; return $this->getMethod() === 'POST';
} }
/** /**
@ -105,7 +105,7 @@ class Request extends \yii\base\Request
*/ */
public function getIsDeleteRequest() public function getIsDeleteRequest()
{ {
return $this->getRequestMethod() === 'DELETE'; return $this->getMethod() === 'DELETE';
} }
/** /**
@ -114,7 +114,7 @@ class Request extends \yii\base\Request
*/ */
public function getIsPutRequest() public function getIsPutRequest()
{ {
return $this->getRequestMethod() === 'PUT'; return $this->getMethod() === 'PUT';
} }
/** /**
@ -141,7 +141,7 @@ class Request extends \yii\base\Request
/** /**
* Returns the request parameters for the RESTful request. * Returns the request parameters for the RESTful request.
* @return array the RESTful request parameters * @return array the RESTful request parameters
* @see getRequestMethod * @see getMethod
*/ */
public function getRestParams() public function getRestParams()
{ {
@ -772,7 +772,7 @@ class Request extends \yii\base\Request
if (!$this->enableCsrfValidation) { if (!$this->enableCsrfValidation) {
return; return;
} }
$method = $this->getRequestMethod(); $method = $this->getMethod();
if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE') { if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE') {
$cookies = $this->getCookies(); $cookies = $this->getCookies();
switch ($method) { switch ($method) {

32
framework/yii/web/UrlManager.php

@ -43,6 +43,31 @@ class UrlManager extends Component
* array, one can use the key to represent the pattern and the value the corresponding route. * array, one can use the key to represent the pattern and the value the corresponding route.
* For example, `'post/<id:\d+>' => 'post/view'`. * For example, `'post/<id:\d+>' => 'post/view'`.
* *
* For RESTful routing the mentioned shortcut format also allows you to specify the
* [[UrlRule::verb|HTTP verb]] that the rule should apply for.
* You can do that by prepending it to the pattern, separated by space.
* For example, `'PUT post/<id:\d+>' => 'post/update'`.
* You may specify multiple verbs by separating them with comma
* like this: `'POST,PUT post/index' => 'post/create'`.
* The supported verbs in the shortcut format are: GET, HEAD, POST, PUT and DELETE.
* Note that [[UrlRule::mode|mode]] will be set to PARSING_ONLY when specifying verb in this way
* so you normally would not specify a verb for normal GET request.
*
* Here is an example configuration for RESTful CRUD controller:
*
* ~~~php
* array(
* 'dashboard' => 'site/index',
*
* 'POST <controller:\w+>s' => '<controller>/create',
* '<controller:\w+>s' => '<controller>/index',
*
* 'PUT <controller:\w+>/<id:\d+>' => '<controller>/update',
* 'DELETE <controller:\w+>/<id:\d+>' => '<controller>/delete',
* '<controller:\w+>/<id:\d+>' => '<controller>/view',
* );
* ~~~
*
* Note that if you modify this property after the UrlManager object is created, make sure * Note that if you modify this property after the UrlManager object is created, make sure
* you populate the array with rule objects instead of rule configurations. * you populate the array with rule objects instead of rule configurations.
*/ */
@ -115,9 +140,14 @@ class UrlManager extends Component
foreach ($this->rules as $key => $rule) { foreach ($this->rules as $key => $rule) {
if (!is_array($rule)) { if (!is_array($rule)) {
$rule = array( $rule = array(
'pattern' => $key,
'route' => $rule, 'route' => $rule,
); );
if (preg_match('/^((?:(GET|HEAD|POST|PUT|DELETE),)*(GET|HEAD|POST|PUT|DELETE))\s+(.*)$/', $key, $matches)) {
$rule['verb'] = explode(',', $matches[1]);
$rule['mode'] = UrlRule::PARSING_ONLY;
$key = $matches[4];
}
$rule['pattern'] = $key;
} }
$rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule)); $rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule));
} }

2
framework/yii/web/UrlRule.php

@ -171,7 +171,7 @@ class UrlRule extends Object
return false; return false;
} }
if ($this->verb !== null && !in_array($request->getRequestMethod(), $this->verb, true)) { if ($this->verb !== null && !in_array($request->getMethod(), $this->verb, true)) {
return false; return false;
} }

2
framework/yii/web/VerbFilter.php

@ -76,7 +76,7 @@ class VerbFilter extends Behavior
{ {
$action = $event->action->id; $action = $event->action->id;
if (isset($this->actions[$action])) { if (isset($this->actions[$action])) {
$verb = Yii::$app->getRequest()->getRequestMethod(); $verb = Yii::$app->getRequest()->getMethod();
$allowed = array_map('strtoupper', $this->actions[$action]); $allowed = array_map('strtoupper', $this->actions[$action]);
if (!in_array($verb, $allowed)) { if (!in_array($verb, $allowed)) {
$event->isValid = false; $event->isValid = false;

3
tests/unit/TestCase.php

@ -38,14 +38,13 @@ abstract class TestCase extends \yii\test\TestCase
* The application will be destroyed on tearDown() automatically. * The application will be destroyed on tearDown() automatically.
* @param array $config The application configuration, if needed * @param array $config The application configuration, if needed
*/ */
protected function mockApplication($config=array()) protected function mockApplication($config = array(), $appClass = '\yii\console\Application')
{ {
static $defaultConfig = array( static $defaultConfig = array(
'id' => 'testapp', 'id' => 'testapp',
'basePath' => __DIR__, 'basePath' => __DIR__,
); );
$appClass = $this->getParam( 'appClass', '\yii\web\Application' );
new $appClass(array_merge($defaultConfig,$config)); new $appClass(array_merge($defaultConfig,$config));
} }

2
tests/unit/data/config.php

@ -1,8 +1,6 @@
<?php <?php
return array( return array(
//'appClass' => '\yii\web\Application',
'appClass' => '\yii\console\Application',
'databases' => array( 'databases' => array(
'mysql' => array( 'mysql' => array(
'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest', 'dsn' => 'mysql:host=127.0.0.1;dbname=yiitest',

1
tests/unit/framework/console/controllers/AssetControllerTest.php

@ -239,6 +239,7 @@ class AssetControllerTest extends TestCase
// Then : // Then :
$this->assertTrue(file_exists($bundleFile), 'Unable to create output bundle file!'); $this->assertTrue(file_exists($bundleFile), 'Unable to create output bundle file!');
$this->assertTrue(is_array(require($bundleFile)), 'Output bundle file has incorrect format!');
$compressedCssFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.css'; $compressedCssFileName = $this->testAssetsBasePath . DIRECTORY_SEPARATOR . 'all.css';
$this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!'); $this->assertTrue(file_exists($compressedCssFileName), 'Unable to compress CSS files!');

109
tests/unit/framework/db/QueryBuilderTest.php

@ -0,0 +1,109 @@
<?php
namespace yiiunit\framework\db;
use yii\db\QueryBuilder;
use yii\db\Schema;
use yii\db\mysql\QueryBuilder as MysqlQueryBuilder;
use yii\db\sqlite\QueryBuilder as SqliteQueryBuilder;
use yii\db\mssql\QueryBuilder as MssqlQueryBuilder;
class QueryBuilderTest extends DatabaseTestCase
{
/**
* @throws \Exception
* @return QueryBuilder
*/
protected function getQueryBuilder()
{
switch($this->driverName)
{
case 'mysql':
return new MysqlQueryBuilder($this->getConnection());
case 'sqlite':
return new SqliteQueryBuilder($this->getConnection());
case 'mssql':
return new MssqlQueryBuilder($this->getConnection());
}
throw new \Exception('Test is not implemented for ' . $this->driverName);
}
/**
* this is not used as a dataprovider for testGetColumnType to speed up the test
* when used as dataprovider every single line will cause a reconnect with the database which is not needed here
*/
public function columnTypes()
{
return array(
array(Schema::TYPE_PK, 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY'),
array(Schema::TYPE_PK . '(8)', 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY'),
array(Schema::TYPE_PK . ' CHECK (value > 5)', 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'),
array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'int(8) NOT NULL AUTO_INCREMENT PRIMARY KEY CHECK (value > 5)'),
array(Schema::TYPE_STRING, 'varchar(255)'),
array(Schema::TYPE_STRING . '(32)', 'varchar(32)'),
array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'),
array(Schema::TYPE_TEXT, 'text'),
array(Schema::TYPE_TEXT . '(255)', 'text'),
array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . ' NOT NULL', 'text NOT NULL'),
array(Schema::TYPE_TEXT . '(255) NOT NULL', 'text NOT NULL'),
array(Schema::TYPE_SMALLINT, 'smallint(6)'),
array(Schema::TYPE_SMALLINT . '(8)', 'smallint(8)'),
array(Schema::TYPE_INTEGER, 'int(11)'),
array(Schema::TYPE_INTEGER . '(8)', 'int(8)'),
array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'int(11) CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'int(8) CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . ' NOT NULL', 'int(11) NOT NULL'),
array(Schema::TYPE_BIGINT, 'bigint(20)'),
array(Schema::TYPE_BIGINT . '(8)', 'bigint(8)'),
array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint(20) CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint(8) CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint(20) NOT NULL'),
array(Schema::TYPE_FLOAT, 'float'),
array(Schema::TYPE_FLOAT . '(16,5)', 'float'),
array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . '(16,5) CHECK (value > 5.6)', 'float CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . ' NOT NULL', 'float NOT NULL'),
array(Schema::TYPE_DECIMAL, 'decimal(10,0)'),
array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'),
array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'),
array(Schema::TYPE_DATETIME, 'datetime'),
array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'),
array(Schema::TYPE_TIMESTAMP, 'timestamp'),
array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'),
array(Schema::TYPE_TIME, 'time'),
array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"),
array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'),
array(Schema::TYPE_DATE, 'date'),
array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'),
array(Schema::TYPE_BINARY, 'blob'),
array(Schema::TYPE_BOOLEAN, 'tinyint(1)'),
array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'tinyint(1) NOT NULL DEFAULT 1'),
array(Schema::TYPE_MONEY, 'decimal(19,4)'),
array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'),
array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'),
);
}
/**
*
*/
public function testGetColumnType()
{
$qb = $this->getQueryBuilder();
foreach($this->columnTypes() as $item) {
list ($column, $expected) = $item;
$this->assertEquals($expected, $qb->getColumnType($column));
}
}
}

74
tests/unit/framework/db/sqlite/SqliteQueryBuilderTest.php

@ -0,0 +1,74 @@
<?php
namespace yiiunit\framework\db\sqlite;
use yii\db\sqlite\Schema;
use yiiunit\framework\db\QueryBuilderTest;
class SqliteQueryBuilderTest extends QueryBuilderTest
{
public $driverName = 'sqlite';
public function columnTypes()
{
return array(
array(Schema::TYPE_PK, 'integer PRIMARY KEY AUTOINCREMENT NOT NULL'),
array(Schema::TYPE_PK . '(8)', 'integer PRIMARY KEY AUTOINCREMENT NOT NULL'),
array(Schema::TYPE_PK . ' CHECK (value > 5)', 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)'),
array(Schema::TYPE_PK . '(8) CHECK (value > 5)', 'integer PRIMARY KEY AUTOINCREMENT NOT NULL CHECK (value > 5)'),
array(Schema::TYPE_STRING, 'varchar(255)'),
array(Schema::TYPE_STRING . '(32)', 'varchar(32)'),
array(Schema::TYPE_STRING . ' CHECK (value LIKE "test%")', 'varchar(255) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . '(32) CHECK (value LIKE "test%")', 'varchar(32) CHECK (value LIKE "test%")'),
array(Schema::TYPE_STRING . ' NOT NULL', 'varchar(255) NOT NULL'),
array(Schema::TYPE_TEXT, 'text'),
array(Schema::TYPE_TEXT . '(255)', 'text'),
array(Schema::TYPE_TEXT . ' CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . '(255) CHECK (value LIKE "test%")', 'text CHECK (value LIKE "test%")'),
array(Schema::TYPE_TEXT . ' NOT NULL', 'text NOT NULL'),
array(Schema::TYPE_TEXT . '(255) NOT NULL', 'text NOT NULL'),
array(Schema::TYPE_SMALLINT, 'smallint'),
array(Schema::TYPE_SMALLINT . '(8)', 'smallint'),
array(Schema::TYPE_INTEGER, 'integer'),
array(Schema::TYPE_INTEGER . '(8)', 'integer'),
array(Schema::TYPE_INTEGER . ' CHECK (value > 5)', 'integer CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . '(8) CHECK (value > 5)', 'integer CHECK (value > 5)'),
array(Schema::TYPE_INTEGER . ' NOT NULL', 'integer NOT NULL'),
array(Schema::TYPE_BIGINT, 'bigint'),
array(Schema::TYPE_BIGINT . '(8)', 'bigint'),
array(Schema::TYPE_BIGINT . ' CHECK (value > 5)', 'bigint CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . '(8) CHECK (value > 5)', 'bigint CHECK (value > 5)'),
array(Schema::TYPE_BIGINT . ' NOT NULL', 'bigint NOT NULL'),
array(Schema::TYPE_FLOAT, 'float'),
array(Schema::TYPE_FLOAT . '(16,5)', 'float'),
array(Schema::TYPE_FLOAT . ' CHECK (value > 5.6)', 'float CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . '(16,5) CHECK (value > 5.6)', 'float CHECK (value > 5.6)'),
array(Schema::TYPE_FLOAT . ' NOT NULL', 'float NOT NULL'),
array(Schema::TYPE_DECIMAL, 'decimal(10,0)'),
array(Schema::TYPE_DECIMAL . '(12,4)', 'decimal(12,4)'),
array(Schema::TYPE_DECIMAL . ' CHECK (value > 5.6)', 'decimal(10,0) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . '(12,4) CHECK (value > 5.6)', 'decimal(12,4) CHECK (value > 5.6)'),
array(Schema::TYPE_DECIMAL . ' NOT NULL', 'decimal(10,0) NOT NULL'),
array(Schema::TYPE_DATETIME, 'datetime'),
array(Schema::TYPE_DATETIME . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "datetime CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATETIME . ' NOT NULL', 'datetime NOT NULL'),
array(Schema::TYPE_TIMESTAMP, 'timestamp'),
array(Schema::TYPE_TIMESTAMP . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "timestamp CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_TIMESTAMP . ' NOT NULL', 'timestamp NOT NULL'),
array(Schema::TYPE_TIME, 'time'),
array(Schema::TYPE_TIME . " CHECK(value BETWEEN '12:00:00' AND '13:01:01')", "time CHECK(value BETWEEN '12:00:00' AND '13:01:01')"),
array(Schema::TYPE_TIME . ' NOT NULL', 'time NOT NULL'),
array(Schema::TYPE_DATE, 'date'),
array(Schema::TYPE_DATE . " CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')", "date CHECK(value BETWEEN '2011-01-01' AND '2013-01-01')"),
array(Schema::TYPE_DATE . ' NOT NULL', 'date NOT NULL'),
array(Schema::TYPE_BINARY, 'blob'),
array(Schema::TYPE_BOOLEAN, 'boolean'),
array(Schema::TYPE_BOOLEAN . ' NOT NULL DEFAULT 1', 'boolean NOT NULL DEFAULT 1'),
array(Schema::TYPE_MONEY, 'decimal(19,4)'),
array(Schema::TYPE_MONEY . '(16,2)', 'decimal(16,2)'),
array(Schema::TYPE_MONEY . ' CHECK (value > 0.0)', 'decimal(19,4) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . '(16,2) CHECK (value > 0.0)', 'decimal(16,2) CHECK (value > 0.0)'),
array(Schema::TYPE_MONEY . ' NOT NULL', 'decimal(19,4) NOT NULL'),
);
}
}

54
tests/unit/framework/web/UrlManagerTest.php

@ -248,4 +248,58 @@ class UrlManagerTest extends TestCase
$result = $manager->parseRequest($request); $result = $manager->parseRequest($request);
$this->assertFalse($result); $this->assertFalse($result);
} }
public function testParseRESTRequest()
{
$manager = new UrlManager(array(
'cache' => null,
));
$request = new Request;
// pretty URL rules
$manager = new UrlManager(array(
'enablePrettyUrl' => true,
'cache' => null,
'rules' => array(
'PUT,POST post/<id>/<title>' => 'post/create',
'DELETE post/<id>' => 'post/delete',
'post/<id>/<title>' => 'post/view',
'POST/GET' => 'post/get',
),
));
// matching pathinfo GET request
$_SERVER['REQUEST_METHOD'] = 'GET';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/view', array('id' => '123', 'title' => 'this+is+sample')), $result);
// matching pathinfo PUT/POST request
$_SERVER['REQUEST_METHOD'] = 'PUT';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/create', array('id' => '123', 'title' => 'this+is+sample')), $result);
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'post/123/this+is+sample';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/create', array('id' => '123', 'title' => 'this+is+sample')), $result);
// no wrong matching
$_SERVER['REQUEST_METHOD'] = 'POST';
$request->pathInfo = 'POST/GET';
$result = $manager->parseRequest($request);
$this->assertEquals(array('post/get', array()), $result);
// createUrl should ignore REST rules
$this->mockApplication(array(
'components' => array(
'request' => array(
'hostInfo' => 'http://localhost/',
'baseUrl' => '/app'
)
)
), \yii\web\Application::className());
$this->assertEquals('/app/post/delete?id=123', $manager->createUrl('post/delete', array('id' => 123)));
$this->destroyApplication();
unset($_SERVER['REQUEST_METHOD']);
}
} }

Loading…
Cancel
Save