mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-02 13:02:24 +08:00
Merge branch 'yiisoft:master' into master
This commit is contained in:
@ -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;
|
||||
/**
|
||||
@ -93,7 +93,7 @@ class BaseYii
|
||||
*/
|
||||
public static function getVersion()
|
||||
{
|
||||
return '2.0.49-dev';
|
||||
return '2.0.50-dev';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,15 +1,34 @@
|
||||
Yii Framework 2 Change Log
|
||||
==========================
|
||||
|
||||
2.0.49 under development
|
||||
2.0.50 under development
|
||||
------------------------
|
||||
|
||||
- Bug #19857: Fix AttributeTypecastBehavior::resetOldAttributes() causes "class has no attribute named" InvalidArgumentException (uaoleg)
|
||||
- Bug #19925: Improved PHP version check when handling MIME types (schmunk42)
|
||||
- Bug #19940: File Log writer without newline (terabytesoftw)
|
||||
- Bug #19951: Removed unneeded MIME file tests (schmunk42)
|
||||
- Bug #19950: Fix `Query::groupBy(null)` causes error for PHP 8.1: `trim(): Passing null to parameter #1 ($string) of type string is deprecated` (uaoleg)
|
||||
|
||||
|
||||
2.0.49 August 29, 2023
|
||||
----------------------
|
||||
|
||||
- Bug #9899: Fix caching a MSSQL query with BLOB data type (terabytesoftw)
|
||||
- Bug #16208: Fix `yii\log\FileTarget` to not export empty messages (terabytesoftw)
|
||||
- Bug #18859: Fix `yii\web\Controller::bindInjectedParams()` to not throw error when argument of `ReflectionUnionType` type is passed (bizley)
|
||||
- Bug #19857: Fix AttributeTypecastBehavior::resetOldAttributes() causes "class has no attribute named" InvalidArgumentException (uaoleg)
|
||||
- Bug #19868: Added whitespace sanitation for tests, due to updates in ICU 72 (schmunk42)
|
||||
- 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 #19906: Fixed multiline strings in the `\yii\console\widgets\Table` widget (rhertogh)
|
||||
- Bug #19908: Fix associative array cell content rendering in Table widget (rhertogh)
|
||||
- Bug #19911: Resolved inconsistency in `ActiveRecord::getAttributeLabel()` with regard of overriding in primary model labels for attributes of related model in favor of allowing such overriding for all levels of relation nesting (PowerGamer1)
|
||||
- Bug #19914: Fixed `ArrayHelper::keyExists()` and `::remove()` functions when the key is a float and the value is `null` (rhertogh)
|
||||
- Bug #19924: Fix `yii\i18n\Formatter` to not throw error `Unknown named parameter` under PHP 8 (arollmann)
|
||||
- Enh #19841: Allow jQuery 3.7 to be installed (wouter90)
|
||||
- Enh #19853: Added support for default value for `\yii\helpers\Console::select()` (rhertogh)
|
||||
- Bug #19868: Added whitespace sanitation for tests, due to updates in ICU 72 (schmunk42)
|
||||
- Enh #19884: Added support Enums in Query Builder (sk1t0n)
|
||||
- Enh #19920: Broadened the accepted type of `Cookie::$expire` from `int` to `int|string|\DateTimeInterface|null` (rhertogh)
|
||||
|
||||
|
||||
2.0.48.1 May 24, 2023
|
||||
|
||||
@ -100,6 +100,11 @@ Upgrade from Yii 2.0.45
|
||||
2.0.45 behavior, [introduce your own method](https://github.com/yiisoft/yii2/pull/19495/files).
|
||||
* `yii\log\FileTarget::$rotateByCopy` is now deprecated and setting it to `false` has no effect since rotating of
|
||||
the files is done only by copy.
|
||||
* `yii\validators\UniqueValidator` and `yii\validators\ExistValidator`, when used on multiple attributes, now only
|
||||
generate an error on a single attribute. Previously, they would report a separate error on each attribute.
|
||||
Old behavior can be achieved by setting `'skipOnError' => false`, but this might have undesired side effects with
|
||||
additional validators on one of the target attributes.
|
||||
See [issue #19407](https://github.com/yiisoft/yii2/issues/19407)
|
||||
|
||||
Upgrade from Yii 2.0.44
|
||||
-----------------------
|
||||
|
||||
@ -317,7 +317,7 @@ class DbCache extends Cache
|
||||
*/
|
||||
protected function getDataFieldName()
|
||||
{
|
||||
return $this->isVarbinaryDataField() ? 'convert(nvarchar(max),[data]) data' : 'data';
|
||||
return $this->isVarbinaryDataField() ? 'CONVERT(VARCHAR(MAX), [[data]]) data' : 'data';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -136,7 +136,11 @@ class Table extends Widget
|
||||
{
|
||||
$this->rows = array_map(function($row) {
|
||||
return array_map(function($value) {
|
||||
return empty($value) && !is_numeric($value) ? ' ' : $value;
|
||||
return empty($value) && !is_numeric($value)
|
||||
? ' '
|
||||
: (is_array($value)
|
||||
? array_values($value)
|
||||
: $value);
|
||||
}, array_values($row));
|
||||
}, $rows);
|
||||
return $this;
|
||||
@ -252,18 +256,32 @@ class Table extends Widget
|
||||
if ($index !== 0) {
|
||||
$buffer .= $spanMiddle . ' ';
|
||||
}
|
||||
|
||||
$arrayFromMultilineString = false;
|
||||
if (is_string($cell)) {
|
||||
$cellLines = explode(PHP_EOL, $cell);
|
||||
if (count($cellLines) > 1) {
|
||||
$cell = $cellLines;
|
||||
$arrayFromMultilineString = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($cell)) {
|
||||
if (empty($renderedChunkTexts[$index])) {
|
||||
$renderedChunkTexts[$index] = '';
|
||||
$start = 0;
|
||||
$prefix = $this->listPrefix;
|
||||
$prefix = $arrayFromMultilineString ? '' : $this->listPrefix;
|
||||
if (!isset($arrayPointer[$index])) {
|
||||
$arrayPointer[$index] = 0;
|
||||
}
|
||||
} else {
|
||||
$start = mb_strwidth($renderedChunkTexts[$index], Yii::$app->charset);
|
||||
}
|
||||
$chunk = Console::ansiColorizedSubstr($cell[$arrayPointer[$index]], $start, $cellSize - 4);
|
||||
$chunk = Console::ansiColorizedSubstr(
|
||||
$cell[$arrayPointer[$index]],
|
||||
$start,
|
||||
$cellSize - 2 - Console::ansiStrwidth($prefix)
|
||||
);
|
||||
$renderedChunkTexts[$index] .= Console::stripAnsiFormat($chunk);
|
||||
$fullChunkText = Console::stripAnsiFormat($cell[$arrayPointer[$index]]);
|
||||
if (isset($cell[$arrayPointer[$index] + 1]) && $renderedChunkTexts[$index] === $fullChunkText) {
|
||||
@ -339,6 +357,9 @@ class Table extends Widget
|
||||
if (is_array($val)) {
|
||||
return max(array_map('yii\helpers\Console::ansiStrwidth', $val)) + Console::ansiStrwidth($this->listPrefix);
|
||||
}
|
||||
if (is_string($val)) {
|
||||
return max(array_map('yii\helpers\Console::ansiStrwidth', explode(PHP_EOL, $val)));
|
||||
}
|
||||
return Console::ansiStrwidth($val);
|
||||
}, $column)) + 2;
|
||||
$this->columnWidths[] = $columnWidth;
|
||||
@ -388,6 +409,9 @@ class Table extends Widget
|
||||
if (is_array($val)) {
|
||||
return array_map('yii\helpers\Console::ansiStrwidth', $val);
|
||||
}
|
||||
if (is_string($val)) {
|
||||
return array_map('yii\helpers\Console::ansiStrwidth', explode(PHP_EOL, $val));
|
||||
}
|
||||
return Console::ansiStrwidth($val);
|
||||
}, $row));
|
||||
return max($rowsPerCell);
|
||||
|
||||
@ -183,15 +183,11 @@ class ActiveDataProvider extends BaseDataProvider
|
||||
$sort->attributes[$attribute] = [
|
||||
'asc' => [$attribute => SORT_ASC],
|
||||
'desc' => [$attribute => SORT_DESC],
|
||||
'label' => $model->getAttributeLabel($attribute),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
foreach ($sort->attributes as $attribute => $config) {
|
||||
if (!isset($config['label'])) {
|
||||
$sort->attributes[$attribute]['label'] = $model->getAttributeLabel($attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($sort->modelClass === null) {
|
||||
$sort->modelClass = $modelClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +191,12 @@ class Sort extends BaseObject
|
||||
* @since 2.0.33
|
||||
*/
|
||||
public $sortFlags = SORT_REGULAR;
|
||||
/**
|
||||
* @var string|null the name of the [[\yii\base\Model]]-based class used by the [[link()]] method to retrieve
|
||||
* attributes' labels. See [[link]] method for details.
|
||||
* @since 2.0.49
|
||||
*/
|
||||
public $modelClass;
|
||||
|
||||
|
||||
/**
|
||||
@ -363,7 +369,8 @@ class Sort extends BaseObject
|
||||
* @param array $options additional HTML attributes for the hyperlink tag.
|
||||
* There is one special attribute `label` which will be used as the label of the hyperlink.
|
||||
* If this is not set, the label defined in [[attributes]] will be used.
|
||||
* If no label is defined, [[\yii\helpers\Inflector::camel2words()]] will be called to get a label.
|
||||
* If no label is defined, it will be retrieved from the instance of [[modelClass]] (if [[modelClass]] is not null)
|
||||
* or generated from attribute name using [[\yii\helpers\Inflector::camel2words()]].
|
||||
* Note that it will not be HTML-encoded.
|
||||
* @return string the generated hyperlink
|
||||
* @throws InvalidConfigException if the attribute is unknown
|
||||
@ -388,6 +395,11 @@ class Sort extends BaseObject
|
||||
} else {
|
||||
if (isset($this->attributes[$attribute]['label'])) {
|
||||
$label = $this->attributes[$attribute]['label'];
|
||||
} elseif ($this->modelClass !== null) {
|
||||
$modelClass = $this->modelClass;
|
||||
/** @var \yii\base\Model $model */
|
||||
$model = $modelClass::instance();
|
||||
$label = $model->getAttributeLabel($attribute);
|
||||
} else {
|
||||
$label = Inflector::camel2words($attribute);
|
||||
}
|
||||
|
||||
@ -144,6 +144,7 @@ interface ActiveRecordInterface extends StaticInstanceInterface
|
||||
* // Use where() to ignore the default condition
|
||||
* // SELECT FROM customer WHERE age>30
|
||||
* $customers = Customer::find()->where('age>30')->all();
|
||||
* ```
|
||||
*
|
||||
* @return ActiveQueryInterface the newly created [[ActiveQueryInterface]] instance.
|
||||
*/
|
||||
|
||||
@ -282,7 +282,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
@ -290,7 +290,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
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);
|
||||
@ -1610,40 +1610,46 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
|
||||
/**
|
||||
* Returns the text label for the specified attribute.
|
||||
* If the attribute looks like `relatedModel.attribute`, then the attribute will be received from the related model.
|
||||
* The attribute may be specified in a dot format to retrieve the label from related model or allow this model to override the label defined in related model.
|
||||
* For example, if the attribute is specified as 'relatedModel1.relatedModel2.attr' the function will return the first label definition it can find
|
||||
* in the following order:
|
||||
* - the label for 'relatedModel1.relatedModel2.attr' defined in [[attributeLabels()]] of this model;
|
||||
* - the label for 'relatedModel2.attr' defined in related model represented by relation 'relatedModel1' of this model;
|
||||
* - the label for 'attr' defined in related model represented by relation 'relatedModel2' of relation 'relatedModel1'.
|
||||
* If no label definition was found then the value of $this->generateAttributeLabel('relatedModel1.relatedModel2.attr') will be returned.
|
||||
* @param string $attribute the attribute name
|
||||
* @return string the attribute label
|
||||
* @see generateAttributeLabel()
|
||||
* @see attributeLabels()
|
||||
* @see generateAttributeLabel()
|
||||
*/
|
||||
public function getAttributeLabel($attribute)
|
||||
{
|
||||
$labels = $this->attributeLabels();
|
||||
if (isset($labels[$attribute])) {
|
||||
return $labels[$attribute];
|
||||
} elseif (strpos($attribute, '.')) {
|
||||
$attributeParts = explode('.', $attribute);
|
||||
$neededAttribute = array_pop($attributeParts);
|
||||
|
||||
$relatedModel = $this;
|
||||
foreach ($attributeParts as $relationName) {
|
||||
if ($relatedModel->isRelationPopulated($relationName) && $relatedModel->$relationName instanceof self) {
|
||||
$relatedModel = $relatedModel->$relationName;
|
||||
} else {
|
||||
try {
|
||||
$relation = $relatedModel->getRelation($relationName);
|
||||
} catch (InvalidParamException $e) {
|
||||
return $this->generateAttributeLabel($attribute);
|
||||
}
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $relation->modelClass;
|
||||
$relatedModel = $modelClass::instance();
|
||||
}
|
||||
$model = $this;
|
||||
$modelAttribute = $attribute;
|
||||
for (;;) {
|
||||
$labels = $model->attributeLabels();
|
||||
if (isset($labels[$modelAttribute])) {
|
||||
return $labels[$modelAttribute];
|
||||
}
|
||||
|
||||
$labels = $relatedModel->attributeLabels();
|
||||
if (isset($labels[$neededAttribute])) {
|
||||
return $labels[$neededAttribute];
|
||||
$parts = explode('.', $modelAttribute, 2);
|
||||
if (count($parts) < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
list ($relationName, $modelAttribute) = $parts;
|
||||
|
||||
if ($model->isRelationPopulated($relationName) && $model->$relationName instanceof self) {
|
||||
$model = $model->$relationName;
|
||||
} else {
|
||||
try {
|
||||
$relation = $model->getRelation($relationName);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
break;
|
||||
}
|
||||
/* @var $modelClass ActiveRecordInterface */
|
||||
$modelClass = $relation->modelClass;
|
||||
$model = $modelClass::instance();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1774,7 +1780,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -322,7 +322,7 @@ class Migration extends Component implements MigrationInterface
|
||||
* // ...
|
||||
* 'column_name double precision null default null',
|
||||
* ```
|
||||
|
||||
*
|
||||
*
|
||||
* @param string $table the name of the table to be created. The name will be properly quoted by the method.
|
||||
* @param array $columns the columns (name => definition) in the new table.
|
||||
|
||||
@ -1049,7 +1049,7 @@ PATTERN;
|
||||
|
||||
/**
|
||||
* Sets the GROUP BY part of the query.
|
||||
* @param string|array|ExpressionInterface $columns the columns to be grouped by.
|
||||
* @param string|array|ExpressionInterface|null $columns the columns to be grouped by.
|
||||
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
|
||||
* The method will automatically quote the column names unless a column contains some parenthesis
|
||||
* (which means the column contains a DB expression).
|
||||
@ -1067,7 +1067,7 @@ PATTERN;
|
||||
{
|
||||
if ($columns instanceof ExpressionInterface) {
|
||||
$columns = [$columns];
|
||||
} elseif (!is_array($columns)) {
|
||||
} elseif (!is_array($columns) && !is_null($columns)) {
|
||||
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
$this->groupBy = $columns;
|
||||
|
||||
@ -460,10 +460,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
|
||||
$columnSchemas = $tableSchema->columns;
|
||||
foreach ($columns as $name => $value) {
|
||||
// @see https://github.com/yiisoft/yii2/issues/12599
|
||||
if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value) || $value === null)) {
|
||||
$phName = $this->bindParam($value, $params);
|
||||
if (isset($columnSchemas[$name]) && $columnSchemas[$name]->type === Schema::TYPE_BINARY && $columnSchemas[$name]->dbType === 'varbinary' && (is_string($value))) {
|
||||
// @see https://github.com/yiisoft/yii2/issues/12599
|
||||
$columns[$name] = new Expression("CONVERT(VARBINARY(MAX), $phName)", $params);
|
||||
$columns[$name] = new Expression('CONVERT(VARBINARY(MAX), ' . ('0x' . bin2hex($value)) . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,7 +327,12 @@ class BaseArrayHelper
|
||||
*/
|
||||
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]);
|
||||
|
||||
@ -608,17 +613,20 @@ class BaseArrayHelper
|
||||
* 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()]]
|
||||
|
||||
@ -299,6 +299,7 @@ return [
|
||||
'application/vnd.fuzzysheet' => 'fzs',
|
||||
'application/vnd.genomatix.tuxedo' => 'txd',
|
||||
'application/vnd.geogebra.file' => 'ggb',
|
||||
'application/vnd.geogebra.slides' => 'ggs',
|
||||
'application/vnd.geogebra.tool' => 'ggt',
|
||||
'application/vnd.geometry-explorer' => [
|
||||
'gex',
|
||||
@ -655,6 +656,7 @@ return [
|
||||
],
|
||||
'application/vnd.zzazz.deck+xml' => 'zaz',
|
||||
'application/voicexml+xml' => 'vxml',
|
||||
'application/wasm' => 'wasm',
|
||||
'application/widget' => 'wgt',
|
||||
'application/winhlp' => 'hlp',
|
||||
'application/wsdl+xml' => 'wsdl',
|
||||
|
||||
@ -282,6 +282,7 @@ $mimeTypes = [
|
||||
'geo' => 'application/vnd.dynageo',
|
||||
'gex' => 'application/vnd.geometry-explorer',
|
||||
'ggb' => 'application/vnd.geogebra.file',
|
||||
'ggs' => 'application/vnd.geogebra.slides',
|
||||
'ggt' => 'application/vnd.geogebra.tool',
|
||||
'ghf' => 'application/vnd.groove-help',
|
||||
'gif' => 'image/gif',
|
||||
@ -887,6 +888,7 @@ $mimeTypes = [
|
||||
'vxml' => 'application/voicexml+xml',
|
||||
'w3d' => 'application/x-director',
|
||||
'wad' => 'application/x-doom',
|
||||
'wasm' => 'application/wasm',
|
||||
'wav' => 'audio/x-wav',
|
||||
'wax' => 'audio/x-ms-wax',
|
||||
'wbmp' => 'image/vnd.wap.wbmp',
|
||||
@ -1001,7 +1003,8 @@ $mimeTypes = [
|
||||
'zmm' => 'application/vnd.handheld-entertainment+xml',
|
||||
];
|
||||
|
||||
if (PHP_VERSION_ID >= 80100) {
|
||||
# fix for bundled libmagic bug, see also https://github.com/yiisoft/yii2/issues/19925
|
||||
if ((PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80122) || (PHP_VERSION_ID >= 80200 && PHP_VERSION_ID < 80209)) {
|
||||
$mimeTypes = array_replace($mimeTypes, array('xz' => 'application/octet-stream'));
|
||||
}
|
||||
|
||||
|
||||
@ -460,7 +460,7 @@ class Formatter extends Component
|
||||
}
|
||||
$method = 'as' . $format;
|
||||
if ($this->hasMethod($method)) {
|
||||
return call_user_func_array([$this, $method], $params);
|
||||
return call_user_func_array([$this, $method], array_values($params));
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Unknown format type: $format");
|
||||
|
||||
@ -106,12 +106,18 @@ class FileTarget extends Target
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
$text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n";
|
||||
$trimmedText = trim($text);
|
||||
|
||||
if (empty($trimmedText)) {
|
||||
return; // No messages to export, so we exit the function early
|
||||
}
|
||||
|
||||
if (strpos($this->logFile, '://') === false || strncmp($this->logFile, 'file://', 7) === 0) {
|
||||
$logPath = dirname($this->logFile);
|
||||
FileHelper::createDirectory($logPath, $this->dirMode, true);
|
||||
}
|
||||
|
||||
$text = implode("\n", array_map([$this, 'formatMessage'], $this->messages)) . "\n";
|
||||
if (($fp = @fopen($this->logFile, 'a')) === false) {
|
||||
throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
|
||||
}
|
||||
|
||||
@ -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;
|
||||
/**
|
||||
|
||||
@ -51,7 +51,7 @@ class CookieCollection extends BaseObject implements \IteratorAggregate, \ArrayA
|
||||
* 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()
|
||||
@ -113,7 +113,18 @@ class CookieCollection extends BaseObject implements \IteratorAggregate, \ArrayA
|
||||
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()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -174,7 +185,7 @@ class CookieCollection extends BaseObject implements \IteratorAggregate, \ArrayA
|
||||
|
||||
/**
|
||||
* 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()
|
||||
|
||||
@ -401,12 +401,21 @@ class Response extends \yii\base\Response
|
||||
}
|
||||
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,
|
||||
@ -420,7 +429,7 @@ class Response extends \yii\base\Response
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,7 +405,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
|
||||
* 'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
|
||||
* ]
|
||||
* ```
|
||||
* See https://www.owasp.org/index.php/SameSite for more information about `sameSite`.
|
||||
* See https://owasp.org/www-community/SameSite for more information about `sameSite`.
|
||||
*
|
||||
* @throws InvalidArgumentException if the parameters are incomplete.
|
||||
* @see https://www.php.net/manual/en/function.session-set-cookie-params.php
|
||||
|
||||
Reference in New Issue
Block a user