пятница, 18 января 2013 г.

Мой блог переехал

Решил попробовать вести на livestreet. Мой новый программистский блог. Тут просто надоело, хоть я и пишу редко

среда, 16 января 2013 г.

Прокладка маршрута по всему миру Open Street Map

ВВедение в суть...


В прошлой своей статье я писал о том, как прокладывать маршруты на Google Maps, но, когда дошел до места, где понял, что по России google отказывается прокладывать маршруты, начал искать решение в Open Street Map. Сразу оговорюсь, что используется сервис cloudmade.com, стандартного способа не нашел.

Итак, приступим. Чтобы проложить маршрут, следует сделать запрос вида: http://routes.cloudmade.com/YOUR-API-KEY-GOES-HERE/api/0.3/start_point,[transit_point1,...,transit_pointN],end_point/route_type[/route_type_modifier].output_format[?lang=(Two letter ISO 3166-1 code)][&units=(km|miles)]


Тут следует пояснить формат запроса. Вот оригинал, а ниже перевод своими словами:


Общие параметры

start_pointШирота и долгота начального пункта, разделенные запятой (lat,lon)
end_pointШирота и долгота конечного пункта, разделенные запятой (lat,lon)
route_typeТип маршрута ( машина - "car", пешком - "foot", велосипед - "bicycle")
output_formatФормат результата ( json - "js", xml - "gpx")

Необязательные параметры

transit_pointsПеречисление промежуточных точек. Также их широта и долгота, разделенные запятой. Сами точки тоже разделены запятой [51.2227,4.4120,51.2,4.41]
route_type_modifierНа момент написания статьи доступны следующие значения:
Кратчайший путь - "shortest"
Быстрейший путь - "fastest"
Используется только для маршрута типа "car"
langЯзык ответа: de, en, es, fr, hu, it, nl, ro, ru, se, vi, zh.
Добавить свой перевод тут: Routing_translation
units(km or miles). Default - km
callbackoptional parameter for JS response, if specified response will be wrapped into the function call. e.g. if callback=routeLoaded, response will be routeLoaded(JSON_object)
Примеры:

На самом деле всё и без перевода ясно в документации. Вообщем перевод для людей, которые учили в школе немецкий =).

Далее лично у меня возникает вопрос этичности. Как я знаю, у гугла достаточно мочный фреймворк для работы с его картой и я уже имел некоторый опыт работы с их картой. Также координаты OSM и google Maps совпадают. Может брать маршруты у cloudmade и рисовать их на карте гугла?

Заметки по yii

Подключить js
Yii::app()->clientScript->registerScriptFile("/js/checkers.js");
Ошибка 404

throw new CHttpException(404,'The requested page does not exist.');
Является ли запрос ajax
if(Yii::app()->getRequest()->getIsAjaxRequest()) {
echo CActiveForm::validate( array( $model)); 
Yii::app()->end(); 
}

if(Yii::app()->request->isAjaxRequest) { $this->renderPartial('_ajaxContent',$data); } else { $this->render('index',$data); }

Простой способ настроить прокси на Ubuntu


проще всего делать форвард траффика через rinetd:

apt-get install rinetd

root@server:/etc# nano rinetd.conf
     тут пишем в формате # bindadress    bindport  connectaddress  connectport
root@server:/etc# echo "rinetd_enable=YES" >> /etc/rc.conf
root@server:/etc# /etc/init.d/rinetd restart

Прокси готов, через iptables тоже можно, но там куча заморочек

четверг, 16 июня 2011 г.

PNG в IE fadeOut и fadeIn

Суть проблемы: при использовании функции fadeOut/fadeIn для png картинки в IE появляются вокруг картинки черные пятна.

Пример кода:

<html>
<head>
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#all").click( function() {
$("#all").fadeOut(1000);
});
});
</script>
</head>
<body>
<img src="images/all.png" id="all" />
</body>
</html>


Очень долго рылся в интернете, но всё же нашел решение. Все пишут про фильтры, но лично у меня сработало только это решение (IE6 / IE7 / IE8 - по IETester)


