Регистрация и авторизация пользователей на PHP. Часть третья

Сначала займемся реализацией работы алгоритма создания экземпляра класса модели пользователя. Но прежде, чем перейти к этому, нужно ещё раз описать, как мы себе представляем работу всей процедуры авторизации/регистрации.

Требования к процедуре регистрации пользователя

  1. Неавторизованному пользователю всегда будет доступна форма аутентификации на сайте.
  2. Пользователь может при аутентификации выбрать опцию «Запомнить меня».
  3. Авторизованный пользователь всегда будет видеть свой логин и кнопку выхода из аккаунта.
  4. Форма регистрации пользователя будет размещена на отдельной странице сайта.
  5. Форма регистрации будет дополнена капчей от Google.
  6. Данные, полученные от пользователя при регистрации, будут проверяться до отправки формы посредством JavaScript.
  7. Будет производиться проверка на уникальность логина пользователя.
  8. Логином пользователя мы будем считать его адрес электронной почты.
  9. Установим обязательные требования к паролю пользователя.
  10. При заполнении пользователем всех необходимых полей и признанием их значений корректными, форма регистрации будет отправлена на обработку.
  11. Пользователь будет зарегистрирован, информация о нем будет сохранена в БД, после чего он сразу же будет авторизован.

Очевидно, что начать нам, прежде всего, следует с формы аутентификации на сайте. Раз уж она должны быть доступной всегда, вынесем её в отдельный файл, который будем подключать к макету страницы. В корневом каталоге сайта создадим каталог blocks, и поместим туда файл header.php. А кроме этого, еще будем из внешних файлов подключать таблицы стилей и сценарии JavaScript.

Все формы будут обрабатываться сценарием файла /service.php

Форма аутентификации на сайте

Таким образом, наши файлы index.php и bootstrap.php несколько преобразятся.

<?php
// Файл bootstrap.php

// Включаем сессионную опцию use_strict_mode
ini_set('session.use_strict_mode', 1);

// Инициализируем константу для защиты от стороннего доступа
define("LEZH", TRUE);

// Определяем каталог исходных файлов нашего сайта
define("SITE_DIR", __DIR__);

// Определяем путь к каталогу файла с определением ядра сайта
define("CORE_DIR", SITE_DIR . DIRECTORY_SEPARATOR . "modules" . DIRECTORY_SEPARATOR . "core" . DIRECTORY_SEPARATOR);

// Подключаем файл с определением класса ядра нашего сайта
require_once(CORE_DIR . "core.php");

// Определяем путь для подключения внешних файлов для страниц сайта
define("INCLUDE_BLOCKS_PATH", SITE_DIR . DIRECTORY_SEPARATOR . "blocks" . DIRECTORY_SEPARATOR);

// Инициализируем ядро сайта
Core::init();

// Получаем информацию об авторизации пользователя
$oCurrentUser = Core::factory("User")->getAuthUser();

?>

В файл bootstrap.php мы добавили путь для подключения внешних блоков (меню и тому подобное).

Мы немного забежали вперед. Последние строки файла bootstrap.php как бы уже намекают на то, как именно будет получена и сохранена информация об авторизованном пользователе. Реализацию этого мы приведем позже.
<?php
// Файл index.php

require_once('bootstrap.php');
?>
<!doctype html>
<html>
	<head>
		<title>Регистрация и авторизация пользователей</title>
		<?php
		// Подключаем необходимые CSS-файлы
		require_once(INCLUDE_BLOCKS_PATH . "css.php");
		
		// Подключаем необходимые JS-файлы сценариев
		require_once(INCLUDE_BLOCKS_PATH . "scripts.php");
		?>
	</head>
	<body>
		
		<?php
		// Подключаем блок header для страницы
		require_once(INCLUDE_BLOCKS_PATH . "header.php");
		?>
		
		<main>
			<div class="container-fluid container-lg">
				
				<h1>Главная страница сайта регистрации и авторизации пользователей</h1>
				
			</div>
		</main>
		
	</body>
</html>

Теперь покажем содержимое следующих файлов:

  • css.php;
  • scripts.php;
  • header.php.
<!-- Файл blocks/css.php -->

<!-- Подключаем необходимые файлы для работы Bootstrap 4 -->
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous" />
<!-- Пользовательские таблицы стилей -->
<link rel="stylesheet" href="/css/styles.css" />
<!-- Файл blocks/scripts.php -->

<!-- jQuery -->
<script src="https://yastatic.net/jquery/3.3.1/jquery.min.js"></script>
<!-- JS Bootstrap 4 -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<!-- Пользовательские JS-сценарии -->
<script src="/js/scripts.js"></script>
<!-- Файл blocks/header.php -->
<header class="bg-dark text-white">
	<div class="container-fluid container-lg">
		<nav class="navbar navbar-dark bg-dark">
			<a class="navbar-brand link-decor" href="/lk.php">Личный кабинет</a>
			<form class="form-inline" autocomplete="off" onsubmit="return false;" method="post" action="/service.php">
				<label class="sr-only" for="user_login">Логин</label>
				<div class="input-group mb-2 mb-lg-0 mr-sm-2">
					<div class="input-group-prepend">
						<div class="input-group-text">@</div>
					</div>
					<input class="form-control mr-sm-2" type="text" id="user_login" name="user_login" placeholder="Логин" aria-label="Логин" />
				</div>
				<input class="form-control mb-2 mb-lg-0  mr-sm-2" type="password" id="user_password" name="user_password" placeholder="Пароль" aria-label="Пароль" />
				<div class="form-check mb-2 mb-lg-0  mr-sm-2">
					<input class="form-check-input" type="checkbox" id="user_remember" name="user_remember" />
					<label class="form-check-label" for="user_remember">
						Запомнить меня
					</label>
				</div>
				<!-- Скрытое поле формы, в котором хранится строка запроса к сценарию-обработчику -->
				<input type="hidden" id="user_login_request" name="request" value="user_login" /
				<button class="btn btn-success my-2 my-sm-0" type="submit">Войти</button>
			</form>
		</nav>
	</div>
</header>

Пора уже взглянуть на главную страницу нашего тестового сайта. Она сейчас должна выглядеть примерно так.

Пользовательские таблицы стилей и JS-сценарии

Внимательный читатель уже заметил, что в файлах css.php и scripts.php у нас добавились еще два файла: пользовательские таблицы стилей и пользовательские JavaScript-сценарии. Уже сейчас в них тоже можно добавить кое-что.

/********* Файл /css/styles.css **********/

/***** Все ссылки в Bootstrap 4 от Twitter, почему-то, неподчеркнутые 
	Для исправления этого добавим свои стили **************/
a.link-decor {
	text-decoration:underline !important;
}

Теперь все ссылки, которым мы присвоим класс .link-decor, будут подчеркнуты. В том числе, и ссылка на личный кабинет пользователя.

// Файл /js/scripts.js
// Используем строгий режим ECMAScript 5
"use strict";

(function($) {
	
	jQuery(function() {
		
		
		
	});
	
})(jQuery);

Содержимое файла пользовательских JavaScript-сценариев, пока что, такое. Будем его пополнять по мере необходимости.

Автор не описывает читателю почему он использует строгий режим ECMAScript 5. Читатель вправе самостоятельно изучить информацию об этом режиме работы JavaScript.

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

Форма регистрации пользователя

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

