Curl в php: примеры post, get запросов с headers, cookie, json и многопоточностью

POST и GET запросы без cURL

С помощью PHP мы можем отправить простой GET запрос используя функцию file_get_contents.

Пример:

$result = file_get_contents('https://phpstack.ru/');

Теперь у нас в переменной $result записан весь html код главной страницы этого сайта.
Мы совершили GET запрос, а html код — это ответ на него.

При помощи file_get_contents мы также можем отправить POST запрос.

Пример:

$postData = http_build_query();

$opts = [
    'http' => [
        'method' => 'POST',
        'header' => 'Content-type: application/x-www-form-urlencoded',
        'content' => $postData
    ]
];

$context = stream_context_create($opts);

$result = file_get_contents('https://httpbin.org/anything', false, $context);

Подробнее о том, какие опции можно передавать в stream_context_create, вы можете изучить здесь: http://docs.php.net/manual/ru/context.http.php

В $result мы получили ответ на POST запрос. httpbin.org — это сторонний сервис, который вы можете использовать для отладки запросов. Он возвращает нам наш собственный запрос в формате JSON и еще некоторую информацию. Так мы можем увидеть, что мы отправляем в своих запросах.

Как видите file_get_contents — полезная функция, которая не только позволяет читать файлы на нашем сервере, но еще и отправлять запросы.

Подробнее о ней вы можете прочитать здесь: https://www.php.net/manual/ru/function.file-get-contents.php

удалить возвращаемое значение

удалить экземпляр запроса, метод запроса использует HttpMethod.DELETE

Другая статья

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

1. Вызовите метод postForObject 2. Используйте метод postForEntity 3. Вызовите метод обмена.

Основное различие между методами postForObject и postForEntity заключается в том, что вы можете установить свойства заголовка в методе postForEntity. Если вам нужно указать значения свойств заголовка, используйте метод postForEntity. Метод обмена похож на postForEntity, но более гибкий. Exchange также может вызывать запросы на получение, размещение и удаление. Используйте эти три метода для вызова почтового запроса для передачи параметров. Карта не может быть определена как следующие два типа (за исключением случаев, когда URL-адрес использует заполнители для передачи параметров)

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

После тестирования правильный способ передачи параметров выглядит следующим образом

Описание передачи параметра GET

Если это запрос получения, и вы хотите инкапсулировать параметры в карту для передачи, карта должна использовать HashMap, а URL-адрес должен использовать заполнители, как показано ниже:

Ссылка:

Более сложный POST запросы¶

Как правило, вы хотите отправить некоторые закодированные в форме данные —
очень похоже на HTML-форму. Для этого просто передайте словарь в аргумент
. Ваш словарь данных будет автоматически закодирован, когда будет
сделан запрос:

>>> payload = {'key1' 'value1', 'key2' 'value2'}

>>> r = requests.post("https://httpbin.org/post", data=payload)
>>> print(r.text)
{
  ...
  "form": {
    "key2": "value2",
    "key1": "value1"
  },
  ...
}

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

