Skip to content

TimedPublishSubscriber silently fails to publish/depublish content on doctrine/dbal 4.x #3748

Description

@Senne

Environment:

  • bolt/core: 6.1.3
  • doctrine/dbal: 4.4.3 (required by bolt/core as ^4.0)
  • PHP: 8.3.31

Description

Bolt\Event\Subscriber\TimedPublishSubscriber::onKernelRequest() is supposed to run on every kernel.request and automatically:

  • flip status: timedpublished once published_at has passed, and
  • flip status: publishedheld once depublished_at has passed.

On a project using doctrine/dbal: ^4.0 (as required by bolt/core's own composer.json), this never works, for three separate reasons, all hidden by the method's blanket catch (Throwable) { /* Fail silently */ } with no logging:

  1. It calls $conn->executeUpdate(...). Connection::executeUpdate() was removed in doctrine/dbal 4.0 (only executeStatement() remains), so every call throws:
    Error: Call to undefined method Doctrine\DBAL\Connection::executeUpdate()
    
  2. Even after switching to executeStatement(), the parameter array uses colon-prefixed keys ([':now' => $now]) instead of plain keys (['now' => $now]), which throws:
    Doctrine\DBAL\ArrayParameters\Exception\MissingNamedParameter: Named parameter "now" does not have a bound value.
    
  3. Once both of those are fixed, binding still fails because $now is a Carbon/DateTime object passed without an explicit DBAL type, throwing:
    Error: Object of class DateTime could not be converted to string
    
    This requires an explicit ['now' => Doctrine\DBAL\Types\Types::DATETIME_MUTABLE] type array.

Impact

Content with a "Depublish date" in the past keeps showing on the frontend indefinitely (status stays published), since nothing ever flips it to held. This affects every content type, not just one — it's a core scheduling mechanism that is completely inert on any project using doctrine/dbal 4.x.

Steps to reproduce

  1. Install bolt/core 6.1.3 with doctrine/dbal ^4.0.
  2. Set any content record to status = published with a depublished_at in the past.
  3. Visit any page (triggers kernel.request).
  4. Observe the record's status is still published (expected: held).
  5. Query/log the raw SQL — the UPDATE from TimedPublishSubscriber never even appears, because it errors out silently before executing.

Suggested fix (verified working)

// src/Event/Subscriber/TimedPublishSubscriber.php
use Doctrine\DBAL\Types\Types;

// ...

try {
    $conn->executeStatement($queryPublish, ['now' => $now], ['now' => Types::DATETIME_MUTABLE]);
    $conn->executeStatement($queryDepublish, ['now' => $now], ['now' => Types::DATETIME_MUTABLE]);
} catch (Throwable) {
    // Fail silently, output user-friendly exception elsewhere.
}

As a secondary concern, it may be worth logging the caught Throwable (even at debug level) instead of swallowing it completely, since that's what made this bug invisible for so long.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions