Как сделать из папки в корне сайта корневой раздел при помощи .htaccess?

Допустим, есть у нас ситуация при котором index.php находится не в самом корне, а в субкаталоге корня сайта и нам необходимо сделать эту папку корнем сайта. для этого необходимо внести некоторые изменения в файл .htaccess

RewriteEngine On

# Map http://www.example.com to /folder.
RewriteRule ^$ /folder/ [L]

# Map http://www.example.com/x to /folder/x unless there is a x in the web root.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/folder/
RewriteRule ^(.*)$ /folder/$1

# Add trailing slash to directories within folder
# This does not expose the internal URL.
RewriteCond %{SCRIPT_FILENAME} -d
RewriteRule ^folder/(.*[^/])$ http://www.example.com/$1/ [R=301]

Как построить и запустить релиз-приложение iOS в Cordova

Будем исходить из того, что вы написали свое первое приложение под Android на Windows, используя, к примеру, редактор Visual Code и теперь хотите на основе уже написанного кода построить приложение под iOS, перекинув все исходник на ваш Mac — машину, где мы будет подготавливать, построить и подписывать приложение под iOS, прибегнув к помощи XCode.

Здесь представлен текст описания одного из вариантов построения приложения Cordova для платформы iOS. Перед началом работы условимся, что у  нас в инструментарии есть следующие необходимые условия:

  • у нас есть Mac OS High Sierra(или неважно какая версия, но чтобы работала стабильно);
  • на нашем Mac OS установлен XCode последней версии(или неважно какой версии, но чтобы работало стабильно);
  • на нашем Mac OS установлен Node.js v8 и выше(или неважно какой версии, но чтобы работало стабильно);
  • на нашем Node.js установлен Cordova;
  • у нас есть годовая подписка на возможность разрабатывать приложения под продукты Apple. Стоит это удовольствие $99, без этого ваш проект так и будет на стадии дебагинга, без возможности устанавливать на сторонние устройства или загружать на App Store.

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

Подписка на Apple Developer

Особо следует остановиться на покупку лицензии разработки в виде подписки, потому что без этого никак, если вы хотите подписать приложение и распространять его. У Apple распространители делятся на 4 категории:

  1. Sign in with Apple ID — распространители, которые имеют возможность использовать разработку только в режиме тестирования. Т.е., вы не можете распространять ваши приложения за пределы эмулятора на вашем Mac или вашего личного устройства, но весь функционал разработки вам все равно доступен. Стоит $0;
  2. Individual — распространитель в виде индивидуального разработчика. Apple выдает вам сертификат, в котором прописаны ваши личные данные, как физического лица. Стоит $99;
  3. Organization — распространитель от имени организации. Apple выдает вам сертификат, в котором прописаны вашей организации. Стоит $99;
  4. Enterprise Program — распространитель корпоративного ПО. Стоит $299.

Подробно про фичи разработки под тем или иной подпиской можно почитать по ссылке.

Настройка проекта Cordova

Добавляем платформу iOS и подготавливаем проект для XCode

cordova platform add ios
cordova prepare              # или "cordova build"

Развертывание проекта в Cordova

Примечание. Этап развертывания средствами Cordova можно пропустить, так как дальнейщую работу с проектом мы будем производить в XCode.

Дополнительно будет полезно установить вспомогательные утилиты, которые пригодятся в работе c эмуляторами

npm install -g ios-sim
npm install -g ios-deploy

В данной статье мы не будем работать через эти утилиты, а будем работать через XCode. Также через командную строку мы можем задеплоить приложение.

Для развертывания приложения на подключенном iOS устройстве:

cordova run ios --device

Для развертывания приложения на iOS эмуляторе по умолчанию:

cordova emulate ios

Можно использовать cordova run android —list чтобы увидеть все доступные цели и cordova run android —target=имя_устройства для запуска приложения на конкретном устройстве или эмуляторе (например, cordova run android --target="Nexus4_emulator").

Чтобы увидеть дополнительные параметры построения и запуска также можно использовать cordova run —help.

Работа с проектом в XCode iOS SDK

Первым делом открываем сгенерированный проект XCode в директории проекта Cordova по пути ProjectName/platforms/ios/PrjectName.xcpdeproj

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

Первым делом выбираем модуль Cordova в списке и при его контексте нажимаем на вкладку General, где устанавливаем галочку на автоуправление подпиской и в выпадающем нижнем списке выбираем команду разработки. В данном случае там указываем вашу купленную подписку разработчика у Apple

Первым делом выбираем конечное устройство в виде физического устройства

Далее заходим в настройку схемы проекты и там должен быть выбран название проекта,а  не библиотека Cordova

Если он не установлен, то заходим в Edit Scheme и во всех вкладках устанавливаем конфигурацию Build Configuration в режим Release, кроме вкладки Test

Теперь, нам нужно сначала почистить проект нажав на Clean, построить, нажав на Build и архивировать, нажав на Archive

