Найкращий спосіб ініціювати клас у плагіні WP?


89

Я створив плагін, і, звичайно, будучи мені, я хотів піти з приємним підходом до ОО. Тепер, що я робив, це створити цей клас, а потім трохи нижче створити екземпляр цього класу:

class ClassName {

    public function __construct(){

    }
}

$class_instance = new ClassName();  

Я припускаю, що для цього класу є ініціативніший WP, і тоді я натрапив на людей, які казали, що вони вважають за краще init()функціонувати, ніж функцію __construct(). І так само я знайшов кількох людей, які використовували наступний гачок:

class ClassName {

    public function init(){

    }
}
add_action( 'load-plugins.php', array( 'ClassName', 'init' ) );

Що взагалі вважається найкращим способом створити екземпляр класу WP для завантаження і мати це як глобально доступну змінну?

ПРИМІТКА: Як цікавий бічний момент, я помітив, що, хоча register_activation_hook()його можна викликати зсередини __construct, його не можна викликати зсередини, init()використовуючи другий приклад. Можливо, хтось міг би просвітити мене з цього приводу.

Редагувати: Дякую за всі відповіді, явно є досить дебати щодо того, як впоратися з ініціалізацією в самому класі, але я думаю, що взагалі існує досить хороший консенсус, який add_action( 'plugins_loaded', ...);є найкращим способом насправді розпочати його ...

Редагувати: Для того, щоб заплутати питання, я також бачив цей використаний (хоча я сам би не використовував цей метод, оскільки перетворення класу OO в функцію, здається, перешкоджає його суті):

// Start up this plugin
add_action( 'init', 'ClassName' );
function ClassName() {
    global $class_name;
    $class_name = new ClassName();
}

1
Щодо останнього редагування, якщо він міститься у тому ж файлі плагіна, що і клас, він стає дещо марним. Ви можете також інстанціювати клас відповідно до описаного нами методу. Навіть якщо його в окремому файлі, його все одно дещо безглуздо. Я можу бачити лише те, що я бачу, якщо ви хочете створити функцію обгортки, яка дозволяє інстанціювати клас поза файлами плагінів, в межах тем тощо. Незважаючи на це, я мав би запитати, яка логіка криється за цим, тому що правильне використання умовних умов і гаків повинно дозволяти тонкозернистий контроль над інстанцією, що дозволяє зосередитись на використанні плагіну.
Адам

Я якось погоджуюся з цим, але вважав, що варто його ввести, оскільки знайшов його в парі плагінів WP.
kalpaitch

Відповіді:


60

Добре питання, є ряд підходів, і це залежить від того, чого ви хочете досягти.

Я часто це роблю;

add_action( 'plugins_loaded', array( 'someClassy', 'init' ));

class someClassy {

    public static function init() {
        $class = __CLASS__;
        new $class;
    }

    public function __construct() {
           //construct what you see fit here...
    }

    //etc...
}

Більш ретельний поглиблений приклад , який виник в результаті деяких недавніх дискусій з цієї самої теми в рамках чату можна побачити в цьому суті чл. - ПРМЕ toscho .

Підхід порожнього конструктора.

Ось уривок переваг / недоліків, викладених із наведеного вище принципу, який повністю ілюструє підхід порожнього конструктора.

  • Переваги:

    • Тестові одиниці можуть створювати нові екземпляри без активації будь-яких гачків автоматично. Не Сінглтон

    • Не потрібна глобальна змінна.

    • Хто хоче працювати з екземпляром плагіна, може просто зателефонувати на T5_Plugin_Class_Demo :: get_instanc ().

    • Легко відключити.

    • Досі справжній OOP: жодні методи роботи не є статичними.

  • Недолік:

    • Може бути, важче читати?

На мою думку, недолік - слабкий, тому саме він повинен бути моїм прихильним підходом, однак не єдиним, яким я користуюся. Насправді, кілька інших важких ваг, без сумніву, будуть звучати на цій темі, і незабаром вони перейдуть на цю тему, оскільки навколо цієї теми є хороші думки, які слід озвучити.


зауважте: мені потрібно знайти приклад суті з toscho, який пройшов через 3 або 4 порівняння того, як інстанціювати клас у плагіні, який розглядав плюси та мінуси кожного, що вищезгадане посилання було сприятливим способом зробити це, але інші приклади добре протиставляють цю тему. Сподіваємось, у Toscho все ще є ця документація.

