On Wed, 5 Nov 2025 at 06:27, Edmond Dantes <[email protected]> wrote:
>
> If PHP applies unset or __enter__/__exit__ depending on whether an
> interface is implemented, it will introduce hidden behavior in the
> code, making it harder for developers to understand what is happening.
> Compare the two cases:
>
> ```php
> // I know for sure that Scope implements the interface
> // required to be used with "with"
> with $scope = new Scope() {}
>
> // I have no idea whether the File class implements
> // the required interface or not. It’s unclear what will happen in the end.
> with $file = new File("...") {}
>
> ```
>
> So, in Python you cannot use arbitrary objects in a with statement,
> only those that implement the __enter__ and __exit__ contract.

Hello Ed,

Thank you for your feedback. Regarding your concern about the clarity
when using a `use` statement with objects that may or may not
implement a `Disposable` interface, it does not matter to the
application developer whether a future `Disposable` interface is
implemented or not.

Consider this example:

```php
// PHP Builtin:
interface DisposableInterface {
   public function dispose(?Throwable $error): void;
}

// Library Code:
interface DatabaseTransaction extends DisposableInterface {
  public function execute(string $q): void;
}

interface DatabaseConnection {
   public function beingTransaction(): DatabaseTransaction;
}

interface DatabasePool {
   public function getConnection(): DatabaseConnection;
}

// Application Code:
function do_work(DatabasePool $pool): void {
  using (
    $connection = $pool->getConnection(),
  ) {
    using ($transaction = $connection->beingTransaction()) {
      $transaction->execute('...');
      sleep(10); // more work.
      $transaction->execute('...');
    }

    sleep(10); // more work
  }

  sleep(10); // more work
}
```

In this scenario, the library author might not implement `Disposable`
for the `DatabaseConnection` because its internal handle is
automatically closed on `__destruct`, so to them, `Disposable` adds no
value. However, for the `DatabaseTransaction`, they do implement it,
as it allows the transaction to commit or rollback based on the exit
status.

>From the application developer's perspective, both are temporary
resources that are "allocated" and will be disposed of after the
scope. How they are disposed of is decided by the maintainer of that
resource (in this example, a third-party library). They might feel
`__destruct` is sufficient (e.g., for a socket to be closed), or they
might feel the need for `Disposable` to perform a specific action
based on whether the operation finished successfully.

Thanks,
Seifeddine

Reply via email to