Last active
August 29, 2015 14:26
-
-
Save believer-ufa/3fc79bc01c8c33eb16a5 to your computer and use it in GitHub Desktop.
Riot serverside render example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Делает запрос на Node.js-приложение для получения результата | |
* рендеринга определённого riot-модуля | |
* @param string $name Название модуля | |
* @param array $params Параметры, которые будут переданые модулю | |
* @return string | |
*/ | |
function render_riot_module($name, $params) { | |
$result = shell_exec('riot-server-start.sh'); | |
if ($result === "starting new server\n") { | |
// Чуток подождём, если riot-сервер был запущен в данном запросе, | |
// чтобы он успел стартануть | |
sleep(2); | |
} | |
$response = UnixSocketHttpClient::quickPost('unix://riot-server.socket', '/riot-module', [ | |
'tag' => $name, | |
'params' => json_encode($params) | |
]); | |
if ($response === false OR $response === 'RENDER ERROR') { | |
throw HTTP_Exception::factory(500, 'Riot component rendering error. Component name: ' . $name); | |
} | |
return $response; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<? | |
/** | |
* Основной шаблон для расширенного списка объектов | |
*/ | |
$list_id = uniqid('adv-list-'); | |
$module_params = []; | |
?> | |
<div id="<?=$list_id?>"> | |
<?=render_riot_module('advanced_list', $module_params)?> | |
</div> | |
<script> | |
AdvancedListApp = riot.mount('#<?=$list_id?>', 'advanced-list', <?=json_encode($module_params)?>)[0]; | |
RiotApps.create(AdvancedListApp); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"private": true, | |
"dependencies": { | |
"body-parser": "^1.13.3", | |
"express": "^4.13.3", | |
"jquery": "^2.1.4", | |
"jsdom": "^3.1.2", | |
"riot": "^2.2.2", | |
"sugar": "^1.4.1" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
BASEDIR=`dirname $0` | |
PROJECT_PATH=`cd $BASEDIR; pwd` | |
# Эта переменная используется для названия screen-сессии | |
PROJECT_PATH_DASHED="${PROJECT_PATH//\//--}" | |
if ! screen -ls | grep -q "riot_server_${PROJECT_PATH_DASHED}"; then | |
echo "starting new server" | |
screen -dmS "riot_server_${PROJECT_PATH_DASHED}" node ${PROJECT_PATH}/riot-server.js | |
else | |
echo "already started" | |
fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Данный файл позволяет запустить собственный nodejs сервер и принимать соединения на unix-сокет, | |
* создающийся в директории веб-приложения. | |
* | |
* Через подключение к данному сокету мы можем получить HTML-представление определённого | |
* riot-компонента, что и делается в PHP-коде. Функция, отвечающая за подключение к | |
* данному серверу, называется render_riot_module($name, $params) и располагается | |
* в файле functions.php | |
*/ | |
var app = require('express')(); | |
var riot = require('riot'); | |
require('sugar'); | |
var env = require('jsdom').env, html = '<html><body></body></html>'; | |
env(html, function (errors, window) { | |
// Подключим жиквери к нашему серверу | |
var jQuery = require('jquery')(window); | |
global.$ = jQuery; | |
global.jQuery = jQuery; | |
require('./themes/js/class/riot-components/raw.tag'); | |
try { // Удалим старый сокет, если он есть. Если нет - ну и ладно. | |
require('fs').unlinkSync('riot-server.socket'); | |
} catch (error) {} | |
/** | |
* Список наших riot-компонентов, которые мы будем рендерить на сервере | |
*/ | |
var tags = { | |
advanced_list : require('./modules/36n6-widget-advanced-list/assets/advanced-list.tag') | |
}; | |
app.use(require('body-parser').urlencoded({ extended: true })); | |
app.post('/riot-module', function(request, response) { | |
if (! request.body.params || ! request.body.tag) { | |
response.send('please send params'); | |
return; | |
} | |
var params = JSON.parse(request.body.params); | |
try { | |
var html = riot.render(tags[request.body.tag], params); | |
} catch (error) { | |
console.log('component render error :(', error); | |
response.send('RENDER ERROR'); | |
} | |
console.log('Generated a "' + request.body.tag + '" riot module'); | |
response.send(html); | |
}); | |
app.listen('./riot-server.socket', function() { | |
console.log('Riot modules app listening'); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Этот код основан наоснове HTTPClient Simon Willison ( http://simon.incutio.com/ ) | |
* Manual: http://scripts.incutio.com/httpclient/ | |
* | |
* Я лишь немного переделал его для возможности отправлять данные | |
* на сокет через один статический метод. | |
**/ | |
class UnixSocketHttpClient { | |
// Request vars | |
var $file; | |
var $path; | |
var $method; | |
var $postdata = ''; | |
var $cookies = array(); | |
var $referer; | |
var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*'; | |
var $accept_encoding = 'gzip'; | |
var $accept_language = 'en-us'; | |
var $user_agent = 'Incutio UnixSocketHttpClient v0.9'; | |
// Options | |
var $timeout = 20; | |
var $use_gzip = true; | |
var $persist_cookies = true; // If true, received cookies are placed in the $this->cookies array ready for the next request | |
// Note: This currently ignores the cookie path (and time) completely. Time is not important, | |
// but path could possibly lead to security problems. | |
var $persist_referers = true; // For each request, sends path of last request as referer | |
var $debug = false; | |
var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found | |
var $max_redirects = 5; | |
var $headers_only = false; // If true, stops receiving once headers have been read. | |
// Basic authorization variables | |
var $username; | |
var $password; | |
// Response vars | |
var $status; | |
var $headers = array(); | |
var $content = ''; | |
var $errormsg; | |
// Tracker variables | |
var $redirect_count = 0; | |
var $cookie_host = ''; | |
function __construct($file) { | |
$this->file = $file; | |
} | |
function get($path, $data = false) { | |
$this->path = $path; | |
$this->method = 'GET'; | |
if ($data) { | |
$this->path .= '?'.$this->buildQueryString($data); | |
} | |
return $this->doRequest(); | |
} | |
function post($path, $data) { | |
$this->path = $path; | |
$this->method = 'POST'; | |
$this->postdata = $this->buildQueryString($data); | |
return $this->doRequest(); | |
} | |
function buildQueryString($data) { | |
$querystring = ''; | |
if (is_array($data)) { | |
// Change data in to postable data | |
foreach ($data as $key => $val) { | |
if (is_array($val)) { | |
foreach ($val as $val2) { | |
$querystring .= urlencode($key).'='.urlencode($val2).'&'; | |
} | |
} else { | |
$querystring .= urlencode($key).'='.urlencode($val).'&'; | |
} | |
} | |
$querystring = substr($querystring, 0, -1); // Eliminate unnecessary & | |
} else { | |
$querystring = $data; | |
} | |
return $querystring; | |
} | |
function doRequest() { | |
// Performs the actual HTTP request, returning true or false depending on outcome | |
if (!$fp = @fsockopen($this->file, 0, $errno, $errstr, $this->timeout)) { | |
// Set error message | |
switch($errno) { | |
case -3: | |
$this->errormsg = 'Socket creation failed (-3)'; | |
case -4: | |
$this->errormsg = 'DNS lookup failure (-4)'; | |
case -5: | |
$this->errormsg = 'Connection refused or timed out (-5)'; | |
default: | |
$this->errormsg = 'Connection failed ('.$errno.')'; | |
$this->errormsg .= ' '.$errstr; | |
$this->debug($this->errormsg); | |
} | |
return false; | |
} | |
socket_set_timeout($fp, $this->timeout); | |
$request = $this->buildRequest(); | |
$this->debug('Request', $request); | |
fwrite($fp, $request); | |
// Reset all the variables that should not persist between requests | |
$this->headers = array(); | |
$this->content = ''; | |
$this->errormsg = ''; | |
// Set a couple of flags | |
$inHeaders = true; | |
$atStart = true; | |
// Now start reading back the response | |
while (!feof($fp)) { | |
$line = fgets($fp, 4096); | |
if ($atStart) { | |
// Deal with first line of returned data | |
$atStart = false; | |
if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) { | |
$this->errormsg = "Status code line invalid: ".htmlentities($line); | |
$this->debug($this->errormsg); | |
return false; | |
} | |
$http_version = $m[1]; // not used | |
$this->status = $m[2]; | |
$status_string = $m[3]; // not used | |
$this->debug(trim($line)); | |
continue; | |
} | |
if ($inHeaders) { | |
if (trim($line) == '') { | |
$inHeaders = false; | |
$this->debug('Received Headers', $this->headers); | |
if ($this->headers_only) { | |
break; // Skip the rest of the input | |
} | |
continue; | |
} | |
if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) { | |
// Skip to the next header | |
continue; | |
} | |
$key = strtolower(trim($m[1])); | |
$val = trim($m[2]); | |
// Deal with the possibility of multiple headers of same name | |
if (isset($this->headers[$key])) { | |
if (is_array($this->headers[$key])) { | |
$this->headers[$key][] = $val; | |
} else { | |
$this->headers[$key] = array($this->headers[$key], $val); | |
} | |
} else { | |
$this->headers[$key] = $val; | |
} | |
continue; | |
} | |
// We're not in the headers, so append the line to the contents | |
$this->content .= $line; | |
} | |
fclose($fp); | |
// If data is compressed, uncompress it | |
if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') { | |
$this->debug('Content is gzip encoded, unzipping it'); | |
$this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php | |
$this->content = gzinflate($this->content); | |
} | |
// If $persist_cookies, deal with any cookies | |
if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->file == $this->cookie_host) { | |
$cookies = $this->headers['set-cookie']; | |
if (!is_array($cookies)) { | |
$cookies = array($cookies); | |
} | |
foreach ($cookies as $cookie) { | |
if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) { | |
$this->cookies[$m[1]] = $m[2]; | |
} | |
} | |
// Record domain of cookies for security reasons | |
$this->cookie_host = $this->file; | |
} | |
// If $persist_referers, set the referer ready for the next request | |
if ($this->persist_referers) { | |
$this->debug('Persisting referer: '.$this->getRequestURL()); | |
$this->referer = $this->getRequestURL(); | |
} | |
// Finally, if handle_redirects and a redirect is sent, do that | |
if ($this->handle_redirects) { | |
if (++$this->redirect_count >= $this->max_redirects) { | |
$this->errormsg = 'Number of redirects exceeded maximum ('.$this->max_redirects.')'; | |
$this->debug($this->errormsg); | |
$this->redirect_count = 0; | |
return false; | |
} | |
$location = isset($this->headers['location']) ? $this->headers['location'] : ''; | |
$uri = isset($this->headers['uri']) ? $this->headers['uri'] : ''; | |
if ($location || $uri) { | |
$url = parse_url($location.$uri); | |
// This will FAIL if redirect is to a different site | |
return $this->get($url['path']); | |
} | |
} | |
return true; | |
} | |
function buildRequest() { | |
$headers = array(); | |
$headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding | |
$headers[] = "Host: {$this->file}"; | |
$headers[] = "User-Agent: {$this->user_agent}"; | |
$headers[] = "Accept: {$this->accept}"; | |
if ($this->use_gzip) { | |
$headers[] = "Accept-encoding: {$this->accept_encoding}"; | |
} | |
$headers[] = "Accept-language: {$this->accept_language}"; | |
if ($this->referer) { | |
$headers[] = "Referer: {$this->referer}"; | |
} | |
// Cookies | |
if ($this->cookies) { | |
$cookie = 'Cookie: '; | |
foreach ($this->cookies as $key => $value) { | |
$cookie .= "$key=$value; "; | |
} | |
$headers[] = $cookie; | |
} | |
// Basic authentication | |
if ($this->username && $this->password) { | |
$headers[] = 'Authorization: BASIC '.base64_encode($this->username.':'.$this->password); | |
} | |
// If this is a POST, set the content type and length | |
if ($this->postdata) { | |
$headers[] = 'Content-Type: application/x-www-form-urlencoded'; | |
$headers[] = 'Content-Length: '.strlen($this->postdata); | |
} | |
$request = implode("\r\n", $headers)."\r\n\r\n".$this->postdata; | |
return $request; | |
} | |
function getStatus() { | |
return $this->status; | |
} | |
function getContent() { | |
return $this->content; | |
} | |
function getHeaders() { | |
return $this->headers; | |
} | |
function getHeader($header) { | |
$header = strtolower($header); | |
if (isset($this->headers[$header])) { | |
return $this->headers[$header]; | |
} else { | |
return false; | |
} | |
} | |
function getError() { | |
return $this->errormsg; | |
} | |
function getCookies() { | |
return $this->cookies; | |
} | |
function getRequestURL() { | |
$url = 'http://'.$this->file; | |
$url .= $this->path; | |
return $url; | |
} | |
// Setter methods | |
function setUserAgent($string) { | |
$this->user_agent = $string; | |
} | |
function setAuthorization($username, $password) { | |
$this->username = $username; | |
$this->password = $password; | |
} | |
function setCookies($array) { | |
$this->cookies = $array; | |
} | |
// Option setting methods | |
function useGzip($boolean) { | |
$this->use_gzip = $boolean; | |
} | |
function setPersistCookies($boolean) { | |
$this->persist_cookies = $boolean; | |
} | |
function setPersistReferers($boolean) { | |
$this->persist_referers = $boolean; | |
} | |
function setHandleRedirects($boolean) { | |
$this->handle_redirects = $boolean; | |
} | |
function setMaxRedirects($num) { | |
$this->max_redirects = $num; | |
} | |
function setHeadersOnly($boolean) { | |
$this->headers_only = $boolean; | |
} | |
function setDebug($boolean) { | |
$this->debug = $boolean; | |
} | |
// "Quick" static methods | |
function quickGet($url) { | |
$bits = parse_url($url); | |
$host = $bits['host']; | |
$port = isset($bits['port']) ? $bits['port'] : 80; | |
$path = isset($bits['path']) ? $bits['path'] : '/'; | |
if (isset($bits['query'])) { | |
$path .= '?'.$bits['query']; | |
} | |
$client = new UnixSocketHttpClient($host, $port); | |
if (!$client->get($path)) { | |
return false; | |
} else { | |
return $client->getContent(); | |
} | |
} | |
static function quickPost($file, $path, $data) { | |
$client = new self($file); | |
if (!$client->post($path, $data)) { | |
return false; | |
} else { | |
return $client->getContent(); | |
} | |
} | |
function debug($msg, $object = false) { | |
if ($this->debug) { | |
print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg; | |
if ($object) { | |
ob_start(); | |
print_r($object); | |
$content = htmlentities(ob_get_contents()); | |
ob_end_clean(); | |
print '<pre>'.$content.'</pre>'; | |
} | |
print '</div>'; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment