Пример программной структуры сайта

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

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

Данная схема может не распространяться на некоторые внешние скрипты, типа форумов, досок объявлений, которые не способны работать таким образом. Для них нужно настроить исключения.

2. Страницы сайта рекомендуется объединить в разделы и подразделы, сайт, таким образом, будет иметь древовидную структуру. Логично распространить ее и на адреса страниц, ведь пользователю понятнее "папочная" структура ссылок, а не строки с параметрами, типа http://www.mysite.ru/?acticle_id=5. Также, в адресе лучше использовать слова, отражающие название статьи  (возможно, в транслитерации), это полезно для SEO. Поэтому ссылки у нас будут выглядеть так:

http://www.mysite.ru/acticles/ – список статей

http://www.mysite.ru/acticles/koe-chto-o-pitanii-homyachkov/ – конкретная статья

http://www.mysite.ru/new/ – список новостей

http://www.mysite.ru/new/5/ – конкретная новость, можно вместо идентификатора "5" использовать и заголовок новости, в этом случае придется следить за их уникальностью

3. Css- и javascript-код нужно стараться максимально вынести во внешние файлы. Это поможет разделить структуру сайта на логические части, уменьшить трафик и упростить его индексацию поисковыми роботами.

4. Весь вывод в браузер пользователя должен происходить в самом конце сценария, уже после формирования контента и завершения работы с БД. Для этого можно было бы собирать вывод в строку, однако, это не очень удобно, поэтому лучше перед формированием контента включить буферизацию вывода запуском функции ob_start(), а после сохранить все содержимое буфера в переменную вызовом ob_get_clean().

Итак, приступим.

Создадим в корне сайта файл .htaccess

код скопирован в буфер
  1. #устанавливаем кодировку сайта - в данном случае utf-8  
  2. AddType "text/html; charset=utf-8" .html .htm .shtml
  3. AddDefaultCharset utf-8
  4. #запрещаем магические кавычки
  5. php_flag magic_quotes_gpc 0
  6. php_flag magic_quotes_runtime 0
  7. #включаем mod_rewrite
  8. RewriteEngine on
  9. Options +FollowSymlinks
  10. #перечисляем правила рерайта ссылок - каждое с новой строки
  11. RewriteRule ^[a-z,0-9,_,/,\-,:]*$ index.php #строки из этих символов передаем на обработку index.php
  12. RewriteRule ^index.php$ - [L] #как только дошли до index.php, обрабатываем обычным способом
  13. RewriteRule ^forum*$ - [L] #исключение для форума
  14. RewriteRule ^.*\.(php|html).*$ index.php  #запрещаем запуск файлов .php и .html
  15. #устанавливаем страницы ошибок
  16. ErrorDocument 404 /index.php?error=404
  17. ErrorDocument 403 /index.php?error=403
  18. ErrorDocument 500 /index.php?error=500

Теперь поясним, что здесь, что.

код скопирован в буфер
  1. RewriteEngine on
  2. Options +FollowSymlinks

Эти две строки инициируют работу mod_rewrite.

код скопирован в буфер
  1. RewriteRule ^[a-z,0-9,_,/,\-,:]*$ index.php

Правило выше означает, что любая строка в url после корня, состоящая из букв латинского алфавита, цифр, символов "_", "/" ,"-", ":" передается на обработку index.php. Предполагается, что все ссылки у нас будут состоять из этих символов. Символ точка среди них не указываем, чтобы обработка файлов, например robots.txt, производилась обычным способом

код скопирован в буфер
  1. RewriteRule ^index.php$ - [L]

Это правило указывает, что если в url после корня оказалось "index.php" – рерайта ссылки не происходит, а обработка передается индексному файлу. Таким образом, параметр [L] выводит строку из под действия mod_rewrite

код скопирован в буфер
  1. RewriteRule ^forum*$ - [L]