<html>
<head>
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var i;
for (i in document.images) {
    if (document.images[i].src) {
        var imgSrc = document.images[i].src;
        if (imgSrc.substr(imgSrc.length-4) === '.png' || imgSrc.substr(imgSrc.length-4) === '.PNG') {
            document.images[i].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled='true',sizingMethod='crop',src='" + imgSrc + "')";
        }
    }
}
$("#all").click( function() {


$("#all").fadeOut(1000);
});
});
</script>
</head>
<body>
<div id="all"><img src="images/all.png" id="all" /></div>
</body>
</html>

среда, 15 июня 2011 г.

PNG в IE Элегантное решение без заморочек


script.js:

function fixPNG(element){
if(/MSIE (5\.5|6).+Win/.test(navigator.userAgent)){
var src;
if(element.tagName=='IMG'){
if (/\.png$/.test(element.src)){
src = element.src;
element.src = "/images/spacer.gif";
}
}
else {
src = element.currentStyle.backgroundImage.match(/url\("(.+\.png)"\)/i)
if(src){
src = src[1];
element.runtimeStyle.backgroundImage="none";
}
}
if(src){
element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + src + "',sizingMethod='scale')";
}
}
}

html:
<!--[if lt IE 7]>
<script type="text/javascript" src="/fixpng.js"></script>
<style type="text/css">
.iePNG, img { filter:expression(fixPNG(this)); }
.iePNG a { position: relative; }
</style>
<![endif]-->

вторник, 14 июня 2011 г.

Безопасность БД Joomla

Замена mysql_real_escape_string:

string getEscaped ( $text ) - экранирует символы для добавления в базу данных
string Quote ( $text ) - тоже что и getEscaped, только ещё строка обрамляется одинарными кавычками

Это методы объекта класса JDatabase

используются так (в модели): 
                $db = &$this->getDBO();
$query = 'INSERT INTO blabla VALUES('.$db->Quote($name).','.$db->Quote($description).');';

Данные вставляются именно так. Перед выводом на экран не забываем htmlspecialchars();

Прокладка маршрута Google Maps на javascript

Добрый день, уважаемый читатель. Заказали у меня скрипт для прокладки маршрута на карте, расчёта расстояний между городами. Первое, на что наткнулся: http://habrahabr.ru/blogs/webdev/110460/ Тут рассказывается о том, как подключить карту на страницу, расставить пины на ней. Был у меня опыт работы с такой фичей на айфоне, а тут мы будем писать полностью рабочее JavaScript решение (без серверной стороны!). Итак, создаём файл map.html и пишем туда обычные для любого html документа строки, сразу подключаем jquery (оно на по-любому где-нибудь понадоибится).

Статья написана не до конца (т.к. маршрут на Google Maps по России не прокладывается)


<html>
<head>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=ru"></script>
<script type="text/javascript" src="jquery-1.6.1.min.js"></script>
<style type="text/css">
#map_canvas
{
width:400px;
height:300px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
    var myLatlng = new google.maps.LatLng(-34.397, 150.644);
    var myOptions = {
        zoom: 8,
        center: myLatlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
width:"200px"
    }


    var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
});
</script>
</head>
<body>
<div id="map_canvas"></div>
</body>
</html>


Этот код выведет у вас на странице карту (на разные файлы стили и javascript не разношу для удобства написания статьи). Далее нам нужно понять как проложить маршрут между двумя точками. Хоть и достаточно сложно найти нужные статьи в интернете, но благо гуглу и его отличной документации: Directions Requests . Из документации мы берём функцию
function calcRoute()

  var start = document.getElementById("start").value;
  var end = document.getElementById("end").value;
  var request = {
    origin:start,
    destination:end,
    travelMode: google.maps.TravelMode.DRIVING
  };
  directionsService.route(request, function(result, status) {
    if (status == google.maps.DirectionsStatus.OK) {
      directionsDisplay.setDirections(result);
    }
  });
}

Т.е. гугл даёт нам вполне удобный фреймворк для работы с его картой. Нам достаточно придать переменный start и end нужный вид (название города или координаты точки) и указать каким образом мы будем передвигаться (travelMode : DRIVING, WALKING, BICYCLING (велосипеды на момент написания статьи поддерживаются только в US)), после чего наш маршрут проложен и останется получить лишь необходимые нам данные: через какие города проходит маршрут, расстояние между каждым населёнными пунктами и полное расстояние между городами, а для этого нам нужно будет использовать Distance Matrix API. Но сначала разберёмся как нам правильно проложить маршрут между двумя нашими городами (например, Москва и Париж).

