Перейти к содержанию

Полезные и интересные статьи....


BearKing

Рекомендуемые сообщения

Поиск на РНР.

95% бесплатных php-скриптов (и не только php) - полный «отстой». Оно и понятно: хороший программист бесплатно ничего писать не будет, а если и будет, то только в свободное время в качестве развлечения, и уж, конечно не всякие банальности, вроде гостевых книг. Или, как говорил Рома Воронежский: «Вот проблема с этими творческими людьми: они всегда желают быть композиторами, художниками и писателями. В результате производством труб большого диаметра занимаются бездарности».

 

Именно так это и происходит.

 

Сегодня опять ковырялся в каталогах бесплатных скриптов, главным образом из любопытства, но еще и в тайной надежде найти что-нибудь забавное. В прошлый раз из «забавных» скриптов я нашел, например, «скрипт вывода текстового файла в php». Думал - парсер. Оказалось - да: почти что парсер. Привожу скрипт целиком: «?php include ("text.txt"); ?». Или вот вижу скрипт, написано «This script will reverse the text you give it. reversed: .ti evig uoy txet eht esrever lliw tpircs siht It isn't very useful, it's just funny. Try it out :)», то есть скрипт переворачивает строку задом-наперед. Самые худшие ожидания оправдались: они делали это циклом. Наверное, не знали, что в php есть специальная, уже готовая функция для этого:

 

Нашел скрипт поиска по сайту: он обшаривает директории, которые вы указали, открывает все html-файлы и тупо сравнивает: У меня тоже такой же скрипт был давным-давно написан, но потом, когда я понял, что народ поиском все-таки пользуется (сюрприз!), решил сделать его по-человечески. С индексом и прочими благами цивилизации: Сделал. В результате 1.5 мегабайта заметок превращаются в 900-килобайтный индекс за 17 секунд (индексацию надо проводить раз в несколько дней, или даже реже - в зависимости от скорости обновления сайта), после чего поиск по индексу происходит меньше одной секунды.

 

В общем, решил я все-таки поделиться этим скриптом. Краткая информация: скрипт на php, для работы никаких mysql не надо, предполагается, что html-ные (или txt-овые, как у меня) файлы где-то лежат, а не хранятся в mysql. В общем, поисковая машина для небольшого (ну, или «среднего») сайта. Как, например, spectator.ru.

 

Итак, начали:

 

 

--------------------------------------------------------------------------------

 

Самое первое - скрипт индексации. Для чего он нужен?.. Вот у меня 278 заметок. Если мы будем открывать каждый файл и искать совпадения, то нам надо будет открыть 278 файлов. А это ой как долго: Более того, нам надо будет 278 раз провести хитрые манипуляции с этими файлами (про манипуляции - ниже). Если же у нас есть индекс, то во-первых, поиск происходит в одном файле (индексе), во-вторых, все эти «хитрые манипуляции» уже выполнены.

 

Алгоритм индексирующего скрипта такой:

 

Открываем очередной файл

Убираем из него «мусор» ( зачем убирается мусор - понятно, чем мусора меньше, тем ищется быстрее: ):

переводы строк

html-тэги

знаки препинания

слова, короче трех букв (а зачем они там?)

Делаем заглавные буквы строчными.

Убираем повторяющиеся слова. (Действительно, зачем нам вся это тавтология?)

Записываем все в индекс.

Если еще есть файлы, переходим к пункту 1.

Реализуется это все на php - легко!

 

<?php

 

// Spectator's Indexing Script

// © Spectator.ru

// Для работы требуется PHP 4 или выше.

// Если вы будете использовать этот скрипт, ссылка на Spectator.ru

// крайне желательна. Спасибо.

 

// ставим скрипт "на счетчик" (чтобы знать, как долго он выполнялся

$ttt=microtime();

$ttt=((double)strstr($ttt, ' ')+(double)substr($ttt,0,strpos($ttt,' ')));

 

 

$indexdir="text"; #индексируемая директория

$indexfile="indexfile.txt"; #файл, в котором будет лежать индекс

// если вы хотите индексировать файлы в нескольких директориях, надо