>>> payload_tuples = 
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
>>> payload_dict = {'key1' 'value1', 'value2']}
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
>>> print(r1.text)
{
  ...
  "form": {
    "key1": [
      "value1",
      "value2"

  },
  ...
}
>>> r1.text == r2.text
True

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

Например, GitHub API v3 принимает данные POST/PATCH в кодировке JSON:

>>> import json

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some' 'data'}

>>> r = requests.post(url, data=json.dumps(payload))

Вместо того, чтобы кодировать самостоятельно, вы также можете передать
его напрямую, используя параметр (добавлен в версии 2.4.2), и он будет
закодирован автоматически:

>>> url = 'https://api.github.com/some/endpoint'
>>> payload = {'some' 'data'}

>>> r = requests.post(url, json=payload)

Обратите внимание, что параметр игнорируется, если передан
или

Дополнительные команды для просмотра параметров Response библиотеки Requests Python

Пример скрипта Python:

# Импорт библиотеки requests
import requests

# Запрос GET (Отправка только URL без параметров)
response = requests.get("http://api.open-notify.org/iss-pass.json?lat=40.71&lon=-74")

# Вывод ответа, через пользовательскую функцию jprint
print("response:\n{}\n\n".format(response))
print("response.url:\n{}\n\n".format(response.url))                 #Посмотреть формат URL (с параметрами)
print("response.headers:\n{}\n\n".format(response.headers))         #Header of the request
print("response.status_code:\n{}\n\n".format(response.status_code)) #Получить код ответа
print("response.text:\n{}\n\n".format(response.text))               #Text Output
print("response.encoding:\n{}\n\n".format(response.encoding))       #Узнать, какую кодировку использует Requests
print("response.content:\n{}\n\n".format(response.content))         #В бинарном виде
print("response.json():\n{}\n\n".format(response.json()))           #JSON Output

Результат:

response:
<Response >


response.url:
http://api.open-notify.org/iss-pass.json?lat=40.71&lon=-74


response.headers:
{'Server': 'nginx/1.10.3', 'Date': 'Tue, 07 Apr 2020 05:44:13 GMT', 'Content-Type': 'application/json', 'Content-Length': '519', 'Connection': 'keep-alive', 'Via': '1.1 vegur'}


response.status_code:
200


response.text:
{
  "message": "success", 
  "request": {
    "altitude": 100, 
    "datetime": 1586237266, 
    "latitude": 40.71, 
    "longitude": -74.0, 
    "passes": 5
  }, 
  "response": 
}



response.encoding:
None


response.content:
b'{\n  "message": "success", \n  "request": {\n    "altitude": 100, \n    "datetime": 1586237266, \n    "latitude": 40.71, \n    "longitude": -74.0, \n    "passes": 5\n  }, \n  "response": \n}\n'


response.json():
{'message': 'success', 'request': {'altitude': 100, 'datetime': 1586237266, 'latitude': 40.71, 'longitude': -74.0, 'passes': 5}, 'response': }

Скачивание больших файлов с помощью cURL

Для того, чтобы скачать большой файл пригодится этот способ:

$url = 'https://example.com/big_file.zip'; // откуда скачиваем файл
$path = __DIR__ . '/big_file.zip';  // куда сохраняем файл

$fp = fopen($path, 'w');
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($ch);
curl_close($ch);
fclose($fp);

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

Также к памяти будет требователен следующий код:

$ch = curl_init('https://example.ru/big_file.zip');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
$data = curl_exec($ch);
curl_close($ch);

file_put_contents(__DIR__ . '/big_file.zip', $data);

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

POST запросы cUrl в PHP

$array = array(
'login' => 'user',
'password' => '123'
);

$ch = curl_init('https://asgeto.ru');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $array);

// Или предать массив строкой:
// curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($array, '', '&'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HEADER, false);
$html = curl_exec($ch);
curl_close($ch);

echo $html;

Функция file_get_contents() так же умеет отправлять POST запросы. Для этого нужно использовать заголовки:

$headers = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded' . PHP_EOL,
'content' => 'login=user&password=123',
),
));

echo file_get_contents('https://asgeto.ru', false, $headers);

👨‍💻 Создаем запрос к OpenWeatherAPI с помощью curl

  • Предположим, что практическое занятие раздела выполнено, возвращаемся в Postman.
  • В любом запросе кликаем на кнопку под кнопкой
  • В диалоговом окне “Generate Code Snippets” выбираем cURL из выпадающего списка и нажимаем на кнопку

Код Postman для запроса прогноза погоды OpenWeatherMap выглядит в формате cURL следующим образом:

Postman добавил свою информацию о хедере (обозначено -Н) Тэги добавленного заголовка можно удалить. Также можно удалить знаки “», они добавлены для читаемости текста.

Кроме того, обратите внимание, что в Windows нужно изменить одинарные кавычки на двойные, потому что одинарные кавычки не поддерживаются в терминале Windows по умолчанию. Вот запрос curl с удаленными символами -H и обратной косой чертой, а одинарные кавычки преобразованы в двойные кавычки:

Вот запрос curl с удаленными символами -H и обратной косой чертой, а одинарные кавычки преобразованы в двойные кавычки:

  • Curl доступен на MacOS по умолчанию. Если на Windows curl еще не установлен, то инструкции по установке по , нужно выбрать одну из бесплатных версий с правами Администратора.
  • Открываем терминал
    • на OS Windows нажимаем и вводим команду , Правой кнопкой мыши вызываем меню и выбираем для вставки запроса.
    • на MacOS открываем iTerm или терминал, нажимая и вводим команду Вставляем запрос в командную строку и жмем кнопку .

Ответ от OpenWeatherMap на наш запрос будет выглядеть так:

Этот запрос минимизирован. Вы можете развернуть его, например на сайте JSON pretty print или, на MacOS с установленным Python добавив в конец cURL запроса, чтобы минимизировать JSON в ответе (Для подробностей можно посмотреть ветку на Stack Overflow по этой теме).

Самостоятельно сделаем curl запрос на 5-дневный прогноз, сохраненный в Postman. И третий API запрос OpenWeatherMap? сохраненный в Postman тоже выполняем в curl

Получаем последний статус Twitter

С помощью PHP и cURL очень просто получить статус определённого пользователя. Данную информацию можно выводить в блоге.

function get_status($twitter_id, $hyperlinks = true) {
    $c = curl_init();
    curl_setopt($c, CURLOPT_URL, "http://twitter.com/statuses/user_timeline/$twitter_id.xml?count=1");
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    $src = curl_exec($c);
    curl_close($c);
    preg_match('/<text>(.*)<\/text>/', $src, $m);
    $status = htmlentities($m);
    if( $hyperlinks ) $status = ereg_replace("]+://]+/]", '<a href="%5C%22%5C%5C0%5C%22">\\0</a>', $status);
    return($status);
}

Использовать функцию очень просто:

echo get_status('catswhocode');

Редиректы (перенаправления)

Guzzle будет автоматически следовать за редиректами, только если чётко не указать этого не делать. Вы можете настроить поведение перенаправления, используя опцию запроса :

  • Установите значение , чтобы включить нормальные перенаправления с максимальным количеством 5 перенаправлений. Это значение по умолчанию.
  • Установите в , чтобы отключить перенаправления.
  • Передайте ассоциативный массив, содержащий ключ , чтобы указать максимальное количество перенаправлений, и при необходимости укажите значение ключа , чтобы указать, следует ли использовать строгие перенаправления, совместимые с RFC (что означает запросы перенаправления POST со следующими запросами тоже типа POST, тогда как в обычном режиме большинство браузеров по умолчанию перенаправляют запросы POST со следующими запросами GET)./li>
$response = $client->request('GET', 'http://github.com');
echo $response->getStatusCode();
// 200

В следующем примере показано, как можно отключить редиректы:

$response = $client->request('GET', 'http://github.com', );
echo $response->getStatusCode();
// 301

Подготовка Requests¶

Каждый раз, когда вы получаете объект из
вызова API или сеанса, атрибут фактически является использованным
. В некоторых случаях вы можете захотеть проделать
дополнительную работу с телом или заголовками (или чем-то ещё) перед отправкой
запроса. Простой рецепт для этого следующий:

from requests import Request, Session

s = Session()

req = Request('POST', url, data=data, headers=headers)
prepped = req.prepare()

# сделать что-нибудь с prepped.body
prepped.body = 'No, I want exactly this as the body.'

# сделать что-нибудь с prepped.headers
del prepped.headers'Content-Type'

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)

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

Однако приведенный выше код теряет некоторые преимущества наличия объекта
Requests . В частности, к вашему запросу не
будет применяться состояние уровня , такое как
cookie. Чтобы получить
с этим состоянием, замените вызов на вызов , например так:

from requests import Request, Session

s = Session()
req = Request('GET',  url, data=data, headers=headers)

prepped = s.prepare_request(req)

# сделать что-нибудь с prepped.body
prepped.body = 'Seriously, send exactly these bytes.'

# сделать что-нибудь с prepped.headers
prepped.headers'Keep-Dead' = 'parrot'

resp = s.send(prepped,
    stream=stream,
    verify=verify,
    proxies=proxies,
    cert=cert,
    timeout=timeout
)

print(resp.status_code)

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

Например: самоподписанные
сертификаты SSL, указанные в , не будут учитываться. В
результате выдается . Вы можете обойти это
поведение, явно объединив настройки среды в свой сеанс:

Ответ (Response)

class Cake\Http\Response

Cake\Http\Response — это класс ответа по умолчанию в CakePHP. Он объединяет в себе определенный функционал для генерации HTTP ответов приложения. Cake\Http\Response также помогает при тестировании, поскольку его можно обойти/пропустить, для проверки заголовков, которые будут отправлены. Подобно Cake\Http\ServerRequest, Cake\Http\Response объединяет ряд методов, ранее относящихся к контроллеру: RequestHandlerComponent и Dispatcher. Старые методы устарели в пользу использования Cake\Http\Response.

Response предоставляет интерфейс для объединения задач, связанных с ответом, таких как:

  • Отправка заголовков для перенаправления;
  • Отправка content type заголовков;
  • Отправка прочих заголовков;
  • Отправка тела ответа.

Работа с типами контента

Cake\Http\Response::withType($contentType = null)

Вы можете управлять Content-Type в ответах приложения с помощью Cake\Http\Response::withType(). Если приложению нужно иметь дело с Content-Types, которые не встроены в Response, их можно также сопоставить с type():

Обычно необходимо отображать дополнительные Content-Type в обратном вызове beforeFilter() контроллера, для этого можно использовать функции автоматического переключения вида , при его использовании.

Отправка файлов

Cake\Http\Response::withFile($path, $options = [])

Бывают случаи, когда необходимо отправлять файлы в качестве ответов на запросы. Это можно делать, используя Cake\Http\Response::withFile():

Как показано в приведённом выше примере, необходимо в метод передать путь к файлу. CakePHP отправит соответствующий заголовок типа контента, если это известный тип файла, указанный в Cake\Http\Reponse::$_mimeTypes. Вы можете добавлять новые типы до вызова Cake\Http\Response::withFile() с помощью метода Cake\Http\Response::withType().

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

Поддерживаемые параметры:

  • name — позволяет указать альтернативное имя файла для отправки пользователю;
  • download — логическое значение, указывающее, следует ли устанавливать заголовки для принудительной загрузки.

Отправка строки в виде файла

Так же, можно ответить файлом, который не существует на диске, например, pdf или ics, сгенерированным «на лету» из строки:

Обратные вызовы также могут возвращать тело в виде строки:

Настройка заголовков

Cake\Http\Response::withHeader($header, $value)

Настройка заголовков выполняется с помощью метода Cake\Http\Response::withHeader(). Как и все методы интерфейса PSR-7, этот метод возвращает экземпляр new с новым заголовком:

Заголовки не отправляются при установке. Вместо этого они сохраняются до тех пор, пока не будет получен ответ Cake\Http\Server.

Теперь вы можете использовать удобный метод Cake\Http\Response::withLocation(), чтобы напрямую установить или получить заголовок местоположения перенаправления.

Настройка body

Cake\Http\Response::withStringBody($string)

Чтобы установить строку в качестве тела ответа, необходимо выполнить следующие действия:

Использование произвольных методов HTTP запроса

Мы уже рассмотрели (здесь) методы GET, PUT, HEAD, OPTIONS и другие. На самом деле, в качестве метода можно указать что угодно. Метод указывается после опции -X

Не все серверы одинаково реагируют на произвольные методы, например, команда:

curl -X 'HACK' -A 'Chrome' https://hackware.ru

вызовет ошибку «403 Forbidden».

Команда

curl -X 'HACK' -A 'Chrome' 87.236.16.208

вызовет ошибку «501 Not Implemented».

Команда на этот же сервер, но на 443 порт вместо 80:

curl -X 'HACK' -A 'Chrome' https://87.236.16.208 -k

вызовет ошибку веб-сервера «502 Bad Gateway».

Но современные веб-серверы Apache 2.4 с настройками по умолчанию просто обрабатывают незнакомые методы как если бы это был GET.

Проверим на нашем локальном сервере:

curl -v -H 'Hackware: Hello! How are you?' -X 'MIAL' localhost/headers.php

Как можно убедиться, несмотря на то, что строка запроса стала такой:

MIAL /headers.php HTTP/1.1

Веб-сервер и скрипт корректно обработали этот запрос и прислали ожидаемые данные.

Кажется, что всё как обычно, но такой запрос уже невозможно найти в Wireshark по фильтру

http

Продемонстрируем это скриншотами.

Поиск по фильтру «http» после выполнения команды с запросом методом GET:

curl -H 'Hackware: Hello! How are you?' localhost/headers.php

Всё как положено: HTTP запрос и ответ присутствуют.

Поиск по фильтру «http» после выполнения команды с запросом методом MIAL:

curl -H 'Hackware: Hello! How are you?' -X 'MIAL' localhost/headers.php

То есть ответ найден (потому что он обычный), а запрос уже нет.

POST несколько файлов с многократным кодированием¶

Вы можете отправить несколько файлов в одном запросе. Например, предположим,
что вы хотите загрузить файлы изображений в HTML-форму с несколькими полями
файлов «images»:

<input type="file" name="images" multiple="true" required="true"/>

Для этого достаточно прописать файлы в список кортежей :

>>> url = 'https://httpbin.org/post'
>>> multiple_files = 
...     ('images', ('foo.png', open('foo.png', 'rb'), 'image/png')),
...     ('images', ('bar.png', open('bar.png', 'rb'), 'image/png'))]
>>> r = requests.post(url, files=multiple_files)
>>> r.text
{
  ...
  'files': {'images': 'data:image/png;base64,iVBORw ....'}
  'Content-Type': 'multipart/form-data; boundary=3131623adb2043caaeb5538cc7aa0b3a',
  ...
}

Пользовательская аутентификация¶

Requests позволяет указать собственный механизм аутентификации.

У любого вызываемого объекта, который передаётся как аргумент методу
запроса, будет возможность изменить запрос перед его отправкой.

Реализации аутентификации являются подклассами , и их легко определить. Requests предоставляет две
общие реализации схемы аутентификации в : и .

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

from requests.auth import AuthBase

class PizzaAuth(AuthBase):
    """Присоединяет HTTP-аутентификацию Pizza к заданному объекту запроса."""
    def __init__(self, username):
        # настройте здесь любые данные, связанные с авторизацией
        self.username = username

    def __call__(self, r):
        # изменить и возвращает запрос
        r.headers'X-Pizza' = self.username
        return r

Затем мы можем сделать запрос, используя нашу Pizza Auth:

Использование прокси

Аргумент proxies используется для настройки прокси-сервера для использования в ваших запросах.

http = "http://10.10.1.10:1080"
https = "https://10.10.1.11:3128"
ftp = "ftp://10.10.1.10:8080"

proxy_dict = {
  "http": http,
  "https": https,
  "ftp": ftp
}

r = requests.get('http://sampleurl.com', proxies=proxy_dict)

Библиотека запросов также поддерживает прокси SOCKS. Это дополнительная функция, и она требует, чтобы перед использованием была установлена зависимость requests . Как и раньше, вы можете установить его с помощью pip:

$ pip install requests

После установки вы можете использовать его:

proxies = {
  'http': 'socks5:user::port'
  'https': 'socks5:user::port'
}

Получить содержимое страницы (GET)

Самый простой и обычный HTTP-запрос — получить содержимое заданного URL. URL может ссылаться на веб-страницу, изображение или какой либо-другой файл. Клиент отсылает GET-запрос на сервер и получает запрашиваемый документ. Если выполнить команду

$ curl http://curl.haxx.se

вы получите веб-страницу, выведенную в окно терминала (точнее, в стандартный вывод). Чтобы сохранить эту страницу в файл , нужно указать

$ curl http://curl.haxx.se -o "curl.html"

Все HTTP-ответы содержат набор заголовков, которые обычно скрыты. Чтобы увидеть эти заголовки вместе с самим документом, используйте ключ .

Команда nslookup

Эта команда также позволяет получить информацию по домену или по IP адресу. Основной синтаксис написания nslookup:

nslookup

где — указывать необязательно.

Самый простой пример использования nslookup приведем ниже:

nslookup freehost.com.ua

Можем выполнить и обратную задачу — по IP адресу узнать доменное имя сайта.

nslookup 194.0.200.202

Ниже приведем основные опции команды nslookup:

  • type – записывается тип записи DNS (к примеру, NS, TXT, SOA и др.);
  • port – указывается номер порта;
  • recurse – в случае, когда DNS не отвечает, использовать другие DNS;
  • retry – задается количество попыток;
  • timeout – время;
  • fail – в случае, когда DNS возвращает ошибку, необходимо использовать другой сервер.

Приведем примеры команды, с использованием опции type (тип записи), например, для получения записей типа NS, MX, TXT, SOA и т.д.:

nslookup -type=ns freehost.com.ua
nslookup -type=mx freehost.com.ua
nslookup -type=txt freehost.com.ua
nslookup -type=soa freehost.com.ua

Техническую информацию о домене можно получить в ответе, запустив команду nslookup с параметром для типа записи SOA:

  • origin — источник информации;
  • mail addr — указывает email address администратора домена;
  • serial — показывает время в формате timestamp;
  • refresh — выводит время в секундах, в течении которого нужно повторить подключения, чтобы обновить информацию;
  • retry — указывает время в секундах, через которое необходимо опять повторить подключения к DNS, в случае, если он недоступен;
  • expire — показывает интервал времени в секундах, через который нужно считать информацию, полученную от первого DNS, устаревшей;
  • minimum — это время в секундах, которое проходит до следующего обновления.
Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: