is->handle)) {
            throw new Exception\RuntimeException('dba_optimize failed');
        }
        return true;
    }

    /* reading */

    /**
     * Internal method to get an item.
     *
     * @param  string  $normalizedKey
     * @param  bool $success
     * @param  mixed   $casToken
     * @return mixed Data on success, null on failure
     * @throws Exception\ExceptionInterface
     */
    protected function internalGetItem(& $normalizedKey, & $success = null, & $casToken = null)
    {
        $options   = $this->getOptions();
        $namespace = $options->getNamespace();
        $prefix    = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();

        $this->_open();
        $value = dba_fetch($prefix . $normalizedKey, $this->handle);

        if ($value === false) {
            $success = false;
            return null;
        }

        $success = true;
        $casToken = $value;
        return $value;
    }

    /**
     * Internal method to test if an item exists.
     *
     * @param  string $normalizedKey
     * @return bool
     * @throws Exception\ExceptionInterface
     */
    protected function internalHasItem(& $normalizedKey)
    {
        $options   = $this->getOptions();
        $namespace = $options->getNamespace();
        $prefix    = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();

        $this->_open();
        return dba_exists($prefix . $normalizedKey, $this->handle);
    }

    /* writing */

    /**
     * Internal method to store an item.
     *
     * @param  string $normalizedKey
     * @param  mixed  $value
     * @return bool
     * @throws Exception\ExceptionInterface
     */
    protected function internalSetItem(& $normalizedKey, & $value)
    {
        $options     = $this->getOptions();
        $namespace   = $options->getNamespace();
        $prefix      = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
        $internalKey = $prefix . $normalizedKey;

        $this->_open();
        if (!dba_replace($internalKey, $value, $this->handle)) {
            throw new Exception\RuntimeException("dba_replace('{$internalKey}', ...) failed");
        }

        return true;
    }

    /**
     * Add an item.
     *
     * @param  string $normalizedKey
     * @param  mixed  $value
     * @return bool
     * @throws Exception\ExceptionInterface
     */
    protected function internalAddItem(& $normalizedKey, & $value)
    {
        $options     = $this->getOptions();
        $namespace   = $options->getNamespace();
        $prefix      = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
        $internalKey = $prefix . $normalizedKey;

        $this->_open();

        // Workaround for PHP-Bug #54242 & #62489
        if (dba_exists($internalKey, $this->handle)) {
            return false;
        }

        // Workaround for PHP-Bug #54242 & #62489
        // dba_insert returns true if key already exists
        ErrorHandler::start();
        $result = dba_insert($internalKey, $value, $this->handle);
        $error  = ErrorHandler::stop();
        if (!$result || $error) {
            return false;
        }

        return true;
    }

    /**
     * Internal method to remove an item.
     *
     * @param  string $normalizedKey
     * @return bool
     * @throws Exception\ExceptionInterface
     */
    protected function internalRemoveItem(& $normalizedKey)
    {
        $options     = $this->getOptions();
        $namespace   = $options->getNamespace();
        $prefix      = ($namespace === '') ? '' : $namespace . $options->getNamespaceSeparator();
        $internalKey = $prefix . $normalizedKey;

        $this->_open();

        // Workaround for PHP-Bug #62490
        if (!dba_exists($internalKey, $this->handle)) {
            return false;
        }

        return dba_delete($internalKey, $this->handle);
    }

    /* status */

    /**
     * Internal method to get capabilities of this adapter
     *
     * @return Capabilities
     */
    protected function internalGetCapabilities()
    {
        if ($this->capabilities === null) {
            $marker       = new stdClass();
            $capabilities = new Capabilities(
                $this,
                $marker,
                array(
                    'supportedDatatypes' => array(
                        'NULL'     => 'string',
                        'boolean'  => 'string',
                        'integer'  => 'string',
                        'double'   => 'string',
                        'string'   => true,
                        'array'    => false,
                        'object'   => false,
                        'resource' => false,
                    ),
                    'minTtl'             => 0,
                    'supportedMetadata'  => array(),
                    'maxKeyLength'       => 0, // TODO: maxKeyLength ????
                    'namespaceIsPrefix'  => true,
                    'namespaceSeparator' => $this->getOptions()->getNamespaceSeparator(),
                )
            );

            // update namespace separator on change option
            $this->getEventManager()->attach('option', function ($event) use ($capabilities, $marker) {
                $params = $event->getParams();

                if (isset($params['namespace_separator'])) {
                    $capabilities->setNamespaceSeparator($marker, $params['namespace_separator']);
                }
            });

            $this->capabilities     = $capabilities;
            $this->capabilityMarker = $marker;
        }

        return $this->capabilities;
    }

    /**
     * Open the database if not already done.
     *
     * @return void
     * @throws Exception\LogicException
     * @throws Exception\RuntimeException
     */
    protected function _open()
    {
        if (!$this->handle) {
            $options = $this->getOptions();
            $pathname = $options->getPathname();
            $mode     = $options->getMode();
            $handler  = $options->getHandler();

            if ($pathname === '') {
                throw new Exception\LogicException('No pathname to database file');
            }

            ErrorHandler::start();
            $dba =  dba_open($pathname, $mode, $handler);
            $err = ErrorHandler::stop();
            if (!$dba) {
                throw new Exception\RuntimeException(
                    "dba_open('{$pathname}', '{$mode}', '{$handler}') failed", 0, $err
                );
            }
            $this->handle = $dba;
        }
    }

    /**
     * Close database file if opened
     *
     * @return void
     */
    protected function _close()
    {
        if ($this->handle) {
            ErrorHandler::start(E_NOTICE);
            dba_close($this->handle);
            ErrorHandler::stop();
            $this->handle = null;
        }
    }
}