Этот урок набрал набрал достаточно большое количество комментариев и дальнейшее его комментирование отключено. Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку, посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали. Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
Ilon 04.09.2018 в 14:05

что же дальше? будут Composer? и хочется делать и решать реальные задачки? вроде как писать блог? это возможно?

ivashkevich 05.09.2018 в 23:42

Да, всё это будет в скором времени.

SBTesla 14.11.2018 в 12:24

Домашку сначала сделай)))

virtual2018 03.01.2019 в 16:51

Классы PHP не могут напрямую реализовать интерфейс Throwable, . (Ссылка на документацию)

ivashkevich 04.01.2019 в 08:52

Верно!

andreskrip 11.02.2020 в 16:17

Спасибо за урок! Единственное, что заметил - это то, что если одновременно и ошибка при подключении к БД и несуществующий роут, то выдаст ошибку 404, а не 500. Так и должно быть?

ivashkevich 12.02.2020 в 13:06

Зависит от того, какая ошибка возникнет первой.

OneMoreTime 15.03.2020 в 18:18

Вроде на первый взгляд - несложная тема, но что-то в ступор вогнало((. Как-то не очень сразу понятно в конкретной ситуации, где оборачивать в try, где ловить исключение. Понятно одно - ловить исключение нет смысла раньше. чем оно может появиться, а пытаться обработать ситуацию на возможное исключение нужно заблаговременно до возможной ситуации. Сложность - в пространственном ориентировании - размещении try/catch сквозь слои классов и методов. Ну и осталась не менее интересная тема за кадром - обработка ошибок. Почему в курсе рассмотрены именно исключения, а ошибки обделены вниманием?

Есть и более конкретные вопросы:

  1. как раз о местоположении try в коде.
<?php

try {
    spl_autoload_register(function (string $className) {
        require_once __DIR__ . '/../src/' . $className . '.php';
    });

    $route = $_GET['route'] ?? '';
    $routes = require __DIR__ . '/../src/routes.php';

    $isRouteFound = false;
    foreach ($routes as $pattern => $controllerAndAction) {
        preg_match($pattern, $route, $matches);
        if (!empty($matches)) {
            $isRouteFound = true;
            break;
        }
    }

    if (!$isRouteFound) {
        throw new \MyProject\Exceptions\NotFoundException();
    }

    unset($matches[0]);

    $controllerName = $controllerAndAction[0];
    $actionName = $controllerAndAction[1];

    $controller = new $controllerName();
    $controller->$actionName(...$matches);
} catch (\MyProject\Exceptions\DbException $e) {
    $view = new \MyProject\View\View(__DIR__ . '/../templates/errors');
    $view->renderHtml('500.php', ['error' => $e->getMessage()], 500);
} catch (\MyProject\Exceptions\NotFoundException $e) {
    $view = new \MyProject\View\View(__DIR__ . '/../templates/errors');
    $view->renderHtml('404.php', ['error' => $e->getMessage()], 404);
}

Почему тут try в самом начале кода, если автозагрузка классов, инициализация переменной route никак не влияет на возможное появление исключения - как то не найдена страница, или ошибка обращения к БД? Понятно, что такой большой захват кода в try не влияет в данном случае на логику, просто это вызывает дополнительные вопросы..

  1. Понятное дело, что ошибки/исключения возникают в некой очередности, а не все одновременно. Затрудняюсь привести конкретный пример, где это было прямо очень важно/необходимо, но может же быть необходимым вывести несколько ошибок/исключений? Насколько правильно корректно - показывать только первую ошибку/исключение или если только ошибка не делает невозможным дальнейшее выполнение кода, - все, что возникли? Да, немного в кучу и ошибки и исключения, но думаю, что суть вопроса понятна).

ivashkevich 16.03.2020 в 20:08
  1. Обрабатываемые исключения действительно могут броситься не во всем коде, обернутом в try. Можно блок try уменьшить. А можно добавить еще один catch с обработкой Throwable, на случай совсем уж непредвиденных ситуаций.
  2. Если требуется вывести несколько ошибок, можно во-первых сформировать готовую строку с ошибками и в конце обернуть это в одно исключение. Во-вторых можно использовать интересный паттерн MultiException. Ознакомьтесь на досуге.
Dimitry 05.04.2020 в 10:33

Спасибо за урок!
А что это за переменная $e, которую передаем в функцию исключения?

ivashkevich 05.04.2020 в 19:21

В какую функцию исключения? Не вижу тут функцию

Dimitry 07.04.2020 в 07:11
catch (\MyProject\Exceptions\NotFoundException $e) {
    $view = new \MyProject\View\View(__DIR__ . '/../templates/errors');
    $view->renderHtml('404.php', ['error' => $e->getMessage()], 404);

Да, я не правильно выразился, это класс, но вопрос остался, мы не объявляли переменную $e, но пользуемся ей,можно узнать, что это за переменная?

ivashkevich 07.04.2020 в 09:32

Прочитайте официальную документацию по try-catch. Если не будет понятно, напишите в личку.

Alexann 15.04.2020 в 18:53

Добрый вечер. Прочитал про интерфейс Throwable и понял, что реализовать его в своем классе можно только путем расширения интерфейса (Throwable) своими методами. Затем в классе исключения использовать уже свой расширенный интерфейс.
У меня как-то так получилось (это все для примера, так как можно было обойтись и без своего нового метода):
index.php:

...
catch (\MyProject\Exceptions\NotIdException $e) {
    $view = new \MyProject\View\View(__DIR__ . '/../templates/errors');
    $view->renderHtml('NotId.php', ['newMethodMessage'=>$e->newMethod()], 404);
}

NotId.php:

<h2>Такой статьи не существует</h2>
<?= $newMethodMessage ?>
<br>
<a href="/">Вернуться на главную станицу</a>

NotIdException.php:

<?php

namespace MyProject\Exceptions;

interface MyInterface extends \ Throwable
{
    public function newMethod();
}

class NotIdException extends \Exception implements MyInterface
{
    public function newMethod(): string
    {
        // TODO: Implement newMethod() method.
        return 'File: ' . $this->getFile() . '  ||  ' . '  Line: ' . $this->getLine();
    }
}

ArticleController.php:

...
if ($article === null) {
            throw new NotIdException();
        }
...

Но такой способ использовать не рекомендуют. Это так или нет? И вообще такой способ где-то используется в практике?

ivashkevich 16.04.2020 в 06:47

Достаточно было написать, что напрямую реализовать его нельзя) Но вы неплохо постарались)
А вот имена исключения и шаблона мне не нравятся. Для этого есть устоявшееся имя NotFound.

