четверг, 1 октября 2009 г.

Регулярные выражения в PHP или как я прикручивал мануал (часть 1)

Долго думал стоит ли начинать писать статью на данную тему…и в конце концов решил почему нет? Я не очень хорошо знаю регулярные выражения, но то что у меня уже получилось-этим я могу поделиться с читателями. Заранее просьба к гуру, давно и хорошо знающим регулярки не ругать сильно данную статью и просто если есть возможность указать на недочеты. Ну это была преамбула. Приступим.
Я давно хотел прикрутить на мой блог PHP мануал в формате html дабы проще было ссылаться на различные функции. Но все как-то руки не доходили. И вот наконец-то вырвался из рутины и решил, что надо просто сесть и написать.
Просто вставить php мануал на сайт было не интересно да и просто стремно. Ведь ман был без дизайна, обычные белые страницы. Мне нужно было интегрировать его в CMS. Тут же я столкнулся с первой проблемой: я никогда не ковырялся в коде WordPress, ну вот не довелось как-то. Ну, что нам стоит дом построить, нарисуем будем жить. :) Дабы сэкономить свое время, я ведь не собираюсь досконально изучать данную систему чтобы постоянно писать под нее плагины, я просто взял несколько плагинов, которые уже были подключены у меня и отлично работали и стал разбираться как же там все устроено. Оказалось все достаточно просто. Ну описывать что и как там я не буду. Ибо статья посвящена не этому(но в дальнейшем обязательно все опишу ;) ). Итак есть мануал, в html форме, есть желание прикрутить его на сайт, и мы разобрались как устроен WP(ну это конечно громко сказано, но все же).
Во первых я решил что в $_SERVER['QUERY_STRING'] будет содержаться имя файла к которому в данный момент обращается пользователь. Т.е например вы ищете информацию о функции substr на сервере файл с информацией о данной функции имеет путь /manual/function.sunstr.html чтобы обратиться к нему используем адрес /php-manual/?function.substr. Ну вот так вот все достаточно просто. Но вот все линки в html файлах старые (/manual/function.sunstr.html) а мне надо было чтобы они соответствовали моей системе. Что ж…вариантов два. Преобразовывать на лету регуляркой (apache или php не важно) или преобразовать один раз. Я решил что ресурсы сервера не бесконечны (преобразование на лету всегда неслабо грузило проц). Значит надо преобразовать единожды… Тут мне помогут регулярные выражения. Надо сказать, что знаю я их весьма поверхностно. Ну что ж…как говорится: «Иду на вы!».
И я начал свои изыскания. Могу сразу сказать, что промучался я пол дня, прежде чем получил нормальный результат, ну ничего тяжело в ученье-легко в бою. Потом будет проще. Вот что у меня получилось:


<?php

$arr_files 
glob('./manual/*.html');

$i 0;

foreach (
$arr_files as $filename)

{

    
$file_data file_get_contents($filename);

    
$file_data preg_replace('!<a(.*?)href="([^"]*)\.html((\#([^"]*))*?)"!is',

    
'<a\1href="?\2\3"',$file_data);

    if(
file_put_contents($filename,$file_data))

    
$i++;

}

print 
'Изменено '.$i.' сстраниц';

?>

Итак что же здесь написано? Первый пункт чтение дирректории с файлами. В этом нам помогает функция glob. Она получает все имена файлов по маске. В данном случе все файлы с расширением .html. Результат работы данной функции-массив данных с именами файлов. Теперь в цикле перебираем данный массив. В этом нам помогает foreach. Читаем файл в переменную-строку(file_get_contents). А вот теперь начинается самое интересное. Обработка ссылки и замена. Функция preg_replace-специальная функция регулярок, она производит поиск по тексту и замену соответствующего шаблону (регулярке) выражения на то что мы хотим.
Во первых надо помнить что любое регулярное выражение обрамляется либо парными символами (скобками например такими[]), либо одинаковыми символами, например как у меня !…!.
Вне данных символов(а точнее после) ставятся модификаторы. В моем случае модификаторы is. Модификатор i означает, что поиск ведется без учета регистра символов..а s здесь для того чтобы точка(которая обычно означает любой символ кроме перевода строки) означала и перевод строки. Таким образом мы пишем часть выражения (<a(.*?)) означающую что после тэга a может стоять любое количество любых символов. Звездочка означает повторение предыдущего символа 0 или более раз, а знак вопроса умеряет ее жадность). После чего пишем href="([^"]*)\.html((\#([^"]*))*?)"
([^"]*) означает что возможны любые символы, но не кавычки. Т.е знак ^ в квадратных скобках можно перевести как «не». Далее идет расширение файла в линке (\.html). Так как точка это спец символ ее надо экранировать обратным слэшем (так же как и далее экранируется решетка). Далее пользуемся тем же приемом что и ранее (([^"]*)) и указываем что выражение с решеткой может повторяться 0 или более раз. Это все конечно весьма сложно для понимания с нуля поэтому советую заглянуть на сайт pcre.ru дабы было понятнее. Внутри регулярки все что в круглых скобках мы получаем при поиске записывается в некий массив. Данные из которого мы можем вытянуть посредством \1\2\3 как это описано в строке замены (<a\1href="?\2\3").
Ну надеюсь в общем и целом вы разобрались что к чему и уловили основную суть. Собственно после этого просто запускаем скрипт и проверяем, что мы получили. Все линки должны переконвертироваться так, как нам надо. На этом я пожалуй закончу данную статью, дабы не перегружать читателя информацией и не валить все в одну кучу. Но прикручивание мануала на этом далеко не закончено и в скором времени (я надеюсь) выйдет продолжение. Надеюсь что данная информация была вам интересна и полезна.