// внести несколько махоньких добавлений...

 

 

// делаем так, чтобы не было таймаута из-за того, что скрипт будет долго

// выполняться (на всякий случай) и из-за того, что пользователь нажмет

// кнопку "стоп" в браузере

 

$abort = ignore_user_abort(1);

set_time_limit(600);

 

// Функция, удалающая слова, короче 3х букв. Пригодится дальше.

function sw (&$item1, $key) { if (strlen($item1)<3) $item1=""; }

 

// по очереди открываем все файлы в директории и проверяем, можно ли их

// индексировать у меня можно индексировать только файлы, которые имеют

// вид "число.txt" то есть && (is_numeric(str_replace (".txt","", $file)))

// это вам наверняка не понадобится.

 

$handle=opendir('./'.$indexdir);

while (false!==($file = readdir($handle))):

if ($file!="." && $file!=".." && (is_numeric(str_replace (".txt","", $file)))):

 

// открываем очередной файл

$fd = fopen ($indexdir."/".$file, "r");

$contents = fread ($fd, filesize ($indexdir."/".$file));

Fclose ($fd);

 

 

// убираем переводы строк

$contents=str_replace ("n"," ", $contents);

$contents=str_replace ("r","", $contents);

 

 

// убираем хтмл-тэги

$contents=str_replace ('<br>', ' ', $contents);

$contents=str_replace ('<p>', ' ', $contents);

$contents=strip_tags ($contents);

 

 

// убираем знаки препинания и цифры

// все эти строки работают быстрей, чем один eregi_replace!

 

$contents=str_replace (' -', ' ', $contents);

$contents=str_replace ('.', ' ', $contents);

$contents=str_replace (',', ' ', $contents);

$contents=str_replace ('!', ' ', $contents);

$contents=str_replace ('?', ' ', $contents);

$contents=str_replace (':', ' ', $contents);

$contents=str_replace (';', ' ', $contents);

$contents=str_replace (')', ' ', $contents);

$contents=str_replace ('(', ' ', $contents);

$contents=str_replace ('"', ' ', $contents);

 

// убираем заглавные буквы

$contents=strtolower ($contents);

 

// разбиваем на слова, убираем слова, короче 3х букв

$contents=explode (" ", $contents);

// вот и функция пригодилась...

array_walk ($contents, 'sw');

 

 

// убираем повторяющиеся слова

$contents=array_unique ($contents);

 

 

// соединяем слова

$contents=implode (" ", $contents);

 

 

// формируем соответствующую строку в индексе.

$fullfile.=$file."| ".$contents." n";

 

 

// индекс-файл будет иметь вид:

// имя_файла|индекс_для_данного_файла n

// имя_файла|индекс_для_данного_файла n

// имя_файла|индекс_для_данного_файла n

 

echo ($file." проиндексирован<br>");

// переходим к следующему файлу

 

endif;

endwhile;

closedir($handle);

 

// убираем двойные пробелы

while (stristr($fullfile, " ")) $fullfile=str_replace (" "," ",$fullfile);

 

// индекс готов, сохраняем его

$fp = fopen($indexfile, "w+");

fwrite($fp, $fullfile);

fclose($fp);

 

// считаем, как долго работал скрипт

$ddd=microtime();

$ddd=((double)strstr($ddd, ' ')+(double)substr($ddd,0,strpos($ddd,' ')));

 

echo ("<br>Время индексации: ".(number_format(($ddd-$ttt),3)).

      " секунд<br>");

echo ("Размер индекса: ".(number_format((round ((filesize($indexfile))/1024)) ,

      0, ".",".")))." Kb";

   

?>

 

 

--------------------------------------------------------------------------------

 

Итак, у нас есть индекс. Дальше - просто. Так ведь?.. Надо просто произвести поиск в нем. Берем функцию eregi, например:

 

Хотя я делал совсем по-другому:

 

Лирическое отступление: часто, когда надо проверить, если в строке какая-нибудь комбинация символов, пишут что-то вроде этого:

 

if (eregi('this must be found',$string))

    echo 'found!!';

else

    echo 'нифига не found!';

 