<!-- Файл blocks/header.php -->
<header class="bg-dark text-white">
	<div class="container-fluid container-lg">
		<nav class="navbar navbar-dark bg-dark">
			<a class="navbar-brand link-decor" href="/lk.php">Личный кабинет</a>
			<form class="form-inline" autocomplete="off" onsubmit="return false;" method="post" action="/service.php">
				<label class="sr-only" for="user_login">Логин</label>
				<div class="input-group mb-2 mb-lg-0 mr-sm-2">
					<div class="input-group-prepend">
						<div class="input-group-text">@</div>
					</div>
					<input class="form-control mr-sm-2" type="text" id="user_login" name="user_login" placeholder="Логин" aria-label="Логин" />
				</div>
				<input class="form-control mb-2 mb-lg-0  mr-sm-2" type="password" id="user_password" name="user_password" placeholder="Пароль" aria-label="Пароль" />
				<div class="form-check mb-2 mb-lg-0  mr-sm-2">
					<input class="form-check-input" type="checkbox" id="user_remember" name="user_remember" />
					<label class="form-check-label" for="user_remember">
						Запомнить меня
					</label>
				</div>
				<!-- Скрытое поле формы, в котором хранится строка запроса к сценарию-обработчику -->
				<input type="hidden" id="user_login_request" name="request" value="user_login" /
				<button class="btn btn-success my-2 my-sm-0" type="submit">Войти</button>
			</form>
		</nav>
		<div class="row text-xl-right">
			<div class="col-12">
				<div class="mr-xl-3 ml-xl-0 ml-3 mb-3">
					<a href="/registration.php" class="link-decor">Зарегистрироваться</a>
				</div>
			</div>
		</div>
	</div>
</header>

После этого наш блок <header> будет выглядеть примено так:

Создаем в корне сайта файл registration.php со следующим содержимым.

<?php
// Файл registration.php

require_once('bootstrap.php');
?>
<!doctype html>
<html>
	<head>
		<title>Регистрация нового пользователя</title>
		<?php
		// Подключаем необходимые CSS-файлы
		require_once(INCLUDE_BLOCKS_PATH . "css.php");
		
		// Подключаем необходимые JS-файлы сценариев
		require_once(INCLUDE_BLOCKS_PATH . "scripts.php");
		?>
	</head>
	<body>
		
		<?php
		// Подключаем блок header для страницы
		require_once(INCLUDE_BLOCKS_PATH . "header.php");
		?>
		
		<main>
			<div class="container-fluid container-lg">
				
				<h1>Регистрация пользователя сайта</h1>
				
			</div>
		</main>
		
	</body>
</html>

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

  1. У пользователя будет логин, которым будет являться его адрес электропочты.
  2. Поля нашей формы будут иметь заголовки.
  3. Поля формы будут иметь описания с инструкциями.
  4. Поля формы будут иметь поля для отображения сообщений об ошибках.
  5. Форма будет иметь три поля: логин, пароль и подтверждение пароля.
  6. Форма будет использовать капчу Google.
  7. Форма будет иметь три кнопки: регистрация, очистить поля, на главную.

Пока что размышлений достаточно. Приступаем. Создадим нашу форму прямо в этом же файле.

<?php
// Файл registration.php

