Skip to content

Commit

Permalink
Merge branch 'master' into arollmann-patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
samdark authored Aug 21, 2023
2 parents cd4a76c + e916e9d commit 43e97e0
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 30 deletions.
2 changes: 1 addition & 1 deletion docs/guide-ru/db-query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ $query->orderBy([
```

В данном коде, ключи массива - это имена столбцов, а значения массива - это соответствующее направление сортировки.
PHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировка по умолчанию.
PHP константа `SORT_ASC` определяет сортировку по возрастанию и `SORT_DESC` сортировку по убыванию.

Если `ORDER BY` содержит только простые имена столбцов, вы можете определить их с помощью столбцов, также
как и при написании обычного SQL. Например,
Expand Down
2 changes: 1 addition & 1 deletion framework/BaseYii.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class BaseYii
*/
public static $classMap = [];
/**
* @var \yii\console\Application|\yii\web\Application|\yii\base\Application the application instance
* @var \yii\console\Application|\yii\web\Application the application instance
*/
public static $app;
/**
Expand Down
3 changes: 3 additions & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Yii Framework 2 Change Log
2.0.49 under development
------------------------

- Bug #19872: Fixed the definition of dirty attributes in AR properties for a non-associative array in case of changing the order of elements (eegusakov)
- Bug #19899: Fixed `GridView` in some cases calling `Model::generateAttributeLabel()` to generate label values that are never used (PowerGamer1)
- Bug #9899: Fix caching a MSSQL query with BLOB data type (terabytesoftw)
- Bug #16208: Fix `yii\log\FileTarget` to not export empty messages (terabytesoftw)
Expand All @@ -16,6 +17,8 @@ Yii Framework 2 Change Log
- Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh)
- Bug #19906: Fixed multiline strings in the `\yii\console\widgets\Table` widget (rhertogh)
- Bug #19924: Fix `yii\i18n\Formatter` to not throw error `Unknown named parameter` under PHP 8 (arollmann)
- Bug #19914: Fixed `ArrayHelper::keyExists()` and `::remove()` functions when the key is a float and the value is `null` (rhertogh)
- Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\DateTimeInterface|null` (rhertogh)


2.0.48.1 May 24, 2023
Expand Down
6 changes: 3 additions & 3 deletions framework/db/BaseActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,15 +282,15 @@ public function canSetProperty($name, $checkVars = true, $checkBehaviors = true)
*/
public function __get($name)
{
if (isset($this->_attributes[$name]) || array_key_exists($name, $this->_attributes)) {
if (array_key_exists($name, $this->_attributes)) {
return $this->_attributes[$name];
}

if ($this->hasAttribute($name)) {
return null;
}

if (isset($this->_related[$name]) || array_key_exists($name, $this->_related)) {
if (array_key_exists($name, $this->_related)) {
return $this->_related[$name];
}
$value = parent::__get($name);
Expand Down Expand Up @@ -1774,7 +1774,7 @@ private function setRelationDependencies($name, $relation, $viaRelationName = nu
*/
private function isValueDifferent($newValue, $oldValue)
{
if (is_array($newValue) && is_array($oldValue) && !ArrayHelper::isAssociative($oldValue)) {
if (is_array($newValue) && is_array($oldValue) && ArrayHelper::isAssociative($oldValue)) {
$newValue = ArrayHelper::recursiveSort($newValue);
$oldValue = ArrayHelper::recursiveSort($oldValue);
}
Expand Down
18 changes: 13 additions & 5 deletions framework/helpers/BaseArrayHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,12 @@ public static function setValue(&$array, $path, $value)
*/
public static function remove(&$array, $key, $default = null)
{
if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
// ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)
if (is_float($key)) {
$key = (int)$key;
}

if (is_array($array) && array_key_exists($key, $array)) {
$value = $array[$key];
unset($array[$key]);

Expand Down Expand Up @@ -608,17 +613,20 @@ public static function map($array, $from, $to, $group = null)
* Checks if the given array contains the specified key.
* This method enhances the `array_key_exists()` function by supporting case-insensitive
* key comparison.
* @param string $key the key to check
* @param string|int $key the key to check
* @param array|ArrayAccess $array the array with keys to check
* @param bool $caseSensitive whether the key comparison should be case-sensitive
* @return bool whether the array contains the specified key
*/
public static function keyExists($key, $array, $caseSensitive = true)
{
// ToDo: This check can be removed when the minimum PHP version is >= 8.1 (Yii2.2)
if (is_float($key)) {
$key = (int)$key;
}

if ($caseSensitive) {
// Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case
// https://www.php.net/manual/en/function.array-key-exists.php#107786
if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) {
if (is_array($array) && array_key_exists($key, $array)) {
return true;
}
// Cannot use `array_has_key` on Objects for PHP 7.4+, therefore we need to check using [[ArrayAccess::offsetExists()]]
Expand Down
4 changes: 2 additions & 2 deletions framework/web/Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class Cookie extends \yii\base\BaseObject
*/
public $domain = '';
/**
* @var int the timestamp at which the cookie expires. This is the server timestamp.
* Defaults to 0, meaning "until the browser is closed".
* @var int|string|\DateTimeInterface|null the timestamp or date at which the cookie expires. This is the server timestamp.
* Defaults to 0, meaning "until the browser is closed" (the same applies to `null`).
*/
public $expire = 0;
/**
Expand Down
17 changes: 14 additions & 3 deletions framework/web/CookieCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function __construct($cookies = [], $config = [])
* Returns an iterator for traversing the cookies in the collection.
* This method is required by the SPL interface [[\IteratorAggregate]].
* It will be implicitly called when you use `foreach` to traverse the collection.
* @return ArrayIterator an iterator for traversing the cookies in the collection.
* @return ArrayIterator<string, Cookie> an iterator for traversing the cookies in the collection.
*/
#[\ReturnTypeWillChange]
public function getIterator()
Expand Down Expand Up @@ -113,7 +113,18 @@ public function getValue($name, $defaultValue = null)
public function has($name)
{
return isset($this->_cookies[$name]) && $this->_cookies[$name]->value !== ''
&& ($this->_cookies[$name]->expire === null || $this->_cookies[$name]->expire === 0 || $this->_cookies[$name]->expire >= time());
&& ($this->_cookies[$name]->expire === null
|| $this->_cookies[$name]->expire === 0
|| (
(is_string($this->_cookies[$name]->expire) && strtotime($this->_cookies[$name]->expire) >= time())
|| (
interface_exists('\\DateTimeInterface')
&& $this->_cookies[$name]->expire instanceof \DateTimeInterface
&& $this->_cookies[$name]->expire->getTimestamp() >= time()
)
|| $this->_cookies[$name]->expire >= time()
)
);
}

/**
Expand Down Expand Up @@ -174,7 +185,7 @@ public function removeAll()

/**
* Returns the collection as a PHP array.
* @return array the array representation of the collection.
* @return Cookie[] the array representation of the collection.
* The array keys are cookie names, and the array values are the corresponding cookie objects.
*/
public function toArray()
Expand Down
15 changes: 12 additions & 3 deletions framework/web/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,21 @@ protected function sendCookies()
}
foreach ($this->getCookies() as $cookie) {
$value = $cookie->value;
if ($cookie->expire != 1 && isset($validationKey)) {
$expire = $cookie->expire;
if (is_string($expire)) {
$expire = strtotime($expire);
} elseif (interface_exists('\\DateTimeInterface') && $expire instanceof \DateTimeInterface) {
$expire = $expire->getTimestamp();
}
if ($expire === null || $expire === false) {
$expire = 0;
}
if ($expire != 1 && isset($validationKey)) {
$value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);
}
if (PHP_VERSION_ID >= 70300) {
setcookie($cookie->name, $value, [
'expires' => $cookie->expire,
'expires' => $expire,
'path' => $cookie->path,
'domain' => $cookie->domain,
'secure' => $cookie->secure,
Expand All @@ -420,7 +429,7 @@ protected function sendCookies()
if (!is_null($cookie->sameSite)) {
$cookiePath .= '; samesite=' . $cookie->sameSite;
}
setcookie($cookie->name, $value, $cookie->expire, $cookiePath, $cookie->domain, $cookie->secure, $cookie->httpOnly);
setcookie($cookie->name, $value, $expire, $cookiePath, $cookie->domain, $cookie->secure, $cookie->httpOnly);
}
}
}
Expand Down
42 changes: 42 additions & 0 deletions tests/framework/db/BaseActiveRecordTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace yiiunit\framework\db;

use yiiunit\data\ar\ActiveRecord;

abstract class BaseActiveRecordTest extends DatabaseTestCase
{
protected function setUp()
{
parent::setUp();
ActiveRecord::$db = $this->getConnection();
}

public function provideArrayValueWithChange()
{
return [
'not an associative array with data change' => [
[1, 2, 3],
[1, 3, 2],
],

'associative array with data change case 1' => [
['pineapple' => 2, 'apple' => 5, 'banana' => 1],
['apple' => 5, 'pineapple' => 1, 'banana' => 3],
],
'associative array with data change case 2' => [
['pineapple' => 2, 'apple' => 5, 'banana' => 1],
['pineapple' => 2, 'apple' => 3, 'banana' => 1],
],

'filling an empty array' => [
[],
['pineapple' => 3, 'apple' => 1, 'banana' => 1],
],
'zeroing the array' => [
['pineapple' => 3, 'apple' => 1, 'banana' => 17],
[],
],
];
}
}
37 changes: 37 additions & 0 deletions tests/framework/db/mysql/BaseActiveRecordTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace yiiunit\framework\db\mysql;

use yiiunit\data\ar\Storage;

class BaseActiveRecordTest extends \yiiunit\framework\db\BaseActiveRecordTest
{
public $driverName = 'mysql';

/**
* @see https://github.com/yiisoft/yii2/issues/19872
*
* @dataProvider provideArrayValueWithChange
*/
public function testJsonDirtyAttributesWithDataChange($actual, $modified)
{
if (version_compare($this->getConnection()->getSchema()->getServerVersion(), '5.7', '<')) {
$this->markTestSkipped('JSON columns are not supported in MySQL < 5.7');
}
if (version_compare(PHP_VERSION, '5.6', '<')) {
$this->markTestSkipped('JSON columns are not supported in PDO for PHP < 5.6');
}

$createdStorage = new Storage(['data' => $actual]);

$createdStorage->save();

$foundStorage = Storage::find()->limit(1)->one();

$this->assertNotNull($foundStorage);

$foundStorage->data = $modified;

$this->assertSame(['data' => $modified], $foundStorage->getDirtyAttributes());
}
}
46 changes: 46 additions & 0 deletions tests/framework/db/pgsql/BaseActiveRecordTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace yiiunit\framework\db\pgsql;

use yii\db\JsonExpression;
use yiiunit\data\ar\ActiveRecord;

class BaseActiveRecordTest extends \yiiunit\framework\db\BaseActiveRecordTest
{
public $driverName = 'pgsql';

/**
* @see https://github.com/yiisoft/yii2/issues/19872
*
* @dataProvider provideArrayValueWithChange
*/
public function testJsonDirtyAttributesWithDataChange($actual, $modified)
{
$createdStorage = new ArrayAndJsonType([
'json_col' => new JsonExpression($actual),
]);

$createdStorage->save();

$foundStorage = ArrayAndJsonType::find()->limit(1)->one();

$this->assertNotNull($foundStorage);

$foundStorage->json_col = $modified;

$this->assertSame(['json_col' => $modified], $foundStorage->getDirtyAttributes());
}
}

/**
* {@inheritdoc}
* @property array id
* @property array json_col
*/
class ArrayAndJsonType extends ActiveRecord
{
public static function tableName()
{
return '{{%array_and_json_types}}';
}
}
Loading

0 comments on commit 43e97e0

Please sign in to comment.