Пользователь
0,0
рейтинг
9 октября 2007 в 02:15

Разработка → Создание простой MVC-системы на PHP 5 перевод

PHP*

Предисловие


В этом руководстве Вы узнаете, как построить простую систему по архитектуре MVC (Model-View-Controller, Модель-Отображение-Контроллер) на PHP 5.1 с использованием возможностей библиотеки SPL (Standard PHP Library, Стандартная Библиотека PHP).




Введение


Добро пожаловать в первое полноценное руководство для PHP 5 на PHPit. Вам понадобится PHP 5.1 с установленной библиотекой SPL, так как мы воспользуемся некоторыми из самых последних возможностей PHP 5.

В этом руководстве я собираюсь показать, как построить простую MVC-систему (архитектура MVC является наиболее распространённым шаблоном проектирования для больших web-приложений). Я проведу Вас через все шаги от начала и до конца создания полноценной MVC-системы.


Одна точка входа


Одной из важных вещей в MVC является одна точка входа в приложение вместо кучи PHP-файлов, делающих примерно следующее:

<?php
include ('global.php'); 

// Здесь код страницы

?>



У нас будет один файл, обрабатывающий все запросы. Это значит, что нам не придётся мучиться с подключением global.php каждый раз, когда нам нужно создать новую страницу. Эта «одна точка входа» будет называться index.php и на данный момент будет такой:

<?php


// Тут чего-нибудь делаем


?>



Как Вы можете заметить, этот скрипт пока ещё ничего не делает, но погодите минутку.

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

RewriteEngine on 

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]


Сперва мы проверяем, существует ли запрашиваемый файл, используя директиву RewriteCond, и, если нет, то перенаправляем запрос на index.php. Такая проверка на существование файла необходима, так как иначе index.php будет пытаться обрабатывать все запросы к сайту, включая запросы на изображения. А это нам как раз и не надо.

Если у Вас нет возможности использовать .htaccess или mod_rewrite, то Вам придётся вручную адресовать все запросы к index.php. Другими словами, все ссылки должны будут иметь вид «index.php?route=[здесь-идёт-запрос]». Например, «index.php?route=chat/index».

Теперь, когда все запросы идут через одну точку входа, мы можем начать написание скрипта index.php. Первая вещь, которую мы должны сделать, это инициализация системы. Создадим директорию includes, а в ней файл startup.php (он будет у нас файлом инициализации). Вставим следующий код в index.php:

<?php

error_reporting 
(E_ALL);

if (
version_compare(phpversion(), '5.1.0''<') == true) { die ('PHP5.1 Only'); }


// Константы:

define ('DIRSEP'DIRECTORY_SEPARATOR);


// Узнаём путь до файлов сайта

$site_path realpath(dirname(__FILE__) . DIRSEP '..' DIRSEP) . DIRSEP;

define ('site_path'$site_path);


В этом примере мы объявляем некоторую константу, узнаём, где лежат файлы системы, а также проверяем, что версия PHP, ну, хотя бы, 5.1.

Следующая вещь, которую необходимо сделать, это объект Registry (журнал, реестр) для хранения глобальных значений. Он будет передаваться в отдельные объекты системы и использоваться для доступа к глобальным значениям, причём без необходимости обозначать переменные как «global» или обращаться к массиву $GLOBALS. Почитайте статью «Использование глобальных значений в PHP» для более подробной информации об объекте реестра.

Добавьте следующий код в файл startup.php после того кода, что приведён в предыдущем примере:

$registry = new Registry;


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

Fatal error: Class 'Registry' not found in g:\Projects\PHPit\content\simple mvc php5\demo\includes\startup.php on line 12


Это, конечно, не большой сюрприз для нас, ведь мы ещё не написали сам класс Registry. Файл с классом можно было бы просто подключить, используя функцию include() (Прим. пер.: кстати говоря, include() не такая уж и функция, а всё-таки выражение языка, управляющая структура, если смотреть по ману), но давайте воспользуемся одной из новых возможностей PHP 5: __autoload().

Волшебная функция __autoload() используется для динамической загрузки классов. Когда PHP обнаруживает несуществующий класс, он сначала вызывает функцию __autoload() и только затем выдаёт ошибку. Мы можем воспользоваться такой возможностью для загрузки классов «на лету».

Вставьте этот код перед кодом из предыдущего примера:

// Загрузка классов «на лету»

function __autoload($class_name) {

        
$filename strtolower($class_name) . '.php';

        
$file site_path 'classes' DIRSEP $filename;


        if (
file_exists($file) == false) {

                return 
false;

        }


        include (
$file);

}



Наша функция __autoload() берёт имя класса, переданное ей как аргумент, и проверяет, существует ли файл с похожим именем в директории с классами. Если файла нет, то функция просто вернёт false и выскочит фатальная ошибка. Но если файл существует, он будет загружен. Т.е. объявится необходимый класс, и никакой ошибки не будет.

Мы ещё не создали сам класс Registry, поэтому ошибка всё ещё будет появляться. Давайте же займёмся этим.


Создание класса Registry


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

Для начала создадим директорию classes и в ней файл registry.php. Вставим следующий код в registry.php:

<?php


Class Registry {

        
private $vars = array();


}


?>



Теперь у нас есть «скелет» класса Registry и нужно нагрузить его методами. Напишем 2 метода: set(), чтобы устанавливать значения и get(), чтобы значения получать. Также можно написать метод remove() для удаления значений. Добавим эти методы в класс Registry:

function set($key$var) {

        if (isset(
$this->vars[$key]) == true) {

                
throw new Exception('Unable to set var `' $key '`. Already set.');

        }


        
$this->vars[$key] = $var;

        return 
true;

}


function 
get($key) {

        if (isset(
$this->vars[$key]) == false) {

                return 
null;

        }


        return 
$this->vars[$key];

}


function 
remove($var) {

        unset(
$this->vars[$key]);

}


?>



Эти методы простые, они устанавливают, получают и удаляют элементы из массива $vars, который является атрибутом класса. В методе set() мы заодно проверяем, не существует ли уже значение с указанным ключом, и, если существует, то мы генерируем исключение. Это нужно, чтобы избежать случайной перезаписи значений.

