Php статические методы. Почему некоторые PHP-разработчики предпочитают статические API? Как быть с грубым синтаксисом



В мире существуют две разновидности PHP-разработчиков. Одни предпочитают статические методы, потому что с ними легко работать, другие же, напротив, считают статические методы - зло и не используют их в своей практике.
В этой статье попробую, используя опыт работы с несколькими фреймворками, объяснить, почему некоторые разработчики игнорируют лучшие практики и используют целую кучу статических методов.Кто любит статические методы?Особенно часто их применяют разработчики, которые когда-либо использовали в своей работе фреймворк CodeIgniter .

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

Почему, спросите вы?

CodeIgniter поддерживал PHP 4 до того, как статические методы были добавлены в PHP 5. Кроме того, CodeIgniter использует «супер-объект», который предоставляет равный доступ всем классам, назначенным контроллеру. Таким образом они становится доступными к использованию в рамках всей системы.

Это означает, что классы могут быть доступны из любой модели с помощью метода __get(), который будет искать запрашиваемое свойство с помощью get_instance()->{$var} . Ранее, когда поддержки функции __get() не было в PHP 4, для этого использовалась конструкция foreach через параметры CI_Controller, а затем они назначались переменной $this в модели.

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

Объем…Получается довольно громоздкая конструкция для доступа к коду. Точно такой ​​же функциональности без дополнительных усилий можно достичь с помощью статического метода.

Да и нет особого смысла рассуждать о такой конструкции. Хорошо, вы можете получить доступ к данным сессии в своей модели. Но зачем вам это делать?

«Решение»Kohana-разработчики были первыми, кто серьезно поработал над статическими методами. Они внесли следующие изменения:
//было $this->input->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= в методе.

Что происходит, когда мы делаем HMVC-запрос к данному методу?
class ControllerB extends Controller { public function action_baz() { echo Input::get("param"); echo " & "; echo Request::forge("controllera/foo?param=val1")->execute(); } }
Вызвав в браузере controllerb/baz , вы увидите вывод «val1», но если вы наберете controllerb/baz?param=override , то получите оба вызова для получения метода, возвращающего то же самое значение.

АктуальностьГлобальный код не даст вам какого-либо отношения. Пример лучше любых слов:
$this->request->input->get("param");
Запрошенный объект будет содержать совершенно новый экземпляр для каждого запроса, тогда ввод объекта будет создаваться для каждого запроса, который содержит только входные данные для конкретного запроса. Это справедливо для FuelPHP 2.0 plans to work и решает проблему Внедрения зависимостей, а также проблемы с HMVC.Как быть с грубым синтаксисом?Symfony- или Zend- разработчики таким не страдают, а вот тем, кто использует CodeIgniter, долго будут сниться кошмары о «возвращении к PHP 4».

$this всегда обращается к «текущему» объекту, и точно не стоит использовать ее для доступа к глобальному коду.

$this->request->input->get() может выглядеть как удлиненная форма CodeIgniter-синтаксиса, но на самом деле мы просто находимся в контроллере. Когда контроллер создает экземпляр нового вложенного в него запроса, конструктор запроса также получает экземпляр на входе.

Если вы находитесь в модели или другом классе, то доступ вида $this->request->input->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

Этот класс предоставляет инструменты для работы с математическими функциями без необходимости создания объекта. В классе есть конструктор, который увеличивает статическое свойство $count на единицу. Класс запоминает значение этого свойства, так как оно статическое.

Кстати, для объявления метода или свойства статическим используется слово static , а для доступа к статическому свойству используется слово self с двойным двоеточием " :: ".

Всё это лучше понять в сравнении, особенно в сравнении рабочего примера с ошибочным. Давайте немного расширим наш пример.

Статические методы PHP

В этом примере мы добавили классу обычное свойство $counter , оно также увеличивалось на единицу в конструкторе. Но обычное свойство принадлежит объекту, поэтому оно не сохраняется между вызовами объектов. При любом создании экземпляра класса (объекта) свойство будет равно нолю, в конструкторе оно будет увеличено на единицу.

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

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

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

Статические методы PHP

Если в этом примере убрать слово static перед именем свойства, то возникнет ошибка "Access to undeclared static property".

