private function _replaceArguments($sql, $arguments)
    {
        if (!is_array($arguments)) {
            $arguments = array($arguments);
        }
        
        // searches string segments (startPos, endPos)
        $stringSegments = [];
        if (preg_match_all('/(["\'`])((?:\\\\\1|.)*?)\1/', $sql, $matches, PREG_OFFSET_CAPTURE)) {
            foreach ($matches[2] as $match) {
                $startPos = $match[1];
                $endPos = $startPos + strlen($match[0]);
                array_push($stringSegments, [$startPos, $endPos]);
            }
        }
        
        // searches arguments position
        $argsPos = [];
        preg_match_all('/\?/', $sql, $matches, PREG_OFFSET_CAPTURE);
        foreach ($matches[0] as $match) {
            array_push($argsPos, $match[1]);
        }

        // replaces arguments
        $matchCount = 0;
        $argCount = 0;
        return preg_replace_callback(
            '/\?/',
            function ($matches) use (&$argCount, &$matchCount, $arguments, $argsPos, $stringSegments) {
                $ret = $matches[0];
                
                if ($argCount < count($arguments)) {
                    // is the current match inside a quoted string?
                    $argPos = $argsPos[$matchCount];
                    $isInsideQuotedString = false;
                    foreach ($stringSegments as $segment) {
                        if ($argPos >= $segment[0] &&  $argPos < $segment[1]) {
                            $isInsideQuotedString = true;
                            break;
                        }
                    }
                    
                    if (!$isInsideQuotedString) {
                        $ret = $this->quote($arguments[$argCount++]);
                    }
                }
                
                $matchCount++;
                return $ret;
            },
            $sql
        );
    }