Как стать автором
Обновить

SimplePage: простой, декларативный фреймворк для быстрого прототипирования

Время на прочтение6 мин
Количество просмотров4.6K

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


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



Пример страницы просмотра статьи
<?php
$sp = [
  'layout' => [
    'title' => 'Статья',
  ],
  'input' => [
    INPUT_GET => [
      'id' => [
        FILTER_SANITIZE_NUMBER_INT,
        [
          'filter' => FILTER_VALIDATE_INT,
          'options' => ['min_range' => 1],
          'comment' => 'Идентификатор должен быть положительным, целым числом'
        ]
      ],
    ],
  ],
  'pdo' => [
    'queries' => [
      'article' => [
        'SELECT * FROM article WHERE id = :id',
        'params' => [
          'id' => &$_GET['id'],
        ],
      ],
    ],
  ],
];
include('../../sp.php');
$article = $article->fetch();
?>
<h1>
    <?= $article->title ?>
</h1>
<div>
    <?= $article->content ?>
</div>
<ul>
    <li>
        <a href="/articles/edit?id=<?= $article->id ?>">edit</a>
    </li>
    <li>
        <a href="/articles/delete.php?id=<?= $article->id ?>">delete</a>
    </li>
</ul>

Пример экшена удаления статьи
<?php
<?php
if($_SERVER['REQUEST_METHOD'] != 'GET'){
  http_response_code(404);
  exit;
}
$sp = [
  'input' => [
    INPUT_GET => [
      'id' => [
        FILTER_SANITIZE_NUMBER_INT,
        [
          'filter' => FILTER_VALIDATE_INT,
          'options' => ['min_range' => 1],
          'comment' => 'Идентификатор должен быть положительным, целым числом'
        ]
      ],
    ],
  ],
  'pdo' => [
    'queries' => [
      [
        'DELETE FROM article WHERE id = :id',
        'params' => [
          'id' => &$_GET['id'],
        ],
      ]
    ],
  ],
];
include('../sp.php');
header('Location: /articles', 302);

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


Краткое введение


Если вы знакомы с Jekyll, то вы уже познакомились с большей частью возможностей SimplePage, а именно:


  • Конфигурация системы на уровне конкретной страницы и всего сайта

<?php
$sp = [ // Вводные страницы заменяют дефолтные конфигурации сайта
  'layout' => [
    'title' => 'Редактор статьи',
  ],
];

include('../../sp.php');
?>
<form action="/articles/create.php" method="POST">
    <div>
      <input type="text" name="title" placeholder="Заголовок"/>
    </div>
    <div>
      <textarea name="content"></textarea>
    </div>
    <div>
        <input type="submit" value="Сохранить"/>
    </div>
</form>

  • Роутинг на уровне веб-сервера

▾ articles/
  ▸ _locale/
  ▸ create/
  ▾ edit/
      index.php // Страница редактирования статьи
  ▸ view/
    create.php // Экшен создания статьи
    delete.php
    edit.php
    index.php // Страница со списком статей
  index.php // Главная страница

  • Отсутствие зависимостей — фреймворк использует только возможности самого PHP. Выбор базы данных и веб-сервера остаются на совести разработчика

Преимущества и недостатки


Преимущества:


  • Очень низкий порог входа — если вы умеете HTML/CSS и немного PHP, вы можете использовать этот фреймворк
  • Очень прост в установке — достаточно скопировать несколько PHP-скриптов, и фреймворк готов к работе
  • Очень прост в использовании — каждая страница сайта это отдельный PHP-скрипт, содержащий как логику для обработки запроса, так и HTML-шаблон для генерации ответа

Недостатки:


  • Процедурный код и global — с увеличением проекта, поддержка может заметно усложниться
  • Низкоуровневая инфраструктура — используются возможности самого PHP, без дополнительных слоев абстракции и интерфейсов

Плагины и модули


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


Для подключения плагина, достаточно указать его в конфигурации:


return [
  'plugins' => [
    '_plugins/autoload.php',
    '_plugins/input.php',
    '_plugins/middleware.php',
    '_plugins/i18n.php',
    '_plugins/error.php',
    '_plugins/pdo.php',
    '_plugins/acl.php',
    '_plugins/layout.php',
    '_plugins/hook.php',
  ],
  ...
];

По умолчанию фреймворк поставляется со следующими плагинами:


  • Acl — разграничение прав доступа к страницам сайта
  • Autoload — загрузка классов с использованием прямых ссылок или путей PSR-4
  • Error — обработка ошибок и исключений
  • Hook — модель событий (хуков)
  • I18n — интернационализация
  • Input — фильтрация ввода
  • Layout — HTML-обертки
  • Middleware — модель последовательной обработки ввода-вывода
  • Pdo — декларативная работа с БД

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


