глючная SPL

grigori

( ͡° ͜ʖ ͡°)
Команда форума
еще одну проблема с SLP нашел, теперь в связке с PDO
PHP:
class ArraySlice extends ArrayObject{
    function __construct(array $array=array()){
        parent::__construct($array,ArrayObject::ARRAY_AS_PROPS);
    }
}
$a = new ArraySlice();
$a->x = 1;
echo $a['x']; // 1
но
PHP:
$a = $PDO->query('select id from links')->fetchObject('ArraySlice');
echo $a->id; //работает
echo $a['id'];//Notice occured ... Undefined index:  id
Конструктор ArraySlice вызывается, режим ARRAY_AS_PROPS устанавливается.
А PDO, похоже, пишет значения полей напрямую в обход сеттеров.
 

fixxxer

К.О.
Партнер клуба
ArrayObject вообще странный, мне он сразу не понравился кучей нюансов - проще свою реализацию использовать.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
это да, implement ArrayAccess, etc позволяет сделать все что угодно
просто он заметно быстрей своих реализаций
 

Духовность™

Продвинутый новичок
ArrayObject вообще странный, мне он сразу не понравился кучей нюансов - проще свою реализацию использовать.
своя реализация тормозит, в разы (по сравнению с обычными массивом, по меньшей мере)

Бред ArrayObject и прочих стандартных реализаций в том, что разработчики PHP разделили массивы и объекты. В итоге получается ООП ради ООП без гибкости ООП. Например, моя реализация умеет так:

PHP:
$obj = new Cover_Array();
$obj['a1']['a2'] = array(1,2,3);
echo $obj->a1->a2->count();
т.е. любой массив в рамках объектной обертки - ТОЖЕ объект.

С ArrayObject так сделать без напильника нельзя. Тогда, какой смысл в этом объекте? Он фактически ничем не отличается от обычного массива.
 

crocodile2u

http://vbolshov.org.ru
Гриша, это [мало]известное поведение PDO, SPL тут ни при чем: дело в том, что, когда PDO делает свой fetch в виде объекта - свойства присваиваются до того как вызывается конструктор. Я с этим столкнулся, и мне пришлось даже что-то специально писать какой-то хак, чтобы обойти это. Я спрашивал на какой-то из PHPConf у Маркуса Бёргера об этом, он сказал, что корни такого поведения где-то во внутренностях реализации PHP, и, скорее всего, такое положение дел сохранится.

UPD: похоже, я таки невнимательно прочитал ветку... Тут проблема занимательнее. Видимо, сеттеры действительно обходятся.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
тогда все понятно

parent::__construct($array,ArrayObject::ARRAY_AS_PROPS);
вызывается после выставления полей объекта, и они не попадают в обработчик.
А ArrayObject::оffsetGet после смены режима на ARRAY_AS_PROPS не видит поля, которые установлены хаком из PDO.

Могли бы и обрабатывать существующие поля после смены режима, я думаю.

"только никто ж не задумывается о том, что объект может вести какую-то частную жизнь до того как отработал его конструктор."(С)crocodile2u
т.е. правая рука не думает о том, что делает левая

попробую добавить __set

кстати, отсутствие какой-либо реакции на багрепорт в течение 10 часов наводит на предположение, что не все так просто
 

tony2001

TeaM PHPClub
> кстати, отсутствие какой-либо реакции на багрепорт в течение 10 часов наводит на предположение, что не все так просто
а возможно, намекает на то, что у тех двух-трёх людей, которые читают баг-репорты, есть своя работа и (сюрприз!) жизнь..
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
не сюрприз никакой, об этом я тоже подумал ;) в мои 5 утра Америка уже отдыхает

просто _обычно_ на репорты сразу дают какие-то отписки, например, "а попробуйте версию из svn"
никакая это не претензия - просто выход из общей тенденции
сколько надо - столько и пусть рассматривают
 

crocodile2u