require_once('bootstrap.php');
?>
<!doctype html>
<html>
	<head>
		<title>Регистрация нового пользователя</title>
		<?php
		// Подключаем необходимые CSS-файлы
		require_once(INCLUDE_BLOCKS_PATH . "css.php");
		
		// Подключаем необходимые JS-файлы сценариев
		require_once(INCLUDE_BLOCKS_PATH . "scripts.php");
		?>
	</head>
	<body>
		
		<?php
		// Подключаем блок header для страницы
		require_once(INCLUDE_BLOCKS_PATH . "header.php");
		?>
		
		<main>
			<div class="container-fluid container-lg">
				
				<h1>Регистрация пользователя сайта</h1>
				
				<div id="user-registration-form">
					
					<!-- Все поля формы обязательны для заполнения -->
					<form action="/service.php" autocomplete="off" method="post" onsubmit="return false;">
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_login" class="col-md-3 col-form-label">Ваш логин:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="email" class="form-control" id="user_login" name="user_login" placeholder="Например: evgeni@lezhenkin.ru" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_login-instr">Укажите ваш действительный адрес электропочты, к которому вы имеете доступ, и на котором вы можете принимать и отправлять письма.</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_login-error"></div>
							</div>
						</div>
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_password" class="col-md-3 col-form-label">Ваш пароль:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="password" min="5" max="32" class="form-control" id="user_password" name="user_password" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_password-instr">Пароль может состоять только из символов латинского алфавита, цифр, и символов: <strong><samp>* - + ! @ # $ [ ] { } ' " ; : > < , .</samp></strong>. Пароль дожен содержать, как минимум, одну заглавную букву, и, как минимум, одну цифру. Длина пароля не должна быть менее шести символов, и не должна превышать 32 символа.</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_password-error"></div>
							</div>
						</div>
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_password_approve" class="col-md-3 col-form-label">Подтвердите введенный пароль:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="password" class="form-control" id="user_password_approve" name="user_password_approve" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_password_approve-instr">Вы должны ввести одинаковые пароли</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_password_approve-error"></div>
							</div>
						</div>
						
						<!-- Капча Google recaptcha -->
						<div class="form-group row">
							<label for="user_captcha" class="col-md-3 col-form-label">Вы не робот?</label>
							<div class="col-md-9">
								
							</div>
						</div>
						
						<!-- Скрытое поле формы, в котором хранится строка запроса к сценарию обработчику -->
						<input type="hidden" id="user_registration_request" name="request" value="user_registration" />
						
						<!-- Кнопки управления формой -->
						<div class="btn-group offset-md-3">
							
							
							<input type="submit" name="submit" value="Зарегистрироваться" class="ml-md-2 btn btn-success" />
							
							<input type="reset" name="reset" value="Очистить" class="btn btn-warning" />
							
							<a href="/" class="btn btn-danger">На главную</a>
							
						</div>
						
					</form>
					
				</div>
				
			</div>
		</main>
		
	</body>
</html>

Пока что всё получяется не очень красивым. Давайте посмотрим.

Преображаем внешний вид формы регистрации пользователя

Размер шрифта полей подсказок, очевидно, великоват. Шрифт в полях сообщений сложно оценить, так как на данном этапе никаких ошибок нет и быть не может. Поля сообщений об ошибках вообще нужно скрыть. Отображать нужно будет только в случае наличия ошибок, и показывать не все поля, а только те, в которых будут соответствующие для пользователя сообщения. Полю описания следует дать небольшой верхний отступ от поля формы. Ну, а там, кто знает. Быть может, вам и так нравится.

/********* Файл /css/styles.css **********/

/***** Все ссылки в Bootstrap 4 от Twitter, почему-то, неподчеркнутые 
	Для исправления этого добавим свои стили **************/
a.link-decor {
	text-decoration:underline !important;
}

/** Поля сообщений об ошибках будут отсутствовать на экране **/
div.alert.error {
	display:none;
}

/** Уменьшим размер шрифта в полях описаний и ошибок для форм **/
div.alert.error, div.alert.instr {
	font-size:0.75rem;
}

/** Поля описания будут иметь верхний отступ **/
div.alert.instr {
	margin-top:15px;
}

Теперь страница будет выглядеть немного опрятнее. Внимательный читатель спросит, а как же быть с капчей? Действительно, давайте её подключим. Дополним код страницы registration.php кодом для генерации и вставки капчи. Обратите внимание на строки 7 и 82.

<?php
// Файл registration.php

require_once('bootstrap.php');

// Получаем параметры конфигурации для генерации нашей капчи
$aCaptcha_Config = Core_Config::instance()->get("captcha");
?>
<!doctype html>
<html>
	<head>
		<title>Регистрация нового пользователя</title>
		<?php
		// Подключаем необходимые CSS-файлы
		require_once(INCLUDE_BLOCKS_PATH . "css.php");
		
		// Подключаем необходимые JS-файлы сценариев
		require_once(INCLUDE_BLOCKS_PATH . "scripts.php");
		?>
	</head>
	<body>
		
		<?php
		// Подключаем блок header для страницы
		require_once(INCLUDE_BLOCKS_PATH . "header.php");
		?>
		
		<main>
			<div class="container-fluid container-lg">
				
				<h1>Регистрация пользователя сайта</h1>
				
				<div id="user-registration-form">
					
					<!-- Все поля формы обязательны для заполнения -->
					<form action="/service.php" autocomplete="off" method="post" onsubmit="return false;">
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_login" class="col-md-3 col-form-label">Ваш логин:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="email" class="form-control" id="user_login" name="user_login" placeholder="Например: evgeni@lezhenkin.ru" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_login-instr">Укажите ваш действительный адрес электропочты, к которому вы имеете доступ, и на котором вы можете принимать и отправлять письма.</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_login-error"></div>
							</div>
						</div>
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_password" class="col-md-3 col-form-label">Ваш пароль:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="password" min="5" max="32" class="form-control" id="user_password" name="user_password" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_password-instr">Пароль может состоять только из символов латинского алфавита, цифр, и символов: <strong><samp>* - + ! @ # $ [ ] { } ' " ; : > < , .</samp></strong>. Пароль дожен содержать, как минимум, одну заглавную букву, и, как минимум, одну цифру. Длина пароля не должна быть менее шести символов, и не должна превышать 32 символа.</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_password-error"></div>
							</div>
						</div>
						
						<!-- Поле формы -->
						<div class="form-group row">
							<label for="user_password_approve" class="col-md-3 col-form-label">Подтвердите введенный пароль:</label>
							<div class="col-md-9">
								<!-- Непосредственно само поле -->
								<input type="password" class="form-control" id="user_password_approve" name="user_password_approve" required />
								<!-- Описание поля формы и требования к его заполнению -->
								<div class="alert alert-info instr" id="user_password_approve-instr">Вы должны ввести одинаковые пароли</div>
								<!-- Поле для вывода сообщения об ошибке, относящегося именно к этому полю формы -->
								<div class="alert alert-danger error" id="user_password_approve-error"></div>
							</div>
						</div>
						
						<!-- Капча Google recaptcha -->
						<div class="form-group row">
							<label for="user_captcha" class="col-md-3 col-form-label">Вы не робот?</label>
							<div class="col-md-9">
								<!-- Чтобы заработала капча Google, нужно лишь добавить одно поле -->
								<div class="g-recaptcha" data-sitekey="<?=$aCaptcha_Config["site_key"]?>"></div>
							</div>
						</div>
						
						<!-- Скрытое поле формы, в котором хранится строка запроса к сценарию обработчику -->
						<input type="hidden" id="user_registration_request" name="request" value="user_registration" />
						
						<!-- Кнопки управления формой -->
						<div class="btn-group offset-md-3">
							
							
							<input type="submit" name="submit" value="Зарегистрироваться" class="ml-md-2 btn btn-success" />
							
							<input type="reset" name="reset" value="Очистить" class="btn btn-warning" />
							
							<a href="/" class="btn btn-danger">На главную</a>
							
						</div>
						
					</form>
					
				</div>
				
			</div>
		</main>
		
	</body>
</html>

Для того, чтобы капча генерировалась, отрисовывалась и работала как положено, необходимо подключить JavaScript-сценарий от Google. Дополним наш файл scripts.php.

<!-- Файл blocks/scripts.php -->

<!-- jQuery -->
<script src="https://yastatic.net/jquery/3.3.1/jquery.min.js"></script>
<!-- JS Bootstrap 4 -->
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<!-- reCAPTCHA Google -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<!-- Пользовательские JS-сценарии -->
<script src="/js/scripts.js"></script>
Автор не описывает способы интеграции Google reCAPTCHA на сайт пользователя. Читателю необходимо самостоятельно изучить документацию, если процесс такой интеграции ему не знаком.

И тут внимательный читатель, конечно же, скажет: «Так, стоп! А это что ещё такое было в строках 7 и 82?». И будет прав.

Чтобы продемонстрированный код работал, необходимо дополнить код класса Core, а также добавить файл с реализацией статического класса Core_Config.

Дело в том, что различные параметры конфигурации чего-либо брать и просто так хранить в файлах со сценариями не стоит. Мы сделали так, чтобы параметры конфигурации из каталога /modules/core/config запрашивались по требованию, без нашего прямого «вмешательства».

Обновление класса Core

<?php
// Файл modules/core/core.php

defined("LEZH") || exit("Доступ к файлу запрещен");

/**
 * Класс ядра сайта будет статическим
 */
class Core 
{
	// Конфигурации чего-либо на нашем сайте
	static protected $_config = array();
	
	/**
	 * Инициализирует ядро сайта
	 * ничего не возвращает
	 */
	static public function init()
	{
		// Регистрируем функции
		Core::_registerFunctions();
		
		// Получаем конфигурацию подключения к СУБД
		// Данные для подключения хранятся внутри каталога core в каталоге config
		static::$_config["database"] = Core_Config::instance()->get("database");
		
		// Подключаемся к СУБД
		Core_Database::instance()
			->setConfig(static::$_config["database"])
			->connect();
			
		// Стартуем сессию
		Core_Session::start();
	}
	
	/**
	 * Регистрируем пользовательские функции
	 * @param string $class путь к файлу с определением класса
	 */
	static protected function _registerFunctions()
	{
		// Реализация метода _autoload
		spl_autoload_register(array("Core", "_autoload"));
	}
	
	/**
	 * Подключает пользовательские классы
	 */
	static public function _autoload($class)
	{
		// Получаем путь к файлу с определением класса
		$path = static::getClassPath($class);
		
		// Подключаем файл с определением класса
		require_once($path);
	}
	
	/**
	 * Получает путь к файлу с определением класса
	 */
	static public function getClassPath($class)
	{
		// Разбираем путь к файлу
		$aPathToClass = explode("_", $class);
		
		// Если имя начиналось с Core, убираем его из массива
		if (strtolower($aPathToClass[0]) == "core")
		{
			array_shift($aPathToClass);
		}
		
		// Последним элементом массива должно быть имя файла
		$sFileName = strtolower(array_pop($aPathToClass));
		
		// Если нет имени файла, ничего не делаем
		if (empty($sFileName))
		{
			return;
		}
		
		// Если есть еще элементы пути к файлу, собираем их
		$sPathToFile = CORE_DIR;
		if (!empty($aPathToClass))
		{
			// Если есть только один элемент пути
			if (count($aPathToClass) == 1)
			{
				$sPathToFile .= strtolower($aPathToClass[0]) . DIRECTORY_SEPARATOR;
			}
			else
			{
				$sPathToFile .= strtolower(implode(DIRECTORY_SEPARATOR, $aPathToClass)) . DIRECTORY_SEPARATOR;
			}
		}
		
		// Если такой файл существует
		if (is_file($sPathToFile . $sFileName . ".php"))
		{
			$path = $sPathToFile . $sFileName . ".php";
		}
		// Если такого файла нет, проверяем, существует ли файл описания модели
		elseif (is_file($sPathToFile . $sFileName . DIRECTORY_SEPARATOR . "model.php"))
		{
			$path = $sPathToFile . $sFileName . DIRECTORY_SEPARATOR . "model.php";
		}
		else
		{
			throw new Exception("Не существует файла с определением класса <strong>" . $class . "</strong>");
		}
		
		// Возвращаем путь к файлу
		return $path;
	}
	
