Начнем с истории. В стародавние времена, когда web еще был на стадии своего бурного развития условно начало двухтысячных и PHP был главным игроком на рынке языков для веба, писали на нем как попало.
Именно тогда сформировалось негативное отношение к пхпешниками как в основном низкоквалифицированным программистам. Порог входа был очень низким, сайты представляли собой наборы несвязанных php файлов, а на код было страшно смотреть.
К счастью веб развивался, развивался PHP, вместе с ними росло и требование к разработчикам, а вслед за ними подвезли и требования к проектированию веб приложений. Архитектура MVC и сотоварищи стали править балом.
Поэтому мы постараемся по-быстрому пройти этап наивного программирования на PHP и как можно быстрее уйти на архитектуру MVC
Добавляем новую страничку
Давайте глянем на наш проект. У нас сейчас там один файлик. А так-то, обычно, на сайтах много разных ссылок. Давайте попробуем зайти на какую-нибудь ссылку, например, http://localhost/hello
В ответ получим ошибку 404
которая означает что файла нет. Что конечно предсказуемо, но уныло.
Так что давайте добавим файлик hello.php
вот с таким содержимым:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
Я новая страничка!!!1111 =)
</body>
</html>
пробуем снова http://localhost/hello
хм, все равно 404
а если такой url: http://localhost/hello.php
О! Заработало! То есть получается мы после http://localhost/ должны указывать путь к php файлу. Ну ладно…
Добавляем картинку
А давайте попробуем картинку добавить. И причем так, чтобы она у нас на сервере лежала. Вот такую, например,:
сохраняем ее в папку где лежит файл, причем лучше подпапку images
создать, вот так:
теперь давайте подключим ее на новой странице, для этого надо указать ее путь относительно папки с проектом. И еще путь ОБЯЗАТЕЛЬНО должен начинаться с прямого слеша /
вот так:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- … -->
</head>
<body>
Я новая страничка!!!1111 =)
<br>
<img src="/images/025_picture-47918.gif" alt="">
</body>
</html>
проверяем:
ура! =)
Только возникает тогда вопрос почему php понял что эта картинка и не стал интерпретировать файлик, мы ведь даже можем его открыть по прямой ссылке http://localhost/images/025_picture-47918.gif
более того мы можем даже открыть папку (если на линуксе запускали через локальный сервер): http://localhost/images/
а это, товарищи, между прочим дыра в безопасности. Неужели все это делает php?
Настраиваем Nginx
Если вы на линуксе то можно пропустить этот раздел.
А вот и нет! На самом деле это делает не php. На самом деле когда мы пишем http://localhost/images/ запросы идут не к php, а сначала к веб-серверу Nginx (engine x — по-русски произносится как энджи́нкс, но я почему-то говорю нгинкс… как пишется так и слышится)
Это специальная программа, которая в общем не предназначена исполнять какой-нибудь код, ее главная задача перенаправлять запросы разным интерпретаторам и раздавать файлы. И делает она это настолько виртуозно что может одновременно обслуживать десятки тысяч соединений и раздавать тысячи файлов. За что ее все нежно любят *_*
Давайте заглянем в настройки этого самого nginx
Так как nginx может обслуживать сразу несколько сайтов, то в блоке sites-enabled можно выбрать какой именно сайт мы хотим настроить. Так как мы работаем только с одним проектом и никаких дополнительных настроек не производили, то мы выбираем 00-default.con, там мы увидим:
в общем первое что нам надо сделать это отключить autoindex, можно поставить значение off, либо просто закомментить строку, вот так:
и теперь надо перезапустить nginx
проверяем:
Forbidden – то есть запрещено, то что нам и надо =)
А теперь сделаем еще одну вещь которая может показаться странной, но мы сделаем так чтобы все запросы, если файл не найден, шли в index.php, для этого поправим строчку с try_files:
пропишем там
try_files $uri $uri/ /index.php$is_args$args;
снова перезапускаем nginx
попробуем теперь зайти, например, на такую страницу http://localhost/page/about
по идее нас должно переправить на index.php,
проверяем:
красота! =)
Узнаем URL запроса
Что же нам это дало? А то что мы теперь управляем всеми запросами, которые идут не напрямую к обычным файлам с помощью одного index.php, а это в свою очередь дает нам возможность более централизовано руководить приложением.
Давайте вообще посмотрим, как мы можем использовать информацию о ссылке внутри нашего файлика index.php
И так, каждый раз, когда запускается php файл ему доступен список предопределенных переменных https://www.php.net/manual/ru/reserved.variables.php
давайте глянем что в них оказывается, когда мы будем открывать страничку с разными адресами, добавим в index.php:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- … -->
</head>
<body>
<?php print_r($_SERVER) ?> <!-- добавил вывод информации о переменной $_SERVER -->
</body>
</html>
получим такую нечитаемую кашу
на самом деле php сформировал нам ответ в красивом виде, но так как браузер игнорит всякие переносы то выглядит это уродливо.
Можно даже глянуть код страницы (Ctrl+U) и увидеть
К счастью в html есть специальный тег pre
которые учитывает все переносы и отступы, я кстати использую его для вывода кода во всех своих статьях. В общем просто берем и оборачиваем тегом нашу php вставку.
<body>
<pre>
<?php print_r($_SERVER) ?>
</pre>
</body>
Запускаем и скроллим пока не найдем пункт REQUEST_URI
если присмотреться в нем написано тоже самое что и в строке бразуера.
Давайте попробуем вывести это значение отдельно.
$_SERVER
– это словарик, то есть у него есть ключи и значения, для доступа используется оператор квадратных скобок []
, вот так:
<body>
<pre>
<?php print_r($_SERVER["REQUEST_URI"]) ?>
</pre>
</body>
смотрим:
прикольно! =) Но что нам с этого?
Делаем менюшку
Попробуем сделать меню. У меня допустим будет сайт о галактиках и туманностях во вселенной. Пусть у меня будет три странички. Например:
- Главная
- Галактика Андромеда
- Туманность Ориона
Давайте запилим меню.
Подключаем бутстрап
Я буду использовать бутстрап. Идем на сайт https://getbootstrap.com/docs/5.2/getting-started/introduction/
и копируем себе css в head файла index.php
как-то так получится
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
</head>
<body>
<?php print_r($_SERVER["REQUEST_URI"]) ?>
</body>
</html>
теперь нам надо вставить меню навигации, опять же его можно скопипастить с сайта bootstrap, идем сюда https://getbootstrap.com/docs/5.2/components/navbar/#nav
и пихаем в body
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Pricing</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
</div>
</div>
</nav>
<?php print_r($_SERVER["REQUEST_URI"]) ?>
если присмотреться, то мы увидим, что за пункты в меню отвечает вот этот блок
давайте перепишем его так чтобы остались только нужные нам:
получится вот так:
чтобы наш /hello/words
выровнялось по навигации, обернем нашу инструкцию в класс .container
, это специальный класс в бутстрапе, который добавляет отступы по краям. Причем он респонсивный, то есть на больших экранах отступы больше, на маленьких – меньше, на телефоне вообще могут отсутствовать.
Вот так:
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- ... -->
</nav>
<div class="container">
<?php print_r($_SERVER["REQUEST_URI"]) ?>
</div>
теперь уже симпатичнее
Кстати вот эта надпись Navbar
это на самом деле место под лого сайта, давайте подключим fontawesome
и запихаем туда какую-нибудь иконку,
<head>
<!-- ... -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" rel="stylesheet" />
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<!-- тут вместо Navbar загнал <i class="fas fa-meteor"></i> -->
<a class="navbar-brand" href="#"><i class="fas fa-meteor"></i></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<!-- ... -->
</ul>
</div>
</div>
</nav>
<div class="container">
<?php print_r($_SERVER["REQUEST_URI"]) ?>
</div>
</body>
проверяем:
давайте теперь как-нибудь сделаем так, чтобы тыкая на разные пункты меню у нас появлялся разный текст внизу, в принципе тут все просто. Достаточно прописать url у каждого пункта меню. Прописываем:
попробуем теперь потыкать:
класс! =)
То есть формально мы вроде страницы не переключаем, но выглядит будто бы мы таки переключаем. Пусть вас это не смущает, так работают 99% сайтов в мире
Управляем содержимым
Давайте теперь попробуем чего-нибудь вывести, например, на главной страницу будет список ссылок на все страницы (ну типа продублируем навигацию), а на страницах туманностей будут картинки
Способ №1
Это самый неправильный способ, никогда так не делайте, но в принципе он сам естественный. Мы просто будем проверять значение переменной $_SERVER["REQUEST_URI"]
и в зависимости от значения выводить тот или иной кусок.
PHP нас не ограничивает в таком написании, у него есть поддержка if
, и делается это так:
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- ... -->
</nav>
<div class="container">
<?php if ($_SERVER["REQUEST_URI"] == "/") { ?>
Вы на главной странице! =)
<?php } elseif ($_SERVER["REQUEST_URI"] == "/andromeda") { ?>
Тут мы вам расскажем о волшебной Галактике Андромеда
<?php } elseif ($_SERVER["REQUEST_URI"] == "/orion") { ?>
Был значит один кот, и носил он галактику в поясе Ориона
<?php } ?>
</div>
</body>
то есть логику мы оборачиваем в <?php ... ?>
а вывод в браузер помещаем внутрь как будто это тело if
ну типа работает, но читать очень тяжело, да и писать долго.
Способ №2
По убогости примерно, как первый, но писать меньше. Делается значит так, вместо того чтобы писать кучу php оберток делается одна и в нее все помещается. Выглядит так
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- ... -->
</nav>
<div class="container">
<?php
if ($_SERVER["REQUEST_URI"] == "/") {
echo "Вы на главной странице! =)<br>";
echo "<b>И разметку могу принтовать</b>";
} elseif ($_SERVER["REQUEST_URI"] == "/andromeda") {
echo "Тут мы вам расскажем о волшебной Галактике Андромеда";
} elseif ($_SERVER["REQUEST_URI"] == "/orion") {
echo "Был значит один кот, и носил он галактику в поясе Ориона";
}
?>
</div>
В PHP есть специальный оператор echo
, который вставляет указанный текст в содержимое html ответа ну или правильнее сказать в содержимое web-ответа, ведь ответ это не всегда html файлик, может быть и json какой-то или вообще картинка, а то и простой файл.
Кстати никто не мешает нам объявлять переменные и использовать их:
<?php
$url = $_SERVER["REQUEST_URI"]; // у переменной в PHP слева ставим $
if ($url == "/") { // и при обращении тоже
echo "Вы на главной странице! =)<br>";
echo "<b>И разметку могу принтовать</b>";
} elseif ($url == "/andromeda") {
echo "Тут мы вам расскажем о волшебной Галактике Андромеда";
} elseif ($url == "/orion") {
echo "Был значит один кот, и носил он галактику в поясе Ориона";
}
?>
может казаться не очень привычным юзать знак доллара, но есть и плюсы. В PHP можно очень быстро вставить переменную в строку, например, так:
<?php
$url = $_SERVER["REQUEST_URI"];
echo "Вы на странице: $url, будьте внимательны!<br>"; // вместо url подставится значение $url
if ($url == "/") {
echo "Вы на главной странице! =)<br>";
echo "<b>И разметку могу принтовать</b>";
} elseif ($url == "/andromeda") {
echo "Тут мы вам расскажем о волшебной Галактике Андромеда";
} elseif ($url == "/orion") {
echo "Был значит один кот, и носил он галактику в поясе Ориона";
}
?>
и вроде как здорово, но ведь если надо будет вывести хотя бы пару десятков строк читать это станет не возможным. Поэтому:
Способ №3
Самый грамотный способ. По крайне мере если используем базовый PHP. Идея такая: взять и создать под страницы отдельные файлы и в зависимости от адреса выводит их содержимое.
Создадим отдельную папку, назовем ее views, создадим в ней три файлика и загоним в файлике содержимое страниц, вот так:
а теперь сделаем так чтобы в нашем index.php в зависимости от url выводилось содержимое этих файлов, для этого будем использовать еще один специальный оператор require, который просто берет и вставляет то что находится внутри указанного после оператора файла:
<div class="container">
<?php
$url = $_SERVER["REQUEST_URI"];
echo "Вы на странице: $url, будьте внимательны!<br>";
if ($url == "/") {
require "views/main.php";
} elseif ($url == "/andromeda") {
require "views/andromeda.php";
} elseif ($url == "/orion") {
require "views/orion.php";
}
?>
</div>
проверяем:
работает так же, но теперь мы можем создавать содержимое любой сложности в отдельных файликах! =)
Настраиваем безопасность
Еще одни тонкий момент. Оно у нас конечно все работает, и даже ошибок, но на самом деле у нас в коде присутствует уязвимость. Потенциальный злоумышленник может получить доступ к отдельным частям нашей программы.
Например, он может зайти по пути http://localhost/views/orion.php и увидеть содержимое файла orion.php
И казалось бы чего страшного то? Но дело в том, что при разработке реального приложения, у вас в файлах может лежать какая-та информация которую вы не хотели бы показывать стороннему пользователю. Или какой-то скрипт которые будучи выполненным не из index.php может повести себя не предсказуемо.
Поэтому все современные веб-движки рекомендуют создавать отдельную папку public
в которой будут лежать index.php, всякие картинки, стили и все такое. Давайте настроим наше приложение чтобы оно работало таким образом:
попытка открыть http://localhost нам выдаст
Для Windows
теперь надо настроить laragon чтобы он в качестве root папки использовал папку public, идем в laragon,
выбираем папку public
и вроде как и всё, но проблема в том что laragon сбросил наши настройки nginx, давайте зайдем туда и глянем:
смотрим
и так, чтобы эти настройки не терялись, нам надо подправить шаблон laragon по которому он пересоздает файлик 00-default.conf
когда вы переключаете папку. Идем в папку куда ставили laragon и заходим там в /usr/tpl
открываем файлик nginx.sites-enabled.00-default.manifest.tpl
и правим ему location, чтобы был как в прошлый раз мы настраивали:
server {
...
location / {
try_files $uri $uri/ /index.php$is_args$args; # добавил
# а это закоментил или можно вообще удалить
#try_files $uri $uri/ =404;
#autoindex on;
}
...
}
теперь еще раз переключаем на php_01
и обратно на php_01\public
На линуксе
Просто запустите локальный сервер указав папку public
php -S localhost:3000 -t public
Тестируем
Проверяем:
хохо! Работает! Только ошибки сыпятся. Но оно и понятно у нас ведь в index.php
пути относительно index.php строятся, а надо чтобы он ходил на папку выше. Поэтому идем в index.php
и правим
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ... -->
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<!-- ... -->
</nav>
<div class="container">
<?php
$url = $_SERVER["REQUEST_URI"];
echo "Вы на странице: $url, будьте внимательны!<br>";
if ($url == "/") {
require "../views/main.php"; // << добавил ../, что означает ищи файл в папке на уровень выше
} elseif ($url == "/andromeda") {
require "../views/andromeda.php"; // << и тут
} elseif ($url == "/orion") {
require "../views/orion.php"; // и здесь
}
?>
</div>
</body>
</html>
тестируем
ляпота! =)
И самое замечательное что теперь никто не сможет посмотреть содержимое отдельного php файла
Такие дела *______*