Как стать автором
Обновить

Комментарии 122

Очень любопытно. По этому поводу даже откопал RFC 2616 и распечатал назначения кодов ответа.
Пользуюсь вот такой шпаргалкой :)
спасибо, очень полезная штука )
Спасибо ещё можно сказать автору: dinamyte :)
НЛО прилетело и опубликовало эту надпись здесь
Могут возникнуть, а могут не возникнуть, всё от архитектуры приложения зависит. Сам часто встречал, что юзается небольшое количество кодов HTTP, из вполне обширного списка. И даже не задумывался о более эффективном их приминении, как в этом способе, но когда первый раз прочёл — впечатлился.
НЛО прилетело и опубликовало эту надпись здесь
Так никто и не заставляет, но лучше знать, что такая возможность есть, потому что это может понадобится… может и не сегодня, но понадобится.
НЛО прилетело и опубликовало эту надпись здесь
Насчет пункта 4: чем конкретно плох механизм сессий и почему не стоит его использовать?
Переменную PHPSESSID можно передать либо через куки, либо POST/GET запросом. В общую специфику описанного алгоритма это уже не вписывается.
Механизм хорош. Только данные сессии должны быть минимальны и храниться в базе. В противном случае не получится организовать кластерную архитектуру.
По какой это причине, позвольте поинтересоваться, не получится? Вы что нибудь про кластерные файловые системы слышали?
И как вообще тема этой статьи затрагивает проблемы организации кластерных систем?
Если запрос клента обрабатывается на разных серверах в разные моменты времени, согласитесь, будет нехорошо, если логика работы будет зависеть от файлов в файловых системах разных серверов. А вообще, да, сессии тут не при чем. Надо просто с умом их использовать.
Вот именно. ключевое слово: 'с умом'
Можно хранить в базе а можно и использовать Coda что позволит иметь доступ к одним и тем же файлам на любом сервере
Можно хранить данные в memcache
А может и достаточно NFS или WebDav…

Решений масса и не все они упираются в базу. И ни одно не противоречит использованию сессий.
может быть попробовать MemCache?
В обще, конечно, для общего развития знать такие вещи нужно, но вот каково практическое применение я не могу понять до сих пор — я не смог придумать действительно значимую причину использования этой части протокола.
ИМХО, слепое следование стандарту такое же зло как и его намеренное неоправданное нарушение, и если, как в данном случае (PHP), используемый инструмент, выбранный для решения конкретной задачи, не поддерживает какие-то части протокола, не стоит подставлять костыли и натягивать инструмент на стандарт — ваши приемники (тот кто будет поддерживать код после вас) навряд ли оценят ваши старания, и будут, пожалуй, правы.
Как по мне, если ты пишешь систему CRUD, то этот стандарт как раз под неё идеально подходит.
Если есть желание использовать стандарт, то и инструмент нужно выбирать такой, который этот стандарт поддерживает, а вот метод порождения $_PUT, предложенный в статье — страшный костыль.
И еще это странное утверждение: «Не используйте $_SESSION». $_SESSION — массив данных ассоциированный с определенным идентификатором передающимся в куках запроса, а как его «крутить-вертеть» решать разработчику: хочешь юзай стандартный метод, а не хочешь пиши свои обработчики.
Зачем такой огород городить вокруг соблюдения стандарта, в ущерб простоте приложения, совершенно не понятно.
Да и вообще говоря, если стандарт не соблюдается в большинстве случаев — это означает только то что надо менять стандарт.
«Не используйте $_SESSION» — почему? Читаю чуть ниже: «В случае если требуется сохранить большие объёмы данных, их можно уложить в базу данных, авторизационную информацию стоит оставить в куках.» Разве $_SESSION — это не данные из базы данных, полученные с помощью авторизационной информации из кук?
По умолчанию это данные из файлов на диске. Но никто не мешает переопределить сессионхендлер и хранить данные в базе, в памяти, да хоть на бересте клинописью, если есть устройство считывания берестяных грамот.
Какая разница откуда эти данные по-умолчанию, в статье об этом ни слова и комментарий не об этом. Чем автору переменная $_SESSION не понравилась-то?
«A truly RESTful PHP application should be entirely stateless» — Правильное RESTful PHP приложение должно быть свободно от сохранения состояния (клиент/сервер). Массив $_SESSION не подпадает под эту идеологию.
Хм. Вот два случая.
1. Юзер на сайте идентифицируется по какому-то хэшу, который записывается ему в куку.
2. Юзер на сайте идентифицируется стандартными средствами, кукой PHPSESSID.