Теперь у нас есть полноценный класс Registry, но мы не будем останавливаться на этом. Воспользуемся одной из возможностей библиотеки SPL: ArrayAccess. SPL (сокращённо от Standard PHP Library, Стандартная Библиотека PHP) — это коллекция интерфейсов и классов, предназначенных для решения стандартных проблем. Один из интерфейсов SPL, ArrayAccess, может быть использован, чтобы предоставить доступ к объекту, как к обычному массиву. Посмотрим на такой пример:

<?php


$registry 
= new Registry;


// Устанавливаем некоторое значение

$registry->set ('name''Dennis Pallett');


// Получаем значение, используя get()

echo $registry->get ('name');


// Получаем значение, используя доступ как к массиву

echo $registry['name']


?>



Фокус в том, что $registry становится как бы массивом, хотя на самом деле это объект. Конечно, ArrayAccess не даёт никаких особых преимуществ, но он позволяет сократить объём кода, так как не придётся каждый раз писать «->get()». Чтобы воспользоваться этим интерфейсом, нужно исправить первую строчку класса («Class Registry») таким образом:

Class Registry Implements ArrayAccess {



Ключевое слово «Implements» говорит интерпретатору, что этим классом мы реализуем интерфейс, чем на самом деле ArrayAccess и является.

Класс, реализующий интерфейс ArrayAccess, должен иметь следующие методы:

function offsetExists($offset) {

        return isset(
$this->vars[$offset]);

}


function 
offsetGet($offset) {

        return 
$this->get($offset);

}


function 
offsetSet($offset$value) {

        
$this->set($offset$value);

}


function 
offsetUnset($offset) {

        unset(
$this->vars[$offset]);

}




Эти методы должны быть понятны сами по себе. Дополнительную информацию можно найти в документации SPL.

Теперь, реализовав интерфейс ArrayAccess, мы можем обращаться к объекту, как к обычному массиву. Это наглядно продемонстрировано, как в предыдущем примере, так и в этом:

<?php


$registry 
= new Registry;


// Устанавливаем некоторое значение

$registry['name'] = 'Dennis Pallett';


// Получаем значение, используя get()

echo $registry->get ('name');


// Получаем значение, используя доступ как к массиву

echo $registry['name']


?>



Класс Registry теперь завершён, и, если попробовать запустить систему, всё должно заработать (хотя ещё ничего не будет выводиться). Мы закончили с файлом инициализации и можно приступать к следующему шагу написания нашей MVC-системы: реализация доступа к базе данных, что в архитектуре MVC называется «Model» («Модель»).



Модель


«M» или модель – часть MVC-системы, которая отвечает за запросы к базе данных (или другому внешнему источнику) и предоставление информации контроллеру. Можно было бы загружать необходимую модель в зависимости от запроса, но я предпочитаю немного стереть границы между моделью и контроллером именно в этом месте, т.е. контроллер работает с БД непосредственно через библиотеку взаимодействия с БД, нежели чем через отдельную модель. Может быть, Вам захочется сделать это по-другому, тут дело вкуса.

Нам нужно написать код, необходимый для установки соединения с БД и поместить его в index.php. Существует множество замечательных библиотек для работы с БД (включая мою собственную, AutoCRUD), но в PHP 5 уже есть такая библиотека – PDO. Поэтому нет нужды использовать какую-либо другую.

Вставим следующий код в файл index.php (после подключения файла инициализации):

# Соединяемся с БД

$db = new PDO('mysql:host=localhost;dbname=demo''[user]''[password]');

$registry->set ('db'$db);



В этом примере мы сначала создаём новый экземпляр библиотеки PDO и соединяемся с нашей БД MySQL. Потом делаем переменную $db доступной глобально при помощи нашего класса Registry.

Модельная компонента нашей системы готова, поэтому давайте перейдём к написанию контроллера.

Написание контроллера подразумевает также и написание класса Router, ответственного за загрузку нужного контроллера в зависимости от запроса (вспомните, в index.php через URL передаётся переменная $route).



Класс Router


Класс Router будет разбирать запрос, а потом загружать требуемый контроллер. Создадим «скелет» класса:

<?php


Class Router {

        
private $registry;

        
private $path;

        
private $args = array();


        function 
__construct($registry) {

                
$this->registry $registry;

        }


}


?>



Затем добавим следующие строки в index.php:

# Загружаем router

$router = new Router($registry);

$registry->set ('router'$router);



Мы добавили класс Router в нашу MVC-систему, но он пока что ничего не делает, поэтому давай добавим в него методы, необходимые для работы.

Первая вещь, которую мы напишем, это метод setPath() для установки директории, где будут лежать все наши контроллеры. Метод выглядит следующим образом и должен быть добавлен в класс Router:

function setPath($path) {

        
$path trim($path'/\\');

        
$path .= DIRSEP;


        if (
is_dir($path) == false) {

                
throw new Exception ('Invalid controller path: `' $path '`');

        }


        
$this->path $path;

}




Потом добавим следующие строки в index.php:

$router->setPath (site_path 'controllers');



Теперь, когда мы установили путь до наших контроллеров, напишем сам метод, ответственный за загрузку контроллера. Этот метод будет называться delegate(), он и будет анализировать запрос. Первый кусочек этого метода такой:

function delegate() {

        
// Анализируем путь

        
$this->getController($file$controller$action$args);



Как Вы можете видеть, он использует ещё один метод, getController(), чтобы получить название контроллера и несколько других переменных. Этот метод выглядит так:

private function getController(&$file, &$controller, &$action, &$args) {

        
$route = (empty($_GET['route'])) ? '' $_GET['route'];


        if (empty(
$route)) { $route 'index'; }


        
// Получаем раздельные части

        
$route trim($route'/\\');

        
$parts explode('/'$route);


        
// Находим правильный контроллер

        
$cmd_path $this->path;

        foreach (
$parts as $part) {

                
$fullpath $cmd_path $part;


                
// Есть ли папка с таким путём?

                
if (is_dir($fullpath)) {

                        
$cmd_path .= $part DIRSEP;

                        
array_shift($parts);

                        continue;

                }


                
// Находим файл

                
if (is_file($fullpath '.php')) {

                        
$controller $part;

                        
array_shift($parts);

                        break;

                }

        }


        if (empty(
$controller)) { $controller 'index'; };


        
// Получаем действие

        
$action array_shift($parts);

        if (empty(
$action)) { $action 'index'; }


        
$file $cmd_path $controller '.php';

        
$args $parts;

}




Пробежимся по этому методу. Сначала он берёт значение переменной $route из запроса, потом разбивает его на части с помощь функции explode(). Например, запрос «members/view» преобразуется в такой массив: array(‘members’, ‘view’).

Потом при помощи цикла foreach он проходит по каждой части и проверяет, является ли эта часть директорией. Если является, то он приписывает её к пути до файла и проверяет следующую часть. Это позволяет поместить контроллеры в поддиректориях и, таким образом, получить иерархию контроллеров. Если же текущая часть запроса не является директорией, но является файлом, она сохраняется в переменную $controller, и мы выходим из цикла, так как нашёлся контроллер, который нам нужен.

После цикла мы проверяем переменную с именем контроллера. Если она пустая, то используем контроллер «index», который будет у нас контроллером по умолчанию. Потом метод определяет действие, которое необходимо выполнить. Контроллер – это класс, который состоит из нескольких методов. Действие же указывает на конкретный метод. Если действие не указано, будем использовать «index» — действие по умолчанию.

И, наконец, получаем полный путь до файла контроллера, объединяя три переменные: путь, имя контроллера и расширение «php».

Теперь, когда мы проанализировали запрос, пора вызывать метод delegate() для загрузки контроллера и выполнения действия. Полностью метод delegate() выглядит так:

function delegate() {

        
// Анализируем путь

        
$this->getController($file$controller$action$args);


        
// Файл доступен?

        
if (is_readable($file) == false) {

                die (
'404 Not Found');

        }


        
// Подключаем файл

        
include ($file);


        
// Создаём экземпляр контроллера

        
$class 'Controller_' $controller;

        
$controller = new $class($this->registry);


        
// Действие доступно?

        
if (is_callable(array($controller$action)) == false) {

                die (
'404 Not Found');

        }


        
// Выполняем действие

        
$controller->$action();

}




Проанализировав запрос при помощи метода getController(), мы проверяем, существует ли в действительности файл, и, если нет, то возвращаем простое сообщение об ошибке.

После этого мы подключаем файл с контроллером и создаём экземпляр его класса, называться который должен «Controller_[имя]». Чуть позже мы поговорим о контроллерах более подробно.

Потом мы проверяем, есть ли указанное действие (т.е. метод) и возможно ли к нему обратиться (используем для этого функцию is_callable()). Наконец, мы выполняем непосредственно само действие, на чём роль класса Router и завершается.

Написав полностью метод delegate(), добавим следующую строчку в файл index.php:

$router->delegate();



Если попробовать сейчас запустить систему, то мы увидим следующую ошибку (разумеется, если директории controllers ещё нет):

Fatal error: Uncaught exception 'Exception' with message 'Invalid controller path: `g:\Projects\PHPit\content\simple mvc php5\demo\controllers\`' in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php:18 Stack trace: #0 g:\Projects\PHPit\content\simple mvc php5\demo\index.php(13): Router->setPath('g:\Projects\PHP...') #1 {main} thrown in g:\Projects\PHPit\content\simple mvc php5\demo\classes\router.php on line 18


Или же мы увидим ошибку «404 Not Found», так как ещё нет ни одного контроллера. Но этим-то мы сейчас и займёмся.



Контроллер


Контроллеры в нашей MVC-системе будут достаточно простыми и потребуют совсем немного времени. Во-первых, удостоверимся, что директория controllers существует. Создадим файл controller_base.php в директории classes и вставим в него следующий код:

<?php


Abstract 
Class Controller_Base {

        
protected $registry;


        function 
__construct($registry) {

                
$this->registry $registry;

        }


        
abstract function index();

}


?>



Этот абстрактный класс будет родительским классом для всех наших контроллеров. Он будет делать всего лишь две вещи: сохранять локальную копию класса Registry и при помощи абстрактного метода index() заставлять все дочерние контроллеры реализовывать этот метод.

Напишем наш первый контроллер. Создадим файл index.php в директории controllers и вставим в него такой код:

<?php


Class Controller_Index Extends Controller_Base {


        function 
index() {

                echo 
'Hello from my MVC system';

        }


}


?>



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


(Полный размер, 1024x357, 115 КБ)


Это означает, что класс Router выполнил свою работу и запустил требуемое действие из требуемого контроллера. Давайте напишем ещё один контроллер, который будет соответствовать запросу «members/view». Создадим файл members.php в директории контроллеров и вставим в него такой код:

<?php


Class Controller_Members Extends Controller_Base {


        function 
index() {

                echo 
'Default index of the `members` controllers';

        }


        function 
view() {

                echo 
'You are viewing the members/view request';

        }


}


?>



Теперь зайдём в нашу MVC-систему по запросу «members/view» или же «index.php?route=members/view». Мы должны увидеть такой результат:



(Полный размер, 1024x320, 117 КБ)


Только лишь написанием нового контроллера и добавлением в него метода, мы смогли создать новую страницу, и ничего не пришлось менять в самой системе. Кроме того, нашим контроллерам не нужно подключать файл global.php или делать что-нибудь в таком роде.

Теперь, когда у нас есть контроллеры, осталась лишь одна вещь: «V» или «View» («Отображение»).



Отображение


Как и в случае с моделями, есть несколько различных вариантов создания компоненты View в MVC-системе. Мы могли бы научить класс Router автоматически загружать ещё один файл, названный как-нибудь так: «view_{имя}.php». Но чтобы сделать руководство более понятным, напишем класс Template, который будет заниматься выводом шаблонов.

Сначала создадим файл template.php в директории classes и вставим в него следующий код:

<?php


Class Template {

        
private $registry;

        
private $vars = array();


        function 
__construct($registry) {

                
$this->registry $registry;

        }


}


?>



Теперь у нас есть основная структура нашего класс Template. Следующим шагом добавим такой код в файл index.php прямо перед строками, связанными с классом Router:

# Создаём объект шаблонов

$template = new Template($registry);

$registry->set ('template'$template);



Так как нам понадобится использовать значения из моделей и контроллеров, то напишем метод set() для установки переменных, доступных в шаблонах. Посмотрим на пример:

function set($varname$value$overwrite=false) {

        if (isset(
$this->vars[$varname]) == true AND $overwrite == false) {

                
trigger_error ('Unable to set var `' $varname '`. Already set, and overwrite not allowed.'E_USER_NOTICE);

                return 
false;

        }


        
$this->vars[$varname] = $value;

        return 
true;

}


function 
remove($varname) {

        unset(
$this->vars[$varname]);

        return 
true;

}



Методы set() и remove() достаточно простые и используются, соответственно, для установки и удаления переменных.

Займёмся написанием метода show(), который будет отображать шаблоны. Простейший путь – это создать отдельную директорию templates, где хранить все файлы шаблонов, и использовать include() для вывода шаблона. Разумеется, Ваш собственный метод show() может быть совершенно другим и загружать шаблоны из базы данных или делать что-нибудь ещё. Посмотрим на кусо
Перевод: Dennis Pallett
Vyrus @Vyrus
карма
25,7
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (116)

  • 0
    довольно кстати наглядно.
  • 0
    отличная статья
  • 0
    Статья хороша наглядностью, но много чего конечно сыровато.

    Вот это просто ужос:
    $router = new Router($registry);
    $registry->set ('router', $router);

    Как минимум можно сделать так:
    $router = new Router();
    Registry::set ('router', $router);

    final class Registry
    {
    private static $_instance;
    private __construct();
    public static set($key, $value) {....}
    private _init()
    {
    if (!isset(self::$_instance)) {
    self::$_instance = new Registry();
    }
    }
    ......
    }
    • 0
      А еще упущены __set() __get() тоде весьма полезные методы
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        а зачем вам несколько регистрЕй!? больше одной вам не надо, а чтобы быть уверенным что больше одной у вас создать не получится юзают синглетон. имхо всё логично :)
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            при этом нужно помнить что работать с ним можно только так, и никак иначе.
            а синглетон защищает от того, что по запарке кто нибудь родит кучу new Registry() и потом будет ломать себе голову почему "ничего не работает!" ;)
            • 0
              new Registry родить не получится, т.к. в реализации AirWorker конструктор - приватный.
            • 0
              Вообще делается

              $ob = SignetoneClass::getInstance();

              public static function getInstance() {
              if(!is_object(self::$_instance)) {
              $c = __CLASS__;
              self::$_instance = new $c;

              return self::$_instance;
              } else {
              return self::$_instance;
              }

              }

              И никаких объектов.

              Как-то так :)
              • 0
                Это для PHP4. В PHP5 можно использовать полноценные классы, что правильнее.
                • 0
                  Кто-то из нас определенно тупит :)
                  в PHP4 нет ни public, ни static в данном контексте. А что вы подразумеваете под "неполноценными" и "полноценными" классами мне не понятно...

                  Есть такой шаблон - Singletone, это и есть его реализация.
                  • 0
                    Дело в том что есть несколько вариантов его реализации в PHP. В Википедии про это есть.

                    Да, кстати, Вы правы, ступил я немного. (В PHP5 действительно реализация отличается не существенно.) Извините. :-\
                    • 0
                      Варианты реализации отличаются по сути только синтаксисом, шаблоны на то и шаблоны, что условия в них жесткие :)

                      А извиняться вам не за что, все мы учимся :)
                      • 0
                        Странно, вот делал я проект. Там был класс, у которого всё что есть — куча статических публичных функций и методов, которые работают с внутренними переменными класса (тоже static).
                        Выходит, раз там не было getInstance — это был не синглетон?
                        • 0
                          Вы понимаете в чем вообще предназначение шаблонов, в чем тут суть и логика заключается? И почему собственно Singleton? :)
                          • 0
                            После рабочего дня голова не варит. Когда дома буду постараюсь ответить =)
              • 0
                ну во-первых как минимум так:

                public static function getInstance()
                {
                        if (!self::$_instance) {
                                self::$_instance = new self;
                        }
                        return self::$_instance;
                }

                А во-вторых приемы то разные.
                В случае с Registry::set ('router', $router); ВООБЩЕ не нужно инстанционировать объект этого класса, даже статичный (через сингелтон). Единственное (и определяющее) преимущество использования класса — глобальная зона видимости. т.е. обратиться к Registry::set ('router', $router); можно из любого места.

                Сингелтон нужен в случае, если класс реализуют несколько более сложную логику, нежели просто хранение объектов.
                • 0
                  Я не пишу "как нужно", если вы заметили, я просто правильно выразил мысли человека, а правильные они или нет - пусть другие исправляют :)

                  А Singleton нужен в случае, когда вам нужен exactly one instance of your class :) Причем экземпляр создается, сам по себе класс несет нагрузку объекта...А Registry в данном случае - нет.
                  • 0
                    это именно то, что я и имел ввиду. в данном конкретном случае Registry не требует функциональности объекта — соответственно инстанционировать его не нужно, даже как сингелтон.
        • 0
          Почему вы думаете что еще один объект registry не нужен?
          А если я еще один хочу сделать, скажем с какими либо отдельными настройками?
          • 0
            Из определения регистра для данного случая следует, что он должен быть ОДИН :)

            А вообще флаг вам в руки, убирайте сингтон и делайте как хотите.
    • +1
      Хочу немного пропиарить свой сайт реализацией паттерна Registry
      mabp.kiev.ua/content/2008/04/17/registry/
      • 0
        Блокирование clone с помощью magic-метода не нужно, клонировать объект такого типа все равно не получится.

        Думаю, что основной недостаток любых решений с использованием registry — это потеря типа. Для PHP это означает всего лишь потерю автокомплишна в ide, но это очень серьезный аргумент для того, чтобы отказаться от registry в пользу конструкций вида

        final class C
        {
        /**
        * Объект БД
        *
        * @var Zend_Db_Adapter_Pdo_Mysql
        */
        public static $db;


        }
        • 0
          Про clone, спасибо, я учту

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

          А создавать финальные классы со статическими свойствами это мне кажеться больше подходит для констант

          final class C
          {

          public static $BYTE = 1;



          }

          • 0
            >> Делегирование тоже не всегда оправдано, например если большой класс использует туже БД только в одном своём методе, то делегировать БД в класс немного излишне

            я имел в виду что класс C является контейнером для общих объектов приложения, юзается в любом месте:

            C::$db->select()->[и тут вываливается автокомплишн]
            • 0
              Есть ли у этого метода ещё какие-то плюсы перед Регистром кроме автокомплишена?

              У меня например возникают вопросы, вроде: как удалить синглтон объект если он больше не нужен чтобы он не занимал память
              • 0
                Скорость

                unset(C::$db);
                синглтон: unset(self::$instance)
                • 0
                  unset(self::$instance)
                  так не выйдет

                  допустим у нас синглтон БД
                  мы удаляем его не из объекта а снаружи
                  • 0
                    private static мы не можем удалить снаружи по определению, и с этим поделать ничего нельзя. Максимум, что мы можем — добавить метод unsetInstance() { unset(self::$instance) } (я это имел в виду)

                    кстати, реализация класса БД в виде синглтона — не лучшая идея.
          • 0
            >> А создавать финальные классы со статическими свойствами это мне кажеться больше подходит для констант

            Вот это совсем непонятно…

            И почему не «const BYTE = 1;»?
            • 0
              final class C{
              public static $BYTE = 1;
              public static $KILOBYTE = 1024;
              public static $MEGABYTE = 1048576;
              }

              я понимаю такой подход в java но видел и в пхп
              это класс констант с которым потом сравнивают переменные
              чтото вроде if($myFileSize < C::$MEGABYTE)
              я не думаю что стоит спорить должен это быть static или const
              • 0
                >> я не думаю что стоит спорить должен это быть static или const

                В больших проектах это важно. Про const мы знаем, что никакой «орел» не поставит заглушку, меняющую «псевдоконстанту», из-за которой возникнет ошибка в логике (например, ошибочный посчет суммы крупного заказа). Это вопрос скорее не технический, управленческий.
  • 0
    Отлично Саш! Спасибо что вспомнил - как раз будет над чем посидеть завтра! Пробежался глазами - радует наличие примеров, без них никак! Еще раз спасибо за перевод!
  • +1

    Автор, найдите, пожалуйста, в статье

    $registry->['name']

    и удалите в ней стрелочку.

    Спасибо

    • 0
      Поправил. Спасибо за замечание
  • +1
    Удивляют условия, где булево значение избыточно сравнивается с булевой константой. Можно проще:

    if (file_exists($file) == false) → if (!file_exists($file))

    А в целом пост добротный.
    • 0
      причем, даже сравнение if (file_exists($file) == false) правильнее было бы использовать с тремя знаками равно === :-)
      • 0
        Это излишне, file_exists возвращает заведомо булево значение.
        • 0
          Простуда - враг программиста :-) Мозг плохо соображает, пойду лечиться ;-)
    • 0
      file_exists($file) == false
      тож не верно
      для ТРУ и ФОЛС нинзя используют эквиваленты
  • 0
    Спасибо! Если честно, то MVC-модель трогал исключительно одним пальцем, не доходили руки никак, чтобы разобраться. Вы мне помогли своим переводом. Спасибо! =)
  • 0
    Очень интересно, спасибо.

    создание переменных для шаблонного класса
    // Загружаем переменные
    foreach ($this->vars as $key => $value) {
    $$key = $value;
    }

    можно заменить на extract($this->vars);
  • 0
    Вау, как раз неделю назад у меня была такая необходимость. Жаль, что теперь поздно, но, думаю, еще понадобится.
  • 0
    Это
    $site_path = realpath(dirname(__FILE__) . DIRSEP . '..' . DIRSEP) . DIRSEP;
    на вот это
    $site_path = dirname(dirname(__FILE__)) . DIRSEP;
  • 0
    Спасибо! Теперь примерно понял что такое MVC, а то без примеров сложно было понять как это все работает.
  • 0
    Супер, спасибо за топик!
    Единственное что не понял - где подключается файл controller_base.php?
    • 0
      Ой, понял, я не туда сохранил его, надо в classes, а я поместил в controllers. :) А потом оказывается функцией __autoload() он автоматически подключается при вызове контроллера.
  • 0
    несмотря на критику в каментах (в заключении кстати написано что методы реализации возможно не самые лучшие),
    очень хорошая статья, я в качестве view прикрутил шаблонизатор smarty,
    если кому интересно - могу кинуть кусок кода ))
  • 0
    вопрос еще кстати про модель MVC, если мне надо использовать БД, общение с ней я так понял должно происходить с контроллере, те все запросы будут тупо храниться в классе-контролере, так? или есть более интеллектуальные методы?
    • 0
      Нет, не совсем так. Можно, конечно, и хранить запросы, и работать с БД в контроллере. Но всё же этими вещами должна заниматься модель. А уже с ней будет работать контроллер.
      • 0
        а пример можно?

        я сейчас создал свой DB-класс, зарегистрировал его и через него общаюсь с PDO. PDO кстати раньше не юзал, да он мне и не очень удобным показался...
        • 0
          значит вы еще не пользовались PEAR::DB, который переопределить у меня НУ НИКАК не получилось, а хотелось добавить кое-какой функционал и логику :)
          • 0
            я тут 1,5 месяца потренировался в mvc - создал небольшую системку.

            к системе, которая приведена примером в этой статье прикрутил более, как мне кажется грамотную "модель" - по аналогии с контроллерами (также грузятся). через эти классы-модели уже непосредственно общаюсь с БД, сейчас есть идея ввести параметр - драйвер чтобы уже не быть привязанным к какой-то определенной бд
            • 0
              Драйвер нужен в любом случае для работы с БД, он никак не вписывается ни в вид, ни в контроллер, только отдаленно относится к модели представления данных.
              • 0
                я к модели и хочу его прикрутить естественно, в настройках прописать драйвер и чтобы автоматически грузилась модель в соотвествии с выбранным драйвером - для контролера это должно быть прозрачным.

                в статье просто про модель очень мало написано - я решил поделиться как можно развить идею модели

                ps а какие сложности возникли с pear::db?
                • 0
                  Мне понадобилось изменить поведение функций quoteIdentifier и quoteSmart, решил делать стандартно через полиморфизм, у меня вообще PEAR::DB подружался через extended класс, который реализован как Singleton, чтобы не держать кучу соединений с базой. Поэтому я и решил переопределять все через DDB просто переопределив функции, но не тут то было...Создаваемый в приложении instance класса DDB на самом деле являлся экземпляром DB и никакими хаками мне это обойти не удалось, предлагали использовать Decorator pattern, по-моему, но результато это не дало никакого.

                  Решил, что больше PEAR штуки в проектах постараюсь не использовать :)
                  • 0
                    у меня довольно тупая модель - простые селекты, апдейты, инсерты ))

                    я в такие дебри не вдавался и в итоге даже от PDO отказался
                    • 0
                      Там все до ужаса просто, никаких особых features я не заметил. Нормальные вещи (те же quote*) реализованы достаточно тупо, когда в метод попадает integer оно зачем-то квотит его, ну и подобные мелочи. Смысл обеих этих функций в одном и том же, только quoteIdentifier еще в `` квотит, но в мануале ничего нормально не неписано, есть еще escapeSimple, короче, полный бред...

                      А еще я хотел иметь аналог mysql_last_id(), т.к. в PEAR::DB этого ПОЧЕМУ-ТО нет :) Зато там есть некие sequences, которые вместо возврата LAST_INSERT_ID позволяют создавать последовательности вставок, где будет храниться LAST_INSERT_ID, короче там вместо 1 функции приходится использовать 3 с мудреной логикой, причем для каждой sequence создается еще и своя таблица в бд...с ума сойти :)
                      • 0
                        советую посмотреть в сторону DbSimple — более удобной библиотеки абстракции БД я не встречал. Это конечно если вам не нужны сверхвозможности (работа с биндами, тригерами и т.д.). а если все таки нужны — то ADODB может вам подойдет больше чем peardb, с ней нет проблем в переназначении методов.
                        • 0
                          Сейчас я ищу нечто большее, чем простую абстракцию к интерфейсу БД, нужен целый фреймворк, который устраивал бы меня по 2/3 своих возможностей, а такого пока я не видел.

                          К тому же планирую чуть позже совсем уйти из php.
                          • 0
                            вы уже смотрели в сторону Zend Framwork? если нет советую хотябы ознакомиться. я использую его в своих больших, но при этом невысоконагруженных проектах. он удовлетворяет как раз эти 2/3 потребностей. в связке со смарти, дбсимпл и JsHttpReques — 3/4 :)
                            • 0
                              Зенд ОЧЕНЬ туп, там фактический тот же PEAR подход, многие вещи просто переписаны под логику этого фреймворка, куча ненужных вещей. Шаблонные движки по моему мнению вообще не нужны, особенно такие, как пресловутый смарти, язык в языке, по-другому не скажешь. Фреймворк должен оптимизировать рутину, типа обработки форм, их вывода, ошибок и т.п., с чем каждый раз приходится сталкиваться. Для асинхронных запросов я использую в данный момент prototype, но думаю перейти на mootools, т.к. функционал в разы выше и круче.
                              • 0
                                у нас различные понятия определения «фреймворк». обработка форм, их вывод, валидация и т.д. очень сильно зависят от конкретного проекта. сделать что-нибудь более менее универсальное и чтобы при этом с ним можно было нормально работать практически невозможно.

                                фреймворк дает базовые инструменты, кирпичики так сказать, с помощью которых легко сложить нужный вам дом. Так например в прототайпе нет реализации валидации форм, нет реализации построения таблиц с сортировкой и другой более высокоуровневой логики. этим фреймвор заниматься не должен. а вот например script.aculo.us построенный на базе прототайпа (ну или того же mootools) прекрасно реализует возложенную на него более высокоуровневую задачу.

                                что касается конкретно Zend Framework, то я не сказал бы что это что-то идеальное и панацея от всех бед. да многое в нем меня не устраивает тоже, но тем он и хорош, что позволяет с легкостью переписать/дописать не устраивающий тебя метод/класс/модуль. его долго можно сравнивать с cakePHP или codeInteger и прийти лишь к тому что у каждого есть свои + и -.

                                а вообще нужно на python переходить :))
                                • 0
                                  Это уже от каждого отдельного товарища зависит.
                                  Мне достаточно prototype, чтобы сделать

                                  function validateLogin(login) {
                                  new Ajax.Updater(...)
                                  }

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

                                  И никто не говорит о том, чтобы фреймворк позволял делать

                                  $obj = new Site();

                                  Зенд - бред, мне нафиг не нужен их Ajax реализация, нафиг не нужна их авторизация банальная, если я могу то же самое сделать в .htaccess, нафиг не нужен переписанный PEAR::DB, есть вещи, которые можно сделать ВНЕ PHP, а это значит, что в нем они совершенно не нужны, к тому же в таком количестве, к тому же такие беспомощные. Остальное я смотрел и мне не понравилось.

                                  И питон мне не нравится :)
                                  • 0
                                    по порядку:
                                    1. валидировать данные, отправляя их на сервер? хм. я так никогда не делаю.
                                    2. поддерживаю технологию двойной валидации — на фронтенде для удобства, на бекенде для безопасности — половинчатые варианты очень не люблю.
                                    3. мне в зенде нравиться реализация контроллеров-роутеров-диспетчеров. легкость и простота ее имплементирования и широкая возможность доработки под собственные нужды не ломая при этом идеологию и не изобретая велосипедов. Zend_Db, как впрочем и зендовскую авторизацию в чистом виде я не использую. тоже самое с Zend_View — имплементировал удовство работы, а движок для парсинга шаблонов использую смартевый. Как то передавать данные в шаблоны нужно, а смарти это делает наиболее прозрачно и понятно для верстальщиков.

                                    3. я python настолько плохо знаю, что не могу сказать нравиться он мне или нет)). пока мнение исключительно субъективное.
            • 0
              а можешь показать как реализовал продгрузку разных моделей?
              • 0
                в startup.php5:
                // если название класса Model_A[_B]
                if(preg_match("/Model_([^_]*)(?:_([^_]*))?/", $class_name, $matches) ) {
                array_shift($matches);
                // имя будет /models/a[/b].php
                $file = site_path . 'models' . DIRSEP . strtolower(join('/',$matches)).ext;
                // иначе грузим из директории classes
                } else {
                $filename = strtolower($class_name).ext;
                $file = site_path . 'classes' . DIRSEP . $filename;
                }

                include ($file);
                • 0
                  Возникло желание посмотреть всю систему, если можно, вышлите на jonsne@ngs.ru
                • 0
                  Сквозь года присоединяюсь к вопросу насчет всей системы. romansivakov@gmail.com
  • 0
    понравилась статья, несмотря на то, что немного смазанно понятие модели. Модель в моем понимании (воспитанном во многом рельсами) это более высокий уровень абстракции чем прослойка к БД. Модель должна представлять собой уже некоторую сущность, к примеру клиента. Получается что модель предоставляет интерфейс для работы с клиентами в контроллере.

    не совсем понятна также целесообразность использования ArrayObject
    код в итоге будет довольно неприятный
    $this->registry['db']->query($sql)
    както щетинисто выглядит

    последнее время вообще тянет в сторону препроцессоров кода. Тянет создать чтото вроде надстройки на пхп. К примеру, чтобы в контроллере конструкции вида
    @clients = &cleints->list();

    преобразовывались в
    $this->env->template->set($clients, $this->env->models->cleints->list());

    Реалезуется элементарно, но до реализации хочется продемать что еще туда впихнуть.
    • 0
      остается только посочувствовать людям, которым возможно придется рефакторить (да и просто читать) ваш код. в любом случае удачи)
  • 0
    А ещё лучше почитать документацию и покопаться в коде какого-то хорошего готового фреймворка.
    Я бы советовал CakePHP. Разработчики умудряются выжимать всё, что только можно из PHP.
    • 0
      ага, из PHP4, который уже dead
      • 0
        в новой версии (которая beta) PHP4 уже не будет поддерживаться. её и нужно смотреть.
        а старой уже больше года
  • 0
    SPL тут ни к чему, все нормально решается стандартными методами и поставкой :)
    Если вы используете PHP5 и пишете, что все ТОЛЬКО для PHP5, будьте добры использовать exceptions, а не die :) И область видимости методов отмечайте, статичность и т.п. :)

    $site_path можно было define('ROOT_PATH', '/var/www/localhost/htdocs'), вместо переменной, DIRSEP как-то не смотрится по сравнению с DIRECTORY_SEPARATOR, последнее хоть и длиннее, но дает четкое понимание "куда я попал" :)

    Ах да, еще include...давайте уже include_once или require_once, что будет правильнее, вы же проверяете на существование файла :)

    В общем все, в принципе понравилось :)
    • 0
      dirname(__FILE__); позволяет не думать о том, что однажды провайдер (к примеру) сменит домашнюю директорию вашего проекта.
      так что define('ROOT_PATH', dirname(__FILE__));

      и еще вместо использования константы DIRECTORY_SEPARATOR достаточно ВСЕГДА использовать прямой слеш /. Он в юниксе родной разделитель, а винде пофигу))
      • 0
        Мало ли какая ОС, да и то, что винда поддерживает - не есть стандарт.

        Полностью динамическое задание path у вас не получится, у PHP с этим огромные косяки, которые никто не думает править, в любом случае вам придется либо один раз жестко дефайнить, либо постоянно держать директорию проекта в include_path.
        • 0
          «придется либо один раз жестко дефайнить» — именно так. вопрос лишь в том, как это делать. define('ROOT_PATH', dirname(__FILE__)); в index.php к примеру вполне для этого подойдет.

          ну а держать в include_path предпочитаю директорию с контроллерами, моделями и библиотеками (т.е. все три, не подумайте что уменя все это в одной папке свалено:) ).
          • 0
            Ну а причем здесь index.php?
            Ты каждый раз будешь его инклудить?
            Есть какой-либо config.php, в котором хранятся настройки, но require_once 'config.php', в котором будет написано ваше dirname(__FILE__); повлечет за собой то, что рут патом будет считаться текущий каталог...а это конец :)

            Поэтому я знаю что пишу, т.к. неоднократно сталкивался с такой фигней :)
            • 0
              ну во-первых константа __FILE__ не изменит своего значения в зависимости от того, где был проинклужен файл config.php.
              большенство моделей построения MVC подразумевают единую точку входа (тот же самый index.php). Так работает зенд фреймворк напрмер — все обращения к серверу реврайтятся на index.php. ну а уже в нем достаточно один раз require_once 'config.php';

              у меня подобный алгоритм использован во всех проектах — все прекрасно работает. при этом я могу спокойно сменить место дислокации проекта с (напрмер) /var/www/ на /home/www/ и при этом ничего править не прийдется.
      • 0
        "... достаточно ВСЕГДА использовать прямой слеш /. ... а винде пофигу"
        вы это проверяли или просто где-то прочитали/услышали? А то прям с капслоком написано.
        А на заборах пишут "хуй".

        <?php
        mkdir ("dir1/dir2/dir3", 0755, true);
        ?>

        <?php
        mkdir ("dir1\dir2\dir3", 0755, true);
        ?>
        • 0
          \\
          • 0
            Ну да:
            <?php
            mkdir ("dir1/dir2/dir3", 0755, true);
            ?>
            <?php
            mkdir ("dir1\\dir2\\dir3", 0755, true);
            ?>
        • 0
          Платформа: winXP, Apache/2.2.4 (Win32), PHP/5.2.4
          результаты тестирования можно увидеть на скриншоте →

          Также пришлось переводить не один проект с win2003/iis/php4(php5) на linux. В процессе перехода сполна ощутил разницу в системах.
          Возможно условия тестирования не вполне объективны? Мне уже самому интересно — неужели на win-платформах прямой слеш (/) не проходит когда-то.
          • 0
            попробуй в консоле на win-платформе начиная с ядра 5.1 написать вот такую команду cd / — и увидишь результат
  • 0
    Очередное изобретение велосипеда как всенда закончилось кучей комментов что и где надо дописать/исправить/заменить (ради которых такие статьи интересно читать).

    советую почитать документацию по Zend Framework (если конечно вы ее уже не читали) — уж очень ваш подход мне напоминает этот фреймворк.
    • 0
      Для новичков это ОЧЕНЬ полезно, а то мастеров, пишущих "говнокод" сейчас достаточно, сам иногда велосипеды писал, пока к паттернам не приучили, это поможет и в планировании, конструировании будущего приложения.
      • 0
        согласен. статья очень полезна для изучения патернов и стандартов. но использовать приведенный в ней код я бы не советовал. Если уж заинтересовал новый подход — «погугли в яндексе» не описан ли он в википедии :)
  • 0
    Всё давольно просто и понятно. Спасибо!

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

    Как их вписать в архитектуру МВЦ?
    В каком месте и где их вызывать?
    Какова иерархия папок (структура приложения)?
    • 0
      Интересный вопрос. На сколько я знаю однозначного ответа на него нет.

      Есть несколько подходов к решению.
      Ну во-первых, если четко придерживаться MVC то все данные для формирования хедеров, меню и подобных повторяющихся блоков нужно формировать в контролерах и передавать в шаблоны в виде готовых к «употреблению» массивов.
      с другой стороны, на мой взгляд, у этого подхода есть один минус — в каждом контролере придется повторять одну и туже рутинную операцию. Конечно ее можно свести к минимуму, но все же.

      есть еще способ — использование так называемых плагинов (виджетов, хелперов) в шаблонах. Тогда мы немного отступаем от стандарта MVC и схема работы сайта получается такой:
      запрос → контроллер → модель → контроллер → шаблон → плагин → модель → плагин → шаблон.

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

      это решение, которое использую я. Есть конечно еще варианты, например еще больше изменить архитектуру MVC, чтобы процесс выглядел примерно так:
      запрос → контроллер → шаблон → плагин → модель → плагин → шаблон.
                                                              → плагин → модель → плагин →
                                                              → плагин → модель → плагин →
      т.е. единственная роль контролера — вызов шаблона. а уже в шаблоне вызываются плагины, которые рендерят контент в вставляют его в сетку шаблона. тогда шаблон собирается как бы из кубиков, а каждый кубик автономен.

      Я не открыл что-то новое, есть довольно большое количество литературы, описывающей тот или иной подход. Стоит рассмотреть несколько вариантов, а потом сделать вывод какой из них наиболее подходит для решения ваших задач. Ну или имплементировать лучшие стороны каждого и (на свой страх и риск) написать свою схему построения приложения. Выбор за вами.

      ps: все сказанное является отражением моей точки зрения на проблему, и не есть руководство к действию. Всегда открыт для конструктивных замечаний :)
    • 0
      да и вобще. стоит взять документацию по какому-нибудь фреймворку (zend подойдет например) — возможно там вы найдете ответы на все интересующие вас вопросы (даже если потом не будете этот фреймворк использовать).
      • 0
        полностью поддерживаю что ZF весьма показан к хотя бы ознакомлению хотя бы за его документацию + внутрение ситили кодирования + способ реализации, которые могут послужить примером оформия framework'a. а то что он весьма тяжел никому не мещает написать некое свое подобие (кто тут еще свой framework/CMS не писал?)
  • 0
    дочитал до половины. Далее уже не в мочь. Статья отстойная.
    1. Класс Registry не что иное как массив $_GLOBALS - т.е. заводя этот класс мало того что воспроизводится уже существующая структура, так еще и навешивается избыточное количество функций на работу с ней. К тому же подобную структуру в системе принято создавать синглтоном.
    2. Именно модель должна работать с БД (то есть в ней находится представление о структуре таблиц, а не просто формирование запросов). Если различие стирается - то это уже не MVC.
    3. getController(&$file, &$controller, &$action, &$args) - зачем создавался Registry если значения гоняются параметрами? При нормальном MVC - параметры у функций вообще не используются.
    n. Ничего не рассказано про структуру-архитектуру приложения.
    "Это был не MVC!"
    • 0
      А я вот не понял зачем вообще несколько контроллеров может понадобиться (
  • 0
    А вы не моглибы выкладывать если не трудно свои реализации и рассказывать чем они лучше?
    • 0
      ета статья лиш предложение идей для основ будущего приложения:)
      довольно хороша (в качестве каркаса...) ... но во что перерастет приложение, построеное на етой модели - зависит только от програмиста.
      • 0
        http://localhost/demo/members/view вот так у меня не работает.... пишет Not Found
        а вот так работает..

        пос отличный может кто выложит этот MVC со всеми поправками которые были выше изложены?
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Очень интересно.
    Объясните пожалуйста:

    Вот допустим в контроллере обработал данные, получил массив($myvars) где формировать html-код?
    Где из этого массива делать таблицу(<table>) и т.п.?

    в контроллере
    ...
    for ($i...){
      $mytable.='<tr>...<td>'.$myvars[$i].'</td>...</tr>';
    }
    ...
    $this->registry['template']->set('mytable',$mytable);

    или

    в контроллере делать:
    $this->registry['template']->set('myvars',$myvars);

    а в самом Template уже обходить массив и строить html
  • 0
    скажите, а как получить дополнительные переменные, т.е. кроме members/view, например мне езе нужно передеть member_id, category_id как мне передать и получить эти переменные? заранее спасибо.
    • 0
      Тут уже надо переписывать Router.
      Или как в зенде — на preg по шаблонам или как я — тупой перебор массива router с подставленями стандартных значений из конфига загруженного сайта.
  • 0
    Какой смысл в singltone, если объекты в php5 передаются по ссылке?
  • 0
    а где продолжение статьи?
    • 0
      Вот здесь можно найти, если интересно:

      1. Предисловие / Введение / Одна точка входа
      2. Запуск задачи / Создание класса Registry
      3. Модель / Класс Router
      4. Контроллер
      5. Отображение — недостающая часть

      Правда на английском, но думаю, что будет понятно.
      • 0
        Ничего, при много благодарен :)
      • 0
        а ты не знаешь как можно обрабатывать аргументы типа controllName/Method/arg1/arg2/
        Вот нашел дополнительные ф-и к Роутеру
        public function getArg($key) {
        if (!isset($this->args[$key])) {
        return null;
        }
        return $this->args[$key];
        }
        private function extractArgs($args) {
        if (count($args) == 0) {
        return false;
        }
        $this->args = $args;
        }

        по доступу видно что одна из них должна внутри класса обрабатываться, другая как-то доступна из вне! о_0`
        • 0
          а почему бы запрос не делать такого вида index.php?router=controllName/Method&args=arg1/arg2&… Другое?
      • 0
        Загрузился только тайтл страницы :( Если у кого-то есть возможность отзеркалить страницу — буду очень благодарен.
  • 0
    А есть где примерчик глянуть всё с использованием отдельных Model, отдельных Controller, отдельных View?
  • 0
    Спиженно отсюда??
  • 0
    добавил в закладки, почитаю
  • 0
    Спасибо за статью, начало уже интересное. Как только начал читать нашел ошибку в определении метода Registry::remove();
  • 0
    Пляха муха, статья обрезана и не полностью, помогите найти полную версию. Спасибо
  • 0
    В каментах выше нашел ссылку на оригинал. Спасибо
  • 0
    Открыта проблема безопасности данного кода. Например, что будет если пользователь перейдет по такой ссылке, например
    http://example.com/?route=../../../folder/script.php

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.