10
Создаем свой PHP MVC framework. Часть 2.
Заметки, Программирование, Руководства
Метки (теги) : framework, mvc, php
В предыдущей статье я описал какая будет структура приложения, привел пример файла конфигураций main.php, который в дальнейшем может разрастаться до большого количества значений на Ваше усмотрение, а также реализацию входного скрипта index.php и файла Application.php. Давайте разберемся с классом Mof_Application.
Входной скрипт index.php создает экземпляр класса Mof_Application с помощью метода инстанции init(), атрибутом которого является значение пути к файлу config/main.php, и вызывает метод run(). Как мы знаем, при создании экземпляра класса срабатывает метод конструктор __construct() который в свою очередь вызывает метод и тем самым регистрирует наш метод _load() как автозагрузчик классов, т.е. вызывается при обращении к классу, который не видно интерпретатором. Атрибутом метода _load() является имя класса к которому мы обращаемся, и поэтому нашей задачей является подключить нужный нам класс любой ценой.
Как я уже говорил ранее, в нашем приложении все классы будут именоваться по соглашению , а это значит что имя класса будет подразумевать путь к нему. Еще раз посмотрим на метод _load().
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; } } |
Сперва мы проверяем первый элемент массива, состоящий и элементов имени класса разделенными символом ‘_’, не является ли он ключом массива $_classMap[]. Если же условие ложно, то проверяем массив $_classMap['modules'] который мы создали
в методе run(). Когда условие истино, переменной $classCoreDir мы присваиваем путь к директории в которой расположен наш класс(но возможно не в корне), и после чего остается совсем за малым, а именно в соответствии с именем класса подключить файл содержащий данный класс. Постарайтесь внимательно разобрать метод _load() и понять каким образом наше приложение будет подключать файлы.
Вернемся к методу run(). Первое что мы делаем – это проверяем наличие указанных модулей в наших конфигурациях, и если таковы есть, то добавляем в массив $_classMap ключ ‘modules’, значение которого будет массив имен модулей. Как Вы видите дальше мы создаем экземпляры следующих классов Mof_Template, Mof_Model и Mof_Router. Постарайтесь разобраться сами для чего это нужно из приведенного ниже кода этих классов.
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 | #library/Mof/Template.php class Mof_Template { /** * @var array */ private $_vars = array(); /** * @var string */ private $_layout; public function __set($index, $value) { $this->_vars[$index] = $value; } /** * Render a view with layout * * @param string $viewName * @param string $controllerName * @throws Exception * @return void */ public function render($viewName, $controllerName) { $layout = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'layouts' . DIRECTORY_SEPARATOR . $this->_layout . '.php'; if (!file_exists($layout)) { throw new Exception('Layout ' . $this->_layout . ' not found.'); } $view = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $controllerName . DIRECTORY_SEPARATOR . $viewName . '.php'; if (!file_exists($view)) { throw new Exception('View ' . $viewName . ' not found.'); } foreach ($this->_vars as $key => $value) { $$key = $value; } ob_start(); include $view; $content = ob_get_contents(); ob_end_clean(); include $layout; } /** * layout setter * * @param string $layoutName * @return void */ public function setLayout($layoutName) { $this->_layout = $layoutName; } } |
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 96 97 98 99 100 101 102 103 104 105 | #library/Mof/Router.php class Mof_Router { /** * @var string */ private $_path; /** * @var string */ private $_controller; /** * @var string */ private $_action; /** * @var string */ private $_module; /** * @var array */ private static $_params = array(); public function __construct() { $request = ''; if (isset($_GET['request'])) { $request = $_GET['request']; } $split = explode('/',trim($request,'/')); if ($split) { foreach ($split as $index => $part) { if (!$index && isset(Mof_Config::get()->modules->$part)) { $this->_module = $part; } elseif (!$this->_controller) { $this->_controller = ucfirst($part); } elseif (!$this->_action) { $this->_action = $part; } else { if (!self::$_params || end(self::$_params) !== null) { self::$_params[$part] = null; } else { self::$_params[end(array_keys(self::$_params))] = $part; } } } } if (!$this->_controller) { $this->_controller = Mof_Config::get()->defaultController; } if (!$this->_action) { $this->_action = 'index'; } } /** * Get array of params * * @return array */ public static function getParams() { return self::$_params; } /** * Router * * @param Mof_Application $registry */ public function route(Mof_Application $registry) { if (!$this->_module) { $file = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . $this->_controller . 'Controller.php'; } else { $file = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . $this->_module . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . $this->_controller . 'Controller.php'; } if (is_file($file)) { include $file; $class = $this->_controller . 'Controller'; } else { include APPLICATION_PATH . DIRECTORY_SEPARATOR . 'controllers' . DIRECTORY_SEPARATOR . 'ErrorController.php'; $class = 'ErrorController'; } $controller = new $class($registry); if (is_callable(array($controller, $this->_action . 'Action'))) { $action = $this->_action . 'Action'; } else { $action = 'indexAction'; } $controller->$action(); } } |
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 96 97 98 99 100 101 102 103 104 105 106 107 | #library/Mof/Model.php class Mof_Model extends Mof_IModel { const NO_ERROR = 0; const ERROR_CONNECT = 1; /** * @var PDO */ private $_db; /** * @var boolean */ private $_error; /** * @var string */ private $_errorMessage; protected $_vars = array(); /** * Set db connection * call __construct() */ public function __construct() { try { $this->_db = new PDO( 'mysql:host=' . Mof_Config::get()->db->dsn. ';dbname=' . Mof_Config::get()->db->database, Mof_Config::get()->db->username, Mof_Config::get()->db->password ); } catch (Exception $e) { $this->_error = self::ERROR_CONNECT; $this->_errorMessage = $e->getMessage(); } } public function __set($index, $value) { $this->_vars[$index] = $value; } public function __get($index) { return $this->_vars[$index]; } /** * Get error status * * @return boolean */ public function getError() { return $this->_error; } public function getErrorMessage() { return $this->_errorMessage; } /** * Set query * * @return string */ protected function setQuery() { $query = $this->query; $this->query = ''; return $this->db->query($query); } /** * Execute query * * @return string */ protected function execQuery() { return $this->db->exec($this->query); } /** * Prepare * * @return string */ protected function prepare() { return $this->db->prepare($this->query); } public function attributes(Array $data) { foreach ($data as $name => $value) { $this->$name = $value; } } } |
Приведенные классы опишу в следующих статьях, а пока все. Спасибо.
masdeft в предыдущей статье в классе Application у тебя описан метод init(), расскажи подробнее для чего он нужен, можно ли его обойти и склеить с __construct() ?
admin, статический метод init() служит для создания экземпляра “собственного” класса. Вообще можно без этого метода и в index.php создавать объект перед вызовом метода run().
Нет вопрос был в другом. Объясни пожалуйста непосвященным, зачем в данном случае создавать экземпляр класса? Что он нам дает???
Ведь на самом то деле эти статьи как я понял для новичков. Профессионалы либо уже имеют свои наработки по данной теме, либо пишут на таких проверенных framework – ах как Zend framework, Yii, Symfony и т.д. Так что, juniors, спрашивайте не стесняйтесь.
Не совсем новички, базовые принципы ооп нужно понимать. Как мне кажется многие программисты боятся смотреть в корень такого фреймворка как, например, zend framework или yii, боясь его мощи.
Чтобы не создавать обьект в файле index.php, т.к. он там не нужен, существует метод init() для создания обьекта внутри класса. Как известно при создании обьекта вызывается конструктор и появляется возможность обращаться к свойствам и методам внутри класса через объект $this.
That’s an apt answer to an interesting question
уважаемый а где 3 часть ?
если проект статей не завершен что толку в критике других статей если вы в своей не можете написать ‘Hello world!’
Правильное замечание. Поспешил я с написание серии статей. Потом не хватило огня закончить, не интересно стало.
Разбирайте исходники Yii, ZF, кропотливо, с дебагом, читая комменты. Будет полезней и эффективней.
Спасибо за то что читали статью, если это так.