Способ хороший, но тормозной - из-за eregi. (Функция это работает с регулярными выражениями, поэтому и тормозит). По той же причине рекомендуется использовать там, где это можно, str_replace вместо ereg_replace. Быстрее раз в 10: Поэтому крутые программеры ;), когда им надо проверить, найдено ли что-то в строке, используют функцию strstr. На самом деле, она для этого не предназначена, (верней, «предназначена не для этого»), ибо она «Find first occurrence of a string», то есть «ищет первое местонахождение строки» и выводит строку, начиная с этого самого местоположения. Запутал, верно? (смайлик).

 

Ок, вот пример с php.net:

 

$email = 'sterling@designmultimedia.com';

$domain = strstr ($email, '@');

print $domain;

// выводит: @designmultimedia.com

 

Теперь понятно? Функция ищет, где в строке встречается подстрока «@» и выводит все после нее (включительно). Что самое главное - если ничего на найдено, то функция возвращает false. Именно поэтому ее можно использовать вот так:

 

if (stristr($string, 'this must be found'))

    echo 'found!!';

else

    echo 'нифига не found!';

 

У меня в скрипте для поиска в индексе используется stristr. Кроме того, поиск понимает простейший синтаксис: «+» (слово должно быть найдено, aka AND), «-» (слово не должно быть найдено, aka NOT) и «*» (звездочка). Но, анализируя то, что искали у меня на сайте, могу сказать только одно: Где-то в дискуссии про поисковые машины и их AI (искусственный интеллект), я нашел такую фразу, что «проще выучить последнего дебила пользоваться языком запросов, чем научить поисковую машину угадывать, что же именно этому дебилу надо/». Действительно, запрограммировать поисковую систему так, чтобы она сразу же выдавала то, что надо по идиотским запросам - сложно. Но, похоже, обучить ИХ составлять запросы правильно еще сложней:

 

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

 

 

--------------------------------------------------------------------------------

 

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

 

<?php

 

// Spectator's Site Search Script

// © Spectator.ru

// Для работы требуется PHP 4 или выше.

// Если вы будете использовать этот скрипт, ссылка на Spectator.ru

// крайне желательна. Спасибо.

 

 

// файл с индексом

$indexfile="indexfile.txt";

 

// обрабатываем запрос

 

$total=0;

$qu2=str_replace ("+","&",$words);

 

// убираем заглавные буквы

$qu2=strtolower ($qu2);

 

// обрубаем в конце лишние пробелы

$qu2=chop($qu2);

 

// убираем двойные пробелы

while (stristr($qu2," ")) $qu2=str_replace (" "," ",$qu2);

 

 

echo ('Запрос: '.$qu2);

echo ('<p><p><p>');

 

// разбиваем запрос на слова

$words = explode (' ', $qu2);

 

// удаляем в запросе все лишнее (знаки препинания, и прочее)

$qu2=eregi_replace ('[.?,!()#":;|]', '', $qu2);

 

// проверяем длинну запроса

if (strlen($qu2)>2):

 

 

// открываем индекс

$index=file ($indexfile);

$num= (count($index)-1);

 

// для каждой сточки из индекса (одна строчка=один файл) выполняем:

for ($i=1; $i<$num+2; $i++):

 

$contents=$index[$i-1];

 

$wordcount =0;

$mustfound=1;

 

// выполняем для каждого их запрошенных слов:

$mustntfound=1;

 

for ($q=0; $q<count($words); $q++):

 

// обработка знаков *, + и -

 

// знак *

if (stristr($words[$q], "*")) {$search=str_replace ("*","",$words[$q]); }

else { $search=" ".$words[$q]." ";}

 

// если в слове есть звездочка, то убираем звездочку и добавляем в начало

// и конец слова по пробелу если нет проблелов, то слово будет искаться не

// целиком, а "вообще", то есть на запрос "чай" будет выводиться и слово

// "случайный".

 

// bug: скрипт не учитывает, где в слове стоит звездочка и считает, что в

// любом случае она стоит в конце (!!)

 

// знак & (или +)

if (stristr($search, "&")) {$search=str_replace ("&","",$search); $mustfound++; }

 

