Russian TYPO3 community Форум больше не используется. Присоединяйтесь к каналу #community-ru в Slack for TYPO3 community  

Вернуться   Russian TYPO3 community > Обсуждение общих технических вопросов > Мастер-класс

Ответ
 
Опции темы Опции просмотра
Старый 05.01.2017, 23:54   #1
alexk
Senior Member
 
Регистрация: 26.10.2007
Сообщений: 108
По умолчанию Typo3 7, Websockets и чашка кофе

Кофе на любителя..

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

Что и откуда брать:
  1. https://getcomposer.org/ (У кого нет)
  2. Ну и http://pecl.php.net/package/zmq модуль ZMQ для апача. (В моем случае все действо творится на XAMP 3.2.1 PHP 5.6 ) в моем случае заработал php_zmq-1.1.2-5.6-nts-vc11-x86
  3. За основу возьмем часть функционала из этого экста: https://github.com/daCyberpunk/websockets (Спасибо Тарасу!)

TYPO3 7.4-7.6!!! 8ку еще не ставил, а ниже версии уже нет.

Ставим ZMQ для апача и composer в первую очередь:
ZMQ на XAMPP так:
libzmq.dll - php\
php_zmq.dll - php\ext
+ добавляем в php.ini

Ставим Ratchet

cmd > mysite
composer requier cboden/retchat v0.3.5
composer requier react/zmq v0.3.0

Ждем..

Чистим Кэш и папку typo3Temp

Ставим Websockets и правим.

По порядку:
  1. Удаляем папку Vendor и все composer файлы из \websockets. Они нам не нужны, там стоит ratchet 2.0.
  2. Открываем \websockets\Classes\ ServerCommand.php и правим action_start()
У вас должно остаться только это:

PHP код:
$extPath ExtensionManagementUtility::extPath('websockets');

// Check whether websockets-server is already running
$thisProcess = new ThisProcess($extPath);

if (!
$thisProcess->already_running) { // Start websockets-server
 
$websocket = new IoServer::factory(
    new \
Ratchet\Http\HttpServer(
        new 
WsServer(
            new 
Connection()
        )
    ),
    
9091
);
$websocket ->run();
} else {
  
$GLOBALS['BE_USER']->writelog(4010'Attempting to start Notify WebSockets Server while it is already running''');

Порт я специально поставил отличный от 8080.. но можно и его оставить.
  1. Открываем \websockets\Classes\Service\ConnectionTest.php И приводим его к такому виду:

PHP код:
$ref->addJsInlineCode(
            
'WS_ConnectionTest',
            
// Authentication parameters for fe-users:
            
GeneralUtility::minifyJavaScript(
                
'var GP_auth = ' json_encode(
                    array(
                        
'FE_SESSION_KEY' => rawurlencode(
                            
$GLOBALS['TSFE']->fe_user->id '-' .
                            
md5(
                                
$GLOBALS['TSFE']->fe_user->id '/' .
                                
$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
                            )
                        ),
                        
'vC'             => $GLOBALS['TSFE']->fe_user->veriCode()
                    )
                ) . 
',' .
                
'WS_isRunning = ' . (GeneralUtility::makeInstance('TYPO3\\CMS\\Websockets\\Server')->isRunning() ? 'true' 'false') . ';'
            
) . "\n"

// Connection test:
. <<<'EOT'
if (WS_isRunning) {
    // Set protocol
    var protocol = 'ws';
    if (window.location.protocol === 'https:') {
        protocol += 's';
    }
    // Establish connection
    var ws_conn = new WebSocket(protocol + '://127.0.0.1:9091/?FE_SESSION_KEY=' + GP_auth.FE_SESSION_KEY);
    ws_conn.onopen = function(e) {
        console.log("Connection established!\nNow you may use WebSocket 'ws_conn' object for testing...");
    };
    ws_conn.onmessage = function(e) {
        console.log(e.data);
    };
} else {
    console.log('WebSockets in testing-mode. WS_Server is not started. Please, start the server from AdminPanel->Scheduler section.');
}
EOT 
protocol + '://127.0.0.1:9091/ - тут я для наглядности оставил 127.0.0.1, можно оставить как в оригинале. Только порт меняем.

Собственно все, добавляем задачи в Scheduler и запускаем сервер!
Если все ок, то вы увидите в консоле что коннект есть, но вот FE-User: false.
В консоле можно отправить сообщение ws_conn.send(‘Hello Obama’);

Правим аутентификацию юзеров.

!Sic, для начало нужно залогиниться как FE User.
!Sic2, если все равно fe-user: false, то у вас та же проблема что и у меня.. (Может касаться только локального хоста, нужно еще раз проверять на сервере.. а по симу далее ненужные строки комментируем, а не удаляем.)
В чем проблема, а проблема в том, что $_SERVER не содержит REMOTE_HOST и HTTP_USER_AGENT, а без них проверка через EID сервис не работает. Вот и все.
Ну ок, не работает и ладно, мы откроем свой луна-парк с блэк-джеком и тайскими трансвеститами.

Делаем HOOK!


\websockets\Classes\Hook\AuthFeUserHook.php

Вписываем его в ext_localconf.php

PHP код:
### Hook for FE User Auth
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['postUserLookUp'][] = MY\EXT\Hook\AuthFeUserHook::class . '->get_user';
### 
Сам HOOK:

PHP код:
<?php

namespace MY\EXT\Hook;

    
/**
     * This file is part of the TYPO3 CMS project.
     *
     * It is free software; you can redistribute it and/or modify it under
     * the terms of the GNU General Public License, either version 2
     * of the License, or any later version.
     *
     * For the full copyright and license information, please read the
     * LICENSE.txt file that was distributed with this source code.
     *
     * The TYPO3 project - inspiring people to share!
     */

use TYPO3\CMS\Core\Utility\GeneralUtility;
use 
TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
/**
 * Authentification Be USer Hook
 *
 */
class AuthFeUserHook
{   
    
/**
     * Params
     */
    
public $params;

    
/**
    

    /**
     * @var DatabaseConnection
     */
    
protected $db null;

    
/**
     * Initialize some important variables
     */
    
public function __construct()
    {
        
$this->db $this->getDatabaseConnection();
    }

    
/**
     * Process User Authentification
     *
     * @param array $params
     * @param AbstractUserAuthentication $authentication
     * @see AbstractUserAuthentication
     * @return void
     */
    
public function get_user($paramsAbstractUserAuthentication $authentication){
        
        if(
            
strpos(GeneralUtility::getIndpEnv('TYPO3_REQUEST_SCRIPT'), 'typo3\cli_dispatch') && 
            
$params['pObj']->session_table == 'fe_sessions' &&
            !
$params['pObj']->user
            
){
            
$this->params $params['pObj'];
            
$statement $this->fetchUserSessionFromDB();
            if (
$statement) {
                
$statement->execute();
                
$params['pObj']->user $statement->fetch();
                
$statement->free();
            }
           
        }
        
    }

    
/*************************
     *
     * SQL Functions  FROM \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
     *
     *************************/
    /**
     * The session_id is used to find user in the database.
     * Two tables are joined: The session-table with user_id of the session and the usertable with its primary key
     * if the client is flash (e.g. from a flash application inside TYPO3 that does a server request)
     * then don't evaluate with the hashLockClause, as the client/browser is included in this hash
     * and thus, the flash request would be rejected
     *
     * @return \TYPO3\CMS\Core\Database\PreparedStatement
     * @access private
     */
    
protected function fetchUserSessionFromDB()
    {
        
$statement null;
        
$statement $this->db->prepare_SELECTquery('*'$this->params->session_table ',' $this->params->user_table$this->params->session_table '.ses_id = :ses_id
                    AND ' 
$this->params->session_table '.ses_name = :ses_name
                    AND ' 
$this->params->session_table '.ses_userid = ' $this->params->user_table '.' $this->params->userid_column ' ' .
                    
$this->user_where_clause());
        
$statement->bindValues(array(
                
':ses_id' => $this->params->id,
                
':ses_name' => $this->params->name
            
));
        return 
$statement;
    }

    
/**
     * This returns the where-clause needed to select the user
     * with respect flags like deleted, hidden, starttime, endtime
     *
     * @return string
     * @access private
     */
    
protected function user_where_clause()
    {
        
$whereClause '';
        if (
$this->params->enablecolumns['rootLevel']) {
            
$whereClause .= 'AND ' $this->params->user_table '.pid=0 ';
        }
        if (
$this->params->enablecolumns['disabled']) {
            
$whereClause .= ' AND ' $this->params->user_table '.' $this->params->enablecolumns['disabled'] . '=0';
        }
        if (
$this->params->enablecolumns['deleted']) {
            
$whereClause .= ' AND ' $this->params->user_table '.' $this->params->enablecolumns['deleted'] . '=0';
        }
        if (
$this->params->enablecolumns['starttime']) {
            
$whereClause .= ' AND (' $this->params->user_table '.' $this->params->enablecolumns['starttime'] . '<=' $GLOBALS['EXEC_TIME'] . ')';
        }
        if (
$this->params->enablecolumns['endtime']) {
            
$whereClause .= ' AND (' $this->params->user_table '.' $this->params->enablecolumns['endtime'] . '=0 OR '
                
$this->params->user_table '.' $this->params->enablecolumns['endtime'] . '>' $GLOBALS['EXEC_TIME'] . ')';
        }
        return 
$whereClause;
    }


    
/**
     * Get global database connection
     * @return DatabaseConnection
     */
    
protected function getDatabaseConnection()
    {
        return 
$GLOBALS['TYPO3_DB'];
    }
    
}
Собственно что происходит, EidUtility во время проверки шлет в хук authdata, мы смотрим есть ли юзер или нет, проверяем что скрипт был запущен от имени typo3\cli_dispatch ну и что идет проверка FE User, т.е. таблица session_table == 'fe_sessions'.

Остальные функции это немного модифицированные из AbstractUserAuthentication.

Еще можно поправить \websockets\Classes\Service\Connection.php -> initFeUser($connection)

Убираем лишнее (комментируем)
PHP код:
// Set 'FE_SESSION_KEY'
        
$FE_SESSION_KEY $connection->WebSocket->request->getQuery()->get('FE_SESSION_KEY');
        
        if (
$FE_SESSION_KEY) {
            
GeneralUtility::_GETset($FE_SESSION_KEY'FE_SESSION_KEY');
        }
$fe_user EidUtility::initFeUser();

if (
$fe_user->user) { // Authenticated successfully
            
if ($this->writeDevLog) {
                
GeneralUtility::devLog('User Autherized: ' $fe_user->user['username'], __CLASS__);
            }
            return 
$fe_user;
        } else {
            if (
$this->writeDevLog) {
                
GeneralUtility::devLog('No User Autherized!'__CLASS__);
            }
            throw new \
Exception('FE-user can not be authenticated.');
        } 
Сохраняем, перезапускаем сервер и в консоле увидим имя пользователя.

И так, что мы можем теперь? Не много… например чат.

Делаем PUSH

Код HTML:
В шапке сайта ставим ссылку на скрипт <script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>
Важно, нам нужно версия 0.8.. т.к. Ratchet использует WAMP Server v.1!
Комментируем наш \Websockets\Classes\ ServerCommand.php, и делаем копию.
И приводим acton_start к виду:

PHP код:
// в Шапке
use \Ratchet\Wamp\WampServer;
use \
Ratchet\WebSocket\WsServer;
use \
Ratchet\Server\IoServer;
use \
React\ZMQ\Context;

private function 
action_start() {
        
$extPath ExtensionManagementUtility::extPath('websockets');

        
// Check whether websockets-server is already running
        
$thisProcess = new ThisProcess($extPath);

        if (!
$thisProcess->already_running) { // Start websockets-server

            
$loop = \React\EventLoop\Factory::create();
               
$pusher = new \MyApp\Pusher;

            
// Listen for the web server to make a ZeroMQ push after an ajax request
            
$context = new Context($loop);

            
$socketWrapper $context->getSocket(\ZMQ::SOCKET_PULL);
            
$socketWrapper->bind('tcp://*:5555');
            
$socketWrapper->on('message', array($pusher'onBlogEntry'));

            
$webSock = new \React\Socket\Server($loop);
            
$webSock->listen(9092);

            
$notifyWamp = new IoServer(
                new \
Ratchet\Http\HttpServer(
                    new 
WsServer(
                        new 
WampServer(
                            
$pusher
                        
)
                    )
                ),
                
$webSock
            
);
            
            
$loop->run();
} else {
            
$GLOBALS['BE_USER']->writelog(4010'Attempting to start Notify WebSockets Server while it is already running''');
        }
    } 
Если сравните с документаций по Ratchet http://socketo.me/docs/push то там будет разница, в основном не существенная.

Далее, делаем $pusher = new \MyApp\Pusher;
Сохраняете куда пожелаете, главное namespace если меняете то и тут так же меняйте.

PHP код:
<?php
namespace MyApp;
use \
Ratchet\ConnectionInterface;
use \
Ratchet\Wamp\WampServerInterface;
 
class 
Pusher implements WampServerInterface {
     
    
/**
     * A lookup of all the topics clients have subscribed to
     */
    
protected $subscribedTopics = array();
     
    public function 
onSubscribe(ConnectionInterface $conn$topic) {
        
$this->subscribedTopics[$topic->getId()] = $topic;
        
$topic->broadcast($topic->getId());
    }
     
    
/**
     * @param string JSON'ified string we'll receive from ZeroMQ
     */
    
public function onBlogEntry($entry) {

        
$entryData json_decode($entrytrue);
        
        
// If the lookup topic object isn't set there is no one to publish to
        
if (!array_key_exists($entryData['category'], $this->subscribedTopics)) {
            return;
        }
     
        
$topic $this->subscribedTopics[$entryData['category']];
     
        
// re-send the data to all the clients subscribed to that category
        
$topic->broadcast($entryData);
    }
     
    
/* The rest of our methods were as they were, omitted from docs to save space */
     
    
public function onUnSubscribe(ConnectionInterface $conn$topic) {
     
    }
     
    public function 
onOpen(ConnectionInterface $conn) {
    }
     
    public function 
onClose(ConnectionInterface $conn) {
    }
     
    public function 
onCall(ConnectionInterface $conn$id$topic, array $params) {
        
// In this application if clients send data it's because the user hacked around in console
        
$conn->callError($id$topic'You are not allowed to make calls')->close();
    }
     
    public function 
onPublish(ConnectionInterface $conn$topic$event, array $exclude, array $eligible) {
        
// In this application if clients send data it's because the user hacked around in console
        
$conn->close();
    }
     
    public function 
onError(ConnectionInterface $conn, \Exception $e) {
    }
}
В своем тестовом Классе, который можно будет вызвать (У меня Ajax обработчик) пишем следующее:

PHP код:
$entryData = array(
            
'category' => 'kittensCategory',
            
'title'    => 'test',
            
'article'  => 'test test test'
        
);

        
$context = new \ZMQContext(1);

        
$socket $context->getSocket(\ZMQ::SOCKET_PUSH'my pusher');
        
$socket->setSockOpt(\ZMQ::SOCKOPT_LINGER30); //ADDED
        
$socket->connect("tcp://127.0.0.1:5555");
        
$socket->send(json_encode($entryData)); 
Обновляем, в косоле увидем kittensCategory - это значит что скрипт подключился и подписался.

Шлем аджакс запрос на запуск нашей аджакс функции и в консоле увидем:
Object {category: "kittensCategory", title: "test", article: "test test test"}

Что может быть не так:
1. Сообщение приходит через раз, проверяем ZMQ пробуем разные версии.
2. Не коннектится, у меня было из за кривых ручек, не ту версию autobhn использовал, или порты не те.

Собственно все, спасибо за внимание.
__________________
Фанат TYPO!
alexk вне форума   Ответить с цитированием
Старый 09.01.2017, 18:29   #2
Николай Сипко
Senior Member
 
Регистрация: 17.09.2012
Сообщений: 675
По умолчанию

Цитата:
Сообщение от alexk Посмотреть сообщение
Начнем, в связи с тем, что я потратил не одну чашку кофе и не одну пачку сигарет на понимание как запустить полноценные сокеты на Typo
Вопрос из чистого любопытства:

https://typo3.org/extensions/reposit...q=chat&tx_solr[filter][extensionMinTYPO3Version]=&tx_solr[filter][extensionMaxTYPO3Version]=

Почему эти расширения не подошли?

"В чем разница между socket'ом и websocket'ом?"

http://ru.stackoverflow.com/question...et%D0%BE%D0%BC

Последний раз редактировалось Николай Сипко; 09.01.2017 в 18:57
Николай Сипко вне форума   Ответить с цитированием
Старый 11.01.2017, 13:11   #3
alexk
Senior Member
 
Регистрация: 26.10.2007
Сообщений: 108
По умолчанию

Может и подошло бы, да только мне не чат нужен.. мне нужен был голый Сокет.
На базе сокетов у меня нотификация, обновления активных элементов (например локальных курсов валют, которые обновляются каждые 5 мин и хранятся в базе), биллинговая нотификация, букинги в режиме онлайн.. короче, много своих экстеншенов, которые используют одно сервисное приложение как сервер, в котором есть пара разных сокетов.
__________________
Фанат TYPO!
alexk вне форума   Ответить с цитированием
Старый 11.01.2017, 16:37   #4
Николай Сипко
Senior Member
 
Регистрация: 17.09.2012
Сообщений: 675
По умолчанию

Цитата:
Сообщение от alexk Посмотреть сообщение
Может и подошло бы, да только мне не чат нужен.. мне нужен был голый Сокет.
На базе сокетов у меня нотификация, обновления активных элементов (например локальных курсов валют, которые обновляются каждые 5 мин и хранятся в базе), биллинговая нотификация, букинги в режиме онлайн.. короче, много своих экстеншенов, которые используют одно сервисное приложение как сервер, в котором есть пара разных сокетов.
__________________
Фанат TYPO!
Но при чем здесь CMS TYPO3?
Николай Сипко вне форума   Ответить с цитированием
Старый 31.01.2017, 14:16   #5
alexk
Senior Member
 
Регистрация: 26.10.2007
Сообщений: 108
По умолчанию

Ну как сказать))) Проект весь на Typo3.. думаю по этому.
__________________
Фанат TYPO!
alexk вне форума   Ответить с цитированием
Старый 31.01.2017, 15:59   #6
Николай Сипко
Senior Member
 