http://vbolshov.org.ru
Вообще, вопрос таки касается PDO, а не ArrayObject, и поведение ArrayObject как раз вполне логичное. "обрабатывать существующие поля после смены режима" - значило бы еще и разрешать конфликты (одно и то же поле в свойствах и в данных - кто должен иметь приоритет?).

А вот вопрос о том, почему PDO сначала выставляет свойства, а уже потом вызывает конструктор - это действительно вопрос. Моих знаний в Си не хватает, чтобы разобраться в коде, к сожалению :( Но править в данном случае ArrayObject, как мне кажется, нелогично - проблема возникает не только с ним, и проблема вызывается нелогичным поведением PDO.
 

Вурдалак

Продвинутый новичок
crocodile2u, +1. Тут, IMHO, надо требовать от объектов реализации интерфейса типа setValues(array $values).
 

crocodile2u

http://vbolshov.org.ru
http://bugs.php.net/bug.php?id=49521 - проблему решили по-другому: нужное поведение PDO->fetch() достигается передачей спец. флага в setFetchMode(). Что ж, имхо, немного досадно. Но при разработке большого продукта, которым пользуются тысячи и тысячи людей, многое приходится делать ради обратной совместимости.
 

fixxxer

К.О.
Партнер клуба
crocodile2u, +1. Тут, IMHO, надо требовать от объектов реализации интерфейса типа setValues(array $values).
Кстати, вполне реализуется без ломания BC:

PHP:
class Storage implements ArrayAccess {
//...
}
PHP:
$PDO->query('select id from links')->fetchToObject(new Storage);
Но это конечно будет медленнее работать, да.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
ну да, но нигде никакой информации на эту тему нет

щас хоть комменты в доку напишу
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
fixxxer, интересно же писать одним вызовом :)
PHP:
$Redirects = new collection(
        $this->DB->query($sql)->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE,'ArraySlice')
);
циклом, конечно, можно
PHP:
$stmt = $PDO->query($sql,PDO::FETCH_INTO, new ArraySlice());
$Collection = new collection();
while ($object = $stmt->fetch()) {
    $Collection->add (clone $object);
}
но тогда теряется весь смысл PDO как элегантного нативного решения!
 

fixxxer

К.О.
Партнер клуба
О, оно и так еще умеет? Я там просто вариант предложил, как можно бы если бы... Но он все равно фиговый :)

Элегантно было бы 2 варианта - fetch в ArrayObject и fetchAll в SplFixedArray of ArrayObject соответственно.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
fetch в ArrayObject я как-раз и делаю
а collection extends SplFixedArray, естесно :)
в ней определяю пару методов для обхода ограничений на фиксированный размер, each, etc

запрос к базе пишу как
$Redirects = $this->DB->getResultsCollection($sql,'RedirectRecord');

PHP:
/**
 * Executes a query and returns the results as a collection of objects of the given class
 * @param string|PDOStatement $stmt
 * @param string $class_name
 * @return collection
 */
function getResultsCollection($stmt,$class_name){
    return new collection(
        $this->query($stmt)->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE,$class_name)
    );
}
сегфолт в SplFixedArray::оffsetSet как-раз пофиксили на той неделе :)
http://phpclub.ru/talk/threads/splfixedarray-segfault.66050/
можно юзать
 
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Может, и хотелось бы сразу в fetchAll указывать, в какой класс (наследуемый из SplFixedArray или ArrayObject) засовывать коллекцию.
Но какой должен быть интерфейс fetchAll, чтобы в параметрах можно было красиво это описать - непонятно.

fetchAll(array(PDO::FETCH_COLLECTION=>$collection_class_name,PDO::FETCH_CLASS=>$class_name)) - не то

ну, не все сразу ...
одним вызовом получить результат в виде массива объектов и 1 раз обернуть в нативный объект,
получить pure OOP со скоростью массивов (или выше?) - уже неплохо
 
Сверху