Некоторые снипеты PHP для работы со STEAM API

👁 420 просмотров

В данном посте представлены некоторые куски кода для элементарной работы со STEAM API на языке PHP. В частности рассматриваются вопросы импорта и экспорта вещей из STEAM и обратно, а также процесс обмена.

Авторизация у себя на сайте через STEAM профиль

Данная процедура дает возможность авторизироваться пользователям на сайте через данные, зарегистрированные на STEAM. Для этого нам , в первую очередь нужно заполучить ApiKey и далее через PHP код отправить запрос на получение. Сгенерировать ApiKey это можно через страницу разработчиков STEAM. Выглядит все это так

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

При создании авторизации необходимо позаботиться о 2-х вещах:

  • нужна кнопка со ссылкой передачи по GET- запросу ключа данных для авторизации;
  • нужна страница, которая получит данные после возврата со STEAM для последующей обработки у нас на сайте.

Давайте сначала передадим данные пользователя через GET по ссылке по нажатию на кнопку авторизации через STEAM

   //Общие данные для авторизации
    $steam=array(
        'key'=>'AAA862C800301CC07DA5D3D6095E8DAA',
        'redirect'=>'http://test.websofter.ru/mygamelogin/'
    );
    // Создание ссылки для аутентификации
    $steamHref="https://steamcommunity.com/openid/login?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=checkid_setup&openid.return_to=".urldecode($steam["redirect"])."%3Fstate=steam&openid.realm=".urldecode($steam["redirect"])."&openid.ns.sreg=http%3A%2F%2Fopenid.net%2Fextensions%2Fsreg%2F1.1&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select"; 
    
    echo "<a href=".$steamHref.">";
        echo "Войти через STEAM";
    echo "</a>";

Теперь необходимо позаботиться о возвращенных данных авторизируемого пользователя. Мы будем перенаправлены на страницу, которая содержится в параметре массива $steam[«redirect»] и после редиректа все данные будут приняты через GET — ответ

    $userInfo = null;
    // Проверка, что данные не пусты
    if(isset($_GET["state"]) && $_GET["state"]=="steam") 
    { 
        // Вытаскиваем id юзера
        preg_match("/^http:\/\/steamcommunity\.com\/openid\/id\/(7[0-9]{15,25}+)$/", $_GET["openid_identity"], $key);
        $key=$key[1];
	$userInfo = json_decode(file_get_contents("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=".$steam["key"]."&steamids=".$key)); // Получаем информацию о пользователе
	$userInfo=$userInfo->response->players[0]; // Переводим полученные данные в класс
    }
    //Выводим полученный массив с данными юзера, который хотел авторизироваться
    echo "<br/>id:".$userInfo->steamid."<br/>";
    print_r($userInfo);

 

Вывод вещей из инвентаря STEAM по ID игры

Допустим, есть задача вывести список вещей определенной игры из его инвентаря.  При просмотре ID игры добавляется в STEAM через http://steamcommunity.com/id/102177895/inventory/json/#gameid. Этот ID можно использовать для вывода данных в виде JSON — строки и обработав вывести весь инвентарь у себя на странице. Код ниже демонстрирует вывод вещей из CS:GO

$get_content = file_get_contents("http://steamcommunity.com/id/102177895/inventory/json/578080/2");
$data_image = (array) json_decode($get_content) -> rgInventory;
$count_content = count($data_image);
$data_content = (array) json_decode($get_content, TRUE);
echo "Total items: $count_content <br><br>";
for ($i=0; $i<$count_content; $i++) {
    $element_name = array_shift($data_content[rgInventory]);
    $name_item = "$element_name[classid]_$element_name[instanceid]";
    echo "<img src='http://steamcommunity-a.akamaihd.net/economy/image/";
    print_r($data_content['rgDescriptions'][$name_item]['icon_url']);
    echo "' style='width:90px; height:90px; float:left; margin: 5px; outline: 1px solid black;'>";
}

 

 

Клиентская часть организации игры «Рулетка»

👁 79 просмотров

Это краткое описание того, как разработать интерфейс игры «Рулетка» или Programming Roulette Game Principle. В посте представлена принципиальная схема и код рабочей программы.

