Перейти к основному содержанию

Основы Drupal 8 ООП

Независимо от того, знакомы вы с кодом Drupal или нет, важно понимать, что использование объектно-ориентированного программирования встречалось очень редко в предыдущих версиях Drupal. Помимо меньшинства подсистем и нескольких дополнительных модулей, Drupal в основном использовал процедурный код. Пришло время перестроить архитектуру Drupal таким образом, чтобы она использовала современные, ориентированные на будущее и широко используемые шаблоны объектно-ориентированного программирования, открывая дверь для становления еще более гибкой CMS.

Эта первая часть серии  демонстрирует основы объектно-ориентированного программирования на PHP. Мы выясним, что такое Objects и Classes, и создадим простой класс, над которым мы будем работать.

 

Объекты и Классы


Хотя эти два термина часто используются взаимозаменяемо, между ними существует большая разница. Думайте о Классе как о шаблоне для реального Объекта, который будет использоваться в программе. Например, можно создать класс для собак. Этот класс может содержать свойства собаки (размер, цвет и т. Д.), А также методы (функции в мире ООП), которые определяют, что эта собака делает (лает, играет, спит и т. Д.).

Давайте создадим наш класс собаки сейчас:

<?php

  class Dog {

    public $color;
    public $size;

    public function play() {
      print "The dog is now playing!";
      return TRUE;
    }  
  }

?>

Объект с другой стороны - это фактическое создание данного класса. Давайте создадим новый объект на основе нашего класса собаки:

<?php

  // Instantiate our class
  $dog = new Dog();

?>

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

Теперь у нас есть базовый объект с двумя свойствами: цвет и размер. Это здорово, но как мы можем изменить эти свойства? Давайте построим на нашем предыдущем примере:

<?php
  // Instantiate our class
  $dog = new Dog();

  // Set the properties
  $dog->color = 'brown';
  $dog->size = 'small';

  // Print the result
  print "This dog is ". $dog->color ." and ". $dog->size;

?>

Приведенный выше пример должен вывести следующий текст:

This dog is brown and small

 

Методы


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

<?php

  // Instantiate our class
  $dog = new Dog();

  // Use our method
  $dog->play();

?>

 

Конструкторы


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

<?php

  class Dog {

    public $color;
    public $size;
    public $likes_bones;

    public function __construct() {
      $this->likes_bones = TRUE;
    }
    
    public function play() {
      print "The dog is now playing!";
      return TRUE;
    }  
  }

?>

Когда мы создаем и проверяем наш объект Object (используя функцию print_r () или var_dump () ), мы должны получить следующее:

Dog Object
(
    [color] => 
    [size] => 
    [likes_bones] => 1
)

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

<?php
  class Dog {

    public $color;
    public $size;
    public $likes_bones;

    public function __construct($color = NULL, $size = NULL) {
      $this->likes_bones = TRUE;
      $this->color = $color;
      $this->size = $size;
    }
    
    public function play() {
      print "The dog is now playing!";
      return true;
    }  
  }

?>

Теперь, когда мы создаем экземпляр этого класса, мы передаем эти параметры конструктору класса при создании объекта:

<?php

  // Create our parameters and pass them on
  // to the class constructor
  $color = 'brown';
  $size = 'small';
  
  $dog = new Dog($color, $size);

  // Print the result
  print "This dog is ". $dog->color ." and ". $dog->size;

?>

Как и следовало ожидать, приведенный выше пример будет выводить:

This dog is brown and small

 

Наследование


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

Давайте начнем с изменения нашего базового класса Dog, добавив свойство breed и добавив к нему метод getDogProperties () :

<?php

  class Dog {

    public $color;
    public $size;
    public $likes_bones;
    public $breed = 'dog';
    
    public function __construct($color = NULL, $size = NULL) {
      $this->likes_bones = TRUE;
      $this->color = $color;
      $this->size = $size;
    }
        
    public function getDogProperties() {
      print "This " . $this->breed . " is " . $this->color . " and " . $this->size;
    }
    
    public function play() {
      print "The dog is now playing!";
      return true;
    }    
  }

?>

Теперь, когда мы подготовили наш базовый класс, давайте расширим его реальным классом породы:

<?php

  class Bulldog extends Dog {
    public $breed = 'Bulldog';    

    public function __construct() {
      $color = 'brown & white';
      $size = 'mid-sized';
      
      parent::__construct($color, $size);      
    }   
  }

?>

