Ответ 1
В вашем вопросе описаны две проблемы:
-
Почему не удается создать объекты с свойствами, имеющими имена пустой строки при использовании
PDO::FETCH_OBJ
, но, видимо, при использовании других методов?Как описано в Внутренние структуры и импликация в книге внутренних языков PHP, "dynamic properties" (т.е. переменные-члены объекта, которые не объявлены в определении класса, а скорее созданы во время выполнения) реализованы как хэш-таблица.
Если вы хотите заполнить новый экземпляр стандартного объекта кучей свойств, которые в настоящее время хранятся в хеш-таблице, можно просто указать переменную object
properties
в этой существующей хеш-таблице — PHPobject_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, потому что они ожидают получить имя столбца, а не заголовок столбца.