Windows. Железо. Браузеры. Безопасность. Операционные системы

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

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

HTTP_ACCEPT - В этой переменной перечислены все MIME-типы данных, которые могут быть восприняты браузером. Строка */* означает, что браузер понимает любой тип.

HTTP_ACCEPT= */*

HTTP_REFERER - Эта переменная представляет сведения о странице, с которой пользователь попал на данную. Эту переменную можно использовать, например, для отслеживания перемещения пользователя по вашему сайту, а затем просматривать наиболее популярные маршруты.

HTTP_REFERER= http://www.spravkaweb.ru/

HTTP_COOKIE - В этой переменной храняться все Cookies в URL-кодировке.

HTTP_COOKIE=

HTTP_USER_AGENT - Идентифицирует браузер пользователя. Для установления типа браузера нужно проверить эту строку на наличие слов: если браузер - Internet Explorer, то будет присутствовать подстрока MSIE, а если в наличии лишь слово Mozilla, то это Netscape.

HTTP_USER_AGENT= Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 4.0)

У вас Internet Explorer

HTTP_HOST - Содержит доменное имя Web-сервера, на котором запустился сценарий. Эту переменную достаточно удобно использовать, например, для генерации полного пути, который требуется в заголовке Location, чтобы не привязываться к конкретному серверу.

HTTP_HOST= www.spravkaweb.ru

HTTP_FROM - Адрес электронной почты пользователя, направившего запрос.

HTTP_FROM=

SERVER_NAME - Доменное имя или IP-адрес сервера.

SERVER_NAME= www.spravkaweb.ru

SERVER_SOFTWARE - Имя и версия программы-сервера, отвечающей на запрос клиента.

SERVER_SOFTWARE= Apache/1.3.33 (Unix) mod_jk/1.2.8 mod_auth_passthrough/1.8 mod_log_bytes/1.2mod_bwlimited/1.4 FrontPage/5.0.2.2635 mod_ssl/2.8.22 OpenSSL/0.9.7a PHP-CGI/0.1b

SERVER_PORT - Эта переменная содержит порт сервера, к которому обратился браузер пользователя. Обычно это 80. Переменная так-же может применяться для формирования параметра заголовка Location.

SERVER_PORT= 80

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

SERVER_PROTOCOL= HTTP/1.0

REMOTE_ADDR - Эта переменная содержит IP-адрес (или доменное имя) узла пользователя, на котором был запущен браузер.

REMOTE_ADDR= 212.94.114.177

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

REMOTE_PORT= 4277

REMOTE_USER - Идентификационное имя пользователя, посылающего запрос.

REMOTE_USER=

SCRIPT_NAME - Содержит имя файла, содержащего данный сценарий. Эту переменную удобно использовать при формировании заголовка Location при переадресации на себя (self-redirect), а также для подставления значения атрибута action тега

на странице, которую вызывает сценарий при запуске без параметров (для того чтобы не привязываться к конкретному имени сценария).

SCRIPT_NAME= /pril.php

DOCUMENT_ROOT - Корневой каталог дерева Web-документов.

DOCUMENT_ROOT= /home/spravka/public_html

REQUEST_METHOD - Метод, который применяет пользователь при передаче данных. Надо заметить, что грамотно составленный сценарий должен сам определять на основе этой переменной, какой метод задействует пользователь, и принимать данные из соответствующего источника, а не расчитывать, что передача будет осуществляться, например, только методом POST.

REQUEST_METHOD= GET

QUERY_STRING - Содержит параметры, которые в URL указаны после вопросительного знака. Напомним, что они доступны как при методе GET, так и при методе POST (если в последнем случае они были определены в атрибуте action тега ).

QUERY_STRING= ss=getenv

PATH_INFO - Содержит дополнительную информацию о пути.

PATH_INFO=

PATH_TRANSLATED - Та же информация, что и в переменной PATH_INFO с префиксом, задающим путь к корневому каталогу дерева Web-документов.

PATH_TRANSLATED=

CONTENT_TYPE - Медиа-тип данных запроса.

CONTENT_TYPE=

CONTENT_LENGTH - Возвращает количество байт данных, присланных пользователем. Эту переменную необходимо анализировать, если вы занимаетесь приемом и обработкой POST-формы.

GATEWAY_INTERFACE - Версия CGI, которую использует сервер.

GATEWAY_INTERFACE= CGI/1.1


Переменные окружения

Переменные окружения:




echo "Вы пришли из: ".getenv("HTTP_REFERER")."
";
$br=getenv("HTTP_USER_AGENT");
if(strpos($br,"MSIE")!==false) echo "У вас Internet Explorer
";
else echo "У вас Netscape или др.
";
echo "Ваш IP: ".getenv("REMOTE_ADDR")."
";
echo "Вот параметры в строке браузера: ".getenv("QUERY_STRING");
?>

Это короткий how-to для реализации конфигурации php-сервиса, зависимого от окружения, в котором он запущен. Я буду рад, если кто-то подскажет более изящное решение или поправит в мелочах.

Основная идея

Запускать сервис, микросервисы и зависимые приложения в рамках одной экосистемы, конфигурируемой с помощью переменных окружения .
Проблема
В этой статье слишком много раз повторяется «переменные окружения».
Из коробки php-fpm игнорирует глобальные переменные окружения (getenv function), в то время как php cli их может получать.
Предыстория
Этот раздел можно пропустить, если вы уже работали с.env

В данный момент я работаю над проектом, написанном на ZF2. Для конфигурации проекта использовались конфиг-файлы для разных окружений . Это порождает большое количество дубликатов конфигурации в репозитории проекта примерно такого вида:
  • session.global.php
  • session.local.php.dist
  • session.unittest.php.dist
  • db.global.php
  • db.local.php.dist
  • db.unittest.php.dist
Эти дубликаты приходится постоянно синхронизировать друг с другом. Кроме того, они хранят определённую php-логику внутри себя, что порождает дублирование кода.

Итак, проект теперь учитывает окружение, но...

Пока разработка велась на рабочих машинках, проект читал.env файл и всё работало. Но когда я развернул тестовую среду, оказалось, что если задать взаправдашние системные переменные окружения, php-fpm их игнорирует. Различные рецепты из гугла и StackOverflow сводились к той или иной автоматизации использования двух известных способов:

1. Передача переменных через nginx параметром fastcgi_param SOMEENV test;
2. Установкой переменных в формате env в конфигурации пула рабочих процессов php-fpm .

И первый, и второй вариант, удобны для каких-то особых ситуаций. Но если мыслить в парадигме «конфигурировать среду, а не приложение», то подобные способы оказываются куда труднее, чем например просто положить.env файл в папку с проектом. Но ведь оркестратор, CI-система или просто системный администратор не должен знать детали реализации проекта, это не изящно.

Предлагаемый способ решения
Скомбинировав различные рецепты из сети, я нащупал следующее рабочее решение.
Тестировалось под Centos 7, PHP 5.6.14.

1. Открыть /etc/php.ini - Заменить variables_order = "GPCS" на variables_order = "EGPCS" # После этого PHP добавит в глобальное пространство переменные окружения # http://php.net/manual/ru/ini.core.php#ini.variables-order 2. Открыть /etc/php-fpm.d/www.conf, не путать с /etc/php-fpm.conf (в разных системах может быть в разном месте, это конфиг www-пула процессов для php-fpm. - Добавить (или заменить, если вдруг есть): clear_env = no # выключить очистку глобальных переменных для запускаемых воркеров 3. Установить необходимые переменные окружения в /etc/environment (стандартный синтаксис A=B) 4. ln -fs /etc/environment /etc/sysconfig/php-fpm # теперь конфиг переменных окружения сервиса php-fpm будет просто ссылкой на глобальный конфиг 5. systemctl daemon-reload && service php-fpm restart

Этот же подход с симлинком, в теории, применим и к другим сервисам.

Плюсы предложенного решения:
- Переменные, хранящиеся в /etc/environment, доступны разным приложениям. Можно вызвать echo $MYSQL_HOST в shell или getenv("MYSQL_HOST") в php.
- Переменные окружения, которые явно не заданы в /etc/environment, не попадут в php-fpm. Это позволяет с помощью оркестратора контролировать окружение извне изолированной системы, в которой запущен сервис.

Минусы:
- К сожалению, у php-fpm я не нашел работающей команды для reload по аналогии с nginx, так что в случае изменения /etc/environment, обязательно нужно делать systemctl daemon-reload && service php-fpm restart .

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

Переменные окружения

Переменные окружения в PHP

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

HTTP_ACCEPT

В этой переменной перечислены все MIME-типы данных , которые могут быть восприняты браузером. Строка */* означает, что браузер понимает любой тип.

HTTP_ACCEPT= image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, */*

HTTP_REFERER