Регистрация: 17.09.2012
Сообщений: 675
По умолчанию

Цитата:
Сообщение от alexk Посмотреть сообщение
Ну как сказать))) Проект весь на Typo3.. думаю по этому.
WebSocket — протокол полнодуплексной связи (может передавать и принимать одновременно) поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени. В настоящее время в W3C осуществляется стандартизация API Web Sockets.
https://ru.wikipedia.org/wiki/WebSocket

TYPO3 (тайпо три) — система управления сайтами (CMS/CMF) с открытым исходным кодом и свободной лицензией. Является гибкой расширяемой системой с большим количеством модулей и функций. Написана на PHP, для хранения данных использует любую реляционную базу данных, поддерживаемую TYPO3 DBAL, включая MySQL, Oracle Database, PostgreSQL и другие. Работает на таких серверах, как Apache или IIS, и на большинстве операционных систем, таких как Linux, Microsoft Windows, FreeBSD, Mac OS X и OS/2. Система создана Каспером Скорхёем и распространяется бесплатно под лицензией GNU GPL.
https://ru.wikipedia.org/wiki/TYPO3

TYPO3 использует LAMP (серверные порты, технологии и операционные системы), но не задает их свойства
Николай Сипко вне форума   Ответить с цитированием
Ответ

Опции темы
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB code is Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
На что это могут указывать следующие нотации? Faab Общие вопросы 6 25.11.2014 17:04
T3CON12DE - Community, the TYPO3 family and Neos RSS Bot Новости TYPO3 (на английском) 0 16.10.2012 20:10
T3CON12DE - Community, the TYPO3 family and Neos RSS Bot Новости TYPO3 (на английском) 0 16.10.2012 13:47
T3CON12DE - Community, the TYPO3 family and Neos RSS Bot Новости TYPO3 (на английском) 0 15.10.2012 20:10
T3CON12DE - Community, the TYPO3 family and Neos RSS Bot Новости TYPO3 (на английском) 0 15.10.2012 16:06


Часовой пояс GMT +4, время: 03:30.


Работает на vBulletin® версия 3.8.1.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot

Хостинг и техническая поддержка: TYPO3 Лаборатория