Главная » Error » PHP для начинающих

PHP для начинающих

Ошибки

Разновидности в семействе ошибок

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

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

Фатальные ошибки

Самый грозный вид ошибок – фатальные, они могут возникнуть как при компиляции, так и при работе парсера или PHP-скрипта, выполнение скрипта при этом прерывается.

E_PARSE
Это ошибка появляется, когда вы допускаете грубую ошибку синтаксиса и интерпретатор PHP не понимает, что вы от него хотите, например если не закрыли фигурную или круглую скобочку:

Или написали на непонятном языке:

Лишние скобочки тоже встречаются, и не важно круглые либо фигурные:

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

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;

Когда обращаются к несуществующему элементу массива:

Когда обращаются к несуществующей константе:

/**
Notice: Use of undefined constant UNKNOWN_CONSTANT – assumed ‘UNKNOWN_CONSTANT’
*/
echo UNKNOWN_CONSTANT;

Когда не конвертируют типы данных:

/**
Notice: Array to string conversion
*/
echo array();

Для избежания подобных ошибок – будьте внимательней, и если вам IDE подсказывает о чём-то – не игнорируйте её:

PHP E_NOTICE in PHPStorm

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’);

В моём редакторе подобные функции будут зачёркнуты:

PHP E_DEPRECATED in PHPStorm

Обрабатываемые

Этот вид, которые разводит сам разработчик кода, я их уже давно не встречал, не рекомендую их вам заводить:

  • 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, то ошибка будет считаться обработанной и выполнение скрипта продолжится, иначе — будет вызван стандартный обработчик, который логирует ошибку и в зависимости от её типа продолжит выполнение скрипта или завершит его. Вот пример обработчика:

$type: $message

“;
echo “

File: $file:$line

“;
echo “

Context: $”. join(‘, $’, array_keys($context)).”

“;
// сообщаем, что мы обработали ошибку, и дальнейшая обработка не требуется
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
‘, microtime(true) – $time);
echo number_format(memory_get_usage() – $memory, 0, ‘.’, ‘ ‘), ‘ bytes
‘;

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

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
‘, microtime(true) – $time);
echo number_format(memory_get_usage() – $memory, 0, ‘.’, ‘ ‘), ‘ bytes
‘;

Результат ожидаемо хуже, и на порядок (даже на два порядка!):

0.263645 seconds
992 bytes

Вывод однозначен – ошибки в коде приводят к лишней прожорливости скриптов – так что во время разработки и тестирования приложения включайте отображение всех ошибок!

Тестирование проводил на PHP версии 5.6, в седьмой версии результат лучше – 0.0004 секунды против 0.0050 – разница только на один порядок, но в любом случае результат стоит прикладываемых усилий по исправлению ошибок

Где собака зарыта

В PHP есть спец символ «@» – оператор подавления ошибок, его используют дабы не писать обработку ошибок, а положится на корректное поведение PHP в случае чего:

При этом обработчик ошибок указанный в 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]’) . ” donen”);
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]”) . ” donen”);
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(), “
n”;
echo $exception->getFile(), ‘:’, $exception->getLine(), “
n”;
echo $exception->getTraceAsString(), “
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 “
“;
// выводим информацию о первоначальном исключении
echo $e->getPrevious()->getMessage();
}

Т.е. запомните – блок finally будет выполнен даже в том случае, если вы в блоке catch пробрасываете исключение выше (собственно именно так он и задумывался).

Для вводной статьи информации в самый раз, кто жаждет ещё подробностей, то вы их найдёте в статье Исключительный код 😉

Задание
Написать свой обработчик исключений, с выводом текста файла где произошла ошибка, и всё это с подсветкой синтаксиса, так же не забудьте вывести trace в читаемом виде. Для ориентира – посмотрите как это круто выглядит у whoops.

PHP7 – всё не так, как было раньше

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

  1. при возникновении фатальных ошибок типа E_ERROR или фатальных ошибок с возможностью обработки E_RECOVERABLE_ERROR PHP выбрасывает исключение
  2. эти исключения не наследуют класс Exception (помните я говорил об обратной совместимости, это всё ради неё)
  3. эти исключения наследуют класс Error
  4. оба класса Exception и Error реализуют интерфейс Throwable
  5. вы не можете реализовать интерфейс 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

Как убрать из карточки товара элемент, вставленный через компоненты в каталоге товаров?

  • 1 подписчик
  • 21 минуту назад
  • 14 просмотров

ответов
0

  • php
    • PHP

    Простой

  • Создание массива, запись его в файл, как получить массив из файла?

    • 1 подписчик
    • час назад
    • 34 просмотра

    ответ
    1

  • php
    • PHP

    Простой

  • Add your solution here

     B  I  U  S smallBIGcodePlain TextASMASPASP.NETBASICBATC#C++COBOLCoffeeScriptCSSDartdbaseF#FORTRANHTMLJavaJavascriptKotlinLuaMIDLMSILObjectiveCPascalPERLPHPPowerShellPythonRazorRubyScalaShellSLNSQLSwiftT4TerminalTypeScriptVBVBScriptXMLvar <  >  & link[^]encodeuntabcaseindentoutdent

    <текстареа name="ctl00$ctl00$MC$AMC$PostEntryObj$Content$MessageText" id="ctl00_ctl00_MC_AMC_PostEntryObj_Content_MessageText" class="resizable text-editor" style="width:100%;height:250px;">

    OKPaste as

    Strip HTML
    Encode HTML
    Paste as-is
    Code block
    Quoted Text
    Best guess

    To display as

    The content must be between 30 and 50000 characters.Treat my content as plain text, not as HTML

    Отладка

    Иногда для отладки кода нужно отследить что происходило с переменной или объектом на определённом этапе, для этих целей есть функция debug_backtrace() и debug_print_backtrace() которые вернут историю вызовов функций/методов в обратном порядке:

    В результате выполнения функции 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 [путь к файлу], но я не встречал использования оных.

    Как правильно сделать выборку из связанных таблицу yii2?

    • 1 подписчик
    • час назад
    • 4 просмотра

    ответов
    0

  • php
    • PHP

    • +2 ещё

    Средний

  • Preview 0

    Existing Members

    Sign in to your account

    Источники

    • https://anton.shevchuk.name/php/php-for-beginners-error-handling/
    • https://qna.habr.com/q/546907
    • https://www.codeproject.com/Questions/1116464/How-can-I-solve-require-once-PHP-warnings-and-fata
    [свернуть]
    Решите Вашу проблему!


    ×
    Adblock
    detector