Допустим, мы хотим настроить исключение для запуска форума. Если url после корня начинается с "forum", запрещаем рерайт (как и в предыдущем правиле используем параметр [L]) – обработка осуществляется обычным способом в зависимости от того, что идет дальше в строке ссылки.

код скопирован в буфер
  1. RewriteRule ^.*\.(php|html).*$ index.php

Данное правило запрещает выполнение файлов с расширением .php и .html, оно, как и раньше, передается файлу index.php. Поэтому при запуске /articles.php даже если такой файл существует, будет исполнен index.php и как показано дальше выведется ошибка 404. Это нужно, чтобы исключить возможность запуска пользователем промежуточных файлов и, таким образом, повысить надежность сайта.

код скопирован в буфер
  1. ErrorDocument 404 /index.php?error=404
  2. ErrorDocument 403 /index.php?error=403
  3. ErrorDocument 500 /index.php?error=500

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

Теперь разберемся с организацией файлов на сервере. Пускай все общие файлы у нас лежат в папке /common/. Директория /common/scripts/ будет хранить файл функций phpfuncs.php и конфигурационный файл config.php, директория /common/html/templates/ – все html-шаблоны, /common/html/сss/ и /common/html/js/ соответственно css- и js- файлы. В папке /sections/ у нас будут директории разделов, например, /sections/articles/ станет директорией с разделом "Статьи".

Выделим три глобальные переменные-массива: $CONFIG – конфигурация сайта, $SCRIPT – вся информация, касающаяся текущего сценария, и $USER – массив данных пользователя, который запускает сценарий. К сожалению, в php нет возможности централизованно определить область видимости переменной, поэтому нам придется объявлять их глобальными в каждой функции, где они могут понадобиться при помощи оператора global.

Создадим в корне файл /index.php. Далее все понятно из комментариев, объясню только основные моменты. В index.php мы подключаем файлы phpfuncs.php и config.php, устанавливаем параметры кодировки, стартуем сессию, подключаемся к БД, идентифицируем пользователя, беря информацию о нем из баз данных и формируем контент $SCRIPT['html_content']. В самом конце идет запуск функции завершения сценария, которая осуществляет вывод.

