Часть 1 — Зачем фреймворк, Composer и установка Laravel
Разбираем, что фреймворк забирает на себя по сравнению с «чистым PHP», смотрим, как Composer ставит пакеты, на эксперименте из одной папки — и устанавливаем Laravel одной командой: composer create-project laravel/laravel project-laravel. К концу части на диске лежит каркас приложения со 110 пакетами, и мы знаем, зачем нужен каждый файл в его корне.
Что понадобится: Linux Mint 22 (база Ubuntu 24.04) со стеком из блока Linux — nginx + PHP-FPM от своего пользователя, MariaDB, dnsmasq с зоной .rs; PHP 8.3; Composer 2.7+; около 100 МБ свободного места.
Laravel 13 PHP 8.3.6 110 пакетов · 76 МБ
Курс закончился на «чистом PHP». Что болит
В HTTP-блоке вы собрали рабочую систему с нуля: брони авиабилетов, профили пассажиров, статистику по странам. Всё руками: единая точка подключения includes.php, свой класс layout с методом render(), свой класс пагинации со страницами через ceil(), профиль по параметру pid в URL. Это сила — вы понимаете, как всё устроено изнутри. Но посмотрите на цену:
- Роутинг руками. Кто обрабатывает запрос, решает
if/elseпо$_GET. Каждая новая страница — новая ветка; про «неправильный» параметр надо не забыть вернуть 404. - Вёрстка-каркас руками. Класс
layout— уже шаг к шаблонам, но вы написали его сами под один проект. В следующем проекте будете писать заново. - Защита руками. В блоке «SQL-инъекции» один непроверенный параметр
sortоткрывал базу черезUNION. Белый список и prepared statements работают — но только там, где вы про них вспомнили. - Подключение к БД руками. DSN, логин, пароль — прямо в PHP-файле; в блоке «Базы данных» так и было.
- Структура «как получится». Процедурный код растёт куда глаза глядят; блок ООП начинался ровно с этого — со спагетти-кода, который не масштабируется.
Вас научили делать всё перечисленное правильно и руками. Фреймворк делает правильное автоматически и одинаково: любой проект на Laravel похож на любой другой проект на Laravel, а рутина — маршруты, экранирование, подключение к БД, настройки — решена за вас один раз и проверена миллионами проектов.
Фреймворк — это договорённость плюс готовый каркас
Фреймворк — готовый каркас приложения плюс договорённость, как добавлять в него свой код:
- каркас написан и проверен за вас: приём запроса, разбор URL, экранирование вывода, запросы к БД, сессии;
- договорённость описывает, где живёт ваш код: маршруты — тут, контроллеры — там, шаблоны — в такой-то папке.
Эту схему вы уже знаете дважды:
- nginx (lesson.linux.06): вы не писали веб-сервер — вы писали конфиг по договорённости:
root,index,fastcgi_pass. Всё остальное nginx делает сам. - Bootstrap (HTTP-блок): фронтенд-фреймворк — договорённость о классах (
container,btn,table) и готовый каркас стилей на все случаи.
Библиотеку вызываете вы
Фреймворк вызывает вас
Такую строку напишем в части 3.
MVC одной картинкой
Договорённость Laravel называется MVC — Model-View-Controller, «модель-вид-контроллер». Путь данных внутри приложения:
В lesson.http.i.01 вы прошли путь запроса снаружи: браузер → DNS → nginx → PHP-FPM → index.php → буфер вывода → ответ. MVC начинается там, где раньше начинался ваш единый большой index.php: внутри скрипта вместо одной простыни кота по очереди работают четыре роли.
К концу части 5 каждая стрелка этой схемы будет живым кодом, который вы написали сами: маршрут /users, контроллер UserController, модель User, шаблоны списка и форм.
Окружение: какая машина и какой PHP
Laravel 13 не установится на PHP старее 8.3 — это требование записано прямо в composer.json каркаса (строка "php": "^8.3", увидим её ниже). На вашем Mint 22 PHP 8.3 стоит ещё с lesson.linux.04 — проверяем.
Все терминалы урока — настоящие захваты. Команды выполнены на стенде, повторяющем ваш: Ubuntu 24.04 — та же база, на которой стоит Linux Mint 22, пользователь student, проекты в ~/recoursia/. В выводе hostnamectl честно видно, что стенд — контейнер: его собрали специально для съемки этого урока.
Static hostname: mint Icon name: computer-container Chassis: container ☐ Machine ID: 83438e1063ca4f0d95715eaab35d3d05 Boot ID: a64ac522c6f84cecbf93b9d4cd689d7a Virtualization: dockerOperating System: Ubuntu 24.04.4 LTS Kernel: Linux 6.18.46 Architecture: x86-64
Разбор команды
С чего начинается стенд: одна команда показывает систему, ядро и архитектуру машины, на которой будет жить Laravel.
hostnamectl| Токен | Зачем он и что меняет |
|---|---|
hostnamectl | утилита systemd: печатает «паспорт» машины — имя, ОС, ядро, виртуализацию. Аргументов и sudo не требует. |
Что произойдёт
Девять строк: hostname, chassis, Machine ID, Boot ID, виртуализация, ОС, ядро, архитектура.
На что смотреть
Строка 1 — hostname mint, строка 7 — Ubuntu 24.04.4 LTS: та же база, на которой стоит Linux Mint 22 у слушателей. Chassis: container и Virtualization: docker честно говорят, что стенд урока — контейнер, собранный для съёмки.
Частые ошибки
hostnamectlопрашивает systemd: там, где systemd не запущен как PID 1, команда вместо паспорта выдаст ошибку подключения к bus.
PHP — 8.3.6, выше минимальных 8.3:
PHP 8.3.6 (cli) (built: Sep 2 2026 12:56:02) (NTS)Copyright (c) The PHP GroupZend Engine v4.3.6, Copyright (c) Zend Technologies with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
Разбор команды
Laravel 13 не установится на PHP старее 8.3, поэтому версию проверяем до установки, а не по сообщению об ошибке.
php -v| Токен | Зачем он и что меняет |
|---|---|
php | исполняемый файл интерпретатора CLI; в Ubuntu/Mint его ставит пакет php8.3-cli. |
-v | флаг версии: напечатать версию PHP, дату сборки и версию Zend Engine, после чего сразу завершиться. |
Что произойдёт
Четыре строки: PHP 8.3.6 (cli) (built: Sep 2 2026 12:56:02) (NTS), копирайт, Zend Engine v4.3.6 и строка про OPcache.
На что смотреть
8.3.6 выше минимальных 8.3, которые требует каркас ("php": "^8.3" в его composer.json). Пометка (cli) — версия показана для консольного PHP: у PHP-FPM свой бинарник и свой php.ini. 8.3 — значение стенда; подставьте свою версию (php -v).
Частые ошибки
php: command not found— пакет php8.3-cli не установлен или не в PATH; без CLI не заработает и Composer.
8.3 и php8.3 в командах выше — версия стенда. Проверьте свою: php -v. Если она другая — замените 8.3 на свою во всех путях (/etc/php/…, /run/php/…), именах пакетов (php8.3-*) и сервисе (php8.3-fpm).
Сначала — ваша версия PHP; дальше в примерах 8.3
php -v на прошлом слайде напечатал 8.3.6 — это версия стенда. Та же версия попадается в командах урока в шести местах: имена пакетов, конфиг CLI, конфиг FPM, пул FPM, unix-сокет и имя сервиса. Поэтому шаг нулевой — узнать свою. php -v и php.ini вы уже трогали в lesson.linux.06, когда включали display_errors; теперь нужна короткая строка «мажор.минор»:
8.3Разбор команды
Строку «мажор.минор» удобно получать программно: её подставляют в имена пакетов, пути конфигов, имя сокета и имя сервиса.
php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_EOL;'| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI. |
-r | выполнить код, переданный аргументом, — без файла на диске. |
'…' | одинарные кавычки вокруг кода: shell отдаёт его PHP дословно и не трогает внутренние двойные кавычки. |
echo | печатает аргументы подряд, без разделителя. |
PHP_MAJOR_VERSION | константа PHP: мажорная версия (на стенде 8). |
. | оператор склейки строк в PHP. |
"." | строка-разделитель: двойные кавычки внутри одинарных с shell не конфликтуют. |
PHP_MINOR_VERSION | константа PHP: минорная версия (на стенде 3). |
, | запятая в echo — второй аргумент, а не склейка: так перевод строки печатают отдельно. |
PHP_EOL | константа перевода строки. |
; | закрывает инструкцию PHP; точка с запятой внутри кода обязательна. |
Что произойдёт
Печатает ровно одну строку — 8.3.
На что смотреть
Это та строка, из которой собираются php8.3-cli, /etc/php/8.3/…, /run/php/php8.3-fpm.sock и имя сервиса php8.3-fpm. 8.3 — стенд; подставьте свою версию (php -v).
Частые ошибки
- Внешние кавычки должны быть одинарными: в двойных shell раскроет
$и подстановки, и до PHP доедет уже другой код.
Одна команда печатает все места, куда эта строка подставляется. V — ваша версия, дальше в таблице она же:
версия: 8.3пакеты: php8.3-cli php8.3-fpm php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-mysql php8.3-sqlite3конфиги: /etc/php/8.3/cli/php.ini и /etc/php/8.3/fpm/php.ini, пул /etc/php/8.3/fpm/pool.d/www.confсокет: /run/php/php8.3-fpm.sockсервис: php8.3-fpmphp-fpm.sockphp8.3-fpm.pidphp8.3-fpm.sock
Разбор команды
Версия PHP зашита в имена пакетов, пути конфигов, имя сокета и имя сервиса. Одна команда печатает все эти места и тут же сверяет их с тем, что лежит на диске.
V=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')| Токен | Зачем он и что меняет |
|---|---|
V= | присваивание переменной shell; пробела вокруг = быть не должно. |
$(…) | подстановка команды: то, что напечатает вложенный php, становится значением V. |
php -r '…' | код PHP в одинарных кавычках — склеивает мажор и минор версии в строку. |
Что произойдёт
В переменную V попадает строка 8.3. Дальше её подставляют во все echo, то есть пути печатаются, а не выдумываются.
На что смотреть
Переменная живёт только в этой оболочке: в новом терминале её нет, команду повторяют целиком.
Частые ошибки
- Пробел вокруг
=(V = $(…)) shell читает как командуVс аргументами — будетcommand not found.
echo "версия: $V"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки: только в них shell раскрывает $V. |
$V | значение переменной — 8.3. |
Что произойдёт
Первая строка вывода: версия: 8.3.
На что смотреть
С этой строки сверяют всё, что напечатано ниже.
Частые ошибки
- В одинарных кавычках
$Vнапечатался бы как текст, а не как версия.
echo "пакеты: php${V}-cli php${V}-fpm php${V}-mbstring php${V}-xml php${V}-curl php${V}-zip php${V}-mysql php${V}-sqlite3"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки, чтобы подстановка сработала. |
${V} | фигурные скобки явно показывают границу имени переменной перед дефисом. |
php${V}-cli | пакет интерпретатора CLI — тот, что запускает php и artisan. |
php${V}-fpm | пакет PHP-FPM — процесса, который отвечает за сайт в браузере. |
php${V}-mbstring … php${V}-sqlite3 | пакеты расширений, нужных Laravel: mbstring, xml, curl, zip, mysql, sqlite3. Имя пакета = php + версия + суффикс. |
Что произойдёт
Вторая строка вывода — восемь имён пакетов: php8.3-cli php8.3-fpm php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip php8.3-mysql php8.3-sqlite3.
На что смотреть
Именно эти пакеты ставят через apt, если чего-то не хватает; все они уже стоят на стенде. 8.3 — стенд; подставьте свою версию (php -v).
Частые ошибки
- Пакет без версии в имени (
php-mbstring) — метапакет версии по умолчанию: он может потянуть не тот PHP.
echo "конфиги: /etc/php/$V/cli/php.ini и /etc/php/$V/fpm/php.ini, пул /etc/php/$V/fpm/pool.d/www.conf"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки для раскрытия $V. |
/etc/php/$V/cli/php.ini | конфиг PHP для консоли — его читает php и artisan. |
/etc/php/$V/fpm/php.ini | отдельный конфиг PHP-FPM — он влияет на сайт в браузере. |
/etc/php/$V/fpm/pool.d/www.conf | файл пула FPM: пользователь, группа, адрес сокета и права на него. |
Что произойдёт
Третья строка вывода — три пути, в которые зашита версия.
На что смотреть
У CLI и FPM разные php.ini: правка одного не меняет другой. 8.3 — стенд; подставьте свою версию (php -v).
Частые ошибки
- Поправили
display_errorsв cli/php.ini, а в браузере ничего не изменилось — FPM читает свой файл, и после правки сервис перезапускают.
echo "сокет: /run/php/php${V}-fpm.sock"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки для раскрытия ${V}. |
/run/php/php${V}-fpm.sock | unix-сокет, по которому nginx передаёт .php-запросы в PHP-FPM. |
Что произойдёт
Четвёртая строка вывода — адрес сокета php8.3-fpm.sock.
На что смотреть
Этот путь пишут в fastcgi_pass конфига nginx (часть 2). Каталог /run живёт до перезагрузки, сокет создаёт сам сервис.
Частые ошибки
- Чужая версия в пути (
php8.4-fpm.sock) даёт в nginxconnect() failed (2: No such file or directory) while connecting to upstreamи 502 в браузере.
echo "сервис: php${V}-fpm"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки для раскрытия ${V}. |
php${V}-fpm | имя сервиса systemd, которым управляют через systemctl. |
Что произойдёт
Пятая строка вывода — имя сервиса php8.3-fpm.
На что смотреть
Оно нужно для sudo systemctl restart php8.3-fpm после любой правки конфигов FPM. 8.3 — стенд; подставьте свою версию (php -v).
Частые ошибки
Unit php8.4-fpm.service could not be found— версия в имени не совпала с установленной.
ls /run/php/| Токен | Зачем он и что меняет |
|---|---|
ls | показать содержимое каталога. |
/run/php/ | каталог рантайма PHP-FPM. |
Что произойдёт
Три имени: php-fpm.sock, php8.3-fpm.pid, php8.3-fpm.sock.
На что смотреть
Проверка на месте, а не на глаз: файл php8.3-fpm.sock из $V совпал с тем, что реально лежит в каталоге.
Частые ошибки
No such file or directory— каталога нет, если FPM не установлен; пустой каталог — сервис не запущен.
| Где версия встречается | На стенде | У вас |
|---|---|---|
| Пакеты apt | php8.3-cli, php8.3-fpm, php8.3-mbstring, … (весь список напечатан выше) |
php$V-cli, php$V-fpm, php$V-mbstring, … |
| Конфиг CLI | /etc/php/8.3/cli/php.ini |
/etc/php/$V/cli/php.ini |
| Конфиг FPM | /etc/php/8.3/fpm/php.ini |
/etc/php/$V/fpm/php.ini |
| Пул FPM | /etc/php/8.3/fpm/pool.d/www.conf |
/etc/php/$V/fpm/pool.d/www.conf |
| Unix-сокет | /run/php/php8.3-fpm.sock |
/run/php/php$V-fpm.sock |
| Сервис systemd | php8.3-fpm |
php$V-fpm |
| Своя строка версии | php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' → 8.3 |
та же команда → ваша |
До конца урока в примерах стоит 8.3 — версия стенда. Если у вас другая версия, подставляйте свою в путях, пакетах, сервисе и сокете. Дальше эта сноска повторяется коротко под слайдами, где версия важна.
Расширения PHP, которые нужны Laravel
Расширения не «доустанавливают по ходу» — их проверяют заранее, и не на глаз, а программно: функция extension_loaded() отвечает точно, собрано ли расширение в ваш PHP.
ctype естьcurl естьdom естьfileinfo естьfilter естьhash естьmbstring естьopenssl естьpcre естьpdo естьpdo_mysql естьpdo_sqlite естьsession естьsimplexml естьtokenizer естьxml естьzip естьbcmath естьintl естьgd есть
Разбор команды
Расширения PHP проверяют заранее и не на глаз: функция extension_loaded() отвечает точно, собрано ли расширение в этот интерпретатор.
php -r 'foreach (["ctype","curl","dom","fileinfo","filter","hash","mbstring","openssl","pcre","pdo","pdo_mysql","pdo_sqlite","session","simplexml","tokenizer","xml","zip","bcmath","intl","gd"] as $m) printf("%-12s %s\n", $m, extension_loaded($m) ? "есть" : "НЕТ");'| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI. |
-r | выполнить код из аргумента, без файла на диске. |
'…' | одинарные кавычки: shell не трогает код, поэтому $m и двойные кавычки внутри доезжают до PHP как есть. |
foreach | цикл по списку имён: одна итерация — одно расширение. |
["ctype","curl",…"gd"] | литерал массива из 20 имён расширений, которые нужны Laravel. |
as $m | $m — переменная цикла, текущее имя расширения. |
printf | форматированный вывод вместо echo — им делают ровные колонки. |
"%-12s %s\n" | формат: имя в поле шириной 12 с выравниванием влево, затем статус и перевод строки. |
extension_loaded($m) | функция PHP: загружено ли расширение в этот интерпретатор. |
? "есть" : "НЕТ" | тернарный оператор: печатаем спокойное «есть» или заметное «НЕТ». |
Что произойдёт
20 строк вида ctype есть — по одной на расширение. На стенде все 20 со значением «есть».
На что смотреть
Ищите «НЕТ»: верхний регистр видно сразу. Критичны pdo_mysql и pdo_sqlite (база), mbstring (строки UTF-8), openssl (ключ APP_KEY), tokenizer и xml (шаблоны), curl, zip, fileinfo, gd, intl, bcmath.
Частые ошибки
- «НЕТ» означает, что не стоит пакет
php8.3-<имя>; послеsudo apt installнуженsudo systemctl restart php8.3-fpm, и только потом — повторная проверка. Версия 8.3 — стенд, подставьте свою (php -v).
Все 20 на месте. Среди них — ваши старые знакомые по курсу:
pdo,pdo_mysql,pdo_sqlite— те самые расширения, через которые вы подключались к MariaDB в блоке «Базы данных»;mbstring— функцииmb_*из блока «Строки» (обычныйstrlenсчитает байты, а не символы UTF-8);gd,intl,bcmath— картинки, форматирование чисел и дат по локали, точная арифметика без погрешностей float;openssl,curl,zip— шифрование, HTTP-запросы, архивы.
Ставится как любой пакет через apt (lesson.linux.04): sudo apt install php8.3-<имя>, затем перезапуск FPM: sudo systemctl restart php8.3-fpm.
8.3 и php8.3 в командах выше — версия стенда. Проверьте свою: php -v. Если она другая — замените 8.3 на свою во всех путях (/etc/php/…, /run/php/…), именах пакетов (php8.3-*) и сервисе (php8.3-fpm).
Что уже стоит — из курса Linux
Ни веб-сервер, ни базу сегодня ставить не придётся: nginx, PHP-FPM, MariaDB и dnsmasq установлены и настроены в lesson.linux.04–06. Проверяем, что все четыре сервиса живы:
nginx activephp8.3-fpm activemariadb activednsmasq active
Разбор команды
Ни веб-сервер, ни базу сегодня ставить не придётся: проверяем одной командой, что все четыре сервиса из блока Linux запущены.
for s in nginx php8.3-fpm mariadb dnsmasq; do printf "%-14s %s\n" "$s" "$(systemctl is-active $s)"; done| Токен | Зачем он и что меняет |
|---|---|
for s in nginx php8.3-fpm mariadb dnsmasq | цикл shell по списку имён сервисов. Имя php8.3-fpm содержит версию: 8.3 — стенд, подставьте свою (php -v). |
do … done | тело цикла: выполняется по разу на каждое имя из списка. |
printf | форматированный вывод — в отличие от echo умеет выравнивать колонки. |
"%-14s %s\n" | формат: имя сервиса в поле шириной 14 с выравниванием влево, затем статус и перевод строки. |
"$s" | текущее имя сервиса; двойные кавычки страхуют значение от разбиения по пробелам. |
$(systemctl is-active $s) | подстановка команды: статус сервиса становится вторым аргументом printf. |
is-active | субкоманда systemctl: печатает active, inactive, failed или activating и возвращает ненулевой код, если сервис не запущен. |
Что произойдёт
Четыре ровные строки: nginx, php8.3-fpm, mariadb, dnsmasq — у всех статус active.
На что смотреть
Ищите любое значение кроме active: inactive — сервис остановлен, failed — упал при запуске, activating — ещё поднимается, сообщение про ненайденный юнит — такого сервиса в системе нет.
Частые ошибки
systemctl is-activeне требует sudo и работает даже у не запущенного сервиса — он просто печатаетinactive. Внутри цикла ненулевой код команды не прерывает вывод: строка напечатается со статусом, а не с ошибкой.
Две детали из блока Linux вернутся в уроке не раз.
nginx работает от вашего пользователя — в linux.05–06 вы заменили www-data в nginx.conf:
1:user student;nginx version: nginx/1.24.0 (Ubuntu)
Разбор команды
nginx в блоке Linux тоже перевели на вашего пользователя — проверяем директиву user и заодно версию сервера.
grep -n '^user' /etc/nginx/nginx.conf| Токен | Зачем он и что меняет |
|---|---|
grep | поиск строк по шаблону. |
-n | номер строки в файле. |
'^user' | ^ — начало строки: закомментированная #user www-data; в выдачу не попадает. |
/etc/nginx/nginx.conf | главный конфиг nginx; директива user живёт в нём, а не в конфигах сайтов. |
Что произойдёт
Одна строка: 1:user student; — директива стоит в первой строке файла.
На что смотреть
Значение student совпадает с user/group пула FPM и с владельцем проекта: читать файлы проекта и писать в storage/ сможет именно он.
Частые ошибки
- Пустой вывод — директива закомментирована, и nginx работает от пользователя по умолчанию (
www-data): тогда права на проект и на сокет FPM придётся согласовывать заново. Правку применяют черезsudo nginx -tи перезагрузку сервиса.
nginx -v| Токен | Зачем он и что меняет |
|---|---|
nginx | бинарник веб-сервера. |
-v | строчная v: напечатать версию и выйти. Заглавная -V добавила бы флаги сборки и список модулей. |
Что произойдёт
nginx version: nginx/1.24.0 (Ubuntu) — команда печатает это в stderr и завершается с кодом 0.
На что смотреть
Версия 1.24.0 — та же, что nginx подставляет в страницу ошибки (<center>nginx/1.24.0 (Ubuntu)</center>): по ней видно, что ответил nginx, а не PHP.
Частые ошибки
nginx -vничего не проверяет: тест конфига делаетnginx -t(в части 2 он даётsyntax is okиtest is successful), и запускать его нужно через sudo.
Пул PHP-FPM — тоже (настройки пула в www.conf из lesson.linux.06; на стенде пользователь student):
28:user = student29:group = student41:listen = /run/php/php8.3-fpm.sock53:listen.owner = student54:listen.group = student55:listen.mode = 0660
Разбор команды
Пул PHP-FPM работает от пользователя student — это настроили в lesson.linux.06. Именно поэтому Laravel сможет писать логи и кэш в свою папку storage/.
grep -nE '^(user|group|listen|listen.owner|listen.group|listen.mode) =' /etc/php/8.3/fpm/pool.d/www.conf| Токен | Зачем он и что меняет |
|---|---|
grep | поиск строк по шаблону. |
-nE | два флага слитно: -n печатает номера строк, -E включает расширенные регулярные выражения — (, ) и | пишутся без обратных слэшей. |
'^(user|group|listen|…) =' | шаблон: начало строки, затем одно из шести имён параметра, пробел и =. Якорь ^ отсекает комментарии, а пробел перед = — похожие параметры вроде listen.allowed_clients. |
/etc/php/8.3/fpm/pool.d/www.conf | файл пула www — пул по умолчанию в поставке Ubuntu. |
Что произойдёт
Шесть строк: user = student (28), group = student (29), listen = /run/php/php8.3-fpm.sock (41), listen.owner = student (53), listen.group = student (54), listen.mode = 0660 (55).
На что смотреть
Строки 28–29: код сайта выполняется от того же пользователя, которому принадлежит проект, — права на запись в storage/ сходятся. Строка 41: адрес сокета совпадает с тем, что пишут в fastcgi_pass. Строки 53–55: владелец и права самого сокета — их задаёт FPM, а не nginx.
Частые ошибки
- Если в пуле
user = www-data, а проект принадлежит вам, PHP не сможет писать скомпилированные шаблоны и кэш: в логе появитсяlocal.ERROR: tempnam(): file created in the system's temporary directory, а браузер получит 500 (воспроизводим в части 6). Любая правка www.conf требуетsudo systemctl restart php8.3-fpm. 8.3 в пути — стенд, подставьте свою версию (php -v).
Почему это важно именно для Laravel: приложение пишет на диск — логи, кэш, скомпилированные шаблоны — в папку storage/ внутри проекта. Пишет php-fpm. У нас он работает от того же пользователя, которому принадлежит проект, — права сойдутся. В части 6 мы специально сломаем права и посмотрим, как выглядят 500 из-за этого.
А этот unix-сокет — адрес, по которому nginx передаёт .php в FPM; в части 2 напишем его в fastcgi_pass:
total 4lrwxrwxrwx 1 root root 30 Oct 17 06:05 php-fpm.sock -> /etc/alternatives/php-fpm.sock-rw-r--r-- 1 root root 5 Oct 17 06:28 php8.3-fpm.pidsrw-rw---- 1 student student 0 Oct 17 06:28 php8.3-fpm.sock
Разбор команды
Смотрим сам unix-сокет: это адрес, по которому nginx передаёт .php-запросы в PHP-FPM, — в части 2 он попадёт в fastcgi_pass.
ls -l /run/php/| Токен | Зачем он и что меняет |
|---|---|
ls | список файлов каталога. |
-l | длинный формат: тип файла, права, владелец, размер, дата. |
/run/php/ | каталог рантайма PHP-FPM: здесь сервис создаёт сокет и pid-файл. |
Что произойдёт
Три записи: симлинк php-fpm.sock → /etc/alternatives/php-fpm.sock, файл php8.3-fpm.pid (5 байт) и сокет php8.3-fpm.sock.
На что смотреть
Первый символ s в srw-rw---- — это сокет, а не обычный файл. Владелец student:student и права 0660 — результат listen.owner, listen.group и listen.mode из www.conf. Симлинк php-fpm.sock без версии ведёт в /etc/alternatives — так Ubuntu выбирает сокет версии по умолчанию.
Частые ошибки
- Нет строки
php8.3-fpm.sock— сервис не запущен, проверьтеsystemctl status php8.3-fpm./run— tmpfs: после перезагрузки файлы создаёт сервис, а не вы вручную.
8.3 и php8.3 в командах выше — версия стенда. Проверьте свою: php -v. Если она другая — замените 8.3 на свою во всех путях (/etc/php/…, /run/php/…), именах пакетов (php8.3-*) и сервисе (php8.3-fpm).
Конфиг PHP: где лежит php.ini и display_errors
Чтобы поменять настройку PHP, надо знать, какой файл править. php --ini показывает, откуда CLI берёт конфигурацию:
Configuration File (php.ini) Path: /etc/php/8.3/cliLoaded Configuration File: /etc/php/8.3/cli/php.iniScan for additional .ini files in: /etc/php/8.3/cli/conf.dAdditional .ini files parsed: /etc/php/8.3/cli/conf.d/10-mysqlnd.ini,/etc/php/8.3/cli/conf.d/10-opcache.ini,/etc/php/8.3/cli/conf.d/10-pdo.ini,/etc/php/8.3/cli/conf.d/15-xml.ini,/etc/php/8.3/cli/conf.d/20-bcmath.ini,/etc/php/8.3/cli/conf.d/20-calendar.ini,/etc/php/8.3/cli/conf.d/20-ctype.ini,/etc/php/8.3/cli/conf.d/20-curl.ini,/etc/php/8.3/cli/conf.d/20-dom.ini,/etc/php/8.3/cli/conf.d/20-exif.ini,/etc/php/8.3/cli/conf.d/20-ffi.ini,/etc/php/8.3/cli/conf.d/20-fileinfo.ini,/etc/php/8.3/cli/conf.d/20-ftp.ini,/etc/php/8.3/cli/conf.d/20-gd.ini,/etc/php/8.3/cli/conf.d/20-gettext.ini,/etc/php/8.3/cli/conf.d/20-iconv.ini,/etc/php/8.3/cli/conf.d/20-intl.ini,/etc/php/8.3/cli/conf.d/20-mbstring.ini,/etc/php/8.3/cli/conf.d/20-mysqli.ini,/etc/php/8.3/cli/conf.d/20-pdo_mysql.ini,/etc/php/8.3/cli/conf.d/20-pdo_sqlite.ini,/etc/php/8.3/cli/conf.d/20-phar.ini,/etc/php/8.3/cli/conf.d/20-posix.ini,/etc/php/8.3/cli/conf.d/20-readline.ini,/etc/php/8.3/cli/conf.d/20-shmop.ini,/etc/php/8.3/cli/conf.d/20-simplexml.ini,/etc/php/8.3/cli/conf.d/20-sockets.ini,/etc/php/8.3/cli/conf.d/20-sqlite3.ini,/etc/php/8.3/cli/conf.d/20-sysvmsg.ini,/etc/php/8.3/cli/conf.d/20-sysvsem.ini,/etc/php/8.3/cli/conf.d/20-sysvshm.ini,/etc/php/8.3/cli/conf.d/20-tokenizer.ini,/etc/php/8.3/cli/conf.d/20-xmlreader.ini,/etc/php/8.3/cli/conf.d/20-xmlwriter.ini,/etc/php/8.3/cli/conf.d/20-xsl.ini,/etc/php/8.3/cli/conf.d/20-zip.ini
Разбор команды
Чтобы поменять настройку PHP, надо знать, какой файл править: php --ini показывает, откуда CLI берёт конфигурацию.
php --ini| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI. |
--ini | служебный флаг: напечатать пути к php.ini и к дополнительным .ini и завершиться; код приложения не выполняется. |
Что произойдёт
Три заголовка и список: каталог поиска php.ini, реально загруженный файл, каталог сканирования conf.d и 36 подключённых дополнительных .ini — от 10-mysqlnd.ini и 10-opcache.ini до 20-zip.ini.
На что смотреть
Строка 2 — Loaded Configuration File указывает на /etc/php/8.3/cli/php.ini: именно его правят для консоли и artisan. Строка 3 — каталог conf.d: расширения подключаются отдельными файлами, и в списке видно каждый из 36 (в том числе 20-pdo_mysql.ini, 20-mbstring.ini, 20-intl.ini). 8.3 — стенд; подставьте свою версию (php -v).
Частые ошибки
Loaded Configuration File: (none)— php.ini не найден, PHP работает на встроенных значениях, и правки настроек ни на что не влияют. И помните: это вывод CLI, у FPM свой файл/etc/php/8.3/fpm/php.ini.
Запомните путь /etc/php/8.3/cli/php.ini — и обратите внимание, что у CLI и у FPM свои php.ini: /etc/php/8.3/cli/ и /etc/php/8.3/fpm/. В lesson.linux.06 вы включили display_errors в обоих — поэтому ошибки видно и в браузере, и в консоли:
/etc/php/8.3/cli/php.ini:518:display_errors = On/etc/php/8.3/fpm/php.ini:518:display_errors = On
Разбор команды
Показ ошибок должен быть включён в обоих конфигах — и для консоли, и для FPM; иначе ошибка в браузере выглядит пустой страницей.
grep -n '^display_errors' /etc/php/8.3/cli/php.ini /etc/php/8.3/fpm/php.ini| Токен | Зачем он и что меняет |
|---|---|
grep | поиск строк по шаблону. |
-n | печатать номер строки — по нему удобно открывать файл в редакторе. |
'^display_errors' | шаблон в одинарных кавычках: ^ означает начало строки, поэтому закомментированные ;display_errors в выдачу не попадают. |
/etc/php/8.3/cli/php.ini | первый файл — конфиг CLI. |
/etc/php/8.3/fpm/php.ini | второй файл — конфиг FPM; он отвечает за ошибки в браузере. |
Что произойдёт
Две строки: в обоих файлах на строке 518 стоит display_errors = On.
На что смотреть
Когда файлов несколько, grep сам подставляет в начало строки имя файла — по нему видно, что включено и там, и там. 8.3 в путях — стенд; подставьте свою версию (php -v).
Частые ошибки
- Пустой вывод — настройка закомментирована или версия в пути чужая. После правки fpm/php.ini нужен
sudo systemctl restart php8.3-fpm, иначе изменения не применятся.display_errors = On— режим разработки: на бою его выключают, а у Laravel показ ошибок регулируется ещё иAPP_DEBUGв.env.
На боевом сервере эта настройка показывает посторонним пути на диске и куски конфигурации. У Laravel показ ошибок регулируется отдельно — APP_DEBUG в .env (доберёмся до него в этой же части), и на бою выключаются оба. В части 6 — подробно.
8.3 и php8.3 в командах выше — версия стенда. Проверьте свою: php -v. Если она другая — замените 8.3 на свою во всех путях (/etc/php/…, /run/php/…), именах пакетов (php8.3-*) и сервисе (php8.3-fpm).
Локальная доменная зона .rs
В lesson.linux.05 вы настроили dnsmasq: любое имя зоны .rs резолвится в 127.0.0.1 — на ваш же компьютер. Весь настройка — файл из пяти строк:
# локальная доменная зона .rs -> всё на этот компьютерaddress=/rs/127.0.0.1no-resolvserver=1.1.1.1server=8.8.8.8
Разбор команды
Локальную зону .rs настроили в lesson.linux.05: показываем файл, из которого dnsmasq узнаёт, что все её имена ведут на этот компьютер.
cat /etc/dnsmasq.d/rs.conf| Токен | Зачем он и что меняет |
|---|---|
cat | напечатать содержимое файла. |
/etc/dnsmasq.d/rs.conf | файл в каталоге drop-in: dnsmasq читает все *.conf из /etc/dnsmasq.d/, поэтому основной конфиг не правят. |
Что произойдёт
Пять строк: комментарий, address=/rs/127.0.0.1, no-resolv и два server= — 1.1.1.1 и 8.8.8.8.
На что смотреть
address=/rs/127.0.0.1 — любое имя зоны .rs резолвится в loopback, то есть на этот же компьютер. no-resolv запрещает брать серверы из /etc/resolv.conf, а server= задаёт их явно: всё, что не .rs, уходит на 1.1.1.1 и 8.8.8.8.
Частые ошибки
- Правка файла ничего не меняет, пока сервис не перезапущен:
sudo systemctl restart dnsmasq. Ошибка в синтаксисе зоны (например, пропущенные слэши вaddress=/rs/…) молча оставляет имя без ответа.
Домен будущего приложения — project-laravel.rs: приложение про пользователей, зона локальная, домен бесплатно «выдаётся» dnsmasq. Сайта за ним пока нет, но зона уже отвечает:
127.0.0.1 project-laravel.rsPING project-laravel.rs (127.0.0.1) 56(84) bytes of data.64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.038 ms64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.054 ms--- project-laravel.rs ping statistics ---2 packets transmitted, 2 received, 0% packet loss, time 1010msrtt min/avg/max/mdev = 0.038/0.046/0.054/0.008 ms
Разбор команды
Домен будущего приложения уже резолвится — проверяем это двумя командами: через системный резолвер и через пинг.
getent hosts project-laravel.rs| Токен | Зачем он и что меняет |
|---|---|
getent | спрашивает системные базы данных через NSS — тем же путём, которым имя резолвит любое приложение. |
hosts | какую базу спрашивать: соответствие «имя → адрес» (/etc/hosts и DNS). |
project-laravel.rs | имя будущего приложения в локальной зоне .rs. |
Что произойдёт
Одна строка: 127.0.0.1 project-laravel.rs.
На что смотреть
getent показывает, каким адрес увидят программы на этой машине, а не только браузер: строка есть — значит nginx, curl и PHP разрешат имя так же.
Частые ошибки
- Пустой вывод — dnsmasq не запущен или системный резолвер не указывает на 127.0.0.1;
getentпри ненайденном имени завершается с кодом 2.
ping -c 2 -W 2 project-laravel.rs| Токен | Зачем он и что меняет |
|---|---|
ping | проверяет доступность адреса ICMP-запросами; заодно сам резолвит имя. |
-c 2 | count: отправить два пакета и завершиться — без флага ping работает бесконечно. |
-W 2 | таймаут ожидания ответа на пакет, в секундах. |
project-laravel.rs | то же имя, что и у getent. |
Что произойдёт
Строка PING project-laravel.rs (127.0.0.1) 56(84) bytes of data., два ответа 64 bytes from localhost (127.0.0.1), статистика 2 packets transmitted, 2 received, 0% packet loss, time 1010ms и rtt min/avg/max/mdev = 0.038/0.046/0.054/0.008 ms.
На что смотреть
В скобках после имени — адрес, в который оно преобразовано: 127.0.0.1, то есть этот же компьютер. 0% packet loss — зона отвечает.
Частые ошибки
- ping проверяет сеть, а не сайт:
0% packet lossпри ненастроенном nginx — норма, веб-сервер настраиваем в части 2. Если ICMP запрещён, ответов не будет, но DNS может работать — тогда верьте строкеgetent.
project-laravel.rs → 127.0.0.1, пинг проходит. В части 2 настроим на этот домен nginx — и откроем сайт в браузере.
MariaDB на месте
mysql Ver 15.1 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapperРазбор команды
MariaDB стоит с lesson.linux.04 — весь блок «Базы данных» пройден на ней. Проверяем, что клиент на месте.
mysql --version| Токен | Зачем он и что меняет |
|---|---|
mysql | клиент командной строки; в MariaDB он называется так же, как в MySQL. |
--version | напечатать версию и выйти: подключения к серверу не происходит. |
Что произойдёт
mysql Ver 15.1 Distrib 10.11.14-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper.
На что смотреть
Два числа: 15.1 — версия клиента MariaDB, 10.11.14-MariaDB — версия дистрибутива. Это версия клиента; версию сервера показывает только подключение к нему.
Частые ошибки
- Команда работает и при остановленном сервере — «клиент есть» не значит «база запущена», это проверяет
systemctl is-active mariadb. Вход от root по TCP на Ubuntu заканчиваетсяERROR 1698 (28000): Access denied for user 'root'@'localhost': у root аутентификация через unix-сокет, поэтому приложению в части 4 заводят своего пользователя со своей базой.
MariaDB 10.11 — установлена в lesson.linux.04, весь блок «Базы данных» вы проработали на ней. Сам Laravel по умолчанию обходится без неё: каркас заводится с файлом SQLite (увидите при установке и в .env). Но в части 4 мы переведём приложение на MariaDB — будет та же боевая схема, что в курсе.
На Ubuntu root у MariaDB входит через unix-сокет, а не по паролю через TCP — приложение так подключиться не сможет, да и не должно: у приложения будет свой пользователь с правами только на свою базу. Тот же принцип минимума прав, что и в блоке «SQL-инъекции», только на уровне всей БД.
Composer — пакетный менеджер PHP
Composer ставит в проект PHP-пакеты по имени, сам разруливает их зависимости, фиксирует версии и подключает автозагрузку классов. Ничего нового по сути: то же самое вы делаете apt для системных программ с lesson.linux.04. «Поставь nginx» — apt находит пакет в репозитории, скачивает вместе с зависимостями, устанавливает. «Поставь библиотеку для дат» — Composer делает ровно это, только для PHP-кода.
Он уже стоит на машине:
Composer version 2.7.1 2024-02-09 15:26:28Разбор команды
Composer — пакетный менеджер PHP, и он уже стоит на машине: все установки в этой части сделаны именно им.
composer --version| Токен | Зачем он и что меняет |
|---|---|
composer | исполняемый файл пакетного менеджера; ставится из apt и работает без sudo, потому что пишет внутрь проекта. |
--version | напечатать версию и дату её сборки, затем выйти. |
Что произойдёт
Composer version 2.7.1 2024-02-09 15:26:28.
На что смотреть
2.7.1 — версия стенда, дата справа — дата релиза. Ту же версию позже покажет php artisan about в блоке Environment, так что её можно сверить.
Частые ошибки
composer: command not found— пакет не установлен или не в PATH.sudo composer …внутри проекта оставит вvendor/файлы, принадлежащие root, и следующие команды от вашего пользователя упрутся в права.
Версия 2.7.1 — в выводе даже дата её сборки. Всё, что дальше происходит в терминалах урока, сделано именно ею.
Composer vs apt: идея та же, масштаб другой
| Вопрос | apt (lesson.linux.04) | Composer |
|---|---|---|
| Что ставит | системные программы и библиотеки | PHP-пакеты: библиотеки и каркасы проектов |
| Откуда | репозитории Ubuntu | Packagist — репозиторий PHP-пакетов |
| Куда ставит | системные каталоги (/usr, /etc) |
папку vendor/ внутри проекта |
| Что запоминает | базу пакетов системы (dpkg) | composer.json + composer.lock проекта |
| Кто имеет право | root (sudo) |
любой пользователь, sudo не нужен |
| Один на всех? | да, пакет один на систему | нет: у каждого проекта свои vendor/ и версии |
Последняя строка — главное отличие. nginx на компьютере один. А библиотека Carbon может быть версии 3 в одном проекте и версии 2 в соседнем — и проекты не мешают друг другу: у каждого своя папка vendor/. Поэтому Composer и не ставит «в систему» — он ставит «в эту папку».
И заметьте: в захватах выше ни одна команда composer не потребовала sudo — файлы в проекте принадлежат тому же student, от которого он запущен.
Эксперимент: папка и один скрипт
Не будем верить документации на слово — посмотрим на Composer вживую на эксперименте из пяти шагов. Создаём папку рядом с будущими проектами:
/home/student/recoursia/composer-demototal 12drwxrwxrwx 2 student student 4096 Oct 17 10:19 .drwxrwxrwx 3 student student 4096 Oct 17 10:19 ..-rw-rw-rw- 1 student student 220 Oct 17 10:19 now.php
Разбор команды
Эксперимент с Composer начинаем в отдельной папке рядом с проектами курса — чтобы не смешивать его с установкой Laravel.
mkdir -p ~/recoursia/composer-demo| Токен | Зачем он и что меняет |
|---|---|
mkdir | создать каталог. |
-p | создавать недостающие родительские каталоги и не считать ошибкой уже существующий — команду можно запускать повторно. |
~/recoursia/composer-demo | ~ shell раскрывает в домашний каталог /home/student; папка создаётся рядом с будущими проектами. |
&& | следующая команда выполнится, только если эта завершилась с кодом 0. |
Что произойдёт
Каталог создан (или уже существовал). Цепочка && гарантирует, что в него зайдут только при успехе.
На что смотреть
Никакого sudo: папка внутри /home/student, владельцем остаётся student — как и у всех файлов Composer дальше.
Частые ошибки
- Без
-pповторный запуск дастmkdir: cannot create directory …: File exists; если заменить&&на;, shell продолжит работу даже после ошибки.
cd ~/recoursia/composer-demo| Токен | Зачем он и что меняет |
|---|---|
cd | сменить текущий каталог. |
~/recoursia/composer-demo | тот же путь, что и у mkdir, — папка эксперимента. |
Что произойдёт
Все следующие команды выполняются внутри composer-demo.
На что смотреть
Composer всегда работает с текущим каталогом: где вы стоите, там и появятся composer.json, composer.lock и vendor/.
Частые ошибки
cdдействует только в этом окне терминала: в новой вкладке вы снова в домашнем каталоге.
pwd| Токен | Зачем он и что меняет |
|---|---|
pwd | напечатать текущий каталог — проверка, что cd сработал. |
Что произойдёт
Строка /home/student/recoursia/composer-demo.
На что смотреть
Привычка из блока Linux: перед командой, которая что-то создаёт, убедиться, что вы стоите там, где думаете.
Частые ошибки
- Без
pwdлегко создатьvendor/в соседнем проекте — Composer подтверждений не спрашивает.
ls -la| Токен | Зачем он и что меняет |
|---|---|
ls | список файлов: проверяем, что в новой папке. |
-la | два флага слитно: -l — длинный формат (права, владелец, размер, дата), -a — показывать скрытые записи, начинающиеся с точки. |
Что произойдёт
total 12, записи . и .. и один файл — now.php, 220 байт, владелец student.
На что смотреть
Папка почти пустая: в ней только заранее написанный now.php, а composer.json ещё не создан — его Composer заведёт сам на следующем шаге. Никакого sudo: владелец всех записей — student.
Частые ошибки
- Если папка не пустая и
composer.jsonв ней уже есть, следующийcomposer requireобновит его, а не создаст.lsбез-aскрывает dot-файлы: в корне Laravel-проекта так не увидеть ни.env, ни.gitignore.
Папка почти пустая: только скрипт now.php (220 байт), который мы написали в редакторе заранее — доберёмся до него после установки пакета. Обратите внимание: composer.json здесь ещё нет.
composer require: один пакет превращается в девять
Carbon — библиотека для работы с датами: «через неделю», «3 дня назад», локализация. В самом уроке она нам не понадобится — важна механика. Из папки проекта, без sudo, одна команда:
./composer.json has been createdRunning composer update nesbot/carbonLoading composer repositories with package informationUpdating dependenciesLock file operations: 9 installs, 0 updates, 0 removals - Locking carbonphp/carbon-doctrine-types (3.2.1) - Locking nesbot/carbon (3.14.2) - Locking psr/clock (1.0.0) - Locking symfony/clock (v7.4.21) - Locking symfony/deprecation-contracts (v3.7.1) - Locking symfony/polyfill-mbstring (v1.43.0) - Locking symfony/polyfill-php83 (v1.43.0) - Locking symfony/translation (v7.4.17) - Locking symfony/translation-contracts (v3.7.1)Writing lock fileInstalling dependencies from lock file (including require-dev)Package operations: 9 installs, 0 updates, 0 removals - Downloading symfony/translation-contracts (v3.7.1) - Downloading symfony/polyfill-mbstring (v1.43.0) - Downloading symfony/deprecation-contracts (v3.7.1) - Downloading symfony/translation (v7.4.17) - Downloading symfony/polyfill-php83 (v1.43.0) - Downloading psr/clock (1.0.0) - Downloading symfony/clock (v7.4.21) - Downloading carbonphp/carbon-doctrine-types (3.2.1) - Downloading nesbot/carbon (3.14.2) 0/9 [>---------------------------] 0% 1/9 [===>------------------------] 11% 5/9 [===============>------------] 55% 7/9 [=====================>------] 77% 8/9 [========================>---] 88% 9/9 [============================] 100% - Installing symfony/translation-contracts (v3.7.1): Extracting archive - Installing symfony/polyfill-mbstring (v1.43.0): Extracting archive - Installing symfony/deprecation-contracts (v3.7.1): Extracting archive - Installing symfony/translation (v7.4.17): Extracting archive - Installing symfony/polyfill-php83 (v1.43.0): Extracting archive - Installing psr/clock (1.0.0): Extracting archive - Installing symfony/clock (v7.4.21): Extracting archive - Installing carbonphp/carbon-doctrine-types (3.2.1): Extracting archive - Installing nesbot/carbon (3.14.2): Extracting archive 0/9 [>---------------------------] 0% 8/9 [========================>---] 88% 9/9 [============================] 100%Generating autoload files8 packages you are using are looking for funding.Use the `composer fund` command to find out more!No security vulnerability advisories found.Using version ^3.14 for nesbot/carbon
Разбор команды
Carbon — библиотека для работы с датами. В самом уроке она не понадобится: важна механика — что Composer делает сам, когда у него просят один пакет.
composer require nesbot/carbon| Токен | Зачем он и что меняет |
|---|---|
composer | пакетный менеджер PHP; запущен от student и без sudo — всё пишется в текущую папку. |
require | субкоманда «добавить зависимость и установить её»; если composer.json ещё нет, Composer создаёт его сам. |
nesbot/carbon | имя пакета в формате издатель/имя — по нему пакеты ищутся в Packagist. |
Что произойдёт
За 5,2 с Composer создал composer.json, построил дерево зависимостей (на один запрошенный пакет — девять), зафиксировал их в composer.lock, скачал и распаковал в vendor/ и сгенерировал автозагрузчик. Вывод — 49 строк.
На что смотреть
Строки-вехи по порядку: ./composer.json has been created (1) → Lock file operations: 9 installs, 0 updates, 0 removals (5) → девять строк - Locking … с точными версиями, среди них - Locking nesbot/carbon (3.14.2) (7) → Writing lock file (15) → Package operations: 9 installs, 0 updates, 0 removals (17) → девять строк - Downloading … и прогресс 0/9 … 9/9 [============================] 100% → девять строк - Installing …: Extracting archive → Generating autoload files (45) → No security vulnerability advisories found. (48) → Using version ^3.14 for nesbot/carbon (49): версию мы не указывали, Composer выбрал 3.14.2 и записал в json диапазон ^3.14.
Частые ошибки
- Повторный запуск той же команды ничего не поставит — Composer ответит, что в lock-файле и в
vendor/менять нечего; это не ошибка. Опечатка в имени пакета — Composer не найдёт его в Packagist. Запуск не из папки проекта создастcomposer.jsonтам, где вы стоите.sudo composer requireоставит у файлов вvendor/владельца root.
Разбираем вывод построчно:
./composer.json has been created— composer.json не существовало, и Composer создал его сам: файл проекта «появился» из пустой папки.Lock file operations: 9 installs— просили один пакет, а установится девять: сам Carbon 3.14.2 и его зависимости —symfony/translation,polyfill-mbstring,psr/clockи другие. Дерево зависимостей построил Composer — наша задача была назвать один пакет.Generating autoload files— Composer генерирует автозагрузчик классов; через два слайда увидим его в деле.No security vulnerability advisories found.— установленные версии сверены с базой известных уязвимостей.- Последняя строка —
Using version ^3.14 for nesbot/carbon: версию мы не указывали, Composer выбрал актуальную и записал в composer.json как^3.14— «третья мажорная, не ниже 14, но не четвёртая».
Вся установка заняла около пяти секунд — счётчик миллисекунд виден в шапке окна терминала.
Что появилось на диске: json, lock, vendor
total 48drwxrwxrwx 3 student student 4096 Oct 17 10:19 .drwxrwxrwx 3 student student 4096 Oct 17 10:19 ..-rw-rw-rw- 1 student student 60 Oct 17 10:19 composer.json-rw-rw-rw- 1 student student 26314 Oct 17 10:19 composer.lock-rw-rw-rw- 1 student student 220 Oct 17 10:19 now.phpdrwxrwxrwx 8 student student 4096 Oct 17 10:19 vendor{ "require": { "nesbot/carbon": "^3.14" }}autoload.phpbincarbonphpcomposernesbotpsrsymfony7.2M vendor
Разбор команды
Смотрим, что осталось на диске после установки. Три вещи — json, lock и vendor/ — пойдут с нами дальше, в Laravel-проект.
ls -la| Токен | Зачем он и что меняет |
|---|---|
ls | список файлов. |
-la | два флага слитно: -l — длинный формат, по нему видны размеры созданных файлов, -a — показывать скрытые. |
Что произойдёт
total 48: composer.json (60 байт), composer.lock (26 314 байт), прежний now.php (220 байт) и каталог vendor.
На что смотреть
Сопоставьте размеры: json — «что мы хотим» — 60 байт, lock — «что реально стоит» — 26 314 байт точных версий. Все записи принадлежат student: Composer работал без sudo. Между командами в строке стоит пустой echo — он просто печатает пустую строку, чтобы блоки вывода не слипались.
Частые ошибки
- Отсутствие
composer.lockозначает, что установка не закончилась: по одномуcomposer.jsonточные версии пакетов не восстановить.
cat composer.json| Токен | Зачем он и что меняет |
|---|---|
cat | напечатать файл. |
composer.json | манифест проекта; зависимость в него дописал сам Composer. |
Что произойдёт
JSON из одной секции require с единственным пакетом: "nesbot/carbon": "^3.14".
На что смотреть
^3.14 — диапазон «любая 3.x не ниже 3.14»: Composer выбрал версию сам. Точная версия каждого из пакетов зафиксирована только в composer.lock (26 314 байт).
Частые ошибки
- Ручная правка
composer.jsonпакет не ставит: после неё нуженcomposer updateилиcomposer install, иначе json и содержимоеvendor/разъедутся.
ls vendor| Токен | Зачем он и что меняет |
|---|---|
ls | список файлов. |
vendor | каталог с кодом установленных пакетов. |
Что произойдёт
Семь имён: autoload.php, bin, carbonphp, composer, nesbot, psr, symfony.
На что смотреть
Каталоги названы по издателям из имён пакетов: nesbot/carbon лежит в vendor/nesbot/. autoload.php — файл, который подключает скрипт; composer/ — служебные карты автозагрузки; bin/ — исполняемые файлы пакетов.
Частые ошибки
- В
vendor/не правят код: изменения затрёт следующая установка. В git каталог не коммитят — он восстанавливается изcomposer.lockкомандойcomposer install.
du -sh vendor| Токен | Зачем он и что меняет |
|---|---|
du | считать место, занятое на диске. |
-sh | два флага слитно: -s — только итог по аргументу, без перечисления подкаталогов, -h — человекочитаемые единицы (K, M, G). |
vendor | каталог с кодом установленных пакетов. |
Что произойдёт
7.2M — столько весит один пакет вместе с восемью зависимостями.
На что смотреть
Сравните с 60 байтами composer.json: чужой код тяжёлый, свой — нет. В проекте Laravel весь проект займёт 76 МБ, и почти всё это будет vendor/.
Частые ошибки
duмеряет занятые блоки, а не байты, поэтому его число чуть больше суммы размеров изls -l.
Три новых вещи — и все три пойдут с нами дальше по уроку:
- composer.json (60 байт) — что мы хотим. Пока одна зависимость:
"nesbot/carbon": "^3.14"— та самая строка, которую Composer дописал сам. - composer.lock (26 314 байт) — что реально стоит: точные версии всех девяти пакетов.
- vendor/ (7.2 МБ) — код пакетов и
autoload.php. Внутри по именам издателей:nesbot/,symfony/,psr/,carbonphp/, и служебная папкаcomposer/с самим автозагрузчиком.
Зачем lock отдельным файлом, если есть json? ^3.14 — это диапазон: через месяц выйдет Carbon 3.15, и свежая установка возьмёт его, хотя вы тестировали 3.14.2. Lock-файл гарантирует: коллега, тестовый сервер и боевой хостинг получат ровно те же версии, что и вы. Отсюда правило, которое действует и в Laravel-проекте: composer.json и composer.lock коммитят в git вместе, а vendor/ — нет: он однозначно восстанавливается из lock одной командой composer install.
Автозагрузка: ни одного require для классов
Тот самый now.php, который мы положили в папку заранее:
<?phprequire __DIR__ . '/vendor/autoload.php';use Carbon\Carbon;echo 'Сейчас: ' . Carbon::now() . PHP_EOL;echo 'Через неделю: ' . Carbon::now()->addWeek()->locale('ru')->diffForHumans() . PHP_EOL;
Разбор команды
Показываем скрипт, ради которого ставили пакет: он работает с классом Carbon и не подключает ни одного файла с этим классом.
cat now.php| Токен | Зачем он и что меняет |
|---|---|
cat | напечатать содержимое файла. |
now.php | скрипт, написанный в редакторе до установки пакета; 220 байт из вывода ls -la. |
Что произойдёт
Семь строк PHP: подключение автозагрузчика, use Carbon\Carbon; и два echo — с Carbon::now() и с Carbon::now()->addWeek()->locale('ru')->diffForHumans().
На что смотреть
require ровно один — __DIR__ . '/vendor/autoload.php': __DIR__ даёт каталог самого скрипта, поэтому путь не зависит от того, откуда его запустили. Строка use Carbon\Carbon; — короткое имя пространства имён, а не подключение файла.
Частые ошибки
- Без строки с
autoload.phpPHP ответит фатальной ошибкой о ненайденном классеCarbon\Carbon: автозагрузчик не зарегистрирован, и имя класса некому превратить в путь к файлу.
Запускаем:
Сейчас: 2026-10-17 10:19:40Через неделю: через 6 дней
Разбор команды
Запускаем скрипт — проверяем, что автозагрузка классов действительно работает, а не просто лежит файлом в vendor/.
php now.php| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI: выполняет файл, путь к которому передан аргументом. |
now.php | скрипт в текущей папке; относительный путь работает, потому что мы стоим в composer-demo. |
Что произойдёт
Две строки: Сейчас: 2026-10-17 10:19:40 и Через неделю: через 6 дней.
На что смотреть
Класс Carbon доступен без единого require файла класса: автозагрузчик подключил его по правилу PSR-4 в момент обращения. «через 6 дней» вместо «через неделю» — признак живого прогона: diffForHumans() считает разницу в момент вызова.
Частые ошибки
- Запуск вне папки (
php composer-demo/now.php) тоже сработает — благодаря__DIR__. А вот запуск с опечаткой в имени дастCould not open input file: now.php, а удалённыйvendor/— ошибку про ненайденныйautoload.php.
Смотрите внимательно: скрипт работает с классом Carbon, но не подключает ни одного файла с ним. require ровно один — vendor/autoload.php. Строка use Carbon\Carbon; — тоже не подключение файла: это короткое имя, чтобы не писать полное пространство имён каждый раз.
Как это работает: Composer знает из своих файлов, какой класс в каком пакете лежит, и autoload.php действует по правилу PSR-4: пространство имён отображается в путь на диске. Код обращается к Carbon\Carbon — автозагрузчик переводит имя в путь, находит файл класса внутри vendor/ и подключает его в момент обращения, а не заранее.
Было в курсе
Стало
В проекте Laravel та же PSR-4 привяжет App\ к папке app/ — увидим это в composer.json через несколько слайдов.
Мелочь, выдающая живой прогон: последняя строка напечатала «через 6 дней», а не «через неделю» — diffForHumans() считает разницу в момент вызова, а к этому моменту от «недели» уже отвалились миллисекунды.
Установка Laravel: одна команда
Эксперимент закончен, возвращаемся в ~/recoursia. Вся установка приложения — одна команда: «возьми каркас laravel/laravel и создай из него проект в папке project-laravel»:
Creating a "laravel/laravel" project at "./project-laravel"Installing laravel/laravel (v13.11.0) - Downloading laravel/laravel (v13.11.0) - Installing laravel/laravel (v13.11.0): Extracting archiveCreated project in /home/student/recoursia/project-laravel> @php -r "file_exists('.env') || copy('.env.example', '.env');"Loading composer repositories with package informationUpdating dependenciesLock file operations: 110 installs, 0 updates, 0 removals - Locking brick/math (1.0.0) - Locking carbonphp/carbon-doctrine-types (3.2.1) - Locking dflydev/dot-access-data (v3.0.3) - Locking doctrine/inflector (2.1.0) - Locking doctrine/lexer (3.0.3) - Locking dragonmantank/cron-expression (v3.6.0) - Locking egulias/email-validator (4.0.4) - Locking fakerphp/faker (v1.24.1) - Locking filp/whoops (2.18.6) - Locking fruitcake/php-cors (v1.4.0) - Locking graham-campbell/result-type (v1.2.0) - Locking guzzlehttp/guzzle (8.2.0) - Locking guzzlehttp/promises (3.0.2) - Locking guzzlehttp/psr7 (3.1.0) - Locking guzzlehttp/uri-template (v2.0.1) - Locking hamcrest/hamcrest-php (v3.0.0) - Locking laravel/agent-detector (v2.0.4) - Locking laravel/framework (v13.37.0) - Locking laravel/pail (v1.2.7) - Locking laravel/pao (v1.1.5) - Locking laravel/pint (v1.32.1) - Locking laravel/prompts (v0.3.26) - Locking laravel/serializable-closure (v2.1.0) - Locking laravel/tinker (v3.0.2) - Locking league/commonmark (2.10.3) - Locking league/config (v1.2.0) - Locking league/flysystem (3.36.0) - Locking league/flysystem-local (3.35.3) - Locking league/mime-type-detection (1.17.0) - Locking league/uri (7.9.0) - Locking league/uri-interfaces (7.9.0) - Locking mockery/mockery (1.6.15) - Locking monolog/monolog (3.12.1) - Locking myclabs/deep-copy (1.14.0) - Locking nesbot/carbon (3.14.2) - Locking nette/schema (v1.3.6) - Locking nette/utils (v4.1.5) - Locking nikic/php-parser (v5.9.0) - Locking nunomaduro/collision (v8.9.5) - Locking nunomaduro/termwind (v2.4.0) - Locking phar-io/manifest (2.0.4) - Locking phar-io/version (3.2.1) - Locking phpoption/phpoption (1.10.0) - Locking phpunit/php-code-coverage (12.5.8) - Locking phpunit/php-file-iterator (6.0.2) - Locking phpunit/php-invoker (6.0.0) - Locking phpunit/php-text-template (5.0.1) - Locking phpunit/php-timer (8.0.0) - Locking phpunit/phpunit (12.5.40) - Locking psr/clock (1.0.0) - Locking psr/container (2.0.2) - Locking psr/event-dispatcher (1.0.0) - Locking psr/http-client (1.0.3) - Locking psr/http-factory (1.1.0) - Locking psr/http-message (2.0) - Locking psr/log (3.0.2) - Locking psr/simple-cache (3.0.0) - Locking psy/psysh (v0.12.24) - Locking ramsey/collection (2.1.1) - Locking ramsey/uuid (4.9.4) - Locking sebastian/cli-parser (4.2.1) - Locking sebastian/comparator (7.1.8) - Locking sebastian/complexity (5.0.0) - Locking sebastian/diff (7.0.1) - Locking sebastian/environment (8.1.2) - Locking sebastian/exporter (7.0.3) - Locking sebastian/global-state (8.0.3) - Locking sebastian/lines-of-code (4.0.1) - Locking sebastian/object-enumerator (7.0.0) - Locking sebastian/object-reflector (5.0.0) - Locking sebastian/recursion-context (7.0.1) - Locking sebastian/type (6.0.4) - Locking sebastian/version (6.0.1) - Locking staabm/side-effects-detector (1.0.5) - Locking symfony/clock (v7.4.21) - Locking symfony/console (v7.4.21) - Locking symfony/css-selector (v7.4.18) - Locking symfony/deprecation-contracts (v3.7.1) - Locking symfony/error-handler (v7.4.20) - Locking symfony/event-dispatcher (v7.4.17) - Locking symfony/event-dispatcher-contracts (v3.7.1) - Locking symfony/finder (v7.4.20) - Locking symfony/http-foundation (v7.4.21) - Locking symfony/http-kernel (v7.4.21) - Locking symfony/mailer (v7.4.19) - Locking symfony/mime (v7.4.19) - Locking symfony/polyfill-ctype (v1.37.0) - Locking symfony/polyfill-intl-grapheme (v1.43.0) - Locking symfony/polyfill-intl-idn (v1.43.0) - Locking symfony/polyfill-intl-normalizer (v1.43.0) - Locking symfony/polyfill-mbstring (v1.43.0) - Locking symfony/polyfill-php80 (v1.43.0) - Locking symfony/polyfill-php82 (v1.43.0) - Locking symfony/polyfill-php83 (v1.43.0) - Locking symfony/polyfill-php84 (v1.43.0) - Locking symfony/polyfill-php85 (v1.43.0) - Locking symfony/polyfill-php86 (v1.43.0) - Locking symfony/polyfill-uuid (v1.43.0) - Locking symfony/process (v7.4.21) - Locking symfony/routing (v7.4.21) - Locking symfony/service-contracts (v3.7.3) - Locking symfony/string (v7.4.21) - Locking symfony/translation (v7.4.17) - Locking symfony/translation-contracts (v3.7.1) - Locking symfony/uid (v7.4.21) - Locking symfony/var-dumper (v7.4.21) - Locking theseer/tokenizer (2.0.1) - Locking tijsverkoyen/css-to-inline-styles (v2.4.0) - Locking vlucas/phpdotenv (v5.7.0) - Locking voku/portable-ascii (2.1.1)Writing lock fileInstalling dependencies from lock file (including require-dev)Package operations: 110 installs, 0 updates, 0 removals - Downloading doctrine/inflector (2.1.0) - Downloading doctrine/lexer (3.0.3) - Downloading dragonmantank/cron-expression (v3.6.0) - Downloading psr/container (2.0.2) - Downloading fakerphp/faker (v1.24.1) - Downloading symfony/http-foundation (v7.4.21) - Downloading fruitcake/php-cors (v1.4.0) - Downloading symfony/polyfill-php82 (v1.43.0) - Downloading symfony/polyfill-php80 (v1.43.0) - Downloading psr/http-message (2.0) - Downloading psr/http-factory (1.1.0) - Downloading psr/http-client (1.0.3) - Downloading guzzlehttp/psr7 (3.1.0) - Downloading guzzlehttp/promises (3.0.2) - Downloading guzzlehttp/guzzle (8.2.0) - Downloading guzzlehttp/uri-template (v2.0.1) - Downloading symfony/polyfill-intl-normalizer (v1.43.0) - Downloading symfony/polyfill-intl-grapheme (v1.43.0) - Downloading symfony/polyfill-ctype (v1.37.0) - Downloading symfony/string (v7.4.21) - Downloading symfony/service-contracts (v3.7.3) - Downloading symfony/console (v7.4.21) - Downloading nunomaduro/termwind (v2.4.0) - Downloading voku/portable-ascii (2.1.1) - Downloading phpoption/phpoption (1.10.0) - Downloading graham-campbell/result-type (v1.2.0) - Downloading vlucas/phpdotenv (v5.7.0) - Downloading symfony/css-selector (v7.4.18) - Downloading tijsverkoyen/css-to-inline-styles (v2.4.0) - Downloading symfony/var-dumper (v7.4.21) - Downloading symfony/polyfill-uuid (v1.43.0) - Downloading symfony/uid (v7.4.21) - Downloading symfony/routing (v7.4.21) - Downloading symfony/process (v7.4.21) - Downloading symfony/polyfill-php86 (v1.43.0) - Downloading symfony/polyfill-php85 (v1.43.0) - Downloading symfony/polyfill-php84 (v1.43.0) - Downloading symfony/polyfill-intl-idn (v1.43.0) - Downloading symfony/mime (v7.4.19) - Downloading psr/event-dispatcher (1.0.0) - Downloading symfony/event-dispatcher-contracts (v3.7.1) - Downloading symfony/event-dispatcher (v7.4.17) - Downloading psr/log (3.0.2) - Downloading egulias/email-validator (4.0.4) - Downloading symfony/mailer (v7.4.19) - Downloading symfony/error-handler (v7.4.20) - Downloading symfony/http-kernel (v7.4.21) - Downloading symfony/finder (v7.4.20) - Downloading ramsey/collection (2.1.1) - Downloading brick/math (1.0.0) - Downloading ramsey/uuid (4.9.4) - Downloading psr/simple-cache (3.0.0) - Downloading monolog/monolog (3.12.1) - Downloading league/uri-interfaces (7.9.0) - Downloading league/uri (7.9.0) - Downloading league/mime-type-detection (1.17.0) - Downloading league/flysystem-local (3.35.3) - Downloading league/flysystem (3.36.0) - Downloading nette/utils (v4.1.5) - Downloading nette/schema (v1.3.6) - Downloading dflydev/dot-access-data (v3.0.3) - Downloading league/config (v1.2.0) - Downloading league/commonmark (2.10.3) - Downloading laravel/serializable-closure (v2.1.0) - Downloading laravel/prompts (v0.3.26) - Downloading laravel/framework (v13.37.0) - Downloading laravel/pail (v1.2.7) - Downloading laravel/agent-detector (v2.0.4) - Downloading laravel/pao (v1.1.5) - Downloading laravel/pint (v1.32.1) - Downloading nikic/php-parser (v5.9.0) - Downloading psy/psysh (v0.12.24) - Downloading laravel/tinker (v3.0.2) - Downloading hamcrest/hamcrest-php (v3.0.0) - Downloading mockery/mockery (1.6.15) - Downloading filp/whoops (2.18.6) - Downloading nunomaduro/collision (v8.9.5) - Downloading staabm/side-effects-detector (1.0.5) - Downloading sebastian/version (6.0.1) - Downloading sebastian/type (6.0.4) - Downloading sebastian/recursion-context (7.0.1) - Downloading sebastian/object-reflector (5.0.0) - Downloading sebastian/object-enumerator (7.0.0) - Downloading sebastian/global-state (8.0.3) - Downloading sebastian/exporter (7.0.3) - Downloading sebastian/environment (8.1.2) - Downloading sebastian/diff (7.0.1) - Downloading sebastian/comparator (7.1.8) - Downloading sebastian/cli-parser (4.2.1) - Downloading phpunit/php-timer (8.0.0) - Downloading phpunit/php-text-template (5.0.1) - Downloading phpunit/php-invoker (6.0.0) - Downloading phpunit/php-file-iterator (6.0.2) - Downloading theseer/tokenizer (2.0.1) - Downloading sebastian/lines-of-code (4.0.1) - Downloading sebastian/complexity (5.0.0) - Downloading phpunit/php-code-coverage (12.5.8) - Downloading phar-io/version (3.2.1) - Downloading phar-io/manifest (2.0.4) - Downloading myclabs/deep-copy (1.14.0) - Downloading phpunit/phpunit (12.5.40) 0/101 [>---------------------------] 0% 4/101 [=>--------------------------] 3% 11/101 [===>------------------------] 10% 27/101 [=======>--------------------] 26% 37/101 [==========>-----------------] 36% 41/101 [===========>----------------] 40% 51/101 [==============>-------------] 50% 62/101 [=================>----------] 61% 74/101 [====================>-------] 73% 86/101 [=======================>----] 85% 91/101 [=========================>--] 90% 101/101 [============================] 100% - Installing doctrine/inflector (2.1.0): Extracting archive - Installing doctrine/lexer (3.0.3): Extracting archive - Installing dragonmantank/cron-expression (v3.6.0): Extracting archive - Installing symfony/deprecation-contracts (v3.7.1): Extracting archive - Installing psr/container (2.0.2): Extracting archive - Installing fakerphp/faker (v1.24.1): Extracting archive - Installing symfony/polyfill-mbstring (v1.43.0): Extracting archive - Installing symfony/http-foundation (v7.4.21): Extracting archive - Installing fruitcake/php-cors (v1.4.0): Extracting archive - Installing symfony/polyfill-php82 (v1.43.0): Extracting archive - Installing symfony/polyfill-php80 (v1.43.0): Extracting archive - Installing psr/http-message (2.0): Extracting archive - Installing psr/http-factory (1.1.0): Extracting archive - Installing psr/http-client (1.0.3): Extracting archive - Installing guzzlehttp/psr7 (3.1.0): Extracting archive - Installing guzzlehttp/promises (3.0.2): Extracting archive - Installing guzzlehttp/guzzle (8.2.0): Extracting archive - Installing guzzlehttp/uri-template (v2.0.1): Extracting archive - Installing symfony/polyfill-intl-normalizer (v1.43.0): Extracting archive - Installing symfony/polyfill-intl-grapheme (v1.43.0): Extracting archive - Installing symfony/polyfill-ctype (v1.37.0): Extracting archive - Installing symfony/string (v7.4.21): Extracting archive - Installing symfony/service-contracts (v3.7.3): Extracting archive - Installing symfony/console (v7.4.21): Extracting archive - Installing nunomaduro/termwind (v2.4.0): Extracting archive - Installing voku/portable-ascii (2.1.1): Extracting archive - Installing phpoption/phpoption (1.10.0): Extracting archive - Installing graham-campbell/result-type (v1.2.0): Extracting archive - Installing vlucas/phpdotenv (v5.7.0): Extracting archive - Installing symfony/css-selector (v7.4.18): Extracting archive - Installing tijsverkoyen/css-to-inline-styles (v2.4.0): Extracting archive - Installing symfony/var-dumper (v7.4.21): Extracting archive - Installing symfony/polyfill-uuid (v1.43.0): Extracting archive - Installing symfony/uid (v7.4.21): Extracting archive - Installing symfony/routing (v7.4.21): Extracting archive - Installing symfony/process (v7.4.21): Extracting archive - Installing symfony/polyfill-php86 (v1.43.0): Extracting archive - Installing symfony/polyfill-php85 (v1.43.0): Extracting archive - Installing symfony/polyfill-php84 (v1.43.0): Extracting archive - Installing symfony/polyfill-intl-idn (v1.43.0): Extracting archive - Installing symfony/mime (v7.4.19): Extracting archive - Installing psr/event-dispatcher (1.0.0): Extracting archive - Installing symfony/event-dispatcher-contracts (v3.7.1): Extracting archive - Installing symfony/event-dispatcher (v7.4.17): Extracting archive - Installing psr/log (3.0.2): Extracting archive - Installing egulias/email-validator (4.0.4): Extracting archive - Installing symfony/mailer (v7.4.19): Extracting archive - Installing symfony/error-handler (v7.4.20): Extracting archive - Installing symfony/http-kernel (v7.4.21): Extracting archive - Installing symfony/finder (v7.4.20): Extracting archive - Installing ramsey/collection (2.1.1): Extracting archive - Installing brick/math (1.0.0): Extracting archive - Installing ramsey/uuid (4.9.4): Extracting archive - Installing psr/simple-cache (3.0.0): Extracting archive - Installing symfony/translation-contracts (v3.7.1): Extracting archive - Installing symfony/translation (v7.4.17): Extracting archive - Installing symfony/polyfill-php83 (v1.43.0): Extracting archive - Installing psr/clock (1.0.0): Extracting archive - Installing symfony/clock (v7.4.21): Extracting archive - Installing carbonphp/carbon-doctrine-types (3.2.1): Extracting archive - Installing nesbot/carbon (3.14.2): Extracting archive - Installing monolog/monolog (3.12.1): Extracting archive - Installing league/uri-interfaces (7.9.0): Extracting archive - Installing league/uri (7.9.0): Extracting archive - Installing league/mime-type-detection (1.17.0): Extracting archive - Installing league/flysystem-local (3.35.3): Extracting archive - Installing league/flysystem (3.36.0): Extracting archive - Installing nette/utils (v4.1.5): Extracting archive - Installing nette/schema (v1.3.6): Extracting archive - Installing dflydev/dot-access-data (v3.0.3): Extracting archive - Installing league/config (v1.2.0): Extracting archive - Installing league/commonmark (2.10.3): Extracting archive - Installing laravel/serializable-closure (v2.1.0): Extracting archive - Installing laravel/prompts (v0.3.26): Extracting archive - Installing laravel/framework (v13.37.0): Extracting archive - Installing laravel/pail (v1.2.7): Extracting archive - Installing laravel/agent-detector (v2.0.4): Extracting archive - Installing laravel/pao (v1.1.5): Extracting archive - Installing laravel/pint (v1.32.1): Extracting archive - Installing nikic/php-parser (v5.9.0): Extracting archive - Installing psy/psysh (v0.12.24): Extracting archive - Installing laravel/tinker (v3.0.2): Extracting archive - Installing hamcrest/hamcrest-php (v3.0.0): Extracting archive - Installing mockery/mockery (1.6.15): Extracting archive - Installing filp/whoops (2.18.6): Extracting archive - Installing nunomaduro/collision (v8.9.5): Extracting archive - Installing staabm/side-effects-detector (1.0.5): Extracting archive - Installing sebastian/version (6.0.1): Extracting archive - Installing sebastian/type (6.0.4): Extracting archive - Installing sebastian/recursion-context (7.0.1): Extracting archive - Installing sebastian/object-reflector (5.0.0): Extracting archive - Installing sebastian/object-enumerator (7.0.0): Extracting archive - Installing sebastian/global-state (8.0.3): Extracting archive - Installing sebastian/exporter (7.0.3): Extracting archive - Installing sebastian/environment (8.1.2): Extracting archive - Installing sebastian/diff (7.0.1): Extracting archive - Installing sebastian/comparator (7.1.8): Extracting archive - Installing sebastian/cli-parser (4.2.1): Extracting archive - Installing phpunit/php-timer (8.0.0): Extracting archive - Installing phpunit/php-text-template (5.0.1): Extracting archive - Installing phpunit/php-invoker (6.0.0): Extracting archive - Installing phpunit/php-file-iterator (6.0.2): Extracting archive - Installing theseer/tokenizer (2.0.1): Extracting archive - Installing sebastian/lines-of-code (4.0.1): Extracting archive - Installing sebastian/complexity (5.0.0): Extracting archive - Installing phpunit/php-code-coverage (12.5.8): Extracting archive - Installing phar-io/version (3.2.1): Extracting archive - Installing phar-io/manifest (2.0.4): Extracting archive - Installing myclabs/deep-copy (1.14.0): Extracting archive - Installing phpunit/phpunit (12.5.40): Extracting archive 0/110 [>---------------------------] 0% 24/110 [======>---------------------] 21% 36/110 [=========>------------------] 32% 51/110 [============>---------------] 46% 63/110 [================>-----------] 57% 76/110 [===================>--------] 69% 87/110 [======================>-----] 79% 101/110 [=========================>--] 91% 110/110 [============================] 100%54 package suggestions were added by new dependencies, use `composer suggest` to see details.Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump> @php artisan package:discover --ansi INFO Discovering packages. laravel/pail .......................................................... DONE laravel/pao ........................................................... DONE laravel/tinker ........................................................ DONE nesbot/carbon ......................................................... DONE nunomaduro/collision .................................................. DONE nunomaduro/termwind ................................................... DONE80 packages you are using are looking for funding.Use the `composer fund` command to find out more!> @php artisan vendor:publish --tag=laravel-assets --ansi --force INFO No publishable resources for tag [laravel-assets]. No security vulnerability advisories found.> @php artisan key:generate --ansi INFO Application key set successfully. > @php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"> @php artisan migrate --graceful --ansi INFO Preparing database. Creating migration table ...................................... 25.27ms DONE INFO Running migrations. 0001_01_01_000000_create_users_table .......................... 82.52ms DONE 0001_01_01_000001_create_cache_table .......................... 54.00ms DONE 0001_01_01_000002_create_jobs_table ........................... 78.40ms DONE
Разбор команды
Главная команда урока: «возьми каркас laravel/laravel и создай из него проект в папке project-laravel». Одна команда — и на диске приложение со всеми зависимостями, ключом и базой.
composer create-project laravel/laravel project-laravel| Токен | Зачем он и что меняет |
|---|---|
composer | пакетный менеджер PHP; работает от student, без sudo — всё создаётся внутри ~/recoursia. |
create-project | субкоманда «создать проект из пакета»: скачать пакет, распаковать в новый каталог, поставить его зависимости и выполнить post-скрипты. |
laravel/laravel | имя пакета-каркаса в формате издатель/имя: папки и файлы пустого приложения, у всех одинаковые. Сам фреймворк — отдельный пакет laravel/framework. |
project-laravel | имя каталога, который будет создан; на момент запуска такого каталога быть не должно. |
Что произойдёт
За 19,4 с создано приложение: каркас laravel/laravel v13.11.0 скачан и распакован в /home/student/recoursia/project-laravel, зафиксировано и установлено 110 пакетов (в том числе laravel/framework v13.37.0), сгенерирован автозагрузчик и выполнены скрипты каркаса. Вывод — 391 строка, из них 102 строки - Downloading … и 21 строка прогресса: кэш Composer был пуст, пакеты скачивались заново.
На что смотреть
Строки-вехи по порядку: Creating a "laravel/laravel" project at "./project-laravel" (1) → Installing laravel/laravel (v13.11.0) (2) → Created project in /home/student/recoursia/project-laravel (5) → > @php -r "file_exists('.env') || copy('.env.example', '.env');" (6) → Lock file operations: 110 installs, 0 updates, 0 removals (9) → - Locking laravel/framework (v13.37.0) (27) → Writing lock file (120) → Package operations: 110 installs, 0 updates, 0 removals (122) → - Installing laravel/framework (v13.37.0): Extracting archive (310) → Generating optimized autoload files (356) → INFO Discovering packages. (360) → No security vulnerability advisories found. (375) → INFO Application key set successfully. (378) → создание database/database.sqlite (380) → > @php artisan migrate --graceful --ansi (381) → INFO Preparing database. (383) с Creating migration table … 25.27ms DONE (385) → INFO Running migrations. (387) и три миграции: create_users_table 82.52ms (389), create_cache_table 54.00ms (390), create_jobs_table 78.40ms (391).
Частые ошибки
- Каталог уже существует и не пуст — Composer откажется создавать проект.
sudoне нужен: все файлы принадлежат тому, кто запустил команду. На PHP старее 8.3 установка упрётся в требование каркаса"php": "^8.3". Если скриптkey:generateне отработал, приложение позже ответитNo application encryption key has been specified.— ключ дописывают командойphp artisan key:generate.
Разбор первых строк:
- Laravel ставится двумя слоями.
laravel/laravel(v13.11.0, строка 2) — каркас: папки и файлы пустого приложения, у всех одинаковые.laravel/framework(v13.37.0 — в спискеLocking …, строка 27 вывода, под кнопкой «ещё») — сам фреймворк: роутер, Blade, Eloquent, валидация. Каркас требует в composer.json"laravel/framework": "^13.17", а в lock попала актуальная 13.37.0 — тот же механизм диапазона и фиксации, что с Carbon. Created project in /home/student/recoursia/project-laravel— каркас распакован на диск.- Строкой ниже —
file_exists('.env') || copy('.env.example', '.env'): уже появился файл настроек.env. Это не магия, а скрипт, который Composer запустил автоматически; до скриптов доберёмся на следующем слайде. Lock file operations: 110 installs— зафиксировано 110 пакетов: фреймворк со всеми зависимостями плюс инструменты разработки — PHPUnit для тестов, Pint для стиля кода, faker для тестовых данных.
Команда целиком заняла около 19 секунд — тоже видно в шапке терминала.
Что команда сделала сама
Хвост того же вывода (строки 356+, под кнопкой «ещё») — работа скриптов, прописанных в composer.json каркаса; сами скрипты увидим в разборе composer.json. Приложение не просто распаковали — его подготовили к запуску. Пост-скрипты выполнились в таком порядке:
> @php artisan key:generate --ansi— сгенерировал случайный ключ шифрования и записал его в.envкакAPP_KEY. Что он шифрует и как выглядит проект без ключа — в части 6.> @php artisan migrate --graceful --ansi— применил к этой базе миграции:Creating migration table ... 25.27ms DONE(журнал применённых миграций) и три миграции каркаса —create_users_table(82.52 мс),create_cache_table(54.00 мс),create_jobs_table(78.40 мс): таблицы пользователей, кэша и очередей задач.
В блоке «Базы данных» вы рисовали схему руками: таблицы в PHPMyAdmin, колонки через ALTER. В Laravel схема — это код: файлы-миграции с версиями, которые применяет artisan migrate. Поэтому только что созданное приложение уже имеет базу с таблицами: их никто не рисовал руками — на любой другой машине та же команда воспроизведёт их в точности.
Сколько это весит и почему vendor/ не коммитят
76M .каталогов в vendor: 36You are using the deprecated option "installed". Only installed packages are shown by default now. The --all option can be used to show all packages.пакетов установлено: 110
Разбор команды
Меряем, сколько весит установленный проект и сколько в нём пакетов, — цифры для разговора о том, почему vendor/ не коммитят в git.
du -sh . vendor| Токен | Зачем он и что меняет |
|---|---|
du | считать место, занятое на диске. |
-sh | два флага слитно: -s — итог по каждому аргументу, без подкаталогов, -h — человекочитаемые единицы. |
. | текущий каталог — проект целиком. |
vendor | второй аргумент — каталог с пакетами. |
Что произойдёт
Одна строка: 76M на весь проект. Отдельной строки для vendor нет — du считает каждый файл один раз, а vendor уже обойдён внутри ..
На что смотреть
76 МБ — это почти весь вес vendor/, чужого кода из lock-файла. Свои файлы на этом фоне невесомы: artisan — 425 байт, composer.json — 2 678.
Частые ошибки
- Нужен размер
vendorотдельно — запуститеdu -sh vendorодной командой: в общем вызове он попадёт в already-counted и строки не даст.duмеряет блоки, поэтому его число чуть больше суммы размеров изls -l.
echo "каталогов в vendor: $(find vendor -maxdepth 1 -mindepth 1 -type d | wc -l)"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки: внутри них shell выполняет $(…) и подставляет результат. |
$(…) | подстановка команды — вывод конвейера становится частью строки. |
find vendor | обойти каталог vendor. |
-maxdepth 1 | не спускаться глубже первого уровня. |
-mindepth 1 | не печатать сам vendor — только его детей. |
-type d | отбирать только каталоги, без файлов вроде autoload.php. |
| | пайп: передать список следующей команде. |
wc -l | посчитать строки, то есть количество каталогов. |
Что произойдёт
каталогов в vendor: 36 — столько каталогов верхнего уровня лежит в vendor/.
На что смотреть
36 каталогов против 110 пакетов: пакеты вида symfony/console живут внутри каталога издателя symfony/, поэтому каталогов меньше, чем пакетов.
Частые ошибки
- Без
-mindepth 1в счёт попал бы самvendorи получилось 37. Флаги глубины уfindпишут до выражений (-type,-name) — иначе find предупреждает, что проверяет их не в том порядке.
echo "пакетов установлено: $(composer show -i | wc -l)"| Токен | Зачем он и что меняет |
|---|---|
echo | печатает строку. |
"…" | двойные кавычки для подстановки $(…). |
composer show | показать установленные пакеты — по одному на строку. |
-i | то же, что --installed: флаг устарел, установленные пакеты показываются по умолчанию — отсюда предупреждение в выводе. |
| | пайп: список пакетов уходит в wc -l. |
wc -l | посчитать строки. |
Что произойдёт
пакетов установлено: 110 — плюс ворчание Composer: You are using the deprecated option "installed". Only installed packages are shown by default now.
На что смотреть
110 совпадает со строкой Lock file operations: 110 installs из вывода create-project. Предупреждение про -i уходит в stderr и на число не влияет.
Частые ошибки
- Запуск
composer showвне проекта (безcomposer.jsonиvendor/) считает не те пакеты: число перестанет относиться к вашему приложению.
Числа: 76 МБ весь проект, 36 каталогов верхнего уровня внутри vendor/, 110 установленных пакетов. (Строка You are using the deprecated option "installed" — ворчание Composer на флаг -i у composer show; на числа она не влияет.)
Почему 76 МБ — не страшно: почти весь вес — vendor/, чужой код, установленный из lock-файла. Правило из демо-папки работает здесь один в один: в git живут composer.json и composer.lock, а vendor/ — нет; клонировал репозиторий, запустил composer install — получил те же 110 пакетов тех же версий.
Собственный код на этом фоне невесом: посмотрите на ls из следующего слайда — artisan занимает 425 байт, composer.json — 2 678 байт. Папки app/, routes/, resources/ после установки почти пустые — их мы начнём заполнять со второй части.
Корень проекта: первый осмотр
total 412drwxrwxrwx 12 student student 4096 Oct 17 10:19 .drwxrwxrwx 4 student student 4096 Oct 17 10:19 ..-rw-rw-rw- 1 student student 275 Sep 30 13:56 .editorconfig-rw-rw-rw- 1 student student 1142 Oct 17 10:19 .env-rw-rw-rw- 1 student student 1091 Sep 30 13:56 .env.example-rw-rw-rw- 1 student student 186 Sep 30 13:56 .gitattributes-rw-rw-rw- 1 student student 341 Sep 30 13:56 .gitignore-rw-rw-rw- 1 student student 31 Sep 30 13:56 .npmrc-rw-rw-rw- 1 student student 1446 Sep 30 13:56 AGENTS.md-rw-rw-rw- 1 student student 1446 Sep 30 13:56 CLAUDE.md-rw-rw-rw- 1 student student 3700 Sep 30 13:56 README.mddrwxrwxrwx 5 student student 4096 Sep 30 13:56 app-rwxr-xr-x 1 student student 425 Sep 30 13:56 artisandrwxrwxrwx 3 student student 4096 Sep 30 13:56 bootstrap-rw-rw-rw- 1 student student 2678 Sep 30 13:56 composer.json-rw-rw-rw- 1 student student 313898 Oct 17 10:19 composer.lockdrwxrwxrwx 2 student student 4096 Sep 30 13:56 configdrwxrwxrwx 5 student student 4096 Oct 17 10:19 database-rw-rw-rw- 1 student student 461 Sep 30 13:56 package.json-rw-rw-rw- 1 student student 1322 Sep 30 13:56 phpunit.xmldrwxrwxrwx 2 student student 4096 Sep 30 13:56 publicdrwxrwxrwx 5 student student 4096 Sep 30 13:56 resourcesdrwxrwxrwx 2 student student 4096 Sep 30 13:56 routesdrwxrwxrwx 5 student student 4096 Sep 30 13:56 storagedrwxrwxrwx 4 student student 4096 Sep 30 13:56 testsdrwxrwxrwx 38 student student 4096 Oct 17 10:19 vendor-rw-rw-rw- 1 student student 632 Sep 30 13:56 vite.config.js
Разбор команды
Первый осмотр корня свежеустановленного проекта: что команда установки оставила на диске.
ls -la| Токен | Зачем он и что меняет |
|---|---|
ls | список файлов текущего каталога — мы стоим в корне проекта. |
-la | два флага слитно: -l — длинный формат (права, владелец, размер, дата изменения), -a — показывать скрытые записи: без него не видно ни .env, ни .env.example, ни .gitignore. |
Что произойдёт
total 412 и 25 записей: 10 каталогов (app, bootstrap, config, database, public, resources, routes, storage, tests, vendor) и 15 файлов, включая шесть скрытых.
На что смотреть
Размеры из вывода: .env — 1 142 байта, .env.example — 1 091 (живые настройки и их шаблон); artisan — 425 байт с правами -rwxr-xr-x, то есть исполняемый; composer.json — 2 678 байт против composer.lock — 313 898. Даты делят записи на две группы: Sep 30 13:56 — файлы из архива каркаса, Oct 17 10:19 — то, что создала установка (.env, composer.lock, database/, vendor/).
Частые ошибки
lsбез-a— главная причина «у меня нет .env»: файл на месте, просто скрытый. Число 38 в строкеvendor— счётчик ссылок каталога: 36 подкаталогов плюс.и...
Что важно заметить уже сейчас (детальный обход каждого каталога — часть 2):
.env(1 142 байта) и.env.example(1 091 байт) — настройки и их шаблон; обсудим отдельно ниже..gitignore— список того, что не попадает в git; в проекте Laravel тамvendor/и.env.artisan(425 байт, исполняемый-rwxr-xr-x) — консоль приложения; ей — два слайда в конце части.composer.json(2 678 байт) иcomposer.lock(313 898 байт — точные версии всех 110 пакетов) — знакомые по демо роли: «хотим» и «реально стоит».vendor/— 36 каталогов чужого кода; в git не живёт.- Каталоги
app/,bootstrap/,config/,database/,public/,resources/,routes/,storage/,tests/— каркас MVC; на следующем слайде — что где. package.jsonиvite.config.js— сборщик фронтенда Vite. В этом уроке мы его не включаем: стили положим обычным CSS-файлом вpublic/(часть 3).phpunit.xmlиtests/— тесты; доберёмся в части 6.
Структура: что где лежит
Тот же корень деревом. Команда tree с флагом -I исключила vendor/, node_modules/ и .git/ — они видны в командной строке терминала:
.├── app│ ├── Http│ ├── Models│ └── Providers├── bootstrap│ ├── cache│ ├── app.php│ └── providers.php├── config│ ├── app.php│ ├── auth.php│ ├── cache.php│ ├── database.php│ ├── filesystems.php│ ├── logging.php│ ├── mail.php│ ├── queue.php│ ├── services.php│ └── session.php├── database│ ├── factories│ ├── migrations│ ├── seeders│ ├── .gitignore│ └── database.sqlite├── public│ ├── .htaccess│ ├── favicon.ico│ ├── index.php│ └── robots.txt├── resources│ ├── css│ ├── js│ └── views├── routes│ ├── console.php│ └── web.php├── storage│ ├── app│ ├── framework│ └── logs├── tests│ ├── Feature│ ├── Unit│ └── TestCase.php├── .editorconfig├── .env├── .env.example├── .gitattributes├── .gitignore├── .npmrc├── AGENTS.md├── CLAUDE.md├── README.md├── artisan├── composer.json├── composer.lock├── package.json├── phpunit.xml└── vite.config.js25 directories, 36 files
Разбор команды
Тот же корень — деревом и без vendor/: так видна структура MVC, а не 110 установленных пакетов.
tree -L 2 -a -I 'vendor|node_modules|.git' --dirsfirst| Токен | Зачем он и что меняет |
|---|---|
tree | рекурсивный список в виде дерева; ставится отдельным пакетом, в базовой системе его может не быть. |
-L 2 | ограничить глубину двумя уровнями: корень и его дети; без флага tree обошёл бы все вложенные каталоги. |
-a | включать скрытые записи — .env, .gitignore, database/.gitignore. |
-I 'vendor|node_modules|.git' | исключить каталоги по шаблону. | здесь — «или» внутри шаблона tree, а не пайп shell; кавычки обязательны, иначе shell разорвал бы команду на части. |
--dirsfirst | сначала каталоги, потом файлы — структуру читаете сверху вниз. |
Что произойдёт
63 строки: девять каталогов каркаса с содержимым, файлы корня и итог 25 directories, 36 files.
На что смотреть
Итоговая строка — это «ваш» проект без vendor/. Сверьте с таблицей урока: в config/ ровно 10 файлов, в routes/ — web.php и console.php, в database/ уже лежит database.sqlite, созданный при установке, а public/ содержит index.php — единственную точку входа.
Частые ошибки
tree: command not found— пакет не установлен (sudo apt install tree). Забытый-Iпревращает вывод в простыню изvendor/, за которой структуру не видно.
| Каталог | Что внутри | Когда пригодится |
|---|---|---|
app/ |
наш код: Http/, Models/, Providers/ |
части 3–5: контроллеры, модель User |
bootstrap/ |
запуск приложения: app.php, providers.php, cache/ |
часть 2 — прочитаем один раз |
config/ |
10 файлов конфигурации: app.php, database.php, session.php… |
часть 2; править будем редко — настройки живут в .env |
database/ |
migrations/, seeders/, factories/ + database.sqlite |
часть 4 — миграции и сидеры |
public/ |
index.php — единственная точка входа; favicon.ico, robots.txt |
часть 2 — root в nginx укажет сюда |
resources/ |
css/, js/, views/ — шаблоны страниц |
части 2–5 — все шаблоны |
routes/ |
web.php — таблица маршрутов; console.php |
часть 2 и дальше — почти в каждой части |
storage/ |
logs/, framework/, app/ — что приложение пишет |
часть 6 — логи и права |
tests/ |
Feature/, Unit/, TestCase.php |
часть 6 |
Итоговая строка tree — «25 directories, 36 files» — это без vendor/. В части 2 пройдём по структуре ещё раз, уже читая файлы.
composer.json: зависимости, автозагрузка, скрипты
Знакомый по демо файл — только на 86 строк. Вставляем его прямо из захвата (цифры слева — номера строк); подсвечены: требование к PHP и фреймворку (9–10), правило PSR-4 (24) и скрипты установки (61–64).
Что нужно проекту
"php": "^8.3"(строка 9) — то самое требование из начала части: PHP 8.3+. Именно эта строка не дала бы каркасу установиться на старый PHP."laravel/framework": "^13.17"(строка 10) — фреймворк. Указан диапазон, а в lock попала 13.37.0 — мы видели это в выводеcreate-project. Та же механика, что^3.14у Carbon."laravel/tinker"— консольный REPL внутри приложения: в части 4 будем создавать в нём записи и смотреть SQL, который получается у Eloquent.require-dev— пакеты только для разработки: PHPUnit (тесты, часть 6), faker (случайные данные для сидеров, часть 4), pint (проверка стиля кода), collision и pail (читаемые ошибки и логи в консоли), mockery (подмена объектов в тестах). На боевой сервер они не поедут — в части 6 поставим пакеты с флагом--no-dev.
Автозагрузка и скрипты
"App\\": "app/"(строка 24) — та самая PSR-4 из демо, теперь для нашего кода: классApp\Models\Userбудет лежать вapp/Models/User.php,App\Http\Controllers\UserController— вapp/Http/Controllers/UserController.php. Достаточно создать класс вapp/— ни одногоrequire. ТестыTests\подключаются отдельно, черезautoload-dev.post-create-project-cmd(строки 61–64) — выполняется один раз при создании проекта: сгенерировать ключ → создать файл SQLite → применить миграции. Ровно эти три действия, в этом порядке, мы видели в хвостеcreate-project.post-autoload-dump— обнаружение пакетов после генерации автозагрузчика: строкаINFO Discovering packages.из вывода — его работа.setup— рецепт первого запуска целиком:composer install, копия.env, ключ, миграции, сборка фронтенда через npm.
.env: настройки вне кода
В блоке «Базы данных» подключение к PDO — DSN, логин, пароль — было записано прямо в PHP-файле. Работает, но приваривает настройки и секреты к коду: другая база на боевом сервере — правь исходники. Laravel выносит всё это в файл .env. Он перед вами целиком, из захвата; подсвечены ключ и отладка (3–4), база (23) и сессии (30):
- APP_KEY (строка 3) — тот самый ключ, который
key:generateсоздал при установке. Он шифрует то, что не должны читать посторонние: cookie и данные сессий. Как выглядит приложение без ключа — часть 6. Ключ на слайде — от локального учебного приложения; настоящий.envне показывают никому. - APP_DEBUG=true (строка 4) — подробные ошибки прямо в браузере. В разработке это помощь, на бою — утечка путей и настроек; там ставят
false. Прямой наследникdisplay_errorsиз php.ini: та же философия — в разработке показываем, на бою молчим. - APP_ENV=local (строка 2) — «это машина разработки»; APP_URL — адрес, которым приложение себя считает.
- DB_CONNECTION=sqlite (строка 23) — база по умолчанию, тот самый файл
database/database.sqlite. Строки 24–28 — закомментированные готовые настройки MySQL/MariaDB: хост, порт, база, пользователь, пароль. В части 4 раскомментируем и заполним. - SESSION_DRIVER=database (строка 30) — сессии хранятся в базе (для них, в том числе, бежали миграции при установке), а не в файлах.
В нём настоящие ключи и пароли. В git живёт шаблон .env.example без секретов — мы видели в ls оба файла, 1 142 и 1 091 байт. При разворачивании проекта .env создают из шаблона и заполняют — ровно это и сделал первый скрипт create-project: file_exists('.env') || copy('.env.example', '.env').
artisan — консоль Laravel
У проекта есть собственная точка входа для командной строки — исполняемый файл artisan в корне (425 байт, права -rwxr-xr-x из ls). Идея вам знакома по Linux: systemctl — единая точка управления всеми сервисами; artisan — единая точка для всего, что делается вокруг приложения.
Laravel Framework 13.37.0Разбор команды
У проекта своя точка входа для консоли — файл artisan в корне. Проверяем, что он запускается и какой фреймворк под ним.
php artisan --version| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI — он выполняет файл artisan. |
artisan | скрипт в корне проекта (425 байт, права -rwxr-xr-x): единая точка для всего, что делается вокруг приложения. |
--version | напечатать версию фреймворка и выйти, не выполняя команду приложения. |
Что произойдёт
Laravel Framework 13.37.0.
На что смотреть
Та же версия, что в lock-файле и в выводе create-project: каркас laravel/laravel и фреймворк laravel/framework — разные пакеты, и версия здесь — про фреймворк. php artisan без аргументов печатает список всех доступных команд.
Частые ошибки
Could not open input file: artisan— вы не в корне проекта. Файл исполняемый, поэтому работает и./artisan --version, но вариантphp artisan …не зависит от прав на исполнение.
php artisan --version печатает версию фреймворка — 13.37.0: та же, что в lock-файле и в выводе create-project.
Команды, которые понадобятся в уроке (запоминать не нужно — каждая встретится не раз):
php artisan serve— dev-сервер, чтобы сразу открыть проект в браузере (часть 2);php artisan about— сводка о приложении (следующий слайд);php artisan make:controller,make:migration,make:seeder— генераторы: создают файл по правильному пути и с правильной заготовкой (части 3–5);php artisan migrate— применить миграции к базе (часть 4);php artisan tinker— REPL внутри приложения (часть 4).
php artisan about — приборная панель
Одна команда — и вся правда о приложении на одном экране:
Environment ................................................................ Application Name ................................................... Laravel Laravel Version .................................................... 13.37.0 PHP Version .......................................................... 8.3.6 Composer Version ..................................................... 2.7.1 Environment .......................................................... local Debug Mode ......................................................... ENABLED URL ......................................................... localhost:8000 Maintenance Mode ....................................................... OFF Timezone ............................................................... UTC Locale .................................................................. en Cache ...................................................................... Config .......................................................... NOT CACHED Events .......................................................... NOT CACHED Routes .......................................................... NOT CACHED Views ........................................................... NOT CACHED Drivers .................................................................... Broadcasting ........................................................... log Cache ............................................................. database Database ............................................................ sqlite Logs ........................................................ stack / single Mail ................................................................... log Queue ............................................................. database Session ........................................................... database Storage .................................................................... public/storage .................................................. NOT LINKED
Разбор команды
Приборная панель приложения: одна команда показывает версию, окружение, состояние кэша, драйверы и хранилище — всё, с чего начинают разбор любой странности.
php artisan about| Токен | Зачем он и что меняет |
|---|---|
php | интерпретатор CLI. |
artisan | консоль проекта. |
about | команда-сводка: собирает значения из конфигов и .env и печатает их блоками. |
Что произойдёт
30 строк в четырёх блоках: Environment, Cache, Drivers, Storage.
На что смотреть
Блок Environment: Laravel Version 13.37.0, PHP Version 8.3.6, Composer Version 2.7.1 — всё сходится с предыдущими захватами; Environment local, Debug Mode ENABLED (это APP_DEBUG=true из .env), URL localhost:8000 (это APP_URL), Maintenance Mode OFF, Timezone UTC, Locale en. Блок Cache: Config, Events, Routes и Views — все NOT CACHED, то есть конфиги, события, маршруты и шаблоны читаются с диска на каждом запросе, и правки действуют сразу. Блок Drivers: Database sqlite, Session database, Cache database, Queue database, Broadcasting log, Mail log, Logs stack / single. Блок Storage: public/storage — NOT LINKED.
Частые ошибки
- Поле URL — это значение
APP_URLиз.env, а не адрес, по которому сайт реально отвечает: в части 2 приложение откроется по локальному домену зоны.rs. Debug ModeENABLEDв разработке помогает, а на бою показывает посторонним пути и настройки — там его выключают.
- Environment: Laravel 13.37.0, PHP 8.3.6, Composer 2.7.1 — всё сходится с захватами выше. Окружение
local, Debug ModeENABLED— этоAPP_DEBUG=trueиз.env. Часовой пояс UTC, локальen. - Cache: везде
NOT CACHED— конфигурация, события, маршруты и шаблоны читаются с диска на каждом запросе. Медленнее, зато правка любого файла действует сразу — то, что нужно в разработке. Когда кэш включать и как сбрасывать — часть 6. - Drivers: где приложение хранит данные.
Database: sqlite— файл, созданный при установке.Session,Cache,Queue:database— таблицы из тех самых миграций.Mail: log— письма не отправляются, а пишутся в лог: в разработке можно «отправить» письмо и прочитать его файлом.Logs: stack / single. - Storage:
public/storage NOT LINKED— символическая ссылка для файлов, загруженных пользователями, не создана. В этом уроке не понадобится.
Любую странность начинайте с php artisan about: неверный драйвер базы, неожиданно включённый кэш, не то окружение — видно на одном экране, без раскопок в конфигах.
8.3 и php8.3 в командах выше — версия стенда. Проверьте свою: php -v. Если она другая — замените 8.3 на свою во всех путях (/etc/php/…, /run/php/…), именах пакетов (php8.3-*) и сервисе (php8.3-fpm).
Итог части: что и где лежит
project-laravel — приложение, которое построим в частях 2–6, а vendor/ внутри него — роутинг, шаблоны, ORM и валидация: то, что больше не пишем руками (76 МБ на весь проект). composer-demo — учебный эксперимент первой половины части.
Системно за часть мы не поставили ничего: весь стек — PHP, nginx, MariaDB, dnsmasq, Composer — уже стоял из блока Linux, а оба проекта собрались внутри пользовательских папок, без sudo.
Проверьте себя
- Мы попросили у Composer один пакет
nesbot/carbon, а установилось девять. Почему? И в каком файле записаны точные версии всех девяти? - В composer.json написано
"nesbot/carbon": "^3.14", а в lock-файле —3.14.2. Зачем второй файл, если есть первый: какую проблему решает lock? .envи.env.example— чем они различаются, какой из них попадает в git, а какой появился из него при установке?- Назовите не меньше трёх действий, которые
composer create-projectвыполнил сам, после того как распаковал файлы каркаса. php artisan aboutпоказываетDatabase: sqlite. Вручную базу никто не создавал — откуда она взялась и какие таблицы в ней появились при установке?
Проверьте себя: ответы коротко
- Carbon тянет за собой зависимости:
symfony/translation,polyfill-mbstring,psr/clockи другие — дерево построил Composer. Точные версии всех девяти — в composer.lock (строкиLocking … (3.14.2)и т. д. в выводе require). ^3.14— диапазон: «любая 3.x не ниже 3.14». Через месяц свежая установка взяла бы 3.15, хотя вы тестировали 3.14.2. Lock фиксирует точные версии, чтобы коллега, CI и сервер поставили то же самое..env— живые настройки с настоящим APP_KEY; в git не коммитят..env.example— шаблон без секретов; коммитят его. При установке.envсоздан копией шаблона — первым скриптом create-project.- Скопировал
.envиз.env.example; сгенерировал и записал APP_KEY (key:generate→INFO Application key set successfully.); создалdatabase/database.sqlite; применил миграции (users, cache, jobs плюс журнал миграций); запустил обнаружение пакетов (package:discover). Достаточно любых трёх. - Её создал тот же create-project: скрипт
touchсделал пустой файл SQLite, аartisan migrateпостроил в нём таблицы — в выводе видноCreating migration tableи три миграции:create_users_table,create_cache_table,create_jobs_table.
Что дальше — часть 2
Приложение лежит на диске, но пока это просто файлы. В части 2 оно начнёт отвечать на запросы:
- прочитаем
public/index.php— единственную точку входа — и свяжем её с путём, который вы уже знаете: браузер → DNS → nginx → PHP-FPM → буфер вывода → ответ (lesson.http.i.01); - разберём
bootstrap/app.phpиroutes/web.php— где живёт таблица маршрутов; - напишем первый маршрут и первый шаблон Blade, откроем их через
php artisan serve; - настроим настоящий nginx на
project-laravel.rs:rootвpublic/,try_files,fastcgi_passв знакомый сокет — и по дороге соберём две классические ошибки конфигурации с настоящими 403 и 404 в захватах.
Всё, что мы сегодня установили, там начнёт работать.