Browse Source

Merge branch 'master' into allow-references-in-array

tags/2.0.36
Andrii Vasyliev 4 years ago
parent
commit
53fa22ecfd
  1. 5
      framework/CHANGELOG.md
  2. 5
      framework/base/Controller.php
  3. 14
      framework/base/ErrorHandler.php
  4. 17
      framework/console/Controller.php
  5. 25
      framework/console/widgets/Table.php
  6. 7
      framework/db/mssql/Schema.php
  7. 12
      framework/helpers/BaseConsole.php
  8. 25
      tests/framework/console/ControllerTest.php
  9. 5
      tests/framework/console/FakePhp71Controller.php
  10. 1
      tests/framework/console/controllers/HelpControllerTest.php
  11. 51
      tests/framework/console/widgets/TableTest.php
  12. 34
      tests/framework/web/ControllerTest.php
  13. 5
      tests/framework/web/FakePhp71Controller.php

5
framework/CHANGELOG.md

@ -4,12 +4,13 @@ Yii Framework 2 Change Log
2.0.36 under development
------------------------
- Bug #18047: Fix colorization markers output in Table.php (cheeseq)
- Bug #18028: Fix division by zero exception in Table.php::calculateRowHeight (fourhundredfour)
- Enh #18019: Allow jQuery 3.5.0 to be installed (wouter90)
- Bug #18026: Fix `ArrayHelper::getValue()` did not work with `ArrayAccess` objects (mikk150)
- Enh #18048: Use `Instance::ensure()` to set `User::$accessChecker` (lav45)
- Bug #18051: Fix missing support for custom validation method in EachValidator (bizley)
- Enh #17722: Add action injection support (SamMousa, samdark)
- Enh #17722: Add action injection support (SamMousa, samdark, erickskrauch)
- Bug #18041: Fix RBAC migration for MSSQL (darkdef)
- Bug #18081: Fix for PDO_DBLIB/MSSQL. Set flag ANSI_NULL_DFLT_ON to ON for current connect to DB (darkdef)
- Bug #13828: Fix retrieving inserted data for a primary key of type uniqueidentifier for SQL Server 2005 or later (darkdef)
@ -20,6 +21,8 @@ Yii Framework 2 Change Log
- Bug #18101: Fix behavior of OUTPUT INSERTED.* for SQL Server query: "insert default values"; correct MSSQL unit tests; turn off profiling echo message in migration test (darkdef)
- Bug #18105: Fix for old trigger in RBAC migration with/without prefixTable (darkdef)
- Enh #18120: Include path to the log file into error message if `FileTarget::export` fails (uaoleg)
- Enh #15202: Add optional param `--silent-exit-on-exception` in `yii\console\Controller` (egorrishe)
- Bug #18110: Add quotes to return value of viewName in MSSQL schema. It is `[someView]` now (darkdef)
2.0.35 May 02, 2020

5
framework/base/Controller.php

@ -565,9 +565,12 @@ class Controller extends Component implements ViewContextInterface
if (($component = $this->module->get($name, false)) instanceof $typeName) {
$args[] = $component;
$requestedParams[$name] = "Component: " . get_class($component) . " \$$name";
} elseif ($this->module->has($typeName) && ($service = $this->module->get($typeName)) instanceof $typeName) {
$args[] = $service;
$requestedParams[$name] = 'Module ' . get_class($this->module) . " DI: $typeName \$$name";
} elseif (\Yii::$container->has($typeName) && ($service = \Yii::$container->get($typeName)) instanceof $typeName) {
$args[] = $service;
$requestedParams[$name] = "DI: $typeName \$$name";
$requestedParams[$name] = "Container DI: $typeName \$$name";
} elseif ($type->allowsNull()) {
$args[] = null;
$requestedParams[$name] = "Unavailable service: $name";

14
framework/base/ErrorHandler.php

@ -41,6 +41,12 @@ abstract class ErrorHandler extends Component
* @var \Exception|null the exception that is being handled currently.
*/
public $exception;
/**
* @var bool if TRUE - `handleException()` will finish script with `ExitCode::OK`.
* FALSE - `ExitCode::UNSPECIFIED_ERROR`.
* @since 2.0.36
*/
public $silentExitOnException;
/**
* @var string Used to reserve memory for fatal error handler.
@ -56,6 +62,12 @@ abstract class ErrorHandler extends Component
private $_registered = false;
public function init()
{
$this->silentExitOnException = $this->silentExitOnException !== null ? $this->silentExitOnException : YII_ENV_TEST;
parent::init();
}
/**
* Register this error handler.
* @since 2.0.32 this will not do anything if the error handler was already registered
@ -121,7 +133,7 @@ abstract class ErrorHandler extends Component
$this->clearOutput();
}
$this->renderException($exception);
if (!YII_ENV_TEST) {
if (!$this->silentExitOnException) {
\Yii::getLogger()->flush(true);
if (defined('HHVM_VERSION')) {
flush();

17
framework/console/Controller.php

@ -65,6 +65,13 @@ class Controller extends \yii\base\Controller
* @since 2.0.10
*/
public $help;
/**
* @var bool if TRUE - script finish with `ExitCode::OK` in case of exception.
* FALSE - `ExitCode::UNSPECIFIED_ERROR`.
* Default: `YII_ENV_TEST`
* @since 2.0.36
*/
public $silentExitOnException;
/**
* @var array the options passed during execution.
@ -72,6 +79,14 @@ class Controller extends \yii\base\Controller
private $_passedOptions = [];
public function beforeAction($action)
{
$silentExit = $this->silentExitOnException !== null ? $this->silentExitOnException : YII_ENV_TEST;
Yii::$app->errorHandler->silentExitOnException = $silentExit;
return parent::beforeAction($action);
}
/**
* Returns a value indicating whether ANSI color is enabled.
*
@ -398,7 +413,7 @@ class Controller extends \yii\base\Controller
public function options($actionID)
{
// $actionId might be used in subclasses to provide options specific to action id
return ['color', 'interactive', 'help'];
return ['color', 'interactive', 'help', 'silentExitOnException'];
}
/**

25
framework/console/widgets/Table.php

@ -241,36 +241,38 @@ class Table extends Widget
$buffer = '';
$arrayPointer = [];
$finalChunk = [];
$alreadyPrintedCells = [];
for ($i = 0, ($max = $this->calculateRowHeight($row)) ?: $max = 1; $i < $max; $i++) {
$buffer .= $spanLeft . ' ';
foreach ($size as $index => $cellSize) {
$cell = isset($row[$index]) ? $row[$index] : null;
$prefix = '';
$chunk = '';
if ($index !== 0) {
$buffer .= $spanMiddle . ' ';
}
if (is_array($cell)) {
if (empty($finalChunk[$index])) {
$finalChunk[$index] = '';
$start = 0;
$prefix = $this->listPrefix;
if (!isset($arrayPointer[$index])) {
$arrayPointer[$index] = 0;
}
} else {
$start = mb_strwidth($finalChunk[$index], Yii::$app->charset);
}
$chunk = mb_substr($cell[$arrayPointer[$index]], $start, $cellSize - 4, Yii::$app->charset);
$chunk = $cell[$arrayPointer[$index]];
$finalChunk[$index] .= $chunk;
if (isset($cell[$arrayPointer[$index] + 1]) && $finalChunk[$index] === $cell[$arrayPointer[$index]]) {
$arrayPointer[$index]++;
$finalChunk[$index] = '';
}
} else {
$chunk = mb_substr($cell, ($cellSize * $i) - ($i * 2), $cellSize - 2, Yii::$app->charset);
if (!isset($alreadyPrintedCells[$index])) {
$chunk = $cell;
}
$alreadyPrintedCells[$index] = true;
}
$chunk = $prefix . $chunk;
$repeat = $cellSize - mb_strwidth($chunk, Yii::$app->charset) - 1;
$repeat = $cellSize - Console::ansiStrwidth($chunk) - 1;
$buffer .= $chunk;
if ($repeat >= 0) {
$buffer .= str_repeat(' ', $repeat);
@ -333,11 +335,9 @@ class Table extends Widget
foreach ($columns as $column) {
$columnWidth = max(array_map(function ($val) {
if (is_array($val)) {
$encodings = array_fill(0, count($val), Yii::$app->charset);
return max(array_map('mb_strwidth', $val, $encodings)) + mb_strwidth($this->listPrefix, Yii::$app->charset);
return max(array_map('yii\helpers\Console::ansiStrwidth', $val)) + Console::ansiStrwidth($this->listPrefix);
}
return mb_strwidth($val, Yii::$app->charset);
return Console::ansiStrwidth($val);
}, $column)) + 2;
$this->columnWidths[] = $columnWidth;
$totalWidth += $columnWidth;
@ -376,10 +376,9 @@ class Table extends Widget
return $size == 2 || $columnWidth == 0 ? 0 : ceil($columnWidth / ($size - 2));
}, $this->columnWidths, array_map(function ($val) {
if (is_array($val)) {
$encodings = array_fill(0, count($val), Yii::$app->charset);
return array_map('mb_strwidth', $val, $encodings);
return array_map('yii\helpers\Console::ansiStrwidth', $val);
}
return mb_strwidth($val, Yii::$app->charset);
return Console::ansiStrwidth($val);
}, $row));
return max($rowsPerCell);
}

7
framework/db/mssql/Schema.php

@ -595,7 +595,12 @@ WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'VIEW'
ORDER BY [t].[table_name]
SQL;
return $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
$views = $this->db->createCommand($sql, [':schema' => $schema])->queryColumn();
$views = array_map(static function ($item) {
return '[' . $item . ']';
}, $views);
return $views;
}
/**

12
framework/helpers/BaseConsole.php

@ -7,6 +7,7 @@
namespace yii\helpers;
use Yii;
use yii\console\Markdown as ConsoleMarkdown;
use yii\base\Model;
@ -344,6 +345,17 @@ class BaseConsole
}
/**
* Returns the width of the string without ANSI color codes.
* @param string $string the string to measure
* @return int the width of the string not counting ANSI format characters
* @since 2.0.36
*/
public static function ansiStrwidth($string)
{
return mb_strwidth(static::stripAnsiFormat($string), Yii::$app->charset);
}
/**
* Converts an ANSI formatted string to HTML.
*
* Note: xTerm 256 bit colors are currently not supported.

25
tests/framework/console/ControllerTest.php

@ -170,12 +170,35 @@ class ControllerTest extends TestCase
$this->assertEquals('Component: yii\console\Request $request', \Yii::$app->requestedParams['request']);
$this->assertEquals($params['between'], $args[2]);
$this->assertInstanceOf(DummyService::className(), $args[3]);
$this->assertEquals('DI: yiiunit\framework\console\stubs\DummyService $dummyService', \Yii::$app->requestedParams['dummyService']);
$this->assertEquals('Container DI: yiiunit\framework\console\stubs\DummyService $dummyService', \Yii::$app->requestedParams['dummyService']);
$this->assertNull($args[4]);
$this->assertEquals('Unavailable service: post', \Yii::$app->requestedParams['post']);
$this->assertEquals($params['after'], $args[5]);
}
public function testInjectedActionParamsFromModule()
{
if (PHP_VERSION_ID < 70100) {
$this->markTestSkipped('Can not be tested on PHP < 7.1');
return;
}
$module = new \yii\base\Module('fake', new Application([
'id' => 'app',
'basePath' => __DIR__,
]));
$module->set('yii\data\DataProviderInterface', [
'class' => \yii\data\ArrayDataProvider::className(),
]);
// Use the PHP71 controller for this test
$this->controller = new FakePhp71Controller('fake', $module);
$this->mockWebApplication(['controller' => $this->controller]);
$injectionAction = new InlineAction('injection', $this->controller, 'actionModuleServiceInjection');
$args = $this->controller->bindActionParams($injectionAction, []);
$this->assertInstanceOf(\yii\data\ArrayDataProvider::className(), $args[0]);
$this->assertEquals('Module yii\base\Module DI: yii\data\DataProviderInterface $dataProvider', \Yii::$app->requestedParams['dataProvider']);
}
public function assertResponseStatus($status, $response)
{
$this->assertInstanceOf('yii\console\Response', $response);

5
tests/framework/console/FakePhp71Controller.php

@ -7,6 +7,7 @@
namespace yiiunit\framework\console;
use yii\data\DataProviderInterface;
use yiiunit\framework\console\stubs\DummyService;
use yii\console\Controller;
use yii\console\Request;
@ -21,4 +22,8 @@ class FakePhp71Controller extends Controller
public function actionNullableInjection(?Request $request, ?Post $post)
{
}
public function actionModuleServiceInjection(DataProviderInterface $dataProvider)
{
}
}

1
tests/framework/console/controllers/HelpControllerTest.php

@ -128,6 +128,7 @@ action:route to action
--interactive: whether to run the command interactively.
--color: whether to enable ANSI color in the output.If not set, ANSI color will only be enabled for terminals that support it.
--help: whether to display help information about current command.
--silent-exit-on-exception: if TRUE - script finish with `ExitCode\:\:OK` in case of exception.FALSE - `ExitCode\:\:UNSPECIFIED_ERROR`.Default\: `YII_ENV_TEST`
STRING
, $result);

51
tests/framework/console/widgets/TableTest.php

@ -8,6 +8,7 @@
namespace yiiunit\framework\console;
use yii\console\widgets\Table;
use yii\helpers\Console;
use yiiunit\TestCase;
/**
@ -314,6 +315,56 @@ EXPECTED;
);
}
public function testColorizedInput()
{
$table = new Table();
$expected = <<<"EXPECTED"
╔═══════╤═══════╤══════════╗
║ test1 │ test2 │ test3 ║
╟───────┼───────┼──────────╢
║ col1 │ \e[33mcol2\e[0m │ col3 ║
╟───────┼───────┼──────────╢
║ col1 │ col2 │ • col3-0 ║
║ │ │ • \e[31mcol3-1\e[0m ║
║ │ │ • col3-2 ║
╚═══════╧═══════╧══════════╝
EXPECTED;
$this->assertEqualsWithoutLE(
$expected,
$table
->setHeaders(['test1', 'test2', 'test3'])
->setRows([
['col1', Console::renderColoredString('%ycol2%n'), 'col3'],
['col1', 'col2', ['col3-0', Console::renderColoredString('%rcol3-1%n'), 'col3-2']],
])
->run()
);
}
public function testColorizedInputStripsANSIMarkersInternally()
{
$table = new Table();
$table
->setHeaders(['t1', 't2', 't3'])
->setRows([
['col1', Console::renderColoredString('%ycol2%n'), 'col3'],
['col1', 'col2', ['col3-0', Console::renderColoredString('%rcol3-1%n'), 'col3-2']],
])
->setScreenWidth(200)
->run();
$columnWidths = \PHPUnit_Framework_Assert::readAttribute($table, "columnWidths");
$this->assertArrayHasKey(1, $columnWidths);
$this->assertEquals(4+2, $columnWidths[1]);
$this->assertArrayHasKey(2, $columnWidths);
$this->assertEquals(8+2, $columnWidths[2]);
}
public function testCalculateRowHeightShouldNotThrowDivisionByZeroException()
{
$rows = [

34
tests/framework/web/ControllerTest.php

@ -155,11 +155,43 @@ class ControllerTest extends TestCase
$this->assertEquals('Component: yii\web\Request $request', \Yii::$app->requestedParams['request']);
$this->assertEquals($params['between'], $args[2]);
$this->assertInstanceOf(VendorImage::className(), $args[3]);
$this->assertEquals('DI: yiiunit\framework\web\stubs\VendorImage $vendorImage', \Yii::$app->requestedParams['vendorImage']);
$this->assertEquals('Container DI: yiiunit\framework\web\stubs\VendorImage $vendorImage', \Yii::$app->requestedParams['vendorImage']);
$this->assertNull($args[4]);
$this->assertEquals('Unavailable service: post', \Yii::$app->requestedParams['post']);
$this->assertEquals($params['after'], $args[5]);
}
public function testInjectedActionParamsFromModule()
{
if (PHP_VERSION_ID < 70100) {
$this->markTestSkipped('Can not be tested on PHP < 7.1');
return;
}
$module = new \yii\base\Module('fake', new \yii\web\Application([
'id' => 'app',
'basePath' => __DIR__,
'components' => [
'request' => [
'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq',
'scriptFile' => __DIR__ . '/index.php',
'scriptUrl' => '/index.php',
],
],
]));
$module->set('yii\data\DataProviderInterface', [
'class' => \yii\data\ArrayDataProvider::className(),
]);
// Use the PHP71 controller for this test
$this->controller = new FakePhp71Controller('fake', $module);
$this->mockWebApplication(['controller' => $this->controller]);
$injectionAction = new InlineAction('injection', $this->controller, 'actionModuleServiceInjection');
$args = $this->controller->bindActionParams($injectionAction, []);
$this->assertInstanceOf(\yii\data\ArrayDataProvider::className(), $args[0]);
$this->assertEquals('Module yii\base\Module DI: yii\data\DataProviderInterface $dataProvider', \Yii::$app->requestedParams['dataProvider']);
}
/**
* @see https://github.com/yiisoft/yii2/issues/17701
*/

5
tests/framework/web/FakePhp71Controller.php

@ -7,6 +7,7 @@
namespace yiiunit\framework\web;
use yii\data\DataProviderInterface;
use yii\web\Controller;
use yii\web\Request;
use yiiunit\framework\web\stubs\VendorImage;
@ -27,4 +28,8 @@ class FakePhp71Controller extends Controller
public function actionNullableInjection(?Request $request, ?Post $post)
{
}
public function actionModuleServiceInjection(DataProviderInterface $dataProvider)
{
}
}

Loading…
Cancel
Save