код скопирован в буфер
  1. <?
  2. // подключение файла конфигураций, формирующего массив $CONFIG
  3. require dirname(__FILE__).'common/scripts/config.php';
  4. // подключение файла, содержащего функции
  5. require $CONFIG['site_dir'].'common/scripts/phpfuncs.php';
  6. // подключаемся к БД
  7. dbConnect();
  8. // опции кодировки
  9. mb_internal_encoding('UTF-8');
  10. mb_regex_encoding('UTF-8');
  11. // устанавливаем локаль
  12. setlocale(LC_ALL, 'Russian_Russia.65001');
  13. // инициируем старт сессии на всех поддоменах
  14. ini_set('session.cookie_domain', $CONFIG['common_cookie_domain']);
  15. session_start();
  16. // идентифицируем пользователя - создаем глобальную переменную $USER
  17. $user_id = (int)$_COOKIE['user_id'];
  18. $password = $_COOKIE['password'];
  19. $db_password = $user_id ? dbFetchResult("SELECT password FROM users WHERE id=$user_id") : '';
  20. // зарегистрированный пользователь
  21. if ($user_id && $db_password == md5($CONFIG['common_cookie_login_seed'].$password))
  22.     $USER = dbFetchString("SELECT * FROM users WHERE id=".$_COOKIE['user_id']);
  23. // гость
  24. else $USER = array();
  25. // ссылка от корня, по которой обратился пользователь
  26. // на некоторых серверах $_SERVER['REQUEST_URI'] - это ссылка от корня, а на некоторых полная ссылка, учитываем это
  27. $SCRIPT['root_url'] = mb_substr($_SERVER['REQUEST_URI'], 0, 4) == 'http' ? mb_strstr(mb_substr(mb_strstr($_SERVER['REQUEST_URI'], '//'), 2), '/') : $_SERVER['REQUEST_URI'];
  28. // полная ссылка, по которой обратился пользователь
  29. $SCRIPT['this_url'] = 'http'.($_SERVER['HTTPS']?'s':'').'://'.$_SERVER['HTTP_HOST'].$SCRIPT['root_url'];
  30. // определяем заголовки - части ссылки, разделенные символом "/"
  31. $SCRIPT['headers'] = explode('/', $SCRIPT['root_url']);
  32. // подключаемый html-шаблон по умолчанию
  33. $SCRIPT['html_template'] = 'main.php'; 
  34. // все разделы, существующие на сайте
  35. $sections = array('main', 'articles');
  36. // имя раздела, контент каждого раздела у нас формируют файлы /sections/<имя_раздела>/<имя_раздела>.php
  37. $section = in_array($SCRIPT['headers'][1], $sections) ? $SCRIPT['headers'][1] : 'main';
  38. // если есть параметр $_GET['error'], значит это страница с ошибкой - завершаем работу
  39. if ($_GET['error']) PageError($_GET['error']);
  40. // контент
  41. ob_start(); // включаем буферизацию вывода
  42. require $CONFIG['site_dir'].'sections/'.$section.'/'.$section.'.php';
  43. $SCRIPT['html_content'] = ob_get_clean(); // теперь весь контент сохранен в переменную $SCRIPT['html_content'], буферизация выключена
  44. // добавление слеша к псевдопапкам в ссылках, таким образом, /articles редиректится на /articles/
  45. if (!$_GET['error'] && $SCRIPT['this_url'][mb_strlen($SCRIPT['this_url'])-1] != '/'
  46. && !mb_strstr($SCRIPT['root_url'], '.') && !mb_strstr($SCRIPT['root_url'], '?'))
  47. {
  48.     array_push($SCRIPT['http_headers'], 'HTTP/1.1 301 Moved Permanently',
  49.         'Location: '.$SCRIPT['this_url'].'/');
  50.     $SCRIPT['html_template'] = '';
  51. }
  52. // завершаем выполнения сценария
  53. ScriptEnd();
  54. ?>

Конфигурационный файл /common/scripts/config.php, в нем происходит формирование глобальной переменной $CONFIG.

код скопирован в буфер
  1. <?
  2. // опции БД
  3. $CONFIG['db_host'] = 'localhost'; // имя хоста
  4. $CONFIG['db_username'] = 'webuser'; // имя пользователя
  5. $CONFIG['db_password'] = '12345abc'; // пароль пользователя
  6. $CONFIG['db_base'] = 'mysite'; // имя базы данных
  7. // строка для шифрования пароля при помощи хеш-функции
  8. $CONFIG['common_cookie_login_seed'] = '%whj24a>hnsn67rel;';
  9. // домен, на котором стартуем сессию, точка в начале нужна, чтобы старт произошел на всех поддоменах домена mysite.ru
  10. $CONFIG['common_cookie_domain'] = '.mysite.ru';
  11. // путь к корню сайта
  12. $CONFIG['site_dir'] = dirname(dirname(dirname(__FILE__))).'/';
  13. // url главной страницы
  14. $CONFIG['site_url'] = 'http://www.mysite.ru/';
  15. ?>

Файл функций /common/scripts/phpfuncs.php, содержит также функции работы с базами данных. Родные функции php для работы с БД, например, mysql_query, лучше запускать не напрямую, а через "обертку". Это позволит легко переключаться при необходимости из режима mysql в mysqli и обратно, считать количество запросов в сценарии для статистики, выводить ошибочные запросы на экран, если включен режим отладки и т.д.

