Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

October 20, 2020

PHP7: static methods are slower

A static method containing the same code is consistently slower than its dynamic(?) counterpart. 8,7 sec vs. 5,5 sec. See also: https://www.codedwell.com/post/59/static-vs-non-static-methods-in-php

January 3, 2017

php composer unrequire undo require remove

If you no longer need a package and want it removed from the configuration, remove it manually from composer.json and run composer update.

This will update the dependencies based on your composer.json. Since you removed that package from composer.json, the actual package will be deleted from the system. If you only want to remove the package without updating the other installed packages, mention it in the update command. For example: composer update example/example.

August 9, 2011

Есть ли жизнь без ISPmanager?

Существует множество панелей управления сервером, и каждый в состоянии выбрать себе подходящую, для меня таковой являлась ISPmanager. Вопрос в том, что её присутствие на сервере не проходит бесследно: при установке она тянет за собой зависимости, в процессе использования вместе с ними жрет ресурсы.

Недавно заметил что Apache стал потреблять до безобразия много виртуальной памяти: из 512 мегабайт RAM на VPS с установленным Debian 6 в режиме ожидания (т.е. простоя) Apache жрал 300 с чем-то. Согласитесь, это слишком. ISPmanager, конечно, меня более чем устраивала, но для её работы требуется тот самый Apache и я решил попробовать не сколько найти ей замену, а просто обойтись без панели управления. Потому как лучшая альтернатива ISPmanager, да и любой другой панели управления сервером — это никакой панели управления сервером, уж простите за каламбур.

