Янв 18

Периодически возникают задачи типа вернуть дату «первого числа прошлого месяца», или «прошлого понедельника».
Это можно решить как-то так:

	$dt = self::getCurrentMonthStartDt();
	$dt->setTime( 0, 0, 0 );
	$dt->setDate( $dt->format( 'Y' ), $dt->format( 'm' ) - 1, 1 );
	return $dt;
...
	$dt = new DateTime();
	$dt->modify( '-' . ( $dt->format( 'N' ) - 8 ) . ' days' );
	$dt->setTime( 0, 0, 0 );
	return $dt;

А можно гораздо проще:

	$dt = new DateTime( 'first day of last month' );
	$dt->setTime( 0, 0, 0 );
	return $dt;
...
	$dt = new DateTime( 'mon this week' );
	$dt->setTime( 0, 0, 0 );
	return $dt;

It’s a kinda magic, но работает.
http://www.php.net/manual/ru/datetime.formats.relative.php

Теги:
Окт 18

Работал раньше сайт с memcache.
Решили перейти на memcached.
И повалились ошибки SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY, причём сначала одна CLIENT ERROR, а уже потом куча SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY.
Гугление ничего не дало, а проблема оказалась в следующем: первая ошибка возникала из-за некорректного ключа (пробел в названии ключа), после чего сервер расстраивался, и больше ничего делать не давал.
Ключ был следующий: info__rating DESC__1, генерился автоматически и никаких проблем при использовании его в memcache не вызывал. Понятное дело, что ключ плохой, но это не мешало другому расширению успешно работать.
А решение применили банальное: $Key = str_replace( ‘ ‘, ‘_’, $Key );, теперь вроде работает).
Окончательно все проблемы решила опция

$this->setOption( Memcached::OPT_BINARY_PROTOCOL,   true );

После этого лог ошибок стал пустым.
Коллега, который этим вопросом плотно занимается, считает, что это оттого, что по умолчанию протокол используется plain text (а именно, ascii), и UTF-символы интерпретируются неправильно (у нас ферма из нескольких машин, и на каждой окружение несколько отличается, к сожалению). И как только включили бинарный протокол, разногласия исчезли.

	function __construct()
	{
		parent::__construct();

		$this->setOption( Memcached::OPT_BINARY_PROTOCOL, true );
		$this->setOption( Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT );
		$this->setOption( Memcached::OPT_HASH, Memcached::HASH_CRC );
		$this->setOption( Memcached::OPT_SERVER_FAILURE_LIMIT, 3 );
		$this->setOption( Memcached::OPT_NO_BLOCK, true );									// асинхронный ввод-вывод
		$this->setOption( Memcached::OPT_TCP_NODELAY, true );									// при работе с сокетами надо потестить - может ускорить работу

		$this->connection = $this->addServers( array(	array( <хост>, <порт> ) ) );
		...
	}

При использовании ключа:

	...
	$key = str_replace( array( ' ', ',', ':' ), '_', $key );

	if ( strlen($key) > 220 ) {
		$key = md5( $key );
	}
	...
Теги:
Сен 23
	$limit = 512;
	$thresholdDelta = 10;
	ini_set('memory_limit', $limit . 'm');
	...
	$memoryThresholdReached = false;
	$memoryThreshold = ( $limit - $thresholdDelta ) * 1024 * 1024;
	...
	while ( $user = $db->plain_fetch( $result ) )
	{
		...
	// тут работа с данными
		...
	// проверка на превышение опасного порога использования памяти
		if ( ! $memoryThresholdReached && memory_get_usage( true ) > $memoryThreshold )
		{
			$memoryThresholdReached = true;
			try {
				throw new SystemException( 'Превышен порог использования памяти...' );
			}
			catch ( Exception $e ) {}
		}
	}

Более серьёзные способы – просмотр стека (strace -f -p $pid) и дебаг php C-шным дебаггером DBG.

Теги:
Июл 03

