yii2: Logging breaks transaction when something with db connection is logged

Logging breaks transaction when something with db connection is logged. BaseVarDumper::exportInternal is using serialize which is closing db connection (calling close inside __sleep)

What steps will reproduce the problem?

Start transaction, do some changes in db and then log something including db connection.

$transaction = \Yii::$app->db->beginTransaction();
// do some changes in db
(new LocationZip([
            'zip' => 12345,
            'federation' => 'Sachsen'
]))->save();

// see changes exists in db
$newRecord = LocationZip::find()->where(['zip' => 12345,])->one();


// log something that has db connection inside (it could be stacktrace of exception)
\Yii::info(\Yii::$app->db);

// connection to db was closed while trying to serialize log message so transaction is lost
$newRecord = LocationZip::find()->where(['zip' => 12345,])->one();

// will throw exception because transaction doesn't exists anymore
$transaction->commit();

What is the expected result?

Logging should’t do side effects like closing db connections

Additional info

Q A
Yii version 2.0.12
PHP version 7.1
Operating system Ubuntu

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 19 (18 by maintainers)

Most upvoted comments

Resolved by commit 9e6f9e3b6d41c918718b953968cf631c6a03ad73

Unit test to reproduce

abstract class ConnectionTest extends DatabaseTestCase
{
    // ...

    public function testSerialize()
    {
        $connection = $this->getConnection(false, false);
        $connection->open();
        $serialized = serialize($connection);

        $this->assertNotNull($connection->pdo);

        $unserialized = unserialize($serialized);
        $this->assertInstanceOf('yii\db\Connection', $unserialized);
        $this->assertNull($unserialized->pdo);

        $this->assertEquals(123, $unserialized->createCommand('SELECT 123')->queryScalar());
    }
}

For me this is an unexpected PHP behavior as __sleep() applied to the instance itself, but it is as it is. The only possible solution I can see is unsetting PDO at __wakeup() instead of __sleep().

Statement unserialize(serialize($db)) has absolutely no purpose. It is unlikely you need to unserialize the serialized object withing the same process. Object serialization is performed to save object for the another process processing, thus serialization process should be aware of such possibility and prevent passing stream resources between different processes.