Опубликован: 22.10.2016 | Доступ: свободный | Студентов: 828 / 84 | Длительность: 04:02:00
Лекция 4:

Серверные технологии

< Лекция 3 || Лекция 4: 1234

Загрузка и хранение файлов на сервере

В папке files у нас будет склад файлов, которые редактор загрузил на сервер. Потом из этого склада можно файлы брать и использовать на сайте для разных задач (например, для создания Фотогалереи). Файлы можно искать по имени (Дом.jpg), можно просматривать каждую картинку – искать "по содержанию". Второй способ явно медленней. Но что делать, если вы загрузили 1000 картинок с наименованиями вида DSC3424.jpg?

Я считаю, что всё равно надо связывать каждую картинку с какими-то ключевыми словами – так искать будет гораздо удобнее и быстрее. Поэтому система хранения у нас будет организована так: все имена при загрузке файлов превращаются в последовательные числа (1.jpg, 2.jpg, 3.jpg...); старые имена загружаемых файлов сохраняются в БД в таблице file, в поле title; это поле потом можно изменить – записать туда более содержательный текст, чем первоначальный DSC4545.

В таблице file есть ещё несколько полей: например, descr, в которое можно записывать более длинный текст (можно использовать HTML), или поле prop1, в которое можно записывать всё, что угодно (например, адрес ссылки для баннера). Только надо договориться с программистом, чтобы он использовал нужные вам поля в соответствующих шаблонах (Фотогалереи, Каталога товаров...). А, поскольку программист сейчас – это вы, договориться будет несложно.

На странице edit.com (которую можно открыть, нажав клавиши Ctrl + Alt + e), в меню "Файлы" вы вводите текст, который будет искаться в таблице file, по полям title, descr, id – так вы можете быстро найти нужный файл по ключевым словам, которые вы же туда и записали.

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

<?php
	$command = 'convert ' . $dest[0] . ' -resize ' . $dim[0] . 'x' . $dim[1] . '\> -quality 80 ' . $dest[1];
	system($command);

Здесь $dest[0] и $dest[1], соответственно, – исходный файл и уменьшенный файл; $dim[0] и $dim[1] – требуемые размеры изображения, которые мы храним в файле config.php (и получаем при работе из Config::$public['img_size']).

Для выполнения команды в операционной системе сервера должен быть установлена система обработки изображений ImageMagick (именно её вызывает команда convert) и у Php должен быть доступ к выполнению команд системы (такой доступ бывает не на каждом хостинге).

Команда системы convert хороша тем, что она очень компактна, в двух её значках – "\>" – спрятана логика, для которой на php понадобится несколько строк кода. Эти значки предписывают конвертеру не менять размеры изображения, если они не больше требуемых. Впрочем, на php можно работать с изображениями более гибко; но это не предмет нашего курса (мы используем в нашей учебной CMS только изменение размеров картинок).

***

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

На сервере для каждого файла сначала создаётся запись в таблице file, затем мы узнаём id новой записи с помощью mysql-функции LAST_INSERT_ID() и перемещаем загруженный файл в нужную папку с нужным именем (id + расширение файла). Мы допускаем, что в одной папке будет храниться до 1000 файлов, а следующие будем складывать в другую папку (по номерам: 0, 1, 2...). Вы можете найти в других CMS разные хитрые способы случайного распределения файлов по разным папкам, но для планируемых нами объёмов (несколько тысяч файлов) сложная система не нужна, и мы по возможности стараемся соблюдать принцип KISS (англ. keep it simple, stupid).

Всё это делает функция Command::upload_file(), которая, в свою очередь, вызывается из функции Command::onwrite_file() (как бы по событию "после записи в таблицу file") в файле Command.php.

При загрузке группы файлов можно сразу указать (выбрать из выпадающего списка) страницу, к которой загружаемые файлы будут привязаны (потом эту страницу можно поменять в форме редактирования каждого файла). Тогда на выбранной странице легком можно будет отображать список изображений, выбрав их запросом "select * from file where topic = 'id_текущей_страницы'": http://nichtig.ru/07/20_Fotogalereya_eto_prosto-21.html

Почтовые уведомления

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

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

В структуру кода "записывающих" функций у нас заложена работа "по событиям": если в файле Command.php мы создадим функцию onwrite_msg, она "автоматически" будет вызываться после записи в БД любого комментария или вопроса.

Вот и напишем в функции Command::onwrite_msg(), чтобы она отправляла на адрес автора сайта текст из полученных данных $_POST['set']['text_q']:

<?php
	mail('onerror@mail.ru', 'Новое сообщение на сайте', $_POST['set']['text_q']);

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

Дело в том, что протокол отправки писем очень чувствителен к переносам строки – коду \r\n. Если вам интересно, как именно этот код может испортить письмо, почитайте спецификацию RFC_5321 https://tools.ietf.org/html/rfc5321 . А мы просто будем удалять сочетание \r\n из тела письма:

<?php
	$msg = preg_replace('/\r/', "\n", $_POST['set']['text_q']);
	mail('onerror@mail.ru', 'Новое сообщение на сайте', $msg);

Ну, и добавится ещё одно ограничение для нашей CMS: для отправки почтовых уведомлений мы используем php функцию mail(), которая требует наличия сервера sendmail. Это значит: веб-сервер должен быть на линуксе, и владелец хостинга должен разрешить использование функции mail(). Хостинг, удовлетворяющий таким условиям, найти несложно, но и обратная ситуация встречается достаточно часто. Поэтому надо просто обращать внимание на такие детали. Или сразу привыкать пользоваться виртуальным сервером (VPS), который вы можете настраивать по своему усмотрению.

Конечно, можно и совсем обойтись без функции mail(), а отправлять письма, скажем, используя привычные бесплатные почтовые серверы (mail.ru, yandex.ru...). Но тут уже php-кода понадобится больше, чем сейчас использует вся наша CMS, – вам надо будет использовать какую-то внешнюю GPL-библиотеку (например, PHPMailer https://github.com/PHPMailer/PHPMailer /). На самом деле можно даже библиотеку не использовать, а самому написать (получится килобайт 10-30), но тогда надо будет сначала выучить упомянутую выше спецификацию (RFC 5321). Так что выбора, по большому счёту, у вас и нет (вы же не осилите?..).

Чтобы при отладке программы (на домашнем компьютере) не сыпались ошибки, добавим в конфигруацию пункт mailer, значения которого могут быть "mail", "smtp" (для сторонних библиотек) или "null" – тогда почту отправлять вовсе не будем пытаться. В какой именно раздел файла конфигурации добавим новый пункт, обсудим в следующей главе.

< Лекция 3 || Лекция 4: 1234
Михаил Гутентог
Михаил Гутентог

Этот курс ( Практикум по разработке CMS ) создавался, когда у PHP была версия 5.3 или 5.4. Со временем какие-то функции PHP устаревают (mysql, each), какие-то начинают работать по-другому (empty). Пожалуйста, следите за изменениями в PHP по сайту php.net!

Александр Мельников
Александр Мельников

Изучаю курс "Практикум по созданию CMS" в листинге 4.3

$n = count($_GET); if ($n > 0) { $param = each($_GET); // самое простое: пропускаем только первый параметр if ($n > 1 || !isset($valid[$param['key']])) { _404(); }

При попытке просмотра в браузере получаю ошибку: Deprecated: The each() function is deprecated.  И не пойму как исправить ситуацию.