Контакти

Статичні члени класу. Не дай їм занапастити твій код. Статичні методи PHP Ділок static php p

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

Спробуємо розібрати «по кісточках» один з таких питань - що означає слово «static» в PHP і навіщо воно застосовується?

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

Значення перше - статична локальна змінна

function foo () ($ a \u003d 0; echo $ a; $ a \u003d $ a + 1;) foo (); // 0 foo (); // 0 foo (); // 0

У PHP змінні локальні. Це означає, що змінна, певна і отримала значення всередині функції (методу), існує тільки під час виконання цієї функції (методу). При виході з методу локальна змінна знищується, а при повторному вході - створюється заново. У коді вище такої локальної змінної є змінна $ a - вона існує тільки всередині функції foo () і кожен раз при виклику цієї функції створюється заново. Інкремент змінної в цьому коді має сенсу, оскільки на наступній же рядку коду функція закінчить свою роботу і значення змінної буде втрачено. Скільки б разів ми не викликали функцію foo (), вона завжди буде виводити 0 ...

Однак все змінюється, якщо ми перед привласненням поставимо ключове слово static:

Function foo () (static $ a \u003d 0; echo $ a; $ a \u003d $ a + 1;) foo (); // 0 foo (); // 1 foo (); // 2

Ключове слово static, написане перед привласненням значення локальної змінної, призводить до наступних ефектів:

  1. Присвоєння виконується тільки один раз, при першому виклику функції
  2. Значення поміченої таким чином змінної зберігається після закінчення роботи функції
  3. При наступних викликах функції замість присвоєння змінна отримує збережене раніше значення
Таке використання слова static називається статична локальна змінна.
Підводні камені статичних змінних
Зрозуміло, як завжди в PHP, не обходиться без «підводних каменів».

Камінь перший - статичної змінної привласнювати можна тільки константи або вирази зі сталими. Ось такий код:
static $ a \u003d bar ();
з неминучістю призведе до помилки парсера. На щастя, починаючи з версії 5.6 стало допустимим привласнення не лише констант, а й константних виразів (наприклад - «1 + 2» або «"), тобто таких виразів, що не залежать від іншого коду і можуть бути обчислені на етапі компіляції

Камінь другий - методи існують в єдиному екземплярі.
Тут все трохи складніше. Для розуміння суті приведу код:
class A (public function foo () (static $ x \u003d 0; echo ++ $ x;)) $ a1 \u003d new A; $ A2 \u003d new A; $ A1-\u003e foo (); // 1 $ a2-\u003e foo (); // 2 $ a1-\u003e foo (); // 3 $ a2-\u003e foo (); // 4
Всупереч інтуїтивного очікуванню «різні об'єкти - різні методи» ми наочно бачимо на цьому прикладі, що динамічні методи в PHP «не розмножуються». Навіть якщо у нас буде сто об'єктів цього класу, метод буде існувати лише в одному екземплярі, просто при кожному виклику в нього буде йти трафік різний $ this.

Така поведінка може бути несподіваним для непідготовленого до нього розробника і послужити джерелом помилок. Потрібно зауважити, що успадкування класу (і методу) призводить до того, що все-таки створюється новий метод:

Class A (public function foo () (static $ x \u003d 0; echo ++ $ x;)) class B extends A () $ a1 \u003d new A; $ B1 \u003d new B; $ A1-\u003e foo (); // 1 $ b1-\u003e foo (); // 1 $ a1-\u003e foo (); // 2 $ b1-\u003e foo (); // 2

Висновок: динамічні методи в PHP існують в контексті класів, а не об'єктів. І тільки лише в Рантайм відбувається підстановка "$ this \u003d текущій_об'ект"

Значення друге - статичні властивості і методи класів

В об'єктній моделі PHP існує можливість задавати властивості і методи не тільки для об'єктів - екземплярів класу, а й для класу в цілому. Для цього теж служить ключове слово static:

Class A (public static $ x \u003d "foo"; public static function test () (return 42;)) echo A :: $ x; // "foo" echo A :: test (); // 42
Для доступу до таких властивостей і методів використовуються конструкції з подвійним двокрапкою ( «Paamayim Nekudotayim»), такі як ім'я_класу :: $ імяПеременной і ім'я_класу :: імяМетода ().

Само собою зрозуміло, що у статичних властивостей і статичних методів є свої особливості і свої «підводні камені», які потрібно знати.

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

Однак, потрібно знати, що на відміну від інших мов, PHP не визначає ситуацію «в статичному методі написано $ this» на етапі парсинга або компіляції. Подібна помилка може виникнути тільки в Рантайм, якщо ви спробуєте виконати код з $ this всередині статичного методу.

Код типу такого:
class A (public $ id \u003d 42; static public function foo () (echo $ this-\u003e id;))
не приведе ні до яких помилок, до тих пір, поки ви не спробуєте використовувати метод foo () неналежним чином:
$ A \u003d new A; $ A-\u003e foo (); (І відразу отримаєте «Fatal error: Using $ this when not in object context»)

Особливість друга - static не аксіома!
class A (static public function foo () (echo 42;)) $ a \u003d new A; $ A-\u003e foo ();
Ось так, так. Статичний метод, якщо він не містить в коді $ this, цілком можна викликати в динамічному контексті, як метод об'єкта. Це не є помилкою в PHP.

Зворотне не зовсім вірно:
class A (public function foo () (echo 42;)) A :: foo ();
Динамічний метод, який не використовує $ this, можна виконувати в статичному контексті. Однак ви отримаєте попередження «Non-static method A :: foo () should not be called statically» рівня E_STRICT. Тут вирішувати вам - чи строго слідувати стандартам коду, або пригнічувати попередження. Перше, зрозуміло, краще.

І до речі, все написане вище відноситься тільки до методів. Використання статичного властивості через "-\u003e" неможливо і веде до фатальної помилки.

Значення третьої, здається найскладнішим - пізніше статичну зв'язування

Розробники мови PHP не зупинилися на двох значеннях ключового слова «static» і в версії 5.3 додали ще одну «фічу» мови, яка реалізована тим же самим словом! Вона називається «пізніше статичну зв'язування» або LSB (Late Static Binding).

Зрозуміти суть LSB найпростіше на нескладних прикладах:

Class Model (public static $ table \u003d "table"; public static function getTable () (return self :: $ table;)) echo Model :: getTable (); // "table"
Ключове слово self в PHP завжди означає «ім'я класу, де це слово написано». В даному випадку self замінюється на клас Model, а self :: $ table - на Model :: $ table.
Така мовна можливість називається «раннім статичним зв'язуванням». Чому рано? Тому що зв'язування self і конкретного імені класу відбувається не в Рантайм, а на більш ранніх етапах - парсинга і компіляції коду. Ну а «статичну» - тому що мова йде про статичних властивості і методи.

Трохи змінимо наш код:

Class Model (public static $ table \u003d "table"; public static function getTable () (return self :: $ table;)) class User extends Model (public static $ table \u003d "users";) echo User :: getTable () ; // "table"

Тепер ви розумієте, чому PHP поводиться в цій ситуації неінтуітівнимі. self був пов'язаний з класом Model тоді, коли про клас User ще нічого не було відомо, тому і вказує на Model.

Як бути?

Для вирішення цієї дилеми був придуманий механізм скріплення «пізнього», на етапі Рантайм. Працює він дуже просто - достатньо замість слова «self» написати «static» і зв'язок буде встановлена \u200b\u200bз тим класом, який викликає даний код, а не з тим, де він написаний:
class Model (public static $ table \u003d "table"; public static function getTable () (return static :: $ table;)) class User extends Model (public static $ table \u003d "users";) echo User :: getTable () ; // "users"

Це і є загадкове «пізніше статичну зв'язування».

Потрібно відзначити, що для більшої зручності в PHP крім слова «static» є ще спеціальна функція get_called_class (), яка повідомить вам - в контексті якого класу в даний момент працює ваш код.

Вдалих співбесід!



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

Хто любить статичні методи?

Особливо часто їх застосовують розробники, які коли-небудь використовували в своїй роботі фреймворк CodeIgniter.

Також, в число послідовників статистичних методів входять більшість Kohana- і Laravel-розробників.
Тут не можна не згадати той факт, що програмісти, які вирішують почати писати власні речі, зазвичай відмовляються від використання CodeIgniter.

Чому, запитаєте ви?

CodeIgniter підтримував PHP 4 до того, як статичні методи були додані в PHP 5. Крім того, CodeIgniter використовує «супер-об'єкт», який надає рівний доступ всіх класах, призначеним контролера. Таким чином вони стає доступними до використання в рамках всієї системи.

Це означає, що класи можуть бути доступні з будь-якої моделі з допомогою методу __get (), який буде шукати запитувана властивість за допомогою get_instance () -\u003e ($ var). Раніше, коли підтримки функції __get () не було в PHP 4, для цього використовувалася конструкція foreach через параметри CI_Controller, а потім вони призначалися змінної $ this в моделі.

В бібліотеці ви повинні викликати get_instance. Бібліотека не буде наслідувати клас в примусовому порядку, тому немає ніякого способу обійти функцію __get ().

Об `єм…

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

Та й немає особливого сенсу міркувати про таку конструкції. Добре, ви можете отримати доступ до даних сесії в своїй моделі. Але навіщо вам це робити?

"Рішення"

Kohana-розробники були першими, хто серйозно попрацював над статичними методами. Вони внесли такі зміни:
// було $ this-\u003e input-\u003e get ( "foo"); // стало Input :: get ( "foo");
Для багатьох CodeIgniter-розробників з їх застарілим PHP 4, які переїхали на фреймфорк Kohana, щоб користуватися всіма принадами PHP 5, в цьому нічого незвичайного немає. Але ж, чим менше символів, тим краще, так?

І в чому ж проблема?

Багато PHP-розробники (особливо ті, хто добре розбирається в Symfony і Zend) скажуть: «Це ж очевидно - використовуй Впровадження залежностей!» Але мало хто розробники в співтоваристві CodeIgniter мають реальний досвід роботи з цим процесом, так як він досить складний.

Ще один факт про фреймворку Fuel PHP - поки в основному статичні методи виступають в якості інтерфейсу. Наприклад, логіка все ще має проблеми зі статикою, особливо коли задіюється концепція HMVC.

Це псевдокод, який я не використав в FuelPHP, починаючи з версії 1.1:
class ControllerA extends Controller (public function action_foo () (echo Input :: get ( "param");))
Досить стандартний код. Цей метод буде виводити значення ? Bar \u003d в методі.

Що відбувається, коли ми робимо HMVC-запит до даного методу?
class ControllerB extends Controller (public function action_baz () (echo Input :: get ( "param"); \u200b\u200becho "&"; echo Request :: forge ( "controllera / foo? param \u003d val1") -\u003e execute ();) )
Викликавши в браузері controllerb / baz, Ви побачите висновок «val1», але якщо ви наберете controllerb / baz? param \u003d override, То отримаєте обидва виклику для отримання методу, який повертає те ж саме значення.

актуальність

Глобальний код не дасть вам жодного стосунку. Приклад краще будь-яких слів:
$ This-\u003e request-\u003e input-\u003e get ( "param");
Запитаний об'єкт буде містити абсолютно новий екземпляр для кожного запиту, тоді введення об'єкта буде створюватися для кожного запиту, який містить тільки вхідні дані для конкретного запиту. Це справедливо для FuelPHP 2.0 plans to work і вирішує проблему Впровадження залежностей, а також проблеми з HMVC.

Як бути з грубим синтаксисом?

Symfony- або Zend- розробники таким не страждають, а ось тим, хто використовує CodeIgniter, довго будуть снитися кошмари про «повернення до PHP 4».

$ This завжди звертається до «поточному» об'єкту, і точно не варто використовувати її для доступу до глобального коду.

$ This-\u003e request-\u003e input-\u003e get () може виглядати як подовжена форма CodeIgniter-синтаксису, але насправді ми просто перебуваємо в контролері. Коли контролер створює екземпляр нового вкладеного в нього запиту, конструктор запиту також отримує екземпляр на вході.

Якщо ви перебуваєте в моделі або іншому класі, то доступ виду $ this-\u003e request-\u003e input-\u003e foo () не працюватиме, тому що $ this - чи не контролери.

Конструкція Input :: get ( "foo") створює фасад для примірників логіки в фоновому режимі. Але це не вирішує питань, пов'язаних з роботою глобального коду. Найледачіші при тестуванні додатків можуть перемикатися між двома режимами без необхідності повністю використовувати новий фреймворк.

Є відмінне відео від Тейлора Отвелла (creator або laravel 4), в якому він описує, як ви можете замінити статичний код з одиничним екземпляром, перевіряється через його DiC-контейнер.

Laravel 4 - IoC Controller Injection & Unit Testing from UserScape on Vimeo.

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

На цій сумній ноті ...

Зараз я займаюся перетворенням PyroCMS з CodeIgniter на Laravel. Намагаюся перейти прямо від глобального коду PHP 4 до скоєного впровадження залежностей - це абсолютне самогубство. Проміжний крок перед використанням завантажувача CI - використання PHP 5, автозавантажувані коду PSR-2 з купою статичних методів. Ну а поки ми все ще перебуваємо в CodeIgniter.

Перехід від статики до DiC-коду можна буде легко показати, коли ми, нарешті, зробимо перехід на Laravel.

Перехід від Сільносвязанная коду CodeIgniter до тестованого PSR-2 - головне завдання. Команда Pyro вже в дорозі - і це буде епічно.

У PHP є можливість визначити метод як статичний. Статичний метод не має доступу до властивостей об'єкта. Такі методи можуть бути викликані тільки в контексті класу, але не в контексті об'єкта.

Найважливіше що потрібно зрозуміти - статичні властивості і методи належать класам, а не об'єктів.

На прикладі стане відразу зрозуміло. Давайте створимо обєкт Math (скорочене назвою математики в англійському).

Статичні методи PHP
"; $ Math_1 \u003d new Math (); $ math_2 \u003d new Math (); $ math_3 \u003d new Math (); $ math_4 \u003d new Math (); echo" Створено об'єктів: ". Math :: getCount ();?\u003e

Цей клас надає інструменти для роботи з математичними функціями без необхідності створення об'єкта. У класі є конструктор, який збільшує статичну властивість $ count на одиницю. Клас запам'ятовує значення цієї властивості, так як воно статичне.

До речі, для оголошення методу або властивості статичним використовується слово static, а для доступу до статичного властивості використовується слово self з подвійним двокрапкою "::".

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

Статичні методи PHP counter ++; ) Public static function calcSin ($ x) (return sin ($ x);) public static function calcSQRT ($ x) (return sqrt ($ x);) public static function getCount () (return self :: $ count;) public function getCounter () (return $ this-\u003e counter;)) echo Math :: calcSin (1); echo "
"; Echo Math :: calcSQRT (9); echo"
"; $ Math_1 \u003d new Math (); $ math_2 \u003d new Math (); $ math_3 \u003d new Math (); $ math_4 \u003d new Math (); echo" Створено об'єктів: ". Math :: getCount (); echo"
"; Echo" Створено об'єктів: ". $ Math_4-\u003e getCounter ();?\u003e

У цьому прикладі ми додали класу звичайне властивість $ counter, воно також збільшувалася на одиницю в конструкторі. Але звичайне властивість належить об'єкту, тому воно не зберігається між викликами об'єктів. При будь-якому створенні екземпляра класу (об'єкта) властивість дорівнюватиме нулю, в конструкторі воно буде збільшено на одиницю.

Статична властивість належить класу, тому його значення зберігається.

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

Спроба використовувати в статичному методі змінну $ this призведе до помилки (Fatal error: Using $ this when not in object context).

Статичні методи PHP age. "Old."; // це помилка "Using $ this when not in object context". )) $ TestClass \u003d new TestClass (); $ TestClass-\u003e sayHello (); ?\u003e

До речі, без рядка:

$ TestClass-\u003e sayHello ();

помилки не буде, але як тільки ви в коді спробуєте запустити статичний метод зі змінною $ this, ви відразу отримаєте повідомлення про помилку.

Якщо в цьому прикладі прибрати слово static, то помилки не буде.

Якщо з статичного методу звернутися до властивості об'єкта, то це призведе до помилки. Можна звертатися тільки до статичних властивостями за допомогою конструкції self :: $ age. Зверніть увагу, що тут є знак $ перед ім'ям змінної, на відміну від конструкції $ this-\u003e age.

Статичні методи PHP sayHello (); ?\u003e

Якщо в цьому прикладі прибрати слово static перед ім'ям властивості, то виникне помилка "Access to undeclared static property".

Статичні властивості відсутні в об'єктах класу.

Статичні методи PHP "; Print_r ($ TestClass); echo""; ?>

Статичний метод можна викликати використовуючи конструкцію self :: метод (). приклад:

Статичні методи PHP printHello (); ?\u003e

Статична властивість можна отримати в контексті класу використовуючи синтаксис:

echo TestClass :: $ age;

Причому спроба звернення до звичайного властивості таким чином призведе до помилки: "Fatal error: Access to undeclared static property".

Статична властивість можна змінювати в контексті класу використовуючи синтаксис:

TestClass :: $ age + \u003d 20; // наприклад

Ще приклад коду зі статичними методами і властивостями

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

Статичні методи PHP ".TestClass :: $ age; // echo TestClass :: $ txt; // Помилка: Fatal error: Access to undeclared static property. Echo"
"; TestClass :: sayHi (); echo"
"; TestClass :: sayHello (); // А ось так у мене вийшло отримати доступ до статичної змінної через об'єкт ($ obj :: $ age) ... echo"
"; $ Obj \u003d new TestClass; echo" Отримуємо поступ до статичної змінної через об'єкт: ". $ Obj :: $ age;?\u003e

Зверніть увагу, і це важливо, в цьому прикладі ми звернулися до нестатичних методу sayHi () використовуючи синтаксис звернення до статичних елементів класу.

резюме

  • Головне: статичні властивості належать класам, а не об'єктів.
  • З статичного методу можна звернутися до звичайних властивостей і методів класу, $ this-\u003e name не працює тут.
  • З статичного методу можна звернутися до статичних властивостями використовуючи self :: $ name.
  • Статичні властивості класу не доступні об'єктам.
  • Звичайний метод може звернутися до статичного властивості використовуючи self :: $ name.
  • Статична властивість можна отримати в контексті класу використовую синтаксис: TestClass :: $ age.
  • Звичайний метод можна викликати в контексті і об'єкта ($ object-\u003e метод ()), і класу використовуючи синтаксис TestClass :: метод ().
  • За допомогою синтаксису $ object :: $ age у мене вийшло отримати доступ до статичного властивості через об'єкт.

Паралелі з JavaScript

В JavaScript є такий клас Math, що містить дуже багато різних математичних функцій.

Для проведення математичних обчислень (розрахунок синуса або експоненти) в JavaScript не потрібно створювати обєкт класу Math, так як його методи є статичними. До вивчення PHP я ніяк не міг зрозуміти що це таке, і тільки вивчивши класи і об'єкти в PHP у мене в голові все встало на свої полички.

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

Давно хотів написати на цю тему. Першим поштовхом послужила стаття Miško Hevery "Static Methods are Death to Testability". Я написав відповідну статтю, але так і не опублікував її. А ось недавно побачив щось, що можна назвати «Клас-орієнтоване програмування». Це освіжило мій інтерес до теми і ось результат.

«Клас-орієнтування Програмування» - це коли використовуються класи, що складаються тільки з статичних методів і властивостей, а екземпляр класу ніколи не створюється. У цій статті я буду говорити про те, що:

  • це не дає ніяких переваг в порівнянні з процедурним програмуванням
  • не варто відмовлятися від об'єктів
  • наявність статичних членів класу! \u003d смерть тестів
Хоча ця стаття про PHP, концепції застосовні і до інших мов.

залежності

Зазвичай, код залежить від іншого коду. наприклад:

$ Foo \u003d substr ($ bar, 42);
Цей код залежить від змінної $ bar і функції substr. $ Bar - це просто локальна змінна, певна трохи вище в цьому ж файлі і в тій же області видимості. substr - це функція ядра PHP. Тут все просто.

Тепер, такий приклад:

Class BloomFilter (... public function __construct ($ m, $ k) (...) public static function getK ($ m, $ n) (return ceil (($ m / $ n) * log (2)); ) ...)
Ця маленька допоміжна функція просто надає обгортку для конкретного алгоритму, який допомагає розрахувати гарне число для аргуметом $ k, що використовується в конструкторі. Оскільки вона повинна бути викликана до створення екземпляра класу, вона повинна бути статичною. Цей алгоритм не має зовнішніх залежностей і навряд чи буде замінений. Він використовується так:

$ M \u003d 10000; $ N \u003d 2000; $ B \u003d new BloomFilter ($ m, BloomFilter :: getK ($ m, $ n));
Це не створює ніяких додаткових залежностей. Клас залежить сам від себе.

  • Альтернативний конструктор. Хорошим прикладом є клас DateTime, вбудований в PHP. Його екземпляр можна створити двома різними способами:

    $ Date \u003d new DateTime ( "2012-11-04"); $ Date \u003d DateTime :: createFromFormat ( "d-m-Y", "04-11-2012");
    В обох випадках результатом буде екземпляр DateTime і в обох випадках код прив'язаний до класу DateTime так чи інакше. Статичний метод DateTime :: createFromFormat - це альтернативний коструктор об'єкта, який повертає те ж саме що і new DateTime, але використовуючи додаткову функціональність. Там, де можна написати new Class, можна написати і Class :: method (). Ніяких нових залежностей при цьому не виникає.

  • Решта варіантів використання статичних методів впливають на зв'язування і можуть утворювати неявні залежності.

    Слово про абстракції

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

    Для прикладу, у Вас є клас Application, який представляє ваша заявка. Він спілкується з класом User, який є предствлений користувача. Який отримує дані від Database. Класу Database потрібен DatabaseDriver. DatabaseDriver потрібні спеціальні установки. І так далі. Якщо просто викликати Application :: start () статично, який викличе User :: getData () статично, який викличе БД статично і так далі, в надії, що кожен шар розбереться зі своїми залежностями, можна отримати жахливий бардак, якщо щось піде не так. Неможливо вгадати, чи буде працювати виклик Application :: start (), тому що зовсім не очевидно, як себе поведуть внутрішні залежності. Ще гірше те, що єдиний спосіб впливати на поведінку Application :: start () - це змінювати вихідний код цього класу і код класів які він визизвает і код класів, які визизвают ті класи ... в будинку який побудував Джек.

    Найбільш ефективний підхід, при створенні великих програм - це створення окремих частин, на які можна спиратися в подальшому. Частин, про які можна перестати думати, в яких можна бути впевненим. Наприклад, при виклику статичного Database :: fetchAll (...), немає ніяких гарантій, що з'єднання з БД вже встановлено або буде встановлено.

    Function (Database $ database) (...)
    Якщо код всередині цієї функції буде виконаний - це значить, що екземпляр Database був успішно переданий, що означає, що екземпляр об'єкта Database був успішно створений. Якщо клас Database спроектований вірно, то можна бути впевненим, що може бути екземпляр цього класу означає можливість виконувати запити до БД. Якщо примірника класу не буде, то тіло функції не буде виконано. Це означає, що функція не повинна піклуватися про стан БД, клас Database це зробить сам. Такий підхід дозволяє забути про залежності і сконцентруватися на вирішенні завдань.

    Без можливості не думати про залежності і залежності цих залежностей, практично неможливо написати хоч скільки-небудь складний додаток. Database може бути маленьким класом-обгорткою або гігантським багатошаровим монстром з купою залежностей, він може початися як маленька обгортка і мутувати в гігантського монстра з часом, Ви можете успадкувати клас Database і передати в функцію нащадок, це все не важливо для Вашої function (Database $ database), до тих пір поки, публічний інтерфейс Database не змінюється. Якщо Ваші класи правильно відокремлені від інших частин програми за допомогою впровадження залежностей, Ви можете тестувати кожен з них, використовуючи заглушки замість їх залежностей. Коли Ви протестували клас досить, щоб переконатися, що він працює як треба, Ви можете викинути зайве з голови, просто знаючи, що для роботи з БД потрібно використовувати екземпляр Database.

    Клас-орієнтоване програмування - дурість. Вчіться використовувати ООП.

    Reg.ru: домени і хостинг

    Найбільший реєстратор і хостинг-провайдер в Росії.

    Більше 2 мільйонів доменних імен на обслуговуванні.

    Просування, пошта для домену, рішення для бізнесу.

    Понад 700 тис. Клієнтів по всьому світу вже зробили свій вибір.

    * Наведіть курсор миші для припинення прокрутки.

    Назад вперед

    Статичні методи і властивості в PHP

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

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

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

    Але насправді не все так просто. Ми можемо отримувати доступ і до методів, і до властивостей в контексті класу, а не об'єкта. Такі методи і властивості називаються "статичними" і повинні бути оголошені за допомогою ключового слова static.

    Class StaticExample (static public $ aNum \u003d 0; static public function sayHello () (print "Привіт!";))

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

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

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

    Print StaticExample :: $ aNum; StaticExample :: sayHello ();

    З цим синтаксисом ви вже повинні бути знайомі з основ ООП в PHP. Ми використовували конструкцію "::" в поєднанні з ключовим словом parent для того, щоб отримати доступ до перевизначення методу батьківського класу.

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

    Щоб отримати доступ до статичного методу або властивості з того ж самого класу (а не з дочірнього класу), ми будемо використовувати ключове слово self.

    Ключове слово self використовується для звернення до поточного класу, а псевдопеременная $ this - до поточного об'єкту. Тому з-за меж класу StaticExample ми звертаємося до властивості $ aNum за допомогою імені його класу.

    StaticExample :: $ aNum;

    А всередині класу StaticExample можна використовувати ключове слово self.

    Class StaticExample (static public $ aNum \u003d 0; static public function sayHello () (self :: $ aNum ++; print "Привіт! (". Self :: $ aNum. ") \\ N";))

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

    За визначенням статичні методи не викликаються в контексті об'єкта. З цієї причини статичні методи і властивості часто називають змінними і властивостями класу. Як наслідок, не можна використовувати псевдопеременную $ this всередині статичного методу.

    А навіщо взагалі використовувати статичний метод або властивість?

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

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

    По-друге, Статичне властивість доступно кожному примірнику об'єкта цього класу. Тому можна визначити значення, які повинні бути доступні всім об'єктам даного типу.

    І наприкінці, по-третє, Сам факт, що не потрібно мати екземпляр класу для доступу до його статичному властивості або методу, дозволить уникнути створення екземплярів об'єктів виключно заради виклику простий функції.

    Щоб продемонструвати це, давайте створимо статичний метод для класу ShopProduct, Який буде автоматично створювати екземпляри об'єктів ShopProduct. C допомогою SQLite визначимо таблицю products наступним чином:

    CREATE TABLE products (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, firstname TEXT, mainname TEXT, title TEXT, price float, numpages int, playlength int, discount int)

    Тепер створимо метод getInstance (), Якому передається ідентифікатор рядки і об'єкт типу PDO. Вони будуть використовуватися для отримання рядка з таблиці бази даних, на підставі якої потім формується об'єкт типу ShopProduct, Що повертається в зухвалу програму.

    Ми можемо додати ці методи до класу ShopProduct, Який був створений нам в більш ранніх матеріалах. Як ви, напевно, знаєте, PDO розшифровується як PHP Data Object (об'єкти даних PHP). Клас PDO забезпечує універсальний інтерфейс для різних додатків баз даних.

    // Клас ShopProduct private $ id \u003d 0; public function setID ($ id) ($ this-\u003e id \u003d $ id;) // ... public static function getInstance ($ id, PDO $ pdo) ($ stmt \u003d $ pdo-\u003e prepare ( "select * from products where id \u003d? "); $ result \u003d $ stmt-\u003e execute (array ($ id)); $ row \u003d $ stmt-\u003e fetch (); if (empty ($ row)) (return null;) if ($ row [ "type"] \u003d\u003d "book") ($ product \u003d new BookProduct ($ row [ "title"], $ row [ "firstname"], $ row [ "mainname"], $ row [ "price"] , $ row [ "numpages"]);) else if ($ row [ "type"] \u003d\u003d "cd") ($ product \u003d new CdProduct ($ row [ "title"], $ row [ "firstname"], $ row [ "mainname"], $ row [ "price"], $ row [ "playlength"]);) else ($ product \u003d new ShopProduct ($ row [ "title"], $ row [ "firstname"], $ row [ "mainname"], $ row [ "price"]);) $ product-\u003e setId ($ row [ "id"]); $ product-\u003e setDiscount ($ row [ "discount"]); return $ product;) // ...

    Як бачите, метод getInstance () повертає об'єкт типу ShopProduct, Причому він досить "розумний" для того, щоб на підставі значення поля type створювати об'єкт з потрібними характеристиками.

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

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

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

    У цьому методі не використовуються будь-які методи або властивості, що вимагають окремого примірника об'єкта, тому немає ніякої причини, щоб не оголосити його статичним. Тоді, маючи коректний PDO-об'єкт, ми можемо викликати даний метод з будь-якого місця програми.

    $ Dsn \u003d "sqlite: //home/bob/projects/products.db"; $ Pdo \u003d new PDO ($ dsn, , null); $ Pdo-\u003e setAttribute (PDO :: ATTR_ERRMODE, PDO :: ERRMODE_EXCEPTION); $ Obj \u003d ShopProduct :: getInstance (1, $ pdo);

    Подібні методи працюють як "фабрики", оскільки вони беруть "сирі" матеріали (наприклад, дані, отримані з рядка бази даних або файлу конфігурації) і використовують їх для створення об'єктів.

    Термін "фабрика" відноситься до коду, призначеному для створення екземплярів об'єктів. З прикладами подібних "фабрик" ми ще зустрінемося з вами далі.


    постійні властивості

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

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

    На відміну від звичайних властивостей, перед ім'ям постійного властивості не ставиться знак долара. За прийнятим угодою їм часто призначають імена, що складаються тільки з великих літер, як в наступному прикладі:

    Class ShopProduct (const AVAILABLE \u003d 0; const OUT_OF_STOCK \u003d 1; // ...

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

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

    Print ShopProduct :: AVAILABLE;

    Спроба привласнити константі значення після того, як вона була оголошена, призведе до помилки на етапі синтаксичного аналізу ..

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

    На цьому дану статтю я завершую, а в наступній мова піде про.

    Сподобався матеріал і хочете віддячити?
    Просто поділіться з друзями і колегами!




    Сподобалася стаття? поділіться їй