Alexann 16.04.2020 в 09:25

Спасибо вам за уроки. Умеете своими вопросами направить на путь более глубокого изучения отдельных областей.
Исключение и шаблон я уже исправил - это в предыдущем задании.

ivashkevich 16.04.2020 в 10:31

Хорошо) Навык самостоятельно искать информацию - самый важный для программиста. Мы мастера спорта по гуглению)

Dmitry.Dudin 22.04.2020 в 21:55

Что-то пропустил, куда у нас делся

$reflector = new \ReflectionObject($article);
        $properties = $reflector->getProperties();
        $propertiesNames = [];
        foreach ($properties as $property) {
            $propertiesNames[] = $property->getName();
        }

Из public function view(int $articleId) в ArticleController?

ivashkevich 23.04.2020 в 10:38

Ты чего? Это всё в ActiveRecordEntity, причем несколько уроков как.

[email protected] 10.05.2020 в 21:21

ДЗ с подвохом)

Пользовательские классы не могут реализовывать Throwable. Это было сделано для предсказуемости: только экземпляры Exception или Error могут быть брошены.

А в целом, что я понял: Можем ловить сначала Error, если не поймали, то ловим Throwable. Либо сразу Throwable.

ivashkevich 12.05.2020 в 07:43

Да можно сразу Throwable

OneMoreTime 13.05.2020 в 20:10

Этот код работает, когда не нужен нужный роутинг

Когда НЕ НАЙДЕН??


В каких случаях необходимо устанавливать

setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

? Даже если этот аттрибут явно не установлен, какие-то ошибки параметров соединения при попытке создания объекта в конструкторе класса Db приведут к выбрасыванию PDO исключения. Но, если возникает какая-то ошибка(например в синтаксисе) при попытке выполнить SQL запрос, то исключения будут сами выбрасываться только если установлен этот аттрибут. Почему в первом случае выбрасываются исключения и без этого аттрибута?

Может по этой причине?:

PDO::construct() выбрасывает исключение PDOException, если попытка подключения к запрашиваемой базе данных завершается с ошибкой.
Метод PDO::
construct() будет всегда бросать исключение PDOException, если соединение оборвалось, независимо от установленного значения PDO::ATTR_ERRMODE. Непойманные исключения фатальны.


описание картинки

Для кого производится подобная обработка исключений, в контексте примера на картинке выше? С одной стороны - если для разработчика на время работы над проектом, зачем заменять более информативную штатную табличку исключения со стеком вызовов, кратким окружением - одной строкой сжатого текста об ошибке? Если для пользователей продукта - зачем выводить подробности ошибки?

ivashkevich 14.05.2020 в 13:06

Когда НЕ НАЙДЕН??

Спасибо, исправил)

Да, ошибка установления это всегда исключение. С синтаксисом по-хорошему тоже стоит так же поступать, но это почему-то оставили на усмотрение разработчиков приложений.

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

OneMoreTime 14.05.2020 в 14:40

ОК. Спасибо.

studentDev 14.05.2020 в 07:14

Пусть у нас есть 3 финкции: func1, func2 и func3. func1 вызывает внутри себя func2, а func2 вызывает func3.
В этом предложение есть отпечатка в слове "функции"

ivashkevich 14.05.2020 в 13:14

Большое спасибо, исправил

studentDev 15.05.2020 в 12:45

Интерфейс Throwable напрямую реализовать нельзя. В нём хранятся только подклассы Exception и Error.

ivashkevich 15.05.2020 в 18:38

Верно

[email protected] 17.05.2020 в 18:10

Проверил на практике, документация не врет)) Реализовать интерфейс напрямую нельзя.

Fatal error: Class MyProject\Exceptions\MyException cannot implement interface Throwable, extend Exception or Error instead in C:\OSPanel\domains\myproject.blog\src\MyProject\Exceptions\MyException.php on line 5

ivashkevich 18.05.2020 в 06:08

Отлично

titelivus 19.05.2020 в 15:44

NotFoundException.php

<?php

namespace MyProject\Exceptions;

class NotFoundException implements \Throwable
{
    /** @var string */
    protected $message;

    /** @var int */
    protected $code;

    /** @var string */
    protected $file;

    /** @var int */
    protected $line;

    public function __construct(string $message = '', int $code = 0)
    {
        $this->message = $message;
        $this->code = $code;
        $this->file = __DIR__ . __FILE__;
        $this->line = __LINE__;
    }

    public function getMessage(): string
    {
        return $this->message;
    }

    public function getCode(): int
    {
        return $this->code;
    }

    public function getFile(): string
    {
        return $this->file;
    }

    public function getLine(): int
    {
        return $this->line;
    }

    public function getTrace() : array
    {
        // Не знаю как реализовать
        return [];
    }

    public function getTraceAsString(): string
    {
        // Не знаю как реализовать
        return '';
    }

    public function getPrevious(): \Throwable
    {
        return \Throwable::getPrevious();
    }

    public function __toString(): string
    {
        return \Throwable::__toString();
    }
}
titelivus 19.05.2020 в 15:53

Попытка запустить скрипт привела к

Fatal error: Class MyProject\Exceptions\NotFoundException cannot implement interface Throwable, extend Exception or Error instead in

Класс MyProject\Exceptions\NotFoundException не может реализовывать интерфейс Throwable, вместо этого следует наследовать класс Exception или Error.

Спустя некоторое время понял что:

Классы PHP не могут напрямую реализовать интерфейс Throwable. Вместо этого они могут наследовать подкласс Exception.

ivashkevich 20.05.2020 в 07:53

Всё верно

[email protected] 08.09.2020 в 08:50

Добрый день!
Почему в моём случае отображаются ещё и окна с ошибками, а не только Хьюстон и строчка с текстом ошибки?
окна помимо текста пойманной ошибки

Конструктор класса Db:
src/MyProject/services/Db.php

private function __construct()
{
    $dbOptions = (require __DIR__ . '/../../settings.php')['db'];

    try {
        $this->pdo = new \PDO(
            'mysql:host=' . $dbOptions['host'] . ';dbname=' . $dbOptions['dbname'],
            $dbOptions['user'],
            $dbOptions['password']
        );
        $this->pdo->exec('SET NAMES UTF8');
    } catch (\PDOException $e) {
        throw new DbException('Ошибка при подключении к базе данных: ' . $e->getMessage());
    }
}