Если мы все правильно настроили в проекте, то проект должен удачно архивировать в формат *.xcarchive по пути проекта ProjectName/platforms/ios/PrjectName.xcarchive

Чтобы увидеть все сгенерированные архивы проектов необходимо найти в верхнем меню Window > Organizer

В данном диалоге мы увидим список всех архивов. Это не полноценное приложение, которое можно уже установить на iOS — устройство и его нельзя разместить в маркет. Для этого нам нужно его экспортировать в один из вариантов и для этого сначала выбираем проект, далее нужный архив из проекта и нажав на кнопку Export вызываем окно выбора типа распространения нашего приложения в формате *.ipa

В способе распространения *.ipa выбираем нужный вариант. Если хотим распространить через App Store, о выбираем первый пункт. Второй пунтк — это распространение подписанного приложения с возможность индивидуальной установки без App Store. Другие можно понять по их кратким описаниям, но основные — это 2 первых, если вы новоиспеченный разработчик iOS.

К примеру выберем тип распространения Ad Hoc и жмем на Next и в следующем диалоге у нас будет возможность выбрать какое-то специфическое устройство или все поддерживающие данной разработкой устройства все сразу, также есть возможность добавить файл манифеста, который даст возможность установить приложение только из определенной ссылки скачивания и жмем на Next

В данном шаге необходимо выбрать режим подписи. Оставим автоподпись средой XCode и жмем на Next

Дальше среда будет генерировать подписанные дистрибутивы под разные устройства iOS

По окончании процесса среда выдаст подробную информацию о сборке

Жмем на кнопку Export и среда нас запросит выбрать место хранения сгенерированных подписанных дистрибутивов *.ipa

После сохранения создастся отдельная папка, в которой будут все наши подписанные дистрибутивы iOS

 

Ошибки построения Android приложения в проекте на Cordova

Есть 2 самые распространенные виды ошибок, которые возможны при построении приложения Android в проекте Cordova:

  • первое — несоответствие версии платформы и версий плагинов Cordova:
Failed to execute aapt
com.android.ide.common.process.ProcessException: Failed to execute aapt
  • второе — отсутствие кода Gradle для импорта дополнительных сервисов. Это может выглядеть так:
google-services plugin could not detect any version for com.google.android.gms or com.google.firebase, default version: 9.0.0 will be used.
please apply google-services plugin at the bottom of the build file.
  1. Возможное решение первой ошибки — принудительное указание версии добавлением в конец файла android/build.gradle:
configurations.all {
    resolutionStrategy {
        force 'com.android.support:support-v4:27.1.0'
    }
}

2. Возможное решение второй ошибки android/build.gradle:

allprojects {
    repositories {
        ...
        configurations.all {
            resolutionStrategy {
                // Add force (11.4.0 is version you want to use)
                force 'com.google.firebase:firebase-messaging:11.4.0'
                force 'com.google.firebase:firebase-core:11.4.0'
                force 'com.google.android.gms:play-services-gcm:11.4.0'
            }
        }
    }
}

 

Загрузка файла на удаленный сервер в Cordova

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

  • прием файла посредством PHP;
  • прием файла посредством Node.js

 

Плагины и доступы

Для начала нам нужно установить необходимые плагины для работы с загружаемыми файлами:

cordova plugin add cordova-plugin-file
cordova plugin add cordova-plugin-file-transfer
cordova plugin add cordova-plugin-white-list
cordova plugin add cordova-plugin-camera
cordova plugin add cordova-plugin-device

Далее даем доступы на права работы с файлами и с сетями платформе. К примеру. для Android они задаются в файле в папке по пути MyApp\platforms\android\app\src\main\AndroidManifest.xml:

<?xml version='1.0' encoding='utf-8'?>
<manifest android:hardwareAccelerated="true" android:versionCode="10000" android:versionName="1.0.0" package="io.cordova.hellocordova" xmlns:android="http://schemas.android.com/apk/res/android">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    ...

 

Клиентский код

Кнопка открытия. Обращаем внимание на meta — тег с разрешениями на удаленную передачу:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
        <link rel="stylesheet" type="text/css" href="css/index.css">
    </head>
    <body>
        <button id="getImage">Выбрать картинку</button>
        <script src="cordova.js" ><script>
        <script src="index.js" ><script>
    </body>
</html>

Скрипт:

var app = {
    initialize: function() {
        this.bindEvents();
        this.setupApp();
    },
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    receivedEvent: function(id) {
        console.log('Received Event: ' + id);
    },
    setupApp: function(){
                function getImage() {
                    navigator.camera.getPicture(
                        this.uploadPhoto,
                        function (message) { alert('get picture failed'); },
                        {
                            quality: 50,
                            destinationType: navigator.camera.DestinationType.FILE_URI,
                            sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY
                        }
                    );
                }
                function uploadPhoto(imageURI) {
                    alert(imageURI);
                    var options = new FileUploadOptions();
                    options.fileKey = "file";
                    options.fileName = imageURI.substr(imageURI.lastIndexOf('/') + 1);
                    options.mimeType = "image/jpeg";
    
                    var params = {};
                    params.value1 = "test";
                    params.value2 = "param";
    
                    options.params = params;
    
                    var ft = new FileTransfer(); 
                    ft.upload(
                        imageURI, 
                        encodeURI("http://83.220.168.205/upload/upload.php"), 
                        this.win, 
                        this.fail, 
                        options);
                }
                function win(r) {
                    console.log("Code = " + r.responseCode);
                    console.log("Response = " + r.response);
                    console.log("Sent = " + r.bytesSent);
                }
                function fail(error) {
                    alert("An error has occurred: Code = " + error.code);
                    console.log("upload error source " + error.source);
                    console.log("upload error target " + error.target);
                    alert("upload error source " + error.source);
                    alert("upload error target " + error.target);
                }
        document.getElementById("getImage").onclick = function() {
            getImage();
        };
        
    }
    
}

app.initialize();

Серверный код на PHP

Сначала в корне сервера надо создать папку upload и в  ней файл upload.php и подпапку upload, куда будут файлы грузиться и храниться:

<?php
//Задаем заголовки на стороний доступ
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE');
header('Access-Control-Allow-Headers: X-Requested-With,content-type');
header('Access-Control-Allow-Credentials: true');
//
$new_image_name = urldecode($_FILES["file"]["name"]).".jpg";
//Переносим файл в нужную папку
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/".$new_image_name);
?>

Обращаем внимание на заголовки, которые разрешают доступ со стороны клиента.

Серверный код на JavaScript для Node.js

Если требуется загрузить и заполучить файл со стороны Node.js, то легче все это сделать при помощи стороннего модуля express-formidable, если приложение express. Сначала устанавливаем сам модуль:

npm install express-formidable

Далее дописываем наш app.js на прием. К пример, у нас есть url — адрес http://myhostname:3000/upload, то код для приема и сохранения загруженного с клиента файла будет следующим:

var formidable = require('formidable');

...

app.post('/upload', function(req, res) {
    res.set({
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'X-Requested-With,content-type',
        'Access-Control-Allow-Credentials': 'true'
    });
    //var data = req.body;
    //console.log(data);
    //console.log(req.files);
    var form = new formidable.IncomingForm();

    form.parse(req);

    form.on('fileBegin', function (name, file){
        file.path = __dirname + '/upload/' + file.name + '.jpg';
    });

    form.on('file', function (name, file){
        console.log('Uploaded ' + file.name);
    });
});

 

Как поделиться в соцсетях в Cordova?

Описание

Допустим, естьу нас задача, чтобы при нажатии на кнопку можно было поделиться в социальных сетях через приложение Cordova. Для данной задачи есть специальные плагин и один из них — это cordova-plugin-share. Данный плагин позволяет приложению через JavaScript вызов открыть нативное попап диалоговое окно, в котором будут иконки социальных сетей, чтобы поделиться с друзьями и подписчиками.

Установка

Для установки достаточно набрать команду добавления нового модуля в нашем Phonegap/Cordova приложении:

cordova plugins add https://github.com/markmarijnissen/cordova-plugin-share.git

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

Для использования в нашем файле скрипта JavaScript необходимо вызвать функцию с указанием параметров:

navigator.share(text,title,mimetype);

Где text — это то, чем нужно поделиться, title — заголовок, а mimetype — это тип объекта, которым делятся.

mimetype может принимать image/jpeg, plain/text и т.д.

 

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

В данном посте рассмотрим пример интеграции 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 сервера, все как на примере внизу.

 

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

В данной статье рассмотрим работу с библиотекой 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;

 

Как прикрепить домен или субдомен к проекту на Java EE / Tomcat, если у вас на сервере еще и Vesta CP со своим Apache/PHP

Допустим, есть такая ситуация, когда на сервере с одним IP — адресом вы хотите разместить параллельно и проекты PHP сервером Apache на порту 80/8080 и проекты Java EE, которые запущены на любом порту, к примеру на 8085, так как к порту 80/8080 уже не привяжешь. Сервер с PHP проектами управляются при помощи Vesta CP, поэтому весь арсенал управления доменами делаем и него. Ниже список операций для прикрепления.

  1. Первым делом прикрепляем наш домен, если он у нас другом ресурсе к нашему серверу через записи NS;
  2. Создаем новый WEB — хостинг с эти доменом через панель Vesta CP;
  3. После создания хостинга с этим доменом или поддоменом залезаем в корень хостинга и кидаем туда .htaccess с записями перенаправления на наш Java EE проект с его портом
RewriteEngine on
RewriteRule ^(.*)$ http://localhost:port/MyJavaProject/$1 [P]
#localhost - ip адрес нашего сервера
#port - порт, на котором запушен наш Tomcat
#MyJavaProject - контекст проекта