08
Создаем свой PHP MVC framework. Часть 1.
Заметки, Мои шедевры, Программирование
Метки (теги) : framework, mvc, php
В данной серии статей я опишу реализацию PHP MVC framework’a. Подобных статей достаточно много, но как мне показалось, в некоторых не достаточно ясно и подробно изложена информация, и даже во многих является устаревшей. Что такое MVC и что представляет собой термин Framework я описывать не буду в связи с избытком информации по данным темам. Все пожелания прошу приводить в комментариях.
В данной статья я опишу:
1. Выбор стиля кодирования.
2. Структура.
3. FrontController.
4. Конфигурации фреймворка.
Первое с чего хочу начать, нужно придерживаться строгих правил и стилей написания кода. Я пользуюсь документацией по стандартам Zend Framework и Вам советую.
Далее разберемся со структурой приложения.
-application
--config
---main.php
--components
--controllers
---IndexController.php
--models
--views
---index
----index.php
---layouts
----default.php
--modules
---components
---controllers
---models
---views
-library
-- ... наша библиотека классов
-public
--index.php
В директории components
будут находиться вспомогательные классы (виджеты, хелперы, и т.д.), каждая группа компонентов может быть расположена в отдельной директории, а отдельные компоненты группы соответственно в поддиректориях. Автозагрузкза классов происходит с использованием namespase по соглашению
-components
--Helpers
---MyHelper
--- ...
--Widgets
...
Подобным образом будет сделано в моделях для более гибкой иерархии классов. Определимся с названием фреймворка. Я его назвал “My own framework” или сокращенно “mof”, таким образом все классы ядра фреймворка будут именоваться начиная с приставки “Mof_”, которая указывает на корневую папку библиотеки.
Идем дальше.
Напишем наш – index.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #public/index.php // Определяем путь к корню папки application defined('APPLICATION_PATH') || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application')); set_include_path(realpath(APPLICATION_PATH . '/../library')); require_once 'Mof/Application.php'; // Присваиваем значение пути к файлу конфигураций $config = APPLICATION_PATH . '/config/main.php'; Mof_Application::init($config)->run(); |
Файл конфигураций main.php содержит в себе массив значений (на подобии Yii фреймворка), которые можно применить в любом мести приложения. Базовые конфигурации будут использоваться ядром фреймворка.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #application/config/main.php return array( 'db' => array( 'dsn' => 'localhost', 'database' => 'test', 'username' => 'root', 'password' => '', 'prefix' => 'enxt_' ), 'modules' => array( 'admin' => array( 'enable' => true, ), ), 'user' => array( 'defaultAdminName' => 'administrator', 'defaultAdminPassword' => 'admin', 'role' => 'admin', ), 'defaultController' => 'Index', 'defaultLayout' => 'default', ); |
Создадим корневую папку нашей библиотеки Mof и в ней файл Application.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | # library/Mof/Application.php class Mof_Application { /** * @var MofApplication */ private static $_loader; /** * @var array */ protected static $_classMap = array( 'Component' => 'components', 'Model' => 'models', ); /** * @var Mof_Template */ public $template; /** * instanse * * @param string $config * @return MofApplication */ public static function init($config) { if (self::$_loader == NULL) { self::$_loader = new self($config); } return self::$_loader; } public function __construct($config) { spl_autoload_register(array($this, '_load')); if (is_string($config)) { $configArray = require($config); Mof_Config::set($configArray); } } /** * Run application * * @throws Exception */ public function run() { if (isset(Mof_Config::get()->modules)) { foreach (Mof_Config::get()->modules as $moduleName => $params) { self::$_classMap['modules'][ucfirst($moduleName)] = $moduleName; } } $this->template = new Mof_Template(); $router = new Mof_Router(); $router->route($this); $baseModelObject = new Mof_Model(); if ($baseModelObject->getError()) { throw new Exception($baseModelObject->getErrorMessage()); } } /** * Autoloader * * @param str $className * @return void */ private function _load($className) { $length = strpos($className, '_'); $classNameItems = explode('_', $className); $lowerFirstItem = strtolower($classNameItems[0]); if (isset(self::$_classMap[$classNameItems[0]])) { $classCoreDir = APPLICATION_PATH . DIRECTORY_SEPARATOR . self::$_classMap[$classNameItems[0]]; } elseif (isset(self::$_classMap['modules'][$classNameItems[0]])) { $classCoreDir = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . self::$_classMap['modules'][$classNameItems[0]] . DIRECTORY_SEPARATOR . Mof_Application::$_classMap[$classNameItems[1]]; $className = str_replace($classNameItems[1] . '_', '', $className); } else { $classCoreDir = dirname(__FILE__); } $classFile = $classCoreDir . strtr(substr($className, $length), '_', DIRECTORY_SEPARATOR) . '.php'; if (is_file($classFile)) { require_once $classFile; } } } |
Класс Mof_Application “запускает” наше приложение и обеспечивает загрузку всех нужных нам классов, детальное описание которого будет в следующей части, которую постарасю написать незамедлительно. А пока на этом все. Спасибо.
masdeft почему ты используешь такую же структуру папок как и zend framework, а не Yii например?
И еще вопрос. Будет ли в данном фреймворке отдельный конфиг для каждого модуля, как например в zend framework 2.0 ?
а что если сделать именование ядрового класса без всяких приставок и путей к папкам, и тупо например Application, WebForm, Route и т.д. , а уже модели и помощники будут иметь префикс который будет браться как название модуля, соответственно по strpos находим вхождение знака подчеркивания “_” и если оно есть значит это класс какого-то кастомного модуля, если нет значит ядра. При чем если мы будем подключать классы с помощью namespace, то в том же ядре будем писать так например Form/Webform
1. Конечно можно было сделать структуры как в yii, но больше по душе мне была именно данная, хотя по большей части они схожи. На самом деле все решения в кокой-то степени были выдернуты из обоих фреймворков.
2 . Пока нет, но в дальнейшем реализую.
3. Это можно сделать, но тут приставка играль роль пространства имен, чтобы была возможность подлючать другие библиотеки без конфликтов имен.
А к чему прятать директории с php файлами за корнем сайта, а все остальное в паблике. Ведь если захотят взломать, то и так взломают. А вот с таким подходом как в Yii и Zend framework частенько бывают проблемы с переносом сайта с хостинга на хостинг, Тем более, что не все хостинги поддерживают работу php скриптов вне корневой директории. Если можно опишите главные причины безопасности такой каталогизации, которая показана у вас.
Совсем не сложно перенести приложение, допустим, в закрытую для глаз директорию protected, как, кстати, в yii.