Классы App и Registry фреймворка

Andre Kowalsy
83 раза
7 мин чтения
Опубликовано: 23-07-2025
Обновлено: 23-03-2026
Категории: Пишем свою CMS

Итак, продолжаем our journey и теперь на повестке дня - написание ядра нашего фреймворка. Это одна из самых сложных частей курса, но без неё не обойтись. В дальнейшем нам будет проще писать код.

В этом посте мы рассмотрим, как создать простой контейнер приложений на PHP, используя паттерн Singleton и регистр свойств. Это может быть основой для вашего блога или другого веб-приложения. Для организации кода мы будем использовать классы, трейты и конфигурационные файлы.

Быстрый обзор необходимых классов

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

class App {
    // Основные переменные и методы будут здесь
}

В классе App будут инициализироваться контейнер и маршрутизация (раутинг) нашего приложения. Ранее мы обсуждали, что такое маршрутизация. Это ключевая концепция паттерна MVC, которая управляет, куда отправить запрос от пользователя.

В классе App будет происходить запуск маршрутизации и создание контейнера приложения. Контейнер будет реализовывать паттерн Registry, который поясню далее.

class Registry {
    // Основные переменные и методы будут здесь
}
Паттерн Singleton

Нам необходимо реализовать паттерн Singleton. Этот паттерн обеспечит создание только одного экземпляра класса для всего приложения.

trait Singleton {
	
private static ?self $instance = NULL;

    private function __construct()
    {
        // Приватный конструктор
    }

    public static function getInstance(): static
    {
        return static::$instance ?? static::$instance = new static();
    }
	
}

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

class Registry {
    use Singleton;

    private static array $properties = [];

    // Методы для работы с свойствами...
}
Создание класса App

Теперь создаём наш класс App:

class App {
    public static Registry $container;

    public function __construct() {
        self::$container = Registry::getInstance();

        // Инициализация параметров
        $this->initializeParams();
    }

    private function initializeParams(): void {
        // Загрузка параметров из файла и заполнение контейнера
        $params = require 'config/params.php';
        foreach ($params as $key => $value) {
            self::$container->setProperty($key, $value);
        }
    }
}
Подключение параметров

Создайте файл конфигурации params.php и верните массив с параметрами:

return [
    'admin_email' => 'admin.new@blog.loc',
    'site_name' => 'My Blog',
    'pagination' => 3,
];

В результате, вы сможете получить доступ к параметрам через контейнер:

$adminEmail = App::$container->getProperty('admin_email');
$pagination = App::$container->getProperty('pagination');

Теперь давайте подробно рассмотрим код:

Основные технологии
  • PHP: Серверный язык программирования, на котором мы пишем наше приложение.
  • Namespaces: Для организации кода и предотвращения конфликтов имен.
  • Конфигурационные файлы: Для хранения параметров приложения.

Структура проекта
/vendor
    └── /blog
        ├── TSingleton.php
        ├── Registry.php
        └── App.php
/config
    └── params.php
/public
    └── index.php
  • vendor/blog/: Папка, содержащая классы для нашего приложения.
  • config/: Папка с конфигурационными параметрами.
  • public/: Папка с доступом к публичным файлам, включая точку входа в приложение.

Разбор кода
1. Трейт TSingleton
namespace blog;

trait TSingleton
{
    private static ?self $instance = NULL;

    private function __construct() {}

    public static function getInstance(): static
    {
        return static::$instance ?? static::$instance = new static();
    }
}

Объяснение:

  • Этот трейт реализует паттерн Singleton, обеспечивая наличие лишь одного экземпляра класса и предоставляя глобальную точку доступа к нему.
  • Конструктор класса закрыт, что предотвращает создание экземпляров класса извне.
  • Метод getInstance() проверяет существование экземпляра и создает новый, если его нет.
2. Класс Registry
namespace blog;

class Registry
{
    use TSingleton;

    protected static array $properties = [];

    public function setProperty($name, $value)
    {
        self::$properties[$name] = $value;
    }

    public function getProperty($name)
    {
        return self::$properties[$name] ?? NULL;
    }

    public function getProperties(): array
    {
        return self::$properties;
    }
}

Объяснение:

  • Класс Registry использует трейт TSingleton, чтобы гарантировать, что он будет единственным экземпляром.
  • Он предоставляет методы для установки и получения свойств, что делает его удобным для хранения глобальных настроек и параметров приложения.

3. Класс App
namespace blog;

class App
{
    public static $app;

    public function __construct()
    {
        session_start(); // Начало сессии
        if (isset($_COOKIE['remember_user']) && !isset($_SESSION['remember_user']['nickname'], $_SESSION['remember_user']['email'])) {
            $_SESSION['remember_user'] = unserialize($_COOKIE['remember_user']);
        }
        $query = trim(urldecode($_SERVER['QUERY_STRING']), '/');
        new ErrorHandler();
        self::$app = Registry::getInstance();
        $this->getParams();
        Router::dispatch($query);
    }

    protected function getParams()
    {
        $file = CONFIG . '/params.php';
        if (is_file($file)) {
            $params = require_once $file;
            if (!empty($params)) {
                foreach ($params as $k => $v) {
                    self::$app->setProperty($k, $v);
                }
            }
        } else {
            echo "Файл по такому пути $file не найден.";
        }
    }
}

Объяснение:

  • Класс App управляет жизненным циклом приложения, включая запуск сессий, обработку куки и загрузку параметров конфигурации.
  • Метод getParams() загружает параметры из файла конфигурации и сохраняет их в реестре.

4. Конфигурационный файл params.php
return [
    'site_author' => 'Nikolay Ogreba',
    'site_avatar' => 'team/admin.jpg',
    'site_title' => 'Nikolay Ogreba :: ',
    'site_name' => 'Nikolay Ogreba — создание сайтов и заработка на партнёрках',
    'pageNotFound' => 'Ой! Ошибка 404',
    'admin_author' => 'Nikolay Ogreba',
    'defaultPageLimit' => 5, // Default pagination limit
];

Объяснение:

  • Этот файл содержит массив с параметрами для приложения. Параметры могут быть расширены по мере развития вашего блога.
  • Здесь хранятся такие данные, как автор сайта, аватар, заголовок, название и информация об ошибке 404.
  • Конфигурационный файл упрощает управление параметрами, позволяя вносить изменения в одном месте.

5. Файл index.php
public/index.php

new \blog\App(); // Создаем экземпляр приложения

Объяснение:

  • Здесь мы подключаем автозагрузчик Composer, который автоматически загружает классы по мере необходимости.
  • Создание нового экземпляра класса App инициализирует приложение, запускает сессии и загружает параметры.

Использование контейнера

Теперь, когда нам настроен контейнер, мы можем легко обращаться к свойствам приложения в любом месте кода:

// Получение настроек
echo \blog\App::$app->getProperty('site_title'); // Выводит: Nikolay Ogreba ::
\blog\App::$app->setProperty('test', 'TEST'); // Устанавливает новое свойство
var_dump(\blog\App::$app->getProperties()); // Выводит все свойства, хранящиеся в реестре

Заключение

В этом посте мы разобрали, как создать простой контейнер приложений на PHP, используя паттерн Singleton и класс Registry для хранения глобальных параметров. Это позволяет организовать код и упростить управление настройками приложения.

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

Надеюсь, этот разбор кода был полезен и вдохновит вас на создание собственного приложения на PHP! Если у вас есть вопросы или хотите узнать больше о конкретных аспектах кода, не стесняйтесь задавать их в комментариях!

Завершение

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