У юзера есть какие-то настройки сайта: часовой пояс, формат даты и времени, ну и карма до кучи. И в том, и в другом случаях эти переменные записываются в базу данных. Но в первом мы имеем RESTful PHP, во втором — не RESTful PHP. Почему?

Или я чего-то не понял, или значительная часть приложений принципиально не может быть restful просто потому что нужна идентификация?
«Переменную PHPSESSID можно передать либо через куки, либо POST/GET запросом. В общую специфику описанного алгоритма это уже не вписывается».
Отлично! Мы вернулись к первому моему вопросу.

«В случае если требуется сохранить большие объёмы данных, их можно уложить в базу данных, авторизационную информацию стоит оставить в куках.» — Очень интересно было бы узнать чем именно $_SESSION не подпадает под эту идеологию.
PHPSESSID, само название говорит о причастности к серверу. $_SESSION находится на сервере. «Настоящее RESTful PHP приложение должно быть полностью независимо, все запросы должны содержать достаточно информации чтобы на них можно было отреагировать без дополнительных усилий со стороны сервера.» При использовании cookie запрос становится самодостаточным.
PHPSESSID всего лишь имя куки. Какое-то имя должно быть и у куки для «restful» авторизации. Как я уже написал выше, не все данные можно хранить в куках, какие-то данные (как было написано в оригинале «большие объемы») приходится хранить в базе данных и в этом случае $_SESSION находится на том же самом сервере, в той же БД. Так в чем разница?

Я по-другому вопрос задам: чем PHPSESSID — не кука?
$_SESSION находится на сервере. Возможно использование куков не в полной мере соответствует изначальному REST, т.к. сами куки появились как надстройка над протоколом.
Да, $_SESSION на сервере. И данные из базы данных, которые грузятся через авторизационную информацию из кук, тоже на сервере.

В общем, идея RESTful приложений в целом неплохая, но некоторые положения совершенно явно притянуты за уши.
Данные из БД относятся к ресурсу сервера и лежат там постоянно. В п.4 говорится о данных влияющих на состояние клиент/сервер.
Авторизационная псевдо-рестфул кука точно так же как и phpsessid влияет на состояние клиент/сервер. Вся разница только в названиях переменных и функций.
Кука находится у клиента, $_SESSION на сервере. Но по сути полумера, если отталкиваться от концепции REST предложенной Роем Филдингом. Здесь про это написано: Representational State Transfer (http://en.wikipedia.org/wiki/REST). Ко всему прочему это только один из принципов REST, есть и ряд других.
У меня складывается ощущение, что я дискутирую со стеной…

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

Все остальные принципы точно так же высосаны из пальца (предположительно) и упираются в проблемы реализации методов PUT и DELETE на популярных платформах.

Еще раз повторю, я нисколько не пытаюсь приуменьшить здравость описанных идей. Мне непонятно желание противопоставить существующим решениям.
Возможно недопонимание ещё и в том, что я чётко разграничиваю понятия COOKIE и SESSION. Первое — файлы на стороне клиента, инициализированные сервером, значения из которых можно посылать серверному скрипту для манипуляций (сравнения значений с данными из БД и т.п.), в данном случае сервер не обязан хранить соответствия, все данные есть у клиента. Второе — файлы в temp директории сервера, SID посылается клиенту, чтобы он его потом вернул, тем самым поддерживая сеанс. Сессию можно и не инициализировать. Сессия может не использовать cookie.

Желания противопоставить нет, это всего лишь другой метод работы.

В споре рождается истина…
Откройте для себя магию функции parse_str.
Известная магия, пример в статье для наглядности сделан через цикл.
Угу. и для наглядности не обрабатывает массивы :)
Интересный плагин, спасибо.
204 No Content перепутали с 304 Not Modified?
Ничего не перепутали, вот добрые люди наглядную инфу указали web-zine.org/files/upload/scheme-http.png
Наглядно перепутали )
304 как бы более подходит для указанной в статье цели ;)
Не спорю, тут всего лишь ещё один вариант такой реализации.
«авторизационную информацию стоит оставить в куках»
что есть авторизационная информация которую надо оставлять в куках?
если это логин и пароль — автор невменяем
если это session_id — противоречие с темой абзаца
Видимо по контрольный сумме из куков можно эту инфу из базы подтянуть. Необязательно логин и пароль хранить.
Зачем? Чем плох массив $_SESSION?
Вообще в REST основная особенность — несохранение состояния между клиентом и сервером, как и задумано в HTTP.
И что? Причем тут SESSION?
Почему им нельзя пользоваться а базой можно?
и чем это будет отличаться от session_id?

Думаю тут недопонимание.

«A truly RESTful PHP application should be entirely stateless», остальные предложения про авторизацию только вводят в заблуждение.

Имеется в виду — ни один результат не должен зависеть от предыдущих запросов.

То есть — /search?query=строка_для_поиска&where=везде&options=aabbccdd
вместо /search?query_id=некий_номер_запроса.

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

А все ради чего? чтобы с гордостью называться RESTful PHP application :)
пункт 4 это вообще просто бред!
гы $_SESSION юзать нельзя а информацию хранить в базе можно. тоесть $_SESSION ну не в коем случае нельзя использовать даже при хранении сессионных данных не в файлах а в базе.
Мои 5 копеек: все эти $_PUT и $_DELETE имеют существенные недостатки:
— отсутствие «суперглобальности»
— отсутствие возможности работать с ними через filter_input(INPUT_*,..).
— нестандартность и недокументированность

В целом топик оставляет спорное впечатление. Вроде человек хочет, чтобы было «как лучше», но для этого предлагается сделать «как всегда» — довольно грязные методы: создание $_PUT и $_DELETE — вместо того чтобы использовать всем знакомый и задокументированный $_POST. Насчет сессий — не понял. А вот часть про заголовки отклика — полезная, имхо.
— нестандартность и недокументированность

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

Мне кажется, что зря люди сразу не стали использовать все возможности протокола HTTP. Теперь мы начинаем удивляться, откуда же взялись такие методы, были ведь только GET и POST :)
"— отсутствие «суперглобальности»
— отсутствие возможности работать с ними через filter_input(INPUT_*,..).
— нестандартность и недокументированность"

Я говорил про переменные, которые предлагается создать в случае использования протоколов PUT и DELETE. Про переменные, не про методы HTTP.
Хороший ООП код не потребует глобальных переменных, про конкретную реализацию тут не говорится, это всего лишь пример.
Я, например, постоянно использую ООП в PHP, практически только его и использую. И это не мешает мне использовать $_GET и $_POST, а также filter_input() — в любом месте моего кода. Не все являются сторонниками обворачивания запроса в объекты — кое-кто таки любит простые вещи.
В любом случае, отсутствие суперглобальности, какой-то неявный минус. По мне так глобальность — зло.
Ок, в таком случае, назовите мне известные PHP-фреймворки, поддерживающие REST. Чтобы в объекте HTTP-запроса были переданные переменные — при использовании методов PUT или DELETE. Если не назовете — я жду, что вы таки согласитесь с тем, что иметь в своем распоряжении именно суперглобальные $_PUT и $_DELETE было бы приятно — приятнее, чем засовывать их в объект запроса самому, разбирая пришедшие данные.
Выделенное слово «известные» не означает истину в конечной инстанции. Обычный в ссылках к статье посмотрите.
Э нет, так не пойдет. Мне не нужен фреймворк, известный одному автору статьи (утрирую, конечно). Я такой фреймворк, который только мне известен — и сам написал. Популярность — не последняя вещь. На что мне фреймворк, релиза которого еще не было, документации к которому нет ни строчки, а на сайте которого — только страница-заглушка.
Обновил ссылки к статье, несколько известных сервисов, которые этим пользуются.
Все это хорошо и мудро, но вернемся к нашим баранам: пожалуйста, хоть один фреймворк на PHP, использующий REST и имеющий больше одной страницы документации.
Причём здесь вообще фреймворк то??? Статья не про фреймворк, а про технологию.
Мы с вами беседуем о «суперглобальности» переменных и ее пользе и вреде. Мое мнение: суперглобальность не только вредна, но и полезна. Особенно когда речь зайдет о том, чтобы получить в программе переменные, переданные по методу PUT или DELETE.

Вы настаиваете на том, что суперглобальность вредна безусловно. Докажите это, пожалуйста, и не общими фразами «глобальные переменные — зло», а на конкретных примерах.

Зачем фреймворк? Откуда взялся фреймворк? Затем, что если во фреймворке, который вы используете, имеется встроенная поддержка PUT- и DELETE — тогда у вас уже есть удобный, вероятно, задокументированный способ работы с переменными, переданными по этим методам. Тогда можете дальше кричать «глобальные переменные — зло» — и говорить: у меня есть объект запроса, в нем все есть. А если фреймворк не поддерживает — значит, вам придется доставать переменные врукопашную и либо вручную заталкивать их в объект запроса, либо еще откуда-то их доставать по мере необходимости. А это значит — вам далеко не так удобно работать с вашими PUT- и DELETE — как мне с моим POST.
кстати да, если использовать REST, то во фреймворке целесообразно иметь объект запроса, который отдает эти данные.
«В любом случае, отсутствие суперглобальности, какой-то неявный минус. По мне так глобальность — зло».

Глобальные переменные сложно отследить в развитом приложении, их можно подменить если используется extract(). Этого не достаточно?
Дорогой вы мой человек. Не нужно мне рассказывать про вред глобальных переменных вообще. Я говорю про две-три вполне конкретные переменные, которые, в случае использования REST — мне хотелось бы видеть суперглобальными — просто чтобы работать с запросом можно было без каких-то надстроек на уровне приложения.

Используете фреймворк, в котором это уже реализовано и задокументировано? — флаг в руки. Вот симфони, оказывается, уже поддерживает REST, по словам develop7.

Но у вас же русским по белому написано: содадим-ка мы $_PUT, который будет а-ля $_POST. А потом вы говорите, что против глобальных переменных. Вас не поймешь.
Это для примера, если нужна глобальность — пожалуйста, её вред моё личное убеждение, я бы обошёлся свойствами объектов и т.п. В примере показано как обойти ограничение в отсутствии PUT, DELETE и прочих методов. Вполне логично показать это на примере, который уже есть: $_POST или $_GET. Назвать переменную можно как угодно: $put_method_data, $this->put и т.п. В примере показано создание $_PUT для простоты восприятия.
НЛО прилетело и опубликовало эту надпись здесь
Вот, спасибо за информацию.
В ЗФ полного нет, но если хочется пишется за 1 минуту.
Там в классе Запроса есть методы getMethod(), isPut(), isDelete(), и т.д.; есть метод getRawBody().
Остается только заэкстендить класс и переписать методы getParam/getParams() и все, дальше можно пользоваться.
Это кривое решение. Логичнее просто передавать параметры запроса (к которым отнросятся POST/GET) в контроллер, примерно так:
class Posts_ctlr {

function add_post($query)
{

}
}
Согласен, возможно, лишние переменные и не нужны.
Да, и про put/delete

Как и писал автор топика:
POST — создание ресурса,
PUT — обновление,
DELETE — удаление.

На мой взгляд, выделений подобных действий в отдельные методы — хороший тон.
А помоему излишество. Иначе не хватает еще

MOVE — перемещение ресурса
CUT — обрезание ресурса
HIDE — спрятать ресурс
HACK — попытаться хакнуть ресурс
бред какой-то несете
А кто спорит)))
Может, и хороший тон — но не для PHP, в котором придется писать какие-то лишние извраты для поддержки этого добра.
Нахрена Вам суперглобальность переменных (каких бы то ни было)? Глобальные переменные — зло и за их использование в 90% случаев надо бить по рукам. И вообще, вся Ваша аргументация против REST относятся только к похапе, что в очередной раз доказывает его убожество.

По теме — человек не хочет сделать «как лучше». Человек хочет использовать возможности, изначально заложенные в HTTPа для организации простых и прозрачных веб-сервисов. Человек не хочет использовать SOAP, XML-RPC и прочие XML-небоскребы, и это вполне понятное желание — зачем трахать себе мозг, когда все можно сделать проще и, что самое главное — правильнее с точки зрения протокола.
Не всегда слепо следовать правилам. Между прочим HTTP достаточно старый протокол и не факт что все заложенные в него вещи актуальны и вообще правильны.
Вы всегда оптимизируете БД до пятой НФ? Как же так, ведь в теории…
БД и нормальные формы это уже немного не по теме. Про глобальность и суперглобальность это весьма верно. То что протокол старый и возможно несовершенный… а что сейчас совершенно, особенно по прошествии времени??? Это совсем не значит, что нужно усугублять и без того несовершенные вещи.
Да мне пофиг вообще, пишите как хотите. Мир от этого хуже не станет, максимум поматерят вас пара человек, которые потом код будут поддерживаь.
Материть будут REST-последователей.
А свой код я как нибудь другими методами сделаю понятным. Например применяя MVC, TDD, Рефакторинг.
Отлично, успехов вам в вашей работе.
Попробуйте из браузера без использования форм послать запрос PUT или DELETE.

Попробуйте из браузера без использования форм послать запрос POST. Не выходит?

Это потому, что запросы PUT, DELETE и POST должны отправляться только с использованием форм. Или вы удаление объектов до сих пор по GET-у делаете?
это понятно, но в чем тогда их отличие от POST? В другом названии?
Действия (или события) приложения, если мы уж говорим о приложениях, оориентированные на события, не ограничиваются Get-Post-Delete-Put. Так зачем загонять приложение в эти рамки?
Чтобы потом дополнительно иметь проблемы с дроблением евентов?

В этих словах как раз таки и заключена инерция в отношении POST/GET. Можно и 200 + 404 хэдерами обойтись…
Это не инерция, это скепсис в отношении ненужной (имхо) суеты вокруг DELETE и PUT.
И вы абсолютно правы — грамотной манипуляции заголовками ответов — вполне достаточно.

Скажу больше — сейчас это просто необходимо, знать в каком случае какой код ответа отправить.
это было необходимо и 30 лет назад, лол :)
30 лет назад это было не критично, потому что конкуренции небыло. Впрочем — это уже отдельная тема.
Вы рассуждаете как авторы браузера IE6 :)
Авторы этого браузера — неглупые люди )) До сих пор держат такую долю рынка.
То что создатели IE неглупые люди не делает IE хорошим браузером. Одни неглупые люди тратят свой ум на то что бы сделать хороший продукт, другие что бы впарить плохой.
Любое веб-приложение полностью (или почти полностью) прекрасно разделяется на ресурсы, и методов GET, POST, PULL и DELETE достаточно, чтобы ими управлять.
Если вы с этим не согласны, то Вы не до конца понимаете концепцию REST.

Какие события в приложении могут выходить за рамки CRUD?
PUT
PUT == create
Не, я в смысле, я с вами согласен, но вы там написали PULL вместо PUT :)
а, да, ошибся :)
Я скептически отнесусь к REST хотя бы из за бреда в 4-м пункте. Даже допуская что это неправильный перевод или додумка автора меня отпугнет уровень образованности тех кто пропагандирует REST.
Блин, ну придумайте тогда свой HTTP, с блекджеком и шлюхами, где будет только 2… или нет, лучше вообще один метод. И, кстати, при организации веб-сервисов классическими методами (SOAP) вы используете сессии? Если нет, то почему?
Да-да, в литературе тоже существуют всего 4 сюжета.
Попробуйте POST послать…
Всё это не имеет значения если использовать AJAX, а с prototype.js это вообще пара строк.
Моя аргументация относится к топику, который называется «RESTful PHP — 5 простых советов».
Я особенно не искал, но это самое толковое объяснение RESTful применительно к PHP на русском языке из всего что я видел.
Все понятно, за исключением одного.

А зачем все это?
Веб-сервисы (читай — API) без еботы.
$exploded = explode('&', $putdata);
foreach($exploded as $pair) {
$item = explode('=', $pair);
if(count($item) == 2) {
$_PUT[urldecode($item[0])] = urldecode($item[1]);
}
}

Опять…

см. parse_str
Тут скорее для наглядности.
GET /book/ — получить список всех книг
GET /book/3/ — получить книгу номер 3
PUT /book/ — добавить книгу (данные в теле запроса)
POST /book/3 – изменить книгу (данные в теле запроса)
DELETE /book/3 – удалить книгу

Мне например нравится такой подход.
POST /book/ — добавить книгу (данные в теле запроса)
PUT /book/3 — изменить книгу (данные в теле запроса)

кстати логичнее и красивше
GET /books/
POST /books/
В rails с версии 2.0.2 практикуется этот подход :)
я могу ошибаться, но разве метод DELETE не отключают из за соображений безопасности? Не добавит ли такое разделение проблем с безопасностью? чем плох вариант
/view/book/ — все книги
/view/book/ — все книги
/view/book/ — все книги
/view/book/ — все книги
сорри клава заела
Это даже плюс, не нужно физически удалять файл заданный как URI ресурса для метода HTTP DELETE. В PHP происходит эмуляция данного метода и решение о нужном действии принимает скрипт. К тому же использование человеко-понятных-урлов вида /tech/news/, делает это вполне логичным.
Вообще вот здесь пишут что для реализации RESTFull в www совсем необязательно заставлять клиента посылать PUT- и DELETE- запросы для реализации CRUD.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Многие прокси не пропускают никакие методы, кроме GET и PUT. А работать по https не всегда возможно. Например, если требуются виртуальные хочты.
для себя писал парсер формы (multipart/form-data) PUT с загрузкой файлов
public function parse_raw_http_request(array &$a_data)
  {
    $input = file_get_contents('php://input');
    preg_match('/boundary=(.*)$/', $_SERVER['CONTENT_TYPE'], $matches);
    $boundary = $matches[1];

    $a_blocks = preg_split("/-+$boundary/", $input);
    array_pop($a_blocks);

    foreach ($a_blocks as $id => $block)
    {
      if (empty($block))
        continue;
        preg_match('/name=\"([^\"]*)\".*filename=\"([^\"]+)\"[\n\r]+Content-Type:\s+([^\s]*?)[\n\r]+?([^\n\r].*?)\r$/Us', $block, $matches);

        if (count($matches)==0){
          preg_match('/name=\"([^\"]*)\"[\n|\r]+([^\n\r].*)?\r$/s', $block, $matches);
          $a_data[$matches[1]] = $matches[2];
        } else {
          $tmp_name = tempnam(sys_get_temp_dir(),'');
          file_put_contents($tmp_name, $matches[4]);
          $size = filesize($tmp_name);

          preg_match('/([a-zA-Z_0-9]+)/s', $matches[1], $name);
          preg_match_all('/\[([a-zA-Z_0-9]*)\]/s', $matches[1], $arr);
          $file = array(
            'name'=>null,
            'type'=>null,
            'tmp_name'=>null,
            'error'=>null,
            'size'=>null,
          );
          $arr = $arr[1];
          $name = $name[1];
          $args = array();
          foreach ($file as $key => &$value)
          {
            $args[]=&$value;
          }
            for ($i = 0; $i < count($arr); $i++)
            {
              for ($k = 0; $k < count($args); $k++)
              {
                $args[$k] = array();
                if ($arr[$i]==''){
                  $args[$k][] = null;
                  $x= count($args[$k])-1;
                  $args[$k] = &$args[$k][$x];
                } else {
                  $args[$k][$arr[$i]] = null;
                  $args[$k] = &$args[$k][$arr[$i]];
                }
              }
            }

          $args[0] = $matches[2]; //filename
          $args[1] = $matches[3]; //type
          $args[2] = $tmp_name; //tmp_name
          $args[3] = 0; //error
          $args[4] = $size; //size
          $_FILES[$name] = $file;
        }
    }
  }


пользоваться так
    $params = array();
    parse_raw_http_request($params);


Особенность: временные файлы приходится удалять вручную после обработки запроса.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации