Вы можете сделать это таким образом, в этом нет ничего плохого (я делаю нечто подобное довольно часто). Единственное, что я хотел бы предложить, это использовать исключения вместо die (таким образом вы можете безопасно обработать ошибку) ...
protected function error($eNo, $eMsg, $extra = '') {
throw new Exception('MySQL error: ['.$eNo.'] '.$eMsg.': '.$extra);
}
Плюс, я бы также предложил перегрузить метод запроса
public function query($sql, $result_mode = MYSQLI_STORE_RESULT) {
$result = parent::query($sql, $result_mode);
if ($result === false) {
$this->error($this->errno, $this->errstr, $sql);
}
return $result;
}
Я бы также предложил хранить копию объекта $ db внутри дочернего класса. Итак:
class db_users extends database {
protected $db = null;
public function __construct(Database $db) {
$this->db = $db;
}
public function test() {
echo 'foo';
}
}
Затем в __call:
if (!isset($this->classes[$class])) {
$class = 'db_'.$class;
$this->classes[$class] = new $class($this);
}