Эта переменная представляет сведения о странице, с которой пользователь попал на данную. Эту переменную можно использовать, например, для отслеживания перемещения пользователя по вашему сайту, а затем просматривать наиболее популярные маршруты.

HTTP_REFERER= http://www.spravkaweb.ru/php/pril/

HTTP_COOKIE

В этой переменной хранятся все Cookies в URL-кодировке.

HTTP_COOKIE= hotlog=1; ZDEDebuggerPresent=php,phtml,php3; b=b; PHPSESSID=16805922a9258cda274316e60f649cf8

HTTP_USER_AGENT

Идентифицирует браузер пользователя. Для установления типа браузера нужно проверить эту строку на наличие слов: если браузер - Internet Explorer, то будет присутствовать подстрока MSIE, а если в наличии лишь слово Mozilla, то это Netscape.

Например:

HTTP_USER_AGENT= Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; MyIE2; Maxthon)

HTTP_HOST

Содержит доменное имя Web-сервера, на котором запустился сценарий. Эту переменную достаточно удобно использовать, например, для генерации полного пути, который требуется в заголовке Location, чтобы не привязываться к конкретному серверу.

HTTP_HOST= www.spravkaweb.ru

HTTP_FROM

Адрес электронной почты пользователя, направившего запрос.

SERVER_NAME

Доменное имя или IP-адрес сервера.

SERVER_NAME= www.spravkaweb.ru

SERVER_SOFTWARE

Имя и версия программы-сервера, отвечающей на запрос клиента.

SERVER_PORT

Эта переменная содержит порт сервера, к которому обратился браузер пользователя. Обычно это 80. Переменная так-же может применяться для формирования параметра заголовка Location.

SERVER_PROTOCOL

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

SERVER_PROTOCOL= HTTP/1.1

REMOTE_ADDR

Эта переменная содержит IP-адрес (или доменное имя) узла пользователя, на котором был запущен браузер.

REMOTE_PORT

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

REMOTE_USER

Идентификационное имя пользователя, посылающего запрос.

SCRIPT_NAME

Содержит имя файла, содержащего данный сценарий. Эту переменную удобно использовать при формировании заголовка Location при переадресации на себя (self-redirect), а также для подставления значения атрибута action тега на странице, которую вызывает сценарий при запуске без параметров (для того чтобы не привязываться к конкретному имени сценария).

DOCUMENT_ROOT

Корневой каталог дерева Web-документов.

REQUEST_METHOD

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

QUERY_STRING

Содержит параметры, которые в URL указаны после вопросительного знака. Напомним, что они доступны как при методе GET, так и при методе POST (если в последнем случае они были определены в атрибуте action тега ).

PATH_INFO

Содержит дополнительную информацию о пути.

PATH_TRANSLATED

Та же информация, что и в переменной PATH_INFO с префиксом, задающим путь к корневому каталогу дерева Web-документов.

CONTENT_TYPE

Медиа-тип данных запроса.

CONTENT_LENGTH

Возвращает количество байт данных, присланных пользователем. Эту переменную необходимо анализировать, если вы занимаетесь приемом и обработкой POST-формы.

GATEWAY_INTERFACE

Версия CGI, которую использует сервер.

Пример использования переменных окружения

Переменные окружения

Переменные окружения:

echo "Вы пришли из: ".getenv("HTTP_REFERER")."
";

$br=getenv("HTTP_USER_AGENT");

if(strpos($br,"MSIE")!==false) echo "У вас Internet Explorer
";

else echo "У вас Netscape или др.
";

echo "Ваш IP: ".getenv("REMOTE_ADDR")."
";

echo "Вот параметры в строке браузера: ".getenv("QUERY_STRING");

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Из книги Программирование на языке Ruby [Идеология языка, теория и практика применения] автора Фултон Хэл

Из книги HTML 5, CSS 3 и Web 2.0. Разработка современных Web-сайтов. автора Дронов Владимир

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.

10.4. Переменные окружения Когда запускается какая-либо Unix-программа, доступная ей среда включает в себя набор связей "имя-значение" (как имена, так и значения являются строками). Некоторые из них устанавливаются пользователем вручную, другие - системой во время

Из книги Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ автора Борри Хелен

10.4.1. Системные переменные окружения Существует множество широко известных переменных окружения, значения которых программа может получить при запуске из оболочки Unix. Данные переменные (особенно НОМЕ) часто требуется оценить до считывания локального файла

Из книги Linux программирование в примерах автора Роббинс Арнольд

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

Из книги автора

10.4.3. Когда использовать переменные окружения Общим для пользовательских и системных переменных окружения является то обстоятельство, что в них содержатся данные, хранение которых в большом количестве конфигурационных файлов было бы утомительным. И крайне утомительным

Из книги автора

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

Из книги автора

Функции и переменные. Локальные переменные Объявленные ранее функции создают внутри своего тела собственные переменные. Это так называемые локальные переменные. Такие переменные доступны только внутри тела функции, в котором они объявлены. При завершении выполнения

Из книги автора

22.3.3. Переменные окружения В программах, работающих с возможностями setuid или setgid, нужно проявлять особую осторожность с установками окружения. Эти переменные определяются пользователем, активизировавшим программу, тем самым открывается путь для атак. Самая явная атака

Из книги автора

Переменные окружения Переменные окружения - глобальные установки системы, которые используются при первоначальной загрузке операционной системы. В Windows, Linux и в большинстве систем UNIX сервер Firebird распознает и использует некоторые переменные окружения, если они

Из книги автора

Где устанавливаются переменные окружения WindowsТип переменных окружения и способ их установки меняется от одной версии Windows к другой. В табл. 3.1 показаны типы (если применимы) и способы установки значений переменным окружения.Таблица 3.1. Установки переменных окружения для

Из книги автора

Глава 2 Аргументы, опции и переменные окружения Первой задачей любой программы обычно является интерпретация опций и аргументов командной строки. Данная глава рассматривает, как программы С (и С++) получают аргументы своей командной строки, описывает стандартные

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

  • Формируемые сервером переменные;
  • Специальные переменные сервера Apache;
  • Переменные HTTP-полей запроса;
  • Переменные SSL-соединения (защищенного соединения).

Рассмотрим первые три группы переменных окружения:

Формируемые сервером переменные:

Переменная окружения

AUTH_TYPE Используется схема аутентификации. Обычно "BASIC"
CONTENT_LENGTH Длина содержимого, например, text/html
CONTENT_TYPE MIME-тип содержимого, например, text/html
GETAWAY_INTERFACE Версия CGI, например CGI/1.1
PATH_INFO HTTP-путь к сценарию
PATH_TRANSLATED Полный путь к сценарию
REMOTE_ADDR IP-адрес запрашиваемого компьютера-клиента
REMOTE_HOST Доменное имя запрашивающего компьютера (если доступно). Доменное имя определяется web-сервером с помощью службы DNS. Директива HostNameLookups сервера Apache разрешает (или запрещает) преобразование IP-адреса в доменное имя.
REMOTE_PORT Порт, закрепленный за браузером для получения ответа от сервера
REMOTE_USER Имя пользователя, прошедшего аутентификацию
QUERY_STRING Строка переданных серверу параметров
SERVER_ADDR IP-адрес сервера
SERVER_NAME Доменное имя сервера. Определяется директивой ServerName файла конфигурации
SERVER_PORT TCP-порт Web-сервера. Обычно 80
SERVER_PROTOCOL Версия протокола HTTP. Например, HTTP/1.1
SERVER_SOFTWARE Программное обеспечение сервера
SCRIPT_NAME HTTP-путь к сценарию
SCRIPT_FILENAME Имя файла сценария в файловой системе сервера (физический путь). Например, /var/www/cgi-bin/script.cgi

Специальные переменные сервера Apache:

Переменные HTTP-полей запроса:

Переменная окружения

Описание переменной окружения

HTTP_HOST Имя виртуального хоста, которому адресован запрос
HTTP_USER_AGENT Программное обеспечение удаленного пользователя. Обычно данная переменная окружения содержит название и версию браузера
HTTP_ACCEPT Список поддерживаемых клиентов типов содержимого. В последнее время вместо списка браузеры возвращают значение *.*, что означает "все типы"
HTTP_ACCEPT_LANGUAGE Список поддерживаемых языков в порядке предпочтения, например, ru, en
HTTP_ACCEPT_ENCODING Список поддерживаемых методов сжатия
HTTP_ACCEPT_CHARSET Список поддерживаемых кодировок
HTTP_CONNECTION

Тип соединения. Возможны два варианта:

  • Keep-alive - если после ответа на запрос не нужно разрывать соединение;
  • Close - если нужно закрыть соединение сразу после ответа на запрос.
HTTP_REFERER Значение поля REFERER. В этом поле браузер передает URL ресурса, который ссылается на наш сервер. Например, если пользователь перешел на сайт со страницы http://www.somehost.com/page.php, то значение поля REFERER будет http://www.somehost.com/page.php.
HTTP_X_FORWARDED_FOR Если пользователь работает через прокси-сервер, то в этом поле будет IP-адрес компьютера, обратившегося к прокси-серверу. Если данное поле уже содержит значение, то новое значение будет добавлено через запятую.

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

Давайте подробнее взглянем на то:

  • как это работает?
  • действительно ли это хорошая идея?
  • как с ними работать в PHP?
  • и в заключение на некоторые рекомендации и распространенные ошибки, которых следует избегать - на те ловушки, на которые мы наткнулись в реальном мире!

Мы не будем рассматривать как настроить переменные окружения в вашем веб-сервере / Docker-е / crontab-ах... т. к. это зависит от системы, программного обеспечения, а мы хотим сосредоточиться на самих переменных окружения.

Если ваш хостинг использует Docker Swarm или AWS , все будет немного по-другому, например, т. к. они решили подсовывать файлы на файловую систему вашего контейнера, чтобы внедрить ваши секретные ключи, а не использовать переменные окружения. Это очень специфично для этих платформ и не является распространённым вариантом для всех.

Env vars 101

При запуске программы, она наследует все переменные окружения от своих родителей. Так что если вы установите переменную YOLO в значение covfefe в bash, а затем выполните команду, вы сможете прочитать YOLO в любом дочернем процессе.

$ YOLO=covfefe php -r "echo getenv("YOLO");" covfefe

Поскольку эта переменная определена только локально, мы не можем прочитать её из другого терминала (другого родителя). Идея в том, чтобы убедиться, что ваше приложение всегда наследует нужные переменные.

Вы можете посмотреть все переменные окружения в командной строке, выполнив следующую команду, но вы не увидите переменной YOLO , т. к. она была передана только в команду php "на лету", а не установлена в текущем процессе:

Вы можете установить переменную окружения с помощью export <имя>=<значение> :

$ export YOLO=covfefe

Имена переменных чувствительны к регистру и соглашение заключается в использовании имён только на английском, в верхнем регистре, с _ в качестве разделителя (т. н. "змеиный" стиль в верхнем регистре). Вы уже наверняка знаете некоторые переменные - такие как PATH , DISPLAY , HTTP_PROXY …

Лучшие практики на сегодня

josegonzalez/dotenv , ориентирована на безопасность:

Эта библиотека не заполнит суперглобальные переменные по умолчанию:

$Loader = new josegonzalez\Dotenv\Loader("path/to/.env"); // Парсим файл.env: $Loader->parse(); // Отправляем результат парсинга.env в переменную $_ENV: $Loader->toEnv();

Она поддерживает обязательные переменные, фильтрацию, и может выбрасывать исключения, когда переменная перезаписывается.

symfony/dotenv , новый малыш на этом поприще:

Доступен начиная с Symfony 3.3. Этот компонент заботится о.env -файле, как остальные, и тоже заполняет суперглобальные массивы:

$dotenv = new Symfony\Component\Dotenv\Dotenv(); $dotenv->load(__DIR__."/.env"); $dbUser = getenv("DB_USER"); $dbUser = $_ENV["DB_USER"]; $dbUser = $_SERVER["DB_USER"];

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

Переменные окружения - всегда строки

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

Class Db { public function connect(string hostname, int port) { } } // Это не будет работать: $db->connect($_SERVER["DATABASE_HOSTNAME"], $_SERVER["DATABASE_PORT"]);

В Symfony теперь можно преобразовывать variables , а даже больше - чтение файла, декодирование json...…

Переменные окружения везде или нет

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

Но если правильно использовать, в приложении на Symfony, например, переменные окружения могут быть изменены "на лету" - без очистки какого-либо кеша, без обращения к файловой системе, без развертывания кода: просто перезапустив процесс, например.

Тенденция иметь только одну переменную, как APP_CONFIG_PATH , и читать её через "%env(json:file:APP_CONFIG_PATH)%" для меня выглядит как заново изобретать старый добрый parameters.yml , если файл не управляется автоматически с помощью надежного инструмента (как AWS Secret Store). И также есть envkey.com , который позволяет управлять вашими переменными окружения из одного места, не возясь с файлами самостоятельно, мне нравится такой подход, т. к. это гораздо проще!