Эпизод 1. Классы – часть 1. Ключевые слова и модификаторы

Привет всем читателям! Это первый эпизод сериала “Сущности ООП”. Здесь я расскажу о классах: что это и какие модификаторы они имеют – но поскольку о них можно поговорить побольше, то это всего лишь первая часть.

Классы

Как сообщает нам Википедия: класс – это шаблон для создания объекта; но мне более нравится: Класс – описание структуры объекта и методов работы с ним – оно более емкое. Если просто говорить – это набор свойств и методов, которые при использовании определенных модификаторов могут быть скрыты в объекте, а также вызваны без создания объекта; также есть вариант, что класс не может порождать объект – здесь мы все это и обсудим…

Класс, не умеющий рождать объект

Обычный класс делать это умеет(“Process finished with exit code 0” – говорит об отсутствии ошибок)

<?php
class Page
{}

$page = new Page();

// OUTPUT:
// Process finished with exit code 0

но если перед class словом добавить abstract – он потеряет данную возможность. Класс не умеющий рождать объекты? зачем? как с ними работать?

<?php
abstract class Page
{}

$page = new Page();

// OUTPUT:
// Fatal error: Uncaught Error: Cannot instantiate abstract class Page
// Process finished with exit code 255

Начнем по порядку – зачем? Полазив на разных сайтах(блогах, форумах…), посмотрев разные мнения и учев свои опыт и взгляд – пришел к выводу – обеспечение полиморфизма классов(принципы OCP, LSP – реализация в виде паттернов); и, мало не забыл сказать, что важно – абстрактные классы могут содержать абстрактные свойства и методы(читай далее). Теперь как с ними играть: наследовать, наследовать и только наследовать!

<?php
abstract class Page
{}

class MainPage extends Page
{}

$page = new MainPage();

// OUTPUT:
// Process finished with exit code 0

Модификаторы доступности свойств и методов

И методы, и свойства имеют одинаковый набор модификаторов открывающих им определенную область видимости – public, рrotected и private. Первый модификатор(public)

<?php
class Page
{
    public $id;

    public function getId()
    {
        return $this->id;
    }
}

class MainPage extends Page
{
    public function index()
    {
        $id = $this->id;
        $id = $this->getId();
    }
}

$page = new MainPage();
$page->index();
$id = $page->getId();
$id = $page->id;

// OUTPUT:
// Process finished with exit code 0

открывает полную область – как внутри объекта класса и классов-наследников, так и при обращении к объекту вне класса; второй – только внутри себя

<?php
class Page
{
    protected $id;

    protected function getId()
    {
        return $this->id;
    }
}

class MainPage extends Page
{
    public function index()
    {
        $id = $this->id;
        $id = $this->getId();
    }
}

$page = new Page();
$page->index();
$id = $page->getId();// error
$id = $page->id;// error

// OUTPUT:
// Fatal error: Uncaught Error: Call to protected method Page::getId() from context ''
// line 23
// Process finished with exit code 255

и классов-наследников; и третий – только внутри себя.

<?php
class Page
{
    private $id;

    private function getId()
    {
        return $this->id;
    }
}

class MainPage extends Page
{
    public function index()
    {
        $id = $this->id;// error
        $id = $this->getId();// error
    }
}

$page = new MainPage();
$page->index();
$id = $page->getId();// error
$id = $page->id;// error

// OUTPUT:
// Fatal error: Uncaught Error: Call to private method Page::getId() from context 'MainPage'
// line 17
// Process finished with exit code 255

Кстати, у функции(метода) класса слово отвечающее за область видимости может отсутствовать – тогда ее область видимости равна public.

Ключевые слова в свойствах и методах

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

<?php
class Page
{
    public static $id;
    
    public static function getId()
    {}
}

Page::$id;
Page::class;
Page::getId();

$page = new Page();
$page::$id;
$page::class;
$page::getId();

// OUTPUT:
// Process finished with exit code 0

Кста, внутри класса автоматически создаётся статическая публичная переменная class, содержащая имя класса с его неймспейсом.

И последний модификатор(для пхп) это final – говорит о том, что при наследовании класса данный метод не возможно переопределить.

OpenCart 3. Написание модификаторов

OpenCart – cms, написанная на PHP и реализующая MVC шаблон проектирования. Специализирован данный движок под создание интернет-магазинов. С коробки вы получаете голый интернет-магазин: набор опций для продажи товаров без всего того, в чем нуждается современный сайт – социальные ссылки, метрика и т.д. Но все это исправимо по средству большого количества модулей…

Немного о модулях

OpenCart – это расширяемый модулями движок для сайтов. Каждый модуль это набот файлов котроллеров, моделей, шаблонов и локализации. Также, в состав модуля входят модификаторы – install.php, install.sql и modificator-name.ocmod.xml(два первых упразднены в версии 3)ю Файловая структура модуля выглядит следующим образом

module-name.ocmod.zip
    upload
        файлы модуля
    modificator-name.ocmod.xml(необязательный)

Я не причастен к разработке этого движка(во всяком случаи – пока что), но думаю, что install.php и install.sql упразднили по причине того, что их функционал с успехом можно запихнуть в метод install() контроллера модуля – который есть”обязательной программой” для любого модуля.

Модификатор это?

Как я уже сказал – OpenCart расширяемая модулями система, однако модули(чаще всего) требуют внедрения кастомного кода в существующие файлы. С этой целью была разработана система модификации VQMod, которая эволюционировала с второйверсии в OCMod. В целом, смысл таков: при применении файла-модификатора происходит чтение указанного файла в строку, применение модификаций и сохранение этой строки, как нового файла(по пути system/modification).

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

Правила написания инструкций

Прежде всего – модификатор это XML файл, а все подобные файлы начинаются с представления

<?xml version="1.0" encoding="utf-8"?>

далее указываем системе что это именно модификатор – ставим парный тег modification

<modification>
    
</modification>

Первое, что здесь нужно указать это информация об авторе модификации и ее идентификация в системе

<name>Название модификатора</name>
<version>1.0</version>
<code>ID модификатора</code>
<author>Имя автора модификатора</author>
<link>ссылка на сайт автора</link>

После этой, “представительской” информации, следуют сами инструкции по тому, какой файл читать, что искать и что с этим делать.

В общем – указатель на файл – парный тег file

<file path="путь к файлу">
    
</file>

понятно, что атрибут path содержит путь к модифицируемому файлу. Следующий шаг – открытие парного тега operation. Можно было б решить, что это бессмысленный тег, однако – каждая замена в пределах одного файла – отдельная операция. А замен в одном файле может быть много

<operation error="skip|abort">
    
</operation>

не обязательный атрибут error указывает на действие которое нужно выполнить при не нахождении искомого: skip пропустить текущую операцию, abort – прервать модификацию вовсе. Тег search указывает на искомую строку/регулярное выражение

<search trim="true|flase" index="0|1|2...">
    <![CDATA[строка/регулярка]]>
</search>

атрибут trim(опционален) отвечает за игнорирование пробелов, index – также необязателен, указывает на номер вхождения(если их несколько).

И наконец, сама строка для внедрения

<add position="before|after|replace" trim="true|flase" offset="0|1|2..">
    <![CDATA[строка]]>
</add>

здесь trim тоже что и ранее, position это действие: вставить до, после, или вовсе заменить; offset – смещение по строкам от вхождения.

Полный файл модификатора выглядит так

<?xml version="1.0" encoding="utf-8"?>
<modification>
    <file path="путь к файлу 1">
        <operation error="skip|abort">
            <search trim="true|flase" index="0|1|2...">
                <![CDATA[строка/регулярка]]>
            </search>
            <add position="before|after|replace" trim="true|flase" offset="0|1|2..">
                <![CDATA[строка]]>
            </add>
        </operation>
        ...
    </file>
    ...
</modification>

И еще, малость не забыл – если есть модификатор, но нет модуля – структура zip-архива все равно должна быть как у модуля, но папка upload пустая.