Статические свойства отсутствуют в объектах класса.

Статические методы PHP

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

Статические методы PHP

Статическое свойство можно получить в контексте класса используя синтаксис:

echo TestClass::$age;

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

Статическое свойство можно менять в контексте класса используя синтаксис:

TestClass::$age += 20; // например

Ещё пример кода со статическими методами и свойствами

В этом примере ещё простые варианты использования статических методов и свойств. Чем больше простого кода вы поймёте, тем лучше запомните материал.

Статические методы PHP

Обратите внимание, и это важно, в этом примере мы обратились к нестатическому методу sayHi() используя синтаксис обращения к статическим элементам класса.

Резюме
  • Главное: статические свойства принадлежат классам, а не объектам.
  • Из статического метода нельзя обратиться к обычным свойствам и методам класса, $this->name не работает тут.
  • Из статического метода можно обратиться к статическим свойствам используя self::$name .
  • Статические свойства класса не доступны объектам.
  • Обычный метод может обратиться к статическому свойству используя self::$name .
  • Статическое свойство можно получить в контексте класса использую синтаксис: TestClass::$age .
  • Обычный метод можно вызвать в контексте и объекта ($object->метод()), и класса используя синтаксис TestClass::метод() .
  • При помощи синтаксиса $object::$age у меня получилось получить доступ к статическому свойству через объект.
Параллели с JavaScript

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

Для проведения математических вычислений (расчёт синуса или экспоненты) в JavaScript не нужно создавать обект класса Math, так как его методы являются статическими. До изучения PHP я никак не мог понять что это такое, и только изучив классы и объекты в PHP у меня в голове всё встало на свои полочки.

На самом деле это очень удобно, иметь прямой доступ к математическим методам класса Math, избегая создания объекта.

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

Самый классический пример - это класс, отвечающий за математические функции. Если кто-то знает Java , то он знает, что там имеется класс Math (в JavaScript такой класс тоже есть), содержащий множество математических функций. И там методы являются статическими. Это означает, что для того, чтобы посчитать какой-нибудь синус или экспоненту, не нужно создавать объект данного класса, что очень удобно.

Давайте мы с Вами напишем уменьшенную копию данного класса, но только для PHP:

В данном коде я показал использование статических методов и свойств . Обратите внимание, что я реализовал классический способ счётчика объектов. Это получилось лишь благодаря тому, что поле count является статическим, и оно имеет одно и то же значение для всех объектов.

Ещё один популярный пример использования статических методов и свойств - это ведение лога. Все записи добавляются через статические методы. Также очень часто делают класс, состоящий из множества настроек, и там также все поля являются статическими. Как видите, примеров использования статических методов и свойств в PHP и других языках более, чем достаточно, поэтому уметь с ними работать надо обязательно.

(Late Static Binding, LSB) является бурно темой обсуждений последние три года в кругах разработчиков PHP (и наконец мы его получили в PHP 5.3). Но зачем оно нужно? В данной статье, как раз и будет рассматриваться, как позднее статическое связывание может значительно упростить ваш код.

На встрече разработчиков PHP, которая проходила в Париже в ноябре 2005 года, тема позднего статического связывания официально обсуждалась основной командой разработчиков. Они согласились реализовать его, наряду со многими другими темами, которые стояли на повестке дня. Детали должны были быть согласованы в ходе открытых дискуссий.

С тех пор как позднее статическое связывание было объявлено как грядущая фишка, прошло два года. И вот наконец LSB стало доступно для использования в PHP 5.3. Но это событие прошло незаметно для разработчиков использующих PHP, из заметок только страничка в мануале .

Если кратко, новая функциональность позднего статического связывания, позволяет объектам все также наследовать методы у родительских классов, но помимо этого дает возможность унаследованным методам иметь доступ к статическим константам, методам и свойствам класса потомка, а не только родительского класса. Давайте рассмотрим пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Beer {
const NAME = "Beer!" ;

return self :: NAME ;
}
}
class Ale extends Beer {
const NAME = "Ale!" ;
}

$beerDrink = new Beer;
$aleDrink = new Ale;

