Чтение и запись символов Unicode

Задача
Необходимо читать символы в кодировке Unicode из файла, базы данных или формы; или требуется записать символы в кодировке Unicode.

Решение
Однобайтовые символы в кодировке ISO-8859-1 преобразуются в код UTF-8 с помощью функции utf8_encode():

print utf8_encode('Kurt Gdel is swell.');


Функция utf8_decode() используется для преобразования символов в кодировке UTF-8 в однобайтовые символы в кодировке ISO-8859-1:

print utf8_decode("Kurt G\xc3\xb6del is swell.");


Обсуждение
Существует 256 ASCII-символов. Символы с кодами от 0 до 127 стандартизованы: управляющие символы, буквы и числа, символы пунк-туации. Однако существуют различные правила для символов с кодами от 128 до 255. Одна из кодировок, называемая ISO-8859-1, охватывает символы, необходимые для письма на большинстве европейских
языков, например

Добавлено: 22 Июля 2018 15:37:47 Добавил: Андрей Ковальчук

Чтение и запись cookies на jQuery & PHP

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

Но эта простота и является также одной из причин его широкого внедрения в Интернете.

Однако, существует способ сохранять информацию между запросами в виде cookies. Таким образом, у вас появится эффективное управление сессиями и данными.

Есть два способа работы с cookies – на стороне сервера (PHP, ASP и т.д.) и на стороне клиента (JavaScript).

В этой статье мы рассмотрим, как можно управлять cookies с помощью PHP и JavaScript.

Cookies и PHP
Запись cookies
Чтобы создать cookie в PHP, вы должны использовать функцию setcookie. Она принимает ряд параметров (все, кроме первого, являются необязательными и могут быть опущены):

setcookie(
	'pageVisits',				// Имя cookie, обязательно
	$visited,					// Значение cookie
	time()+7*24*60*60,			// Срок действия
	'/',						// Каталог
	'demo.tutorialzine.com'		// Домен
);

Если вы передаете 0, как срок действия (по умолчанию), cookie будет потерян при перезагрузке браузера. Параметр “/” указывает, что cookie будет доступен для всех директорий домена (при желании, можно связать cookie с одним каталогом, например с /admin/ в качестве параметра).

Есть два дополнительных параметра, которые вы могли бы использовать в функции, но, которые здесь не приводятся. Они могут принимать, только булевые значения — истина или ложь.

Первый означает, что cookie будет передан только через защищенное соединение HTTPS, а второй, что cookie не будет доступен через JavaScript (введено в PHP 5.2).

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

Чтение cookies
Чтение cookie с помощью PHP намного проще. Все cookies, которые были переданы скрипту, доступны в суперглобальном массиве $_COOKIE.

В нашем примере, чтобы прочитать cookie, мы написали следующий код:
$visits = (int)$_COOKIE['pageVisits']+1;
echo "You visited this site: ".$visits." times";

Стоит отметить, что запись cookies через setcookie доступна в массиве $ _COOKIE на next page load.
Запомните это.

Удаление cookies
Чтобы удалить cookies, просто используйте функцию setcookie с параметром срока действия прошедшего времени.
setcookie(
	'pageVisits',
	$visited,
	time()-7*24*60*60,		//Одна неделя назад и cookie будет удален.
	'/',
	'demo.tutorialzine.com'
);

Cookies и jQuery
Чтобы использовать cookies с jQuery, вам необходим специальный .

Запись cookies
Запись cookies с плагином Cookie интуитивно понятна:
$(document).ready(function(){

	$.cookie("kittens","Seven Kittens");

	$.cookie("demoCookie",text,{expires: 7, path: '/', domain: 'demo.tutorialzine.com'});
});

Чтение cookies
Чтение cookie еще проще. Просто вызовите функцию $.cookie() с параметром имени, значение cookie будет возвращено:
$(document).ready(function(){

	var str = $.cookie("kittens");
});

Удаление cookies
Чтобы удалить куки, снова используйте функцию $.cookie(), но в качестве второго параметра укажите значение null.
$(document).ready(function(){

	var str = $.cookie("kittens",null);
});

Заключение
В этой статье мы рассмотрели создание и чтение данных cookie. Стоит отметить, что Вы не должны хранить в cookies какой-либо конфиденциальной информации, такой как, имена пользователей и пароли, так как они передаются, как обычные заголовки на каждой странице, и могут быть легко прочитаны злоумышленниками.

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

Добавлено: 04 Мая 2018 08:38:00 Добавил: Андрей Ковальчук

Чтение значений cookie

PHP

Задача
Необходимо прочитать ранее установленное значение cookie.

Решение
Загляните в суперглобальный массив $_COOKIE:

if (isset($_COOKIE['flavor'])) {
print "You ate a ".$_COOKIE['flavor']." cookie.";
}


Обсуждение
Значение cookie не доступно в массиве $_COOKIE в пределах того самого запроса, в котором cookie установлен. Другими словами, функция setcookie() не изменяет значения массива $_COOKIE. Однако при всех последующих запросах каждый установленный ранее cookie помещаетсяв массив $_COOKIE. Кроме того, если опция register_globals установлена в on, то значение cookie присваивается глобальной переменной.

Когда броузер посылает cookie обратно на сервер, то он посылает только значение. Невозможно получить доступ к домену, пути, времени истечения срока действия или статусу безопасности cookie через массив $_COOKIE, поскольку броузер не посылает его серверу.

Чтобы вывести на печать имена и значения всех cookies, посланных в текущем запросе, выполните цикл по массиву $_COOKIE:

foreach ($_COOKIE as $cookie_name => $cookie_value) {
print "$cookie_name = $cookie_value
";
}

Добавлено: 21 Июля 2018 16:26:43 Добавил: Андрей Ковальчук

Чтение записей с шаблоном-разделителем

PHP

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

Решение
Прочитайте весь файл в строку, а затем выделите из нее регулярное выражение:

$filename = '/path/to/your/file.txt';
$fh = fopen($filename, 'r') or die($php_errormsg);
$contents = fread($fh, filesize($filename));fclose($fh);
$records = preg_split('/[0-9]+\) /', $contents);


Обсуждение
Этот пример разделяет нумерованный список на части и помещает отдельные элементы списка в элементы массива. Поэтому если есть такой список:

1) Go
..
del
2) Escher
3) Bach

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

Array
(
[0] =>
=> Go
..
del
=> Escher
=> Bach
)


Это можно рассматривать как дополнительную функциональность, а не как ошибку, поскольку теперь n-й элемент массива содержит n-й элемент списка. Но чтобы сделать массив более компактным, можно удалить первый элемент:

$records = preg_split('/[0-9]+\) /', $contents);
array_shift($records);


Можно также выполнить другое преобразование, а именно удалить из всех элементов символ новой строки и заменить его пустой строкой:

$records = preg_split('/[0-9]+\) /', str_replace("\n",'',$contents));
array_shift($records);

PHP не позволяет изменить при вводе записей разделитель на что-нибудь другое, кроме символа новой строки, поэтому данный прием также полезен при поиске отдельных записей, разделенных переводами строки. Однако если вы просто разделяете записи внутри одной строки, то следует предпочесть более эффективную функцию explode(), а не
preg_split().

Добавлено: 22 Июля 2018 15:50:07 Добавил: Андрей Ковальчук

Чтение данных из стандартного ввода

Получить данные из стандартного потока ввода несложно. Мы уже делали это при помощи оператора <STDIN>.1 Выполнение этого оператора в скалярном контексте дает следующую строку входных данных:

$line = <STDIN>; # Прочитать следующую строку
chomp($line); # Удалить завершитель
chomp($line = <STDIN>); # То же самое, но более идиоматично