// если стоит знак +, то количество слов, которые _должны_ быть найдены,

// увеличиваются на 1

 

// знак -

if (stristr($search, "-")) {

    $search=str_replace ("-","",$search);

    $mustntfound=0;

    }

 

// если стоит знак +, то если слово найдено, весь результат умножается на 0

// (смотри дальше).

 

// если слово найдено, считаем его и умножаем на $mustntfound, то есть на 1,

// если найдено "правильное" слово и на 0, если найдено слово, помеченное

// знаком -

 

if (stristr($contents, $search)) {

    $wordcount++;

    $wordcount=$wordcount*$mustntfound;

    }

 

endfor;

 

// проверяем, все ли слова, помеченные знаком + найдены,

// либо (если таких слов нет), найдено ли вообще хоть одно слово

 

if ($wordcount >= $mustfound):

 

// находим имя файла, в котором это найдено

$file=explode ("|",$contents);

$file=$file[0];

 

// выводим имя файла, в котором это найдено с ссылкой

// (этот кусочек вам надо будет переделать под собственные нужды).

 

$file=str_replace (".txt", "", $file);

$file=str_replace ("_", ".", $file);

echo ("<a href=".$file.">".$file."</a><br>");

 

 

// считаем, колько всего файлов надено

$total++;

endif;

 

// переходим к следующемуу файлу

endfor;

 

// выводим результаты

if ($total!=0) echo ('<br><br>Всего найдено страниц: '.$total);

else echo ("<b>Ничего не найдено!</b><p>Возможно, вы".

          ." просто не правильно составили запрос. Как это сделать правильно".

          ." - смотрите <a href=search>вот здесь</a>.");

 

else:

echo ("<br>Слишком короткий запрос!");

endif;

 

 

?>

 

<!-- Форма для поиска: -->

<form method=get action=search.php>

<input type=text size=19 name=words value="" maxlength=150>

<input type=submit class=frm value=Go>

</form>

 

 

_http://www.codenet.ru

Ссылка на комментарий
Поделиться на другие сайты

Использование XML Paser Functions при работе с шаблонами.

© mike.nov.ru

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

 

В PHP синтаксис языка основан не внедрении кода в данные и в этой статье мы рассмотрим один из достаточно удобных способов их разделения. Способ будет основываться на языке XML.

 

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

 

Данные:

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

 

Так, мы могли бы быстро исправить допущенную ошибку простой заменой старого кода на новый, при это сохранилось бы оригинальное оформление.

 

Опишем шаблон гостевой книги с помощью XML следующим образом:

 

<?xml version="1.0" encoding="windows-1251"?>

 

<guestbook>

<include url="../top.html" />

 

<![CDATA[

<H3>Гостевая книга - место для трепа</H3>

<table width=100% cellspacing=5><tr><td>

<br><center>

]]>

 

<record><![CDATA[

<table cellpadding=7 cellspacing=0 bgcolor=#F0F8F8 width=95%>

<tr bgcolor=#E0F0F0><td>__NAME__

    (<a href=mailto:__EMAIL__>__EMAIL__</a>)</tr>

<tr><td>__COMMENT__

</td></tr>

</table>

<br>

]]></record>

 

<![CDATA[

<br>

<center><BR><font face=Verdana size=2>

<A href=/add/>Добавить запись (Add record)</A>

</font></center>

</td></tr></table>

]]>

 

<include url="../bottom.htm />

 

</guestbook>

 

Каждый шаблон состоит их основной секции <guestbook></guestbook> внутрь которой может помещаться секция <records></record> описывающая одну запись в гостевой книге.

 

Кроме того, там может находится одиночный тег <include />, не место которого будет вставлен документ описанный с помощью свойства url, например:

 

<include url="../bottom.htm />

 

Ниже приведена сокращения схема описанного документа:

 

<?xml version="1.0" encoding="windows-1251"?>

 

<guestbook>

  <include url="../top.html" />

  <record>

    тело одной записи

  </record>

<include url="../bottom.htm />

</guestbook>

 

Код:

Осталось малость - написать программу, которая превратит описанный выше шаблон в HTML документ, содержащий как внешнее оформление, так и динамически изменяемые данные (записи в гостевой книге)

 

Для обработки шаблона мы будем использовать XML Parser functions (http://www.php.net/manual/en/ref.xml.php). Это расширение PHP предоставляет доступ к функциям библиотеки Expat, автором которой является Джеймс Кларк (James Clark). Библиотека Expat написана на языке C и предназначенная для разбора XML документов основанного на событиях. Она не проверят XML документ на ошибки и не работает с объектой моделью XML документа, так как это делают некоторые другие библиотеки (например tinyXML)

 

В версии PHP 4.3.1 (а возможно, что и в более ранних) XML Parser functions поддерживаются по умолчанию.

 

Перейдем непосредственно к написанию программы - обработчика шаблона.

 

Сначала считаем шаблон в переменную $xmldata:

 

<?

$xmldata=implode("",file("template.xml"));

// Ассоциативный массив - хранит данные, соответствующие тегам.

$TMPL=Array();

// Обрабатываемый тег

$ce="";

/*

 

...

 

пропущено подключение к серверу MySql

 

...

 

*/

 

$result=mysql_query("SELECT * FROM guestbook_database");

 

В переменную $html мы будет выводить результат работы нашего скрипта. В самом конце мы сделаем просто print $html;

 

$html="";

 

Создаем объект обработчик XML документа

 

$xml_parser = xml_parser_create();

 

Задаем ему опции и обработчики. Функция startElement() будет вызываться, когда в XML документе встретится открывающийся тег. Функция endElement() будет вызываться, когда будет встречен закрывающий тег. Для данных (то, что внутри тега) будет вызываться функция characterData()

 

 

xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "characterData");

 

Вызовем обработчик XML документа

 

if (!xml_parse($xml_parser, $xmldata)) {

    $error=sprintf("Ошибка в шаблоне: %s at line %d",

           xml_error_string(xml_get_error_code($xml_parser)),

           xml_get_current_line_number($xml_parser));

    die();

    }

 

xml_parser_free($xml_parser);

 

Вывод данных

 

print $html;

 

Теперь рассмотрим основную часть - это три функции-обработчика:

 

startElement()

endElement()

characterData()

В глобальной переменной $ce запомним название обрабатываемого тега, чтобы в обработчике characterData() знать к какому элементу массива $TMPL дописывать содержимое.

 

function startElement($parser, $name, $attrs) {

    GLOBAL $ce,$TMPL,$html;

 

    switch ($name) {

        case "INCLUDE":

            $html.=@implode("",@file($attrs["URL"]));

            break;

        }

    $TMPL[$name]="";

    $ce=$name;

    }

 

 

function endElement($parser, $name) {

    GLOBAL $ce,$TMPL,$result,$html;

 

    switch ($name) {

        case "RECORD":

            while ($D=mysql_fetch_array($result)) {

                $t=$TMPL["RECORD"];

                $t=str_replace("__NAME__",$D["name"],$t);

                $t=str_replace("__EMAIL__",$D["email"],$t);

                $t=str_replace("__COMMENT__",$D["comment"],$t);

                $html.=$t;

                }

            break;

        }

    $ce="";

    }

 

 

function characterData($parser, $data) {

    GLOBAL $ce,$TMPL,$html;

 

    switch ($ce) {

        case "RECORD":

            $TMPL[$ce].=$data;

            break;

        default:

            $html.=$data;

        }

    }

 

Вот вроде и все. Если вы разобрались с "XML parser functions", то рекомендую изучить "XSLT functions" и "DOM XML functions". Они вам помогут решить подобные задачи.

 

 

_http://www.codenet.ru

Ссылка на комментарий
Поделиться на другие сайты

-ExER-

популярность перепечаткой не повысишь.

слабай свое , оригинальное, вроде не глупый

321012[/snapback]

Понимаешь, самому как-то влом писать.....:)

Ссылка на комментарий
Поделиться на другие сайты

Заархивировано

Эта тема находится в архиве и закрыта для дальнейших ответов.

×
×
  • Создать...