Подтвердить что ты не робот

PDO, возвращающее пустое имя свойства

У меня возникла странная проблема с pdo_odbc и PDO:: FETCH_OBJ (и PDO:: FETCH_CLASS), из-за чего появляется следующее сообщение об ошибке:

PHP Fatal error:  Cannot access empty property

Здесь код:

$dbh = new PDO("odbc:FOO");

$sth = $dbh->query("
  SELECT rolename
  FROM dbc.allrolerights
  WHERE databasename = 'BAR'
");

$result = $sth->fetch(PDO::FETCH_OBJ);

FOO DSN, для справки, является источником данных Teradata с использованием драйвера tdata.so, предоставляемого пакетом tdodbc.

Я считаю, что это происходит потому, что имя поля (как возвращено из запроса ODBC) пуст, когда PDO вызывает zend_API.h: object_init_ex() для создания объекта stdClass. Если я переключу на PDO:: FETCH_LAZY и var_dump() строку, я получаю следующее:

object(PDORow)#3 (2) {
  ["queryString"]=>
  string(95) "
  SELECT rolename
  FROM dbc.allrolerights
  WHERE databasename = 'BAR'
"
  [""]=>
  string(30) "FNAR                          "
}

Который, кажется, поддерживает это. Я пробовал несколько различных сочетаний атрибутов PDO и множество разных углов для решения этой проблемы. Одним из решений является выбор ассоциативного массива и передача его в конструктор класса. Однако это не работает для определенных фреймворков и ORM, которые используют PDO:: FETCH_CLASS напрямую, за кулисами.

Я хочу добавить, что другие методы выборки, похоже, делают правильные вещи, например PDO:: FETCH_NAMED:

array(1) {
  ["RoleName"]=>
  string(30) "FNAR                          "
}

Я ищу что-то, что я могу внести в определение PDO dbh или sth, или в odbc.ini или odbcinst.ini для источника данных или драйвера, чтобы решить эту проблему. Заранее спасибо.

Обновление: odbc_fetch_object() (т.е. не PDO) отлично работает с тем же самым полным. Просто хотел упомянуть об этом. Очевидно, что нет никаких серьезных проблем с PHP, unixODBC или драйвером ODBC. Это что-то в коде PDO. Время, чтобы открыть отчет об ошибке... открыт

$dbh = odbc_connect("FOO", NULL, NULL)
  or die(odbc_error_msg());

$sth = odbc_exec($dbh, "
  SELECT rolename
  FROM dbc.allrolerights
  WHERE databasename = 'BAR'
");

$result = odbc_fetch_object($sth);
var_dump($result);

И вывод:

object(stdClass)#1 (1) {
  ["RoleName"]=>
  string(30) "FNAR                          "
}

Обновление 2: Ситуация продолжает расти все более странно. Я могу сделать PDO:: FETCH_LAZY и увидеть пустое имя столбца, как показано выше в var_dump(), но если я попытаюсь получить доступ к свойству по имени (например, $result- > RoleName), он будет работать! Какие методы выборки делают так по-другому, что некоторые из них иногда могут обращаться к именам полей, а другие не могут?

Сравнительные сравнения ODBC-следов ( "рабочий", cf. "не работает" ) не показывает различий (кроме разных адресов указателей). PDO:: FETCH_BOUND работает как с нумерованными, так и с именованными столбцами. PDO:: FETCH_INTO объекта с свойством RoleName нет.

4b9b3361

Ответ 1

