Пытаясь изобрести свой метод toArray()1 для сериализации объектов в ассоциативный массив наткнулся на пару интересных моментов.

Методы method_exists и is_callable

Можно предположить, что is_callable будет возвращать true для всех методов где method_exists возращает true. Но в действительности это не так.

var_dump(is_callable($class, '__toString')); // bool(false)
var_dump(method_exists($class, '__toString')); // bool(true)

Доступ к закрытым и защищенным свойствам

И существование документированной фичи, что при явном приведении типа объекта к массиву, можно получить содержимое скрытых свойств. Так следующий код:

class A {private $private = 0;}
class B extends A {protected $protected = 1; private $private2 = 2; public $public = 3;}
$x = new B();
echo json_encode((array)$x, JSON_PRETTY_PRINT);

выведет:

{
    "\u0000*\u0000protected": 1,
    "\u0000B\u0000private2": 2,
    "public": 3,
    "\u0000A\u0000private": 0
}

Где \u0000 обозначает символ машинного нуля в JSON.

До появления PHP 5.4 это было единственным способом получения значений защищенных членов класса без использования Reflection (который все-таки довольно медленный и неуклюжий). Сейчас же можно воспользоваться анонимными функциями и привязыванием конекста: https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/


  1. апдейт, есть вероятность, что эта функциональность таки попадет в сам язык https://wiki.php.net/rfc/to-array ↩︎