(PHP 7, PHP 
Error::getLine — Получает номер строки, в которой произошла ошибка
Описание
final public Error::getLine(): int
Список параметров
У этой функции нет параметров.
Возвращаемые значения
Возвращает номер строки, в которой произошла ошибка.
Примеры
Пример #1 Пример использованияError::getLine()
<?php
try {
throw new Error("Какое-то сообщение об ошибке");
} catch(Error $e) {
echo "Ошибка создана в строке: " . $e->getLine();
}
?>
Результатом выполнения данного примера
будет что-то подобное:
Ошибка создана в строке: 3
There are no user contributed notes for this page.
I am about to finish one project and I noticed that I am getting errors in my error_log file. I have been getting this error when I load my index.php file and in one reload I am getting 21 line lines of the error code.
I have tried debugging from the header.php file. The craziest thing is don’t get any error until I load header.php file but when I call header.php in index.php file I get the errors. So I tried to catch the errors by try and catch of from PDO. In the error log file, I am getting error message so I changed my code from this Select Query: ".$e->getMessage() to Select Query: ".$e->getFile() and Select Query: ".$e->getLine(). But, doing this I got line number where the error is getting in not the line it was thrown from.
I think I have a problem in my select query in databse.php. Following is my select query code:
final protected function select($args = array(), $is_die = false){
try {
$this->sql = "SELECT ";
if (isset($args['fields'])) {
if (is_array($args['fields'])) {
$this->sql .= implode(', ', $args['fields']);
} else {
$this->sql .= $args['fields'];
}
} else {
$this->sql .= " * ";
}
$this->sql .= " FROM ";
if (!isset($this->table) || empty($this->table)) {
throw new Exception("Table not set");
}
$this->sql .= $this->table;
/*Join Query*/
if (isset($args['join']) && !empty($args['join'])) {
$this->sql .= " ".$args['join'];
}
/*Join Query*/
if (isset($args['where']) && !empty($args['where'])) {
if (is_array($args['where'])) {
$temp = array();
foreach ($args['where'] as $column_name => $data) {
if (!is_array($data)) {
$data = array(
'value' => $data,
'operator' => '=',
);
}
$str = $column_name.' '.$data['operator'].' :'.str_replace('.', '_', $column_name);
$temp[] = $str;
}
$this->sql .= " WHERE ".implode(' AND ', $temp);
} else {
$this->sql .= " WHERE ".$args['where'];
}
}
/*Group*/
if (isset($args['group_by']) && !empty($args['group_by'])) {
$this->sql .= " GROUP BY ".$args['group_by'];
}
/*Group*/
/*Order*/
if (isset($args['order_by']) && !empty($args['order_by'])) {
$this->sql .= " ORDER BY ".$args['order_by'];
} else {
$this->sql .= " ORDER BY ".$this->table.".id DESC";
}
/*Order*/
/*Limit*/
if (isset($args['limit']) && !empty($args['limit'])) {
if (is_array($args['limit'])) {
$this->sql .= " LIMIT ".$args['limit'][0].",".$args['limit'][1];
} else {
$this->sql .= " LIMIT ".$args['limit'];
}
}
/*Limit*/
$this->stmt = $this->conn->prepare($this->sql);
if (is_array($args['where']) || is_object($args['where'])){
foreach ($args['where'] as $column_name => $data) {
$value = is_array($data) ? $data['value'] : $data; //check if passed where statement was an array, fetch value if so
if (is_int($value)) {
$param = PDO::PARAM_INT;
}elseif (is_bool($value)) {
$param = PDO::PARAM_BOOL;
}elseif (is_null($value)) {
$param = PDO::PARAM_NULL;
}else {
$param = PDO::PARAM_STR;
}
if ($param) {
$this->stmt->bindValue(":".str_replace('.', '_', $column_name), $value, $param);
}
}
}
if ($is_die) {
echo $this->sql;
debugger($this->stmt);
debugger($args, true);
}
$this->stmt->execute();
$data = $this->stmt->fetchAll(PDO::FETCH_OBJ);
return $data;
} catch (PDOException $e) {
error_log(
date('Y-m-d h:i:s A').", Select Query: ".$e->getMessage()."\r\n"
, 3, ERROR_PATH.'/error.log');
return false;
} catch (Exception $e) {
error_log(
date('Y-m-d h:i:s A').", General: ".$e->getMessage()."\r\n"
, 3, ERROR_PATH.'/error.log');
return false;
}
}
Is there a possible way to get the line number of a file where the error is thrown form using try catch or any other?
I am using a code where code is in code.php and the user defined functions are stored in function.php.
The functions are called multiple times.
While executing the code in code.php — I got the following error message.
Warning: in_array() expects parameter 2 to be array, null given in C:\xampp\htdocs\SanskritVerb\function.php on line 1177
the line 1177 of function.php falls inside a user defined function ‘ends’.
How can I know which line of code.php called this function ‘ends’ ?
I think some languages show error like — Error in line X of function.php called in line Y of code.php
If something of this sort exists for PHP — it would save my debug time.
asked Jan 14, 2015 at 8:53
5
For such a task you need to assign an error handler and call
Exception::getTrace()
to get trace of Exception. see the documentation set_error_handler.
Using xdebug extension for PHP can give you higher tracing capabilities even without error. If you choose xdebug you should enable tracing by adding lines to php.ini
xdebug.auto_trace=On
xdebug.trace_output_dir=c:\path
Anyway this is up to you.
answered Jan 14, 2015 at 9:05
2
Отладка программ
- PHP и MySQL
- Основы PHP
- Отладка программ
Внимание! Данный курс устарел!
Переходите к новому курсу «PHP для начинающих».
Отладка (поиск и устранение ошибок) представляет собой неотъемлемую часть разработки программного обеспечения. Программист, работающий на языке PHP, должен быть знаком со всеми доступными инструментальными средствами, позволяющими выявлять неправильно функционирующие компоненты в программных системах.
Количество инструментальных средств отладки достаточно велико. Не в последнюю очередь такая ситуация объясняется тем, что в приложениях PHP обычно используются возможности нескольких серверов (таких как сервер HTTP и сервер, входящий в состав системы управления базами данных), а в комплект каждого из таких серверов обычно входят собственные компоненты ведения журналов и формирования сообщений об ошибках, с помощью которых эти серверы предоставляют своим пользователям возможность следить за происходящим.
Кроме того, система PHP имеет собственные развитые средства формирования сообщений об ошибках (они позволяют организовать вывод сообщений об ошибках вместе с обычными выходными данными или регистрировать эти сообщения в файле для более тщательного анализа). К тому же большое количество функций, позволяющих обеспечить выработку в программах специализированных отчетов об обнаруженных ошибках, предусмотрено в самом языке PHP. По крайней мере, всегда есть возможность использовать в программах на языке PHP условные операторы вывода для контроля над действиями, осуществляемыми в программах в ходе их выполнения (и над значениями переменных в программах).
Кроме встроенных средств формирования сообщений об ошибках языка PHP и технологий, поддерживаемых этим языком, программисты, работающие на языке PHP, в последнее время получили возможность использовать такие же разновидности инструментальных средств отладки, которые в течение многих лет находились в распоряжении программистов, работающих на других языках. Основной среди этих инструментальных средств является среда отладки Zend, которая позволяет контролировать значения переменных, устанавливать точки прерывания и обеспечивать пошаговое выполнение программ с любой желаемой скоростью. В этой статье среда отладки Zend не рассматривается, но ее описание и другие дополнительные сведения можно найти по адресу www.zend.com.
В этой статье приведено лишь вводное описание инструментальных средств и методов, которыми может воспользоваться разработчик, стремящийся создать безукоризненно действующее программное обеспечение на языке PHP.
Общие стратегии поиска неисправностей
Двумя основными составляющими всей деятельности по отладке являются обнаружение причин нарушения в работе и последующее их устранение (без нарушения функционирования чего-либо иного под воздействием побочных эффектов реализации принятого решения). Это утверждение остается справедливым независимо от того, осуществляется ли диагностика программы PHP, телефонного коммутатора, электронной схемы или автомобиля «Копейка», — определенные принципы остаются в силе, о какой бы рассматриваемой проблемной области не шла речь. Всегда руководствуйтесь таким подходом, пытаясь выяснить, в чем причина нарушений в работе рассматриваемого программного обеспечения.
Внесение изменений только в одном месте
При проведении любых экспериментов необходимо руководствоваться основным правилом: если функционирование рассматриваемого объекта зависит от многочисленных факторов, то невозможно заранее узнать со всей определенностью, чем вызван какой-то конкретный сбой. Поэтому всегда следует вносить только одно изменение, затем проверять полученные результаты и определять, удалось ли устранить нежелательное поведение объекта. В случае отрицательного ответа необходимо внести еще одно изменение (возможно, полностью отменив внесенное перед этим изменение).
Ограничение области проявления проблемы
Если удастся свести поиск причин проблемы до единственной библиотеки или функции, это можно рассматривать как значительный успех в процессе поиска источника проблемы. Используйте специальные вызовы функций echo() и print_r() для своевременного вывода информации трассировки. Это позволяет определить тот момент, когда возникают изменения, являющиеся причиной нарушения в работе, и в какое время переменные приобретают такие значения, которые не должны были содержать.
Кроме того, для контроля над функционированием программ и за поведением компонентов программ в ходе их функционирования можно использовать отладчик с графическим интерфейсом (такой как Zend Studio).
Упрощение и последующее усложнение
Эта рекомендация может показаться очевидной, но о ней часто забывают. Если приходится сталкиваться с нарушениями в работе, связанными с использованием какой-то конкретной функции или средства, соответствующий компонент необходимо исключить из программы (либо заменив фиктивным вызовом, либо обозначив комментариями) и проверить, позволяет ли это добиться нормального функционирования программы. Еще один вариант состоит в том, чтобы заменить динамические данные статическими данными (например, вместо получения данных с помощью запроса к базе данных применить простые операторы присваивания значений переменным). Добейтесь успешного функционирования программы в наиболее упрощенных условиях, а затем поэтапно усложняйте программу, каждый раз проводя проверку, чтобы узнать, на каком этапе обнаруживаются ошибки.
Документирование принятых решений
Такая ситуация встречается слишком часто: программист часами отыскивает причину ошибки (или даже откладывает эту работу на следующий день) и наконец находит решение. Не следует после этого сразу же отправляться праздновать победу. Найдите время, чтобы отразить в комментариях к программе то, в чем состояла ошибка и каковым является решение. Это позволит вам быть во всеоружии, если снова возникнет та же проблема, а она непременно возникнет.
Повторная проверка после исправления ошибок
Нередко встречается такая ситуация, когда устранение проблемы в одном компоненте приводит к нарушению в работе какого-то другого компонента. Именно поэтому необходимо еще раз проверить систему, чтобы убедиться в ее нормальной работе не только в том месте, где была первоначально обнаружена ошибка, но и во всех других местах, где могут возникнуть нарушения. Данная рекомендация позволяет также понять, почему так важно ограничивать область действия ошибок в максимально возможной степени, — это дает возможность уменьшить объем необходимого повторного тестирования.
Общая классификация ошибок
Программистам приходится сталкиваться с весьма разнообразными ошибками. Некоторые ошибки являются не только простыми по своему характеру, но и легко обнаруживаемыми (к ним относятся синтаксические ошибки и ошибки, связанные с неправильным написанием). Задача поиска других ошибок является намного более сложной, поэтому рекомендации, приведенные в этом разделе, помогут многим программистам.
Ошибки на этапе компиляции
Язык PHP является компилируемым, но компиляция программы осуществляется непосредственно перед ее выполнением, поэтому сам процесс компиляции не столь очевиден, как в языке C или Java.
Ошибки, возникающие на этапе компиляции, обнаруживаются машиной Zend Engine, которая осуществляет компиляцию. Компилятор формирует сообщение об ошибке, часто указывая номер строки, и после получения такого сообщения программист может приступить к устранению проблемы. К категории ошибок, обнаруживаемых на этапе компиляции, относятся неправильно введенные имена переменных, отсутствующие точки с запятой и несогласованные круглые скобки.
Ошибки этапа выполнения
Ошибка этапа выполнения не обнаруживается до тех пор, пока не начинается эксплуатация программы. Причиной такой ошибки могут стать какие-то внешние факторы, такие как неправильно введенные пользователем данные или непредвиденные результаты, возвращенные из базы данных. Подобные ошибки выявляются только с помощью тестирования, поскольку обычно программист не имеет возможности проанализировать все условия, при которых эти ошибки стали бы очевидными.
Логические ошибки
По-видимому, логические ошибки относятся к категории ошибок, наиболее трудно поддающихся обнаружению. А если первоисточником подобной ошибки является неправильное понимание программистом каких-то аспектов решаемой задачи, то такая ошибка очень сложно поддается исправлению.
Предположим, перед программистом поставлена задача обеспечить запуск космического зонда и вывести его на орбиту вокруг Марса. Программист из США предусмотрел в своем навигационном алгоритме получение входных данных в фунтах и дюймах, но требуемые данные поступают из европейского центра управления полетами в метрической системе. Очевидно, что при таких условиях космический зонд непременно врежется в марсианскую поверхность. Программное обеспечение действовало в полном соответствии с заданием, но, строго говоря, при таких условиях задание предусматривало вывод ракеты прямо на Марс. Это — логическая ошибка.
Из этого следует, что необходимо добиться того, чтобы программа не просто вырабатывала выходные данные, но вырабатывала их правильно. Воспользуйтесь отдельно взятой процедурой расчетов и убедитесь в том, что формируемые программой результаты являются правильными; еще один вариант состоит в том, что результаты программы должны сравниваться с заведомо известными правильными значениями. А для того чтобы не попадать в неприятную историю, не пишите программы на языке PHP для космических кораблей ;).
Использование журналов веб-сервера
Эксплуатация большинства программ PHP приводит к получению HTML-страниц того или иного рода, а эти страницы, в свою очередь, передаются пользователю с помощью HTTP-сервера, такого как Apache или Microsoft Internet Information Server (IIS). Это означает, что дополнительным источником ошибок может стать программное обеспечение веб-сервера. По этой причине важно знать, какой способ применяется в веб-сервере для формирования и регистрации сообщений об ошибках, а также знать о том, как получить доступ и провести синтаксический анализ журналов, в которых регистрируются ошибки.
Сервер Apache
На HTTP-сервере Apache ведутся два файла журнала в формате открытого текста. Эти журналы описаны ниже:
- Apache/logs/access.log
-
Предназначен для регистрации каждого запроса на получение файла, передаваемого по протоколу HTTP. К регистрируемым данным относятся дата, время и полученные результаты (успешное или неудачное завершение, о чем можно судить по числовому коду состояния). Этот журнал представляет собой также журнал регистрации доступа, в котором фиксируется IP-адрес, из которого поступил каждый запрос.
- Apache/logs/error.log
-
Это — журнал регистрации ошибок, в котором фиксируются только ситуации, связанные с возникновением ошибок.
Унифицированный формат журнала
По умолчанию для оформления записей в файле error.log сервера Apache используется унифицированный формат журнала, который принят в качестве стандартного. В этом формате каждая запись соответствует отдельному экземпляру действия, касающегося запроса и/или ответа (в конечном итоге HTTP-серверы занимаются обработкой именно запросов и ответов). Например, одна строка журнала может соответствовать запросу на получение HTML-страницы (и содержать сведения о том, в каких обстоятельствах эта страница была впоследствии предоставлена сервером Apache). А следующая строка может описывать (автоматическое) выполнение запроса и передачу файла JPEG, связанного с ранее затребованным документом HTML.
В любом случае записи в унифицированном формате журнала выглядят примерно так, как показано ниже (в действительности каждая запись занимает только одну строку):
127.0.0.1 - - [12/Jan/2013:03:18:33 +0800] "GET /images/lang.gif HTTP/1.1" 200 6590
Ниже перечислены наиболее важные компоненты данной строки:
-
127.0.0.1. IP-адрес клиента, от которого поступил запрос по протоколу HTTP (в данном случае локальный хост).
-
[12/Jan/2013:03:18:33 +0400]. Дата, время и разница между временем текущего часового пояса и всеобщим скоординированным временем (Universal Coordinated Time — UTC).
-
GET. Тип запроса HTTP — GET или POST.
-
/images/lang.gif. Затребованный файл.
-
HTTP/1.1. Версия протокола HTTP, используемого для передачи запроса.
-
200. Код ответа, описывающий результат запроса (дополнительная информация на эту тему приведена ниже в данном разделе).
-
6590. Количество байтов, переданных в ответе HTTP, соответствующем этому запросу.
Коды ответов HTTP
Количество кодов ответов HTTP весьма велико (хотя самым знаменитым остается код сообщения об ошибке «404 Not Found»), но сами эти коды подчиняются общему шаблону, с помощью которого можно быстро узнать назначение каждого кода. По существу, данные шаблоны подразделяются на категории, описанные ниже:
-
Коды ряда 200 указывают на успешное завершение.
-
Коды ряда 300 обозначают перенаправление.
-
Коды ряда 400 указывают на клиентскую ошибку (подобную указанию в запросе несуществующего документа).
-
Коды ряда 500 указывают на серверную ошибку.
Полный список кодов ответов HTTP приведен по адресу www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
Сервер IIS
В HTTP-сервере IIS компании Microsoft задача ведения журнала осуществляется иначе. Сервер IIS не записывает журнал в файл, а регистрирует полученную им информацию о состоянии и об обнаруженных ошибках таким образом, чтобы эта информация была доступна для исследования в программе Event Viewer. Ошибки, зарегистрированные сервером IIS, можно найти в части System Log окна Event Viewer, где для обозначения источника применяется имя W3SVC.
Неоценимую помощь при выявлении ошибок может оказать сам интерпретатор PHP. Даже без какой-либо дополнительной настройки система PHP способна выводить сообщения об ошибках вместе с выходными данными. Эти сообщения передаются прямо в окно браузера вместе с номерами строк. Для большинства программистов этих сведений вполне достаточно, чтобы можно было воспользоваться самим интерпретатором PHP как средством отладки, но, чтобы получить максимум пользы от этих средств, важно знать все нюансы настройки конфигурации, от которых зависят условия формирования сообщений об ошибках.
Безусловно, интерпретатор PHP показывает номер строки, в которой была обнаружена ошибка, но следует знать о том, что этот номер не всегда указывает на ту строку, к которой следует перейти, чтобы внести исправление. Иногда отсутствующая закрывающая кавычка или пропущенная точка с запятой не обнаруживается интерпретатором до завершения обработки нескольких следующих строк, поэтому нужно быть готовым к тому, что придется вернуться немного назад, чтобы найти причины синтаксических ошибок такого рода.
Формирование сообщений об ошибках
Выполняемая интерпретатором PHP операция включения сообщения об ошибке в вывод программы (что чаще всего приводит к отображению сообщения об ошибке в окне браузера) по существу представляет собой операцию формирования сообщения об ошибке. Формирование сообщений об ошибках является полезным диагностическим инструментальным средством, использование которого разрешено по умолчанию. Если же интерпретатор PHP подключен к серверу производственного назначения, то данное средство должно быть запрещено.
Чтобы разрешить или запретить формирование сообщений об ошибках, необходимо внести изменение в файл php.ini. При этом корректировка применяется к параметру display_errors. Если требуется, чтобы сообщения выводились в составе формируемых выходных данных, то в файле php.ini должна присутствовать следующая строка:
display_errors=On
Если же сообщения об ошибках не должны отображаться (и это действительно не допустимо, если сервер используется для предоставления к нему общего доступа), то соответствующая строка должна выглядеть таким образом:
display_errors=Off
Игнорирование данной рекомендации при эксплуатации сервера производственного назначения приводит к тому, что функционирование средств формирования сообщений об ошибках вызовет непреднамеренное раскрытие перед пользователями важных сведений об организации программного обеспечения. Например, какое-либо непредвиденное условие может вызвать появление в окне незащищенного браузера имени переменной или таблицы базы данных. А потенциальный нарушитель сможет воспользоваться этой информацией для несанкционированного доступа к серверу.
Регистрация ошибок
Регистрация ошибок аналогична по своему назначению формированию сообщений об ошибках, но предусматривает запись информации о событиях, связанных с ошибками, в текстовый файл, а не вывод этой информации на экран. Такой вариант представления сведений об ошибках более приемлем с точки зрения защиты. Кроме того, поскольку файлы журналов должны храниться в каталоге с ограниченным доступом, этот метод регистрации ошибок более предпочтителен для использования на HTTP-серверах производственного назначения.
Как и в случае средств формирования сообщений об ошибках, применение средств регистрации ошибок может быть разрешено или запрещено с помощью файла php.ini. Чтобы разрешить использование этих средств, необходимо применить следующую опцию:
log_errors=On
В противном случае следует задать такую опцию:
log_errors=Off
По умолчанию средства регистрации ошибок запрещены в файле php.ini.
Определение категории ошибок, подлежащих выводу на экран или записи в журнал
Принимая решение об использовании средств формирования сообщений об ошибках (для вывода на экран) или средств регистрации ошибок (для записи в файл), необходимо дополнительно указать, какие ошибки рассматриваются как достаточно серьезные для предоставления информации о них пользователю. Параметры настройки средств ведения журнала задаются в файле php.ini с помощью значения параметра error_reporting. По умолчанию параметру error_reporting присваивается следующее значение:
error_reporting=E_ALL & ~E_NOTICE
Такая настройка указывает, что пользователю должна предоставляться информация обо всех ошибках и предупреждениях (что показывает параметр E_ALL), но вместе с тем действует условие (что показывает оператор &), согласно которому извещения этапа прогона не подлежат выводу (для этого служит параметр ~E_NOTICE, где ~ представляет собой оператор отрицания).
Степень серьезности отображаемых сообщений об ошибках, которая определена параметром error_reporting, отражается на поведении средств регистрации ошибок (если их использование разрешено с помощью параметра log_errors=On) и средств формирования сообщений об ошибках (если их использование разрешено с помощью параметра display_errors=On) или одновременно тех и других средств, если все они разрешены.
Функции формирования сообщений об ошибках
Язык PHP не только в значительной степени упрощает работу программистов, но и включает в себя широкий набор функций, которые могут использоваться программистами для облегчения поиска причин нарушений в работе и в целом для получения сведений обо всех аспектах состояния разрабатываемых ими программ. Спектр таких средств начинается с обычных операторов формирования вывода (print, echo и тому подобных), используемых в тех контекстах, когда требуется контролировать значения переменных, и заканчивается специализированными функциями, осуществляющими вывод данных с помощью механизмов ведения журналов операционной системы.
В настоящем разделе даны вводные сведения о некоторых функциях PHP, которые позволяют выявлять причины проблем и создавать отчеты об условиях функционирования программ.
Диагностические операторы вывода
Простейший метод устранения нарушений в работе предусматривает размещение в ключевых местах кода операторов echo и print, для того чтобы формируемые выходные данные содержали информацию о ходе выполнения программы, осуществляемого на основе вызова различных функций, а также о том, какие значения принимают наиболее важные переменные в различных точках.
Использование таких средств можно сравнить с применением простейшего отладчика, поскольку операторы вывода позволяют отслеживать значения переменных во время выполнения программы и следить за тем, не происходит ли присваивание этим переменным каких-либо непредусмотренных значений (и в случае положительного ответа на этот вопрос позволяют узнать, когда это происходит).
Использование функции var_dump()
Обычные функции вывода являются довольно удобными, но с точки зрения отладки более специализированные функции может оказаться гораздо полезнее. Наиболее важной среди функций подобного типа является функция var_dump(). Она может рассматриваться как чрезвычайно «остроумный» оператор вывода, который, кроме всего прочего, автоматически представляет содержимое любого массива в таком виде, который очень удобен для восприятия человеком.
Напомним, что выполнение приведенного ниже кода приводит к получению результатов, от которых мало пользы:
Код PHP
$fruit = array('orange' => 'Апельсин',
'red' => 'Яблоко',
'yellow' => 'Банан',
'green' => 'Груша');
echo $fruit;
Данные результаты сводятся к следующему:
Это нам ничего не дает. С другой стороны, если за определением того же массива следует строка
Код PHP
var_dump($fruit);
то формируется следующие намного более полезные выходные данные:
На основании таких данных программист, занимающийся отладкой, может немедленно определить, каково содержимое данного массива (чему равны ключи и значения элементов этого массива).
Использование функции syslog()
В языке PHP предусмотрена функция syslog(), которая позволяет осуществлять запись непосредственно в журнал операционной системы, под управлением которой функционирует среда PHP. Это удобная функция, которая становится особенно полезной, если требуется регистрировать всю информацию о проблемах, возникающих в системе, с помощью стандартных средств, или есть необходимость предупредить о нарушении в работе системного администратора, если он непосредственно не соприкасается с разработками на языке PHP.
В целом функция syslog() позволяет указать степень серьезности, связанную с регистрируемым событием, а также ввести сообщение, которое описывает это событие. Затем указанные значения могут быть выведены в журнал для использования в качестве вспомогательной информации для диагностики.
Все возможные опции определения степени серьезности функции syslog() показаны в следующем коде:
Код PHP
$logOptions = array(LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING,
LOG_ERR, LOG_CRIT, LOG_ALERT, LOG_EMERG);
$msg = array('Сообщение отладки', 'Информация', 'Уведомление', 'Предупреждение',
'Ошибка', 'Критическая ошибка', 'Серьезная ошибка', 'Кабздец');
foreach($logOptions as $key => $value) {
syslog($value, $msg[$key]);
}
В системе Microsoft Windows первые три из этих сообщений об ошибках (от LOG_DEBUG до LOG_NOTICE) рассматриваются как информационные, четвертое и пятое считаются предупреждающими, а последние три отмечаются в программе Event Viewer как относящиеся к категории Alerts. Все эти сообщения отображаются со значением источника c-client, которое соответствует одному из вспомогательных процессов сервера Apache:
Использование функции error_log()
Функция error_log() может использоваться для передачи сообщения об ошибке почти в любое место назначения, включая адрес электронной почты. Эта функция предоставляет легкий и удобный способ формирования отчета о непредвиденных условиях, возникших в процессе функционирования программного обеспечения PHP, но, к сожалению, этим средством пользуются лишь немногие разработчики. Более подробно она описана в предыдущей статье в разделе «Ведение журнала и отладка».
Антон Шевчук // Web-разработчик

Не совершает ошибок только тот, кто ничего не делает, и мы тому пример – трудимся не покладая рук над созданием рабочих мест для тестировщиков 
О да, в этой статье я поведу свой рассказа об ошибках в PHP, и том как их обуздать.
Ошибки
Разновидности в семействе ошибок
Перед тем как приручать ошибки, я бы рекомендовал изучить каждый вид и отдельно обратить внимание на самых ярких представителей.
Чтобы ни одна ошибка не ушла незамеченной потребуется включить отслеживание всех ошибок с помощью функции error_reporting(), а с помощью директивы display_errors включить их отображение:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
Фатальные ошибки
Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.
E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:
<?php
/**
Parse error: syntax error, unexpected end of file
*/
{
Или написали на непонятном языке:
<?php /** Parse error: syntax error, unexpected '...' (T_STRING) */ Тут будет ошибка парсера
Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:
<?php /** Parse error: syntax error, unexpected '}' */ }
Отмечу один важный момент – код файла, в котором вы допустили parse error не будет выполнен, следовательно, если вы попытаетесь включить отображение ошибок в том же файле, где возникла ошибка парсера то это не сработает:
<?php
// этот код не сработает
error_reporting(E_ALL);
ini_set('display_errors', 1);
// т.к. вот тут
ошибка парсера
E_ERROR
Это ошибка появляется, когда PHP понял что вы хотите, но сделать сие не получилось ввиду ряда причин, так же прерывает выполнение скрипта, при этом код до появления ошибки сработает:
Не был найден подключаемый файл:
/** Fatal error: require_once(): Failed opening required 'not-exists.php' (include_path='.:/usr/share/php:/usr/share/pear') */ require_once 'not-exists.php';
Было брошено исключение (что это за зверь, расскажу немного погодя), но не было обработано:
/** Fatal error: Uncaught exception 'Exception' */ throw new Exception();
При попытке вызвать несуществующий метод класса:
/** Fatal error: Call to undefined method stdClass::notExists() */ $stdClass = new stdClass(); $stdClass->notExists();
Отсутствия свободной памяти (больше, чем прописано в директиве memory_limit) или ещё чего-нить подобного:
/**
Fatal Error: Allowed Memory Size
*/
$arr = array();
while (true) {
$arr[] = str_pad(' ', 1024);
}
Очень часто происходит при чтении либо загрузки больших файлов, так что будьте внимательны с вопросом потребляемой памяти
Рекурсивный вызов функции. В данном примере он закончился на 256-ой итерации, ибо так прописано в настройках xdebug:
/**
Fatal error: Maximum function nesting level of '256' reached, aborting!
*/
function deep() {
deep();
}
deep();
Не фатальные
Данный вид не прерывает выполнение скрипта, но именно их обычно находит тестировщик, и именно они доставляют больше всего хлопот у начинающих разработчиков.
E_WARNING
Частенько встречается, когда подключаешь файл с использованием include, а его не оказывается на сервере или ошиблись указывая путь к файлу:
/** Warning: include_once(): Failed opening 'not-exists.php' for inclusion */ include_once 'not-exists.php';
Бывает, если используешь неправильный тип аргументов при вызове функций:
/**
Warning: join(): Invalid arguments passed
*/
join('string', 'string');
Их очень много, и перечислять все не имеет смысла…
E_NOTICE
Это самые распространенные ошибки, мало того, есть любители отключать вывод ошибок и клепают их целыми днями. Возникают при целом ряде тривиальных ошибок.
Когда обращаются к неопределенной переменной:
/** Notice: Undefined variable: a */ echo $a;
Когда обращаются к несуществующему элементу массива:
<?php /** Notice: Undefined index: a */ $b = array(); $b['a'];
Когда обращаются к несуществующей константе:
/** Notice: Use of undefined constant UNKNOWN_CONSTANT - assumed 'UNKNOWN_CONSTANT' */ echo UNKNOWN_CONSTANT;
Когда не конвертируют типы данных:
/** Notice: Array to string conversion */ echo array();
Для избежания подобных ошибок – будьте внимательней, и если вам IDE подсказывает о чём-то – не игнорируйте её:
E_STRICT
Это ошибки, которые научат вас писать код правильно, чтобы не было стыдно, тем более IDE вам эти ошибки сразу показывают. Вот например, если вызвали не статический метод как статику, то код будет работать, но это как-то неправильно, и возможно появление серьёзных ошибок, если в дальнейшем метод класса будет изменён, и появится обращение к $this:
/**
Strict standards: Non-static method Strict::test() should not be called statically
*/
class Strict {
public function test() {
echo 'Test';
}
}
Strict::test();
E_DEPRECATED
Так PHP будет ругаться, если вы используете устаревшие функции (т.е. те, что помечены как deprecated, и в следующем мажорном релизе их не будет):
/**
Deprecated: Function split() is deprecated
*/
// популярная функция, всё никак не удалят из PHP
// deprecated since 5.3
split(',', 'a,b');
В моём редакторе подобные функции будут зачёркнуты:
Обрабатываемые
Этот вид, которые разводит сам разработчик кода, я их уже давно не встречал, не рекомендую их вам заводить:
E_USER_ERROR– критическая ошибкаE_USER_WARNING– не критическая ошибкаE_USER_NOTICE– сообщения которые не являются ошибками
Отдельно стоит отметить E_USER_DEPRECATED – этот вид всё ещё используется очень часто для того, чтобы напомнить программисту, что метод или функция устарели и пора переписать код без использования оной. Для создания этой и подобных ошибок используется функция trigger_error():
/**
* @deprecated Deprecated since version 1.2, to be removed in 2.0
*/
function generateToken() {
trigger_error('Function `generateToken` is deprecated, use class `Token` instead', E_USER_DEPRECATED);
// ...
// code ...
// ...
}
Теперь, когда вы познакомились с большинством видов и типов ошибок, пора озвучить небольшое пояснение по работе директивы
display_errors:
- если
display_errors = on, то в случае ошибки браузер получит html c текстом ошибки и кодом 200- если же
display_errors = off, то для фатальных ошибок код ответа будет 500 и результат не будет возвращён пользователю, для остальных ошибок – код будет работать неправильно, но никому об этом не расскажет
Приручение
Для работы с ошибками в PHP существует 3 функции:
- set_error_handler() — устанавливает обработчик для ошибок, которые не обрывают работу скрипта (т.е. для не фатальных ошибок)
- error_get_last() — получает информацию о последней ошибке
- register_shutdown_function() — регистрирует обработчик который будет запущен при завершении работы скрипта. Данная функция не относится непосредственно к обработчикам ошибок, но зачастую используется именно для этого
Теперь немного подробностей об обработке ошибок с использованием set_error_handler(), в качестве аргументов данная функция принимает имя функции, на которую будет возложена миссия по обработке ошибок и типы ошибок которые будут отслеживаться. Обработчиком ошибок может так же быть методом класса, или анонимной функцией, главное, чтобы он принимал следующий список аргументов:
$errno– первый аргумент содержит тип ошибки в виде целого числа$errstr– второй аргумент содержит сообщение об ошибке$errfile– необязательный третий аргумент содержит имя файла, в котором произошла ошибка$errline– необязательный четвертый аргумент содержит номер строки, в которой произошла ошибка$errcontext– необязательный пятый аргумент содержит массив всех переменных, существующих в области видимости, где произошла ошибка
В случае если обработчик вернул true, то ошибка будет считаться обработанной и выполнение скрипта продолжится, иначе — будет вызван стандартный обработчик, который логирует ошибку и в зависимости от её типа продолжит выполнение скрипта или завершит его. Вот пример обработчика:
<?php
// включаем отображение всех ошибок, кроме E_NOTICE
error_reporting(E_ALL & ~E_NOTICE);
ini_set('display_errors', 1);
// наш обработчик ошибок
function myHandler($level, $message, $file, $line, $context) {
// в зависимости от типа ошибки формируем заголовок сообщения
switch ($level) {
case E_WARNING:
$type = 'Warning';
break;
case E_NOTICE:
$type = 'Notice';
break;
default;
// это не E_WARNING и не E_NOTICE
// значит мы прекращаем обработку ошибки
// далее обработка ложится на сам PHP
return false;
}
// выводим текст ошибки
echo "<h2>$type: $message</h2>";
echo "<p><strong>File</strong>: $file:$line</p>";
echo "<p><strong>Context</strong>: $". join(', $', array_keys($context))."</p>";
// сообщаем, что мы обработали ошибку, и дальнейшая обработка не требуется
return true;
}
// регистрируем наш обработчик, он будет срабатывать на для всех типов ошибок
set_error_handler('myHandler', E_ALL);
У вас не получится назначить более одной функции для обработки ошибок, хотя очень бы хотелось регистрировать для каждого типа ошибок свой обработчик, но нет – пишите один обработчик, и всю логику отображения для каждого типа описывайте уже непосредственно в нём
С обработчиком, который написан выше есть одна существенная проблема – он не ловит фатальные ошибки, и вместо сайта пользователи увидят лишь пустую страницу, либо, что ещё хуже, сообщение об ошибке. Дабы не допустить подобного сценария следует воспользоваться функцией register_shutdown_function() и с её помощью зарегистрировать функцию, которая всегда будет выполняться по окончанию работы скрипта:
function shutdown() {
echo 'Этот текст будет всегда отображаться';
}
register_shutdown_function('shutdown');
Данная функция будет срабатывать всегда!
Но вернёмся к ошибкам, для отслеживания появления в коде ошибки воспользуемся функцией error_get_last(), с её помощью можно получить информацию о последней выявленной ошибке, а поскольку фатальные ошибки прерывают выполнение кода, то они всегда будут выполнять роль “последних”:
function shutdown() {
$error = error_get_last();
if (
// если в коде была допущена ошибка
is_array($error) &&
// и это одна из фатальных ошибок
in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])
) {
// очищаем буфер вывода (о нём мы ещё поговорим в последующих статьях)
while (ob_get_level()) {
ob_end_clean();
}
// выводим описание проблемы
echo 'Сервер находится на техническом обслуживании, зайдите позже';
}
}
register_shutdown_function('shutdown');
Задание
Дополнить обработчик фатальных ошибок выводом исходного кода файла где была допущена ошибка, а так же добавьте подсветку синтаксиса выводимого кода.
О прожорливости
Проведём простой тест, и выясним – сколько драгоценных ресурсов кушает самая тривиальная ошибка:
/**
* Этот код не вызывает ошибок
*/
// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);
$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
$arr[$a] = $i;
}
printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';
В результате запуска данного скрипта у меня получился вот такой результат:
0.002867 seconds 984 bytes
Теперь добавим ошибку в цикле:
/**
* Этот код содержит ошибку
*/
// сохраняем параметры памяти и времени выполнения скрипта
$memory = memory_get_usage();
$time= microtime(true);
$a = '';
$arr = [];
for ($i = 0; $i < 10000; $i++) {
$arr[$b] = $i; // тут ошиблись с именем переменной
}
printf('%f seconds <br/>', microtime(true) - $time);
echo number_format(memory_get_usage() - $memory, 0, '.', ' '), ' bytes<br/>';
Результат ожидаемо хуже, и на порядок (даже на два порядка!):
0.263645 seconds 992 bytes
Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!
Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок
Где собака зарыта
В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:
<?php
echo @UNKNOWN_CONSTANT;
При этом обработчик ошибок указанный в set_error_handler() всё равно будет вызван, а факт того, что к ошибке было применено подавление можно отследить вызвав функцию error_reporting() внутри обработчика, в этом случае она вернёт 0.
Если вы в такой способ подавляете ошибки, то это уменьшает нагрузку на процессор в сравнении с тем, если вы их просто скрываете (см. сравнительный тест выше), но в любом случае, подавление ошибок это зло
Исключения
В эру PHP4 не было исключений (exceptions), всё было намного сложнее, и разработчики боролись с ошибками как могли, это было сражение не на жизнь, а на смерть… Окунуться в эту увлекательную историю противостояния можете в статье Исключительный код. Часть 1. Стоит ли её читать сейчас? Думаю да, ведь это поможет вам понять эволюцию языка, и раскроет всю прелесть исключений
Исключения — исключительные событие в PHP, в отличии от ошибок не просто констатируют наличие проблемы, а требуют от программиста дополнительных действий по обработке каждого конкретного случая.
К примеру, скрипт должен сохранить какие-то данные в кеш файл, если что-то пошло не так (нет доступа на запись, нет места на диске), генерируется исключение соответствующего типа, а в обработчике исключений принимается решение – сохранить в другое место или сообщить пользователю о проблеме.
Исключение – это объект который наследуется от класса Exception, содержит текст ошибки, статус, а также может содержать ссылку на другое исключение которое стало первопричиной данного. Модель исключений в PHP схожа с используемыми в других языках программирования. Исключение можно инициировать (как говорят, “бросить”) при помощи оператора throw, и можно перехватить (“поймать”) оператором catch. Код генерирующий исключение, должен быть окружен блоком try, для того чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally:
try {
// код который может выбросить исключение
if (rand(0, 1)) {
throw new Exception('One')
} else {
echo 'Zero';
}
} catch (Exception $e) {
// код который может обработать исключение
echo $e->getMessage();
}
В каких случаях стоит применять исключения:
- если в рамках одного метода/функции происходит несколько операций которые могут завершиться неудачей
- если используемый вами фреймверк или библиотека декларируют их использование
Для иллюстрации первого сценария возьмём уже озвученный пример функции для записи данных в файл – помешать нам может очень много факторов, а для того, чтобы сообщить выше стоящему коду в чем именно была проблема необходимо создать и выбросить исключение:
$directory = __DIR__ . DIRECTORY_SEPARATOR . 'logs';
// директории может не быть
if (!is_dir($directory)) {
throw new Exception('Directory `logs` is not exists');
}
// может не быть прав на запись в директорию
if (!is_writable($directory)) {
throw new Exception('Directory `logs` is not writable');
}
// возможно кто-то уже создал файл, и закрыл к нему доступ
if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
throw new Exception('System can\'t create log file');
}
fputs($file, date('[H:i:s]') . " done\n");
fclose($file);
Соответственно ловить данные исключения будем примерно так:
try {
// код который пишет в файл
// ...
} catch (Exception $e) {
// выводим текст ошибки
echo 'Не получилось: '. $e->getMessage();
}
В данном примере приведен очень простой сценарий обработки исключений, когда у нас любая исключительная ситуация обрабатывается на один манер. Но зачастую – различные исключения требуют различного подхода к обработке, и тогда следует использовать коды исключений и задать иерархию исключений в приложении:
// исключения файловой системы
class FileSystemException extends Exception {}
// исключения связанные с директориями
class DirectoryException extends FileSystemException {
// коды исключений
const DIRECTORY_NOT_EXISTS = 1;
const DIRECTORY_NOT_WRITABLE = 2;
}
// исключения связанные с файлами
class FileException extends FileSystemException {}
Теперь, если использовать эти исключения то можно получить следующий код:
try {
// код который пишет в файл
if (!is_dir($directory)) {
throw new DirectoryException('Directory `logs` is not exists', DirectoryException::DIRECTORY_NOT_EXISTS);
}
if (!is_writable($directory)) {
throw new DirectoryException('Directory `logs` is not writable', DirectoryException::DIRECTORY_NOT_WRITABLE);
}
if (!$file = @fopen($directory . DIRECTORY_SEPARATOR . date('Y-m-d') . '.log', 'a+')) {
throw new FileException('System can\'t open log file');
}
fputs($file, date('[H:i:s]'') . " done\n");
fclose($file);
} catch (DirectoryException $e) {
echo 'С директорией возникла проблема: '. $e->getMessage();
} catch (FileException $e) {
echo 'С файлом возникла проблема: '. $e->getMessage();
} catch (FileSystemException $e) {
echo 'Ошибка файловой системы: '. $e->getMessage();
} catch (Exception $e) {
echo 'Ошибка сервера: '. $e->getMessage();
}
Важно помнить, что Exception — это прежде всего исключительное событие, иными словами исключение из правил. Не нужно использовать их для обработки очевидных ошибок, к примеру, для валидации введённых пользователем данных (хотя тут не всё так однозначно). При этом обработчик исключений должен быть написан в том месте, где он будет способен его обработать. К примеру, обработчик для исключений вызванных недоступностью файла для записи должен быть в методе, который отвечает за выбор файла или методе его вызывающем, для того что бы он имел возможность выбрать другой файл или другую директорию.
Так, а что будет если не поймать исключение? Вы получите “Fatal Error: Uncaught exception …”. Неприятно.
Чтобы избежать подобной ситуации следует использовать функцию set_exception_handler() и установить обработчик для исключений, которые брошены вне блока try-catch и не были обработаны. После вызова такого обработчика выполнение скрипта будет остановлено:
// в качестве обработчика событий
// будем использовать анонимную функцию
set_exception_handler(function($exception) {
/** @var Exception $exception */
echo $exception->getMessage(), "<br/>\n";
echo $exception->getFile(), ':', $exception->getLine(), "<br/>\n";
echo $exception->getTraceAsString(), "<br/>\n";
});
Ещё расскажу про конструкцию с использованием блока finally – этот блок будет выполнен вне зависимости от того, было выброшено исключение или нет:
try {
// код который может выбросить исключение
} catch (Exception $e) {
// код который может обработать исключение
// если конечно оно появится
} finally {
// код, который будет выполнен при любом раскладе
}
Для понимания того, что это нам даёт приведу следующий пример использования блока finally:
try {
// где-то глубоко внутри кода
// соединение с базой данных
$handler = mysqli_connect('localhost', 'root', '', 'test');
try {
// при работе с БД возникла исключительная ситуация
// ...
throw new Exception('DB error');
} catch (Exception $e) {
// исключение поймали, обработали на своём уровне
// и должны его пробросить вверх, для дальнейшей обработки
throw new Exception('Catch exception', 0, $e);
} finally {
// но, соединение с БД необходимо закрыть
// будем делать это в блоке finally
mysqli_close($handler);
}
// этот код не будет выполнен, если произойдёт исключение в коде выше
echo "Ok";
} catch (Exception $e) {
// ловим исключение, и выводим текст
echo $e->getMessage();
echo "<br/>";
// выводим информацию о первоначальном исключении
echo $e->getPrevious()->getMessage();
}
Т.е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).
Для вводной статьи информации в самый раз, кто жаждет ещё подробностей, то вы их найдёте в статье Исключительный код 
Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.
PHP7 – всё не так, как было раньше
Так, вот вы сейчас всю информацию выше усвоили и теперь я буду грузить вас нововведениями в PHP7, т.е. я буду рассказывать о том, с чем вы столкнётесь через год работы PHP разработчиком. Ранее я вам рассказывал и показывал на примерах какой костыль нужно соорудить, чтобы отлавливать критические ошибки, так вот – в PHP7 это решили исправить, но как обычно завязались на обратную совместимость кода, и получили хоть и универсальное решение, но оно далеко от идеала. А теперь по пунктам об изменениях:
- при возникновении фатальных ошибок типа
E_ERRORили фатальных ошибок с возможностью обработкиE_RECOVERABLE_ERRORPHP выбрасывает исключение - эти исключения не наследуют класс Exception (помните я говорил об обратной совместимости, это всё ради неё)
- эти исключения наследуют класс Error
- оба класса Exception и Error реализуют интерфейс Throwable
- вы не можете реализовать интерфейс Throwable в своём коде
Интерфейс Throwable практически полностью повторяет нам Exception:
interface Throwable
{
public function getMessage(): string;
public function getCode(): int;
public function getFile(): string;
public function getLine(): int;
public function getTrace(): array;
public function getTraceAsString(): string;
public function getPrevious(): Throwable;
public function __toString(): string;
}
Сложно? Теперь на примерах, возьмём те, что были выше и слегка модернизируем:
try {
// файл, который вызывает ошибку парсера
include 'e_parse_include.php';
} catch (Error $e) {
var_dump($e);
}
В результате ошибку поймаем и выведем:
object(ParseError)#1 (7) {
["message":protected] => string(48) "syntax error, unexpected 'будет' (T_STRING)"
["string":"Error":private] => string(0) ""
["code":protected] => int(0)
["file":protected] => string(49) "/www/education/error/e_parse_include.php"
["line":protected] => int(4)
["trace":"Error":private] => array(0) { }
["previous":"Error":private] => NULL
}
Как видите – поймали исключение ParseError, которое является наследником исключения Error, который реализует интерфейс Throwable, в доме который построил Джек. Ещё есть другие, но не буду мучать – для наглядности приведу иерархию исключений:
interface Throwable
|- Exception implements Throwable
| |- ErrorException extends Exception
| |- ... extends Exception
| `- ... extends Exception
`- Error implements Throwable
|- TypeError extends Error
|- ParseError extends Error
|- ArithmeticError extends Error
| `- DivisionByZeroError extends ArithmeticError
`- AssertionError extends Error
TypeError – для ошибок, когда тип аргументов функции не совпадает с передаваемым типом:
try {
(function(int $one, int $two) {
return;
})('one', 'two');
} catch (TypeError $e) {
echo $e->getMessage();
}
ArithmeticError – могут возникнуть при математических операциях, к примеру когда результат вычисления превышает лимит выделенный для целого числа:
try {
1 << -1;
} catch (ArithmeticError $e) {
echo $e->getMessage();
}
DivisionByZeroError – ошибка деления на ноль:
try {
1 / 0;
} catch (ArithmeticError $e) {
echo $e->getMessage();
}
AssertionError – редкий зверь, появляется когда условие заданное в assert() не выполняется:
ini_set('zend.assertions', 1);
ini_set('assert.exception', 1);
try {
assert(1 === 0);
} catch (AssertionError $e) {
echo $e->getMessage();
}
При настройках production-серверов, директивы
zend.assertionsиassert.exceptionотключают, и это правильно
Задание
Написать универсальный обработчик ошибок для PHP7, который будет отлавливать все возможные исключения.
При написании данного раздела были использованы материалы из статьи Throwable Exceptions and Errors in PHP 7
Отладка
Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:
<?php
function example() {
echo '<pre>';
debug_print_backtrace();
echo '</pre>';
}
class ExampleClass {
public static function method () {
example();
}
}
ExampleClass::method();
В результате выполнения функции debug_print_backtrace() будет выведен список вызовов приведших нас к данной точке:
#0 example() called at [/www/education/error/backtrace.php:10] #1 ExampleClass::method() called at [/www/education/error/backtrace.php:14]
Проверить код на наличие синтаксических ошибок можно с помощью функции php_check_syntax() или же команды php -l [путь к файлу], но я не встречал использования оных.
Assert
Отдельно хочу рассказать о таком экзотическом звере как assert() в PHP, собственно это кусочек контрактной методологии программирования, и дальше я расскажу вам как я никогда его не использовал 
Первый случай – это когда вам надо написать TODO прямо в коде, да так, чтобы точно не забыть реализовать заданный функционал:
// включаем вывод ошибок
error_reporting(E_ALL);
ini_set('display_errors', 1);
// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);
assert(false, "Remove it!");
В результате выполнения данного кода получим E_WARNING:
Warning: assert(): Remove it! failed
PHP7 можно переключить в режим exception, и вместо ошибки будет всегда появляться исключение AssertionError:
// включаем asserts
ini_set('zend.assertions', 1);
ini_set('assert.active', 1);
// переключаем на исключения
ini_set('assert.exception', 1);
assert(false, "Remove it!");
В результате ожидаемо получаем не пойманный AssertionError. При необходимости, можно выбрасывать произвольное исключение:
assert(false, new Exception("Remove it!"));
Но я бы рекомендовал использовать метки
@TODO, современные IDE отлично с ними работают, и вам не нужно будет прикладывать дополнительные усилия и ресурсы для работы с ними
Второй вариант использования – это создание некоего подобия TDD, но помните – это лишь подобие. Хотя, если сильно постараться, то можно получить забавный результат, который поможет в тестировании вашего кода:
// callback-функция для вывода информации в браузер
function backlog($script, $line, $code, $message) {
echo "<h3>$message</h3>";
highlight_string ($code);
}
// устанавливаем callback-функцию
assert_options(ASSERT_CALLBACK, 'backlog');
// отключаем вывод предупреждений
assert_options(ASSERT_WARNING, false);
// пишем проверку и её описание
assert("sqr(4) == 16", "When I send integer, function should return square of it");
// функция, которую проверяем
function sqr($a) {
return; // она не работает
}
Третий теоретический вариант – это непосредственно контрактное программирование – когда вы описали правила использования своей библиотеки, но хотите точно убедится, что вас поняли правильно, и в случае чего сразу указать разработчику на ошибку (я вот даже не уверен, что правильно его понимаю, но пример кода вполне рабочий):
/**
* Настройки соединения должны передаваться в следующем виде
*
* [
* 'host' => 'localhost',
* 'port' => 3306,
* 'name' => 'dbname',
* 'user' => 'root',
* 'pass' => ''
* ]
*
* @param $settings
*/
function setupDb ($settings) {
// проверяем настройки
assert(isset($settings['host']), 'Db `host` is required');
assert(isset($settings['port']) && is_int($settings['port']), 'Db `port` is required, should be integer');
assert(isset($settings['name']), 'Db `name` is required, should be integer');
// соединяем с БД
// ...
}
setupDb(['host' => 'localhost']);
Никогда не используйте
assert()для проверки входных параметров, ведь фактическиassert()интерпретирует строковую переменную (ведёт себя какeval()), а это чревато PHP-инъекцией. И да, это правильное поведение, т.к. просто отключив assert’ы всё что передаётся внутрь будет проигнорировано, а если делать как в примере выше, то код будет выполняться, а внутрь отключенного assert’a будет передан булевый результат выполнения
Если у вас есть живой опыт использования assert() – поделитесь со мной, буду благодарен. И да, вот вам ещё занимательно чтива по этой теме – PHP Assertions, с таким же вопросом в конце 
В заключение
Я за вас напишу выводы из данной статьи:
- Ошибкам бой – их не должно быть в вашем коде
- Используйте исключения – работу с ними нужно правильно организовать и будет счастье
- Assert – узнали о них, и хорошо
P.S. Спасибо Максиму Слесаренко за помощь в написании статьи






