Вер 032013
 

Установить geoip для Apache2 Debian

apt-get install -y libapache2-mod-geoip php5-geoip

Проверяем наличие модулей в Apache (путь может отличатся, проверить наличие файлов по указанным путям):

LoadModule geoip_module /usr/lib/apache2/modules/mod_geoip.so
GeoIPEnable On
GeoIPDBFile /usr/share/GeoIP/GeoIP.dat
GeoIPDBFile /usr/share/GeoIP/GeoLiteCity.dat

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

#nano /etc/apache2/mods-enabled/geoip.load
#nano /etc/apache2/mods-enabled/geoip.conf
#nano /etc/apache2/mods-available/geoip.conf

Копируем регулярно обновляемую базу городов:

# wget http://www.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
# gunzip GeoLiteCity.dat.gz
# mv GeoLiteCity.dat /var/lib/GeoIP/GeoLiteCity.dat
# wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
# gunzip GeoIP.dat.gz
# mv GeoIP.dat /var/lib/GeoIP/GeoIP.dat

Перегружаем Apache:

/etc/init.d/httpd restart
Сер 282013
 

Безопасность в PHP. Проверяем типы переменных.

Известно, что PHP относится к слабо типизированным языкам программирования. Что же это значит? Мы можем проводить различные операции между переменными разного типа, и получать «что-то» на выходе. С одной стороны, это удобно. Строка превращается в целое число, целое число может стать объектом, а объект плавно перейти в массив. Но любой программист при создании сайтов на языке php рано или поздно сталкивается с тем, что во многих случаях типы переменных следует приводить (или, как минимум, проверять перед началом использования).

Примечание: такое строгое отношение к переменным связано с тем, что большинство скриптов на php подвержено нездоровому вниманию злоумышленников с целью подстановки параметров различного типа в строке запроса сайта. Это может привести от генерации ошибки с раскрытием пути, по которому находится файл, вплоть до обхода некоторых ограничений. Например: повышения прав пользователя в ранних версиях WordPress путём передачи имени пользователя как массива, а не строки, в скрипт аутентификации в admin панели, или же подстановки в строку запроса вместо целых чисел (например, id новости) – специально сформированных запросов sql с целью несанкционированного доступа, создание на сайте так называемых sql инъекций.

Так что, по возможности, проверяйте типы переменных перед их использованием или явно приводите их во время программирования на PHP.

Приведём функции, которые будут нам полезны с целью контроля безопасности входных данных:

(string) trim($str) – Принимаемые параметры: $var – строка для удаления пробельных символов и символов разрыва строки. Обрезает символы конца строки и пробельные символы вначале и конце строки, возвращает переменную строкового типа.
(bool) is_string($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var строкой? Возвращает: true: если является, false: если не является.
(bool) is_numeric($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var набором цифр ? Возвращает: true: если является, false: если не является.
(bool) is_float($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var вещественным числом? Возвращает: true: если является, false: если не является.
(bool) is_array($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var массивом? Возвращает: true: если является, false: если не является.
(bool) is_int($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var целым числом? Возвращает: true: если является, false: если не является.
(bool) isset($var) – Принимаемые параметры: $var – переменная для проверки. Проверяет, существует ли переменная $var (любой тип данных), если существует, возвращает true, иначе возвращает false.
(bool) is_resource($var) – Принимаемые параметры: $var – переменная для проверки. Является ли переменная $var ресурсом? Возвращает: true: если является, false: если не является.
(bool) empty($var) – Принимаемые параметры: $var – переменная для проверки. Проверяет, пуста ли существующая переменная или нет, возвращаемые значения: true для значений (“”, 0, 0.0, “0”, NULL,FALSE,array()), false в остальных случаях
Пример использования при программировании в PHP скриптах:

Передаем в форму через пост запрос массив данных $user, полученный при регистрации пользователя:

<?php
        $user               = $_POST["user"];
        $sizeof             = sizeof($user);
        $username           = "";
        $password           = "";
        $user_id            = 0;
if(is_array($user) && $sizeof && isset($user["username"]) && isset($user["password"];)){
        $username           = trim($user["username"]);
        $password           = trim($user["password"]);
        $password           = is_int($user["user_id"]) ? $user["user_id"]: 0;
}
?>

Еще один пример использования в PHP скриптах:

Зададим вопрос к базе данных:

<?php
        $query              = "SELECT `news_text` FROM `news` WHERE `news_id`=5";
        $p                  = mysql_query($query);
        if($p && is_resource($p)){
            list($news_text)= mysql_fetch_row($p);
        }
?>

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

Сер 282013
 

Безопасность в PHP. Явное приведение типов.

Операторы прямого приведения данных.

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

Приведём несколько операндов, которые позволяют сделать это легко и без особых усилий при программировании на языке PHP, а так же поговорим о функциях приведения типов.

Примечание автора: операторы не изменяют тип переменной, но делает попытку приведения переменной и возвращает значение соответствующего типа.

$var = (int)$some_var, (integer)$some_var – явное приведение переменной $some_var к целочисленному типу
$var = (float)$some_var, (real)$some_var, (double)$some_var – явное приведение переменной $some_var к вещественному типу
$var = (array)$some_var – явное приведение переменной $some_var к массиву
$var = (string)$some_var – явное приведение переменной $some_var к массиву
$var = (bool)$some_var, (boolean)$some_var – явное приведение переменной $some_var логическому значению (true,false)
$var = (object)$some_var – явное приведение переменной $some_var к объекту
Новшества PHP 5:

(unset)$some_var – присвоение типа NULL, уничтожение переменной, освобождение памяти.
Новшества PHP 5.2.1:

(binary) $some_var – приведение переменной к бинарному представлению.
Примеры использования:

Рассмотрим передачу id статьи через массив GET news.php?id=23

Вариант подготовки параметра для безопасной передачи в запрос к базе данных:

?
1
$news_id = (int)$_GET[“id”];
Еще одной интересной функцией является abs, которую советую применять при фильтрации входных данных для заведомо неотрицательных величин, например уникального идентификатора статьи, который является по умолчанию числом уникальным, положительным, с auto increment:

abs($var) – Принимаемые параметры: $var – переменная целого (или вещественного) типа. Возвращает абсолютное по модулю число: для всех отрицательных величин возвращается положительное, для всех положительных возвращается просто значение переменной

Рассмотрим предыдущий пример в новом ракурсе:

Так как integer может быть как положительным, так и отрицательным целым числом, перепишем предыдущий пример с использованием функции abs:

Вариант подготовки параметра для безопасной передачи в запрос к базе данных:

?
1
$news_id = abs((int)$_GET[“id”]);
Функции явного приведения данных.

(int) intval($var,[$base = 10]) – Принимаемые параметры: $var – переменная для приведения типов. Приводит переменную к целочисленному типу. Возвращает переменную, приведенную к целочисленному типу, в случае с массивом 0 если массив пуст, 1 – если есть элементы, в случае со строкой: если удаётся преобразовать к целому числу – то целое число, или 0 в обратном случае.

$base = основание, десятичная система исчисления по умолчанию.

(float) floatval($var) – Принимаемые параметры: $var – переменная для приведения типов. Приводит переменную к вещественному типу. Возвращает переменную, приведенную к вещественному типу, в случае неудачи возвращает 0.
(bool) settype(&$var, string $type ) – Принимаемые параметры: $var – переменная для приведения типов, $type – к какому типу привести переменную:

“boolean”,”bool” – приведение типа переменной к логическому значению (true, false),
“integer”,”int” – приведение типа переменной к целому значению,
“float”,”double” – приведение типа переменной к вещественному значению,
“string” – приведение типа переменной к строковому значению,
“array” – приведение к массиву,
“object” – приведение типа переменной к объекту,
“null” – приведение типа переменной к типу NULL (освобождение занимаемой памяти).
Производит прямое приведение переменной к значению указанного типа. В случае удачи возвращает true, иначе – false.

Общирный пример практического использования вышеописанных функций при программировании на языке PHP:

Приведём пример безопасного запроса к базе данных с получением id новости из строки запроса:

<?php
$id             = isset($_GET["id"]) ? abs(intval($_GET["id"])) : 0;
If(!empty($id)){
    $q          = "SELECT `news_text` FROM `news` WHERE `news_id`=".$id;
    $p          = mysql_query($q);
    if($p && is_resource($p)){
    list($text) = mysql_fetch_row($p);
    echo $text;
}
}
?>
Сер 282013
 

Изучаем PHP. Пишем parser похожих запросов Yandex.

В жизни программиста очень часто возникает задача получения полезной информации из других источников. Это может быть всесторонняя оценка цен конкурентов по определённому товару, сбор изображений и описаний для автоматизации процесса загрузки новых позиций в интернет – магазин, поиск аудио и видео – информации и многое другое.

Здесь на выручку нам так называемый «скрипт – парсер». В задачу которого входит получение и проверка информации, поиск нужных фрагментов и исключение «мусора».

Рассмотрим на примере так называемый «parser – spider» (парсер – паук), в задачу которого будет входить сбор информации со страниц yandex для поиска похожих ключевых слов, которые мы будем использовать в раскрутке нашего проекта. И напишем данный парсер на PHP

Как же нам может помочь parser?
Мы получаем список сходных фраз, которыми пользуются посетители яндекса при поиске нашей тематики, и в дальнейшем сможем ориентироваться на них при написании материалов на нашем сайте, оцениваем спрос к тематике нашего сайта. А так же это даст нам возможность определить поисковые запросы с меньшей конкурентной борьбой сходной тематики, чтобы выйти по ним в ТОП поисковой системы Yandex. Как следствие, благодаря этой информации мы расширим аудиторию нашего проекта.

А откуда мы можем получить информацию по похожим поисковым запросам Yandex?
Дело в том, что при поиске Яндекс подсказывает сам, отображая блок внизу страницы: Вместе с «[фраза для поиска]» ищут – несколько подобных поисковых запросов.

Вверху в левой части каждой страницы указывает, сколько нашлось вариантов, например:
создать сайт – Нашлось 104 млн ответов

Итак, какие же задачи поставим перед скриптом – парсером?

Возможность рекурсивного парсинга похожих поисковых запросов с определением уровня вложенности
Если не найдено ни одного похожего запроса по указанной фразе (а такое тоже бывает), попробуем просмотреть запросы по её сокращению:

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

Обходить парсер будет не только одну фразу, а займётся обработкой целого списка из фраз (передадим массивом)
Сохранение найденных поисковых фраз и их встречаемостью в файл
Нюансы:

Поставим задержку на опрос поисковой системы (не меньше 2 секунд, а то и до 30 секунд в случайном порядке) на каждый поисковый запрос. Мы же не хотим, чтобы Яша нас «отругал» за плохое поведение?

<?php
function getURIContent($url){
        $tuCurl                         = curl_init();
        $tuData                         = '';
        if($tuCurl && is_resource($tuCurl)){
                $opts                   = array(
                CURLOPT_URL             => $url,
                CURLOPT_HTTPGET         => 1,
                CURLOPT_HEADER          => 0,
                CURLOPT_RETURNTRANSFER  => 1,
                CURLOPT_FOLLOWLOCATION  => 1,
                CURLOPT_BINARYTRANSFER  => 1,
                CURLOPT_AUTOREFERER     => 1,
                CURLOPT_CONNECTTIMEOUT  => 90,
                CURLOPT_USERAGENT       => $_SERVER['HTTP_USER_AGENT'],
                CURLOPT_COOKIEJAR       => dirname(__FILE__)."/cookie.txt",
                CURLOPT_COOKIEFILE      => dirname(__FILE__)."/cookie.txt",
                CURLOPT_REFERER         => $url
                                         );
        foreach($opts as $key=>$value){
          curl_setopt($tuCurl,$key,$value);
        }
        $tuData   = curl_exec($tuCurl);
        curl_close($tuCurl);
        }
        return $tuData;
}
function parseRecursive($question,$max_depth,$first = true){
    global $time_wait;
    $time_wait  = $time_wait < 2 ? 2 : $time_wait;
    $rand       = mt_rand($time_wait, $time_wait + 30);
    sleep($rand);
    $question   = urlencode($question);
    $where      = 'http://yandex.ua/yandsearch?text='.$question;
    $content    = getURIContent($where);
    $found      = false;
    if(!empty($content)){
        $how_many= array();
        preg_match_all('~<strong[^>]*?class="b-head-logo__text"[^>]*?>(.*?)</strong>~is',$content,$how_many);
        $numbers= '';
        if(is_array($how_many)
                     && isset($how_many[1][0])
                           && !empty($how_many[1][0])){
            $numbers   = trim($how_many[1][0]);
            $numbers   = preg_replace("~<br[^>]*?>~is",' ', $numbers);
            $numbers   = str_ireplace("&nbsp;",' ', $numbers);
            $numbers   = str_ireplace("\r\n",' ', $numbers);
            $numbers   = str_ireplace("\r",' ', $numbers);
            $numbers   = str_ireplace("\n",' ', $numbers);
            $numbers   = str_ireplace("\t",' ', $numbers);
            $numbers   = str_ireplace("\p",' ', $numbers);
            $numbers   = str_ireplace("\b",' ', $numbers);
            $numbers   = html_entity_decode($numbers,ENT_QUOTES,'UTF-8');
            $numbers   = strip_tags($numbers);
        }
        if(!empty($numbers)){
            $numbers = urldecode($question) . ' - ' . $numbers. "\n";
            $fp = fopen(WHERE_TO_SAVE,'a+');
            if($fp && is_resource($fp)){
                echo 'ADDING '.$numbers. "<br />\n";
                flock($fp,LOCK_EX);
                fwrite($fp,$numbers);
                flock($fp,LOCK_UN);
                fclose($fp);
            }
        }
        $related= array();
        $links  = array();
        preg_match_all('~<table[^>]*?class="b-related__table"[^>]*?>(.*?)</table>~is',$content,$related);
        /* <a[^>]*?href=("|\')([^"\']*?)(\1)[^>]*?>(.*?)</a> */
        if(is_array($related[1]) &&
                    isset($related[1][0])
                        && $max_depth){
            --$max_depth;
            preg_match_all('~<a[^>]*?href=("|\')([^"\']*?)(\1)[^>]*?>(.*?)</a>~is',$related[1][0],$links);
            if(is_array($links)
                           && isset($links[2])
                                && isset($links[4])
                                     && sizeof($links[2])
                                && sizeof($links[4])
                                     && $max_depth){
                $sizeof     = sizeof($links[4]);
                for($i = 0; $i < $sizeof; $i ++){
                    $text   = '';
                    $text   = trim($links[4][$i]);
                    $text   = preg_replace("~<br[^>]*?>~is",' ', $text);
                    $text   = str_ireplace("&nbsp;",' ', $text);
                    $text   = str_ireplace("\r\n",' ', $text);
                    $text   = str_ireplace("\r",' ', $text);
                    $text   = str_ireplace("\n",' ', $text);
                    $text   = str_ireplace("\t",' ', $text);
                    $text   = str_ireplace("\p",' ', $text);
                    $text   = str_ireplace("\b",' ', $text);
                    $text   = html_entity_decode($text,ENT_QUOTES,'UTF-8');
                    $text   = strip_tags($text);
                    if(!empty($links[2][$i]) && !empty($text)){
parseRecursive($text,$max_depth,false);
                    }
                }
            }
        } elseif (is_array($related[1]) && $max_depth){
            $question    = urldecode($question);
            $words= array();
            $words= explode(' ',$question);
            $sizeof= sizeof($words);
            $words= array_map('trim',$words);
            --$sizeof;
            $words= array_slice($words,0,$sizeof);
            $question    = join(' ',$words);
            if(strlen($question) > 4){
                --$max_depth;
                if($max_depth){
                parseRecursive($question,$max_depth,false);
                }
            }
        }
    }
}
ini_set('max_execution_time',999999);
ini_set('max_input_time',999999);
$max_depth      = 2;
$time_wait      = 2;
$questions      = array('создать сайт',
                          'сайт на joomla',
                          'создать сайт Харьков',
                          'сайт на wordpress');
define('WHERE_TO_SAVE',dirname(__FILE__).'/prases.txt');
if(!is_file(WHERE_TO_SAVE)){
    $fp  = fopen(WHERE_TO_SAVE,'w+');
    if($fp && is_resource($fp)){
        fclose($fp);
    }
    if(is_file(WHERE_TO_SAVE) && !is_writable(WHERE_TO_SAVE)){
        chmod(WHERE_TO_SAVE,0777);
    }
}
if(is_file(WHERE_TO_SAVE)){
    $fp  = fopen(WHERE_TO_SAVE,'w+');
    if($fp && is_resource($fp)){
        fclose($fp);
    }
}
if(is_file(WHERE_TO_SAVE) && is_writable(WHERE_TO_SAVE)){
    $sizeof     = sizeof($questions);
    for($i = 0; $i < $sizeof; $i ++){
        parseRecursive($questions[$i],$max_depth);
    };
};
?>

Итак, что же здесь происходит:

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

В переменной $max_depth укажем, сколько вложенных уровней «обходить» (на какую глубину «погружаться») для сбора похожих фраз.

Например: $max_depth = 2; – находим похожие поисковые фразы, переходим по каждой из них, и собираем результаты по похожим уже на них поисковым фразам.

Совет: не делайте слишком большим уровень вложенности. Иначе сильно отклонитесь от первоначальной тематической фразы.

Переменной $time_wait указываем, сколько секунд ждать до следующего запроса к поисковику.

Обратите внимание – 2 секунды – это минимальное значение. Иначе сайт выдаст вам капчу или наложит бан по ай-пи адресу.

В массив $questions = array(‘создать сайт’,’сайт на joomla’,’создать сайт Харьков’,’сайт на wordpress’); – списком строк через запятую добавляем запросы для обработки parser ом.

Далее, определяем, существует ли файл prases.txt – в который мы и добавим найденный результат. Если не существует, создаём его и делаем доступным на запись.

После этого обходим массив $questions и передаём каждый запрос функции parseRecursive вместе с уровнем вложения.

(void) function parseRecursive($question,$max_depth,$first = true): принимает одну поисковую фразу (string)$questions[$i] и уровень вложенности поиска $max_depth, а так же неявный флаг (bool) $first, для определения, в какой раз подряд вызывается функция (для чего, рассмотрим ниже).

В функции мы кодируем для возможности передачи в виде запроса нашу ключевую фразу с помощью urlencode и получаем содержимое запроса в функции getURIContent, основанной на curl. После чего регулярным выражением ~]*?class=”b-head-logo__text”[^>]*?>(.*?)~is узнаём, сколько раз встречается данный поисковой запрос.

Очищаем полученный запрос от html и спец символов. Помещаем выражение и количество найденных страниц по этой поисковой фразе в файл prases.txt.

Исследуем содержимое полученной страницы на предмет содержания «похожих поисковых запросов» ~]*?class=”b-related__table”[^>]*?>(.*?)

~is.

Если похожие поисковые фразы найдены, извлекаем ссылки на них и их анкоры: ~]*?href=(“|’)([^”‘]*?)(\1)[^>]*?>(.*?)~is

При этом уменьшая на единицу наш уровень вложенности –$max_depth

Если уровень вложенности позволяет (отличен от нуля), передаём анкоры (текст, заключённый в тег a) рекурсивно в функцию parseRecursive. Обрабатываем следующий уровень.

Если же на странице не было найдено по указанной поисковой фразе ни одного результата, и вызов функции parseRecursive был осуществлён впервые (неявный флаг $first со значением true) то проверяем, содержит ли слова поисковый запрос? (Разбиваем по пробельному символу фразу на слова). Исключаем последнее слово, и передаём полученное словосочетание заново функции parseRecursive.

Примечание автора: для получения содержимого страниц по указанным поисковым запросам используется функция getURIContent, которая принимает адрес страницы и возвращает её содержимое.

Её работа основывается на curl, что даёт ряд преимуществ перед url wrapper ами функции file, fopen, file_get_contents и т.п.:

Более быстрый процесс получения информации
Передача referrer, cookie, user agent
Управление временем ожидания (таймаутом) на получение информации
Возможность корректно перейти по всем пере направлениям (Location:), если таковые встречаются
Бинарная передача данных
В следующих статьях мы поведаем Вам более подробно о возможностях функции curl и приведём другие интересные примеры её использования.

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

Убрать дубли фраз, обнаруженные при поиске.
Для получения более достоверных вариантов проверять, встречается хотя бы одно слово из целевого поискового словосочетания, заданного в $questions в фразах, обнаруженных при многоуровневом обходе страниц
Каждый результат поиска записать в отдельный файл.
Визуализировать результат при помощи графиков
Задание для тех, кто учит parser ы на PHP:
Для выполнения этой же задачи напишите парсер, который обходит wordstat yandex

Сер 282013
 

Изучаем PHP. Тонкости экстремальной отладки скриптов.

Иногда сайт ведёт себя неадекватно. А разработка сайта всё затягивается. Очень трудно разобраться, в чём проблема, когда вообще выпадает Белый лист, и нигде нет никаких ошибок.

Дело в том, что многие проекты находятся на обычных хостингах, администрация которых действует по правилу «меньше знаешь, лучше спишь». Обычно под выполнение скриптов зарезервированы довольно скудные ресурсы. А вывод ошибок подавляется, или перенаправляется. Как же быть в этой ситуации?

Чем нам может помочь PHP при отладке скриптов?
Но, как показывает практика, для разработчиков администраторы предусматривают переопределение многих параметров.

Примечание автора: здесь будут рассмотрены подходы, которые касаются самого языка программирования PHP. Этот способ является более универсальным, чем рассмотрение методов, связанных с конфигурацией определённого серверного П.О.

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

<?php
ini_set("error_reporting",E_ALL & ~E_NOTICE);
ini_set("display_errors",1);
ini_set("error_log","");
function errPrint(){
    print_r(error_get_last());
}
register_shutdown_function("errPrint");
 ...
?>

Примечание: подход актуален для полного перехвата даже критических ошибок при отладке в PHP версии, равной или выше 5.2. В ином случае обратите внимание на эту часть статьи.

Разберём приведённый выше PHP код:

Описание функции ini_set:

(string) ini_set ($server_var_name,$server_var_value) – устанавливает значение $server_var_value для переменной среды выполнения PHP $server_var_name, в случае удачного присвоения возвращает значение переменной, иначе возвращает false.

Примечание автора: со всеми переменными окружения, значение которых можно переопределить, Вы можете ознакомиться здесь

Для начала мы включаем отображение ошибок ini_set(“display_errors”,1).

После этого мы устанавливаем значение для вывода ошибок “error_reporting”, E_ALL & ~E_NOTICE, равное отображению всех ошибок, но без подсказок E_NOTICE.

Примечание автора: E_NOTICE – подсказки, как «правильно программировать» на усмотрение PHP интерпретатора. Зачастую, подсказки могут быть очень полезны при отладке сайта в целом, но при отладке в критических ситуациях они, в основном, несут малополезную информацию да ещё и в огромном количестве.

Затем мы переопределяем вывод ошибок в стандартный поток браузера посетителя ini_set(“error_log”,””);.

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

Всё бы хорошо, но есть определённые критические ошибки, связанные с работой самого интерпретатора, которые могут вызвать прерывание в работе скрипта, и не будут выведены в поток обозревателя посетителя. Вот такой загадочный он наш PHP.

Но, благо, с версии 5.2 (о чудо!), заявленная ещё в php 4, функция – перехватчик register_shutdown_function заработала!.

Описание функции register_shutdown_function:

(void) register_shutdown_function ( callback $function ) – принимает имя функции $function, которая выполняется по завершению исполнения кода, даже во время возникновения критической ошибки с прерыванием работы скрипта.

Примечание автора: в данном случае функция $function должна быть определена (или подключена) до передачи в register_shutdown_function.

Определим функцию errPrint, которая распечатает последнюю ошибку print_r(error_get_last()) после завершения скрипта. И передадим название этой функции обработчику register_shutdown_function:

<?php
register_shutdown_function("errPrint");
?>

Описание функции error_get_last:

(array) error_get_last ( void ) – возвращает в виде массива информацию о последней случившейся ошибке. Массив содержит ключи “type” – тип ошибки, “message” – подробное сообщение об ошибке, “file” – путь к файлу, в котором произошла ошибка, “line” – номер строки, в которой произошла ошибка и номер символа.

А в нашем случае, последняя «случившаяся» ошибка – и есть та самая, которая приводит к остановке выполнения скрипта.

Благодаря такому подходу можно будет «отловить» буквально любую ошибку и получить детальную информацию о ней.

Не забывайте отключать информацию о выводе ошибок в браузер пользователя!
Но, после отладки php скрипта и устранения критической ошибки, не забывайте отключить полностью информацию о выводе ошибок в браузер пользователя. Связано это как с соображениями безопасности (многие ошибки и предупреждения могут доступной для злоумышленника конфиденциальную информацию, например: об абсолютном пути на сервере к Вашим скриптам, их местоположении и т.п.), а так же могут препятствовать нормальной инициализации сессий и работе функций, передающих заголовки header, так и с эстетической точки зрения не стоит выводить много малопонятной информации в браузер посетителя Вашего сайта. Для этого можно подавить полностью вывод ошибок. Или же переопределить вывод ошибок в файл с помощью функции ini_set(“error_log”,”путь к файлу ошибок, доступному на запись”);. При этом желательно, чтобы этот файл находился вне папок Вашего сайта, или доступ к файлу извне был полностью ограничен.

Примечание автора:

Приведём код полного подавления ошибок:

<?php
ini_set("error_reporting",0);
ini_set("display_errors",0);
ini_set("error_log","путь к файлу хранения ошибок");
?>

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

<?php
@mkdir("/site/cahce",0777);
?>

Хотя, настоятельно рекомендую Вам вместо этого «ленивого» способа с помощью условных операторов обработать все ситуации, которые приводят к появлению данной ошибки. Это добавит стабильности и увеличит «запас прочности» Ваших скриптов во время возникновения внештатных ситуаций, и оставит меньше «почвы для размышления» злоумышленникам.
Я всё равно вижу белую страницу!

Здесь может быть несколько причин:

Данный подход универсален для php 5.2 и выше. Для того, чтобы узнать версию Вашего PHP интерпретатора, добавьте в Ваш скрипт вызов функции phpinfo();
В первых строках будет описана версия PHP.

Если версия php ниже 5.2:

Для более ранних версий php Дмитрием Котеровым было разработано гибкое решение по перехвату критических ошибок, с ним Вы можете ознакомиться здесь

Если у Вас версия PHP выше 5.2, но белая страница всё таки продолжает появляться с настроенным выводом ошибок, значит Ваш скрипт автоматически завершается сервером (Вы превышаете ресурсы процессорного времени, происходит не обрабатываемая критическая ситуация в одной из библиотек PHP и т.п.). В данном случае советую Вам обратиться к хостеру за предоставлением логов ошибок и нагрузки.

Но если Вы не нашли ничего полезного и в логах, предоставленных хостером – самая последняя инстанция: пошаговая отладка с помощью функций принудительной остановки exit или die в «проблемных местах» и распечаткой нужной информации обнаружить «неустойчивый код», и переписать его с учётом меньшей нагрузки.

Приведём пример выполнения пошаговой отладки с простой установкой флагов состояний:

<?php
function add_user_avatar($user_id,$avatar_name,$watermark){
...
};
echo "вызов функции add_user_avatar";
add_user_avatar($user_id,$avatar_name,$watermark);
echo "окончание работы функции add_user_avatar";
die();
?>

Если же Вы увидели белый лист раньше отображения «окончание работы функции add_user_avatar», значит проблему стоит искать именно в ходе выполнения данной функции add_user_avatar. Если же нет, мы «передвигаем» функцию die или exit ниже, обозначая вызов и завершение далее следующих функций подобным способом.

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