Примітка. Відповідь WPSE на цю тему з відповідними прикладами та порівняннями. Також найкраще рішення, наприклад, клас WordPress.

add_shortcode( 'baztag', array( My_Plugin::get_instance(), 'foo' ) );
class My_Plugin {

    private $var = 'foo';

    protected static $instance = NULL;

    public static function get_instance() {

        // create an object
        NULL === self::$instance and self::$instance = new self;

        return self::$instance; // return the object
    }

    public function foo() {

        return $this->var; // never echo or print in a shortcode!
    }
}

яка буде різниця між додаванням ("plugins_loaded", ...); та додати запуск ('load-plugins.php', ...); Приклад, який я взяв, використовував останній
kalpaitch

1
З того, що я розумію load-plugins.php, хоча він працює, пов'язаний з основним update.phpфайлом і не є частиною звичайних дій за замовчуванням, на які слід покладатися, коли йдеться про послідовність подій, які запускаються під час ініціалізації, і тому я вважаю за краще використовувати в цьому випадку ті гачки, які дійсно застосовуються plugins_loaded. Це те, що я часто називаю швидким оглядом того, що відбувається, коли довідка про дії . Моє пояснення не є повною в повному обсязі.
Адам

4
Мені подобається такий однотонний підхід. Однак я сумніваюся, використовуючи plugins_loaded як ваш ініціалізаційний гачок дій. Цей гак призначений для запуску після завантаження всіх плагінів. Підключившись після цього, ви начебто викрадаєте цей гачок, і може втішатись конфліктами чи проблемами послідовності запуску з іншими плагінами або темами, які підключаються до плагінів_ завантажених. Я б не підключався до жодної дії для запуску вашого методу ініціалізації. Архітектура плагінів була розроблена так, щоб вона працювала вбудовано, а не на дії.
Том Ожер

2
Зауважте, що якщо ви використовуєте, register_activation_hook()вам потрібно викликати цю функцію до початку plugins_loadedдії.
Герт

1
В якості додаткової інформації дивіться цю публікацію від @mikeschinkel та дикуси у коментарях. hardcorewp.com/2012/…
bueltge

78

Приїхавши сюди рівно через 2 роки після того, як було задано оригінальне запитання, я хочу зазначити кілька речей . (Не вимагайте від мене вказувати багато речей , коли-небудь).

Правильний гачок

Для створення класу плагінів слід використовувати належний гачок. Не існує загального правила, для якого воно є, тому що це залежить від того, що робить клас.

Використовувати дуже ранній гачок, як "plugins_loaded"правило, не має сенсу, оскільки такий гак запускається для запитів адміністратора, фронтену та AJAX, але дуже часто пізніший гак набагато кращий, оскільки він дозволяє інстанціювати класи плагінів лише за потреби.

Наприклад, клас, який робить речі для шаблонів, може бути ініційований "template_redirect".

Взагалі дуже рідко виникає необхідність екзаментувати клас, перш ніж "wp_loaded"його звільнити.

Без класу Бога

Більшість з усіх класів, які використовуються як приклади в старих відповідях, використовують клас, який називається like "Prefix_Example_Plugin"or "My_Plugin"... Це вказує на те, що плагін є основним класом.

Ну, якщо плагін не буде зроблений одним класом (у такому випадку іменування його після імені плагіна абсолютно розумне), щоб створити клас, який керує всім плагіном (наприклад, додавання всіх гаків, які потрібні плагіну, або інстанціювання всіх інших класів плагіна ) можна вважати поганою практикою, як приклад об’єкта бога .

В об'єктно-орієнтованому коді програмування має бути SOLID, де "S" означає "принцип єдиної відповідальності" .

Це означає, що кожен клас повинен робити одну справу. У розробці плагінів WordPress це означає, що розробникам слід уникати використання одного гака для створення основного класу плагінів, але для створення екземплярів різних класів слід використовувати різні гачки відповідно до відповідальності класу.

Уникайте гачків у конструкторі

Цей аргумент був введений в інших відповідях тут, проте я хочу зауважити цю концепцію і пов'язати цю іншу відповідь там, де це було досить широко пояснено в рамках тестування одиниці.

Майже 2015 рік: PHP 5.2 призначений для зомбі

З 14 серпня 2014 р. PHP 5.3 досяг свого кінця . Це напевно мертвий. PHP 5.4 буде підтримуватися на весь 2015 рік, це означає ще один рік, в той момент, коли я пишу.

Однак WordPress все ще підтримує PHP 5.2, але ніхто не повинен писати єдиний рядок коду, який підтримує цю версію, особливо якщо код є OOP.

Є різні причини:

  • PHP 5.2 давно помер, для нього не випущено жодних виправлень безпеки, це означає, що це не захищено
  • PHP 5.3 додав багато функцій до PHP, анонімних функцій та просторів імен über alles
  • новіші версії PHP набагато швидші . PHP безкоштовний. Оновлення це безкоштовно. Навіщо використовувати повільнішу і незахищену версію, якщо ви можете безкоштовно скористатися швидшою, безпечнішою?

Якщо ви не хочете використовувати код PHP 5.4+, використовуйте принаймні 5.3+

Приклад

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

Як тільки нам більше не потрібно піклуватися про 5.2, ми можемо і повинні використовувати простори імен.

Для кращого пояснення принципу єдиної відповідальності, у моєму прикладі будуть використані 3 класи, один, який робить щось на фронтальному рівні, один - за бекендером і третій, що використовується в обох випадках.

Клас адміністратора:

namespace GM\WPSE\Example;

class AdminStuff {

   private $tools;

   function __construct( ToolsInterface $tools ) {
     $this->tools = $tools;
   }

   function setup() {
      // setup class, maybe add hooks
   }

}

Клас Frontend:

namespace GM\WPSE\Example;

class FrontStuff {

   private $tools;

   function __construct( ToolsInterface $tools ) {
     $this->tools = $tools;
   }

   function setup() {
      // setup class, maybe add hooks
   }

}

Інтерфейс інструментів:

namespace GM\WPSE\Example;

interface ToolsInterface {

   function doSomething();

}

І клас Інструменти, використовуваний двома іншими:

namespace GM\WPSE\Example;

class Tools implements ToolsInterface {

   function doSomething() {
      return 'done';
   }

}

Маючи ці заняття, я можу створити їх за допомогою правильних гачків. Щось на зразок:

require_once plugin_dir_path( __FILE__ ) . 'src/ToolsInterface.php';
require_once plugin_dir_path( __FILE__ ) . 'src/Tools.php';

add_action( 'admin_init', function() {

   require_once plugin_dir_path( __FILE__ ) . 'src/AdminStuff.php';
   $tools = new GM\WPSE\Example\Tools;
   global $admin_stuff; // this is not ideal, reason is explained below
   $admin_stuff = new GM\WPSE\Example\AdminStuff( $tools ); 
} );

add_action( 'template_redirect', function() {

   require_once plugin_dir_path( __FILE__ ) . 'src/FrontStuff.php';
   $tools = new GM\WPSE\Example\Tools;
   global $front_stuff; // this is not ideal, reason is explained below
   $front_stuff = new GM\WPSE\Example\FrontStuff( $tools );    
} );

Інверсія залежності та ін'єкція залежності

У наведеному вище прикладі я використовував простори імен та анонімні функції, щоб створювати різні класи на різних гачках, застосовуючи те, що я говорив вище.

Зверніть увагу, як простори імен дозволяють створювати класи, названі без будь-якого префікса.

Я застосував іншу концепцію, про яку побічно згадувалося вище: " Вприскування вприскування" , це один метод застосування принципу інверсії залежності - "D" в абревіатурі SOLID.

ToolsКлас «впорскується» в двох інших класах , коли вони інстанціруется, так що в цьому випадку можна розділити відповідальність.

Крім того, AdminStuffі FrontStuffкласи використовують натяк на тип, щоб заявити, що їм потрібен клас, який реалізує ToolsInterface.

Таким чином, ми самі або користувачі, які використовують наш код, можуть використовувати різні реалізації одного і того ж інтерфейсу, завдяки чому наш код не поєднується з конкретним класом, а з абстракцією: саме про це йдеться в принципі Dependpend Inversion.

Однак приклад вище можна вдосконалити. Подивимося, як.

Автонавантажувач

Хороший спосіб написати краще читаний код OOP - не змішувати визначення типів (інтерфейси, класи) з іншим кодом, а кожен тип вводити у власний файл.

Це правило також є одним із стандартів кодування PSR- 1 .

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

Це може бути непосильним, але PHP надає корисні функції для автоматичного завантаження класу, коли це потрібно, використовуючи зворотний виклик, який завантажує файл на основі його імені.

Використовувати простори імен, це стає дуже просто, тому що зараз можна зіставити структуру папок зі структурою простору імен.

Це не тільки можливо, але це ще й інший стандарт PSR (або краще 2: PSR-0 тепер застарілий, і PSR-4 ).

Дотримуючись цих стандартів, можна використовувати різні інструменти, які обробляють автоматичне завантаження, без необхідності кодувати спеціальний автонавантажувач.

Треба сказати, що стандарти кодування WordPress мають різні правила іменування файлів.

Тож при написанні коду для ядра WordPress розробники повинні дотримуватися правил WP, але при написанні власного коду це вибір розробника, але за допомогою стандарту PSR простіше використовувати вже написані інструменти 2 .

Моделі глобального доступу, реєстру та пошуку послуг.

Одне з найбільших проблем при створенні класів плагінів у WordPress - це доступ до них з різних частин коду.

Сам WordPress використовує глобальний підхід: змінні зберігаються в глобальному масштабі, роблячи їх доступними всюди. Кожен розробник WP вводить слово globalтисячі разів у своїй кар’єрі.

Це також підхід, який я використав на прикладі вище, але це зло .

Ця відповідь вже занадто довга, щоб я міг далі пояснити, чому, але читання перших результатів у СЕРП щодо "глобальних змінних зло" є хорошою відправною точкою.

Але як можливо уникнути глобальних змінних?

Існують різні способи.

Деякі з старих відповідей тут використовують підхід статичного екземпляра .

public static function instance() {

  if ( is_null( self::$instance ) ) {
    self::$instance = new self;
  }

  return self::$instance;
}

Це легко і досить добре, але це змушує реалізувати схему для кожного класу, до якого ми хочемо отримати доступ.

Більше того, багато разів такий підхід ставить на шлях потрапляння до проблеми класу god, оскільки розробники роблять доступним основний клас за допомогою цього методу, а потім використовують його для доступу до всіх інших класів.

Я вже пояснив, наскільки поганий клас богів, тому статичний підхід екземпляра - хороший шлях, коли плагіну потрібно зробити доступним один або два класи.

Це не означає, що його можна використовувати лише для плагінів, що мають лише пару класів, адже, коли правильно застосовується принцип введення залежності, можна створити досить складні програми без необхідності зробити глобально доступною велику кількість об'єктів.

Однак іноді плагінам потрібно зробити доступними деякі класи, і в цьому випадку статичний підхід екземпляра є надзвичайним.

Іншим можливим підходом є використання шаблону реєстру .

Це дуже проста його реалізація:

namespace GM\WPSE\Example;

class Registry {

   private $storage = array();

   function add( $id, $class ) {
     $this->storage[$id] = $class;
   }

   function get( $id ) {
      return array_key_exists( $id, $this->storage ) ? $this->storage[$id] : NULL;
   }

}

За допомогою цього класу можна зберігати об’єкти в об'єкті реєстру за допомогою ідентифікатора, тому, маючи доступ до реєстру, можна мати доступ до всіх об'єктів. Звичайно, коли об’єкт створюється вперше, його потрібно додати до реєстру.

Приклад:

global $registry;

if ( is_null( $registry->get( 'tools' ) ) ) {
  $tools = new GM\WPSE\Example\Tools;
  $registry->add( 'tools', $tools );
}

if ( is_null( $registry->get( 'front' ) ) ) {
  $front_stuff = new GM\WPSE\Example\FrontStuff( $registry->get( 'tools' ) );    
  $registry->add( 'front', front_stuff );
}

add_action( 'wp', array( $registry->get( 'front' ), 'wp' ) );

З наведеного вище прикладу видно, що щоб бути корисним, реєстр повинен бути доступним у всьому світі. Глобальна змінна для єдиного реєстру не дуже погана, проте для неглобальних пуристів можливо реалізувати підхід статичного екземпляра для реєстру, або, можливо, функцію зі статичною змінною:

function gm_wpse_example_registry() {
  static $registry = NULL;
  if ( is_null( $registry ) ) {
    $registry = new GM\WPSE\Example\Registry;
  }
  return $registry;
}

Перший раз, коли функція викликається, вона інстанціює реєстр, при наступних викликах вона просто поверне її.

Ще один специфічний для WordPress метод зробити клас глобально доступним - повернення екземпляра об'єкта з фільтра. Щось на зразок цього:

$registry = new GM\WPSE\Example\Registry;

add_filter( 'gm_wpse_example_registry', function() use( $registry ) {
  return $registry;
} );

Після цього скрізь потрібен реєстр:

$registry = apply_filters( 'gm_wpse_example_registry', NULL );

Інший шаблон, який можна використовувати, - це шаблон локатора обслуговування . Це аналогічно шаблону реєстру, але локатори служб передаються різним класам за допомогою введення залежності.

Основна проблема цього шаблону полягає в тому, що він приховує залежності класів, що ускладнює підтримку та зчитування коду.

Контейнери DI

Незалежно від методу, який використовується для того, щоб зробити локатор реєстру чи послуги глобально доступним, об’єкти повинні зберігатися там, а перед тим, як їх зберігати, їх потрібно інстанціювати.

У складних додатках, де класів досить багато, і багато з них мають кілька залежностей, для інстанціювання класів потрібно багато коду, тому можливість помилок збільшується: код, який не існує, не може мати помилок.

В останні роки з'явилися деякі бібліотеки PHP, які допомагають розробникам PHP легко створювати і зберігати екземпляри об'єктів, автоматично вирішуючи їх залежності.

Ці бібліотеки відомі як контейнери ін'єкційних залежностей, оскільки вони здатні створювати класи, розв'язуючи залежності, а також зберігати об'єкти та повертати їх у разі потреби, діючи аналогічно об’єкту реєстру.

Зазвичай при використанні контейнерів DI розробникам доводиться встановлювати залежності для кожного класу програми, і тоді, коли вперше потрібен клас у коді, він створюється належними залежностями, і той самий екземпляр повертається знову і знову за наступними запитами .

Деякі контейнери DI також здатні автоматично виявляти залежності без конфігурації, але за допомогою відображення PHP .

Деякі відомі контейнери DI:

та багато інших.

Хочу зазначити, що для простих плагінів, які включають лише кілька класів і класів, не так багато залежностей, напевно, не варто використовувати контейнери DI: статичний примірник методу або глобальний доступний реєстр - це хороші рішення, але для складних плагінів вигода контейнера DI стає очевидною.

Зрозуміло, навіть об’єкти контейнерів DI повинні бути доступними, щоб використовуватись у додатку, і для цього можна використовувати один із способів, які бачили вище, глобальну змінну, змінну статичного екземпляра, повертаючий об'єкт через фільтр тощо.

Композитор

Використовувати контейнер DI часто означає використання стороннього коду. В даний час, в PHP, коли ми повинні використовувати зовнішній LIB (так як контейнери DI, але будь-який код , який не є частиною програми), просто завантаживши його і покласти його в нашій папці програми не вважається хорошою практикою. Навіть якщо ми є авторами цього іншого кодексу.

Розв'язка коду програми від зовнішніх залежностей знак кращої організації, підвищення надійності і поліпшення осудності коди.

Композитор , це фактичний стандарт у спільноті PHP для управління залежностями PHP. Це далеко не мейнстрім у спільноті WP, це інструмент, про який кожен розробник PHP та WordPress повинен хоча б знати, якщо не використовувати.

Ця відповідь вже розміром у книжці, щоб дозволити подальше обговорення, а також обговорення Композитора тут, мабуть, поза темою, вона згадувалася лише заради повноти.

Для отримання більш докладної інформації відвідайте сайт Composer , і це також варто дати читання цієї Minisite Куратор @Rarst .


1 PSR - це правила стандартів PHP, оприлюднені групою PHP Framework Interop

2 Композитор (бібліотека, про яку буде сказано у цій відповіді), серед іншого, також містить утиліту автозавантажувача.


1
Додаткова примітка, що PHP 5.3 - це також кінець життя. Відповідальний хост запропонує принаймні 5.4 як варіант, якщо не за замовчуванням
Tom J Nowell

2
"З 14 серпня 2014 р. PHP 5.3 закінчився термін життя. Він, безумовно, мертвий." Перший рядок у розділі "PHP 5.2 призначений для зомбі" @TomJNowell
gmazzap

3
Просто цікаво, як би ви вказали на "багато речей" ? ;-)
MikeSchinkel

Намагаючись уникати глобальних змінних, мені подобається ідея шаблону реєстру з функцією & статична змінна. Перший раз, коли функція викликається, вона створить реєстр, при наступних викликах він просто поверне її.
Майкл Еклунд

10

Я використовую таку структуру:

Prefix_Example_Plugin::on_load();

/**
 * Example of initial class-based plugin load.
 */
class Prefix_Example_Plugin {

    /**
     * Hooks init (nothing else) and calls things that need to run right away.
     */
    static function on_load() {

        // if needed kill switch goes here (if disable constant defined then return)

        add_action( 'init', array( __CLASS__, 'init' ) );
    }

    /**
     * Further hooks setup, loading files, etc.
     *
     * Note that for hooked methods name equals hook (when possible).
     */
    static function init(  ) {


    }
}

Примітки:

  • визначила місце для речей, які потрібно запустити відразу
  • відключити / переосмислити налаштування легко (зніміть один initметод)
  • Я не думаю, що я коли-небудь використовував / потрібний об’єкт класу плагінів - вимагає відстеження його тощо; це дійсно підроблений простір імен за призначенням, а не OOP (більшість часу)

Заперечення Я ще не використовую одиничні тести ( так багато речей на моєму шаблоні ), і я чую, що статичність для них може бути менш бажаною. Виконайте дослідження з цього питання, якщо вам потрібно провести його тестування.


3
Я знаю, що великі люди на тестуванні одиниць дійсно не люблять статичних / однотонних рішень. Я думаю, якщо ви повністю розумієте, чого ви намагаєтеся досягти, використовуючи статику, і, принаймні, знаєте про наслідки цього, тоді буде абсолютно чудово реалізувати такі методи. Хороші теми, пов’язані з цим, на " Переповнення стека"
Адам

Це змусило мене по-справжньому задуматися. То чому б тоді використовувати Класи, а не просто повертатися до простих приставних функцій. Ми робимо це лише для того, щоб мати більш чисті назви функцій / методів? Я маю на увазі наявність їх вкладених у "статичний" b4, чи це набагато читабельніше? Шанс виникнення конфлікту імен приблизно такий самий, як для одного імені класу, якщо ви використовуєте префікси проппер або я щось пропускаю?
Джеймс Мітч

1
@JamesMitch так, загальностатичні методи - це здебільшого лише функції з фальшивим простором імен, як це використовується в WP. Однак класи мають деякі переваги перед чистими функціями навіть у цьому випадку, такі як автоматичне завантаження та успадкування. Останнім часом я рухаюся від статичних методів і до реальних об'єктів, організованих контейнером для нагнітання залежності.
Рарст

3

Все залежить від функціональності.

Я колись зробив плагін, який зареєстрував сценарії, коли конструктор викликався, тому мені довелося зачепити його на wp_enqueue_scriptsгачок.

Якщо ви хочете зателефонувати йому під час functions.phpзавантаження вашого файлу, ви також можете створити екземпляр самостійно, $class_instance = new ClassName();як ви згадали.

Ви можете розглянути швидкість та використання пам'яті. Мені про це невідомо, але я можу уявити, що в деяких випадках є гачки, які не викликаються. Створивши свій примірник на цьому гачку, ви можете зберегти деякі ресурси сервера.


Хороший спасибі за це, я вважаю, що на це питання також є два моменти. Інша справа, чи підходить __construct або чи краще init () є кращим способом ініціалізації класу.
kalpaitch

1
Ну, я б пішов на статичний init()метод, тому екземпляр класу викликається в області класу замість іншої області, де ви, можливо, можете перезаписати існуючі змінні.
Тім С.

0

Я знаю, що це пару років, але тим часом php 5.3 підтримує анонімні методи , тому я придумав таке:

add_action( 'plugins_loaded', function() { new My_Plugin(); } );

і мені якось подобається найбільше. Я можу використовувати звичайні конструктори і мені не потрібно визначати будь-які "init" або "on_load" методи, які псують мої структури OOP.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.