Расширение класса позволяет нам наследовать все свойства и методы, определенные родительским классом, и использовать их в нашем расширяющем классе. В нашем классе Bulldog мы определяем свойство породы, а в функции конструктора мы передаем свойства цвета и размера родительскому конструктору. Давайте создадим наш новый объект Bulldog и вызовем для него метод getDogProperties () :

<?php

  $bulldog = new Bulldog();
  print $bulldog->getDogProperties();

?>

Код выше должен вывести:

This Bulldog is brown & white and mid-sized

Точно так же мы можем создать дополнительные классы породы, которые расширены от нашего базового класса собаки:

<?php

  class Vizsla extends Dog {
    public $breed = 'Hungarian Vizsla';
    
    public function __construct($color, $size) {
      $color = 'brown';
      $size = 'large';
      
      parent::__construct($color, $size);      
    }   
  }  
  
  $vizsla = new Vizsla();
  print $vizsla->getDogProperties();

?>

Расширение класса также позволяет нам переопределять методы, определенные родительским классом - это просто делается путем реализации функции с тем же именем, что и в родительском классе. Допустим, наш бульдог немного ленив и не любит играть (на самом деле я владелец бульдога и могу сказать, что иногда это так). Чтобы отразить это в нашем классе Bulldog, мы переопределим метод play (), определенный родительским классом:

<?php

  class Bulldog extends Dog {
    public $breed = 'Bulldog';    

    public function __construct() {
      $color = 'brown & white';
      $size = 'mid-sized';
      
      parent::__construct($color, $size);      
    }
    
    public function play() {
      print "I'm a Bulldog, I don't like to play!";
    }
  }

?>

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

Теперь, когда мы знаем основы, давайте перейдем к некоторым более продвинутым концепциям PHP, которые используются в ядре и модулях Drupal 8.

 

Пространства имен


По мере увеличения размера нашего проекта и кода PHP возрастает риск повторного объявления имен функций или классов, которые уже существуют в нашем приложении. Давайте представим, что мы создаем новый класс с именем User . Что происходит , когда мы интегрируем в 3 - й библиотеку в наше приложение , что также объявляет класс с именем пользователя ? Эти два класса будут сталкиваться, так как в нашем приложении нельзя создавать несколько классов с одинаковыми именами. Один из способов обойти эту проблему - переименовать наш класс User во что-то другое, например MyUser , но этот подход будет работать только в очень небольших масштабах и не решает общую проблему. Решает проблему пространства имен .

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

 

Определение пространств имен


Пространства имен объявляются ключевым словом namespace . Это объявление должно быть первой командой в нашем коде, чтобы PHP знал, в каком пространстве имен мы находимся, прежде чем идти дальше:

<?php

  // This code will be under the MyModule namespace
  namespace MyModule;

  class MyClass {
    
  }

?>

 

Суб-пространств имен


Также возможно создать подпространства имен для их организации и создать древовидную иерархию пространств имен, разделяющую каждый уровень иерархии обратной косой чертой («\»). Вы увидите эту концепцию во время разработки для Drupal. Для примера, давайте посмотрим на пространство имен, используемое модулем Ban:

<?php

  namespace Drupal\ban;

?>

В Drupal каждый модуль (или компонент) имеет свое собственное пространство имен, аналогичное приведенному выше примеру. Эта древовидная иерархия предоставляет полезный способ для классификации наших классов на «деревья», и мы также увидим позже, как Drupal использует эту структуру, когда мы говорим о PSR .

 

Импорт пространств имен


Чтобы импортировать пространства имен в наш текущий код, мы можем использовать инструкцию use, за которой следует имя пространства имен. Мы еще раз посмотрим на модуль Ban:

<?php

namespace Drupal\ban;

use Drupal\Component\Utility\SafeMarkup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

Здесь модуль Ban сначала определяет свое собственное пространство имен, а затем использует команду use для использования различных классов, определенных в их соответствующих пространствах имен. Если рассматривать это под другим углом, пространства имен позволяют нам организовывать наши классы в виртуальные «папки», где каждое полностью определенное пространство имен указывает на определенный класс PHP.

Теперь, когда мы понимаем пространства имен, давайте немного углубимся в концепцию, которая использует их и позволяет нам (или, скорее, заставляет нас) организовать наш код в физическую структуру каталогов, называемую PSR-4 .

 

PSR-4


