2008-07-25

PHP: Уменьшаем исходящий трафик (или изобретаем очередной велосипед)

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

class OutBufferFilter {
function OutBufferFilter() {
if( @ob_start( array(&$this, 'filter') ) ) {
register_shutdown_function( array(&$this, 'shutdown') );
}
}
function filter( $chunk, $mode ) {
return(
str_replace( '> <', '><',
ereg_replace( "[ \t\r\n]{2,}", ' ',
$chunk
)
)
);
}
function shutdown() {
while( @ob_end_flush() );
}
}

Понятное дело, что метод filter не идеален, но на том HTML-коде который есть у меня он работает (в дизайне не используется теги типа pre)

Потом была добавлена компрессия gzip-ом:

class OutBufferFilterGZ extends OutBufferFilter {
var $use_gzhandler = false;

function OutBufferFilterGZ() {
$this->use_gzhandler = function_exists('ob_gzhandler');

if( @ob_start( array(&$this, 'filter') ) ) {
register_shutdown_function( array(&$this, 'shutdown') );
}
}
function filter( $chunk, $mode ) {
return(
$this->use_gzhandler ?
ob_gzhandler( parent::filter( $chunk ), $mode ) :
parent::filter( $chunk )
);
}
}

Потом класс был расширен на предмет того что-бы кешировать полученные страницы в memcached-е, и если в качестве frontend-а используется nginx, то контент отдавался без участия php, а напрямую из memecache

class OutBufferCached extends OutBufferFilterGZ {
var $memcache = false;
var $content = '';
var $timeout = 10;

function key() {
global $_SERVER;

return( ( isset( $_SERVER )
and isset( $_SERVER["REQUEST_METHOD"] )
and $_SERVER["REQUEST_METHOD"] == 'GET'
) ?
( 'pda:html:'
. (isset( $_SERVER["SCRIPT_NAME"] ) ? $_SERVER["SCRIPT_NAME"] : '')
. (isset( $_SERVER["PATH_INFO"] ) ? $_SERVER["PATH_INFO"] : '' )
. '?'
. (isset( $_SERVER["REQUEST_URI"] ) ? $_SERVER["REQUEST_URI"] : '' )
) :
''
);
}
function OutBufferCached( $memcache = false ) {
$key = $this->key();

if( !empty($key) and $memcache and @$memcache->getVersion() !== false ) {
$this->memcache = $memcache;
$this->content = $this->memcache->get( $key );

if( $this->content !== false ) {
$this->shutdown( false );
exit();
}
else {
$this->content = '';
}
}
if( @ob_start( array(&$this, 'filter') ) ) {
register_shutdown_function( array(&$this, 'shutdown') );
}
}
function filter( $chunk, $mode ) {
$this->content .= parent::filter( $chunk, $mode );

return(null);
}
function shutdown( $store = true ) {
parent::shutdown();
$this->output();
if( $store ) {
$key = $this->key();

if( $this->memcache and !empty($key) and $this->timeout ) {
$this->memcache->set(
$key,
$this->content,
MEMCACHE_COMPRESSED,
$this->timeout
);
}
}
$this->content = '';
}
function output() {
echo $this->content;
flush();
}
function timeout( $t = 10 ) {
$this->timeout = $t;
}
function nocache() {
$this->timeout( 0 );
}
}

Все тестировалось на PHP4 и PHP5, в качестве http-сервера использовалось: apache, nginx + php/fastCGI - пока проблем не возникло.
Собственно хотелось-бы узнать насколько интересен изобретенный мною велосипед ?