echo "Beer is: " . $beerDrink -> getName () . "\n " ;
echo "Ale is: " . $aleDrink -> getName () . "\n " ;

Этот код выдаст такой результат:

Beer is: Beer!

Ale is: Beer!

Класс Ale унаследовал метод getName() , но при этом self все еще указывает на класс в котором оно используется (в данном случае это класс Beer ). Это осталось и в PHP 5.3, но добавилось слово static . И снова рассмотрим пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class Beer {
const NAME = "Beer!" ;
public function getName() {
return self :: NAME ;
}
public function getStaticName() {
return static:: NAME ;
}
}

class Ale extends Beer {
const NAME = "Ale!" ;
}

$beerDrink = new Beer;

$aleDrink = new Ale;

echo "Beer is: " . $beerDrink -> getName () . "\n " ;
echo "Ale is: " . $aleDrink -> getName () . "\n " ;

echo "Beer is actually: " . $beerDrink -> getStaticName () . "\n " ;
echo "Ale is actually: " . $aleDrink -> getStaticName () . "\n " ;

Новое ключевое слово static указывает, что необходимо использовать константу унаследованного класса, вместо константы которая была определена в классе где объявлен метод getStaticName() . Слово static было добавлено, чтобы реализовать новый функционал, а для обратной совместимости self работает также как и в предыдущих версиях PHP.

Внутренне, основное отличие (и, собственно, причина почему связывание назвали поздним) между этими двумя способами доступа, в том, что PHP определят значение для self::NAME во время «компиляции» (когда симовлы PHP преобразуются в машинный код, который будет обрабатываться движком Zend), а для static::NAME значение будет определено в момент запуска (в тот момент, когда машинный код будет выполнятся в движке Zend).

Это еще один инструмент для PHP-разработчиков. Во второй части рассмотрим как его можно использовать во благо.

Начиная с версии PHP 5.3.0 появилась особенность, называемая позднее статическое связывание, которая может быть использована для того чтобы получить ссылку на вызываемый класс в контексте статического наследования.

Если говорить более точно, позднее статическое связывание сохраняет имя класса указанного в последнем "не перенаправленном вызове". В случае статических вызовов это явно указанный класс (обычно слева от оператора :: ); в случае не статических вызовов это класс объекта. "Перенаправленный вызов" - это статический вызов, начинающийся с self:: , parent:: , static:: , или, если двигаться вверх по иерархии классов, forward_static_call() . Функция get_called_class() может быть использована чтобы получить строку с именем вызванного класса, и static:: представляет ее область действия.

Само название "позднее статическое связывание" отражает в себе внутреннюю реализацию этой особенности. "Позднее связывание" отражает тот факт, что обращения через static:: не будут вычисляться по отношению к классу, в котором вызываемый метод определен, а будут вычисляться на основе информации в ходе исполнения. Также эта особенность была названа "статическое связывание" потому, что она может быть использована (но не обязательно) в статических методах.

Ограничения self::

Пример #1 Использование self::

Использование позднего статического связывания

Позднее статическое связывание пытается устранить это ограничение предоставляя ключевое слово, которое ссылается на класс, вызванный непосредственно в ходе выполнения. Попросту говоря, ключевое слово, которое позволит вам ссылаться на B из test() в предыдущем примере. Было решено не вводить новое ключевое слово, а использовать static , которое уже зарезервировано.

Пример #2 Простое использованиеstatic::

Результат выполнения данного примера:

Замечание :

В нестатическом контексте вызванным классом будет тот, к которому относится экземпляр объекта. Поскольку $this-> будет пытаться вызывать закрытые методы из той же области действия, использование static:: может дать разные результаты. Другое отличие в том, что static:: может ссылаться только на статические поля класса.

Пример #3 Использование static:: в нестатическом контексте

Результат выполнения данного примера:

success! success! success! Fatal error: Call to private method C::foo() from context "A" in /tmp/test.php on line 9

Замечание :

Разрешающая область позднего статического связывания будет фиксирована вычисляющем ее статическим вызовом. С другой стороны, статические вызовы с использованием таких директив как parent:: или self:: перенаправляют информацию вызова.

Пример #4 Перенаправленные и не перенаправленные вызовы

Публикации по теме