PSR означает  Рекомендацию по стандартам PHP . Это инициатива, которая делает автозагрузку классов PHP проще, учитывая, что они следуют определенной схеме. В предыдущем разделе этого поста мы рассмотрели пространства имен и выяснили, как мы можем использовать их в наших классах для определения виртуальной иерархии. Думайте о PSR-4 как об аналоге пространств имен: помещая наши классы в структуру папок, которая соответствует нашему шаблону иерархии пространства имен, Drupal автоматически попытается загрузить эти классы для нас.

Давайте посмотрим пример, чтобы понять, как он на самом деле работает. Мы рассмотрим одну из форм, которую предоставляет основной модуль Ban, расположенный в core / modules / ban / src / Form / BanAdmin.php :

ban

Теперь давайте посмотрим на первые несколько строк BanAdmin.php :

<?php

namespace Drupal\ban\Form;

...

/**
 * Displays banned IP addresses.
 */
class BanAdmin extends FormBase {

....

Здесь мы видим, что пространство имен класса ( Drupal \ ban \ Form ), имя класса ( BanAdmin ), имя файла ( BanAdmin.php ) и папка, в которой находится файл ( ban / src). / Form ) следуют шаблону. Сначала это может быть немного трудно понять и запомнить этот паттерн, но это гораздо проще, чем вы думаете. Давайте пройдемся по шагам, основываясь на примере выше.

В версии 8 классы, которые вы хотите, чтобы Drupal пытался выполнить и автозагрузку, должны быть помещены в папку с именем src в каталоге вашего модуля. В предыдущих версиях в 8 случаях из 10 большая часть тяжелой работы в модуле Drupal выполнялась одним  модулем .module.файл, что означало, что в зависимости от сложности модуля, ремонтопригодность кода может стать проблемой. Drupal 8 меняет этот подход и позволяет нам использовать шаблон, в котором часть функциональности нашего приложения (например, форма, блок, контроллер и т. Д.) Содержится в отдельном файле PHP в нашем модуле. Хотя такой подход означает, что нам может потребоваться создать большее количество файлов, преимущество организованной структуры файлов и папок делает его более чем оправданным.

Как и любой другой модуль, Ban также следует этому шаблону. Все его классы находятся в папке src и ее подпапках. Поскольку  BanAdmin.php предоставляет форму, файл помещается в папку с именем Form . 

Теперь давайте посмотрим на другую сторону истории. Давайте еще раз посмотрим на объявление пространства имен класса, содержащегося в  BanAdmin.php . Оно также содержит Form , совпадение? Конечно, нет. Вот как выглядит структура полного пространства имен в Drupal:

<?php

namespace Drupal\{module_name}\{annotation}\{class_name}

В следующей главе мы обсудим аннотацию более подробно. Пока все, что вам нужно знать, это то, что аннотация в пространстве имен и имя папки, в которой находится PHP-файл, должны совпадать. 
Итак, вот что вам нужно помнить при разработке модуля для Drupal 8:

первая часть пространства имен всегда Drupal (в конце концов, мы разрабатываем модуль Drupal) 
следующая часть - это всегда название вашего модуля 
затем идет аннотация, которая должна соответствовать названию подпапки 
наконец, имя класса должно совпадать с именем файла PHP (без расширения .php )
пространства имен чувствительны к регистру, поэтому убедитесь, что вы следуете точным шаблонам имен
В некоторых случаях вам не нужно организовывать свой класс PHP в подпапку. В этих случаях вы просто пропустите часть {annotation} , чтобы ваш шаблон пространства имен соответствовал структуре вашей папки.


В нашем случае это объявление:

<?php

namespace Drupal\ban\Form;

...

/**
 * Displays banned IP addresses.
 */
class BanAdmin extends FormBase {

....

приводит к следующему полностью определенному пространству имен:

Drupal\ban\Form\BanAdmin

Резюме


В первой части этой серии мы обсудили объектно-ориентированное программирование на PHP, рассмотрели некоторые основные концепции ООП, такие как классы, объекты и пространства имен, и познакомились с PSR-4 и шаблонами структуры папок, которые использует Drupal 8. Теперь, когда вы понимаете основы ООП в PHP, давайте применим эти знания на практике и рассмотрим компоненты Symfony, используемые в Drupal 8 .

Теги

Добавить комментарий

Restricted HTML

  • Допустимые HTML-теги: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Строки и абзацы переносятся автоматически.
  • Адреса веб-страниц и email-адреса преобразовываются в ссылки автоматически.
Нажимая кнопку "Отправить комментарий", Вы автоматически соглашаетесь с политикой конфиденциальности и даете свое согласие на обработку персональных данных. Ваши данные не будут переданы третьим лицам.
Top