Отдельная большая тема в программировании – обработка ошибок. До сих пор нам удавалось избегать её, но в реальном мире, где приложения содержат тысячи, десятки и сотни тысяч (а то и все миллионы) строк кода, обработка ошибок влияет на многое: простоту модификации и расширения, адекватное поведение программы для пользователя в разных ситуациях.
В этом уроке мы рассмотрим механизм исключений. Но перед тем, как изучать новые конструкции, поговорим про ошибки вообще.
В PHP есть функция, которая называется strpos($text, $substr). Она ищет подстроку $substr внутри текста $text и возвращает индекс начала этой подстроки в тексте. Что произойдёт, если подстрока не была найдена? Является ли это поведение ошибкой? Нет. Это штатное поведение функции. От того, что подстрока не была найдена, ничего страшного не случилось. Представьте себе любой редактор текста и механизм поиска внутри него. Ситуация, когда ничего не было найдено, возникает постоянно, и это не ломает работу программы.
Кстати, посмотрите в документацию этой функции, каким образом она говорит о том, что подстрока не была найдена?
Другая ситуация. В тех же редакторах есть функция "открыть файл". Представьте, что во время открытия файла, что-то пошло не так, например, его удалили. А это ошибка или нет? Да, в этой ситуации произошла ошибка, но это не ошибка программирования. Подобная ошибка может возникнуть всегда, независимо от желания программиста. Он не может избежать её появления. Единственное, что он может — правильно реализовать её обработку.
Ещё один интересный вопрос, насколько это критичная ошибка? Должно ли оно приводить к остановке всего приложения или нет? В плохо написанных приложениях, там, где неправильно реализована обработка ошибок, такая ситуация приведёт к краху всего приложения, и оно завершится. В хорошо написанном приложении не произойдёт ничего страшного. Пользователь увидит предупреждение о том, что файл не читается, и сможет выбрать дальнейшие действия, например, попытаться прочитать его снова или выполнить другое действие.
Сказанное выше имеет очень серьёзные следствия. Одна и та же ситуация на разных уровнях может как являться ошибкой, так и быть вполне штатной ситуацией. Например, если задача функции читать файл, а она не смогла этого сделать, то с точки зрения этой функции произошла ошибка. Должна ли она приводить к остановке всего приложения? Как мы выяснили выше – не должна. О том, насколько критична данная ситуация, может решать приложение, которое использует эту функцию, но не сама функция.
Коды возврата
В языках появившихся до 1990 года (примерно), обработка ошибок выполнялась через механизм возврата функцией специального значения. Например, в Си, если функция не может выполнить свою задачу, то она должна вернуть специальное значение, либо NULL либо отрицательное число. Значение этого числа говорит о том, какая ошибка произошла. Например:
Обратите внимание на условные конструкции и постоянное присваивание переменной ret. Фактически каждая потенциально опасная операция, должна проверяться на успешность выполнения. Если что-то пошло не так, то функция возвращает специальный код.
И вот тут начинаются проблемы. Как показывает жизнь, в большинстве ситуаций ошибка обрабатывается не там где она возникла и даже не уровнем выше. Предположим, что есть функция A, которая вызывает код, потенциально приводящий к ошибке, и она его должна уметь правильно обработать и сообщить пользователю о проблеме. При этом сама ошибка происходит внутри функции E, которая вызывается внутри A не напрямую, а через цепочку функций: A => B => C => D => E. Подумайте, к чему приводит такая схема? Все функции в этой цепочке, даже несмотря на то, что они не обрабатывают ошибку, обязаны знать про неё, отлавливать её и так же возвращать наружу код этой ошибки. В результате, количества кода, который занимается ошибками, становится так много, что за ним теряется код, выполняющий исходную задачу.
Стоит сказать, что существуют схемы обработки ошибок, которые не обладают такими недостатками, но работают по принципу возврата. Например, монада Either.
Исключения
Именно на этом фоне и возник механизм исключений. Его главная цель, передать ошибку из того места, где она возникла, в место, где её можно обработать, минуя все промежуточные уровни. Другими словами, механизм исключений раскручивает стек вызовов самостоятельно.
С исключениями нужно запомнить две вещи: код, в котором произошла ошибка, выбрасывает исключение, а код, в котором ошибка обрабатывается – её ловит.
Сами исключения – это объекты класса \Exception и его наследников (о наследовании в одном из следующих курсов). Этот объект содержит внутри себя сообщение, переданное в конструктор, трассировка стека и другие полезные данные:
Выбросить исключение проще простого, достаточно использовать инструкцию throw:
throw прерывает дальнейшее выполнение кода. В этом смысле оно подобно return, но в отличие от него, прерывает выполнение не только текущей функции, но и всего кода, вплоть до ближайшего в стеке вызовов блока catch.
Блок try/catch обычно ставится на самом верхнем уровне программы, но это не обязательно. Вполне вероятно, что есть несколько промежуточных блоков, которые могут отлавливать ошибки и снова их возбуждать. Эта тема достаточно сложная и требует некоторого опыта работы.
Когда PHP только появился, то в нём не был реализован механизм исключений. Поэтому многие функции либо возвращают какие-то значения указывающие на ошибку, либо об ошибке можно узнать с помощью специальных ухищрений. Эти подходы пришли прямиком из Си, и в современных языках больше нигде не встречаются. Все ещё ухудшается наличием альтернативного механизма: Notice, Warning, Fatal. К счастью, в новых версиях PHP эти ошибки заменяют на исключения, но вряд ли когда-нибудь произойдёт полный переход. Написано слишком много кода, а язык должен сохранять обратную совместимость.
<!DOCTYPE html>
<html class="h-100" data-bs-theme="light" data-mantine-color-scheme="light" lang="ru" prefix="og: https://ogp.me/ns#">
<head>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<link crossorigin="true" href="https://cdn.hexlet.io" rel="preconnect">
<link href="https://mc.yandex.ru" rel="preconnect">
<meta content="aa2vrdtq64dub8knuf83lwywit311w" name="facebook-domain-verification">
<link href="/favicon.ico" rel="icon" sizes="any">
<link href="/favicon.svg" rel="icon" type="image/svg+xml">
<link href="/apple-touch-icon.png" rel="apple-touch-icon">
<link href="/manifest.webmanifest" rel="manifest">
<script>
//<![CDATA[
window.gon={};gon.ym_counter="25559621";gon.is_bot=true;gon.applications={};gon.current_user={"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26 22:26:59 UTC","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false};gon.token="E01ffqUa8tKqMMv2ZrOeP7BnXFf3suBIJFdkFenoKAH8nJRJV2Rfshxz725qvG5IcG5x_f-FHuqZt_5Bu-_Pbw";gon.locale="ru";gon.language="ru";gon.theme="light";gon.rails_env="production";gon.mobile=false;gon.google={"analytics_key":"UA-1360700-51","optimize_key":"GTM-5QDVFPF"};gon.captcha={"google_v3_site_key":"6LenGbgZAAAAAM7HbrDbn5JlizCSzPcS767c9vaY","yandex_site_key":"ysc1_Vyob5ZPPUdPBsu0ykt8bVFdzsfpoVjQChLGl2b4g19647a89","verification_failed":null};gon.social_signin=false;gon.typoreporter_google_form_id="1FAIpQLSeibfGq-KvWQ2Fyru-zkFFRVTLBuzXAHAoEyN1p49FtDmNoNA";
//]]>
</script>
<meta charset="utf-8">
<title>Исключения | PHP: Введение в ООП</title>
<meta name="description" content="Исключения / PHP: Введение в ООП: Знакомимся с исключениями, учимся их генерировать и перехватывать">
<link rel="canonical" href="https://ru.hexlet.io/courses/php-introduction-to-oop/lessons/exceptions/theory_unit">
<meta name="robots" content="noarchive">
<meta property="og:title" content="Исключения">
<meta property="og:title" content="PHP: Введение в ООП">
<meta property="og:description" content="Исключения / PHP: Введение в ООП: Знакомимся с исключениями, учимся их генерировать и перехватывать">
<meta property="og:url" content="https://ru.hexlet.io/courses/php-introduction-to-oop/lessons/exceptions/theory_unit">
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="f4JBJ9Vb5WW_p-ETQ9J0irNft1fYvx4U1d_L13_rXBGQU4oQJyVIBQnkxYtP3YT9c1aa_dCI4LZoP1GDLey7fw" />
<script src="/vite/assets/inertia-DfXos102.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/preload-helper-BJ4cLWpC.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ahoy-DrlRQ-1D.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/analytics-cb8xch9l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Surface-DL2bpZA-.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/extends-C-EagtpE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/inheritsLoose-BBd-DCVI.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/objectWithoutPropertiesLoose-DRHXDhjp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/index.esm-DAqKOkZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Button-CGPUux8l.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/CloseButton-D1euiPao.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Group-BX48WcuU.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Loader-BQEY8g6v.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Modal-Cy3HByv7.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/OptionalPortal-1Hza5P2w.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Stack-CtjJzfw4.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Textarea-Ck64llAy.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/DirectionProvider-Dc9zdUke.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/events-DJQOhap0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-reduced-motion-D2owz4wa.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-disclosure-zKtK5W1r.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/use-hotkeys-Cnc_Rwkb.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/random-id-DOQyszCZ.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/exports-C_MrNx_T.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<link rel="stylesheet" href="/vite/assets/application-BqhCP46M.js" />
<script src="/vite/assets/application-Df9RExpe.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/autocomplete-VMNbxKGl.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/createPopper-C3aM9r1M.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/js.cookie-D1-O8zkX.js" as="script" crossorigin="anonymous"><link rel="stylesheet" href="/vite/assets/application-C8HjmMaq.css" media="screen" />
<script>
window.ym = function(){(ym.a=ym.a||[]).push(arguments)};
window.addEventListener('load', function() {
setTimeout(function() {
ym.l = 1*new Date();
ym(window.gon.ym_counter, "init", {
clickmap: true,
trackLinks: true,
accurateTrackBounce: true,
webvisor: true
});
// Загружаем скрипт
var k = document.createElement('script');
k.async = 1;
k.src = 'https://mc.yandex.ru/metrika/tag.js';
document.head.appendChild(k);
ym(window.gon.ym_counter, 'getClientID', function(clientID) {
window.ymClientId = clientID;
});
}, 1500);
});
</script>
<!-- Google Tag Manager - deferred -->
<script>
// dataLayer stub сразу — пуши работают до загрузки скрипта
window.dataLayer = window.dataLayer || [];
// Сам скрипт — отложенно после load
window.addEventListener('load', function() {
setTimeout(function() {
dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});
var j = document.createElement('script');
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-WK88TH';
document.head.appendChild(j);
}, 1500);
});
</script>
<!-- End Google Tag Manager -->
</head>
<body>
<noscript>
<div>
<img alt="" src="https://mc.yandex.ru/watch/25559621" style="position:absolute; left:-9999px;">
</div>
</noscript>
<header class="sticky-top bg-body">
<nav class="navbar navbar-expand-lg">
<div class="container-xxl">
<a class="navbar-brand" href="/"><img alt="Логотип Хекслета" height="24" src="https://ru.hexlet.io/vite/assets/logo_ru_light-BpiEA1LT.svg" width="96">
</a><button aria-controls="collapsable" aria-expanded="false" aria-label="Меню" class="navbar-toggler border-0 mb-0 mt-1" data-bs-target="#collapsable" data-bs-toggle="collapse">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsable">
<ul class="navbar-nav mb-lg-0 mt-lg-1">
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
Все курсы
<span class="bi bi-chevron-down align-middle ms-1"></span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item d-flex py-2" href="/courses"><div class="fw-bold me-auto">Все что есть</div>
<div class="text-muted">117</div>
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные категории</b>
</li>
<li>
<a class="dropdown-item py-2" href="/courses_devops">Курсы по DevOps
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_data_analytics">Курсы по аналитике данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_programming">Курсы по программированию
</a></li>
<li>
<a class="dropdown-item py-2" href="/courses_testing">Курсы по тестированию
</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li class="dropdown-item">
<b>Популярные курсы</b>
</li>
<li>
<a class="dropdown-item py-2" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/go">Go-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/java">Java-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/python">Python-разработчик
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/qa-auto-engineer-java">Автоматизатор тестирования на Java
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/data-analytics">Аналитик данных
</a></li>
<li>
<a class="dropdown-item py-2" href="/programs/frontend">Фронтенд-разработчик
</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<button aria-haspopup class="btn nav-link" data-bs-toggle="dropdown" type="button">
О Хекслете
<span class="bi bi-chevron-down align-middle"></span>
</button>
<ul class="dropdown-menu bg-body">
<li>
<a class="dropdown-item py-2" href="/pages/about">О нас
</a></li>
<li>
<a class="dropdown-item py-2" href="/blog">Блог
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/hse-research" role="button">Результаты (Исследование)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://career.hexlet.io" role="button">Хекслет Карьера
</span></li>
<li>
<a class="dropdown-item py-2" href="/testimonials">Отзывы студентов
</a></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://t.me/hexlet_help_bot" role="button">Поддержка (В ТГ)
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/referal-program/?promo_creative=priglasite-druzei&promo_name=referal-program&promo_position=promo_position&promo_start=010724&promo_type=link" role="button">Реферальная программа
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://special.hexlet.io/certificate" role="button">Подарочные сертификаты
</span></li>
<li>
<span class="dropdown-item py-2 external-link" data-href="https://hh.ru/employer/4307094" role="button">Вакансии
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://b2b.hexlet.io" data-target="_blank" role="button">Компаниям
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexly.ru/" data-target="_blank" role="button">Колледж
</span></li>
<li>
<span class="dropdown-item d-flex external-link" rel="noopener noreferrer nofollow" data-href="https://hexlyschool.ru/" data-target="_blank" role="button">Частная школа
</span></li>
</ul>
</li>
<li><a class="nav-link" href="/subscription/new">Подписка</a></li>
</ul>
<ul class="navbar-nav flex-lg-row align-items-lg-center gap-2 ms-auto">
<li>
<a class="nav-link" aria-label="Переключить тему" href="/theme/switch?new_theme=dark"><span aria-hidden="true" class="bi bi-moon"></span>
</a></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="/u/new" role="button"><span>Регистрация</span>
</span></li>
<li>
<span data-target="_self" class="nav-link external-link" data-href="https://ru.hexlet.io/session/new" role="button"><span>Вход</span>
</span></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="x-container-xxxl">
</div>
<main class="mb-6 min-vh-100 h-100">
<link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"/><link rel="preload" as="image" href="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"/><link rel="preload" as="image" href="/vite/assets/development-BVihs_d5.png"/><div id="app" data-page="{"component":"web/courses/lessons/theory_unit","props":{"errors":{},"locale":"ru","language":"ru","httpsHost":"https://ru.hexlet.io","host":"ru.hexlet.io","colorScheme":"light","auth":{"user":{"id":null,"last_viewed_notification_id":null,"email":null,"state":null,"first_name":"","last_name":"","created_at":"2026-02-26T22:26:59.722Z","current_program":null,"current_team":null,"full_name":"","guest":true,"can_use_paid_features":false,"is_hexlet_employee":false,"sanitized_phone_number":"","can_subscribe":true,"can_renew_education":false}},"cloudflareTurnstileSiteKey":"0x4AAAAAAA15KmeFXzd2H0Xo","vkIdClientId":"51586979","yandexIdClientId":"88d071f1d3384eb4bd1deb37910235c7","formAuthToken":"zdO0xhS7Dl559GsnH6qg6wSCcun48T70eqrv18TKdMwiAn_x5sWjPs-3T78TpVCcxItfQ_DGwFbHSnWDls2Tog","topics":[{"id":48339,"title":"Добрый день.\nЯ, методом научного тыка, создал нечто странное и оно прошло тесты\n\nhttps://ru.hexlet.io/code_reviews/326552\n\nЭто вообще законно?","plain_title":"Добрый день. Я, методом научного тыка, создал нечто странное и оно прошло тесты https://ru.hexlet.io/code_reviews/326552 Это вообще законно? ","creator":{"public_name":"Pavel Mironovic","id":292030,"is_tutor":false},"comments":[{"creator":{"public_name":"Pavel Mironovic","id":292030,"is_tutor":false},"id":103855,"body":"Ну, я не нарочно, задача подогнать не ставилась, оно само написалось от долгого глядения на тесты и выдачу. А json_last_error_msg оказалось для меня внезапным открытием. А что такое Exception::class осталось загадкой.","topic_id":48339},{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":103852,"body":"Добрый день. Нет, это не законно :) Вы подогнали решение под тесты. Если в вашу функцию передать другую строку, не ту что в тестах, но которая является валидным json, ваша функция отработает не верно. Посмотрите, как организована проверка на валидность в решении учителя. \nКроме того, обратите внимание, что блок try/catch в этой функции не нужен. Он должен стоять в коде, который будет вызывать вашу функцию. Ваша функция только пробрасывает исключение, а ловит его вызывающий код","topic_id":48339}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":45292,"title":"Добрый вечер! \n\nПочему-то второй вопрос теста считает правильным ответ \"Да, исключение не влияют на поток выполнения кода\".\n\n[Ссылка на Скриншот](https://yadi.sk/i/_IClmLHnjdKLLQ).","plain_title":"Добрый вечер! Почему-то второй вопрос теста считает правильным ответ \"Да, исключение не влияют на поток выполнения кода\". enter image description here https://yadi.sk/i/_IClmLHnjdKLLQ ","creator":{"public_name":"Николай Калашников","id":198605,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":98072,"body":"Добрый день, Николай!\n\nЯ исправил тест. Спасибо, что указали на ошибку! Согласно теории:\n\n> throw прерывает дальнейшее выполнение кода.\n\nПоэтому ответ \"Да, исключение не влияют на поток выполнения кода\", не является верным.","topic_id":45292}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":41472,"title":"Что нет так?\n1) App\\Tests\\SafeTest::testJsonDecode2\n\nFailed asserting that exception of type \"Exception\" is thrown.\nНе возникло исключение?\n```\nfunction json_decode($json, $assoc = false)\n{\n // BEGIN (write your solution here)\n try{\n \n \n $json_array =\\json_decode($json, $assoc);\n if( $json_array != NULL || json_last_error() !== JSON_ERROR_NONE)\n {\n return $json_array;\n }\n \n }catch (\\Exception $e) {\n //throw $e;\n \n showErrorToUser($e);\n }\n // END\n}\n```\n","plain_title":"У меня этот код на ревью не проходит ``` function json_decode($json, $assoc = false) { // BEGIN (write your solution here) try{ $json_array =\\json_decode($json, $assoc); if( $json_array != NULL || json_last_error() !== JSON_ERROR_NONE) { return $json_array; } }catch (\\Exception $e) { //throw $e; showErrorToUser($e); } // END } Вместо этого кода показывается на ревью : function json_decode($json, $assoc = false) { // BEGIN (write your solution here) try{ $json_array =\\json_decode($json, $assoc); if( $json_array != NULL ) { return $json_array; } }catch (\\Exception $e) { //throw $e; showErrorToUser($e); } // END } ``` ","creator":{"public_name":"Светлана Ильина","id":149206,"is_tutor":false},"comments":[{"creator":{"public_name":"Andy Groza","id":206149,"is_tutor":true},"id":90636,"body":"Здравствуйте.\n\nДа, конечно, подсказка к этому ассерту говорит, что ожидалось исключение, а его небыло. \n\nЕще смотрите, в этом уроке написано:\n> Например, обработка ошибок парсинга JSON в PHP реализована крайне странно по современным меркам. Чтобы узнать об ошибке, нужно вызвать функцию json_last_error() сразу после парсинга. А текст ошибки получается функцией json_last_error_msg().\n\nВидите, здесь json_decode сам по себе не выбрасывает исключение (и Вы не можете просто так обернуть в try ... catch), а ошибка появляется в json_last_error().\n\nСтало немножко понятнее?","topic_id":41472}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":29459,"title":"В тексте задания в одном месте указана функция json_decode, а в другом - json_encode. Видимо, опечатка?","plain_title":"В тексте задания в одном месте указана функция jsondecode, а в другом - jsonencode. Видимо, опечатка? ","creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"comments":[{"creator":{"public_name":"Александр О.","id":61806,"is_tutor":false},"id":63781,"body":"Да, поправил опечатку, спасибо!","topic_id":29459}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":54783,"title":"$json1 = \\json_decode($json, $assoc);\n\nа в этой строчке для чего ставится слеш перед функцией?","plain_title":"$json1 = \\json_decode($json, $assoc); а в этой строчке для чего ставится слеш перед функцией? ","creator":{"public_name":"Дилянур","id":321193,"is_tutor":false},"comments":[{"creator":{"public_name":"Maksim Litvinov","id":198906,"is_tutor":true},"id":116381,"body":"Обратный слэш перед именем функции говорит о том, что должна быть вызвана функция из глобального пространства имен, т.к. в текущем пространстве имен объявлена одноименная функция","topic_id":54783}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":28113,"title":"Здравствуйте! Результат первого теста это массив, а второй ожидает исключение класса исключение?? Логика задания такая что:\n1) Проверить внутренней функцией json_decode переданную строку.\n2) При наличии ошибок бросаем throw.\nНо что должен вернуть catch? Я ни как не могу пройти второй тест. \n```\nFailed asserting that exception of type \"Exception\" is thrown.\n```\nthrow бросаю в условной конструкции проверки количества ошибок. \nВот код\n\n`// removed`","plain_title":"Здравствуйте! Результат первого теста это массив, а второй ожидает исключение класса исключение?? Логика задания такая что: 1) Проверить внутренней функцией json_decode переданную строку. 2) При наличии ошибок бросаем throw. Но что должен вернуть catch? Я ни как не могу пройти второй тест. Failed asserting that exception of type \"Exception\" is thrown. throw бросаю в условной конструкции проверки количества ошибок. Вот код // removed ","creator":{"public_name":"Андрей Захватошин","id":183403,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":60472,"body":"Вам не нужно обрабатывать ошибку. Задача подразумевает лишь то, что в случае наличия ошибки вы бросаете исключение.","topic_id":28113}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":35581,"title":"Добрый вечер)Подскажите пожалуйста,в чем я допустил ошибку?\nhttps://ru.hexlet.io/code_reviews/184334","plain_title":"Добрый вечер)Подскажите пожалуйста,в чем я допустил ошибку? https://ru.hexlet.io/code_reviews/184334 ","creator":{"public_name":"Анатолий Москалев","id":252219,"is_tutor":false},"comments":[{"creator":{"public_name":"Nikolai Gagarinov","id":104929,"is_tutor":true},"id":77691,"body":"Анатолий, привет. Можешь объяснить как у тебя должны работать условные конструкции?","topic_id":35581},{"creator":{"public_name":"Анатолий Москалев","id":252219,"is_tutor":false},"id":77718,"body":"**Nikolai Gagarinov**, В if происходит проверка ошибок парсинга.Если они есть выбрасывается исключение если нет ф-ия возвращает результат.","topic_id":35581},{"creator":{"public_name":"Анатолий Москалев","id":252219,"is_tutor":false},"id":77720,"body":"**Анатолий Москалев**, Понял свою ошибку))Спасибо за помощь!)","topic_id":35581}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":37919,"title":"В этом шаге Теории, насколько я понимаю, допущена ошибка. Мы в примере кода с исключением определяем функцию **read**File, а далее по тексту, в функции **run** обращаемся к функции **open**File, хотя должно быть **read**File","plain_title":"В этом шаге Теории, насколько я понимаю, допущена ошибка. Мы в примере кода с исключением определяем функцию readFile, а далее по тексту, в функции run обращаемся к функции openFile, хотя должно быть readFile ","creator":{"public_name":"Дмитрий Безбородов","id":244161,"is_tutor":false},"comments":[{"creator":{"public_name":"Kirill Mokevnin","id":1,"is_tutor":false},"id":82981,"body":"Да и там специально дан комментарий по этому поводу. Что readFile вызывается внутри openFile. Так подчеркивается то, что для исключений не важна глубина вызова функции.","topic_id":37919}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":54472,"title":"Так есть же константа JSON_THROW_ON_ERROR для json_decode()","plain_title":"Так есть же константа JSONTHROWONERROR для jsondecode() ","creator":{"public_name":"Константин Кузьмин","id":177503,"is_tutor":false},"comments":[{"creator":{"public_name":"Roman Ashikov","id":226258,"is_tutor":true},"id":115779,"body":"Спасибо, что обратили внимание! Верно, сейчас можно легко перехватывать и обрабатывать ошибки `json_decode()`. Убрал последний абзац из теории к уроку.","topic_id":54472}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}},{"id":34799,"title":"Добрый день, тоже стопорнул на данном задании, судя по количеству обсуждений, люди пропускают. либо сами быстро разбираются =) \n\n\n1. После отлова ошибки, в блоке catch ее просто вывести или что с ней надо сделать? \n2. https://ru.hexlet.io/code_reviews/176471 - Накидал набросок, и тупик, не совсем понимаю, что дальше делать.\n","plain_title":"Добрый день, тоже стопорнул на данном задании, судя по количеству обсуждений, люди пропускают. либо сами быстро разбираются =) Для чего переменная \"$assoc = false\" в вызываемой нашей функции?? После отлова ошибки, в блоке catch ее просто вывести или что с ней надо сделать? ","creator":{"public_name":"Александр Новиков","id":169561,"is_tutor":false},"comments":[{"creator":{"public_name":"Stanislav Dzisiak","id":212236,"is_tutor":true},"id":76010,"body":"**Александр Новиков**, приветствую!\n\nПо условию задачи, если возникнет ошибка, необходимо выбросить исключение. Это делается для того, что бы код, вызывающий вашу функцию, узнал о том, что при выполнении произошла ошибка. И уже в нем будет происходить обработка исключения. В вашей функции, благодаря конструкции try catch, вы ловите сгенерированное исключение, соответственно вызывающий код не узнает об ошибке. Поэтому в данном случае не нужно использовать конструкцию try catch. Функция должна вернуть результат в случае успешного выполнения или выбросить исключение, если возникла ошибка.","topic_id":34799}],"communitable":{"parent_entity_name":null,"parent_entity_url":null,"entity_name":"Исключения","entity_url":null,"active":true}}],"lesson":{"exercise":{"id":871,"slug":"php_introduction_to_oop_exceptions_exercise","name":null,"state":"active","kind":"exercise","language":"php","locale":"ru","has_web_view":false,"has_test_view":false,"reviewable":true,"readme":"### src/Safe.php\n\nРеализуйте функцию `json_decode`, которая работает почти как встроенная, но вместо возврата ошибки, выбрасывает исключение `\\Exception`.\n\n```php\n<?php\n\nuse App\\Safe;\n\n// Второй параметр соответствует второму параметру во встроенной функции json_decode.\n// Его нужно передать как есть во внутренний вызов встроенной функции json_decode.\n$data = Safe\\json_decode('{ \"key\": \"value\" }', true);\n// ['key' => 'value']\n```\n\n### Подсказки\n\n* Проверить наличие ошибок парсинга можно так: `json_last_error() !== JSON_ERROR_NONE`. Здесь используются специальная функция и константа, определённые в PHP.\n","prepared_readme":"### src/Safe.php\n\nРеализуйте функцию `json_decode`, которая работает почти как встроенная, но вместо возврата ошибки, выбрасывает исключение `\\Exception`.\n\n```php\n<?php\n\nuse App\\Safe;\n\n// Второй параметр соответствует второму параметру во встроенной функции json_decode.\n// Его нужно передать как есть во внутренний вызов встроенной функции json_decode.\n$data = Safe\\json_decode('{ \"key\": \"value\" }', true);\n// ['key' => 'value']\n```\n\n### Подсказки\n\n* Проверить наличие ошибок парсинга можно так: `json_last_error() !== JSON_ERROR_NONE`. Здесь используются специальная функция и константа, определённые в PHP.\n","has_solution":true,"entity_name":"Исключения"},"units":[{"id":2887,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/exceptions/theory_unit"},{"id":3916,"name":"quiz","url":"/courses/php-introduction-to-oop/lessons/exceptions/quiz_unit"},{"id":2913,"name":"exercise","url":"/courses/php-introduction-to-oop/lessons/exceptions/exercise_unit"}],"links":[{"id":424960,"name":"Официальная документация","url":"https://www.php.net/manual/ru/language.exceptions.php\n"},{"id":424961,"name":"Коды возврата & исключения\n","url":"https://habr.com/ru/post/131212/\n"},{"id":424962,"name":"Библиотека safe","url":"https://github.com/thecodingmachine/safe\n"}],"ordered_units":[{"id":2887,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/exceptions/theory_unit"},{"id":3916,"name":"quiz","url":"/courses/php-introduction-to-oop/lessons/exceptions/quiz_unit"},{"id":2913,"name":"exercise","url":"/courses/php-introduction-to-oop/lessons/exceptions/exercise_unit"}],"id":1355,"slug":"exceptions","state":"approved","name":"Исключения","course_order":950,"goal":"Знакомимся с исключениями, учимся их генерировать и перехватывать","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Отдельная большая тема в программировании – обработка ошибок. До сих пор нам удавалось избегать её, но в реальном мире, где приложения содержат тысячи, десятки и сотни тысяч (а то и все миллионы) строк кода, обработка ошибок влияет на многое: простоту модификации и расширения, адекватное поведение программы для пользователя в разных ситуациях.\n\nВ этом уроке мы рассмотрим механизм исключений. Но перед тем, как изучать новые конструкции, поговорим про ошибки вообще.\n\nВ PHP есть функция, которая называется `strpos($text, $substr)`. Она ищет подстроку `$substr` внутри текста `$text` и возвращает индекс начала этой подстроки в тексте. Что произойдёт, если подстрока не была найдена? Является ли это поведение ошибкой? Нет. Это штатное поведение функции. От того, что подстрока не была найдена, ничего страшного не случилось. Представьте себе любой редактор текста и механизм поиска внутри него. Ситуация, когда ничего не было найдено, возникает постоянно, и это не ломает работу программы.\n\n_Кстати, посмотрите в документацию этой функции, каким образом она говорит о том, что подстрока не была найдена?_\n\nДругая ситуация. В тех же редакторах есть функция \"открыть файл\". Представьте, что во время открытия файла, что-то пошло не так, например, его удалили. А это ошибка или нет? Да, в этой ситуации произошла ошибка, но это не ошибка программирования. Подобная ошибка может возникнуть всегда, независимо от желания программиста. Он не может избежать её появления. Единственное, что он может — правильно реализовать её обработку.\n\nЕщё один интересный вопрос, насколько это критичная ошибка? Должно ли оно приводить к остановке всего приложения или нет? В плохо написанных приложениях, там, где неправильно реализована обработка ошибок, такая ситуация приведёт к краху всего приложения, и оно завершится. В хорошо написанном приложении не произойдёт ничего страшного. Пользователь увидит предупреждение о том, что файл не читается, и сможет выбрать дальнейшие действия, например, попытаться прочитать его снова или выполнить другое действие.\n\nСказанное выше имеет очень серьёзные следствия. Одна и та же ситуация на разных уровнях может как являться ошибкой, так и быть вполне штатной ситуацией. Например, если задача функции читать файл, а она не смогла этого сделать, то с точки зрения этой функции произошла ошибка. Должна ли она приводить к остановке всего приложения? Как мы выяснили выше – не должна. О том, насколько критична данная ситуация, может решать приложение, которое использует эту функцию, но не сама функция.\n\n## Коды возврата\n\nВ языках появившихся до 1990 года (примерно), обработка ошибок выполнялась через механизм возврата функцией специального значения. Например, в Си, если функция не может выполнить свою задачу, то она должна вернуть специальное значение, либо `NULL` либо отрицательное число. Значение этого числа говорит о том, какая ошибка произошла. Например:\n\n```c\nint write_log()\n{\n int ret = 0; // return value 0 if success\n FILE *f = fopen(\"logfile.txt\", \"w+\");\n\n // Проверяем, получилось ли открыть файл\n if (!f)\n return -1;\n\n // Проверяем, что не достигли конца файла\n if (fputs(\"hello logfile!\", f) != EOF) {\n // continue using the file resource\n } else {\n // Файл закончился\n ret = -2;\n }\n\n // Не получилось закрыть файл\n if (fclose(f) == EOF)\n ret = -3;\n\n return ret;\n}\n```\n\nОбратите внимание на условные конструкции и постоянное присваивание переменной `ret`. Фактически каждая потенциально опасная операция, должна проверяться на успешность выполнения. Если что-то пошло не так, то функция возвращает специальный код.\n\nИ вот тут начинаются проблемы. Как показывает жизнь, в большинстве ситуаций ошибка обрабатывается не там где она возникла и даже не уровнем выше. Предположим, что есть функция _A_, которая вызывает код, потенциально приводящий к ошибке, и она его должна уметь правильно обработать и сообщить пользователю о проблеме. При этом сама ошибка происходит внутри функции _E_, которая вызывается внутри _A_ не напрямую, а через цепочку функций: _A => B => C => D => E_. Подумайте, к чему приводит такая схема? Все функции в этой цепочке, даже несмотря на то, что они не обрабатывают ошибку, обязаны знать про неё, отлавливать её и так же возвращать наружу код этой ошибки. В результате, количества кода, который занимается ошибками, становится так много, что за ним теряется код, выполняющий исходную задачу.\n\n_Стоит сказать, что существуют схемы обработки ошибок, которые не обладают такими недостатками, но работают по принципу возврата. Например, монада Either._\n\n## Исключения\n\nИменно на этом фоне и возник механизм исключений. Его главная цель, передать ошибку из того места, где она возникла, в место, где её можно обработать, минуя все промежуточные уровни. Другими словами, механизм исключений раскручивает стек вызовов самостоятельно.\n\nС исключениями нужно запомнить две вещи: код, в котором произошла ошибка, выбрасывает исключение, а код, в котором ошибка обрабатывается – её ловит.\n\n```php\n<?php\n\n// Функция, которая может выбросить исключение\nfunction readFile($filepath)\n{\n if (!is_readable($filepath)) {\n throw new \\Exception(\"'{$filepath}' is not readable\");\n }\n // ...\n}\n\n// Где-то в другом месте программы\n\nfunction run($filepath)\n{\n try {\n // Функция, которая вызывает readFile. Возможно не напрямую, а через другие функции.\n // Для механизма исключений это не важно.\n openFile($filepath);\n } catch (\\Exception $e) {\n // Этот блок выполняется только в одном случае, если в блоке try было выброшено исключение\n showErrorToUser($e);\n }\n // Если тут будет код, он продолжит выполняться\n}\n```\n\nСами исключения – это объекты класса `\\Exception` и его наследников (о наследовании в одном из следующих курсов). Этот объект содержит внутри себя сообщение, переданное в конструктор, трассировка стека и другие полезные данные:\n\n```php\n<?php\n\nclass Exception implements Throwable\n{\n protected $message = 'Unknown exception';\n private $string;\n protected $code = 0;\n protected $file;\n protected $line;\n private $trace;\n private $previous;\n\n public function __construct($message = '', $code = 0, Throwable $previous = null);\n\n final private function __clone();\n\n final public function getMessage();\n final public function getCode();\n final public function getFile();\n final public function getLine();\n final public function getTrace();\n final public function getPrevious();\n final public function getTraceAsString();\n\n public function __toString();\n}\n```\n\nВыбросить исключение проще простого, достаточно использовать инструкцию `throw`:\n\n```php\n<?php\n\n$e = new \\Exception('Тут любой текст');\nthrow $e; // Исключение можно создать отдельно, а можно сразу же там, где используется throw\n```\n\n`throw` прерывает дальнейшее выполнение кода. В этом смысле оно подобно `return`, но в отличие от него, прерывает выполнение не только текущей функции, но и всего кода, вплоть до ближайшего в стеке вызовов блока `catch`.\n\nБлок _try/catch_ обычно ставится на самом верхнем уровне программы, но это не обязательно. Вполне вероятно, что есть несколько промежуточных блоков, которые могут отлавливать ошибки и снова их возбуждать. Эта тема достаточно сложная и требует некоторого опыта работы.\n\n## PHP\n\nКогда PHP только появился, то в нём не был реализован механизм исключений. Поэтому многие функции либо возвращают какие-то значения указывающие на ошибку, либо об ошибке можно узнать с помощью специальных ухищрений. Эти подходы пришли прямиком из Си, и в современных языках больше нигде не встречаются. Все ещё ухудшается наличием альтернативного механизма: Notice, Warning, Fatal. К счастью, в новых версиях PHP эти ошибки заменяют на исключения, но вряд ли когда-нибудь произойдёт полный переход. Написано слишком много кода, а язык должен сохранять обратную совместимость.\n"},"lessonMember":null,"courseMember":null,"course":{"start_lesson":{"exercise":null,"units":[{"id":2333,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/about/theory_unit"}],"links":[],"ordered_units":[{"id":2333,"name":"theory","url":"/courses/php-introduction-to-oop/lessons/about/theory_unit"}],"id":1110,"slug":"about","state":"approved","name":"О курсе","course_order":1,"goal":"Знакомимся с целями и задачами курса","self_study":null,"theory_video_provider":null,"theory_video_uid":null,"theory":"Этим курсом мы открываем цикл, посвящённый объектно-ориентированному программированию. Первый курс затрагивает самые основы, знакомит с базовым синтаксисом, концепциями и механизмом работы ООП в PHP. Следующие больше посвящены принципам написания программ в ООП-стиле, но и нового синтаксиса будет предостаточно.\n\nООП — большая тема, которой овладевают далеко не сразу. Пройдёт, возможно, не один год работы над реальными проектами, перед тем как картинка сложится.\n\nЭта часть курса содержит большое количество нового синтаксиса и терминов. Основные темы:\n\n* Объекты\n* Классы\n* Интерфейсы\n* Сокрытие данных (Data hiding)\n* Инкапсуляция\n* Автозагрузка\n* Исключения\n\nДанные темы крайне важны даже для начинающего PHP-разработчика потому, что с этими понятиями он начинает сталкиваться буквально с первых дней на новой работе. С другой стороны, требуется немало времени перед тем, как вы сможете действительно качественно использовать изучаемые подходы и техники в работе. К теме объектно-ориентированного программирования мы вернёмся в наших курсах ещё не раз и углубим не только наше понимание, но и разберём [оставшиеся понятия](https://php.net/manual/ru/language.oop5.php) вместе с их синтаксисом, например, абстрактные классы и трейты.\n"},"id":163,"slug":"php-introduction-to-oop","challenges_count":8,"name":"PHP: Введение в ООП","allow_indexing":true,"state":"approved","course_state":"finished","pricing_type":"paid","description":"На этом курсе вы познакомитесь с объектно ориентированным программированием на PHP. Вы узнаете о классах, интерфейсах, инкапсуляции и магических методах. В итоге поймете, для чего нужны классы как абстракция данных. Знание основ объектно ориентированного программирования пригодится, чтобы понимать плюсы и минусы кода с классами и объектами. Также это поможет использовать объекты для реализации программной логики.","kind":"basic","updated_at":"2026-01-20T11:51:04.565Z","language":"php","duration_cache":73500,"skills":["Описывать сущности предметной области с помощью классов","Использовать объекты для реализации программной логики","Подключать механизм автозагрузки классов (стандарт PSR4) в composer","Защищать состояние объектов от внешнего воздействия с помощью модификаторов доступа","Понимать плюсы и минусы кода с классами и объектами","Использовать исключения в своем коде"],"keywords":["классы","инкапсуляция","интерфейсы","исключения","автозагрузка","магические методы"],"lessons_count":18,"cover":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6ODMyNiwicHVyIjoiYmxvYl9pZCJ9fQ==--9a298bb6a7915cc9145958490291b3af909fc177/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJwbmciLCJyZXNpemVfdG9fZmlsbCI6WzYwMCw0MDBdfSwicHVyIjoidmFyaWF0aW9uIn19--6067466c2912ca31a17eddee04b8cf2a38c6ad17/image.png"},"recommendedLandings":[{"stack":{"id":2,"slug":"php","title":"PHP-разработчик","audience":"for_beginners","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"finished","order":60,"duration_in_months":10},"id":1,"slug":"php","title":"РНР-разработчик","subtitle":"Изучите PHP и Laravel для разработки и проектирования REST API","subtitle_for_lists":"Изучите PHP и Laravel для разработки и проектирования REST API","locale":"ru","current":true,"duration_in_months_text":"10 месяцев","stack_slug":"php","price_text":"от 5 650 ₽","duration_text":"10 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png"},{"stack":{"id":25,"slug":"php-oop","title":"ООП В PHP","audience":"for_programmers","start_type":"anytime","pricing_model":"subscription","priority":"medium","kind":"track","state":"published","stack_state":"finished","order":4300,"duration_in_months":2},"id":38,"slug":"php-oop","title":"ООП В PHP","subtitle":"Навык глубокого понимания архитектуры и написания чистого кода, позволяющий решать сложные задачи","subtitle_for_lists":"Изучите архитектуру и чистый код на PHP","locale":"ru","current":true,"duration_in_months_text":"2 месяца","stack_slug":"php-oop","price_text":"от 3 900 ₽","duration_text":"2 месяца","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png"},{"stack":{"id":468,"slug":"middle-php","title":"Middle-php разработчик","audience":"for_programmers","start_type":"weekly","pricing_model":"purchase","priority":"high","kind":"profession","state":"published","stack_state":"not_finished","order":2003,"duration_in_months":5},"id":596,"slug":"middle-php","title":"Middle-php разработчик","subtitle":"Освоите продвинутую работу с БД и серверной логикой","subtitle_for_lists":"Освоите продвинутую работу с БД и серверной логикой","locale":"ru","current":true,"duration_in_months_text":"5 месяцев","stack_slug":"middle-php","price_text":"от 4 050 ₽","duration_text":"5 месяцев","cover_list_variant":"https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png"}],"lessonMemberUnit":null,"accessToLearnUnitExists":false,"accessToCourseExists":false},"url":"/courses/php-introduction-to-oop/lessons/exceptions/theory_unit","version":"8f286f6358a90a7bef2263b3a6edf5a90a94fa42","encryptHistory":false,"clearHistory":false}"><style data-mantine-styles="true">:root, :host{--mantine-font-family: Arial, sans-serif;--mantine-font-family-headings: Arial, sans-serif;--mantine-heading-font-weight: normal;--mantine-radius-default: 0rem;--mantine-primary-color-filled: var(--mantine-color-indigo-filled);--mantine-primary-color-filled-hover: var(--mantine-color-indigo-filled-hover);--mantine-primary-color-light: var(--mantine-color-indigo-light);--mantine-primary-color-light-hover: var(--mantine-color-indigo-light-hover);--mantine-primary-color-light-color: var(--mantine-color-indigo-light-color);--mantine-spacing-xxl: calc(4rem * var(--mantine-scale));--mantine-font-size-xs: 12px;--mantine-font-size-sm: 14px;--mantine-font-size-md: 16px;--mantine-font-size-lg: clamp(16.0000px, calc(15.2727px + 0.2273vw), 18.0000px);--mantine-font-size-xl: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-display-3: clamp(32.0000px, calc(26.1818px + 1.8182vw), 48.0000px);--mantine-font-size-display-2: clamp(36.0000px, calc(25.8182px + 3.1818vw), 64.0000px);--mantine-font-size-display-1: clamp(40.0000px, calc(25.4545px + 4.5455vw), 80.0000px);--mantine-font-size-h1: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-font-size-h2: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-font-size-h3: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-font-size-h4: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-font-size-h5: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-font-size-h6: 1rem;--mantine-primary-color-0: var(--mantine-color-indigo-0);--mantine-primary-color-1: var(--mantine-color-indigo-1);--mantine-primary-color-2: var(--mantine-color-indigo-2);--mantine-primary-color-3: var(--mantine-color-indigo-3);--mantine-primary-color-4: var(--mantine-color-indigo-4);--mantine-primary-color-5: var(--mantine-color-indigo-5);--mantine-primary-color-6: var(--mantine-color-indigo-6);--mantine-primary-color-7: var(--mantine-color-indigo-7);--mantine-primary-color-8: var(--mantine-color-indigo-8);--mantine-primary-color-9: var(--mantine-color-indigo-9);--mantine-color-red-0: #ffeaea;--mantine-color-red-1: #fed4d4;--mantine-color-red-2: #f4a7a8;--mantine-color-red-3: #ec7878;--mantine-color-red-4: #e55050;--mantine-color-red-5: #e03131;--mantine-color-red-6: #e02829;--mantine-color-red-7: #c71a1c;--mantine-color-red-8: #b21218;--mantine-color-red-9: #9c0411;--mantine-color-violet-0: #fce9ff;--mantine-color-violet-1: #f1cfff;--mantine-color-violet-2: #e09bff;--mantine-color-violet-3: #d16fff;--mantine-color-violet-4: #be37fe;--mantine-color-violet-5: #b51afe;--mantine-color-violet-6: #b009ff;--mantine-color-violet-7: #9b00e4;--mantine-color-violet-8: #8a00cc;--mantine-color-violet-9: #7800b3;--mantine-color-indigo-0: #edecff;--mantine-color-indigo-1: #d6d5fe;--mantine-color-indigo-2: #aaa9f4;--mantine-color-indigo-3: #7b79eb;--mantine-color-indigo-4: #5451e4;--mantine-color-indigo-5: #3b37e0;--mantine-color-indigo-6: #2d2adf;--mantine-color-indigo-7: #1f1ec7;--mantine-color-indigo-8: #1819b2;--mantine-color-indigo-9: #0c149e;--mantine-color-cyan-0: #dffdff;--mantine-color-cyan-1: #caf5ff;--mantine-color-cyan-2: #99e8ff;--mantine-color-cyan-3: #64daff;--mantine-color-cyan-4: #3ccffe;--mantine-color-cyan-5: #24c8fe;--mantine-color-cyan-6: #00c2ff;--mantine-color-cyan-7: #00ade4;--mantine-color-cyan-8: #009acd;--mantine-color-cyan-9: #0085b5;--mantine-color-green-0: #e9fdec;--mantine-color-green-1: #d7f6dc;--mantine-color-green-2: #b0eab9;--mantine-color-green-3: #86df94;--mantine-color-green-4: #62d574;--mantine-color-green-5: #4ccf5f;--mantine-color-green-6: #3fcc54;--mantine-color-green-7: #2fb344;--mantine-color-green-8: #25a03b;--mantine-color-green-9: #138a2e;--mantine-color-yellow-0: #fff7e2;--mantine-color-yellow-1: #ffeecd;--mantine-color-yellow-2: #ffdc9c;--mantine-color-yellow-3: #ffc966;--mantine-color-yellow-4: #feb93a;--mantine-color-yellow-5: #feae1e;--mantine-color-yellow-6: #ffa90f;--mantine-color-yellow-8: #ca8200;--mantine-color-yellow-9: #af7000;--mantine-h1-font-size: clamp(28.0000px, calc(23.6364px + 1.3636vw), 40.0000px);--mantine-h1-font-weight: normal;--mantine-h2-font-size: clamp(24.0000px, calc(21.0909px + 0.9091vw), 32.0000px);--mantine-h2-font-weight: normal;--mantine-h3-font-size: clamp(20.0000px, calc(17.0909px + 0.9091vw), 28.0000px);--mantine-h3-font-weight: normal;--mantine-h4-font-size: clamp(16.0000px, calc(13.0909px + 0.9091vw), 24.0000px);--mantine-h4-font-weight: normal;--mantine-h5-font-size: clamp(16.0000px, calc(14.5455px + 0.4545vw), 20.0000px);--mantine-h5-font-weight: normal;--mantine-h6-font-size: 1rem;--mantine-h6-font-weight: normal;}
:root[data-mantine-color-scheme="dark"], :host([data-mantine-color-scheme="dark"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-dark-filled: var(--mantine-color-dark-5);--mantine-color-dark-filled-hover: var(--mantine-color-dark-6);--mantine-color-dark-light: rgba(105, 105, 105, 0.15);--mantine-color-dark-light-hover: rgba(105, 105, 105, 0.2);--mantine-color-dark-light-color: var(--mantine-color-dark-0);--mantine-color-dark-outline: var(--mantine-color-dark-1);--mantine-color-dark-outline-hover: rgba(184, 184, 184, 0.05);--mantine-color-gray-filled: var(--mantine-color-gray-5);--mantine-color-gray-filled-hover: var(--mantine-color-gray-6);--mantine-color-gray-light: rgba(222, 226, 230, 0.15);--mantine-color-gray-light-hover: rgba(222, 226, 230, 0.2);--mantine-color-gray-light-color: var(--mantine-color-gray-0);--mantine-color-gray-outline: var(--mantine-color-gray-1);--mantine-color-gray-outline-hover: rgba(241, 243, 245, 0.05);--mantine-color-red-filled: var(--mantine-color-red-5);--mantine-color-red-filled-hover: var(--mantine-color-red-6);--mantine-color-red-light: rgba(236, 120, 120, 0.15);--mantine-color-red-light-hover: rgba(236, 120, 120, 0.2);--mantine-color-red-light-color: var(--mantine-color-red-0);--mantine-color-red-outline: var(--mantine-color-red-1);--mantine-color-red-outline-hover: rgba(254, 212, 212, 0.05);--mantine-color-pink-filled: var(--mantine-color-pink-5);--mantine-color-pink-filled-hover: var(--mantine-color-pink-6);--mantine-color-pink-light: rgba(250, 162, 193, 0.15);--mantine-color-pink-light-hover: rgba(250, 162, 193, 0.2);--mantine-color-pink-light-color: var(--mantine-color-pink-0);--mantine-color-pink-outline: var(--mantine-color-pink-1);--mantine-color-pink-outline-hover: rgba(255, 222, 235, 0.05);--mantine-color-grape-filled: var(--mantine-color-grape-5);--mantine-color-grape-filled-hover: var(--mantine-color-grape-6);--mantine-color-grape-light: rgba(229, 153, 247, 0.15);--mantine-color-grape-light-hover: rgba(229, 153, 247, 0.2);--mantine-color-grape-light-color: var(--mantine-color-grape-0);--mantine-color-grape-outline: var(--mantine-color-grape-1);--mantine-color-grape-outline-hover: rgba(243, 217, 250, 0.05);--mantine-color-violet-filled: var(--mantine-color-violet-5);--mantine-color-violet-filled-hover: var(--mantine-color-violet-6);--mantine-color-violet-light: rgba(209, 111, 255, 0.15);--mantine-color-violet-light-hover: rgba(209, 111, 255, 0.2);--mantine-color-violet-light-color: var(--mantine-color-violet-0);--mantine-color-violet-outline: var(--mantine-color-violet-1);--mantine-color-violet-outline-hover: rgba(241, 207, 255, 0.05);--mantine-color-indigo-filled: var(--mantine-color-indigo-5);--mantine-color-indigo-filled-hover: var(--mantine-color-indigo-6);--mantine-color-indigo-light: rgba(123, 121, 235, 0.15);--mantine-color-indigo-light-hover: rgba(123, 121, 235, 0.2);--mantine-color-indigo-light-color: var(--mantine-color-indigo-0);--mantine-color-indigo-outline: var(--mantine-color-indigo-1);--mantine-color-indigo-outline-hover: rgba(214, 213, 254, 0.05);--mantine-color-blue-filled: var(--mantine-color-blue-5);--mantine-color-blue-filled-hover: var(--mantine-color-blue-6);--mantine-color-blue-light: rgba(116, 192, 252, 0.15);--mantine-color-blue-light-hover: rgba(116, 192, 252, 0.2);--mantine-color-blue-light-color: var(--mantine-color-blue-0);--mantine-color-blue-outline: var(--mantine-color-blue-1);--mantine-color-blue-outline-hover: rgba(208, 235, 255, 0.05);--mantine-color-cyan-filled: var(--mantine-color-cyan-5);--mantine-color-cyan-filled-hover: var(--mantine-color-cyan-6);--mantine-color-cyan-light: rgba(100, 218, 255, 0.15);--mantine-color-cyan-light-hover: rgba(100, 218, 255, 0.2);--mantine-color-cyan-light-color: var(--mantine-color-cyan-0);--mantine-color-cyan-outline: var(--mantine-color-cyan-1);--mantine-color-cyan-outline-hover: rgba(202, 245, 255, 0.05);--mantine-color-teal-filled: var(--mantine-color-teal-5);--mantine-color-teal-filled-hover: var(--mantine-color-teal-6);--mantine-color-teal-light: rgba(99, 230, 190, 0.15);--mantine-color-teal-light-hover: rgba(99, 230, 190, 0.2);--mantine-color-teal-light-color: var(--mantine-color-teal-0);--mantine-color-teal-outline: var(--mantine-color-teal-1);--mantine-color-teal-outline-hover: rgba(195, 250, 232, 0.05);--mantine-color-green-filled: var(--mantine-color-green-5);--mantine-color-green-filled-hover: var(--mantine-color-green-6);--mantine-color-green-light: rgba(134, 223, 148, 0.15);--mantine-color-green-light-hover: rgba(134, 223, 148, 0.2);--mantine-color-green-light-color: var(--mantine-color-green-0);--mantine-color-green-outline: var(--mantine-color-green-1);--mantine-color-green-outline-hover: rgba(215, 246, 220, 0.05);--mantine-color-lime-filled: var(--mantine-color-lime-5);--mantine-color-lime-filled-hover: var(--mantine-color-lime-6);--mantine-color-lime-light: rgba(192, 235, 117, 0.15);--mantine-color-lime-light-hover: rgba(192, 235, 117, 0.2);--mantine-color-lime-light-color: var(--mantine-color-lime-0);--mantine-color-lime-outline: var(--mantine-color-lime-1);--mantine-color-lime-outline-hover: rgba(233, 250, 200, 0.05);--mantine-color-yellow-filled: var(--mantine-color-yellow-5);--mantine-color-yellow-filled-hover: var(--mantine-color-yellow-6);--mantine-color-yellow-light: rgba(255, 201, 102, 0.15);--mantine-color-yellow-light-hover: rgba(255, 201, 102, 0.2);--mantine-color-yellow-light-color: var(--mantine-color-yellow-0);--mantine-color-yellow-outline: var(--mantine-color-yellow-1);--mantine-color-yellow-outline-hover: rgba(255, 238, 205, 0.05);--mantine-color-orange-filled: var(--mantine-color-orange-5);--mantine-color-orange-filled-hover: var(--mantine-color-orange-6);--mantine-color-orange-light: rgba(255, 192, 120, 0.15);--mantine-color-orange-light-hover: rgba(255, 192, 120, 0.2);--mantine-color-orange-light-color: var(--mantine-color-orange-0);--mantine-color-orange-outline: var(--mantine-color-orange-1);--mantine-color-orange-outline-hover: rgba(255, 232, 204, 0.05);--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-9) 0%, var(--mantine-color-cyan-7) 100%);--app-color-surface: #2e2e2e;}
:root[data-mantine-color-scheme="light"], :host([data-mantine-color-scheme="light"]){--mantine-color-anchor: var(--mantine-color-text);--mantine-color-dimmed: #495057;--mantine-color-red-light: rgba(224, 40, 41, 0.1);--mantine-color-red-light-hover: rgba(224, 40, 41, 0.12);--mantine-color-red-outline-hover: rgba(224, 40, 41, 0.05);--mantine-color-violet-light: rgba(176, 9, 255, 0.1);--mantine-color-violet-light-hover: rgba(176, 9, 255, 0.12);--mantine-color-violet-outline-hover: rgba(176, 9, 255, 0.05);--mantine-color-indigo-light: rgba(45, 42, 223, 0.1);--mantine-color-indigo-light-hover: rgba(45, 42, 223, 0.12);--mantine-color-indigo-outline-hover: rgba(45, 42, 223, 0.05);--mantine-color-cyan-light: rgba(0, 194, 255, 0.1);--mantine-color-cyan-light-hover: rgba(0, 194, 255, 0.12);--mantine-color-cyan-outline-hover: rgba(0, 194, 255, 0.05);--mantine-color-green-light: rgba(63, 204, 84, 0.1);--mantine-color-green-light-hover: rgba(63, 204, 84, 0.12);--mantine-color-green-outline-hover: rgba(63, 204, 84, 0.05);--mantine-color-yellow-light: rgba(255, 169, 15, 0.1);--mantine-color-yellow-light-hover: rgba(255, 169, 15, 0.12);--mantine-color-yellow-outline-hover: rgba(255, 169, 15, 0.05);--app-color-surface: #f1f3f5;--app-cta-gradient: linear-gradient(90deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-5) 100%);}</style><style data-mantine-styles="classes">@media (max-width: 35.99375em) {.mantine-visible-from-xs {display: none !important;}}@media (min-width: 36em) {.mantine-hidden-from-xs {display: none !important;}}@media (max-width: 47.99375em) {.mantine-visible-from-sm {display: none !important;}}@media (min-width: 48em) {.mantine-hidden-from-sm {display: none !important;}}@media (max-width: 61.99375em) {.mantine-visible-from-md {display: none !important;}}@media (min-width: 62em) {.mantine-hidden-from-md {display: none !important;}}@media (max-width: 74.99375em) {.mantine-visible-from-lg {display: none !important;}}@media (min-width: 75em) {.mantine-hidden-from-lg {display: none !important;}}@media (max-width: 87.99375em) {.mantine-visible-from-xl {display: none !important;}}@media (min-width: 88em) {.mantine-hidden-from-xl {display: none !important;}}</style><div style="position:absolute;top:0rem" class=""></div><div style="max-width:var(--container-size-xl);height:100%;min-height:0rem" class=""><style data-mantine-styles="inline">.__m__-_R_5ub_{--grid-gutter:0rem;}</style><div style="height:100%;min-height:0rem" class="m_410352e9 mantine-Grid-root __m__-_R_5ub_"><div class="m_dee7bd2f mantine-Grid-inner" style="height:100%"><style data-mantine-styles="inline">.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:91.66666666666667%;--col-max-width:91.66666666666667%;}@media(min-width: 48em){.__m__-_R_rdub_{--col-flex-grow:auto;--col-flex-basis:83.33333333333334%;--col-max-width:83.33333333333334%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem;display:flex" class="m_96bdd299 mantine-Grid-col __m__-_R_rdub_"><style data-mantine-styles="inline">.__m__-_R_6qrdub_{margin-top:0rem;padding-inline:var(--mantine-spacing-xs);width:100%;}@media(min-width: 48em){.__m__-_R_6qrdub_{margin-top:var(--mantine-spacing-xl);width:80%;}}@media(min-width: 62em){.__m__-_R_6qrdub_{padding-inline:var(--mantine-spacing-xl);}}</style><div style="margin-inline:auto;max-width:var(--mantine-breakpoint-xl)" class="__m__-_R_6qrdub_"><div style="color:var(--mantine-color-dimmed)" class="m_4451eb3a mantine-Center-root" data-inline="true"><div style="--ti-size:var(--ti-size-xs);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;margin-inline-end:calc(0.125rem * var(--mantine-scale));color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="xs"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-lock "><path d="M5 13a2 2 0 0 1 2 -2h10a2 2 0 0 1 2 2v6a2 2 0 0 1 -2 2h-10a2 2 0 0 1 -2 -2v-6"></path><path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0"></path><path d="M8 11v-4a4 4 0 1 1 8 0v4"></path></svg></div><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">PHP: Введение в ООП</p></div><h1 style="--title-fw:var(--mantine-h1-font-weight);--title-lh:var(--mantine-h1-line-height);--title-fz:var(--mantine-h1-font-size);margin-bottom:var(--mantine-spacing-xl)" class="m_8a5d1357 mantine-Title-root" data-order="1">Теория: Исключения</h1><script type="application/ld+json">{"@context":"https://schema.org","@type":"LearningResource","name":"Исключения","inLanguage":"ru","isPartOf":{"@type":"LearningResource","name":"PHP: Введение в ООП"},"isAccessibleForFree":"False","hasPart":{"@type":"WebPageElement","isAccessibleForFree":"False","cssSelector":".paywalled"}}</script><div class=""><div style="--alert-color:var(--mantine-color-indigo-light-color);margin-bottom:var(--mantine-spacing-lg);font-size:var(--mantine-font-size-lg)" class="m_66836ed3 mantine-Alert-root" id="mantine-_R_remqrdub_" role="alert" aria-describedby="mantine-_R_remqrdub_-body" aria-labelledby="mantine-_R_remqrdub_-title"><div class="m_a5d60502 mantine-Alert-wrapper"><div class="m_667f2a6a mantine-Alert-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-rocket "><path d="M4 13a8 8 0 0 1 7 7a6 6 0 0 0 3 -5a9 9 0 0 0 6 -8a3 3 0 0 0 -3 -3a9 9 0 0 0 -8 6a6 6 0 0 0 -5 3"></path><path d="M7 14a6 6 0 0 0 -3 6a6 6 0 0 0 6 -3"></path><path d="M14 9a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"></path></svg></div><div class="m_667c2793 mantine-Alert-body"><div class="m_6a03f287 mantine-Alert-title"><span id="mantine-_R_remqrdub_-title" class="m_698f4f23 mantine-Alert-label">Полный доступ к материалам</span></div><div id="mantine-_R_remqrdub_-body" class="m_7fa78076 mantine-Alert-message"><div style="--group-gap:var(--mantine-spacing-md);--group-align:center;--group-justify:space-between;--group-wrap:wrap" class="m_4081bf90 mantine-Group-root"><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Зарегистрируйтесь и получите доступ к этому и десяткам других курсов</p><a style="--button-height:var(--button-height-xs);--button-padding-x:var(--button-padding-x-xs);--button-fz:var(--mantine-font-size-xs);--button-bg:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-hover:linear-gradient(45deg, var(--mantine-color-blue-filled) 0%, var(--mantine-color-cyan-filled) 100%);--button-color:var(--mantine-color-white);--button-bd:none" class="mantine-focus-auto mantine-active m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root" data-variant="gradient" data-size="xs" href="/u/new"><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">Зарегистрироваться</span></span></a></div></div></div></div></div><div class="paywalled m_d08caa0 mantine-Typography-root"><p>Отдельная большая тема в программировании – обработка ошибок. До сих пор нам удавалось избегать её, но в реальном мире, где приложения содержат тысячи, десятки и сотни тысяч (а то и все миллионы) строк кода, обработка ошибок влияет на многое: простоту модификации и расширения, адекватное поведение программы для пользователя в разных ситуациях.</p>
<p>В этом уроке мы рассмотрим механизм исключений. Но перед тем, как изучать новые конструкции, поговорим про ошибки вообще.</p>
<p>В PHP есть функция, которая называется <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">strpos($text, $substr)</code>. Она ищет подстроку <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$substr</code> внутри текста <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">$text</code> и возвращает индекс начала этой подстроки в тексте. Что произойдёт, если подстрока не была найдена? Является ли это поведение ошибкой? Нет. Это штатное поведение функции. От того, что подстрока не была найдена, ничего страшного не случилось. Представьте себе любой редактор текста и механизм поиска внутри него. Ситуация, когда ничего не было найдено, возникает постоянно, и это не ломает работу программы.</p>
<p><em>Кстати, посмотрите в документацию этой функции, каким образом она говорит о том, что подстрока не была найдена?</em></p>
<p>Другая ситуация. В тех же редакторах есть функция "открыть файл". Представьте, что во время открытия файла, что-то пошло не так, например, его удалили. А это ошибка или нет? Да, в этой ситуации произошла ошибка, но это не ошибка программирования. Подобная ошибка может возникнуть всегда, независимо от желания программиста. Он не может избежать её появления. Единственное, что он может — правильно реализовать её обработку.</p>
<p>Ещё один интересный вопрос, насколько это критичная ошибка? Должно ли оно приводить к остановке всего приложения или нет? В плохо написанных приложениях, там, где неправильно реализована обработка ошибок, такая ситуация приведёт к краху всего приложения, и оно завершится. В хорошо написанном приложении не произойдёт ничего страшного. Пользователь увидит предупреждение о том, что файл не читается, и сможет выбрать дальнейшие действия, например, попытаться прочитать его снова или выполнить другое действие.</p>
<p>Сказанное выше имеет очень серьёзные следствия. Одна и та же ситуация на разных уровнях может как являться ошибкой, так и быть вполне штатной ситуацией. Например, если задача функции читать файл, а она не смогла этого сделать, то с точки зрения этой функции произошла ошибка. Должна ли она приводить к остановке всего приложения? Как мы выяснили выше – не должна. О том, насколько критична данная ситуация, может решать приложение, которое использует эту функцию, но не сама функция.</p>
<h2 id="heading-2-1">Коды возврата</h2>
<p>В языках появившихся до 1990 года (примерно), обработка ошибок выполнялась через механизм возврата функцией специального значения. Например, в Си, если функция не может выполнить свою задачу, то она должна вернуть специальное значение, либо <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">NULL</code> либо отрицательное число. Значение этого числа говорит о том, какая ошибка произошла. Например:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code">int write_log()
{
int ret = 0; // return value 0 if success
FILE *f = fopen("logfile.txt", "w+");
// Проверяем, получилось ли открыть файл
if (!f)
return -1;
// Проверяем, что не достигли конца файла
if (fputs("hello logfile!", f) != EOF) {
// continue using the file resource
} else {
// Файл закончился
ret = -2;
}
// Не получилось закрыть файл
if (fclose(f) == EOF)
ret = -3;
return ret;
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Обратите внимание на условные конструкции и постоянное присваивание переменной <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">ret</code>. Фактически каждая потенциально опасная операция, должна проверяться на успешность выполнения. Если что-то пошло не так, то функция возвращает специальный код.</p>
<p>И вот тут начинаются проблемы. Как показывает жизнь, в большинстве ситуаций ошибка обрабатывается не там где она возникла и даже не уровнем выше. Предположим, что есть функция <em>A</em>, которая вызывает код, потенциально приводящий к ошибке, и она его должна уметь правильно обработать и сообщить пользователю о проблеме. При этом сама ошибка происходит внутри функции <em>E</em>, которая вызывается внутри <em>A</em> не напрямую, а через цепочку функций: <em>A => B => C => D => E</em>. Подумайте, к чему приводит такая схема? Все функции в этой цепочке, даже несмотря на то, что они не обрабатывают ошибку, обязаны знать про неё, отлавливать её и так же возвращать наружу код этой ошибки. В результате, количества кода, который занимается ошибками, становится так много, что за ним теряется код, выполняющий исходную задачу.</p>
<p><em>Стоит сказать, что существуют схемы обработки ошибок, которые не обладают такими недостатками, но работают по принципу возврата. Например, монада Either.</em></p>
<h2 id="heading-2-2">Исключения</h2>
<p>Именно на этом фоне и возник механизм исключений. Его главная цель, передать ошибку из того места, где она возникла, в место, где её можно обработать, минуя все промежуточные уровни. Другими словами, механизм исключений раскручивает стек вызовов самостоятельно.</p>
<p>С исключениями нужно запомнить две вещи: код, в котором произошла ошибка, выбрасывает исключение, а код, в котором ошибка обрабатывается – её ловит.</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
// Функция, которая может выбросить исключение
function readFile($filepath)
{
if (!is_readable($filepath)) {
throw new \Exception("'{$filepath}' is not readable");
}
// ...
}
// Где-то в другом месте программы
function run($filepath)
{
try {
// Функция, которая вызывает readFile. Возможно не напрямую, а через другие функции.
// Для механизма исключений это не важно.
openFile($filepath);
} catch (\Exception $e) {
// Этот блок выполняется только в одном случае, если в блоке try было выброшено исключение
showErrorToUser($e);
}
// Если тут будет код, он продолжит выполняться
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Сами исключения – это объекты класса <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">\Exception</code> и его наследников (о наследовании в одном из следующих курсов). Этот объект содержит внутри себя сообщение, переданное в конструктор, трассировка стека и другие полезные данные:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
class Exception implements Throwable
{
protected $message = 'Unknown exception';
private $string;
protected $code = 0;
protected $file;
protected $line;
private $trace;
private $previous;
public function __construct($message = '', $code = 0, Throwable $previous = null);
final private function __clone();
final public function getMessage();
final public function getCode();
final public function getFile();
final public function getLine();
final public function getTrace();
final public function getPrevious();
final public function getTraceAsString();
public function __toString();
}</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p>Выбросить исключение проще простого, достаточно использовать инструкцию <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">throw</code>:</p>
<div style="margin-bottom:var(--mantine-spacing-lg)" class="m_e597c321 mantine-CodeHighlight-codeHighlight" dir="ltr"><div class="m_be7e9c9c mantine-CodeHighlight-controls"><button style="--ai-bg:transparent;--ai-hover:transparent;--ai-color:inherit;--ai-bd:none" class="mantine-focus-auto mantine-active m_d498bab7 mantine-CodeHighlight-control m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="none" type="button" aria-label="Copy code"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M8 8m0 2a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8a2 2 0 0 1 -2 2h-8a2 2 0 0 1 -2 -2z"></path><path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2"></path></svg></span></button></div><div style="--scrollarea-scrollbar-size:calc(0.25rem * var(--mantine-scale));--sa-corner-width:0px;--sa-corner-height:0px" class="m_f744fd40 mantine-CodeHighlight-scrollarea m_d57069b5 mantine-ScrollArea-root" dir="ltr"><div style="overflow-x:hidden;overflow-y:hidden;overscroll-behavior-inline:none" class="m_c0783ff9 mantine-ScrollArea-viewport" data-scrollbars="xy"><div class="m_b1336c6 mantine-ScrollArea-content"><pre class="m_2c47c4fd mantine-CodeHighlight-pre" style="padding:0"><code class="m_5caae6d3 mantine-CodeHighlight-code"><?php
$e = new \Exception('Тут любой текст');
throw $e; // Исключение можно создать отдельно, а можно сразу же там, где используется throw</code></pre></div></div></div><button class="mantine-focus-auto m_c9378bc2 mantine-CodeHighlight-showCodeButton m_87cf2631 mantine-UnstyledButton-root" data-hidden="true" type="button">Expand code</button></div>
<p><code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">throw</code> прерывает дальнейшее выполнение кода. В этом смысле оно подобно <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">return</code>, но в отличие от него, прерывает выполнение не только текущей функции, но и всего кода, вплоть до ближайшего в стеке вызовов блока <code style="margin-bottom:var(--mantine-spacing-lg)" class="m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight m_e597c321 mantine-CodeHighlight-codeHighlight m_dfe9c588 mantine-InlineCodeHighlight-inlineCodeHighlight">catch</code>.</p>
<p>Блок <em>try/catch</em> обычно ставится на самом верхнем уровне программы, но это не обязательно. Вполне вероятно, что есть несколько промежуточных блоков, которые могут отлавливать ошибки и снова их возбуждать. Эта тема достаточно сложная и требует некоторого опыта работы.</p>
<h2 id="heading-2-3">PHP</h2>
<p>Когда PHP только появился, то в нём не был реализован механизм исключений. Поэтому многие функции либо возвращают какие-то значения указывающие на ошибку, либо об ошибке можно узнать с помощью специальных ухищрений. Эти подходы пришли прямиком из Си, и в современных языках больше нигде не встречаются. Все ещё ухудшается наличием альтернативного механизма: Notice, Warning, Fatal. К счастью, в новых версиях PHP эти ошибки заменяют на исключения, но вряд ли когда-нибудь произойдёт полный переход. Написано слишком много кода, а язык должен сохранять обратную совместимость.</p></div><div style="margin-block:var(--mantine-spacing-xl)" class=""><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md)" class="m_8a5d1357 mantine-Title-root" data-order="2">Рекомендуемые программы</h2><style data-mantine-styles="inline">.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xs);--carousel-slide-size:70%;}@media(min-width: 36em){.__m__-_R_2mremqrdub_{--carousel-slide-gap:var(--mantine-spacing-xl);--carousel-slide-size:50%;}}</style><div style="--carousel-control-size:calc(2.5rem * var(--mantine-scale));--carousel-controls-offset:var(--mantine-spacing-sm);margin-bottom:var(--mantine-spacing-lg);padding-block:var(--mantine-spacing-sm);background:var(--app-color-surface)" class="m_17884d0f mantine-Carousel-root responsiveClassName" data-orientation="horizontal" data-include-gap-in-size="true"><div class="m_39bc3463 mantine-Carousel-controls" data-orientation="horizontal"><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="previous" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button><button class="mantine-focus-auto m_64f58e10 mantine-Carousel-control m_87cf2631 mantine-UnstyledButton-root" type="button" data-inactive="true" data-type="next" tabindex="-1"><svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform:rotate(-90deg);width:calc(1rem * var(--mantine-scale));height:calc(1rem * var(--mantine-scale));display:block"><path d="M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd"></path></svg></button></div><div class="m_a2dae653 mantine-Carousel-viewport" data-type="media"><div class="m_fcd81474 mantine-Carousel-container __m__-_R_2mremqrdub_" data-orientation="horizontal"><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/php?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">10 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">С нуля</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">РНР-разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите PHP и Laravel для разработки и проектирования REST API</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzk5MiwicHVyIjoiYmxvYl9pZCJ9fQ==--e9d0f30948ea766a7e6bc3e3d56c192344d45fb8/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Programming-cuate%20(1).png" alt="РНР-разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 5 650 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/php-oop?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">2 месяца</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">ООП В PHP</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Изучите архитектуру и чистый код на PHP</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6Mzc1MSwicHVyIjoiYmxvYl9pZCJ9fQ==--e5793a1818ff43d73135cc7ed88c1998d7650470/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Developer%20activity-bro.png" alt="ООП В PHP" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 3 900 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/programs/middle-php?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card" target="_blank"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><div style="--group-gap:calc(0.25rem * var(--mantine-scale));--group-align:center;--group-justify:flex-start;--group-wrap:nowrap" class="m_4081bf90 mantine-Group-root"><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">5 месяцев</span><span class="mantine-focus-auto m_b6d8b162 mantine-Text-root">·</span><span style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Для продвинутых</span></div><p style="margin-bottom:var(--mantine-spacing-sm);font-size:var(--mantine-font-size-h5);font-weight:bold" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Middle-php разработчик</p><p class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Освоите продвинутую работу с БД и серверной логикой</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="https://hexlet.io/rails/active_storage/representations/proxy/eyJfcmFpbHMiOnsiZGF0YSI6NTIxNSwicHVyIjoiYmxvYl9pZCJ9fQ==--ee06f514098550b5ac7eebce6227b1543399a37c/eyJfcmFpbHMiOnsiZGF0YSI6eyJmb3JtYXQiOiJ3ZWJwIiwicmVzaXplX3RvX2xpbWl0IjpbNDAwLDQwMF0sInNhdmVyIjp7InF1YWxpdHkiOjg1fX0sInB1ciI6InZhcmlhdGlvbiJ9fQ==--5b6f46dacd1af664f27558553a58076185091823/Hand%20coding-rafiki.png" alt="Middle-php разработчик" loading="eager"/></div><div style="--group-gap:var(--mantine-spacing-md);--group-align:end;--group-justify:space-between;--group-wrap:wrap;margin-top:var(--mantine-spacing-xs)" class="m_4081bf90 mantine-Group-root"><p style="font-size:var(--mantine-font-size-xl)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">от 4 050 ₽</p><p style="font-size:var(--mantine-font-size-sm)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Посмотреть →</p></div></div></div></a></div></div><div class="m_d98df724 mantine-Carousel-slide" data-orientation="horizontal"><div tabindex="0" style="cursor:pointer;height:100%"><a style="text-decoration:none" class="mantine-focus-auto m_849cf0da m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses?promo_name=programs_list&promo_position=course&promo_creative=catalog_card&promo_type=card"><div style="height:100%" class="m_e615b15f mantine-Card-root m_1b7284a3 mantine-Paper-root" data-with-border="true"><h2 style="--title-fw:var(--mantine-h2-font-weight);--title-lh:var(--mantine-h2-line-height);--title-fz:var(--mantine-h2-font-size);margin-bottom:var(--mantine-spacing-md);font-size:var(--mantine-font-size-h3)" class="m_8a5d1357 mantine-Title-root" data-order="2" data-responsive="true">Каталог</h2><p style="margin-bottom:auto" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Полный список доступных курсов по разным направлениям</p><div style="margin-top:auto" class=""><div class="m_4451eb3a mantine-Center-root"><img style="opacity:0.8;width:70%" class="m_9e117634 mantine-Image-root mantine-visible-from-xs" src="/vite/assets/development-BVihs_d5.png" alt="Orientation"/></div></div></div></a></div></div></div></div></div></div></div></div></div><style data-mantine-styles="inline">.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:8.333333333333334%;--col-max-width:8.333333333333334%;}@media(min-width: 48em){.__m__-_R_1bdub_{--col-flex-grow:auto;--col-flex-basis:16.666666666666668%;--col-max-width:16.666666666666668%;}}</style><div style="min-width:0rem;height:100%;min-height:0rem" class="m_96bdd299 mantine-Grid-col __m__-_R_1bdub_"><div style="margin-inline:var(--mantine-spacing-xs)" class="mantine-visible-from-sm"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-lg);text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/php-introduction-to-oop/lessons/exceptions/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label"><span style="margin-inline-end:var(--mantine-spacing-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Дальше</span>→</span></span></a><a style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Навигация по теме</span><span class="m_57492dcc mantine-NavLink-description">Теория</span></div><span class="m_690090b5 mantine-NavLink-section" data-position="right"></span></a><div style="margin-block:var(--mantine-spacing-lg)" class="m_3eebeb36 mantine-Divider-root" data-orientation="horizontal" role="separator"></div><div style="margin-block:var(--mantine-spacing-lg)" class=""><div style="justify-content:space-between;margin-bottom:calc(0.1875rem * var(--mantine-scale));color:var(--mantine-color-dimmed);font-size:var(--mantine-font-size-xs)" class="m_8bffd616 mantine-Flex-root __m__-_R_qimrbdub_"><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">Завершено</p><p style="font-size:var(--mantine-font-size-xs)" class="mantine-focus-auto m_b6d8b162 mantine-Text-root">0 / 18</p></div><div style="--progress-size:var(--progress-size-sm)" class="m_db6d6462 mantine-Progress-root" data-size="sm"><div style="--progress-section-size:0%;--progress-section-color:var(--mantine-color-gray-filled)" class="m_2242eb65 mantine-Progress-section" role="progressbar" aria-valuemax="100" aria-valuemin="0" aria-valuenow="0" aria-valuetext="0%"></div></div></div><button style="padding-inline:0rem" class="mantine-focus-auto m_f0824112 mantine-NavLink-root m_87cf2631 mantine-UnstyledButton-root" type="button"><span class="m_690090b5 mantine-NavLink-section" data-position="left"><div style="--ti-size:var(--ti-size-sm);--ti-bg:transparent;--ti-color:var(--mantine-color-indigo-light-color);--ti-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;color:inherit" class="m_7341320d mantine-ThemeIcon-root" data-variant="transparent" data-size="sm"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></div></span><div class="m_f07af9d2 mantine-NavLink-body"><span class="m_1f6ac4c4 mantine-NavLink-label">Обсуждения (архив)</span><span class="m_57492dcc mantine-NavLink-description"></span></div></button><div style="--toc-bg:var(--mantine-color-blue-light);--toc-color:var(--mantine-color-blue-light-color);--toc-size:var(--mantine-font-size-sm);--toc-radius:var(--mantine-radius-sm);margin-top:var(--mantine-spacing-xl)" class="m_bcaa9990 mantine-TableOfContents-root" data-variant="light" data-size="sm"></div></div><div class="mantine-hidden-from-sm"><div style="--stack-gap:0rem;--stack-align:stretch;--stack-justify:flex-start" class="m_6d731127 mantine-Stack-root"><a style="--button-color:var(--mantine-color-white);margin-bottom:var(--mantine-spacing-xs);padding:0rem;text-decoration:none" class="mantine-focus-auto m_849cf0da mantine-focus-auto m_77c9d27d mantine-Button-root m_87cf2631 mantine-UnstyledButton-root m_b6d8b162 mantine-Text-root mantine-Anchor-root" data-underline="hover" href="/courses/php-introduction-to-oop/lessons/exceptions/finish_unit?unit=theory" data-disabled="true" data-block="true" disabled=""><span class="m_80f1301b mantine-Button-inner"><span class="m_811560b9 mantine-Button-label">→</span></span></a><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" data-disabled="true" type="button" disabled=""><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list-numbers "><path d="M11 6h9"></path><path d="M11 12h9"></path><path d="M12 18h8"></path><path d="M4 16a2 2 0 1 1 4 0c0 .591 -.5 1 -1 1.5l-3 2.5h4"></path><path d="M6 10v-6l-2 2"></path></svg></span></button><button style="--ai-size:var(--ai-size-sm);--ai-bg:transparent;--ai-hover:var(--mantine-color-indigo-light-hover);--ai-color:var(--mantine-color-indigo-light-color);--ai-bd:calc(0.0625rem * var(--mantine-scale)) solid transparent;padding-block:var(--mantine-spacing-lg);color:inherit;width:100%" class="mantine-focus-auto mantine-active m_8d3f4000 mantine-ActionIcon-root m_87cf2631 mantine-UnstyledButton-root" data-variant="subtle" data-size="sm" type="button"><span class="m_8d3afb97 mantine-ActionIcon-icon"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-message "><path d="M8 9h8"></path><path d="M8 13h6"></path><path d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3h12"></path></svg></span></button></div></div></div></div></div></div></div>
</main>
<footer class="bg-dark fw-light text-light px-3 py-5">
<div class="row small">
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 mb-3">Хекслет</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/about">О нас</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/testimonials">Отзывы</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://b2b.hexlet.io" role="button">Корпоративное обучение</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/blog">Блог</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/qna">Вопросы и ответы</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/glossary">Глоссарий</a>
</li>
<li>
<span class="nav-link link-light py-1 ps-0 external-link" data-href="https://help.hexlet.io" data-target="_blank" role="button">Справка</span>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" target="_blank" rel="noopener noreferrer" href="/map">Карта сайта</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5 fw-normal mb-3">Направления</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_devops">DevOps
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_data_analytics">Аналитика
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_backend_development">Бэкенд
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_programming">Программирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_testing">Тестирование
</a></li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/courses_front_end_dev">Фронтенд
</a></li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Профессии</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/devops-engineer-from-scratch">DevOps-инженер с нуля</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/go">Go-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/java">Java-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python">Python-разработчик </a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/data-analytics">Аналитик данных</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/qa-engineer">Инженер по ручному тестированию</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php">РНР-разработчик</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/frontend">Фронтенд-разработчик</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-6 col-md-3">
<div class="h5">Навыки</div>
<ul class="list-unstyled">
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/python-django-developer">Django</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/docker">Docker</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/php-laravel-developer">Laravel</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/postman">Postman</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-react-developer">React</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/js-rest-api">REST API в Node.js</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/spring-boot">Spring Boot</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/programs/typescript">Typescript</a>
</li>
</ul>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-sm-4 col-md-2">
<div class="fs-4">
<ul class="list-unstyled d-flex">
<li class="me-3">
<a aria-label="Telegram" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://t.me/hexlet_ru"><span class="bi bi-telegram"></span>
</a></li>
<li>
<a aria-label="Youtube" target="_blank" class="link-light" rel="noopener noreferrer nofollow" href="https://www.youtube.com/user/HexletUniversity"><span class="bi bi-youtube"></span>
</a></li>
</ul>
</div>
<div class="mb-2 d-flex flex-column">
<a class="link-light text-decoration-none" rel="nofollow" href="mailto:support@hexlet.io">support@hexlet.io</a>
<a class="link-light text-decoration-none py-2" target="_blank" href="https://t.me/hexlet_help_bot">t.me/hexlet_help_bot</a>
</div>
<ul class="list-unstyled d-flex">
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://hexlet.io/locale/switch?new_locale=en" data-target="_self" role="button"><span class="my-auto">EN</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 opacity-100 external-link" rel="nofollow" data-href="https://ru.hexlet.io/locale/switch?new_locale=ru" data-target="_self" role="button"><span class="my-auto">RU</span>
</span></li>
<li class="me-3">
<span class="link-light text-decoration-none opacity-50 x-font-size-18 external-link" rel="nofollow" data-href="https://kz.hexlet.io/locale/switch?new_locale=kz" data-target="_self" role="button"><span class="my-auto">KZ</span>
</span></li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<ul class="list-unstyled fs-4">
<li class="mb-3">
<a class="link-light text-decoration-none" href="tel:8%20800%20100%2022%2047">8 800 100 22 47</a>
<span class="d-block opacity-50 small">бесплатно по РФ</span>
</li>
<li>
<a class="link-light text-decoration-none" href="tel:%2B7%20495%20085%2021%2062">+7 495 085 21 62</a>
<span class="d-block opacity-50 small">бесплатно по Москве</span>
</li>
</ul>
</div>
<div class="col-12 col-sm-4 col-md-3">
<div class="small mb-3">Образовательные услуги оказываются на основании Л035-01298-77/01989008 от 14.03.2025</div>
<ul class="list-unstyled small">
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/legal">Правовая информация</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/offer">Оферта</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/license">Лицензия</a>
</li>
<li>
<a class="nav-link link-light py-1 ps-0" href="/pages/contacts">Контакты</a>
</li>
</ul>
</div>
<div class="col-12 col-sm-12 col-md-4 small">
<div class="mb-2">
<div>ООО «<a href="/" class="text-decoration-none link-light">Хекслет Рус</a>»</div>
<div>108813 г. Москва, вн.тер.г. поселение Московский,</div>
<div>г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3</div>
<div>ОГРН 1217300010476</div>
<div>ИНН 7325174845</div>
</div>
<hr>
<div>АНО ДПО «<a href="/" class="text-decoration-none link-light">Учебный центр «Хекслет</a>»</div>
<div>119331 г. Москва, вн. тер. г. муниципальный округ</div>
<div>Ломоносовский, пр-кт Вернадского, д. 29</div>
<div>ОГРН 1247700712390</div>
<div>ИНН 7736364948</div>
</div>
</div>
</footer>
<div id="root-assistant-offcanvas"></div>
<script src="/vite/assets/assistant-Bukl1lYy.js" crossorigin="anonymous" type="module"></script><link rel="modulepreload" href="/vite/assets/chunk-DsPFFUou.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/init-BrRXra1y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/ErrorFallbackBlock-naDSYSy9.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/MarkdownBlock-DbyKWoR_.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/gon-D3e4yh1x.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/mantine-CGMYrt2Y.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/shiki-V011pkdv.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/utils-DRqSHbQE.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/routes-CCH8ilKF.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-XR8Qr8kR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dist-GCHh59xr.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/Box-B5-OOzBf.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/notifications.store-C-3AFSMn.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useIsomorphicEffect-HJ6VK0D3.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/lib-KSp6QbZ0.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/axios-BEvgo0ym.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/classnames-l6ipYlLR.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/dayjs.min-BkKovM-s.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/debounce-jMQ_Cf4f.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/i18next-BlSq9s7B.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/client-U9M77rxp.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-dom-DaLxUz_h.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/useTranslation-Bx1Cdrkz.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/compiler-runtime-6XxiPFnt.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/jsx-runtime-CwjcCKJi.js" as="script" crossorigin="anonymous">
<link rel="modulepreload" href="/vite/assets/react-CkL4ZRHB.js" as="script" crossorigin="anonymous">
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v67327c56f0bb4ef8b305cae61679db8f1769101564043" integrity="sha512-rdcWY47ByXd76cbCFzznIcEaCN71jqkWBBqlwhF1SY7KubdLKZiEGeP7AyieKZlGP9hbY/MhGrwXzJC/HulNyg==" data-cf-beacon='{"version":"2024.11.0","token":"d11015b65d11429ea6b4a2ef37dd7e0b","server_timing":{"name":{"cfCacheStatus":true,"cfEdge":true,"cfExtPri":true,"cfL4":true,"cfOrigin":true,"cfSpeedBrain":true},"location_startswith":null}}' crossorigin="anonymous"></script>
</body>
</html>