Browse Source

Fixes for PHP 7.2 compatibility (#14959)

tags/2.0.13
Dmitry Naumenko 7 years ago committed by Alexander Makarov
parent
commit
64d8af61a6
  1. 12
      .travis.yml
  2. 4
      framework/db/ActiveQuery.php
  3. 4
      framework/di/Container.php
  4. 9
      framework/i18n/Formatter.php
  5. 15
      framework/validators/EmailValidator.php
  6. 2
      framework/validators/UniqueValidator.php
  7. 12
      framework/validators/UrlValidator.php
  8. 2
      framework/web/User.php
  9. 3
      phpunit.xml.dist
  10. 38
      tests/ResultPrinter.php
  11. 20
      tests/compatibility.php
  12. 3
      tests/framework/ar/ActiveRecordTestTrait.php
  13. 2
      tests/framework/db/GetTablesAliasTestTrait.php
  14. 3
      tests/framework/di/InstanceTest.php
  15. 2
      tests/framework/filters/PageCacheTest.php
  16. 3
      tests/framework/filters/RateLimiterTest.php
  17. 8
      tests/framework/helpers/ArrayHelperTest.php
  18. 23
      tests/framework/helpers/HtmlTest.php
  19. 2
      tests/framework/helpers/JsonTest.php
  20. 30
      tests/framework/i18n/FormatterTest.php
  21. 10
      tests/framework/i18n/IntlTestHelper.php
  22. 2
      tests/framework/rest/DataFilterTest.php
  23. 10
      tests/framework/validators/FileValidatorTest.php
  24. 10
      tests/framework/validators/data/mimeType/test.xml
  25. 2
      tests/framework/widgets/ActiveFormTest.php

12
.travis.yml

@ -55,9 +55,9 @@ addons:
matrix:
fast_finish: true
include:
# run tests coverage on PHP 7.1
- php: 7.2
# run tests coverage on PHP 7.1
- php: 7.1
env: TASK_TESTS_COVERAGE=1
@ -110,7 +110,6 @@ matrix:
allow_failures:
- php: nightly
- php: 7.2
install:
- |
@ -178,6 +177,15 @@ before_script:
PHPUNIT_FLAGS="--coverage-clover=coverage.clover"
fi
# Disable DEPRECATE messages during PHPUnit initialization on PHP 7.2. To fix them, PHPUnit should be updated to 6.*
# For Yii2 tests, messages will be enabled by tests/bootstrap.php
- |
if [ $TRAVIS_PHP_VERSION == 7.2 ]; then
echo 'Disabled DEPRECATED notifications for PHP 7.2';
echo 'error_reporting = E_ALL & ~E_DEPRECATED' >> /tmp/php-config.ini;
phpenv config-add /tmp/php-config.ini;
fi
script:
# PHP tests

4
framework/db/ActiveQuery.php

@ -759,7 +759,9 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function viaTable($tableName, $link, callable $callable = null)
{
$relation = new self(get_class($this->primaryModel), [
$modelClass = $this->primaryModel !== null ? get_class($this->primaryModel) : get_class();
$relation = new self($modelClass, [
'from' => [$tableName],
'link' => $link,
'multiple' => true,

4
framework/di/Container.php

@ -624,7 +624,7 @@ class Container extends Component
public function setDefinitions(array $definitions)
{
foreach ($definitions as $class => $definition) {
if (count($definition) === 2 && array_values($definition) === $definition) {
if (is_array($definition) && count($definition) === 2 && array_values($definition) === $definition) {
$this->set($class, $definition[0], $definition[1]);
continue;
}
@ -646,7 +646,7 @@ class Container extends Component
public function setSingletons(array $singletons)
{
foreach ($singletons as $class => $definition) {
if (count($definition) === 2 && array_values($definition) === $definition) {
if (is_array($definition) && count($definition) === 2 && array_values($definition) === $definition) {
$this->setSingleton($class, $definition[0], $definition[1]);
continue;
}

9
framework/i18n/Formatter.php

@ -1529,7 +1529,14 @@ class Formatter extends Component
$multipliers = array_values($this->measureUnits[$unitType][$unitSystem]);
list($params, $position) = $this->formatNumber($value * $baseUnit, $decimals, null, $multipliers, $options, $textOptions);
list($params, $position) = $this->formatNumber(
$this->normalizeNumericValue($value) * $baseUnit,
$decimals,
null,
$multipliers,
$options,
$textOptions
);
$message = $this->getUnitMessage($unitType, $unitFormat, $unitSystem, $position);

15
framework/validators/EmailValidator.php

@ -76,8 +76,8 @@ class EmailValidator extends Validator
$valid = false;
} else {
if ($this->enableIDN) {
$matches['local'] = idn_to_ascii($matches['local']);
$matches['domain'] = idn_to_ascii($matches['domain']);
$matches['local'] = $this->idnToAscii($matches['local']);
$matches['domain'] = $this->idnToAscii($matches['domain']);
$value = $matches['name'] . $matches['open'] . $matches['local'] . '@' . $matches['domain'] . $matches['close'];
}
@ -104,6 +104,17 @@ class EmailValidator extends Validator
return $valid ? null : [$this->message, []];
}
private function idnToAscii($idn)
{
if (PHP_VERSION_ID < 50600) {
// TODO: drop old PHP versions support
return idn_to_ascii($idn);
}
return idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
}
/**
* @inheritdoc
*/

2
framework/validators/UniqueValidator.php

@ -132,7 +132,7 @@ class UniqueValidator extends Validator
}
if ($this->modelExists($targetClass, $conditions, $model)) {
if (count($targetAttribute) > 1) {
if (is_array($targetAttribute) && count($targetAttribute) > 1) {
$this->addComboNotUniqueError($model, $attribute);
} else {
$this->addError($model, $attribute, $this->message);

12
framework/validators/UrlValidator.php

@ -96,7 +96,7 @@ class UrlValidator extends Validator
if ($this->enableIDN) {
$value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) {
return '://' . idn_to_ascii($matches[1]);
return '://' . $this->idnToAscii($matches[1]);
}, $value);
}
@ -108,6 +108,16 @@ class UrlValidator extends Validator
return [$this->message, []];
}
private function idnToAscii($idn)
{
if (PHP_VERSION_ID < 50600) {
// TODO: drop old PHP versions support
return idn_to_ascii($idn);
}
return idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
}
/**
* @inheritdoc
*/

2
framework/web/User.php

@ -561,7 +561,7 @@ class User extends Component
return null;
}
$data = json_decode($value, true);
if (count($data) == 3) {
if (is_array($data) && count($data) == 3) {
list($id, $authKey, $duration) = $data;
/* @var $class IdentityInterface */
$class = $this->identityClass;

3
phpunit.xml.dist

@ -5,7 +5,8 @@
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false">
stopOnFailure="false"
printerClass="yiiunit\ResultPrinter">
<testsuites>
<testsuite name="Yii Test Suite">
<directory>./tests</directory>

38
tests/ResultPrinter.php

@ -0,0 +1,38 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit;
/**
* Class ResultPrinter overrides \PHPUnit\TextUI\ResultPrinter constructor
* to change default output to STDOUT and prevent some tests from fail when
* they can not be executed after headers have been sent.
*/
class ResultPrinter extends \PHPUnit\TextUI\ResultPrinter
{
public function __construct(
$out = null,
$verbose = false,
$colors = \PHPUnit\TextUI\ResultPrinter::COLOR_DEFAULT,
$debug = false,
$numberOfColumns = 80,
$reverse = false
) {
if ($out === null) {
$out = STDOUT;
}
parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);
}
public function flush()
{
if ($this->out !== STDOUT) {
parent::flush();
}
}
}

20
tests/compatibility.php

@ -17,10 +17,26 @@ namespace PHPUnit\Framework\Constraint {
}
}
namespace PHPUnit\TextUI {
if (!class_exists('\PHPUnit\TextUI\ResultPrinter') && class_exists('PHPUnit_TextUI_ResultPrinter')) {
class ResultPrinter extends \PHPUnit_TextUI_ResultPrinter
{
}
}
}
namespace PHPUnit\Framework\Error {
if (!class_exists('PHPUnit\Framework\Error\Notice') && class_exists('PHPUnit_Framework_Error_Notice')) {
class Notice extends \PHPUnit_Framework_Error_Notice
{
}
}
}
namespace PHPUnit\Framework {
if (!class_exists('PHPUnit\Framework\TestCase') && class_exists('PHPUnit_Framework_TestCase')) {
echo "Applying compatibility patch for PHPUnit 6...\n";
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
/**

3
tests/framework/ar/ActiveRecordTestTrait.php

@ -84,8 +84,7 @@ trait ActiveRecordTestTrait
$customer = $customerClass::findOne(5);
$this->assertNull($customer);
$customer = $customerClass::findOne(['id' => [5, 6, 1]]);
// can't use assertCount() here since it will count model attributes instead
$this->assertEquals(1, count($customer));
$this->assertInstanceOf($customerClass, $customer);
$customer = $customerClass::find()->where(['id' => [5, 6, 1]])->one();
$this->assertNotNull($customer);

2
tests/framework/db/GetTablesAliasTestTrait.php

@ -56,7 +56,7 @@ trait GetTablesAliasTestTrait
$query = $this->createQuery();
$query->from = new \stdClass();
$this->setExpectedException('\yii\base\InvalidConfigException');
$this->expectException('\yii\base\InvalidConfigException');
$query->getTablesUsedInFrom();
}

3
tests/framework/di/InstanceTest.php

@ -193,7 +193,8 @@ PHP
public function testExceptionInvalidDataTypeInArray()
{
$this->setExpectedException('yii\base\InvalidConfigException', 'Invalid data type: yii\db\Connection. yii\base\Widget is expected.');
$this->expectException('yii\base\InvalidConfigException');
$this->expectExceptionMessage('Invalid data type: yii\db\Connection. yii\base\Widget is expected.');
Instance::ensure([
'class' => Connection::className(),
], 'yii\base\Widget');

2
tests/framework/filters/PageCacheTest.php

@ -166,7 +166,7 @@ class PageCacheTest extends TestCase
Yii::$app->response->cookies->add(new Cookie([
'name' => $name,
'value' => $value,
'expire' => PHP_INT_MAX,
'expire' => strtotime('now +1 year'),
]));
$cookies[$name] = $value;
}

3
tests/framework/filters/RateLimiterTest.php

@ -15,6 +15,7 @@ use yii\web\Request;
use yii\web\Response;
use yii\web\User;
use yiiunit\framework\filters\stubs\RateLimit;
use yiiunit\framework\filters\stubs\UserIdentity;
use yiiunit\TestCase;
/**
@ -117,7 +118,7 @@ class RateLimiterTest extends TestCase
->setAllowance([1, time() + 2]);
$rateLimiter = new RateLimiter();
$this->setExpectedException('yii\web\TooManyRequestsHttpException');
$this->expectException('yii\web\TooManyRequestsHttpException');
$rateLimiter->checkRateLimit($rateLimit, Yii::$app->request, Yii::$app->response, 'testAction');
}

8
tests/framework/helpers/ArrayHelperTest.php

@ -56,7 +56,7 @@ class ArrayHelperTest extends TestCase
public function testToArray()
{
$dataArrayable = $this->getMock('yii\\base\\Arrayable');
$dataArrayable = $this->getMockBuilder('yii\\base\\Arrayable')->getMock();
$dataArrayable->method('toArray')->willReturn([]);
$this->assertEquals([], ArrayHelper::toArray($dataArrayable));
$this->assertEquals(['foo'], ArrayHelper::toArray('foo'));
@ -810,20 +810,22 @@ class ArrayHelperTest extends TestCase
/**
* This is expected to result in a PHP error.
* @expectedException \PHPUnit_Framework_Error
* @requires PHPUnit 6.0
*/
public function testGetValueNonexistingProperties1()
{
$this->expectException('PHPUnit\Framework\Error\Notice');
$object = new Post1();
$this->assertEquals(null, ArrayHelper::getValue($object, 'nonExisting'));
}
/**
* This is expected to result in a PHP error.
* @expectedException \PHPUnit_Framework_Error
* @requires PHPUnit 6.0
*/
public function testGetValueNonexistingProperties2()
{
$this->expectException('PHPUnit\Framework\Error\Notice');
$arrayObject = new \ArrayObject(['id' => 23], \ArrayObject::ARRAY_AS_PROPS);
$this->assertEquals(23, ArrayHelper::getValue($arrayObject, 'nonExisting'));
}

23
tests/framework/helpers/HtmlTest.php

@ -140,12 +140,17 @@ class HtmlTest extends TestCase
public function testCsrfMetaTagsEnableCsrfValidationWithoutCookieValidationKey()
{
$request = $this->getMock('yii\\web\\Request');
$request->method('enableCsrfValidation')->willReturn(true);
Yii::$app->set('request', $request);
$pattern = '<meta name="csrf-param" content="_csrf">%A<meta name="csrf-token">';
$actual = Html::csrfMetaTags();
$this->assertStringMatchesFormat($pattern, $actual);
$this->mockApplication([
'components' => [
'request' => [
'class' => 'yii\web\Request',
'enableCsrfValidation' => true,
]
],
]);
$this->expectException('yii\base\InvalidConfigException');
$this->expectExceptionMessage('yii\web\Request::cookieValidationKey must be configured with a secret key.');
Html::csrfMetaTags();
}
/**
@ -1458,7 +1463,7 @@ EOD;
$actual = Html::getAttributeValue($model, 'types');
$this->assertSame($expected, $actual);
$activeRecord = $this->getMock('yii\\db\\ActiveRecordInterface');
$activeRecord = $this->getMockBuilder('yii\\db\\ActiveRecordInterface')->getMock();
$activeRecord->method('getPrimaryKey')->willReturn(1);
$model->types = $activeRecord;
@ -1491,14 +1496,14 @@ EOD;
*/
public function testGetInputNameInvalidParamExceptionFormName()
{
$model = $this->getMock('yii\\base\\Model');
$model = $this->getMockBuilder('yii\\base\\Model')->getMock();
$model->method('formName')->willReturn('');
Html::getInputName($model, '[foo]bar');
}
public function testGetInputName()
{
$model = $this->getMock('yii\\base\\Model');
$model = $this->getMockBuilder('yii\\base\\Model')->getMock();
$model->method('formName')->willReturn('');
$expected = 'types';
$actual = Html::getInputName($model, 'types');

2
tests/framework/helpers/JsonTest.php

@ -30,7 +30,7 @@ class JsonTest extends TestCase
public function testEncode()
{
// Arrayable data encoding
$dataArrayable = $this->getMock('yii\\base\\Arrayable');
$dataArrayable = $this->getMockBuilder('yii\\base\\Arrayable')->getMock();
$dataArrayable->method('toArray')->willReturn([]);
$actual = Json::encode($dataArrayable);
$this->assertSame('{}', $actual);

30
tests/framework/i18n/FormatterTest.php

@ -237,7 +237,8 @@ class FormatterTest extends TestCase
],
[
'Wrong value is casted properly',
['NaN'], '0 millimeters', '0 mm'
['NaN'], '0 millimeters', '0 mm',
['yii\base\InvalidParamException', "'NaN' is not a numeric value"]
],
[
'Negative value works',
@ -293,18 +294,26 @@ class FormatterTest extends TestCase
/**
* @dataProvider lengthDataProvider
*/
public function testIntlAsLength($message, $arguments, $expected)
public function testIntlAsLength($message, $arguments, $expected, $_shortLength, $expectedException = [])
{
$this->ensureIntlUnitDataIsAvailable();
if ($expectedException !== []) {
$this->expectException($expectedException[0]);
$this->expectExceptionMessage($expectedException[1]);
}
$this->assertSame($expected, call_user_func_array([$this->formatter, 'asLength'], $arguments), 'Failed asserting that ' . $message);
}
/**
* @dataProvider lengthDataProvider
*/
public function testIntlAsShortLength($message, $arguments, $_, $expected)
public function testIntlAsShortLength($message, $arguments, $_length, $expected, $expectedException = [])
{
$this->ensureIntlUnitDataIsAvailable();
if ($expectedException !== []) {
$this->expectException($expectedException[0]);
$this->expectExceptionMessage($expectedException[1]);
}
$this->assertSame($expected, call_user_func_array([$this->formatter, 'asShortLength'], $arguments), 'Failed asserting that ' . $message);
}
@ -317,7 +326,8 @@ class FormatterTest extends TestCase
],
[
'Wrong value is casted properly',
['NaN'], '0 grams', '0 g'
['NaN'], '0 grams', '0 g',
['yii\base\InvalidParamException', "'NaN' is not a numeric value"]
],
[
'Negative value works',
@ -373,18 +383,26 @@ class FormatterTest extends TestCase
/**
* @dataProvider weightDataProvider
*/
public function testIntlAsWeight($message, $arguments, $expected)
public function testIntlAsWeight($message, $arguments, $expected, $_shortWeight, $expectedException = [])
{
$this->ensureIntlUnitDataIsAvailable();
if ($expectedException !== []) {
$this->expectException($expectedException[0]);
$this->expectExceptionMessage($expectedException[1]);
}
$this->assertSame($expected, call_user_func_array([$this->formatter, 'asWeight'], $arguments), 'Failed asserting that ' . $message);
}
/**
* @dataProvider weightDataProvider
*/
public function testIntlAsShortWeight($message, $arguments, $_, $expected)
public function testIntlAsShortWeight($message, $arguments, $_weight, $expected, $expectedException = [])
{
$this->ensureIntlUnitDataIsAvailable();
if ($expectedException !== []) {
$this->expectException($expectedException[0]);
$this->expectExceptionMessage($expectedException[1]);
}
$this->assertSame($expected, call_user_func_array([$this->formatter, 'asShortWeight'], $arguments), 'Failed asserting that ' . $message);
}

10
tests/framework/i18n/IntlTestHelper.php

@ -24,10 +24,18 @@ namespace yiiunit\framework\i18n {
{
static::$enableIntl = null;
if (strncmp($test->getName(false), 'testIntl', 8) === 0) {
static::$enableIntl = true;
if (version_compare(PHP_VERSION, '7.2.0.RC.1', '>=') && version_compare(PHP_VERSION, '7.2.0.RC.3', '<=')) {
// IntlDateFormatter::parse() is broken in PHP 7.2. Disabled INTL tests until regression is fixed:
// https://bugs.php.net/bug.php?id=75378
$test->markTestSkipped('intl extension is broken in PHP 7.2');
return;
}
if (!extension_loaded('intl')) {
$test->markTestSkipped('intl extension is not installed.');
}
static::$enableIntl = true;
} else {
static::$enableIntl = false;
}

2
tests/framework/rest/DataFilterTest.php

@ -49,7 +49,7 @@ class DataFilterTest extends TestCase
$model = $builder->getSearchModel();
$this->assertTrue($model instanceof DynamicModel);
$this->setExpectedException('yii\base\InvalidConfigException');
$this->expectException('yii\base\InvalidConfigException');
$builder->setSearchModel(new \stdClass());
}

10
tests/framework/validators/FileValidatorTest.php

@ -388,15 +388,19 @@ class FileValidatorTest extends TestCase
public function validMimeTypes()
{
return [
return array_filter([
['test.svg', 'image/*', 'svg'],
['test.jpg', 'image/*', 'jpg'],
['test.png', 'image/*', 'png'],
['test.png', 'IMAGE/*', 'png'],
['test.txt', 'text/*', 'txt'],
['test.xml', '*/xml', 'xml'],
// Disabled for PHP 7.2 RC because of regression:
// https://bugs.php.net/bug.php?id=75380
version_compare(PHP_VERSION, '7.2.0.RC.1', '>=') && version_compare(PHP_VERSION, '7.2.0.RC.3', '<=')
? null
: ['test.xml', '*/xml', 'xml'],
['test.odt', 'application/vnd*', 'odt'],
];
]);
}
public function invalidMimeTypes()

10
tests/framework/validators/data/mimeType/test.xml

@ -1,2 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<tag></tag>
<?xml version="1.0" encoding="UTF-8"?>
<application>
<message>
<from>SilverFire</from>
<to>World</to>
<body>Hello, world!</body>
</message>
</application>

2
tests/framework/widgets/ActiveFormTest.php

@ -116,7 +116,7 @@ HTML
$model = new DynamicModel(['name']);
$model->addRule(['name'], 'required');
$view = $this->getMock(View::className());
$view = $this->getMockBuilder(View::className())->getMock();
$view->method('registerJs')->with($this->matches("jQuery('#w0').yiiActiveForm([], {\"validateOnSubmit\":false});"));
$view->method('registerAssetBundle')->willReturn(true);

Loading…
Cancel
Save