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

от автора

Хочу поделиться с Хабром простым 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);

ссылка на оригинал статьи https://habrahabr.ru/post/328002/