src\MyProject\Exceptions\DbException.php

namespace MyProject\Exceptions;

class DbException extends \Exception
{
}

www\index.php

try {
    spl_autoload_register(function (string $className) {
        require_once __DIR__ . '/../src/' . $className . '.php';
    });

    $route = $_GET['route'] ?? '';
    $routes = require __DIR__ . '/../src/routes.php';

    $isRouteFound = false;
    foreach ($routes as $pattern => $controllerAndAction) {
        preg_match($pattern, $route, $matches);
        if (!empty($matches)) {
            $isRouteFound = true;
            break;
        }
    }

    if (!$isRouteFound) {
        echo 'Страница не найдена!';
        return;
    }

    unset($matches[0]);

    $controllerName = $controllerAndAction[0];
    $actionName = $controllerAndAction[1];

    $controller = new $controllerName();
    $controller->$actionName(...$matches);
} catch (\MyProject\Exceptions\DbException $e) {
    $view = new \MyProject\View\View(__DIR__ . '/../templates/errors');
    $view->renderHtml('500.php', ['error' => $e->getMessage()], 500);
}
ivashkevich 08.09.2020 в 18:41

Текст ошибки пробовали читать?

[email protected] 08.09.2020 в 19:41

1) в тексте та же ошибка, которую я ловлю
2) код ничем не отличается от предоставленного в уроке
3) ошибка дублируется по неизвестной мне причине (сначала PDOException, потом только созданное нами DbException)

[email protected] 08.09.2020 в 23:49

Я прочитал текст ошибки, но что мне это даёт? Я же ловлю \PDOException и инициирую \DbException, почему выводится окно с сообщением о возникшем \PDOException? Не говоря уже об окне с \DbException

ivashkevich 10.09.2020 в 08:31

При чем тут класс? Текст ошибки читайте. Нет такой базы.

[email protected] 10.09.2020 в 09:17

Так ведь и нужно, чтобы эта ошибка была! Не нужно только, чтобы возникали дополнительные окна с сообщениями об этой ошибке, а только текст "Хьюстон, у нас проблема! Ошибка при подключении к базе данных: (текст ошибки)". Вопрос в том, почему там ещё и окна, как на скриншоте, который я прикрепил в первом сообщении?

ivashkevich 10.09.2020 в 10:55

Ох ёлки) Прошу прощения за невнимательность.

Дебаггером смотрели, что может пойти не так?

Самый простой путь - подобавлять echo 1, echo 2 и т.д. по коду, и понять, в каком месте вылетает ошибка.

[email protected] 10.09.2020 в 23:35

Разобрался. В моём php.ini директива xdebug.show_exception_trace имела значение 1, её необходимо выставить в 0. Теперь выводится только то, что я говорю выводить в случае пойманного исключения

[email protected] 11.09.2020 в 00:29

Огромное спасибо за уроки! Суперские, современные, с учётом всех модных фишек (нэймспейсы, дебаггер, ловля ошибок и т.д.) - учите, как писать код правильно, надоело мне говнокодить, хочу творить красиво!

ivashkevich 11.09.2020 в 19:30

На здоровье!)

[email protected] 09.09.2020 в 22:55

Пожалуйста, помогите!

[email protected] 15.09.2020 в 13:18

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

ivashkevich 27.09.2020 в 16:42

Как исправить?)

[email protected] 29.09.2020 в 15:47

Сделать как и во вьюхе, чтобы объект юзера всегда был в исключения. Ну я в общем сделал уже)

ivashkevich 29.09.2020 в 19:40

Молодцом!

SkSeMi 21.01.2021 в 00:13

Поделитесь, пожалуйста, данным участком кода.
Спасибо!

pixel 31.12.2020 в 20:24

Подсмотрел в документации, классы не могут реализовать Throwable только через интерфейс и от класса Exception

interface MyPacketThrowable extends Throwable
{
}

class MyPackExeption extends Exception implements MyPacketThrowable
{
}

try {
    throw new MyPackExeption();
} catch (Throwable $e) {
    echo 'thro222222222222';
}
ivashkevich 01.01.2021 в 12:40

Верно

Dimdim 05.04.2021 в 05:03

в коде в ArticlesController в методе add() ,видимо опечатка: добавлен
$article->delete();

ivashkevich 08.04.2021 в 19:43

Спасибо, поправил

Логические задачи с собеседований