Skip to content

Fixed issues for someone who needs this in 2026 #5

@alexchexes

Description

@alexchexes
function pg_parse_array(string $arraystring, bool $reset=true): array
{
    if ($arraystring === '') {
        throw new SQLException('['.__FUNCTION__.']: Empty string is not a Postgres array literal');
    }

    static $i = 0;
    if ($reset) {
        $i = 0;
    }

    $matches = [];
    
    // $indexer = 1;   // by default sql arrays start at 1
    $indexer = 0;

    // handle [2:4]= cases
    if (preg_match('/^\[(?P<index_start>\d+):(?P<index_end>\d+)]=/', substr($arraystring, $i), $matches)) {
        $indexer = (int)$matches['index_start'];
        $pos = strpos($arraystring, '{');
        if ($pos === false) {
            throw new SQLException('['.__FUNCTION__.']: Unexpected input (no "{")');
        }
        $i = $pos;
    }

    if (!isset($arraystring[$i]) || $arraystring[$i] !== '{') {
        throw new SQLException('['.__FUNCTION__.']: Unexpected input');
    }

    $i++;
    $work = [];
    $curr = '';
    $length = strlen($arraystring);
    $quoted = false;

    while ($i < $length) {
        switch ($arraystring[$i]) {
            case '{':
                $sub = pg_parse_array($arraystring, false);
                $work[$indexer++] = $sub;   // keep empty sub-arrays too
                break;
            case '}':
                $i++;
                if ($quoted || strlen($curr) > 0) {
                    if (!$quoted && strtoupper($curr) === 'NULL') {
                        $curr = null;
                    }
                    $work[$indexer++] = $curr; // will correctly add '' for {""}
                }
                return $work;
            case '\\':
                $i++;
                $curr .= $arraystring[$i];
                $i++;
                break;
            case '"':
                $quoted = true;
                $openq = $i;
                do {
                    $closeq = strpos($arraystring, '"' , $i + 1);
                    if ($closeq === false) {
                        throw new SQLException('['.__FUNCTION__.']: Unterminated quote');
                    }
                    
                    $escaped = $closeq > $openq
                        && preg_match('/(\\\\+)$/', substr($arraystring, $openq + 1, $closeq - ($openq + 1)), $matches)
                        && (strlen($matches[1]) % 2);

                    if ($escaped) {
                        $i = $closeq;
                    } else {
                        break;
                    }
                } while(true);

                if ($closeq <= $openq) {
                    throw new SQLException('['.__FUNCTION__.']: Unexpected condition');
                }

                $chunk = substr($arraystring, $openq + 1, $closeq - ($openq + 1));
                // Unescape Postgres array output escapes inside quoted elements:
                // \" -> "   and   \\ -> \
                $chunk = str_replace('\\\\', '\\', $chunk);
                $chunk = str_replace('\\"', '"', $chunk);

                $curr .= $chunk;

                $i = $closeq + 1;
                break;
            case ',':
                if ($quoted || strlen($curr) > 0) {
                    if (!$quoted && strtoupper($curr) === 'NULL') {
                        $curr = null;
                    }
                    $work[$indexer++] = $curr;
                }
                $curr = '';
                $quoted = false;
                $i++;
                break;
            default:
                $curr .= $arraystring[$i];
                $i++;
        }
    }

    throw new SQLException('['.__FUNCTION__.']: Unexpected line end');
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions