{"record":{"id":"03e031b84d08baf0","repo":"phalcon/cphalcon","slug":"cannot-disconnect-a-decorated-connection-instance","errorCode":null,"errorMessage":"Cannot disconnect a Decorated connection instance","messagePattern":"Cannot disconnect a Decorated connection instance","errorType":"exception","errorClass":"Phalcon\\DataMapper\\Pdo\\Exception\\CannotDisconnect","httpStatus":null,"severity":"error","filePath":"phalcon/DataMapper/Pdo/Connection/Decorated.zep","lineNumber":64,"sourceCode":"        this->setProfiler(profiler);\n    }\n\n    /**\n     * Connects to the database.\n     */\n    public function connect() -> void\n    {\n        // already connected\n    }\n\n    /**\n     * Disconnects from the database; disallowed with decorated PDO connections.\n     *\n     * @throws CannotDisconnect\n     */\n    public function disconnect() -> void\n    {\n        throw new CannotDisconnect(\n            \"Cannot disconnect a Decorated connection instance\"\n        );\n    }\n}\n","sourceCodeStart":46,"sourceCodeEnd":69,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/DataMapper/Pdo/Connection/Decorated.zep#L46-L69","documentation":"Connection\\Decorated wraps an already-constructed \\PDO instance. The wrapper does not own the underlying connection's lifecycle — connect() is a no-op ('already connected') and disconnect() always throws CannotDisconnect, because closing the shared PDO is the original owner's job.","triggerScenarios":"$decorated = new Decorated($pdo); $decorated->disconnect(); teardown/pool code that iterates all connections and calls disconnect() on each; reusing shutdown logic written for Connection against a Decorated instance.","commonSituations":"Connection registries or pools holding mixed Connection and Decorated instances; long-lived shared PDO handed to several components; test tear-down that disconnects everything it created.","solutions":["Skip teardown for wrapped instances: if (!$conn instanceof Connection\\Decorated) { $conn->disconnect(); }","Let the code that created the \\PDO manage its lifecycle (null the reference where it was constructed)","In pools, drop the reference instead of calling disconnect() on decorated connections","Make ownership explicit in design: whoever news up the \\PDO destroys it"],"exampleFix":"// before\nforeach ($this->connections as $conn) {\n    $conn->disconnect(); // CannotDisconnect on Decorated\n}\n\n// after\nforeach ($this->connections as $conn) {\n    if (!$conn instanceof \\Phalcon\\DataMapper\\Pdo\\Connection\\Decorated) {\n        $conn->disconnect();\n    }\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"use Phalcon\\DataMapper\\Pdo\\Connection\\Decorated;\n\nfunction canDisconnect(object $connection): bool\n{\n    return !$connection instanceof Decorated;\n}\n\n// foreach ($connections as $conn) { if (canDisconnect($conn)) $conn->disconnect(); }","tryCatchPattern":"use Phalcon\\DataMapper\\Pdo\\Connection\\Decorated;\nuse Phalcon\\DataMapper\\Pdo\\Exception\\CannotDisconnect;\n\ntry {\n    $connection->disconnect();\n} catch (CannotDisconnect $e) {\n    // wrapped PDO: lifecycle belongs to the code that created it; just drop the reference\n}","preventionTips":["Guard teardown loops with an instanceof Decorated check before calling disconnect()","Keep ownership rules explicit: whoever constructs the \\PDO disposes it","In pools, replace disconnect() with reference release for decorated connections"],"tags":["pdo","database","decorator","connection-lifecycle","phalcon"],"backgroundTag":"unsupported-operation","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}