Smarty – странноватый и глючноватый шаблонизатор, если сравнивать с Django.
Но под php ничего под руку не попалось, чтобы работало из коробки, было просто в настройке, и, к тому же, когда-то давно я его уже ковырял.
В общем, нужно было срочно прикрутить шаблонизатор – сделал следующее.

  • В классе, ответственном за вывод:
    	private static function getSmarty( array $Params )
    	{
    		require_once( SMARTY . 'Smarty.class.php' );
    		$smarty = new Smarty();
    		$smarty->error_reporting = E_ALL & ~E_NOTICE;
    		$smarty->force_compile = true;
    		$smarty->left_delimiter = '{{';
    		$smarty->right_delimiter = '}}';
    		$smarty->template_dir = TEMPLATE;
    		foreach ( $Params as $key => $value ) {
    			$smarty->assign( $key, $value );
    		}
    
    		return $smarty;
    	}
    
    	public function renderToView( rController $Controller, $Debug = false )
    	{
    		$contentType = $Controller->getContentType();
    		self::setHeaders( $contentType );
    		switch ( $contentType )
    		{
    			case self::CONTENT_TYPE_JSON :
    				echo json_encode( $Controller->getTemplateData() );
    				break;
    			case self::CONTENT_TYPE_HTML :
    			default :
    				$templatePath = $Controller->getTemplatePath();
    				if ( ! $templatePath ) {
    					$templatePath = $this->generateTemplatePath( $Controller );
    				}
    				if ( ! file_exists( TEMPLATE . $templatePath ) )
    				{
    					if ( $Debug ) {
    						echo 'tried ' . TEMPLATE . $templatePath;
    					}
    					$templatePath = $this->defaultTemplatePath;
    				}
    
    				self::getSmarty( $Controller->getTemplateData() )->display( $templatePath );
    		}
    	}
  • Во вьюхе:
    // layout.tpl
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <head>
    <title>{{if isset( $title )}}{{$title}}{{else}}{{$defaultTitle}}{{/if}}</title>
    <script type="text/javascript" src="/js/main.js"></script>
    {{block name="head"}}{{/block}}
    </head>
    <body>
    {{block name="content"}}{{/block}}
    </body>
    
    // index.tpl
    {{extends 'layout.tpl'}}
    {{block name="content"}}
    	{{assign 'myparamm' 'Bob'}}
    	myparamm {{$myparamm}}
    {{/block}}

Примечательно то, что если (см. код index.tpl) присвоение значения переменной myparamm вынести за пределы конструкции block, то переменная внутри будет неопределена, в документации об этом ни слова)). Также для конструкции block можно указать параметры prepend или append, чтобы не перетирать родительский блок, а дописывать в конец или в начало блока.
В общем получаются похожие на django шаблоны, и жить немного легче.

Насчёт наследования в Smarty 3 можно почитать на официальном сайте:

Теги:
Июн 08

Решили использовать Pheanstalk, который представляет собой PHP-клиента для Beanstalk.
Простой, чётко работает, всё построено на иерархии исключений, т.е. легко применять.
Всё сводится к следующему:

$this->queueManager = new Pheanstalk( '127.0.0.1' );
$this->queueManager->useTube('my_jobs_1')->put( $JobData );
$this->job = $this->queueManager->watch('my_jobs_1')->ignore('default')->reserve( 0 ); // 0 - чтобы не ждать, когда появятся новые джобы (чтобы запускать php-скрипт кроном, а не требовать висения в памяти демоном)
$this->queueManager->delete( $this->job );

Для работы должен быть запущен демон Beanstalk:

$ beanstalkd -d

Ещё почитать.
Интересный пример использования Pheanstalk, менее интересный пример.

Теги:
Мар 28

Вкратце: трейт (аналог mixin в Ruby) – возможность в классе использовать функционал нескольких классов в обход традиционного наследования (по-моему, костыль, чтобы не реализовывать множественное наследование).
Всё сводится к новой инструкции use внутри класса, при этом класс, функциональность которого нужно подмешивать. объявляется инструкцией trait.

trait Singleton {
    public static function getInstance() { ... }
}

class A {
    use Singleton;
    // ...
}

class B extends ArrayObject {
    use Singleton;
    // ...
}

// Singleton method is now available for both classes
A::getInstance();
B::getInstance();

Подробнее:
New to PHP 5.4: Traits

Теги:
Мар 27

http://css.dzone.com/books/practical-php-patterns

Теги:
Мар 03

Есть такая библиотека – twitter-async (лицензия – свободное распространение и использование).
Из неё нужно включить в проект файлы

  • EpiCurl
  • EpiOAuth
  • EpiSequence
  • EpiTwitter

Далее на twitter.com нужно завести приложение.
После успешной регистрации Twitter выдаст полезные данные для oAuth-авторизации, нужны из них два: Consumer key и Consumer secret – это авторизационные данные приложения, их нужно сохранить где-нибудь, к примеру, в базе.
Continue reading »

Теги:
Авг 17

1. Скачиваем расширение – http://www.xdebug.org/download.php (я качаю ts) и копируем в папку с расширениями php.
2. В php.ini пишем

zend_extension_ts = "< путь-к-папке-ext>/php_xdebug.dll"
xdebug.remote_enable=1
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_port=9000
xdebug.remote_host=localhost
xdebug.idekey=bullgare ;ну или другой

;это для профилирования
xdebug.profiler_enable=Off
xdebug.profiler_output_dir="c:\traces"
xdebug.profiler_enable_trigger=On
xdebug.profiler_output_name = cachegrind.out.%t.%p

После перезапуска апача phpinfo будет содержать раздел xdebug.
Continue reading »

Теги:
Июл 30

Вот достаточно подробная статья на эту тему.
В принципе достаточно написать

xdebug_start_trace('<Путь-к файлу>.html', XDEBUG_TRACE_HTML);
...
...
xdebug_stop_trace();
Теги:
preload preload preload