Суть проблемы в том, что база данных возвращает все поля как текст. В том числе и массивы,- упаковывая их отнюдь не как удобоваримый 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.