При достижении конца файла оператор построчного ввода возвращает undef; это обстоятельство часто используется для выхода из цикла:

while (defined($line = <STDIN>)) {
print "I saw $line";
}


В первой строке происходит много всего: мы читаем входные данные в переменную, проверяем ее на определенность, и если она определена (то есть если цикл еще не достиг конца входных данных), выполняется тело цикла while. Таким образом, в теле цикла переменная $line1 по очереди содержит каждую строку цикла. Подобная операция выполняется очень часто; вполне естественно, что в Perl для нее была создана сокращенная форма записи, которая выглядит так:

while (<STDIN>) {
print "I saw $_";
}


Под это сокращение Ларри задействовал заведомо бесполезную синтаксическую конструкцию. Буквально здесь говорится следующее: «Прочитать строку входных данных и проверить ее на истинность (обычно условие выполняется). Если строка истинна, войти в цикл while и отбросить прочитанную строку!» Ларри знал, что это бесполезная операция; никому не придет в голову выполнять ее в реальной программе Perl. Ларри взял этот бесполезный синтаксис и сделал его полезным. По сути эта конструкция означает, что Perl должен сделать то, что мы видели в более раннем цикле: прочитать входные данные в переменную и (при условии, что результат определен, то есть мы не достигли конца файла) войти в цикл while. Но вместо того чтобы сохранять результат в $line, Perl использует переменную по умолчанию $_, как если бы запись выглядела так:

while (defined($_ = <STDIN>)) {
print "I saw $_";
}


Прежде чем двигаться дальше, вы должны предельно четко понять: это сокращение работает только в такой записи. Если оператор построчного ввода находится в другом месте (например, в отдельной команде), строка не будет прочитана в переменную $_ по умолчанию. Сокращение работает только тогда, когда условие цикла while2 не содержит ничего, кроме оператора построчного ввода. Если в условии появится что-нибудь еще, сокращение работать не будет. В остальном оператор построчного ввода (<STDIN>) никак не связан с «главной» переменной по умолчанию Perl $_. Но именно в данном конкретном случае ввод сохраняется в этой переменной.

С другой стороны, при выполнении оператора построчного ввода в списочном контексте вы получаете все (оставшиеся) строки ввода в виде списка – каждый элемент соответствует одной строке:

foreach (<STDIN>) {
print "I saw $_";
}


Еще раз подчеркнем: оператор построчного ввода никак не связан с «главной» переменной по умолчанию. Но в данном случае в цикле foreach по умолчанию используется управляющая переменная $_. Таким образом, в этом цикле в переменную $_ последовательно заносятся все строки входных данных. Звучит знакомо и не без оснований: это же поведение мы встречали в цикле while. Разве не так? Различия скрыты внутри. В цикле while Perl читает строку входных данных, помещает ее в переменную и выполняет тело цикла, после чего возвращается за следующей строкой ввода. Но в цикле foreach оператор построчного ввода используется в списочном контексте (так как для выполнения цикла foreach необходим список). Таким образом, перед началом выполнения должны быть прочитаны все входные данные. Попробуйте прочитать 400Qмегабайтный журнал веб-сервера и вы мгновенно поймете эти различия! Обычно подобный код рекомендуется использовать в сокращенной записи с циклом while, чтобы входные данные обрабатывались по строкам (если это возможно).

Добавлено: 20 Июля 2018 08:13:32 Добавил: Андрей Ковальчук

Чтение ввода с клавиатуры

PHP

Задача
Необходимо прочитать клавиатурный ввод пользователя.

Решение
Для этого нужна функция fopen() со специальным именем файла php://stdin:

print "Type your message. Type '.' on a line by itself when you're done.\n";
$fh = fopen('php://stdin','r') or die($php_errormsg);
$last_line = false; $message = '';
while (! $last_line) {
$next_line = fgets($fp,1024);
if (".\n" == $next_line) {
$last_line = true;
} else {
$message .= $next_line;
}
}
print "\nYour message is:\n$message\n";


Если инсталлировано расширение Readline, вызывайте функцию readline():

$last_line = false; $message = '';
while (! $last_line) {
$next_line = readline();
if ('.' == $next_line) {
$last_line = true;
} else {
$message .= $next_line."\n";
}
}
print "\nYour message is:\n$message\n";


Обсуждение
Получив с помощью функции fopen() дескриптор файла, указывающего на stdin, для обработки ввода можно использовать все стандартные функции чтения файла (fread(), fgets() и т. д.). В данном решении задействована функция fgets(), за один раз возвращающая одну строку ввода. Если вызывается функция fread(), то ввод все равно должен завершаться символом новой строки, чтобы заставить функцию fread() вернуть значение. Например, если выполнить код:

$fh = fopen('php://stdin','r') or die($php_errormsg);
$msg = fread($fh,4);
print "[$msg]";


и ввести строку tomato, а затем символ новой строки, то на выходе получится строка
. Функция fread(), как указано, забирает из stdin только четыре символа, но ей нужен символ новой строки – как сигнал выхода из ожидания ввода с клавиатуры. Расширение Readline предоставляет интерфейс к библиотеке GNU Readline. Функция readline() возвращает строку за один раз без символа новой строки в конце. Readline позволяет редактировать строки в стиле Emacs и vi. Ее можно применять для хранения истории введенных ранее команд:

[PHP]$command_count = 1;
while (true) {
$line = readline("[$command_count]--> ");
readline_add_history($line);
if (is_readable($line)) {
print "$line is a readable file.\n";
}
$command_count++;
}


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

Добавлено: 22 Июля 2018 18:50:14 Добавил: Андрей Ковальчук

Чтение RSS-рассылок

PHP

Задача
Необходимо получить RSS-рассылку и просмотреть ее содержание. Это позволит нам включить рассылку новостей с нескольких веб-сайтов в ваше приложение.

Решение
Используйте PEAR-класс XML_RSS. Приведем пример, который читает RSS-рассылку для списка почтовых адресов php.announce:

require 'XML/RSS.php';
$feed = 'http://news.php.net/group.php?group=php.announce&format=rss';
$rss =& new XML_RSS($feed);
$rss->parse();
print "<ul>\n";
foreach ($rss->getItems() as $item) {
print '<li>' . $item['title'] . "</li>\n";
}
print "</ul>\n";


Обсуждение
RSS, что означает RDF Site Summary, является простым в использовании форматом заголовков или синдикации статей, написанным на XML.1 Многие новостные веб-сайты, такие как Slashdot и Meerkat издательства O’Reilly, предоставляют RSS-рассылки, которые обновляются при каждой публикации нового материала. Популярные сетевые издания также включают RSS, а RSS-рассылка в вашем собственном онлайн-дневнике является уже стандартной функциональностью.

Веб-сайт PHP также публикует RSS-рассылки для большинства серверов почтовых рассылок PHP.

Получать и анализировать RSS-рассылку просто:

$feed = 'http://news.php.net/group.php?group=php.announce&format=rss';
$rss =& new XML_RSS($feed);
$rss->parse();


Этот пример присваивает переменной $rss новый объект XML_RSS и передает значение переменной feed в RSS-рассылку новостей php.announce.

Затем рассылка анализируется с помощью функции XML_RSS::parse() и сохраняется в переменной $rss.Впоследствии к элементам RSS обращаются как к ассоциативному массиву, полученному с помощью функции XML_RSS:getItems():

print "<ul>\n";
foreach ($rss->getItems() as $item) {
print '<li>' . $item['title'] . "</li>\n";
}
print "</ul>\n";


Этот цикл foreach создает неупорядоченный список элементов ссылок на статьи так, как показано на рис. 12.1. Помимо обязательных полей title и link, элемент может иметь и необязательное поле description, в котором содержится краткое описание статьи.
Каждый канал также содержит вход с общей информацией о рассылке. Для извлечения данных используем функцию XML_RSS::getChannelInfo():

$feed = 'http://news.php.net/group.php?group=php.announce&format=rss';
$rss =& new XML_RSS($feed);
$rss->parse();
print "<ul>\n";
foreach ($rss->getChannelInfo() as $key => $value) {
print "<li>$key: $value</li>\n";
}
print "</ul>\n";

Добавлено: 22 Июля 2018 13:45:03 Добавил: Андрей Ковальчук

Чтение RSS-новостей. Часть 1

Здравствуйте, давно я не писал в блог ничего, но сами понимаете времени не так много, скоро начну писать о том, как я пытался зарабатывать в сети Интернет, в том числе это будет связано и с программированием. Ну а пока что, я хотел рассказать о том, как можно без сторонних компонентов очень хорошо и быстро прочитать RSS-новости, ну по крайней мере отобразить их названия и ссылку на них. Что нам для этого понадобится из визуальных компонентов:

TListBox — 2 шт
TButton
TEdit

В TEdit мы будет вводить url нашего rss-потока, в 1 TListBox мы будем получать url нашей новости, а во второй TListBox будем получать название нашей новости. Да кстати, если Вы обратили внимание, то в новости присутсвует Часть 1, это означает, что в следующей статье я покажу как можно сделать чтение rss-новостей с помощью парсинга и тогда Вы увидите различия. Конечно все будет зависеть от того на сколько быстрый парсинг новостей, но об этом немного потом. Для начала давайте подключим модули, необходимые нам при работе:

MSXML
Shellapi

MSXML — для чтение rss-потоков, Shellapi — для того, чтобы воспользоваться функцией ShellExecute, которая поможет нам открывать ссылки новостей в браузере. Ну так вроде бы у нас все готово, теперь можем приступать к работе.
Для начала объявим необходимые переменные

Исходный код

var
 CoD:CoDOMDocument;
 XML:DOMDocument;
 ixml:IXMLDOMElement;
 node:IXMLDOMNode;
 url:WideString;
 i:integer;


XML — объект самого DOM документа, ixml — элемент DOM документа, а node — узел DOM документа, то есть узел rss-потока, url — будет хранить адресс нашего rss-потока, а i — счетчик, который понадобится нам для того, чтобы пройтись по всем узлам нашего rss-потока.
Весь код, который я написал на событие OnClick TButton выглядет следующим образом

Исходный код
procedure TForm1.Button1Click(Sender: TObject);
var
 CoD:CoDOMDocument;
 XML:DOMDocument;
 ixml:IXMLDOMElement;
 node:IXMLDOMNode;
 url:WideString;
 i:integer;
begin
   try
    url:=Edit1.Text;
    XML:=CoD.Create;
    XML.async:=False;
    XML.load(url);
    ixml:=XML.documentElement;
    node:=ixml.selectSingleNode('//rss');
    if node.attributes.getNamedItem('version').text<>'2.0' then
     begin
      ShowMessage('Error is version rss');
      exit;
     end;
    Caption:=node.selectSingleNode('//title').text;
    for i:=0 to node.selectnodes('//item').length-1 do
     begin
      ListBox2.Items.Add(node.selectnodes('//item').item[i].childNodes.item[0].text);
      ListBox1.Items.Add(node.selectnodes('//item').item[i].childNodes.item[1].text);
     end;
   except
    on e:Exception do
   end;
end;


Вначале мы создаем наш документ XML, затем задаем как будем грузить в него данные, а с помощью метода load загружаем сам наш поток, причем его можно загрузить не только как url, но и как текстовый файл, либо xml-файл. Далее мы просто перебираем нам нужные узлы в нашем rss-потоке. Как получить нужные узлы Вы увидели, с помощью двойной наклонной черты, а затем само название узла, так можно получить любой узел. В данном примере мы только получаем версию rss, заголов сайта и затем название 10 последних новостей и ссылки на них. Можете поэксперментировать, доработать кое-что, а потом будем сравнивать насколько быстрее работает чтение rss-новостей с помощью обычного парсинга.
С помощью данного примера можно очень легко сделать отличную и очень простую rss-читалку, причем без всяких сторонних компонентов.
Полный исходник можно скачать тут (в исходнике по двойному клику по названию статьи открывается в браузере по умолчанию url статьи)

Добавлено: 01 Марта 2015 10:13:55 Добавил: Андрей Ковальчук

Чтение ID3 тегов и другой информации из mp3 файлов средствами php

PHP

Все ID3 теги (ID3 tag) хранятся в последних 128-ми байтах mp3 файла. Это означает, что их можно читать без специальных компонентов. Распределение информации следующее:

Byte 1-3 = ID 'TAG'
Byte 4-33 = Title
Byte 34-63 = Artist
Byte 64-93 = Album
Byte 94-97 = Year
Byte 98-127 = Comment
Byte 128 = Genre


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

$f = fopen('test.mp3', 'rb');
rewind($f);
fseek($f, -128, SEEK_END);
$tmp = fread($f,128);
if ($tmp[125] == Chr(0) and $tmp[126] != Chr(0)) {
    // ID3 v1.1
    $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
} else {
    // ID3 v1
    $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
}

$id3tag = unpack($format, $tmp);


Результат работы скрипта:

Array ( [TAG] => TAG [NAME] => test [ARTISTS] => oz [ALBUM] => [YEAR] => 2007 [COMMENT] => [GENRENO] => 0 )


Иногда бывает необходимо кроме ID3 tag 'ов узнать и другую инфу об mp3 файле (частоту, bitrate, длительность и др.), я взял за основу библиотеку http://pear.php.net/package/MP3_ID и написал следующую функцию, которая позволяет это сделать. Я не буду ее комментировать, потому что здесь мало программирования, а в основном знание структуры mp3 файла, кто захочет - разберется.

