public function query($sql, $binders = array(), $suggestedClass = null, $debug = false)
    {
        //By keeping track of the last binding location, we can skip
        //binders that are within bound data
        $minBinderLocation = 0;

        if (!is_array($binders)) $binders = array($binders);

        if (!empty($binders)) {
            foreach ($binders as &$bound) {
                $bound = $this->provider->escapeItem($bound);

                $bindingLocation = strpos($sql, static::DATABASE_BINDING_TOKEN, $minBinderLocation);

                if ($bindingLocation !== false) {
                    $sql = substr_replace($sql, $bound, $bindingLocation, 1);

                    $minBinderLocation = $bindingLocation + mb_strlen($bound) + 1;
                }
            }
        }

        $data = $this->provider->query($sql, $suggestedClass);

        if (!empty($debug)) {
            $output = new \stdClass();
            $output->sql = $sql;
            $output->response = $data;
            $output->inputs = $binders;

            if (!empty($this->log))
            {
                $this->log->notice('Debug Output', [$output]);
            }
        }

        return $data;
    }