diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 5a7da0ef23..c5b76e8208 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -7,6 +7,7 @@ Yii Framework 2 Change Log - Bug #19243: Handle `finfo_open` for tar.xz as `application/octet-stream` on PHP 8.1 (longthanhtran) - Bug #19235: Fix return type compatibility of `yii\web\SessionIterator` class methods for PHP 8.1 (virtual-designer) - Bug #19291: Reset errors and validators in `yii\base\Model::__clone()` (WinterSilence) +- Bug #19303: Fix serialization in `yii\caching\Dependency::generateReusableHash()` (WinterSilence) - Enh #19304: Add filtering validator `yii\validators\TrimValidator` (WinterSilence) - Enh #19309: Optimize `yii\base\Model::attributes()` (WinterSilence) - Bug #19322: Revert force setting value to empty string in case it's `null` in `yii\validators\FilterValidator::validateAttribute()` (bizley) diff --git a/framework/caching/Dependency.php b/framework/caching/Dependency.php index 9093e8ef19..a0a02f16a8 100644 --- a/framework/caching/Dependency.php +++ b/framework/caching/Dependency.php @@ -99,16 +99,28 @@ abstract class Dependency extends \yii\base\BaseObject /** * Generates a unique hash that can be used for retrieving reusable dependency data. + * * @return string a unique hash value for this cache dependency. * @see reusable */ protected function generateReusableHash() { - $data = $this->data; - $this->data = null; // https://github.com/yiisoft/yii2/issues/3052 - $key = sha1(serialize($this)); - $this->data = $data; - return $key; + $clone = clone $this; + $clone->data = null; // https://github.com/yiisoft/yii2/issues/3052 + + try { + $serialized = serialize($clone); + } catch (\Exception $e) { + // unserializable properties are nulled + foreach ($clone as $name => $value) { + if (is_object($value) && $value instanceof \Closure) { + $clone->{$name} = null; + } + } + $serialized = serialize($clone); + } + + return sha1($serialized); } /** diff --git a/tests/framework/caching/DbQueryDependencyTest.php b/tests/framework/caching/DbQueryDependencyTest.php index 0d718544f7..f46c952f1f 100644 --- a/tests/framework/caching/DbQueryDependencyTest.php +++ b/tests/framework/caching/DbQueryDependencyTest.php @@ -108,4 +108,30 @@ class DbQueryDependencyTest extends DatabaseTestCase $this->assertTrue($dependency->isChanged($cache)); } + + /** + * @depends testCustomMethod + */ + public function testReusableAndCustomMethodCallback() + { + $db = $this->getConnection(false); + $cache = new ArrayCache(); + + $dependency = new DbQueryDependency(); + $dependency->db = $db; + $dependency->query = (new Query()) + ->from('dependency_item') + ->andWhere(['value' => 'not exist']); + $dependency->reusable = true; + $dependency->method = function (Query $query, $db) { + return $query->orWhere(['value' => 'initial'])->exists($db); + }; + + $dependency->evaluateDependency($cache); + $this->assertFalse($dependency->isChanged($cache)); + + $db->createCommand()->delete('dependency_item')->execute(); + + $this->assertTrue($dependency->isChanged($cache)); + } }