barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
Показаны сообщения с ярлыком php. Показать все сообщения
Показаны сообщения с ярлыком php. Показать все сообщения

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

php: определение ОС сервера

Вариантов определения ОС сервера (win/unix) несколько:
  1. Константа PHP_OS. Для Win (по крайней мере XP и 7) равна 'WINNT', для Linux - 'Linux' и т.п.
  2. php_uname() - дает более полную информацию об ОС строкой 
  3. posix_uname() - выдает примерно ту же информацию массивом

вторник, 15 января 2013 г.

wamp: установка pear-пакетов с помощью pyrus

Попробовал установить пакет pear/Mail с помощью Pyrus:
php pyrus.phar install pear/Mail
Однако получил:
You must compile PHP with the following extensions enabled:
sqlite3
or install the necessary extensions for your distribution.
При этом в php.ini, используемом wamp`ом, расширение sqlite3 включено:
extension=php_sqlite3.dll
Проблема оказалась вот в чем. При запуске из cmd php использует не тот php.ini, что под апачем : php.exe использует \bin\php\php5.3.10\php.ini относительно директории установки wamp`а (узнать это можно просто, командой "php --ini"), а под апачем - bin\apache\Apache2.2.21\bin\php.ini. Т.е. для запуска pyrus нужно внести изменения именно в \bin\php\php5.3.10\php.ini (или указать путь в апачевскому php.ini с помощью опции -c).

пятница, 5 октября 2012 г.

php: установка заголовков запроса при загрузке данных по http с помощью функции file_get_contents()