Используем Geocoding API. Для того, чтобы получить  координаты города нам нужно послать обычный GET-запрос: http://maps.google.com/maps/geo?q=Москва&output=json&oe=utf8&sensor=false. Но, как мы знаем, москва есть и в Америке: http://maps.google.com/maps/geo?q=Moscow+US&output=json&oe=utf8&sensor=false . Это всё хорошо, но тут я наткнулся в документации, что это дедовские методы, а сейчас пора Google Maps V3. Вуаля, есть повод для размышлений. Открываю консоль в google chrome и начинаю писать запрос через классы.

Копаем Google Maps V3.4
http://maps.google.com/maps/api/js?sensor=false&v=3.4&language=ru


Все методы, которые были описаны выше работают, но теперь я задаюсь вопросом: как продвинутые пользователи работают с картами. Для начала мы начнём с геокодирования. Есть класс google.maps.Geocoder() который имеет один метод geocode(request:GeocoderRequestcallback:function(Array.<GeocoderResult>,GeocoderStatus)).


GeocoderRequest в свою очередь это обычная спецификация объекта (т.е. как объект должен выглядеть):

var Request = { address : "Адресс для геокодирования",
bounds : "Квадрат поиска, является объектом LatLngBounds",
language : "Желаемый язык результата",
location : "Координаты, вокруг которых искать, является объектом LatLng",
region : "Домен страны, по которой идёт поиск" } - вот и весь объект

Далее привожу описание классов из документации гугла:

google.maps.GeocoderStatus class

The status returned by the Geocoder on the completion of a call to geocode().

Constant

ConstantDescription
ERRORThere was a problem contacting the Google servers.
INVALID_REQUESTThis GeocoderRequest was invalid.
OKThe response contains a valid GeocoderResponse.
OVER_QUERY_LIMITThe webpage has gone over the requests limit in too short a period of time.
REQUEST_DENIEDThe webpage is not allowed to use the geocoder.
UNKNOWN_ERRORA geocoding request could not be processed due to a server error. The request may succeed if you try again.
ZERO_RESULTSNo result was found for this GeocoderRequest.


google.maps.GeocoderResult object specification

A single geocoder result retrieved from the geocode server. A geocode request may return multiple result objects. Note that though this result is "JSON-like," it is not strictly JSON, as it indirectly includes a LatLng object.

Properties

PropertiesTypeDescription
address_componentsArray.<GeocoderAddressComponent>An array of GeocoderAddressComponents
geometryGeocoderGeometryGeocoderGeometry object
typesArray.<string>An array of strings denoting the type of the returned geocoded element. A type consists of a unique string identifying the geocode result. (For example, "administrative_area_level_1", "country", etc.)

google.maps.GeocoderAddressComponent object specification

A single address component within a GeocoderResult. A full address may consist of multiple address components.

Properties

PropertiesTypeDescription
long_namestringThe full text of the address component
short_namestringThe abbreviated, short text of the given address component
typesArray.<string>An array of strings denoting the type of this address component


Теперь с помощью этих классов мы можем поставить карту на нужный адрес. Пример:

var geocoder = new google.maps.Geocoder(); geocoder.geocode( { 'address': "Moscow"}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); } else { alert("Geocode was not successful for the following reason: " + status); } });

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

var geocoder = new google.maps.Geocoder(); geocoder.geocode( { 'address': "Odessa USA"}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { for(var i = 0; i < results.length; i++) console.log(results[i].formatted_address, results[i].geometry.location.lat(), results[i].geometry.location.lng()); } else { alert("Geocode was not successful for the following reason: " + status); } });

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

Первый запрос отлично возвращает информацию о маршруте, а второй совсем не хочет.. пишет ZERO_RESULTS. В документации написано, что не может быть найден маршрут. Но на другом сайте, использующем google maps маршрут прокладывается. В чем дело, гугл? На этом пока ступор.. даже на maps.google.com не выводит маршрута.