function readframe($file) {
	if (! ($f = fopen($file, 'rb')) ) die("Unable to open " . $file);
    $res['filesize'] = filesize($file);
    do {
        while (fread($f,1) != Chr(255)) { // Find the first frame        
        	if (feof($f))  die( "No mpeg frame found") ;
        }
        fseek($f, ftell($f) - 1); // back up one byte

        $frameoffset = ftell($f);

        $r = fread($f, 4);
        
        $bits = sprintf("%'08b%'08b%'08b%'08b", ord($r{0}), ord($r{1}), ord($r{2}), ord($r{3}));
    } 
	while (!$bits[8] and !$bits[9] and !$bits[10]); // 1st 8 bits true from the while    

    // Detect VBR header
    if ($bits[11] == 0) {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 9; // MPEG 2.5 Mono
        } else {
            $vbroffset = 17; // MPEG 2.5 Stereo
        }
    } else if ($bits[12] == 0) {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 9; // MPEG 2 Mono
        } else {
            $vbroffset = 17; // MPEG 2 Stereo
        }
    } else {
        if (($bits[24] == 1) && ($bits[25] == 1)) {
            $vbroffset = 17; // MPEG 1 Mono
        } else {
            $vbroffset = 32; // MPEG 1 Stereo
        }
    }

    fseek($f, ftell($f) + $vbroffset);
    $r = fread($f, 4);

    switch ($r) {
        case 'Xing':
            $res['encoding_type'] = 'VBR';
        case 'VBRI':
        default:
            if ($vbroffset != 32) {
                // VBRI Header is fixed after 32 bytes, so maybe we are looking at the wrong place.
                fseek($f, ftell($f) + 32 - $vbroffset);
                $r = fread($f, 4);

                if ($r != 'VBRI') {
                    $res['encoding_type'] = 'CBR';
                    break;
                }
            } else {
                $res['encoding_type'] = 'CBR';
                break;
            }

            $res['encoding_type'] = 'VBR';
    }

    fclose($f);

    if ($bits[11] == 0) {
        $res['mpeg_ver'] = "2.5";
        $bitrates = array(
            '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
            '2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
            '3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
                 );
    } else if ($bits[12] == 0) {
        $res['mpeg_ver'] = "2";
        $bitrates = array(
            '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
            '2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
            '3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
                 );
    } else {
        $res['mpeg_ver'] = "1";
        $bitrates = array(
            '1' => array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0),
            '2' => array(0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
            '3' => array(0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0),
                 );
    }
    
    $layer = array(
        array(0,3),
        array(2,1),
              );
    $res['layer'] = $layer[$bits[13]][$bits[14]];
    
    if ($bits[15] == 0) {
        // It's backwards, if the bit is not set then it is protected.
        $res['crc'] = true;
    }

    $bitrate = 0;
    if ($bits[16] == 1) $bitrate += 8;
    if ($bits[17] == 1) $bitrate += 4;
    if ($bits[18] == 1) $bitrate += 2;
    if ($bits[19] == 1) $bitrate += 1;
    $res['bitrate'] = $bitrates[$res['layer']][$bitrate];

    $frequency = array(
        '1' => array(
            '0' => array(44100, 48000),
            '1' => array(32000, 0),
                ),
        '2' => array(
            '0' => array(22050, 24000),
            '1' => array(16000, 0),
                ),
        '2.5' => array(
            '0' => array(11025, 12000),
            '1' => array(8000, 0),
                  ),
          );
    $res['frequency'] = $frequency[$res['mpeg_ver']][$bits[20]][$bits[21]];

    $mode = array(
        array('Stereo', 'Joint Stereo'),
        array('Dual Channel', 'Mono'),
             );
    $res['mode'] = $mode[$bits[24]][$bits[25]];
    
    $samplesperframe = array(
        '1' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 1152
        ),
        '2' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 576
        ),
        '2.5' => array(
            '1' => 384,
            '2' => 1152,
            '3' => 576
        ),
    );
    $res['samples_per_frame'] = $samplesperframe[$res['mpeg_ver']][$res['layer']];

    if ($res['encoding_type'] != 'VBR') {
        if ($res['bitrate'] == 0) {
            $s = -1;
        } else {
            $s = ((8*filesize($file))/1000) / $res['bitrate'];
        }
        $res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengths'] = (int)$s;

        $res['samples'] = ceil($res['lengths'] * $res['frequency']);
        if(0 != $res['samples_per_frame']) {
            $res['frames'] = ceil($res['samples'] / $res['samples_per_frame']);
        } else {
            $res['frames'] = 0;
        }
        $res['musicsize'] = ceil($res['lengths'] * $res['bitrate'] * 1000 / 8);
    } else {
        $res['samples'] = $res['samples_per_frame'] * $res['frames'];
        $s = $res['samples'] / $res['frequency'];

        $res['length'] = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengthh'] = sprintf('%02d:%02d:%02d',floor($s/3600),floor($s/60),floor($s-(floor($s/60)*60)));
        $res['lengths'] = (int)$s;

        $res['bitrate'] = (int)(($res['musicsize'] / $s) * 8 / 1000);
    }
    
    return $res;
}


Результат работы скрипта:

Array ( 
	[filesize] => 1369759 
	[encoding_type] => CBR 
	[mpeg_ver] => 1 
	[layer] => 3 
	[bitrate] => 192 
	[frequency] => 44100 
	[mode] => Stereo 
	[samples_per_frame] => 1152 
	[length] => 00:57 
	[lengthh] => 00:00:57 
	[lengths] => 57 
	[samples] => 2513700 
	[frames] => 2183 
	[musicsize] => 1368000 
)

Добавлено: 27 Февраля 2015 18:14:39 Добавил: Андрей Ковальчук

ЧПУ ссылки на WordPress без плагина

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

Как обычно, чтобы добавить новую функцию в WordPress, вам понадобится файл пользовательских функций function.php. Добавляете в самый конец перед закрывающим тегом PHP ?>. Если его нет, то просто в самый конец.

Перед изменением function.php, обязательно сохраните его копию, чтобы в дальнейшем, в случаи ошибки, вернуть все как было. Так же, можете использовать дочернюю тему.

$iso_my_thm = array(
   "Є"=>"EH","є"=>"eh","І"=>"I","і"=>"i","Ї"=>"i","ї"=>"i",
   "А"=>"A","Б"=>"B","В"=>"V","Г"=>"G","Д"=>"D",
   "Е"=>"E","Ё"=>"JO","Ж"=>"ZH",
   "З"=>"Z","И"=>"I","Й"=>"JJ","К"=>"K","Л"=>"L",
   "М"=>"M","Н"=>"N","О"=>"O","П"=>"P","Р"=>"R",
   "С"=>"S","Т"=>"T","У"=>"U","Ф"=>"F","Х"=>"KH",
   "Ц"=>"C","Ч"=>"CH","Ш"=>"SH","Щ"=>"SHH","Ъ"=>"'",
   "Ы"=>"Y","Ь"=>"","Э"=>"EH","Ю"=>"YU","Я"=>"YA",
   "а"=>"a","б"=>"b","в"=>"v","г"=>"g","д"=>"d",
   "е"=>"e","ё"=>"jo","ж"=>"zh",
   "з"=>"z","и"=>"i","й"=>"jj","к"=>"k","л"=>"l",
   "м"=>"m","н"=>"n","о"=>"o","п"=>"p","р"=>"r",
   "с"=>"s","т"=>"t","у"=>"u","ф"=>"f","х"=>"kh",
   "ц"=>"c","ч"=>"ch","ш"=>"sh","щ"=>"shh","ъ"=>"",
   "ы"=>"y","ь"=>"","э"=>"eh","ю"=>"yu","я"=>"ya",
   "—"=>"-","«"=>"","»"=>"","…"=>"","№"=>"#"
   );
  
function url_translit_iso($title){
global $iso_my_thm;
return strtr($title, $iso_my_thm);
}
 
add_action('sanitize_title', 'url_translit_iso', 0);

Давайте пробежимся по коду. В самом начале мы создаем переменную - $iso_my_thm ,внутри которой и заданы правила транслитерации. В второй строке как раз и заданы украинские буквы, если они вам не нужны, то просто удалите эту строку с буквами
"Є"=>"EH","є"=>"eh","І"=>"I","і"=>"i","Ї"=>"i","ї"=>"i",

Как вы уже поняли, каждое правило имеет вид - "Д"=>"D" и разделяются запятой. Вы можете добавить свои, если нужно или удалить лишние.

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

В последней строке мы создаем событие - add_action, таким образом вызываем нашу функцию, чтобы она начала работать.

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

Добавлено: 22 Марта 2021 09:36:20 Добавил: Андрей Ковальчук

ЧПУ своими руками (PHP). Хранение ЧПУ в базе данных

PHP

В статье ЧПУ своими руками PHP я уже немного рассказал о методе построения ЧПУ. Конечно, тот вариант не сильно привлекателен, если у вас огромный сайт и несколько сотен (а то и тысяч) страниц. Для таких больших проектов стоит применять немного другие методы. В этой статье будет рассмотрен один из них:

Хранение ЧПУ в базе данных

1) Плюсы:
Такие урлы доступны в любой точке кода. Просто сделал выборку по какой-то переменной из базы и получили ЧПУ-шный урл.
При хранении ссылки в базе можно расширить таблицу и внести туда 3 поля (для мета данных). В таком случае каждому урлу можно руками присваивать title,keywords & description. А это порой очень и очень нужная штука.