Схема работы

Для лучшего понимания хорошо бы рассмотреть схему того, что мы хотим проектировать

Опишем все параметры, которые участвуют в процессе:

  • ni — исходный порядковый номер выигранной карты;
  • m — число карт в видимой части галереи;
  • gW — длина галереи;
  • W — длина галереи, вычисленная исходя из помещающихся колод в видимой части;
  • xW — длина колоды карт до выигранной карты;
  • dW — разница между длиной до выигранной и длиной галереи;
  • alignLeft — искомый параметр для перемещения, чтобы выигранная карта встала по центру.

Рулетка у нас будет иметь один входной параметр и один выходной параметр, т.е., выходной зависит от входного:

  • ni — номер выигранной карты в списке;
  • alignLeft — это то, насколько надо передвинуть весь список, чтобы выигранная карта оказалась в центре рулетки.

Примечание. ni — обычно генерируется на сервере и передается всем пользователям через сокет-сообщение. Генерацию получают случайными числами из всего списка. К примеру, есть массив всех карт и мы вычисляем общее количество карт в массиве и генерируем случайное число от 0 до количества карт. Серверную часть мы не рассматриваем, поэтому ограничимся этим кратким описанием.

alignLeft — это величина css — параметра align-left первой карты. По сути дела мы определяем с серверной части номер выигранной карты из всего списка и в клиентской части при помощи JS мы визуально показываем, что именно карта и выиграна, хотя, в необходимости такой бы не было, можно было бы просто номер выигранной карты показать, но тогда смысл назвать весь этот процесс игры «Рулеткой» потерялся бы.

Исходный код рулетки

Код HTML

<!DOCTYPE html>
<title>Roulette</title>
<body>
  <div id="container">
    <div class="cardList">
      <div class="card">1</div>
      <div class="card">14</div>
      <div class="card">2</div>
      <div class="card">13</div>
      <div class="card">3</div>
      <div class="card">12</div>
      <div class="card">4</div>
      <div class="card">0</div>
      <div class="card">11</div>
      <div class="card">5</div>
      <div class="card">10</div>
      <div class="card">6</div>
      <div class="card">9</div>
      <div class="card">7</div>
      <div class="card">8</div>
      <div class="card">1</div>
      <div class="card">14</div>
      <div class="card">2</div>
      <div class="card">13</div>
      <div class="card">3</div>
      <div class="card">12</div>
      <div class="card">4</div>
      <div class="card">0</div>
      <div class="card">11</div>
      <div class="card">5</div>
      <div class="card winner">10</div>
      <div class="card">6</div>
      <div class="card">9</div>
      <div class="card">7</div>
      <div class="card">8</div>
    </div>
    <div class="bts">
      <span>Balance:</span>
      <span>*</span>
      <button id="spin" class="cooldown">Spin</button>
    </div>
    <span id="timer"></span>
  </div>
</body>

Код JavaScript

var cardList = $('.cardList');
var cards = $('.card');
var cardWidth = $(cards).width();
var cardListWidth = $(cardList).width();
var winnerCard = ".winner"; //Класс, выигранной карты
var winner = null;
var speed = 3000;
var winnerNumber = null;
/**Вычислим номер с эти классом или можно без класса сразу указать номер*/
$.each(cards, function(i, value) {
  var isWinner = $(this).hasClass("winner");
  if (isWinner) {
    winner = $(this);
    winnerNumber = (i + 1);
  }
});
console.log("winNumber:" + winnerNumber + " winner" + winner.html());
//ni - порядковый номер выигранной карты
//m - число карт в видимой части галереи
//gW - длина галереи
//xW - длина колоды карт до выигранной карты
//dW - разница между длиной до выигранной и длиной галереи
var ni = winnerNumber;
//
var m = cardListWidth / cardWidth; //Math.floor(cardListWidth/cardWidth);
var gW = m * cardWidth; //
var sW = gW / 2;
var sw = cardWidth / 2
var xW = ni * cardWidth;
var dW = xW - gW + sW + sw; //
//
var leftAlign = dW;

$('#spin').click(function() {
  cards.first().css('margin-left', 0);
  cards.first().animate({
    "margin-left": -leftAlign + "px"
  }, speed);
  return false;
});