Чтобы установить какой-нибудь заголовок http-запроса при загрузке данных по http функцией file_get_contents(), нужно использовать третий параметр этой функции - $context. Например, чтобы установить User-Agent как у 15ого Firefox`а, нужно:
$opts = array('http'=>array('header' => "User-Agent:Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20100101 Firefox/15.0.1\r\n"));
$context = stream_context_create($opts);
$html = file_get_contents($link,false,$context);
А то некоторые сайты, например, без этого плюются 500ой ошибкой и не дают себя по-человечески сграбить =)
Warning: file_get_contents(http://***.ru/***): failed to open stream: HTTP request failed! HTTP/1.1 500 Internal Server Error

пятница, 13 апреля 2012 г.

Скробблинг на last.fm с MTP-девайсов в Linux

Скробблить всё, что слушаешь, на ласт - достаточно навязчивая привычка. Т.к. львиную долю музыки я слушал на плеере (сейчас правда перешел на андроидофон), возникло желание как-то вытягивать с него проигранные треки и скробблить на ласт. Встал  вопрос - каким же образом?
Плеер мой - старичок Creative Zen V, никакой операционки в нём и в помине нет, поэтому написанием приложения под неё не отделаться. Писать свою прошивку тоже желания не было =) Надежду во мне посеял тот факт, что плеер сохраняет для каждого трека число его проигрываний, которое можно посмотреть, зайдя в свойства трека в интерфейсе плеера. Осталось теперь научиться получать это число с ПК, и желательно, уметь ещё и обнулять его после скробблинга. Потеря времени проигрывания терка для меня лично была некритична - я готов был скробблить все загруженные с девайса треки "пачкой", начиная с некоторого заданного момента времени.
Т.к. ПК видит мой плеер как MTP-устройство, я сразу решил посмотреть, можно ли по MTP видеть число воспроизведений. Оказалось можно - это умеет даже проигрыватель Amarok. Раз умеет амарок - значит научусь и я, даже если придется покопаться в сорцах амарока. Копаться правда не пришлось - то ли в зависимостях, то ли где-то ещё, я увидел, что для работы с MTP-устаройствами он использует библиотеку libmtp, обладающую, как оказалось, отличной документацией с достаточным количеством примеров.  Так что встала задача разработки приложения, извлекающего проигрывания в некоторую БД, из которой потом уже можно сробблить. 
В итоге получилась консольная программка, извлекающая все треки с девайса и сохраняющая их в БД sqlite, после чего сбрасывающая счетчики проигрываний в плеере. По каким-то причинам, правда, я позже решил отказаться от sqlite, и стал вместо вставки данных в БД формировать SQL-дамп, который можно потом загрузить куда угодно (точнее в ту СУБД, которая съест используемый синтаксис).
Теперь дело встало за малым - извлечь инфу из БД, и заскробблить на ласт. Тут я решил поискать опенсорсный скробблер, т.к. копать REST-API ласта не хотелось. Вариант нашелся, имя ему - lastfmsubmitd, его можно поставить из репов Debian / Ubuntu. В его комплект, помимо демона-скробблера, входит вспомогательный скрипт-скробблер, принимающий параметры скроблимого трека через аргументы командной строки. Задача формирования последовательности вызовов этого скробблера (в виде sh-скрипта) на основании инфы из базы легла на php-скрипт, который написался достаточно быстро. 
В итоге последовательность действий для сробблинга получилась такая:
1) Подключаю девайс, запускаю свою C++-программку, формирую SQL дамп
2) Гружу дамп в MySQL
3) Правлю в php-скрипте время начала скробблинга, запускаю его, получаю на выходе sh-скрипт.
4) Запускаю sh, после того, как он отработает, запускаю демон скробблера lastfmsubmitd. И вуаля - треки улетают на ласт.
Сложновато конечно вышло =) Но работает, и с учетом того, что повторял я эти действия не очень часто - пару раз в неделю, меня это не особо напрягало. Ну а потом я разжился андридофоном (вернее, купил уже 4ый по счету андроидофон, на котором качество проигрывания музыки меня наконец-то устроило), и необходимость скробблить с плеера отпала - сробблеров под андроид полным полно.
Сорцы своих "творений" я выложил на github - вдруг кому будет интересно: https://github.com/barbitoff/mtpScrobblingUtils.

суббота, 7 января 2012 г.

1C-Bitrix на Debian

Для установки битрикса с кодировкой UTF-8 на установленный из репозитория Debian Apache2+php нужно изменить следующие настройки:
1. /etc/apache2/sites-available/default: в блоке <Directory /var/www/> установить:
AllowOverride All
2. /etc/php5/apache2/php.ini: раскомментировать и отредактировать следующие значения:
mbstring.internal_encoding = UTF-8
mbstring.func_overload = 2

вторник, 27 декабря 2011 г.

JSP-аналог $_SERVER['DOCUMENT_ROOT'] в php

Иногда бывает необходимо из веб-странице определить реальный путь к какому-либо файлу (т.е. не путь относительно контекста сервлета / корня виртуального хоста, а именно путь в файловой системе). В php такой путь можно определить исходя из $_SERVER['DOCUMENT_ROOT'] и пути относительно корня виртуального хоста, в JSP же это делается так:
this.getServletContext().getRealPath("/some/path/relative/to/context");
Путь, аналогичный $_SERVER['DOCUMENT_ROOT'], получается вызовом:
this.getServletContext().getRealPath("/");

четверг, 15 декабря 2011 г.

Получение заголовков запроса из php


Если сервер - apache2 и используется mod_php, заголовки получаются вызовом apache_request_headers(). Чтобы код был переносимым и не зависел от сервера, под которым работает скрипт, можно сделать так:
if(!function_exists('apache_request_headers'))
{
function apache_request_headers()
{
$headers = array();
foreach($_SERVER as $key => $value) {
if (substr($key, 0, 5) <> 'HTTP_') {
continue;
}
$header = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$header] = $value;
}
return $headers;
}
}


вторник, 22 ноября 2011 г.

PHP Startup: Unable to load dynamic library '.../php_pgsql.dll' - Не найден указанный модуль

Проблема:
При попытке запуска apache + php под Windows с подключенным в php.ini расширением php_pgsql.dll в логи апача вываливает ошибка:
PHP Startup: Unable to load dynamic library '.../php_pgsql.dll' - Не найден указанный модуль
Ну и модуль, естественно, не загружается.
Причина:
Php не может найти динамические библиотеки Postgres, необходимые расширению для работы.
Решение:
Добавить директории bin и lib установки Postgres в системный PATH (например, E:\Programs\Postgres8.3\lib;E:\Programs\Postgres8.3\bin).

воскресенье, 20 ноября 2011 г.

Как мне НЕ удалось подружить WSO2 WSF/PHP 2.1.0 и 1С-Битрикс Веб-Окружение 2.1.

Собственно возникла необходимость использования внутри компонента 1С-Битрикс функционала SOAP-клиента, и для этого был выбран WSO2 WSF/PHP (http://wso2.org/library/wsf/php).
Для начала был скачан бинарник WSF/PHP версии 2.1.0, распакован (в директорию "F:\Install\wso2-wsf-php-bin-2.1.0-win32\"), wsf.dll был положен в папку "apache2\zendserver\lib\phpext\" относительно директории установки окружения 1С-Битрикс. В php.ini Битрикса (apache2\zendserver\etc\php.ini) были внесены следующие изменения:
...
[Zend]
include_path=".;F:\Install\wso2-wsf-php-bin-2.1.0-win32\scripts"
...
extension=php_xsl.dll
extension=wsf.dll
...
wsf.home="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c"
wsf.log_path="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c\logs"
wsf.log_level=3
wsf.rm_db_dir="F:\Install\wso2-wsf-php-bin-2.1.0-win32\wsf_c"
...
Однако при попытке запустить битрикс стала появляться ошибка о том, что не найден файл "php5ts.dll". Действительно, в директории apache2\zendserver\bin можно найти только php5.dll, т.к. php в Битриксе видимо не-threadsafe. Ладно, попробуем подложить туда ещё и php5ts.dll (взят он был из php 5.3.5). Теперь сообщение об отсутствующем php5ts.dll ушло, однако расширение wsf все равно не подключается, а в логах видим:
[20-Nov-2011 17:23:36] PHP Warning:  PHP Startup: wsf: Unable to initialize module
Module compiled with build ID=API20090626,TS,VC9
PHP    compiled with build ID=API20090626,NTS,VC9
These options need to match
 in Unknown on line 0
Теперь build ID модуля конфликтует с версией битриксовского php - у битрикса php non-threadsafe, о чем говорит аббревиатура NTS, а модуль собран для threadsafe (TS). 
Придется видимо собирать WSF из сорцов, благо они есть на wso2.org.
Помимо самих сорцов, пришлось скачать и установить Win32 OpenSSL (http://www.slproweb.com/products/Win32OpenSSL.html), ну и конечно же, понадобилась Visual Studio. Лучше девятка (2008). У меня была только 2010, что в дальнейшем и послужило граблями. Также понадобились исходники и бинарники php, которые были скачаны с php.net (бинарники, правда, были взяты threadsafe, что в дальнейшем послужило проблемой). В принципе, в качестве бинарников можно было взять php из самого Битрикса, однако их там в явном виде не присутствует, т.к. в основе окружения битрикса лежит Zend Server, так что пришлось брать со стороны.
Для того, чтобы собрать не-threadsafe версию WSF/PHP, нужно в configure.in распакованных исходников закомментировать строку:
# ZTS =1
Далее в этом же файле нужно прописать путь к директории установки Open SSL, к исходниками и бинарникам php. В моём случае это выглядело так:
OPENSSL_BIN_DIR = E:\Programs\OpenSSL-Win32
PHP_SRC_DIR = F:\Install\php-5.3.8-src
PHP_BIN_DIR = C:\php\php5.3.8
Перед сборкой необходимо проверить, что директория VC\bin относительно директории установки Visual Studio включена в PATH.
Пробуем собрать, запуская build.bat в директории исходников WSF/PHP, однако получаем ошибку:
f:\install\php-5.3.8-src\zend\zend_config.w32.h(25) : fatal error C1083: Cannot open
include file: '../main/config.w32.h': No such file or directory
Оказывается, прежде чем выполнять build.bat исходников WSF/PHP, нужно запустить в директории исходников php:
buildconf.bat
cscript configure.js
После этого при запуске cscript вылезла ошибка "Отсутствует исполняющее ядро для расширения имени файла ".js"" - см. http://barbitoff.blogspot.com/2011/11/cscript-js.html, потом -  "The application has failed to start bacause mspdb100.dll was not found ..." при попытке вызова cl.exe, пришлось предварительно запустить vcvars32.bat из Visual Studio (см. http://barbitoff.blogspot.com/2011/11/ms-visual-studio-clexe-mspdb100dll-not.html).
Но и тут ждет неудача:
ERROR: bison is required
Идем на http://gnuwin32.sourceforge.net/packages/bison.htm, качаем и устанавливаем Бизон, добавляем путь к его исполняемым файлам (расположены в bin директории) в PATH.
После этого configure.js отработал удачно, создав недостающий ранее файл "main/config.w32.h".
Возвращаемся в исходники WSF/PHP, жмем build.bat, но снова ошибка:
LINK : fatal error LNK1181: cannot open input file 'php5.lib'
Библиотеку линкер ищет в бинарниках php, и, естественно, там её не находит, т.к. бинарники threadsafe, и там есть только php5ts.lib. Берем php5.lib из "apache2\zendserver\bin" Битрикса, кладем в папку dev бинарников php. Снова делаем build.bat... и ура, WSF собрался.
Кладем wsf.dll куда надо, меняем пути в php.ini к новым, только что собранным директориям. Пытаемся запустить Битрикс, и видим в логах:
[20-Nov-2011 21:21:12] PHP Warning:  PHP Startup: wsf: Unable to initialize module
Module compiled with build ID=API20090626,NTS,VC10
PHP    compiled with build ID=API20090626,NTS,VC9
These options need to match
 in Unknown on line 0
Да, теперь не совпадает версия студии. И на этом моменте надо было бы поискать 2008 студию, но я решил обмануть php. В Zend\zend_build.h исходников php поправил 
#define ZEND_BUILD_SYSTEM "," PHP_COMPILER_ID
на 
#define ZEND_BUILD_SYSTEM ",VC9"
Пересобрал WSF/PHP. Модуль наконец-таки загрузился, вот только примеры из дистрибутива не работают, php падает молча, а в логах винды видно:
Имя сбойного приложения: php-cgi.exe, версия: 5.3.2.0, отметка времени: 0x4c0b8710
Имя сбойного модуля: MSVCR90.dll, версия: 9.0.30729.4974, отметка времени 0x4b7a226f
Код исключения: 0xc0000005
Смещение ошибки: 0x0003ae7a
Идентификатор сбойного процесса: 0x20fc
Время запуска сбойного приложения: 0x01cca817c8b93692
Путь сбойного приложения: E:\Programs\Bitrix Environment\Apache2\ZendServer\bin\php-cgi.exe
Путь сбойного модуля: C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.4974_none_50940634bcb759cb\MSVCR90.dll
Код отчета: 0845dff7-140b-11e1-8fee-fcf4765882af
Мда, придется все-таки пересобрать 9ой студией, вдруг поможет.
2008 студия поставлена, теперь нужно перезапустить config.js исходников php (на всякий случай), для этого потребуется, чтобы в PATH были директории "Common7\Tools" и "VC\bin" 2008 студии, причем в таком порядке и перед путями к 2010 студии (удобнее менять в данном случае не глобальный PATH системы, а устанавливать PATH командой set).

И да, слеши в путях в php.ini пришлось поставить в другую сторону, как в Unix ("/"), т.к. глупый WSF использует "/" независимо от платформы, конкатенируя свои пути с путями из php.ini.

Однако и после всех этих манипуляций собранная 2008-ой студией библиотека WSF работать отказалась, с теми же сообщениями в логах, что и собранная 2010-ой.

Плюнув на самостоятельную сборку non-threadsafe библиотеки WSF, я развернул битрикс не в его родном веб-окружении, а на wamp 2.2a, благо там php threadsafe и собран 2008-ой студией, т.е. бинарник WSF должен подойти. Собственно, ожидания оправдались, WSF подключился, тестовые примеры заработали.

Дальше правда всё снова стало грустно: при попытке создать клиент по моей wsdl WSF стал молча виснуть, ничего не говоря в логи, после чего я забил на него и воспользовался стандартной php-шной библиотекой php_soap, которая, по крайней мере по функционалу soap-клиента, ничем не хуже WSF.

пятница, 18 ноября 2011 г.

"1С-Битрикс Веб-окружение": добавление поддержки Postgres в php

В "Веб-окружении" "1С-Битрикс" php-модуль для работы с Postgres уже есть, нужно только включить его в php.ini, расположенном в apache2\zendserver\etc относительно директории установки окружения:
extension=php_pgsql.dll

суббота, 12 ноября 2011 г.

Округление чисел с плавающей точкой до нужного знака после запятой в php и MySQL

php:
round(3.01234, 4); // округление до 4 знака после запятой, получим 3.0123
MySQL:
FORMAT(12332.123456, 4); -- округление до 4 знака после запятой, получим 12332.1235

среда, 17 августа 2011 г.

Кэширование данных в компонентах 1С-Битрикс

Иногда бывает необходимо закэшировать какие-нибудь данные (а не html), например, результат работы логики компонента, т.е. массив $arResult. Ниже приведен пример использования средств кэширования 1С-Битрикс, имеющий следующие особенности:
  • используется 1С-Битрикс-класс CPHPCache
  • управление кэшированием ведется через стандартные параметры компонента CACHE_TYPE и CACHE_TIME (кэширование включается, когда тип кэширования установлен в "Авто+Управляемое", что соответствует значению 'A' в коде включения компонента)
  • кэш зависит от всех параметров массива $_GET 
  • кэш хранится в папке /bitrix/cache/my_component_name/

$useCache = ($arParams["CACHE_TYPE"] == 'A');
$cacheValid = false;

if($useCache)
	{
	$cachedResult = new CPHPCache();

	$cacheId = "";
	foreach($_GET as $key => $val)
		$cacheId.=$key.$val;

	$cacheValid = $cachedResult->InitCache(intval($arParams["CACHE_TIME"]), $cacheId, "/my_component_name");

	if($cacheValid)
		$arResult = $cachedResult->GetVars();
	}

if(!$useCache || !$cacheValid) // либо кэш отключен, либо нет действительного кэша
	{
	/*
	* Тут заполняется массив $arResult и формируется флаг $success, равный true,
	* если результат сформирован успешно или false, если возникла ошибка и кэшировать
	* результат не имеет смысла
	*/

	if($success && $useCache)
		{
		// сохранить в кэше массив $arResult
		$cachedResult->StartDataCache();
		$cachedResult->EndDataCache($arResult);
		}
	}



понедельник, 8 августа 2011 г.

php: рекурсивное удаление из директории файлов, имена которых не соответствуют шаблону

/**
 * Deletes from $dir all files, names of which not matching regexp
 * @param  $dir directory path (without tailing slash)
 * @param  $regexp regular expression to check filenames with preg_match.
 * Non mathing files will be deleted.
 * @param int $silent if 0, all messages (info / error) will be echoed, if 1 -
 * only error messages, if 2 - all output will be suppressed (defaults to 1)
 * @return int number of files, matching pattern and thus not deleted, -1 on failure
 */
function cleanDir($dir, $regexp, $silent=1)
{
    if(($dh=opendir($dir))!==false)
        {
        $noError = true;
        $filesStillInDir = 0;
        if(!$silent)
            echo "Cleaning dir $dir";
        while ($file = readdir($dh))
            {
            if($file != ".." && $file!=".")
                {
                if(is_dir($dir."/".$file)) // If directory - recursive call
                    {
                    $filesStillInSubDir = cleanDir($dir."/".$file,$regexp,$silent);
                    if(-1 != $filesStillInSubDir)
                        {
                        $filesStillInDir+=$filesStillInSubDir; // increase count of files, still remaining in current directory
                        
                        if(0 == $filesStillInSubDir) // no files, matching pattern, found in dir - delete it
                            {
                            if(!$silent)
                                echo "Dir $dir/$file empty - deleting";
                            if(!@rmdir($dir."/".$file))
                                {
                                $noError = false;
                                if($silent < 2)
                                    echo "
Error deleting dir after cleaning: $dir/$file
"; } } } else { if($silent < 2) echo "
Error cleaning dir $dir/$file
"; $noError = false; } } else // is file => check name and, if not matches - delete it { if(preg_match($regexp, $file) == 0) { if(!@unlink($dir."/".$file)) { $noError = false; if($silent < 2) echo "
Error deleting file $dir/$file
"; } } else $filesStillInDir++; } } } closedir($dh); if($noError) return $filesStillInDir; else return -1; } if($silent < 2) echo "
Error opening dir $dir for cleaning!
"; return -1; }

Дирактории, в которых не осталось файлов, также удаляются (кроме родительской директории, для которой функция была вызвана изначально).
Пример вызова (удаление всех файлов, кроме файлов интернационализации в Java):
$propFilesFound = cleanDir($pathToUnpack,'/Resources.*\.properties$/');

четверг, 28 июля 2011 г.

Имя пакета PostgreSQL-модуля для PHP в Debian

 php5-pgsql

Отключение "живой сессии" в 1С-Битрикс

"Живая сессия" в 1С-Битрикс позволяет не закрывать сессию на сервере, пока активно окно браузера пользователя. Однако, несмотря на очевидные плюсы, решение это потребляет трафик, причем значительно (10-20 Мб за пару часов). Хорошо, что разработчики Битрикса предусмотрели возможность её отключить:

Настройки -> Настройки Продукта -> Настройки модулей -> Главный модуль -> Вкладка "Авторизация" -> Снять галочку "Продлевать сессию при активности посетителя в окне браузера".

понедельник, 25 июля 2011 г.

Извлечение двоичных данных (типа bytea) из PostgreSQL в PHP

После выполнения SELECT`а с извлечением данных из столбца Postgres необходимо данные, извлеченные из таблицы, пропустить через функцию pg_unescape_bytea():

$arr = pg_fetch_assoc($result);
$arr["blob_col"] = pg_unescape_bytea($arr["blob_col"]);

Для записи двоичных данных в Postgres перед выполнением соответствующего INSERT / UPDATE двоичные данные пропускаются через функцию pg_escape_bytea().

среда, 20 июля 2011 г.

cp1251 в php + utf8 в PostgreSQL

Проблема: БД использует кодировку UTF-8, php-страница, на которой нужно отображать данные из БД - cp1251.

Решение: в строку соединения с БД postgres добавить опцию options='--client_encoding=WIN', т.е. получится нечто вроде:

pg_connect("host=some_host port=5432 user=root password=root dbname=mydb options='--client_encoding=WIN'");

пятница, 15 июля 2011 г.

Файл настройки соединения с БД в 1С-Битрикс

Файл лежит тут (относительно директории устновки битрикса):

bitrix/php_interface/dbconn.php

В нём находятся все параметры соединения с БД, которую Битрикс использует для хранения данных. Там можно, например, посмотреть пароль от БД, если Вы его забыли или никогда и не знали ;)