Баг в xPDOQuery::set(). Округление десятичных.

Сегодня наткнулся на жесточайшую багу…

У кого развернута сборка магазина или хотя бы стоит пакет shopModx, попробуйте в консоли выполнить вот такой код:

<?php
print '<pre>';
$q = $this->modx->newQuery('ShopmodxProduct');
$q->command('update');
$q->set(array(
    'sm_price' => 154.99,    
));
 
if($q->prepare()){   
    print $q->toSQL();
}

Результат: UPDATE modx_shopmodx_productsSETsm_price = 154

Не 154.99, а просто 154… Думаю, каждый понимает печальность последствий, особенно когда полно товаров с ценниками типа 0.99

Но что тут самое интересное? А то, что если просто выполнить вот так, то все будет ОК:

$o = $modx->getObject('ShopmodxProduct', $id);
$o->set('sm_price', 154.99);
$o->save();

Первое: такое переменчивое поведение связано с тем, что через разные методы используются различные механизмы формирования запросов. К примеру, при вызове xPDOObject::save() у нас вполне явный SQL формируется. А вот при использовании xPDOQuery::set() уже механизм другой. И здесь самая западня вот в этих строчках:

elseif (!in_array($fieldMeta[$key]['phptype'], $this->_quotable)) {
     $type= PDO::PARAM_INT;
}

А какие типы у нас quotable? 'string', 'password', 'date', 'datetime', 'timestamp', 'time'

Как видим, там нет ни double, ни float. В итоге все приводится к INT-типу, а это, как известно, не подразумевает дробные числа. При этом в PDO в принципе нет ничего типа FLOAT или типа того. В сети полно вопросов PDO::PARAM_FLOAT does not exist, why?.

В итоге, видится только один вариант — определить float и double как строковые. А там сама база данных пусть занимается вопросами конвертации, она это обязательно сделает. Отправил PR.

Честно говоря, бага на столько скрытая, что я даже уже почти забил… Но в последний момент чутье вывело меня к нужной строчке. немного похвастаюсь)))