2) Минусы:
Для каждого товара, для каждой записи на странице будет необходимо обращаться к базе данных за ЧПУ-шным урлом. Думаю любому понятно, что это не есть хорошо. С другой стороны - если реализовать нормальное кеширование самых популярных ссылок - особых проблем (тем более на небольшом магазине) быть не должно.
При добавлении какой-то переменной (будь то страница, язык и т.д.) в базе будет создаваться новый урл (ведь исходный был изменен). В итоге, если вы не будете делать необходимых проверок и эту особенность заметят конкуренты - у вас будет вся база данных забита левыми ссылками и, вскоре, просто подохнет.

3) Реализация.

Для начала создайте табличку, в которой будете хранить сгенерированные ЧПУ.

У меня она имеет такой вид:

CREATE TABLE IF NOT EXISTS `pp_sef_links` (
 `link_id` int(11) NOT NULL auto_increment,
 `hits` int(11) NOT NULL,
 `ufu` varchar(255) NOT NULL,
 `link` varchar(255) NOT NULL,
 `mdate` date NOT NULL,
 `status` tinyint(1) NOT NULL default '1',
 PRIMARY KEY (`link_id`),
 UNIQUE KEY `ufu` (`ufu`),
 UNIQUE KEY `link` (`link`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Видим, что таким образом можно отслеживать обращения к ссылке, включать\выключать ее и т.д.

Далее нужен будет класс, который будет содержать набор методов для работы с ссылками.

При формировании страницы в месте, где должна быть ссылка будет вызов метода, который вернет ЧПУ

$sef->getUfu("index.php?task=news");


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

С генерацией и сохранением урлов разобрались. Теперь рассмотрим случай, когда пользователь кликнул по этому самому ЧПУ.

Для того, что новый урл обрабатывать, необходимо 2 вещи:

1) Единая точка входа (один файл, который будет отрабатывать наши ЧПУ, подключать необходимые модули, создавать переменные и т.д.). В моем случае - это index.php

2) Работающий .htaccess файл на сервере и включенный мод_rewrite

Содержимое .htaccess

# включаем мод реврайт
RewriteEngine on
#пишем условия срабатывания - всегда, кроме файлов (реально существующих на сервере)
RewriteCond %{REQUEST_FILENAME} !-f
# и директорый
RewriteCond %{REQUEST_FILENAME} !-d
# перебрасывать все на нашу точку входа, запихнув параметры запроса в переменную sefq
RewriteRule ^(.*)$ index.php?sefq=$1 [L,QSA] 

Надо сказать, что sefq - я вообще не использую, вместо этого - проверяю $_SERVER['REQUEST_URI']

Часть кода из index.php, отрабатывающая запрос

if (isset($_SERVER['REDIRECT_STATUS'])) { // если есть редирект
 $url=urldecode($_SERVER['REQUEST_URI']);// отрабатываем урл 
 $url=ltrim($url,"/"); // убираем лишние "/"
 
 if (!$sef->parseURL($url)) { // метод по входящему ЧПУ получает "нормальную" ссылку 
 // и создает необходимые переменные в $_GET
 // если не нашел ссылку - возвращает false
 // error 404;
 $sef->register404($_SERVER); // регистрируем 404 (чтоб потом можно было их чинить)
 $app->notice($langData['_ERROR_404_']);//выдаем пользователю сообщение, что стрнаица не найдена
 header("http/1.0 404 Not found");// ставим соответствующий хеадер
 } else
 header("http/1.0 200 Ok");// ставим соответствующий хеадер
}else { // direct page is ...
 $newUrl=$sef->getUrl($_SERVER['REQUEST_URI']); // пытаемся сформировать ЧПУ по прямой ссылке
 if ($newUrl === false){
 $app->notice($langData['_ERROR_404_']); // если ничего не вышло - 404 ..
 header("http/1.0 404 Not found");
 
 } 
 elseif($newUrl != "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] && $_SERVER['REQUEST_METHOD'] == "GET") {
 header("HTTP/1.1 301 Moved Permanently"); // если все нормально - по 301 редиректу перенаправляем пользователя на новую страницу
 header("Location: ".$newUrl);
 exit;
 }
 
 
}

Плюшки, я дума, вы уже придумаете сами.

Добавлено: 20 Апреля 2018 09:30:04 Добавил: Андрей Ковальчук

ЧПУ своими руками (PHP). Хранение ЧПУ в базе данных

PHP

В статье ЧПУ своими руками PHP я уже немного рассказал о методе построения ЧПУ. Конечно, тот вариант не сильно привлекателен, если у вас огромный сайт и несколько сотен (а то и тысяч) страниц. Для таких больших проектов стоит применять немного другие методы. В этой статье будет рассмотрен один из них:

Хранение ЧПУ в базе данных

1) Плюсы:
Такие урлы доступны в любой точке кода. Просто сделал выборку по какой-то переменной из базы и получили ЧПУ-шный урл.
При хранении ссылки в базе можно расширить таблицу и внести туда 3 поля (для мета данных). В таком случае каждому урлу можно руками присваивать title,keywords & description. А это порой очень и очень нужная штука.

2) Минусы:
Для каждого товара, для каждой записи на странице будет необходимо обращаться к базе данных за ЧПУ-шным урлом. Думаю любому понятно, что это не есть хорошо. С другой стороны - если реализовать нормальное кеширование самых популярных ссылок - особых проблем (тем более на небольшом магазине) быть не должно.
При добавлении какой-то переменной (будь то страница, язык и т.д.) в базе будет создаваться новый урл (ведь исходный был изменен). В итоге, если вы не будете делать необходимых проверок и эту особенность заметят конкуренты - у вас будет вся база данных забита левыми ссылками и, вскоре, просто подохнет.

3) Реализация.

Для начала создайте табличку, в которой будете хранить сгенерированные ЧПУ.

У меня она имеет такой вид:

CREATE TABLE IF NOT EXISTS `pp_sef_links` (
 `link_id` int(11) NOT NULL auto_increment,
 `hits` int(11) NOT NULL,
 `ufu` varchar(255) NOT NULL,
 `link` varchar(255) NOT NULL,
 `mdate` date NOT NULL,
 `status` tinyint(1) NOT NULL default '1',
 PRIMARY KEY (`link_id`),
 UNIQUE KEY `ufu` (`ufu`),
 UNIQUE KEY `link` (`link`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Видим, что таким образом можно отслеживать обращения к ссылке, включать\выключать ее и т.д.

Далее нужен будет класс, который будет содержать набор методов для работы с ссылками.

При формировании страницы в месте, где должна быть ссылка будет вызов метода, который вернет ЧПУ
$sef->getUfu("index.php?task=news");

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

С генерацией и сохранением урлов разобрались. Теперь рассмотрим случай, когда пользователь кликнул по этому самому ЧПУ.

Для того, что новый урл обрабатывать, необходимо 2 вещи:

1) Единая точка входа (один файл, который будет отрабатывать наши ЧПУ, подключать необходимые модули, создавать переменные и т.д.). В моем случае - это index.php

2) Работающий .htaccess файл на сервере и включенный мод_rewrite

Содержимое .htaccess
# включаем мод реврайт
RewriteEngine on
#пишем условия срабатывания - всегда, кроме файлов (реально существующих на сервере)
RewriteCond %{REQUEST_FILENAME} !-f
# и директорый
RewriteCond %{REQUEST_FILENAME} !-d
# перебрасывать все на нашу точку входа, запихнув параметры запроса в переменную sefq
RewriteRule ^(.*)$ index.php?sefq=$1 [L,QSA] 

Надо сказать, что sefq - я вообще не использую, вместо этого - проверяю $_SERVER['REQUEST_URI']

Часть кода из index.php, отрабатывающая запрос
if (isset($_SERVER['REDIRECT_STATUS'])) { // если есть редирект
 $url=urldecode($_SERVER['REQUEST_URI']);// отрабатываем урл 
 $url=ltrim($url,"/"); // убираем лишние "/"
 
 if (!$sef->parseURL($url)) { // метод по входящему ЧПУ получает "нормальную" ссылку 
 // и создает необходимые переменные в $_GET
 // если не нашел ссылку - возвращает false
 // error 404;
 $sef->register404($_SERVER); // регистрируем 404 (чтоб потом можно было их чинить)
 $app->notice($langData['_ERROR_404_']);//выдаем пользователю сообщение, что стрнаица не найдена
 header("http/1.0 404 Not found");// ставим соответствующий хеадер
 } else
 header("http/1.0 200 Ok");// ставим соответствующий хеадер
}else { // direct page is ...
 $newUrl=$sef->getUrl($_SERVER['REQUEST_URI']); // пытаемся сформировать ЧПУ по прямой ссылке
 if ($newUrl === false){
 $app->notice($langData['_ERROR_404_']); // если ничего не вышло - 404 ..
 header("http/1.0 404 Not found");
 
 } 
 elseif($newUrl != "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'] && $_SERVER['REQUEST_METHOD'] == "GET") {
 header("HTTP/1.1 301 Moved Permanently"); // если все нормально - по 301 редиректу перенаправляем пользователя на новую страницу
 header("Location: ".$newUrl);
 exit;
 }
}




Плюшки, я дума, вы уже придумаете сами.

В следующей статье я напишу еще более (на мой взгляд) прикольный метод формирования ЧПУ, который наиболее подходит для больших магазинов или высоко нагруженных проектов!

Добавлено: 23 Августа 2013 11:10:18 Добавил: Андрей Ковальчук

ЧПУ своими руками (PHP)

PHP

ЧПУ (ЧеловекоПонятныеУрлы) - это ссылка вида site.com/page/2 вместо site.com/index.php?option=page&page_id=2 . Приемущества использования ЧПУ думаю всем понятны. Это более короткие, простые и понятные урлы. К тому же "ходят слухи", что поисковикам они "больше нравятся". Последний факт я не проверял и было б странно, если б поисковики отсеивали или как то ограничивали ссылки, которые им "не нравятся", но если таке мнение есть - к ним надо прислушаться... Причина, из-за которой я начал смотреть в сторону ЧПУ - многие заказчики очень хотят их видеть у себя на сайте ...

Разработано достаточно много способов создания ЧПУ :

mod_rewrite - часто используемая технология. Идея заключается в установке спецового модуля к апачу и добавлению правил для преобразования короткого урла в полный в файл .htaccess

        RewriteRule ^([^/]*)\.rss  /rss.php?type=$1 [L]

+ : достаточно простой в реализации метод - накатал в .htaccess правила и все тут

-: надо, чтоб на сервере был установлен собственно mod_rewrite, при обьемном сайте - накопление правил в .htaccess
FileMatch - Основан на директиве FilesMatch, которая в Апаче является core feature. Все просто. Пишем опять же в .htaccess
        Action throw /index.php <FilesMatch "^([^.]+)$">
        ForceType throw
        </FilesMatch>
        <FilesMatch index.php>
        ForceType application/x-httpd-php
        </FilesMatch>

Эти директивы будут перенаправлять все запросы , не содержащие точки , на index.php. Дальше разобрать запрос - не особо сложно...

+: та же простота в реализации

-: надо, чтоб на сервере был установлен модуль mod_action
ErrorDocument - основан на дерективах в .htaccess файле для переброски запросов с несуществующих урлы на указаный файл. Для этого в .htaccess надо добавить:
    ErrorDocument 404 /index.php ErrorDocument 401 /index.php 

+: ИМХО самый простой способ, ничего устанавливать на сервере не надо, в индексе получил запрос , обработал и радуешься

-: на сервере в логах накапливается сообщения об 404 урлах... надо отключать логирование таких ошибок.

Еще стоит сказать, что даже после перенаправления запроса с несуществующего урла на существующий файл, сервер покажет нужную страницу, но все ровно вернет стаату 404 для этой страницы. Чтоб этого не было - надо вначало index.php добавить
    header("http/1.0 200 Ok"); 

Уверен, что есть еще множество методов, но мы ограничимся этими.

В моем случае первый способ брать смысла не было ... непонятно будет ли на сервере у хостера стоять mod_rewrite (хотя обычно стоит ;)). Второй - у меня не заработал ... даже не знаю почему ;( Остался третий способ. Он как раз заработал как надо. Вся сложность заключалась в преобразовании ЧПУ-шного урла и обычный и наоборот. CMS Joomla (и многие другие видимо) используют для этих целей базу данных и динамически генерят новые урлы. Мну это не прет ... бо поработав с той же джумлой убедился, что львиная доля времени формирования страницы уходит на формирование и поиск этих урлов в БД ... Хотелось найти решение без баз данных, и я его нашел. Суть заключается в наборе правил вида:
    $linksUrls[]=array('pages',"do=?");
    $linksUrls[]=array('pages/?',"do=?&pid=?");

Для обработки таких урлов я написал спецовый класс (сам класс и файлы примеров можно найти вконце статьи). Разбирать сам класс мы не будем, а посомтрим способы его приминения на примерах.

Подключение и использование ЧПУ на сайте
        $links=new Links($rl,$use_ufu); //$url - название сайта. Будет добавляться к ссылам при генерации, $use_ufu (0\1) - использовать ЧПУ или нет
        if ($use_ufu == 1) {
        require_once("Links.rules.php"); // подгружаем файл с правилами
        if (isset($linksUrls) && is_array($linksUrls)) // если есть массив с правилами - делаем инициализацию
        $links->init($linksUrls);
        }

Содержимое файла Links.rules.php :
        $linksUrls=array();
        $linksUrls[]=array('news',"do=?");
        $linksUrls[]=array('news/?',"do=?&news_id=?");
        // mod pages
        $linksUrls[]=array('pages',"do=?");
        $linksUrls[]=array('pages/?',"do=?&pid=?");

Разбор ЧПУ-шного урла и формирование GET запроса
        if (isset($_SERVER['REDIRECT_STATUS']) && $_SERVER['REDIRECT_STATUS'] == '404') {
        $url=$_SERVER['REQUEST_URI'];
        if (!$links->parseURL($url)) {
        // error 404;
        die('Sorry, page not found'');
        }
        }
        print_r($_GET);

Если все прошло хорошо - в массив $_GET будут добавлены переменные, которые б использовались при обычном запросе. Следует сказать, что запрос должен быть вида /pages/2 ю Следует так же указать, что можно указывать переменные, не определенные в правилах как в обычном запросе /pages/2?foo=5
Генерация ЧПУ из обычного запроса
    $links->createUFU('index.php?do=pages');
    // результат: site.com/pages/

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

В любом случае буду благодарен за любые коментарии\критику, поскольку они могут помочь усовершенствовать код\метод\способ ;)

TODO: Сейчас ЧПУ-шный урл формируется соединением значений из обычного урла с помощью "/". В дальнейшем планирую убрать привязку к порядку следования переменных в обычном урле.

Добавлено: 23 Августа 2013 06:34:06 Добавил: Андрей Ковальчук

ЧПУ своими руками (PHP)

PHP

ЧПУ (ЧеловекоПонятныеУрлы) - это ссылка вида site.com/page/2 вместо site.com/index.php?option=page&page_id=2 .
Приемущества использования ЧПУ думаю всем понятны. Это более короткие, простые и понятные урлы. К тому же "ходят слухи", что поисковикам они "больше нравятся". Последний факт я не проверял и было б странно, если б поисковики отсеивали или как то ограничивали ссылки, которые им "не нравятся", но если таке мнение есть - к ним надо прислушаться... Причина, из-за которой я начал смотреть в сторону ЧПУ - многие заказчики очень хотят их видеть у себя на сайте ...

Разработано достаточно много способов создания ЧПУ :

1. mod_rewrite - часто используемая технология. Идея заключается в установке спецового модуля к апачу и добавлению правил для преобразования короткого урла в полный в файл .htaccess

RewriteRule ^([^/]*)\.rss /rss.php?type=$1 [L]

+ : достаточно простой в реализации метод - накатал в .htaccess правила и все тут

-: надо, чтоб на сервере был установлен собственно mod_rewrite, при обьемном сайте - накопление правил в .htaccess

2. FileMatch - Основан на директиве FilesMatch, которая в Апаче является core feature. Все просто. Пишем опять же в .htaccess
Action throw /index.php <FilesMatch "^([^.]+)$">
 ForceType throw
 
 ForceType application/x-httpd-php

Эти директивы будут перенаправлять все запросы , не содержащие точки , на index.php. Дальше разобрать запрос - не особо сложно...

+: та же простота в реализации

-: надо, чтоб на сервере был установлен модуль mod_action

3. ErrorDocument - основан на дерективах в .htaccess файле для переброски запросов с несуществующих урлы на указаный файл. Для этого в .htaccess надо добавить:
ErrorDocument 404 /index.php ErrorDocument 401 /index.php

+: ИМХО самый простой способ, ничего устанавливать на сервере не надо, в индексе получил запрос , обработал и радуешься

-: на сервере в логах накапливается сообщения об 404 урлах... надо отключать логирование таких ошибок.

Еще стоит сказать, что даже после перенаправления запроса с несуществующего урла на существующий файл, сервер покажет нужную страницу, но все ровно вернет стаату 404 для этой страницы. Чтоб этого не было - надо вначало index.php добавить
header("http/1.0 200 Ok"); 

Уверен, что есть еще множество методов, но мы ограничимся этими.

В моем случае первый способ брать смысла не было ... непонятно будет ли на сервере у хостера стоять mod_rewrite (хотя обычно стоит ;)). Второй - у меня не заработал ... даже не знаю почему ;( Остался третий способ. Он как раз заработал как надо. Вся сложность заключалась в преобразовании ЧПУ-шного урла и обычный и наоборот. CMS Joomla (и многие другие видимо) используют для этих целей базу данных и динамически генерят новые урлы. Мну это не прет ... бо поработав с той же джумлой убедился, что львиная доля времени формирования страницы уходит на формирование и поиск этих урлов в БД ... Хотелось найти решение без баз данных, и я его нашел. Суть заключается в наборе правил вида:
$linksUrls[]=array('pages',"do=?");
$linksUrls[]=array('pages/?',"do=?&pid=?");

Для обработки таких урлов я написал спецовый класс (сам класс и файлы примеров можно найти вконце статьи). Разбирать сам класс мы не будем, а посомтрим способы его приминения на примерах.

1. Подключение и использование ЧПУ на сайте
$links=new Links($url,$use_ufu); //$url - название сайта. Будет добавляться к ссылам при генерации, $use_ufu (0\1) - использовать ЧПУ или нет
 if ($use_ufu == 1) {
 require_once("Links.rules.php"); // подгружаем файл с правилами
 if (isset($linksUrls) && is_array($linksUrls)) // если есть массив с правилами - делаем инициализацию
 $links->init($linksUrls);
 }

Содержимое файла Links.rules.php :
$linksUrls=array();
 $linksUrls[]=array('news',"do=?");
 $linksUrls[]=array('news/?',"do=?&news_id=?");
 // mod pages
 $linksUrls[]=array('pages',"do=?");
 $linksUrls[]=array('pages/?',"do=?&pid=?");

2. Разбор ЧПУ-шного урла и формирование GET запроса
if (isset($_SERVER['REDIRECT_STATUS']) && $_SERVER['REDIRECT_STATUS'] == '404') {
 $url=$_SERVER['REQUEST_URI'];
 if (!$links->parseURL($url)) {
 // error 404;
 die('Sorry, page not found'');
 }
 }
 print_r($_GET);

Если все прошло хорошо - в массив $_GET будут добавлены переменные, которые б использовались при обычном запросе. Следует сказать, что запрос должен быть вида /pages/2 ю Следует так же указать, что можно указывать переменные, не определенные в правилах как в обычном запросе /pages/2?foo=5

3. Генерация ЧПУ из обычного запроса
$links->createUFU('index.php?do=pages');
 // результат: site.com/pages/

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

В любом случае буду благодарен за любые коментарии\критику, поскольку они могут помочь усовершенствовать код\метод\способ ;)

TODO: Сейчас ЧПУ-шный урл формируется соединением значений из обычного урла с помощью "/". В дальнейшем планирую убрать привязку к порядку следования переменных в обычном урле.

Добавлено: 23 Августа 2013 10:23:20 Добавил: Андрей Ковальчук

ЧПУ на PHP. Прочь от ModeRewrite правил. Единая точка входа.

PHP

Человекопонятный URL (ЧПУ) - одна из самых часто затрагиваемых тем на различных форумах, посвящённых языку PHP. Можно до бесконечности спорить, нужны ли красивые URL-адреса для SEO-оптимизации, но факт того, что веб-сайт с ЧПУ выглядит аккуратно и профессионально отрицать глупо.

URL адрес вида

http://server.com/catalog/tv/samsung/5

выглядит опрятно и интуитивно понятно для пользователя, нежели адрес вида
http://server.com/catalog.php?product=tv&brand=samsung&page=5

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

ЧПУ на базе модуля Apache mod_rewrite
Долгое вря ЧПУ делались с помощью небезызвестного модуля веб-сервера Apache mod_rewrite, который предназначен для манипуляции URL адресами. Директивы для mod_rewrite обычно писались разработчиками в .htaccess файле конфигурации Apache и выглядели примерно так:
RewriteEngine on
RewriteBase /
 
# Досье пользователя.
RewriteRule ^users/([0-9]+)\.html$  userinfo.php?user_id=$1
# Список всех пользователей
RewriteRule ^users/?$           users.php?%{QUERY_STRING}
 
# Новости общим списком
RewriteRule ^news/?$            news.php
# Новости по разделам
RewriteRule ^news/([a-z_]+)/?$      news.php?cat=$1
# Страница одной новости
RewriteRule ^news/([0-9]+).html$    news.php?id=$1

Чем плох подобный механизм преобразований URL адресов, освоенный на mode_rewrite? Ничем не плох, но для разработки гибких веб-приложений он не подходит. В первую очередь потому, что преобразованием занимается сам mod_rewrite и система фактически завязана на файле конфигурации .htaccess. Мы не имеем возможности влиять на процесс преобразования, добавлять в автоматическом режиме новые виртуальные URL адреса, привязывать сложную логику к определённым виртуальным URL-адресам и делать многое другое.

ЧПУ на PHP
Конечно же, на одном PHP ЧПУ не сделать. Моудуль mode_rewrite как и раньше нужно использовать, но только лишь для того, что бы перенаправить все запросы к виртуальным ЧПУ-адресам в единую точку входа в приложении - в index.php. Для этого в .htaccess файле конфигурации можно прописать следующий код:
RewriteEngine on
RewriteBase /
 
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ [NC,L]
RewriteRule ^.*$ index.php [NC,L]

Данная запись означает буквально следующее: если запрошенный URL-адрес не является файлом, не является символической ссылкой и не является директорией, то подменить виртуальный адрес файлом index.php. При этом, суперглобальная переменная PHP $_SERVER['REQUEST_URI'] будет содержать именно запрошенный виртуальный адрес. Что это нам даёт? Фактически - безграничные возможности для манипулирования виртуальными адресами!

Пример № 1.
Во многих фреймворках ЧПУ строится по следующему принципу:
http://server.com/модуль/действие/параметр1/значение1/параметр2/значение2/параметрН/значениеН/

"Распарсить" такой URL и получить имя модуля, действие и параметры нет никаких проблем.

В данном случае под определением "модуль" и "действие" можно понимать что угодно:

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

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

Возьмем в пример такой URL-адрес:
http://localhost/guestbook/edit_message/id/123

и "распарсим" его с помощью нижестоящего кода, который поместим в единую точку входа - в index.php:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
 
// Назначаем модуль и действие по умолчанию.
$module = 'index';
$action = 'index';
// Массив параметров из URI запроса.
$params = array();
 
// Если запрошен любой URI, отличный от корня сайта.
if ($_SERVER['REQUEST_URI'] != '/') {
try {
// Для того, что бы через виртуальные адреса можно было также передавать параметры
// через QUERY_STRING (т.е. через "знак вопроса" - ?param=value),
// необходимо получить компонент пути - path без QUERY_STRING.
// Данные, переданные через QUERY_STRING, также как и раньше будут содержаться в
// суперглобальных массивах $_GET и $_REQUEST.
$url_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
 
// Разбиваем виртуальный URL по символу "/"
$uri_parts = explode('/', trim($url_path, ' /'));
 
// Если количество частей не кратно 2, значит, в URL присутствует ошибка и такой URL
// обрабатывать не нужно - кидаем исключение, что бы назначить в блоке catch модуль и действие,
// отвечающие за показ 404 страницы.
if (count($uri_parts) % 2) {
throw new Exception();
}
 
$module = array_shift($uri_parts); // Получили имя модуля
$action = array_shift($uri_parts); // Получили имя действия
 
// Получили в $params параметры запроса
for ($i=0; $i < count($uri_parts); $i++) {
$params[$uri_parts[$i]] = $uri_parts[++$i];
}
} catch (Exception $e) {
$module = '404';
$action = 'main';
}
}
 
echo "\$module: $module\n";
echo "\$action: $action\n";
echo "\$params:\n";
print_r($params);

Вот так будет выглядеть результат для URL-адреса http://localhost/guestbook/edit_message/id/123:
$module: guestbook
$action: edit_message
$params:
Array
(
[id] => 123
)

Теперь, если у вас есть класс Guestbook, отвечающий за работу гостевой книги, то вы просто инстанцируете этот класс и вызываете его метод отвечающий за редактирование сообщения. Массив $params можно передать в метод в качестве аргумента метода:
$guestbook = new $module();
$guestbook->$action($params); // редактирование сообщения с ID = 123

но лучше всего при подобном подходе поместить все полученные переменные из парсинга URL адреса в суперглобальный массив $_REQUEST:
$_REQUEST = array_merge($_REQUEST, $params);

Если же будет запрошен ошибочный URL -адрес:
http://localhost/guestbook/edit_message/id/

то сработает исключение и в качестве обработчика будет фигурировать обработчик 404 ошибки:
$module: 404
$action: main
$params:
Array
(
)

Пример № 2.
Для понимания следующего подхода вам необходимы базовые знания Perl-совместимых регулярных выражений (POSIX).

Данный подход очень гибок и интересен, т.к. позволяет создавать практические любые виртуальные адреса, с абсолютно произвольной структурой и в отличие от предыдущего подхода - без упоминания в URL реального имени обработчика (модуля или действия). Суть метода заключается в следующем: каждый виртуальный URL-адрес, который может быть в системе, необходимо описать в виде регулярного выражения. Назначить для него обработчик. Если запрошенный URL совпадает с одним из таких регулярных выражений, то с помощью функций регулярных выражений нужно получить из URL необходимые данные, после чего передать их в обработчик. Пример:
// Конфигурация маршрутов URL проекта.
$routes = array
(
// Главная страница сайта (http://localhost/)
array(
// паттерн в формате Perl-совместимого реулярного выражения
'pattern' => '~^/$~',
// Имя класса обработчика
'class' => 'Index',
// Имя метода класса обработчика
'method' => 'index'
),
 
// Страница регистрации пользователя (http://localhost/registration.xhtml)
array(
'pattern' => '~^/registration\.xhtml$~',
'class' => 'User',
'method' => 'registration',
),
 
// Досье пользователя (http://localhost/userinfo/12345.xhtml)
array(
'pattern' => '~^/userinfo/([0-9]+)\.xhtml$~',
'class' => 'User',
'method' => 'infoInfo',
// В aliases перечисляются имена переменных, которые должны быть в дальнейшем созданы
// и заполнены значениями, взятыми на основании разбора URL адреса.
// В данном случае в переменную user_id должен будет записаться числовой
// идентификатор пользователя - 12345
'aliases' => array('user_id'),
),
 
// Форум (http://localhost/forum/web-development/php/12345.xhtml)
array(
'pattern' => '~^/forum(/[a-z0-9_/\-]+/)([0-9]+)\.xhtml$~',
'class' => 'Forum',
'method' => 'viewTopick',
// Будут созданы переменные:
// forum_url = '/web-development/php/'
// topic_id = 12345
'aliases' => array('forum_url', 'topic_id'),
),
 
// и т.д.
);

Как видно из примера, так нужно будет описать все виртуальные URL адреса проекта. Напишем обработчик таких URL адресов:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
 
// Тут нужно подключить через include файл с маршрутами, который описали выше.
 
// Назначаем модуль и действие по умолчанию.
$module = 'Not_Found';
$action = 'main';
 
// Массив параметров из URI запроса.
$params = array();
 
foreach ($routes as $map)
{
// Для того, что бы через виртуальные адреса можно было также передавать параметры
// через QUERY_STRING (т.е. через "знак вопроса" - ?param=value),
// необходимо получить компонент пути - path без QUERY_STRING, т.к. в ином
// случае виртуальный адрес попросту не совпадет ни с одним паттерном из массива $routes.
// Данные, переданные через QUERY_STRING, также как и раньше будут содержаться в
// суперглобальных массивах $_GET и $_REQUEST.
$url_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
 
if (preg_match($map['pattern'], $url_path, $matches))
{
// Выталкиваем первый элемент - он содержит всю строку URI запроса
// и в массиве $params он не нужен.
array_shift($matches);
 
// Формируем массив $params с теми названиями ключей переменных,
// которые мы указали в $routes
foreach ($matches as $index => $value)
{
$params[$map['aliases'][$index]] = $value;
}
 
$module = $map['class'];
$action = $map['method'];
 
break;
}
}
 
echo "\$module: $module\n";
echo "\$action: $action\n";
echo "\$params:\n";
print_r($params);

Теперь при запросе
http://localhost/forum/web-development/php/12345.xhtml

мы получим информацию о нужном нам обработчике (классе и методе класса), а так же массив параметров, с помощью которых и получим из СУБД информацию о теме в форуме:
$module: Forum
$action: viewTopick
$params:
Array
(
[forum_url] => /web-development/php/
[topic_id] => 12345
)

Соответственно, инстанцирование нужного обработчика, т.е. класса Forum и запуск его метода

Добавлено: 14 Сентября 2013 10:43:26 Добавил: Андрей Ковальчук