Парсинг в PHP многомерного массива PostgreSQL

Oct 28, 2014 14:43


Суть проблемы в том, что база данных возвращает все поля как текст. В том числе и массивы,- упаковывая их отнюдь не как удобоваримый JSON, а в некотором своём формате. А нам этот формат не нужен, нам нужен массив. Поэтому в PHP ответ базы приходится распаковывать.

Не совсем в PHP. Я почитал сложным и ненадёжным писать парсер в PHP самому, а готового решения, вроде бы, нет. Поэтому обращаюсь из PHP к возможностям самого PostgreSQL.

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

$fieldValueArr=str_getcsv(trim($fieldValue, '{}'), ',', '"', '\\');// http://stackoverflow.com/questions/3068683/convert-postgresql-array-to-php-array //Массивы обычно имеют разделителем запятую, кроме box[], который имеет разделителем точку с запятой, но разделитель может быть переопределён настройкой typdelim для типа foreach($fieldValueArr as &$fieldValueArrEl){ //str_getcsv имеет в PHP 5.3.6 баг (https://bugs.php.net/bug.php?id=55413), поэтому нужна дополнительная постобработка для разэкранирования $fieldValueArrEl=preg_replace('/\\\\([\\"])/', '$1', $fieldValueArrEl); $fieldValueArrEl=$fieldValueArrEl==='NULL'?NULL:$this->parseResultField($fieldValueArrEl, $fieldType); }
Но упаковка многомерного массива за CSV в дополнительных скобочках уже не сойдёт. Нужно изощряться. Как-то так:

function parseResultFieldArray($fieldValue, $fieldType){ $escapedFieldValue= pg_escape_string($fieldValue);//в PHP 5.4.4+ лучше использовать pg_escape_literal //$fieldType для пущей надёжности можно пропускать через pg_escape_identifier, но только в PHP 5.4.4+ $resultResource=$this->simpleQuery("WITH arr(val) AS( SELECT '$escapedFieldValue'::{$fieldType}[] ), vals(val) AS( SELECT unnest(val) FROM arr ), dims(val) AS( SELECT generate_series(1, array_ndims(arr.val)) FROM arr ) SELECT (SELECT array_agg(vals.val) FROM vals), (SELECT string_agg(array_length(arr.val, dims.val)::text, ',') FROM arr CROSS JOIN dims)"); list($fieldValue, $fieldArrayDims)=pg_fetch_array($resultResource); $fieldValueArr=str_getcsv(trim($fieldValue, '{}'), ',', '"', '\\');// http://stackoverflow.com/questions/3068683/convert-postgresql-array-to-php-array //Массивы обычно имеют разделителем запятую, кроме box[], который имеет разделителем точку с запятой, но разделитель может быть переопределён настройкой typdelim для типа foreach($fieldValueArr as &$fieldValueArrEl){ //str_getcsv имеет в PHP 5.3.6 баг (https://bugs.php.net/bug.php?id=55413), поэтому нужна дополнительная постобработка для разэкранирования $fieldValueArrEl=preg_replace('/\\\\([\\"])/', '$1', $fieldValueArrEl); $fieldValueArrEl=$fieldValueArrEl==='NULL'?NULL:$this->parseResultField($fieldValueArrEl, $fieldType); } $fieldArrayDims=explode(',', $fieldArrayDims); array_shift($fieldArrayDims); $fieldArrayDims=array_reverse($fieldArrayDims); foreach($fieldArrayDims as $fieldArrayDim){ $fieldValueArr=array_chunk($fieldValueArr, $fieldArrayDim); } return $fieldValueArr; }
Попросту говоря, мы берём упаковку многомерного массива и суём её снова в запрос к базе, а вытаскиваем оным запросом тот же массив, но развёрнутый в одномерный (который мы уже знаем, как распаковать), плюс информацию о его измерениях, с помощью которой сворачиваем одномерный массив в многомерный.

Конечно, с помощью array_to_json вышло бы гораздо, гораздо проще. Но, увы, эта функция появилась в PostgreSQL 9.2, а мы только-только смогли перейти с версии 8.4 на 9.1.6.

php, Web-программирование, postgresql

Previous post Next post
Up