	/**
	 * Создает экземпляр указанного класса
	 */
	static public function factory($class, $iPrimaryKey = 0)
	{
		// Получаем путь к файлу
		$path = static::getClassPath($class);
		
		// Проверяем, существует ли класс с указанным именем
		// попробуем дополнить имя класса 
		if (!class_exists($class))
		{
			$class = $class . "_Model";
		}
		
		return new $class($iPrimaryKey);
	}
}
?>

В классе Core мы переписали реализацию метода init(). Здесь именно таким образом мы получаем параметры конфигурации подключения с СУБД. А выше, там где нам потребовались параметры конфигурации для подключения капчи Google, мы, воспользовавшись кодом Core_Config::instance()->get("captcha"), получили содержимое файла /modules/core/config/captcha.php. И это будут не единственные параметры конфигурации, которые мы будем запрашивать подобным образом.

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

Класс для работы с сессиями

<?php
// Файл modules/core/session.php

defined("LEZH") || exit("Доступ к файлу запрещен");

/**
 * Класс для работы с сессиями
 */
class Core_Session
{
	// Время устаревания сессии в секундах
	static protected $_sessionTime = 180;
	
	/**
	 * Стартует сессию
	 */
	static public function start()
	{
		// Если сессия еще не запущена
		if (session_status() !== PHP_SESSION_ACTIVE)
		{
			// Вызываем метод изменения идентификатора сессии
			static::sessionRegenerateId();
		}
		
		// Не используем слишком старые идентификаторы сессии
		if (!empty($_SESSION["deleted_time"]) && $_SESSION["deleted_time"] < (time() - static::$_sessionTime))
		{
			// Удаляем устаревшую сессию
			session_destroy();
			
			// Меняем идентификатор сессии
			static::sessionRegenerateId();
		}
	}
	
	/**
	 * Обновляет идентификатор сессии
	 */
	static public function sessionRegenerateId($deleteOldSession = FALSE)
	{
		// Если сессия не запущена
		if (session_status() !== PHP_SESSION_ACTIVE)
		{
			// Запускает сессию
			session_start();
		}
		
		// Устанавливаем время отсчета для удаления сессии
		$_SESSION["deleted_time"] = time();
		
		// Обновляем идентификатор сессии
		session_regenerate_id($deleteOldSession);
		
	}
}
?>
При написании реализации класса Core_Session были применены рекомендации по безопасности сессий от разработчиков языка программирования PHP.

Теперь посмотрим на реализацию класса Core_Config.

<?php
// Файл modules/core/config.php

defined("LEZH") || exit("Доступ к файлу запрещен");

/**
 * Класс для обеспечения работы страницы, по сути, хранит различные данные, являясь глобальным хранилищем
 * Статический класс
 */
class Core_Config
{
	// Полученные конфигурации
	static protected $_cachedConfig = array();
	
	// Путь хранения файлов с параметрами конфигурации
	static protected $_path = CORE_DIR . "config" . DIRECTORY_SEPARATOR;
	
	// Экземпляр класса
	static protected $_instance = NULL;
	
	/**
	 * Возвращает экземпляр класса
	 */
	static public function instance()
	{
		if (is_null(static::$_instance))
		{
			static::$_instance = new self();
		}
		
		return static::$_instance;
	}
	
	/**
	 * Получает указанную конфигурацию
	 */
	public function get($configName)
	{
		// Если ранее уже была получена конфигурация, вернем её
		if (array_key_exists($configName, static::$_cachedConfig))
		{
			return static::$_cachedConfig[$configName];
		}
		
		// Проверяем, есть ли такая конфигурация
		$sFileName = strtolower(trim($configName)) . ".php";
		
		if (is_file(static::$_path . $sFileName))
		{
			// Сохраняем конфигурацию
			static::$_cachedConfig[$configName] = include_once(static::$_path . $sFileName);
		}
		
		// Возвращаем вызову метода полученную конфигурацию
		return static::$_cachedConfig[$configName];
	}
}
?>

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


Сайт принадлежит ООО Группа Ралтэк. 2014 — 2021 гг