Чтобы для создания/удаления сайтов (т.е. Virtualhost'ов в Nginx, баз данных MySQL, папок и т.д.) не приходилось лазить по серверу и править конфиги руками, написал небольшой скрипт добавления хостов, бд, об этом ниже. Но сначала установим связку Nginx + PHP-CGI.

Забегая наперед отмечу что установка с нуля Nginx, PHP-FastCGI, MySQL и phpMyAdmin по предложенной в этой статье схеме методом копировать-вставить заняла у меня не более семи минут. Безусловно, я слепо и не проверяя копировал в консоль команды из этой статьи т.к. сам же их писал и смыла самого себя опасаться нет. Но читателю я рекомендую изучать предлагаемые команды прежде чем использовать их.

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

Часть 0. Приготовление

  1. Обновляем список пакетов:
    apt-get update
  2. Если при выполнении apt-get update вылетает GPG error — загружаем GPG ключи:
    apt-get install debian-archive-keyring
  3. Можем поубирать все необязательные/ненужные пакеты, в т.ч. Апач (см. следующую часть этой статьи).
  4. Пробуем обновиться:
    apt-get update && apt-get upgrade

Часть 1. Web-сервер

Берем чистую систему.
Открываем список всех установленных в системе пакетов:
dpkg --get-selections
Смотрим есть ли конфликтующие. Например, тот же Apache2:
dpkg --get-selections | grep apache
Автоматически сносим всё что похоже на пакет с заданным названием, в моем примере — "apache":
apt-get purge -y `dpkg --get-selections | grep apache | awk '{print $1}'`
Узнаем версию nginx:
apt-cache policy nginx
Переходим к установке:
  • В Debian 6 при помощи apt-get устанавливается nginx версии 0.7 (при том, что на момент написания статьи текущая версия nginx — 1.1.0). Если устраивает — приступаем, если же нет — смотрим следующий пункт. UPD: можно, конечно, включить testing-репозиторий где таки лежит новая версия, но это не всегда хорошее решение.
    apt-get update && apt-get install -y nginx php5-cgi
  • В Debian 5 лежит только 0.6. И если версия 0.7, которая в Debian 6, ещё терпима, то здесь придётся устанавливать из исходников. Зато в результате получим версию на 5 порядков выше!

    Но сначала — требуемые пакеты: libpcre3-dev для PRCE и zlib1g-dev для zlib[7] (а zlib, в свою очередь, нужна для работы gzip), иначе будут вываливаться ошибки:
    ./configure: error: the HTTP rewrite module requires the PCRE library
    ./configure: error: the HTTP gzip module requires the zlib library
    apt-get update && apt-get install -y gcc libpcre3-dev zlib1g-dev
    Теперь сам Engine X, он же nginx. Внимание! Если модуль gzip не нужен — используйте параметр --without-http_gzip_module.
    UPDATE Изменил версию на 1.0.8 — самая последния stable-версия на момент редактирования. Можно также поставить свежайшую development-версию (т.е. 1.1.5) — для этого просто замените номер в команде ниже.
    mkdir /root/nginx && cd /root/nginx && wget http://nginx.org/download/nginx-1.0.8.tar.gz && tar zxf nginx-1.0.8.tar.gz && cd nginx-1.0.8 && ./configure --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --pid-path=/var/log/nginx/nginx.pid --user=www-data --group=www-data && make && make install && rm -rf /root/nginx && cd ~
    Конфиг nginx, как правило, расположен в файле /etc/nginx/nginx.conf. Проверьте наличие в нём строчки
    include /etc/nginx/sites-enabled/*;
    Если её нет — добавьте. Она должна располагаться в конце директивы http {}. В итоге мой конфиг /etc/nginx/nginx.conf, например, выглядел так:
    worker_processes  1;
    pid /var/run/nginx.pid;
    events {
        worker_connections  1024;
    }
    http {
        include mime.types;
        default_type application/octet-stream;
        charset utf-8;
        sendfile on;
        keepalive_timeout 65;
        server {
            listen 80;
            server_name localhost;
            location / {
                root /var/www;
                index index.html index.htm;
            }
        }
        include /etc/nginx/sites-enabled/*;
    }
    Чтобы сохранить предложенный мной конфиг нужно сначала очистить существующий:
    cat /dev/null > /etc/nginx/nginx.conf
    Затем открыть его в редакторе и вставить предложенный вариант конфигурации:
    vi /etc/nginx/nginx.conf
    Когда файл откроется скопируйте конфиг из блока с кодом выше в буффер обмена, перейдите в режим INSERT (нажав соответствующую клавишу) и вставьте текст в консоль (нажав правой кнопкой), затем сохраните. Для сохранения файла в vi нужно нажать клавишу Escape (чтобы выйти из режима редактирования) и ввести :wq, где двоеточие — начало команд, w — write (записать), а q — quit (покинуть редактор) соответственно.

    В моём конфиге используется виректива charset со значением utf-8 — это добавляет charset=utf-8 к отправляемому nginx'ом заголовку Content-Type, в результате чего он принимает вид:
    Content-Type: text/html; charset=utf-8


    Старт-скрипт /etc/init.d/nginx можно взять отсюда: http://wiki.nginx.org/Nginx-init-ubuntu. Я же скопировал из ранее установленного nginx 0.6.
    Открываем файл в редакторе:
    vi /etc/init.d/nginx
    Прежде чем вставлять текст нажмите клавишу Insert.
    #! /bin/sh
    
    ### BEGIN INIT INFO
    # Provides:          nginx
    # Required-Start:    $all
    # Required-Stop:     $all
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: starts the nginx web server
    # Description:       starts nginx using start-stop-daemon
    ### END INIT INFO
    
    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    DAEMON=/usr/local/nginx/sbin/nginx
    NAME=nginx
    DESC=nginx
    
    test -x $DAEMON || exit 0
    
    # Include nginx defaults if available
    if [ -f /etc/default/nginx ] ; then
        . /etc/default/nginx
    fi
    
    set -e
    
    case "$1" in
        start)
            echo -n "Starting $DESC: "
            start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
            --exec $DAEMON -- $DAEMON_OPTS || true
            echo "$NAME."
            ;;
        stop)
            echo -n "Stopping $DESC: "
            start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
            --exec $DAEMON || true
            echo "$NAME."
            ;;
        restart|force-reload)
            echo -n "Restarting $DESC: "
            start-stop-daemon --stop --quiet --pidfile \
            /var/run/$NAME.pid --exec $DAEMON || true
            sleep 1
            start-stop-daemon --start --quiet --pidfile \
            /var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS || true
            echo "$NAME."
            ;;
        reload)
            echo -n "Reloading $DESC configuration: "
            start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \
            --exec $DAEMON || true
            echo "$NAME."
            ;;
        *)
            N=/etc/init.d/$NAME
            echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
            exit 1
            ;;
    esac
    
    exit 0
    Внимание! В скрипте выше выделен путь к nginx. Узнать где он расположен у вас в системе можно следующим образом:
    whereis nginx
    Делаем его исполняемым и добавляем в автозапуск:
    chmod +x /etc/init.d/nginx && update-rc.d nginx defaults
Теперь fastcgi:
apt-get install -y php5-cgi
Nginx и PHP как FastCGI установлен. Никакого дополнительного ПО типа Lighttpd, вопреки рекомендациям многочисленных статей в "блогах сисадминов", я не ставил, заработало и без этого.
Создаем /etc/init.d/php-fastcgi. Откройте редактор vi:
vi /etc/init.d/php-fastcgi
И скопируйте следующий текст:
#!/bin/bash
BIND=127.0.0.1:9000
USER=www-data
PHP_FCGI_CHILDREN=4
PHP_FCGI_MAX_REQUESTS=4096

PHP_CGI=/usr/bin/php-cgi
PHP_CGI_NAME=`basename $PHP_CGI`
PHP_CGI_ARGS="- USER=$USER PATH=/usr/bin PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND"
RETVAL=0

start() {
    echo -n "Starting PHP FastCGI: "
    start-stop-daemon --quiet --start --background --chuid "$USER" --exec /usr/bin/env -- $PHP_CGI_ARGS
    RETVAL=$?
    echo "$PHP_CGI_NAME."
}
stop() {
    echo -n "Stopping PHP FastCGI: "
    pkill $PHP_CGI_NAME
    RETVAL=$?
    echo "$PHP_CGI_NAME."
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo "Usage: php-fastcgi {start|stop|restart}"
        exit 1
        ;;
esac
exit $RETVAL
Делаем файл исполняемым и добавляем его в автозапуск:
chmod +x /etc/init.d/php-fastcgi && update-rc.d php-fastcgi defaults
Отключаем Magic Quotes (выставляем директиву magic_quotes_gpc в положение Off):
sed -i 's/magic_quotes_gpc = On/magic_quotes_gpc = Off/g' /etc/php5/cgi/php.ini
Теперь можем запустить PHP:
/etc/init.d/php-fastcgi start

Часть 2. База данных MySQL

Устанавливаем MySQL и соответствующий модуль для PHP.
Внимание! Если устанавливать MySQL через менеджер пакетов apt-get то будут также установлены зависимости, среди которых exim. Зачем базам данных нужен почтовый сервер — для меня загадка. Поэтому я ставлю через aptitude, позволяющий указать флаг --without-recommends [8]. Вариант с установкой через apt-get всё же приведу для справки.
  • Установка MySQL при помощи aptitude и без зависимостей:
    aptitude --without-recommends install -y mysql-server mysql-client php5-mysql
  • Установка MySQL при помощи apt-get и с зависимостями:
    apt-get install -y --force-yes mysql-server mysql-client php5-mysql
Приготовьтесь неоднократно нажимать Enter для принятия пустого пароля MySQL, или же введите его сразу.
Смотрим нагрузку и расход ресурсов процессора, RAM и т.д.:
top -d 1
Всё бы ничего, но mysqld стал жрать много памяти. Если вы не будете использовать таблицы отличные от MyISAM, отключите лишнее. Для этого надо раскомментировать строчку skip-innodb:
sed -i 's/#skip-innodb/skip-innodb/g' /etc/mysql/my.cnf && /etc/init.d/mysql restart
Эта строчка сбросила потребление MySQL'ом виртуальной памяти со ~150-ти до ~50-ти МБ т.е. в почти 3 раза!
P.S. интересная статья по оптимизации MySQL: http://www.databasejournal.com/features/mysql/article.php/3367871/Optimizing-the-mysqld-variables.htm. Датируется 2004-м годом. Трогательно читать как человек пишет, мол, "предыдущую статью по оптимизации MySQL я написал в 2001-м году, а это было три года назад...", а их-то, лет прошло, уже десять [5].

Создаем пароль для MySQL-root'а (если не сделали это на этапе регистрации — как раз самое время). Вместо 'hackme' введите свой пароль.
mysqladmin -u root password hackme

Часть 3. phpMyadmin

Нужна библиотека mcrypt:
apt-get install -y libmcrypt-dev php5-mcrypt
Установим phpMyadmin:
mkdir /root/pma && cd /root/pma && wget http://sourceforge.net/projects/phpmyadmin/files/phpMyAdmin/3.4.5/phpMyAdmin-3.4.5-all-languages.tar.gz && tar xzvf phpMyAdmin-3.4.5-all-languages.tar.gz && mkdir /usr/share/phpmyadmin && mv /root/pma/phpMyAdmin-3.4.5-all-languages/* /usr/share/phpmyadmin && cd ~ && rm -rf /root/pma
Настроим nginx для работы с phpMyadmin. Сделать это на IP-адресе у меня не полчилось, поэтому придётся задействовать домен. Создадим файл /etc/nginx/sites-enabled/phpmyadmin:
mkdir /etc/nginx/sites-enabled && vi /etc/nginx/sites-enabled/phpmyadmin
Скопируйте в него:
server {
    server_name phpmyadmin.example.com;
    access_log /var/log/nginx/phpmyadmin.example.com.access.log;
    error_log /var/log/nginx/phpmyadmin.example.com.error.log;
    root /usr/share/phpmyadmin;
    include /etc/nginx/common_params;
}
Не забывайте перед вставкой текста переводить редактор vi в режим Insert! иначе часть текста будет потеряна.
Файл common_params будет создан автоматически позже.
Отключим AJAX-окна в phpMyAdmin. Для этого добавьте параметр $cfg['AjaxEnable'] = false; в конфигурационный файл config.inc.php:
echo "<?php \$cfg['AjaxEnable'] = false; ?>">/usr/share/phpmyadmin/config.inc.php
Обратите внимание что обратный слеш в команде выше нужен для ее корректной обработки консолью Linux.

Часть 4. Добавление сайтов, баз данных

Создайте файл /root/random_string.sh: для этого откройте редактор vi...
vi /root/random_string.sh
... и скопируйте в него следующий текст:
#!/bin/bash
if [ $1=="-l" ]
    then
        length=$2
    else
        length="8"
    fi
echo `cat /dev/urandom | tr -dc "a-zA-Z0-9" | fold -w $length | head -1`
Сохраните файл (клавиша Escape -> :wq ).
Этот скрипт понадобится для генерации случайных паролей в shell.
Делаем его исполняемым:
chmod +x /root/random_string.sh

Автоматизируем создание виртуалхостов (т.е. сайтов) в nginx'е, баз данных и директорий для каждого сайта. Пароли и пользователи генерируются случайные, и записываются в файл config.php, расположенный в корне свежесозданного домена. Скрипт спрашивает добавлять ли псевдонимы (алиасы) домена, как назвать папку, в которой будет лежать добавляемый сайт, где разместить эту папку, как назвать базу данных MySQL для сайта, и как назвать добавляемого к ней пользователя. В квадратных скобках пишутся значения, которые будут использованы если вы ничего не введете а просто нажмете Enter.
Создаем файл /root/add_host.sh. Скачать его можно отсюда: add_host.sh. Файл сравнительно большой, поэтому забирать будем wget'ом:
wget -N http://dl.dropbox.com/u/45515900/Shell_Scripts/add_host.sh
Делаем add_host.sh исполняемым:
chmod +x /root/add_host.sh

Добавляем постоянный (permanent) alias (т.е. чтобы можно было запускать одной командой):
echo 'alias addhost="sh /root/add_host.sh"'>>~/.bashrc


Всё, теперь создайте хотя бы один хост чтобы скрипт создал файл /etc/nginx/common_params и nginx запустился.

Использование скрипта:
  • добавление сайта:
    addhost example.com
  • удаление сайта:
    addhost -r example.com

Перезапустим PHP чтобы задействовались новые модули:
/etc/init.d/php-fastcgi restart

Все проверено на Debian 5 Lenny. Вот как выглядит работающий под nginx'ом phpmyadmin:


Если для работы свежесозданного сайта нужно импортировать дамп таблиц из SQL-файла в базу MySQL, то воспользуйтесь phpMyAdmin (см. статью «Загрузить большой дамп БД MySQL в PhpMyAdmin») или зайдите в консоль mysql:
mysql -u root -phackme

Пробел между параметром -p и самим паролем не ставится.
Выберите базу данных в которую нужно загрузить файл .sql:
use %database_name%;

(где %database_name% — название БД в которую производится загрузка файла).
И осуществите импорт:
source /path/to/dump.sql

(где /path/to/dump.sql — путь к .sql-файлу).

Вы также можете создать отдельного пользователя для работы с сайтами по SFTP:
useradd -d /var/www -p `openssl passwd -crypt passsword` -U admin

(где passsword — пароль для создаваемого пользователя, а admin — его имя/логин; параметр -d задает его домашнюю директорию, -p шифрует заданный пароль с помощью openssl, а -U автоматически создает для пользователя одноименную группу, куда сразу же его добавляет).

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

Источники:
  1. http://tomasz.sterna.tv/2009/04/php-fastcgi-with-nginx-on-ubuntu/
  2. https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/
  3. http://www.chaugule.in/linux/shell-script-adding-virtual-hosts-ubuntu-apache/
  4. http://habrahabr.ru/blogs/sysadm/113101/
  5. http://www.databasejournal.com/features/mysql/article.php/3367871/Optimizing-the-mysqld-variables.htm
  6. http://bash.cyberciti.biz/backup/backup-mysql-database-server-2/
  7. http://www.linuxquestions.org/questions/debian-26/compile-php-with-zlib-on-debian-lenny-debian-5-0-a-828200/#post4076297
  8. http://citkit.ru/articles/838/

P.S. я не профессионал в написании shell-скриптов, более того — это моя первая "серьезная" проба. Поэтому если у вас есть пожелания/советы по отптимизации предложенного кода, пишите в комментарии. Спасибо.

March 25, 2011

phpmyadmin arrayobject not found

Fatal error: Class 'ArrayObject' not found in /usr/local/www/phpMyAdmin/libraries/List.class.php on line 16

Enable spl.so and simplexml.so libraries.
cd /usr/ports/lang/php5-extensions && make config && make install clean

March 17, 2011

Get Monday of week in PHP

$date=date('d-m-Y');
echo date('Y-m-d',strtotime("last monday", strtotime('tomorrow', strtotime($date))));

By Shakti Singh and zerkms on «how do i get the 1st day of week from current date..» @ Stackoverflow

February 7, 2011

Regular Expressions - Greedy Patterns

<a href="1.html">some unpredictable text</a>,
<a href="2.html">yet another unpredictable text</a>,
<a href="3.html">text with specialchars</a>

Используйте не .* (что покажется верным на первый взгляд), а .*?, что говорит парсеру забирать не по краям, а с первого совпадения. Тогда результат работы регулярного выражения будет соответствовать ожидаемому.
$html=preg_replace("/<a href="[\d]+.html">.*?</a>/i", $html);

February 5, 2011

Включение записи ошибок PHP в лог файл через .htaccess

This is how we enable logging of PHP errors via .htaccess and disable displaying those errors to visitors.

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

#disable displaying errors to visitors
php_flag display_startup_errors off
php_flag display_errors off
php_flag html_errors off

#enable php errors logging
php_flag log_errors on

# set file to log to
php_value error_log /home/path_to_file/php_errors.log

January 5, 2011

Cut UTF-8 string without breaking words

A PHP function to cut UTF-8 string by spaces without breaking words:
if (!function_exists('cut_utf8_string')) {
function cut_utf8_string($str, $len) {
if (mb_strlen($str, 'UTF-8') > $len){
$str = mb_substr($str, 0, $len, 'UTF-8');
$pos = mb_strrpos($str, ' ', false, 'UTF-8');
if($pos === false) {
return mb_substr($str, 0, $len, 'UTF-8').'…';
}
return mb_substr($str, 0, $pos, 'UTF-8').'…';
}else{
return $str;
}
}
}


(modified function by blog.insicdesigns.com)

August 31, 2010

Как округлить время при помощи PHP?

Возникла необходимость округлить время до ближайшего, скажем, получаса. Решение нашёл в комментариях к статье:
function round_time($ts, $step) {
return(floor(floor($ts/60)/60)*3600+floor(date("i",$ts)/$step)*$step*60);
}

August 19, 2010

ucfirst(), strtolower(), strtoupper и UTF-8

Столкнулся с несовместимостью функций strtolower(), strtoupper(), ucfirst() и ucwords() с кириллицей в кодировке UTF-8. Некоторым из них есть альтернатива, причем доступная штатными средствами PHP: функция mb_convert_case().
string mb_convert_case ($str, $mode [, $encoding])

В качестве $mode может выступать:

  • MB_CASE_UPPER — все символы ЗАГЛАВНЫМИ буквами (аналог strtoupper() и mb_strtoupper())

  • MB_CASE_LOWER — все символы строчными буквами (аналог strtolower() и mb_strtolower())

  • MB_CASE_TITLE — первая буква в каждом слове заглавная (аналог ucwords())

По умолчанию, $mode = MB_CASE_UPPER.

В качестве $encoding может выступать название кодировки, например "utf-8" или "windows-1251". По-умолчанию, $encoding = mb_internal_encoding().

July 7, 2010

PHP: считать Excel без XMLReader и XMLWriter

Возникла необходимость работать с Excel-файлами (а именно — парсить) через PHP на сервере без поддержки XMLReader и XMLWriter.

Нашел проект http://code.google.com/p/php-excel-reader/, но без сложностей не обошлось — скрипт отказался читать русскую кодировку Windows-1251 из Excel 2007.

Все решил благодаря radostinstefanov'у, автору этого комментария, порекомендовавшему в файле excel_reader2.php заменить 635-ю строчку:
$val = htmlentities($val,ENT_NOQUOTES, 'Windows-1251');

November 5, 2009

Как работает Facebook, презентация.

Конечно же нам не дадут исходники Facebook'а, но кое-что интересное расписано. С этой презентацией стоит ознакомиться.

October 22, 2009

php5-5.2.9 has known vulnerabilities при установке PHP5


root# cd /usr/ports/lang/php5
root# make install clean
===> php5-5.2.9 has known vulnerabilities:
=> php5 -- Multiple security issues.
Reference:
=> Please update your ports tree and try again.
*** Error code 1

Stop in /usr/ports/lang/php5.
*** Error code 1

Stop in /usr/ports/lang/php5.


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

root# cd /usr/ports/lang/php5
root# make DISABLE_VULNERABILITIES=yes install clean

May 10, 2009

Как добавить поиск с автозаполнением Яндекса на сайт

Функция автозаполнения на сайте состоит из трёх компонентов: клиентской части (стили и скрипты, запрашивающие и отрисовывающие предположения по мере набора текста), серверной части, осуществляющей поиск в БД и/или запрос к поисковой системе, и собственно интерфейса поисковой системы (если решено её использовать). В своё время в качестве последнего компонента я решил выбрать Яндекс.

Я подсмотрел какие AJAX-запросы и куда отправляются по мере набора запроса на сайте Яндекса. Единственная сложность возникла лишь в том, что он возвращает данные в нестандартном формате, который становится уж более-менее ликвидным JSON'ом после замысловатых манипуляций, любезно подсказанных Alternator'ом на форуме nulled.ws, за что ему большое спасибо.

На клиентской стороне было решено поставить Autocompleter, разработанный Digitarald'ом. Безусловно, можно написать и собственный обработчик, но и этот вполне справляется.

Серверная часть. Для начала получим предположения, затем выполним над ними манипуляции, и в результате получим массив PHP. Получился эдакий скрипт преобразования предположений Яндекса в PHP массив:
<?php
$file=file_get_contents("http://suggest.yandex.ru/suggest-ya.cgi?ct=text/html&part=".urlencode($_REQUEST['value'])."&v=2");
$file=strrchr($file,"[");
$file=substr($file,0,strpos($file,"]"))."]";
$file=iconv("CP1251","UTF-8",$file);
$data=json_decode($file);
header("Content-type: text/html; charset=utf-8");
foreach($data as $key=>$val){
print '<li>'.$val.'</li>';
}
?>

В скрипт передаётся переменная $value, в которой содержится набираемый текст. Она ловко подставляется в запрос, по которому мы получаем список предположений. Затем, мы преобразовываем их в формат JSON, понятный функции json_decode(), посредством которой мы и разобьём всё это богатство в полноценный PHP-массив. Однако, прежде чем это сделать, в четвёртой строке полученные в windows-1251 данные перекодируются в понятную json'у utf-8. Шестой строкой объявляется та самая utf-8, начиная со следующей строки мы можем свободно в зависимости от ситуации управлять массивом. Как я уже сказал выше, этот скрипт передаёт данные autocompleter'у, посему адаптируем их под него.
Ну и напоследок классика жанра в качестве примера работы скрипта на одном моём сайте:


UPD от 15.02.2011: Уважаемый Глеб, обновил пост. Ещё подробнее дополню позже. Спасибо за отзыв!