код скопирован в буфер
  1. <?
  2. // вывод страницы ошибки
  3. function PageError($error = 404)
  4. {
  5.     global $CONFIG, $SCRIPT;
  6.     ob_get_clean();
  7.     if ($error == 404)
  8.     {
  9.         $SCRIPT['http_headers'][] = 'HTTP/1.1 404 Not Found';
  10.         $SCRIPT['html_title'] = 'Страница не найдена';
  11.         $SCRIPT['html_content'] = '<p>Страница с таким адресом на сайте не существует, возможно она была перенесена или вы перешли по некорректной ссылке. Попробуйте воспользоваться <a href="'.$CONFIG['site_url'].'main/sitemap/">картой сайта</a>, чтобы найти нужную страницу.</p>';     
  12.     }
  13.     elseif ($error == 403)  // доступ запрещен
  14.     {
  15.         $SCRIPT['http_headers'][] = 'HTTP/1.1 403 Forbidden';
  16.         $SCRIPT['html_title'] = 'Доступ запрещен';
  17.         $SCRIPT['html_content'] = '<p>У вас нет прав доступа для просмотра содержимого данной страницы. Если вы забрели сюда по ошибке, попробуйте воспользоваться <a href=""'.$CONFIG['site_url'].'main/sitemap/">картой сайта</a> или свяжитесь с администратором.</p>';
  18.     }
  19.     elseif ($error == 500)  // ошибка в скрипте
  20.     {
  21.         $SCRIPT['http_headers'][] = 'HTTP/1.1 500 Internal Server Error';
  22.         $SCRIPT['html_title'] = 'Ошибка на сервере';
  23.         $SCRIPT['html_content'] = '<p>Из-за ошибки на сервере невозможно отобразить страницу. В самое ближайшее время она будет исправлена.</p>';     
  24.     }
  25.     $SCRIPT['html_template'] = 'main.php';
  26.    
  27.     ScriptEnd();
  28. }
  29. // завершение выполнения сценария
  30. function ScriptEnd()
  31. {
  32.     global $CONFIG, $SCRIPT, $USER;
  33.    
  34.     $SCRIPT['http_headers'][] = 'X-Powered-By: MYSite Runner'; // кем сделан сайт
  35.     // выводим все HTTP-заголовки
  36.     for ($i=0;$i<count($SCRIPT['http_headers']);$i++) header($SCRIPT['http_headers'][$i]);
  37.    
  38.     // выводим заполненный макет страницы
  39.     if ($SCRIPT['html_template']) require $CONFIG[site_dir].'common/html/templates/'.$SCRIPT['html_template'];
  40.    
  41.     dbDisconnect();
  42.     exit();
  43. }
  44. //////////////////////////////////////
  45. // ФУНКЦИИ РАБОТЫ С БД
  46. //////////////////////////////////////
  47. // подсоединение к БД
  48. function dbConnect()
  49. {
  50.     global $CONFIG;
  51.     // подключаемся к БД
  52.     mysql_connect($CONFIG['db_host'], $CONFIG['db_username'], $CONFIG['db_password']);
  53.     // выбираем базу данных
  54.     mysql_select_db($CONFIG['db_base']);   
  55.     // ставим кодировку БД - Юникод в данном случае
  56.     mysql_query("SET NAMES utf8");
  57. }
  58. // закрытие соединения к БД
  59. function dbDisconnect()
  60. {
  61.     mysql_close();
  62. }
  63. // запрос к БД
  64. function dbQuery($query)
  65. {
  66.     global $USER; // $USER['is_test'] - включен ли режим отладки?
  67.     $result = mysql_query($query);
  68.     if (!$result && $USER['is_test']) echo '<br>'.mysql_error().'<br><b style="color: red">Запрос: </b>'.$query.'<br>';
  69.     return $result;
  70. }
  71. // получение записи
  72. function dbFetchArray($result, $param = 'assoc')
  73. {
  74.     $param = ($param == 'both') ? MYSQL_BOTH : ($param == 'num' ? MYSQL_NUM : MYSQL_ASSOC);
  75.     return mysql_fetch_array($result, $param);
  76. }
  77. // возвращение единственного поля результата
  78. function dbFetchResult($query)
  79. {
  80.     $arr = dbFetchArray(dbQuery($query));
  81.     return @reset($arr);
  82. }
  83. // возвращение единственной записи результата (первой записи)
  84. function dbFetchString($query, $param = 'assoc')
  85. {
  86.     $result = dbQuery($query);
  87.     return dbFetchArray($result, $param);
  88. }
  89. ?>

Файл /sections/articles/articles.php формирует контент раздела "Статьи", а также определяет переменные:

$SCRIPT['html_title']  – html-заголовок страницы,

$SCRIPT[‘html_description’] – содержимое метатега description,

$SCRIPT['html_keywords'] – содержимое метатега keywords,

$SCRIPT['html_revisit'] – частота поcещения страницы поисковыми роботами

$SCRIPT['html_robots'] – правила индексации страницы поисковыми роботами

Эти переменные потом будут подставлены в html-шаблон.

код скопирован в буфер
  1. <?
  2. // конкретная статья
  3. if ($SCRIPT['headers'][2])
  4. {
  5.     // ищем статью в БД по имени, содержащемся в ссылке
  6.     $arr = dbFetchString("SELECT * FROM articles WHERE name='".$SCRIPT['headers'][2]."'");
  7.     if ($arr)
  8.     {
  9.         $SCRIPT['html_title'] = $arr['title'];
  10.         $SCRIPT['html_keywords'] = $arr['keywords'];
  11.         // поисковой робот будет заходить на страницу статьи каждые 5 дней
  12.         $SCRIPT['html_revisit'] = '5';
  13.         $SCRIPT['html_robots'] = 'index, follow'; // разрешаем индексацию
  14.         echo '<h1>'.$arr['title'].'</h1>'.$arr['content'];
  15.     }
  16.     else PageError(404); // такой статьи не существует - выводим страницу с ошибкой
  17. }
  18. else
  19. // список статей
  20. {
  21.     // пускай поисковой робот заходит на эту страинцу каждый день
  22.     $SCRIPT['html_revisit'] = '1';
  23.     $SCRIPT['html_robots'] = 'index, follow'; // разрешаем индексацию
  24.     $result = dbQuery("SELECT * FROM articles ORDER BY title");
  25.     while ($arr = dbFetchArray($result))
  26.         echo '<a href="/articles/'.$arr['name'].'/">'.$arr['title'].'</a><br>';
  27. }
  28. ?>

Файл /common/html/templates/main.php – основной html-шаблон сайта, можно сделать также шаблоны для всплывающих окон window.php, шаблон сообщения message.php и т.д. В шаблоны подставляются переменные $SCRIPT['html_title'] , $SCRIPT['html_description'], $SCRIPT['html_keywords'], $SCRIPT['html_revisit'], $SCRIPT['html_robots'] , $SCRIPT['html_content'].

код скопирован в буфер
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  2. <html>
  3. <head>
  4. <title><?=$SCRIPT['html_title']?></title>
  5. <meta name="description" content="<?=$SCRIPT['html_description']?>">
  6. <meta name="keywords" content="<?=$SCRIPT['html_keywords']?>">
  7. <meta name="revisit" content="<?=$SCRIPT['html_revisit']?> days">
  8. <meta name="robots" content="<?=$SCRIPT['html_robots']?>">
  9. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  10. </head>
  11. <body>
  12. <div id="menu">
  13.     <a href="/">Главная</a><br>
  14.     <a href="/articles/">Статьи о хомячках</a><br>
  15.     <a href="/forum/">Форум</a><br>
  16. </div>
  17. <div id="content">
  18. <?=$SCRIPT['html_content']?>
  19. </div>
  20. </body>

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

Теги: PHP
Другие готовые решения...
4 декабря 2013

Добавление комментария

Имя:
Преобразовывать в тексте комментария смайлики в графические иконки
Защитный код:
Защитный код
 
 
                                                                                                                                                                                                       
Copyright © 2019 ScriptScript.ru, по всем вопросам пишите нам через форму обратной связи.
364319675