Skip to content

Fixed issues for someone who needs this in 2026 #5

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');
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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