Конвертировать postgresql hstore в массив php - PullRequest
10 голосов
/ 19 июля 2011

Есть ли хороший фрагмент кода php для преобразования postgresql hstore в массив php, который будет правильно преобразовывать NULL без кавычек в hstore в php NULL?

EG: предположим, у нас есть следующая строка hstore:

"k1"=>"v1", "k2"=>NULL, "k3"=>"NULL", "k4"=>"\"v4"
(aka SELECT '"k1"=>"v1","k2"=>NULL,"k3"=>"NULL","k4"=>"\\"v4"'::hstore;)

Как мы можем преобразовать это в следующий массив php?

массив ('k1' => 'v1', 'k2' => NULL, 'k3' => 'NULL', 'k4' => '\ "v4');

Я следую за следующим конвертером, но он не обрабатывает NULL без кавычек: https://github.com/chanmix51/Pomm/blob/master/Pomm/Converter/PgHStore.php

Ответы [ 11 ]

4 голосов
/ 20 июля 2011

Я считаю, что синтаксис будет примерно таким:

$pdo = new PDO( /*connection string*/ );
// h is the hstore column.
$stmt = $pdo->query( "SELECT (each(h)).key, (each(h)).value FROM <table name>" );
$output = array();
foreach( $stmt->fetchAll( PDO::FETCH_NUM ) as $row )
{
   // $row[ 0 ] is the key, $row[ 1 ] is the value.
   $output[ $row[ 0 ] ] = $row[ 1 ];
}
2 голосов
/ 11 декабря 2012

Я попытался использовать метод PmHStore от Pomm, однако он сломался под полдюжиной или около того разных обстоятельств. Я не помню их всех, но вот несколько, которые я помню:

  • Отсутствие встроенной поддержки PHP Null
  • Отсутствие правильно экранированных двойных кавычек
  • Не удалось правильно экранировать значения для безопасной вставки PostgreSQL

В конечном итоге я получил собственное решение, придуманное PHPG. Поддерживает автоматическое преобразование массивов любого типа данных, Hstores, Geometric-type-типов, дат / временных меток и т. Д .: https://github.com/JDBurnZ/PHPG

Если вы хотите решить эту проблему без использования внешнего класса или библиотеки, это будет очень трудным и, скорее всего, будет надежным, и вот почему:

Можно предположить, что вы можете просто взорваться на ",", чтобы создать список пар ключ / значение, из которого вы можете взорвать каждый элемент в списке на "=>". Это работает, если все значения являются строками, но PostgreSQL Hstores поддерживает значения NULL. Значения NULL не заключаются в двойные кавычки, поэтому приведенное выше решение не будет работать надлежащим образом в этих сценариях.

2 голосов
/ 12 марта 2012
$hstore = '"A"=>"AAA", "B"=>"BBB"';
print_r(json_decode('{' . str_replace('"=>"', '":"', $hstore) . '}', true));
1 голос
/ 11 сентября 2013

У меня была такая же проблема, поэтому я нашел следующее решение:

function hstore2array($data) {
    preg_match_all('/(?:"((?:\\\\"|[^"])+)"|(\w+))\s*=>\s*(?:"((?:\\\\"|[^"])*)"|(NULL))/ms',
        $data, $matches, PREG_SET_ORDER);
    $hstore = array();
    foreach ($matches as $set) {
        $key = $set[1] ? $set[1] : $set[2];
        $val = $set[4]=='NULL' ? null : $set[3];
        $hstore[$key] = $val;
    }
    return $hstore;
}

Это правильно распознало бы следующую строку:

"a"=>"1", "b"=>"", "c"=>NULL, "d"=>"Some \"quoted\" value"
1 голос
/ 26 марта 2013

Поскольку ни одно из других решений, кажется, не работает идеально, я получил довольно консервативный подход, состоящий из двух частей:

protected function toPostgresHStoreLiteral(array $array) {
    return join(',', F\map($array, function ($value, $key) {
        switch (strtolower(gettype($value))) {
            case 'null' :
            case 'boolean' :
                $value = $value ? 'true' : '';
                break;

            case 'object' :
                if (!is_callable([$value, '__toString'])) {
                    throw new \InvalidArgumentException(sprintf('Cannot cast object of type %s to string', get_class($value)));
                }
                // deliberate fallthrough
            case 'integer' :
            case 'double' :
            case 'string' :
                settype($value, 'string');
                break;

            default :
                throw new \InvalidArgumentException(sprintf('Cannot store values of type %s in an hstore', gettype($value)));
        }

        return call_user_func_array('sprintf', array_reduce(
            [$key, $value],
            function (array $args, $value) { return array_merge($args, [sprintf('"%s"', addcslashes($value, '"\\'))]); },
            ['%s=>%s']
        ));
    }));
}

Этот метод форматирует массив в литеральную строку hstore, готовую для вставки в запрос. Это может быть немного более функционально в стиле, чем необходимо, извините за это. ^ _ ^ ;; Зависит от PHP 5.4+ и functions-php .

Чтобы получить значения hstore из Postgres, я использую JSON в качестве посредника:

SELECT array_to_json(hstore_to_array(value)) AS value ...

Получает кодированный в JSON массив, который можно превратить в обычный массив PHP, используя это:

protected function postgresJsonHstoreToArray($json) {
    $values = json_decode($json, true);
    $array  = [];

    for ($i = 0, $length = count($values); $i < $length; $i++) {
        $key   = $values[$i];
        $value = $values[++$i];
        $array[$key] = $value;
    }

    return $array;
}

Это зависит от Postgres 9.2+ или 9.1 с бэкпортом json_91 .

Это скопировано прямо из некоторого пользовательского кода, не обязательно очень обобщенного, но оно уже выдержало некоторое использование в реальном мире.

0 голосов
/ 09 января 2015

На всякий случай, если кому-то это все еще интересно: начиная с 9.2, есть функция hstore_to_json, которую можно использовать в предложениях select для преобразования содержимого hstore в JSON. Это будет выглядеть так:

SELECT id, hstore_to_json(my_hstore_field) AS myField FROM mytable WHERE ...

Тогда в PHP просто используйте

json_decode($row['myField'])

чтобы декодировать его в массив php ...

0 голосов
/ 19 июня 2014

Если вам нужно преобразовать строку в PHP (не в запросе db), вы можете использовать следующую подготовленную инструкцию (на основе решения cwallenpoole):

select (each(h)).key, (each(h)).value from (select ?::hstore as h) as s

Вы можете использовать любойПеременная PHP в этом запросе:

$st = $db->prepare("select (each(h)).key, (each(h)).value from (select ?::hstore as h) as s"); $st->execute(array('"abc"=>"123", "def"=>"654"'); $out = $st->fetchAll(PDO::FETCH_KEY_PAIR);

0 голосов
/ 12 сентября 2013

Другое решение:

function convertHstoreToArray($hstoreString) {
    $hstoreArray = array();
    // explode array elements
    $keyValueStringsArray = explode(', ', $hstoreString);
    foreach($keyValueStringsArray as $keyValueString) {
        // trim first and last "
        $keyValueString = substr($keyValueString, 1, -1);
        // explode key and value
        $keyValueArray = explode('"=>"', $keyValueString);
        $hstoreArray[$keyValueArray[0]] = $keyValueArray[1];
    }
    return $hstoreArray;
}

или

function convertHstoreToArray($hstoreString) {
    $hstoreArray = array();
    // explode array elements
    $keyValueStringsArray = explode(', ', $hstoreString);
    foreach($keyValueStringsArray as $keyValueString) {
        // explode key and value
        $keyValueArray = explode('=>', $keyValueString);
        $key = $keyValueArray[0];
        // trim first and last "
        $key = substr($key, 1, -1);
        $value = $keyValueArray[1];
        if ($value === 'NULL') {
            $value = null;
        } else {
            // trim first and last "
            $value = substr($value, 1, -1);
        }
        $hstoreArray[$key] = $value;
    }
    return $hstoreArray;
}
0 голосов
/ 11 апреля 2013

Попробуйте это:

<?php
function encode_hstore($array) {
    if (!$array)
        return NULL;

    if (!is_array($array))
        return $array;

    $expr = array();

    foreach ($array as $key => $val) {
        $search = array('\\', "'", '"');
        $replace = array('\\\\', "''", '\"');

        $key = str_replace($search, $replace, $key);
        $val = $val === NULL
             ? 'NULL'
             : '"'.str_replace($search, $replace, $val).'"';

        $expr[] = sprintf('"%s"=>%s', $key, $val);
    }

    return sprintf("'%s'::hstore", implode(',', $expr));
}

function decode_hstore($hstore) {
    if (!$hstore || !preg_match_all('/"(.+)(?<!\\\)"=>(""|NULL|".+(?<!\\\)"),?/U', $hstore, $match, PREG_SET_ORDER))
        return array();

    $array = array();

    foreach ($match as $set) {
        list(, $k, $v) = $set;

        $v = $v === 'NULL'
           ? NULL
           : substr($v, 1, -1);

        $search = array('\"', '\\\\');
        $replace = array('"', '\\');

        $k = str_replace($search, $replace, $k);
        if ($v !== NULL)
            $v = str_replace($search, $replace, $v);

        $array[$k] = $v;
    }

    return $array;
}

$dsn = 'pgsql:host=127.0.0.1;dbname=test';
$user = 'user';
$pass = 'pass';
$pdo = new \PDO($dsn, $user, $pass);

$data = array(
    'k1' => 'v1',
    'k2' => NULL,
    'k3' => 'NULL',
    'k4' => '"v4',
    'k5' => 'a\'b"c\\d,e',
    'k6' => '"k1"=>"v1", ',
    'k7"=>' => 'v7',
    'k8' => '',
);
var_dump($data);

$expr = encode_hstore($data);
echo $expr . PHP_EOL;

$encoded = $pdo->query("select {$expr}")->fetchColumn();
echo $encoded . PHP_EOL;

$decoded = decode_hstore($encoded);
var_dump($decoded);
0 голосов
/ 28 февраля 2013

Использование:

hstore (, false) преобразует входные данные в допустимый строковый литерал hStore и возвращает его:

hstore(array('k1' => 'v1', 'k2' => 'v2')) => "k1"=>"v1","k2"=>"v2"

hstore () преобразует ввод в действительный hStore, заключенный в одинарные кавычки, за которым следует :: hstore

hstore(array('k1' => 'v1', 'k2' => 'v2')) => '"k1"=>"v1","k2"=>"v2"'::hstore

hstore () преобразует из строки hstore (как это происходит из запроса) в массив

hstore('"k1"=>"v1","k2"=>"v2"') => array('k1' => 'v1', 'k2' => 'v2')

Он обрабатывает значения NULL (в обе стороны) и правильно экранирует / удаляет ключи и значения.

<?php


/**
 * mixed hstore(mixed $input[, bool $prepared = false])
 *      Convert from hstore string to array, or from array/object to hstore.
 * Inner arrays/objects are serialized but deserialization is up to you; you
 * are expected to keep track of which fields you sent as non-scalars.
 *
 * @param mixed $input          A string (from hstore) or an array/object
 * @param type $prepared        Array or object to convert to hstore string
 * @return mixed                Depends on the input
 */
function hstore($input, $prepared=false)
{
    if (is_string($input))
    {
        if ($input === 'NULL')
        {
            $output = NULL;
        }
        else
        {
        $re = '_("|^)(.*?[^\\\\"])"=>"(.*?[^\\\\"])("|$)_s';
        preg_match_all($re, $input, $pairs);
        $mid = $pairs ? array_combine($pairs[2], $pairs[3]) : array();

            foreach ($mid as $k => $v)
            {
                $output[trim($k, '"')] = stripslashes($v);
            }
        }
    }
    elseif (is_null($input))
    {
        $output = $prepared  ? 'NULL::hstore' : 'NULL';
    }
    elseif (!is_scalar($input))
    {
        foreach ((array)$input as $k => $v)
        {
            !is_scalar($v) && ($v = serialize($v));
            $entries[] = '"' . addslashes($k) . '"=>' .
                         '"' . addslashes($v) . '"';
        }

        $mid = empty($entries) ? '' : join(', ', $entries);

        $output = $prepared ? "'{$mid}'::hstore" : $mid;
    }

    return $output;
}

?>

edit: добавлен переключатель 's' в регулярное выражение для правильной обработки перевода строки внутри ключей или значений

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...