Translate

Поиск по этому блогу

среда, 13 сентября 2017 г.

Создание сайта на PHP. Часть 1.

Схема работы сайта

Запос пользователя в адресной строке поступает в Front Controller где он обрабатывается с помощью роутера (маршрутизатора) в соответсвии с нашими маршрутами (роутами) прописанными в отдельном файле. Здесь же определяется какой контроллер и какой его метод (экшен) будет обрабатывать данный запрос. Уже назначенный контролер будет взаимодействовать с БД (базой данных) для получения необходимой информации и вызывать соответствующий файл представления (вид) для передачи пользователю запрашиваемой информации.

Для того, чтобы понять как это работает, лучше всего представить все в виде такой схемы:





Создаем файловую структуру сайта:

Папки:

  1. controller
  2. config
  3. components
  4. model
  5. views

Файлы:

  • index.php
  • .htaccess




Сразу приведу файл .htaccess полностью. Его нужно создать раньше, если вы работаете на OpenServer
DirectoryIndex index.php

AddDefaultCharset utf-8

RewriteEngine on
RewriteBase /
RewriteRUle ^(.*)$ index.php


index.php
<?php

// FRONT COTROLLER

// 1. Общие настройки

ini_set('display_errors', 1);
error_reporting(E_ALL);

// 2. Подключение файлов системы

define('ROOT', dirname(__FILE__));
require_once(ROOT.'/components/Router.php');

// 3. Установка соединения с БД


// 4. Вызов Router

$router = new Router();
$router->run();



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


Для подключения мы используем полный путь на диске, который мы получаем с помощью функции dirname() и псевдо константы __FILE__.


Для проверки содержимого константы ROOT(полный путь от корня файловой системы), мы можем вывести переменную ROOT следующим образом:


еcho ROOT;

Сразу после строк создания константы ROOT и подключения файла Router.php -
define('ROOT', dirname(__FILE__));
require_once(ROOT . '/components/Router.php'); 

еcho ROOT;


Соединения с БД на этом этапе мы выполнять не будем и вернемся к нему позже.

Вызов роутера можно добавить сейчас или сразу после описания нашего ротера я следующем шаге. Он будет заключаться в создании экземпляра класса Роутер - $router = new Router() и запуске метода run() - $router->run();;

Коль скоро мы подключили файл Router.php, то нам остается его создать в папке components

B нем пропишем следующий строки :
<?php

class Router {

private $routes;//создаем массив в кот. хранятся наши маршруты

public function __constructor(){

}
public function run() {
    echo "It is file Roter.php";//Это для проверки подключения файла
}
}


Теперь, если мы откроем наш сайт, то увидим строки - It is file Roter.php -. Это говорит о том. что все подключено и правильно работает

Таким образом мы запустили наш Router и передали ему управление



Router

Первое с чего нам следует начать - маршруты (роуты). Они будут храниться в отдельном файле. Это очень удобно.

В папке config создадим для этого файл routes.php

Здесь мы и разместим наши роуты в виде массива:

routes.php

<?php
return array(
	'news' => 'news/index', // actionIndex in NewsController
	'products' => 'product/list', // actionList in ProductController
);


Роуты представляют собой пару в массиве.Запрос (то что пользователь набирает в адресной строке браузера) - Например: 'news' и строка - 'news/index', которая содержит имя контроллера news (первая часть), и имя экшена index(функции обработчика запроса) - вторая часть.

Этот код - 'news' => 'news/index' показывает, что будет вызван метод - actionIndex в контроллере - NewsController


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


Теперь наша задача заставить наш Router прочитать маршруты и помнить их на время выполнения кода.


Специально для этого мы создали переменную private $routes; и конструктор public function __construct(), в котором мы прочитаем и запомним роуты.


В конструкторе пишем две строки:

$routesPath = ROOT.'/config/routes.php';
$this->routes = include($routesPath);

В первой - $routesPath = ROOT.'/config/routes.php'; мы указываем путь к роутам. Она состоит из пути к базовой директории ROOT и пути к созданному файлу.


Во второй строке мы присваиваем свойству routes -$this->routes массив, который хранится в файле routes.php

Теперь в наше свойство попадет нужный нам массив.

Для того чтобы проверить все ли у нас подключилось и правильно работает мы в методе run() сделаем распечатку массива : print_r($this->routes);

Получим такое сообщение на сайте :
Array ( [news] => news/index [products] => product/list ) It is file Router.php


Я буду выводить в тексте промежуточные состояния файла.

Все конечные версии вы можете посмотреть на Гугл Диске


Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {
        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    public function run() {
        print_r($this->routes);
        echo "It is file Router.php";
    }
}

ЗАДАЧА 3

Теперь нам нужно реализовать метод run(). Это та часть, которая отвечает за анализ запроса и передачу управления

  1. Получение запроса
  2. Проверить наличие такого запроса в файле routes.php
  3. Если найдется совпадение. то определить какой контроллер и экшен будут обрабатывать это запрос
  4. Подключить файл, который содержит класс-контроллер
  5. Создать объект этого класса контроллер и вызвать нужный метод