В вашем вопросе описаны две проблемы:

  • Почему не удается создать объекты с свойствами, имеющими имена пустой строки при использовании PDO::FETCH_OBJ, но, видимо, при использовании других методов?

    Как описано в Внутренние структуры и импликация в книге внутренних языков PHP, "dynamic properties" (т.е. переменные-члены объекта, которые не объявлены в определении класса, а скорее созданы во время выполнения) реализованы как хэш-таблица.

    Если вы хотите заполнить новый экземпляр стандартного объекта кучей свойств, которые в настоящее время хранятся в хеш-таблице, можно просто указать переменную object properties в этой существующей хеш-таблице — PHP object_and_properties_init() function, которая вызываемый odbc_fetch_object(), делает именно это без выполнения любые проверки работоспособности на клавишах таблицы. Следовательно, можно создать экземпляр объекта со странными именами свойств (например, пустую строку).

    С другой стороны, если у вас уже есть экземпляр объекта и ему нужно установить значение свойства (сохраняя все остальные, которые уже существуют), нужно скопировать это значение в метод хеш-таблицы объекта — PHP zend_std_write_property(), который обрабатывает это действие для стандартных объектов, делает именно это, сначала выполнив проверку работоспособности имени свойства. Следовательно, нельзя добавить свойство с странным именем (например, пустую строку) к существующему объекту.

    Это несоответствие в проверке здравомыслия между двумя подходами, по моему мнению, является ошибкой: любые ограничения на имена динамических свойств должны выполняться независимо от метода создания таких свойств. Следует ли допускать странные имена такого рода (и, следовательно, проверка работоспособности должна быть удалена из последнего метода) или запрещена (и, следовательно, какая-либо проверка здравомыслия должна быть добавлена ​​к первому методу), это решение, которое я должен оставить Разработчики PHP.

    Как PDO вписывается во все это?

    PDOStatement::fetch() сначала подготавливает пункт назначения, в который будут сохраняться результаты, а затем выполняет итерации по столбцам, сохраняя каждое поле по очереди: делает это, чтобы упростить кодовую базу, поскольку каждый стиль выборки может быть реализован в рамках одной и той же структуры. Однако это означает, что при вызове с использованием стиля PDO::FETCH_OBJ (а также и PDO::FETCH_CLASS и PDO::FETCH_INTO, как вы это заметили), объект созданный в первый раз и его свойства заселяются позже. Следовательно, странные имена свойств (такие как пустая строка) приводят к наблюдаемому сбою.

    Другие стили выборки, которые вы пробовали, не испытывают той же проблемы, потому что:

    • PDO::FETCH_BOUND извлекает в переменные, которые были указаны предыдущим вызовом PDOStatement::bindColumn(), поэтому PHP никогда не пытается записать свойство, имеющее пустое имя;

    • PDO::FETCH_LAZY пропускает весь shebang и делает вещи похожими на odbc_fetch_object() выше.

    Аналогично, стили выборки на основе массива не будут иметь подобных проблем, поскольку ключи с пустой строкой отлично подходят в этих хэш-таблицах.

  • Почему PDO считает имена столбцов в этом наборе записей ODBC пустыми?

    Ответ на этот вопрос мне гораздо менее очевиден.

    Ранее мы видели, что для заполнения свойств PDO использует stmt->columns[i].name как имя свойства. Это должно было быть правильно заполнено на более раннем пункте, когда pdo_stmt_describe_columns() было называется. Эта функция, в свою очередь, вызвала метод describer драйвера для каждого столбца в наборе результатов: в случае PDO_ODBC это odbc_stmt_describe(), которое делает действительно присваивать значение этому полю.

    Итак, все выглядит отлично на стороне PHP. Было бы интересно узнать, будет ли вызов функции SQLDescribeCol() драйвераправильно заполнил имя столбца в буфер, предоставленный в качестве третьего аргумента: никто не представляет, что предполагает, что проблема заключается в самом драйвере ODBC. Вы упомянули, что используете Teradata: но вы уверены, что используете один и тот же драйвер для PDO_ODBC (который не работает) и ext/odbc (который)?

    В частности, документ Teradata под Функции уровня расширения:

    По умолчанию SQLDescribeCol и SQLColAttribute возвращают имя столбца вместо заголовка столбца Teradata. Если приложение хочет, чтобы драйвер ODBC для Teradata возвращал заголовок столбца вместо фактического имени столбца, тогда опция Использовать имена столбцов в диалоговом окне Teradata ODBC Driver Options не должна выбирается для используемого DSN или задает DontUseTitles = No на ОС UNIX.

    Возвращение названия столбца вместо фактического имени столбца может вызвать проблемы для определенных приложений, таких как Crystal Reports, потому что они ожидают получить имя столбца, а не заголовок столбца.

Ответ 2

Я думаю, что "решение" на данный момент wold будет использовать стиль выборки PDO:: FETCH_NAMED, а затем преобразовать возвращенный массив и заполнить динамический класс:

function arrayToObject(array $array){
   $obj = new stdClass();
   foreach($array as $k => $v)
      $obj->$k = $v;

   return $obj;
}