Skip to content
A Python with-statement clone for PHP.
Branch: master
Clone or download
Latest commit 9bf308f Jan 21, 2019

README.md

With

This function simply mocks Python's with statement for PHP.

Build Status Coverage Status StyleCI License

Installation

The recommended way to install is through composer.

$ composer require ohmybrew/with

Usage

The with statement is used to wrap the execution of code with methods defined by a object. This allows common try...except...finally usage patterns to be encapsulated for convenient reuse.

A with statement is defined as followed: with([object], [callback]);.

The executation of a with statement is done as followed:

  1. The [object]'s __enter() method is invoked
  2. The return value from __enter() is assigned to the first argument of the [callback]

Note: The with statement guarantees that if the __enter() method returns without an error, then __exit() will always be called. Thus, if an error occurs during __enter(), it will be treated the same as an error occurring within the [callback] would be. See step 4 below

  1. The [callback] is executed
  2. The [object]'s __exit() method is invoked. If an exception caused the [callback] to be exited, the return value from __enter() and the [callback]'s exception are passed as arguments to __exit(). Otherwise, only the return value from __enter() is passed and the exception is set to null

If the [callback] was exited due to an exception, and the return value from the __exit() method was false, the exception is rethrown. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.

Here is a sample object and a with-statement:

class Foo
{
    private $db;

    //.. other code ..//

    public function __enter()
    {
        $db = new PDO(
                sprintf('%s:host=%s;dbname=%s', $this->config['driver'], $this->config['host'], $this->config['db']),
                $this->config['user'],
                $this->config['pass']
        );

        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        return $db;
    }

    public function __exit($db, $error = null)
    {
        if ($error instanceof Exception) {
            $this->log('We had en error; Rolling back.');

            $db->rollback();
        } else {
            $this->log('Code ran fine. Committed.');

            $db->commit();
        }

        return true;
    }
}

$foo = new Foo;
OhMyBrew\with($foo, function($db) {
    $db->beginTransaction();

    $foo = 'ohmybrew';
    $sql = $db->prepare("INSERT INTO non_existant_table SET name = :foo");
    $sql->bindParam('foo', $foo, PDO::PARAM_STR);
    $sql->execute();
});

You're also free to implement the interface provided to ensure you're classes are compatible:

use OhMyBrew\Withable;

class Foo implements Withable
{
    // ...
}

The above example is processed as follows:

  • with will call $foo->__enter()
  • $foo->__enter() will setup the database, and return the PDO object
  • with will now pass the PDO object to the callback as $db for use within the closure
  • with now executes the callback closure
  • The callback will throw an exception because the table does not exist
  • with now calls $foo->__exit() and passed the $db object and the exception from the callback to it
  • $foo->__exit() now checks for a exception and rollsback the changes. It returns true to suppress re-throwing the exception
  • with now checks the return from $foo->__exit(), it sees it returns a true value, and does not re-throw the exception

Requirements

Usage

See examples/ for some basic usage code or this README.

You can’t perform that action at this time.