Теперь по порядку:

Получение запроса


Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {
        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    public function run() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        $uri = trim($_SERVER['REQUEST_URI'], '/');
        }
        echo $uri;
    }
}



Здесь мы из супер глобального массива $_SERVER по ключу 'REQUEST_URI' получаем нужную строке запроса

Теперь идем на наш сайт и в строке запроса дописываем /news/ - например. Или любое другое значение. Нажимаем Enter и видим значение из строки на нашем сайте.





Теперь, для того, чтобы сделать наш код понятнее и красивее, мы можем вынести этот код в отдельный метод - private function getURI(),а вызов сделаем из функции run() таким образом - $uri = $this->getURI();



Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;
       
    public function __construct() {

        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
//Returns request string
    private function getURI() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        return trim($_SERVER['REQUEST_URI'], '/');
        }
    }
    public function run() {

        $uri = $this->getURI();
        echo $uri;
    }
}

Метод getURI() мы сделали приватным, тем самым применили инкапсуляцию, потому как обращаться к этому методу мы планируем только из класса Роутер

Еще раз проверяем. Должно все работать точно так же как и в прошлый раз.

Проверить наличие такого запроса в файле routes.php

Теперь нам нужно найти такую строку запроса, которая содержится в переменной - $uri в наших маршрутах в файле -routes.php

Файл Router.php на данный момент полностью:
foreach ($this->routes as $uriPattern => $path) {
            echo "
$uriPattern -> $path"; }


Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {

        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    //Returns request string
    private function getURI() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        return trim($_SERVER['REQUEST_URI'], '/');
        }
    }
    public function run() {

        $uri = $this->getURI();
        foreach ($this->routes as $uriPattern => $path) {
            echo "
$uriPattern -> $path"; } } }

Здесь для каждого маршрута $this->routes, который находится в нашем массиве,мы помещаем в переменную -$uriPattern строку запросов из роутов файла- routes.php(левая часть до => знака), а в переменную $path - путь обработчика


Перезагружаем страницу и видим:





Теперь мы будем сравнивать строку запроса $uri с данными которые содержатся в роутах- $uriPattern

Это легко сделать используя функцию preg_match() Для этого мы передаем в нее строку запроса - $uri и данные из наших роутов - $uriPattern

Для проверки выводим +. То есть если наш запрос в адресной строке будет совпадать с роутами то увидим на странице +.
 if(preg_match("~$uriPattern~", $uri)) {
                echo '+';
            }
        }

Сохранили изменения в файле. Заходим на сайт, перезагружаем и получаем:





Причем, если теперь вы введете в строку запроса значение несоответствующее нашим роутам ('news' => 'news/index','products' => 'product/list' ), то ничего не увидите на сайте.

То есть, пока что наш роутер готов обработать два запроса - news и products по определенному правилу.

Обратите внимание, что в качестве разделителей в патерне "~$uriPattern~" я использовал тильду, так как в нашем патерне могут содержаться слэши- '/' например при таком адресе страницы (' news/archive' => 'news/archive'). Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {

        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    //Returns request string
    private function getURI() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        return trim($_SERVER['REQUEST_URI'], '/');
        }
    }
    public function run() {

        $uri = $this->getURI();
        foreach ($this->routes as $uriPattern => $path) {
            if(preg_match("~$uriPattern~", $uri)) {
                echo '+';
            }
        }
    }
}

Итак, если условие соблюдается. то в переменной $path будут находиться имя контроллера и экшена. Чтобы в этом убедиться вам нужно заменить + в выводе echo на переменную $path - вот так: echo $path;

Идем на страницу сайта, вводим запрос (существующий) и получаем:





Определяем какой контроллер и экшен обрабатывают запрос

Мы можем это получить с помощью функции - explode(), чтобы разделить строку на две части

Результат вы можете увидеть на скриншоте, а код файла Router.php ниже





Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {

        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    //Returns request string
    private function getURI() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        return trim($_SERVER['REQUEST_URI'], '/');
        }
    }
    public function run() {

        $uri = $this->getURI();
        foreach ($this->routes as $uriPattern => $path) {
            if(preg_match("~$uriPattern~", $uri)) {
                $segments = explode('/', $path);
                echo '
';
                print_r ($segments);
                echo '
'; } } } }


В результате мы получили два элемента, первый относящийся к контроллеру и второй к экшену

Теперь мы можем получить имя контроллера:

$controllerName = array_shift($segments).'Controller';

Здесь мы используем функцию array_shift(), она получает первый элемент из массива $segments и удаляет его. К этому значению мы добавляем слово 'Controller', поскольку мы приняли за систему такое именование конроллеров.


теперь нам остается сделать заглавной первую букву в названии контроллера функцией :


$controllerName = ucfirst($controllerName);
И теперь можно сохранить и проверить наш сайт.



Таким образом мы получили контроллер. Точно так же мы получаем и наш экшен, с той лишь разницей, что экшен имеет свой стиль наименований (с добавление вначале слова action, чтобы отличать их от других методов класса.


$actionName = 'action'.ucfirst((array_shift($segments)));



Файл Router.php на данный момент полностью:
<?php

class Router {
    private $routes;

    public function __construct() {

        $routesPath = ROOT.'/config/routes.php';
        $this->routes = include($routesPath);
    }
    //Returns request string
    private function getURI() {

        if (!empty($_SERVER['REQUEST_URI'])) {
        return trim($_SERVER['REQUEST_URI'], '/');
        }
    }
    public function run() {

        $uri = $this->getURI();
        foreach ($this->routes as $uriPattern => $path) {
            if(preg_match("~$uriPattern~", $uri)) {
                $segments = explode('/', $path);

                $controllerName = array_shift($segments).'Controller';
                $controllerName = ucfirst($controllerName);
                $actionName = 'action'.ucfirst((array_shift($segments)));
                    echo $controllerName.'
'.$actionName; } } } }


Да, кстати, теперь самое время добавить в папку controllers новые файлы ArticleController.php , NewsController.php и ProductController.php


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


Вначале нам нужно создать наши контроллеры.


И вот теперь самое время добавить в папку controllers новые файлы ArticleController.php , NewsController.php и ProductController.php

Содержимое файлов NewsController.php и ProductController.php и ArticleController.php это соответствующий класс и один метод, который пустой и просто возвращает - true



NewsController.php
<?php

class NewsController {
	public function actionIndex()
		{
			return true;
		}

		
}




ProductController.php
<?php

class ProductController
{


		public function actionList()
		{
			return true;
		}


}


ArticleController.php
&jt;?php

class ArticleController
{


		public function actionList()
		{
			return true;
		}


}



Продолжаем работать над файлом Router.php и далее нам нужно подключить файл класса контроллера. Так как мы уже знаем его имя - $controllerName и используем определенный подход к названию фалов в папке контролеров, то сделать это очень просто вот таким кодом:



$controllerFile = ROOT . '/controllers/' .$controllerName. '.php';
				if (file_exists($controllerFile)) {
					include_once($controllerFile);
				}

В этой части кода $controllerFile = ROOT . '/controllers/' .$controllerName. '.php'; мы определяем файл который необходимо подключить. Для этого мы прописываем путь к нему используя имя класса


Далее мы непосредственно подключаем его, предварительно проверяя существует ли он?



if (file_exists($controllerFile)) {
                    include_once($controllerFile);
                } 


Все необходимые действия в виде анализа запроса и подключения нужного файла мы выполнили


Создаем объект класса контрллера - $controllerObject = new $controllerName;. Вместо имени класса мы подставляем переменную, которая содержит строку с именем этого класса.


Далее для этого объекта $controllerObject мы вызываем метод $actionName(), точно так же мы используем переменную, которая содержит строку с названием нужного метода. В наших методах в файлах контолерах, мы писали return true для того, чтобы мы могли передать это значение определенной переменной - $result. В рузельтате, если метод сработал, то мы сможем об этом узнать и оборвать весь цикл - if ($result != null) { break; }. , который ищет совпадения в наших маршрутах.



$controllerObject = new $controllerName;
				$result = $controllerObject->$actionName();
				if ($result != null) {
					break;
				}



Теперь наша система практически готова. Осталось добавить несколько штрихов в наши методы контроллеров и проверим ее на работоспособность. Например в NewsController.php (перед return true) - echo "NewsController.php actionIndex ";





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


Теперь если мы зайдем по другому адресу, мы получим уже совсем другой контроллер с новым экшеном. Если мы зайдем по адресу, которого нет в таблице наших маршрутов (роутах) то мы не получим ничего.





Все файлы проекта можно посмотреть на Гугл Диске

Далее Модель, Представление и усовершенствования класса Роутер

                                                                                                                                                             

1 комментарий:

  1. VarangaOfficial - варанга от грибка цена - исключительно достоверные, проверенные факты. Воспользовавшись данным ресурсом, вы получите возможность узнать полную информацию касательно данного лекарственного средства. Лично увидеть данные о проведенных клинических исследований, прочитать отзывы реальных покупателей и врачей. Ознакомиться с инструкцией по применению, прочитать об особенностях и методах работы комплекса, уяснить, в чем заключаются особенности работы крема Варанга, где можно купить оригинальный препарат и, как избежать покупки подделки. Мы очень тщательно и скурпулезно проверяем размещаемые на сайте данные. Предоставляем посетителям нашего онлайн-ресурса сведения, которые были взяты исключительно из надежных источников. Если вы обнаружили у себя признаки грибкового поражения стоп или же долго и безрезультатно пытаетесь избавиться от этого неприятного недуга, наш сайт покажет вам быстрый и простой способ решения проблемы. Присоединяетесь и живите полноценной, здоровой жизнью. Теперь все ответы на самые популярные и волнующие пользователей вопросы, собраны на одном ресурсе.

    ОтветитьУдалить



Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com