JQuery UI и Вывод на экран
jquery тоже имеет очень хорошую документацию и прямо там я нашёл такой пример: http://jqueryui.com/demos/autocomplete/#remote-jsonp 
Это как раз то, что нужно, полностью берём  код примера и копируем себе на страницу
следующие файлы:
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/base/jquery-ui.css" type="text/css" media="all" /> 
<link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all" /> 

и картинку можно взять отсюда

вторник, 24 мая 2011 г.

Перенаправление на страницу авторизации со своего компонента Joomla 1.5

Мне понадобилось добиться от компонента того, чтобы если каким-то образом не авторизованный пользователь перейдёт на страницу компонента, то выскочит форма авторизации и после авторизации пользователь получит желанное содержимое. Не буду многословным, просто приведу код контроллера:


<?php
defined('_JEXEC') or die( 'Restricted access' );
jimport( 'joomla.application.component.controller' );
class HostOrderController extends JController
{
function show()
{
$user =& JFactory::getUser();

if ($user->guest) {
$redirecturl = base64_encode("index.php?option=com_hostorder");
$joomlaLoginUrl = 'index.php?option=com_user&view=login&return=';
       $finalUrl = $joomlaLoginUrl.$redirecturl;

global $mainframe;
$mainframe->redirect($finalUrl);
}
echo "YEAP!";
}
}


На всякий случай код точки доступа:

<?php
defined('_JEXEC') or die('Restricted access');
require_once (JPATH_COMPONENT.DS.'controller.php');


$controller = new HostOrderController( array('default_task' => 'show') );
$controller->execute(JRequest::getVar('task', null, 'default', 'cmd'));
$controller->redirect();

воскресенье, 5 декабря 2010 г.

Socket-соединения в Веб-приложениях


источник