Код CSS

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.card:nth-child(odd) {
  background: red;
}

.card:nth-child(even) {
  background: blue;
}

.cardList {
  height: 100px;
  width: 100%;
  position: relative;
  margin: 10px;
  overflow: hidden;
  white-space: nowrap;
}

.card {
  display: inline-block;
  text-align: center;
  height: 100px;
  width: 100px;
  line-height: 100px;
  font-family: monospace;
  font-size: 2em;
  color: #fff;
  margin: 0 auto;
}

#container {
  text-align: center;
  background: #f7f7f7;
}

.cardcontainer {}

.cardList::before,
.cardList::after {
  content: '';
  display: block;
  z-index: 100;
  width: 0px;
  height: 0px;
  transform: translateX(-50%);
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
}

.cardList::before {
  position: absolute;
  top: 0px;
  left: 50%;
  border-top: 12px solid #c3c3c3;
}

.cardList::after {
  position: absolute;
  bottom: 0px;
  left: 50%;
  border-bottom: 12px solid #c3c3c3;
}

.cardList {
  margin: 0 auto;
}

button {
  margin: 10px;
  padding: 8px;
  font-family: monospace;
}

В коде мы указываем для выигришной карты класс winner и дальше в списке мы ищем порядковый номер карты с этим классом, но можно было бы указать сразу номер — тут как программу составить. В результате работы данного кода, при нажатии на кнопку рулетка переместится так, что карта, помеченная, как побежденная будет в центре между стрелками, что покажет пользователю, что она выиграла. Результат показан ниже на картинке

 

Полностью код можно скачать из репозитория проекта на GitHub.

Интеграция Visual Studio с Mac OS на VirtualBox для построения проекта на Cordova

👁 94 просмотров

В данном посте рассмотрим пример интеграции Visual Studio 2017 с Mac OS, который запущен через VirtualBox. Это нам нужно для построения проекта Cordova для запуска и эмуляции под iOS. Построить и эмулировать на нативном уровне для iOS можно только имея систему Mac, поэтому у VisualStudio для этого в параметрах есть пункт настройки удаленного сервера с Mac OS, куда он отправляет исходники для построения и запускает построенное приложение в IDE XCode.

Требования к хосту Windows

Хост Windows должен иметь:

  • Память не меньше 4 Гб;
  • Windows OS;
  • Установленный VirtualBox;
  • Установленный NodeJS;
  • Установленный VisualStudio;
  • Установленные компоненты для VisualStudio для построения проектов Cordova;
  • Включенный параметр SMB по пути в панели управления Панель управления -> Все элементы панели управления -> Программы и компоненты -> Компоненты Windows 

 

Требования к виртуальному серверу на VirtualBox

  • Виртуальный образ Mac OS для запуска через VirtualBox;
  • Настройки VirtualBox могут быть любыми для комфортной работы, как на хосте, так и на виртуальном сервере, но основной параметр сетевого адаптера обязательно должен быть установлен, как «Сетевой мост», он будет отвечать за удачное соединения с сервером построения Mac OS через его IP 

Требования к Mac OS

  • Установленный IDE XCode;
  • Установленный NodeJS;
  • Установленный remotebuild

 

Все, по порядку

Для начала на Mac OS устанавливаем инструменты командной строки для XCode

xcode-select -–install

Устанавливаем пакеты NodeJS для удаленного построения

sudo npm install -g remotebuild

Данный пакет построения нам нужен будет для удаленного соединения и построения приложения.

Теперь нам необходимо на Mac OS запустить агент удаленного построения, чтобы потом через Visual Studio к нему подключаться. Подключиться можно 2-мя путями:

  • через безопасное соединение, необходимо будет генерировать ключи доступа;
  • небезопасный режим соединения через HTTP, нужен будет только IP адрес и порт запущенного процесса построения.

Мы будем использовать небезопасный режим, потому что так проще для демонстрации построения. Для соединения с VisualStudio, как уже говорилось нужен будет порт процесса построения и IP — адрес сервера.

Для установки и запуска агента удаленного запуска выполняем команду в консоли Mac

 remotebuild --secure false

Котоырй выдаст нам порт. По умолчанию он равен 3000. А чтобы узнать текущий IP — адрес Mac — машины заходим в параметры и там в пункте настройки сетей копируем IP

После того, как мы запустили агент удаленного построения, узнали порт и IP — адрес Mac OS заходим в параметры настройки Cordova iOS в Visual Studio и прописываем все их туда

Должно быть, как ниже на фото

  • Enable remote iOS processing — устанавливаем в True, так как мы строим приложении на через удаленный IP;
  • Host — IP  -адрес нашего Mac OS сервера;
  • Port — порт нашего запущенного агента удаленного сервера;
  • Secure mode — режим безопасного соединения устанавливаем в False, как мы раньше говорили, будем соединяться через небезопасный режим;
  • Securyti PIN — пин код длябезопасного соединения. В данном случае, оставляем пустым.

Все выше перечисленные параметры у нас основные, но стоит нам запустить, как Visual Studio начнет строить приложении и к какому-то момент выдаст ошибку, что не хватает прав на чтение файлов из папки построения на удаленном Mac OS. Для решения этой проблемы у нас в данном диалоге есть еще один параметр, который называется iOS device supported folder которому надо задать значение в виде пути

\\<IPAddress>\Applications\Xcode.app\Contents\Developer\Platforms\iPhoneOS.platform\DeviceSupport

где <IPAddress> — это IP адрес вашего Mac OS сервера, все как на примере внизу.

 

Время таймаута SSH — соединения

👁 159 просмотров

Проблема возникла из-за необходимости поддерживать достаточно много времени процесса соединения с сервером Linux, чтобы каждый раз не соединяться и каждый раз не вводит монотонные строки входа в папку проекта на сервере. Для настройки. Настроить нужно, как серверную часть, так и клиентскую, если к примеру соединение производится через PuTTY или через встроенный терминал в рабочем IDE. У меня была проблема последнего характера.

Настройка серверной части

Проблема изначально заключалась в следующем — при подключении к серверу по SSH сессия обрывалась достаточно быстро, задачу можно было решить увеличив интервал таймаута. Соотвествующие настройки можно произвести отредактировав файл sshd_config расположенный в директории /etc/ssh/sshd_config.

В данном файле необходимо найти несколько параметров, которые отвечают за данную опцию:

  • TCPKeepAlive — определяет поддержку соединение в активном состоянии, для этого используются специальные контрольные сообщения, посылаемые с определенным интервалом времени. Если директива установлена, обрыв соединения будет вовремя замечен, а соответствующий процесс будет убит. Возможные значения «yes», «no».
  • ClientAliveInterval — время простоя клиента в секундах, после которого демон sshd отправляет через защищённый канал запрос клиенту. Директива работает только для протокола 2. По-умолчанию установлен в 0, т.е. клиенту вообще не будут направляться такие запросы.
  • ClientAliveCountMax — количество проверок доступности клиента, которые могут оставаться без ответа. Если предел достигнут, sshd завершит сеанс. Данные запросы, отличаются от TCPKeepAlive, так как отправляются через защищённый канал и не могут быть подменены, в то время как TCPKeepAlive такую возможность допускает. СlientAlive полезен, если поведение клиента или сервера зависит от активности соединения. Если ClientAliveInterval равно 15 секундам, а значение ClientAliveCountMax оставлено по-умолчанию, не отвечающие клиенты SSH, будут отключаться приблизительно через 45 секунд. Параметр работает только для протокола версии 2. По-умолчанию установлено значение 3.

В нашем случае установим данные параметры следующим образом:

TCPKeepAlive yes
ClientAliveInterval 60
ClientAliveCountMax 180

После этого перезапускаем сервис SSHD

service sshd restart

Это соответствует тому, что TCPKeepAlive проверяет находится ли клиент на линии сервера, ClientAliveInterval —  это время простоя в секундах, после которого демон sshd отправляет запрос клиенту, ClientAliveCountMax указывает количество проверок доступности клиента, выше в примере указана поддержка соединения в течении 3 часов (180 минут).

Настройка таймаута соединения в SSH — клиенте PuTTY

В самом клиенте PuTTy также желательно произвести нехитрые настройки. Для этого идем в секцию «Connecton» и выставляем параметр «Seconds between keepalives» равным 60 секундам. Так же можно отметить опцию «Enable TCP keepalives»

Настройка таймаута соединения в WinSCP

Открываем менеджер соединений WinSCP и клацаем на нужное соединением, после жмем на кнопку «Еще»Далее, в открывшемся окне, в ветке «Подключение» указываем необходимое время для таймаута

 

Как это сделать в своем рабочем IDE

Зависит от IDE, в любом случае в параметрах и настройках в разделе SSH — соединения. В моем случае проблема была с таймаутом соединения терминала IDE NetBeans. Может и есть, где настраивается время, но я не нашел данный параметр в среде, но проблема исчезла после увеличения таймаута со стороны сервера, так что проблемы с клиентом нет.

Сокеты в PHP. Использование WebSocket с phpws

👁 338 просмотров

В данной статье рассмотрим работу с библиотекой phpws, которая нужна для организации приложений или WEB — приложений на основе сокетов и запустим парочку стандартных примеров, которые представлены на странице репозитория GitHub данного проекта.

Примечание. Сокеты у нас будут работать, как на серверной части, так и на клиентской. На серверной части этим займется стандартный WebSocket, который появился в HTML5,  а работу на серверной части, где у нас PHP будет выполнять библиотека phpws. Есть много подобных библиотек, пожалуй, особенно следует отметить Ratchet, который мне показался громоздким для моего маленького проекта и я остановился на phpws.

Нам нужен Composer

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

$ curl -s https://getcomposer.org/installer | php

Мы его скачали, но команды composer не будут выполняться через PATH, поэтому переместим скачанное в /usr/local/bin

$ mv composer.phar /usr/local/bin/composer

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

$ composer

Для Windows и Mac можно посмотреть инструкцию на офф-сайте.

Примечание. Все зависимости, которые нужно подключать надо указывать в файле composer.json в корне проекта, который скачает, обновит и соберет все зависимости в одну папку vendor, из которого потом можно загружать через автозагрузчик классов. У Composer есть свое хранилище пакетов и библиотек и называется Packagist, который позволяет указывать vendor/package и он будет установлен. Да, можно указывать конкретные адреса svn/git репозиториев в composer.json, но это неудобно. Намного удобнее иметь какой-то центральный пункт, где есть соответствия пакетов с их адресами репозиториев. Это Packagist.

Нам нужна библиотека phpws

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

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/Devristo/phpws"
        }
    ],
    "require": {
        "devristo/phpws": "dev-master"
    }
}

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

Выполним данный файл командой

$ composer install

После чего в папке появится подпапка vendor со скачанными библиотеками и нам остается их подключить и использовать.

Нам нужны базовые понимания работы WebSocket с PHP

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

  1. client.html — файл клиентской части, который видит тот, кто за браузером. В нем с сокетом работает JavaScript;
  2. server.php — собственно, наш сокет-сервер, который обрабатывает все запросы от клиента и отвечает ему обработанными обтветами.

Для соединения нам надо указать схему соединения или протокол связи, ip — адрес сервера. Если удаленный сервер, то надо указать ip — адрес хоста или VPS, а если локальный, то localhost, который равен адресу 127.0.0.0 и указываем еще порт, на котором служба сервера будет запущена под собственным PID. Все эти данные указываются при создании экземпляра соединения.

Для клиентской части:

var socket = "ws://127.0.0.0:12345/";

Для серверной части:

$server = new WebSocketServer("tcp://127.0.0.0:12345", $loop, $logger);

 

Стандартный пример вывода текущего времени сервера с обновлением до секунды

Для работы данного примера нужно единожды запустить файл server.php через консоль и после выполнения данного скрипта запуститься сокет-сервер со своим PID

Что делает пример? В примере показано, как до долей секунды сокет обновляет информацию времени на сервер и выдает его клиенту

Клиентская часть:

<html>
    <head>
        <title>Timers</title>
    </head>
    <body>
        <h1>Server Time</h1>
        <div>Status: <span id="status"></span></div>
        <div>Time: <span style="font-size:bold;" id="time"></span></div>
    </body>
</html>

и

var socket = new WebSocket("ws://localhost:12345");
socket.onopen    	= function(msg){ document.getElementById("status").innerHTML = 'Online'; };
socket.onclose   	= function(msg){ document.getElementById("status").innerHTML = 'Offline'; }
socket.onmessage 	= function(msg){ document.getElementById("time").innerHTML = msg.data; };

Серверная часть:

#!/php -q
<?php
require_once("vendor/autoload.php");
use Devristo\Phpws\Server\WebSocketServer;

$loop = \React\EventLoop\Factory::create();

// Create a logger which writes everything to the STDOUT
$logger = new \Zend\Log\Logger();
$writer = new Zend\Log\Writer\Stream("php://output");
$logger->addWriter($writer);

// Create a WebSocket server using SSL
$server = new WebSocketServer("tcp://127.0.0.0:12345", $loop, $logger);

// Each 0.5 seconds sent the time to all connected clients
$loop->addPeriodicTimer(0.5, function() use($server, $logger){
    $time = new DateTime();
    $string = $time->format("Y-m-d H:i:s");
    $logger->notice("Broadcasting time to all clients: $string");
    foreach($server->getConnections() as $client)
        $client->sendString($string);
});


// Bind the server
$server->bind();

// Start the event loop
$loop->run();

 

Стандартный пример простого чата

Показан пример простого чата. Визуально он имеет вид, как на картинке

Клиентская часть:

<!DOCTYPE html>
<html>
    <head>
        <title>WebSocket TEST</title>
    </head>
    <body onload="init()">
        <h3>WebSocket Test</h3>
        <div id="log"></div>
        <label>Message <input id="msg" type="text" onkeypress="onkey(event)"/></label>
        <button onclick="send()">Send</button>
        <button onclick="quit()">Quit</button>
        <div>Server will echo your response!</div>
    </body>
</html>
var socket; 


function createSocket(host) {

    if ('WebSocket' in window)
        return new WebSocket(host);
    else if ('MozWebSocket' in window)
        return new MozWebSocket(host);

    throw new Error("No web socket support in browser!");
}

function init() {
    var host = "ws://127.0.0.0:12345/chat";
    try {
        socket = createSocket(host);
        log('WebSocket - status ' + socket.readyState);
        socket.onopen = function(msg) {
            log("Welcome - status " + this.readyState);
        };
        socket.onmessage = function(msg) {
            log(msg.data);
        };
        socket.onclose = function(msg) {
            log("Disconnected - status " + this.readyState);
        };
    }
    catch (ex) {
        log(ex);
    }
    document.getElementById("msg").focus();
}

function send() {
    var msg = document.getElementById('msg').value;

    try {
        socket.send(msg);
    } catch (ex) {
        log(ex);
    }
}
function quit() {
    log("Goodbye!");
    socket.close();
    socket = null;
}

function log(msg) {
    document.getElementById("log").innerHTML += "<br>" + msg;
}
function onkey(event) {
    if (event.keyCode == 13) {
        send();
    }
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>

Серверная часть:

#!/php -q
<?php

// Set timezone of script to UTC inorder to avoid DateTime warnings in
// vendor/zendframework/zend-log/Zend/Log/Logger.php
date_default_timezone_set('UTC');

require_once("vendor/autoload.php");

// Run from command prompt > php chat.php
use Devristo\Phpws\Framing\WebSocketFrame;
use Devristo\Phpws\Framing\WebSocketOpcode;
use Devristo\Phpws\Messaging\WebSocketMessageInterface;
use Devristo\Phpws\Protocol\WebSocketTransportInterface;
use Devristo\Phpws\Server\IWebSocketServerObserver;
use Devristo\Phpws\Server\UriHandler\WebSocketUriHandler;
use Devristo\Phpws\Server\WebSocketServer;

/**
 * This ChatHandler handler below will respond to all messages sent to /chat (e.g. ws://localhost:12345/chat)
 */
class ChatHandler extends WebSocketUriHandler {

    /**
     * Notify everyone when a user has joined the chat
     *
     * @param WebSocketTransportInterface $user
     */
    public function onConnect(WebSocketTransportInterface $user){
        foreach($this->getConnections() as $client){
            $client->sendString("User {$user->getId()} joined the chat: ");
        }
    }

    /**
     * Broadcast messages sent by a user to everyone in the room
     *
     * @param WebSocketTransportInterface $user
     * @param WebSocketMessageInterface $msg
     */
    public function onMessage(WebSocketTransportInterface $user, WebSocketMessageInterface $msg) {
        $this->logger->notice("Broadcasting " . strlen($msg->getData()) . " bytes");

        foreach($this->getConnections() as $client){
            $client->sendString("User {$user->getId()} said: ".$msg->getData());
        }
    }
}
class ChatHandlerForUnroutedUrls extends WebSocketUriHandler {
    /**
     * This class deals with users who are not routed
     */
    public function onConnect(WebSocketTransportInterface $user){
		//do nothing
		$this->logger->notice("User {$user->getId()} did not join any room");
    }
    public function onMessage(WebSocketTransportInterface $user, WebSocketMessageInterface $msg) {
    	//do nothing
        $this->logger->notice("User {$user->getId()} is not in a room but tried to say: {$msg->getData()}");
    }
}


$loop = \React\EventLoop\Factory::create();

// Create a logger which writes everything to the STDOUT
$logger = new \Zend\Log\Logger();
$writer = new Zend\Log\Writer\Stream("php://output");
$logger->addWriter($writer);

// Create a WebSocket server
$server = new WebSocketServer("tcp://127.0.0.0:12345", $loop, $logger);

// Create a router which transfers all /chat connections to the ChatHandler class
$router = new \Devristo\Phpws\Server\UriHandler\ClientRouter($server, $logger);
// route /chat url
$router->addRoute('#^/chat$#i', new ChatHandler($logger));
// route unmatched urls durring this demo to avoid errors
$router->addRoute('#^(.*)$#i', new ChatHandlerForUnroutedUrls($logger));

// Bind the server
$server->bind();

// Start the event loop
$loop->run();

Запускать данный пример, надо, как и предыдущий — единожды через консоль запускаем файл server.php и через браузер входим в клиентскую часть client.html, подключив скрипт script.js .

Работа с PHP  — сокет сервером из консоли

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

Сначала выводим PID процесса запущенного сокет-сервера. Его мы узнаем посмотрев список запущенных сокетов через их порты через команду:

netstat --tcp --listening --program

Находя из списка нужный PID убиваем его через команду:

kill %pid%

Идеально, если закроем WebSocket через клиентскую JavaScript часть командой перед запуском «убийства» PID:

socket.close();
socket = null;

 

Использование CasperJS/PhantomJS на Linux — сервере

👁 67 просмотров

PhantomJS – это возможность работать с WebKit из консоли используя JavaScript и без браузера. Для чего это нужно? Основное предназначение — парсить сайты, заполучив весь сгенерированный HTML — код, даже если DOM — дерево генерируют запросы и ответы AJAX или просто отскринить сайт целиком. Как правило, PHP не позволяет парсить динамически загружаемые страницы и отличным решением является метод, когда мы получаем сгенерированный DOM HTML страницы, сохранив в какой-то директории и далее вскармливаем его статическому PHP  -парсеру.

CasperJS – вспомогательный инструмент написанный на JavaScript как обертка PhantomJS. На официальном сайте перечислены следующие основные возможности:

  • определение и порядок итераций браузера
  • заполнение и отправка форм
  • клик и переход по ссылкам
  • создание скриншотов страницы и ее части
  • удаленное тестирование DOM
  • логирование событий
  • загрузка ресурсов и подключение библиотек
  • написание функциональных тестов и сохранение в формате JUnit XML
  • Допиливание веб контента

CasperJS облегчает использование сложного и голого API PhantomJS, поэтому он и называется вспомогательным инструментом, который облегчает использование PhantomJS. Оба они устанавливаются в систему и вызываются из командной строки, через которую передаются параметры для ввода и вывода значений и результата, но мы можем вызывать их и из PHP через команды в функции exec(…), после чего можем отскринить, сохранять или статически допарсить полученный результат через инструменты PHP.

Установка PhantomJS

cd /usr/local/share
sudo wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
sudo tar -xvf phantomjs-2.1.1-linux-x86_64.tar.bz2
sudo mv phantomjs-2.1.1-linux-x86_64 phantomjs-2.1.1
sudo ln -s /usr/local/share/phantomjs-2.1.1/ /usr/local/share/phantomjs
sudo ln -s /usr/local/share/phantomjs/bin/phantomjs /usr/local/bin/phantomjs

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

Чтобы убедиться, что у нас все нормально установилось необходимо выполнить команду показа версии PhantomJS

phantomjs --version

И если установка прошла удачно, то эта команда вернет версии без каких-либо «варнингов» и «эрроров»

Установка CasperJS

CasperJS устанавливаем схожим образом, даже чуть полегче, если указать ссылку на GitHub

cd /usr/local/share
sudo git clone -b master git://github.com/n1k0/casperjs.git
cd casperjs
sudo ln -sf `pwd`/bin/casperjs /usr/local/bin/casperjs

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

casperjs --version

 

Выполняем операции CasperJS из PHP

Как уже писали, для выполнения команд PhantomJS нам нужно из PHP выполнять команды на системном уровне и для этого у нас есть специальная функция exec(…). Так как весь код PhantomJS через обертку CasperJS пишется на языке JavaScript, то полезно разделить логику работы, поделив задачи в отдельных файлах, поэтому создадим отдельный файл casper-script.js для написания кода запросов на JS и в итоге код на PHP для передачи и выполнения команд будет следующим

exec('casperjs /home/UserName/web/my.site.com/public_html/parser/js/casper-script.js https://www.google.ru/', $output, $return_var););

print_r($output);
print_r($return_var);

В первом параметре передаем строку с командами для OS в котором через пробелы указываем первым сервис, вторым — скрипт на JavaScript для описания того, что должен PhantomJS делать через обертку CasperJS, третьим — URL адрес сайта или ресурса, над которым мы хотим совершать операции. Данная строка не фиксированна, поэтому, количество параметров строки может меняться. Далее у нас идут второй и третий параметры функции exec(…) для вывода результата выполнения.

Как видно выше путь к файлу casper-script.js указан абсолютным. По умолчанию, файлы, скриншоты после опрации будут сохранятся в текущей директории, где выполняется команда exec(). Для начала выполним обычную операцию и напишем в файле casper-script.js следующий код

var casper = require("casper").create();

casper.echo("Casper CLI passed args:");
require("utils").dump(casper.cli.args);

casper.echo("Casper CLI passed options:");
require("utils").dump(casper.cli.options);

casper.exit();

 

Пример получения скриншота сайта на CasperJS/PhantomJS

Данный код демонстрирует получение скриншота сайта по его URL и сохранение в текущей директории

exec('casperjs /home/UserName/web/my.site.com/public_html/parser/js/casper-script.js 2>&1', $output, $return_var);
print_r($output);
print_r($return_var);
var casper = require('casper').create({   
    verbose: true, 
    logLevel: 'debug',
    pageSettings: {
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36'
    }
});
casper.options.viewportSize = {width: 1280, height: 1024};

casper.start("http://stackoverflow.com/questions/tagged/phantomjs");
casper.then(function() {
    casper.capture("stackoverflow.png");
});

casper.run();

После выполнения скриншот будет сохранен под названием stackoverflow.png

Пример получения сгенерированного динамически DOM HTML — документа через CasperJS/PhantomJS

exec('casperjs /home/UserName/web/my.site.com/public_html/parser/js/casper-script.js https://google.com html.txt --ssl-protocol=TLSv1', $output, $return_var);
var casper = require('casper').create();
casper.userAgent('Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19');
var url = casper.cli.args[0];
var outputPath = casper.cli.args[1];
casper.start(url);
casper.then(function() {
  casper.wait(5000, function() {
    // Get HTML
    var html = this.evaluate(function(){
      return document.querySelector("html").outerHTML;
    });
    // Save HTML
    fs = require('fs');
    fs.write(outputPath, html, 'w');
  });
});
casper.run();

В данном примере получим первый аргумент из командной строки в виде URL, который надо заполучить и второй аргумент — путь и имя файла, в котором будет сгенерированный DOM HTML.