return [
  'plugins' => [
    'vendor/autoload.php',
    '_plugins/markdown.php',
    ...
  ],
  ...
];

В отличии от плагинов, модули представлены набором PHP-скриптов и даже страниц, решающих прикладные задачи проекта, такие как "Панель администратора" или "Форум".


Пример простого блога


Рассмотрим пример создания блога с использованием данного фреймворка.


Для начала реализуем общую конфигурацию сайта, создав файл config.php в корне проекта:


<?php
return [
  'plugins' => [
    '_plugins/input.php',
    '_plugins/middleware.php',
    '_plugins/i18n.php',
    '_plugins/error.php',
    '_plugins/pdo.php',
    '_plugins/layout.php',
  ],
  'layout' => [
    'title' => 'SimplePage',
    'layout' => '_layout/default.html',
  ],
  'pdo' => [
    'dsn' => [
      'mysql',
      'dbname' => 'sp',
      'charset' => 'UTF8',
    ],
    'username' => 'root',
    'password' => 'root',
    'options' => [
      PDO::ATTR_PERSISTENT => true
    ],
  ],
];

Далее реализуем layout, создав шаблон _layout/default.html, который будет применяться ко всем страницам сайта:


<!DOCTYPE html>
<html>
    <head>
        <title><?= i18n($title) ?></title>
        <meta charset="utf-8" />
        <link href="/_css/style.css" rel="stylesheet">
    </head>
    <body>
        <?= $content ?>
    </body>
</html>

Пришло время создать главную страницу сайта, для этого запишем в файл index.php следующее:


<?php include('sp.php') ?>
<h1>Hello world</h1>
<p>
  Моя главная страница
<p>
<ul>
    <li>
        <a href="/articles">Статьи</a>
    </li>
</ul>

Далее создадим страницу со списком статей (предполагается, что таблицы для этого модуля уже созданы в БД), для этого запишем в файл articles/index.php следующий код:


<?php
$start = isset($_GET['start'])? (int) $_GET['start'] : 0;
$offset = isset($_GET['offset'])? (int) $_GET['offset'] : 2;

$sp = [
  'layout' => [
    'title' => 'Articles',
  ],
  'pdo' => [
    'queries' => [
      'articles' => [
        'SELECT * FROM article LIMIT :start, :offset',
        'params' => [
          'start' => $start,
          'offset' => $offset,
        ],
      ],
      'articlesCount' => [
        'SELECT COUNT(*) FROM article',
      ],
    ],
  ],
];
include('../sp.php');
$articlesCount = $articlesCount->fetchColumn();
?>
<h1>Статьи</h1>
<p>
  <ul>
      <?php foreach($articles as $article): ?>
          <li>
              <a href="/articles/view?id=<?= $article->id ?>">
                  <?= $article->title ?>
              </a>
          </li>
      <?php endforeach; ?>
  </ul>
  <ul>
      <li>
          Всего <?= i18n_plural($articlesCount, '%d article') ?>
      </li>
      <li>
        <a href="/articles/create"><?= i18n('create') ?></a>
      </li>
      <li>
        <a href="/articles?start=<?= $start - $offset ?>"><?= i18n('prev') ?></a>
      </li>
      <li>
        <a href="/articles?start=<?= $start + $offset?>"><?= i18n('next') ?></a>
      </li>
  </ul>
</p>

Как можно заметить, в шаблоне страницы используется интернационализация. Следовательно необходимо создать файл articles/_locale/ru_RU.php для ее корректной работы:


<?php
return [
  '' => [
    'plural_forms' => function($n){
      if($n % 10 == 1 && $n % 100 != 11){
        return 0;
      }
      elseif($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20)){
        return 1;
      }
      else{
        return 2;
      }
    },
  ],
  '%d article' => [
    '%d статья',
    '%d статьи',
    '%d статей',
  ],
  'Articles' => 'Статьи',
  'create' => 'создать',
  'prev' => 'назад',
  'next' => 'вперед',
];

На последок рассмотрим экшен создания новой статьи в блоге, для этого создадим файл articles/create.php:


<?php
if($_SERVER['REQUEST_METHOD'] != 'POST'){
  http_response_code(404);
  exit;
}

$sp = [
  'pdo' => [
    'queries' => [
      [
        'INSERT INTO article (title, content) VALUES (:title, :content)',
        'params' => [
          'title' => $_POST['title'],
          'content' => $_POST['content'],
        ],
      ],
    ],
  ],
];

include('../sp.php');
header('Location: /articles/view?id=' . pdo_build($sp['pdo'])->lastInsertId(), 302);
Теги:
Хабы:
-6
Комментарии86

Публикации

Истории

Работа

PHP программист
157 вакансий

Ближайшие события