Конечно же в JavaScript`е нету сокетов. Но зато есть такая чудесная вещь как AJAX, а также есть еще более привлекательная вещь как Comet. И с помощью этих двух технологий можно добиться от веб-страницы поразительного сходства с прикладными программами по части socket-соединений.

Хочу немного расказать о сокетах своими словами. Сокет — это что-то вроде радио-приемника, который появляется на сервере после соединения с ним клиента, для обратной связи с клиентским приложением. Как только в этот приемник попадают (записываются) данные, он сразу отправляет их своему приложению-слушателю. Приложение, услышав (прочитав) данные, выполняет какие-либо действия на их основе. Это может быть простое сообщение, которое следует напечатать в чате, или команда которую нужно выполнить итд. Если не углубляться в подробности работы TCP/IP, то примерно так все и происходит.

Ну а теперь давайте вернемся к нашим бара… сокетам. Итак, что нам нужно? Нам нужно сделать так, чтобы как только один соединенный с сервером клиент что-то отправит на сервер, это сразу же было отправлено всем другим соединенным в данный момент с сервером клиентам. Или другая ситуация. Допустим при определенных обстоятельствах послать с сервера сообщение всем соединенным с ним клиентам, и чтобы они его мгновенно (!) получили. Как этого добиться? Я раскажу ниже.

Итак, определим что нам нужно для этого:
  • Один php-файл для сервера.
  • Один html-файл для клиента.
  • Библиотека jQuery.
  • И папка sockets для хранения сокетов.

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

Клиент (index.html):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"«http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd»>

<html xmlns=«http://www.w3.org/1999/xhtml»>
  
    <head>
        <title>Socket AppClient</title>
        <meta http-equiv=«content-type»content=«text/html; charset=utf-8» />
        <meta name=«author» content=«Melnaron»/>
        <link rel=«stylesheet» type=«text/css»href=«css/style.css» />
        <style type=«text/css»>
          
            #log {
               border: 1px solid #999999;
               height: 250px;
               overflow: auto;
               margin: 10px 0;
               padding: 10px;
            }
          
        </style>
        <script type=«text/javascript»src=«lib/jquery.js»></script>
        <script type=«text/javascript»>
          
           $(document).ready(function() {
              
                /*
                * Настройка AJAX
                */
               $.ajaxSetup({url: 'server.php', type: 'post', dataType: 'json'});
              
                /*
                * События кнопок и поля ввода
                */
               $('#btnConnect').click(user.Connect);
               $('#btnDisconnect').click(user.Disconnect);
               $('#btnSend').click(user.Send);
               $('#input')
                   .keydown(function(e){ if (e.keyCode == 13) { user.Send();return false; } })
                   .keypress(function(e){ if (e.keyCode == 13) { return false; }})
                   .keyup(function(e){ if (e.keyCode == 13) { return false; } })
                ;
              
            });
          
        </script>
        <script type=«text/javascript»>
          
            /*
             * Лог
             */
            var log = {
              
               print: function(s) {
                  $('#log').append('<div>'+s+'</div>').get(0).scrollTop += 100;
                }
              
            };
          
            /*
             * Действияприсылаемые с сервера
             */
            var actions = {
              
               Connect: function(params) {
                   log.print('Connected.');
                   log.print('Sock: '+params.sock);
                   user.sock = params.sock;
                   user.conn = true;
                   user.Read();
                },
              
               Disconnect: function(params) {
                   log.print('Disconnected.');
                },
              
               Print: function(params) {
                   log.print(params.message);
                }
              
            };
          
            /*
             * Пользователь(клиент)
             */
            var user = {
              
               sock: null,
              
               conn: false,
              
               busy: false,
              
               read: null,
              
                /*
                * Эта функция обрабатывает приходящие с сервера действия и выполняет их.
                */
               onSuccess: function(data) {
                   if (typeof data.actions == 'object') {
                       for (var i = 0; i <data.actions.length; i++) {
                           if (typeofactions[data.actions[i].action] == 'function') {
                              actions[data.actions[i].action](data.actions[i].params);
                           }
                       }
                   }
                },
              
                /*
                * Эта функция выполняется по завершении ajax-запроса.
                */
               onComplete: function(xhr) {
                   if (xhr.status == 404) {
                       actions.Disconnect();
                   }
                   user.busy = false;
                },
              
                /*
                * Эта функция выполняется по завершении запроса-слушания.
                * При удачном завершении запроса (==200) моментальное возобновлениепрослушивания соккета.
                * При неудачном (!=200) возобновление через 5 секунд.
                */
               onCompleteRead: function(xhr) {
                   if (xhr.status == 200) {
                       user.Read();
                   } else {
                       setTimeout(user.Read, 5000);
                   }
                },
              
                /*
                * Действие.
                * Соединение с сервером.
                */
               Connect: function() {
                   if (user.conn == false && user.busy == false) {
                       log.print('Connecting...');
                       user.busy = true;
                       $.ajax({
                           data: 'action=Connect',
                           success:user.onSuccess,
                           complete:user.onComplete
                       });
                   }
                },
              
                /*
                * Действие.
                * Отсоединение от сервера.
                */
               Disconnect: function() {
                   if (user.conn && user.busy == false &&user.read) {
                       log.print('Disconnecting...');
                       user.busy = true;
                       $.ajax({
                           data:'action=Disconnect&sock='+user.sock,
                           success:user.onSuccess,
                           complete:user.onComplete
                       });
                       user.sock = null;
                       user.conn = false;
                       user.read.abort();
                   }
                },
              
                /*
                * Действие.
                * Отправка данных на сервер.
                */
               Send: function() {
                   if (user.conn) {
                       var data = $.trim($('#input').val());
                       if (!data) {
                           return;
                       }
                       $.ajax({
                           data:'action=Send&sock='+user.sock+'&data='+data,
                           success:user.onSuccess,
                           complete:user.onComplete
                       });
                       $('#input').val('');
                   } else {
                       log.print('Please connect.');
                   }
                },
              
                /*
                * Действие.
                * Прослушивание соккета.
                */
               Read: function() {
                   if (user.conn) {
                       user.read = $.ajax({
                           data:'action=Read&sock='+user.sock,
                           success:user.onSuccess,
                           complete:user.onCompleteRead
                       });
                   }
                }
              
            };
          
        </script>
    </head>
  
    <body>
        <input id=«btnConnect» type=«button»value=«Connect» />
        <input id=«btnDisconnect» type=«button»value=«Disconnect» />
        <div id=«log»></div>
        <input id=«input» type=«text» />
        <input id=«btnSend» type=«button»value=«Send» />
    </body>
  
</html>



Сервер (server.php):

<?php

class Server {
  
    /*
     * Стек действий для отправки клиенту в текущем запросе.
     */
    static private $actions;
  
    /*
     * Основная функция сервера.
     * Запускает на выполнение переданное клиентом действие.
     */
    static function Run() {
        foreach (glob('sockets/*') as $sock) {
            if (filesize($sock)> 2048) {
               unlink($sock);
            }
        }
        $action = 'action'.$_POST['action'];
        if (is_callable('self::'.$action)) {
            self::$action();
            self::Send();
        }
    }
  
    /*
     * Эта функция пишет действие в сокеты.
     * Если передан параметр $self, то исключает указанный в этом параметре сокет.
     */
    static function AddToSock($action, $params = '', $self =null) {
        foreach (glob('sockets/*') as $sock) {
            if ($self &&strpos($sock, $self) !== false) {
               continue;
            }
            $f = fopen($sock,'a+b') or die('socket not found');
            flock($f, LOCK_EX);
            fwrite($f, '{action:"'.$action.'", params: {'.$params.'}}'."\r\n");
            fclose($f);
        }
    }
  
    /*
     * Эта функция добавляет действие в стек для отправки в текущем запросе.
     */
    static function AddToSend($action, $params = '') {
        self::$actions[] = '{action:"'.$action.'", params: {'.$params.'}}';
    }
  
    /*
     * Отправка стека действий на выполнение клиенту.
     */
    static function Send() {
        if (self::$actions) {
            exit('{actions:['.implode(', ', self::$actions).']}');
        }
    }
  
    /*
     * Действие.
     * Соединение с сервером.
     * Создает сокет и отправляет его идентификатор клиенту.
     */
    static function actionConnect() {
        $sock = md5(microtime().rand(1, 1000));
        fclose(fopen('sockets/'.$sock, 'a+b'));
        self::AddToSock('Print', 'message: «Clientconnected.»', $sock);
        self::AddToSend('Connect', 'sock:"'.$sock.'"');
    }
  
    /*
     * Действие.
     * Отсоединение от сервера.
     * Удаляет сокет.
     */
    static function actionDisconnect() {
        $sock = $_POST['sock'];
        unlink('sockets/'.$sock);
        self::AddToSock('Print', 'message: «Client disconnected.»');
        self::AddToSend('Disconnect');
    }
  
    /*
     * Действие.
     * Отправляет введенные данные всем клиентам.
     */
    static function actionSend() {
        $sock = $_POST['sock'];
        $data =htmlspecialchars(trim($_POST['data']), ENT_QUOTES);
        if (strlen($data)) {
           self::AddToSock('Print', 'message: "'.$data.'"', $sock);
           self::AddToSend('Print', 'message: "'.$data.'"');
        }
    }
  
    /*
     * Действие.
     * Слушает сокет до момента когда в нем появятся данные или же до истечения таймаута.
     */
    static function actionRead() {
        $sock = $_POST['sock'];
        $time = time();
        while ((time() — $time) < 30) {
            if ($data =file_get_contents('sockets/'.$sock)) {
                $f =fopen('sockets/'.$sock, 'r+b') or die('socket not found');
               flock($f, LOCK_EX);
               ftruncate($f, 0);
               fwrite($f, '');
               fclose($f);
               $data = trim($data, "\r\n");
               foreach (explode("\r\n", $data) as $action) {
                   self::$actions[] = $action;
                }
               self::Send();
            }
            usleep(250);
        }
    }
  
}

if (@$_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
  
    header('content-type: text/plain; charset=utf-8');
  
    Server::Run();
  
}

?>



Для того чтобы увидеть это чудо в действии вам следует открыть приведенную ниже ссылку в 2х или более окнах браузера (или вообще в разных браузерах).

Итак, работающий пример находится тут.

Открываем, распологаем окна так чтобы работая в одном окне видить что происходит в другом. Соединяемся с сервером.

Соединив с сервером первое окно-приложение вы увидите идентификатор своего сокета отправленный с сервера. Соединив второе окно, в первом вы тут же увидите сообщение о соединении клиента. А теперь попробуйте наблюдая за первым окном что-то отправить из второго. Я надеюсь интернет у вас не очень медленный/загруженный, и вы увидите как быстро сообщение отобразится в обоих окнах — почти мгновенно.

Итак, это простой пример. В коде конечно можно еще покопаться. Для старта я думаю хватит.

Исходники полностью работающего примера можно скачать тут.