Как правильно наследовать классы в других классах

Svyatoi

Новичок
Допиливаю свою CMS, вижу что много где отстал от жизни. Сейчас переделываю ядро и основные классы, а для остального планирую использовать фреймворки.

Читаю про интерфейсы, абстрактные классы и не могу придумать как использовать их в работе.

Есть классы которые часто используются в других классах, например, класс базы данных, емейл, шаблонизатор и т.д.
Я работаю с ними так:
Код:
// Если класс постоянно нужен, то подгружаю в конструкторе
function __construct()
{
global $db;
$this->db=@$db;
}

.....

// Если класс используется редко, то создаю его внутри метода, при этом класс Tree тоже работает с базой
function greateTree()
{
include(DIR.'class/extra/Tree.php');
$Tree = new Tree();
}

// Класс Tree вызывается всегда и только в одном месте, но некоторые классы могут дублироваться, например, Email.

function sendMsg()
{
  ...
  include_once(DIR.'class/extra/Email.php');
  $Email = new Email();
}
Интересуют также возможности расширения классов. Допустим, у меня есть класс Login, который отвечает за авторизацию, а также есть LoginExtra, позволяющий авторизоваться через социальные сеть. Правильно, по идее, в LoginExtra подгружать экстендсом класс Login но мне лучше наоборот в Login по мере недобности подгрузить методы LoginExtra.

Интересует опыт практикующих программистов в части удобной организации работы с классами и что бы вы могли посоветовать мне.
 

WMix

герр M:)ller
Партнер клуба
начни с autoloading. забудь про global, include и DIR. читай дальше "про интерфейсы, абстрактные классы", классы стараться не наследовать а имплементировать. остальное слишком сложно чтоб просто взять и на форуме обьяснить.
ЗЫ код ужасен
 
Последнее редактирование:

Тугай

Новичок
Абстрактные классы - это некоторая функциональность, которая сама по себе не имеет смысла без определенной работы в дочерних классах.
Например, нам нужен клиент к веб сервису, который посылает/принимает запросы в xml.
Запросов несколько, целое api, посылать данные и принимать ответ по http надо всегда - работа для абстрактного класса , но нужно знать протокол сервиса (формат xml запроса/ответа)- работа для дочерних классов.
Дочерние формируют запросы и разбираются с ответами используя при этом абстрактный класс как транспорт.
Абстрактный класс не умеет конструировать запросы и разбираться с ответами, но умеет и знает как и куда отправить запрос и получить ответ.
Не зная протокола сервиса использование абстрактного класса бессмысленно, соединиться с сервисом и отправить что-то можем но без дочерних классов не знаем что.

Интерфейсы в PHP нужны для реализации внедрения зависимостей, проверки типов в аргументах функций/методов и прочих сложных штук.
Осваивай современные фреймворки, в их коде куча примеров что и как. :)

Расширения это шаблон visitor, наделай в LoginExtra статические методы с аргументом класса Login. И вызывай где надо LoginExtra::Extension($login);
Можно и неявно, используя магию, лови __call в Login и перенаправляй на методы LoginExtra::Extension($this);.
 

Svyatoi

Новичок
начни с autoloading. забудь про global, include и DIR. читай дальше "про интерфейсы, абстрактные классы", классы стараться не наследовать а имплементировать. остальное слишком сложно чтоб просто взять и на форуме обьяснить.
Спасибо за советы.

Эмплементы тоже не всегда удобны, например в той же реализации авторизации через социальные сети. Нужно подключать класс LoginExtra внутри класса Login только если классом получен определенный тип данных.

__autoload это здорово, на одну строчку кода стало меньше.

Допустим, я хочу для класса, в зависимости от ситуации, определить наследуемые классы. И в голову приходит примерно это.

PHP:
$Core = new Core(array('db', 'module' ));  // db - это класс базы данных, module - имя модуля.

// В самом классе

function __construct($global)
{
foreach ($global as $var) $this->$var=@$GLOBALS[$var];
}
 
Последнее редактирование:

Svyatoi

Новичок
Расширения это шаблон visitor, наделай в LoginExtra статические методы с аргументом класса Login. И вызывай где надо LoginExtra::Extension($login);
Можно и неявно, используя магию, лови __call в Login и перенаправляй на методы LoginExtra::Extension($this);.
Спасибо, буду изучать.
 

WMix

герр M:)ller
Партнер клуба
а ты сделай логин несколько абстрактней, понятно что существуют 2 точки входа одна через форму другая по запросу соцсети. далее некая стратегия подбора правильного метода авторизации (через user/pass или uid). ну а дальше понимая что таких точек больше каждая точка отдельный класс (уже собранный обьект) имплементирующий некий login():int к примеру другими словами один отвечает за саму авторизацию а 2 других за проверку пользователя
 
Последнее редактирование:

hell0w0rd

Продвинутый новичок
Читаю про интерфейсы, абстрактные классы и не могу придумать как использовать их в работе.
Если ты не можешь придумать как их использовать - не используй. Это инструмент, если ты ешь суп, а рядом лежит вилка - не надо думать, как ее бы использовать, подожди, пока пройдет время, принесут второе блюдо и ложкой станет не слишком то и удобно орудовать.

И фреймворки надо учить не после того, как свое напишешь, все наоборот. Свое надо писать, когда чужих библиотек становится мало.
 

WMix

герр M:)ller
Партнер клуба
PHP:
interface ILogin{
  public function login(): int;
}

class DbLogin implements ILogin{
  private $user;
  private $pass;
  // ...
}

class FacebookLogin implements ILogin{
  private $uid;
  // ...
}

class Login{
  private $login;

  public setMethod( ILogin $login ){
    $this->login = $login;
  }

  public function login(){
    try{
      $user_id = $this->login->login();
      // ...
    }
    catch( UserNotFoundException $e ){}
  }
}
 
Последнее редактирование:

Svyatoi

Новичок
а ты сделай логин несколько абстрактней, понятно что существуют 2 точки входа одна через форму другая по запросу соцсети.
У меня так: если в классе Login определяем, что авторизация выполняется через социальную есть, то просто подгружается класс LoginExtra, причем ему передается имя родительского класса, и , допустим, какие то параметры. Классу LoginExtra никакие методы класса Login не нужны, зато ему нужны методы других классов, например, Image, чтобы брать аватар и резать ее под нужные размеры, а также класс Registration для создания учетной записи, а также класс Email. После того, как LoginExtra все обработает он передает родительскому классу данные пользователя (LoginExtra может вызываться и другим классом).

Правильнее ли мне будет всю эту структуру переложить на интерфейсы и абстрактные классы, я просто пока не понимаю какие плюсы в плане удобства и функциональности я извлеку.
 
Последнее редактирование:

WMix

герр M:)ller
Партнер клуба
простота, расширяемость и независимость классов друг от друга. вот и все преимущества.
 

Svyatoi

Новичок
И фреймворки надо учить не после того, как свое напишешь, все наоборот. Свое надо писать, когда чужих библиотек становится мало.
Может и так, но свое я писал лет 8 назад когда еще ни фрейворков ни пхп 5 небыло и дорабатывал под разные задачи, получается моя кмс тоже своего рода фреймворк с вполне современным функционалом но устаревшим кодом. И сейчас буду потихоньку менять всю структуру кода, начиная с ядра, какие то модули заимствовать из фреймворков.
 
Последнее редактирование:

Svyatoi

Новичок
простота, расширяемость и независимость классов друг от друга. вот и все преимущества.
Хорошо, а чем плох trait если класс LoginExtra вспомогательный.

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

Svyatoi

Новичок
Это я поторопился, мне в таком случае проще создавать экземляр класса внутри метода класса, чем объявлять его заранее, ведь я еще даже не знаю, нужен ли он мне.

Куда больше волнуют вспомогательные классы и данные, которые нужны постоянно. Это класс базы данных, шаблонизатор, кеш и т.д.
Самый вездесущий - это класс базы данных. Как его импортировать, не используя global? Постоянно экстендить/эмплементить? А чем это лучше, чем дать ссылку на глобальный объект, например, так $this->db=@$GLOBALS['db'], ведь ссылаясь на объект мы можем дополнять его информацией, например, класс базы данных имеет переменную $_log, которая содержит все запросы к бд.

Кроме того, есть наборы настроек, которые я подключаю в ядре. Например, при подключении модуля Каталога, подключаются настройки для дерева каталога, корзины, товаров. Они хранятся в глобальной переменной $settings (например, $settings['products']['count_page'], количество товаров на странице), которая подгружается внутри классов опять же через ссылку на глобальную переменную, например, так $this->settings=@$GLOBALS['settings']['products']. Для определения наборов настроек для конкретного модуля у меня служит обычный массив, который задается в главном конфиге, выглядит так (реализация для магазина): $modules=array('news'=>array('comments'), 'catalog'=>array('products', 'cart', 'users', 'comments'), 'order'=>array('cart'), 'cart'=>array('products', 'order'), 'users'=>array('regisration', 'orders', 'login') ). С его помощью я определяю какие настройки подгружать в определенном разделе сайта, остальное организуют исполняемые модули, а должны, насколько понимаю, интерфейсы.
 

Svyatoi

Новичок
Линейкой по пальцам.
Во многих движках именно так, через глобал, вордпресс вообще на функциях построен и в каждой функции объявляет бд.
А вот в Хенфоро все на абстрактных классах.

База данных в классах там вызывается примерно так
PHP:
protected function _getDb()
    {
        if ($this->_db === null)
        {
            $this->_db = XenForo_Application::getDb();
        }

        return $this->_db;
    }
Это своего рода транслятор, а в дальнейшем класс обращается к нему так $this->_getDb()->query. Возьму на вооружение, это подходит, если вспомогательный класс нужен не всегда, как в моем случае с Login. Но если мы точно знаем что без бд не обойтись, что же плохого сослаться на нее через глобал в конструкторе? Или я даю себе установку не использовать глобал и не использую его даже там, где это удобнее абстрактных классов.

Когда я делаю так в случаях, если переменные обязательно будут использоваться, разве это не удобнее?
PHP:
function __construct($global)
{
global $db, $modules;

$this->db=@$db;  $this->modules=@$modules;
}
А в ситуациях, когда вспомогательный класс нужен не всегда, тогда подключаем его через метод как на примере выше, который создает класс если его нет.
 
Последнее редактирование:

AnrDaemon

Продвинутый новичок
Плохого то, что каждая сволочь может эту переменную поменять.
 

Svyatoi

Новичок
Плохого то, что каждая сволочь может эту переменную поменять.
Так я не случайно использую ссылку. Например класс базы данных у меня хранит лог и данные парсера, если я сделаю так $this->db=$db; то просто сдублирую класс, мне это не нужно. То же самое с Логином и другими классами - они не должны дублироваться, так как по мере обработки в других классах будут накапливать данные. И мне пока не понятно зачем лишний раз заморачиваться с абстрактными классами, когда ссылки никто не отменял.
 
Сверху